Refresh UI when receiving deep link
This commit is contained in:
@@ -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>
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -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}
|
||||||
|
|||||||
34
src/contexts/DeepLinkContext.tsx
Normal file
34
src/contexts/DeepLinkContext.tsx
Normal 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);
|
||||||
@@ -98,7 +98,7 @@ export function useSettings() {
|
|||||||
);
|
);
|
||||||
},
|
},
|
||||||
refreshSettings: () => {
|
refreshSettings: () => {
|
||||||
loadInitialData();
|
return loadInitialData();
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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);
|
||||||
|
};
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
12
src/main.ts
12
src/main.ts
@@ -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,
|
||||||
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -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];
|
||||||
|
|||||||
Reference in New Issue
Block a user