Skip to content
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

UI tweaks #10946

Merged
merged 5 commits into from
Apr 11, 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/review.py
Original file line number Diff line number Diff line change
Expand Up @@ -439,7 +439,7 @@ def motion_activity():
# normalize data
motion = (
df["motion"]
.resample(f"{scale}S")
.resample(f"{scale}s")
.apply(lambda x: max(x, key=abs, default=0.0))
.fillna(0.0)
.to_frame()
Expand Down
4 changes: 3 additions & 1 deletion web/src/components/filter/ReviewFilterGroup.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -693,7 +693,9 @@ function ShowMotionOnlyButton({
variant={motionOnlyButton ? "select" : "default"}
onClick={() => setMotionOnlyButton(!motionOnlyButton)}
>
<FaRunning />
<FaRunning
className={`${motionOnlyButton ? "text-selected-foreground" : "text-secondary-foreground"}`}
/>
</Button>
</div>
</>
Expand Down
16 changes: 5 additions & 11 deletions web/src/components/navigation/Bottombar.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
import { navbarLinks } from "@/pages/site-navigation";
import NavItem from "./NavItem";
import { IoIosWarning } from "react-icons/io";
import { Drawer, DrawerContent, DrawerTrigger } from "../ui/drawer";
Expand All @@ -9,20 +8,15 @@ import { useMemo } from "react";
import useStats from "@/hooks/use-stats";
import GeneralSettings from "../settings/GeneralSettings";
import AccountSettings from "../settings/AccountSettings";
import useNavigation from "@/hooks/use-navigation";

function Bottombar() {
const navItems = useNavigation("secondary");

return (
<div className="absolute h-16 inset-x-4 bottom-0 flex flex-row items-center justify-between">
{navbarLinks.map((item) => (
<NavItem
className=""
variant="secondary"
key={item.id}
Icon={item.icon}
title={item.title}
url={item.url}
dev={item.dev}
/>
{navItems.map((item) => (
<NavItem key={item.id} item={item} Icon={item.icon} />
))}
<GeneralSettings />
<AccountSettings />
Expand Down
28 changes: 10 additions & 18 deletions web/src/components/navigation/NavItem.tsx
Original file line number Diff line number Diff line change
@@ -1,13 +1,13 @@
import { IconType } from "react-icons";
import { NavLink } from "react-router-dom";
import { ENV } from "@/env";
import {
Tooltip,
TooltipContent,
TooltipTrigger,
} from "@/components/ui/tooltip";
import { isDesktop } from "react-device-detect";
import { TooltipPortal } from "@radix-ui/react-tooltip";
import { NavData } from "@/types/navigation";
import { IconType } from "react-icons";

const variants = {
primary: {
Expand All @@ -21,37 +21,29 @@ const variants = {
};

type NavItemProps = {
className: string;
variant?: "primary" | "secondary";
className?: string;
item: NavData;
Icon: IconType;
title: string;
url: string;
dev?: boolean;
onClick?: () => void;
};

export default function NavItem({
className,
variant = "primary",
item,
Icon,
title,
url,
dev,
onClick,
}: NavItemProps) {
const shouldRender = dev ? ENV !== "production" : true;

if (!shouldRender) {
if (item.enabled == false) {
return;
}

const content = (
<NavLink
to={url}
to={item.url}
onClick={onClick}
className={({ isActive }) =>
`${className} flex flex-col justify-center items-center rounded-lg ${
variants[variant][isActive ? "active" : "inactive"]
`flex flex-col justify-center items-center rounded-lg ${className ?? ""} ${
variants[item.variant ?? "primary"][isActive ? "active" : "inactive"]
}`
}
>
Expand All @@ -65,7 +57,7 @@ export default function NavItem({
<TooltipTrigger>{content}</TooltipTrigger>
<TooltipPortal>
<TooltipContent side="right">
<p>{title}</p>
<p>{item.title}</p>
</TooltipContent>
</TooltipPortal>
</Tooltip>
Expand Down
8 changes: 4 additions & 4 deletions web/src/components/navigation/Sidebar.tsx
Original file line number Diff line number Diff line change
@@ -1,14 +1,16 @@
import Logo from "../Logo";
import { navbarLinks } from "@/pages/site-navigation";
import NavItem from "./NavItem";
import { CameraGroupSelector } from "../filter/CameraGroupSelector";
import { useLocation } from "react-router-dom";
import GeneralSettings from "../settings/GeneralSettings";
import AccountSettings from "../settings/AccountSettings";
import useNavigation from "@/hooks/use-navigation";

function Sidebar() {
const location = useLocation();

const navbarLinks = useNavigation();

return (
<aside className="absolute w-[52px] z-10 left-o inset-y-0 overflow-y-auto scrollbar-hidden py-4 flex flex-col justify-between bg-background_alt border-r border-secondary-highlight">
<span tabIndex={0} className="sr-only" />
Expand All @@ -22,10 +24,8 @@ function Sidebar() {
<div key={item.id}>
<NavItem
className={`mx-[10px] ${showCameraGroups ? "mb-2" : "mb-4"}`}
item={item}
Icon={item.icon}
title={item.title}
url={item.url}
dev={item.dev}
/>
{showCameraGroups && <CameraGroupSelector className="mb-4" />}
</div>
Expand Down
59 changes: 59 additions & 0 deletions web/src/hooks/use-navigation.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
import Logo from "@/components/Logo";
import { ENV } from "@/env";
import { FrigateConfig } from "@/types/frigateConfig";
import { NavData } from "@/types/navigation";
import { useMemo } from "react";
import { FaCompactDisc, FaVideo } from "react-icons/fa";
import { LuConstruction } from "react-icons/lu";
import { MdVideoLibrary } from "react-icons/md";
import useSWR from "swr";

export default function useNavigation(
variant: "primary" | "secondary" = "primary",
) {
const { data: config } = useSWR<FrigateConfig>("config");

return useMemo(
() =>
[
{
id: 1,
variant,
icon: FaVideo,
title: "Live",
url: "/",
},
{
id: 2,
variant,
icon: MdVideoLibrary,
title: "Review",
url: "/review",
},
{
id: 3,
variant,
icon: FaCompactDisc,
title: "Export",
url: "/export",
},
{
id: 5,
variant,
icon: Logo,
title: "Frigate+",
url: "/plus",
enabled: config?.plus?.enabled == true,
},
{
id: 4,
variant,
icon: LuConstruction,
title: "UI Playground",
url: "/playground",
enabled: ENV !== "production",
},
] as NavData[],
[config?.plus.enabled, variant],
);
}
38 changes: 0 additions & 38 deletions web/src/pages/site-navigation.ts

This file was deleted.

10 changes: 10 additions & 0 deletions web/src/types/navigation.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
import { IconType } from "react-icons";

export type NavData = {
id: number;
variant?: "primary" | "secondary";
icon: IconType;
title: string;
url: string;
enabled?: boolean;
};
61 changes: 36 additions & 25 deletions web/src/views/events/EventView.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -295,6 +295,7 @@ export default function EventView({
filter={filter}
timeRange={timeRange}
startTime={startTime}
loading={severity != severityToggle}
markItemAsReviewed={markItemAsReviewed}
markAllItemsAsReviewed={markAllItemsAsReviewed}
onSelectReview={onSelectReview}
Expand Down Expand Up @@ -334,6 +335,7 @@ type DetectionReviewProps = {
filter?: ReviewFilter;
timeRange: { before: number; after: number };
startTime?: number;
loading: boolean;
markItemAsReviewed: (review: ReviewSegment) => void;
markAllItemsAsReviewed: (currentItems: ReviewSegment[]) => void;
onSelectReview: (review: ReviewSegment, ctrl: boolean) => void;
Expand All @@ -349,6 +351,7 @@ function DetectionReview({
filter,
timeRange,
startTime,
loading,
markItemAsReviewed,
markAllItemsAsReviewed,
onSelectReview,
Expand Down Expand Up @@ -600,33 +603,41 @@ function DetectionReview({
</div>
<div className="w-[65px] md:w-[110px] flex flex-row">
<div className="w-[55px] md:w-[100px] overflow-y-auto no-scrollbar">
<EventReviewTimeline
segmentDuration={segmentDuration}
timestampSpread={15}
timelineStart={timeRange.before}
timelineEnd={timeRange.after}
showMinimap={showMinimap && !previewTime}
minimapStartTime={minimapBounds.start}
minimapEndTime={minimapBounds.end}
showHandlebar={previewTime != undefined}
handlebarTime={previewTime}
visibleTimestamps={visibleTimestamps}
events={reviewItems?.all ?? []}
severityType={severity}
contentRef={contentRef}
timelineRef={reviewTimelineRef}
dense={isMobile}
/>
{loading ? (
<Skeleton className="size-full" />
) : (
<EventReviewTimeline
segmentDuration={segmentDuration}
timestampSpread={15}
timelineStart={timeRange.before}
timelineEnd={timeRange.after}
showMinimap={showMinimap && !previewTime}
minimapStartTime={minimapBounds.start}
minimapEndTime={minimapBounds.end}
showHandlebar={previewTime != undefined}
handlebarTime={previewTime}
visibleTimestamps={visibleTimestamps}
events={reviewItems?.all ?? []}
severityType={severity}
contentRef={contentRef}
timelineRef={reviewTimelineRef}
dense={isMobile}
/>
)}
</div>
<div className="w-[10px]">
<SummaryTimeline
reviewTimelineRef={reviewTimelineRef}
timelineStart={timeRange.before}
timelineEnd={timeRange.after}
segmentDuration={segmentDuration}
events={reviewItems?.all ?? []}
severityType={severity}
/>
{loading ? (
<Skeleton className="w-full" />
) : (
<SummaryTimeline
reviewTimelineRef={reviewTimelineRef}
timelineStart={timeRange.before}
timelineEnd={timeRange.after}
segmentDuration={segmentDuration}
events={reviewItems?.all ?? []}
severityType={severity}
/>
)}
</div>
</div>
</>
Expand Down
29 changes: 22 additions & 7 deletions web/src/views/events/RecordingView.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ import MobileTimelineDrawer from "@/components/overlay/MobileTimelineDrawer";
import MobileReviewSettingsDrawer from "@/components/overlay/MobileReviewSettingsDrawer";
import Logo from "@/components/Logo";
import { Skeleton } from "@/components/ui/skeleton";
import { FaVideo } from "react-icons/fa";

const SEGMENT_DURATION = 30;

Expand Down Expand Up @@ -251,14 +252,28 @@ export function RecordingView({
{isMobile && (
<Logo className="absolute inset-x-1/2 -translate-x-1/2 h-8" />
)}
<Button
className="flex items-center gap-2 rounded-lg"
size="sm"
onClick={() => navigate(-1)}
<div
className={`flex items-center gap-2 ${isMobile ? "landscape:flex-col" : ""}`}
>
<IoMdArrowRoundBack className="size-5" size="small" />
{isDesktop && <div className="text-primary">Back</div>}
</Button>
<Button
className={`flex items-center gap-2.5 rounded-lg`}
size="sm"
onClick={() => navigate(-1)}
>
<IoMdArrowRoundBack className="size-5 text-secondary-foreground" />
{isDesktop && <div className="text-primary">Back</div>}
</Button>
<Button
className="flex items-center gap-2.5 rounded-lg"
size="sm"
onClick={() => {
navigate(`/#${mainCamera}`);
}}
>
<FaVideo className="size-5 text-secondary-foreground" />
{isDesktop && <div className="text-primary">Live</div>}
</Button>
</div>
<div className="flex items-center justify-end gap-2">
<MobileCameraDrawer
allCameras={allCameras}
Expand Down
Loading
Loading