Implement smart notes switching in contextPane (#2671)

Fixes #2650
This commit is contained in:
Martynas Bagdonas 2022-07-01 10:28:20 +03:00 committed by GitHub
parent ec68216310
commit f0b896d282
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
4 changed files with 129 additions and 42 deletions

View file

@ -353,15 +353,16 @@
<body> <body>
<![CDATA[ <![CDATA[
(async () => { (async () => {
let n = 0;
while (!this._editorInstance && n++ < 100) {
await Zotero.Promise.delay(10);
}
await this._editorInstance._initPromise;
this._iframe.focus();
try { try {
let n = 0;
while (!this._editorInstance && n++ < 100) {
await Zotero.Promise.delay(10);
}
await this._editorInstance._initPromise;
this._iframe.focus();
this._editorInstance._iframeWindow.document.querySelector('.toolbar-button-return').focus(); this._editorInstance._iframeWindow.document.querySelector('.toolbar-button-return').focus();
} catch(e) { }
catch (e) {
} }
})(); })();
]]> ]]>

View file

@ -51,7 +51,10 @@ var ZoteroContextPane = new function () {
var _itemContexts = []; var _itemContexts = [];
var _notesContexts = []; var _notesContexts = [];
var _globalDeckIndex = [];
var _preventGlobalDeckChange = false;
// Using attribute instead of property to set 'selectedIndex' // Using attribute instead of property to set 'selectedIndex'
// is more reliable // is more reliable
@ -176,6 +179,20 @@ var ZoteroContextPane = new function () {
if (Zotero_Tabs.deck.children.length == 1) { if (Zotero_Tabs.deck.children.length == 1) {
_notesContexts.forEach(x => x.notesListRef.current.setExpanded(false)); _notesContexts.forEach(x => x.notesListRef.current.setExpanded(false));
} }
// Close tab specific notes if tab id no longer exists, but
// do that only when unloaded tab is reloaded
setTimeout(() => {
var contextNodes = Array.from(_notesPaneDeck.children);
for (let contextNode of contextNodes) {
var nodes = Array.from(contextNode.children[2].children);
for (let node of nodes) {
var tabID = node.getAttribute('data-tab-id');
if (!document.getElementById(tabID)) {
node.remove();
}
}
}
});
} }
else if (action == 'select') { else if (action == 'select') {
// It seems that changing `hidden` or `collapsed` values might // It seems that changing `hidden` or `collapsed` values might
@ -188,6 +205,14 @@ var ZoteroContextPane = new function () {
_tabCover.hidden = true; _tabCover.hidden = true;
} }
else if (Zotero_Tabs.selectedType == 'reader') { else if (Zotero_Tabs.selectedType == 'reader') {
if (_panesDeck.selectedIndex == 1
&& _notesPaneDeck.selectedPanel.selectedIndex != 2
&& !_preventGlobalDeckChange) {
let libraryID = _notesPaneDeck.selectedPanel.getAttribute('data-library-id');
_globalDeckIndex[libraryID] = _notesPaneDeck.selectedPanel.selectedIndex;
}
_preventGlobalDeckChange = false;
var reader = Zotero.Reader.getByTabID(Zotero_Tabs.selectedID); var reader = Zotero.Reader.getByTabID(Zotero_Tabs.selectedID);
if (reader) { if (reader) {
_tabCover.hidden = false; _tabCover.hidden = false;
@ -207,6 +232,17 @@ var ZoteroContextPane = new function () {
var notesContext = _getNotesContext(attachment.libraryID); var notesContext = _getNotesContext(attachment.libraryID);
notesContext.updateFromCache(); notesContext.updateFromCache();
} }
let tabNotesDeck = _notesPaneDeck.selectedPanel.children[2];
let selectedIndex = Array.from(tabNotesDeck.children).findIndex(x => x.getAttribute('data-tab-id') == ids[0]);
if (selectedIndex != -1) {
tabNotesDeck.setAttribute('selectedIndex', selectedIndex);
_notesPaneDeck.selectedPanel.setAttribute('selectedIndex', 2);
}
else {
let libraryID = _notesPaneDeck.selectedPanel.getAttribute('data-library-id');
_notesPaneDeck.selectedPanel.setAttribute('selectedIndex', _globalDeckIndex[libraryID]);
}
})(); })();
} }
@ -419,12 +455,17 @@ var ZoteroContextPane = new function () {
editor.className = 'zotero-context-pane-pinned-note'; editor.className = 'zotero-context-pane-pinned-note';
editor.setAttribute('flex', 1); editor.setAttribute('flex', 1);
noteContainer.append(title, editor); noteContainer.append(title, editor);
let tabNotesDeck = document.createElement('deck');
tabNotesDeck.className = 'zotero-context-pane-tab-notes-deck';
tabNotesDeck.setAttribute('flex', 1);
let contextNode = document.createElement('deck'); let contextNode = document.createElement('deck');
contextNode.append(list, noteContainer); contextNode.append(list, noteContainer, tabNotesDeck);
_notesPaneDeck.append(contextNode); _notesPaneDeck.append(contextNode);
contextNode.className = 'context-node'; contextNode.className = 'context-node';
contextNode.setAttribute('data-library-id', libraryID);
contextNode.setAttribute('selectedIndex', 0); contextNode.setAttribute('selectedIndex', 0);
editor.returnHandler = () => { editor.returnHandler = () => {
@ -451,7 +492,7 @@ var ZoteroContextPane = new function () {
input.value = ''; input.value = '';
_updateNotesList(); _updateNotesList();
_setPinnedNote(note.id); _setPinnedNote(note);
} }
function _createNote(child) { function _createNote(child) {
@ -465,10 +506,7 @@ var ZoteroContextPane = new function () {
} }
item.parentID = attachment.parentID; item.parentID = attachment.parentID;
} }
editor.mode = 'edit'; _setPinnedNote(item);
editor.item = item;
editor.parentItem = null;
editor.focus();
_updateAddToNote(); _updateAddToNote();
input.value = ''; input.value = '';
@ -674,7 +712,10 @@ var ZoteroContextPane = new function () {
<NotesList <NotesList
ref={notesListRef} ref={notesListRef}
onClick={(id) => { onClick={(id) => {
_setPinnedNote(id); let item = Zotero.Items.get(id);
if (item) {
_setPinnedNote(item);
}
}} }}
onContextMenu={(id, event) => { onContextMenu={(id, event) => {
document.getElementById('context-pane-list-move-to-trash').setAttribute('disabled', readOnly); document.getElementById('context-pane-list-move-to-trash').setAttribute('disabled', readOnly);
@ -729,19 +770,56 @@ var ZoteroContextPane = new function () {
return !Zotero.Libraries.get(libraryID).editable; return !Zotero.Libraries.get(libraryID).editable;
} }
function _setPinnedNote(itemID) { function _setPinnedNote(item) {
var item = Zotero.Items.get(itemID);
if (!item) {
return;
}
var readOnly = _isLibraryReadOnly(item.libraryID); var readOnly = _isLibraryReadOnly(item.libraryID);
var context = _getNotesContext(item.libraryID); var context = _getNotesContext(item.libraryID);
if (context) { if (context) {
var { editor, node } = context; var { editor, node } = context;
node.setAttribute('selectedIndex', 1);
editor.mode = readOnly ? 'view' : 'edit'; let isChild = false;
editor.item = item; var reader = Zotero.Reader.getByTabID(Zotero_Tabs.selectedID);
editor.parentItem = null; if (reader) {
let attachment = Zotero.Items.get(reader.itemID);
if (attachment.parentItemID == item.parentItemID) {
isChild = true;
}
}
var tabNotesDeck = _notesPaneDeck.selectedPanel.children[2];
if (isChild) {
var vbox = document.createElement('vbox');
vbox.setAttribute('data-tab-id', Zotero_Tabs.selectedID);
vbox.style.display = 'flex';
editor = document.createElement('zoteronoteeditor');
editor.style.display = 'flex';
editor.style.width = '100%';
vbox.append(editor);
tabNotesDeck.append(vbox);
editor.mode = readOnly ? 'view' : 'edit';
editor.item = item;
editor.parentItem = null;
editor.returnHandler = () => {
_panesDeck.setAttribute('selectedIndex', 1);
_notesPaneDeck.selectedPanel.setAttribute('selectedIndex', 0);
vbox.remove();
_updateAddToNote();
_preventGlobalDeckChange = true;
};
_notesPaneDeck.selectedPanel.setAttribute('selectedIndex', 2);
tabNotesDeck.setAttribute('selectedIndex', tabNotesDeck.children.length - 1);
}
else {
node.setAttribute('selectedIndex', 1);
editor.mode = readOnly ? 'view' : 'edit';
editor.item = item;
editor.parentItem = null;
}
editor.focus();
node.querySelector('.zotero-context-pane-editor-parent-line').innerHTML = ''; node.querySelector('.zotero-context-pane-editor-parent-line').innerHTML = '';
var parentItem = item.parentItem; var parentItem = item.parentItem;

View file

@ -152,7 +152,7 @@ var Zotero_Tabs = new function () {
* @param {Function} onClose * @param {Function} onClose
* @return {{ id: string, container: XULElement}} id - tab id, container - a new tab container created in the deck * @return {{ id: string, container: XULElement}} id - tab id, container - a new tab container created in the deck
*/ */
this.add = function ({ type, data, title, index, select, onClose }) { this.add = function ({ id, type, data, title, index, select, onClose }) {
if (typeof type != 'string') { if (typeof type != 'string') {
} }
if (typeof title != 'string') { if (typeof title != 'string') {
@ -164,7 +164,7 @@ var Zotero_Tabs = new function () {
if (onClose !== undefined && typeof onClose != 'function') { if (onClose !== undefined && typeof onClose != 'function') {
throw new Error(`'onClose' should be a function (was ${typeof onClose})`); throw new Error(`'onClose' should be a function (was ${typeof onClose})`);
} }
var id = 'tab-' + Zotero.Utilities.randomString(); id = id || 'tab-' + Zotero.Utilities.randomString();
var container = document.createElement('vbox'); var container = document.createElement('vbox');
container.id = id; container.id = id;
this.deck.appendChild(container); this.deck.appendChild(container);
@ -251,7 +251,7 @@ var Zotero_Tabs = new function () {
closedIDs.push(id); closedIDs.push(id);
} }
this._history.push(historyEntry); this._history.push(historyEntry);
Zotero.Notifier.trigger('close', 'tab', [closedIDs]); Zotero.Notifier.trigger('close', 'tab', [closedIDs], true);
this._update(); this._update();
}; };
@ -333,6 +333,7 @@ var Zotero_Tabs = new function () {
if (tab.type === 'reader-unloaded') { if (tab.type === 'reader-unloaded') {
this.close(tab.id); this.close(tab.id);
Zotero.Reader.open(tab.data.itemID, null, { Zotero.Reader.open(tab.data.itemID, null, {
tabID: tab.id,
title: tab.title, title: tab.title,
tabIndex, tabIndex,
allowDuplicate: true allowDuplicate: true
@ -385,6 +386,7 @@ var Zotero_Tabs = new function () {
} }
this.close(tab.id); this.close(tab.id);
this.add({ this.add({
id: tab.id,
type: 'reader-unloaded', type: 'reader-unloaded',
title: tab.title, title: tab.title,
index: tabIndex, index: tabIndex,

View file

@ -1010,7 +1010,7 @@ class ReaderInstance {
} }
class ReaderTab extends ReaderInstance { class ReaderTab extends ReaderInstance {
constructor({ itemID, title, sidebarWidth, sidebarOpen, bottomPlaceholderHeight, index, background }) { constructor({ itemID, title, sidebarWidth, sidebarOpen, bottomPlaceholderHeight, index, tabID, background }) {
super(); super();
this._itemID = itemID; this._itemID = itemID;
this._sidebarWidth = sidebarWidth; this._sidebarWidth = sidebarWidth;
@ -1019,6 +1019,7 @@ class ReaderTab extends ReaderInstance {
this._showItemPaneToggle = true; this._showItemPaneToggle = true;
this._window = Services.wm.getMostRecentWindow('navigator:browser'); this._window = Services.wm.getMostRecentWindow('navigator:browser');
let { id, container } = this._window.Zotero_Tabs.add({ let { id, container } = this._window.Zotero_Tabs.add({
id: tabID,
type: 'reader', type: 'reader',
title: title || '', title: title || '',
index, index,
@ -1053,20 +1054,24 @@ class ReaderTab extends ReaderInstance {
// events in PDF reader iframe when mouse up happens over another iframe // events in PDF reader iframe when mouse up happens over another iframe
// i.e. note-editor. There should be a better way to solve this // i.e. note-editor. There should be a better way to solve this
this._window.addEventListener('pointerup', (event) => { this._window.addEventListener('pointerup', (event) => {
if (this._window.Zotero_Tabs.selectedID === this.tabID try {
&& this._iframeWindow if (this._window.Zotero_Tabs.selectedID === this.tabID
&& event.target && this._iframeWindow
&& event.target.closest && event.target
&& !event.target.closest('#outerContainer')) { && event.target.closest
let evt = new this._iframeWindow.CustomEvent('mouseup', { bubbles: false }); && !event.target.closest('#outerContainer')) {
evt.clientX = event.clientX; let evt = new this._iframeWindow.CustomEvent('mouseup', { bubbles: false });
evt.clientY = event.clientY; evt.clientX = event.clientX;
this._iframeWindow.dispatchEvent(evt); evt.clientY = event.clientY;
this._iframeWindow.dispatchEvent(evt);
evt = new this._iframeWindow.CustomEvent('pointerup', { bubbles: false }); evt = new this._iframeWindow.CustomEvent('pointerup', { bubbles: false });
evt.clientX = event.clientX; evt.clientX = event.clientX;
evt.clientY = event.clientY; evt.clientY = event.clientY;
this._iframeWindow.dispatchEvent(evt); this._iframeWindow.dispatchEvent(evt);
}
}
catch(e) {
} }
}); });
} }
@ -1355,7 +1360,7 @@ class Reader {
await this.open(item.id, location, options); await this.open(item.id, location, options);
} }
async open(itemID, location, { title, tabIndex, openInBackground, openInWindow, allowDuplicate } = {}) { async open(itemID, location, { title, tabIndex, tabID, openInBackground, openInWindow, allowDuplicate } = {}) {
this._loadSidebarState(); this._loadSidebarState();
this.triggerAnnotationsImportCheck(itemID); this.triggerAnnotationsImportCheck(itemID);
let reader; let reader;
@ -1397,6 +1402,7 @@ class Reader {
itemID, itemID,
title, title,
index: tabIndex, index: tabIndex,
tabID,
background: openInBackground, background: openInBackground,
sidebarWidth: this._sidebarWidth, sidebarWidth: this._sidebarWidth,
sidebarOpen: this._sidebarOpen, sidebarOpen: this._sidebarOpen,