diff --git a/web/public/locales/en/views/events.json b/web/public/locales/en/views/events.json index 77c626adf..87ec23fd3 100644 --- a/web/public/locales/en/views/events.json +++ b/web/public/locales/en/views/events.json @@ -18,6 +18,16 @@ "aria": "Select events", "noFoundForTimePeriod": "No events found for this time period." }, + "activity": { + "noActivitiesFound": "No activities found", + "activitiesCount_one": "{{count}} activity", + "activitiesCount_other": "{{count}} activities", + "object": "Object" + }, + "objectTrack": { + "trackedPoint": "Tracked point", + "clickToSeek": "Click to seek to this time" + }, "documentTitle": "Review - Frigate", "recordings": { "documentTitle": "Recordings - Frigate" diff --git a/web/src/components/overlay/ObjectTrackOverlay.tsx b/web/src/components/overlay/ObjectTrackOverlay.tsx index 25f9e12c4..8fbdcc049 100644 --- a/web/src/components/overlay/ObjectTrackOverlay.tsx +++ b/web/src/components/overlay/ObjectTrackOverlay.tsx @@ -10,6 +10,7 @@ import { } from "@/components/ui/tooltip"; import { TooltipPortal } from "@radix-ui/react-tooltip"; import { cn } from "@/lib/utils"; +import { useTranslation } from "react-i18next"; type ObjectTrackOverlayProps = { camera: string; @@ -32,6 +33,7 @@ export default function ObjectTrackOverlay({ onSeekToTime, objectTimeline, }: ObjectTrackOverlayProps) { + const { t } = useTranslation("views/events"); const { data: config } = useSWR("config"); const { annotationOffset } = useActivityStream(); @@ -358,10 +360,10 @@ export default function ObjectTrackOverlay({ {pos.lifecycle_item ? `${pos.lifecycle_item.class_type.replace("_", " ")} at ${new Date(pos.timestamp * 1000).toLocaleTimeString()}` - : "Tracked point"} + : t("objectTrack.trackedPoint")} {onSeekToTime && (
- Click to seek to this time + {t("objectTrack.clickToSeek")}
)}
diff --git a/web/src/components/timeline/ActivityStream.tsx b/web/src/components/timeline/ActivityStream.tsx index 704be1081..38d70802e 100644 --- a/web/src/components/timeline/ActivityStream.tsx +++ b/web/src/components/timeline/ActivityStream.tsx @@ -5,6 +5,11 @@ import { getLifecycleItemDescription } from "@/utils/lifecycleUtil"; import { useActivityStream } from "@/contexts/ActivityStreamContext"; import scrollIntoView from "scroll-into-view-if-needed"; import useUserInteraction from "@/hooks/use-user-interaction"; +import { formatUnixTimestampToDateTime } from "@/utils/dateUtil"; +import { useTranslation } from "react-i18next"; +import { FrigateConfig } from "@/types/frigateConfig"; +import useSWR from "swr"; +import ActivityIndicator from "../indicators/activity-indicator"; type ActivityStreamProps = { timelineData: ObjectLifecycleSequence[]; @@ -17,6 +22,8 @@ export default function ActivityStream({ currentTime, onSeek, }: ActivityStreamProps) { + const { data: config } = useSWR("config"); + const { t } = useTranslation("views/events"); const { selectedObjectId, annotationOffset } = useActivityStream(); const scrollRef = useRef(null); @@ -101,6 +108,10 @@ export default function ActivityStream({ setProgrammaticScroll, ]); + if (!config) { + return ; + } + return (
{filteredGroups.length === 0 ? (
- No activities found + {t("activity.noActivitiesFound")}
) : ( filteredGroups.map((group) => ( @@ -132,11 +144,18 @@ type ActivityGroupProps = { effectiveTimestamp: number; activities: ObjectLifecycleSequence[]; }; + config: FrigateConfig; isCurrent: boolean; onSeek: (timestamp: number) => void; }; -function ActivityGroup({ group, isCurrent, onSeek }: ActivityGroupProps) { +function ActivityGroup({ + group, + config, + isCurrent, + onSeek, +}: ActivityGroupProps) { + const { t } = useTranslation("views/events"); const shouldExpand = group.activities.length > 1; return ( @@ -152,11 +171,25 @@ function ActivityGroup({ group, isCurrent, onSeek }: ActivityGroupProps) {
- {new Date(group.timestamp * 1000).toLocaleTimeString()} + {formatUnixTimestampToDateTime(group.timestamp, { + timezone: config.ui.timezone, + date_format: + config.ui.time_format == "24hour" + ? t("time.formattedTimestamp.24hour", { + ns: "common", + }) + : t("time.formattedTimestamp.12hour", { + ns: "common", + }), + time_style: "medium", + date_style: "medium", + })}
{shouldExpand && (
- {group.activities.length} activities + {t("activity.activitiesCount", { + count: group.activities.length, + })}
)}
@@ -177,6 +210,7 @@ type ActivityItemProps = { }; function ActivityItem({ activity }: ActivityItemProps) { + const { t } = useTranslation("views/events"); const { selectedObjectId, setSelectedObjectId } = useActivityStream(); const handleObjectClick = (e: React.MouseEvent) => { e.stopPropagation(); @@ -202,7 +236,7 @@ function ActivityItem({ activity }: ActivityItemProps) { : "bg-muted hover:bg-muted/80" }`} > - Object + {t("activity.object")} )}