"use client"; import { useEffect, useMemo, useRef, useState } from "react"; import { Box, Stack, Text, Title } from "@mantine/core"; import dayjs from "dayjs"; import advancedFormat from "dayjs/plugin/advancedFormat"; import timezones from "dayjs/plugin/timezone"; import utc from "dayjs/plugin/utc"; import { clientApi } from "@homarr/api/client"; import type { WidgetComponentProps } from "../definition"; import { AnimatedWeatherIcon } from "../weather/animated-icon"; dayjs.extend(advancedFormat); dayjs.extend(utc); dayjs.extend(timezones); export default function ClockWidget({ options, width }: WidgetComponentProps<"clock">) { const secondsFormat = options.showSeconds ? ":ss" : ""; const timeFormat = options.is24HourFormat ? `HH:mm${secondsFormat}` : `hh:mm${secondsFormat} A`; const dateFormat = options.dateFormat; const customTimeFormat = options.customTimeFormat; const customDateFormat = options.customDateFormat; const timezone = options.useCustomTimezone ? options.timezone : Intl.DateTimeFormat().resolvedOptions().timeZone; const time = useCurrentTime(options); const sizing = width < 128 ? "xs" : width < 196 ? "sm" : "md"; const showWeatherCorner = options.showWeather && sizing !== "xs"; return ( {showWeatherCorner && ( )} {options.customTitleToggle && ( {options.customTitle} )} {options.customTimeFormat ? dayjs(time).tz(timezone).format(customTimeFormat) : dayjs(time).tz(timezone).format(timeFormat)} {options.showDate && ( {options.customDateFormat ? dayjs(time).tz(timezone).format(customDateFormat) : dayjs(time).tz(timezone).format(dateFormat)} )} ); } interface ClockWeatherCornerProps { latitude: number; longitude: number; isFahrenheit: boolean; } const ClockWeatherCorner = ({ latitude, longitude, isFahrenheit }: ClockWeatherCornerProps) => { const { data: weather } = clientApi.widget.weather.atLocation.useQuery({ latitude, longitude }); if (!weather) return null; const temp = isFahrenheit ? weather.current.temperature * (9 / 5) + 32 : weather.current.temperature; const unit = isFahrenheit ? "°F" : "°C"; return ( {Math.round(temp)} {unit} ); }; interface UseCurrentTimeProps { showSeconds: boolean; } const useCurrentTime = ({ showSeconds }: UseCurrentTimeProps) => { const [time, setTime] = useState(new Date()); const timeoutRef = useRef(null); const intervalRef = useRef(null); const intervalMultiplier = useMemo(() => (showSeconds ? 1 : 60), [showSeconds]); useEffect(() => { setTime(new Date()); timeoutRef.current = setTimeout( () => { setTime(new Date()); intervalRef.current = setInterval(() => { setTime(new Date()); }, intervalMultiplier * 1000); }, intervalMultiplier * 1000 - (1000 * (showSeconds ? 0 : dayjs().second()) + dayjs().millisecond()), ); return () => { if (timeoutRef.current) clearTimeout(timeoutRef.current); if (intervalRef.current) clearInterval(intervalRef.current); }; }, [intervalMultiplier, showSeconds]); return time; };