[newsfeed] Suppress unsightly animation edge cases when there are 0 or 1 active news items (#3336)

When the newsfeed module has an items list of size 1, every
`updateInterval` the animation runs to transition from the active story
to itself. This is unsightly. This PR suppresses that.

To reproduce: configure newsfeed with a single news source,
`ignoreOldItems` true, a short `updateInterval` (e.g. 3000), and a
carefully-chosen small `ignoreOlderThan` lining up with the current
contents of your news source.
This commit is contained in:
Ross Younger 2024-01-14 21:12:32 +13:00 committed by GitHub
parent b47600e0d8
commit dadc7ba0a2
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 36 additions and 7 deletions

View File

@ -21,6 +21,8 @@ _This release is scheduled to be released on 2024-04-01._
### Fixed ### Fixed
- Skip changelog requirement when running tests for dependency updates (#3320) - Skip changelog requirement when running tests for dependency updates (#3320)
- [newsfeed] Suppress unsightly animation cases when there are 0 or 1 active news items
- [newsfeed] Always compute the feed item URL using the same helper function
### Deleted ### Deleted

View File

@ -118,27 +118,33 @@ Module.register("newsfeed", {
//Override template data and return whats used for the current template //Override template data and return whats used for the current template
getTemplateData () { getTemplateData () {
if (this.activeItem >= this.newsItems.length) {
this.activeItem = 0;
}
this.activeItemCount = this.newsItems.length;
// this.config.showFullArticle is a run-time configuration, triggered by optional notifications // this.config.showFullArticle is a run-time configuration, triggered by optional notifications
if (this.config.showFullArticle) { if (this.config.showFullArticle) {
this.activeItemHash = this.newsItems[this.activeItem]?.hash;
return { return {
url: this.getActiveItemURL() url: this.getActiveItemURL()
}; };
} }
if (this.error) { if (this.error) {
this.activeItemHash = undefined;
return { return {
error: this.error error: this.error
}; };
} }
if (this.newsItems.length === 0) { if (this.newsItems.length === 0) {
this.activeItemHash = undefined;
return { return {
empty: true empty: true
}; };
} }
if (this.activeItem >= this.newsItems.length) {
this.activeItem = 0;
}
const item = this.newsItems[this.activeItem]; const item = this.newsItems[this.activeItem];
this.activeItemHash = item.hash;
const items = this.newsItems.map(function (item) { const items = this.newsItems.map(function (item) {
item.publishDate = moment(new Date(item.pubdate)).fromNow(); item.publishDate = moment(new Date(item.pubdate)).fromNow();
return item; return item;
@ -150,7 +156,7 @@ Module.register("newsfeed", {
sourceTitle: item.sourceTitle, sourceTitle: item.sourceTitle,
publishDate: moment(new Date(item.pubdate)).fromNow(), publishDate: moment(new Date(item.pubdate)).fromNow(),
title: item.title, title: item.title,
url: this.getUrlPrefix(item) + item.url, url: this.getActiveItemURL(),
description: item.description, description: item.description,
items: items items: items
}; };
@ -312,8 +318,27 @@ Module.register("newsfeed", {
if (this.timer) clearInterval(this.timer); if (this.timer) clearInterval(this.timer);
this.timer = setInterval(() => { this.timer = setInterval(() => {
this.activeItem++;
this.updateDom(this.config.animationSpeed); /*
* When animations are enabled, don't update the DOM unless we are actually changing what we are displaying.
* (Animating from a headline to itself is unsightly.)
* Cases:
*
* Number of items | Number of items | Display
* at last update | right now | Behaviour
* ----------------------------------------------------
* 0 | 0 | do not update
* 0 | >0 | update
* 1 | 0 or >1 | update
* 1 | 1 | update only if item details (hash value) changed
* >1 | any | update
*
* (N.B. We set activeItemCount and activeItemHash in getTemplateData().)
*/
if (this.newsItems.length !== this.activeItemCount || this.activeItemHash !== this.newsItems[0]?.hash) {
this.activeItem++; // this is OK if newsItems.Length==1; getTemplateData will wrap it around
this.updateDom(this.config.animationSpeed);
}
// Broadcast NewsFeed if needed // Broadcast NewsFeed if needed
if (this.config.broadcastNewsFeeds) { if (this.config.broadcastNewsFeeds) {

View File

@ -5,6 +5,7 @@
* MIT Licensed. * MIT Licensed.
*/ */
const crypto = require("node:crypto");
const stream = require("node:stream"); const stream = require("node:stream");
const FeedMe = require("feedme"); const FeedMe = require("feedme");
const iconv = require("iconv-lite"); const iconv = require("iconv-lite");
@ -67,7 +68,8 @@ const NewsfeedFetcher = function (url, reloadInterval, encoding, logFeedWarnings
description: description, description: description,
pubdate: pubdate, pubdate: pubdate,
url: url, url: url,
useCorsProxy: useCorsProxy useCorsProxy: useCorsProxy,
hash: crypto.createHash("sha256").update(`${pubdate} :: ${title} :: ${url}`).digest("hex")
}); });
} else if (logFeedWarnings) { } else if (logFeedWarnings) {
Log.warn("Can't parse feed item:"); Log.warn("Can't parse feed item:");