mirror of
https://github.com/blakeblackshear/frigate.git
synced 2026-07-25 21:29:01 +03:00
Compare commits
3
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
3efb224f99 | ||
|
|
12dd242151 | ||
|
|
a573ea49bf |
Generated
+532
-530
File diff suppressed because it is too large
Load Diff
+6
-6
@@ -66,7 +66,7 @@
|
||||
"i18next-http-backend": "^3.0.1",
|
||||
"idb-keyval": "^6.2.1",
|
||||
"immer": "^10.1.1",
|
||||
"js-yaml": "^4.1.1",
|
||||
"js-yaml": "^5.2.2",
|
||||
"konva": "^10.2.3",
|
||||
"lodash": "^4.18.1",
|
||||
"lucide-react": "^0.577.0",
|
||||
@@ -87,7 +87,7 @@
|
||||
"react-markdown": "^9.0.1",
|
||||
"react-router-dom": "^6.30.3",
|
||||
"react-swipeable": "^7.0.2",
|
||||
"react-zoom-pan-pinch": "^3.7.0",
|
||||
"react-zoom-pan-pinch": "3.6.1",
|
||||
"remark-gfm": "^4.0.0",
|
||||
"scroll-into-view-if-needed": "^3.1.0",
|
||||
"sonner": "^2.0.7",
|
||||
@@ -112,14 +112,14 @@
|
||||
"@types/react": "^19.2.14",
|
||||
"@types/react-dom": "^19.2.3",
|
||||
"@types/strftime": "^0.9.8",
|
||||
"@typescript-eslint/eslint-plugin": "^7.5.0",
|
||||
"@typescript-eslint/parser": "^7.5.0",
|
||||
"@typescript-eslint/eslint-plugin": "^8.65.0",
|
||||
"@typescript-eslint/parser": "^8.65.0",
|
||||
"@vitejs/plugin-react-swc": "^3.8.0",
|
||||
"@vitest/coverage-v8": "^3.0.7",
|
||||
"autoprefixer": "^10.4.20",
|
||||
"eslint": "^8.57.0",
|
||||
"eslint": "^10.8.0",
|
||||
"eslint-config-prettier": "^9.1.0",
|
||||
"eslint-plugin-jest": "^28.2.0",
|
||||
"eslint-plugin-jest": "^28.14.0",
|
||||
"eslint-plugin-prettier": "^5.0.1",
|
||||
"eslint-plugin-react-hooks": "^5.2.0",
|
||||
"eslint-plugin-react-refresh": "^0.4.8",
|
||||
|
||||
@@ -125,5 +125,7 @@
|
||||
"baby": "Baby",
|
||||
"baby_stroller": "Baby Stroller",
|
||||
"rickshaw": "Rickshaw",
|
||||
"rodent": "Rodent"
|
||||
"rodent": "Rodent",
|
||||
"possum": "Possum",
|
||||
"garbage_truck": "Garbage Truck"
|
||||
}
|
||||
|
||||
@@ -0,0 +1,39 @@
|
||||
import { SVGProps } from "react";
|
||||
|
||||
/**
|
||||
* Skunk silhouette for the `skunk` object label.
|
||||
*
|
||||
* react-icons has no skunk in any of its packs. The usable stand-ins are
|
||||
* either squirrels, which are indistinguishable from the `squirrel` label, or
|
||||
* animals such as porcupine and hedgehog that are themselves Frigate+
|
||||
* candidate labels.
|
||||
*
|
||||
* Adapted from "skunk silhouette" by dear_theophilus, published by Openclipart
|
||||
* and released into the public domain, which permits reproduction,
|
||||
* distribution and derivative works:
|
||||
* https://openclipart.org/detail/170808/skunk-silhouette-by-dear_theophilus-170808
|
||||
*
|
||||
* Changes from the original: the unused Inkscape text region was dropped, the
|
||||
* layer translate was folded into the viewBox, the viewBox was padded to give
|
||||
* the same optical margin as the surrounding react-icons, and the fill was
|
||||
* switched to currentColor. The white back stripe is negative space in a
|
||||
* single path under the default nonzero fill rule, so no fill-rule override is
|
||||
* needed here.
|
||||
*
|
||||
* Sized to sit alongside the react-icons set: currentColor fill and a 1em
|
||||
* default box.
|
||||
*/
|
||||
export default function SkunkIcon(props: SVGProps<SVGSVGElement>) {
|
||||
return (
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
viewBox="96.87 93.174 110.58 109.81"
|
||||
fill="currentColor"
|
||||
height="1em"
|
||||
width="1em"
|
||||
{...props}
|
||||
>
|
||||
<path d="m169.58 196.93c-0.18864-0.85889 0.0134-1.3274 0.99603-2.31 0.67796-0.67797 1.7065-1.3304 2.2857-1.4498 1.3854-0.2857 5.2538-1.7692 5.8796-2.2549 0.27087-0.2102 0.4925-0.61406 0.4925-0.89746 0-0.77202-6.6659-6.0863-7.6343-6.0863-0.46161 0-2.0798 0.36441-3.596 0.80981-3.4618 1.0169-10.449 1.4599-16.39 1.0391-7.1408-0.50577-6.6473-0.58559-8.3439 1.3494-0.8327 0.94969-1.9642 2.5979-2.5144 3.6627-1.164 2.2525-4.5952 6.5163-5.8025 7.2106-1.0288 0.59154-4.5445 0.64844-5.9949 0.097-0.71722-0.27269-1.4467-0.28055-2.184-0.0235-2.4986 0.871-1.4597-1.341 1.2885-2.7437 0.78536-0.40084 1.9099-1.3016 2.499-2.0016 1.0163-1.2078 1.0634-1.4452 0.92116-4.6468-0.12622-2.8412-0.38301-3.9062-1.6262-6.7446-2.6443-6.0373-7.3573-11.099-12.678-13.617-1.5889-0.75179-2.62-0.93285-5.3302-0.93595-3.0016-0.004-3.6532-0.14511-6.107-1.3278-5.2109-2.5116-5.8227-4.8841-1.4913-5.7833 1.8591-0.38596 2.618-0.82052 4.4338-2.539 1.2049-1.1403 2.7581-2.3618 3.4516-2.7146 1.9684-1.001 7.7872-0.84521 13.816 0.37 5.7138 1.1517 10.339 1.3571 13.488 0.59905 1.1919-0.28691 4.6492-1.5446 7.683-2.7949 9.8002-4.0389 14.311-4.9373 19.109-3.806 1.4086 0.33208 2.7106 0.4572 2.8935 0.27806 1.0374-1.0164-1.1645-3.1382-6.6365-6.3947-5.6439-3.3588-9.8091-9.1231-10.764-14.897-0.60953-3.6839 0.72362-12.081 1.2923-13.596 0.56871-1.5152 1.4436-3.3754 1.9442-4.1338 2.9223-4.4276 8.177-9.1772 12.256-11.078 6.691-3.1177 17.754-2.3107 25.847 1.8855 3.5353 1.8329 6.7739 4.7994 8.0035 7.3309 1.3563 2.7924 2.3788 9.4802 1.6321 10.675-0.28831 0.46149-1.0187 1.9915-1.6231 3.4001-1.0066 2.3458-2.3392 4.3362-2.9009 4.3326-0.12475-0.00078-0.81827-1.1453-1.5412-2.5434-1.642-3.1756-6.3156-7.6992-7.9544-7.6992-0.58481 0-2.2156 0.52248-3.624 1.1611-3.1804 1.4421-4.0269 2.8569-3.7769 6.3127 0.24247 3.3521 2.1549 6.8946 9.8511 18.247 3.3307 4.9132 4.5143 10.219 3.7106 16.634-0.40898 3.2645-0.77579 4.5016-2.1396 7.2158-0.90527 1.8017-2.1169 3.8932-2.6924 4.6478-1.3686 1.7944-3.6318 10.202-4.0199 14.935-0.34632 4.2221-1.4143 6.7775-3.7479 8.9681l-1.5853 1.4881s-10.87 1.3087-11.076 0.36929zm-0.94988-19.482c5.4569-0.84327 10.316-2.9685 15.305-6.694 2.2799-1.7026 7.2867-9.9628 7.5372-11.17 1.1907-5.7355-0.81743-10.348-8.1457-18.709-1.1818-1.3484-3.251-4.3132-4.5981-6.5886 0 0-1.8246-3.0395-2.2503-4.734-0.4037-1.6068-0.24133-4.9643-0.24133-4.9643-0.0233-3.0587 1.7125-7.8176 2.7258-8.9913 2.4126-2.7945 5.1792-3.93 8.9395-3.6689 2.4539 0.17036 6.2631 1.6358 6.8904 2.6509 0.46463 0.75179 1.7776 0.45185 1.7776-0.40608 0-2.1062-2.1313-4.3793-5.8702-6.261-8.5709-4.3134-18.12-1.6112-22.089 6.2506-0.84198 1.6679-1.9752 7.8544-1.5778 9.9776 0.98137 5.2435 7.314 15.066 15.216 23.602 2.3474 2.5357 2.891 3.3814 3.2505 5.0579 0.62386 2.909 0.53762 3.9625-0.45679 5.5804-2.2494 3.6597-6.0764 5.9532-12.424 7.446-5.4931 1.2918-14.085 0.80251-19.932-1.135-2.8291-0.93745-3.2146-1.1799-3.3204-2.0877-0.26031-2.2347 5.8542-6.1292 12.294-7.8306 1.712-0.45228 3.469-1.013 3.9045-1.2461 2.613-1.3984-5.139-1.3894-10.001 0.0116-1.4244 0.41047-4.1726 1.3784-6.107 2.151-4.0916 1.6341-9.9725 3.2146-13.412 3.6046-1.9171 0.21734-3.2711 0.0654-6.6311-0.74437-6.6813-1.6101-8.8678-1.2892-6.6918 0.98197 1.1258 1.1751 6.0865 4.3046 6.8234 4.3046 0.22537 0 0.58888 0.3347 0.80781 0.74378 0.50815 0.94949 5.6856 4.2866 11.253 7.2529 3.5996 1.918 5.4022 2.5812 10.638 3.9141 3.4672 0.88264 7.7224 1.7278 9.456 1.8781 1.7336 0.15029 3.1675 0.30072 3.1864 0.33427 0.0189 0.0336 1.7032-0.1969 3.743-0.5121zm-55.67-16.07c0.0817-0.43098-0.25131-0.85888-0.89769-1.1534-0.8518-0.38811-1.1373-0.35853-1.6687 0.17289-0.53858 0.53858-0.55739 0.74197-0.11767 1.2718 0.67722 0.816 2.5119 0.61688 2.6841-0.29131zm7.5598-0.11521c0-0.95213-0.94275-1.7372-1.4622-1.2177-0.30143 0.30141 0.67899 2.2151 1.1348 2.2151 0.18008 0 0.32742-0.44882 0.32742-0.99738z" />
|
||||
</svg>
|
||||
);
|
||||
}
|
||||
@@ -20,6 +20,26 @@ function formatCalendarDay(day: Date): string {
|
||||
return `${y}-${m}-${d}`;
|
||||
}
|
||||
|
||||
function getTodayInTimezone(timezone?: string): {
|
||||
year: number;
|
||||
month: number;
|
||||
day: number;
|
||||
offset: number;
|
||||
} {
|
||||
const now = new Date();
|
||||
const offset = Math.round(getUTCOffset(now, timezone));
|
||||
|
||||
// shifting by the offset makes the UTC getters read the timezone's wall clock
|
||||
const wallClock = new Date(now.getTime() + offset * 60000);
|
||||
|
||||
return {
|
||||
year: wallClock.getUTCFullYear(),
|
||||
month: wallClock.getUTCMonth(),
|
||||
day: wallClock.getUTCDate(),
|
||||
offset,
|
||||
};
|
||||
}
|
||||
|
||||
type ReviewActivityCalendarProps = {
|
||||
reviewSummary?: ReviewSummary;
|
||||
recordingsSummary?: RecordingsSummary;
|
||||
@@ -37,12 +57,14 @@ export default function ReviewActivityCalendar({
|
||||
const [weekStartsOn] = useUserPersistence("weekStartsOn", 0);
|
||||
|
||||
const disabledDates = useMemo(() => {
|
||||
const tomorrow = new Date();
|
||||
tomorrow.setHours(tomorrow.getHours() + 24, -1, 0, 0);
|
||||
const future = new Date();
|
||||
future.setFullYear(tomorrow.getFullYear() + 10);
|
||||
return { from: tomorrow, to: future };
|
||||
}, []);
|
||||
// day cells are TZDate in `timezone`, so the cutoff must be a real instant
|
||||
const { year, month, day, offset } = getTodayInTimezone(timezone);
|
||||
// midday: ranges match by calendar day, so this dodges DST edges
|
||||
const from = new Date(Date.UTC(year, month, day + 1, 12) - offset * 60000);
|
||||
const to = new Date(from);
|
||||
to.setFullYear(from.getFullYear() + 10);
|
||||
return { from, to };
|
||||
}, [timezone]);
|
||||
|
||||
const modifiers = useMemo(() => {
|
||||
const recordingsSet = new Set<string>();
|
||||
@@ -182,48 +204,25 @@ export function TimezoneAwareCalendar({
|
||||
};
|
||||
}, [recordingsSummary]);
|
||||
|
||||
const timezoneOffset = useMemo(
|
||||
() =>
|
||||
timezone ? Math.round(getUTCOffset(new Date(), timezone)) : undefined,
|
||||
// callers pre-shift dates so the local clock reads `timezone`, so boundaries
|
||||
// are built in local time rather than as instants
|
||||
const { year, month, day } = useMemo(
|
||||
() => getTodayInTimezone(timezone),
|
||||
[timezone],
|
||||
);
|
||||
|
||||
const disabledDates = useMemo(() => {
|
||||
const tomorrow = new Date();
|
||||
// midday: ranges match by calendar day, so this dodges DST edges
|
||||
const from = new Date(year, month, day + 1, 12);
|
||||
const to = new Date(from);
|
||||
to.setFullYear(from.getFullYear() + 10);
|
||||
return { from, to };
|
||||
}, [year, month, day]);
|
||||
|
||||
if (timezoneOffset) {
|
||||
tomorrow.setHours(
|
||||
tomorrow.getHours() + 24,
|
||||
tomorrow.getMinutes() + timezoneOffset,
|
||||
0,
|
||||
0,
|
||||
);
|
||||
} else {
|
||||
tomorrow.setHours(tomorrow.getHours() + 24, -1, 0, 0);
|
||||
}
|
||||
|
||||
const future = new Date();
|
||||
future.setFullYear(tomorrow.getFullYear() + 10);
|
||||
return { from: tomorrow, to: future };
|
||||
}, [timezoneOffset]);
|
||||
|
||||
const today = useMemo(() => {
|
||||
if (!timezoneOffset) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
const date = new Date();
|
||||
const utc = Date.UTC(
|
||||
date.getUTCFullYear(),
|
||||
date.getUTCMonth(),
|
||||
date.getUTCDate(),
|
||||
date.getUTCHours(),
|
||||
date.getUTCMinutes(),
|
||||
date.getUTCSeconds(),
|
||||
);
|
||||
const todayUtc = new Date(utc);
|
||||
todayUtc.setMinutes(todayUtc.getMinutes() + timezoneOffset, 0, 0);
|
||||
return todayUtc;
|
||||
}, [timezoneOffset]);
|
||||
const today = useMemo(
|
||||
() => new Date(year, month, day, 12),
|
||||
[year, month, day],
|
||||
);
|
||||
|
||||
return (
|
||||
<Calendar
|
||||
|
||||
@@ -1,9 +1,12 @@
|
||||
import { IconName } from "@/components/icons/IconPicker";
|
||||
import SkunkIcon from "@/components/icons/SkunkIcon";
|
||||
import { FrigateConfig } from "@/types/frigateConfig";
|
||||
import { EventType } from "@/types/search";
|
||||
import { BsPersonWalking } from "react-icons/bs";
|
||||
import {
|
||||
FaAmazon,
|
||||
FaBaby,
|
||||
FaBabyCarriage,
|
||||
FaBicycle,
|
||||
FaBus,
|
||||
FaCarSide,
|
||||
@@ -24,6 +27,8 @@ import {
|
||||
FaUsps,
|
||||
} from "react-icons/fa";
|
||||
import {
|
||||
GiBarbecue,
|
||||
GiCow,
|
||||
GiDeer,
|
||||
GiFox,
|
||||
GiGoat,
|
||||
@@ -32,7 +37,9 @@ import {
|
||||
GiPostStamp,
|
||||
GiRabbit,
|
||||
GiRaccoonHead,
|
||||
GiRat,
|
||||
GiSailboat,
|
||||
GiSeatedMouse,
|
||||
GiSoundWaves,
|
||||
GiSquirrel,
|
||||
} from "react-icons/gi";
|
||||
@@ -40,6 +47,7 @@ import { LuBox, LuLassoSelect, LuScanBarcode } from "react-icons/lu";
|
||||
import * as LuIcons from "react-icons/lu";
|
||||
import { MdRecordVoiceOver } from "react-icons/md";
|
||||
import { PiBirdFill } from "react-icons/pi";
|
||||
import { HiMiniTruck } from "react-icons/hi2";
|
||||
|
||||
export function getAttributeLabels(config?: FrigateConfig) {
|
||||
if (!config) {
|
||||
@@ -74,6 +82,12 @@ export function getIconForLabel(
|
||||
|
||||
switch (label) {
|
||||
// objects
|
||||
case "baby":
|
||||
return <FaBaby key={iconKey} className={className} />;
|
||||
case "baby_stroller":
|
||||
return <FaBabyCarriage key={iconKey} className={className} />;
|
||||
case "bbq_grill":
|
||||
return <GiBarbecue key={iconKey} className={className} />;
|
||||
case "bear":
|
||||
return <GiPolarBear key={iconKey} className={className} />;
|
||||
case "bicycle":
|
||||
@@ -90,6 +104,8 @@ export function getIconForLabel(
|
||||
return <FaCarSide key={iconKey} className={className} />;
|
||||
case "cat":
|
||||
return <FaCat key={iconKey} className={className} />;
|
||||
case "cow":
|
||||
return <GiCow key={iconKey} className={className} />;
|
||||
case "deer":
|
||||
return <GiDeer key={iconKey} className={className} />;
|
||||
case "animal":
|
||||
@@ -98,6 +114,8 @@ export function getIconForLabel(
|
||||
return <FaDog key={iconKey} className={className} />;
|
||||
case "fox":
|
||||
return <GiFox key={iconKey} className={className} />;
|
||||
case "garbage_truck":
|
||||
return <HiMiniTruck key={iconKey} className={className} />;
|
||||
case "goat":
|
||||
return <GiGoat key={iconKey} className={className} />;
|
||||
case "horse":
|
||||
@@ -114,18 +132,22 @@ export function getIconForLabel(
|
||||
return <LuBox key={iconKey} className={className} />;
|
||||
case "person":
|
||||
return <BsPersonWalking key={iconKey} className={className} />;
|
||||
case "possum":
|
||||
return <GiSeatedMouse key={iconKey} className={className} />;
|
||||
case "rabbit":
|
||||
return <GiRabbit key={iconKey} className={className} />;
|
||||
case "raccoon":
|
||||
return <GiRaccoonHead key={iconKey} className={className} />;
|
||||
case "robot_lawnmower":
|
||||
return <FaHockeyPuck key={iconKey} className={className} />;
|
||||
case "rodent":
|
||||
return <GiRat key={iconKey} className={className} />;
|
||||
case "sports_ball":
|
||||
return <FaFootballBall key={iconKey} className={className} />;
|
||||
case "skunk":
|
||||
return <GiSquirrel key={iconKey} className={className} />;
|
||||
return <SkunkIcon key={iconKey} className={className} />;
|
||||
case "squirrel":
|
||||
return <LuIcons.LuSquirrel key={iconKey} className={className} />;
|
||||
return <GiSquirrel key={iconKey} className={className} />;
|
||||
case "umbrella":
|
||||
return <FaUmbrella key={iconKey} className={className} />;
|
||||
case "waste_bin":
|
||||
|
||||
Reference in New Issue
Block a user