From f0b896d2829c019dcc64ee8ae56afc98b822a7a6 Mon Sep 17 00:00:00 2001 From: Martynas Bagdonas Date: Fri, 1 Jul 2022 10:28:20 +0300 Subject: [PATCH] Implement smart notes switching in contextPane (#2671) Fixes #2650 --- chrome/content/zotero/bindings/noteeditor.xml | 15 +-- chrome/content/zotero/contextPane.js | 112 +++++++++++++++--- chrome/content/zotero/tabs.js | 8 +- chrome/content/zotero/xpcom/reader.js | 36 +++--- 4 files changed, 129 insertions(+), 42 deletions(-) diff --git a/chrome/content/zotero/bindings/noteeditor.xml b/chrome/content/zotero/bindings/noteeditor.xml index 8dc4d400cc..f5b98dea63 100644 --- a/chrome/content/zotero/bindings/noteeditor.xml +++ b/chrome/content/zotero/bindings/noteeditor.xml @@ -353,15 +353,16 @@ { - let n = 0; - while (!this._editorInstance && n++ < 100) { - await Zotero.Promise.delay(10); - } - await this._editorInstance._initPromise; - this._iframe.focus(); 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(); - } catch(e) { + } + catch (e) { } })(); ]]> diff --git a/chrome/content/zotero/contextPane.js b/chrome/content/zotero/contextPane.js index 24d6b5493d..b355a6facb 100644 --- a/chrome/content/zotero/contextPane.js +++ b/chrome/content/zotero/contextPane.js @@ -51,7 +51,10 @@ var ZoteroContextPane = new function () { var _itemContexts = []; var _notesContexts = []; - + + var _globalDeckIndex = []; + var _preventGlobalDeckChange = false; + // Using attribute instead of property to set 'selectedIndex' // is more reliable @@ -176,6 +179,20 @@ var ZoteroContextPane = new function () { if (Zotero_Tabs.deck.children.length == 1) { _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') { // It seems that changing `hidden` or `collapsed` values might @@ -188,6 +205,14 @@ var ZoteroContextPane = new function () { _tabCover.hidden = true; } 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); if (reader) { _tabCover.hidden = false; @@ -207,6 +232,17 @@ var ZoteroContextPane = new function () { var notesContext = _getNotesContext(attachment.libraryID); 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.setAttribute('flex', 1); 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'); - contextNode.append(list, noteContainer); + contextNode.append(list, noteContainer, tabNotesDeck); _notesPaneDeck.append(contextNode); contextNode.className = 'context-node'; + contextNode.setAttribute('data-library-id', libraryID); contextNode.setAttribute('selectedIndex', 0); editor.returnHandler = () => { @@ -451,7 +492,7 @@ var ZoteroContextPane = new function () { input.value = ''; _updateNotesList(); - _setPinnedNote(note.id); + _setPinnedNote(note); } function _createNote(child) { @@ -465,10 +506,7 @@ var ZoteroContextPane = new function () { } item.parentID = attachment.parentID; } - editor.mode = 'edit'; - editor.item = item; - editor.parentItem = null; - editor.focus(); + _setPinnedNote(item); _updateAddToNote(); input.value = ''; @@ -674,7 +712,10 @@ var ZoteroContextPane = new function () { { - _setPinnedNote(id); + let item = Zotero.Items.get(id); + if (item) { + _setPinnedNote(item); + } }} onContextMenu={(id, event) => { 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; } - function _setPinnedNote(itemID) { - var item = Zotero.Items.get(itemID); - if (!item) { - return; - } + function _setPinnedNote(item) { var readOnly = _isLibraryReadOnly(item.libraryID); var context = _getNotesContext(item.libraryID); if (context) { var { editor, node } = context; - node.setAttribute('selectedIndex', 1); - editor.mode = readOnly ? 'view' : 'edit'; - editor.item = item; - editor.parentItem = null; + + let isChild = false; + var reader = Zotero.Reader.getByTabID(Zotero_Tabs.selectedID); + 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 = ''; var parentItem = item.parentItem; diff --git a/chrome/content/zotero/tabs.js b/chrome/content/zotero/tabs.js index 12878a7cb9..b9e107b518 100644 --- a/chrome/content/zotero/tabs.js +++ b/chrome/content/zotero/tabs.js @@ -152,7 +152,7 @@ var Zotero_Tabs = new function () { * @param {Function} onClose * @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 title != 'string') { @@ -164,7 +164,7 @@ var Zotero_Tabs = new function () { if (onClose !== undefined && typeof onClose != 'function') { 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'); container.id = id; this.deck.appendChild(container); @@ -251,7 +251,7 @@ var Zotero_Tabs = new function () { closedIDs.push(id); } this._history.push(historyEntry); - Zotero.Notifier.trigger('close', 'tab', [closedIDs]); + Zotero.Notifier.trigger('close', 'tab', [closedIDs], true); this._update(); }; @@ -333,6 +333,7 @@ var Zotero_Tabs = new function () { if (tab.type === 'reader-unloaded') { this.close(tab.id); Zotero.Reader.open(tab.data.itemID, null, { + tabID: tab.id, title: tab.title, tabIndex, allowDuplicate: true @@ -385,6 +386,7 @@ var Zotero_Tabs = new function () { } this.close(tab.id); this.add({ + id: tab.id, type: 'reader-unloaded', title: tab.title, index: tabIndex, diff --git a/chrome/content/zotero/xpcom/reader.js b/chrome/content/zotero/xpcom/reader.js index d7734cafdd..2293492a56 100644 --- a/chrome/content/zotero/xpcom/reader.js +++ b/chrome/content/zotero/xpcom/reader.js @@ -1010,7 +1010,7 @@ class ReaderInstance { } class ReaderTab extends ReaderInstance { - constructor({ itemID, title, sidebarWidth, sidebarOpen, bottomPlaceholderHeight, index, background }) { + constructor({ itemID, title, sidebarWidth, sidebarOpen, bottomPlaceholderHeight, index, tabID, background }) { super(); this._itemID = itemID; this._sidebarWidth = sidebarWidth; @@ -1019,6 +1019,7 @@ class ReaderTab extends ReaderInstance { this._showItemPaneToggle = true; this._window = Services.wm.getMostRecentWindow('navigator:browser'); let { id, container } = this._window.Zotero_Tabs.add({ + id: tabID, type: 'reader', title: title || '', index, @@ -1053,20 +1054,24 @@ class ReaderTab extends ReaderInstance { // 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 this._window.addEventListener('pointerup', (event) => { - if (this._window.Zotero_Tabs.selectedID === this.tabID - && this._iframeWindow - && event.target - && event.target.closest - && !event.target.closest('#outerContainer')) { - let evt = new this._iframeWindow.CustomEvent('mouseup', { bubbles: false }); - evt.clientX = event.clientX; - evt.clientY = event.clientY; - this._iframeWindow.dispatchEvent(evt); + try { + if (this._window.Zotero_Tabs.selectedID === this.tabID + && this._iframeWindow + && event.target + && event.target.closest + && !event.target.closest('#outerContainer')) { + let evt = new this._iframeWindow.CustomEvent('mouseup', { bubbles: false }); + evt.clientX = event.clientX; + evt.clientY = event.clientY; + this._iframeWindow.dispatchEvent(evt); - evt = new this._iframeWindow.CustomEvent('pointerup', { bubbles: false }); - evt.clientX = event.clientX; - evt.clientY = event.clientY; - this._iframeWindow.dispatchEvent(evt); + evt = new this._iframeWindow.CustomEvent('pointerup', { bubbles: false }); + evt.clientX = event.clientX; + evt.clientY = event.clientY; + this._iframeWindow.dispatchEvent(evt); + } + } + catch(e) { } }); } @@ -1355,7 +1360,7 @@ class Reader { 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.triggerAnnotationsImportCheck(itemID); let reader; @@ -1397,6 +1402,7 @@ class Reader { itemID, title, index: tabIndex, + tabID, background: openInBackground, sidebarWidth: this._sidebarWidth, sidebarOpen: this._sidebarOpen,