diff --git a/src/renderer/components/SetupWizard.tsx b/src/renderer/components/SetupWizard.tsx index fe82bfb..29f1244 100644 --- a/src/renderer/components/SetupWizard.tsx +++ b/src/renderer/components/SetupWizard.tsx @@ -13,7 +13,15 @@ interface SetupWizardProps { initialStep?: "imap"; } -type Step = "loading" | "credentials" | "apikey" | "oauth" | "extensions" | "analytics" | "imap"; +type Step = + | "loading" + | "credentials" + | "apikey" + | "oauth" + | "extensions" + | "permissions" + | "analytics" + | "imap"; interface ExtensionAuthInfo { extensionId: string; @@ -44,6 +52,13 @@ export function SetupWizard({ onComplete, initialStep }: SetupWizardProps) { // Analytics opt-in (default ON — session replay is bundled under analytics) const [analyticsEnabled, setAnalyticsEnabled] = useState(true); + // Notification permission state. "unknown" before the user has clicked + // Grant; "granted" / "denied" after macOS has decided. Used by the + // permissions wizard step to render the right copy + CTA. + const [notificationPermission, setNotificationPermission] = useState< + "unknown" | "granted" | "denied" | "requesting" + >("unknown"); + // Check what's already configured and skip to the right step. // If `initialStep` was passed (e.g. "imap" from the empty-state CTA), // we still compute the visible-step indicator but don't override the @@ -73,6 +88,7 @@ export function SetupWizard({ onComplete, initialStep }: SetupWizardProps) { if (!hasAnthropicKey) flow.push("apikey"); if (!hasTokens) flow.push("oauth"); flow.push("extensions"); + flow.push("permissions"); flow.push("analytics"); setVisibleSteps(flow); @@ -86,7 +102,14 @@ export function SetupWizard({ onComplete, initialStep }: SetupWizardProps) { enterExtensionsStep(); } } else { - setVisibleSteps(["credentials", "apikey", "oauth", "extensions", "analytics"]); + setVisibleSteps([ + "credentials", + "apikey", + "oauth", + "extensions", + "permissions", + "analytics", + ]); setStep("credentials"); } }) @@ -221,19 +244,20 @@ export function SetupWizard({ onComplete, initialStep }: SetupWizardProps) { setStep("extensions"); setIsLoading(false); } else { - // No extensions need auth (or IPC failed) — skip extensions step entirely + // No extensions need auth (or IPC failed) — skip extensions step + // entirely and move to the permissions step (next in the flow). if (!result.success) { console.error("[SetupWizard] getPendingAuths failed:", result.error); } setVisibleSteps((prev) => prev.filter((s) => s !== "extensions")); setIsLoading(false); - setStep("analytics"); + setStep("permissions"); } } catch (err) { console.error("[SetupWizard] getPendingAuths failed:", err); setVisibleSteps((prev) => prev.filter((s) => s !== "extensions")); setIsLoading(false); - setStep("analytics"); + setStep("permissions"); } }, []); @@ -569,7 +593,7 @@ export function SetupWizard({ onComplete, initialStep }: SetupWizardProps) { {error &&
{error}
} + )} + {notificationPermission === "denied" && ( + + )} + + + + + )} + {step === "analytics" && ( <>