"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;
};