Improve PDF reader and note editor error handling

This commit is contained in:
Martynas Bagdonas 2021-03-18 10:03:36 +02:00
parent 1ede4240e0
commit 4f71c2ab09
2 changed files with 172 additions and 163 deletions

View file

@ -46,6 +46,7 @@ class EditorInstance {
async init(options) { async init(options) {
Zotero.Notes.registerEditorInstance(this); Zotero.Notes.registerEditorInstance(this);
this.onNavigate = options.onNavigate; this.onNavigate = options.onNavigate;
// TODO: Consider to use only itemID instead of loaded item
this._item = options.item; this._item = options.item;
this._viewMode = options.viewMode; this._viewMode = options.viewMode;
this._readOnly = options.readOnly; this._readOnly = options.readOnly;
@ -323,189 +324,197 @@ class EditorInstance {
return; return;
} }
let message = e.data.message; let message = e.data.message;
switch (message.action) { try {
case 'insertObject': { switch (message.action) {
let { type, data, pos } = message; case 'insertObject': {
if (this._readOnly) { let { type, data, pos } = message;
if (this._readOnly) {
return;
}
let html = '';
await this._ensureNoteCreated();
if (type === 'zotero/item') {
let ids = data.split(',').map(id => parseInt(id));
html = await this._digestItems(ids);
}
else if (type === 'zotero/annotation') {
let annotations = JSON.parse(data);
html = await this._serializeAnnotations(annotations);
}
if (html) {
this._postMessage({ action: 'insertHTML', pos, html });
}
return; return;
} }
let html = ''; case 'openAnnotation': {
await this._ensureNoteCreated(); let { uri, position } = message;
if (type === 'zotero/item') { if (this.onNavigate) {
let ids = data.split(',').map(id => parseInt(id)); this.onNavigate(uri, { position });
html = await this._digestItems(ids); }
else {
await Zotero.Reader.openURI(uri, { position });
}
return;
} }
else if (type === 'zotero/annotation') { case 'openCitationPage': {
let annotations = JSON.parse(data); let { citation } = message;
html = await this._serializeAnnotations(annotations); if (!citation.citationItems.length) {
return;
}
let citationItem = citation.citationItems[0];
let item = await this._getItemFromURIs(citationItem.uris);
if (!item) {
return;
}
let attachments = Zotero.Items.get(item.getAttachments()).filter(x => x.isPDFAttachment());
if (citationItem.locator && attachments.length === 1) {
await Zotero.Reader.open(attachments[0].id, { pageLabel: citationItem.locator });
}
else {
this._showInLibrary(item.id);
}
return;
} }
if (html) { case 'showCitationItem': {
let { citation } = message;
let items = [];
for (let citationItem of citation.citationItems) {
let item = await this._getItemFromURIs(citationItem.uris);
if (item) {
items.push(item);
}
}
if (items.length) {
this._showInLibrary(items.map(item => item.id));
}
return;
}
case 'openURL': {
let { url } = message;
let zp = Zotero.getActiveZoteroPane();
if (zp) {
zp.loadURI(url);
}
return;
}
case 'showNote': {
this._showInLibrary(this._item.id);
return;
}
case 'openWindow': {
// TODO: Can we can avoid creating empty note just to open it in a new window?
await this._ensureNoteCreated();
let zp = Zotero.getActiveZoteroPane();
zp.openNoteWindow(this._item.id);
return;
}
case 'openBackup': {
let zp = Zotero.getActiveZoteroPane();
if (zp) {
zp.openBackupNoteWindow(this._item.id);
}
return;
}
case 'update': {
let { noteData } = message;
if (this._readOnly) {
return;
}
await this._save(noteData);
return;
}
case 'generateCitation': {
if (this._readOnly) {
return;
}
let { citation, pos } = message;
let formatted = (await this._getFormattedCitationParts(citation)).join(';');
let html = `<span class="citation" data-citation="${encodeURIComponent(JSON.stringify(citation))}">(${formatted})</span>`;
this._postMessage({ action: 'insertHTML', pos, html }); this._postMessage({ action: 'insertHTML', pos, html });
}
return;
}
case 'openAnnotation': {
let { uri, position } = message;
if (this.onNavigate) {
this.onNavigate(uri, { position });
}
else {
await Zotero.Reader.openURI(uri, { position });
}
return;
}
case 'openCitationPage': {
let { citation } = message;
if (!citation.citationItems.length) {
return; return;
} }
let citationItem = citation.citationItems[0]; case 'subscribeProvider': {
let item = await this._getItemFromURIs(citationItem.uris); let { subscription } = message;
if (!item) { this._subscriptions.push(subscription);
await this._feedSubscription(subscription);
return; return;
} }
let attachments = Zotero.Items.get(item.getAttachments()).filter(x => x.isPDFAttachment()); case 'unsubscribeProvider': {
if (citationItem.locator && attachments.length === 1) { let { id } = message;
await Zotero.Reader.open(attachments[0].id, { pageLabel: citationItem.locator }); this._subscriptions.splice(this._subscriptions.findIndex(s => s.id === id), 1);
return;
} }
else { case 'openCitationPopup': {
this._showInLibrary(item.id); let { nodeID, citation } = message;
} if (this._readOnly) {
return; return;
}
case 'showCitationItem': {
let { citation } = message;
let items = [];
for (let citationItem of citation.citationItems) {
let item = await this._getItemFromURIs(citationItem.uris);
if (item) {
items.push(item);
} }
} citation = JSON.parse(JSON.stringify(citation));
let availableCitationItems = [];
if (items.length) { for (let citationItem of citation.citationItems) {
this._showInLibrary(items.map(item => item.id)); let item = await this._getItemFromURIs(citationItem.uris);
} if (item) {
return; availableCitationItems.push({ ...citationItem, id: item.id });
} }
case 'openURL': {
let { url } = message;
let zp = Zotero.getActiveZoteroPane();
if (zp) {
zp.loadURI(url);
}
return;
}
case 'showNote': {
this._showInLibrary(this._item.id);
return;
}
case 'openWindow': {
// TODO: Can we can avoid creating empty note just to open it in a new window?
await this._ensureNoteCreated();
let zp = Zotero.getActiveZoteroPane();
zp.openNoteWindow(this._item.id);
return;
}
case 'openBackup': {
let zp = Zotero.getActiveZoteroPane();
if (zp) {
zp.openBackupNoteWindow(this._item.id);
}
return;
}
case 'update': {
let { noteData } = message;
if (this._readOnly) {
return;
}
this._save(noteData);
return;
}
case 'generateCitation': {
if (this._readOnly) {
return;
}
let { citation, pos } = message;
let formatted = (await this._getFormattedCitationParts(citation)).join(';');
let html = `<span class="citation" data-citation="${encodeURIComponent(JSON.stringify(citation))}">(${formatted})</span>`;
this._postMessage({ action: 'insertHTML', pos, html });
return;
}
case 'subscribeProvider': {
let { subscription } = message;
this._subscriptions.push(subscription);
await this._feedSubscription(subscription);
return;
}
case 'unsubscribeProvider': {
let { id } = message;
this._subscriptions.splice(this._subscriptions.findIndex(s => s.id === id), 1);
return;
}
case 'openCitationPopup': {
let { nodeID, citation } = message;
if (this._readOnly) {
return;
}
citation = JSON.parse(JSON.stringify(citation));
let availableCitationItems = [];
for (let citationItem of citation.citationItems) {
let item = await this._getItemFromURIs(citationItem.uris);
if (item) {
availableCitationItems.push({ ...citationItem, id: item.id });
} }
} citation.citationItems = availableCitationItems;
citation.citationItems = availableCitationItems; let libraryID = this._item.libraryID;
let libraryID = this._item.libraryID; this._openQuickFormatDialog(nodeID, citation, [libraryID]);
this._openQuickFormatDialog(nodeID, citation, [libraryID]);
return;
}
case 'importImages': {
let { images } = message;
if (this._readOnly) {
return; return;
} }
if (this._isAttachment) { case 'importImages': {
return; let { images } = message;
} if (this._readOnly) {
for (let image of images) { return;
let { nodeID, src } = image; }
let attachmentKey = await this._importImage(src, true); if (this._isAttachment) {
// TODO: Inform editor about the failed to import images return;
if (attachmentKey) { }
this._postMessage({ action: 'attachImportedImage', nodeID, attachmentKey }); for (let image of images) {
let { nodeID, src } = image;
let attachmentKey = await this._importImage(src, true);
// TODO: Inform editor about the failed to import images
if (attachmentKey) {
this._postMessage({ action: 'attachImportedImage', nodeID, attachmentKey });
}
} }
}
return;
}
case 'syncAttachmentKeys': {
if (this._readOnly) {
return; return;
} }
let { attachmentKeys } = message; case 'syncAttachmentKeys': {
if (this._isAttachment) { if (this._readOnly) {
return;
}
let { attachmentKeys } = message;
if (this._isAttachment) {
return;
}
let attachmentItems = this._item.getAttachments().map(id => Zotero.Items.get(id));
let abandonedItems = attachmentItems.filter(item => !attachmentKeys.includes(item.key));
for (let item of abandonedItems) {
// Store image data for undo. Although it stays as long
// as the note is opened. TODO: Find a better way
this._deletedImages[item.key] = await this._getDataURL(item);
await item.eraseTx();
}
return; return;
} }
let attachmentItems = this._item.getAttachments().map(id => Zotero.Items.get(id)); case 'openContextMenu': {
let abandonedItems = attachmentItems.filter(item => !attachmentKeys.includes(item.key)); let { x, y, pos, itemGroups } = message;
for (let item of abandonedItems) { this._openPopup(x, y, pos, itemGroups);
// Store image data for undo. Although it stays as long return;
// as the note is opened. TODO: Find a better way }
this._deletedImages[item.key] = await this._getDataURL(item); case 'return': {
await item.eraseTx(); this._onReturn();
return;
} }
return;
} }
case 'openContextMenu': { }
let { x, y, pos, itemGroups } = message; catch (e) {
this._openPopup(x, y, pos, itemGroups); if (message && ['update', 'importImages'].includes(message.action)) {
return; this._postMessage({ action: 'crash' });
}
case 'return': {
this._onReturn();
return;
} }
throw e;
} }
} }
@ -678,7 +687,7 @@ class EditorInstance {
catch (e) { catch (e) {
Zotero.logError(e); Zotero.logError(e);
Zotero.crash(true); Zotero.crash(true);
// TODO: Prevent further writing in the note throw e;
} }
} }

View file

@ -479,7 +479,7 @@ class ReaderInstance {
} }
} }
catch (e) { catch (e) {
let crash = ['setAnnotation'].includes(message.action); let crash = message && ['setAnnotation'].includes(message.action);
this._postMessage({ this._postMessage({
action: crash ? 'crash' : 'error', action: crash ? 'crash' : 'error',
message: `An error occurred during '${message ? message.action : ''}'`, message: `An error occurred during '${message ? message.action : ''}'`,