mirror of
https://github.com/blakeblackshear/frigate.git
synced 2026-07-30 07:39:01 +03:00
Miscellaneous fixes (#20833)
* remove frigate+ icon from explore grid footer * add margin * pointer cursor on event menu items in detail stream * don't show submit to plus for non-objects and if plus is disabled * tweak spacing in annotation settings popover * Fix deletion of classification images and library * Ensure after creating a class that things are correct * Fix dialog getting stuck * Only show the genai summary popup on mobile when timeline is open * fix audio transcription embedding * spacing * hide x icon on restart sheet to prevent closure issues * prevent x overflow in detail stream on mobile safari * ensure name is valid for search effect trigger * add trigger to detail actions menu * move find similar to actions menu * Use a column layout for MobilePageContent in PlatformAwareSheet This is so the header is outside the scrolling area and the content can grow/scroll independently. This now matches the way it's done in classification * Skip azure execution provider * add optional ref to always scroll to top the more filters in explore was not scrolled to the top on open due to the use of framer motion * fix title classes on desktop --------- Co-authored-by: Nicolas Mowen <nickmowen213@gmail.com>
This commit is contained in:
co-authored by
Nicolas Mowen
parent
a15399fed5
commit
530b69b877
@@ -97,14 +97,12 @@ export default function ClassificationSelectionDialog({
|
||||
|
||||
return (
|
||||
<div className={className ?? "flex"}>
|
||||
{newClass && (
|
||||
<TextEntryDialog
|
||||
open={true}
|
||||
setOpen={setNewClass}
|
||||
title={t("createCategory.new")}
|
||||
onSave={(newCat) => onCategorizeImage(newCat)}
|
||||
/>
|
||||
)}
|
||||
<TextEntryDialog
|
||||
open={newClass}
|
||||
setOpen={setNewClass}
|
||||
title={t("createCategory.new")}
|
||||
onSave={(newCat) => onCategorizeImage(newCat)}
|
||||
/>
|
||||
|
||||
<Tooltip>
|
||||
<Selector>
|
||||
|
||||
@@ -141,50 +141,52 @@ export function AnnotationSettingsPane({
|
||||
<Form {...form}>
|
||||
<form
|
||||
onSubmit={form.handleSubmit(onSubmit)}
|
||||
className="flex flex-1 flex-col space-y-6"
|
||||
className="flex flex-1 flex-col space-y-3"
|
||||
>
|
||||
<FormField
|
||||
control={form.control}
|
||||
name="annotationOffset"
|
||||
render={({ field }) => (
|
||||
<FormItem className="flex flex-row items-start justify-between space-x-2">
|
||||
<div className="flex flex-col gap-1">
|
||||
<FormLabel>
|
||||
{t("trackingDetails.annotationSettings.offset.label")}
|
||||
</FormLabel>
|
||||
<FormDescription>
|
||||
<Trans ns="views/explore">
|
||||
trackingDetails.annotationSettings.offset.millisecondsToOffset
|
||||
</Trans>
|
||||
<FormMessage />
|
||||
<div className="mt-2">
|
||||
{t("trackingDetails.annotationSettings.offset.tips")}
|
||||
<div className="mt-2 flex items-center text-primary">
|
||||
<Link
|
||||
to={getLocaleDocUrl("configuration/reference")}
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
className="inline"
|
||||
>
|
||||
{t("readTheDocumentation", { ns: "common" })}
|
||||
<LuExternalLink className="ml-2 inline-flex size-3" />
|
||||
</Link>
|
||||
</div>
|
||||
<>
|
||||
<FormItem className="flex flex-row items-start justify-between space-x-2">
|
||||
<div className="flex flex-col gap-1">
|
||||
<FormLabel>
|
||||
{t("trackingDetails.annotationSettings.offset.label")}
|
||||
</FormLabel>
|
||||
<FormDescription>
|
||||
<Trans ns="views/explore">
|
||||
trackingDetails.annotationSettings.offset.millisecondsToOffset
|
||||
</Trans>
|
||||
<FormMessage />
|
||||
</FormDescription>
|
||||
</div>
|
||||
<div className="flex flex-col gap-3">
|
||||
<div className="min-w-24">
|
||||
<FormControl>
|
||||
<Input
|
||||
className="text-md w-full border border-input bg-background p-2 text-center hover:bg-accent hover:text-accent-foreground dark:[color-scheme:dark]"
|
||||
placeholder="0"
|
||||
{...field}
|
||||
/>
|
||||
</FormControl>
|
||||
</div>
|
||||
</FormDescription>
|
||||
</div>
|
||||
<div className="flex flex-col gap-3">
|
||||
<div className="min-w-24">
|
||||
<FormControl>
|
||||
<Input
|
||||
className="text-md w-full border border-input bg-background p-2 text-center hover:bg-accent hover:text-accent-foreground dark:[color-scheme:dark]"
|
||||
placeholder="0"
|
||||
{...field}
|
||||
/>
|
||||
</FormControl>
|
||||
</div>
|
||||
</FormItem>
|
||||
<div className="mt-1 text-sm text-secondary-foreground">
|
||||
{t("trackingDetails.annotationSettings.offset.tips")}
|
||||
<div className="mt-2 flex items-center text-primary-variant">
|
||||
<Link
|
||||
to={getLocaleDocUrl("configuration/reference")}
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
className="inline"
|
||||
>
|
||||
{t("readTheDocumentation", { ns: "common" })}
|
||||
<LuExternalLink className="ml-2 inline-flex size-3" />
|
||||
</Link>
|
||||
</div>
|
||||
</div>
|
||||
</FormItem>
|
||||
</>
|
||||
)}
|
||||
/>
|
||||
|
||||
|
||||
@@ -111,6 +111,23 @@ export default function DetailActionsMenu({
|
||||
</div>
|
||||
</DropdownMenuItem>
|
||||
)}
|
||||
|
||||
{config?.semantic_search.enabled && search.data.type == "object" && (
|
||||
<DropdownMenuItem
|
||||
onClick={() => {
|
||||
setIsOpen(false);
|
||||
setTimeout(() => {
|
||||
navigate(
|
||||
`/settings?page=triggers&camera=${search.camera}&event_id=${search.id}`,
|
||||
);
|
||||
}, 0);
|
||||
}}
|
||||
>
|
||||
<div className="flex cursor-pointer items-center gap-2">
|
||||
<span>{t("itemMenu.addTrigger.label")}</span>
|
||||
</div>
|
||||
</DropdownMenuItem>
|
||||
)}
|
||||
</DropdownMenuContent>
|
||||
</DropdownMenuPortal>
|
||||
</DropdownMenu>
|
||||
|
||||
@@ -1242,106 +1242,110 @@ function ObjectDetailsTab({
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div
|
||||
className={cn(
|
||||
"my-2 flex w-full flex-col justify-between gap-1.5",
|
||||
state == "submitted" && "flex-row",
|
||||
)}
|
||||
>
|
||||
<div className="text-sm text-primary/40">
|
||||
<div className="flex flex-row items-center gap-1">
|
||||
{t("explore.plus.submitToPlus.label", {
|
||||
ns: "components/dialog",
|
||||
})}
|
||||
<Popover>
|
||||
<PopoverTrigger asChild>
|
||||
<div className="cursor-pointer p-0">
|
||||
<LuInfo className="size-4" />
|
||||
<span className="sr-only">Info</span>
|
||||
</div>
|
||||
</PopoverTrigger>
|
||||
<PopoverContent
|
||||
container={popoverContainerRef.current}
|
||||
className="w-80 text-xs"
|
||||
>
|
||||
{t("explore.plus.submitToPlus.desc", {
|
||||
{search.data.type === "object" &&
|
||||
!search.plus_id &&
|
||||
config?.plus?.enabled && (
|
||||
<div
|
||||
className={cn(
|
||||
"my-2 flex w-full flex-col justify-between gap-1.5",
|
||||
state == "submitted" && "flex-row",
|
||||
)}
|
||||
>
|
||||
<div className="text-sm text-primary/40">
|
||||
<div className="flex flex-row items-center gap-1">
|
||||
{t("explore.plus.submitToPlus.label", {
|
||||
ns: "components/dialog",
|
||||
})}
|
||||
</PopoverContent>
|
||||
</Popover>
|
||||
</div>
|
||||
</div>
|
||||
<Popover>
|
||||
<PopoverTrigger asChild>
|
||||
<div className="cursor-pointer p-0">
|
||||
<LuInfo className="size-4" />
|
||||
<span className="sr-only">Info</span>
|
||||
</div>
|
||||
</PopoverTrigger>
|
||||
<PopoverContent
|
||||
container={popoverContainerRef.current}
|
||||
className="w-80 text-xs"
|
||||
>
|
||||
{t("explore.plus.submitToPlus.desc", {
|
||||
ns: "components/dialog",
|
||||
})}
|
||||
</PopoverContent>
|
||||
</Popover>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="flex flex-row items-center justify-between gap-2 text-sm">
|
||||
{state == "reviewing" && (
|
||||
<>
|
||||
<div>
|
||||
{i18n.language === "en" ? (
|
||||
// English with a/an logic plus label
|
||||
<>
|
||||
{/^[aeiou]/i.test(search?.label || "") ? (
|
||||
<Trans
|
||||
ns="components/dialog"
|
||||
values={{ label: search?.label }}
|
||||
>
|
||||
explore.plus.review.question.ask_an
|
||||
</Trans>
|
||||
<div className="flex flex-row items-center justify-between gap-2 text-sm">
|
||||
{state == "reviewing" && (
|
||||
<>
|
||||
<div>
|
||||
{i18n.language === "en" ? (
|
||||
// English with a/an logic plus label
|
||||
<>
|
||||
{/^[aeiou]/i.test(search?.label || "") ? (
|
||||
<Trans
|
||||
ns="components/dialog"
|
||||
values={{ label: search?.label }}
|
||||
>
|
||||
explore.plus.review.question.ask_an
|
||||
</Trans>
|
||||
) : (
|
||||
<Trans
|
||||
ns="components/dialog"
|
||||
values={{ label: search?.label }}
|
||||
>
|
||||
explore.plus.review.question.ask_a
|
||||
</Trans>
|
||||
)}
|
||||
</>
|
||||
) : (
|
||||
// For other languages
|
||||
<Trans
|
||||
ns="components/dialog"
|
||||
values={{ label: search?.label }}
|
||||
values={{
|
||||
untranslatedLabel: search?.label,
|
||||
translatedLabel: getTranslatedLabel(search?.label),
|
||||
}}
|
||||
>
|
||||
explore.plus.review.question.ask_a
|
||||
explore.plus.review.question.ask_full
|
||||
</Trans>
|
||||
)}
|
||||
</>
|
||||
) : (
|
||||
// For other languages
|
||||
<Trans
|
||||
ns="components/dialog"
|
||||
values={{
|
||||
untranslatedLabel: search?.label,
|
||||
translatedLabel: getTranslatedLabel(search?.label),
|
||||
}}
|
||||
>
|
||||
explore.plus.review.question.ask_full
|
||||
</Trans>
|
||||
)}
|
||||
</div>
|
||||
<div className="flex max-w-xl flex-row gap-2">
|
||||
<Button
|
||||
className="flex-1 bg-success"
|
||||
aria-label={t("button.yes", { ns: "common" })}
|
||||
onClick={() => {
|
||||
setState("uploading");
|
||||
onSubmitToPlus(false);
|
||||
}}
|
||||
>
|
||||
{t("button.yes", { ns: "common" })}
|
||||
</Button>
|
||||
<Button
|
||||
className="flex-1 text-white"
|
||||
aria-label={t("button.no", { ns: "common" })}
|
||||
variant="destructive"
|
||||
onClick={() => {
|
||||
setState("uploading");
|
||||
onSubmitToPlus(true);
|
||||
}}
|
||||
>
|
||||
{t("button.no", { ns: "common" })}
|
||||
</Button>
|
||||
</div>
|
||||
</>
|
||||
)}
|
||||
{state == "uploading" && <ActivityIndicator />}
|
||||
{state == "submitted" && (
|
||||
<div className="flex flex-row items-center justify-center gap-2">
|
||||
<FaCheckCircle className="size-4 text-success" />
|
||||
{t("explore.plus.review.state.submitted")}
|
||||
</div>
|
||||
<div className="flex max-w-xl flex-row gap-2">
|
||||
<Button
|
||||
className="flex-1 bg-success"
|
||||
aria-label={t("button.yes", { ns: "common" })}
|
||||
onClick={() => {
|
||||
setState("uploading");
|
||||
onSubmitToPlus(false);
|
||||
}}
|
||||
>
|
||||
{t("button.yes", { ns: "common" })}
|
||||
</Button>
|
||||
<Button
|
||||
className="flex-1 text-white"
|
||||
aria-label={t("button.no", { ns: "common" })}
|
||||
variant="destructive"
|
||||
onClick={() => {
|
||||
setState("uploading");
|
||||
onSubmitToPlus(true);
|
||||
}}
|
||||
>
|
||||
{t("button.no", { ns: "common" })}
|
||||
</Button>
|
||||
</div>
|
||||
</>
|
||||
)}
|
||||
{state == "uploading" && <ActivityIndicator />}
|
||||
{state == "submitted" && (
|
||||
<div className="flex flex-row items-center justify-center gap-2">
|
||||
<FaCheckCircle className="size-4 text-success" />
|
||||
{t("explore.plus.review.state.submitted")}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
<div className="flex flex-col gap-1.5">
|
||||
{config?.cameras[search.camera].objects.genai.enabled &&
|
||||
!search.end_time &&
|
||||
|
||||
@@ -457,7 +457,7 @@ export function TrackingDetails({
|
||||
>
|
||||
{config?.cameras[event.camera]?.onvif.autotracking
|
||||
.enabled_in_config && (
|
||||
<div className="mb-2 text-sm text-danger">
|
||||
<div className="mb-2 ml-3 text-sm text-danger">
|
||||
{t("trackingDetails.autoTrackingTips")}
|
||||
</div>
|
||||
)}
|
||||
|
||||
@@ -20,7 +20,9 @@ import {
|
||||
SheetTitle,
|
||||
SheetTrigger,
|
||||
} from "@/components/ui/sheet";
|
||||
import { cn } from "@/lib/utils";
|
||||
import { isMobile } from "react-device-detect";
|
||||
import { useRef } from "react";
|
||||
|
||||
type PlatformAwareDialogProps = {
|
||||
trigger: JSX.Element;
|
||||
@@ -79,6 +81,8 @@ export function PlatformAwareSheet({
|
||||
open,
|
||||
onOpenChange,
|
||||
}: PlatformAwareSheetProps) {
|
||||
const scrollerRef = useRef<HTMLDivElement>(null);
|
||||
|
||||
if (isMobile) {
|
||||
return (
|
||||
<MobilePage open={open} onOpenChange={onOpenChange}>
|
||||
@@ -86,14 +90,22 @@ export function PlatformAwareSheet({
|
||||
{trigger}
|
||||
</MobilePageTrigger>
|
||||
<MobilePagePortal>
|
||||
<MobilePageContent className="h-full overflow-hidden">
|
||||
<MobilePageContent
|
||||
className="flex h-full flex-col"
|
||||
scrollerRef={scrollerRef}
|
||||
>
|
||||
<MobilePageHeader
|
||||
className="mx-2"
|
||||
onClose={() => onOpenChange(false)}
|
||||
>
|
||||
<MobilePageTitle>{title}</MobilePageTitle>
|
||||
</MobilePageHeader>
|
||||
<div className={contentClassName}>{content}</div>
|
||||
<div
|
||||
ref={scrollerRef}
|
||||
className={cn("flex-1 overflow-y-auto", contentClassName)}
|
||||
>
|
||||
{content}
|
||||
</div>
|
||||
</MobilePageContent>
|
||||
</MobilePagePortal>
|
||||
</MobilePage>
|
||||
|
||||
@@ -98,7 +98,11 @@ export default function RestartDialog({
|
||||
open={restartingSheetOpen}
|
||||
onOpenChange={() => setRestartingSheetOpen(false)}
|
||||
>
|
||||
<SheetContent side="top" onInteractOutside={(e) => e.preventDefault()}>
|
||||
<SheetContent
|
||||
side="top"
|
||||
onInteractOutside={(e) => e.preventDefault()}
|
||||
className="[&>button:first-of-type]:hidden"
|
||||
>
|
||||
<div className="flex flex-col items-center">
|
||||
<ActivityIndicator />
|
||||
<SheetHeader className="mt-5 text-center">
|
||||
|
||||
@@ -230,6 +230,7 @@ export default function SearchFilterDialog({
|
||||
<PlatformAwareSheet
|
||||
trigger={trigger}
|
||||
title={t("more")}
|
||||
titleClassName="mb-5 -mt-3"
|
||||
content={content}
|
||||
contentClassName={cn(
|
||||
"w-auto lg:min-w-[275px] scrollbar-container h-full overflow-auto px-4",
|
||||
|
||||
Reference in New Issue
Block a user