diff --git a/chrome/content/zotero/xpcom/data/items.js b/chrome/content/zotero/xpcom/data/items.js index 0f869f7bbc..ca54cce62b 100644 --- a/chrome/content/zotero/xpcom/data/items.js +++ b/chrome/content/zotero/xpcom/data/items.js @@ -189,6 +189,37 @@ Zotero.Items = function () { }; + /** + * Clear lastRead from all attachments in a library. + * + * @param {Integer} libraryID + */ + this.clearAllLastRead = async function (libraryID) { + let sql = "SELECT itemID FROM itemAttachments WHERE lastRead IS NOT NULL " + + "AND itemID IN (SELECT itemID FROM items WHERE libraryID = ?)"; + let ids = await Zotero.DB.columnQueryAsync(sql, [libraryID]); + if (!ids.length) { + return; + } + + let items = await this.getAsync(ids); + await Zotero.Utilities.Internal.forEachChunkAsync( + items, + 100, + async (chunk) => { + await Zotero.DB.executeTransaction(async () => { + for (let item of chunk) { + item.attachmentLastRead = null; + await item.save({ skipDateModifiedUpdate: true, skipEditCheck: true }); + } + }); + } + ); + + this._lastReadCutoffs.delete(libraryID); + }; + + // // Bulk data loading functions // diff --git a/chrome/content/zotero/zoteroPane.js b/chrome/content/zotero/zoteroPane.js index 5a48549b52..8055c9a3e0 100644 --- a/chrome/content/zotero/zoteroPane.js +++ b/chrome/content/zotero/zoteroPane.js @@ -2431,6 +2431,25 @@ var ZoteroPane = new function () { } } + this.clearAllLastRead = async function () { + let collectionTreeRow = this.getCollectionTreeRow(); + if (!collectionTreeRow.isRecentlyRead()) { + return; + } + + let result = Services.prompt.confirm( + window, + Zotero.getString('general.warning'), + Zotero.getString('recently-read-clear-all-confirm') + ); + if (!result) { + return; + } + + await Zotero.Items.clearAllLastRead(collectionTreeRow.ref.libraryID); + }; + + /** * Check whether every selected item can be restored from trash * @@ -3347,6 +3366,10 @@ var ZoteroPane = new function () { id: "deleteCollection", oncommand: () => this.deleteSelectedCollection() }, + { + id: "clearAllLastRead", + oncommand: () => this.clearAllLastRead() + }, { id: "deleteCollectionAndItems", oncommand: () => this.deleteSelectedCollection(true) @@ -3539,13 +3562,18 @@ var ZoteroPane = new function () { else if (collectionTreeRow.isTrash()) { show = ['emptyTrash']; } - else if (collectionTreeRow.isDuplicates() || collectionTreeRow.isUnfiled() || collectionTreeRow.isRecentlyRead() - || collectionTreeRow.isRetracted()) { + else if (collectionTreeRow.isDuplicates() || collectionTreeRow.isUnfiled() || collectionTreeRow.isRetracted()) { show = ['deleteCollection']; m.deleteCollection.setAttribute('label', Zotero.getString('general.hide')); useHideOrDelete = "hide"; } + else if (collectionTreeRow.isRecentlyRead()) { + show = ['deleteCollection', 'clearAllLastRead']; + + m.deleteCollection.setAttribute('label', Zotero.getString('general.hide')); + useHideOrDelete = "hide"; + } else if (collectionTreeRow.isHeader()) { } else if (collectionTreeRow.isPublications()) { diff --git a/chrome/content/zotero/zoteroPane.xhtml b/chrome/content/zotero/zoteroPane.xhtml index 62de1383fd..2aec7342db 100644 --- a/chrome/content/zotero/zoteroPane.xhtml +++ b/chrome/content/zotero/zoteroPane.xhtml @@ -966,6 +966,7 @@ + diff --git a/chrome/locale/en-US/zotero/zotero.ftl b/chrome/locale/en-US/zotero/zotero.ftl index df65a30cbf..7cbe88d020 100644 --- a/chrome/locale/en-US/zotero/zotero.ftl +++ b/chrome/locale/en-US/zotero/zotero.ftl @@ -185,6 +185,9 @@ collections-menu-show-recently-read = .label = Show { recently-read } item-menu-remove-from-recently-read = .label = Remove from { recently-read }… +collections-menu-clear-all-last-read = + .label = Clear All Last Read… +recently-read-clear-all-confirm = All Last Read dates in this library will be erased. collections-menu-rename-collection = .label = Rename Collection collections-menu-edit-saved-search =