diff --git a/chrome/content/zotero/xpcom/data/items.js b/chrome/content/zotero/xpcom/data/items.js index 07487c95ef..dc6b20bc65 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 627b70dcc1..08f79a0b47 100644 --- a/chrome/content/zotero/zoteroPane.js +++ b/chrome/content/zotero/zoteroPane.js @@ -2480,6 +2480,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 * @@ -3396,6 +3415,10 @@ var ZoteroPane = new function () { id: "deleteCollection", oncommand: () => this.deleteSelectedCollection() }, + { + id: "clearAllLastRead", + oncommand: () => this.clearAllLastRead() + }, { id: "deleteCollectionAndItems", oncommand: () => this.deleteSelectedCollection(true) @@ -3588,13 +3611,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 ba1049efab..df852e9669 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 6a4807407a..e5aae211d6 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 =