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 Dan Stillman
parent 5920c5d3e5
commit b5bdae3d6d
4 changed files with 127 additions and 41 deletions

View file

@ -48,7 +48,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
@ -173,6 +176,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
@ -185,6 +202,14 @@ var ZoteroContextPane = new function () {
_tabCover.classList.add('hidden');
}
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.classList.remove('hidden');
@ -204,6 +229,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]);
}
})();
}
@ -416,12 +452,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.createXULElement('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 = () => {
@ -448,7 +489,7 @@ var ZoteroContextPane = new function () {
input.value = '';
_updateNotesList();
_setPinnedNote(note.id);
_setPinnedNote(note);
}
function _createNote(child) {
@ -462,10 +503,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 = '';
@ -670,7 +708,10 @@ var ZoteroContextPane = new function () {
<NotesList
ref={notesListRef}
onClick={(id) => {
_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);
@ -725,19 +766,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;

View file

@ -306,13 +306,13 @@
}
async focusFirst() {
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) {

View file

@ -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.createXULElement('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
@ -388,6 +389,7 @@ var Zotero_Tabs = new function () {
}
this.close(tab.id);
this.add({
id: tab.id,
type: 'reader-unloaded',
title: tab.title,
index: tabIndex,

View file

@ -1017,7 +1017,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;
@ -1026,6 +1026,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,
@ -1060,20 +1061,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) {
}
});
}
@ -1362,7 +1367,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;
@ -1404,6 +1409,7 @@ class Reader {
itemID,
title,
index: tabIndex,
tabID,
background: openInBackground,
sidebarWidth: this._sidebarWidth,
sidebarOpen: this._sidebarOpen,