From 530b69b877b73f47ad6cec6041ecbb46f84f25bf Mon Sep 17 00:00:00 2001 From: Josh Hawkins <32435876+hawkeye217@users.noreply.github.com> Date: Fri, 7 Nov 2025 07:53:27 -0600 Subject: [PATCH] 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 --- frigate/api/classification.py | 9 +- .../post/audio_transcription.py | 8 +- frigate/embeddings/maintainer.py | 4 +- frigate/util/model.py | 4 + .../components/card/SearchThumbnailFooter.tsx | 3 - .../components/menu/SearchResultActions.tsx | 67 +------ web/src/components/mobile/MobilePage.tsx | 18 +- .../overlay/ClassificationSelectionDialog.tsx | 14 +- .../overlay/detail/AnnotationSettingsPane.tsx | 74 +++---- .../overlay/detail/DetailActionsMenu.tsx | 17 ++ .../overlay/detail/SearchDetailDialog.tsx | 184 +++++++++--------- .../overlay/detail/TrackingDetails.tsx | 2 +- .../overlay/dialog/PlatformAwareDialog.tsx | 16 +- .../overlay/dialog/RestartDialog.tsx | 6 +- .../overlay/dialog/SearchFilterDialog.tsx | 1 + web/src/components/timeline/DetailStream.tsx | 4 +- web/src/components/timeline/EventMenu.tsx | 10 +- .../classification/ModelTrainingView.tsx | 32 +-- web/src/views/explore/ExploreView.tsx | 5 - web/src/views/recording/RecordingView.tsx | 2 +- web/src/views/search/SearchView.tsx | 3 - web/src/views/settings/TriggerView.tsx | 3 +- 22 files changed, 249 insertions(+), 237 deletions(-) diff --git a/frigate/api/classification.py b/frigate/api/classification.py index a167911c4..eee637585 100644 --- a/frigate/api/classification.py +++ b/frigate/api/classification.py @@ -662,8 +662,11 @@ def delete_classification_dataset_images( if os.path.isfile(file_path): os.unlink(file_path) + if os.path.exists(folder) and not os.listdir(folder): + os.rmdir(folder) + return JSONResponse( - content=({"success": True, "message": "Successfully deleted faces."}), + content=({"success": True, "message": "Successfully deleted images."}), status_code=200, ) @@ -723,7 +726,7 @@ def categorize_classification_image(request: Request, name: str, body: dict = No os.unlink(training_file) return JSONResponse( - content=({"success": True, "message": "Successfully deleted faces."}), + content=({"success": True, "message": "Successfully categorized image."}), status_code=200, ) @@ -761,7 +764,7 @@ def delete_classification_train_images(request: Request, name: str, body: dict = os.unlink(file_path) return JSONResponse( - content=({"success": True, "message": "Successfully deleted faces."}), + content=({"success": True, "message": "Successfully deleted images."}), status_code=200, ) diff --git a/frigate/data_processing/post/audio_transcription.py b/frigate/data_processing/post/audio_transcription.py index 146b4e0f1..066287707 100644 --- a/frigate/data_processing/post/audio_transcription.py +++ b/frigate/data_processing/post/audio_transcription.py @@ -9,7 +9,6 @@ from typing import Optional from faster_whisper import WhisperModel from peewee import DoesNotExist -from frigate.comms.embeddings_updater import EmbeddingsRequestEnum from frigate.comms.inter_process import InterProcessRequestor from frigate.config import FrigateConfig from frigate.const import ( @@ -32,11 +31,13 @@ class AudioTranscriptionPostProcessor(PostProcessorApi): self, config: FrigateConfig, requestor: InterProcessRequestor, + embeddings, metrics: DataProcessorMetrics, ): super().__init__(config, metrics, None) self.config = config self.requestor = requestor + self.embeddings = embeddings self.recognizer = None self.transcription_lock = threading.Lock() self.transcription_thread = None @@ -128,10 +129,7 @@ class AudioTranscriptionPostProcessor(PostProcessorApi): ) # Embed the description - self.requestor.send_data( - EmbeddingsRequestEnum.embed_description.value, - {"id": event_id, "description": transcription}, - ) + self.embeddings.embed_description(event_id, transcription) except DoesNotExist: logger.debug("No recording found for audio transcription post-processing") diff --git a/frigate/embeddings/maintainer.py b/frigate/embeddings/maintainer.py index bde81522d..6f34f4556 100644 --- a/frigate/embeddings/maintainer.py +++ b/frigate/embeddings/maintainer.py @@ -226,7 +226,9 @@ class EmbeddingMaintainer(threading.Thread): for c in self.config.cameras.values() ): self.post_processors.append( - AudioTranscriptionPostProcessor(self.config, self.requestor, metrics) + AudioTranscriptionPostProcessor( + self.config, self.requestor, self.embeddings, metrics + ) ) semantic_trigger_processor: SemanticTriggerProcessor | None = None diff --git a/frigate/util/model.py b/frigate/util/model.py index 308a16689..338303e2d 100644 --- a/frigate/util/model.py +++ b/frigate/util/model.py @@ -369,6 +369,10 @@ def get_ort_providers( "enable_cpu_mem_arena": False, } ) + elif provider == "AzureExecutionProvider": + # Skip Azure provider - not typically available on local hardware + # and prevents fallback to OpenVINO when it's the first provider + continue else: providers.append(provider) options.append({}) diff --git a/web/src/components/card/SearchThumbnailFooter.tsx b/web/src/components/card/SearchThumbnailFooter.tsx index 7cd510629..808ad2831 100644 --- a/web/src/components/card/SearchThumbnailFooter.tsx +++ b/web/src/components/card/SearchThumbnailFooter.tsx @@ -14,7 +14,6 @@ type SearchThumbnailProps = { findSimilar: () => void; refreshResults: () => void; showTrackingDetails: () => void; - showSnapshot: () => void; addTrigger: () => void; }; @@ -24,7 +23,6 @@ export default function SearchThumbnailFooter({ findSimilar, refreshResults, showTrackingDetails, - showSnapshot, addTrigger, }: SearchThumbnailProps) { const { t } = useTranslation(["views/search"]); @@ -62,7 +60,6 @@ export default function SearchThumbnailFooter({ findSimilar={findSimilar} refreshResults={refreshResults} showTrackingDetails={showTrackingDetails} - showSnapshot={showSnapshot} addTrigger={addTrigger} /> diff --git a/web/src/components/menu/SearchResultActions.tsx b/web/src/components/menu/SearchResultActions.tsx index 28644d3a7..f5128e268 100644 --- a/web/src/components/menu/SearchResultActions.tsx +++ b/web/src/components/menu/SearchResultActions.tsx @@ -6,10 +6,7 @@ import { toast } from "sonner"; import axios from "axios"; import { LuCamera, LuDownload, LuTrash2 } from "react-icons/lu"; import { FiMoreVertical } from "react-icons/fi"; -import { FaArrowsRotate } from "react-icons/fa6"; import { MdImageSearch } from "react-icons/md"; -import FrigatePlusIcon from "@/components/icons/FrigatePlusIcon"; -import { isMobileOnly } from "react-device-detect"; import { buttonVariants } from "@/components/ui/button"; import { ContextMenu, @@ -33,23 +30,18 @@ import { AlertDialogHeader, AlertDialogTitle, } from "@/components/ui/alert-dialog"; -import { - Tooltip, - TooltipContent, - TooltipTrigger, -} from "@/components/ui/tooltip"; import useSWR from "swr"; import { Trans, useTranslation } from "react-i18next"; import { BsFillLightningFill } from "react-icons/bs"; import BlurredIconButton from "../button/BlurredIconButton"; +import { PiPath } from "react-icons/pi"; type SearchResultActionsProps = { searchResult: SearchResult; findSimilar: () => void; refreshResults: () => void; showTrackingDetails: () => void; - showSnapshot: () => void; addTrigger: () => void; isContextMenu?: boolean; children?: ReactNode; @@ -60,7 +52,6 @@ export default function SearchResultActions({ findSimilar, refreshResults, showTrackingDetails, - showSnapshot, addTrigger, isContextMenu = false, children, @@ -129,7 +120,7 @@ export default function SearchResultActions({ aria-label={t("itemMenu.viewTrackingDetails.aria")} onClick={showTrackingDetails} > - + {t("itemMenu.viewTrackingDetails.label")} )} @@ -152,18 +143,14 @@ export default function SearchResultActions({ {t("itemMenu.addTrigger.label")} )} - {isMobileOnly && - config?.plus?.enabled && - searchResult.has_snapshot && - searchResult.end_time && - searchResult.data.type == "object" && - !searchResult.plus_id && ( + {config?.semantic_search?.enabled && + searchResult.data.type == "object" && ( - - {t("itemMenu.submitToPlus.label")} + + {t("itemMenu.findSimilar.label")} )} ) : ( <> - {config?.semantic_search?.enabled && - searchResult.data.type == "object" && ( - - - - - - - - {t("itemMenu.findSimilar.label")} - - - )} - - {!isMobileOnly && - config?.plus?.enabled && - searchResult.has_snapshot && - searchResult.end_time && - searchResult.data.type == "object" && - !searchResult.plus_id && ( - - - - - - - - {t("itemMenu.submitToPlus.label")} - - - )} - diff --git a/web/src/components/mobile/MobilePage.tsx b/web/src/components/mobile/MobilePage.tsx index b5084d65a..a4b80b2cb 100644 --- a/web/src/components/mobile/MobilePage.tsx +++ b/web/src/components/mobile/MobilePage.tsx @@ -4,6 +4,7 @@ import { useEffect, useState, useCallback, + useRef, } from "react"; import { createPortal } from "react-dom"; import { motion, AnimatePresence } from "framer-motion"; @@ -121,17 +122,20 @@ export function MobilePagePortal({ type MobilePageContentProps = { children: React.ReactNode; className?: string; + scrollerRef?: React.RefObject; }; export function MobilePageContent({ children, className, + scrollerRef, }: MobilePageContentProps) { const context = useContext(MobilePageContext); if (!context) throw new Error("MobilePageContent must be used within MobilePage"); const [isVisible, setIsVisible] = useState(context.open); + const containerRef = useRef(null); useEffect(() => { if (context.open) { @@ -140,15 +144,27 @@ export function MobilePageContent({ }, [context.open]); const handleAnimationComplete = () => { - if (!context.open) { + if (context.open) { + // After opening animation completes, ensure scroller is at the top + if (scrollerRef?.current) { + scrollerRef.current.scrollTop = 0; + } + } else { setIsVisible(false); } }; + useEffect(() => { + if (context.open && scrollerRef?.current) { + scrollerRef.current.scrollTop = 0; + } + }, [context.open, scrollerRef]); + return ( {isVisible && ( - {newClass && ( - onCategorizeImage(newCat)} - /> - )} + onCategorizeImage(newCat)} + /> diff --git a/web/src/components/overlay/detail/AnnotationSettingsPane.tsx b/web/src/components/overlay/detail/AnnotationSettingsPane.tsx index 33bf10c5c..75f586e40 100644 --- a/web/src/components/overlay/detail/AnnotationSettingsPane.tsx +++ b/web/src/components/overlay/detail/AnnotationSettingsPane.tsx @@ -141,50 +141,52 @@ export function AnnotationSettingsPane({
( - -
- - {t("trackingDetails.annotationSettings.offset.label")} - - - - trackingDetails.annotationSettings.offset.millisecondsToOffset - - -
- {t("trackingDetails.annotationSettings.offset.tips")} -
- - {t("readTheDocumentation", { ns: "common" })} - - -
+ <> + +
+ + {t("trackingDetails.annotationSettings.offset.label")} + + + + trackingDetails.annotationSettings.offset.millisecondsToOffset + + + +
+
+
+ + +
- -
-
-
- - - +
+ +
+ {t("trackingDetails.annotationSettings.offset.tips")} +
+ + {t("readTheDocumentation", { ns: "common" })} + +
- + )} /> diff --git a/web/src/components/overlay/detail/DetailActionsMenu.tsx b/web/src/components/overlay/detail/DetailActionsMenu.tsx index a22551875..27cf7e6f8 100644 --- a/web/src/components/overlay/detail/DetailActionsMenu.tsx +++ b/web/src/components/overlay/detail/DetailActionsMenu.tsx @@ -111,6 +111,23 @@ export default function DetailActionsMenu({
)} + + {config?.semantic_search.enabled && search.data.type == "object" && ( + { + setIsOpen(false); + setTimeout(() => { + navigate( + `/settings?page=triggers&camera=${search.camera}&event_id=${search.id}`, + ); + }, 0); + }} + > +
+ {t("itemMenu.addTrigger.label")} +
+
+ )} diff --git a/web/src/components/overlay/detail/SearchDetailDialog.tsx b/web/src/components/overlay/detail/SearchDetailDialog.tsx index e6c101b16..059ac7c89 100644 --- a/web/src/components/overlay/detail/SearchDetailDialog.tsx +++ b/web/src/components/overlay/detail/SearchDetailDialog.tsx @@ -1242,106 +1242,110 @@ function ObjectDetailsTab({
-
-
-
- {t("explore.plus.submitToPlus.label", { - ns: "components/dialog", - })} - - -
- - Info -
-
- - {t("explore.plus.submitToPlus.desc", { + {search.data.type === "object" && + !search.plus_id && + config?.plus?.enabled && ( +
+
+
+ {t("explore.plus.submitToPlus.label", { ns: "components/dialog", })} - - -
-
+ + +
+ + Info +
+
+ + {t("explore.plus.submitToPlus.desc", { + ns: "components/dialog", + })} + +
+
+
-
- {state == "reviewing" && ( - <> -
- {i18n.language === "en" ? ( - // English with a/an logic plus label - <> - {/^[aeiou]/i.test(search?.label || "") ? ( - - explore.plus.review.question.ask_an - +
+ {state == "reviewing" && ( + <> +
+ {i18n.language === "en" ? ( + // English with a/an logic plus label + <> + {/^[aeiou]/i.test(search?.label || "") ? ( + + explore.plus.review.question.ask_an + + ) : ( + + explore.plus.review.question.ask_a + + )} + ) : ( + // For other languages - explore.plus.review.question.ask_a + explore.plus.review.question.ask_full )} - - ) : ( - // For other languages - - explore.plus.review.question.ask_full - - )} -
-
- - -
- - )} - {state == "uploading" && } - {state == "submitted" && ( -
- - {t("explore.plus.review.state.submitted")} +
+
+ + +
+ + )} + {state == "uploading" && } + {state == "submitted" && ( +
+ + {t("explore.plus.review.state.submitted")} +
+ )}
- )} -
-
+
+ )}
{config?.cameras[search.camera].objects.genai.enabled && !search.end_time && diff --git a/web/src/components/overlay/detail/TrackingDetails.tsx b/web/src/components/overlay/detail/TrackingDetails.tsx index cd4e18e3b..da64c1965 100644 --- a/web/src/components/overlay/detail/TrackingDetails.tsx +++ b/web/src/components/overlay/detail/TrackingDetails.tsx @@ -457,7 +457,7 @@ export function TrackingDetails({ > {config?.cameras[event.camera]?.onvif.autotracking .enabled_in_config && ( -
+
{t("trackingDetails.autoTrackingTips")}
)} diff --git a/web/src/components/overlay/dialog/PlatformAwareDialog.tsx b/web/src/components/overlay/dialog/PlatformAwareDialog.tsx index ad03ba7eb..ab69e1288 100644 --- a/web/src/components/overlay/dialog/PlatformAwareDialog.tsx +++ b/web/src/components/overlay/dialog/PlatformAwareDialog.tsx @@ -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(null); + if (isMobile) { return ( @@ -86,14 +90,22 @@ export function PlatformAwareSheet({ {trigger} - + onOpenChange(false)} > {title} -
{content}
+
+ {content} +
diff --git a/web/src/components/overlay/dialog/RestartDialog.tsx b/web/src/components/overlay/dialog/RestartDialog.tsx index a8085c92c..6d269b7cb 100644 --- a/web/src/components/overlay/dialog/RestartDialog.tsx +++ b/web/src/components/overlay/dialog/RestartDialog.tsx @@ -98,7 +98,11 @@ export default function RestartDialog({ open={restartingSheetOpen} onOpenChange={() => setRestartingSheetOpen(false)} > - e.preventDefault()}> + e.preventDefault()} + className="[&>button:first-of-type]:hidden" + >
diff --git a/web/src/components/overlay/dialog/SearchFilterDialog.tsx b/web/src/components/overlay/dialog/SearchFilterDialog.tsx index dcee85523..1327f39af 100644 --- a/web/src/components/overlay/dialog/SearchFilterDialog.tsx +++ b/web/src/components/overlay/dialog/SearchFilterDialog.tsx @@ -230,6 +230,7 @@ export default function SearchFilterDialog({
{reviewItems?.length === 0 ? ( @@ -811,7 +811,7 @@ function ObjectTimeline({ if (!timeline || timeline.length === 0) { return ( -
+
{t("detail.noObjectDetailData")}
); diff --git a/web/src/components/timeline/EventMenu.tsx b/web/src/components/timeline/EventMenu.tsx index 1caed65e4..ca14fcae8 100644 --- a/web/src/components/timeline/EventMenu.tsx +++ b/web/src/components/timeline/EventMenu.tsx @@ -55,20 +55,24 @@ export default function EventMenu({ - + {isSelected ? t("itemMenu.hideObjectDetails.label") : t("itemMenu.showObjectDetails.label")} { navigate(`/explore?event_id=${event.id}`); }} > {t("details.item.button.viewInExplore")} - + { setIsOpen(false); onOpenUpload?.(event); @@ -97,6 +102,7 @@ export default function EventMenu({ {event.has_snapshot && config?.semantic_search?.enabled && ( { if (onOpenSimilarity) onOpenSimilarity(event); else diff --git a/web/src/views/classification/ModelTrainingView.tsx b/web/src/views/classification/ModelTrainingView.tsx index a27a06a9e..614d23c96 100644 --- a/web/src/views/classification/ModelTrainingView.tsx +++ b/web/src/views/classification/ModelTrainingView.tsx @@ -118,6 +118,11 @@ export default function ModelTrainingView({ model }: ModelTrainingViewProps) { const [trainFilter, setTrainFilter] = useApiFilter(); + const refreshAll = useCallback(() => { + refreshTrain(); + refreshDataset(); + }, [refreshTrain, refreshDataset]); + // image multiselect const [selectedImages, setSelectedImages] = useState([]); @@ -183,11 +188,12 @@ export default function ModelTrainingView({ model }: ModelTrainingViewProps) { ); const onDelete = useCallback( - (ids: string[], isName: boolean = false) => { + (ids: string[], isName: boolean = false, category?: string) => { + const targetCategory = category || pageToggle; const api = - pageToggle == "train" + targetCategory == "train" ? `/classification/${model.name}/train/delete` - : `/classification/${model.name}/dataset/${pageToggle}/delete`; + : `/classification/${model.name}/dataset/${targetCategory}/delete`; axios .post(api, { ids }) @@ -408,7 +414,7 @@ export default function ModelTrainingView({ model }: ModelTrainingViewProps) { trainImages={trainImages || []} trainFilter={trainFilter} selectedImages={selectedImages} - onRefresh={refreshTrain} + onRefresh={refreshAll} onClickImages={onClickImages} onDelete={onDelete} /> @@ -432,7 +438,7 @@ type LibrarySelectorProps = { dataset: { [id: string]: string[] }; trainImages: string[]; setPageToggle: (toggle: string) => void; - onDelete: (ids: string[], isName: boolean) => void; + onDelete: (ids: string[], isName: boolean, category?: string) => void; onRename: (old_name: string, new_name: string) => void; }; function LibrarySelector({ @@ -448,7 +454,7 @@ function LibrarySelector({ // data const [confirmDelete, setConfirmDelete] = useState(null); - const [renameClass, setRenameFace] = useState(null); + const [renameClass, setRenameClass] = useState(null); const pageTitle = useMemo(() => { if (pageToggle != "train") { return pageToggle; @@ -463,12 +469,12 @@ function LibrarySelector({ // interaction - const handleDeleteFace = useCallback( + const handleDeleteCategory = useCallback( (name: string) => { - // Get all image IDs for this face + // Get all image IDs for this category const imageIds = dataset?.[name] || []; - onDelete(imageIds, true); + onDelete(imageIds, true, name); setPageToggle("train"); }, [dataset, onDelete, setPageToggle], @@ -476,7 +482,7 @@ function LibrarySelector({ const handleSetOpen = useCallback( (open: boolean) => { - setRenameFace(open ? renameClass : null); + setRenameClass(open ? renameClass : null); }, [renameClass], ); @@ -503,7 +509,7 @@ function LibrarySelector({ className="text-white" onClick={() => { if (confirmDelete) { - handleDeleteFace(confirmDelete); + handleDeleteCategory(confirmDelete); setConfirmDelete(null); } }} @@ -521,7 +527,7 @@ function LibrarySelector({ description={t("renameCategory.desc", { name: renameClass })} onSave={(newName) => { onRename(renameClass!, newName); - setRenameFace(null); + setRenameClass(null); }} defaultValue={renameClass || ""} regexPattern={/^[\p{L}\p{N}\s'_-]{1,50}$/u} @@ -588,7 +594,7 @@ function LibrarySelector({ className="size-7 lg:opacity-0 lg:transition-opacity lg:group-hover:opacity-100" onClick={(e) => { e.stopPropagation(); - setRenameFace(id); + setRenameClass(id); }} > diff --git a/web/src/views/explore/ExploreView.tsx b/web/src/views/explore/ExploreView.tsx index cb53e74e0..2e3dd4755 100644 --- a/web/src/views/explore/ExploreView.tsx +++ b/web/src/views/explore/ExploreView.tsx @@ -236,10 +236,6 @@ function ExploreThumbnailImage({ onSelectSearch(event, false, "tracking_details"); }; - const handleShowSnapshot = () => { - onSelectSearch(event, false, "snapshot"); - }; - const handleAddTrigger = () => { navigate( `/settings?page=triggers&camera=${event.camera}&event_id=${event.id}`, @@ -252,7 +248,6 @@ function ExploreThumbnailImage({ findSimilar={handleFindSimilar} refreshResults={mutate} showTrackingDetails={handleShowTrackingDetails} - showSnapshot={handleShowSnapshot} addTrigger={handleAddTrigger} isContextMenu={true} > diff --git a/web/src/views/recording/RecordingView.tsx b/web/src/views/recording/RecordingView.tsx index 0c13876e8..c56af0eb7 100644 --- a/web/src/views/recording/RecordingView.tsx +++ b/web/src/views/recording/RecordingView.tsx @@ -985,7 +985,7 @@ function Timeline({ ), )} > - {isMobile && ( + {isMobile && timelineType == "timeline" && ( )} diff --git a/web/src/views/search/SearchView.tsx b/web/src/views/search/SearchView.tsx index 090685a62..bbe2078aa 100644 --- a/web/src/views/search/SearchView.tsx +++ b/web/src/views/search/SearchView.tsx @@ -688,9 +688,6 @@ export default function SearchView({ showTrackingDetails={() => onSelectSearch(value, false, "tracking_details") } - showSnapshot={() => - onSelectSearch(value, false, "snapshot") - } addTrigger={() => { if ( config?.semantic_search.enabled && diff --git a/web/src/views/settings/TriggerView.tsx b/web/src/views/settings/TriggerView.tsx index 883c1f391..2b8c2bfef 100644 --- a/web/src/views/settings/TriggerView.tsx +++ b/web/src/views/settings/TriggerView.tsx @@ -403,7 +403,8 @@ export default function TriggerView({ setShowCreate(true); setSelectedTrigger({ enabled: true, - name: "", + name: eventId, + friendly_name: "", type: "thumbnail", data: eventId, threshold: 0.5,