diff --git a/web/src/components/card/ExportCard.tsx b/web/src/components/card/ExportCard.tsx index 1ad98be0d..5d6e64865 100644 --- a/web/src/components/card/ExportCard.tsx +++ b/web/src/components/card/ExportCard.tsx @@ -3,7 +3,7 @@ import { LuTrash } from "react-icons/lu"; import { Button } from "../ui/button"; import { useCallback, useState } from "react"; import { isDesktop } from "react-device-detect"; -import { FaDownload, FaPlay } from "react-icons/fa"; +import { FaDownload, FaPlay, FaShare, FaShareAlt } from "react-icons/fa"; import Chip from "../indicators/Chip"; import { Skeleton } from "../ui/skeleton"; import { @@ -147,6 +147,19 @@ export default function ExportCard({
+ {!exportedRecording.in_progress && ( + + navigator.share({ + url: `${baseUrl}exports?id=${exportedRecording.id}`, + title: exportedRecording.name.replaceAll("_", " "), + }) + } + > + + + )} {!exportedRecording.in_progress && ( ( @@ -103,33 +103,29 @@ export function useHashState(): [ export function useSearchEffect( key: string, - callback: (value: string) => void, + callback: (value: string) => boolean, ) { - const location = useLocation(); + const [searchParams, setSearchParams] = useSearchParams(); const param = useMemo(() => { - if (!location || !location.search || location.search.length == 0) { + const param = searchParams.get(key); + + if (!param) { return undefined; } - const params = location.search.substring(1).split("&"); - - const foundParam = params - .find((p) => p.includes("=") && p.split("=")[0] == key) - ?.split("="); - - if (foundParam && foundParam.length === 2) { - return [foundParam[0], decodeURIComponent(foundParam[1])]; - } - - return undefined; - }, [location, key]); + return [key, decodeURIComponent(param)]; + }, [searchParams, key]); useEffect(() => { if (!param) { return; } - callback(param[1]); - }, [param, callback]); + const remove = callback(param[1]); + + if (remove) { + setSearchParams(); + } + }, [param, callback, setSearchParams]); } diff --git a/web/src/pages/Events.tsx b/web/src/pages/Events.tsx index 68e5c9adc..a8164b8dd 100644 --- a/web/src/pages/Events.tsx +++ b/web/src/pages/Events.tsx @@ -54,6 +54,8 @@ export default function Events() { } }) .catch(() => {}); + + return true; }); const [startTime, setStartTime] = useState(); @@ -83,7 +85,11 @@ export default function Events() { cameras: group.cameras, }); } + + return true; } + + return false; }); const onUpdateFilter = useCallback( diff --git a/web/src/pages/Explore.tsx b/web/src/pages/Explore.tsx index 750c75fde..f68bb5068 100644 --- a/web/src/pages/Explore.tsx +++ b/web/src/pages/Explore.tsx @@ -43,6 +43,7 @@ export default function Explore() { setSearch(`similarity:${similarityId}`); // @ts-expect-error we want to clear this setSearchFilter({ ...searchFilter, similarity_search_id: undefined }); + return false; }); useEffect(() => { diff --git a/web/src/pages/Exports.tsx b/web/src/pages/Exports.tsx index 451d52052..a4659551b 100644 --- a/web/src/pages/Exports.tsx +++ b/web/src/pages/Exports.tsx @@ -13,6 +13,7 @@ import { Button } from "@/components/ui/button"; import { Dialog, DialogContent, DialogTitle } from "@/components/ui/dialog"; import { Input } from "@/components/ui/input"; import { Toaster } from "@/components/ui/sonner"; +import { useSearchEffect } from "@/hooks/use-overlay-state"; import { cn } from "@/lib/utils"; import { DeleteClipType, Export } from "@/types/export"; import axios from "axios"; @@ -46,6 +47,20 @@ function Exports() { ); }, [exports, search]); + // Viewing + + const [selected, setSelected] = useState(); + const [selectedAspect, setSelectedAspect] = useState(0.0); + + useSearchEffect("id", (id) => { + if (!exports) { + return false; + } + + setSelected(exports.find((exp) => exp.id == id)); + return true; + }); + // Deleting const [deleteClip, setDeleteClip] = useState(); @@ -91,11 +106,6 @@ function Exports() { [mutate], ); - // Viewing - - const [selected, setSelected] = useState(); - const [selectedAspect, setSelectedAspect] = useState(0.0); - return (