-
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
11 changed files
with
260 additions
and
36 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,29 @@ | ||
import type { CreatePost } from '@yuki/api/types/post' | ||
import { Button } from '@yuki/ui/button' | ||
import { Input } from '@yuki/ui/input' | ||
|
||
import { api } from '@/lib/trpc' | ||
|
||
export const Post: React.FC = () => { | ||
const { data: latestPost, isLoading, refetch } = api.post.getLatestPost.useQuery() | ||
const createPost = api.post.createPost.useMutation({ onSuccess: () => refetch() }) | ||
|
||
return ( | ||
<div className="mt-4 w-full max-w-screen-sm"> | ||
<span className="text-lg"> | ||
Latest post: {isLoading ? 'Loading...' : (latestPost?.content ?? 'No posts')} | ||
</span> | ||
<form | ||
className="flex w-full gap-4" | ||
onSubmit={(e) => { | ||
e.preventDefault() | ||
createPost.mutate(Object.fromEntries(new FormData(e.currentTarget)) as CreatePost) | ||
e.currentTarget.reset() | ||
}} | ||
> | ||
<Input name="content" placeholder="Post's content" disabled={createPost.isPending} /> | ||
<Button disabled={createPost.isPending}>Post</Button> | ||
</form> | ||
</div> | ||
) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,21 @@ | ||
import { createContext, use } from 'react' | ||
|
||
type Cookies<T extends Record<string, string>> = T | undefined | ||
|
||
const cookiesContext = createContext<Cookies<Record<string, string>>>(undefined) | ||
|
||
export const CookiesProvider: React.FC<{ | ||
allCookies: Record<string, string> | ||
children: React.ReactNode | ||
}> = ({ allCookies, children }) => ( | ||
<cookiesContext.Provider value={allCookies}>{children}</cookiesContext.Provider> | ||
) | ||
|
||
export const useCookies = <T extends Record<string, string>>(): Cookies<T> => { | ||
const context = use(cookiesContext) | ||
if (context === undefined) throw new Error('useCookies must be used within a CookiesProvider') | ||
|
||
return { | ||
...context, | ||
} as T | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,64 @@ | ||
import type { QueryClient } from '@tanstack/react-query' | ||
import { useState } from 'react' | ||
import { QueryClientProvider } from '@tanstack/react-query' | ||
import { httpBatchLink, loggerLink } from '@trpc/client' | ||
import { createTRPCReact } from '@trpc/react-query' | ||
import SuperJSON from 'superjson' | ||
|
||
import type { AppRouter } from '@yuki/api' | ||
|
||
import { useCookies } from '@/lib/hooks/use-cookies' | ||
import { createQueryClient } from '@/lib/trpc/query-client' | ||
|
||
let clientQueryClientSingleton: QueryClient | undefined = undefined | ||
const getQueryClient = () => { | ||
if (typeof window === 'undefined') { | ||
// Server: always make a new query client | ||
return createQueryClient() | ||
} else { | ||
// Browser: use singleton pattern to keep the same query client | ||
return (clientQueryClientSingleton ??= createQueryClient()) | ||
} | ||
} | ||
|
||
export const api = createTRPCReact<AppRouter>() | ||
|
||
export const TRPCReactProvider: React.FC<React.PropsWithChildren> = ({ children }) => { | ||
const queryClient = getQueryClient() | ||
const cookies = useCookies<{ auth_session: string }>() | ||
|
||
const [trpcClient] = useState(() => | ||
api.createClient({ | ||
links: [ | ||
loggerLink({ | ||
enabled: (op) => | ||
// eslint-disable-next-line turbo/no-undeclared-env-vars | ||
import.meta.env.DEV || (op.direction === 'down' && op.result instanceof Error), | ||
}), | ||
httpBatchLink({ | ||
transformer: SuperJSON, | ||
url: getBaseUrl() + '/api/trpc', | ||
headers() { | ||
const headers = new Headers() | ||
headers.set('x-trpc-source', 'react-router') | ||
headers.set('Authorization', `Bearer ${cookies?.auth_session}`) | ||
return headers | ||
}, | ||
}), | ||
], | ||
}), | ||
) | ||
|
||
return ( | ||
<QueryClientProvider client={queryClient}> | ||
<api.Provider client={trpcClient} queryClient={queryClient}> | ||
{children} | ||
</api.Provider> | ||
</QueryClientProvider> | ||
) | ||
} | ||
|
||
const getBaseUrl = () => { | ||
// if () return `https://your.next.app.url` | ||
return `http://localhost:3000` | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,21 @@ | ||
import { defaultShouldDehydrateQuery, QueryClient } from '@tanstack/react-query' | ||
import SuperJSON from 'superjson' | ||
|
||
export const createQueryClient = () => | ||
new QueryClient({ | ||
defaultOptions: { | ||
queries: { | ||
// With SSR, we usually want to set some default staleTime | ||
// above 0 to avoid refetching immediately on the client | ||
staleTime: 60 * 1000, | ||
}, | ||
dehydrate: { | ||
serializeData: SuperJSON.serialize, | ||
shouldDehydrateQuery: (query) => | ||
defaultShouldDehydrateQuery(query) || query.state.status === 'pending', | ||
}, | ||
hydrate: { | ||
deserializeData: SuperJSON.deserialize, | ||
}, | ||
}, | ||
}) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,9 +1,64 @@ | ||
import { Button } from '@yuki/ui/button' | ||
import { Typography } from '@yuki/ui/typography' | ||
|
||
import type { Route } from './+types/_index' | ||
import { Post } from '@/components/post' | ||
|
||
export const loader = ({ context }: Route.LoaderArgs) => { | ||
return { message: context.VALUE_FROM_VERCEL } | ||
return { session: context.cookies.auth_session } | ||
Check failure on line 8 in apps/react-router/app/routes/_index.tsx
|
||
} | ||
|
||
export default ({ loaderData }: Route.ComponentProps) => { | ||
return <div>{loaderData.message}</div> | ||
const session = loaderData.session | ||
Check failure on line 12 in apps/react-router/app/routes/_index.tsx
|
||
|
||
return ( | ||
<main className="container flex min-h-dvh max-w-screen-lg flex-col items-center justify-center overflow-x-hidden"> | ||
<div className="pointer-events-none relative flex place-items-center before:absolute before:h-[700px] before:w-[140px] before:translate-x-1 before:translate-y-[-10px] before:rotate-[-32deg] before:rounded-full before:bg-gradient-to-r before:from-[#AB1D1C] before:to-[#E18317] before:opacity-30 before:blur-[100px] before:content-[''] lg:before:h-[700px] lg:before:w-[240px] lg:before:translate-x-[-100px]" /> | ||
|
||
<img | ||
src="https://tiesen.id.vn/assets/tiesen.png" | ||
width={2500} | ||
height={400} | ||
alt="tiesen" | ||
loading="lazy" | ||
/> | ||
|
||
<Typography level="h1" className="mb-4 text-center brightness-150"> | ||
A Next.js template with{' '} | ||
<span className="bg-[linear-gradient(135deg,#3178C6,69%,hsl(var(--background)))] bg-clip-text text-transparent"> | ||
TypeScript | ||
</span> | ||
,{' '} | ||
<span className="bg-[linear-gradient(135deg,#06B6D4,69%,hsl(var(--background)))] bg-clip-text text-transparent"> | ||
Tailwind CSS | ||
</span> | ||
,{' '} | ||
<span className="bg-[linear-gradient(135deg,#4B32C3,69%,hsl(var(--background)))] bg-clip-text text-transparent"> | ||
ESLint | ||
</span>{' '} | ||
and{' '} | ||
<span className="bg-[linear-gradient(135deg,#F7B93E,69%,hsl(var(--background)))] bg-clip-text text-transparent"> | ||
Prettier | ||
</span> | ||
</Typography> | ||
|
||
{session ? ( | ||
<> | ||
<div className="flex items-center justify-center gap-4"> | ||
<Typography className="text-center text-2xl">Logged in as {session}</Typography> | ||
|
||
<form> | ||
<Button size="sm">Sign out</Button> | ||
</form> | ||
</div> | ||
|
||
<Post /> | ||
</> | ||
) : ( | ||
<form action="/api/auth/discord"> | ||
<Button size="lg">Sign in with Discord</Button> | ||
</form> | ||
)} | ||
</main> | ||
) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,25 +1,37 @@ | ||
import { createRequestHandler } from "@react-router/express"; | ||
import express from "express"; | ||
import "react-router"; | ||
import { createRequestHandler } from '@react-router/express' | ||
import express from 'express' | ||
|
||
declare module "react-router" { | ||
import 'react-router' | ||
|
||
declare module 'react-router' { | ||
export interface AppLoadContext { | ||
VALUE_FROM_VERCEL: string; | ||
cookies: Record<string, string> | ||
} | ||
} | ||
|
||
const app = express(); | ||
const app = express() | ||
|
||
app.use( | ||
createRequestHandler({ | ||
// @ts-expect-error - virtual module provided by React Router at build time | ||
build: () => import("virtual:react-router/server-build"), | ||
getLoadContext() { | ||
build: () => import('virtual:react-router/server-build'), | ||
getLoadContext({ headers }) { | ||
const cookies = headers.cookie | ||
? headers.cookie.split(';').reduce( | ||
(acc, cookie) => { | ||
const [key, value] = cookie.split('=') as [string, string] | ||
acc[key.trim()] = value.trim() | ||
return acc | ||
}, | ||
{} as Record<string, string>, | ||
) | ||
: {} | ||
|
||
return { | ||
VALUE_FROM_VERCEL: "Hello from Vercel", | ||
}; | ||
cookies, | ||
} | ||
}, | ||
}) | ||
); | ||
}), | ||
) | ||
|
||
export default app; | ||
export default app |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,24 +1,31 @@ | ||
import { reactRouter } from '@react-router/dev/vite' | ||
import autoprefixer from 'autoprefixer' | ||
import tailwindcss from 'tailwindcss' | ||
import { defineConfig } from 'vite' | ||
import { defineConfig, loadEnv } from 'vite' | ||
import tsconfigPaths from 'vite-tsconfig-paths' | ||
|
||
export default defineConfig(({ isSsrBuild, command }) => ({ | ||
build: { | ||
rollupOptions: isSsrBuild | ||
? { | ||
input: './server/app.ts', | ||
} | ||
: undefined, | ||
}, | ||
css: { | ||
postcss: { | ||
plugins: [tailwindcss, autoprefixer], | ||
export default defineConfig(({ isSsrBuild, command, mode }) => { | ||
const env = loadEnv(mode, process.cwd(), '') | ||
|
||
return { | ||
build: { | ||
rollupOptions: isSsrBuild | ||
? { | ||
input: './server/app.ts', | ||
} | ||
: undefined, | ||
}, | ||
css: { | ||
postcss: { | ||
plugins: [tailwindcss, autoprefixer], | ||
}, | ||
}, | ||
ssr: { | ||
noExternal: command === 'build' ? true : undefined, | ||
}, | ||
define: { | ||
'process.env': JSON.stringify(env), | ||
}, | ||
}, | ||
ssr: { | ||
noExternal: command === 'build' ? true : undefined, | ||
}, | ||
plugins: [reactRouter(), tsconfigPaths()], | ||
})) | ||
plugins: [reactRouter(), tsconfigPaths()], | ||
} | ||
}) |