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 (