From 6717bcf8b464ec9d65e3d99ac7003c5d513aaacc Mon Sep 17 00:00:00 2001 From: Manuel <30572287+manuel-rw@users.noreply.github.com> Date: Thu, 11 Jan 2024 19:19:59 +0100 Subject: [PATCH] feature: rss improvements (#1810) --- public/locales/en/modules/rss.json | 3 + src/server/api/routers/rss.ts | 28 +++++-- src/widgets/rss/RssWidgetTile.tsx | 127 +++++++++++++++-------------- 3 files changed, 92 insertions(+), 66 deletions(-) diff --git a/public/locales/en/modules/rss.json b/public/locales/en/modules/rss.json index 20b827fcd..ddf0dc71d 100644 --- a/public/locales/en/modules/rss.json +++ b/public/locales/en/modules/rss.json @@ -17,6 +17,9 @@ }, "textLinesClamp": { "label": "Text lines clamp" + }, + "sortByPublishDateAscending": { + "label": "Sort by publish date (ascending)" } }, "card": { diff --git a/src/server/api/routers/rss.ts b/src/server/api/routers/rss.ts index 02bc618b7..e0d2d30d6 100644 --- a/src/server/api/routers/rss.ts +++ b/src/server/api/routers/rss.ts @@ -15,6 +15,11 @@ type CustomItem = { enclosure: { url: string; }; + 'media:group'?: { + 'media:description'?: string; + 'media:thumbnail'?: string; + }, + pubDate?: string; }; const rssFeedResultObjectSchema = z @@ -38,7 +43,7 @@ const rssFeedResultObjectSchema = z categories: z.array(z.string()).or(z.undefined()), title: z.string(), content: z.string(), - pubDate: z.string().optional(), + pubDate: z.date().optional(), }), ), }), @@ -73,13 +78,11 @@ export const rssRouter = createTRPCRouter({ return []; } - const result = await Promise.all( + return await Promise.all( input.feedUrls.map(async (feedUrl) => getFeedUrl(feedUrl, rssWidget.properties.dangerousAllowSanitizedItemContent), ), ); - - return result; }), }); @@ -97,7 +100,12 @@ const getFeedUrl = async (feedUrl: string, dangerousAllowSanitizedItemContent: b title: string; content: string; 'content:encoded': string; + 'media:group'?: { + 'media:description'?: string; + 'media:thumbnail'?: string; + } categories: string[] | { _: string }[]; + pubDate?: string; }) => ({ ...item, categories: item.categories @@ -105,11 +113,12 @@ const getFeedUrl = async (feedUrl: string, dangerousAllowSanitizedItemContent: b .filter((category: unknown): category is string => typeof category === 'string'), title: item.title ? decode(item.title) : undefined, content: processItemContent( - item['content:encoded'] ?? item.content, + 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 }) => { @@ -183,11 +192,18 @@ 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 + }; + } + return undefined; }; const parser: RssParser = new RssParser({ customFields: { - item: ['media:content', 'enclosure'], + item: ['media:content', 'enclosure', 'media:group'], }, }); diff --git a/src/widgets/rss/RssWidgetTile.tsx b/src/widgets/rss/RssWidgetTile.tsx index 1a2bb2807..ab0016482 100644 --- a/src/widgets/rss/RssWidgetTile.tsx +++ b/src/widgets/rss/RssWidgetTile.tsx @@ -51,6 +51,10 @@ const definition = defineWidget({ max: 50, step: 1, }, + sortByPublishDateAscending: { + type: 'switch', + defaultValue: true, + }, }, gridstack: { minWidth: 2, @@ -74,7 +78,7 @@ function RssTile({ widget }: RssTileProps) { configName, widget.properties.rssFeedUrl, widget.properties.refreshInterval, - widget.id + widget.id, ); const { classes } = useStyles(); @@ -100,7 +104,7 @@ function RssTile({ widget }: RssTileProps) { ); } - if (!data || data.length < 1 || !data[0].feed || isError) { + if (!data || data.length < 1 || isError) { return (
@@ -113,70 +117,73 @@ function RssTile({ widget }: RssTileProps) { ); } + const flatFeeds = data.filter(feed => feed.success).flatMap(feed => feed.feed); + const flatFeedItems = flatFeeds.flatMap(feed => feed!.items); + const orderedFeedItems = widget.properties.sortByPublishDateAscending ? + flatFeedItems.sort((item1, item2) => + (item2.pubDate?.getTime() as number) - (item1.pubDate?.getTime() as number)) : flatFeedItems; + return ( - {data.map((feed, index) => ( - - {feed.feed && - feed.feed.items.map((item: any, index: number) => ( - - {item.enclosure && ( - // eslint-disable-next-line @next/next/no-img-element - + {orderedFeedItems.map((item: any, index: number) => ( + + {item.enclosure && ( + // eslint-disable-next-line @next/next/no-img-element + backdrop + )} + + + {item.enclosure && item.enclosure.url && ( + + backdrop + + )} + + {item.categories && ( + + {item.categories.map((category: any, categoryIndex: number) => ( + {category} + ))} + )} - - {item.enclosure && item.enclosure.url && ( - - - - )} - - {item.categories && ( - - {item.categories.map((category: any, categoryIndex: number) => ( - {category} - ))} - - )} + {item.title} + - {item.title} - - - {item.pubDate && ( - - )} - - - - ))} - - ))} + {item.pubDate && ( + + )} + + + + ))} + @@ -188,7 +195,7 @@ export const useGetRssFeeds = ( configName: string | undefined, feedUrls: string[], refreshInterval: number, - widgetId: string + widgetId: string, ) => api.rss.all.useQuery( { @@ -201,7 +208,7 @@ export const useGetRssFeeds = ( cacheTime: 1000 * 60 * 60 * 24, staleTime: 1000 * 60 * refreshInterval, enabled: !!configName, - } + }, ); interface RefetchButtonProps {