2025-04-23 00:50:21 +03:00
|
|
|
import { useFormattedTimestamp } from "@/hooks/use-date-utils";
|
2024-06-02 20:00:59 +03:00
|
|
|
import { FrigateConfig } from "@/types/frigateConfig";
|
2024-11-04 17:07:57 +03:00
|
|
|
import { useMemo } from "react";
|
2025-04-23 00:50:21 +03:00
|
|
|
import { useTranslation } from "react-i18next";
|
2024-06-02 20:00:59 +03:00
|
|
|
import useSWR from "swr";
|
|
|
|
|
|
2024-03-04 19:42:51 +03:00
|
|
|
type MinimapSegmentProps = {
|
|
|
|
|
isFirstSegmentInMinimap: boolean;
|
|
|
|
|
isLastSegmentInMinimap: boolean;
|
|
|
|
|
alignedMinimapStartTime: number;
|
|
|
|
|
alignedMinimapEndTime: number;
|
|
|
|
|
firstMinimapSegmentRef: React.MutableRefObject<HTMLDivElement | null>;
|
2024-03-28 18:03:06 +03:00
|
|
|
dense: boolean;
|
2024-03-04 19:42:51 +03:00
|
|
|
};
|
|
|
|
|
|
|
|
|
|
type TickSegmentProps = {
|
|
|
|
|
timestamp: Date;
|
|
|
|
|
timestampSpread: number;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
type TimestampSegmentProps = {
|
|
|
|
|
isFirstSegmentInMinimap: boolean;
|
|
|
|
|
isLastSegmentInMinimap: boolean;
|
|
|
|
|
timestamp: Date;
|
|
|
|
|
timestampSpread: number;
|
2025-02-10 00:13:32 +03:00
|
|
|
segmentKey: string;
|
2024-03-04 19:42:51 +03:00
|
|
|
};
|
|
|
|
|
|
|
|
|
|
export function MinimapBounds({
|
|
|
|
|
isFirstSegmentInMinimap,
|
|
|
|
|
isLastSegmentInMinimap,
|
|
|
|
|
alignedMinimapStartTime,
|
|
|
|
|
alignedMinimapEndTime,
|
|
|
|
|
firstMinimapSegmentRef,
|
2024-03-28 18:03:06 +03:00
|
|
|
dense,
|
2024-03-04 19:42:51 +03:00
|
|
|
}: MinimapSegmentProps) {
|
2024-06-02 20:00:59 +03:00
|
|
|
const { data: config } = useSWR<FrigateConfig>("config");
|
2025-04-23 00:50:21 +03:00
|
|
|
const { t } = useTranslation(["common"]);
|
|
|
|
|
const timeFormat = config?.ui.time_format === "24hour" ? "24hour" : "12hour";
|
|
|
|
|
|
|
|
|
|
const formatKey = dense
|
|
|
|
|
? `time.formattedTimestampHourMinute.${timeFormat}`
|
|
|
|
|
: `time.formattedTimestampMonthDayHourMinute.${timeFormat}`;
|
|
|
|
|
|
|
|
|
|
const formattedStartTime = useFormattedTimestamp(
|
|
|
|
|
alignedMinimapStartTime,
|
|
|
|
|
t(formatKey),
|
|
|
|
|
config?.ui.timezone,
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
const formattedEndTime = useFormattedTimestamp(
|
|
|
|
|
alignedMinimapEndTime,
|
|
|
|
|
t(formatKey),
|
|
|
|
|
config?.ui.timezone,
|
|
|
|
|
);
|
2024-06-02 20:00:59 +03:00
|
|
|
|
2024-03-04 19:42:51 +03:00
|
|
|
return (
|
|
|
|
|
<>
|
|
|
|
|
{isFirstSegmentInMinimap && (
|
|
|
|
|
<div
|
2024-05-14 18:06:44 +03:00
|
|
|
className="pointer-events-none absolute inset-0 -bottom-7 z-20 flex w-full select-none scroll-mt-8 items-center justify-center text-center text-[10px] font-medium text-primary"
|
2024-03-04 19:42:51 +03:00
|
|
|
ref={firstMinimapSegmentRef}
|
|
|
|
|
>
|
2025-04-23 00:50:21 +03:00
|
|
|
{formattedStartTime}
|
2024-03-04 19:42:51 +03:00
|
|
|
</div>
|
|
|
|
|
)}
|
|
|
|
|
|
|
|
|
|
{isLastSegmentInMinimap && (
|
2024-05-14 18:06:44 +03:00
|
|
|
<div className="pointer-events-none absolute inset-0 -top-3 z-20 flex w-full select-none items-center justify-center text-center text-[10px] font-medium text-primary">
|
2025-04-23 00:50:21 +03:00
|
|
|
{formattedEndTime}
|
2024-03-04 19:42:51 +03:00
|
|
|
</div>
|
|
|
|
|
)}
|
|
|
|
|
</>
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
export function Tick({ timestamp, timestampSpread }: TickSegmentProps) {
|
|
|
|
|
return (
|
|
|
|
|
<div className="absolute">
|
2024-05-14 18:06:44 +03:00
|
|
|
<div className="flex h-[8px] w-[12px] content-end items-end">
|
2024-03-04 19:42:51 +03:00
|
|
|
<div
|
2024-05-14 18:06:44 +03:00
|
|
|
className={`pointer-events-none h-0.5 select-none ${
|
2024-03-04 19:42:51 +03:00
|
|
|
timestamp.getMinutes() % timestampSpread === 0 &&
|
|
|
|
|
timestamp.getSeconds() === 0
|
2024-07-11 16:22:02 +03:00
|
|
|
? "w-[12px] bg-neutral_variant dark:bg-neutral"
|
2024-03-12 18:23:54 +03:00
|
|
|
: timestamp.getMinutes() % (timestampSpread == 15 ? 5 : 1) ===
|
|
|
|
|
0 && timestamp.getSeconds() === 0
|
2024-07-11 16:22:02 +03:00
|
|
|
? "w-[8px] bg-neutral" // Minor tick mark
|
|
|
|
|
: "w-[5px] bg-neutral-400 dark:bg-neutral_variant"
|
2024-03-04 19:42:51 +03:00
|
|
|
}`}
|
|
|
|
|
></div>
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
export function Timestamp({
|
|
|
|
|
isFirstSegmentInMinimap,
|
|
|
|
|
isLastSegmentInMinimap,
|
|
|
|
|
timestamp,
|
|
|
|
|
timestampSpread,
|
|
|
|
|
segmentKey,
|
|
|
|
|
}: TimestampSegmentProps) {
|
2025-04-23 00:50:21 +03:00
|
|
|
const { t } = useTranslation(["common"]);
|
2024-06-02 20:00:59 +03:00
|
|
|
const { data: config } = useSWR<FrigateConfig>("config");
|
|
|
|
|
|
2025-04-23 00:50:21 +03:00
|
|
|
const timeFormat = config?.ui.time_format === "24hour" ? "24hour" : "12hour";
|
|
|
|
|
const format = t(`time.formattedTimestampHourMinute.${timeFormat}`);
|
|
|
|
|
|
|
|
|
|
const formattedTimestamp = useFormattedTimestamp(
|
|
|
|
|
timestamp.getTime() / 1000,
|
|
|
|
|
format,
|
|
|
|
|
config?.ui.timezone,
|
|
|
|
|
);
|
2024-11-04 17:07:57 +03:00
|
|
|
|
2025-04-23 00:50:21 +03:00
|
|
|
const shouldDisplay = useMemo(() => {
|
|
|
|
|
return (
|
|
|
|
|
timestamp.getMinutes() % timestampSpread === 0 &&
|
|
|
|
|
timestamp.getSeconds() === 0
|
|
|
|
|
);
|
|
|
|
|
}, [timestamp, timestampSpread]);
|
2024-11-04 17:07:57 +03:00
|
|
|
|
2024-03-04 19:42:51 +03:00
|
|
|
return (
|
2024-05-14 18:06:44 +03:00
|
|
|
<div className="absolute left-[15px] z-10 h-[8px]">
|
2025-04-23 00:50:21 +03:00
|
|
|
{!isFirstSegmentInMinimap && !isLastSegmentInMinimap && shouldDisplay && (
|
2024-03-04 19:42:51 +03:00
|
|
|
<div
|
|
|
|
|
key={`${segmentKey}_timestamp`}
|
2024-07-11 16:22:02 +03:00
|
|
|
className="pointer-events-none select-none text-[8px] text-neutral_variant dark:text-neutral"
|
2024-03-04 19:42:51 +03:00
|
|
|
>
|
2024-11-04 17:07:57 +03:00
|
|
|
{formattedTimestamp}
|
2024-03-04 19:42:51 +03:00
|
|
|
</div>
|
|
|
|
|
)}
|
|
|
|
|
</div>
|
|
|
|
|
);
|
|
|
|
|
}
|