зеркало из https://github.com/mozilla/snowl.git
display feeds from other open tabs in the river view
--HG-- extra : rebase_source : 41d8556403e74850c093d9e3dceecf613a4a0ece
This commit is contained in:
Родитель
4b6081e8db
Коммит
7ada648717
|
@ -348,36 +348,17 @@ let Snowl = {
|
|||
// Feed Button
|
||||
|
||||
_onClickFeedButton: function(event) {
|
||||
let feeds = gBrowser.selectedBrowser.feeds;
|
||||
|
||||
// How could this happen? Users shouldn't be able to click the button
|
||||
// if there are no feeds.
|
||||
// FIXME: figure out if we need this and what to do if we encounter it.
|
||||
if (feeds == null)
|
||||
if (gBrowser.selectedBrowser.feeds == null)
|
||||
return;
|
||||
|
||||
let feedsToPreview = feeds;
|
||||
|
||||
// If there are two feeds, one of which seems to be an Atom feed, the other
|
||||
// of which seems to be RSS, assume they're duplicates and only preview one
|
||||
// (in this case we choose the Atom feed).
|
||||
let areDupes = function(a, b) (/atom/i.test(a) && /rss/i.test(b)) ||
|
||||
(/atom/i.test(b) && /rss/i.test(a));
|
||||
if (feeds.length == 2 && areDupes(feeds[0].title, feeds[1].title)) {
|
||||
// FIXME: log this so developers/testers know it happened.
|
||||
|
||||
// Filter the array for the item whose title contains "atom", but only
|
||||
// grab the first item from the filtered array on the off chance that both
|
||||
// items contain "atom".
|
||||
feedsToPreview = [feeds.filter(function(v) /atom/i.test(v.title))[0]];
|
||||
|
||||
// Use the title of the page instead of the Atom-specific feed title.
|
||||
if (gBrowser.selectedBrowser.contentTitle)
|
||||
feedsToPreview[0].title = gBrowser.selectedBrowser.contentTitle;
|
||||
}
|
||||
let feeds = SnowlUtils.canonicalizeFeeds(gBrowser.selectedBrowser.feeds,
|
||||
gBrowser.selectedBrowser.contentTitle);
|
||||
|
||||
// Open the river view, passing it the feeds to preview.
|
||||
let param = "feedsToPreview=" + encodeURIComponent(JSON.stringify(feedsToPreview));
|
||||
let param = "feedsToPreview=" + encodeURIComponent(JSON.stringify(feeds));
|
||||
let href = "chrome://snowl/content/river.xul?" + param;
|
||||
openUILink(href, event, false, true, false, null);
|
||||
},
|
||||
|
|
|
@ -878,25 +878,32 @@ let Sources = {
|
|||
this._log.info("selected " + source.name + " with ID " + source.id);
|
||||
|
||||
if (!source.messages) {
|
||||
let constraints = [];
|
||||
|
||||
constraints.push({ expression: "sources.id = " + source.id });
|
||||
|
||||
// FIXME: use a left join here once the SQLite bug breaking left joins to
|
||||
// virtual tables has been fixed (i.e. after we upgrade to SQLite 3.5.7+).
|
||||
if (SnowlMessageView._filter.value) {
|
||||
constraints.push({ expression: "messages.id IN (SELECT messageID FROM parts JOIN partsText ON parts.id = partsText.docid WHERE partsText.content MATCH :filter)",
|
||||
parameters: { filter: SnowlUtils.appendAsterisks(SnowlMessageView._filter.value) } });
|
||||
if (source.id) {
|
||||
let constraints = [];
|
||||
|
||||
constraints.push({ expression: "sources.id = " + source.id });
|
||||
|
||||
// FIXME: use a left join here once the SQLite bug breaking left joins to
|
||||
// virtual tables has been fixed (i.e. after we upgrade to SQLite 3.5.7+).
|
||||
if (SnowlMessageView._filter.value) {
|
||||
constraints.push({ expression: "messages.id IN (SELECT messageID FROM parts JOIN partsText ON parts.id = partsText.docid WHERE partsText.content MATCH :filter)",
|
||||
parameters: { filter: SnowlUtils.appendAsterisks(SnowlMessageView._filter.value) } });
|
||||
}
|
||||
|
||||
if (SnowlMessageView._periodMenu.selectedItem) {
|
||||
constraints.push({ expression: "received >= :startTime AND received < :endTime",
|
||||
parameters: { startTime: SnowlMessageView._periodStartTime,
|
||||
endTime: SnowlMessageView._periodEndTime } });
|
||||
}
|
||||
|
||||
// XXX replace this with a SnowlSource::retrieve method that handles
|
||||
// constraints (and ultimately multiple source IDs)?
|
||||
source.messages = new Collection2({ constraints: constraints,
|
||||
order: "messages.id DESC" });
|
||||
}
|
||||
|
||||
if (SnowlMessageView._periodMenu.selectedItem) {
|
||||
constraints.push({ expression: "received >= :startTime AND received < :endTime",
|
||||
parameters: { startTime: SnowlMessageView._periodStartTime,
|
||||
endTime: SnowlMessageView._periodEndTime } });
|
||||
else {
|
||||
source.refresh();
|
||||
}
|
||||
|
||||
source.messages = new Collection2({ constraints: constraints,
|
||||
order: "messages.id DESC" });
|
||||
}
|
||||
|
||||
SnowlMessageView._collection = source.messages;
|
||||
|
@ -924,6 +931,22 @@ let Sources = {
|
|||
}
|
||||
}
|
||||
|
||||
let otherTabFeeds = this._getFeedsInOtherTabs();
|
||||
if (otherTabFeeds.length > 0) {
|
||||
let item = document.createElementNS(XUL_NS, "richlistitem");
|
||||
// FIXME: make this localizable.
|
||||
item.setAttribute("label", "Other Tabs");
|
||||
item.className = "header";
|
||||
this._list.appendChild(item);
|
||||
|
||||
for each (let otherTabFeed in otherTabFeeds) {
|
||||
let feed = new SnowlFeed(null, otherTabFeed.title, new URI(otherTabFeed.href), undefined, null);
|
||||
let item = this._list.appendItem(otherTabFeed.title);
|
||||
item.source = feed;
|
||||
item.className = "source";
|
||||
}
|
||||
}
|
||||
|
||||
let item = document.createElementNS(XUL_NS, "richlistitem");
|
||||
// FIXME: make this localizable.
|
||||
item.setAttribute("label", "Subscriptions");
|
||||
|
@ -937,6 +960,22 @@ let Sources = {
|
|||
item.setAttribute("subscription", "true");
|
||||
item.className = "source";
|
||||
}
|
||||
},
|
||||
|
||||
_getFeedsInOtherTabs: function() {
|
||||
// I would use FUEL here, but its tab API doesn't provide access to feeds.
|
||||
|
||||
let tabBrowser = gBrowserWindow.gBrowser;
|
||||
let tabs = tabBrowser.mTabs;
|
||||
let pages = [];
|
||||
for (let i = 0; i < tabs.length; i++) {
|
||||
let tab = tabs[i];
|
||||
let browser = tabBrowser.getBrowserForTab(tab);
|
||||
if (browser.feeds)
|
||||
pages.push({ feeds: browser.feeds, title: browser.contentTitle });
|
||||
}
|
||||
|
||||
return SnowlUtils.canonicalizeFeedsFromMultiplePages(pages);
|
||||
}
|
||||
};
|
||||
|
||||
|
|
117
modules/utils.js
117
modules/utils.js
|
@ -590,6 +590,123 @@ let SnowlUtils = {
|
|||
container.appendChild(desc);
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Canonicalize the feeds provided by a web page by removing probable
|
||||
* duplicates that use different protocols (Atom, RSS) and titling them
|
||||
* after the page itself, since we treat feeds as the transport protocol
|
||||
* for updates from web pages, so the web page itself is the first-class
|
||||
* object to which we expose users, and its title is the more memorable
|
||||
* in that regard.
|
||||
*
|
||||
* @param feeds {Array}
|
||||
* the feeds to canonicalize; each element is a feed {Object}
|
||||
* with two properties:
|
||||
* href {String} URL of the feed
|
||||
* title {String} title of the feed
|
||||
*
|
||||
* @param pageTitle {String}
|
||||
* title of page providing the feeds
|
||||
*
|
||||
* @returns {Array} canonicalized array of feeds
|
||||
*/
|
||||
canonicalizeFeeds: function(feeds, pageTitle) {
|
||||
// Operate on a copy of the feeds array so we don't hork other extensions
|
||||
// or core code that expect that array to remain intact.
|
||||
let canonicalFeeds = feeds.concat();
|
||||
|
||||
if (canonicalFeeds.length == 1) {
|
||||
// If the page title is available, name the feed after the page,
|
||||
// as the page's title is likely to be better than the feed title.
|
||||
if (pageTitle)
|
||||
canonicalFeeds[0].title = pageTitle;
|
||||
}
|
||||
else if (canonicalFeeds.length == 2) {
|
||||
// If the two feeds appear to be duplicates (i.e. one RSS, the other
|
||||
// Atom), then remove one. We remove the RSS feed by default, assuming
|
||||
// that the Atom feed is better because Atom is better specified, but we
|
||||
// could just as well remove the Atom feed if the RSS feed seems better
|
||||
// under certain circumstances.
|
||||
let areDupes = function(a, b) (/atom/i.test(a) && /rss/i.test(b)) ||
|
||||
(/atom/i.test(b) && /rss/i.test(a));
|
||||
if (areDupes(canonicalFeeds[0].title, canonicalFeeds[1].title)) {
|
||||
// This code is overly complicated (filtering to an array, extracting
|
||||
// its first element, and then putting that into another array)
|
||||
// to ensure we always reduce the array to a single element even if
|
||||
// both of their names happen to contain the string "atom"
|
||||
// (f.e. if one was called "Atom Feed" and the other was called
|
||||
// "RSS Feed - Not Atom").
|
||||
canonicalFeeds =
|
||||
[canonicalFeeds.filter(function(v) /atom/i.test(v.title))[0]];
|
||||
}
|
||||
|
||||
// If the page title is available, name the feed after the page,
|
||||
// as the page's title is likely to be better than the feed title.
|
||||
if (pageTitle)
|
||||
canonicalFeeds[0].title = pageTitle;
|
||||
}
|
||||
|
||||
// If there are more than two feeds, we don't currently do anything.
|
||||
// Perhaps there are things we could do? Use cases would be handy.
|
||||
|
||||
return canonicalFeeds;
|
||||
},
|
||||
|
||||
/**
|
||||
* Canonicalize feeds provided by multiple web pages. This calls
|
||||
* canonicalizeFeeds on the feeds for each individual page, then it removes
|
||||
* any exact duplicates from the list, so if you generate a list of feeds
|
||||
* from a set of pages (f.e. pages open in tabs), and you have the same page
|
||||
* in the set twice (or two pages from the same site that both provide
|
||||
* the same feeds), you don't get duplicate feeds.
|
||||
*
|
||||
* @param pages {Array}
|
||||
* the pages to canonicalize; each element is a page {Object}
|
||||
* with two properties:
|
||||
* feeds {Array} the feeds to canonicalize (@see canonicalizeFeeds
|
||||
* for a description of feed objects)
|
||||
* title {String} the title of the page
|
||||
*
|
||||
* @returns {Array} canonicalized array of feeds
|
||||
*/
|
||||
canonicalizeFeedsFromMultiplePages: function(pages) {
|
||||
let feeds = [];
|
||||
|
||||
// Convert the array of pages into an array of feeds from those pages
|
||||
// which have been canonicalized with respect to each individual page.
|
||||
for each (let page in pages)
|
||||
feeds = feeds.concat(this.canonicalizeFeeds(page.feeds, page.title));
|
||||
|
||||
// We can do the above with a single statement, but I don't think we gain
|
||||
// anything, since I can't find a way to make the statement more compact
|
||||
// while retaining readability.
|
||||
//feeds =
|
||||
// pages.map(function(page) this.canonicalizeFeeds(page.feeds, page.title),
|
||||
// this).reduce(function(feeds, feed) feeds.concat(feed), []);
|
||||
|
||||
// Convert the array of feeds into an array of feeds that have been
|
||||
// canonicalized with respect to the set of pages as a whole (i.e. remove
|
||||
// exact duplicates from the list of feeds from all pages). We consider
|
||||
// two feeds to be duplicates if their URLs match, even if their titles
|
||||
// are different, since users only benefit from subscribing to any given
|
||||
// feed once, even if the feed is offered by two different pages
|
||||
// with different titles.
|
||||
let uniqueFeeds = {};
|
||||
for each (let feed in feeds)
|
||||
uniqueFeeds[feed.href] = feed;
|
||||
feeds = [feed for ([, feed] in Iterator(uniqueFeeds))];
|
||||
|
||||
// We can do the above with a single statement, but I don't think we gain
|
||||
// anything, since I can't find a way to make the statement more compact
|
||||
// while retaining readibility. I suppose there's benefit to not having
|
||||
// to declare the temporary uniqueFeeds object.
|
||||
//feeds = [feed for ([, feed] in
|
||||
// Iterator(feeds.reduce(function(uniqueFeeds, feed) {
|
||||
// uniqueFeeds[feed.href] = feed; return uniqueFeeds
|
||||
// }, {})))];
|
||||
|
||||
return feeds;
|
||||
}
|
||||
|
||||
};
|
||||
|
|
Загрузка…
Ссылка в новой задаче