set the received time of messages received in the same refresh cycle, including those from different sources, to the same value, which makes a list of messages sorted by received, then published times look better when a bunch of messages are received at the same time (f.e. when the user starts their browser in the morning), because the messages aren't all bunched up by source because the source's individual received times are slightly different; this is the backend fix, but some views may still need a frontend fix to go along with this; in particular, the stream view, which inserts messages at the top, still bunches them up, because it receives them as they come in, not all at once for a given refresh cycle; it needs to get a batch message notification rather than notifications of individual messages

This commit is contained in:
Myk Melez 2008-12-07 17:41:12 -08:00
Родитель f6a2942ce0
Коммит 0a5aa41043
4 изменённых файлов: 67 добавлений и 37 удалений

Просмотреть файл

@ -134,7 +134,7 @@ SnowlFeed.prototype = {
},
// refresh is defined elsewhere.
//refresh: function() {},
//refresh: function(refreshTime) {},
persist: function() {
SnowlSource.persist.call(this);
@ -223,26 +223,38 @@ SnowlFeed.prototype = {
throw Cr.NS_ERROR_NOT_IMPLEMENTED;
},
refresh: function() {
let request = Cc["@mozilla.org/xmlextras/xmlhttprequest;1"].createInstance();
_refreshTime: null,
refresh: function(refreshTime) {
// Cache the refresh time so we can use it as the received time when adding
// messages to the datastore.
this._refreshTime = refreshTime;
let request = Cc["@mozilla.org/xmlextras/xmlhttprequest;1"].createInstance();
request.QueryInterface(Ci.nsIDOMEventTarget);
let t = this;
request.addEventListener("load", function(e) { t.onRefreshLoad(e) }, false);
request.addEventListener("error", function(e) { t.onRefreshError(e) }, false);
request.QueryInterface(Ci.nsIXMLHttpRequest);
// The feed processor is going to parse the XML, so override the MIME type
// in order to turn off parsing by XMLHttpRequest itself.
request.overrideMimeType("text/plain");
request.open("GET", this.machineURI.spec, true);
// Register a listener for notification callbacks so we handle authentication.
request.channel.notificationCallbacks = this;
request.send(null);
// We set the last refreshed timestamp here even though the refresh
// is asynchronous, so we don't yet know whether it has succeeded.
// The upside of this approach is that we don't keep trying to refresh
// a source that isn't responding, but the downside is that it takes
// a long time for us to refresh a source that is only down for a short
// period of time. We should instead keep trying when a source fails,
// but with a progressively longer interval (up to the standard one).
// FIXME: implement the approach described above.
this.lastRefreshed = refreshTime;
},
onRefreshLoad: function(aEvent) {
@ -270,6 +282,8 @@ SnowlFeed.prototype = {
createInstance(Ci.nsIFeedProcessor);
parser.listener = { t: this, handleResult: function(r) { this.t.onRefreshResult(r) } };
parser.parseFromString(request.responseText, request.channel.URI);
this._refreshTime = null;
},
onRefreshError: function(aEvent) {
@ -280,6 +294,8 @@ SnowlFeed.prototype = {
try {statusText = request.statusText;} catch(ex) {statusText = "[no status text]"}
this._log.error("onRefreshError: " + request.status + " (" + statusText + ")");
this._refreshTime = null;
},
onRefreshResult: function(aResult) {
@ -289,10 +305,6 @@ SnowlFeed.prototype = {
// the user is already subscribed.
Observers.notify(this, "snowl:subscribe:get:start", null);
// Now that we know we successfully downloaded the feed and obtained
// a result from it, update the "last refreshed" timestamp.
this.lastRefreshed = new Date();
// FIXME: handle the case where this throws |aResult.doc is null|
// because the feed processor couldn't parse the feed file
// (f.e. because its content isn't a valid feed).
@ -328,7 +340,7 @@ SnowlFeed.prototype = {
messagesChanged = true;
this._log.info(this.name + " adding message " + externalID);
internalID = this._addMessage(feed, entry, externalID, this.lastRefreshed);
internalID = this._addMessage(feed, entry, externalID, this._refreshTime);
currentMessageIDs.push(internalID);
}

Просмотреть файл

@ -248,24 +248,17 @@ let SnowlService = {
refreshAllSources: function(sources) {
let allSources = sources ? sources : this.sources;
for each (let source in allSources)
this._refreshSource(source);
},
_refreshSource: function(source) {
// We specify the same refresh time when refreshing sources so that all
// new messages have the same received time, which makes messages sorted by
// received, then published times look better (more mixed together by
// publication time, not clumped up by source based on the received time)
// when retrieved in the same refresh (f.e. when the user starts their
// browser in the morning after leaving it off overnight).
let refreshTime = new Date();
for each (let source in allSources) {
this._log.info("refreshing source " + source.name);
source.refresh();
// We reset the last refreshed timestamp here even though the refresh
// is asynchronous, so we don't yet know whether it has succeeded.
// The upside of this approach is that we don't keep trying to refresh
// a source that isn't responding, but the downside is that it takes
// a long time for us to refresh a source that is only down for a short
// period of time. We should instead keep trying when a source fails,
// but with a progressively longer interval (up to the standard one).
// FIXME: implement the approach described above.
source.lastRefreshed = new Date();
source.refresh(refreshTime);
}
},
/**

Просмотреть файл

@ -202,8 +202,17 @@ let SnowlSource = {
* Check for new messages and update the local store of messages to reflect
* the latest updates available from the source. This method is a stub that
* is expected to be overridden by subclass implementations.
*
* @param refreshTime {Date}
* the time at which a refresh currently in progress began
* Note: we use this as the received time when adding messages to
* the datastore. We get it from the caller instead of generating it
* ourselves to allow the caller to synchronize received times
* across refreshes of multiple sources, which makes message views
* sorted by received, then published look better for messages
* received in the same refresh cycle.
*/
refresh: function() {},
refresh: function(refreshTime) {},
/**
* Insert a record for this source into the database, or update an existing

Просмотреть файл

@ -122,7 +122,7 @@ SnowlTwitter.prototype = {
},
// refresh is defined elsewhere.
//refresh: function() {},
//refresh: function(refreshTime) {},
persist: function() {
SnowlSource.persist.call(this);
@ -319,7 +319,7 @@ SnowlTwitter.prototype = {
this.persist();
this.subscribed = true;
this.refresh();
this.refresh(new Date());
},
onSubscribeError: function(event) {
@ -341,9 +341,15 @@ SnowlTwitter.prototype = {
//**************************************************************************//
// Refreshment
refresh: function() {
_refreshTime: null,
refresh: function(refreshTime) {
Observers.notify(this, "snowl:subscribe:get:start", null);
// Cache the refresh time so we can use it as the received time when adding
// messages to the datastore.
this._refreshTime = refreshTime;
let request = Cc["@mozilla.org/xmlextras/xmlhttprequest;1"].createInstance();
request.QueryInterface(Ci.nsIDOMEventTarget);
@ -363,6 +369,16 @@ SnowlTwitter.prototype = {
request.channel.notificationCallbacks = this;
request.send(null);
// We set the last refreshed timestamp here even though the refresh
// is asynchronous, so we don't yet know whether it has succeeded.
// The upside of this approach is that we don't keep trying to refresh
// a source that isn't responding, but the downside is that it takes
// a long time for us to refresh a source that is only down for a short
// period of time. We should instead keep trying when a source fails,
// but with a progressively longer interval (up to the standard one).
// FIXME: implement the approach described above.
this.lastRefreshed = refreshTime;
},
onRefreshLoad: function(event) {
@ -398,6 +414,8 @@ SnowlTwitter.prototype = {
}
this._processRefresh(request.responseText);
this._refreshTime = null;
},
onRefreshError: function(event) {
@ -408,13 +426,11 @@ SnowlTwitter.prototype = {
try {statusText = request.statusText;} catch(ex) {statusText = "[no status text]"}
this._log.error("onRefreshError: " + request.status + " (" + statusText + ")");
this._refreshTime = null;
},
_processRefresh: function(responseText) {
// Now that we know we successfully downloaded the source and obtained
// a result from it, update the "last refreshed" timestamp.
this.lastRefreshed = new Date();
var JSON = Cc["@mozilla.org/dom/json;1"].createInstance(Ci.nsIJSON);
let messages = JSON.decode(responseText);
@ -433,7 +449,7 @@ SnowlTwitter.prototype = {
messagesChanged = true;
this._log.info(this.name + " adding message " + externalID);
internalID = this._addMessage(message, this.lastRefreshed);
internalID = this._addMessage(message, this._refreshTime);
currentMessages.push(internalID);
}