Refresh UI when receiving deep link

This commit is contained in:
Will Chen
2025-04-22 21:56:43 -07:00
parent ae2cb0fc6b
commit 42b759d85c
7 changed files with 90 additions and 10 deletions

View File

@@ -1,6 +1,7 @@
import { SidebarProvider } from "@/components/ui/sidebar"; import { SidebarProvider } from "@/components/ui/sidebar";
import { AppSidebar } from "@/components/app-sidebar"; import { AppSidebar } from "@/components/app-sidebar";
import { ThemeProvider } from "../contexts/ThemeContext"; import { ThemeProvider } from "../contexts/ThemeContext";
import { DeepLinkProvider } from "../contexts/DeepLinkContext";
import { Toaster } from "sonner"; import { Toaster } from "sonner";
import { TitleBar } from "./TitleBar"; import { TitleBar } from "./TitleBar";
@@ -13,6 +14,7 @@ export default function RootLayout({
<> <>
<TitleBar /> <TitleBar />
<ThemeProvider> <ThemeProvider>
<DeepLinkProvider>
<SidebarProvider> <SidebarProvider>
<AppSidebar /> <AppSidebar />
<div className="flex h-screenish w-full overflow-x-hidden mt-8 mb-4 mr-4 border-t border-l border-border rounded-lg bg-background"> <div className="flex h-screenish w-full overflow-x-hidden mt-8 mb-4 mr-4 border-t border-l border-border rounded-lg bg-background">
@@ -20,6 +22,7 @@ export default function RootLayout({
</div> </div>
<Toaster richColors /> <Toaster richColors />
</SidebarProvider> </SidebarProvider>
</DeepLinkProvider>
</ThemeProvider> </ThemeProvider>
</> </>
); );

View File

@@ -23,12 +23,24 @@ import {
} from "@/components/ui/card"; } from "@/components/ui/card";
import { Skeleton } from "@/components/ui/skeleton"; import { Skeleton } from "@/components/ui/skeleton";
import { useLoadApp } from "@/hooks/useLoadApp"; import { useLoadApp } from "@/hooks/useLoadApp";
import { useDeepLink } from "@/contexts/DeepLinkContext";
const OAUTH_CLIENT_ID = "bf747de7-60bb-48a2-9015-6494e0b04983"; const OAUTH_CLIENT_ID = "bf747de7-60bb-48a2-9015-6494e0b04983";
export function SupabaseConnector({ appId }: { appId: number }) { export function SupabaseConnector({ appId }: { appId: number }) {
const [isConnecting, setIsConnecting] = useState(false); const [isConnecting, setIsConnecting] = useState(false);
const { settings } = useSettings(); const { settings, refreshSettings } = useSettings();
const { app, refreshApp } = useLoadApp(appId); const { app, refreshApp } = useLoadApp(appId);
const { lastDeepLink } = useDeepLink();
useEffect(() => {
const handleDeepLink = async () => {
if (lastDeepLink?.type === "supabase-oauth-return") {
await refreshSettings();
await refreshApp();
}
};
handleDeepLink();
}, [lastDeepLink]);
const { const {
projects, projects,
loading, loading,
@@ -176,7 +188,7 @@ export function SupabaseConnector({ appId }: { appId: number }) {
return ( return (
<div className="flex flex-col space-y-4 p-4 border rounded-md"> <div className="flex flex-col space-y-4 p-4 border rounded-md">
<h2 className="text-lg font-semibold">Connect to Supabase</h2> <h2 className="text-lg font-semibold">Supabase Integration</h2>
<Button <Button
onClick={handleConnect} onClick={handleConnect}

View File

@@ -0,0 +1,34 @@
import React, { createContext, useContext, useEffect, useState } from "react";
import { IpcClient, DeepLinkData } from "../ipc/ipc_client";
type DeepLinkContextType = {
lastDeepLink: (DeepLinkData & { timestamp: number }) | null;
};
const DeepLinkContext = createContext<DeepLinkContextType>({
lastDeepLink: null,
});
export function DeepLinkProvider({ children }: { children: React.ReactNode }) {
const [lastDeepLink, setLastDeepLink] = useState<
(DeepLinkData & { timestamp: number }) | null
>(null);
useEffect(() => {
const ipcClient = IpcClient.getInstance();
const unsubscribe = ipcClient.onDeepLinkReceived((data) => {
// Update with timestamp to ensure state change even if same type comes twice
setLastDeepLink({ ...data, timestamp: Date.now() });
});
return unsubscribe;
}, []);
return (
<DeepLinkContext.Provider value={{ lastDeepLink }}>
{children}
</DeepLinkContext.Provider>
);
}
export const useDeepLink = () => useContext(DeepLinkContext);

View File

@@ -98,7 +98,7 @@ export function useSettings() {
); );
}, },
refreshSettings: () => { refreshSettings: () => {
loadInitialData(); return loadInitialData();
}, },
}; };
} }

View File

@@ -45,6 +45,11 @@ export interface GitHubDeviceFlowErrorData {
error: string; error: string;
} }
export interface DeepLinkData {
type: string;
url?: string;
}
export class IpcClient { export class IpcClient {
private static instance: IpcClient; private static instance: IpcClient;
private ipcRenderer: IpcRenderer; private ipcRenderer: IpcRenderer;
@@ -731,4 +736,17 @@ export class IpcClient {
throw error; throw error;
} }
} }
// Listen for deep link events
public onDeepLinkReceived(
callback: (data: DeepLinkData) => void
): () => void {
const listener = (data: any) => {
callback(data as DeepLinkData);
};
this.ipcRenderer.on("deep-link-received", listener);
return () => {
this.ipcRenderer.removeListener("deep-link-received", listener);
};
}
} }

View File

@@ -152,6 +152,13 @@ app.on("open-url", (event, url) => {
function handleDeepLinkReturn(url: string) { function handleDeepLinkReturn(url: string) {
// example url: "dyad://supabase-oauth-return?token=a&refreshToken=b" // example url: "dyad://supabase-oauth-return?token=a&refreshToken=b"
const parsed = new URL(url); const parsed = new URL(url);
// Intentionally do NOT log the full URL which may contain sensitive tokens.
log.log(
"Handling deep link: protocol",
parsed.protocol,
"hostname",
parsed.hostname
);
if (parsed.protocol !== "dyad:") { if (parsed.protocol !== "dyad:") {
dialog.showErrorBox( dialog.showErrorBox(
"Invalid Protocol", "Invalid Protocol",
@@ -170,6 +177,11 @@ function handleDeepLinkReturn(url: string) {
return; return;
} }
handleSupabaseOAuthReturn({ token, refreshToken, expiresIn }); handleSupabaseOAuthReturn({ token, refreshToken, expiresIn });
// Send message to renderer to trigger re-render
mainWindow?.webContents.send("deep-link-received", {
type: parsed.hostname,
url,
});
} }
} }

View File

@@ -56,6 +56,7 @@ const validReceiveChannels = [
"github:flow-update", "github:flow-update",
"github:flow-success", "github:flow-success",
"github:flow-error", "github:flow-error",
"deep-link-received",
] as const; ] as const;
type ValidInvokeChannel = (typeof validInvokeChannels)[number]; type ValidInvokeChannel = (typeof validInvokeChannels)[number];