diff --git a/web/src/components/camera/DebugCameraImage.tsx b/web/src/components/camera/DebugCameraImage.tsx index bc3b6a8c3..924eb86a5 100644 --- a/web/src/components/camera/DebugCameraImage.tsx +++ b/web/src/components/camera/DebugCameraImage.tsx @@ -5,7 +5,7 @@ import { Button } from "../ui/button"; import { LuSettings } from "react-icons/lu"; import { useCallback, useMemo, useState } from "react"; import { Card, CardContent, CardHeader, CardTitle } from "../ui/card"; -import { usePersistence } from "@/hooks/use-persistence"; +import { useUserPersistence } from "@/hooks/use-user-persistence"; import AutoUpdatingCameraImage from "./AutoUpdatingCameraImage"; import { useTranslation } from "react-i18next"; @@ -24,7 +24,7 @@ export default function DebugCameraImage({ }: DebugCameraImageProps) { const { t } = useTranslation(["components/camera"]); const [showSettings, setShowSettings] = useState(false); - const [options, setOptions] = usePersistence( + const [options, setOptions] = useUserPersistence( `${cameraConfig?.name}-feed`, emptyObject, ); diff --git a/web/src/components/card/AnimatedEventCard.tsx b/web/src/components/card/AnimatedEventCard.tsx index b0773eba0..a67dd8305 100644 --- a/web/src/components/card/AnimatedEventCard.tsx +++ b/web/src/components/card/AnimatedEventCard.tsx @@ -13,7 +13,7 @@ import { baseUrl } from "@/api/baseUrl"; import { VideoPreview } from "../preview/ScrubbablePreview"; import { useApiHost } from "@/api"; import { isDesktop, isSafari } from "react-device-detect"; -import { usePersistence } from "@/hooks/use-persistence"; +import { useUserPersistence } from "@/hooks/use-user-persistence"; import { Skeleton } from "../ui/skeleton"; import { Button } from "../ui/button"; import { FaCircleCheck } from "react-icons/fa6"; @@ -112,7 +112,7 @@ export function AnimatedEventCard({ // image behavior - const [alertVideos, _, alertVideosLoaded] = usePersistence( + const [alertVideos, _, alertVideosLoaded] = useUserPersistence( "alertVideos", true, ); diff --git a/web/src/components/filter/CameraGroupSelector.tsx b/web/src/components/filter/CameraGroupSelector.tsx index 295477b2a..14845fdb8 100644 --- a/web/src/components/filter/CameraGroupSelector.tsx +++ b/web/src/components/filter/CameraGroupSelector.tsx @@ -7,7 +7,6 @@ import { import { isDesktop, isMobile } from "react-device-detect"; import useSWR from "swr"; import { MdHome } from "react-icons/md"; -import { usePersistedOverlayState } from "@/hooks/use-overlay-state"; import { Button, buttonVariants } from "../ui/button"; import { useCallback, useEffect, useMemo, useState } from "react"; import { Tooltip, TooltipContent, TooltipTrigger } from "../ui/tooltip"; @@ -57,7 +56,7 @@ import { Toaster } from "@/components/ui/sonner"; import { toast } from "sonner"; import ActivityIndicator from "../indicators/activity-indicator"; import { ScrollArea, ScrollBar } from "../ui/scroll-area"; -import { usePersistence } from "@/hooks/use-persistence"; +import { useUserPersistence } from "@/hooks/use-user-persistence"; import { TooltipPortal } from "@radix-ui/react-tooltip"; import { cn } from "@/lib/utils"; import * as LuIcons from "react-icons/lu"; @@ -79,6 +78,7 @@ import { Trans, useTranslation } from "react-i18next"; import { CameraNameLabel } from "../camera/FriendlyNameLabel"; import { useAllowedCameras } from "@/hooks/use-allowed-cameras"; import { useIsAdmin } from "@/hooks/use-is-admin"; +import { useUserPersistedOverlayState } from "@/hooks/use-overlay-state"; type CameraGroupSelectorProps = { className?: string; @@ -109,9 +109,9 @@ export function CameraGroupSelector({ className }: CameraGroupSelectorProps) { [timeoutId], ); - // groups + // groups - use user-namespaced key for persistence to avoid cross-user conflicts - const [group, setGroup, , deleteGroup] = usePersistedOverlayState( + const [group, setGroup, , deleteGroup] = useUserPersistedOverlayState( "cameraGroup", "default" as string, ); @@ -276,7 +276,7 @@ function NewGroupDialog({ const [editState, setEditState] = useState<"none" | "add" | "edit">("none"); const [isLoading, setIsLoading] = useState(false); - const [, , , deleteGridLayout] = usePersistence( + const [, , , deleteGridLayout] = useUserPersistence( `${activeGroup}-draggable-layout`, ); diff --git a/web/src/components/input/InputWithTags.tsx b/web/src/components/input/InputWithTags.tsx index 58098743b..298537136 100755 --- a/web/src/components/input/InputWithTags.tsx +++ b/web/src/components/input/InputWithTags.tsx @@ -37,7 +37,7 @@ import { import { cn } from "@/lib/utils"; import { Tooltip, TooltipContent, TooltipTrigger } from "../ui/tooltip"; import { TooltipPortal } from "@radix-ui/react-tooltip"; -import { usePersistence } from "@/hooks/use-persistence"; +import { useUserPersistence } from "@/hooks/use-user-persistence"; import { SaveSearchDialog } from "./SaveSearchDialog"; import { DeleteSearchDialog } from "./DeleteSearchDialog"; import { @@ -128,9 +128,8 @@ export default function InputWithTags({ // TODO: search history from browser storage - const [searchHistory, setSearchHistory, searchHistoryLoaded] = usePersistence< - SavedSearchQuery[] - >("frigate-search-history"); + const [searchHistory, setSearchHistory, searchHistoryLoaded] = + useUserPersistence("frigate-search-history"); const [isSaveDialogOpen, setIsSaveDialogOpen] = useState(false); const [isDeleteDialogOpen, setIsDeleteDialogOpen] = useState(false); diff --git a/web/src/components/overlay/ReviewActivityCalendar.tsx b/web/src/components/overlay/ReviewActivityCalendar.tsx index 10617d3c9..86bc424ba 100644 --- a/web/src/components/overlay/ReviewActivityCalendar.tsx +++ b/web/src/components/overlay/ReviewActivityCalendar.tsx @@ -5,7 +5,7 @@ import { FaCircle } from "react-icons/fa"; import { getUTCOffset } from "@/utils/dateUtil"; import { type DayButtonProps, TZDate } from "react-day-picker"; import { LAST_24_HOURS_KEY } from "@/types/filter"; -import { usePersistence } from "@/hooks/use-persistence"; +import { useUserPersistence } from "@/hooks/use-user-persistence"; import { cn } from "@/lib/utils"; import { FrigateConfig } from "@/types/frigateConfig"; import useSWR from "swr"; @@ -27,7 +27,7 @@ export default function ReviewActivityCalendar({ }: ReviewActivityCalendarProps) { const { data: config } = useSWR("config"); const timezone = useTimezone(config); - const [weekStartsOn] = usePersistence("weekStartsOn", 0); + const [weekStartsOn] = useUserPersistence("weekStartsOn", 0); const disabledDates = useMemo(() => { const tomorrow = new Date(); @@ -176,7 +176,7 @@ export function TimezoneAwareCalendar({ selectedDay, onSelect, }: TimezoneAwareCalendarProps) { - const [weekStartsOn] = usePersistence("weekStartsOn", 0); + const [weekStartsOn] = useUserPersistence("weekStartsOn", 0); const timezoneOffset = useMemo( () => diff --git a/web/src/components/player/HlsVideoPlayer.tsx b/web/src/components/player/HlsVideoPlayer.tsx index 4d7068204..045b8366f 100644 --- a/web/src/components/player/HlsVideoPlayer.tsx +++ b/web/src/components/player/HlsVideoPlayer.tsx @@ -15,7 +15,7 @@ import { FrigateConfig } from "@/types/frigateConfig"; import { AxiosResponse } from "axios"; import { toast } from "sonner"; import { useOverlayState } from "@/hooks/use-overlay-state"; -import { usePersistence } from "@/hooks/use-persistence"; +import { useUserPersistence } from "@/hooks/use-user-persistence"; import { cn } from "@/lib/utils"; import { ASPECT_VERTICAL_LAYOUT, RecordingPlayerError } from "@/types/record"; import { useTranslation } from "react-i18next"; @@ -210,9 +210,9 @@ export default function HlsVideoPlayer({ const [tallCamera, setTallCamera] = useState(false); const [isPlaying, setIsPlaying] = useState(true); - const [muted, setMuted] = usePersistence("hlsPlayerMuted", true); + const [muted, setMuted] = useUserPersistence("hlsPlayerMuted", true); const [volume, setVolume] = useOverlayState("playerVolume", 1.0); - const [defaultPlaybackRate] = usePersistence("playbackRate", 1); + const [defaultPlaybackRate] = useUserPersistence("playbackRate", 1); const [playbackRate, setPlaybackRate] = useOverlayState( "playbackRate", defaultPlaybackRate ?? 1, diff --git a/web/src/components/player/MsePlayer.tsx b/web/src/components/player/MsePlayer.tsx index b7d1aab01..e643530a4 100644 --- a/web/src/components/player/MsePlayer.tsx +++ b/web/src/components/player/MsePlayer.tsx @@ -1,5 +1,5 @@ import { baseUrl } from "@/api/baseUrl"; -import { usePersistence } from "@/hooks/use-persistence"; +import { useUserPersistence } from "@/hooks/use-user-persistence"; import { LivePlayerError, PlayerStatsType, @@ -72,7 +72,10 @@ function MSEPlayer({ const [errorCount, setErrorCount] = useState(0); const totalBytesLoaded = useRef(0); - const [fallbackTimeout] = usePersistence("liveFallbackTimeout", 3); + const [fallbackTimeout] = useUserPersistence( + "liveFallbackTimeout", + 3, + ); const videoRef = useRef(null); const wsRef = useRef(null); diff --git a/web/src/components/timeline/DetailStream.tsx b/web/src/components/timeline/DetailStream.tsx index be01eb16e..ec2bc3b27 100644 --- a/web/src/components/timeline/DetailStream.tsx +++ b/web/src/components/timeline/DetailStream.tsx @@ -24,7 +24,7 @@ import { cn } from "@/lib/utils"; import { Tooltip, TooltipContent, TooltipTrigger } from "../ui/tooltip"; import { Link } from "react-router-dom"; import { Switch } from "@/components/ui/switch"; -import { usePersistence } from "@/hooks/use-persistence"; +import { useUserPersistence } from "@/hooks/use-user-persistence"; import { isDesktop } from "react-device-detect"; import { resolveZoneName } from "@/hooks/use-zone-friendly-name"; import { PiSlidersHorizontalBold } from "react-icons/pi"; @@ -58,7 +58,7 @@ export default function DetailStream({ const effectiveTime = currentTime - annotationOffset / 1000; const [upload, setUpload] = useState(undefined); const [controlsExpanded, setControlsExpanded] = useState(false); - const [alwaysExpandActive, setAlwaysExpandActive] = usePersistence( + const [alwaysExpandActive, setAlwaysExpandActive] = useUserPersistence( "detailStreamActiveExpanded", true, ); diff --git a/web/src/context/streaming-settings-provider.tsx b/web/src/context/streaming-settings-provider.tsx index 82558722e..bcf6da9c5 100644 --- a/web/src/context/streaming-settings-provider.tsx +++ b/web/src/context/streaming-settings-provider.tsx @@ -6,7 +6,7 @@ import { useContext, } from "react"; import { AllGroupsStreamingSettings } from "@/types/frigateConfig"; -import { usePersistence } from "@/hooks/use-persistence"; +import { useUserPersistence } from "@/hooks/use-user-persistence"; type StreamingSettingsContextType = { allGroupsStreamingSettings: AllGroupsStreamingSettings; @@ -29,7 +29,7 @@ export function StreamingSettingsProvider({ persistedGroupStreamingSettings, setPersistedGroupStreamingSettings, isPersistedStreamingSettingsLoaded, - ] = usePersistence("streaming-settings"); + ] = useUserPersistence("streaming-settings"); useEffect(() => { if (isPersistedStreamingSettingsLoaded) { diff --git a/web/src/pages/Events.tsx b/web/src/pages/Events.tsx index e9ec25d2b..a9867cf58 100644 --- a/web/src/pages/Events.tsx +++ b/web/src/pages/Events.tsx @@ -3,7 +3,7 @@ import useApiFilter from "@/hooks/use-api-filter"; import { useCameraPreviews } from "@/hooks/use-camera-previews"; import { useTimezone } from "@/hooks/use-date-utils"; import { useOverlayState, useSearchEffect } from "@/hooks/use-overlay-state"; -import { usePersistence } from "@/hooks/use-persistence"; +import { useUserPersistence } from "@/hooks/use-user-persistence"; import { FrigateConfig } from "@/types/frigateConfig"; import { RecordingStartingPoint } from "@/types/record"; import { @@ -42,7 +42,10 @@ export default function Events() { "alert", ); - const [showReviewed, setShowReviewed] = usePersistence("showReviewed", false); + const [showReviewed, setShowReviewed] = useUserPersistence( + "showReviewed", + false, + ); const [recording, setRecording] = useOverlayState( "recording", diff --git a/web/src/pages/Explore.tsx b/web/src/pages/Explore.tsx index 9acc066ba..53ebd0401 100644 --- a/web/src/pages/Explore.tsx +++ b/web/src/pages/Explore.tsx @@ -7,7 +7,7 @@ import ActivityIndicator from "@/components/indicators/activity-indicator"; import AnimatedCircularProgressBar from "@/components/ui/circular-progress-bar"; import { useApiFilterArgs } from "@/hooks/use-api-filter"; import { useTimezone } from "@/hooks/use-date-utils"; -import { usePersistence } from "@/hooks/use-persistence"; +import { useUserPersistence } from "@/hooks/use-user-persistence"; import { FrigateConfig } from "@/types/frigateConfig"; import { SearchFilter, SearchQuery, SearchResult } from "@/types/search"; import { ModelState } from "@/types/ws"; @@ -47,7 +47,10 @@ export default function Explore() { // grid - const [columnCount, setColumnCount] = usePersistence("exploreGridColumns", 4); + const [columnCount, setColumnCount] = useUserPersistence( + "exploreGridColumns", + 4, + ); const gridColumns = useMemo(() => { if (isMobileOnly) { return 2; @@ -57,7 +60,7 @@ export default function Explore() { // default layout - const [defaultView, setDefaultView, defaultViewLoaded] = usePersistence( + const [defaultView, setDefaultView, defaultViewLoaded] = useUserPersistence( "exploreDefaultView", "summary", ); diff --git a/web/src/pages/Live.tsx b/web/src/pages/Live.tsx index 18ec7f469..a8777aec7 100644 --- a/web/src/pages/Live.tsx +++ b/web/src/pages/Live.tsx @@ -1,10 +1,7 @@ import { useFullscreen } from "@/hooks/use-fullscreen"; import useKeyboardListener from "@/hooks/use-keyboard-listener"; -import { - useHashState, - usePersistedOverlayState, - useSearchEffect, -} from "@/hooks/use-overlay-state"; +import { useHashState, useSearchEffect } from "@/hooks/use-overlay-state"; +import { useUserPersistedOverlayState } from "@/hooks/use-overlay-state"; import { FrigateConfig } from "@/types/frigateConfig"; import LiveBirdseyeView from "@/views/live/LiveBirdseyeView"; import LiveCameraView from "@/views/live/LiveCameraView"; @@ -24,7 +21,7 @@ function Live() { // selection const [selectedCameraName, setSelectedCameraName] = useHashState(); - const [cameraGroup, setCameraGroup, loaded, ,] = usePersistedOverlayState( + const [cameraGroup, setCameraGroup, loaded] = useUserPersistedOverlayState( "cameraGroup", "default" as string, ); diff --git a/web/src/views/live/DraggableGridLayout.tsx b/web/src/views/live/DraggableGridLayout.tsx index 6b1985bd7..1370bc93b 100644 --- a/web/src/views/live/DraggableGridLayout.tsx +++ b/web/src/views/live/DraggableGridLayout.tsx @@ -1,4 +1,4 @@ -import { usePersistence } from "@/hooks/use-persistence"; +import { useUserPersistence } from "@/hooks/use-user-persistence"; import { AllGroupsStreamingSettings, BirdseyeConfig, @@ -40,7 +40,7 @@ import { IoClose } from "react-icons/io5"; import { LuLayoutDashboard, LuPencil } from "react-icons/lu"; import { cn } from "@/lib/utils"; import { EditGroupDialog } from "@/components/filter/CameraGroupSelector"; -import { usePersistedOverlayState } from "@/hooks/use-overlay-state"; +import { useUserPersistedOverlayState } from "@/hooks/use-overlay-state"; import { FaCompress, FaExpand } from "react-icons/fa"; import { Tooltip, @@ -102,8 +102,8 @@ export default function DraggableGridLayout({ // preferred live modes per camera - const [globalAutoLive] = usePersistence("autoLiveView", true); - const [displayCameraNames] = usePersistence("displayCameraNames", false); + const [globalAutoLive] = useUserPersistence("autoLiveView", true); + const [displayCameraNames] = useUserPersistence("displayCameraNames", false); const { allGroupsStreamingSettings, setAllGroupsStreamingSettings } = useStreamingSettings(); @@ -118,11 +118,14 @@ export default function DraggableGridLayout({ const ResponsiveGridLayout = useMemo(() => WidthProvider(Responsive), []); - const [gridLayout, setGridLayout, isGridLayoutLoaded] = usePersistence< + const [gridLayout, setGridLayout, isGridLayoutLoaded] = useUserPersistence< Layout[] >(`${cameraGroup}-draggable-layout`); - const [group] = usePersistedOverlayState("cameraGroup", "default" as string); + const [group] = useUserPersistedOverlayState( + "cameraGroup", + "default" as string, + ); const groups = useMemo(() => { if (!config) { diff --git a/web/src/views/live/LiveCameraView.tsx b/web/src/views/live/LiveCameraView.tsx index ada72bee3..a6b0beb4b 100644 --- a/web/src/views/live/LiveCameraView.tsx +++ b/web/src/views/live/LiveCameraView.tsx @@ -101,7 +101,7 @@ import { SelectTrigger, SelectValue, } from "@/components/ui/select"; -import { usePersistence } from "@/hooks/use-persistence"; +import { useUserPersistence } from "@/hooks/use-user-persistence"; import { Label } from "@/components/ui/label"; import { Switch } from "@/components/ui/switch"; import axios from "axios"; @@ -146,7 +146,7 @@ export default function LiveCameraView({ // supported features - const [streamName, setStreamName] = usePersistence( + const [streamName, setStreamName] = useUserPersistence( `${camera.name}-stream`, Object.values(camera.live.streams)[0], ); @@ -279,7 +279,7 @@ export default function LiveCameraView({ const [pip, setPip] = useState(false); const [lowBandwidth, setLowBandwidth] = useState(false); - const [playInBackground, setPlayInBackground] = usePersistence( + const [playInBackground, setPlayInBackground] = useUserPersistence( `${camera.name}-background-play`, false, ); diff --git a/web/src/views/live/LiveDashboardView.tsx b/web/src/views/live/LiveDashboardView.tsx index dcaedc87a..50179fd9b 100644 --- a/web/src/views/live/LiveDashboardView.tsx +++ b/web/src/views/live/LiveDashboardView.tsx @@ -13,7 +13,7 @@ import { TooltipProvider, TooltipTrigger, } from "@/components/ui/tooltip"; -import { usePersistence } from "@/hooks/use-persistence"; +import { useUserPersistence } from "@/hooks/use-user-persistence"; import { AllGroupsStreamingSettings, CameraConfig, @@ -78,7 +78,7 @@ export default function LiveDashboardView({ // layout - const [mobileLayout, setMobileLayout] = usePersistence<"grid" | "list">( + const [mobileLayout, setMobileLayout] = useUserPersistence<"grid" | "list">( "live-layout", isDesktop ? "grid" : "list", ); @@ -211,8 +211,8 @@ export default function LiveDashboardView({ }; }, []); - const [globalAutoLive] = usePersistence("autoLiveView", true); - const [displayCameraNames] = usePersistence("displayCameraNames", false); + const [globalAutoLive] = useUserPersistence("autoLiveView", true); + const [displayCameraNames] = useUserPersistence("displayCameraNames", false); const { allGroupsStreamingSettings, setAllGroupsStreamingSettings } = useStreamingSettings(); diff --git a/web/src/views/settings/ObjectSettingsView.tsx b/web/src/views/settings/ObjectSettingsView.tsx index 53ad85efa..836cb3cc3 100644 --- a/web/src/views/settings/ObjectSettingsView.tsx +++ b/web/src/views/settings/ObjectSettingsView.tsx @@ -7,7 +7,7 @@ import { Label } from "@/components/ui/label"; import useSWR from "swr"; import Heading from "@/components/ui/heading"; import { Switch } from "@/components/ui/switch"; -import { usePersistence } from "@/hooks/use-persistence"; +import { useUserPersistence } from "@/hooks/use-user-persistence"; import { Skeleton } from "@/components/ui/skeleton"; import { useCameraActivity } from "@/hooks/use-camera-activity"; import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/components/ui/tabs"; @@ -104,7 +104,7 @@ export default function ObjectSettingsView({ }, ]; - const [options, setOptions, optionsLoaded] = usePersistence( + const [options, setOptions, optionsLoaded] = useUserPersistence( `${selectedCamera}-feed`, emptyObject, ); diff --git a/web/src/views/settings/UiSettingsView.tsx b/web/src/views/settings/UiSettingsView.tsx index 34df0ddc8..a2293685e 100644 --- a/web/src/views/settings/UiSettingsView.tsx +++ b/web/src/views/settings/UiSettingsView.tsx @@ -9,7 +9,7 @@ import { Button } from "../../components/ui/button"; import useSWR from "swr"; import { FrigateConfig } from "@/types/frigateConfig"; import { del as delData } from "idb-keyval"; -import { usePersistence } from "@/hooks/use-persistence"; +import { useUserPersistence } from "@/hooks/use-user-persistence"; import { isSafari } from "react-device-detect"; import { Select, @@ -91,15 +91,15 @@ export default function UiSettingsView() { // settings - const [autoLive, setAutoLive] = usePersistence("autoLiveView", true); - const [cameraNames, setCameraName] = usePersistence( + const [autoLive, setAutoLive] = useUserPersistence("autoLiveView", true); + const [cameraNames, setCameraName] = useUserPersistence( "displayCameraNames", false, ); - const [playbackRate, setPlaybackRate] = usePersistence("playbackRate", 1); - const [weekStartsOn, setWeekStartsOn] = usePersistence("weekStartsOn", 0); - const [alertVideos, setAlertVideos] = usePersistence("alertVideos", true); - const [fallbackTimeout, setFallbackTimeout] = usePersistence( + const [playbackRate, setPlaybackRate] = useUserPersistence("playbackRate", 1); + const [weekStartsOn, setWeekStartsOn] = useUserPersistence("weekStartsOn", 0); + const [alertVideos, setAlertVideos] = useUserPersistence("alertVideos", true); + const [fallbackTimeout, setFallbackTimeout] = useUserPersistence( "liveFallbackTimeout", 3, );