Optimize contextPane notes list rendering

This commit is contained in:
Martynas Bagdonas 2021-04-26 22:08:11 +03:00
parent 76d8818bed
commit e7eae06d8d
2 changed files with 21 additions and 8 deletions

View file

@ -23,14 +23,14 @@
***** END LICENSE BLOCK ***** ***** END LICENSE BLOCK *****
*/ */
import React, { forwardRef, useImperativeHandle, useState } from 'react'; import React, { forwardRef, useImperativeHandle, useState, memo } from 'react';
import cx from 'classnames'; import cx from 'classnames';
const MAX_ALL_NOTES = 7; const MAX_ALL_NOTES = 7;
const NoteRow = ({ title, body, date, onClick, onContextMenu, parentItemType, parentTitle }) => { const NoteRow = memo(({ id, title, body, date, onClick, onContextMenu, parentItemType, parentTitle }) => {
return ( return (
<div className={cx('note-row', { 'standalone-note-row': !parentItemType })} onClick={onClick} onContextMenu={onContextMenu}> <div className={cx('note-row', { 'standalone-note-row': !parentItemType })} onClick={() => onClick(id)} onContextMenu={(event) => onContextMenu(id, event)}>
<div className="inner"> <div className="inner">
{ parentItemType { parentItemType
? <div className="parent-line"> ? <div className="parent-line">
@ -49,7 +49,7 @@ const NoteRow = ({ title, body, date, onClick, onContextMenu, parentItemType, pa
</div> </div>
</div> </div>
); );
}; });
const NotesList = forwardRef(({ onClick, onContextMenu, onAddChildButtonDown, onAddStandaloneButtonDown }, ref) => { const NotesList = forwardRef(({ onClick, onContextMenu, onAddChildButtonDown, onAddStandaloneButtonDown }, ref) => {
const [notes, setNotes] = useState([]); const [notes, setNotes] = useState([]);
@ -72,7 +72,7 @@ const NotesList = forwardRef(({ onClick, onContextMenu, onAddChildButtonDown, on
</div> </div>
{!childNotes.length && <div className="empty-row">{Zotero.getString('pane.context.noNotes')}</div>} {!childNotes.length && <div className="empty-row">{Zotero.getString('pane.context.noNotes')}</div>}
{childNotes.map(note => <NoteRow key={note.id} {...note} {childNotes.map(note => <NoteRow key={note.id} {...note}
onClick={() => onClick(note.id)} onContextMenu={(event) => onContextMenu(note.id, event)}/>)} onClick={onClick} onContextMenu={onContextMenu}/>)}
</section>} </section>}
<section> <section>
<div className="header-row"> <div className="header-row">
@ -82,7 +82,7 @@ const NotesList = forwardRef(({ onClick, onContextMenu, onAddChildButtonDown, on
{!allNotes.length && <div className="empty-row">{Zotero.getString('pane.context.noNotes')}</div>} {!allNotes.length && <div className="empty-row">{Zotero.getString('pane.context.noNotes')}</div>}
{(expanded ? allNotes : allNotes.slice(0, MAX_ALL_NOTES)) {(expanded ? allNotes : allNotes.slice(0, MAX_ALL_NOTES))
.map(note => <NoteRow key={note.id} {...note} .map(note => <NoteRow key={note.id} {...note}
onClick={() => onClick(note.id)} onContextMenu={(event) => onContextMenu(note.id, event)}/>)} onClick={onClick} onContextMenu={onContextMenu}/>)}
{!expanded && allNotes.length > MAX_ALL_NOTES {!expanded && allNotes.length > MAX_ALL_NOTES
&& <div className="more-row" onClick={handleClickMore}>{ && <div className="more-row" onClick={handleClickMore}>{
Zotero.getString('general.numMore', Zotero.Utilities.numberFormat([allNotes.length - MAX_ALL_NOTES], 0)) Zotero.getString('general.numMore', Zotero.Utilities.numberFormat([allNotes.length - MAX_ALL_NOTES], 0))

View file

@ -54,7 +54,7 @@ var ZoteroContextPane = new function () {
var _itemContexts = []; var _itemContexts = [];
var _notesContexts = []; var _notesContexts = [];
// Using attribute instead of propery to set 'selectedIndex' // Using attribute instead of property to set 'selectedIndex'
// is more reliable // is more reliable
this.update = _update; this.update = _update;
@ -131,7 +131,7 @@ var ZoteroContextPane = new function () {
let libraryIDs = []; let libraryIDs = [];
for (let id of ids) { for (let id of ids) {
let item = Zotero.Items.get(id); let item = Zotero.Items.get(id);
if (item && item.isNote()) { if (item && (item.isNote() || item.isRegularItem())) {
libraryIDs.push(item.libraryID); libraryIDs.push(item.libraryID);
} }
else if (action == 'delete') { else if (action == 'delete') {
@ -140,6 +140,7 @@ var ZoteroContextPane = new function () {
} }
for (let context of _notesContexts) { for (let context of _notesContexts) {
if (libraryIDs.includes(context.libraryID)) { if (libraryIDs.includes(context.libraryID)) {
context.affectedIDs = new Set([...context.affectedIDs, ...ids]);
context.update(); context.update();
} }
} }
@ -496,6 +497,15 @@ var ZoteroContextPane = new function () {
notes = notes.map(note => { notes = notes.map(note => {
var parentItem = note.parentItem; 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);
if (cachedNote) {
return cachedNote;
}
}
var text = note.note; var text = note.note;
text = Zotero.Utilities.unescapeHTML(text); text = Zotero.Utilities.unescapeHTML(text);
text = text.trim(); text = text.trim();
@ -503,6 +513,7 @@ var ZoteroContextPane = new function () {
var parts = text.split('\n').map(x => x.trim()).filter(x => x.length); 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 title = parts[0] && parts[0].slice(0, Zotero.Notes.MAX_TITLE_LENGTH);
var date = Zotero.Date.sqlToDate(note.dateModified, true); var date = Zotero.Date.sqlToDate(note.dateModified, true);
// This takes half of the CPU time
date = Zotero.Date.toFriendlyDate(date); date = Zotero.Date.toFriendlyDate(date);
return { return {
@ -517,6 +528,7 @@ var ZoteroContextPane = new function () {
}); });
context.cachedNotes = notes; context.cachedNotes = notes;
} }
context.affectedIDs = new Set();
var attachment = _getCurrentAttachment(); var attachment = _getCurrentAttachment();
var parentID = attachment && attachment.parentID; var parentID = attachment && attachment.parentID;
@ -533,6 +545,7 @@ var ZoteroContextPane = new function () {
editor, editor,
notesListRef, notesListRef,
cachedNotes: [], cachedNotes: [],
affectedIDs: new Set(),
update: Zotero.Utilities.throttle(_updateNotesList, 1000, { leading: false }), update: Zotero.Utilities.throttle(_updateNotesList, 1000, { leading: false }),
updateFromCache: () => _updateNotesList(true) updateFromCache: () => _updateNotesList(true)
}; };