From 6ba0f424e664951cfaae10bc6e1fbb1130774eb9 Mon Sep 17 00:00:00 2001 From: TkDodo Date: Fri, 8 May 2026 15:46:27 +0200 Subject: [PATCH 1/5] fix: framework switcher --- src/components/FrameworkSelect.tsx | 40 +++++++++++++++---- .../AccountProfilePictureSection.client.tsx | 7 ++-- src/components/game/ui/DebugPanel.tsx | 5 +-- src/contexts/LoginModalContext.tsx | 3 +- src/hooks/useCurrentUser.ts | 16 +++++--- src/libraries/libraries.ts | 4 +- src/libraries/query.tsx | 2 +- 7 files changed, 54 insertions(+), 23 deletions(-) diff --git a/src/components/FrameworkSelect.tsx b/src/components/FrameworkSelect.tsx index 21069e686..df347db20 100644 --- a/src/components/FrameworkSelect.tsx +++ b/src/components/FrameworkSelect.tsx @@ -1,11 +1,16 @@ import * as React from 'react' import { create } from 'zustand' -import { useNavigate, useParams } from '@tanstack/react-router' +import { useLocation, useNavigate, useParams } from '@tanstack/react-router' +import { useQueryClient } from '@tanstack/react-query' import { Select } from './Select' import { Framework, getLibrary, LibraryId } from '~/libraries' import { getFrameworkOptions } from '~/libraries/frameworks' -import { useCurrentUserQuery } from '~/hooks/useCurrentUser' +import { + currentUserQueryOptions, + useCurrentUserQuery, +} from '~/hooks/useCurrentUser' import { updateLastUsedFramework } from '~/utils/users.functions' +import type { User } from '~/db/types' function persistFrameworkToServer(framework: string) { void updateLastUsedFramework({ data: { framework } }).catch(() => { @@ -72,18 +77,22 @@ export function getStoredFrameworkPreference(): string | undefined { */ export function usePersistFrameworkPreference() { const userQuery = useCurrentUserQuery() + const queryClient = useQueryClient() const localCurrentFramework = useLocalCurrentFramework() return React.useCallback( (framework: string) => { // Always update localStorage as fallback localCurrentFramework.setCurrentFramework(framework) + queryClient.setQueryData(currentUserQueryOptions.queryKey, (user) => + user ? { ...user, lastUsedFramework: framework } : user, + ) // Update DB for logged-in users (fire-and-forget) if (userQuery.data) { persistFrameworkToServer(framework) } }, - [localCurrentFramework, userQuery.data], + [localCurrentFramework, queryClient, userQuery.data], ) } @@ -114,7 +123,9 @@ function useFrameworkConfig({ frameworks }: { frameworks: Framework[] }) { */ export function useCurrentFramework(frameworks: Framework[]) { const navigate = useNavigate() + const location = useLocation() const userQuery = useCurrentUserQuery() + const queryClient = useQueryClient() const { framework: paramsFramework } = useParams({ strict: false, @@ -133,17 +144,32 @@ export function useCurrentFramework(frameworks: Framework[]) { const setFramework = React.useCallback( (framework: string) => { - navigate({ - params: { framework } as any, - }) // Always update localStorage as fallback localCurrentFramework.setCurrentFramework(framework) + queryClient.setQueryData(currentUserQueryOptions.queryKey, (user) => + user ? { ...user, lastUsedFramework: framework } : user, + ) + const nextPathname = location.pathname.replace( + /(\/docs\/framework\/)[^/]+/, + `$1${framework}`, + ) + if (nextPathname !== location.pathname) { + const queryString = window.location.search + const hash = window.location.hash + navigate({ href: `${nextPathname}${queryString}${hash}` }) + } // Update DB for logged-in users (fire-and-forget) if (userQuery.data) { persistFrameworkToServer(framework) } }, - [localCurrentFramework, navigate, userQuery.data], + [ + localCurrentFramework, + location.pathname, + navigate, + queryClient, + userQuery.data, + ], ) React.useEffect(() => { diff --git a/src/components/account/AccountProfilePictureSection.client.tsx b/src/components/account/AccountProfilePictureSection.client.tsx index 316488552..9061ce1d8 100644 --- a/src/components/account/AccountProfilePictureSection.client.tsx +++ b/src/components/account/AccountProfilePictureSection.client.tsx @@ -7,6 +7,7 @@ import { useToast } from '~/components/ToastProvider' import { Button } from '~/ui' import { removeProfileImage, revertProfileImage } from '~/utils/users.functions' import { useUploadThing } from '~/utils/uploadthing.client' +import { currentUserQueryOptions } from '~/hooks/useCurrentUser' type AccountProfileUser = { image?: string | null @@ -33,7 +34,7 @@ export function AccountProfilePictureSection({ const { startUpload } = useUploadThing('avatarUploader', { onClientUploadComplete: () => { setIsUploading(false) - queryClient.invalidateQueries({ queryKey: ['currentUser'] }) + queryClient.invalidateQueries(currentUserQueryOptions) notify(
Profile picture updated
@@ -81,7 +82,7 @@ export function AccountProfilePictureSection({ setIsReverting(true) try { await revertProfileImage() - queryClient.invalidateQueries({ queryKey: ['currentUser'] }) + queryClient.invalidateQueries(currentUserQueryOptions) notify(
Profile picture reverted
@@ -108,7 +109,7 @@ export function AccountProfilePictureSection({ setIsRemoving(true) try { await removeProfileImage() - queryClient.invalidateQueries({ queryKey: ['currentUser'] }) + queryClient.invalidateQueries(currentUserQueryOptions) notify(
Profile picture removed
diff --git a/src/components/game/ui/DebugPanel.tsx b/src/components/game/ui/DebugPanel.tsx index 3b1b6136b..f03fec78f 100644 --- a/src/components/game/ui/DebugPanel.tsx +++ b/src/components/game/ui/DebugPanel.tsx @@ -5,17 +5,16 @@ import { PARTNER_UPGRADE_ORDER, SHOWCASE_UPGRADE_ORDER, } from '../utils/upgrades' +import { currentUserQueryOptions } from '~/hooks/useCurrentUser' const UPGRADE_ORDER = [...PARTNER_UPGRADE_ORDER, ...SHOWCASE_UPGRADE_ORDER] -import { getCurrentUser } from '~/utils/auth.functions' export function DebugPanel() { const [isCollapsed, setIsCollapsed] = useState(true) // Fetch user directly without route context dependency const userQuery = useQuery({ - queryKey: ['currentUser'], - queryFn: () => getCurrentUser(), + ...currentUserQueryOptions, staleTime: 30 * 1000, }) diff --git a/src/contexts/LoginModalContext.tsx b/src/contexts/LoginModalContext.tsx index eeba99647..694d124e2 100644 --- a/src/contexts/LoginModalContext.tsx +++ b/src/contexts/LoginModalContext.tsx @@ -3,6 +3,7 @@ import * as React from 'react' import { useQueryClient } from '@tanstack/react-query' import { LoginModal } from '~/components/LoginModal' +import { currentUserQueryOptions } from '~/hooks/useCurrentUser' interface LoginModalContextValue { openLoginModal: (options?: { onSuccess?: () => void }) => void @@ -47,7 +48,7 @@ export function LoginModalProvider({ children }: LoginModalProviderProps) { const handleMessage = (event: MessageEvent) => { if (event.origin !== window.location.origin) return if (event.data?.type === 'TANSTACK_AUTH_SUCCESS') { - queryClient.invalidateQueries({ queryKey: ['currentUser'] }) + queryClient.invalidateQueries(currentUserQueryOptions) const onSuccess = pendingOnSuccessRef.current setIsOpen(false) pendingOnSuccessRef.current = undefined diff --git a/src/hooks/useCurrentUser.ts b/src/hooks/useCurrentUser.ts index 05e259057..6c7b0abfc 100644 --- a/src/hooks/useCurrentUser.ts +++ b/src/hooks/useCurrentUser.ts @@ -1,19 +1,23 @@ -import { useQuery } from '@tanstack/react-query' +import { useQuery, queryOptions } from '@tanstack/react-query' import { useRouteContext } from '@tanstack/react-router' import { getCurrentUser } from '~/utils/auth.functions' +export const currentUserQueryOptions = queryOptions({ + queryKey: ['currentUser'], + queryFn: async () => { + return getCurrentUser() + }, + staleTime: 5 * 1000, +}) + export function useCurrentUserQuery() { // Get user from route context (set in beforeLoad) const routeContext = useRouteContext({ strict: false }) const contextUser = routeContext?.user return useQuery({ - queryKey: ['currentUser'], - queryFn: async () => { - return getCurrentUser() - }, + ...currentUserQueryOptions, initialData: contextUser, - staleTime: 5 * 1000, }) } diff --git a/src/libraries/libraries.ts b/src/libraries/libraries.ts index 9f596ecfc..d296b1299 100644 --- a/src/libraries/libraries.ts +++ b/src/libraries/libraries.ts @@ -13,7 +13,7 @@ export const query: LibrarySlim = { tagline: 'Powerful asynchronous state management, server-state utilities and data fetching', description: - 'Powerful asynchronous state management, server-state utilities and data fetching. Fetch, cache, update, and wrangle all forms of async data in your TS/JS, React, Vue, Solid, Svelte & Angular applications all without touching any "global state"', + 'Powerful asynchronous state management, server-state utilities and data fetching. Fetch, cache, update, and wrangle all forms of async data in your TS/JS, React, Vue, Solid, Svelte, Angular & Lit applications all without touching any "global state"', bgStyle: 'bg-red-500', borderStyle: 'border-red-500/50', textStyle: 'text-red-500', @@ -23,7 +23,7 @@ export const query: LibrarySlim = { bgRadial: 'from-red-500 via-red-500/60 to-transparent', badge: undefined, repo: 'tanstack/query', - frameworks: ['react', 'preact', 'solid', 'vue', 'svelte', 'angular'], + frameworks: ['react', 'preact', 'solid', 'vue', 'svelte', 'angular', 'lit'], latestVersion: 'v5', latestBranch: 'main', availableVersions: ['v5', 'v4', 'v3'], diff --git a/src/libraries/query.tsx b/src/libraries/query.tsx index de697d2a2..2253a63b0 100644 --- a/src/libraries/query.tsx +++ b/src/libraries/query.tsx @@ -8,7 +8,7 @@ const textStyles = 'text-red-500 dark:text-red-400' export const queryProject = { ...query, description: - 'Powerful asynchronous state management, server-state utilities and data fetching. Fetch, cache, update, and wrangle all forms of async data in your TS/JS, React, Vue, Solid, Svelte & Angular applications all without touching any "global state"', + 'Powerful asynchronous state management, server-state utilities and data fetching. Fetch, cache, update, and wrangle all forms of async data in your TS/JS, React, Vue, Solid, Svelte, Angular & Lit applications all without touching any "global state"', latestBranch: 'main', bgRadial: 'from-red-500 via-red-500/60 to-transparent', textColor: 'text-amber-500', From eec71da7b5f6261bc122bf4e524375b70e01818a Mon Sep 17 00:00:00 2001 From: TkDodo Date: Fri, 8 May 2026 15:50:04 +0200 Subject: [PATCH 2/5] revert ai slop --- src/components/FrameworkSelect.tsx | 18 ++---------------- 1 file changed, 2 insertions(+), 16 deletions(-) diff --git a/src/components/FrameworkSelect.tsx b/src/components/FrameworkSelect.tsx index df347db20..462de2214 100644 --- a/src/components/FrameworkSelect.tsx +++ b/src/components/FrameworkSelect.tsx @@ -149,27 +149,13 @@ export function useCurrentFramework(frameworks: Framework[]) { queryClient.setQueryData(currentUserQueryOptions.queryKey, (user) => user ? { ...user, lastUsedFramework: framework } : user, ) - const nextPathname = location.pathname.replace( - /(\/docs\/framework\/)[^/]+/, - `$1${framework}`, - ) - if (nextPathname !== location.pathname) { - const queryString = window.location.search - const hash = window.location.hash - navigate({ href: `${nextPathname}${queryString}${hash}` }) - } + // Update DB for logged-in users (fire-and-forget) if (userQuery.data) { persistFrameworkToServer(framework) } }, - [ - localCurrentFramework, - location.pathname, - navigate, - queryClient, - userQuery.data, - ], + [localCurrentFramework, queryClient, userQuery.data], ) React.useEffect(() => { From 3de42caa1ff134c7bcee66b61aed3795455f55fc Mon Sep 17 00:00:00 2001 From: TkDodo Date: Fri, 8 May 2026 15:53:06 +0200 Subject: [PATCH 3/5] bring back navigation --- src/components/FrameworkSelect.tsx | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/src/components/FrameworkSelect.tsx b/src/components/FrameworkSelect.tsx index 462de2214..b4f23e1a6 100644 --- a/src/components/FrameworkSelect.tsx +++ b/src/components/FrameworkSelect.tsx @@ -1,6 +1,6 @@ import * as React from 'react' import { create } from 'zustand' -import { useLocation, useNavigate, useParams } from '@tanstack/react-router' +import { useNavigate, useParams } from '@tanstack/react-router' import { useQueryClient } from '@tanstack/react-query' import { Select } from './Select' import { Framework, getLibrary, LibraryId } from '~/libraries' @@ -123,7 +123,6 @@ function useFrameworkConfig({ frameworks }: { frameworks: Framework[] }) { */ export function useCurrentFramework(frameworks: Framework[]) { const navigate = useNavigate() - const location = useLocation() const userQuery = useCurrentUserQuery() const queryClient = useQueryClient() @@ -144,6 +143,9 @@ export function useCurrentFramework(frameworks: Framework[]) { const setFramework = React.useCallback( (framework: string) => { + navigate({ + params: { framework } as any, + }) // Always update localStorage as fallback localCurrentFramework.setCurrentFramework(framework) queryClient.setQueryData(currentUserQueryOptions.queryKey, (user) => @@ -155,7 +157,7 @@ export function useCurrentFramework(frameworks: Framework[]) { persistFrameworkToServer(framework) } }, - [localCurrentFramework, queryClient, userQuery.data], + [localCurrentFramework, navigate, queryClient, userQuery.data], ) React.useEffect(() => { From 55d495c92172be564b4df813bb24555eacd5649b Mon Sep 17 00:00:00 2001 From: TkDodo Date: Fri, 8 May 2026 15:54:43 +0200 Subject: [PATCH 4/5] remove unused import --- src/components/FrameworkSelect.tsx | 1 - 1 file changed, 1 deletion(-) diff --git a/src/components/FrameworkSelect.tsx b/src/components/FrameworkSelect.tsx index b4f23e1a6..65f7a65e4 100644 --- a/src/components/FrameworkSelect.tsx +++ b/src/components/FrameworkSelect.tsx @@ -10,7 +10,6 @@ import { useCurrentUserQuery, } from '~/hooks/useCurrentUser' import { updateLastUsedFramework } from '~/utils/users.functions' -import type { User } from '~/db/types' function persistFrameworkToServer(framework: string) { void updateLastUsedFramework({ data: { framework } }).catch(() => { From 17ac026f3afdcbfc1c2800aab274845432976bc4 Mon Sep 17 00:00:00 2001 From: TkDodo Date: Fri, 8 May 2026 16:16:07 +0200 Subject: [PATCH 5/5] fix: make select larger for that many frameworks --- src/components/Select.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/Select.tsx b/src/components/Select.tsx index 286262337..5579a2dbf 100644 --- a/src/components/Select.tsx +++ b/src/components/Select.tsx @@ -67,7 +67,7 @@ export function Select({ - + {available.map((option) => (