fix: rss widget post sorting (#1855)

This commit is contained in:
Manuel
2024-01-28 21:15:46 +01:00
committed by GitHub
parent 6fdf1dfaa8
commit ff21033b0c
3 changed files with 60 additions and 31 deletions

View File

@@ -20,6 +20,12 @@
}, },
"sortByPublishDateAscending": { "sortByPublishDateAscending": {
"label": "Sort by publish date (ascending)" "label": "Sort by publish date (ascending)"
},
"sortPostsWithoutPublishDateToTheTop": {
"label": "Put posts without publish date to the top"
},
"maximumAmountOfPosts": {
"label": "Maximum amount of posts"
} }
}, },
"card": { "card": {

View File

@@ -18,7 +18,7 @@ type CustomItem = {
'media:group'?: { 'media:group'?: {
'media:description'?: string; 'media:description'?: string;
'media:thumbnail'?: string; 'media:thumbnail'?: string;
}, };
pubDate?: string; pubDate?: string;
}; };
@@ -44,10 +44,10 @@ const rssFeedResultObjectSchema = z
title: z.string(), title: z.string(),
content: z.string(), content: z.string(),
pubDate: z.date().optional(), pubDate: z.date().optional(),
}), })
), ),
}), }),
}), })
); );
export const rssRouter = createTRPCRouter({ export const rssRouter = createTRPCRouter({
@@ -57,7 +57,7 @@ export const rssRouter = createTRPCRouter({
widgetId: z.string().uuid(), widgetId: z.string().uuid(),
feedUrls: z.array(z.string()), feedUrls: z.array(z.string()),
configName: z.string(), configName: z.string(),
}), })
) )
.output(z.array(rssFeedResultObjectSchema)) .output(z.array(rssFeedResultObjectSchema))
.query(async ({ input }) => { .query(async ({ input }) => {
@@ -80,13 +80,23 @@ export const rssRouter = createTRPCRouter({
return await Promise.all( return await Promise.all(
input.feedUrls.map(async (feedUrl) => 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}`); Consola.info(`Requesting RSS feed at url ${feedUrl}`);
const stopWatch = new Stopwatch(); const stopWatch = new Stopwatch();
const feed = await parser.parseURL(feedUrl); const feed = await parser.parseURL(feedUrl);
@@ -103,32 +113,34 @@ const getFeedUrl = async (feedUrl: string, dangerousAllowSanitizedItemContent: b
'media:group'?: { 'media:group'?: {
'media:description'?: string; 'media:description'?: string;
'media:thumbnail'?: string; 'media:thumbnail'?: string;
} };
categories: string[] | { _: string }[]; categories: string[] | { _: string }[];
pubDate?: string; pubDate?: string;
}) => ({ }) => {
...item, Consola.info('item ' + item.title + ' has pub date ' + item.pubDate);
categories: item.categories return {
?.map((category) => (typeof category === 'string' ? category : category._)) ...item,
.filter((category: unknown): category is string => typeof category === 'string'), categories: item.categories
title: item.title ? decode(item.title) : undefined, ?.map((category) => (typeof category === 'string' ? category : category._))
content: processItemContent( .filter((category: unknown): category is string => typeof category === 'string'),
item['content:encoded'] ?? item.content ?? item['media:group']?.['media:description'], title: item.title ? decode(item.title) : undefined,
dangerousAllowSanitizedItemContent, content: processItemContent(
), item['content:encoded'] ?? item.content ?? item['media:group']?.['media:description'],
enclosure: createEnclosure(item), dangerousAllowSanitizedItemContent
link: createLink(item), ),
pubDate: item.pubDate ? new Date(item.pubDate) : null, enclosure: createEnclosure(item),
}), link: createLink(item),
pubDate: item.pubDate ? new Date(item.pubDate) : null,
};
}
) )
.sort((a: { pubDate: number }, b: { pubDate: number }) => { .sort((post1: { pubDate: number }, post2: { pubDate: number }) => {
if (!a.pubDate || !b.pubDate) { if (!post1.pubDate || !post2.pubDate) {
return 0; return sortPostsWithoutPubDateToTop ? 1 : 0;
} }
return a.pubDate - b.pubDate; return post1.pubDate - post2.pubDate;
}) }),
.slice(0, 20),
}; };
return { return {
@@ -169,7 +181,7 @@ const processItemContent = (content: string, dangerousAllowSanitizedItemContent:
} }
return encode(content, { return encode(content, {
level: "html5" level: 'html5',
}); });
}; };
@@ -195,7 +207,7 @@ const createEnclosure = (item: any) => {
if (item['media:group'] && item['media:group']['media:thumbnail']) { if (item['media:group'] && item['media:group']['media:thumbnail']) {
// no clue why this janky parse is needed // no clue why this janky parse is needed
return { return {
url: item['media:group']['media:thumbnail'][0].$.url url: item['media:group']['media:thumbnail'][0].$.url,
}; };
} }

View File

@@ -55,6 +55,17 @@ const definition = defineWidget({
type: 'switch', type: 'switch',
defaultValue: true, defaultValue: true,
}, },
sortPostsWithoutPublishDateToTheTop: {
type: 'switch',
defaultValue: false
},
maximumAmountOfPosts: {
type: 'slider',
defaultValue: 20,
min: 1,
max: 350,
step: 1
}
}, },
gridstack: { gridstack: {
minWidth: 2, minWidth: 2,
@@ -127,7 +138,7 @@ function RssTile({ widget }: RssTileProps) {
<Stack h="100%"> <Stack h="100%">
<ScrollArea className="scroll-area-w100" w="100%" mt="sm" mb="sm"> <ScrollArea className="scroll-area-w100" w="100%" mt="sm" mb="sm">
<Stack w="100%" spacing="xs"> <Stack w="100%" spacing="xs">
{orderedFeedItems.map((item: any, index: number) => ( {orderedFeedItems.slice(0, widget.properties.maximumAmountOfPosts).map((item: any, index: number) => (
<Card <Card
key={index} key={index}
withBorder withBorder