implement custom hook

This commit is contained in:
Josh Hawkins 2024-04-04 09:33:12 -05:00
parent 4f0a7d961a
commit f91116df52
3 changed files with 56 additions and 9 deletions

View File

@ -2,7 +2,7 @@ import { Button } from "../ui/button";
import { Popover, PopoverContent, PopoverTrigger } from "../ui/popover";
import useSWR from "swr";
import { CameraGroupConfig, FrigateConfig } from "@/types/frigateConfig";
import { useCallback, useEffect, useMemo, useState } from "react";
import { useCallback, useMemo, useState } from "react";
import {
DropdownMenu,
DropdownMenuContent,
@ -29,6 +29,7 @@ import ReviewActivityCalendar from "../overlay/ReviewActivityCalendar";
import MobileReviewSettingsDrawer, {
DrawerFeatures,
} from "../overlay/MobileReviewSettingsDrawer";
import useOptimisticState from "@/hooks/use-optimistic-state";
const REVIEW_FILTERS = [
"cameras",
@ -631,12 +632,10 @@ function ShowMotionOnlyButton({
motionOnly,
setMotionOnly,
}: ShowMotionOnlyButtonProps) {
const [motionOnlyButton, setMotionOnlyButton] = useState(motionOnly);
useEffect(() => {
const timeoutId = setTimeout(() => setMotionOnly(motionOnlyButton), 10);
return () => clearTimeout(timeoutId);
}, [motionOnlyButton, setMotionOnly]);
const [motionOnlyButton, setMotionOnlyButton] = useOptimisticState(
motionOnly,
setMotionOnly,
);
return (
<>

View File

@ -0,0 +1,43 @@
import { useState, useEffect, useCallback, useRef } from "react";
type OptimisticStateResult<T> = [T, (newValue: T) => void];
const useOptimisticState = <T>(
initialState: T,
setState: (newValue: T) => void,
delay: number = 20,
): OptimisticStateResult<T> => {
const [optimisticValue, setOptimisticValue] = useState<T>(initialState);
const debounceTimeout = useRef<ReturnType<typeof setTimeout> | null>(null);
const handleValueChange = useCallback(
(newValue: T) => {
// Update the optimistic value immediately
setOptimisticValue(newValue);
// Clear any pending debounce timeout
if (debounceTimeout.current) {
clearTimeout(debounceTimeout.current);
}
// Set a new debounce timeout
debounceTimeout.current = setTimeout(() => {
// Update the actual value using the provided setter function
setState(newValue);
}, delay);
},
[delay, setState],
);
useEffect(() => {
return () => {
if (debounceTimeout.current) {
clearTimeout(debounceTimeout.current);
}
};
}, []);
return [optimisticValue, handleValueChange];
};
export default useOptimisticState;

View File

@ -41,6 +41,7 @@ import { RecordingStartingPoint } from "@/types/record";
import VideoControls from "@/components/player/VideoControls";
import { TimeRange } from "@/types/timeline";
import { useCameraMotionNextTimestamp } from "@/hooks/use-camera-activity";
import useOptimisticState from "@/hooks/use-optimistic-state";
type EventViewProps = {
reviews?: ReviewSegment[];
@ -199,6 +200,10 @@ export default function EventView({
);
const [motionOnly, setMotionOnly] = useState(false);
const [severityToggle, setSeverityToggle] = useOptimisticState(
severity,
setSeverity,
);
if (!config) {
return <ActivityIndicator />;
@ -214,9 +219,9 @@ export default function EventView({
className="*:px-3 *:py-4 *:rounded-md"
type="single"
size="sm"
value={severity}
value={severityToggle}
onValueChange={(value: ReviewSeverity) =>
value ? setSeverity(value) : null
value ? setSeverityToggle(value) : null
} // don't allow the severity to be unselected
>
<ToggleGroupItem