fix(settings): harden settings and diagnostic controls

This commit is contained in:
NimBold
2026-07-17 00:39:26 +03:30
parent 5144ecd39e
commit 6ef911919d
10 changed files with 226 additions and 18 deletions
+16 -2
View File
@@ -1,6 +1,7 @@
import { useEffect, useRef, useState } from 'react';
import { invokeCommand as invoke } from '../ipc';
import { save } from '@tauri-apps/plugin-dialog';
import { homeDir } from '@tauri-apps/api/path';
import { attachLogger, setLogPaused, initLogger, setLogStreamActive } from '../utils/logger';
import { FileDown, Trash2, Terminal, Filter, Play, Pause, Info, Copy } from 'lucide-react';
import { WindowDragRegion } from './WindowDragRegion';
@@ -24,6 +25,7 @@ export default function LogsView() {
const [levelFilter, setLevelFilter] = useState<LogEntry['level'] | 'All'>('All');
const [contextMenu, setContextMenu] = useState<{ x: number; y: number; text: string } | null>(null);
const [pageVisible, setPageVisible] = useState(() => document.visibilityState !== 'hidden');
const homeDirectoryRef = useRef('');
const scrollRef = useRef<HTMLDivElement>(null);
const liveBatchRef = useRef<LogEntry[]>([]);
const liveFrameRef = useRef<number | null>(null);
@@ -33,6 +35,18 @@ export default function LogsView() {
const [isClearing, setIsClearing] = useState(false);
const [isToggling, setIsToggling] = useState(false);
useEffect(() => {
let active = true;
void homeDir()
.then(directory => {
if (active) homeDirectoryRef.current = directory;
})
.catch(() => undefined);
return () => {
active = false;
};
}, []);
useEffect(() => {
const handleVisibilityChange = () => setPageVisible(document.visibilityState !== 'hidden');
document.addEventListener('visibilitychange', handleVisibilityChange);
@@ -80,7 +94,7 @@ export default function LogsView() {
if (logsEnabled) {
unlistenPromise = attachLogger((log) => {
if (!active) return;
scheduleLiveEntry(liveLogEntry(log.level, log.message));
scheduleLiveEntry(liveLogEntry(log.level, log.message, new Date(), homeDirectoryRef.current));
});
await unlistenPromise;
if (!active) return;
@@ -93,7 +107,7 @@ export default function LogsView() {
const lines = await invoke('read_logs', { limit: MAX_LOG_LINES });
if (!active) return;
const snapshot = lines.map(persistedLogEntry);
const snapshot = lines.map(line => persistedLogEntry(line, homeDirectoryRef.current));
initialized = true;
if (initGeneration !== clearGenerationRef.current) {
pendingLiveEntries = [];
+39 -4
View File
@@ -29,6 +29,7 @@ import {
} from '../utils/downloadLocations';
import { usePlatformInfo } from '../utils/platform';
import { isTrustedFirelinkReleaseUrl } from '../utils/releaseUrls';
import { normalizeCustomProxy } from '../store/useDownloadStore';
const settingsTabs: { type: SettingsTab; label: string; icon: typeof Download }[] = [
{ type: 'downloads', label: 'Downloads', icon: Download },
@@ -76,6 +77,8 @@ type ManualUpdateStatus =
| { type: 'update-available'; version: string; releaseUrl: string }
| { type: 'error'; message: string };
type SystemProxyStatus = 'idle' | 'checking' | 'detected' | 'none' | 'error';
const engineStatusCache = new Map<string, EngineStatusItem>();
const engineStatusInFlight = new Map<string, Promise<EngineStatusItem>>();
@@ -276,8 +279,9 @@ const [engineStatus, setEngineStatus] = useState<EngineStatusItem[] | null>(null
const [expandedEngine, setExpandedEngine] = useState<string | null>(null);
const [isRecheckingEngines, setIsRecheckingEngines] = useState(false);
const engineRunId = useRef(0);
const [appVersion, setAppVersion] = useState('Unknown');
const [extensionServerPort, setExtensionServerPort] = useState<number | null>(null);
const [appVersion, setAppVersion] = useState('Unknown');
const [extensionServerPort, setExtensionServerPort] = useState<number | null>(null);
const [systemProxyStatus, setSystemProxyStatus] = useState<SystemProxyStatus>('idle');
// Local state for adding site login
const [loginPattern, setLoginPattern] = useState('');
@@ -320,6 +324,27 @@ useEffect(() => {
};
}, [settings.activeView, activeTab]);
useEffect(() => {
if (settings.activeView !== 'settings' || activeTab !== 'network' || settings.proxyMode !== 'system') {
setSystemProxyStatus('idle');
return;
}
let active = true;
setSystemProxyStatus('checking');
invoke('get_system_proxy')
.then(proxy => {
if (active) setSystemProxyStatus(typeof proxy === 'string' && proxy.trim() ? 'detected' : 'none');
})
.catch(() => {
if (active) setSystemProxyStatus('error');
});
return () => {
active = false;
};
}, [settings.activeView, settings.proxyMode, activeTab]);
const runEngineChecks = useCallback((force = false) => {
const runId = ++engineRunId.current;
const cached = engineChecks
@@ -880,10 +905,20 @@ runEngineChecks(false);
<p className="settings-group-footer">
{settings.proxyMode === 'none' && 'Downloads ignore configured proxies.'}
{settings.proxyMode === 'system' && `Downloads use the detected ${platform.os === 'macos' ? 'macOS' : platform.os === 'windows' ? 'Windows' : 'desktop'} system proxy. Normal file downloads require an HTTP or HTTPS proxy endpoint; media downloads can use SOCKS.`}
{settings.proxyMode === 'custom' && (settings.proxyHost
{settings.proxyMode === 'custom' && (normalizeCustomProxy(settings.proxyHost, settings.proxyPort)
? 'Downloads use the configured HTTP proxy endpoint for metadata and download engines.'
: 'Enter a proxy host and port to enable the custom proxy.')}
: settings.proxyHost
? 'Enter a valid HTTP proxy host and port to enable the custom proxy.'
: 'Enter a proxy host and port to enable the custom proxy.')}
</p>
{settings.proxyMode === 'system' && (
<p className="settings-group-footer" role="status">
{systemProxyStatus === 'checking' && 'Checking system proxy configuration…'}
{systemProxyStatus === 'detected' && 'A system proxy was detected. Normal file downloads require an HTTP or HTTPS endpoint; media downloads can use SOCKS.'}
{systemProxyStatus === 'none' && 'No usable system proxy was detected. Downloads will use no proxy.'}
{systemProxyStatus === 'error' && 'System proxy configuration could not be read. Downloads will use no proxy until it is available.'}
</p>
)}
</div>
<h2 className="settings-section-title">Identity</h2>