From 72efd9a08d3fbe91945990a0e8ec6495c70c411f Mon Sep 17 00:00:00 2001 From: Denis Papec Date: Wed, 17 Jan 2024 23:00:51 +0000 Subject: [PATCH 1/3] Fix: calendar timezone performance improvements (#2668) --- docs/widgets/services/calendar.md | 2 +- src/utils/config/service-helpers.js | 2 ++ src/widgets/calendar/agenda.jsx | 10 ++++------ src/widgets/calendar/component.jsx | 5 ++++- src/widgets/calendar/event.jsx | 5 +---- src/widgets/calendar/integrations/ical.jsx | 13 ++++++------- src/widgets/calendar/monthly.jsx | 16 +++++++--------- 7 files changed, 25 insertions(+), 28 deletions(-) diff --git a/docs/widgets/services/calendar.md b/docs/widgets/services/calendar.md index 74751f6e..bb8b5016 100644 --- a/docs/widgets/services/calendar.md +++ b/docs/widgets/services/calendar.md @@ -16,6 +16,7 @@ widget: view: monthly # optional - possible values monthly, agenda maxEvents: 10 # optional - defaults to 10 showTime: true # optional - show time for event happening today - defaults to false + timezone: America/Los_Angeles # optional and only when timezone is not detected properly (slightly slower performance) - force timezone for ical events (if it's the same - no change, if missing or different in ical - will be converted to this timezone) integrations: # optional - type: sonarr # active widget type that is currently enabled on homepage - possible values: radarr, sonarr, lidarr, readarr, ical service_group: Media # group name where widget exists @@ -27,7 +28,6 @@ widget: url: https://domain.url/with/link/to.ics # URL with calendar events name: My Events # required - name for these calendar events color: zinc # optional - defaults to pre-defined color for the service (zinc for ical) - timezone: America/Los_Angeles # optional - force timezone for events (if it's the same - no change, if missing or different in ical - will be converted to this timezone) params: # optional - additional params for the service showName: true # optional - show name before event title in event line - defaults to false ``` diff --git a/src/utils/config/service-helpers.js b/src/utils/config/service-helpers.js index b0854b08..cfc88fdc 100644 --- a/src/utils/config/service-helpers.js +++ b/src/utils/config/service-helpers.js @@ -368,6 +368,7 @@ export function cleanServiceGroups(groups) { showTime, previousDays, view, + timezone, // coinmarketcap currency, @@ -538,6 +539,7 @@ export function cleanServiceGroups(groups) { if (maxEvents) cleanedService.widget.maxEvents = maxEvents; if (previousDays) cleanedService.widget.previousDays = previousDays; if (showTime) cleanedService.widget.showTime = showTime; + if (timezone) cleanedService.widget.timezone = timezone; } if (type === "healthchecks") { if (uuid !== undefined) cleanedService.widget.uuid = uuid; diff --git a/src/widgets/calendar/agenda.jsx b/src/widgets/calendar/agenda.jsx index 9313cb8e..6a3be031 100644 --- a/src/widgets/calendar/agenda.jsx +++ b/src/widgets/calendar/agenda.jsx @@ -2,7 +2,7 @@ import { DateTime } from "luxon"; import classNames from "classnames"; import { useTranslation } from "next-i18next"; -import Event, { compareDateTimezoneAware } from "./event"; +import Event, { compareDateTimezone } from "./event"; export default function Agenda({ service, colorVariants, events, showDate }) { const { widget } = service; @@ -15,10 +15,8 @@ export default function Agenda({ service, colorVariants, events, showDate }) { const eventsArray = Object.keys(events) .filter( (eventKey) => - showDate - .setZone(events[eventKey].date.zoneName) - .minus({ days: widget?.previousDays ?? 0 }) - .startOf("day").ts <= events[eventKey].date?.startOf("day").ts, + showDate.minus({ days: widget?.previousDays ?? 0 }).startOf("day").ts <= + events[eventKey].date?.startOf("day").ts, ) .map((eventKey) => events[eventKey]) .sort((a, b) => a.date - b.date) @@ -58,7 +56,7 @@ export default function Agenda({ service, colorVariants, events, showDate }) { event={event} colorVariants={colorVariants} showDate={j === 0} - showTime={widget?.showTime && compareDateTimezoneAware(showDate, event)} + showTime={widget?.showTime && compareDateTimezone(showDate, event)} /> ))} diff --git a/src/widgets/calendar/component.jsx b/src/widgets/calendar/component.jsx index 0e10d0ed..ff93c41b 100644 --- a/src/widgets/calendar/component.jsx +++ b/src/widgets/calendar/component.jsx @@ -41,7 +41,8 @@ export default function Component({ service }) { const { i18n } = useTranslation(); const [showDate, setShowDate] = useState(null); const [events, setEvents] = useState({}); - const currentDate = DateTime.now().setLocale(i18n.language).startOf("day"); + const nowDate = DateTime.now().setLocale(i18n.language); + const currentDate = widget?.timezone ? nowDate.setZone(widget?.timezone).startOf("day") : nowDate; const { settings } = useContext(SettingsContext); useEffect(() => { @@ -93,6 +94,7 @@ export default function Component({ service }) { params={params} setEvents={setEvents} hideErrors={settings.hideErrors} + timezone={widget?.timezone} className="fixed bottom-0 left-0 bg-red-500 w-screen h-12" /> ); @@ -106,6 +108,7 @@ export default function Component({ service }) { events={events} showDate={showDate} setShowDate={setShowDate} + currentDate={currentDate} className="flex" /> )} diff --git a/src/widgets/calendar/event.jsx b/src/widgets/calendar/event.jsx index 7d348285..13e736a3 100644 --- a/src/widgets/calendar/event.jsx +++ b/src/widgets/calendar/event.jsx @@ -39,7 +39,4 @@ export default function Event({ event, colorVariants, showDate = false, showTime ); } - -export function compareDateTimezoneAware(date, event) { - return date.setZone(event.date.zoneName).startOf("day").valueOf() === event.date.startOf("day").valueOf(); -} +export const compareDateTimezone = (date, event) => date.startOf("day").ts === event.date.startOf("day").ts; diff --git a/src/widgets/calendar/integrations/ical.jsx b/src/widgets/calendar/integrations/ical.jsx index 4c4ec9ca..78d0fe1d 100644 --- a/src/widgets/calendar/integrations/ical.jsx +++ b/src/widgets/calendar/integrations/ical.jsx @@ -7,7 +7,7 @@ import { RRule } from "rrule"; import useWidgetAPI from "../../../utils/proxy/use-widget-api"; import Error from "../../../components/services/widget/error"; -export default function Integration({ config, params, setEvents, hideErrors }) { +export default function Integration({ config, params, setEvents, hideErrors, timezone }) { const { t } = useTranslation(); const { data: icalData, error: icalError } = useWidgetAPI(config, config.name, { refreshInterval: 300000, // 5 minutes @@ -23,9 +23,8 @@ export default function Integration({ config, params, setEvents, hideErrors }) { } } - const zone = config?.timezone || null; - const startDate = DateTime.fromISO(params.start, { zone }); - const endDate = DateTime.fromISO(params.end, { zone }); + const startDate = DateTime.fromISO(params.start); + const endDate = DateTime.fromISO(params.end); if (icalError || !parsedIcal || !startDate.isValid || !endDate.isValid) { return; @@ -33,6 +32,7 @@ export default function Integration({ config, params, setEvents, hideErrors }) { const eventsToAdd = {}; const events = parsedIcal?.getEventsBetweenDates(startDate.toJSDate(), endDate.toJSDate()); + const now = timezone ? DateTime.now().setZone(timezone) : DateTime.now(); events?.forEach((event) => { let title = `${event?.summary?.value}`; @@ -44,8 +44,7 @@ export default function Integration({ config, params, setEvents, hideErrors }) { const duration = event.dtend.value - event.dtstart.value; const days = duration / (1000 * 60 * 60 * 24); - const now = DateTime.now().setZone(zone); - const eventDate = DateTime.fromJSDate(date, { zone }); + const eventDate = timezone ? DateTime.fromJSDate(date, { zone: timezone }) : DateTime.fromJSDate(date); for (let j = 0; j < days; j += 1) { eventsToAdd[`${event?.uid?.value}${i}${j}${type}`] = { @@ -72,7 +71,7 @@ export default function Integration({ config, params, setEvents, hideErrors }) { }); setEvents((prevEvents) => ({ ...prevEvents, ...eventsToAdd })); - }, [icalData, icalError, config, params, setEvents, t]); + }, [icalData, icalError, config, params, setEvents, timezone, t]); const error = icalError ?? icalData?.error; return error && !hideErrors && ; diff --git a/src/widgets/calendar/monthly.jsx b/src/widgets/calendar/monthly.jsx index ddb9cd87..43694354 100644 --- a/src/widgets/calendar/monthly.jsx +++ b/src/widgets/calendar/monthly.jsx @@ -3,16 +3,14 @@ import { DateTime, Info } from "luxon"; import classNames from "classnames"; import { useTranslation } from "next-i18next"; -import Event, { compareDateTimezoneAware } from "./event"; +import Event, { compareDateTimezone } from "./event"; const cellStyle = "relative w-10 flex items-center justify-center flex-col"; const monthButton = "pl-6 pr-6 ml-2 mr-2 hover:bg-theme-100/20 dark:hover:bg-white/5 rounded-md cursor-pointer"; -export function Day({ weekNumber, weekday, events, colorVariants, showDate, setShowDate }) { - const currentDate = DateTime.now(); - +export function Day({ weekNumber, weekday, events, colorVariants, showDate, setShowDate, currentDate }) { const cellDate = showDate.set({ weekday, weekNumber }).startOf("day"); - const filteredEvents = events?.filter((event) => compareDateTimezoneAware(cellDate, event)); + const filteredEvents = events?.filter((event) => compareDateTimezone(cellDate, event)); const dayStyles = (displayDate) => { let style = "h-9 "; @@ -77,10 +75,9 @@ const dayInWeekId = { sunday: 7, }; -export default function Monthly({ service, colorVariants, events, showDate, setShowDate }) { +export default function Monthly({ service, colorVariants, events, showDate, setShowDate, currentDate }) { const { widget } = service; const { i18n } = useTranslation(); - const currentDate = DateTime.now().setLocale(i18n.language).startOf("day"); const dayNames = Info.weekdays("short", { locale: i18n.language }); @@ -164,6 +161,7 @@ export default function Monthly({ service, colorVariants, events, showDate, setS colorVariants={colorVariants} showDate={showDate} setShowDate={setShowDate} + currentDate={currentDate} /> )), )} @@ -171,7 +169,7 @@ export default function Monthly({ service, colorVariants, events, showDate, setS
{eventsArray - ?.filter((event) => compareDateTimezoneAware(showDate, event)) + ?.filter((event) => compareDateTimezone(showDate, event)) .slice(0, widget?.maxEvents ?? 10) .map((event) => ( ))}
From 7f621ed5181d0aaface4f408e2b7eee4e6e4fb1b Mon Sep 17 00:00:00 2001 From: Mitchell <37925797+ping-localhost@users.noreply.github.com> Date: Thu, 18 Jan 2024 01:01:20 +0100 Subject: [PATCH 2/3] Feature: Netdata service widget (#2672) --- docs/widgets/services/netdata.md | 12 +++++++++++ mkdocs.yml | 1 + public/locales/en/common.json | 4 ++++ src/widgets/components.js | 1 + src/widgets/netdata/component.jsx | 33 +++++++++++++++++++++++++++++++ src/widgets/netdata/widget.js | 14 +++++++++++++ src/widgets/widgets.js | 2 ++ 7 files changed, 67 insertions(+) create mode 100644 docs/widgets/services/netdata.md create mode 100644 src/widgets/netdata/component.jsx create mode 100644 src/widgets/netdata/widget.js diff --git a/docs/widgets/services/netdata.md b/docs/widgets/services/netdata.md new file mode 100644 index 00000000..70acf490 --- /dev/null +++ b/docs/widgets/services/netdata.md @@ -0,0 +1,12 @@ +--- +title: Netdata +description: Netdata Widget Configuration +--- + +Allowed fields: `["warnings", "criticals"]`. + +```yaml +widget: + type: Netdata + url: http://netdata.host.or.ip +``` diff --git a/mkdocs.yml b/mkdocs.yml index 77f55dde..0968da60 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -84,6 +84,7 @@ nav: - widgets/services/moonraker.md - widgets/services/mylar.md - widgets/services/navidrome.md + - widgets/services/netdata.md - widgets/services/nextcloud.md - widgets/services/nextdns.md - widgets/services/nginx-proxy-manager.md diff --git a/public/locales/en/common.json b/public/locales/en/common.json index 1c43e506..eaed3344 100644 --- a/public/locales/en/common.json +++ b/public/locales/en/common.json @@ -795,5 +795,9 @@ "digitalRelease": "Digital release", "noEventsToday": "No events for today!", "noEventsFound": "No events found" + }, + "netdata": { + "warnings": "Warnings", + "criticals": "Criticals" } } diff --git a/src/widgets/components.js b/src/widgets/components.js index 4209b69a..30e95740 100644 --- a/src/widgets/components.js +++ b/src/widgets/components.js @@ -58,6 +58,7 @@ const components = { moonraker: dynamic(() => import("./moonraker/component")), mylar: dynamic(() => import("./mylar/component")), navidrome: dynamic(() => import("./navidrome/component")), + netdata: dynamic(() => import("./netdata/component")), nextcloud: dynamic(() => import("./nextcloud/component")), nextdns: dynamic(() => import("./nextdns/component")), npm: dynamic(() => import("./npm/component")), diff --git a/src/widgets/netdata/component.jsx b/src/widgets/netdata/component.jsx new file mode 100644 index 00000000..9d7f2469 --- /dev/null +++ b/src/widgets/netdata/component.jsx @@ -0,0 +1,33 @@ +import { useTranslation } from "next-i18next"; + +import Container from "components/services/widget/container"; +import Block from "components/services/widget/block"; +import useWidgetAPI from "utils/proxy/use-widget-api"; + +export default function Component({ service }) { + const { t } = useTranslation(); + + const { widget } = service; + + const { data: netdataData, error: netdataError } = useWidgetAPI(widget, "info"); + + if (netdataError) { + return ; + } + + if (!netdataData) { + return ( + + + + + ); + } + + return ( + + + + + ); +} diff --git a/src/widgets/netdata/widget.js b/src/widgets/netdata/widget.js new file mode 100644 index 00000000..9c7b2457 --- /dev/null +++ b/src/widgets/netdata/widget.js @@ -0,0 +1,14 @@ +import genericProxyHandler from "utils/proxy/handlers/generic"; + +const widget = { + api: "{url}/api/v1/{endpoint}", + proxyHandler: genericProxyHandler, + + mappings: { + info: { + endpoint: "info", + }, + }, +}; + +export default widget; diff --git a/src/widgets/widgets.js b/src/widgets/widgets.js index 904bd701..b925bf74 100644 --- a/src/widgets/widgets.js +++ b/src/widgets/widgets.js @@ -50,6 +50,7 @@ import mjpeg from "./mjpeg/widget"; import moonraker from "./moonraker/widget"; import mylar from "./mylar/widget"; import navidrome from "./navidrome/widget"; +import netdata from "./netdata/widget"; import nextcloud from "./nextcloud/widget"; import nextdns from "./nextdns/widget"; import npm from "./npm/widget"; @@ -155,6 +156,7 @@ const widgets = { moonraker, mylar, navidrome, + netdata, nextcloud, nextdns, npm, From a24b5d2fd124cb9cb9a4166bcc9d3a72f536085f Mon Sep 17 00:00:00 2001 From: Luke Hagar Date: Thu, 18 Jan 2024 16:58:42 -0600 Subject: [PATCH 3/3] Fix: remove date time load delay (#2675) --- src/components/widgets/datetime/datetime.jsx | 1 + 1 file changed, 1 insertion(+) diff --git a/src/components/widgets/datetime/datetime.jsx b/src/components/widgets/datetime/datetime.jsx index 4e1cd6f1..a7aec5da 100644 --- a/src/components/widgets/datetime/datetime.jsx +++ b/src/components/widgets/datetime/datetime.jsx @@ -23,6 +23,7 @@ export default function DateTime({ options }) { useEffect(() => { const dateFormat = new Intl.DateTimeFormat(dateLocale, { ...format }); + setDate(dateFormat.format(new Date())); const interval = setInterval(() => { setDate(dateFormat.format(new Date())); }, 1000);