mirror of
https://github.com/blakeblackshear/frigate.git
synced 2026-02-07 11:45:24 +03:00
Cleanup
This commit is contained in:
parent
9bc67e9ab8
commit
70d61832fe
@ -39,7 +39,7 @@ export function Dashboard() {
|
|||||||
|
|
||||||
{config && (
|
{config && (
|
||||||
<div>
|
<div>
|
||||||
<div className="grid gap-2 sm:grid-cols-2 md:grid-cols-3 lg:grid-cols-4">
|
<div className="grid gap-2 sm:grid-cols-2 lg:grid-cols-3 xl:grid-cols-4">
|
||||||
{sortedCameras.map((camera) => {
|
{sortedCameras.map((camera) => {
|
||||||
return <Camera key={camera.name} camera={camera} />;
|
return <Camera key={camera.name} camera={camera} />;
|
||||||
})}
|
})}
|
||||||
|
|||||||
@ -26,6 +26,14 @@ function Live() {
|
|||||||
const cameraConfig = useMemo(() => {
|
const cameraConfig = useMemo(() => {
|
||||||
return config?.cameras[camera];
|
return config?.cameras[camera];
|
||||||
}, [camera, config]);
|
}, [camera, config]);
|
||||||
|
const sortedCameras = useMemo(() => {
|
||||||
|
if (!config) {
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
|
||||||
|
return Object.values(config.cameras)
|
||||||
|
.sort((aConf, bConf) => aConf.ui.order - bConf.ui.order);
|
||||||
|
}, [config]);
|
||||||
const restreamEnabled = useMemo(() => {
|
const restreamEnabled = useMemo(() => {
|
||||||
return (
|
return (
|
||||||
config &&
|
config &&
|
||||||
@ -66,7 +74,7 @@ function Live() {
|
|||||||
<DropdownMenuLabel>Select A Camera</DropdownMenuLabel>
|
<DropdownMenuLabel>Select A Camera</DropdownMenuLabel>
|
||||||
<DropdownMenuSeparator />
|
<DropdownMenuSeparator />
|
||||||
<DropdownMenuRadioGroup value={camera} onValueChange={setCamera}>
|
<DropdownMenuRadioGroup value={camera} onValueChange={setCamera}>
|
||||||
{Object.keys(config?.cameras || {}).map((item) => (
|
{Object.keys(sortedCameras).map((item) => (
|
||||||
<DropdownMenuRadioItem
|
<DropdownMenuRadioItem
|
||||||
className="capitalize"
|
className="capitalize"
|
||||||
key={item}
|
key={item}
|
||||||
|
|||||||
@ -6,6 +6,8 @@ export interface UiConfig {
|
|||||||
strftime_fmt?: string;
|
strftime_fmt?: string;
|
||||||
live_mode?: string;
|
live_mode?: string;
|
||||||
use_experimental?: boolean;
|
use_experimental?: boolean;
|
||||||
|
dashboard: boolean;
|
||||||
|
order: number;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface CameraConfig {
|
export interface CameraConfig {
|
||||||
|
|||||||
@ -1,6 +1,5 @@
|
|||||||
import strftime from 'strftime';
|
import strftime from "strftime";
|
||||||
import { fromUnixTime, intervalToDuration, formatDuration } from 'date-fns';
|
import { fromUnixTime, intervalToDuration, formatDuration } from "date-fns";
|
||||||
import { UiConfig } from "@/types/frigateConfig";
|
|
||||||
export const longToDate = (long: number): Date => new Date(long * 1000);
|
export const longToDate = (long: number): Date => new Date(long * 1000);
|
||||||
export const epochToLong = (date: number): number => date / 1000;
|
export const epochToLong = (date: number): number => date / 1000;
|
||||||
export const dateToLong = (date: Date): number => epochToLong(date.getTime());
|
export const dateToLong = (date: Date): number => epochToLong(date.getTime());
|
||||||
@ -40,23 +39,45 @@ export const getNowYesterdayInLong = (): number => {
|
|||||||
// only used as a fallback if the browser does not support dateStyle/timeStyle in Intl.DateTimeFormat
|
// only used as a fallback if the browser does not support dateStyle/timeStyle in Intl.DateTimeFormat
|
||||||
const formatMap: {
|
const formatMap: {
|
||||||
[k: string]: {
|
[k: string]: {
|
||||||
date: { year: 'numeric' | '2-digit'; month: 'long' | 'short' | '2-digit'; day: 'numeric' | '2-digit' };
|
date: {
|
||||||
time: { hour: 'numeric'; minute: 'numeric'; second?: 'numeric'; timeZoneName?: 'short' | 'long' };
|
year: "numeric" | "2-digit";
|
||||||
|
month: "long" | "short" | "2-digit";
|
||||||
|
day: "numeric" | "2-digit";
|
||||||
|
};
|
||||||
|
time: {
|
||||||
|
hour: "numeric";
|
||||||
|
minute: "numeric";
|
||||||
|
second?: "numeric";
|
||||||
|
timeZoneName?: "short" | "long";
|
||||||
|
};
|
||||||
};
|
};
|
||||||
} = {
|
} = {
|
||||||
full: {
|
full: {
|
||||||
date: { year: 'numeric', month: 'long', day: 'numeric' },
|
date: { year: "numeric", month: "long", day: "numeric" },
|
||||||
time: { hour: 'numeric', minute: 'numeric', second: 'numeric', timeZoneName: 'long' },
|
time: {
|
||||||
|
hour: "numeric",
|
||||||
|
minute: "numeric",
|
||||||
|
second: "numeric",
|
||||||
|
timeZoneName: "long",
|
||||||
|
},
|
||||||
},
|
},
|
||||||
long: {
|
long: {
|
||||||
date: { year: 'numeric', month: 'long', day: 'numeric' },
|
date: { year: "numeric", month: "long", day: "numeric" },
|
||||||
time: { hour: 'numeric', minute: 'numeric', second: 'numeric', timeZoneName: 'long' },
|
time: {
|
||||||
|
hour: "numeric",
|
||||||
|
minute: "numeric",
|
||||||
|
second: "numeric",
|
||||||
|
timeZoneName: "long",
|
||||||
|
},
|
||||||
},
|
},
|
||||||
medium: {
|
medium: {
|
||||||
date: { year: 'numeric', month: 'short', day: 'numeric' },
|
date: { year: "numeric", month: "short", day: "numeric" },
|
||||||
time: { hour: 'numeric', minute: 'numeric', second: 'numeric' },
|
time: { hour: "numeric", minute: "numeric", second: "numeric" },
|
||||||
|
},
|
||||||
|
short: {
|
||||||
|
date: { year: "2-digit", month: "2-digit", day: "2-digit" },
|
||||||
|
time: { hour: "numeric", minute: "numeric" },
|
||||||
},
|
},
|
||||||
short: { date: { year: '2-digit', month: '2-digit', day: '2-digit' }, time: { hour: 'numeric', minute: 'numeric' } },
|
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -79,11 +100,11 @@ const getResolvedTimeZone = () => {
|
|||||||
return Intl.DateTimeFormat().resolvedOptions().timeZone;
|
return Intl.DateTimeFormat().resolvedOptions().timeZone;
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
const offsetMinutes = new Date().getTimezoneOffset();
|
const offsetMinutes = new Date().getTimezoneOffset();
|
||||||
return `UTC${offsetMinutes < 0 ? '+' : '-'}${Math.abs(offsetMinutes / 60)
|
return `UTC${offsetMinutes < 0 ? "+" : "-"}${Math.abs(offsetMinutes / 60)
|
||||||
.toString()
|
.toString()
|
||||||
.padStart(2, '0')}:${Math.abs(offsetMinutes % 60)
|
.padStart(2, "0")}:${Math.abs(offsetMinutes % 60)
|
||||||
.toString()
|
.toString()
|
||||||
.padStart(2, '0')}`;
|
.padStart(2, "0")}`;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -103,11 +124,21 @@ const getResolvedTimeZone = () => {
|
|||||||
*
|
*
|
||||||
* @throws {Error} If the given unixTimestamp is not a valid number, the function will return 'Invalid time'.
|
* @throws {Error} If the given unixTimestamp is not a valid number, the function will return 'Invalid time'.
|
||||||
*/
|
*/
|
||||||
export const formatUnixTimestampToDateTime = (unixTimestamp: number, config: UiConfig): string => {
|
export const formatUnixTimestampToDateTime = (
|
||||||
const { timezone, time_format, date_style, time_style, strftime_fmt } = config;
|
unixTimestamp: number,
|
||||||
const locale = window.navigator?.language || 'en-US';
|
config: {
|
||||||
|
timezone?: string;
|
||||||
|
time_format?: "browser" | "12hour" | "24hour";
|
||||||
|
date_style?: "full" | "long" | "medium" | "short";
|
||||||
|
time_style?: "full" | "long" | "medium" | "short";
|
||||||
|
strftime_fmt?: string;
|
||||||
|
}
|
||||||
|
): string => {
|
||||||
|
const { timezone, time_format, date_style, time_style, strftime_fmt } =
|
||||||
|
config;
|
||||||
|
const locale = window.navigator?.language || "en-US";
|
||||||
if (isNaN(unixTimestamp)) {
|
if (isNaN(unixTimestamp)) {
|
||||||
return 'Invalid time';
|
return "Invalid time";
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
@ -125,7 +156,7 @@ export const formatUnixTimestampToDateTime = (unixTimestamp: number, config: UiC
|
|||||||
const options: Intl.DateTimeFormatOptions = {
|
const options: Intl.DateTimeFormatOptions = {
|
||||||
dateStyle: date_style,
|
dateStyle: date_style,
|
||||||
timeStyle: time_style,
|
timeStyle: time_style,
|
||||||
hour12: time_format !== 'browser' ? time_format == '12hour' : undefined,
|
hour12: time_format !== "browser" ? time_format == "12hour" : undefined,
|
||||||
};
|
};
|
||||||
|
|
||||||
// Only set timeZone option when resolvedTimeZone does not match UTC±HH:MM format, or when timezone is set in config
|
// Only set timeZone option when resolvedTimeZone does not match UTC±HH:MM format, or when timezone is set in config
|
||||||
@ -143,15 +174,26 @@ export const formatUnixTimestampToDateTime = (unixTimestamp: number, config: UiC
|
|||||||
// fallback if the browser does not support dateStyle/timeStyle in Intl.DateTimeFormat
|
// fallback if the browser does not support dateStyle/timeStyle in Intl.DateTimeFormat
|
||||||
// This works even tough the timezone is undefined, it will use the runtime's default time zone
|
// This works even tough the timezone is undefined, it will use the runtime's default time zone
|
||||||
if (!containsTime) {
|
if (!containsTime) {
|
||||||
const dateOptions = { ...formatMap[date_style ?? ""]?.date, timeZone: options.timeZone, hour12: options.hour12 };
|
const dateOptions = {
|
||||||
const timeOptions = { ...formatMap[time_style ?? ""]?.time, timeZone: options.timeZone, hour12: options.hour12 };
|
...formatMap[date_style ?? ""]?.date,
|
||||||
|
timeZone: options.timeZone,
|
||||||
|
hour12: options.hour12,
|
||||||
|
};
|
||||||
|
const timeOptions = {
|
||||||
|
...formatMap[time_style ?? ""]?.time,
|
||||||
|
timeZone: options.timeZone,
|
||||||
|
hour12: options.hour12,
|
||||||
|
};
|
||||||
|
|
||||||
return `${date.toLocaleDateString(locale, dateOptions)} ${date.toLocaleTimeString(locale, timeOptions)}`;
|
return `${date.toLocaleDateString(
|
||||||
|
locale,
|
||||||
|
dateOptions
|
||||||
|
)} ${date.toLocaleTimeString(locale, timeOptions)}`;
|
||||||
}
|
}
|
||||||
|
|
||||||
return formattedDateTime;
|
return formattedDateTime;
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
return 'Invalid time';
|
return "Invalid time";
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -169,28 +211,31 @@ interface DurationToken {
|
|||||||
* @param end_time: number|null - Unix timestamp for end time
|
* @param end_time: number|null - Unix timestamp for end time
|
||||||
* @returns string - duration or 'In Progress' if end time is not provided
|
* @returns string - duration or 'In Progress' if end time is not provided
|
||||||
*/
|
*/
|
||||||
export const getDurationFromTimestamps = (start_time: number, end_time: number | null): string => {
|
export const getDurationFromTimestamps = (
|
||||||
|
start_time: number,
|
||||||
|
end_time: number | null
|
||||||
|
): string => {
|
||||||
if (isNaN(start_time)) {
|
if (isNaN(start_time)) {
|
||||||
return 'Invalid start time';
|
return "Invalid start time";
|
||||||
}
|
}
|
||||||
let duration = 'In Progress';
|
let duration = "In Progress";
|
||||||
if (end_time !== null) {
|
if (end_time !== null) {
|
||||||
if (isNaN(end_time)) {
|
if (isNaN(end_time)) {
|
||||||
return 'Invalid end time';
|
return "Invalid end time";
|
||||||
}
|
}
|
||||||
const start = fromUnixTime(start_time);
|
const start = fromUnixTime(start_time);
|
||||||
const end = fromUnixTime(end_time);
|
const end = fromUnixTime(end_time);
|
||||||
const formatDistanceLocale: DurationToken = {
|
const formatDistanceLocale: DurationToken = {
|
||||||
xSeconds: '{{count}}s',
|
xSeconds: "{{count}}s",
|
||||||
xMinutes: '{{count}}m',
|
xMinutes: "{{count}}m",
|
||||||
xHours: '{{count}}h',
|
xHours: "{{count}}h",
|
||||||
};
|
};
|
||||||
const shortEnLocale = {
|
const shortEnLocale = {
|
||||||
formatDistance: (token: keyof DurationToken, count: number) =>
|
formatDistance: (token: keyof DurationToken, count: number) =>
|
||||||
formatDistanceLocale[token].replace('{{count}}', count.toString()),
|
formatDistanceLocale[token].replace("{{count}}", count.toString()),
|
||||||
};
|
};
|
||||||
duration = formatDuration(intervalToDuration({ start, end }), {
|
duration = formatDuration(intervalToDuration({ start, end }), {
|
||||||
format: ['hours', 'minutes', 'seconds'],
|
format: ["hours", "minutes", "seconds"],
|
||||||
locale: shortEnLocale,
|
locale: shortEnLocale,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@ -209,14 +254,18 @@ const getUTCOffset = (date: Date, timezone: string): number => {
|
|||||||
if (utcOffsetMatch) {
|
if (utcOffsetMatch) {
|
||||||
const hours = parseInt(utcOffsetMatch[2], 10);
|
const hours = parseInt(utcOffsetMatch[2], 10);
|
||||||
const minutes = parseInt(utcOffsetMatch[3], 10);
|
const minutes = parseInt(utcOffsetMatch[3], 10);
|
||||||
return (utcOffsetMatch[1] === '+' ? 1 : -1) * (hours * 60 + minutes);
|
return (utcOffsetMatch[1] === "+" ? 1 : -1) * (hours * 60 + minutes);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Otherwise, calculate offset using provided timezone
|
// Otherwise, calculate offset using provided timezone
|
||||||
const utcDate = new Date(date.getTime() - date.getTimezoneOffset() * 60 * 1000);
|
const utcDate = new Date(
|
||||||
|
date.getTime() - date.getTimezoneOffset() * 60 * 1000
|
||||||
|
);
|
||||||
// locale of en-CA is required for proper locale format
|
// locale of en-CA is required for proper locale format
|
||||||
let iso = utcDate.toLocaleString('en-CA', { timeZone: timezone, hour12: false }).replace(', ', 'T');
|
let iso = utcDate
|
||||||
iso += `.${utcDate.getMilliseconds().toString().padStart(3, '0')}`;
|
.toLocaleString("en-CA", { timeZone: timezone, hour12: false })
|
||||||
|
.replace(", ", "T");
|
||||||
|
iso += `.${utcDate.getMilliseconds().toString().padStart(3, "0")}`;
|
||||||
let target = new Date(`${iso}Z`);
|
let target = new Date(`${iso}Z`);
|
||||||
|
|
||||||
// safari doesn't like the default format
|
// safari doesn't like the default format
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user