Implement read/unread functionality in feeds
This commit is contained in:
parent
9686758c7d
commit
2c3eb205ab
10 changed files with 204 additions and 98 deletions
|
@ -50,6 +50,7 @@ Zotero.CollectionTreeView = function()
|
||||||
'publications',
|
'publications',
|
||||||
'share',
|
'share',
|
||||||
'group',
|
'group',
|
||||||
|
'feedItem',
|
||||||
'trash',
|
'trash',
|
||||||
'bucket'
|
'bucket'
|
||||||
],
|
],
|
||||||
|
@ -225,10 +226,10 @@ Zotero.CollectionTreeView.prototype.refresh = Zotero.Promise.coroutine(function*
|
||||||
}, 0),
|
}, 0),
|
||||||
added++
|
added++
|
||||||
);
|
);
|
||||||
for (let i = 0, len = groups.length; i < len; i++) {
|
for (let feed of feeds) {
|
||||||
this._addRowToArray(
|
this._addRowToArray(
|
||||||
newRows,
|
newRows,
|
||||||
new Zotero.CollectionTreeRow('feed', feeds[i]),
|
new Zotero.CollectionTreeRow('feed', feed),
|
||||||
added++
|
added++
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -251,10 +252,10 @@ Zotero.CollectionTreeView.prototype.refresh = Zotero.Promise.coroutine(function*
|
||||||
}, 0),
|
}, 0),
|
||||||
added++
|
added++
|
||||||
);
|
);
|
||||||
for (let i = 0, len = groups.length; i < len; i++) {
|
for (let group of groups) {
|
||||||
this._addRowToArray(
|
this._addRowToArray(
|
||||||
newRows,
|
newRows,
|
||||||
new Zotero.CollectionTreeRow('group', groups[i]),
|
new Zotero.CollectionTreeRow('group', group),
|
||||||
added++
|
added++
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -314,6 +315,13 @@ Zotero.CollectionTreeView.prototype.selectWait = Zotero.Promise.method(function
|
||||||
* Called by Zotero.Notifier on any changes to collections in the data layer
|
* Called by Zotero.Notifier on any changes to collections in the data layer
|
||||||
*/
|
*/
|
||||||
Zotero.CollectionTreeView.prototype.notify = Zotero.Promise.coroutine(function* (action, type, ids, extraData) {
|
Zotero.CollectionTreeView.prototype.notify = Zotero.Promise.coroutine(function* (action, type, ids, extraData) {
|
||||||
|
if (type == 'feed' && action == 'unreadCountUpdated') {
|
||||||
|
for (let i=0; i<ids.length; i++) {
|
||||||
|
this._treebox.invalidateRow(this._rowMap['L' + ids[i]]);
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
if ((!ids || ids.length == 0) && action != 'refresh' && action != 'redraw') {
|
if ((!ids || ids.length == 0) && action != 'refresh' && action != 'redraw') {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -2143,6 +2151,8 @@ Zotero.CollectionTreeView.prototype.getCellProperties = function(row, col, prop)
|
||||||
}
|
}
|
||||||
else if (treeRow.isPublications()) {
|
else if (treeRow.isPublications()) {
|
||||||
props.push("notwisty");
|
props.push("notwisty");
|
||||||
|
} else if (treeRow.ref && treeRow.ref.unreadCount) {
|
||||||
|
props.push('unread');
|
||||||
}
|
}
|
||||||
|
|
||||||
return props.join(" ");
|
return props.join(" ");
|
||||||
|
|
|
@ -178,6 +178,7 @@ Zotero.Feed.prototype._set = function (prop, val) {
|
||||||
Zotero.Feed.prototype._loadDataFromRow = function(row) {
|
Zotero.Feed.prototype._loadDataFromRow = function(row) {
|
||||||
Zotero.Feed._super.prototype._loadDataFromRow.call(this, row);
|
Zotero.Feed._super.prototype._loadDataFromRow.call(this, row);
|
||||||
|
|
||||||
|
this._feedName = row._feedName;
|
||||||
this._feedUrl = row._feedUrl;
|
this._feedUrl = row._feedUrl;
|
||||||
this._feedLastCheckError = row._feedLastCheckError || null;
|
this._feedLastCheckError = row._feedLastCheckError || null;
|
||||||
this._feedLastCheck = row._feedLastCheck || null;
|
this._feedLastCheck = row._feedLastCheck || null;
|
||||||
|
@ -274,8 +275,8 @@ Zotero.Feed.prototype._finalizeErase = Zotero.Promise.method(function(env) {
|
||||||
|
|
||||||
Zotero.Feed.prototype.getExpiredFeedItemIDs = Zotero.Promise.coroutine(function* () {
|
Zotero.Feed.prototype.getExpiredFeedItemIDs = Zotero.Promise.coroutine(function* () {
|
||||||
let sql = "SELECT itemID AS id FROM feedItems "
|
let sql = "SELECT itemID AS id FROM feedItems "
|
||||||
+ "WHERE readTimestamp IS NOT NULL "
|
+ "WHERE readTime IS NOT NULL "
|
||||||
+ "AND (julianday(readTimestamp, 'utc') + (?) - julianday('now', 'utc')) > 0";
|
+ "AND (julianday(readTime, 'utc') + (?) - julianday('now', 'utc')) > 0";
|
||||||
let expiredIDs = yield Zotero.DB.queryAsync(sql, [{int: this.cleanupAfter}]);
|
let expiredIDs = yield Zotero.DB.queryAsync(sql, [{int: this.cleanupAfter}]);
|
||||||
return expiredIDs.map(row => row.id);
|
return expiredIDs.map(row => row.id);
|
||||||
});
|
});
|
||||||
|
@ -289,7 +290,7 @@ Zotero.Feed.prototype._updateFeed = Zotero.Promise.coroutine(function* () {
|
||||||
Zotero.debug("Cleaning up read feed items...");
|
Zotero.debug("Cleaning up read feed items...");
|
||||||
if (expiredItems.length) {
|
if (expiredItems.length) {
|
||||||
Zotero.debug(expiredItems.join(', '));
|
Zotero.debug(expiredItems.join(', '));
|
||||||
yield Zotero.FeedItems.erase(expiredItems);
|
yield Zotero.FeedItems.eraseTx(expiredItems);
|
||||||
} else {
|
} else {
|
||||||
Zotero.debug("No expired feed items");
|
Zotero.debug("No expired feed items");
|
||||||
}
|
}
|
||||||
|
@ -301,7 +302,7 @@ Zotero.Feed.prototype._updateFeed = Zotero.Promise.coroutine(function* () {
|
||||||
|
|
||||||
try {
|
try {
|
||||||
let fr = new Zotero.FeedReader(this.url);
|
let fr = new Zotero.FeedReader(this.url);
|
||||||
let itemIterator = fr.createItemIterator();
|
let itemIterator = fr.itemIterator;
|
||||||
let item, toAdd = [], processedGUIDs = [];
|
let item, toAdd = [], processedGUIDs = [];
|
||||||
while (item = yield itemIterator.next().value) {
|
while (item = yield itemIterator.next().value) {
|
||||||
if (item.dateModified && this.lastUpdate
|
if (item.dateModified && this.lastUpdate
|
||||||
|
@ -320,14 +321,14 @@ Zotero.Feed.prototype._updateFeed = Zotero.Promise.coroutine(function* () {
|
||||||
}
|
}
|
||||||
processedGUIDs.push(item.guid);
|
processedGUIDs.push(item.guid);
|
||||||
|
|
||||||
Zotero.debug("New feed item retrieved:");
|
Zotero.debug("New feed item retrieved:", 5);
|
||||||
Zotero.debug(item);
|
Zotero.debug(item, 5);
|
||||||
|
|
||||||
let feedItem = yield Zotero.FeedItems.getAsyncByGUID(item.guid);
|
let feedItem = yield Zotero.FeedItems.getAsyncByGUID(item.guid);
|
||||||
if (!feedItem) {
|
if (!feedItem) {
|
||||||
feedItem = new Zotero.FeedItem();
|
feedItem = new Zotero.FeedItem();
|
||||||
feedItem.guid = item.guid;
|
feedItem.guid = item.guid;
|
||||||
feedItem.setCollections([this.id]);
|
feedItem.libraryID = this.id;
|
||||||
} else {
|
} else {
|
||||||
Zotero.debug("Feed item " + item.guid + " already in library.");
|
Zotero.debug("Feed item " + item.guid + " already in library.");
|
||||||
if (item.dateModified && feedItem.dateModified
|
if (item.dateModified && feedItem.dateModified
|
||||||
|
@ -364,7 +365,7 @@ Zotero.Feed.prototype._updateFeed = Zotero.Promise.coroutine(function* () {
|
||||||
|
|
||||||
this.lastCheck = Zotero.Date.dateToSQL(new Date(), true);
|
this.lastCheck = Zotero.Date.dateToSQL(new Date(), true);
|
||||||
this.lastCheckError = errorMessage || null;
|
this.lastCheckError = errorMessage || null;
|
||||||
yield this.save({skipEditCheck: true});
|
yield this.saveTx({skipEditCheck: true});
|
||||||
});
|
});
|
||||||
|
|
||||||
Zotero.Feed.prototype.updateFeed = function() {
|
Zotero.Feed.prototype.updateFeed = function() {
|
||||||
|
@ -380,3 +381,15 @@ Zotero.Feed.prototype.erase = Zotero.Promise.coroutine(function* () {
|
||||||
yield Zotero.FeedItems.erase(childItemIDs);
|
yield Zotero.FeedItems.erase(childItemIDs);
|
||||||
return Zotero.Feed._super.prototype.erase.call(this); // Don't tell it to delete child items. They're already gone
|
return Zotero.Feed._super.prototype.erase.call(this); // Don't tell it to delete child items. They're already gone
|
||||||
})
|
})
|
||||||
|
|
||||||
|
Zotero.Feed.prototype.updateUnreadCount = Zotero.Promise.coroutine(function* () {
|
||||||
|
let sql = "SELECT " + this._ObjectsClass._primaryDataSQLParts.feedUnreadCount
|
||||||
|
+ this._ObjectsClass.primaryDataSQLFrom
|
||||||
|
+ " AND O.libraryID=?";
|
||||||
|
let newCount = yield Zotero.DB.valueQueryAsync(sql, [this.id]);
|
||||||
|
|
||||||
|
if (newCount != this._feedUnreadCount) {
|
||||||
|
this._feedUnreadCount = newCount;
|
||||||
|
Zotero.Notifier.trigger('unreadCountUpdated', 'feed', this.id);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
|
@ -132,6 +132,51 @@ Zotero.FeedItem.prototype._saveData = Zotero.Promise.coroutine(function* (env) {
|
||||||
yield Zotero.DB.queryAsync(sql, [env.id, this.guid, this._feedItemReadTime]);
|
yield Zotero.DB.queryAsync(sql, [env.id, this.guid, this._feedItemReadTime]);
|
||||||
|
|
||||||
this._clearChanged('feedItemData');
|
this._clearChanged('feedItemData');
|
||||||
|
|
||||||
|
/* let itemID;
|
||||||
|
if (env.isNew) {
|
||||||
|
// For new items, run this first so we get an item ID
|
||||||
|
yield Zotero.FeedItem._super.prototype._saveData.apply(this, arguments);
|
||||||
|
itemID = env.id;
|
||||||
|
} else {
|
||||||
|
itemID = this.id;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!env.isNew) {
|
||||||
|
if (this.hasChanged()) {
|
||||||
|
yield Zotero.FeedItem._super.prototype._saveData.apply(this, arguments);
|
||||||
|
} else {
|
||||||
|
env.skipPrimaryDataReload = true;
|
||||||
|
}
|
||||||
|
Zotero.Notifier.trigger('modify', 'feedItem', itemID);
|
||||||
|
} else {
|
||||||
|
Zotero.Notifier.trigger('add', 'feedItem', itemID);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (env.collectionsAdded || env.collectionsRemoved) {
|
||||||
|
let affectedCollections = (env.collectionsAdded || [])
|
||||||
|
.concat(env.collectionsRemoved || []);
|
||||||
|
if (affectedCollections.length) {
|
||||||
|
let feeds = yield Zotero.Feeds.getAsync(affectedCollections);
|
||||||
|
for (let i=0; i<feeds.length; i++) {
|
||||||
|
feeds[i].updateUnreadCount();
|
||||||
|
}
|
||||||
|
}*/
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
Zotero.FeedItem.prototype.toggleRead = Zotero.Promise.coroutine(function* (state) {
|
||||||
|
state = state !== undefined ? !!state : !this.isRead;
|
||||||
|
let changed = this.isRead != state;
|
||||||
|
this.isRead = state;
|
||||||
|
if (changed) {
|
||||||
|
yield this.save({skipEditCheck: true, skipDateModifiedUpdate: true});
|
||||||
|
|
||||||
|
yield this.loadCollections();
|
||||||
|
let feed = Zotero.Feeds.get(this.libraryID);
|
||||||
|
feed.updateUnreadCount();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
|
@ -94,6 +94,19 @@ Zotero.FeedItems = new Proxy(function() {
|
||||||
return this.getAsync(id);
|
return this.getAsync(id);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
this.toggleReadById = Zotero.Promise.coroutine(function* (ids, state) {
|
||||||
|
if (!Array.isArray(ids)) {
|
||||||
|
if (typeof ids != 'string') throw new Error('ids must be a string or array in Zotero.FeedItems.toggleReadById');
|
||||||
|
|
||||||
|
ids = [ids];
|
||||||
|
}
|
||||||
|
|
||||||
|
let items = yield this.getAsync(ids);
|
||||||
|
for (let i=0; i<items.length; i++) {
|
||||||
|
items[i].toggleRead(state);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
return this;
|
return this;
|
||||||
}.call({}),
|
}.call({}),
|
||||||
|
|
||||||
|
|
|
@ -23,7 +23,7 @@
|
||||||
***** END LICENSE BLOCK *****
|
***** END LICENSE BLOCK *****
|
||||||
*/
|
*/
|
||||||
|
|
||||||
// Add some feed methods, but otherwise proxy to Zotero.Collections
|
// Mimics Zotero.Libraries
|
||||||
Zotero.Feeds = new function() {
|
Zotero.Feeds = new function() {
|
||||||
this._cache = null;
|
this._cache = null;
|
||||||
|
|
||||||
|
@ -105,12 +105,15 @@ Zotero.Feeds = new function() {
|
||||||
.map(id => Zotero.Libraries.get(id));
|
.map(id => Zotero.Libraries.get(id));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
this.get = Zotero.Libraries.get;
|
||||||
|
|
||||||
this.haveFeeds = function() {
|
this.haveFeeds = function() {
|
||||||
if (!this._cache) throw new Error("Zotero.Feeds cache is not initialized");
|
if (!this._cache) throw new Error("Zotero.Feeds cache is not initialized");
|
||||||
|
|
||||||
return !!Object.keys(this._cache.urlByLibraryID).length
|
return !!Object.keys(this._cache.urlByLibraryID).length
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let globalFeedCheckDelay = Zotero.Promise.resolve();
|
||||||
this.scheduleNextFeedCheck = Zotero.Promise.coroutine(function* () {
|
this.scheduleNextFeedCheck = Zotero.Promise.coroutine(function* () {
|
||||||
Zotero.debug("Scheduling next feed update.");
|
Zotero.debug("Scheduling next feed update.");
|
||||||
let sql = "SELECT ( CASE "
|
let sql = "SELECT ( CASE "
|
||||||
|
@ -146,4 +149,24 @@ Zotero.Feeds = new function() {
|
||||||
Zotero.debug("No feeds with auto-update.");
|
Zotero.debug("No feeds with auto-update.");
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
this.updateFeeds = Zotero.Promise.coroutine(function* () {
|
||||||
|
let sql = "SELECT libraryID AS id FROM feeds "
|
||||||
|
+ "WHERE refreshInterval IS NOT NULL "
|
||||||
|
+ "AND ( lastCheck IS NULL "
|
||||||
|
+ "OR (julianday(lastCheck, 'utc') + (refreshInterval/1440) - julianday('now', 'utc')) <= 0 )";
|
||||||
|
let needUpdate = yield Zotero.DB.queryAsync(sql).map(row => row.id);
|
||||||
|
Zotero.debug("Running update for feeds: " + needUpdate.join(', '));
|
||||||
|
let feeds = Zotero.Libraries.get(needUpdate);
|
||||||
|
let updatePromises = [];
|
||||||
|
for (let i=0; i<feeds.length; i++) {
|
||||||
|
updatePromises.push(feeds[i]._updateFeed());
|
||||||
|
}
|
||||||
|
|
||||||
|
return Zotero.Promise.settle(updatePromises)
|
||||||
|
.then(() => {
|
||||||
|
Zotero.debug("All feed updates done.");
|
||||||
|
this.scheduleNextFeedCheck()
|
||||||
|
});
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
|
@ -1660,6 +1660,9 @@ Zotero.Item.prototype._saveData = Zotero.Promise.coroutine(function* (env) {
|
||||||
|
|
||||||
let toAdd = Zotero.Utilities.arrayDiff(newCollections, oldCollections);
|
let toAdd = Zotero.Utilities.arrayDiff(newCollections, oldCollections);
|
||||||
let toRemove = Zotero.Utilities.arrayDiff(oldCollections, newCollections);
|
let toRemove = Zotero.Utilities.arrayDiff(oldCollections, newCollections);
|
||||||
|
|
||||||
|
env.collectionsAdded = toAdd;
|
||||||
|
env.collectionsRemoved = toRemove;
|
||||||
|
|
||||||
if (toAdd.length) {
|
if (toAdd.length) {
|
||||||
for (let i=0; i<toAdd.length; i++) {
|
for (let i=0; i<toAdd.length; i++) {
|
||||||
|
|
|
@ -53,9 +53,9 @@ Zotero.ItemTreeView = function (collectionTreeRow, sourcesOnly) {
|
||||||
|
|
||||||
this._refreshPromise = Zotero.Promise.resolve();
|
this._refreshPromise = Zotero.Promise.resolve();
|
||||||
|
|
||||||
this._unregisterID = Zotero.Notifier.registerObserver(
|
this._unregisterID = Zotero.Notifier.registerObserver(
|
||||||
this,
|
this,
|
||||||
['item', 'collection-item', 'item-tag', 'share-items', 'bucket'],
|
['item', 'collection-item', 'item-tag', 'share-items', 'bucket', 'feedItem'],
|
||||||
'itemTreeView',
|
'itemTreeView',
|
||||||
50
|
50
|
||||||
);
|
);
|
||||||
|
@ -391,6 +391,14 @@ Zotero.ItemTreeView.prototype.notify = Zotero.Promise.coroutine(function* (actio
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// FeedItem may have changed read/unread state
|
||||||
|
if (type == 'feedItem' && action == 'modify') {
|
||||||
|
for (let i=0; i<ids.length; i++) {
|
||||||
|
this._treebox.invalidateRow(this._itemRowMap[ids[i]]);
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
// Clear item type icon and tag colors when a tag is added to or removed from an item
|
// Clear item type icon and tag colors when a tag is added to or removed from an item
|
||||||
if (type == 'item-tag') {
|
if (type == 'item-tag') {
|
||||||
// TODO: Only update if colored tag changed?
|
// TODO: Only update if colored tag changed?
|
||||||
|
@ -526,12 +534,9 @@ Zotero.ItemTreeView.prototype.notify = Zotero.Promise.coroutine(function* (actio
|
||||||
// Since a remove involves shifting of rows, we have to do it in order,
|
// Since a remove involves shifting of rows, we have to do it in order,
|
||||||
// so sort the ids by row
|
// so sort the ids by row
|
||||||
var rows = [];
|
var rows = [];
|
||||||
|
let push = action == 'delete' || action == 'trash';
|
||||||
for (var i=0, len=ids.length; i<len; i++) {
|
for (var i=0, len=ids.length; i<len; i++) {
|
||||||
let push = false;
|
if (!push) {
|
||||||
if (action == 'delete' || action == 'trash') {
|
|
||||||
push = true;
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
push = !collectionTreeRow.ref.hasItem(ids[i]);
|
push = !collectionTreeRow.ref.hasItem(ids[i]);
|
||||||
}
|
}
|
||||||
// Row might already be gone (e.g. if this is a child and
|
// Row might already be gone (e.g. if this is a child and
|
||||||
|
@ -567,7 +572,7 @@ Zotero.ItemTreeView.prototype.notify = Zotero.Promise.coroutine(function* (actio
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else if (action == 'modify')
|
else if (type == 'item' && action == 'modify')
|
||||||
{
|
{
|
||||||
// Clear row caches
|
// Clear row caches
|
||||||
var items = yield Zotero.Items.getAsync(ids);
|
var items = yield Zotero.Items.getAsync(ids);
|
||||||
|
@ -685,7 +690,7 @@ Zotero.ItemTreeView.prototype.notify = Zotero.Promise.coroutine(function* (actio
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else if(action == 'add')
|
else if(type == 'item' && action == 'add')
|
||||||
{
|
{
|
||||||
let items = yield Zotero.Items.getAsync(ids);
|
let items = yield Zotero.Items.getAsync(ids);
|
||||||
|
|
||||||
|
@ -3054,56 +3059,25 @@ Zotero.ItemTreeView.prototype.getCellProperties = function(row, col, prop) {
|
||||||
|
|
||||||
// Mark items not matching search as context rows, displayed in gray
|
// Mark items not matching search as context rows, displayed in gray
|
||||||
if (this._searchMode && !this._searchItemIDs[itemID]) {
|
if (this._searchMode && !this._searchItemIDs[itemID]) {
|
||||||
// <=Fx21
|
props.push("contextRow");
|
||||||
if (prop) {
|
|
||||||
var aServ = Components.classes["@mozilla.org/atom-service;1"].
|
|
||||||
getService(Components.interfaces.nsIAtomService);
|
|
||||||
prop.AppendElement(aServ.getAtom("contextRow"));
|
|
||||||
}
|
|
||||||
// Fx22+
|
|
||||||
else {
|
|
||||||
props.push("contextRow");
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Mark hasAttachment column, which needs special image handling
|
// Mark hasAttachment column, which needs special image handling
|
||||||
if (col.id == 'zotero-items-column-hasAttachment') {
|
if (col.id == 'zotero-items-column-hasAttachment') {
|
||||||
// <=Fx21
|
props.push("hasAttachment");
|
||||||
if (prop) {
|
|
||||||
var aServ = Components.classes["@mozilla.org/atom-service;1"].
|
|
||||||
getService(Components.interfaces.nsIAtomService);
|
|
||||||
prop.AppendElement(aServ.getAtom("hasAttachment"));
|
|
||||||
}
|
|
||||||
// Fx22+
|
|
||||||
else {
|
|
||||||
props.push("hasAttachment");
|
|
||||||
}
|
|
||||||
|
|
||||||
// Don't show pie for open parent items, since we show it for the
|
// Don't show pie for open parent items, since we show it for the
|
||||||
// child item
|
// child item
|
||||||
if (this.isContainer(row) && this.isContainerOpen(row)) {
|
if (!this.isContainer(row) || !this.isContainerOpen(row)) {
|
||||||
return props.join(" ");
|
var num = Zotero.Sync.Storage.getItemDownloadImageNumber(treeRow.ref);
|
||||||
}
|
//var num = Math.round(new Date().getTime() % 10000 / 10000 * 64);
|
||||||
|
if (num !== false) props.push("pie", "pie" + num);
|
||||||
var num = Zotero.Sync.Storage.getItemDownloadImageNumber(treeRow.ref);
|
|
||||||
//var num = Math.round(new Date().getTime() % 10000 / 10000 * 64);
|
|
||||||
if (num !== false) {
|
|
||||||
// <=Fx21
|
|
||||||
if (prop) {
|
|
||||||
if (!aServ) {
|
|
||||||
var aServ = Components.classes["@mozilla.org/atom-service;1"].
|
|
||||||
getService(Components.interfaces.nsIAtomService);
|
|
||||||
}
|
|
||||||
prop.AppendElement(aServ.getAtom("pie"));
|
|
||||||
prop.AppendElement(aServ.getAtom("pie" + num));
|
|
||||||
}
|
|
||||||
// Fx22+
|
|
||||||
else {
|
|
||||||
props.push("pie", "pie" + num);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Style unread items in feeds
|
||||||
|
if (treeRow.ref.isFeedItem && !treeRow.ref.isRead) props.push('unread');
|
||||||
|
|
||||||
return props.join(" ");
|
return props.join(" ");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -95,8 +95,9 @@ Zotero.Notifier = new function(){
|
||||||
* Possible values:
|
* Possible values:
|
||||||
*
|
*
|
||||||
* event: 'add', 'modify', 'delete', 'move' ('c', for changing parent),
|
* event: 'add', 'modify', 'delete', 'move' ('c', for changing parent),
|
||||||
* 'remove' (ci, it), 'refresh', 'redraw', 'trash'
|
* 'remove' (ci, it), 'refresh', 'redraw', 'trash', 'unreadCountUpdated'
|
||||||
* type - 'collection', 'search', 'item', 'collection-item', 'item-tag', 'tag', 'group', 'relation'
|
* type - 'collection', 'search', 'item', 'collection-item', 'item-tag', 'tag',
|
||||||
|
* 'group', 'relation', 'feed', 'feedItem'
|
||||||
* ids - single id or array of ids
|
* ids - single id or array of ids
|
||||||
*
|
*
|
||||||
* Notes:
|
* Notes:
|
||||||
|
|
|
@ -501,9 +501,9 @@ var ZoteroPane = new function()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function handleKeyUp(event) {
|
||||||
function handleKeyUp(event, from) {
|
var from = event.originalTarget.id;
|
||||||
if (from == 'zotero-pane') {
|
if (from == 'zotero-items-tree') {
|
||||||
if ((Zotero.isWin && event.keyCode == 17) ||
|
if ((Zotero.isWin && event.keyCode == 17) ||
|
||||||
(!Zotero.isWin && event.keyCode == 18)) {
|
(!Zotero.isWin && event.keyCode == 18)) {
|
||||||
if (this.highlightTimer) {
|
if (this.highlightTimer) {
|
||||||
|
@ -511,6 +511,33 @@ var ZoteroPane = new function()
|
||||||
this.highlightTimer = null;
|
this.highlightTimer = null;
|
||||||
}
|
}
|
||||||
ZoteroPane_Local.collectionsView.setHighlightedRows();
|
ZoteroPane_Local.collectionsView.setHighlightedRows();
|
||||||
|
return;
|
||||||
|
} else if (event.keyCode == event.DOM_VK_BACK_QUOTE) {
|
||||||
|
// Toggle read/unread
|
||||||
|
let row = this.collectionsView.getRow(this.collectionsView.selection.currentIndex);
|
||||||
|
if (!row || !row.isFeed()) return;
|
||||||
|
if(itemReadTimeout) {
|
||||||
|
itemReadTimeout.cancel();
|
||||||
|
itemReadTimeout = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
let itemIDs = this.getSelectedItems(true);
|
||||||
|
Zotero.FeedItems.getAsync(itemIDs)
|
||||||
|
.then(function(feedItems) {
|
||||||
|
// Determine what most items are set to;
|
||||||
|
let allUnread = true;
|
||||||
|
for (let item of feedItems) {
|
||||||
|
if (item.isRead) {
|
||||||
|
allUnread = false;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// If something is unread, toggle all read by default
|
||||||
|
for (let i=0; i<feedItems.length; i++) {
|
||||||
|
feedItems[i].toggleRead(!allUnread);
|
||||||
|
}
|
||||||
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -594,21 +621,6 @@ var ZoteroPane = new function()
|
||||||
//event.preventDefault();
|
//event.preventDefault();
|
||||||
//event.stopPropagation();
|
//event.stopPropagation();
|
||||||
return;
|
return;
|
||||||
} else if (event.keyCode == event.DOM_VK_BACK_QUOTE) {
|
|
||||||
// Toggle read/unread
|
|
||||||
if (!this.collectionsView.selection.currentIndex) return;
|
|
||||||
let row = this.collectionsView.getRow(this.collectionsView.selection.currentIndex);
|
|
||||||
if (!row || !row.isFeed()) return;
|
|
||||||
|
|
||||||
if(itemReadTimeout) {
|
|
||||||
itemReadTimeout.cancel();
|
|
||||||
itemReadTimeout = null;
|
|
||||||
}
|
|
||||||
|
|
||||||
let itemIDs = this.getSelectedItems(true);
|
|
||||||
for (var i=0; i<itemIDs; i++) {
|
|
||||||
this.markItemRead(itemIDs[i]);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1377,15 +1389,11 @@ var ZoteroPane = new function()
|
||||||
tabs.selectedIndex = document.getElementById('zotero-view-item').selectedIndex;
|
tabs.selectedIndex = document.getElementById('zotero-view-item').selectedIndex;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (collectionTreeRow.isFeed()) {
|
if (item.isFeedItem) {
|
||||||
// Fire timer for read item
|
this.startItemReadTimeout(item.id);
|
||||||
let feedItem = yield Zotero.FeedItems.getAsync(item.id);
|
|
||||||
if (feedItem) {
|
|
||||||
this.startItemReadTimeout(feedItem.id);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
// Zero or multiple items selected
|
// Zero or multiple items selected
|
||||||
else {
|
else {
|
||||||
var count = this.itemsView.selection.count;
|
var count = this.itemsView.selection.count;
|
||||||
|
@ -4290,14 +4298,6 @@ var ZoteroPane = new function()
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
this.markItemRead = Zotero.Promise.coroutine(function* (feedItemID, toggle) {
|
|
||||||
let feedItem = yield Zotero.FeedItems.getAsync(feedItemID);
|
|
||||||
if (!feedItem) return;
|
|
||||||
|
|
||||||
feedItem.isRead = toggle !== undefined ? !!toggle : !feedItem.isRead;
|
|
||||||
yield feedItem.save({skipEditCheck: true, skipDateModifiedUpdate: true});
|
|
||||||
})
|
|
||||||
|
|
||||||
let itemReadTimeout;
|
let itemReadTimeout;
|
||||||
this.startItemReadTimeout = function(feedItemID) {
|
this.startItemReadTimeout = function(feedItemID) {
|
||||||
if (itemReadTimeout) {
|
if (itemReadTimeout) {
|
||||||
|
@ -4305,8 +4305,18 @@ var ZoteroPane = new function()
|
||||||
itemReadTimeout = null;
|
itemReadTimeout = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
itemReadTimeout = Zotero.Promise.delay(3000)
|
let feedItem;
|
||||||
|
itemReadTimeout = Zotero.FeedItems.getAsync(feedItemID)
|
||||||
.cancellable()
|
.cancellable()
|
||||||
|
.then(function(newFeedItem) {
|
||||||
|
if (!newFeedItem) {
|
||||||
|
throw new Zotero.Promise.CancellationError('Not a FeedItem');
|
||||||
|
} else if(newFeedItem.isRead) {
|
||||||
|
throw new Zotero.Promise.CancellationError('FeedItem already read.');
|
||||||
|
}
|
||||||
|
feedItem = newFeedItem;
|
||||||
|
})
|
||||||
|
.delay(3000)
|
||||||
.then(() => {
|
.then(() => {
|
||||||
itemReadTimeout = null;
|
itemReadTimeout = null;
|
||||||
// Check to make sure we're still on the same item
|
// Check to make sure we're still on the same item
|
||||||
|
@ -4315,7 +4325,15 @@ var ZoteroPane = new function()
|
||||||
let row = this.itemsView.getRow(this.itemsView.selection.currentIndex);
|
let row = this.itemsView.getRow(this.itemsView.selection.currentIndex);
|
||||||
if (!row || !row.ref || !row.ref.id == feedItemID) return;
|
if (!row || !row.ref || !row.ref.id == feedItemID) return;
|
||||||
|
|
||||||
return this.markItemRead(feedItemID, true);
|
return feedItem.toggleRead(true);
|
||||||
|
})
|
||||||
|
.catch(function(e) {
|
||||||
|
if (e instanceof Zotero.Promise.CancellationError) {
|
||||||
|
Zotero.debug(e.message);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
Zotero.debug(e, 1);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -215,6 +215,12 @@
|
||||||
color: inherit;
|
color: inherit;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Style unread items/collections in bold */
|
||||||
|
#zotero-items-tree treechildren::-moz-tree-cell-text(unread),
|
||||||
|
#zotero-collections-tree treechildren::-moz-tree-cell-text(unread) {
|
||||||
|
font-weight: bold;
|
||||||
|
}
|
||||||
|
|
||||||
#zotero-items-pane
|
#zotero-items-pane
|
||||||
{
|
{
|
||||||
min-width: 290px;
|
min-width: 290px;
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue