diff --git a/src/features/collections/data_products/Biolog.tsx b/src/features/collections/data_products/Biolog.tsx index 6ce5840d..2866946e 100644 --- a/src/features/collections/data_products/Biolog.tsx +++ b/src/features/collections/data_products/Biolog.tsx @@ -19,7 +19,11 @@ import { useAppDispatch } from '../../../common/hooks'; import { formatNumber } from '../../../common/utils/stringUtils'; import { useAppParam } from '../../params/hooks'; import classes from '../Collections.module.scss'; -import { useGenerateSelectionId } from '../collectionsSlice'; +import { + useFilterContextState, + useGenerateSelectionId, +} from '../collectionsSlice'; +import { useFilterContexts } from '../Filters'; import { useProcessStatePolling } from '../hooks'; import { HeatMap, HeatMapCallback, MAX_HEATMAP_PAGE } from './HeatMap'; @@ -102,42 +106,47 @@ const useBiolog = (collection_id: string | undefined) => { const selId = useGenerateSelectionId(collection_id || '', { skip: !collection_id, }); - const [matchMark, setMatchMark] = useState(true); - const [selMark, setSelMark] = useState(true); const [pagination, setPagination] = useState({ pageIndex: 0, pageSize: 56, }); + const context = useFilterContextState(collection_id); const pageLastIdCache: Record = useMemo( () => ({}), // eslint-disable-next-line react-hooks/exhaustive-deps - [collection_id, selMark, matchMark, pagination.pageSize] + [collection_id, context, pagination.pageSize] ); const heatMapParams = useMemo( () => ({ collection_id: collection_id ?? '', limit: pagination.pageSize, + match_mark: !context.endsWith('.matched'), + selection_mark: !context.endsWith('.selected'), ...(pagination.pageIndex !== 0 ? { start_after: pageLastIdCache[pagination.pageIndex - 1] } : {}), - ...(matchId ? { match_id: matchId, match_mark: matchMark } : {}), - ...(selId ? { selection_id: selId, selection_mark: selMark } : {}), + ...(matchId ? { match_id: matchId } : {}), + ...(selId ? { selection_id: selId } : {}), }), [ collection_id, matchId, - matchMark, pageLastIdCache, pagination.pageIndex, pagination.pageSize, selId, - selMark, + context, ] ); - const countParams = useMemo( - () => ({ ...heatMapParams, count: true }), + const allCountParams = useMemo( + () => ({ + ...heatMapParams, + count: true, + match_mark: true, + selection_mark: true, + }), [heatMapParams] ); const metaParams = useMemo( @@ -153,13 +162,19 @@ const useBiolog = (collection_id: string | undefined) => { skipPoll: !collection_id || !(matchId || selId), }); + // Reload on context change + useEffect(() => { + biologQuery.refetch(); + // eslint-disable-next-line react-hooks/exhaustive-deps + }, [context]); + //cache last row of each page, we should implement better backend pagination this is silly useEffect(() => { if (!biologQuery.isFetching && biologQuery.data) { - pageLastIdCache[pagination.pageIndex] = - biologQuery.data.data[ - biologQuery.data.data.length - 1 - ].kbase_display_name; + const name = + biologQuery.data.data[biologQuery.data.data.length - 1] + ?.kbase_display_name; + if (name) pageLastIdCache[pagination.pageIndex] = name; } }, [ biologQuery.data, @@ -168,14 +183,74 @@ const useBiolog = (collection_id: string | undefined) => { pageLastIdCache, ]); - const { data: count, ...countQuery } = getBiolog.useQuery(countParams, { + const { data: meta, ...metaQuery } = getBiologMeta.useQuery(metaParams, { skip: !collection_id, }); - const { data: meta, ...metaQuery } = getBiologMeta.useQuery(metaParams, { + const { data: count, ...countQuery } = getBiolog.useQuery(allCountParams, { skip: !collection_id, }); + const [matchCountParams, selCountParams] = useMemo( + () => [ + { + ...allCountParams, + match_mark: false, + }, + { + ...allCountParams, + selection_mark: false, + }, + ], + [allCountParams] + ); + + const matchCount = getBiolog.useQuery(matchCountParams, { + skip: !collection_id || !matchId, + }); + + const { match_state } = useProcessStatePolling(matchCount, ['match_state'], { + skipPoll: !collection_id || !matchId, + }); + + const selCount = getBiolog.useQuery(selCountParams, { + skip: !collection_id || !selId, + }); + + const { selection_state } = useProcessStatePolling( + selCount, + ['selection_state'], + { + skipPoll: !collection_id || !selId, + } + ); + + useFilterContexts(collection_id || '', [ + { + label: 'All', + value: 'biolog.all', + count: count?.count, + }, + { + label: 'Matched', + value: 'biolog.matched', + count: heatMapParams.match_id ? matchCount?.data?.count : undefined, + disabled: !heatMapParams.match_id || matchCount?.data?.count === 0, + loading: + (heatMapParams.match_id && !match_state) || + match_state === 'processing', + }, + { + label: 'Selected', + value: 'biolog.selected', + count: heatMapParams.selection_id ? selCount?.data?.count : undefined, + disabled: !heatMapParams.selection_id || selCount?.data?.count === 0, + loading: + (heatMapParams.selection_id && !selection_state) || + selection_state === 'processing', + }, + ]); + type RowDatum = NonNullable['data'][number]; const cols = createColumnHelper(); @@ -255,8 +330,6 @@ const useBiolog = (collection_id: string | undefined) => { }); return { - setMatchMark, - setSelMark, setPagination, biolog, biologQuery, diff --git a/src/features/collections/data_products/Microtrait.tsx b/src/features/collections/data_products/Microtrait.tsx index 85fab350..dbbb1e23 100644 --- a/src/features/collections/data_products/Microtrait.tsx +++ b/src/features/collections/data_products/Microtrait.tsx @@ -18,7 +18,12 @@ import { Pagination, usePageBounds } from '../../../common/components/Table'; import { useAppDispatch } from '../../../common/hooks'; import { formatNumber } from '../../../common/utils/stringUtils'; import classes from '../Collections.module.scss'; -import { useMatchId, useGenerateSelectionId } from '../collectionsSlice'; +import { + useMatchId, + useGenerateSelectionId, + useFilterContextState, +} from '../collectionsSlice'; +import { useFilterContexts } from '../Filters'; import { useProcessStatePolling } from '../hooks'; import { HeatMap, HeatMapCallback, MAX_HEATMAP_PAGE } from './HeatMap'; @@ -104,8 +109,8 @@ const useMicrotrait = (collection_id: string | undefined) => { const selId = useGenerateSelectionId(collection_id || '', { skip: !collection_id, }); - const [matchMark, setMatchMark] = useState(true); - const [selMark, setSelMark] = useState(true); + const context = useFilterContextState(collection_id); + const [pagination, setPagination] = useState({ pageIndex: 0, pageSize: 50, @@ -114,32 +119,38 @@ const useMicrotrait = (collection_id: string | undefined) => { const pageLastIdCache: Record = useMemo( () => ({}), // eslint-disable-next-line react-hooks/exhaustive-deps - [collection_id, selMark, matchMark, pagination.pageSize] + [collection_id, context, pagination.pageSize] ); const heatMapParams = useMemo( () => ({ collection_id: collection_id ?? '', limit: pagination.pageSize, + match_mark: !context.endsWith('.matched'), + selection_mark: !context.endsWith('.selected'), ...(pagination.pageIndex !== 0 ? { start_after: pageLastIdCache[pagination.pageIndex - 1] } : {}), - ...(matchId ? { match_id: matchId, match_mark: matchMark } : {}), - ...(selId ? { selection_id: selId, selection_mark: selMark } : {}), + ...(matchId ? { match_id: matchId } : {}), + ...(selId ? { selection_id: selId } : {}), }), [ collection_id, matchId, - matchMark, pageLastIdCache, pagination.pageIndex, pagination.pageSize, selId, - selMark, + context, ] ); - const countParams = useMemo( - () => ({ ...heatMapParams, count: true }), + const allCountParams = useMemo( + () => ({ + ...heatMapParams, + count: true, + match_mark: true, + selection_mark: true, + }), [heatMapParams] ); const metaParams = useMemo( @@ -156,13 +167,19 @@ const useMicrotrait = (collection_id: string | undefined) => { skipPoll: !collection_id || !(matchId || selId), }); + // Reload on context change + useEffect(() => { + microtraitQuery.refetch(); + // eslint-disable-next-line react-hooks/exhaustive-deps + }, [context]); + //cache last row of each page, we should implement better backend pagination this is silly useEffect(() => { if (!microtraitQuery.isFetching && microtraitQuery.data) { - pageLastIdCache[pagination.pageIndex] = - microtraitQuery.data.data[ - microtraitQuery.data.data.length - 1 - ].kbase_display_name; + const name = + microtraitQuery.data.data[microtraitQuery.data.data.length - 1] + ?.kbase_display_name; + if (name) pageLastIdCache[pagination.pageIndex] = name; } }, [ microtraitQuery.data, @@ -171,14 +188,77 @@ const useMicrotrait = (collection_id: string | undefined) => { pageLastIdCache, ]); - const { data: count, ...countQuery } = getMicroTrait.useQuery(countParams, { + const { data: meta, ...metaQuery } = getMicroTraitMeta.useQuery(metaParams, { skip: !collection_id, }); - const { data: meta, ...metaQuery } = getMicroTraitMeta.useQuery(metaParams, { - skip: !collection_id, + const { data: count, ...countQuery } = getMicroTrait.useQuery( + allCountParams, + { + skip: !collection_id, + } + ); + + const [matchCountParams, selCountParams] = useMemo( + () => [ + { + ...allCountParams, + match_mark: false, + }, + { + ...allCountParams, + selection_mark: false, + }, + ], + [allCountParams] + ); + + const matchCount = getMicroTrait.useQuery(matchCountParams, { + skip: !collection_id || !matchId, + }); + + const { match_state } = useProcessStatePolling(matchCount, ['match_state'], { + skipPoll: !collection_id || !matchId, }); + const selCount = getMicroTrait.useQuery(selCountParams, { + skip: !collection_id || !selId, + }); + + const { selection_state } = useProcessStatePolling( + selCount, + ['selection_state'], + { + skipPoll: !collection_id || !selId, + } + ); + + useFilterContexts(collection_id || '', [ + { + label: 'All', + value: 'microtrait.all', + count: count?.count, + }, + { + label: 'Matched', + value: 'microtrait.matched', + count: heatMapParams.match_id ? matchCount?.data?.count : undefined, + disabled: !heatMapParams.match_id || matchCount?.data?.count === 0, + loading: + (heatMapParams.match_id && !match_state) || + match_state === 'processing', + }, + { + label: 'Selected', + value: 'microtrait.selected', + count: heatMapParams.selection_id ? selCount?.data?.count : undefined, + disabled: !heatMapParams.selection_id || selCount?.data?.count === 0, + loading: + (heatMapParams.selection_id && !selection_state) || + selection_state === 'processing', + }, + ]); + type RowDatum = NonNullable['data'][number]; const cols = createColumnHelper(); @@ -260,8 +340,6 @@ const useMicrotrait = (collection_id: string | undefined) => { }); return { - setMatchMark, - setSelMark, setPagination, microtrait, microtraitQuery, diff --git a/src/features/collections/hooks.ts b/src/features/collections/hooks.ts index 186db295..1e8847eb 100644 --- a/src/features/collections/hooks.ts +++ b/src/features/collections/hooks.ts @@ -104,7 +104,7 @@ export const useProcessStatePolling = < result: R, processStateKeys: StateKey[], options?: { baseInterval?: number; rate?: number; skipPoll?: boolean } -) => { +): Partial<{ [processStateKey in StateKey]: ProcessState }> => { useBackoffPolling( result, (result) => { @@ -128,4 +128,9 @@ export const useProcessStatePolling = < }, options ); + const results: Partial<{ [processStateKey in StateKey]: ProcessState }> = {}; + processStateKeys.forEach((key) => { + results[key] = result.data?.[key]; + }); + return results; };