feat: add way to select game dir
This commit is contained in:
@@ -5,7 +5,8 @@ import type {
|
||||
GameLogLine,
|
||||
DeviceCodeInfo,
|
||||
UpdateStatus,
|
||||
PackMeta
|
||||
PackMeta,
|
||||
DataDirInfo
|
||||
} from '../../shared/ipc'
|
||||
|
||||
/** Découpe une chaîne d'args JVM en tableau (espaces, vides ignorés). */
|
||||
@@ -13,7 +14,7 @@ function parseArgs(s: string): string[] {
|
||||
return s.split(/\s+/).filter(Boolean)
|
||||
}
|
||||
|
||||
type Status = 'loading' | 'logged-out' | 'logged-in' | 'working' | 'running'
|
||||
type Status = 'loading' | 'setup' | 'logged-out' | 'logged-in' | 'working' | 'running'
|
||||
|
||||
export default function App(): JSX.Element {
|
||||
const [status, setStatus] = useState<Status>('loading')
|
||||
@@ -27,23 +28,34 @@ export default function App(): JSX.Element {
|
||||
const [appVersion, setAppVersion] = useState('')
|
||||
const [update, setUpdate] = useState<UpdateStatus | null>(null)
|
||||
const [pack, setPack] = useState<PackMeta | null>(null)
|
||||
const [dataDir, setDataDir] = useState<DataDirInfo | null>(null)
|
||||
const [setupDir, setSetupDir] = useState('')
|
||||
const consoleRef = useRef<HTMLDivElement>(null)
|
||||
|
||||
// Restaure la session + réglages au démarrage.
|
||||
useEffect(() => {
|
||||
;(async () => {
|
||||
const [p, s, v] = await Promise.all([
|
||||
window.api.getProfile(),
|
||||
const [firstLaunch, dirInfo, s, v] = await Promise.all([
|
||||
window.api.isFirstLaunch(),
|
||||
window.api.getDataDir(),
|
||||
window.api.getSettings(),
|
||||
window.api.getAppVersion()
|
||||
])
|
||||
setDataDir(dirInfo)
|
||||
setMaxMemoryMb(s.maxMemoryMb)
|
||||
setJvmArgs(s.extraJvmArgs.join(' '))
|
||||
setAppVersion(v)
|
||||
|
||||
if (firstLaunch) {
|
||||
setSetupDir(dirInfo.defaultSuggestion)
|
||||
setStatus('setup')
|
||||
return
|
||||
}
|
||||
|
||||
const p = await window.api.getProfile()
|
||||
setProfile(p)
|
||||
setStatus(p ? 'logged-in' : 'logged-out')
|
||||
})()
|
||||
// Récupère le nom/version réels du pack (tolérant au offline).
|
||||
void window.api.getPackMeta().then((m) => m && setPack(m))
|
||||
}, [])
|
||||
|
||||
@@ -129,6 +141,30 @@ export default function App(): JSX.Element {
|
||||
}
|
||||
}
|
||||
|
||||
async function handleSetupBrowse(): Promise<void> {
|
||||
const picked = await window.api.browseDataDir()
|
||||
if (picked) setSetupDir(picked)
|
||||
}
|
||||
|
||||
async function handleSetupConfirm(): Promise<void> {
|
||||
const dir = setupDir.trim()
|
||||
if (!dir) return
|
||||
await window.api.setDataDir(dir, false)
|
||||
const updated = await window.api.getDataDir()
|
||||
setDataDir(updated)
|
||||
const p = await window.api.getProfile()
|
||||
setProfile(p)
|
||||
setStatus(p ? 'logged-in' : 'logged-out')
|
||||
}
|
||||
|
||||
async function handleChangeDataDir(): Promise<void> {
|
||||
const picked = await window.api.browseDataDir()
|
||||
if (!picked) return
|
||||
// Relaunch = true : les fichiers existants restent dans l'ancien dossier,
|
||||
// le launcher redémarre et tout se retélécharge dans le nouveau.
|
||||
await window.api.setDataDir(picked, true)
|
||||
}
|
||||
|
||||
if (status === 'loading') {
|
||||
return (
|
||||
<div className="app">
|
||||
@@ -137,6 +173,36 @@ export default function App(): JSX.Element {
|
||||
)
|
||||
}
|
||||
|
||||
if (status === 'setup') {
|
||||
return (
|
||||
<div className="app">
|
||||
<div className="center setup">
|
||||
<h1>OFLauncher</h1>
|
||||
<p className="muted">Bienvenue ! Choisis où seront stockés les fichiers du jeu.</p>
|
||||
<p className="muted" style={{ fontSize: 12 }}>
|
||||
Minecraft, Java, mods et sauvegardes iront dans ce dossier.
|
||||
</p>
|
||||
<div className="datadir-row">
|
||||
<input
|
||||
type="text"
|
||||
className="datadir-input"
|
||||
value={setupDir}
|
||||
onChange={(e) => setSetupDir(e.target.value)}
|
||||
spellCheck={false}
|
||||
/>
|
||||
<button className="linkbtn" onClick={() => void handleSetupBrowse()}>
|
||||
Parcourir…
|
||||
</button>
|
||||
</div>
|
||||
<button className="play" onClick={() => void handleSetupConfirm()} disabled={!setupDir.trim()}>
|
||||
Continuer
|
||||
</button>
|
||||
<div className="muted" style={{ fontSize: 12 }}>v{appVersion}</div>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
if (status === 'logged-out') {
|
||||
return (
|
||||
<div className="app">
|
||||
@@ -157,7 +223,7 @@ export default function App(): JSX.Element {
|
||||
</p>
|
||||
<div className="code">{authCode.userCode}</div>
|
||||
<p className="muted" style={{ fontSize: 12 }}>
|
||||
La page s’est ouverte dans ton navigateur. Reviens ici une fois connecté.
|
||||
La page s'est ouverte dans ton navigateur. Reviens ici une fois connecté.
|
||||
</p>
|
||||
</div>
|
||||
)}
|
||||
@@ -258,6 +324,9 @@ export default function App(): JSX.Element {
|
||||
<button className="linkbtn" onClick={handleOpenLogs}>
|
||||
Ouvrir les logs
|
||||
</button>
|
||||
<button className="linkbtn" onClick={() => void handleChangeDataDir()} disabled={busy} title={dataDir?.current}>
|
||||
Dossier de données
|
||||
</button>
|
||||
</div>
|
||||
|
||||
{status === 'running' ? (
|
||||
|
||||
@@ -283,3 +283,27 @@ button.play.stop:hover:not(:disabled) {
|
||||
display: inline-block;
|
||||
user-select: all;
|
||||
}
|
||||
|
||||
.setup {
|
||||
max-width: 560px;
|
||||
margin: 0 auto;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.datadir-row {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.datadir-input {
|
||||
flex: 1;
|
||||
background: var(--bg-soft);
|
||||
border: 1px solid var(--border);
|
||||
color: var(--text);
|
||||
border-radius: 6px;
|
||||
padding: 8px 10px;
|
||||
font-size: 13px;
|
||||
font-family: 'Cascadia Code', 'Consolas', monospace;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user