mirror of
https://github.com/nimbold/Firelink.git
synced 2026-08-28 11:37:17 +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
|
// frontend. The field is kept on legacy persisted JSON only; serde ignores
|
||||||
// unknown fields when decoding, so existing installs migrate cleanly.
|
// unknown fields when decoding, so existing installs migrate cleanly.
|
||||||
pub auto_check_updates: bool,
|
pub auto_check_updates: bool,
|
||||||
|
#[serde(default)]
|
||||||
|
pub keychain_access_granted: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Debug, Serialize, TS)]
|
#[derive(Clone, Debug, Serialize, TS)]
|
||||||
|
|||||||
@@ -289,6 +289,7 @@ fn default_settings() -> PersistedSettings {
|
|||||||
media_cookie_source: MediaCookieSource::None,
|
media_cookie_source: MediaCookieSource::None,
|
||||||
site_logins: Vec::new(),
|
site_logins: Vec::new(),
|
||||||
auto_check_updates: true,
|
auto_check_updates: true,
|
||||||
|
keychain_access_granted: false,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -8,4 +8,4 @@ import type { SettingsTab } from "./SettingsTab";
|
|||||||
import type { SiteLogin } from "./SiteLogin";
|
import type { SiteLogin } from "./SiteLogin";
|
||||||
import type { Theme } from "./Theme";
|
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 {
|
try {
|
||||||
const result = await invoke('grant_keychain_access');
|
const result = await invoke('grant_keychain_access');
|
||||||
if (result.persistent) {
|
if (result.persistent) {
|
||||||
|
useSettingsStore.setState({ keychainAccessGranted: true });
|
||||||
await useSettingsStore.getState().hydratePairingToken();
|
await useSettingsStore.getState().hydratePairingToken();
|
||||||
setShowKeychainModal(false);
|
setShowKeychainModal(false);
|
||||||
} else {
|
} else {
|
||||||
|
|||||||
@@ -8,7 +8,7 @@ import {
|
|||||||
} from '../store/useSettingsStore';
|
} from '../store/useSettingsStore';
|
||||||
import {
|
import {
|
||||||
Download, Palette, Globe, Folder, Key,
|
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';
|
} from 'lucide-react';
|
||||||
import { open } from '@tauri-apps/plugin-dialog';
|
import { open } from '@tauri-apps/plugin-dialog';
|
||||||
import { getVersion } from '@tauri-apps/api/app';
|
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>
|
||||||
</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">
|
<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" />
|
<ShieldAlert className="w-5 h-5 text-orange-500 flex-shrink-0 mt-0.5" />
|
||||||
<div className="flex-1">
|
<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>
|
</p>
|
||||||
<button
|
<button
|
||||||
onClick={() => settings.setShowKeychainModal(true)}
|
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
|
Grant Keychain Access
|
||||||
</button>
|
</button>
|
||||||
|
|||||||
@@ -106,6 +106,7 @@ export interface SettingsState {
|
|||||||
siteLogins: SiteLogin[];
|
siteLogins: SiteLogin[];
|
||||||
extensionPairingToken: string;
|
extensionPairingToken: string;
|
||||||
isPairingTokenPersistent: boolean;
|
isPairingTokenPersistent: boolean;
|
||||||
|
keychainAccessGranted: boolean;
|
||||||
autoCheckUpdates: boolean;
|
autoCheckUpdates: boolean;
|
||||||
showKeychainModal: boolean;
|
showKeychainModal: boolean;
|
||||||
|
|
||||||
@@ -178,7 +179,7 @@ const generateSecureToken = () => {
|
|||||||
|
|
||||||
export const useSettingsStore = create<SettingsState>()(
|
export const useSettingsStore = create<SettingsState>()(
|
||||||
persist(
|
persist(
|
||||||
(set) => ({
|
(set, get) => ({
|
||||||
theme: 'system',
|
theme: 'system',
|
||||||
baseDownloadFolder: '~/Downloads',
|
baseDownloadFolder: '~/Downloads',
|
||||||
categorySubfolders: { ...DEFAULT_CATEGORY_SUBFOLDERS },
|
categorySubfolders: { ...DEFAULT_CATEGORY_SUBFOLDERS },
|
||||||
@@ -224,6 +225,7 @@ export const useSettingsStore = create<SettingsState>()(
|
|||||||
siteLogins: [],
|
siteLogins: [],
|
||||||
extensionPairingToken: '',
|
extensionPairingToken: '',
|
||||||
isPairingTokenPersistent: true,
|
isPairingTokenPersistent: true,
|
||||||
|
keychainAccessGranted: false,
|
||||||
autoCheckUpdates: true,
|
autoCheckUpdates: true,
|
||||||
showKeychainModal: false,
|
showKeychainModal: false,
|
||||||
|
|
||||||
@@ -314,7 +316,8 @@ export const useSettingsStore = create<SettingsState>()(
|
|||||||
set({ extensionPairingToken: token });
|
set({ extensionPairingToken: token });
|
||||||
},
|
},
|
||||||
hydratePairingToken: async () => {
|
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({
|
set({
|
||||||
extensionPairingToken: result.token,
|
extensionPairingToken: result.token,
|
||||||
isPairingTokenPersistent: result.persistent
|
isPairingTokenPersistent: result.persistent
|
||||||
@@ -324,8 +327,8 @@ export const useSettingsStore = create<SettingsState>()(
|
|||||||
}
|
}
|
||||||
return result.tokenChanged;
|
return result.tokenChanged;
|
||||||
},
|
},
|
||||||
setAutoCheckUpdates: (autoCheckUpdates) => set({ autoCheckUpdates }),
|
setAutoCheckUpdates: (autoCheckUpdates: boolean) => set({ autoCheckUpdates }),
|
||||||
setShowKeychainModal: (show) => set({ showKeychainModal: show }),
|
setShowKeychainModal: (show: boolean) => set({ showKeychainModal: show }),
|
||||||
}),
|
}),
|
||||||
{
|
{
|
||||||
name: 'firelink-settings',
|
name: 'firelink-settings',
|
||||||
@@ -393,6 +396,7 @@ export const useSettingsStore = create<SettingsState>()(
|
|||||||
preventsSleepWhileDownloading: state.preventsSleepWhileDownloading,
|
preventsSleepWhileDownloading: state.preventsSleepWhileDownloading,
|
||||||
mediaCookieSource: state.mediaCookieSource,
|
mediaCookieSource: state.mediaCookieSource,
|
||||||
siteLogins: state.siteLogins,
|
siteLogins: state.siteLogins,
|
||||||
|
keychainAccessGranted: state.keychainAccessGranted,
|
||||||
autoCheckUpdates: state.autoCheckUpdates
|
autoCheckUpdates: state.autoCheckUpdates
|
||||||
}),
|
}),
|
||||||
merge: (persistedState: unknown, currentState) => {
|
merge: (persistedState: unknown, currentState) => {
|
||||||
|
|||||||
Reference in New Issue
Block a user