Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 5 additions & 5 deletions apps/desktop/src/lib/trpc/routers/auth/index.ts
Original file line number Diff line number Diff line change
@@ -1,16 +1,16 @@
import { AUTH_PROVIDERS } from "@superset/shared/constants";
import { observable } from "@trpc/server/observable";
import { authService } from "main/lib/auth";
import { type AuthSession, authService } from "main/lib/auth";
import { z } from "zod";
import { publicProcedure, router } from "../..";

/** Auth state emitted by onAuthState subscription */
export type AuthState = (AuthSession & { token: string | null }) | null;

export const createAuthRouter = () => {
return router({
onAuthState: publicProcedure.subscription(() => {
return observable<
| (ReturnType<typeof authService.getSession> & { token: string | null })
| null
>((emit) => {
return observable<AuthState>((emit) => {
const emitCurrent = () => {
const sessionData = authService.getSession();
const token = authService.getAccessToken();
Expand Down
5 changes: 4 additions & 1 deletion apps/desktop/src/main/lib/auth/auth.ts
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,10 @@ interface StoredAuth {
expiresAt: string;
}

type Session = Awaited<ReturnType<typeof authClient.getSession>>;
type SessionResponse = Awaited<ReturnType<typeof authClient.getSession>>;
/** Session data from the auth API */
export type AuthSession = NonNullable<SessionResponse["data"]>;
type Session = AuthSession;

class AuthService extends EventEmitter {
private token: string | null = null;
Expand Down
2 changes: 1 addition & 1 deletion apps/desktop/src/main/lib/auth/index.ts
Original file line number Diff line number Diff line change
@@ -1,2 +1,2 @@
export type { SignInResult } from "./auth";
export type { AuthSession, SignInResult } from "./auth";
export { authService, parseAuthDeepLink } from "./auth";
11 changes: 5 additions & 6 deletions apps/desktop/src/renderer/contexts/AuthProvider/AuthProvider.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,22 +2,21 @@ import { createContext, type ReactNode, useContext } from "react";
import type { RouterOutputs } from "../../lib/trpc";
import { trpc } from "../../lib/trpc";

type AuthState = RouterOutputs["auth"]["onAuthState"];

interface AuthContextValue {
token: string | null;
session: RouterOutputs["auth"]["onAuthState"] | null;
session: AuthState;
}

const AuthContext = createContext<AuthContextValue | null>(null);

export function AuthProvider({ children }: { children: ReactNode }) {
const { data: authState } = trpc.auth.onAuthState.useSubscription();

const token = authState?.token ?? null;
const session = authState ?? null;

const value: AuthContextValue = {
token,
session,
token: authState?.token ?? null,
session: authState ?? null,
};

return <AuthContext.Provider value={value}>{children}</AuthContext.Provider>;
Expand Down
11 changes: 3 additions & 8 deletions apps/desktop/src/renderer/contexts/TRPCProvider/TRPCProvider.tsx
Original file line number Diff line number Diff line change
@@ -1,8 +1,7 @@
import { QueryClient, QueryClientProvider } from "@tanstack/react-query";
import { trpc } from "lib/trpc";
import { useState } from "react";
import superjson from "superjson";
import { ipcLink } from "trpc-electron/renderer";
import { reactClient } from "../../lib/trpc-client";

export function TRPCProvider({ children }: { children: React.ReactNode }) {
const [queryClient] = useState(
Expand All @@ -20,13 +19,9 @@ export function TRPCProvider({ children }: { children: React.ReactNode }) {
},
}),
);
const [trpcClient] = useState(() =>
trpc.createClient({
links: [ipcLink({ transformer: superjson })],
}),
);

return (
<trpc.Provider client={trpcClient} queryClient={queryClient}>
<trpc.Provider client={reactClient} queryClient={queryClient}>
<QueryClientProvider client={queryClient}>{children}</QueryClientProvider>
</trpc.Provider>
);
Expand Down
33 changes: 33 additions & 0 deletions apps/desktop/src/renderer/lib/session-id-link.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
import type { TRPCLink } from "@trpc/client";
import { observable } from "@trpc/server/observable";
import type { AppRouter } from "lib/trpc/routers";

/**
* Global counter for unique operation IDs across all tRPC clients.
* Starts from Date.now() to ensure uniqueness across page refreshes.
*/
let globalOperationId = Date.now();

/**
* Assigns globally unique operation IDs to prevent collisions between
* the React client and proxy client (each creates separate IPCClients
* that both receive all IPC responses and match by ID).
*/
export function sessionIdLink(): TRPCLink<AppRouter> {
return () => {
return ({ op, next }) => {
const uniqueId = ++globalOperationId;

return observable((observer) => {
return next({
...op,
id: uniqueId,
}).subscribe({
next: (result) => observer.next(result),
error: (err) => observer.error(err),
complete: () => observer.complete(),
});
});
};
};
}
13 changes: 9 additions & 4 deletions apps/desktop/src/renderer/lib/trpc-client.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,15 @@ import { createTRPCProxyClient } from "@trpc/client";
import type { AppRouter } from "lib/trpc/routers";
import superjson from "superjson";
import { ipcLink } from "trpc-electron/renderer";
import { sessionIdLink } from "./session-id-link";
import { trpc } from "./trpc";
Comment thread
Kitenite marked this conversation as resolved.

/**
* Use this when you need to call tRPC procedures from stores, utilities, etc.
*/
/** tRPC client for React hooks (used by TRPCProvider). */
export const reactClient = trpc.createClient({
links: [sessionIdLink(), ipcLink({ transformer: superjson })],
});

/** tRPC proxy client for imperative calls from stores/utilities. */
export const trpcClient = createTRPCProxyClient<AppRouter>({
links: [ipcLink({ transformer: superjson })],
links: [sessionIdLink(), ipcLink({ transformer: superjson })],
});
Loading