mirror of
https://github.com/ajnart/homarr.git
synced 2026-01-18 13:32:18 +01:00
fix: rss widget post sorting (#1855)
This commit is contained in:
@@ -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": {
|
||||
|
||||
@@ -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,
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@@ -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) {
|
||||
<Stack h="100%">
|
||||
<ScrollArea className="scroll-area-w100" w="100%" mt="sm" mb="sm">
|
||||
<Stack w="100%" spacing="xs">
|
||||
{orderedFeedItems.map((item: any, index: number) => (
|
||||
{orderedFeedItems.slice(0, widget.properties.maximumAmountOfPosts).map((item: any, index: number) => (
|
||||
<Card
|
||||
key={index}
|
||||
withBorder
|
||||
|
||||
Reference in New Issue
Block a user