Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions ui/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@
"react-markdown": "^9.0.1",
"react-plotly.js": "^2.6.0",
"react-router-dom": "^6.8.2",
"react-zoom-pan-pinch": "^4.0.3",
"tailwind-merge": "^3.6.0"
},
"scripts": {
Expand Down
10 changes: 6 additions & 4 deletions ui/src/data-services/models/capture.ts
Original file line number Diff line number Diff line change
Expand Up @@ -167,17 +167,19 @@ export class Capture {
return this._capture.event?.name ?? ''
}

get thumbnail_small(): string {
if (this._capture.thumbnails && this._capture.thumbnails.small) {
get thumbnailSmall(): string {
if (this._capture.thumbnails?.small) {
return this._capture.thumbnails.small
}

return this._capture.url
}

get thumbnail_medium(): string {
if (this._capture.thumbnails && this._capture.thumbnails.medium) {
get thumbnailMedium(): string {
if (this._capture.thumbnails?.medium) {
return this._capture.thumbnails.medium
}

return this._capture.url
}

Expand Down
2 changes: 1 addition & 1 deletion ui/src/pages/captures/capture-columns.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ export const columns = ({

return (
<ImageTableCell
images={[{ src: item.thumbnail_small }]}
images={[{ src: item.thumbnailSmall }]}
theme={ImageCellTheme.Light}
to={detailsRoute}
/>
Expand Down
2 changes: 1 addition & 1 deletion ui/src/pages/captures/capture-gallery.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ export const CaptureGallery = ({
() =>
captures.map((c) => ({
id: c.id,
image: { src: c.thumbnail_small },
image: { src: c.thumbnailSmall },
title: c.dateTimeLabel,
to: c.sessionId
? getAppRoute({
Expand Down
22 changes: 1 addition & 21 deletions ui/src/pages/session-details/capture/capture.module.scss
Original file line number Diff line number Diff line change
@@ -1,14 +1,6 @@
.wrapper {
position: relative;
width: 100%;
height: 0;
}

.image,
.overlay,
.details,
.detections,
.loadingWrapper {
.detections {
position: absolute;
width: 100%;
height: 100%;
Expand Down Expand Up @@ -56,15 +48,3 @@
}
}
}

.loadingWrapper {
display: flex;
align-items: center;
justify-content: center;
}

@media only screen and (max-width: $breakpoint-md) {
.wrapper {
grid-column: span 2;
}
}
55 changes: 32 additions & 23 deletions ui/src/pages/session-details/capture/capture.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,11 @@ import {
TABS,
} from 'pages/occurrence-details/occurrence-details'
import { useLayoutEffect, useMemo, useRef, useState } from 'react'
import {
ReactZoomPanPinchRef,
TransformComponent,
TransformWrapper,
} from 'react-zoom-pan-pinch'
import { SCORE_THRESHOLDS } from 'utils/constants'
import { STRING, translate } from 'utils/language'
import { useActiveOccurrences } from '../hooks/useActiveOccurrences'
Expand All @@ -28,6 +33,7 @@ interface CaptureProps {
height: number | null
showDetections?: boolean
src?: string
transformRef: React.RefObject<ReactZoomPanPinchRef>
width: number | null
}

Expand All @@ -37,6 +43,7 @@ export const Capture = ({
height,
showDetections,
src,
transformRef,
width,
}: CaptureProps) => {
const [naturalSize, setNaturalSize] = useState<{
Expand Down Expand Up @@ -118,31 +125,33 @@ export const Capture = ({
}, [width, height, naturalSize])

return (
<div
className={classNames(styles.wrapper)}
style={{
paddingBottom: `${(1 / ratio) * 100}%`,
}}
>
<img ref={imageRef} className={styles.image} />
<div
className={classNames(styles.details, {
[styles.showOverlay]: showDetections && detections.length,
})}
>
{renderOverlay && <CaptureOverlay boxStyles={boxStyles} />}
<CaptureDetections
boxStyles={boxStyles}
defaultFilters={defaultFilters}
detections={detections}
showDetections={showDetections}
/>
</div>
{isLoading && (
<div className={styles.loadingWrapper}>
<div className="relative w-full" style={{ aspectRatio: ratio }}>
<TransformWrapper ref={transformRef}>
<TransformComponent
contentClass="!w-full !h-full"
wrapperClass="!w-full !h-full"
>
<img className="w-full h-full" ref={imageRef} />
<div
className={classNames(styles.details, {
[styles.showOverlay]: showDetections && detections.length,
})}
>
{renderOverlay ? <CaptureOverlay boxStyles={boxStyles} /> : null}
<CaptureDetections
boxStyles={boxStyles}
defaultFilters={defaultFilters}
detections={detections}
showDetections={showDetections}
/>
</div>
</TransformComponent>
</TransformWrapper>
{isLoading ? (
<div className="absolute inset-0 flex items-center justify-center">
<LoadingSpinner />
</div>
)}
) : null}
</div>
)
}
Expand Down
21 changes: 13 additions & 8 deletions ui/src/pages/session-details/session-details.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -13,9 +13,10 @@ import {
Tabs,
} from 'nova-ui-kit'
import { cn } from 'nova-ui-kit/utils'
import { useContext, useEffect, useState } from 'react'
import { useContext, useEffect, useRef, useState } from 'react'
import { Helmet } from 'react-helmet-async'
import { useParams } from 'react-router-dom'
import { ReactZoomPanPinchRef } from 'react-zoom-pan-pinch'
import { BreadcrumbContext } from 'utils/breadcrumbContext'
import { STRING, translate } from 'utils/language'
import { useUser } from 'utils/user/userContext'
Expand All @@ -31,6 +32,7 @@ import { SessionPlots } from './session-plots'
import { StarButton } from './star-button'
import { TimelineSlider } from './timeline-slider/timeline-slider'
import { ViewSettings } from './view-settings'
import { ZoomSettings } from './zoom-settings'

const TABS = {
SESSION: 'session',
Expand Down Expand Up @@ -83,6 +85,7 @@ export const SessionDetailsPage = () => {
const Content = ({ session }: { session: SessionDetails }) => {
// Settings
const [poll, setPoll] = useState(false)
const transformRef = useRef<ReactZoomPanPinchRef>(null)
const [settings, setSettings] = useState({
defaultFilters: true,
showDetections: true,
Expand Down Expand Up @@ -125,8 +128,8 @@ const Content = ({ session }: { session: SessionDetails }) => {
>
{user.loggedIn ? <Process capture={activeCapture} /> : null}
</PageHeader>
<div className="grid grid-cols-1 gap-4 mt-6 md:grid-cols-[auto_1fr] md:gap-6">
<Box className="order-last p-2 bg-background rounded-lg md:order-first md:p-4 md:rounded-xl">
<div className="grid grid-cols-1 gap-4 mt-6 xl:grid-cols-[auto_1fr] md:gap-6">
<Box className="order-last p-2 bg-background rounded-lg xl:order-first md:p-4 md:rounded-xl">
<Tabs.Root defaultValue={TABS.SESSION}>
<Tabs.List>
<Tabs.Trigger
Expand Down Expand Up @@ -166,12 +169,13 @@ const Content = ({ session }: { session: SessionDetails }) => {
detections={activeCapture?.detections ?? []}
height={activeCapture?.height ?? session.firstCapture.height}
showDetections={settings.showDetections}
src={activeCapture?.thumbnail_medium}
src={activeCapture?.url}
transformRef={transformRef}
width={activeCapture?.width ?? session.firstCapture.width}
/>
</div>
<div className="flex flex-col flex-wrap justify-between gap-2 p-2 border-t border-border md:flex-row md:p-6">
<div className="min-h-8 flex-1 flex items-center gap-2">
<div className="min-h-8 flex-1 flex items-center justify-center gap-2 md:justify-start">
{activeCapture ? (
<>
<span className="pt-0.5 text-muted-foreground truncate">
Expand Down Expand Up @@ -203,22 +207,23 @@ const Content = ({ session }: { session: SessionDetails }) => {
</span>
)}
</div>
<div className="flex items-center md:justify-center">
<div className="flex items-center justify-center">
<CaptureNavigation
activeCapture={activeCapture}
timeline={timeline}
setActiveCaptureId={setActiveCaptureId}
/>
</div>
<div className="flex-1 flex items-center md:justify-end">
<div className="flex-1 flex items-center justify-center gap-2 md:justify-end">
<ZoomSettings transformRef={transformRef} />
<ViewSettings
onSettingsChange={setSettings}
settings={settings}
/>
</div>
</div>
</div>
<div className="p-2 bg-background rounded-lg border border-border overflow-hidden md:col-span-2 md:p-4">
<div className="p-2 bg-background rounded-lg border border-border overflow-hidden xl:col-span-2 md:p-4">
<ActivityPlot
session={session}
setActiveCaptureId={setActiveCaptureId}
Expand Down
2 changes: 1 addition & 1 deletion ui/src/pages/session-details/view-settings.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ export const ViewSettings = ({
<Button
aria-label={translate(STRING.VIEW_SETTINGS)}
size="icon"
variant="outline"
variant="ghost"
>
<SettingsIcon className="w-4 h-4" />
</Button>
Expand Down
39 changes: 39 additions & 0 deletions ui/src/pages/session-details/zoom-settings.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
import { MinusIcon, PlusIcon } from 'lucide-react'
import { BasicTooltip, Button } from 'nova-ui-kit'
import { ReactZoomPanPinchRef } from 'react-zoom-pan-pinch'
import { STRING, translate } from 'utils/language'

export const ZoomSettings = ({
transformRef,
}: {
transformRef: React.RefObject<ReactZoomPanPinchRef>
}) => (
<>
<Button
onClick={() => transformRef.current?.resetTransform()}
size="small"
variant="ghost"
>
<span>{translate(STRING.RESET)}</span>
</Button>

<BasicTooltip content={translate(STRING.ZOOM_IN)}>
<Button
aria-label={translate(STRING.ZOOM_IN)}
onClick={() => transformRef.current?.zoomIn()}
size="icon"
>
<PlusIcon className="w-4 h-4" />
</Button>
</BasicTooltip>
<BasicTooltip content={translate(STRING.ZOOM_OUT)}>
<Button
aria-label={translate(STRING.ZOOM_OUT)}
onClick={() => transformRef.current?.zoomOut()}
size="icon"
>
<MinusIcon className="w-4 h-4" />
</Button>
</BasicTooltip>
</>
)
4 changes: 4 additions & 0 deletions ui/src/utils/language.ts
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,8 @@ export enum STRING {
VIEW_ALL,
VIEW_DOCS,
VIEW_PUBLIC_PROJECTS,
ZOOM_IN,
ZOOM_OUT,

/* ENTITY */
ENTITY_ADD,
Expand Down Expand Up @@ -407,6 +409,8 @@ const ENGLISH_STRINGS: { [key in STRING]: string } = {
[STRING.VIEW_ALL]: 'View all',
[STRING.VIEW_DOCS]: 'View docs',
[STRING.VIEW_PUBLIC_PROJECTS]: 'View public projects',
[STRING.ZOOM_IN]: 'Zoom in',
[STRING.ZOOM_OUT]: 'Zoom out',

/* FIELD_LABEL */
[STRING.FIELD_LABEL_ADDED_AT]: 'Added at',
Expand Down
11 changes: 11 additions & 0 deletions ui/yarn.lock
Original file line number Diff line number Diff line change
Expand Up @@ -4752,6 +4752,7 @@ __metadata:
react-markdown: "npm:^9.0.1"
react-plotly.js: "npm:^2.6.0"
react-router-dom: "npm:^6.8.2"
react-zoom-pan-pinch: "npm:^4.0.3"
sass: "npm:^1.58.3"
tailwind-merge: "npm:^3.6.0"
tailwindcss: "npm:^3.4.14"
Expand Down Expand Up @@ -11396,6 +11397,16 @@ __metadata:
languageName: node
linkType: hard

"react-zoom-pan-pinch@npm:^4.0.3":
version: 4.0.3
resolution: "react-zoom-pan-pinch@npm:4.0.3"
peerDependencies:
react: "*"
react-dom: "*"
checksum: 611bc498891550c5e59da5ee94996ff9c31eae533affa10f2fa0b0cb7b5333b51c1e7aa1bb918dcfff2a103c42de0b1963e1fdfe4fa87fcae36b046c37a822b1
languageName: node
linkType: hard

"react@npm:^18.2.0":
version: 18.2.0
resolution: "react@npm:18.2.0"
Expand Down