From d516c0ab6e7b9c6ba5035f6027d27db5e8ca0dda Mon Sep 17 00:00:00 2001 From: Xharles Date: Mon, 5 Jan 2026 09:07:36 +0100 Subject: [PATCH 1/5] fix: Improve subscribed views handling in useView hook --- packages/frontend/src/api/hooks/useView.ts | 47 ++++++++++++---------- 1 file changed, 26 insertions(+), 21 deletions(-) diff --git a/packages/frontend/src/api/hooks/useView.ts b/packages/frontend/src/api/hooks/useView.ts index fa94175e0..94abaa2d4 100644 --- a/packages/frontend/src/api/hooks/useView.ts +++ b/packages/frontend/src/api/hooks/useView.ts @@ -179,15 +179,18 @@ export const useView: () => IView = () => { * We do this to show the shared views in the views tab. */ const subscribedViews = useMemo(() => { - const allViews = [ - ...Object.values(subscribedViewsCache ?? {}), - ...Object.values(sharedViewsCache ?? {}), - ].reduce((acc, view) => { - if (!acc.find((v) => v.data.id === view.data.id)) { - return [...acc, view]; - } - return acc; - }, [] as TSubscribedView[]); + const subscribedCacheViews = Object.values(subscribedViewsCache ?? {}); + const sharedCacheViews = Object.values(sharedViewsCache ?? {}); + + const allViews = [...subscribedCacheViews, ...sharedCacheViews].reduce( + (acc, view) => { + if (!acc.find((v) => v.data.id === view.data.id)) { + return [...acc, view]; + } + return acc; + }, + [] as TSubscribedView[] + ); const systemViews = allViews .filter((v) => v.data.is_system_view) .sort( @@ -198,6 +201,7 @@ export const useView: () => IView = () => { .sort( (viewA, viewD) => viewA.data.sort_order - viewD.data.sort_order ); + return [...systemViews, ...customViews]; }, [subscribedViewsCache, sharedViewsCache]); @@ -246,19 +250,20 @@ export const useView: () => IView = () => { * cache. Otherwise, we will add it to the shared views cache. * This is to ensure the views cache is always up to date. */ - if ( - // the user must be subscribed to the view + const isSubscribed = remoteSubscribedViews?.find((v) => v.id === view.id) !== - undefined - ) { - dispatch( - viewsStore.setViewsCache({ - ...viewsCache, - [view.id]: { - ...remoteViewAsCachedView(view), - }, - }) - ); + undefined; + + if (isSubscribed) { + /** + * Even though subscribed views are loaded via the remoteSubscribedViews + * useEffect, we still need to add them here because: + * 1. The resolved view data might be more recent than the cached subscription data + * 2. On first navigation, remoteSubscribedViews might not have loaded yet + * 3. This ensures the view is immediately available in subscribedViewsCache + * for the Views Tab to display + */ + dispatch(viewsStore.updateSubscribedViewsCache([view])); return; } // if the view is already in cache, we don't need to add it again From ecde2b7d489f98ed670291902f1c415c97b646b1 Mon Sep 17 00:00:00 2001 From: Xharles Date: Mon, 5 Jan 2026 09:07:44 +0100 Subject: [PATCH 2/5] refactor: Simplify query parameter construction in useResolvedView hook --- .../frontend/src/api/hooks/useResolvedView.ts | 21 +++++++------------ 1 file changed, 7 insertions(+), 14 deletions(-) diff --git a/packages/frontend/src/api/hooks/useResolvedView.ts b/packages/frontend/src/api/hooks/useResolvedView.ts index 62464cd5a..2ccf6d7b3 100644 --- a/packages/frontend/src/api/hooks/useResolvedView.ts +++ b/packages/frontend/src/api/hooks/useResolvedView.ts @@ -40,20 +40,13 @@ export const useResolvedView = (): ReturnType => { selectedView?.isReadOnly, ]); - return useGetViewByHashQuery( - { - ...(isViewHash - ? { hash: routeInfo?.value } - : // if the route is not a hash, it's a slug. - // if the route info has no slug, we'll use the selected view slug - // this should pose no issues if the selected view is not a system view - // and should work as expected if the selected view is a system view - { slug: routeInfo?.value ?? selectedView?.data.slug }), - }, - { - skip: skipViewFetch, - } - ); + const queryParams = isViewHash + ? { hash: routeInfo?.value } + : { slug: routeInfo?.value ?? selectedView?.data.slug }; + + return useGetViewByHashQuery(queryParams, { + skip: skipViewFetch, + }); }; export default useResolvedView; From 76d425fc510c3db3bfcd743d24d53bbb267a8433 Mon Sep 17 00:00:00 2001 From: Xharles Date: Mon, 5 Jan 2026 09:07:48 +0100 Subject: [PATCH 3/5] fix: Enhance cache handling and view selection logic in useViewUpdater hook --- .../frontend/src/api/hooks/useViewUpdater.ts | 31 +++++++++++++------ 1 file changed, 22 insertions(+), 9 deletions(-) diff --git a/packages/frontend/src/api/hooks/useViewUpdater.ts b/packages/frontend/src/api/hooks/useViewUpdater.ts index 9f33194a9..70e515e33 100644 --- a/packages/frontend/src/api/hooks/useViewUpdater.ts +++ b/packages/frontend/src/api/hooks/useViewUpdater.ts @@ -131,18 +131,23 @@ export const useViewUpdater: () => void = () => { }); const shouldAddToCache = useMemo(() => { - if (!resolvedView.currentData) return false; + if (!resolvedView.currentData) { + return false; + } const isLangChanged = isViewLangModified( selectedView, resolvedView.currentData ); + + const foundInAvailableViews = availableViews?.find( + (v) => v.data.hash === resolvedView.data?.hash + ); + const isNewView = !isViewModified && !isFetchingSubscribedViews && - !availableViews?.find( - (v) => v.data.hash === resolvedView.data?.hash - ); + !foundInAvailableViews; return isLangChanged || isNewView; }, [ @@ -155,10 +160,6 @@ export const useViewUpdater: () => void = () => { ]); if (shouldAddToCache && resolvedView.currentData) { - Logger.debug( - "useViewUpdater: adding view to cache", - resolvedView.currentData.name - ); addViewToCache(resolvedView.currentData); } @@ -281,7 +282,6 @@ export const useViewUpdater: () => void = () => { const viewFromPathInCache = useMemo(() => { if (routeInfo === undefined) return undefined; - // Look for the current view slug/hash in the views cache return subscribedViews.find( (v) => v.data.slug === routeInfo.value || @@ -299,6 +299,7 @@ export const useViewUpdater: () => void = () => { routeInfo !== undefined && !resolvedView.isError ) { + // First, try to select from cache if available if ( viewFromPathInCache !== undefined && viewFromPathInCache.data.id !== selectedViewId @@ -309,6 +310,18 @@ export const useViewUpdater: () => void = () => { setSelectedViewId(viewFromPathInCache.data.id); return; } + + // If not in cache but successfully resolved from backend, select it directly + // This handles the case where a view is resolved but not yet reflected in the + // subscribedViews cache (e.g., shared/public boards on first visit) + if ( + viewFromPathInCache === undefined && + resolvedView.currentData !== undefined && + resolvedView.currentData.id !== selectedViewId + ) { + setSelectedViewId(resolvedView.currentData.id); + return; + } } /** * If resolving the view from the path fails, we should reset the selectedViewId. From 906985587c925482db6d1bd75ec595c4cc039d00 Mon Sep 17 00:00:00 2001 From: Xharles Date: Mon, 5 Jan 2026 09:31:40 +0100 Subject: [PATCH 4/5] fix: Improve subscribed views handling by updating both subscribedViewsCache and viewsCache --- packages/frontend/src/api/hooks/useView.ts | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/packages/frontend/src/api/hooks/useView.ts b/packages/frontend/src/api/hooks/useView.ts index 94abaa2d4..17bc7d523 100644 --- a/packages/frontend/src/api/hooks/useView.ts +++ b/packages/frontend/src/api/hooks/useView.ts @@ -260,10 +260,19 @@ export const useView: () => IView = () => { * useEffect, we still need to add them here because: * 1. The resolved view data might be more recent than the cached subscription data * 2. On first navigation, remoteSubscribedViews might not have loaded yet - * 3. This ensures the view is immediately available in subscribedViewsCache - * for the Views Tab to display + * 3. This ensures the view is immediately available in both caches: + * - subscribedViewsCache (minimal metadata) for the Views Tab + * - viewsCache (full data) for sort order calculation and other operations */ dispatch(viewsStore.updateSubscribedViewsCache([view])); + dispatch( + viewsStore.setViewsCache({ + ...viewsCache, + [view.id]: { + ...remoteViewAsCachedView(view), + }, + }) + ); return; } // if the view is already in cache, we don't need to add it again From 79765db9e42f356d57bcdc0308a995c9c2a07762 Mon Sep 17 00:00:00 2001 From: Charles Nwankwo <39612820+Xavier-Charles@users.noreply.github.com> Date: Tue, 6 Jan 2026 10:37:14 +0100 Subject: [PATCH 5/5] Update subscribed views cache with view preview --- packages/frontend/src/api/hooks/useView.ts | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/packages/frontend/src/api/hooks/useView.ts b/packages/frontend/src/api/hooks/useView.ts index 17bc7d523..c24ad0229 100644 --- a/packages/frontend/src/api/hooks/useView.ts +++ b/packages/frontend/src/api/hooks/useView.ts @@ -264,7 +264,21 @@ export const useView: () => IView = () => { * - subscribedViewsCache (minimal metadata) for the Views Tab * - viewsCache (full data) for sort order calculation and other operations */ - dispatch(viewsStore.updateSubscribedViewsCache([view])); + // Extract only preview fields (without widgets, keywords, max_widgets, language) + const viewPreview: TRemoteUserViewPreview = { + id: view.id, + hash: view.hash, + name: view.name, + slug: view.slug, + icon: view.icon, + description: view.description, + is_subscribed: view.is_subscribed, + is_system_view: view.is_system_view, + is_smart: view.is_smart, + sort_order: view.sort_order, + updated_at: view.updated_at, + }; + dispatch(viewsStore.updateSubscribedViewsCache([viewPreview])); dispatch( viewsStore.setViewsCache({ ...viewsCache,