diff --git a/apps/spu-ui/next.config.js b/apps/spu-ui/next.config.js index 5817cc1..267214e 100644 --- a/apps/spu-ui/next.config.js +++ b/apps/spu-ui/next.config.js @@ -13,7 +13,12 @@ const config = { /** enable standalone output for docker self-hosting */ // output: "standalone", /** Enables hot reloading for local packages without a build step */ - transpilePackages: ["@sophys-web/ui", "@sophys-web/api", "@sophys-web/auth"], + transpilePackages: [ + "@sophys-web/ui", + "@sophys-web/api", + "@sophys-web/auth", + "@sophys-web/api-client", + ], /** We already do linting and typechecking as separate tasks in CI */ eslint: { ignoreDuringBuilds: true }, typescript: { ignoreBuildErrors: true }, diff --git a/apps/spu-ui/package.json b/apps/spu-ui/package.json index 4a8c348..7e95e55 100644 --- a/apps/spu-ui/package.json +++ b/apps/spu-ui/package.json @@ -18,6 +18,7 @@ "@sophys-web/api": "workspace:*", "@sophys-web/auth": "workspace:*", "@sophys-web/ui": "workspace:*", + "@sophys-web/api-client": "workspace:*", "@t3-oss/env-nextjs": "^0.10.1", "@trpc/next": "11.0.0-rc.645", "@tanstack/react-query-devtools": "catalog:", diff --git a/apps/spu-ui/src/app/_components/console.tsx b/apps/spu-ui/src/app/_components/console.tsx index 6cfb4cd..f6e6ea1 100644 --- a/apps/spu-ui/src/app/_components/console.tsx +++ b/apps/spu-ui/src/app/_components/console.tsx @@ -2,6 +2,7 @@ import { useEffect, useRef } from "react"; import { DownloadIcon, RotateCcwIcon, Trash2Icon } from "lucide-react"; +import { api } from "@sophys-web/api-client/react"; import { cn } from "@sophys-web/ui"; import { Button } from "@sophys-web/ui/button"; import { ScrollArea } from "@sophys-web/ui/scroll-area"; @@ -11,7 +12,6 @@ import { TooltipProvider, TooltipTrigger, } from "@sophys-web/ui/tooltip"; -import { api } from "../../trpc/react"; export function Console({ className }: { className?: string }) { const { data: messages, refetch } = api.consoleOutput.stream.useQuery(); diff --git a/apps/spu-ui/src/app/_components/history.tsx b/apps/spu-ui/src/app/_components/history.tsx index 094248d..e6f2326 100644 --- a/apps/spu-ui/src/app/_components/history.tsx +++ b/apps/spu-ui/src/app/_components/history.tsx @@ -1,6 +1,7 @@ "use client"; import { RotateCcwIcon } from "lucide-react"; +import { api } from "@sophys-web/api-client/react"; import { cn } from "@sophys-web/ui"; import { Badge } from "@sophys-web/ui/badge"; import { Button } from "@sophys-web/ui/button"; @@ -16,7 +17,6 @@ import { ScrollArea } from "@sophys-web/ui/scroll-area"; import type { HistoryItemProps } from "../../lib/types"; import { useQueue } from "../_hooks/use-queue"; import { schema } from "../../lib/schemas/plans/complete-acquisition"; -import { api } from "../../trpc/react"; function RedoButton(props: HistoryItemProps) { const { add } = useQueue(); diff --git a/apps/spu-ui/src/app/_components/queue/queue.tsx b/apps/spu-ui/src/app/_components/queue/queue.tsx index 140378f..108a679 100644 --- a/apps/spu-ui/src/app/_components/queue/queue.tsx +++ b/apps/spu-ui/src/app/_components/queue/queue.tsx @@ -2,6 +2,7 @@ import { useCallback } from "react"; import { PlayIcon, SquareIcon, Trash2Icon } from "lucide-react"; +import { api } from "@sophys-web/api-client/react"; import { Button } from "@sophys-web/ui/button"; import { DropdownMenu, @@ -13,7 +14,6 @@ import { ScrollArea } from "@sophys-web/ui/scroll-area"; import { toast } from "@sophys-web/ui/sonner"; import { useQueue } from "../../_hooks/use-queue"; import { useStatus } from "../../_hooks/use-status"; -import { api } from "../../../trpc/react"; import { EnvMenu } from "../env-menu"; import { History } from "../history"; import { getEngineStatus } from "../run-engine-controls"; diff --git a/apps/spu-ui/src/app/_components/queue/upload.tsx b/apps/spu-ui/src/app/_components/queue/upload.tsx index cb68629..7d42305 100644 --- a/apps/spu-ui/src/app/_components/queue/upload.tsx +++ b/apps/spu-ui/src/app/_components/queue/upload.tsx @@ -5,6 +5,7 @@ import { zodResolver } from "@hookform/resolvers/zod"; import { MoveRightIcon, UploadIcon } from "lucide-react"; import { useForm } from "react-hook-form"; import { z } from "zod"; +import { api } from "@sophys-web/api-client/react"; import { Button } from "@sophys-web/ui/button"; import { Checkbox } from "@sophys-web/ui/checkbox"; import { @@ -33,7 +34,6 @@ import type { cleaningSchema as cleaningKwargsSchema, } from "../../../lib/schemas/plans/complete-acquisition"; import type { Sample } from "../sample"; -import { api } from "~/trpc/react"; import { useQueue } from "../../_hooks/use-queue"; import { name as cleanCapillaryPlanName } from "../../../lib/schemas/plans/clean-and-acquire"; import { name as acquisitionPlanName } from "../../../lib/schemas/plans/complete-acquisition"; diff --git a/apps/spu-ui/src/app/_components/run-engine-controls.tsx b/apps/spu-ui/src/app/_components/run-engine-controls.tsx index c9c00a2..9c12337 100644 --- a/apps/spu-ui/src/app/_components/run-engine-controls.tsx +++ b/apps/spu-ui/src/app/_components/run-engine-controls.tsx @@ -7,6 +7,7 @@ import { Play, Square, } from "lucide-react"; +import { api } from "@sophys-web/api-client/react"; import { Button } from "@sophys-web/ui/button"; import { DropdownMenu, @@ -16,7 +17,6 @@ import { } from "@sophys-web/ui/dropdown-menu"; import { toast } from "@sophys-web/ui/sonner"; import { useStatus } from "../_hooks/use-status"; -import { api } from "../../trpc/react"; type EngineStatus = "idle" | "running" | "paused" | "unknown"; type EngineAction = "pause" | "resume" | "stop" | "halt" | "abort"; diff --git a/apps/spu-ui/src/app/_components/select-plan.tsx b/apps/spu-ui/src/app/_components/select-plan.tsx index ffaad4b..4ae237c 100644 --- a/apps/spu-ui/src/app/_components/select-plan.tsx +++ b/apps/spu-ui/src/app/_components/select-plan.tsx @@ -1,6 +1,7 @@ "use client"; import { useState } from "react"; +import { api } from "@sophys-web/api-client/react"; import { Select, SelectContent, @@ -8,7 +9,6 @@ import { SelectTrigger, SelectValue, } from "@sophys-web/ui/select"; -import { api } from "../../trpc/react"; export function SelectPlan() { const { data } = api.plans.allowed.useQuery(undefined); diff --git a/apps/spu-ui/src/app/_hooks/use-capillary-state.tsx b/apps/spu-ui/src/app/_hooks/use-capillary-state.tsx index 75580d1..580d87c 100644 --- a/apps/spu-ui/src/app/_hooks/use-capillary-state.tsx +++ b/apps/spu-ui/src/app/_hooks/use-capillary-state.tsx @@ -1,4 +1,5 @@ import { useEffect, useState } from "react"; +import { api } from "@sophys-web/api-client/react"; import type { sampleTypeOptions, trayColumns, @@ -11,7 +12,6 @@ import { schema as loadSampleKwargs, name as loadSamplePlan, } from "../../lib/schemas/plans/load"; -import { api } from "../../trpc/react"; const STALE_TIME_IN_SECONDS = 15 * 60; // 15 minutes diff --git a/apps/spu-ui/src/app/_hooks/use-queue.tsx b/apps/spu-ui/src/app/_hooks/use-queue.tsx index 5fc55d4..6e5a660 100644 --- a/apps/spu-ui/src/app/_hooks/use-queue.tsx +++ b/apps/spu-ui/src/app/_hooks/use-queue.tsx @@ -1,8 +1,8 @@ "use client"; import { nanoid } from "nanoid"; +import { api } from "@sophys-web/api-client/react"; import type { QueueItemProps } from "../../lib/types"; -import { api } from "../../trpc/react"; export const useQueue = () => { const utils = api.useUtils(); diff --git a/apps/spu-ui/src/app/_hooks/use-status.tsx b/apps/spu-ui/src/app/_hooks/use-status.tsx index 3a64352..90a3974 100644 --- a/apps/spu-ui/src/app/_hooks/use-status.tsx +++ b/apps/spu-ui/src/app/_hooks/use-status.tsx @@ -1,6 +1,6 @@ "use client"; -import { api } from "../../trpc/react"; +import { api } from "@sophys-web/api-client/react"; export const useStatus = () => { const utils = api.useUtils(); diff --git a/apps/spu-ui/src/app/layout.tsx b/apps/spu-ui/src/app/layout.tsx index c738842..b647b07 100644 --- a/apps/spu-ui/src/app/layout.tsx +++ b/apps/spu-ui/src/app/layout.tsx @@ -3,9 +3,9 @@ import "./globals.css"; import type { Metadata } from "next"; import Image from "next/image"; import { GeistMono } from "geist/font/mono"; +import { TRPCReactProvider } from "@sophys-web/api-client/react"; import { Toaster } from "@sophys-web/ui/sonner"; import { env } from "../env"; -import { TRPCReactProvider } from "../trpc/react"; import UserAvatar from "./_components/user-avatar"; export const metadata: Metadata = { diff --git a/apps/spu-ui/src/trpc/query-client.ts b/apps/spu-ui/src/trpc/query-client.ts deleted file mode 100644 index a57ebce..0000000 --- a/apps/spu-ui/src/trpc/query-client.ts +++ /dev/null @@ -1,25 +0,0 @@ -import { - defaultShouldDehydrateQuery, - QueryClient, -} from "@tanstack/react-query"; -import { deserialize, serialize } 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: 30 * 1000, - }, - dehydrate: { - serializeData: serialize, - shouldDehydrateQuery: (query) => - defaultShouldDehydrateQuery(query) || - query.state.status === "pending", - }, - hydrate: { - deserializeData: deserialize, - }, - }, - }); diff --git a/apps/spu-ui/src/trpc/react.tsx b/apps/spu-ui/src/trpc/react.tsx deleted file mode 100644 index d26ae56..0000000 --- a/apps/spu-ui/src/trpc/react.tsx +++ /dev/null @@ -1,66 +0,0 @@ -"use client"; - -import type { QueryClient } from "@tanstack/react-query"; -import { useState } from "react"; -import { QueryClientProvider } from "@tanstack/react-query"; -import { ReactQueryDevtools } from "@tanstack/react-query-devtools"; -import { loggerLink, unstable_httpBatchStreamLink } from "@trpc/client"; -import { createTRPCReact } from "@trpc/react-query"; -import superJSON from "superjson"; -import type { AppRouter } from "@sophys-web/api"; -import { env } from "../env"; -import { createQueryClient } from "./query-client"; - -let clientQueryClientSingleton: QueryClient | undefined; -const getQueryClient = () => { - if (typeof window === "undefined") { - // Server: always make a new query client - return createQueryClient(); - } - // Browser: use singleton pattern to keep the same query client - return (clientQueryClientSingleton ??= createQueryClient()); -}; - -export const api = createTRPCReact(); - -export function TRPCReactProvider(props: { children: React.ReactNode }) { - const queryClient = getQueryClient(); - - const [trpcClient] = useState(() => - api.createClient({ - links: [ - loggerLink({ - enabled: (op) => - env.NODE_ENV === "development" || - (op.direction === "down" && op.result instanceof Error), - }), - unstable_httpBatchStreamLink({ - transformer: superJSON, - url: `${getBaseUrl()}/api/trpc`, - headers() { - const headers = new Headers(); - headers.set("x-trpc-source", "nextjs-react"); - return headers; - }, - }), - ], - }), - ); - - return ( - - - - {props.children} - - - ); -} - -const getBaseUrl = () => { - if (env.COOLIFY_URL) return `https://${env.COOLIFY_URL}`; - if (typeof window !== "undefined") - return `${window.location.origin}${env.NEXT_PUBLIC_BASE_PATH}`; - - return `http://localhost${env.NEXT_PUBLIC_BASE_PATH}:${env.PORT ?? 3000}`; -}; diff --git a/apps/spu-ui/src/trpc/server.ts b/apps/spu-ui/src/trpc/server.ts deleted file mode 100644 index e716af2..0000000 --- a/apps/spu-ui/src/trpc/server.ts +++ /dev/null @@ -1,28 +0,0 @@ -import "server-only"; -import { headers } from "next/headers"; -import { createHydrationHelpers } from "@trpc/react-query/rsc"; -import type { AppRouter } from "@sophys-web/api"; -import { createCaller, createTRPCContext } from "@sophys-web/api"; -import { auth } from "@sophys-web/auth"; -import { createQueryClient } from "./query-client"; - -/** - * This wraps the `createTRPCContext` helper and provides the required context for the tRPC API when - * handling a tRPC call from a React Server Component. - */ -const createContext = async () => { - const heads = new Headers(headers()); - heads.set("x-trpc-source", "rsc"); - - return createTRPCContext({ - session: await auth(), - headers: heads, - }); -}; - -const caller = createCaller(createContext); - -export const { trpc: api, HydrateClient } = createHydrationHelpers( - caller, - createQueryClient, -); diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 206d354..c17553d 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -97,6 +97,9 @@ importers: '@sophys-web/api': specifier: workspace:* version: link:../../packages/api + '@sophys-web/api-client': + specifier: workspace:* + version: link:../../packages/api-client '@sophys-web/auth': specifier: workspace:* version: link:../../packages/auth