Optimize contextPane notes list rendering
This commit is contained in:
parent
76d8818bed
commit
e7eae06d8d
2 changed files with 21 additions and 8 deletions
|
@ -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))
|
||||||
|
|
|
@ -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)
|
||||||
};
|
};
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue