mirror of
https://github.com/blakeblackshear/frigate.git
synced 2026-02-17 00:25:23 +03:00
context menu for explore summary thumbnail images
This commit is contained in:
parent
2a8a1a1b1b
commit
92efc4a3dc
@ -18,15 +18,22 @@ import ActivityIndicator from "@/components/indicators/activity-indicator";
|
|||||||
import { useEventUpdate } from "@/api/ws";
|
import { useEventUpdate } from "@/api/ws";
|
||||||
import { isEqual } from "lodash";
|
import { isEqual } from "lodash";
|
||||||
import TimeAgo from "@/components/dynamic/TimeAgo";
|
import TimeAgo from "@/components/dynamic/TimeAgo";
|
||||||
|
import SearchResultActions from "@/components/menu/SearchResultActions";
|
||||||
|
import { SearchTab } from "@/components/overlay/detail/SearchDetailDialog";
|
||||||
|
import { FrigateConfig } from "@/types/frigateConfig";
|
||||||
|
|
||||||
type ExploreViewProps = {
|
type ExploreViewProps = {
|
||||||
searchDetail: SearchResult | undefined;
|
searchDetail: SearchResult | undefined;
|
||||||
setSearchDetail: (search: SearchResult | undefined) => void;
|
setSearchDetail: (search: SearchResult | undefined) => void;
|
||||||
|
setSimilaritySearch: (search: SearchResult) => void;
|
||||||
|
onSelectSearch: (item: SearchResult, index: number, page?: SearchTab) => void;
|
||||||
};
|
};
|
||||||
|
|
||||||
export default function ExploreView({
|
export default function ExploreView({
|
||||||
searchDetail,
|
searchDetail,
|
||||||
setSearchDetail,
|
setSearchDetail,
|
||||||
|
setSimilaritySearch,
|
||||||
|
onSelectSearch,
|
||||||
}: ExploreViewProps) {
|
}: ExploreViewProps) {
|
||||||
// title
|
// title
|
||||||
|
|
||||||
@ -102,6 +109,9 @@ export default function ExploreView({
|
|||||||
isValidating={isValidating}
|
isValidating={isValidating}
|
||||||
objectType={label}
|
objectType={label}
|
||||||
setSearchDetail={setSearchDetail}
|
setSearchDetail={setSearchDetail}
|
||||||
|
mutate={mutate}
|
||||||
|
setSimilaritySearch={setSimilaritySearch}
|
||||||
|
onSelectSearch={onSelectSearch}
|
||||||
/>
|
/>
|
||||||
))}
|
))}
|
||||||
</div>
|
</div>
|
||||||
@ -113,6 +123,9 @@ type ThumbnailRowType = {
|
|||||||
searchResults?: SearchResult[];
|
searchResults?: SearchResult[];
|
||||||
isValidating: boolean;
|
isValidating: boolean;
|
||||||
setSearchDetail: (search: SearchResult | undefined) => void;
|
setSearchDetail: (search: SearchResult | undefined) => void;
|
||||||
|
mutate: () => void;
|
||||||
|
setSimilaritySearch: (search: SearchResult) => void;
|
||||||
|
onSelectSearch: (item: SearchResult, index: number, page?: SearchTab) => void;
|
||||||
};
|
};
|
||||||
|
|
||||||
function ThumbnailRow({
|
function ThumbnailRow({
|
||||||
@ -120,6 +133,9 @@ function ThumbnailRow({
|
|||||||
searchResults,
|
searchResults,
|
||||||
isValidating,
|
isValidating,
|
||||||
setSearchDetail,
|
setSearchDetail,
|
||||||
|
mutate,
|
||||||
|
setSimilaritySearch,
|
||||||
|
onSelectSearch,
|
||||||
}: ThumbnailRowType) {
|
}: ThumbnailRowType) {
|
||||||
const navigate = useNavigate();
|
const navigate = useNavigate();
|
||||||
|
|
||||||
@ -155,6 +171,9 @@ function ThumbnailRow({
|
|||||||
<ExploreThumbnailImage
|
<ExploreThumbnailImage
|
||||||
event={event}
|
event={event}
|
||||||
setSearchDetail={setSearchDetail}
|
setSearchDetail={setSearchDetail}
|
||||||
|
mutate={mutate}
|
||||||
|
setSimilaritySearch={setSimilaritySearch}
|
||||||
|
onSelectSearch={onSelectSearch}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
))}
|
))}
|
||||||
@ -184,54 +203,78 @@ function ThumbnailRow({
|
|||||||
type ExploreThumbnailImageProps = {
|
type ExploreThumbnailImageProps = {
|
||||||
event: SearchResult;
|
event: SearchResult;
|
||||||
setSearchDetail: (search: SearchResult | undefined) => void;
|
setSearchDetail: (search: SearchResult | undefined) => void;
|
||||||
|
mutate: () => void;
|
||||||
|
setSimilaritySearch: (search: SearchResult) => void;
|
||||||
|
onSelectSearch: (item: SearchResult, index: number, page?: SearchTab) => void;
|
||||||
};
|
};
|
||||||
function ExploreThumbnailImage({
|
function ExploreThumbnailImage({
|
||||||
event,
|
event,
|
||||||
setSearchDetail,
|
setSearchDetail,
|
||||||
|
mutate,
|
||||||
|
setSimilaritySearch,
|
||||||
|
onSelectSearch,
|
||||||
}: ExploreThumbnailImageProps) {
|
}: ExploreThumbnailImageProps) {
|
||||||
const apiHost = useApiHost();
|
const apiHost = useApiHost();
|
||||||
|
const { data: config } = useSWR<FrigateConfig>("config");
|
||||||
const [imgRef, imgLoaded, onImgLoad] = useImageLoaded();
|
const [imgRef, imgLoaded, onImgLoad] = useImageLoaded();
|
||||||
|
|
||||||
return (
|
const handleFindSimilar = () => {
|
||||||
<>
|
if (config?.semantic_search.enabled) {
|
||||||
<ImageLoadingIndicator
|
setSimilaritySearch(event);
|
||||||
className="absolute inset-0"
|
}
|
||||||
imgLoaded={imgLoaded}
|
};
|
||||||
/>
|
|
||||||
|
|
||||||
<img
|
const handleShowObjectLifecycle = () => {
|
||||||
ref={imgRef}
|
onSelectSearch(event, 0, "object lifecycle");
|
||||||
className={cn(
|
};
|
||||||
"absolute h-full w-full cursor-pointer rounded-lg object-cover transition-all duration-300 ease-in-out lg:rounded-2xl",
|
|
||||||
)}
|
return (
|
||||||
style={
|
<SearchResultActions
|
||||||
isIOS
|
searchResult={event}
|
||||||
? {
|
findSimilar={handleFindSimilar}
|
||||||
WebkitUserSelect: "none",
|
refreshResults={mutate}
|
||||||
WebkitTouchCallout: "none",
|
showObjectLifecycle={handleShowObjectLifecycle}
|
||||||
}
|
isContextMenu={true}
|
||||||
: undefined
|
>
|
||||||
}
|
<div className="relative size-full">
|
||||||
loading={isSafari ? "eager" : "lazy"}
|
<ImageLoadingIndicator
|
||||||
draggable={false}
|
className="absolute inset-0"
|
||||||
src={`${apiHost}api/events/${event.id}/thumbnail.jpg`}
|
imgLoaded={imgLoaded}
|
||||||
onClick={() => setSearchDetail(event)}
|
/>
|
||||||
onLoad={() => {
|
<img
|
||||||
onImgLoad();
|
ref={imgRef}
|
||||||
}}
|
className={cn(
|
||||||
/>
|
"absolute size-full cursor-pointer rounded-lg object-cover transition-all duration-300 ease-in-out lg:rounded-2xl",
|
||||||
{isDesktop && (
|
!imgLoaded && "invisible",
|
||||||
<div className="absolute bottom-1 right-1 z-10 rounded-lg bg-black/50 px-2 py-1 text-xs text-white">
|
|
||||||
{event.end_time ? (
|
|
||||||
<TimeAgo time={event.start_time * 1000} dense />
|
|
||||||
) : (
|
|
||||||
<div>
|
|
||||||
<ActivityIndicator size={10} />
|
|
||||||
</div>
|
|
||||||
)}
|
)}
|
||||||
</div>
|
style={
|
||||||
)}
|
isIOS
|
||||||
</>
|
? {
|
||||||
|
WebkitUserSelect: "none",
|
||||||
|
WebkitTouchCallout: "none",
|
||||||
|
}
|
||||||
|
: undefined
|
||||||
|
}
|
||||||
|
loading={isSafari ? "eager" : "lazy"}
|
||||||
|
draggable={false}
|
||||||
|
src={`${apiHost}api/events/${event.id}/thumbnail.jpg`}
|
||||||
|
onClick={() => setSearchDetail(event)}
|
||||||
|
onLoad={onImgLoad}
|
||||||
|
alt={`${event.label} thumbnail`}
|
||||||
|
/>
|
||||||
|
{isDesktop && (
|
||||||
|
<div className="absolute bottom-1 right-1 z-10 rounded-lg bg-black/50 px-2 py-1 text-xs text-white">
|
||||||
|
{event.end_time ? (
|
||||||
|
<TimeAgo time={event.start_time * 1000} dense />
|
||||||
|
) : (
|
||||||
|
<div>
|
||||||
|
<ActivityIndicator size={10} />
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
</SearchResultActions>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -489,6 +489,8 @@ export default function SearchView({
|
|||||||
<ExploreView
|
<ExploreView
|
||||||
searchDetail={searchDetail}
|
searchDetail={searchDetail}
|
||||||
setSearchDetail={setSearchDetail}
|
setSearchDetail={setSearchDetail}
|
||||||
|
setSimilaritySearch={setSimilaritySearch}
|
||||||
|
onSelectSearch={onSelectSearch}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user