* camera wizard input mobile font zooming

* ensure the selected page is visible when navigating via url on mobile

* Filter detail stream to only show items from within the review item

* remove incorrect classes causing extra scroll in detail stream

* change button label

* fix mobile menu button highlight issue

---------

Co-authored-by: Nicolas Mowen <nickmowen213@gmail.com>
This commit is contained in:
Josh Hawkins 2025-11-05 08:49:31 -06:00 committed by GitHub
parent 81faa8899d
commit 4638c22c16
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
5 changed files with 40 additions and 13 deletions

View File

@ -385,7 +385,7 @@ export default function Step1NameCamera({
</FormLabel> </FormLabel>
<FormControl> <FormControl>
<Input <Input
className="h-8" className="text-md h-8"
placeholder={t( placeholder={t(
"cameraWizard.step1.cameraNamePlaceholder", "cameraWizard.step1.cameraNamePlaceholder",
)} )}
@ -475,7 +475,7 @@ export default function Step1NameCamera({
</FormLabel> </FormLabel>
<FormControl> <FormControl>
<Input <Input
className="h-8" className="text-md h-8"
placeholder="192.168.1.100" placeholder="192.168.1.100"
{...field} {...field}
/> />
@ -495,7 +495,7 @@ export default function Step1NameCamera({
</FormLabel> </FormLabel>
<FormControl> <FormControl>
<Input <Input
className="h-8" className="text-md h-8"
placeholder={t( placeholder={t(
"cameraWizard.step1.usernamePlaceholder", "cameraWizard.step1.usernamePlaceholder",
)} )}
@ -518,7 +518,7 @@ export default function Step1NameCamera({
<FormControl> <FormControl>
<div className="relative"> <div className="relative">
<Input <Input
className="h-8 pr-10" className="text-md h-8 pr-10"
type={showPassword ? "text" : "password"} type={showPassword ? "text" : "password"}
placeholder={t( placeholder={t(
"cameraWizard.step1.passwordPlaceholder", "cameraWizard.step1.passwordPlaceholder",
@ -558,7 +558,7 @@ export default function Step1NameCamera({
</FormLabel> </FormLabel>
<FormControl> <FormControl>
<Input <Input
className="h-8" className="text-md h-8"
placeholder="rtsp://username:password@host:port/path" placeholder="rtsp://username:password@host:port/path"
{...field} {...field}
/> />

View File

@ -458,6 +458,7 @@ function ReviewGroup({
<EventList <EventList
key={event.id} key={event.id}
event={event} event={event}
review={review}
effectiveTime={effectiveTime} effectiveTime={effectiveTime}
annotationOffset={annotationOffset} annotationOffset={annotationOffset}
onSeek={onSeek} onSeek={onSeek}
@ -492,6 +493,7 @@ function ReviewGroup({
type EventListProps = { type EventListProps = {
event: Event; event: Event;
review: ReviewSegment;
effectiveTime?: number; effectiveTime?: number;
annotationOffset: number; annotationOffset: number;
onSeek: (ts: number, play?: boolean) => void; onSeek: (ts: number, play?: boolean) => void;
@ -499,6 +501,7 @@ type EventListProps = {
}; };
function EventList({ function EventList({
event, event,
review,
effectiveTime, effectiveTime,
annotationOffset, annotationOffset,
onSeek, onSeek,
@ -617,6 +620,7 @@ function EventList({
<div className="mt-2"> <div className="mt-2">
<ObjectTimeline <ObjectTimeline
review={review}
eventId={event.id} eventId={event.id}
onSeek={handleTimelineClick} onSeek={handleTimelineClick}
effectiveTime={effectiveTime} effectiveTime={effectiveTime}
@ -765,6 +769,7 @@ function LifecycleItem({
// Fetch and render timeline entries for a single event id on demand. // Fetch and render timeline entries for a single event id on demand.
function ObjectTimeline({ function ObjectTimeline({
review,
eventId, eventId,
onSeek, onSeek,
effectiveTime, effectiveTime,
@ -772,6 +777,7 @@ function ObjectTimeline({
startTime, startTime,
endTime, endTime,
}: { }: {
review: ReviewSegment;
eventId: string; eventId: string;
onSeek: (ts: number, play?: boolean) => void; onSeek: (ts: number, play?: boolean) => void;
effectiveTime?: number; effectiveTime?: number;
@ -780,13 +786,27 @@ function ObjectTimeline({
endTime?: number; endTime?: number;
}) { }) {
const { t } = useTranslation("views/events"); const { t } = useTranslation("views/events");
const { data: timeline, isValidating } = useSWR<TrackingDetailsSequence[]>([ const { data: fullTimeline, isValidating } = useSWR<
TrackingDetailsSequence[]
>([
"timeline", "timeline",
{ {
source_id: eventId, source_id: eventId,
}, },
]); ]);
const timeline = useMemo(() => {
if (!fullTimeline) {
return fullTimeline;
}
return fullTimeline.filter(
(t) =>
t.timestamp >= review.start_time &&
(review.end_time == undefined || t.timestamp <= review.end_time),
);
}, [fullTimeline, review]);
if (isValidating && (!timeline || timeline.length === 0)) { if (isValidating && (!timeline || timeline.length === 0)) {
return <ActivityIndicator className="ml-2 size-3" />; return <ActivityIndicator className="ml-2 size-3" />;
} }

View File

@ -157,9 +157,11 @@ function MobileMenuItem({
const { t } = useTranslation(["views/settings"]); const { t } = useTranslation(["views/settings"]);
return ( return (
<Button <div
variant="ghost" className={cn(
className={cn("w-full justify-between pr-2", className)} "inline-flex h-10 w-full cursor-pointer items-center justify-between whitespace-nowrap rounded-md px-4 py-2 pr-2 text-sm font-medium text-primary-variant disabled:pointer-events-none disabled:opacity-50",
className,
)}
onClick={() => { onClick={() => {
onSelect(item.key); onSelect(item.key);
onClose?.(); onClose?.();
@ -167,7 +169,7 @@ function MobileMenuItem({
> >
<div className="smart-capitalize">{t("menu." + item.key)}</div> <div className="smart-capitalize">{t("menu." + item.key)}</div>
<LuChevronRight className="size-4" /> <LuChevronRight className="size-4" />
</Button> </div>
); );
} }
@ -273,6 +275,9 @@ export default function Settings() {
} else { } else {
setPageToggle(page as SettingsType); setPageToggle(page as SettingsType);
} }
if (isMobile) {
setContentMobileOpen(true);
}
} }
// don't clear url params if we're creating a new object mask // don't clear url params if we're creating a new object mask
return !(searchParams.has("object_mask") || searchParams.has("event_id")); return !(searchParams.has("object_mask") || searchParams.has("event_id"));
@ -282,6 +287,9 @@ export default function Settings() {
const cameraNames = cameras.map((c) => c.name); const cameraNames = cameras.map((c) => c.name);
if (cameraNames.includes(camera)) { if (cameraNames.includes(camera)) {
setSelectedCamera(camera); setSelectedCamera(camera);
if (isMobile) {
setContentMobileOpen(true);
}
} }
// don't clear url params if we're creating a new object mask or trigger // don't clear url params if we're creating a new object mask or trigger
return !(searchParams.has("object_mask") || searchParams.has("event_id")); return !(searchParams.has("object_mask") || searchParams.has("event_id"));

View File

@ -970,7 +970,6 @@ function Timeline({
"relative overflow-hidden", "relative overflow-hidden",
isDesktop isDesktop
? cn( ? cn(
"no-scrollbar overflow-y-auto",
timelineType == "timeline" timelineType == "timeline"
? "w-[100px] flex-shrink-0" ? "w-[100px] flex-shrink-0"
: timelineType == "detail" : timelineType == "detail"

View File

@ -717,11 +717,11 @@ export default function CameraSettingsView({
<div className="flex w-full flex-row items-center gap-2 pt-2 md:w-[25%]"> <div className="flex w-full flex-row items-center gap-2 pt-2 md:w-[25%]">
<Button <Button
className="flex flex-1" className="flex flex-1"
aria-label={t("button.cancel", { ns: "common" })} aria-label={t("button.reset", { ns: "common" })}
onClick={onCancel} onClick={onCancel}
type="button" type="button"
> >
<Trans>button.cancel</Trans> <Trans>button.reset</Trans>
</Button> </Button>
<Button <Button
variant="select" variant="select"