Skip to content

UI tweaks #10998

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 9 commits into from
Apr 16, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion frigate/api/app.py
Original file line number Diff line number Diff line change
Expand Up @@ -139,7 +139,7 @@ def stats_history():
def config():
config_obj: FrigateConfig = current_app.frigate_config
config: dict[str, dict[str, any]] = config_obj.model_dump(
mode="json", exclude_none=True
mode="json", warnings="none", exclude_none=True
)

# remove the mqtt password
Expand Down
17 changes: 11 additions & 6 deletions frigate/config.py
Original file line number Diff line number Diff line change
Expand Up @@ -1351,11 +1351,12 @@ def runtime_config(self, plus_api: PlusApi = None) -> FrigateConfig:
"timestamp_style": ...,
},
exclude_unset=True,
warnings="none",
)

for name, camera in config.cameras.items():
merged_config = deep_merge(
camera.model_dump(exclude_unset=True), global_config
camera.model_dump(exclude_unset=True, warnings="none"), global_config
)
camera_config: CameraConfig = CameraConfig.model_validate(
{"name": name, **merged_config}
Expand Down Expand Up @@ -1466,7 +1467,7 @@ def runtime_config(self, plus_api: PlusApi = None) -> FrigateConfig:
# Set runtime filter to create masks
camera_config.objects.filters[object] = RuntimeFilterConfig(
frame_shape=camera_config.frame_shape,
**filter.model_dump(exclude_unset=True),
**filter.model_dump(exclude_unset=True, warnings="none"),
)

# Convert motion configuration
Expand All @@ -1478,7 +1479,9 @@ def runtime_config(self, plus_api: PlusApi = None) -> FrigateConfig:
camera_config.motion = RuntimeMotionConfig(
frame_shape=camera_config.frame_shape,
raw_mask=camera_config.motion.mask,
**camera_config.motion.model_dump(exclude_unset=True),
**camera_config.motion.model_dump(
exclude_unset=True, warnings="none"
),
)
camera_config.motion.enabled_in_config = camera_config.motion.enabled

Expand Down Expand Up @@ -1515,7 +1518,9 @@ def runtime_config(self, plus_api: PlusApi = None) -> FrigateConfig:
for key, detector in config.detectors.items():
adapter = TypeAdapter(DetectorConfig)
model_dict = (
detector if isinstance(detector, dict) else detector.model_dump()
detector
if isinstance(detector, dict)
else detector.model_dump(warnings="none")
)
detector_config: DetectorConfig = adapter.validate_python(model_dict)
if detector_config.model is None:
Expand All @@ -1536,8 +1541,8 @@ def runtime_config(self, plus_api: PlusApi = None) -> FrigateConfig:
"Customizing more than a detector model path is unsupported."
)
merged_model = deep_merge(
detector_config.model.model_dump(exclude_unset=True),
config.model.model_dump(exclude_unset=True),
detector_config.model.model_dump(exclude_unset=True, warnings="none"),
config.model.model_dump(exclude_unset=True, warnings="none"),
)

if "path" not in merged_model:
Expand Down
4 changes: 3 additions & 1 deletion frigate/ptz/onvif.py
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,9 @@ def __init__(
cam.onvif.port,
cam.onvif.user,
cam.onvif.password,
wsdl_dir=Path(find_spec("onvif").origin).parent / "../wsdl",
wsdl_dir=str(
Path(find_spec("onvif").origin).parent / "wsdl"
).replace("dist-packages/onvif", "site-packages"),
Copy link
Contributor

@mweinelt mweinelt Apr 17, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hm, ok. That breaks for me, given that the onvif package lands in site-packages in my environment. Need to put some more thought into this.

/nix/store/0q055hskam5ihnrhxxk6s3rgw376fpjq-python3-3.11.8-env/lib/python3.11/site-packages/onvif/wsdl

),
"init": False,
"active": False,
Expand Down
4 changes: 2 additions & 2 deletions web/src/components/filter/CameraGroupSelector.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -22,8 +22,8 @@ import {
DropdownMenuSeparator,
DropdownMenuTrigger,
} from "../ui/dropdown-menu";
import FilterCheckBox from "./FilterCheckBox";
import axios from "axios";
import FilterSwitch from "./FilterSwitch";

type CameraGroupSelectorProps = {
className?: string;
Expand Down Expand Up @@ -305,7 +305,7 @@ function NewGroupDialog({ open, setOpen, currentGroups }: NewGroupDialogProps) {
...(birdseyeConfig?.enabled ? ["birdseye"] : []),
...Object.keys(config?.cameras ?? {}),
].map((camera) => (
<FilterCheckBox
<FilterSwitch
key={camera}
isChecked={cameras.includes(camera)}
label={camera.replaceAll("_", " ")}
Expand Down
34 changes: 0 additions & 34 deletions web/src/components/filter/FilterCheckBox.tsx

This file was deleted.

29 changes: 29 additions & 0 deletions web/src/components/filter/FilterSwitch.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
import { Switch } from "../ui/switch";
import { Label } from "../ui/label";

type FilterSwitchProps = {
label: string;
isChecked: boolean;
onCheckedChange: (checked: boolean) => void;
};
export default function FilterSwitch({
label,
isChecked,
onCheckedChange,
}: FilterSwitchProps) {
return (
<div className="flex justify-between items-center gap-1">
<Label
className="w-full mx-2 text-primary capitalize cursor-pointer"
htmlFor={label}
>
{label}
</Label>
<Switch
id={label}
checked={isChecked}
onCheckedChange={onCheckedChange}
/>
</div>
);
}
77 changes: 39 additions & 38 deletions web/src/components/filter/ReviewFilterGroup.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -24,12 +24,12 @@ import { isDesktop, isMobile } from "react-device-detect";
import { Drawer, DrawerContent, DrawerTrigger } from "../ui/drawer";
import { Switch } from "../ui/switch";
import { Label } from "../ui/label";
import FilterCheckBox from "./FilterCheckBox";
import ReviewActivityCalendar from "../overlay/ReviewActivityCalendar";
import MobileReviewSettingsDrawer, {
DrawerFeatures,
} from "../overlay/MobileReviewSettingsDrawer";
import useOptimisticState from "@/hooks/use-optimistic-state";
import FilterSwitch from "./FilterSwitch";

const REVIEW_FILTERS = [
"cameras",
Expand Down Expand Up @@ -248,8 +248,8 @@ export function CamerasFilterButton({
<DropdownMenuSeparator />
</>
)}
<div className="h-auto overflow-y-auto overflow-x-hidden">
<FilterCheckBox
<div className="h-auto pt-2 overflow-y-auto overflow-x-hidden">
<FilterSwitch
isChecked={currentCameras == undefined}
label="All Cameras"
onCheckedChange={(isChecked) => {
Expand All @@ -260,51 +260,52 @@ export function CamerasFilterButton({
/>
{groups.length > 0 && (
<>
<DropdownMenuSeparator />
<DropdownMenuSeparator className="mt-2" />
{groups.map(([name, conf]) => {
return (
<FilterCheckBox
<div
key={name}
label={name}
isChecked={false}
onCheckedChange={() => {
setCurrentCameras([...conf.cameras]);
}}
/>
className="w-full px-2 py-1.5 text-sm text-primary capitalize cursor-pointer rounded-lg hover:bg-muted"
onClick={() => setCurrentCameras([...conf.cameras])}
>
{name}
</div>
);
})}
</>
)}
<DropdownMenuSeparator />
{allCameras.map((item) => (
<FilterCheckBox
key={item}
isChecked={currentCameras?.includes(item) ?? false}
label={item.replaceAll("_", " ")}
onCheckedChange={(isChecked) => {
if (isChecked) {
const updatedCameras = currentCameras
? [...currentCameras]
: [];

updatedCameras.push(item);
setCurrentCameras(updatedCameras);
} else {
const updatedCameras = currentCameras
? [...currentCameras]
: [];

// can not deselect the last item
if (updatedCameras.length > 1) {
updatedCameras.splice(updatedCameras.indexOf(item), 1);
<DropdownMenuSeparator className="my-2" />
<div className="flex flex-col gap-2.5">
{allCameras.map((item) => (
<FilterSwitch
key={item}
isChecked={currentCameras?.includes(item) ?? false}
label={item.replaceAll("_", " ")}
onCheckedChange={(isChecked) => {
if (isChecked) {
const updatedCameras = currentCameras
? [...currentCameras]
: [];

updatedCameras.push(item);
setCurrentCameras(updatedCameras);
} else {
const updatedCameras = currentCameras
? [...currentCameras]
: [];

// can not deselect the last item
if (updatedCameras.length > 1) {
updatedCameras.splice(updatedCameras.indexOf(item), 1);
setCurrentCameras(updatedCameras);
}
}
}
}}
/>
))}
}}
/>
))}
</div>
</div>
<DropdownMenuSeparator />
<DropdownMenuSeparator className="my-2" />
<div className="p-2 flex justify-evenly items-center">
<Button
variant="select"
Expand Down
2 changes: 1 addition & 1 deletion web/src/components/graph/SystemGraph.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -72,7 +72,7 @@ export function ThresholdBarGraph({
} else if (value >= threshold.warning) {
return "#FF9966";
} else {
return (systemTheme || theme) == "dark" ? "#404040" : "#E5E5E5";
return "#217930";
}
},
],
Expand Down
4 changes: 2 additions & 2 deletions web/src/components/navigation/NavItem.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,8 @@ import { IconType } from "react-icons";

const variants = {
primary: {
active: "font-bold text-white bg-selected",
inactive: "text-secondary-foreground bg-secondary",
active: "font-bold text-white bg-selected hover:bg-selected/80",
inactive: "text-secondary-foreground bg-secondary hover:bg-muted",
},
secondary: {
active: "font-bold text-selected",
Expand Down
2 changes: 1 addition & 1 deletion web/src/components/navigation/Sidebar.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ function Sidebar() {
);
})}
</div>
<div className="flex flex-col items-center mb-8">
<div className="flex flex-col items-center gap-4 mb-8">
<GeneralSettings />
<AccountSettings />
</div>
Expand Down
6 changes: 5 additions & 1 deletion web/src/components/player/dynamic/DynamicVideoPlayer.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -105,9 +105,13 @@ export default function DynamicVideoPlayer({
return;
}

if (isLoading) {
setIsLoading(false);
}

onTimestampUpdate(controller.getProgress(time));
},
[controller, onTimestampUpdate, isScrubbing],
[controller, onTimestampUpdate, isScrubbing, isLoading],
);

// state of playback player
Expand Down
10 changes: 6 additions & 4 deletions web/src/components/settings/AccountSettings.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,16 +3,18 @@ import {
TooltipContent,
TooltipTrigger,
} from "@/components/ui/tooltip";
import { isDesktop } from "react-device-detect";
import { VscAccount } from "react-icons/vsc";
import { Button } from "../ui/button";

export default function AccountSettings() {
return (
<Tooltip>
<TooltipTrigger asChild>
<Button size="icon" variant="ghost">
<VscAccount />
</Button>
<div
className={`flex flex-col justify-center items-center ${isDesktop ? "rounded-lg text-secondary-foreground bg-secondary hover:bg-muted cursor-pointer" : "text-secondary-foreground"}`}
>
<VscAccount className="size-5 md:m-[6px]" />
</div>
</TooltipTrigger>
<TooltipContent side="right">
<p>Account</p>
Expand Down
8 changes: 5 additions & 3 deletions web/src/components/settings/GeneralSettings.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -118,9 +118,11 @@ export default function GeneralSettings({ className }: GeneralSettings) {
<a href="#">
<Tooltip>
<TooltipTrigger asChild>
<Button size="icon" variant="ghost">
<LuSettings />
</Button>
<div
className={`flex flex-col justify-center items-center ${isDesktop ? "rounded-lg text-secondary-foreground bg-secondary hover:bg-muted cursor-pointer" : "text-secondary-foreground"}`}
>
<LuSettings className="size-5 md:m-[6px]" />
</div>
</TooltipTrigger>
<TooltipContent side="right">
<p>Settings</p>
Expand Down
4 changes: 2 additions & 2 deletions web/src/pages/Logs.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ function Logs() {
const [logService, setLogService] = useState<LogType>("frigate");

useEffect(() => {
document.title = `${logService[0].toUpperCase()}${logService.substring(1)} Stats - Frigate`;
document.title = `${logService[0].toUpperCase()}${logService.substring(1)} Logs - Frigate`;
}, [logService]);

// log data handling
Expand Down Expand Up @@ -366,7 +366,7 @@ function Logs() {
size="sm"
onClick={handleCopyLogs}
>
<FaCopy />
<FaCopy className="text-secondary-foreground" />
<div className="hidden md:block text-primary">
Copy to Clipboard
</div>
Expand Down