diff --git a/src/components/Dashboard/Tiles/Widgets/WidgetsEditModal.tsx b/src/components/Dashboard/Tiles/Widgets/WidgetsEditModal.tsx index e89d32176..643a8b64b 100644 --- a/src/components/Dashboard/Tiles/Widgets/WidgetsEditModal.tsx +++ b/src/components/Dashboard/Tiles/Widgets/WidgetsEditModal.tsx @@ -243,6 +243,25 @@ const WidgetOptionTypeSwitch: FC<{ ); + case 'multiple-text': + return ( + ({ value: v, label: v }))} + label={t(`descriptor.settings.${key}.label`)} + description={t(`descriptor.settings.${key}.description`)} + defaultValue={value as string[]} + withinPortal + searchable + creatable + getCreateLabel={(query) => `+ Add ${query}`} + onChange={(values) => + handleChange( + key, + values.map((item: string) => item) + ) + } + /> + ); /* eslint-enable no-case-declarations */ default: return null; diff --git a/src/widgets/rss/RssWidgetTile.tsx b/src/widgets/rss/RssWidgetTile.tsx index b1eab57cd..d2b8907ff 100644 --- a/src/widgets/rss/RssWidgetTile.tsx +++ b/src/widgets/rss/RssWidgetTile.tsx @@ -1,9 +1,9 @@ +import Link from 'next/link'; import { ActionIcon, Badge, Card, Center, - createStyles, Flex, Group, Image, @@ -14,31 +14,27 @@ import { Stack, Text, Title, + createStyles, } from '@mantine/core'; import { - IconBulldozer, - IconCalendarTime, IconClock, - IconCopyright, IconRefresh, IconRss, - IconSpeakerphone, } from '@tabler/icons'; import { useQuery } from '@tanstack/react-query'; import dayjs from 'dayjs'; import { useTranslation } from 'next-i18next'; -import Link from 'next/link'; -import { useState } from 'react'; -import { IWidget } from '../widgets'; + import { defineWidget } from '../helper'; +import { IWidget } from '../widgets'; const definition = defineWidget({ id: 'rss', icon: IconRss, options: { rssFeedUrl: { - type: 'text', - defaultValue: '', + type: 'multiple-text', + defaultValue: ['https://japantimes.co.jp/feed'], }, }, gridstack: { @@ -56,34 +52,42 @@ interface RssTileProps { widget: IRssWidget; } -export const useGetRssFeed = (feedUrl: string, widgetId: string) => +export const useGetRssFeeds = (feedUrls: string[], widgetId: string) => useQuery({ - queryKey: ['rss-feed', feedUrl], + queryKey: ['rss-feeds', feedUrls], queryFn: async () => { - const response = await fetch(`/api/modules/rss?widgetId=${widgetId}`); - return response.json(); + const responses = await Promise.all( + feedUrls.map((feedUrl) => + fetch( + `/api/modules/rss?widgetId=${widgetId}&feedUrl=${encodeURIComponent(feedUrl)}` + ).then((response) => response.json()) + ) + ); + return responses; }, }); function RssTile({ widget }: RssTileProps) { const { t } = useTranslation('modules/rss'); - const { data, isLoading, isFetching, isError, refetch } = useGetRssFeed( + const { data, isLoading, isFetching, isError, refetch } = useGetRssFeeds( widget.properties.rssFeedUrl, widget.id ); const { classes } = useStyles(); - const [loadingOverlayVisible, setLoadingOverlayVisible] = useState(false); function formatDate(input: string): string { // Parse the input date as a local date - const inputDate = dayjs(new Date(input)); - const now = dayjs(); // Current date and time - - // The difference between the input date and now - const difference = now.diff(inputDate, 'ms'); - const duration = dayjs.duration(difference, 'ms'); - const humanizedDuration = duration.humanize(); - return `${humanizedDuration} ago`; + try { + const inputDate = dayjs(new Date(input)); + const now = dayjs(); // Current date and time + // The difference between the input date and now + const difference = now.diff(inputDate, 'ms'); + const duration = dayjs.duration(difference, 'ms'); + const humanizedDuration = duration.humanize(); + return `${humanizedDuration} ago`; + } catch (e) { + return 'Error'; + } } if (!data || isLoading) { @@ -94,7 +98,7 @@ function RssTile({ widget }: RssTileProps) { ); } - if (!data.success || isError) { + if (data.length < 1 || isError) { return (
@@ -109,122 +113,78 @@ function RssTile({ widget }: RssTileProps) { return ( - {data.feed.title && {data.feed.title}} - - - {data.feed.items.map((item: any, index: number) => ( - - {item.enclosure && ( - // eslint-disable-next-line @next/next/no-img-element - backdrop - )} - - + + {data.map((item, index) => ( + + {item.feed.items.map((item: any, index: number) => ( + {item.enclosure && ( - - - + // eslint-disable-next-line @next/next/no-img-element + backdrop )} - - {item.categories && ( - - {item.categories.map((category: any, categoryIndex: number) => ( - {category} - ))} - + + + {item.enclosure && ( + + + )} + + {item.categories && ( + + {item.categories.map((category: any, categoryIndex: number) => ( + {category} + ))} + + )} - {item.title} - - {item.content} - + {item.title} + + {item.content} + - {item.pubDate && } + {item.pubDate && } + - - - ))} - + + ))} + + ))} - - {data.feed.copyright && ( - - - - {data.feed.copyright} - - - )} - {data.feed.pubDate && ( - - - - {data.feed.pubDate} - - - )} - {data.feed.lastBuildDate && ( - - - - {formatDate(data.feed.lastBuildDate)} - - - )} - {data.feed.feedUrl && ( - - - - Feed URL - - - )} - refetch()} - bottom={10} - styles={{ - root: { - borderColor: 'red', - }, - }} - > - {data.feed.image ? ( - {data.feed.image.title} - ) : ( - - )} - - + refetch()} + bottom={10} + styles={{ + root: { + borderColor: 'red', + }, + }} + > + + ); } diff --git a/src/widgets/widgets.ts b/src/widgets/widgets.ts index 998413fea..a5b6b9729 100644 --- a/src/widgets/widgets.ts +++ b/src/widgets/widgets.ts @@ -1,3 +1,4 @@ +import React from 'react'; import { MultiSelectProps, NumberInputProps, @@ -7,7 +8,7 @@ import { TextInputProps, } from '@mantine/core'; import { TablerIcon } from '@tabler/icons'; -import React from 'react'; + import { AreaType } from '../types/area'; import { ShapeType } from '../types/shape'; @@ -37,7 +38,8 @@ export type IWidgetOptionValue = | ISliderInputOptionValue | ISelectOptionValue | INumberInputOptionValue - | IDraggableListInputValue; + | IDraggableListInputValue + | IMultipleTextInputOptionValue; // Interface for data type interface DataType { @@ -105,6 +107,13 @@ export type IDraggableListInputValue = { >; }; +// will show a text-input with a button to add a new line +export type IMultipleTextInputOptionValue = { + type: 'multiple-text'; + defaultValue: string[]; + inputProps?: Partial; +}; + // is used to type the widget definitions which will be used to display all widgets export type IWidgetDefinition = { id: TKey;