From 171edd5cba6576ef0923ebf38e4cd9fcf0494303 Mon Sep 17 00:00:00 2001 From: yyh Date: Fri, 29 May 2026 12:39:56 +0800 Subject: [PATCH] fix(web): keep app tag filter local --- .../__tests__/use-apps-query-state.spec.tsx | 21 +++++++++---------- .../apps/hooks/use-apps-query-state.ts | 21 +++++++++---------- 2 files changed, 20 insertions(+), 22 deletions(-) diff --git a/web/app/components/apps/hooks/__tests__/use-apps-query-state.spec.tsx b/web/app/components/apps/hooks/__tests__/use-apps-query-state.spec.tsx index 782f6ec3534..722b8025193 100644 --- a/web/app/components/apps/hooks/__tests__/use-apps-query-state.spec.tsx +++ b/web/app/components/apps/hooks/__tests__/use-apps-query-state.spec.tsx @@ -28,14 +28,14 @@ describe('useAppsQueryState', () => { expect(typeof result.current.setIsCreatedByMe).toBe('function') }) - it('should parse app list filters from URL', () => { + it('should parse app list URL filters and keep tag filter local', () => { const { result } = renderWithAdapter( '?category=workflow&tagIDs=tag1;tag2&keywords=search+term&isCreatedByMe=true', ) expect(result.current.query).toEqual({ category: AppModeEnum.WORKFLOW, - tagIDs: ['tag1', 'tag2'], + tagIDs: [], keywords: 'search term', isCreatedByMe: true, }) @@ -117,31 +117,30 @@ describe('useAppsQueryState', () => { } }) - it('should update tag filter URL state', async () => { + it('should update tag filter local state without writing URL state', () => { const { result, onUrlUpdate } = renderWithAdapter() act(() => { result.current.setTagIDs(['tag1', 'tag2']) }) - await waitFor(() => expect(onUrlUpdate).toHaveBeenCalled()) - const update = onUrlUpdate.mock.calls.at(-1)![0] expect(result.current.query.tagIDs).toEqual(['tag1', 'tag2']) - expect(update.searchParams.get('tagIDs')).toBe('tag1;tag2') - expect(update.options.history).toBe('push') + expect(onUrlUpdate).not.toHaveBeenCalled() }) - it('should remove tagIDs from URL when empty', async () => { + it('should clear tag filter local state without writing URL state', () => { const { result, onUrlUpdate } = renderWithAdapter('?tagIDs=tag1;tag2') + act(() => { + result.current.setTagIDs(['tag1', 'tag2']) + }) + act(() => { result.current.setTagIDs([]) }) - await waitFor(() => expect(onUrlUpdate).toHaveBeenCalled()) - const update = onUrlUpdate.mock.calls.at(-1)![0] expect(result.current.query.tagIDs).toEqual([]) - expect(update.searchParams.has('tagIDs')).toBe(false) + expect(onUrlUpdate).not.toHaveBeenCalled() }) it('should update created-by-me URL state', async () => { diff --git a/web/app/components/apps/hooks/use-apps-query-state.ts b/web/app/components/apps/hooks/use-apps-query-state.ts index a0109eb0618..84ed3031650 100644 --- a/web/app/components/apps/hooks/use-apps-query-state.ts +++ b/web/app/components/apps/hooks/use-apps-query-state.ts @@ -1,5 +1,5 @@ -import { debounce, parseAsArrayOf, parseAsBoolean, parseAsString, parseAsStringLiteral, useQueryStates } from 'nuqs' -import { useCallback, useMemo } from 'react' +import { debounce, parseAsBoolean, parseAsString, parseAsStringLiteral, useQueryStates } from 'nuqs' +import { useCallback, useMemo, useState } from 'react' import { AppModes } from '@/types/app' import { APP_LIST_SEARCH_DEBOUNCE_MS } from '../constants' @@ -16,9 +16,6 @@ const appListQueryParsers = { category: parseAsStringLiteral(APP_LIST_CATEGORY_VALUES) .withDefault('all') .withOptions({ history: 'push' }), - tagIDs: parseAsArrayOf(parseAsString, ';') - .withDefault([]) - .withOptions({ history: 'push' }), keywords: parseAsString.withDefault('').withOptions({ limitUrlUpdates: debounce(APP_LIST_SEARCH_DEBOUNCE_MS), }), @@ -29,6 +26,7 @@ const appListQueryParsers = { export function useAppsQueryState() { const [query, setQuery] = useQueryStates(appListQueryParsers) + const [tagIDs, setTagIDs] = useState([]) const setCategory = useCallback((category: AppListCategory) => { setQuery({ category }) @@ -38,19 +36,20 @@ export function useAppsQueryState() { setQuery({ keywords }) }, [setQuery]) - const setTagIDs = useCallback((tagIDs: string[]) => { - setQuery({ tagIDs }) - }, [setQuery]) - const setIsCreatedByMe = useCallback((isCreatedByMe: boolean) => { setQuery({ isCreatedByMe }) }, [setQuery]) + const queryWithLocalTags = useMemo(() => ({ + ...query, + tagIDs, + }), [query, tagIDs]) + return useMemo(() => ({ - query, + query: queryWithLocalTags, setCategory, setKeywords, setTagIDs, setIsCreatedByMe, - }), [query, setCategory, setKeywords, setTagIDs, setIsCreatedByMe]) + }), [queryWithLocalTags, setCategory, setKeywords, setTagIDs, setIsCreatedByMe]) }