Add share menu to export

This commit is contained in:
Nicolas Mowen 2024-09-11 19:17:08 -06:00
parent 1b6d11a2b0
commit b813bcdb20
5 changed files with 50 additions and 24 deletions

View File

@ -3,7 +3,7 @@ import { LuTrash } from "react-icons/lu";
import { Button } from "../ui/button"; import { Button } from "../ui/button";
import { useCallback, useState } from "react"; import { useCallback, useState } from "react";
import { isDesktop } from "react-device-detect"; 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 Chip from "../indicators/Chip";
import { Skeleton } from "../ui/skeleton"; import { Skeleton } from "../ui/skeleton";
import { import {
@ -147,6 +147,19 @@ export default function ExportCard({
<div> <div>
<div className="absolute inset-0 rounded-lg bg-black bg-opacity-60 md:rounded-2xl" /> <div className="absolute inset-0 rounded-lg bg-black bg-opacity-60 md:rounded-2xl" />
<div className="absolute right-1 top-1 flex items-center gap-2"> <div className="absolute right-1 top-1 flex items-center gap-2">
{!exportedRecording.in_progress && (
<Chip
className="cursor-pointer rounded-md bg-gray-500 bg-gradient-to-br from-gray-400 to-gray-500"
onClick={() =>
navigator.share({
url: `${baseUrl}exports?id=${exportedRecording.id}`,
title: exportedRecording.name.replaceAll("_", " "),
})
}
>
<FaShareAlt className="size-4 text-white" />
</Chip>
)}
{!exportedRecording.in_progress && ( {!exportedRecording.in_progress && (
<a <a
download download

View File

@ -1,5 +1,5 @@
import { useCallback, useEffect, useMemo } from "react"; import { useCallback, useEffect, useMemo } from "react";
import { useLocation, useNavigate } from "react-router-dom"; import { useLocation, useNavigate, useSearchParams } from "react-router-dom";
import { usePersistence } from "./use-persistence"; import { usePersistence } from "./use-persistence";
export function useOverlayState<S>( export function useOverlayState<S>(
@ -103,33 +103,29 @@ export function useHashState<S extends string>(): [
export function useSearchEffect( export function useSearchEffect(
key: string, key: string,
callback: (value: string) => void, callback: (value: string) => boolean,
) { ) {
const location = useLocation(); const [searchParams, setSearchParams] = useSearchParams();
const param = useMemo(() => { const param = useMemo(() => {
if (!location || !location.search || location.search.length == 0) { const param = searchParams.get(key);
if (!param) {
return undefined; return undefined;
} }
const params = location.search.substring(1).split("&"); return [key, decodeURIComponent(param)];
}, [searchParams, key]);
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]);
useEffect(() => { useEffect(() => {
if (!param) { if (!param) {
return; return;
} }
callback(param[1]); const remove = callback(param[1]);
}, [param, callback]);
if (remove) {
setSearchParams();
}
}, [param, callback, setSearchParams]);
} }

View File

@ -54,6 +54,8 @@ export default function Events() {
} }
}) })
.catch(() => {}); .catch(() => {});
return true;
}); });
const [startTime, setStartTime] = useState<number>(); const [startTime, setStartTime] = useState<number>();
@ -83,7 +85,11 @@ export default function Events() {
cameras: group.cameras, cameras: group.cameras,
}); });
} }
return true;
} }
return false;
}); });
const onUpdateFilter = useCallback( const onUpdateFilter = useCallback(

View File

@ -43,6 +43,7 @@ export default function Explore() {
setSearch(`similarity:${similarityId}`); setSearch(`similarity:${similarityId}`);
// @ts-expect-error we want to clear this // @ts-expect-error we want to clear this
setSearchFilter({ ...searchFilter, similarity_search_id: undefined }); setSearchFilter({ ...searchFilter, similarity_search_id: undefined });
return false;
}); });
useEffect(() => { useEffect(() => {

View File

@ -13,6 +13,7 @@ import { Button } from "@/components/ui/button";
import { Dialog, DialogContent, DialogTitle } from "@/components/ui/dialog"; import { Dialog, DialogContent, DialogTitle } from "@/components/ui/dialog";
import { Input } from "@/components/ui/input"; import { Input } from "@/components/ui/input";
import { Toaster } from "@/components/ui/sonner"; import { Toaster } from "@/components/ui/sonner";
import { useSearchEffect } from "@/hooks/use-overlay-state";
import { cn } from "@/lib/utils"; import { cn } from "@/lib/utils";
import { DeleteClipType, Export } from "@/types/export"; import { DeleteClipType, Export } from "@/types/export";
import axios from "axios"; import axios from "axios";
@ -46,6 +47,20 @@ function Exports() {
); );
}, [exports, search]); }, [exports, search]);
// Viewing
const [selected, setSelected] = useState<Export>();
const [selectedAspect, setSelectedAspect] = useState(0.0);
useSearchEffect("id", (id) => {
if (!exports) {
return false;
}
setSelected(exports.find((exp) => exp.id == id));
return true;
});
// Deleting // Deleting
const [deleteClip, setDeleteClip] = useState<DeleteClipType | undefined>(); const [deleteClip, setDeleteClip] = useState<DeleteClipType | undefined>();
@ -91,11 +106,6 @@ function Exports() {
[mutate], [mutate],
); );
// Viewing
const [selected, setSelected] = useState<Export>();
const [selectedAspect, setSelectedAspect] = useState(0.0);
return ( return (
<div className="flex size-full flex-col gap-2 overflow-hidden px-1 pt-2 md:p-2"> <div className="flex size-full flex-col gap-2 overflow-hidden px-1 pt-2 md:p-2">
<Toaster closeButton={true} /> <Toaster closeButton={true} />