mirror of
https://github.com/nimbold/Firelink.git
synced 2026-07-26 12:08:27 +00:00
fix(keychain): persist access state, update granted UI, and resolve build errors
This commit is contained in:
@@ -278,6 +278,8 @@ pub struct PersistedSettings {
|
||||
// frontend. The field is kept on legacy persisted JSON only; serde ignores
|
||||
// unknown fields when decoding, so existing installs migrate cleanly.
|
||||
pub auto_check_updates: bool,
|
||||
#[serde(default)]
|
||||
pub keychain_access_granted: bool,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Serialize, TS)]
|
||||
|
||||
@@ -289,6 +289,7 @@ fn default_settings() -> PersistedSettings {
|
||||
media_cookie_source: MediaCookieSource::None,
|
||||
site_logins: Vec::new(),
|
||||
auto_check_updates: true,
|
||||
keychain_access_granted: false,
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -8,4 +8,4 @@ import type { SettingsTab } from "./SettingsTab";
|
||||
import type { SiteLogin } from "./SiteLogin";
|
||||
import type { Theme } from "./Theme";
|
||||
|
||||
export type PersistedSettings = { theme: Theme, baseDownloadFolder: string, categorySubfolders: { [key in string]: string }, categoryDirectoryOverrides: { [key in string]: string }, approvedDownloadRoots: Array<string>, maxConcurrentDownloads: number, globalSpeedLimit: string, isSidebarVisible: boolean, activeSettingsTab: SettingsTab, scheduler: SchedulerSettings, schedulerRunning: boolean, schedulerActiveDownloadIds: Array<string>, schedulerLastStartKey: string, schedulerLastStopKey: string, lastCustomSpeedLimitKiB: number, perServerConnections: number, maxAutomaticRetries: number, showNotifications: boolean, playCompletionSound: boolean, appFontSize: AppFontSize, listRowDensity: ListRowDensity, showDockBadge: boolean, showMenuBarIcon: boolean, proxyMode: ProxyMode, proxyHost: string, proxyPort: number, customUserAgent: string, askWhereToSaveEachFile: boolean, preventsSleepWhileDownloading: boolean, mediaCookieSource: MediaCookieSource, siteLogins: Array<SiteLogin>, autoCheckUpdates: boolean, };
|
||||
export type PersistedSettings = { theme: Theme, baseDownloadFolder: string, categorySubfolders: { [key in string]: string }, categoryDirectoryOverrides: { [key in string]: string }, approvedDownloadRoots: Array<string>, maxConcurrentDownloads: number, globalSpeedLimit: string, isSidebarVisible: boolean, activeSettingsTab: SettingsTab, scheduler: SchedulerSettings, schedulerRunning: boolean, schedulerActiveDownloadIds: Array<string>, schedulerLastStartKey: string, schedulerLastStopKey: string, lastCustomSpeedLimitKiB: number, perServerConnections: number, maxAutomaticRetries: number, showNotifications: boolean, playCompletionSound: boolean, appFontSize: AppFontSize, listRowDensity: ListRowDensity, showDockBadge: boolean, showMenuBarIcon: boolean, proxyMode: ProxyMode, proxyHost: string, proxyPort: number, customUserAgent: string, askWhereToSaveEachFile: boolean, preventsSleepWhileDownloading: boolean, mediaCookieSource: MediaCookieSource, siteLogins: Array<SiteLogin>, autoCheckUpdates: boolean, keychainAccessGranted: boolean, };
|
||||
|
||||
@@ -19,6 +19,7 @@ export const KeychainPermissionModal: React.FC = () => {
|
||||
try {
|
||||
const result = await invoke('grant_keychain_access');
|
||||
if (result.persistent) {
|
||||
useSettingsStore.setState({ keychainAccessGranted: true });
|
||||
await useSettingsStore.getState().hydratePairingToken();
|
||||
setShowKeychainModal(false);
|
||||
} else {
|
||||
|
||||
@@ -8,7 +8,7 @@ import {
|
||||
} from '../store/useSettingsStore';
|
||||
import {
|
||||
Download, Palette, Globe, Folder, Key,
|
||||
Moon, Terminal, Puzzle, Info, Plus, Trash2, Copy, RefreshCw, Code, ShieldAlert
|
||||
Moon, Terminal, Puzzle, Info, Plus, Trash2, Copy, RefreshCw, Code, ShieldAlert, Check
|
||||
} from 'lucide-react';
|
||||
import { open } from '@tauri-apps/plugin-dialog';
|
||||
import { getVersion } from '@tauri-apps/api/app';
|
||||
@@ -1055,7 +1055,19 @@ className="app-button px-3 py-1.5 text-[12px] flex items-center gap-1.5 disabled
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{!settings.isPairingTokenPersistent && (
|
||||
{settings.isPairingTokenPersistent ? (
|
||||
<div className="bg-green-500/10 border border-green-500/20 rounded-lg p-3 flex items-center gap-3">
|
||||
<div className="p-1.5 bg-green-500/20 rounded-full text-green-500 flex-shrink-0">
|
||||
<Check size={16} strokeWidth={2.5} />
|
||||
</div>
|
||||
<div className="flex-1">
|
||||
<h4 className="text-sm font-semibold text-green-500 m-0">Keychain Access Granted</h4>
|
||||
<p className="text-xs text-text-secondary m-0 mt-0.5">
|
||||
Your pairing token is securely saved and will persist across restarts.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
) : (
|
||||
<div className="bg-orange-500/10 border border-orange-500/20 rounded-lg p-4 flex items-start gap-3">
|
||||
<ShieldAlert className="w-5 h-5 text-orange-500 flex-shrink-0 mt-0.5" />
|
||||
<div className="flex-1">
|
||||
@@ -1066,7 +1078,7 @@ className="app-button px-3 py-1.5 text-[12px] flex items-center gap-1.5 disabled
|
||||
</p>
|
||||
<button
|
||||
onClick={() => settings.setShowKeychainModal(true)}
|
||||
className="app-button primary text-xs py-1"
|
||||
className="px-4 py-1.5 rounded-md text-xs font-medium transition-colors bg-accent text-white hover:bg-accent/90 shadow-sm"
|
||||
>
|
||||
Grant Keychain Access
|
||||
</button>
|
||||
|
||||
@@ -106,6 +106,7 @@ export interface SettingsState {
|
||||
siteLogins: SiteLogin[];
|
||||
extensionPairingToken: string;
|
||||
isPairingTokenPersistent: boolean;
|
||||
keychainAccessGranted: boolean;
|
||||
autoCheckUpdates: boolean;
|
||||
showKeychainModal: boolean;
|
||||
|
||||
@@ -178,7 +179,7 @@ const generateSecureToken = () => {
|
||||
|
||||
export const useSettingsStore = create<SettingsState>()(
|
||||
persist(
|
||||
(set) => ({
|
||||
(set, get) => ({
|
||||
theme: 'system',
|
||||
baseDownloadFolder: '~/Downloads',
|
||||
categorySubfolders: { ...DEFAULT_CATEGORY_SUBFOLDERS },
|
||||
@@ -224,6 +225,7 @@ export const useSettingsStore = create<SettingsState>()(
|
||||
siteLogins: [],
|
||||
extensionPairingToken: '',
|
||||
isPairingTokenPersistent: true,
|
||||
keychainAccessGranted: false,
|
||||
autoCheckUpdates: true,
|
||||
showKeychainModal: false,
|
||||
|
||||
@@ -314,7 +316,8 @@ export const useSettingsStore = create<SettingsState>()(
|
||||
set({ extensionPairingToken: token });
|
||||
},
|
||||
hydratePairingToken: async () => {
|
||||
const result = await invoke('hydrate_extension_pairing_token');
|
||||
const granted = get().keychainAccessGranted;
|
||||
const result = await invoke(granted ? 'grant_keychain_access' : 'hydrate_extension_pairing_token');
|
||||
set({
|
||||
extensionPairingToken: result.token,
|
||||
isPairingTokenPersistent: result.persistent
|
||||
@@ -324,8 +327,8 @@ export const useSettingsStore = create<SettingsState>()(
|
||||
}
|
||||
return result.tokenChanged;
|
||||
},
|
||||
setAutoCheckUpdates: (autoCheckUpdates) => set({ autoCheckUpdates }),
|
||||
setShowKeychainModal: (show) => set({ showKeychainModal: show }),
|
||||
setAutoCheckUpdates: (autoCheckUpdates: boolean) => set({ autoCheckUpdates }),
|
||||
setShowKeychainModal: (show: boolean) => set({ showKeychainModal: show }),
|
||||
}),
|
||||
{
|
||||
name: 'firelink-settings',
|
||||
@@ -393,6 +396,7 @@ export const useSettingsStore = create<SettingsState>()(
|
||||
preventsSleepWhileDownloading: state.preventsSleepWhileDownloading,
|
||||
mediaCookieSource: state.mediaCookieSource,
|
||||
siteLogins: state.siteLogins,
|
||||
keychainAccessGranted: state.keychainAccessGranted,
|
||||
autoCheckUpdates: state.autoCheckUpdates
|
||||
}),
|
||||
merge: (persistedState: unknown, currentState) => {
|
||||
|
||||
Reference in New Issue
Block a user