31ace174e3
Validate the persisted game directory before sending it to the backend or showing library content for it. When the saved path no longer exists, the launcher keeps the top bar visible but shows the folder picker empty state and labels the Game Folder button as an unset folder. This keeps stale local data from being presented as the active library when an old path is deleted or disconnected. Test Plan: - git diff --check - just frontend-test - just build
91 lines
2.9 KiB
TypeScript
91 lines
2.9 KiB
TypeScript
import { useCallback, useEffect, useState } from 'react';
|
|
import { invoke } from '@tauri-apps/api/core';
|
|
import { load } from '@tauri-apps/plugin-store';
|
|
|
|
import { GAME_DIR_KEY, SETTINGS_FILE, SETTINGS_FILE_OPTIONS } from '../lib/store';
|
|
|
|
/**
|
|
* Owns the user's selected game directory. Hydrates from the persistent store
|
|
* on mount, writes back on every change, and pushes the value to the Tauri
|
|
* backend so it can scan/rescan.
|
|
*/
|
|
export const useGameDirectory = () => {
|
|
const [gameDir, setGameDir] = useState('');
|
|
const [gameDirExists, setGameDirExists] = useState(false);
|
|
|
|
useEffect(() => {
|
|
let cancelled = false;
|
|
const hydrate = async () => {
|
|
try {
|
|
const store = await load(SETTINGS_FILE, SETTINGS_FILE_OPTIONS);
|
|
const saved = await store.get<string>(GAME_DIR_KEY);
|
|
if (saved && !cancelled) setGameDir(saved);
|
|
} catch (err) {
|
|
console.error('Failed to load game directory:', err);
|
|
}
|
|
};
|
|
void hydrate();
|
|
return () => {
|
|
cancelled = true;
|
|
};
|
|
}, []);
|
|
|
|
useEffect(() => {
|
|
if (!gameDir.trim()) {
|
|
setGameDirExists(false);
|
|
return;
|
|
}
|
|
let cancelled = false;
|
|
const sync = async () => {
|
|
try {
|
|
const store = await load(SETTINGS_FILE, SETTINGS_FILE_OPTIONS);
|
|
await store.set(GAME_DIR_KEY, gameDir);
|
|
} catch (err) {
|
|
console.error('Failed to persist game directory:', err);
|
|
}
|
|
|
|
let exists = false;
|
|
try {
|
|
exists = await invoke<boolean>('game_directory_exists', { path: gameDir });
|
|
} catch (err) {
|
|
console.error('Failed to validate game directory:', err);
|
|
}
|
|
if (cancelled) return;
|
|
setGameDirExists(exists);
|
|
if (!exists) return;
|
|
|
|
invoke('update_game_directory', { path: gameDir }).catch(err =>
|
|
console.error('Failed to push game directory to backend:', err),
|
|
);
|
|
};
|
|
void sync();
|
|
return () => {
|
|
cancelled = true;
|
|
};
|
|
}, [gameDir]);
|
|
|
|
const rescan = useCallback(() => {
|
|
if (!gameDir.trim()) {
|
|
setGameDirExists(false);
|
|
return;
|
|
}
|
|
const sync = async () => {
|
|
let exists = false;
|
|
try {
|
|
exists = await invoke<boolean>('game_directory_exists', { path: gameDir });
|
|
} catch (err) {
|
|
console.error('Failed to validate game directory:', err);
|
|
}
|
|
setGameDirExists(exists);
|
|
if (!exists) return;
|
|
|
|
invoke('update_game_directory', { path: gameDir }).catch(err =>
|
|
console.error('Failed to rescan game directory:', err),
|
|
);
|
|
};
|
|
void sync();
|
|
}, [gameDir]);
|
|
|
|
return { gameDir, gameDirExists, setGameDir, rescan };
|
|
};
|