Release v0.1.1 2026-07-10
This commit is contained in:
+43
-10
@@ -1,4 +1,4 @@
|
||||
import { useMemo, useState } from "react";
|
||||
import { useMemo, useRef, useState } from "react";
|
||||
import { MAX_HUNTER_NAME_LENGTH, MODE_COPY, normalizeHunterName, selectRandomBossPair } from "../frontend/data";
|
||||
import { formatPlayTime, formatSaveTimestamp } from "../frontend/saveRepository";
|
||||
import { useActiveHunter, useFrontendStore } from "../frontend/store";
|
||||
@@ -51,15 +51,24 @@ function ControllerLegend({ back = false }: { back?: boolean }) {
|
||||
|
||||
function LoginScreen() {
|
||||
const signIn = useFrontendStore((state) => state.signIn);
|
||||
const createAccount = useFrontendStore((state) => state.createAccount);
|
||||
const continueOffline = useFrontendStore((state) => state.continueOffline);
|
||||
const notice = useFrontendStore((state) => state.notice);
|
||||
const [hunterId, setHunterId] = useState("wayfinder");
|
||||
const [username, setUsername] = useState("");
|
||||
const [password, setPassword] = useState("");
|
||||
const usernameRef = useRef<HTMLInputElement>(null);
|
||||
const passwordRef = useRef<HTMLInputElement>(null);
|
||||
const actions = useMemo<MenuAction[]>(() => [
|
||||
{ id: "sign-in", run: () => signIn(hunterId) },
|
||||
{ id: "username", run: () => usernameRef.current?.focus() },
|
||||
{ id: "password", run: () => passwordRef.current?.focus() },
|
||||
{ id: "sign-in", run: () => { void signIn(username, password); } },
|
||||
{ id: "create-account", run: () => { void createAccount(username, password); } },
|
||||
{ id: "offline", run: continueOffline },
|
||||
], [continueOffline, hunterId, signIn]);
|
||||
], [continueOffline, createAccount, password, signIn, username]);
|
||||
const controller = useMenuController(actions);
|
||||
|
||||
const submitSignIn = () => { void signIn(username, password); };
|
||||
|
||||
return (
|
||||
<DualDisplayFrame
|
||||
top={
|
||||
@@ -71,17 +80,41 @@ function LoginScreen() {
|
||||
<h1>Keep everyone standing.</h1>
|
||||
<p>Your save always lives on this device. Sign in only when you want a second copy for PC ↔ AYN Thor handoff.</p>
|
||||
</div>
|
||||
<form className="login-panel" onSubmit={(event) => { event.preventDefault(); signIn(hunterId); }}>
|
||||
<label htmlFor="hunter-id">Hunter ID</label>
|
||||
<input id="hunter-id" value={hunterId} onChange={(event) => setHunterId(event.target.value)} autoComplete="username" />
|
||||
<form className="login-panel" onSubmit={(event) => { event.preventDefault(); submitSignIn(); }}>
|
||||
<label htmlFor="account-username">Username</label>
|
||||
<input
|
||||
ref={usernameRef}
|
||||
id="account-username"
|
||||
className={controller.focusedId === "username" ? "is-controller-focused" : ""}
|
||||
value={username}
|
||||
onChange={(event) => setUsername(event.target.value)}
|
||||
onFocus={() => controller.focus("username")}
|
||||
autoComplete="username"
|
||||
required
|
||||
/>
|
||||
<label htmlFor="account-password">Password</label>
|
||||
<input
|
||||
ref={passwordRef}
|
||||
id="account-password"
|
||||
className={controller.focusedId === "password" ? "is-controller-focused" : ""}
|
||||
type="password"
|
||||
value={password}
|
||||
onChange={(event) => setPassword(event.target.value)}
|
||||
onFocus={() => controller.focus("password")}
|
||||
autoComplete="current-password"
|
||||
required
|
||||
/>
|
||||
<FocusButton id="sign-in" focusedId={controller.focusedId} focus={controller.focus} className="front-primary" type="submit">
|
||||
<span>Sign in & sync</span><small>Online saves enabled</small>
|
||||
</FocusButton>
|
||||
<FocusButton id="create-account" focusedId={controller.focusedId} focus={controller.focus} className="front-secondary" type="button" onClick={() => { void createAccount(username, password); }}>
|
||||
<span>Create account</span><small>Required for first sync</small>
|
||||
</FocusButton>
|
||||
<FocusButton id="offline" focusedId={controller.focusedId} focus={controller.focus} className="front-secondary" type="button" onClick={continueOffline}>
|
||||
<span>Continue with offline save</span><small>No account required</small>
|
||||
</FocusButton>
|
||||
</form>
|
||||
{notice && <div className="front-notice">{notice}</div>}
|
||||
{notice && <div className="front-notice" role="status" aria-live="polite">{notice}</div>}
|
||||
<ControllerLegend />
|
||||
</FrontSurface>
|
||||
}
|
||||
@@ -92,8 +125,8 @@ function LoginScreen() {
|
||||
<span className="context-kicker">How saving works</span>
|
||||
<ol>
|
||||
<li><b>01</b><span><strong>Play offline</strong><small>Every change writes to device storage first.</small></span></li>
|
||||
<li><b>02</b><span><strong>Sync when ready</strong><small>Upload any slot after signing in.</small></span></li>
|
||||
<li><b>03</b><span><strong>Move devices</strong><small>Download the online copy and overwrite local.</small></span></li>
|
||||
<li><b>02</b><span><strong>Create or sign in</strong><small>Username and password unlock online sync.</small></span></li>
|
||||
<li><b>03</b><span><strong>Move devices</strong><small>Sign in, then upload or download an online copy.</small></span></li>
|
||||
</ol>
|
||||
</div>
|
||||
<div className="device-route"><span>PC</span><i>↔</i><b>ONLINE COPY</b><i>↔</i><span>THOR</span></div>
|
||||
|
||||
Reference in New Issue
Block a user