Optimistic updates for item tags box
Add/update/remove rows immediately and save after. If there's an error during saving, reload the pane.
This commit is contained in:
parent
ef7da3486a
commit
df353bdc05
|
@ -154,9 +154,10 @@
|
||||||
}
|
}
|
||||||
let data = extraData[ids[i]];
|
let data = extraData[ids[i]];
|
||||||
let tagName = data.tag;
|
let tagName = data.tag;
|
||||||
|
let tagType = data.type;
|
||||||
|
|
||||||
if (event == 'add') {
|
if (event == 'add') {
|
||||||
var newTabIndex = this.add(tagName);
|
var newTabIndex = this.add(tagName, tagType);
|
||||||
if (newTabIndex == -1) {
|
if (newTabIndex == -1) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -174,7 +175,7 @@
|
||||||
else if (event == 'modify') {
|
else if (event == 'modify') {
|
||||||
let oldTagName = data.old.tag;
|
let oldTagName = data.old.tag;
|
||||||
this.remove(oldTagName);
|
this.remove(oldTagName);
|
||||||
this.add(tagName);
|
this.add(tagName, tagType);
|
||||||
}
|
}
|
||||||
else if (event == 'remove') {
|
else if (event == 'remove') {
|
||||||
var oldTabIndex = this.remove(tagName);
|
var oldTabIndex = this.remove(tagName);
|
||||||
|
@ -325,15 +326,25 @@
|
||||||
// "-" button
|
// "-" button
|
||||||
if (this.editable) {
|
if (this.editable) {
|
||||||
remove.setAttribute('disabled', false);
|
remove.setAttribute('disabled', false);
|
||||||
var self = this;
|
remove.addEventListener('click', function (event) {
|
||||||
remove.addEventListener('click', function () {
|
|
||||||
Zotero.spawn(function* () {
|
Zotero.spawn(function* () {
|
||||||
self._lastTabIndex = false;
|
this._lastTabIndex = false;
|
||||||
if (tagData) {
|
if (tagData) {
|
||||||
let item = document.getBindingParent(this).item
|
let item = this.item;
|
||||||
|
this.remove(tagName);
|
||||||
|
try {
|
||||||
item.removeTag(tagName);
|
item.removeTag(tagName);
|
||||||
yield item.saveTx()
|
yield item.saveTx()
|
||||||
}
|
}
|
||||||
|
catch (e) {
|
||||||
|
this.reload();
|
||||||
|
throw e;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Remove empty textbox row
|
||||||
|
else {
|
||||||
|
row.parentNode.removeChild(row);
|
||||||
|
}
|
||||||
|
|
||||||
// Return focus to items pane
|
// Return focus to items pane
|
||||||
var tree = document.getElementById('zotero-items-tree');
|
var tree = document.getElementById('zotero-items-tree');
|
||||||
|
@ -341,7 +352,7 @@
|
||||||
tree.focus();
|
tree.focus();
|
||||||
}
|
}
|
||||||
}.bind(this));
|
}.bind(this));
|
||||||
});
|
}.bind(this));
|
||||||
}
|
}
|
||||||
]]></body>
|
]]></body>
|
||||||
</method>
|
</method>
|
||||||
|
@ -447,7 +458,7 @@
|
||||||
var box = elem.parentNode;
|
var box = elem.parentNode;
|
||||||
box.replaceChild(t, elem);
|
box.replaceChild(t, elem);
|
||||||
|
|
||||||
t.setAttribute('onblur', "return document.getBindingParent(this).blurHandler(this)");
|
t.setAttribute('onblur', "return document.getBindingParent(this).blurHandler(event)");
|
||||||
t.setAttribute('onkeypress', "return document.getBindingParent(this).handleKeyPress(event)");
|
t.setAttribute('onkeypress', "return document.getBindingParent(this).handleKeyPress(event)");
|
||||||
t.setAttribute('onpaste', "return document.getBindingParent(this).handlePaste(event)");
|
t.setAttribute('onpaste', "return document.getBindingParent(this).handlePaste(event)");
|
||||||
|
|
||||||
|
@ -498,11 +509,12 @@
|
||||||
var fieldname = 'tag';
|
var fieldname = 'tag';
|
||||||
|
|
||||||
var row = Zotero.getAncestorByTagName(target, 'row');
|
var row = Zotero.getAncestorByTagName(target, 'row');
|
||||||
|
let blurOnly = false;
|
||||||
|
|
||||||
// If non-empty last row, add new row
|
// If non-empty last row, only blur, because the open textbox will
|
||||||
|
// be cleared in hideEditor() and remain in place
|
||||||
if (row == row.parentNode.lastChild && !empty) {
|
if (row == row.parentNode.lastChild && !empty) {
|
||||||
var focusField = true;
|
blurOnly = true;
|
||||||
this._tabDirection = 1;
|
|
||||||
}
|
}
|
||||||
// If empty non-last row, refocus current row
|
// If empty non-last row, refocus current row
|
||||||
else if (row != row.parentNode.lastChild && empty) {
|
else if (row != row.parentNode.lastChild && empty) {
|
||||||
|
@ -514,9 +526,11 @@
|
||||||
this._lastTabIndex = false;
|
this._lastTabIndex = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
target.onblur = null;
|
yield this.blurHandler(event);
|
||||||
yield this.blurHandler(target);
|
|
||||||
|
|
||||||
|
if (blurOnly) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
if (focusField) {
|
if (focusField) {
|
||||||
this._focusField();
|
this._focusField();
|
||||||
}
|
}
|
||||||
|
@ -537,8 +551,7 @@
|
||||||
var tagsbox = Zotero.getAncestorByTagName(focused, 'tagsbox');
|
var tagsbox = Zotero.getAncestorByTagName(focused, 'tagsbox');
|
||||||
|
|
||||||
this._lastTabIndex = false;
|
this._lastTabIndex = false;
|
||||||
target.onblur = null;
|
yield this.blurHandler(event);
|
||||||
yield this.blurHandler(target);
|
|
||||||
|
|
||||||
if (tagsbox) {
|
if (tagsbox) {
|
||||||
tagsbox.closePopup();
|
tagsbox.closePopup();
|
||||||
|
@ -562,8 +575,7 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
this._tabDirection = event.shiftKey ? -1 : 1;
|
this._tabDirection = event.shiftKey ? -1 : 1;
|
||||||
target.onblur = null;
|
yield this.blurHandler(event);
|
||||||
yield this.blurHandler(target);
|
|
||||||
this._focusField();
|
this._focusField();
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
@ -633,8 +645,10 @@
|
||||||
|
|
||||||
|
|
||||||
<method name="hideEditor">
|
<method name="hideEditor">
|
||||||
<parameter name="textbox"/>
|
<parameter name="event"/>
|
||||||
<body><![CDATA[
|
<body><![CDATA[
|
||||||
|
var textbox = event.target;
|
||||||
|
|
||||||
return Zotero.spawn(function* () {
|
return Zotero.spawn(function* () {
|
||||||
Zotero.debug('Hiding editor');
|
Zotero.debug('Hiding editor');
|
||||||
|
|
||||||
|
@ -675,15 +689,36 @@
|
||||||
if (value !== "") {
|
if (value !== "") {
|
||||||
if (oldValue !== value) {
|
if (oldValue !== value) {
|
||||||
// The existing textbox will be removed in notify()
|
// The existing textbox will be removed in notify()
|
||||||
|
this.removeRow(row);
|
||||||
|
this.add(value);
|
||||||
|
if (event.type != 'blur') {
|
||||||
|
this._focusField();
|
||||||
|
}
|
||||||
|
try {
|
||||||
this.item.replaceTag(oldValue, value);
|
this.item.replaceTag(oldValue, value);
|
||||||
yield this.item.saveTx();
|
yield this.item.saveTx();
|
||||||
}
|
}
|
||||||
|
catch (e) {
|
||||||
|
this.reload();
|
||||||
|
throw e;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
// Existing tag cleared
|
// Existing tag cleared
|
||||||
else {
|
else {
|
||||||
|
try {
|
||||||
|
this.removeRow(row);
|
||||||
|
if (event.type != 'blur') {
|
||||||
|
this._focusField();
|
||||||
|
}
|
||||||
this.item.removeTag(oldValue);
|
this.item.removeTag(oldValue);
|
||||||
yield this.item.saveTx();
|
yield this.item.saveTx();
|
||||||
}
|
}
|
||||||
|
catch (e) {
|
||||||
|
this.reload();
|
||||||
|
throw e;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
// Multiple tags
|
// Multiple tags
|
||||||
else if (tags.length > 1) {
|
else if (tags.length > 1) {
|
||||||
|
@ -714,12 +749,22 @@
|
||||||
}
|
}
|
||||||
// Single tag at end
|
// Single tag at end
|
||||||
else {
|
else {
|
||||||
// Remove the textbox row. The new tag will be added in notify()
|
if (event.type == 'blur') {
|
||||||
// if it doesn't already exist.
|
this.removeRow(row);
|
||||||
row.parentNode.removeChild(row);
|
}
|
||||||
|
else {
|
||||||
|
textbox.value = '';
|
||||||
|
}
|
||||||
|
this.add(value);
|
||||||
this.item.addTag(value);
|
this.item.addTag(value);
|
||||||
|
try {
|
||||||
yield this.item.saveTx();
|
yield this.item.saveTx();
|
||||||
}
|
}
|
||||||
|
catch (e) {
|
||||||
|
this.reload();
|
||||||
|
throw e;
|
||||||
|
}
|
||||||
|
}
|
||||||
}.bind(this));
|
}.bind(this));
|
||||||
]]></body>
|
]]></body>
|
||||||
</method>
|
</method>
|
||||||
|
@ -732,7 +777,7 @@
|
||||||
var rows = rowsElement.childNodes;
|
var rows = rowsElement.childNodes;
|
||||||
|
|
||||||
// Don't add new row if there already is one
|
// Don't add new row if there already is one
|
||||||
if (rows.length > this.count) {
|
if (rows.length && rows[rows.length - 1].querySelector('textbox')) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -758,6 +803,7 @@
|
||||||
|
|
||||||
<method name="add">
|
<method name="add">
|
||||||
<parameter name="tagName"/>
|
<parameter name="tagName"/>
|
||||||
|
<parameter name="tagType"/>
|
||||||
<body><![CDATA[
|
<body><![CDATA[
|
||||||
var rowsElement = this.id('tagRows');
|
var rowsElement = this.id('tagRows');
|
||||||
var rows = rowsElement.childNodes;
|
var rows = rowsElement.childNodes;
|
||||||
|
@ -772,7 +818,7 @@
|
||||||
|
|
||||||
var tagData = {
|
var tagData = {
|
||||||
tag: tagName,
|
tag: tagName,
|
||||||
type: this.item.getTagType(tagName)
|
type: tagType
|
||||||
};
|
};
|
||||||
|
|
||||||
if (row) {
|
if (row) {
|
||||||
|
@ -810,7 +856,9 @@
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (collation.compareString(1, tagName, labels[i].textContent) > 0) {
|
if (collation.compareString(1, tagName, labels[i].textContent) > 0
|
||||||
|
// Ignore textbox at end
|
||||||
|
&& labels[i].tagName != 'textbox') {
|
||||||
labels[i].setAttribute('ztabindex', index);
|
labels[i].setAttribute('ztabindex', index);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
@ -826,6 +874,8 @@
|
||||||
rowsElement.appendChild(row);
|
rowsElement.appendChild(row);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
this.updateCount(this.count + 1);
|
||||||
|
|
||||||
return newTabIndex;
|
return newTabIndex;
|
||||||
]]></body>
|
]]></body>
|
||||||
</method>
|
</method>
|
||||||
|
@ -841,16 +891,8 @@
|
||||||
for (var i=0; i<rows.length; i++) {
|
for (var i=0; i<rows.length; i++) {
|
||||||
let value = rows[i].getAttribute('tagName');
|
let value = rows[i].getAttribute('tagName');
|
||||||
if (value === tagName) {
|
if (value === tagName) {
|
||||||
oldTabIndex = i + 1;
|
oldTabIndex = this.removeRow(rows[i]);
|
||||||
removed = true;
|
break;
|
||||||
rowsElement.removeChild(rows[i]);
|
|
||||||
i--;
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
// After the removal, update tab indexes
|
|
||||||
if (removed) {
|
|
||||||
var elem = rows[i].getElementsByAttribute('fieldname', 'tag')[0];
|
|
||||||
elem.setAttribute('ztabindex', i + 1);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return oldTabIndex;
|
return oldTabIndex;
|
||||||
|
@ -858,6 +900,27 @@
|
||||||
</method>
|
</method>
|
||||||
|
|
||||||
|
|
||||||
|
<!--
|
||||||
|
Remove the row and update tab indexes
|
||||||
|
-->
|
||||||
|
<method name="removeRow">
|
||||||
|
<parameter name="row"/>
|
||||||
|
<body><![CDATA[
|
||||||
|
var origTabIndex = row.getElementsByAttribute('fieldname', 'tag')[0]
|
||||||
|
.getAttribute('ztabindex');
|
||||||
|
var origRow = row;
|
||||||
|
var i = origTabIndex;
|
||||||
|
while (row = row.nextSibling) {
|
||||||
|
let elem = row.getElementsByAttribute('fieldname', 'tag')[0];
|
||||||
|
elem.setAttribute('ztabindex', i++);
|
||||||
|
}
|
||||||
|
origRow.parentNode.removeChild(origRow);
|
||||||
|
this.updateCount(this.count - 1);
|
||||||
|
return origTabIndex;
|
||||||
|
]]></body>
|
||||||
|
</method>
|
||||||
|
|
||||||
|
|
||||||
<method name="removeAll">
|
<method name="removeAll">
|
||||||
<body><![CDATA[
|
<body><![CDATA[
|
||||||
if (Services.prompt.confirm(null, "", Zotero.getString('pane.item.tags.removeAll'))) {
|
if (Services.prompt.confirm(null, "", Zotero.getString('pane.item.tags.removeAll'))) {
|
||||||
|
@ -878,11 +941,13 @@
|
||||||
|
|
||||||
if(typeof count == 'undefined') {
|
if(typeof count == 'undefined') {
|
||||||
var tags = this.item.getTags();
|
var tags = this.item.getTags();
|
||||||
if(tags)
|
if (tags) {
|
||||||
count = tags.length;
|
count = tags.length;
|
||||||
else
|
}
|
||||||
|
else {
|
||||||
count = 0;
|
count = 0;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
var str = 'pane.item.tags.count.';
|
var str = 'pane.item.tags.count.';
|
||||||
switch (count){
|
switch (count){
|
||||||
|
@ -993,6 +1058,16 @@
|
||||||
]]></body>
|
]]></body>
|
||||||
</method>
|
</method>
|
||||||
|
|
||||||
|
<method name="_onAddButtonPress">
|
||||||
|
<parameter name="event"/>
|
||||||
|
<body><![CDATA[
|
||||||
|
return async function () {
|
||||||
|
await this.blurOpenField();
|
||||||
|
this.newTag();
|
||||||
|
}.bind(this)();
|
||||||
|
]]></body>
|
||||||
|
</method>
|
||||||
|
|
||||||
|
|
||||||
<method name="_onBackgroundContextMenuShowing">
|
<method name="_onBackgroundContextMenuShowing">
|
||||||
<body><![CDATA[
|
<body><![CDATA[
|
||||||
|
@ -1044,14 +1119,20 @@
|
||||||
|
|
||||||
|
|
||||||
<method name="blurOpenField">
|
<method name="blurOpenField">
|
||||||
|
<parameter name="stayOpen"/>
|
||||||
<body><![CDATA[
|
<body><![CDATA[
|
||||||
return Zotero.spawn(function* () {
|
return Zotero.spawn(function* () {
|
||||||
this._lastTabIndex = false;
|
this._lastTabIndex = false;
|
||||||
|
|
||||||
var textboxes = document.getAnonymousNodes(this)[0].getElementsByTagName('textbox');
|
var textboxes = document.getAnonymousNodes(this)[0].getElementsByTagName('textbox');
|
||||||
if (textboxes && textboxes.length) {
|
if (textboxes && textboxes.length) {
|
||||||
textboxes[0].inputField.onblur = null;
|
yield this.blurHandler({
|
||||||
yield this.blurHandler(textboxes[0].inputField);
|
target: textboxes[0],
|
||||||
|
// If coming from the Add button, pretend user pressed return
|
||||||
|
type: stayOpen ? 'keypress' : 'blur',
|
||||||
|
// DOM_VK_RETURN
|
||||||
|
keyCode: stayOpen ? 13 : undefined
|
||||||
|
});
|
||||||
}
|
}
|
||||||
}.bind(this));
|
}.bind(this));
|
||||||
]]>
|
]]>
|
||||||
|
@ -1082,7 +1163,7 @@
|
||||||
<xul:label id="tagsNum"/>
|
<xul:label id="tagsNum"/>
|
||||||
<xul:button id="addButton" label="&zotero.item.add;"
|
<xul:button id="addButton" label="&zotero.item.add;"
|
||||||
onkeypress="return document.getBindingParent(this)._onAddButtonKeypress(event)"
|
onkeypress="return document.getBindingParent(this)._onAddButtonKeypress(event)"
|
||||||
oncommand="document.getBindingParent(this).newTag();"/>
|
oncommand="return document.getBindingParent(this)._onAddButtonPress(event)"/>
|
||||||
</xul:hbox>
|
</xul:hbox>
|
||||||
<xul:grid>
|
<xul:grid>
|
||||||
<xul:columns>
|
<xul:columns>
|
||||||
|
|
Loading…
Reference in New Issue
Block a user