mirror of
https://github.com/blakeblackshear/frigate.git
synced 2026-09-27 03:28:59 +03:00
Miscellaneous Fixes (0.17 beta) (#21575)
CI / AMD64 Build (push) Waiting to run
CI / ARM Build (push) Waiting to run
CI / Jetson Jetpack 6 (push) Waiting to run
CI / AMD64 Extra Build (push) Blocked by required conditions
CI / ARM Extra Build (push) Blocked by required conditions
CI / Synaptics Build (push) Blocked by required conditions
CI / Assemble and push default build (push) Blocked by required conditions
CI / AMD64 Build (push) Waiting to run
CI / ARM Build (push) Waiting to run
CI / Jetson Jetpack 6 (push) Waiting to run
CI / AMD64 Extra Build (push) Blocked by required conditions
CI / ARM Extra Build (push) Blocked by required conditions
CI / Synaptics Build (push) Blocked by required conditions
CI / Assemble and push default build (push) Blocked by required conditions
* icon improvements add type to getIconForLabel provide default icon for audio events * Add preferred language to review docs * prevent react Suspense crash during auth redirect add redirect-check guards to stop rendering lazy routes while navigation is pending (fixes some users seeing React error #426 when auth expires) * Uppsercase model name --------- Co-authored-by: Nicolas Mowen <nickmowen213@gmail.com>
This commit is contained in:
co-authored by
Nicolas Mowen
parent
f3543cfee2
commit
c08fa15724
@@ -28,6 +28,14 @@ export default function ProtectedRoute({
|
||||
}
|
||||
}, [auth.isLoading, auth.isAuthenticated, auth.user]);
|
||||
|
||||
// Show loading indicator during redirect to prevent React from attempting to render
|
||||
// lazy components, which would cause error #426 (suspension during synchronous navigation)
|
||||
if (isRedirectingToLogin()) {
|
||||
return (
|
||||
<ActivityIndicator className="absolute left-1/2 top-1/2 -translate-x-1/2 -translate-y-1/2" />
|
||||
);
|
||||
}
|
||||
|
||||
if (auth.isLoading) {
|
||||
return (
|
||||
<ActivityIndicator className="absolute left-1/2 top-1/2 -translate-x-1/2 -translate-y-1/2" />
|
||||
|
||||
@@ -181,7 +181,7 @@ export default function ReviewCard({
|
||||
key={`${object}-${idx}`}
|
||||
className="rounded-full bg-muted-foreground p-1"
|
||||
>
|
||||
{getIconForLabel(object, "size-3 text-white")}
|
||||
{getIconForLabel(object, "object", "size-3 text-white")}
|
||||
</div>
|
||||
))}
|
||||
{event.data.audio.map((audio, idx) => (
|
||||
@@ -189,7 +189,7 @@ export default function ReviewCard({
|
||||
key={`${audio}-${idx}`}
|
||||
className="rounded-full bg-muted-foreground p-1"
|
||||
>
|
||||
{getIconForLabel(audio, "size-3 text-white")}
|
||||
{getIconForLabel(audio, "audio", "size-3 text-white")}
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
|
||||
@@ -133,7 +133,11 @@ export default function SearchThumbnail({
|
||||
className={`z-0 flex items-center justify-between gap-1 space-x-1 bg-gray-500 bg-gradient-to-br from-gray-400 to-gray-500 text-xs capitalize`}
|
||||
onClick={() => onClick(searchResult, false, true)}
|
||||
>
|
||||
{getIconForLabel(objectLabel, "size-3 text-white")}
|
||||
{getIconForLabel(
|
||||
objectLabel,
|
||||
searchResult.data.type,
|
||||
"size-3 text-white",
|
||||
)}
|
||||
{Math.floor(
|
||||
(searchResult.data.score ??
|
||||
searchResult.data.top_score ??
|
||||
|
||||
@@ -1296,7 +1296,11 @@ function ObjectDetailsTab({
|
||||
{t("details.label")}
|
||||
</div>
|
||||
<div className="flex flex-row items-center gap-2 text-sm smart-capitalize">
|
||||
{getIconForLabel(search.label, "size-4 text-primary")}
|
||||
{getIconForLabel(
|
||||
search.label,
|
||||
search.data.type,
|
||||
"size-4 text-primary",
|
||||
)}
|
||||
{getTranslatedLabel(search.label, search.data.type)}
|
||||
{search.sub_label && ` (${search.sub_label})`}
|
||||
{isAdmin && search.end_time && (
|
||||
|
||||
@@ -665,6 +665,7 @@ export function TrackingDetails({
|
||||
>
|
||||
{getIconForLabel(
|
||||
event.sub_label ? event.label + "-verified" : event.label,
|
||||
event.data.type,
|
||||
"size-4 text-white",
|
||||
)}
|
||||
</div>
|
||||
|
||||
@@ -359,7 +359,11 @@ export default function LivePlayer({
|
||||
]),
|
||||
]
|
||||
.map((label) => {
|
||||
return getIconForLabel(label, "size-3 text-white");
|
||||
return getIconForLabel(
|
||||
label,
|
||||
"object",
|
||||
"size-3 text-white",
|
||||
);
|
||||
})
|
||||
.sort()}
|
||||
</Chip>
|
||||
|
||||
@@ -262,10 +262,18 @@ export default function PreviewThumbnailPlayer({
|
||||
onClick={() => onClick(review, false, true)}
|
||||
>
|
||||
{review.data.objects.sort().map((object) => {
|
||||
return getIconForLabel(object, "size-3 text-white");
|
||||
return getIconForLabel(
|
||||
object,
|
||||
"object",
|
||||
"size-3 text-white",
|
||||
);
|
||||
})}
|
||||
{review.data.audio.map((audio) => {
|
||||
return getIconForLabel(audio, "size-3 text-white");
|
||||
return getIconForLabel(
|
||||
audio,
|
||||
"audio",
|
||||
"size-3 text-white",
|
||||
);
|
||||
})}
|
||||
</Chip>
|
||||
</>
|
||||
|
||||
@@ -14,6 +14,7 @@ import { FrigateConfig } from "@/types/frigateConfig";
|
||||
import useSWR from "swr";
|
||||
import ActivityIndicator from "../indicators/activity-indicator";
|
||||
import { Event } from "@/types/event";
|
||||
import { EventType } from "@/types/search";
|
||||
import { getIconForLabel } from "@/utils/iconUtil";
|
||||
import { REVIEW_PADDING, ReviewSegment } from "@/types/review";
|
||||
import { LuChevronDown, LuCircle, LuChevronRight } from "react-icons/lu";
|
||||
@@ -346,22 +347,29 @@ function ReviewGroup({
|
||||
: null,
|
||||
);
|
||||
|
||||
const rawIconLabels: string[] = [
|
||||
const rawIconLabels: Array<{ label: string; type: EventType }> = [
|
||||
...(fetchedEvents
|
||||
? fetchedEvents.map((e) =>
|
||||
e.sub_label ? e.label + "-verified" : e.label,
|
||||
)
|
||||
: (review.data?.objects ?? [])),
|
||||
...(review.data?.audio ?? []),
|
||||
? fetchedEvents.map((e) => ({
|
||||
label: e.sub_label ? e.label + "-verified" : e.label,
|
||||
type: e.data.type,
|
||||
}))
|
||||
: (review.data?.objects ?? []).map((obj) => ({
|
||||
label: obj,
|
||||
type: "object" as EventType,
|
||||
}))),
|
||||
...(review.data?.audio ?? []).map((audio) => ({
|
||||
label: audio,
|
||||
type: "audio" as EventType,
|
||||
})),
|
||||
];
|
||||
|
||||
// limit to 5 icons
|
||||
const seen = new Set<string>();
|
||||
const iconLabels: string[] = [];
|
||||
for (const lbl of rawIconLabels) {
|
||||
if (!seen.has(lbl)) {
|
||||
seen.add(lbl);
|
||||
iconLabels.push(lbl);
|
||||
const iconLabels: Array<{ label: string; type: EventType }> = [];
|
||||
for (const item of rawIconLabels) {
|
||||
if (!seen.has(item.label)) {
|
||||
seen.add(item.label);
|
||||
iconLabels.push(item);
|
||||
if (iconLabels.length >= 5) break;
|
||||
}
|
||||
}
|
||||
@@ -418,12 +426,12 @@ function ReviewGroup({
|
||||
<div className="flex flex-row gap-3">
|
||||
<div className="text-sm font-medium">{displayTime}</div>
|
||||
<div className="relative flex items-center gap-2 text-white">
|
||||
{iconLabels.slice(0, 5).map((lbl, idx) => (
|
||||
{iconLabels.slice(0, 5).map(({ label: lbl, type }, idx) => (
|
||||
<div
|
||||
key={`${lbl}-${idx}`}
|
||||
className="rounded-full bg-muted-foreground p-1"
|
||||
>
|
||||
{getIconForLabel(lbl, "size-3 text-white")}
|
||||
{getIconForLabel(lbl, type, "size-3 text-white")}
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
@@ -516,7 +524,11 @@ function ReviewGroup({
|
||||
>
|
||||
<div className="ml-1.5 flex items-center gap-2 text-sm font-medium">
|
||||
<div className="rounded-full bg-muted-foreground p-1">
|
||||
{getIconForLabel(audioLabel, "size-3 text-white")}
|
||||
{getIconForLabel(
|
||||
audioLabel,
|
||||
"audio",
|
||||
"size-3 text-white",
|
||||
)}
|
||||
</div>
|
||||
<span>{getTranslatedLabel(audioLabel, "audio")}</span>
|
||||
</div>
|
||||
@@ -618,6 +630,7 @@ function EventList({
|
||||
>
|
||||
{getIconForLabel(
|
||||
event.sub_label ? event.label + "-verified" : event.label,
|
||||
event.data.type,
|
||||
"size-3 text-white",
|
||||
)}
|
||||
</div>
|
||||
|
||||
Reference in New Issue
Block a user