forked from Github/frigate
Add status bar provider (#11066)
This commit is contained in:
@@ -1,7 +1,12 @@
|
||||
import { useFrigateStats } from "@/api/ws";
|
||||
import {
|
||||
StatusBarMessagesContext,
|
||||
StatusMessage,
|
||||
} from "@/context/statusbar-provider";
|
||||
import useStats from "@/hooks/use-stats";
|
||||
import { FrigateStats } from "@/types/stats";
|
||||
import { useMemo } from "react";
|
||||
import { useContext, useEffect, useMemo } from "react";
|
||||
import { FaCheck } from "react-icons/fa";
|
||||
import { IoIosWarning } from "react-icons/io";
|
||||
import { MdCircle } from "react-icons/md";
|
||||
import useSWR from "swr";
|
||||
@@ -11,6 +16,10 @@ export default function Statusbar() {
|
||||
revalidateOnFocus: false,
|
||||
});
|
||||
const { payload: latestStats } = useFrigateStats();
|
||||
const { messages, addMessage, clearMessages } = useContext(
|
||||
StatusBarMessagesContext,
|
||||
)!;
|
||||
|
||||
const stats = useMemo(() => {
|
||||
if (latestStats) {
|
||||
return latestStats;
|
||||
@@ -31,6 +40,13 @@ export default function Statusbar() {
|
||||
|
||||
const { potentialProblems } = useStats(stats);
|
||||
|
||||
useEffect(() => {
|
||||
clearMessages("stats");
|
||||
potentialProblems.forEach((problem) => {
|
||||
addMessage("stats", problem.text, problem.color);
|
||||
});
|
||||
}, [potentialProblems, addMessage, clearMessages]);
|
||||
|
||||
return (
|
||||
<div className="absolute left-0 bottom-0 right-0 w-full h-8 flex justify-between items-center px-4 bg-background_alt z-10 dark:text-secondary-foreground border-t border-secondary-highlight">
|
||||
<div className="h-full flex items-center gap-2">
|
||||
@@ -86,15 +102,25 @@ export default function Statusbar() {
|
||||
})}
|
||||
</div>
|
||||
<div className="h-full flex items-center gap-2">
|
||||
{potentialProblems.map((prob) => (
|
||||
<div
|
||||
key={prob.text}
|
||||
className="flex items-center text-sm gap-2 capitalize"
|
||||
>
|
||||
<IoIosWarning className={`size-5 ${prob.color}`} />
|
||||
{prob.text}
|
||||
{Object.entries(messages).length === 0 ? (
|
||||
<div className="flex items-center text-sm gap-2">
|
||||
<FaCheck className="size-3 text-green-500" />
|
||||
System is healthy
|
||||
</div>
|
||||
))}
|
||||
) : (
|
||||
Object.entries(messages).map(([key, messageArray]) => (
|
||||
<div key={key} className="h-full flex items-center gap-2">
|
||||
{messageArray.map(({ id, text, color }: StatusMessage) => (
|
||||
<div key={id} className="flex items-center text-sm gap-2">
|
||||
<IoIosWarning
|
||||
className={`size-5 ${color || "text-danger"}`}
|
||||
/>
|
||||
{text}
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
))
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
|
||||
@@ -4,11 +4,15 @@ import { Drawer, DrawerContent, DrawerTrigger } from "../ui/drawer";
|
||||
import useSWR from "swr";
|
||||
import { FrigateStats } from "@/types/stats";
|
||||
import { useFrigateStats } from "@/api/ws";
|
||||
import { useMemo } from "react";
|
||||
import { useContext, useEffect, useMemo } from "react";
|
||||
import useStats from "@/hooks/use-stats";
|
||||
import GeneralSettings from "../menu/GeneralSettings";
|
||||
import AccountSettings from "../menu/AccountSettings";
|
||||
import useNavigation from "@/hooks/use-navigation";
|
||||
import {
|
||||
StatusBarMessagesContext,
|
||||
StatusMessage,
|
||||
} from "@/context/statusbar-provider";
|
||||
|
||||
function Bottombar() {
|
||||
const navItems = useNavigation("secondary");
|
||||
@@ -30,6 +34,11 @@ function StatusAlertNav() {
|
||||
revalidateOnFocus: false,
|
||||
});
|
||||
const { payload: latestStats } = useFrigateStats();
|
||||
|
||||
const { messages, addMessage, clearMessages } = useContext(
|
||||
StatusBarMessagesContext,
|
||||
)!;
|
||||
|
||||
const stats = useMemo(() => {
|
||||
if (latestStats) {
|
||||
return latestStats;
|
||||
@@ -39,7 +48,14 @@ function StatusAlertNav() {
|
||||
}, [initialStats, latestStats]);
|
||||
const { potentialProblems } = useStats(stats);
|
||||
|
||||
if (!potentialProblems || potentialProblems.length == 0) {
|
||||
useEffect(() => {
|
||||
clearMessages("stats");
|
||||
potentialProblems.forEach((problem) => {
|
||||
addMessage("stats", problem.text, problem.color);
|
||||
});
|
||||
}, [potentialProblems, addMessage, clearMessages]);
|
||||
|
||||
if (!messages || Object.keys(messages).length === 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -50,13 +66,16 @@ function StatusAlertNav() {
|
||||
</DrawerTrigger>
|
||||
<DrawerContent className="max-h-[75dvh] px-2 mx-1 rounded-t-2xl overflow-hidden">
|
||||
<div className="w-full h-auto py-4 overflow-y-auto overflow-x-hidden flex flex-col items-center gap-2">
|
||||
{potentialProblems.map((prob) => (
|
||||
<div
|
||||
key={prob.text}
|
||||
className="w-full flex items-center text-xs gap-2 capitalize"
|
||||
>
|
||||
<IoIosWarning className={`size-5 ${prob.color}`} />
|
||||
{prob.text}
|
||||
{Object.entries(messages).map(([key, messageArray]) => (
|
||||
<div key={key} className="w-full flex items-center gap-2">
|
||||
{messageArray.map(({ id, text, color }: StatusMessage) => (
|
||||
<div key={id} className="flex items-center text-xs gap-2">
|
||||
<IoIosWarning
|
||||
className={`size-5 ${color || "text-danger"}`}
|
||||
/>
|
||||
{text}
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
|
||||
@@ -1,7 +1,14 @@
|
||||
import { FrigateConfig } from "@/types/frigateConfig";
|
||||
import useSWR from "swr";
|
||||
import ActivityIndicator from "@/components/indicators/activity-indicator";
|
||||
import { useCallback, useEffect, useMemo, useRef, useState } from "react";
|
||||
import {
|
||||
useCallback,
|
||||
useContext,
|
||||
useEffect,
|
||||
useMemo,
|
||||
useRef,
|
||||
useState,
|
||||
} from "react";
|
||||
import { PolygonCanvas } from "./PolygonCanvas";
|
||||
import { Polygon, PolygonType } from "@/types/canvas";
|
||||
import { interpolatePoints, parseCoordinates } from "@/utils/canvasUtil";
|
||||
@@ -25,6 +32,7 @@ import ObjectMaskEditPane from "./ObjectMaskEditPane";
|
||||
import PolygonItem from "./PolygonItem";
|
||||
import { Link } from "react-router-dom";
|
||||
import { isDesktop } from "react-device-detect";
|
||||
import { StatusBarMessagesContext } from "@/context/statusbar-provider";
|
||||
|
||||
type MasksAndZoneProps = {
|
||||
selectedCamera: string;
|
||||
@@ -50,6 +58,8 @@ export default function MasksAndZones({
|
||||
const containerRef = useRef<HTMLDivElement | null>(null);
|
||||
const [editPane, setEditPane] = useState<PolygonType | undefined>(undefined);
|
||||
|
||||
const { addMessage } = useContext(StatusBarMessagesContext)!;
|
||||
|
||||
const cameraConfig = useMemo(() => {
|
||||
if (config && selectedCamera) {
|
||||
return config.cameras[selectedCamera];
|
||||
@@ -167,7 +177,8 @@ export default function MasksAndZones({
|
||||
setAllPolygons([...(editingPolygons ?? [])]);
|
||||
setHoveredPolygonIndex(null);
|
||||
setUnsavedChanges(false);
|
||||
}, [editingPolygons, setUnsavedChanges]);
|
||||
addMessage("masks_zones", "Restart required (masks/zones changed)");
|
||||
}, [editingPolygons, setUnsavedChanges, addMessage]);
|
||||
|
||||
useEffect(() => {
|
||||
if (isLoading) {
|
||||
|
||||
@@ -4,7 +4,7 @@ import useSWR from "swr";
|
||||
import axios from "axios";
|
||||
import ActivityIndicator from "@/components/indicators/activity-indicator";
|
||||
import AutoUpdatingCameraImage from "@/components/camera/AutoUpdatingCameraImage";
|
||||
import { useCallback, useEffect, useMemo, useState } from "react";
|
||||
import { useCallback, useContext, useEffect, useMemo, useState } from "react";
|
||||
import { Slider } from "@/components/ui/slider";
|
||||
import { Label } from "@/components/ui/label";
|
||||
import {
|
||||
@@ -20,6 +20,7 @@ import { toast } from "sonner";
|
||||
import { Separator } from "../ui/separator";
|
||||
import { Link } from "react-router-dom";
|
||||
import { LuExternalLink } from "react-icons/lu";
|
||||
import { StatusBarMessagesContext } from "@/context/statusbar-provider";
|
||||
|
||||
type MotionTunerProps = {
|
||||
selectedCamera: string;
|
||||
@@ -41,6 +42,8 @@ export default function MotionTuner({
|
||||
const [changedValue, setChangedValue] = useState(false);
|
||||
const [isLoading, setIsLoading] = useState(false);
|
||||
|
||||
const { addMessage, clearMessages } = useContext(StatusBarMessagesContext)!;
|
||||
|
||||
const { send: sendMotionThreshold } = useMotionThreshold(selectedCamera);
|
||||
const { send: sendMotionContourArea } = useMotionContourArea(selectedCamera);
|
||||
const { send: sendImproveContrast } = useImproveContrast(selectedCamera);
|
||||
@@ -145,7 +148,16 @@ export default function MotionTuner({
|
||||
const onCancel = useCallback(() => {
|
||||
setMotionSettings(origMotionSettings);
|
||||
setChangedValue(false);
|
||||
}, [origMotionSettings]);
|
||||
clearMessages("motion_tuner");
|
||||
}, [origMotionSettings, clearMessages]);
|
||||
|
||||
useEffect(() => {
|
||||
if (changedValue) {
|
||||
addMessage("motion_tuner", "Unsaved motion tuner changes");
|
||||
} else {
|
||||
clearMessages("motion_tuner");
|
||||
}
|
||||
}, [changedValue, addMessage, clearMessages]);
|
||||
|
||||
if (!cameraConfig && !selectedCamera) {
|
||||
return <ActivityIndicator />;
|
||||
|
||||
Reference in New Issue
Block a user