fix: rss widget post sorting (#1855)
This commit is contained in:
@@ -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": {
|
||||||
|
|||||||
@@ -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,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
Reference in New Issue
Block a user