diff --git a/.github/pr-title-checker-config.json b/.github/pr-title-checker-config.json index fc9475a96..2df6603cd 100644 --- a/.github/pr-title-checker-config.json +++ b/.github/pr-title-checker-config.json @@ -4,7 +4,7 @@ "color": "CDAC11" }, "CHECKS": { - "regexp": "^([A-Z]{3,}-[0-9]+|[A-Z]{3,}-[0-9]+( )[A-Z]{3,}-[0-9]+) ((feat|fix|chore)[(](portal|1ui|core|subgraph|protocol|sdk|substream|api|tools|repo|graphql|data-populator)(,(portal|1ui|core|subgraph|protocol|sdk|substream|api|tools|repo|graphql|data-populator))*[)]): .+$", + "regexp": "^([A-Z]{3,}-[0-9]+|[A-Z]{3,}-[0-9]+( )[A-Z]{3,}-[0-9]+) ((feat|fix|chore)[(](portal|1ui|core|subgraph|protocol|sdk|substream|api|tools|repo|graphql|template|data-populator)(,(portal|1ui|core|subgraph|protocol|sdk|substream|api|tools|repo|graphql|template|data-populator))*[)]): .+$", "regexpFlags": "i" }, "MESSAGES": { diff --git a/apps/template/app/.client/providers.tsx b/apps/template/app/.client/providers.tsx index d57df0008..9d7d59068 100644 --- a/apps/template/app/.client/providers.tsx +++ b/apps/template/app/.client/providers.tsx @@ -2,9 +2,20 @@ import { wagmiConfig } from '@lib/utils/wagmi' import type { PrivyClientConfig } from '@privy-io/react-auth' import { PrivyProvider } from '@privy-io/react-auth' import { WagmiProvider } from '@privy-io/wagmi' -import { QueryClient, QueryClientProvider } from '@tanstack/react-query' +import { + HydrationBoundary, + QueryClient, + QueryClientProvider, +} from '@tanstack/react-query' -const queryClient = new QueryClient() +const queryClient = new QueryClient({ + defaultOptions: { + queries: { + // Disable automatic refetching on window focus for SSR + refetchOnWindowFocus: false, + }, + }, +}) const privyConfig: PrivyClientConfig = { embeddedWallets: { @@ -22,21 +33,20 @@ const privyConfig: PrivyClientConfig = { export default function Providers({ privyAppId, children, + dehydratedState, }: { privyAppId: string children: React.ReactNode + dehydratedState?: unknown }) { return ( - + - - {children} - + + + {children} + + ) diff --git a/apps/template/app/routes/app+/playground-hydration-search.tsx b/apps/template/app/routes/app+/playground-hydration-search.tsx new file mode 100644 index 000000000..2a5606d4c --- /dev/null +++ b/apps/template/app/routes/app+/playground-hydration-search.tsx @@ -0,0 +1,97 @@ +import { useEffect, useState } from 'react' + +import { Input, Text } from '@0xintuition/1ui' +import { + fetcher, + GetAtomsDocument, + useGetAtomsQuery, +} from '@0xintuition/graphql' + +import { json, type LoaderFunctionArgs } from '@remix-run/node' +import { useLoaderData, useSearchParams } from '@remix-run/react' +import { + dehydrate, + HydrationBoundary, + QueryClient, +} from '@tanstack/react-query' + +// Simple debounce hook +function useDebounce(value: T, delay: number): T { + const [debouncedValue, setDebouncedValue] = useState(value) + + useEffect(() => { + const timer = setTimeout(() => setDebouncedValue(value), delay) + return () => clearTimeout(timer) + }, [value, delay]) + + return debouncedValue +} + +export async function loader({ request }: LoaderFunctionArgs) { + const url = new URL(request.url) + const search = url.searchParams.get('search') || '' + + const queryClient = new QueryClient() + + await queryClient.prefetchQuery({ + queryKey: ['get-atoms-query', { search }], + queryFn: () => + fetcher(GetAtomsDocument, { + where: search + ? { + _or: [{ label: { _ilike: `%${search}%` } }], + } + : undefined, + })(), + }) + + return json({ + dehydratedState: dehydrate(queryClient), + initialSearch: search, + }) +} + +export default function PlaygroundHydration() { + const { dehydratedState, initialSearch } = useLoaderData() + const [searchParams, setSearchParams] = useSearchParams() + const [inputValue, setInputValue] = useState( + searchParams.get('search') || initialSearch, + ) + const debouncedSearch = useDebounce(inputValue, 300) // 300ms delay + + // Update URL params when debounced search changes + useEffect(() => { + const params = new URLSearchParams(searchParams) + params.set('search', debouncedSearch) + setSearchParams(params) + }, [debouncedSearch, setSearchParams]) + + const { data: atomsData } = useGetAtomsQuery( + { + where: debouncedSearch + ? { + _or: [{ label: { _ilike: `%${debouncedSearch}%` } }], + } + : undefined, + }, + { + queryKey: ['get-atoms-query', { search: debouncedSearch }], + }, + ) + + return ( + +
+ Playground Hydration + setInputValue(e.target.value)} + placeholder="Search atoms by label or description..." + /> + +
{JSON.stringify(atomsData?.atoms || [], null, 2)}
+
+
+ ) +} diff --git a/apps/template/app/routes/app+/playground-hydration.tsx b/apps/template/app/routes/app+/playground-hydration.tsx new file mode 100644 index 000000000..455d848a1 --- /dev/null +++ b/apps/template/app/routes/app+/playground-hydration.tsx @@ -0,0 +1,84 @@ +import { Text } from '@0xintuition/1ui' +import { + fetcher, + GetAtomsDocument, + GetAtomsQuery, + GetAtomsQueryVariables, + useGetAtomsQuery, +} from '@0xintuition/graphql' + +import { json, type LoaderFunctionArgs } from '@remix-run/node' +import { useLoaderData, useSearchParams } from '@remix-run/react' +import { dehydrate, QueryClient } from '@tanstack/react-query' + +export async function loader({ request }: LoaderFunctionArgs) { + const url = new URL(request.url) + const limit = parseInt(url.searchParams.get('limit') || '10') + const offset = parseInt(url.searchParams.get('offset') || '0') + + const queryClient = new QueryClient() + + await queryClient.prefetchQuery({ + queryKey: ['GetAtoms', { limit, offset }], + queryFn: () => + fetcher(GetAtomsDocument, { + limit, + offset, + })(), + }) + + return json({ + dehydratedState: dehydrate(queryClient), + initialParams: { limit, offset }, + }) +} + +export default function PlaygroundHydration() { + const { initialParams } = useLoaderData() + const [searchParams, setSearchParams] = useSearchParams() + + const limit = parseInt( + searchParams.get('limit') || String(initialParams.limit), + ) + const offset = parseInt( + searchParams.get('offset') || String(initialParams.offset), + ) + + const { data: atomsData } = useGetAtomsQuery( + { limit, offset }, + { + queryKey: ['GetAtoms', { limit, offset }], + }, + ) + + return ( +
+ Playground Hydration Pagination +
+ + Page {offset / limit + 1} + +
+ +
{JSON.stringify(atomsData?.atoms || [], null, 2)}
+
+ ) +}