Mercurial > snowl
changeset 112:754441ddeaa3
combine the SnowlFeed and SnowlFeedSubscriber objects, and make it inherit from SnowlSource; also, remove the obsolete SnowlFeedClient
author | Myk Melez <myk@mozilla.org> |
---|---|
date | Sun, 01 Jun 2008 17:45:31 -0700 |
parents | 0d9dd49db284 |
children | f633d4f1fefa |
files | extension/content/sidebar.js extension/modules/feed.js extension/modules/service.js |
diffstat | 3 files changed, 106 insertions(+), 170 deletions(-) [+] |
line wrap: on
line diff
--- a/extension/content/sidebar.js Tue May 20 00:29:09 2008 -0700 +++ b/extension/content/sidebar.js Sun Jun 01 17:45:31 2008 -0700 @@ -108,9 +108,9 @@ let uri = URI.get(aOutline.getAttribute("xmlUrl")); if (uri) { // FIXME: make sure the user isn't already subscribed to the feed - // before subscribing them. + // before subscribing her to it. let name = aOutline.getAttribute("title") || aOutline.getAttribute("text") || "untitled"; - this._importItem(uri, name); + new SnowlFeed(null, name, uri).subscribe(); } if (aOutline.hasChildNodes()) { @@ -127,11 +127,6 @@ } }, - _importItem: function(aURL, aName) { - let subscriber = new SnowlFeedSubscriber(aURL, aName); - subscriber.subscribe(); - }, - //**************************************************************************// // nsITreeView
--- a/extension/modules/feed.js Tue May 20 00:29:09 2008 -0700 +++ b/extension/modules/feed.js Sun Jun 01 17:45:31 2008 -0700 @@ -1,4 +1,4 @@ -EXPORTED_SYMBOLS = ["SnowlFeed", "SnowlFeedSubscriber"]; +EXPORTED_SYMBOLS = ["SnowlFeed"]; const Cc = Components.classes; const Ci = Components.interfaces; @@ -12,73 +12,72 @@ Cu.import("resource://snowl/modules/log4moz.js"); Cu.import("resource://snowl/modules/datastore.js"); Cu.import("resource://snowl/modules/URI.js"); +Cu.import("resource://snowl/modules/source.js"); -var SnowlFeedClient = { - // XXX Make this take a feed ID once it stores the list of subscribed feeds - // in the datastore. - refresh: function(aFeedURL) { +function SnowlFeed(aID, aName, aMachineURI, aHumanURI, aLastRefreshed, aImportance) { + // Call the superclass's constructor to initialize the new instance. + SnowlSource.call(this, aID, aName, aMachineURI, aHumanURI, aLastRefreshed, aImportance); + + // XXX Can't this be defined when the prototype is? + this._log = Log4Moz.Service.getLogger("Snowl.Feed"); +} + +SnowlFeed.prototype = { + __proto__: SnowlSource.prototype, + + _log: null, + + // Observer Service + get _obsSvc() { + let obsSvc = Cc["@mozilla.org/observer-service;1"]. + getService(Ci.nsIObserverService); + this.__defineGetter__("_obsSvc", function() { return obsSvc }); + return this._obsSvc; + }, + + // FIXME: rename this refresh. + getNewMessages: function() { let request = Cc["@mozilla.org/xmlextras/xmlhttprequest;1"].createInstance(); request.QueryInterface(Ci.nsIDOMEventTarget); let t = this; - request.addEventListener("load", function(aEvent) { t.onLoad(aEvent) }, false); - request.addEventListener("error", function(aEvent) { t.onError(aEvent) }, false); + request.addEventListener("load", function(e) { t.onRefreshLoad(e) }, false); + request.addEventListener("error", function(e) { t.onRefreshError(e) }, false); request.QueryInterface(Ci.nsIXMLHttpRequest); - request.open("GET", aFeedURL, true); + + // 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); request.send(null); }, - onLoad: function(aEvent) { + onRefreshLoad: function(aEvent) { let request = aEvent.target; - if (request.responseText.length > 0) { - let parser = Cc["@mozilla.org/feed-processor;1"]. - createInstance(Ci.nsIFeedProcessor); - parser.listener = new SnowlFeed(request.channel.originalURI); - parser.parseFromString(request.responseText, request.channel.URI); - } + // XXX What's the right way to handle this? + if (request.responseText.length == 0) + throw("feed contains no data"); + + let parser = Cc["@mozilla.org/feed-processor;1"]. + createInstance(Ci.nsIFeedProcessor); + parser.listener = { t: this, handleResult: function(r) { this.t.onRefreshResult(r) } }; + parser.parseFromString(request.responseText, request.channel.URI); }, - onError: function(aEvent) { - // FIXME: figure out what to do here. - Log4Moz.Service.getLogger("Snowl.FeedClient").error("loading feed " + aEvent.target.channel.originalURI.spec); - } -}; - -function SnowlFeed(aID, aURL, aTitle) { - this.id = aID; - this.url = aURL; - this.title = aTitle; - - this._log = Log4Moz.Service.getLogger("Snowl.Feed"); -} - -// FIXME: make this a subclass of SnowlSource. - -SnowlFeed.prototype = { - id: null, - url: null, - title: null, - - _log: null, - - QueryInterface: function(aIID) { - if (aIID.equals(Ci.nsIFeedResultListener) || - aIID.equals(Ci.nsISupports)) - return this; - - throw Cr.NS_ERROR_NO_INTERFACE; + onRefreshError: function(aEvent) { + this._log.error("onRefreshError: " + aEvent.target.status + " " + + aEvent.target.statusText + " " + aEvent.target.responseText.length); }, - // nsIFeedResultListener - - handleResult: function(result) { + onRefreshResult: function(aResult) { // Now that we know we successfully downloaded the feed and obtained // a result from it, update the "last refreshed" timestamp. this.resetLastRefreshed(this); - let feed = result.doc.QueryInterface(Components.interfaces.nsIFeed); + let feed = aResult.doc.QueryInterface(Components.interfaces.nsIFeed); let currentMessages = []; @@ -97,17 +96,17 @@ externalID = entry.id || this.generateID(entry); } catch(ex) { - this._log.warn(this.title + " couldn't retrieve a message: " + ex); + this._log.warn(this.name + " couldn't retrieve a message: " + ex); continue; } let internalID = this.getInternalIDForExternalID(externalID); if (internalID) { - //this._log.info(this.title + " has message " + externalID); + //this._log.info(this.name + " has message " + externalID); } else { - this._log.info(this.title + " adding message " + externalID); + this._log.info(this.name + " adding message " + externalID); internalID = this.addMessage(entry, externalID); } @@ -124,42 +123,14 @@ SnowlDatastore.dbConnection.rollbackTransaction(); throw ex; } + + // FIXME: only do this if something has actually changed. + this._obsSvc.notifyObservers(null, "messages:changed", null); }, // nsIFeedTextConstruct::type to media type mappings. mediaTypes: { html: "text/html", xhtml: "application/xhtml+xml", text: "text/plain" }, - getNewMessages: function() { - let request = Cc["@mozilla.org/xmlextras/xmlhttprequest;1"].createInstance(); - - request.QueryInterface(Ci.nsIDOMEventTarget); - // FIXME: just pass "this" and make this implement nsIDOMEventListener. - let t = this; - request.addEventListener("load", function(aEvent) { t.onLoad(aEvent) }, false); - request.addEventListener("error", function(aEvent) { t.onError(aEvent) }, false); - - request.QueryInterface(Ci.nsIXMLHttpRequest); -//dump("about to getNewMessages for " + this.url + "\n"); - request.open("GET", this.url, true); - request.send(null); - }, - - onLoad: function(aEvent) { - let request = aEvent.target; - - if (request.responseText.length > 0) { - let parser = Cc["@mozilla.org/feed-processor;1"]. - createInstance(Ci.nsIFeedProcessor); - parser.listener = this; - parser.parseFromString(request.responseText, request.channel.URI); - } - }, - - onError: function(aEvent) { - // FIXME: figure out what to do here. - this._log.error("loading feed " + aEvent.target.channel.originalURI); - }, - /** * Add a message to the datastore for the given feed entry. * @@ -376,114 +347,85 @@ * * aSource {SnowlMessageSource} the source for which to set the time */ + // FIXME: make this a setter for the lastRefreshed property. resetLastRefreshed: function() { let stmt = SnowlDatastore.createStatement("UPDATE sources SET lastRefreshed = :lastRefreshed WHERE id = :id"); stmt.params.lastRefreshed = new Date().getTime(); stmt.params.id = this.id; stmt.execute(); - } - -}; - -// XXX Should we make this part of the Feed object? -// FIXME: make this accept a callback to which it reports on its progress -// so we can provide feedback to the user in subscription interfaces. -function SnowlFeedSubscriber(aURI, aName) { - this.uri = aURI; - this.name = aName; -} - -SnowlFeedSubscriber.prototype = { - uri: null, - name: null, - - // Observer Service - get _obsSvc() { - let obsSvc = Cc["@mozilla.org/observer-service;1"]. - getService(Ci.nsIObserverService); - this.__defineGetter__("_obsSvc", function() { return obsSvc }); - return this._obsSvc; }, - QueryInterface: function(aIID) { - if (aIID.equals(Ci.nsIDOMEventListener) || - aIID.equals(Ci.nsIFeedResultListener) || - aIID.equals(Ci.nsISupports)) - return this; + // FIXME: make this accept a callback to which it reports on its progress + // so we can provide feedback to the user in subscription interfaces. + subscribe: function() { + this._log.info("subscribing to " + this.name + " <" + this.machineURI.spec + ">"); - throw Cr.NS_ERROR_NO_INTERFACE; - }, - - subscribe: function() { let request = Cc["@mozilla.org/xmlextras/xmlhttprequest;1"].createInstance(); request = request.QueryInterface(Ci.nsIDOMEventTarget); - request.addEventListener("load", this, false); - request.addEventListener("error", this, false); + + let t = this; + request.addEventListener("load", function(e) { t.onSubscribeLoad(e) }, false); + request.addEventListener("error", function(e) { t.onSubscribeError(e) }, false); request = request.QueryInterface(Ci.nsIXMLHttpRequest); - // The feed processor is going to parse the XML, so set the MIME type + // 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.uri.spec, true); + request.open("GET", this.machineURI.spec, true); request.send(null); }, - // nsIDOMEventListener - - handleEvent: function(aEvent) { - switch(aEvent.type) { - case "load": - this.onLoad(aEvent); - break; - case "error": - this.onError(aEvent); - break; - } - }, - - onError: function(aEvent) { -dump("XMLHTTPRequest.onError for " + this.name + " <" + this.uri.spec + ">: " + aEvent.target.status + " " + aEvent.target.statusText + " " + aEvent.target.responseText.length + "\n"); - }, - - onLoad: function(aEvent) { + onSubscribeLoad: function(aEvent) { let request = aEvent.target; - // FIXME: notify the user about the problem. + // XXX What's the right way to handle this? if (request.responseText.length == 0) throw("feed contains no data"); let parser = Cc["@mozilla.org/feed-processor;1"]. createInstance(Ci.nsIFeedProcessor); - parser.listener = this; + parser.listener = { t: this, handleResult: function(r) { this.t.onSubscribeResult(r) } }; parser.parseFromString(request.responseText, request.channel.URI); }, - // nsIFeedResultListener + onSubscribeError: function(aEvent) { + this._log.error("onSubscribeError: " + aEvent.target.status + " " + + aEvent.target.statusText + " " + aEvent.target.responseText.length); + }, - handleResult: function(aResult) { + onSubscribeResult: function(aResult) { let feed = aResult.doc.QueryInterface(Components.interfaces.nsIFeed); - // Subscribe to the feed. - let name = this.name || feed.title.plainText(); - let statement = SnowlDatastore.createStatement("INSERT INTO sources (name, machineURI, humanURI) VALUES (:name, :machineURI, :humanURI)"); - statement.params.name = name; - statement.params.machineURI = this.uri.spec; - statement.params.humanURI = feed.link.spec; - //dump("subscribing to " + name + " <" + this.uri.spec + ">\n"); - statement.step(); + // Extract the name (if we don't already have one) and human URI from the feed. + if (!this.name) + this.name = feed.title.plainText(); + this.humanURI = feed.link; - let id = SnowlDatastore.dbConnection.lastInsertRowID; + // Add the source to the database. + let statement = + SnowlDatastore.createStatement("INSERT INTO sources (name, machineURI, humanURI) " + + "VALUES (:name, :machineURI, :humanURI)"); + try { + statement.params.name = this.name; + statement.params.machineURI = this.machineURI.spec; + statement.params.humanURI = this.humanURI.spec; + statement.step(); + } + finally { + statement.reset(); + } - // Now refresh the feed to import all its items. - //dump("refreshing " + this.uri.spec + "\n"); - let feed2 = new SnowlFeed(id, this.uri.spec, name); - feed2.handleResult(aResult); + // Extract the ID of the source from the newly-created database record. + this.id = SnowlDatastore.dbConnection.lastInsertRowID; + // Let observers know about the new source. this._obsSvc.notifyObservers(null, "sources:changed", null); - this._obsSvc.notifyObservers(null, "messages:changed", null); + + // Refresh the feed to import all its items. + this.onRefreshResult(aResult); } };
--- a/extension/modules/service.js Tue May 20 00:29:09 2008 -0700 +++ b/extension/modules/service.js Sun Jun 01 17:45:31 2008 -0700 @@ -186,12 +186,12 @@ try { while (this._getSourcesStatement.step()) { let row = this._getSourcesStatement.row; - sources.push(new SnowlSource(row.id, - row.name, - URI.get(row.machineURI), - URI.get(row.humanURI), - new Date(row.lastRefreshed), - row.importance)); + sources.push(new SnowlFeed(row.id, + row.name, + URI.get(row.machineURI), + URI.get(row.humanURI), + new Date(row.lastRefreshed), + row.importance)); } } finally { @@ -230,9 +230,8 @@ _refreshSources: function(aSources) { for each (let source in aSources) { -dump("_refreshSources: " + source.machineURI + "\n"); - let feed = new SnowlFeed(source.id, source.machineURI.spec, source.name); - feed.getNewMessages(); +dump("_refreshSources: " + source.machineURI.spec + "\n"); + source.getNewMessages(); // We reset the last refreshed timestamp here even though the refresh // is asynchronous, so we don't yet know whether it has succeeded. @@ -242,7 +241,7 @@ // 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. - feed.resetLastRefreshed(); + source.resetLastRefreshed(); } this._obsSvc.notifyObservers(null, "messages:changed", null);