Update camera activity indicator (#10208)

* new indicator

* create indicators directory and update imports

* create indicators directory and update imports

* remove vite
This commit is contained in:
Josh Hawkins 2024-03-03 10:32:47 -06:00 committed by GitHub
parent 312dc95156
commit c74eb75554
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
17 changed files with 85 additions and 39 deletions

View File

@ -2,7 +2,7 @@ import { FrigateConfig } from "@/types/frigateConfig";
import { GraphDataPoint } from "@/types/graph"; import { GraphDataPoint } from "@/types/graph";
import { formatUnixTimestampToDateTime } from "@/utils/dateUtil"; import { formatUnixTimestampToDateTime } from "@/utils/dateUtil";
import useSWR from "swr"; import useSWR from "swr";
import ActivityIndicator from "../ui/activity-indicator"; import ActivityIndicator from "../indicators/activity-indicator";
type TimelineBarProps = { type TimelineBarProps = {
startTime: number; startTime: number;

View File

@ -1,7 +1,7 @@
import { useApiHost } from "@/api"; import { useApiHost } from "@/api";
import { useEffect, useRef, useState } from "react"; import { useEffect, useRef, useState } from "react";
import useSWR from "swr"; import useSWR from "swr";
import ActivityIndicator from "../ui/activity-indicator"; import ActivityIndicator from "../indicators/activity-indicator";
type CameraImageProps = { type CameraImageProps = {
className?: string; className?: string;

View File

@ -1,7 +1,7 @@
import { useApiHost } from "@/api"; import { useApiHost } from "@/api";
import { useCallback, useEffect, useMemo, useRef, useState } from "react"; import { useCallback, useEffect, useMemo, useRef, useState } from "react";
import useSWR from "swr"; import useSWR from "swr";
import ActivityIndicator from "../ui/activity-indicator"; import ActivityIndicator from "../indicators/activity-indicator";
import { useResizeObserver } from "@/hooks/resize-observer"; import { useResizeObserver } from "@/hooks/resize-observer";
type CameraImageProps = { type CameraImageProps = {

View File

@ -1,5 +1,5 @@
import { baseUrl } from "@/api/baseUrl"; import { baseUrl } from "@/api/baseUrl";
import ActivityIndicator from "../ui/activity-indicator"; import ActivityIndicator from "../indicators/activity-indicator";
import { Card } from "../ui/card"; import { Card } from "../ui/card";
import { LuPlay, LuTrash } from "react-icons/lu"; import { LuPlay, LuTrash } from "react-icons/lu";
import { Button } from "../ui/button"; import { Button } from "../ui/button";

View File

@ -0,0 +1,33 @@
function CameraActivityIndicator() {
return (
<div className="flex items-center justify-center relative z-20">
<div className="flex">
<div className="absolute size-[5px] inset-0 bg-severity_alert-dimmed rounded-full shadow-[0px_0px_10px_0px_#00000024,0px_0px_15px_0px_#00000024] z-20 animate-move"></div>
<div className="flex-1 size-[5px] mr-[2px] bg-severity_alert rounded-full animate-scale1"></div>
<div className="flex-1 size-[5px] mr-[2px] bg-severity_alert rounded-full animate-scale2"></div>
<div className="flex-1 size-[5px] mr-[2px] bg-severity_alert rounded-full animate-scale3"></div>
<div className="flex-1 size-[5px] mr-[2px] bg-severity_alert rounded-full animate-scale4"></div>
</div>
<svg className="hidden" xmlns="http://www.w3.org/2000/svg" version="1.1">
<defs>
<filter id="goo">
<feGaussianBlur
in="SourceGraphic"
stdDeviation="10"
result="blur"
/>
<feColorMatrix
in="blur"
type="matrix"
values="1 0 0 0 0 0 1 0 0 0 0 0 1 0 0 0 0 0 18 -7"
result="goo"
/>
<feBlend in="SourceGraphic" in2="goo" />
</filter>
</defs>
</svg>
</div>
);
}
export default CameraActivityIndicator;

View File

@ -1,6 +1,6 @@
import WebRtcPlayer from "./WebRTCPlayer"; import WebRtcPlayer from "./WebRTCPlayer";
import { BirdseyeConfig } from "@/types/frigateConfig"; import { BirdseyeConfig } from "@/types/frigateConfig";
import ActivityIndicator from "../ui/activity-indicator"; import ActivityIndicator from "../indicators/activity-indicator";
import JSMpegPlayer from "./JSMpegPlayer"; import JSMpegPlayer from "./JSMpegPlayer";
import MSEPlayer from "./MsePlayer"; import MSEPlayer from "./MsePlayer";

View File

@ -12,7 +12,7 @@ import TimelineEventOverlay from "../overlay/TimelineDataOverlay";
import { useApiHost } from "@/api"; import { useApiHost } from "@/api";
import useSWR from "swr"; import useSWR from "swr";
import { FrigateConfig } from "@/types/frigateConfig"; import { FrigateConfig } from "@/types/frigateConfig";
import ActivityIndicator from "../ui/activity-indicator"; import ActivityIndicator from "../indicators/activity-indicator";
import useKeyboardListener from "@/hooks/use-keyboard-listener"; import useKeyboardListener from "@/hooks/use-keyboard-listener";
import { Recording } from "@/types/record"; import { Recording } from "@/types/record";
import { Preview } from "@/types/preview"; import { Preview } from "@/types/preview";

View File

@ -1,18 +1,17 @@
import WebRtcPlayer from "./WebRTCPlayer"; import WebRtcPlayer from "./WebRTCPlayer";
import { CameraConfig } from "@/types/frigateConfig"; import { CameraConfig } from "@/types/frigateConfig";
import AutoUpdatingCameraImage from "../camera/AutoUpdatingCameraImage"; import AutoUpdatingCameraImage from "../camera/AutoUpdatingCameraImage";
import ActivityIndicator from "../ui/activity-indicator"; import ActivityIndicator from "../indicators/activity-indicator";
import { useEffect, useMemo, useState } from "react"; import { useEffect, useMemo, useState } from "react";
import MSEPlayer from "./MsePlayer"; import MSEPlayer from "./MsePlayer";
import JSMpegPlayer from "./JSMpegPlayer"; import JSMpegPlayer from "./JSMpegPlayer";
import { MdCircle, MdLeakAdd } from "react-icons/md"; import { MdCircle } from "react-icons/md";
import { BsSoundwave } from "react-icons/bs";
import Chip from "../Chip";
import useCameraActivity from "@/hooks/use-camera-activity"; import useCameraActivity from "@/hooks/use-camera-activity";
import { useRecordingsState } from "@/api/ws"; import { useRecordingsState } from "@/api/ws";
import { LivePlayerMode } from "@/types/live"; import { LivePlayerMode } from "@/types/live";
import useCameraLiveMode from "@/hooks/use-camera-live-mode"; import useCameraLiveMode from "@/hooks/use-camera-live-mode";
import { isDesktop } from "react-device-detect"; import { isDesktop } from "react-device-detect";
import CameraActivityIndicator from "../indicators/CameraActivityIndicator";
type LivePlayerProps = { type LivePlayerProps = {
className?: string; className?: string;
@ -159,35 +158,19 @@ export default function LivePlayer({
/> />
</div> </div>
<div className="absolute flex left-2 top-2 gap-2"> <div className="absolute right-2 bottom-2 w-[40px]">
<Chip {(activeMotion ||
in={activeMotion} (cameraConfig.audio.enabled_in_config && activeAudio)) && (
className={`bg-gradient-to-br from-gray-400 to-gray-500 bg-gray-500`} <CameraActivityIndicator />
>
<MdLeakAdd className="size-4 text-motion" />
<div className="hidden md:block ml-1 text-white text-xs">Motion</div>
</Chip>
{cameraConfig.audio.enabled_in_config && (
<Chip
in={activeAudio}
className={`bg-gradient-to-br from-gray-400 to-gray-500 bg-gray-500`}
>
<BsSoundwave className="size-4 text-audio" />
<div className="hidden md:block ml-1 text-white text-xs">Sound</div>
</Chip>
)} )}
</div> </div>
{isDesktop && ( {isDesktop && (
<Chip className="absolute right-2 top-2 bg-gradient-to-br from-gray-400 to-gray-500 bg-gray-500"> <div className="absolute right-2 top-2 size-4">
{recording == "ON" && ( {recording == "ON" && (
<MdCircle className="size-2 drop-shadow-md shadow-danger text-danger" /> <MdCircle className="size-2 drop-shadow-md shadow-danger text-danger animate-pulse" />
)} )}
<div className="ml-1 capitalize text-white text-xs"> </div>
{cameraConfig.name.replaceAll("_", " ")}
</div>
</Chip>
)} )}
</div> </div>
); );

View File

@ -14,7 +14,7 @@ import TimeAgo from "../dynamic/TimeAgo";
import useSWR from "swr"; import useSWR from "swr";
import { FrigateConfig } from "@/types/frigateConfig"; import { FrigateConfig } from "@/types/frigateConfig";
import { isFirefox, isMobile, isSafari } from "react-device-detect"; import { isFirefox, isMobile, isSafari } from "react-device-detect";
import Chip from "../Chip"; import Chip from "@/components/indicators/Chip";
import { useFormattedTimestamp } from "@/hooks/use-date-utils"; import { useFormattedTimestamp } from "@/hooks/use-date-utils";
import useImageLoaded from "@/hooks/use-image-loaded"; import useImageLoaded from "@/hooks/use-image-loaded";
import { Skeleton } from "../ui/skeleton"; import { Skeleton } from "../ui/skeleton";

View File

@ -57,7 +57,7 @@ import {
TooltipContent, TooltipContent,
TooltipTrigger, TooltipTrigger,
} from "@/components/ui/tooltip"; } from "@/components/ui/tooltip";
import ActivityIndicator from "../ui/activity-indicator"; import ActivityIndicator from "../indicators/activity-indicator";
type SettingsNavItemsProps = { type SettingsNavItemsProps = {
className?: string; className?: string;

View File

@ -4,7 +4,7 @@ import { configureMonacoYaml } from "monaco-yaml";
import { useCallback, useEffect, useRef, useState } from "react"; import { useCallback, useEffect, useRef, useState } from "react";
import { useApiHost } from "@/api"; import { useApiHost } from "@/api";
import Heading from "@/components/ui/heading"; import Heading from "@/components/ui/heading";
import ActivityIndicator from "@/components/ui/activity-indicator"; import ActivityIndicator from "@/components/indicators/activity-indicator";
import { Button } from "@/components/ui/button"; import { Button } from "@/components/ui/button";
import axios from "axios"; import axios from "axios";
import copy from "copy-to-clipboard"; import copy from "copy-to-clipboard";

View File

@ -1,5 +1,5 @@
import { useWs } from "@/api/ws"; import { useWs } from "@/api/ws";
import ActivityIndicator from "@/components/ui/activity-indicator"; import ActivityIndicator from "@/components/indicators/activity-indicator";
import { Button } from "@/components/ui/button"; import { Button } from "@/components/ui/button";
import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card"; import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card";
import Heading from "@/components/ui/heading"; import Heading from "@/components/ui/heading";

View File

@ -2,10 +2,11 @@ import { useMemo, useRef, useState } from "react";
import Heading from "@/components/ui/heading"; import Heading from "@/components/ui/heading";
import useSWR from "swr"; import useSWR from "swr";
import { FrigateConfig } from "@/types/frigateConfig"; import { FrigateConfig } from "@/types/frigateConfig";
import ActivityIndicator from "@/components/ui/activity-indicator"; import ActivityIndicator from "@/components/indicators/activity-indicator";
import EventReviewTimeline from "@/components/timeline/EventReviewTimeline"; import EventReviewTimeline from "@/components/timeline/EventReviewTimeline";
import { ReviewData, ReviewSegment, ReviewSeverity } from "@/types/review"; import { ReviewData, ReviewSegment, ReviewSeverity } from "@/types/review";
import { Button } from "@/components/ui/button"; import { Button } from "@/components/ui/button";
import CameraActivityIndicator from "@/components/indicators/CameraActivityIndicator";
// Color data // Color data
const colors = [ const colors = [
@ -174,6 +175,9 @@ function UIPlayground() {
<p className="text-small"> <p className="text-small">
Handlebar is dragging: {isDragging ? "yes" : "no"} Handlebar is dragging: {isDragging ? "yes" : "no"}
</p> </p>
<div className="w-[40px] my-4">
<CameraActivityIndicator />
</div>
<p> <p>
<Button onClick={handleZoomOut} disabled={zoomLevel === 0}> <Button onClick={handleZoomOut} disabled={zoomLevel === 0}>
Zoom Out Zoom Out

View File

@ -4,7 +4,7 @@ import ReviewActionGroup from "@/components/filter/ReviewActionGroup";
import ReviewFilterGroup from "@/components/filter/ReviewFilterGroup"; import ReviewFilterGroup from "@/components/filter/ReviewFilterGroup";
import PreviewThumbnailPlayer from "@/components/player/PreviewThumbnailPlayer"; import PreviewThumbnailPlayer from "@/components/player/PreviewThumbnailPlayer";
import EventReviewTimeline from "@/components/timeline/EventReviewTimeline"; import EventReviewTimeline from "@/components/timeline/EventReviewTimeline";
import ActivityIndicator from "@/components/ui/activity-indicator"; import ActivityIndicator from "@/components/indicators/activity-indicator";
import { ToggleGroup, ToggleGroupItem } from "@/components/ui/toggle-group"; import { ToggleGroup, ToggleGroupItem } from "@/components/ui/toggle-group";
import { useEventUtils } from "@/hooks/use-event-utils"; import { useEventUtils } from "@/hooks/use-event-utils";
import { useScrollLockout } from "@/hooks/use-mouse-listener"; import { useScrollLockout } from "@/hooks/use-mouse-listener";

View File

@ -27,6 +27,11 @@ module.exports = {
animation: { animation: {
"accordion-down": "accordion-down 0.2s ease-out", "accordion-down": "accordion-down 0.2s ease-out",
"accordion-up": "accordion-up 0.2s ease-out", "accordion-up": "accordion-up 0.2s ease-out",
move: "move 3s ease-in-out infinite",
scale1: "scale1 3s ease-in-out infinite",
scale2: "scale2 3s ease-in-out infinite",
scale3: "scale3 3s ease-in-out infinite",
scale4: "scale4 3s ease-in-out infinite",
}, },
aspectRatio: { aspectRatio: {
wide: "32 / 9", wide: "32 / 9",
@ -96,6 +101,27 @@ module.exports = {
from: { height: "var(--radix-accordion-content-height)" }, from: { height: "var(--radix-accordion-content-height)" },
to: { height: 0 }, to: { height: 0 },
}, },
move: {
"50%": { left: "calc(100% - 7px)" },
},
scale1: {
"0%, 100%": { transform: "scale(1.3)" },
"10%, 90%": { transform: "scale(1.4)" },
"20%, 80%": { transform: "scale(1)" },
},
scale2: {
"20%, 80%": { transform: "scale(1.4)" },
"10%, 30%, 70%, 90%": { transform: "scale(1)" },
},
scale3: {
"30%, 70%": { transform: "scale(1.4)" },
"20%, 40%, 60%, 80%": { transform: "scale(1)" },
},
scale4: {
"50%": { transform: "scale(1.3)" },
"40%, 60%": { transform: "scale(1.4)" },
"30%, 70%": { transform: "scale(1)" },
},
}, },
screens: { screens: {
xs: "480px", xs: "480px",