Enhance OAuth event handling by notifying the renderer on provider disconnection. Update ConnectorsPopover to listen for OAuth state changes, and refine WelcomeStep component by removing feature highlights and adjusting layout for improved user experience.

This commit is contained in:
tusharmagar 2026-03-16 22:33:30 +05:30
parent ebc2b7b77d
commit e8457b1f54
3 changed files with 14 additions and 49 deletions

View file

@ -286,6 +286,8 @@ export async function disconnectProvider(provider: string): Promise<{ success: b
try { try {
const oauthRepo = getOAuthRepo(); const oauthRepo = getOAuthRepo();
await oauthRepo.delete(provider); await oauthRepo.delete(provider);
// Notify renderer so sidebar, voice, and billing re-check state
emitOAuthEvent({ provider, success: false });
return { success: true }; return { success: true };
} catch (error) { } catch (error) {
console.error('OAuth disconnect failed:', error); console.error('OAuth disconnect failed:', error);

View file

@ -422,10 +422,10 @@ export function ConnectorsPopover({ children, tooltip, open: openProp, onOpenCha
} }
}, [open, providers, refreshAllStatuses]) }, [open, providers, refreshAllStatuses])
// Listen for OAuth completion events // Listen for OAuth state change events (connect + disconnect)
useEffect(() => { useEffect(() => {
const cleanup = window.ipc.on('oauth:didConnect', async (event) => { const cleanup = window.ipc.on('oauth:didConnect', async (event) => {
const { provider, success, error } = event const { provider, success } = event
setProviderStates(prev => ({ setProviderStates(prev => ({
...prev, ...prev,
@ -464,9 +464,9 @@ export function ConnectorsPopover({ children, tooltip, open: openProp, onOpenCha
// Refresh status to ensure consistency // Refresh status to ensure consistency
refreshAllStatuses() refreshAllStatuses()
} else {
toast.error(error || `Failed to connect to ${provider}`)
} }
// Note: error toasts for failed connections are handled by startConnect/handleConnect.
// Disconnect events (success: false) are handled by handleDisconnect which shows its own toast.
}) })
return cleanup return cleanup

View file

@ -1,4 +1,4 @@
import { Loader2, CheckCircle2, Brain, Plug, ShieldCheck } from "lucide-react" import { Loader2, CheckCircle2 } from "lucide-react"
import { motion } from "motion/react" import { motion } from "motion/react"
import { Button } from "@/components/ui/button" import { Button } from "@/components/ui/button"
import type { OnboardingState } from "../use-onboarding-state" import type { OnboardingState } from "../use-onboarding-state"
@ -7,24 +7,6 @@ interface WelcomeStepProps {
state: OnboardingState state: OnboardingState
} }
const features = [
{
icon: Brain,
label: "Memory",
desc: "Builds a knowledge graph from your work",
},
{
icon: Plug,
label: "Connected",
desc: "Syncs email, calendar, and meetings",
},
{
icon: ShieldCheck,
label: "Private",
desc: "Your data stays local on your machine",
},
]
export function WelcomeStep({ state }: WelcomeStepProps) { export function WelcomeStep({ state }: WelcomeStepProps) {
const rowboatState = state.providerStates['rowboat'] || { isConnected: false, isLoading: false, isConnecting: false } const rowboatState = state.providerStates['rowboat'] || { isConnected: false, isLoading: false, isConnecting: false }
@ -35,10 +17,10 @@ export function WelcomeStep({ state }: WelcomeStepProps) {
initial={{ opacity: 0, scale: 0.8 }} initial={{ opacity: 0, scale: 0.8 }}
animate={{ opacity: 1, scale: 1 }} animate={{ opacity: 1, scale: 1 }}
transition={{ duration: 0.5, ease: [0.16, 1, 0.3, 1] }} transition={{ duration: 0.5, ease: [0.16, 1, 0.3, 1] }}
className="relative mb-6" className="relative mb-8"
> >
<div className="absolute inset-0 size-14 rounded-2xl bg-primary/10 blur-xl scale-[2]" /> <div className="absolute inset-0 size-16 rounded-2xl bg-primary/10 blur-xl scale-[2.5]" />
<img src="/logo-only.png" alt="Rowboat" className="relative size-14" /> <img src="/logo-only.png" alt="Rowboat" className="relative size-16" />
</motion.div> </motion.div>
{/* Tagline badge */} {/* Tagline badge */}
@ -65,35 +47,16 @@ export function WelcomeStep({ state }: WelcomeStepProps) {
initial={{ opacity: 0 }} initial={{ opacity: 0 }}
animate={{ opacity: 1 }} animate={{ opacity: 1 }}
transition={{ delay: 0.3 }} transition={{ delay: 0.3 }}
className="text-base text-muted-foreground leading-relaxed max-w-sm mb-8" className="text-base text-muted-foreground leading-relaxed max-w-sm mb-10"
> >
Connect your Rowboat account for instant access to all models through our gateway no API keys needed. Rowboat connects to your work, builds a knowledge graph, and uses that context to help you get things done. Private and on your machine.
</motion.p> </motion.p>
{/* Feature highlights */}
<div className="grid grid-cols-3 gap-3 w-full max-w-md mb-8">
{features.map((f, i) => (
<motion.div
key={f.label}
initial={{ opacity: 0, y: 10 }}
animate={{ opacity: 1, y: 0 }}
transition={{ delay: 0.35 + i * 0.08 }}
className="flex flex-col items-center gap-2 rounded-xl border bg-muted/30 p-4"
>
<div className="size-9 rounded-lg bg-primary/10 flex items-center justify-center">
<f.icon className="size-4.5 text-primary/80" />
</div>
<span className="text-xs font-semibold">{f.label}</span>
<span className="text-[11px] leading-tight text-muted-foreground">{f.desc}</span>
</motion.div>
))}
</div>
{/* Sign in / connected state */} {/* Sign in / connected state */}
<motion.div <motion.div
initial={{ opacity: 0, y: 8 }} initial={{ opacity: 0, y: 8 }}
animate={{ opacity: 1, y: 0 }} animate={{ opacity: 1, y: 0 }}
transition={{ delay: 0.6 }} transition={{ delay: 0.4 }}
className="w-full max-w-xs" className="w-full max-w-xs"
> >
{rowboatState.isConnected ? ( {rowboatState.isConnected ? (
@ -143,7 +106,7 @@ export function WelcomeStep({ state }: WelcomeStepProps) {
<motion.div <motion.div
initial={{ opacity: 0 }} initial={{ opacity: 0 }}
animate={{ opacity: 1 }} animate={{ opacity: 1 }}
transition={{ delay: 0.7 }} transition={{ delay: 0.5 }}
className="mt-8" className="mt-8"
> >
<button <button