auto-update oauth connect state in ui

This commit is contained in:
Ramnique Singh 2026-01-20 15:18:47 +05:30
parent 24837f867b
commit bdaca80d59
5 changed files with 86 additions and 20 deletions

View file

@ -210,6 +210,15 @@ function emitRunEvent(event: z.infer<typeof RunEvent>): void {
}
}
export function emitOAuthEvent(event: { provider: string; success: boolean; error?: string }): void {
const windows = BrowserWindow.getAllWindows();
for (const win of windows) {
if (!win.isDestroyed() && win.webContents) {
win.webContents.send('oauth:didConnect', event);
}
}
}
let runsWatcher: (() => void) | null = null;
export async function startRunsWatcher(): Promise<void> {
if (runsWatcher) {

View file

@ -9,6 +9,7 @@ import { IClientRegistrationRepo } from '@x/core/dist/auth/client-repo.js';
import { triggerSync as triggerGmailSync } from '@x/core/dist/knowledge/sync_gmail.js';
import { triggerSync as triggerCalendarSync } from '@x/core/dist/knowledge/sync_calendar.js';
import { triggerSync as triggerFirefliesSync } from '@x/core/dist/knowledge/sync_fireflies.js';
import { emitOAuthEvent } from './ipc.js';
const REDIRECT_URI = 'http://localhost:8080/oauth/callback';
@ -160,8 +161,13 @@ export async function connectProvider(provider: string): Promise<{ success: bool
} else if (provider === 'fireflies-ai') {
triggerFirefliesSync();
}
// Emit success event to renderer
emitOAuthEvent({ provider, success: true });
} catch (error) {
console.error('OAuth token exchange failed:', error);
const errorMessage = error instanceof Error ? error.message : 'Unknown error';
emitOAuthEvent({ provider, success: false, error: errorMessage });
throw error;
} finally {
// Clean up
@ -178,6 +184,7 @@ export async function connectProvider(provider: string): Promise<{ success: bool
console.log(`[OAuth] Cleaning up abandoned OAuth flow for ${provider} (timeout)`);
activeFlows.delete(state);
server.close();
emitOAuthEvent({ provider, success: false, error: 'OAuth flow timed out' });
}
}, 5 * 60 * 1000); // 5 minutes

View file

@ -127,6 +127,32 @@ export function ConnectorsPopover({ children, tooltip }: ConnectorsPopoverProps)
}
}, [open, providers, refreshAllStatuses])
// Listen for OAuth completion events
useEffect(() => {
const cleanup = window.ipc.on('oauth:didConnect', (event) => {
const { provider, success, error } = event
setProviderStates(prev => ({
...prev,
[provider]: {
isConnected: success,
isLoading: false,
isConnecting: false,
}
}))
if (success) {
toast(`Successfully connected to ${provider}`, 'success')
// Refresh status to ensure consistency
refreshAllStatuses()
} else {
toast(error || `Failed to connect to ${provider}`, 'error')
}
})
return cleanup
}, [refreshAllStatuses])
// Connect to a provider
const handleConnect = useCallback(async (provider: string) => {
setProviderStates(prev => ({
@ -138,18 +164,10 @@ export function ConnectorsPopover({ children, tooltip }: ConnectorsPopoverProps)
const result = await window.ipc.invoke('oauth:connect', { provider })
if (result.success) {
toast(`Successfully connected to ${provider}`, 'success')
// Refresh the status after successful connection
const checkResult = await window.ipc.invoke('oauth:is-connected', { provider })
setProviderStates(prev => ({
...prev,
[provider]: {
isConnected: checkResult.isConnected,
isLoading: false,
isConnecting: false,
}
}))
// OAuth flow started - keep isConnecting state, wait for event
// Event listener will handle the actual completion
} else {
// Immediate failure (e.g., couldn't start flow)
toast(result.error || `Failed to connect to ${provider}`, 'error')
setProviderStates(prev => ({
...prev,

View file

@ -9,11 +9,6 @@ export function useOAuth(provider: string) {
const [isLoading, setIsLoading] = useState<boolean>(true);
const [isConnecting, setIsConnecting] = useState<boolean>(false);
// Check connection status on mount and when provider changes
useEffect(() => {
checkConnection();
}, [provider]);
const checkConnection = useCallback(async () => {
try {
setIsLoading(true);
@ -27,23 +22,52 @@ export function useOAuth(provider: string) {
}
}, [provider]);
// Check connection status on mount and when provider changes
useEffect(() => {
checkConnection();
}, [provider, checkConnection]);
// Listen for OAuth completion events
useEffect(() => {
const cleanup = window.ipc.on('oauth:didConnect', (event) => {
if (event.provider !== provider) {
return; // Ignore events for other providers
}
setIsConnected(event.success);
setIsConnecting(false);
setIsLoading(false);
if (event.success) {
toast(`Successfully connected to ${provider}`, 'success');
// Refresh connection status to ensure consistency
checkConnection();
} else {
toast(event.error || `Failed to connect to ${provider}`, 'error');
}
});
return cleanup;
}, [provider, checkConnection]);
const connect = useCallback(async () => {
try {
setIsConnecting(true);
const result = await window.ipc.invoke('oauth:connect', { provider });
if (result.success) {
toast(`Successfully connected to ${provider}`, 'success');
await checkConnection();
// OAuth flow started - keep isConnecting state, wait for event
// Event listener will handle the actual completion
} else {
// Immediate failure (e.g., couldn't start flow)
toast(result.error || `Failed to connect to ${provider}`, 'error');
setIsConnecting(false);
}
} catch (error) {
console.error('Failed to connect:', error);
toast(`Failed to connect to ${provider}`, 'error');
} finally {
setIsConnecting(false);
}
}, [provider, checkConnection]);
}, [provider]);
const disconnect = useCallback(async () => {
try {