From ff21033b0c45bc0db03ff500f5c8ef3a878bc980 Mon Sep 17 00:00:00 2001 From: Manuel <30572287+manuel-rw@users.noreply.github.com> Date: Sun, 28 Jan 2024 21:15:46 +0100 Subject: [PATCH] fix: rss widget post sorting (#1855) --- public/locales/en/modules/rss.json | 6 +++ src/server/api/routers/rss.ts | 72 +++++++++++++++++------------- src/widgets/rss/RssWidgetTile.tsx | 13 +++++- 3 files changed, 60 insertions(+), 31 deletions(-) diff --git a/public/locales/en/modules/rss.json b/public/locales/en/modules/rss.json index ddf0dc71d..0d02639fb 100644 --- a/public/locales/en/modules/rss.json +++ b/public/locales/en/modules/rss.json @@ -20,6 +20,12 @@ }, "sortByPublishDateAscending": { "label": "Sort by publish date (ascending)" + }, + "sortPostsWithoutPublishDateToTheTop": { + "label": "Put posts without publish date to the top" + }, + "maximumAmountOfPosts": { + "label": "Maximum amount of posts" } }, "card": { diff --git a/src/server/api/routers/rss.ts b/src/server/api/routers/rss.ts index e0d2d30d6..7f91285a1 100644 --- a/src/server/api/routers/rss.ts +++ b/src/server/api/routers/rss.ts @@ -18,7 +18,7 @@ type CustomItem = { 'media:group'?: { 'media:description'?: string; 'media:thumbnail'?: string; - }, + }; pubDate?: string; }; @@ -44,10 +44,10 @@ const rssFeedResultObjectSchema = z title: z.string(), content: z.string(), pubDate: z.date().optional(), - }), + }) ), }), - }), + }) ); export const rssRouter = createTRPCRouter({ @@ -57,7 +57,7 @@ export const rssRouter = createTRPCRouter({ widgetId: z.string().uuid(), feedUrls: z.array(z.string()), configName: z.string(), - }), + }) ) .output(z.array(rssFeedResultObjectSchema)) .query(async ({ input }) => { @@ -80,13 +80,23 @@ export const rssRouter = createTRPCRouter({ return await Promise.all( input.feedUrls.map(async (feedUrl) => - getFeedUrl(feedUrl, rssWidget.properties.dangerousAllowSanitizedItemContent), - ), + getFeedUrl( + feedUrl, + rssWidget.properties.dangerousAllowSanitizedItemContent, + rssWidget.properties.sortPostsWithoutPublishDateToTheTop, + rssWidget.properties.maximumAmountOfPosts + ) + ) ); }), }); -const getFeedUrl = async (feedUrl: string, dangerousAllowSanitizedItemContent: boolean) => { +const getFeedUrl = async ( + feedUrl: string, + dangerousAllowSanitizedItemContent: boolean, + sortPostsWithoutPubDateToTop: boolean, + maximumAmountOfPosts: number +) => { Consola.info(`Requesting RSS feed at url ${feedUrl}`); const stopWatch = new Stopwatch(); const feed = await parser.parseURL(feedUrl); @@ -103,32 +113,34 @@ const getFeedUrl = async (feedUrl: string, dangerousAllowSanitizedItemContent: b 'media:group'?: { 'media:description'?: string; 'media:thumbnail'?: string; - } + }; categories: string[] | { _: string }[]; pubDate?: string; - }) => ({ - ...item, - categories: item.categories - ?.map((category) => (typeof category === 'string' ? category : category._)) - .filter((category: unknown): category is string => typeof category === 'string'), - title: item.title ? decode(item.title) : undefined, - content: processItemContent( - item['content:encoded'] ?? item.content ?? item['media:group']?.['media:description'], - dangerousAllowSanitizedItemContent, - ), - enclosure: createEnclosure(item), - link: createLink(item), - pubDate: item.pubDate ? new Date(item.pubDate) : null, - }), + }) => { + Consola.info('item ' + item.title + ' has pub date ' + item.pubDate); + return { + ...item, + categories: item.categories + ?.map((category) => (typeof category === 'string' ? category : category._)) + .filter((category: unknown): category is string => typeof category === 'string'), + title: item.title ? decode(item.title) : undefined, + content: processItemContent( + item['content:encoded'] ?? item.content ?? item['media:group']?.['media:description'], + dangerousAllowSanitizedItemContent + ), + enclosure: createEnclosure(item), + link: createLink(item), + pubDate: item.pubDate ? new Date(item.pubDate) : null, + }; + } ) - .sort((a: { pubDate: number }, b: { pubDate: number }) => { - if (!a.pubDate || !b.pubDate) { - return 0; + .sort((post1: { pubDate: number }, post2: { pubDate: number }) => { + if (!post1.pubDate || !post2.pubDate) { + return sortPostsWithoutPubDateToTop ? 1 : 0; } - return a.pubDate - b.pubDate; - }) - .slice(0, 20), + return post1.pubDate - post2.pubDate; + }), }; return { @@ -169,7 +181,7 @@ const processItemContent = (content: string, dangerousAllowSanitizedItemContent: } return encode(content, { - level: "html5" + level: 'html5', }); }; @@ -195,7 +207,7 @@ const createEnclosure = (item: any) => { if (item['media:group'] && item['media:group']['media:thumbnail']) { // no clue why this janky parse is needed return { - url: item['media:group']['media:thumbnail'][0].$.url + url: item['media:group']['media:thumbnail'][0].$.url, }; } diff --git a/src/widgets/rss/RssWidgetTile.tsx b/src/widgets/rss/RssWidgetTile.tsx index ab0016482..6b6f22efc 100644 --- a/src/widgets/rss/RssWidgetTile.tsx +++ b/src/widgets/rss/RssWidgetTile.tsx @@ -55,6 +55,17 @@ const definition = defineWidget({ type: 'switch', defaultValue: true, }, + sortPostsWithoutPublishDateToTheTop: { + type: 'switch', + defaultValue: false + }, + maximumAmountOfPosts: { + type: 'slider', + defaultValue: 20, + min: 1, + max: 350, + step: 1 + } }, gridstack: { minWidth: 2, @@ -127,7 +138,7 @@ function RssTile({ widget }: RssTileProps) { - {orderedFeedItems.map((item: any, index: number) => ( + {orderedFeedItems.slice(0, widget.properties.maximumAmountOfPosts).map((item: any, index: number) => (