Search functionality and UI tweaks (#13978)

* Portal tooltips

* Add ability to time_range filter chroma searches

* centering and padding consistency

* add event id back to chroma metadata

* query sqlite first and pass those ids to chroma for embeddings search

* ensure we pass timezone to the api call

* remove object lifecycle from search details for non-object events

* simplify hour calculation

* fix query without filters

* bump chroma version

* chroma 0.5.7

* fix selecting camera group in cameras filter button
This commit is contained in:
Josh Hawkins
2024-09-26 15:30:56 -05:00
committed by GitHub
parent 20fd1db0f4
commit 40fe3b4358
13 changed files with 127 additions and 84 deletions

View File

@@ -15,6 +15,7 @@ import { capitalizeFirstLetter } from "@/utils/stringUtil";
import { SearchResult } from "@/types/search";
import useContextMenu from "@/hooks/use-contextmenu";
import { cn } from "@/lib/utils";
import { TooltipPortal } from "@radix-ui/react-tooltip";
type SearchThumbnailProps = {
searchResult: SearchResult;
@@ -95,16 +96,18 @@ export default function SearchThumbnail({
</div>
</TooltipTrigger>
</div>
<TooltipContent className="capitalize">
{[...new Set([searchResult.label])]
.filter(
(item) => item !== undefined && !item.includes("-verified"),
)
.map((text) => capitalizeFirstLetter(text))
.sort()
.join(", ")
.replaceAll("-verified", "")}
</TooltipContent>
<TooltipPortal>
<TooltipContent className="capitalize">
{[...new Set([searchResult.label])]
.filter(
(item) => item !== undefined && !item.includes("-verified"),
)
.map((text) => capitalizeFirstLetter(text))
.sort()
.join(", ")
.replaceAll("-verified", "")}
</TooltipContent>
</TooltipPortal>
</Tooltip>
</div>
<div className="rounded-t-l pointer-events-none absolute inset-x-0 top-0 z-10 h-[30%] w-full bg-gradient-to-b from-black/60 to-transparent"></div>

View File

@@ -112,7 +112,10 @@ export function CamerasFilterButton({
<div
key={name}
className="w-full cursor-pointer rounded-lg px-2 py-0.5 text-sm capitalize text-primary hover:bg-muted"
onClick={() => setCurrentCameras([...conf.cameras])}
onClick={() => {
setAllCamerasSelected(false);
setCurrentCameras([...conf.cameras]);
}}
>
{name}
</div>

View File

@@ -27,7 +27,7 @@ function Bottombar() {
isPWA && isIOS
? "portrait:items-start portrait:pt-1 landscape:items-center"
: "items-center",
isMobile && !isPWA && "h-12 landscape:md:h-16",
isMobile && !isPWA && "h-12 md:h-16",
)}
>
{navItems.map((item) => (

View File

@@ -201,21 +201,24 @@ export default function MobileReviewSettingsDrawer({
Calendar
</div>
</div>
<ReviewActivityCalendar
reviewSummary={reviewSummary}
selectedDay={
filter?.after == undefined
? undefined
: new Date(filter.after * 1000)
}
onSelect={(day) => {
onUpdateFilter({
...filter,
after: day == undefined ? undefined : day.getTime() / 1000,
before: day == undefined ? undefined : getEndOfDayTimestamp(day),
});
}}
/>
<div className="flex w-full flex-row justify-center">
<ReviewActivityCalendar
reviewSummary={reviewSummary}
selectedDay={
filter?.after == undefined
? undefined
: new Date(filter.after * 1000)
}
onSelect={(day) => {
onUpdateFilter({
...filter,
after: day == undefined ? undefined : day.getTime() / 1000,
before:
day == undefined ? undefined : getEndOfDayTimestamp(day),
});
}}
/>
</div>
<SelectSeparator />
<div className="flex items-center justify-center p-2">
<Button

View File

@@ -95,6 +95,11 @@ export default function SearchDetailDialog({
views.splice(index, 1);
}
if (search.data.type != "object") {
const index = views.indexOf("object lifecycle");
views.splice(index, 1);
}
// TODO implement
//if (!config.semantic_search.enabled) {
// const index = views.indexOf("similar-calendar");

View File

@@ -26,7 +26,7 @@ export default function PlatformAwareDialog({
return (
<Drawer open={open} onOpenChange={onOpenChange}>
<DrawerTrigger asChild>{trigger}</DrawerTrigger>
<DrawerContent className="max-h-[75dvh] overflow-hidden p-4">
<DrawerContent className="max-h-[75dvh] overflow-hidden px-4">
{content}
</DrawerContent>
</Drawer>

View File

@@ -82,14 +82,9 @@ export function useFormattedHour(
const [hour, minute] = time.includes(":") ? time.split(":") : [time, "00"];
const hourNum = parseInt(hour);
if (hourNum < 12) {
if (hourNum == 0) {
return `12:${minute} AM`;
}
const adjustedHour = hourNum % 12 || 12;
const period = hourNum < 12 ? "AM" : "PM";
return `${hourNum}:${minute} AM`;
} else {
return `${hourNum - 12}:${minute} PM`;
}
return `${adjustedHour}:${minute} ${period}`;
}, [hour24, time]);
}

View File

@@ -103,6 +103,7 @@ export default function Explore() {
time_range: searchSearchParams["time_range"],
search_type: searchSearchParams["search_type"],
event_id: searchSearchParams["event_id"],
timezone,
include_thumbnails: 0,
},
];

View File

@@ -11,13 +11,7 @@ import {
} from "@/components/ui/tooltip";
import { cn } from "@/lib/utils";
import { FrigateConfig } from "@/types/frigateConfig";
import {
DEFAULT_SEARCH_FILTERS,
SearchFilter,
SearchFilters,
SearchResult,
SearchSource,
} from "@/types/search";
import { SearchFilter, SearchResult, SearchSource } from "@/types/search";
import { useCallback, useEffect, useMemo, useRef, useState } from "react";
import { isMobileOnly } from "react-device-detect";
import { LuImage, LuSearchX, LuText } from "react-icons/lu";
@@ -31,6 +25,7 @@ import InputWithTags from "@/components/input/InputWithTags";
import { ScrollArea, ScrollBar } from "@/components/ui/scroll-area";
import { isEqual } from "lodash";
import { formatDateToLocaleString } from "@/utils/dateUtil";
import { TooltipPortal } from "@radix-ui/react-tooltip";
type SearchViewProps = {
search: string;
@@ -144,20 +139,6 @@ export default function SearchView({
const [searchDetail, setSearchDetail] = useState<SearchResult>();
const selectedFilters = useMemo<SearchFilters[]>(() => {
const filters = [...DEFAULT_SEARCH_FILTERS];
if (
searchFilter &&
(searchFilter?.query?.length || searchFilter?.event_id?.length)
) {
const index = filters.indexOf("time");
filters.splice(index, 1);
}
return filters;
}, [searchFilter]);
// search interaction
const [selectedIndex, setSelectedIndex] = useState<number | null>(null);
@@ -335,7 +316,6 @@ export default function SearchView({
"w-full justify-between md:justify-start lg:justify-end",
)}
filter={searchFilter}
filters={selectedFilters as SearchFilters[]}
onUpdateFilter={onUpdateFilter}
/>
<ScrollBar orientation="horizontal" className="h-0" />
@@ -401,14 +381,16 @@ export default function SearchView({
%
</Chip>
</TooltipTrigger>
<TooltipContent>
Matched {value.search_source} at{" "}
{zScoreToConfidence(
value.search_distance,
value.search_source,
)}
%
</TooltipContent>
<TooltipPortal>
<TooltipContent>
Matched {value.search_source} at{" "}
{zScoreToConfidence(
value.search_distance,
value.search_source,
)}
%
</TooltipContent>
</TooltipPortal>
</Tooltip>
</div>
)}