diff --git a/web/public/locales/en/common.json b/web/public/locales/en/common.json index 907ff6d36..37566117a 100644 --- a/web/public/locales/en/common.json +++ b/web/public/locales/en/common.json @@ -131,8 +131,6 @@ "close": "Close", "copy": "Copy", "copiedToClipboard": "Copied to clipboard", - "shareTimestamp": "Share Timestamp", - "shareTimestampUrl": "Share Timestamp URL", "back": "Back", "history": "History", "fullscreen": "Fullscreen", diff --git a/web/public/locales/en/components/dialog.json b/web/public/locales/en/components/dialog.json index b01745e43..767dc7dcf 100644 --- a/web/public/locales/en/components/dialog.json +++ b/web/public/locales/en/components/dialog.json @@ -100,11 +100,13 @@ }, "recording": { "shareTimestamp": { + "label": "Share Timestamp", "title": "Share Review Timestamp", "description": "Share the current player position or choose a custom timestamp.", "current": "Current Player Timestamp", "custom": "Custom Timestamp", "customDescription": "Pick a specific point in time to share.", + "button": "Share Timestamp URL", "shareTitle": "Frigate Review Timestamp: {{camera}}" }, "confirmDelete": { diff --git a/web/src/components/overlay/ActionsDropdown.tsx b/web/src/components/overlay/ActionsDropdown.tsx index 0b71038b1..7f841be4f 100644 --- a/web/src/components/overlay/ActionsDropdown.tsx +++ b/web/src/components/overlay/ActionsDropdown.tsx @@ -40,7 +40,7 @@ export default function ActionsDropdown({ {t("menu.export", { ns: "common" })} - {t("button.shareTimestamp", { ns: "common" })} + {t("recording.shareTimestamp.label", { ns: "components/dialog" })} {t("title", { ns: "views/replay" })} diff --git a/web/src/components/overlay/MobileReviewSettingsDrawer.tsx b/web/src/components/overlay/MobileReviewSettingsDrawer.tsx index 24b09e59f..6df197e47 100644 --- a/web/src/components/overlay/MobileReviewSettingsDrawer.tsx +++ b/web/src/components/overlay/MobileReviewSettingsDrawer.tsx @@ -26,6 +26,7 @@ import SaveExportOverlay from "./SaveExportOverlay"; import { isIOS, isMobile } from "react-device-detect"; import { useTranslation } from "react-i18next"; import { useNavigate } from "react-router-dom"; +import { ShareTimestampContent } from "./ShareTimestampDialog"; type DrawerMode = | "none" @@ -70,7 +71,7 @@ type MobileReviewSettingsDrawerProps = { debugReplayRange?: TimeRange; setDebugReplayMode?: (mode: ExportMode) => void; setDebugReplayRange?: (range: TimeRange | undefined) => void; - onShareTimestampClick?: () => void; + onShareTimestamp?: (timestamp: number) => void; onUpdateFilter: (filter: ReviewFilter) => void; setRange: (range: TimeRange | undefined) => void; setMode: (mode: ExportMode) => void; @@ -94,7 +95,7 @@ export default function MobileReviewSettingsDrawer({ debugReplayRange, setDebugReplayMode = () => {}, setDebugReplayRange = () => {}, - onShareTimestampClick = () => {}, + onShareTimestamp = () => {}, onUpdateFilter, setRange, setMode, @@ -112,6 +113,15 @@ export default function MobileReviewSettingsDrawer({ "1" | "5" | "custom" | "timeline" >("1"); const [isDebugReplayStarting, setIsDebugReplayStarting] = useState(false); + const [selectedShareOption, setSelectedShareOption] = useState< + "current" | "custom" + >("current"); + const [shareTimestampAtOpen, setShareTimestampAtOpen] = useState( + Math.floor(currentTime), + ); + const [customShareTimestamp, setCustomShareTimestamp] = useState( + Math.floor(currentTime), + ); // exports @@ -284,14 +294,22 @@ export default function MobileReviewSettingsDrawer({ {features.includes("share-timestamp") && ( { - setDrawerMode("none"); - onShareTimestampClick(); + const initialTimestamp = Math.floor(currentTime); + + setShareTimestampAtOpen(initialTimestamp); + setCustomShareTimestamp(initialTimestamp); + setSelectedShareOption("current"); + setDrawerMode("share-timestamp"); }} > - {t("button.shareTimestamp", { ns: "common" })} + {t("recording.shareTimestamp.label", { + ns: "components/dialog", + })} )} {features.includes("calendar") && ( @@ -496,6 +514,34 @@ export default function MobileReviewSettingsDrawer({ }} /> ); + } else if (drawerMode == "share-timestamp") { + content = ( + + + setDrawerMode("select")} + > + {t("button.back", { ns: "common" })} + + + {t("recording.shareTimestamp.title", { ns: "components/dialog" })} + + + { + onShareTimestamp(timestamp); + setDrawerMode("none"); + }} + onCancel={() => setDrawerMode("select")} + /> + + ); } return ( diff --git a/web/src/components/overlay/ShareTimestampDialog.tsx b/web/src/components/overlay/ShareTimestampDialog.tsx index 77f1f7ce0..80d713a95 100644 --- a/web/src/components/overlay/ShareTimestampDialog.tsx +++ b/web/src/components/overlay/ShareTimestampDialog.tsx @@ -3,6 +3,7 @@ import { Dialog, DialogContent, DialogDescription, + DialogFooter, DialogHeader, DialogTitle, } from "@/components/ui/dialog"; @@ -22,7 +23,6 @@ import useSWR from "swr"; import { TimezoneAwareCalendar } from "./ReviewActivityCalendar"; import { FaCalendarAlt } from "react-icons/fa"; import { isDesktop, isIOS, isMobile } from "react-device-detect"; -import { LuShare2 } from "react-icons/lu"; import { useTranslation } from "react-i18next"; type ShareTimestampDialogProps = { @@ -42,15 +42,19 @@ export default function ShareTimestampDialog({ const [selectedOption, setSelectedOption] = useState<"current" | "custom">( "current", ); - const [customTimestamp, setCustomTimestamp] = useState( + const [openedCurrentTime, setOpenedCurrentTime] = useState( Math.floor(currentTime), ); + const [customTimestamp, setCustomTimestamp] = useState(openedCurrentTime); const handleOpenChange = useCallback( (nextOpen: boolean) => { if (nextOpen) { + const initialTimestamp = Math.floor(currentTime); + + setOpenedCurrentTime(initialTimestamp); setSelectedOption("current"); - setCustomTimestamp(Math.floor(currentTime)); + setCustomTimestamp(initialTimestamp); } onOpenChange(nextOpen); @@ -60,7 +64,7 @@ export default function ShareTimestampDialog({ const content = ( void; onShareTimestamp: (timestamp: number) => void; + onCancel?: () => void; }; -function ShareTimestampContent({ +export function ShareTimestampContent({ currentTime, selectedOption, setSelectedOption, customTimestamp, setCustomTimestamp, onShareTimestamp, + onCancel, }: Readonly) { const { t } = useTranslation(["common", "components/dialog"]); const { data: config } = useSWR("config"); @@ -191,16 +197,28 @@ function ShareTimestampContent({ - + + {onCancel && ( + + {t("button.cancel", { ns: "common" })} + + )} onShareTimestamp(Math.floor(selectedTimestamp))} > - {t("button.shareTimestampUrl", { ns: "common" })} - + {t("recording.shareTimestamp.button", { ns: "components/dialog" })} - + ); } @@ -298,7 +316,7 @@ function CustomTimestampSelector({ {formattedTimestamp} - + ("none"); const [debugReplayRange, setDebugReplayRange] = useState(); const [shareTimestampOpen, setShareTimestampOpen] = useState(false); + const [shareTimestampAtOpen, setShareTimestampAtOpen] = useState( + Math.floor(startTime), + ); // move to next clip @@ -686,15 +689,19 @@ export function RecordingView({ setMotionOnly={() => {}} /> )} - + {isDesktop && ( + + )} {isDesktop && ( { + setShareTimestampAtOpen(Math.floor(currentTime)); setShareTimestampOpen(true); }} onDebugReplayClick={() => { @@ -776,9 +783,7 @@ export function RecordingView({ mainControllerRef.current?.pause(); } }} - onShareTimestampClick={() => { - setShareTimestampOpen(true); - }} + onShareTimestamp={onShareReviewLink} onUpdateFilter={updateFilter} setRange={setExportRange} setMode={setExportMode}