fix(onboarding): require macOS Accessibility grant before first-run completes (#703)
On macOS the hotkey needs Accessibility, but first-run onboarding could finish
after Microphone only and drop users into the main UI with a dead hotkey
("nothing happens when I press the key").
Cause: desktop onboarding gated completion on the hotkey *capability* flag
(requiresAccessibilityPermission). That flag is null while the capability IPC
loads, so requiresAccessibility was briefly false — if Mic got granted in that
window, refresh() called onComplete() before Accessibility was ever requested.
App.tsx's startup gate, by contrast, always requires Accessibility, so the two
disagreed.
Fix: gate completion on the real permission status, mirroring App.tsx — macOS
returns granted/denied, every other platform returns notApplicable, so this is
timing-independent and cross-platform safe. Also surface the Accessibility step
whenever it is denied (macOS-only state) and drop the capability dependency from
its grant button, so the step always appears and is actionable even before the
capability finishes loading.
No change on Windows/Linux/Android (Accessibility = notApplicable there).
Co-authored-by: sim <sim@local>
This commit is contained in:
1 parent
4903f4442e
commit
f3467ae5f9
1 file changed
+9
-4
@@ -293,7 +293,12 @@ function DesktopOnboarding({
|
||||
]);
|
||||
setAccessibility(a);
|
||||
setMicrophone(m);
|
||||
const aOk = !requiresAccessibility || a === 'granted' || a === 'notApplicable';
|
||||
// Gate on the real permission status — macOS returns granted/denied, other
|
||||
// platforms return notApplicable. This must NOT depend on the hotkey
|
||||
// capability flag: it is null while it loads, so trusting it let mic-only
|
||||
// onboarding finish before we knew macOS needs Accessibility, dropping users
|
||||
// into the app with a dead hotkey. Mirrors the gate in App.tsx.
|
||||
const aOk = a === 'granted' || a === 'notApplicable';
|
||||
const mOk = m === 'granted' || m === 'notApplicable';
|
||||
if (aOk && mOk) {
|
||||
onComplete();
|
||||
@@ -362,7 +367,7 @@ function DesktopOnboarding({
|
||||
>
|
||||
<BrandHeader title={t('onboarding.welcome')} desc={t('onboarding.intro')} />
|
||||
|
||||
{requiresAccessibility && (
|
||||
{(requiresAccessibility || accessibility === 'denied') && (
|
||||
<PermissionStep
|
||||
index={1}
|
||||
title={capability?.requiresAccessibilityPermission ? t('onboarding.accessibilityTitle') : t('onboarding.hotkeyTitle')}
|
||||
@@ -380,13 +385,13 @@ function DesktopOnboarding({
|
||||
: t('onboarding.actionGrant')
|
||||
}
|
||||
onAction={onGrantAccessibility}
|
||||
disabled={busy || !capability?.requiresAccessibilityPermission || accessibility === 'granted' || accessibility === 'notApplicable'}
|
||||
disabled={busy || accessibility === 'granted' || accessibility === 'notApplicable'}
|
||||
hint={capability?.requiresAccessibilityPermission ? t('onboarding.accessibilityHint') : undefined}
|
||||
/>
|
||||
)}
|
||||
|
||||
<PermissionStep
|
||||
index={requiresAccessibility ? 2 : 1}
|
||||
index={(requiresAccessibility || accessibility === 'denied') ? 2 : 1}
|
||||
title={t('onboarding.micTitle')}
|
||||
desc={t('onboarding.micDesc')}
|
||||
status={microphone}
|
||||
|
||||
Reference in new issue
Block a user