From 6a94ea216b46fb8d3eb83670a485743348f7ae6f Mon Sep 17 00:00:00 2001 From: Elodie MORIN Date: Mon, 8 Jun 2026 18:14:41 +0200 Subject: [PATCH] fix(frontend.Storage): Dataset list with analysis query should be invalidated on dataset import --- .../src/features/Storage/display/Item.tsx | 48 +++++++++---------- .../_admin/annotation-campaign/new.tsx | 14 ++++-- 2 files changed, 33 insertions(+), 29 deletions(-) diff --git a/frontend/src/features/Storage/display/Item.tsx b/frontend/src/features/Storage/display/Item.tsx index 770855af9..34e3746ed 100644 --- a/frontend/src/features/Storage/display/Item.tsx +++ b/frontend/src/features/Storage/display/Item.tsx @@ -1,11 +1,13 @@ import React, { Fragment, type MouseEvent, useCallback, useMemo, useState } from 'react'; -import { ImportStatusEnum, } from '@/api'; +import { ImportStatusEnum } from '@/api'; import styles from './styles.module.scss'; import { - type ImportDatasetFromStorageMutation, + type ImportDatasetFromStorageMutationVariables, ItemList, type StorageAnalysisFragment, type StorageItemFragment, + Slice as StorageSlice, + useStorageSearch } from '@/features/Storage'; import { IonButton, IonNote, IonSpinner } from '@ionic/react'; import { @@ -24,7 +26,6 @@ import { DatasetName } from '@/features/Dataset'; import { importMutation } from '../api' import { useMutation } from '@tanstack/react-query'; import { useAppDispatch } from '@/features/App'; -import { Storage } from '@/features'; type Props = { parentItem?: StorageItemFragment, @@ -43,7 +44,7 @@ export const Item: React.FC = ({ forceOpen, disableImport, }) => { - const item = Storage.useStorageSearch(path) + const item = useStorageSearch(path) // Open const [ _isOpen, _setIsOpen ] = useState(forceOpen || false); @@ -61,19 +62,7 @@ export const Item: React.FC = ({ // Import const toast = useToast() const dispatch = useAppDispatch() - const onError = useCallback((error: Error) => { - toast.raiseError({ error }) - }, [ toast ]) - const onSuccess = useCallback((data: ImportDatasetFromStorageMutation) => { - const path = data.importDataset?.dataset.path - if (!path) return - dispatch(Storage.Slice.actions.invalidatePath(path)) - }, [ onUpdated, dispatch ]) - const { mutate, isPending } = useMutation({ - ...importMutation, - onError, - onSuccess, - }) + const { mutateAsync, isPending } = useMutation(importMutation) const canImport = useMemo(() => { if (!item || item.error || isPending || disableImport) return false switch (item.__typename) { @@ -84,25 +73,32 @@ export const Item: React.FC = ({ return parentItem && item.importStatus === ImportStatusEnum.Partial || item.importStatus === ImportStatusEnum.Available } }, [ item, isPending, disableImport ]) - const download = useCallback((event: MouseEvent) => { + const download = useCallback(async (event: MouseEvent) => { event.stopPropagation() if (!canImport || !item) return; + const options: ImportDatasetFromStorageMutationVariables = { + datasetPath: item.path, + } switch (item.__typename) { case 'DatasetStorageNode': - mutate({ - datasetPath: item.path, - }) + options.datasetPath = item.path break; case 'AnalysisStorageNode': - mutate({ - analysisPath: item.path, - datasetPath: parentItem!.path, - }) + options.analysisPath = item.path + options.datasetPath = parentItem!.path break; default: return; } - }, [ canImport, item, mutate, parentItem ]) + try { + const data = await mutateAsync(options) + const path = data.importDataset?.dataset.path + if (!path) return + dispatch(StorageSlice.actions.invalidatePath(path)) + } catch (error) { + toast.raiseError({ error }) + } + }, [ canImport, item, mutateAsync, parentItem, toast, dispatch ]) return useMemo(() => { let rowIcon; diff --git a/frontend/src/routes/_authenticated/_admin/annotation-campaign/new.tsx b/frontend/src/routes/_authenticated/_admin/annotation-campaign/new.tsx index f60b0781f..dc68e4725 100644 --- a/frontend/src/routes/_authenticated/_admin/annotation-campaign/new.tsx +++ b/frontend/src/routes/_authenticated/_admin/annotation-campaign/new.tsx @@ -9,12 +9,14 @@ import { type Colormap, COLORMAPS } from '@/features/Colormap'; import { DatasetSelect } from '@/features/Dataset'; import styles from './new.module.scss'; -import { AnnotationCampaign, Dataset, Storage } from '@/features'; +import { AnnotationCampaign, Dataset } from '@/features'; +import { Slice as StorageSlice } from '@/features/Storage'; import { queryClient } from '@/api/queryClient'; import { useMutation } from '@tanstack/react-query'; import type { GqlError } from '@/api/utils'; import type { CreateCampaignMutationVariables } from '@/features/AnnotationCampaign'; import { useAppDispatch } from '@/features/App'; +import { queryKeys } from '@/api/queryKeys'; const NewAnnotationCampaign: React.FC = () => { const dataset_id = Route.useSearch({ select: ({ dataset_id }) => dataset_id }); @@ -99,7 +101,7 @@ const NewAnnotationCampaign: React.FC = () => { colormapInvertedDefault, }).then(data => { if (!data?.annotationCampaign) return; - dispatch(Storage.Slice.actions.invalidatePath(data.annotationCampaign.dataset.path)) + dispatch(StorageSlice.actions.invalidatePath(data.annotationCampaign.dataset.path)) }) }, [ name, description, instructionsUrl, createCampaign, deadline, datasetID, analysisIDs, allowImageTuning, allowColormapTuning, colormapDefault, colormapInvertedDefault, dispatch ]) @@ -202,6 +204,12 @@ export const Route = createFileRoute('/_authenticated/_admin/annotation-campaign validateSearch: (search: Record) => ({ dataset_id: search['dataset_id'] as string, }), - loader: () => queryClient.ensureQueryData(Dataset.API.listWithAnalysisQuery), + loader: async () => { + const datasets = await queryClient.ensureQueryData(Dataset.API.listWithAnalysisQuery) + if (queryClient.getQueryState(queryKeys.dataset.listWithAnalysis)?.isInvalidated) { + return await queryClient.fetchQuery(Dataset.API.listWithAnalysisQuery) + } + return datasets + }, component: NewAnnotationCampaign, }) \ No newline at end of file