mirror of
https://github.com/blakeblackshear/frigate.git
synced 2026-02-15 07:35:27 +03:00
Add cameras page to more filters
This commit is contained in:
parent
e11f4b8940
commit
9bce6efddc
@ -69,6 +69,70 @@ export function CamerasFilterButton({
|
|||||||
</Button>
|
</Button>
|
||||||
);
|
);
|
||||||
const content = (
|
const content = (
|
||||||
|
<CamerasFilterContent
|
||||||
|
allCameras={allCameras}
|
||||||
|
groups={groups}
|
||||||
|
currentCameras={currentCameras}
|
||||||
|
setCurrentCameras={setCurrentCameras}
|
||||||
|
setOpen={setOpen}
|
||||||
|
updateCameraFilter={updateCameraFilter}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
|
||||||
|
if (isMobile) {
|
||||||
|
return (
|
||||||
|
<Drawer
|
||||||
|
open={open}
|
||||||
|
onOpenChange={(open) => {
|
||||||
|
if (!open) {
|
||||||
|
setCurrentCameras(selectedCameras);
|
||||||
|
}
|
||||||
|
|
||||||
|
setOpen(open);
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<DrawerTrigger asChild>{trigger}</DrawerTrigger>
|
||||||
|
<DrawerContent className="max-h-[75dvh] overflow-hidden">
|
||||||
|
{content}
|
||||||
|
</DrawerContent>
|
||||||
|
</Drawer>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<DropdownMenu
|
||||||
|
modal={false}
|
||||||
|
open={open}
|
||||||
|
onOpenChange={(open) => {
|
||||||
|
if (!open) {
|
||||||
|
setCurrentCameras(selectedCameras);
|
||||||
|
}
|
||||||
|
setOpen(open);
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<DropdownMenuTrigger asChild>{trigger}</DropdownMenuTrigger>
|
||||||
|
<DropdownMenuContent>{content}</DropdownMenuContent>
|
||||||
|
</DropdownMenu>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
type CamerasFilterContentProps = {
|
||||||
|
allCameras: string[];
|
||||||
|
currentCameras: string[] | undefined;
|
||||||
|
groups: [string, CameraGroupConfig][];
|
||||||
|
setCurrentCameras: (cameras: string[] | undefined) => void;
|
||||||
|
setOpen: (open: boolean) => void;
|
||||||
|
updateCameraFilter: (cameras: string[] | undefined) => void;
|
||||||
|
};
|
||||||
|
export function CamerasFilterContent({
|
||||||
|
allCameras,
|
||||||
|
currentCameras,
|
||||||
|
groups,
|
||||||
|
setCurrentCameras,
|
||||||
|
setOpen,
|
||||||
|
updateCameraFilter,
|
||||||
|
}: CamerasFilterContentProps) {
|
||||||
|
return (
|
||||||
<>
|
<>
|
||||||
{isMobile && (
|
{isMobile && (
|
||||||
<>
|
<>
|
||||||
@ -158,40 +222,4 @@ export function CamerasFilterButton({
|
|||||||
</div>
|
</div>
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
|
|
||||||
if (isMobile) {
|
|
||||||
return (
|
|
||||||
<Drawer
|
|
||||||
open={open}
|
|
||||||
onOpenChange={(open) => {
|
|
||||||
if (!open) {
|
|
||||||
setCurrentCameras(selectedCameras);
|
|
||||||
}
|
|
||||||
|
|
||||||
setOpen(open);
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<DrawerTrigger asChild>{trigger}</DrawerTrigger>
|
|
||||||
<DrawerContent className="max-h-[75dvh] overflow-hidden">
|
|
||||||
{content}
|
|
||||||
</DrawerContent>
|
|
||||||
</Drawer>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
return (
|
|
||||||
<DropdownMenu
|
|
||||||
modal={false}
|
|
||||||
open={open}
|
|
||||||
onOpenChange={(open) => {
|
|
||||||
if (!open) {
|
|
||||||
setCurrentCameras(selectedCameras);
|
|
||||||
}
|
|
||||||
setOpen(open);
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<DropdownMenuTrigger asChild>{trigger}</DropdownMenuTrigger>
|
|
||||||
<DropdownMenuContent>{content}</DropdownMenuContent>
|
|
||||||
</DropdownMenu>
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|||||||
@ -10,7 +10,6 @@ import { Switch } from "../ui/switch";
|
|||||||
import { Label } from "../ui/label";
|
import { Label } from "../ui/label";
|
||||||
import FilterSwitch from "./FilterSwitch";
|
import FilterSwitch from "./FilterSwitch";
|
||||||
import { FilterList } from "@/types/filter";
|
import { FilterList } from "@/types/filter";
|
||||||
import { CalendarRangeFilterButton } from "./CalendarFilterButton";
|
|
||||||
import { CamerasFilterButton } from "./CamerasFilterButton";
|
import { CamerasFilterButton } from "./CamerasFilterButton";
|
||||||
import {
|
import {
|
||||||
DEFAULT_SEARCH_FILTERS,
|
DEFAULT_SEARCH_FILTERS,
|
||||||
@ -80,8 +79,6 @@ export default function SearchFilterGroup({
|
|||||||
return [...labels].sort();
|
return [...labels].sort();
|
||||||
}, [config, filterList, filter]);
|
}, [config, filterList, filter]);
|
||||||
|
|
||||||
const { data: allSubLabels } = useSWR(["sub_labels", { split_joined: 1 }]);
|
|
||||||
|
|
||||||
const allZones = useMemo<string[]>(() => {
|
const allZones = useMemo<string[]>(() => {
|
||||||
if (filterList?.zones) {
|
if (filterList?.zones) {
|
||||||
return filterList.zones;
|
return filterList.zones;
|
||||||
@ -169,60 +166,12 @@ export default function SearchFilterGroup({
|
|||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
<SearchFilterDialog />
|
<SearchFilterDialog
|
||||||
{filters.includes("date") && (
|
filter={filter}
|
||||||
<CalendarRangeFilterButton
|
filterValues={filterValues}
|
||||||
range={
|
groups={groups}
|
||||||
filter?.after == undefined || filter?.before == undefined
|
onUpdateFilter={onUpdateFilter}
|
||||||
? undefined
|
/>
|
||||||
: {
|
|
||||||
from: new Date(filter.after * 1000),
|
|
||||||
to: new Date(filter.before * 1000),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
defaultText={isMobile ? "Dates" : "All Dates"}
|
|
||||||
updateSelectedRange={onUpdateSelectedRange}
|
|
||||||
/>
|
|
||||||
)}
|
|
||||||
{filters.includes("time") && (
|
|
||||||
<TimeRangeFilterButton
|
|
||||||
config={config}
|
|
||||||
timeRange={filter?.time_range}
|
|
||||||
updateTimeRange={(time_range) =>
|
|
||||||
onUpdateFilter({ ...filter, time_range })
|
|
||||||
}
|
|
||||||
/>
|
|
||||||
)}
|
|
||||||
{filters.includes("zone") && allZones.length > 0 && (
|
|
||||||
<ZoneFilterButton
|
|
||||||
allZones={filterValues.zones}
|
|
||||||
selectedZones={filter?.zones}
|
|
||||||
updateZoneFilter={(newZones) =>
|
|
||||||
onUpdateFilter({ ...filter, zones: newZones })
|
|
||||||
}
|
|
||||||
/>
|
|
||||||
)}
|
|
||||||
{filters.includes("sub") && (
|
|
||||||
<SubFilterButton
|
|
||||||
allSubLabels={allSubLabels}
|
|
||||||
selectedSubLabels={filter?.sub_labels}
|
|
||||||
updateSubLabelFilter={(newSubLabels) =>
|
|
||||||
onUpdateFilter({ ...filter, sub_labels: newSubLabels })
|
|
||||||
}
|
|
||||||
/>
|
|
||||||
)}
|
|
||||||
{config?.semantic_search?.enabled &&
|
|
||||||
filters.includes("source") &&
|
|
||||||
!filter?.search_type?.includes("similarity") && (
|
|
||||||
<SearchTypeButton
|
|
||||||
selectedSearchSources={
|
|
||||||
filter?.search_type ?? ["thumbnail", "description"]
|
|
||||||
}
|
|
||||||
updateSearchSourceFilter={(newSearchSource) =>
|
|
||||||
onUpdateFilter({ ...filter, search_type: newSearchSource })
|
|
||||||
}
|
|
||||||
/>
|
|
||||||
)}
|
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -65,9 +65,7 @@ export function PlatformAwareSheet({
|
|||||||
if (isMobile) {
|
if (isMobile) {
|
||||||
return (
|
return (
|
||||||
<MobilePage open={open} onOpenChange={onOpenChange}>
|
<MobilePage open={open} onOpenChange={onOpenChange}>
|
||||||
<Button asChild onClick={() => onOpenChange(!open)}>
|
<Button asChild>{trigger}</Button>
|
||||||
{trigger}
|
|
||||||
</Button>
|
|
||||||
<MobilePageContent className="max-h-[75dvh] overflow-hidden px-4">
|
<MobilePageContent className="max-h-[75dvh] overflow-hidden px-4">
|
||||||
{content}
|
{content}
|
||||||
</MobilePageContent>
|
</MobilePageContent>
|
||||||
|
|||||||
@ -3,9 +3,37 @@ import { FaCog } from "react-icons/fa";
|
|||||||
import { useState } from "react";
|
import { useState } from "react";
|
||||||
import { PlatformAwareSheet } from "./PlatformAwareDialog";
|
import { PlatformAwareSheet } from "./PlatformAwareDialog";
|
||||||
import { Button } from "@/components/ui/button";
|
import { Button } from "@/components/ui/button";
|
||||||
|
import { CamerasFilterContent } from "@/components/filter/CamerasFilterButton";
|
||||||
|
import useSWR from "swr";
|
||||||
|
import { SearchFilter, SearchSource } from "@/types/search";
|
||||||
|
import { CameraGroupConfig } from "@/types/frigateConfig";
|
||||||
|
|
||||||
|
type SearchFilterDialogProps = {
|
||||||
|
filter?: SearchFilter;
|
||||||
|
filterValues: {
|
||||||
|
cameras: string[];
|
||||||
|
labels: string[];
|
||||||
|
zones: string[];
|
||||||
|
search_type: SearchSource[];
|
||||||
|
};
|
||||||
|
groups: [string, CameraGroupConfig][];
|
||||||
|
onUpdateFilter: (filter: SearchFilter) => void;
|
||||||
|
};
|
||||||
|
export default function SearchFilterDialog({
|
||||||
|
filter,
|
||||||
|
filterValues,
|
||||||
|
groups,
|
||||||
|
onUpdateFilter,
|
||||||
|
}: SearchFilterDialogProps) {
|
||||||
|
// data
|
||||||
|
|
||||||
|
const { data: allSubLabels } = useSWR(["sub_labels", { split_joined: 1 }]);
|
||||||
|
const [currentCameras, setCurrentCameras] = useState<string[] | undefined>(
|
||||||
|
filter?.cameras,
|
||||||
|
);
|
||||||
|
|
||||||
|
// state
|
||||||
|
|
||||||
type SearchFilterDialogProps = {};
|
|
||||||
export default function SearchFilterDialog({}: SearchFilterDialogProps) {
|
|
||||||
const [open, setOpen] = useState(false);
|
const [open, setOpen] = useState(false);
|
||||||
|
|
||||||
const trigger = (
|
const trigger = (
|
||||||
@ -14,7 +42,20 @@ export default function SearchFilterDialog({}: SearchFilterDialogProps) {
|
|||||||
More Filters
|
More Filters
|
||||||
</Button>
|
</Button>
|
||||||
);
|
);
|
||||||
const content = <></>;
|
const content = (
|
||||||
|
<>
|
||||||
|
<CamerasFilterContent
|
||||||
|
allCameras={filterValues.cameras}
|
||||||
|
currentCameras={currentCameras}
|
||||||
|
groups={groups}
|
||||||
|
setCurrentCameras={setCurrentCameras}
|
||||||
|
setOpen={setOpen}
|
||||||
|
updateCameraFilter={(newCameras) => {
|
||||||
|
onUpdateFilter({ ...filter, cameras: newCameras });
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<PlatformAwareSheet
|
<PlatformAwareSheet
|
||||||
@ -26,3 +67,59 @@ export default function SearchFilterDialog({}: SearchFilterDialogProps) {
|
|||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {filters.includes("date") && (
|
||||||
|
<CalendarRangeFilterButton
|
||||||
|
range={
|
||||||
|
filter?.after == undefined || filter?.before == undefined
|
||||||
|
? undefined
|
||||||
|
: {
|
||||||
|
from: new Date(filter.after * 1000),
|
||||||
|
to: new Date(filter.before * 1000),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
defaultText={isMobile ? "Dates" : "All Dates"}
|
||||||
|
updateSelectedRange={onUpdateSelectedRange}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
{filters.includes("time") && (
|
||||||
|
<TimeRangeFilterButton
|
||||||
|
config={config}
|
||||||
|
timeRange={filter?.time_range}
|
||||||
|
updateTimeRange={(time_range) =>
|
||||||
|
onUpdateFilter({ ...filter, time_range })
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
{filters.includes("zone") && allZones.length > 0 && (
|
||||||
|
<ZoneFilterButton
|
||||||
|
allZones={filterValues.zones}
|
||||||
|
selectedZones={filter?.zones}
|
||||||
|
updateZoneFilter={(newZones) =>
|
||||||
|
onUpdateFilter({ ...filter, zones: newZones })
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
{filters.includes("sub") && (
|
||||||
|
<SubFilterButton
|
||||||
|
allSubLabels={allSubLabels}
|
||||||
|
selectedSubLabels={filter?.sub_labels}
|
||||||
|
updateSubLabelFilter={(newSubLabels) =>
|
||||||
|
onUpdateFilter({ ...filter, sub_labels: newSubLabels })
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
{config?.semantic_search?.enabled &&
|
||||||
|
filters.includes("source") &&
|
||||||
|
!filter?.search_type?.includes("similarity") && (
|
||||||
|
<SearchTypeButton
|
||||||
|
selectedSearchSources={
|
||||||
|
filter?.search_type ?? ["thumbnail", "description"]
|
||||||
|
}
|
||||||
|
updateSearchSourceFilter={(newSearchSource) =>
|
||||||
|
onUpdateFilter({ ...filter, search_type: newSearchSource })
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
*/
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user