From 1ac5ad2c84df24ca7ee421224caeeff09364df5b Mon Sep 17 00:00:00 2001
From: Abe Jellinek <1770299+AbeJellinek@users.noreply.github.com>
Date: Tue, 7 Apr 2026 15:03:27 -0400
Subject: [PATCH] =?UTF-8?q?Add=20Clear=20All=20Last=20Read=E2=80=A6=20to?=
=?UTF-8?q?=20Recently=20Read=20context=20menu?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
chrome/content/zotero/xpcom/data/items.js | 31 ++++++++++++++++++++++
chrome/content/zotero/zoteroPane.js | 32 +++++++++++++++++++++--
chrome/content/zotero/zoteroPane.xhtml | 1 +
chrome/locale/en-US/zotero/zotero.ftl | 3 +++
4 files changed, 65 insertions(+), 2 deletions(-)
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 =