From 09f103ea049008373d6ff62c9185b079b746333c Mon Sep 17 00:00:00 2001 From: NimBold Date: Thu, 25 Jun 2026 02:38:40 +0330 Subject: [PATCH] fix: stop keychain modal on every launch and fix grant-access loop MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Revert hydrate_extension_pairing_token to consult the DB's keychainAccessGranted flag instead of unconditionally skipping the keychain. When the flag is true the backend tries the keychain; if macOS trusts the current binary (no code-signature change) the read succeeds silently and the modal is suppressed. When the flag is false the keychain is skipped and the modal is shown exactly once. - Fix the Grant Access button in the KeychainPermissionModal by writing the result (token, persistent flag) directly into the store instead of calling hydratePairingToken() again. The re-hydration raced with Zustand's async persist middleware, which could read keychainAccessGranted=false from the DB and flip persistent back to false, re‑showing the modal in an endless loop. - The scheduler defaults (enabled: false) were already correct in both the Rust default_settings() and the frontend Zustand store; no code change needed. --- src-tauri/src/lib.rs | 7 +------ src/components/KeychainPermissionModal.tsx | 11 +++++++++-- 2 files changed, 10 insertions(+), 8 deletions(-) diff --git a/src-tauri/src/lib.rs b/src-tauri/src/lib.rs index abc516d..341d202 100644 --- a/src-tauri/src/lib.rs +++ b/src-tauri/src/lib.rs @@ -3435,12 +3435,7 @@ fn hydrate_extension_pairing_token( app_state: tauri::State<'_, AppState>, ) -> Result { let mut connection = database.lock()?; - // Always skip the keychain during automatic hydration so the operating - // system never presents a credential-access prompt before the app's own - // modal is visible. The frontend will display the KeychainPermissionModal - // and only call grant_keychain_access — which touches the keychain — after - // the user explicitly clicks "Grant Access". - let skip_keychain = true; + let skip_keychain = !crate::db::is_keychain_access_granted(&connection).unwrap_or(false); match crate::db::hydrate_pairing_token(&mut connection, skip_keychain) { Ok((token, token_changed)) => { if let Ok(mut pairing_token) = app_state.extension_pairing_token.write() { diff --git a/src/components/KeychainPermissionModal.tsx b/src/components/KeychainPermissionModal.tsx index 6faebc8..a22822a 100644 --- a/src/components/KeychainPermissionModal.tsx +++ b/src/components/KeychainPermissionModal.tsx @@ -19,8 +19,15 @@ export const KeychainPermissionModal: React.FC = () => { try { const result = await invoke('grant_keychain_access'); if (result.persistent) { - useSettingsStore.setState({ keychainAccessGranted: true }); - await useSettingsStore.getState().hydratePairingToken(); + // Set all keychain-related state directly from the grant result + // instead of calling hydratePairingToken() again, which would + // re-read the DB before Zustand's persist middleware has written + // keychainAccessGranted and could flip persistent back to false. + useSettingsStore.setState({ + keychainAccessGranted: true, + extensionPairingToken: result.token, + isPairingTokenPersistent: true + }); setShowKeychainModal(false); } else { setError(result.error || 'Failed to grant keychain access.');