diff --git a/docs/widgets/services/calendar.md b/docs/widgets/services/calendar.md index 74751f6ec..bb8b5016c 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 b0854b08c..cfc88fdcf 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 9313cb8e4..6a3be0319 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 0e10d0ed3..ff93c41be 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 7d3482858..13e736a36 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 4c4ec9ca5..78d0fe1d8 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 ddb9cd874..43694354f 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) => ( ))}