Skip to content
Merged
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
3 changes: 0 additions & 3 deletions frontend/src/components/ui/Modal/modal.hook.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,15 +6,12 @@ export type ModalProps = { onClose: () => void };
export const useModal = (component: React.FC<ModalProps & any>, extraArgs?: object) => {
const [ isOpen, setIsOpen ] = useState<boolean>(false);
const toggle = useCallback(() => {
console.log('toggle', component.name)
setIsOpen(prev => !prev)
}, [ setIsOpen, component ])
const open = useCallback(() => {
console.log('open', component.name)
setIsOpen(true)
}, [ setIsOpen , component])
const close = useCallback(() => {
console.log('close', component.name)
setIsOpen(false)
}, [ setIsOpen, component ])

Expand Down
6 changes: 2 additions & 4 deletions frontend/src/features/AnnotationCampaign/CampaignCard.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -27,13 +27,11 @@ const Card: React.FC<{ campaign: Campaign }> = React.memo(({ campaign }) => {
let color: Color = 'secondary';
let badge: string = 'Open';

const deadline = campaign.deadline ? new Date(campaign.deadline) : undefined;
if (campaign.isArchived) {
badge = 'Archived'
color = 'medium'
}

const deadline = campaign.deadline ? new Date(campaign.deadline) : undefined;
if (deadline && (deadline.getTime() - 7 * 24 * 60 * 60 * 1000) <= NOW) {
} else if (deadline && (deadline.getTime() - 7 * 24 * 60 * 60 * 1000) <= NOW) {
badge = `Due date: ${ dateToString(deadline) }`
color = 'warning'
}
Expand Down
1 change: 0 additions & 1 deletion frontend/src/features/AnnotationTask/api/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,6 @@ import { queryKeys } from '@/api/queryKeys';
export const submitMutation = mutationOptions({
mutationFn: (variables: SubmitTaskMutationVariables) => graphqlClient.request<SubmitTaskMutation>(SubmitTaskDocument, variables),
onSuccess: (_data, { campaignID, phase, spectrogramID }) => {
console.debug('invalidate', queryKeys.spectrogram.get({ campaignID, phaseType: phase, spectrogramID }))
queryClient.invalidateQueries({ type: 'all', queryKey: queryKeys.spectrogram.get({ campaignID, phaseType: phase, spectrogramID }) })
queryClient.invalidateQueries({ queryKey: queryKeys.phase.get({ campaignID, phase }) })
},
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ export const AnalysisSelect: React.FC = () => {
}
const range = `[${ frequencyToString(min) }Hz-${ frequencyToString(max) }Hz]`
label += ` | scale: ${ parts.length > 0 ? parts.length : 1 } ${ range }`
label += ` | ${ a.colormap.name }`
return { value: a!.id, label }
}) ?? []
}, [ allAnalysis ]);
Expand Down
6 changes: 3 additions & 3 deletions frontend/src/features/Annotator/Canvas/hooks.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ export const useFocusCanvasOnTime = () => {
return useCallback((time: number) => {
const left = timeScale.valueToPosition(time) - containerWidth / 2;
mainCanvasRef?.current?.parentElement?.scrollTo({ left })
}, [ timeScale, containerWidth ])
}, [ timeScale, containerWidth, mainCanvasRef ])
}

export const useDrawCanvas = () => {
Expand All @@ -49,7 +49,7 @@ export const useDrawCanvas = () => {
await drawSpectrogram(context)
applyColormap(context)
drawTempAnnotation(context)
}, [ width, height, drawSpectrogram, applyFilter, applyColormap, drawTempAnnotation ]);
}, [ width, height, drawSpectrogram, applyFilter, applyColormap, drawTempAnnotation, mainCanvasRef ]);
}

export const useDownloadCanvas = () => {
Expand Down Expand Up @@ -127,5 +127,5 @@ export const useDownloadCanvas = () => {
link.target = '_blank';
link.download = filename;
link.click();
}, [ height, zoom, draw ])
}, [ height, zoom, draw, mainCanvasRef, xAxisCanvasRef, yAxisCanvasRef ])
}
34 changes: 15 additions & 19 deletions frontend/src/features/Annotator/Skeleton.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,15 +7,15 @@ import styles from './styles.module.scss';
import { IoCheckmarkCircleOutline, IoChevronForwardOutline } from 'react-icons/io5';
import { AnnotationTaskStatus } from '@/api';
import { gqlAPI } from '@/api/baseGqlApi';
import { useAppDispatch, useAppSelector } from '@/features/App';
import { useAppDispatch } from '@/features/App';
import { useAnnotatorCanNavigate } from '@/features/Annotator/Navigation';
import { AnnotatorCanvasContextProvider } from '@/features/Annotator/Canvas';
import { PointerProvider } from '@/features/Annotator/Pointer/context';
import { useLoaderData, useSearch } from '@tanstack/react-router';
import { AnnotatorVisualConfigurationSlice } from '@/features/Annotator/VisualConfiguration';
import type { Colormap } from '@/features/Colormap';
import { AnnotatorConfidenceSlice } from '@/features/Annotator/Confidence';
import { AnnotatorAnalysisSlice, selectAnalysisID } from '@/features/Annotator/Analysis';
import { AnnotatorAnalysisSlice } from '@/features/Annotator/Analysis';
import { AnnotatorLabelSlice } from '@/features/Annotator/Label';
import { AnnotatorUXSlice } from '@/features/Annotator/UX';
import { AnnotatorCommentSlice } from '@/features/Annotator/Comment';
Expand All @@ -28,11 +28,15 @@ export const AnnotatorSkeleton: React.FC<{ children?: ReactNode }> = ({ children
const {
campaign,
confidences,
analysis,
} = useLoaderData({ from: '/_authenticated/annotation-campaign/$campaignID' })
const { phase } = useLoaderData({ from: '/_authenticated/annotation-campaign/$campaignID/phase/$phaseType' })
const { spectrogram, annotations, info, isEditionAuthorized } = useLoaderData({ from: '/_authenticated/annotation-campaign/$campaignID/phase/$phaseType/spectrogram/$spectrogramID' })
const analysisID = useAppSelector(selectAnalysisID)
const {
spectrogram,
annotations,
info,
isEditionAuthorized,
defaultAnalysis,
} = useLoaderData({ from: '/_authenticated/annotation-campaign/$campaignID/phase/$phaseType/spectrogram/$spectrogramID' })
const canNavigate = useAnnotatorCanNavigate()
const dispatch = useAppDispatch()

Expand All @@ -55,26 +59,14 @@ export const AnnotatorSkeleton: React.FC<{ children?: ReactNode }> = ({ children
default: confidences?.find(c => c?.isDefault) ?? confidences.length ? confidences[0].label : undefined,
}))
dispatch(AnnotatorLabelSlice.actions.initCampaign())

// Select default analysis when none existing is selected
if (analysis.length && !analysis.find(a => a.id === analysisID)) {
const baseScaleAnalysis = analysis.find(a =>
!a.frequencyScaleParts || a.frequencyScaleParts.length == 0 ||
(a.frequencyScaleParts.length == 1 && a.frequencyScaleParts[0]!.minValue == 0 && a.frequencyScaleParts[0]!.maxValue == a.fft.samplingFrequency / 2),
);
const minID = Math.min(...analysis.map(a => +a!.id))?.toString();
if (minID) {
dispatch(AnnotatorAnalysisSlice.actions.setAnalysis(analysis.find(a => a.id === (baseScaleAnalysis?.id ?? minID))));
}
}
}, [ campaign ]);

useEffect(() => {
dispatch(AnnotatorVisualConfigurationSlice.actions.initSpectrogram())
dispatch(AnnotatorUXSlice.actions.initSpectrogram())

const allAnnotations = convertGqlToAnnotations(annotations, phase.phase, user.id)
const defaultAnnotation = [...allAnnotations].pop()
const defaultAnnotation = [ ...allAnnotations ].pop()
dispatch(AnnotatorConfidenceSlice.actions.initSpectrogram({
focus: defaultAnnotation?.confidence ?? undefined,
}))
Expand All @@ -86,10 +78,14 @@ export const AnnotatorSkeleton: React.FC<{ children?: ReactNode }> = ({ children
}))
dispatch(AnnotatorAnnotationSlice.actions.initSpectrogram({
all: allAnnotations,
default: defaultAnnotation
default: defaultAnnotation,
}))
}, [ spectrogram ]);

useEffect(() => {
dispatch(AnnotatorAnalysisSlice.actions.setAnalysis(defaultAnalysis));
}, [ defaultAnalysis ]);

return <PointerProvider>
<AnnotatorCanvasContextProvider>
<div className={ styles.page }>
Expand Down
23 changes: 17 additions & 6 deletions frontend/src/features/Annotator/Spectrogram/hooks.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { useCallback, useRef } from 'react';
import { useCallback, useEffect, useRef } from 'react';
import { selectZoom } from '@/features/Annotator/Zoom';
import { useToast } from '@/components/ui';
import { useWindowHeight } from '@/features/Annotator/Canvas';
Expand All @@ -16,24 +16,35 @@ export const useDrawSpectrogram = () => {

const {
data: paths,
refetch,
} = useQuery({
...AnnotationSpectrogram.API.getPathQuery({
spectrogramID: spectrogram.id,
analysisID: analysis?.id ?? '',
}), enabled: !!analysis,
}),
enabled: !!analysis,
});
const height = useWindowHeight()
const timeScale = useTimeScale()
const toast = useToast()
const images = useRef<Map<number, Array<HTMLImageElement | undefined>>>(new Map);
const images = useRef<Map<number, Array<HTMLImageElement | undefined>>>(new Map());
const failedImagesSources = useRef<string[]>([])

useEffect(() => {
images.current = new Map();
}, [analysis]);

const areAllImagesLoaded = useCallback((): boolean => {
return images.current.get(zoom)?.filter(i => !!i).length === zoom
}, [ zoom ])

const loadImages = useCallback(async () => {
if (!analysis || !paths?.spectrogramPath || !spectrogram) {
let _paths = paths
if (!_paths) {
const { data } = await refetch()
_paths = data
}
if (!analysis || !_paths?.spectrogramPath || !spectrogram) {
images.current = new Map();
return;
}
Expand All @@ -42,7 +53,7 @@ export const useDrawSpectrogram = () => {
const filename = spectrogram.filename
return Promise.all(
Array.from(new Array<HTMLImageElement | undefined>(zoom)).map(async (_, index) => {
let src = paths.spectrogramPath;
let src = _paths.spectrogramPath;
if (!src) return;
if (analysis.legacy) {
src = `${ src.split(filename)[0] }${ filename }_${ zoom }_${ index }${ src.split(filename)[1] }`
Expand All @@ -69,7 +80,7 @@ export const useDrawSpectrogram = () => {
).then(loadedImages => {
images.current.set(zoom, loadedImages)
})
}, [ analysis, zoom, failedImagesSources, areAllImagesLoaded, spectrogram, analysis, paths, toast ])
}, [ analysis, zoom, failedImagesSources, areAllImagesLoaded, spectrogram, analysis, paths, toast, refetch ])

return useCallback(async (context: CanvasRenderingContext2D) => {
if (!areAllImagesLoaded()) await loadImages();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ import { AnnotationsBloc } from '@/features/Annotator/Annotation/AnnotationsBloc
import styles from './$spectrogramID.module.scss';
import { type AllSpectrogramsFilters } from '@/features/AnnotationSpectrogram';
import { queryClient } from '@/api/queryClient';
import { AnnotationSpectrogram, User } from '@/features';
import { AnnotationCampaign, AnnotationSpectrogram, User } from '@/features';
import { useQuery } from '@tanstack/react-query';

const AnnotatorPage: React.FC = () => {
Expand Down Expand Up @@ -134,11 +134,30 @@ export const Route = createFileRoute(
loaderDeps: ({ search }) => search as AllSpectrogramsFilters,
loader: async ({ params: { campaignID, phaseType, spectrogramID }, deps }) => {
const user = await queryClient.ensureQueryData(User.API.currentQuery)
const { spectrogram, ...data } = await queryClient.ensureQueryData(AnnotationSpectrogram.API.getQuery({
campaignID, phaseType, spectrogramID, ...deps, annotatorID: user.id,
}))
const [
{ spectrogram, ...data },
{ analysis }
] = await Promise.all([
queryClient.ensureQueryData(AnnotationSpectrogram.API.getQuery({
campaignID, phaseType, spectrogramID, ...deps, annotatorID: user!.id,
})),
queryClient.ensureQueryData(AnnotationCampaign.API.byIdQuery({ id: campaignID }))
])
if (!spectrogram) throw notFound()
return { spectrogram, ...data }
const baseScaleAnalysis = analysis.find(a =>
!a.frequencyScaleParts || a.frequencyScaleParts.length == 0 ||
(a.frequencyScaleParts.length == 1 && a.frequencyScaleParts[0]!.minValue == 0 && a.frequencyScaleParts[0]!.maxValue == a.fft.samplingFrequency / 2),
);
const minID = Math.min(...analysis.map(a => +a!.id))?.toString();
const defaultAnalysis = minID ? analysis.find(a => a.id === (baseScaleAnalysis?.id ?? minID)) : undefined

if (defaultAnalysis) {
await queryClient.ensureQueryData(AnnotationSpectrogram.API.getPathQuery({
spectrogramID: spectrogram.id,
analysisID: defaultAnalysis.id,
}))
}
return { spectrogram, defaultAnalysis, ...data }
},
component: AnnotatorPage,
})
Loading