diff --git a/chrome/content/zotero/components/itemPane/notesList.jsx b/chrome/content/zotero/components/itemPane/notesList.jsx
index aced7d0b3b..08611e5ca8 100644
--- a/chrome/content/zotero/components/itemPane/notesList.jsx
+++ b/chrome/content/zotero/components/itemPane/notesList.jsx
@@ -26,7 +26,7 @@
import React, { forwardRef, useImperativeHandle, useState, memo } from 'react';
import cx from 'classnames';
-const MAX_ALL_NOTES = 7;
+const MAX_UNEXPANDED_ALL_NOTES = 7;
const NoteRow = memo(({ id, title, body, date, onClick, onContextMenu, parentItemType, parentTitle }) => {
return (
@@ -54,15 +54,32 @@ const NoteRow = memo(({ id, title, body, date, onClick, onContextMenu, parentIte
const NotesList = forwardRef(({ onClick, onContextMenu, onAddChildButtonDown, onAddStandaloneButtonDown }, ref) => {
const [notes, setNotes] = useState([]);
const [expanded, setExpanded] = useState(false);
+ const [numVisible, setNumVisible] = useState(0);
const [hasParent, setHasParent] = useState(true);
- useImperativeHandle(ref, () => ({ setNotes, setExpanded, setHasParent }));
+
+ const _setExpanded = (value) => {
+ setExpanded(value);
+ if (value) {
+ setNumVisible(numVisible + 1000);
+ }
+ else {
+ setNumVisible(0);
+ }
+ };
+
+ useImperativeHandle(ref, () => ({
+ setNotes,
+ setHasParent,
+ setExpanded: _setExpanded
+ }));
function handleClickMore() {
- setExpanded(true);
+ _setExpanded(true);
}
let childNotes = notes.filter(x => x.isCurrentChild);
let allNotes = notes.filter(x => !x.isCurrentChild);
+ let visibleNotes = allNotes.slice(0, expanded ? numVisible : MAX_UNEXPANDED_ALL_NOTES);
return (
{hasParent &&
@@ -80,12 +97,12 @@ const NotesList = forwardRef(({ onClick, onContextMenu, onAddChildButtonDown, on
{!allNotes.length && {Zotero.getString('pane.context.noNotes')}
}
- {(expanded ? allNotes : allNotes.slice(0, MAX_ALL_NOTES))
- .map(note => )}
- {!expanded && allNotes.length > MAX_ALL_NOTES
+ {visibleNotes.map(note => )}
+ {allNotes.length > visibleNotes.length
&& {
- Zotero.getString('general.numMore', Zotero.Utilities.numberFormat([allNotes.length - MAX_ALL_NOTES], 0))
+ Zotero.getString('general.numMore', Zotero.Utilities.numberFormat(
+ [(allNotes.length - visibleNotes.length) - MAX_UNEXPANDED_ALL_NOTES], 0))
}
}
diff --git a/chrome/content/zotero/contextPane.js b/chrome/content/zotero/contextPane.js
index 67a9e0c41a..9b65b5205a 100644
--- a/chrome/content/zotero/contextPane.js
+++ b/chrome/content/zotero/contextPane.js
@@ -156,6 +156,9 @@ var ZoteroContextPane = new function () {
}
}
else if (action == 'select') {
+ // It seems that changing `hidden` or `collapsed` values might
+ // be related with significant slow down when there are too many
+ // DOM nodes (i.e. 10k notes)
if (Zotero_Tabs.selectedIndex == 0) {
_contextPaneSplitter.setAttribute('hidden', true);
_contextPane.setAttribute('collapsed', true);
@@ -470,9 +473,27 @@ var ZoteroContextPane = new function () {
var notesListRef = React.createRef();
+ function _isVisible() {
+ let splitter = Zotero.Prefs.get('layout') == 'stacked'
+ ? _contextPaneSplitterStacked : _contextPaneSplitter;
+
+ return Zotero_Tabs.selectedID != 'zotero-pane'
+ && _panesDeck.selectedIndex == 1
+ && context.node.selectedIndex == 0
+ && splitter.getAttribute('state') != 'collapsed';
+ }
+
async function _updateNotesList(useCached) {
var query = input.value;
var notes;
+
+ // Calls itself and debounces until notes list becomes
+ // visible, and then updates
+ if (!useCached && !_isVisible()) {
+ context.update();
+ return;
+ }
+
if (useCached && context.cachedNotes.length) {
notes = context.cachedNotes;
}
@@ -490,22 +511,25 @@ var ZoteroContextPane = new function () {
notes = await s.search();
notes = Zotero.Items.get(notes);
notes.sort((a, b) => {
- a = a.getField('dateModified');
- b = b.getField('dateModified');
- return b.localeCompare(a);
+ a = a.dateModified;
+ b = b.dateModified;
+ return (a > b ? -1 : (a < b ? 1 : 0));
});
+ let cachedNotesIndex = new Map();
+ for (let cachedNote of context.cachedNotes) {
+ cachedNotesIndex.set(cachedNote.id, cachedNote);
+ }
notes = notes.map(note => {
var parentItem = note.parentItem;
// If neither note nor parent item is affected try to return the cached note
if (!context.affectedIDs.has(note.id)
&& (!parentItem || !context.affectedIDs.has(parentItem.id))) {
- let cachedNote = context.cachedNotes.find(x => x.id == note.id);
+ let cachedNote = cachedNotesIndex.get(note.id);
if (cachedNote) {
return cachedNote;
}
}
-
var text = note.note;
text = Zotero.Utilities.unescapeHTML(text);
text = text.trim();
@@ -513,7 +537,6 @@ var ZoteroContextPane = new function () {
var parts = text.split('\n').map(x => x.trim()).filter(x => x.length);
var title = parts[0] && parts[0].slice(0, Zotero.Notes.MAX_TITLE_LENGTH);
var date = Zotero.Date.sqlToDate(note.dateModified, true);
- // This takes half of the CPU time
date = Zotero.Date.toFriendlyDate(date);
return {
diff --git a/chrome/content/zotero/xpcom/date.js b/chrome/content/zotero/xpcom/date.js
index 848a6acc84..9f13c0fc0b 100644
--- a/chrome/content/zotero/xpcom/date.js
+++ b/chrome/content/zotero/xpcom/date.js
@@ -852,17 +852,28 @@ Zotero.Date = new function(){
return Zotero.getString("date.relative." + key + "." + (n ? "multiple" : "one"), n);
}
+ // Initializing `toFriendlyDate` formatters, since
+ // `toLocaleDateString` is extremely slow (4500ms vs 200ms
+ // for 10k calls)
+ var _friendlyDateTodayFormatter = new Intl.DateTimeFormat(
+ false, { hour: 'numeric', minute: 'numeric' });
+
+ var _friendlyDateWeekFormatter = new Intl.DateTimeFormat(
+ false, { weekday: 'long' });
+
+ var _friendlyDateRegularFormatter = new Intl.DateTimeFormat(
+ false, { year: '2-digit', month: 'numeric', day: 'numeric' });
this.toFriendlyDate = function (date) {
// 6:14:36 PM
if (isToday(date)) {
- return date.toLocaleString(false, { hour: 'numeric', minute: 'numeric' })
+ return _friendlyDateTodayFormatter.format(date);
}
// 'Thursday'
if (isThisWeek(date)) {
- return date.toLocaleString(false, { weekday: 'long' });
+ return _friendlyDateWeekFormatter.format(date);
}
- return date.toLocaleDateString(false, { year: '2-digit', month: 'numeric', day: 'numeric' });
+ return _friendlyDateRegularFormatter.format(date);
};