Skip to content

Commit 1a4abda

Browse files
committed
Show loading screen before switch console is ready
1 parent 215ba34 commit 1a4abda

3 files changed

Lines changed: 147 additions & 8 deletions

File tree

tests/strategy_switch_worker_validation.mjs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,10 @@ const indexHtml = readFileSync(resolve(root, "web/strategy-switch-console/index.
1010
const renderPlatformsBody = indexHtml.match(/function renderPlatforms\(\) \{([\s\S]*?)\n \}/)?.[1] || "";
1111
assert.ok(!renderPlatformsBody.includes("syncStrategyForAccount("));
1212
assert.equal(indexHtml.includes(".innerHTML"), false);
13+
assert.ok(indexHtml.includes('<body class="app-loading">'));
14+
assert.ok(indexHtml.includes('id="boot-screen"'));
15+
assert.ok(indexHtml.includes('id="app-shell"'));
16+
assert.match(indexHtml, /body\.app-loading \.shell\s*\{\s*display: none;/);
1317

1418
const headers = __test.responseHeaders({ "Content-Type": "text/html; charset=utf-8" });
1519
assert.equal(headers.get("X-Frame-Options"), "DENY");

web/strategy-switch-console/index.html

Lines changed: 142 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -132,6 +132,69 @@
132132
gap: 16px;
133133
}
134134

135+
body.app-loading .shell {
136+
display: none;
137+
}
138+
139+
body:not(.app-loading) .boot-screen {
140+
display: none;
141+
}
142+
143+
.boot-screen {
144+
width: min(680px, calc(100vw - 36px));
145+
min-height: calc(100svh - 72px);
146+
margin: 0 auto;
147+
display: grid;
148+
align-items: center;
149+
padding: 34px 0;
150+
}
151+
152+
.boot-panel {
153+
display: grid;
154+
gap: 13px;
155+
padding: 8px 0;
156+
}
157+
158+
.boot-kicker {
159+
color: var(--accent);
160+
font-size: 12px;
161+
line-height: 1.2;
162+
font-weight: 800;
163+
text-transform: uppercase;
164+
}
165+
166+
.boot-panel h2 {
167+
color: var(--ink);
168+
font-size: clamp(28px, 5vw, 44px);
169+
line-height: 1.04;
170+
font-weight: 820;
171+
}
172+
173+
.boot-panel p {
174+
color: var(--muted);
175+
font-size: 14px;
176+
line-height: 1.5;
177+
max-width: 520px;
178+
}
179+
180+
.loading-track {
181+
width: 100%;
182+
height: 5px;
183+
overflow: hidden;
184+
border-radius: 999px;
185+
background: #dde4ea;
186+
}
187+
188+
.loading-track::before {
189+
content: "";
190+
display: block;
191+
width: 42%;
192+
height: 100%;
193+
border-radius: inherit;
194+
background: var(--accent);
195+
animation: loading-sweep 1.05s ease-in-out infinite;
196+
}
197+
135198
.pill,
136199
.btn {
137200
min-height: 36px;
@@ -536,6 +599,12 @@
536599
width: 100%;
537600
}
538601

602+
.boot-screen {
603+
width: min(100% - 28px, 680px);
604+
min-height: calc(100svh - 122px);
605+
padding-top: 22px;
606+
}
607+
539608
.shell {
540609
width: min(100% - 28px, 1160px);
541610
padding-top: 18px;
@@ -580,10 +649,26 @@
580649
transform: translateY(0);
581650
}
582651
}
652+
653+
@keyframes loading-sweep {
654+
from {
655+
transform: translateX(-110%);
656+
}
657+
to {
658+
transform: translateX(260%);
659+
}
660+
}
661+
}
662+
663+
@media (prefers-reduced-motion: reduce) {
664+
.loading-track::before {
665+
animation: none;
666+
transform: translateX(0);
667+
}
583668
}
584669
</style>
585670
</head>
586-
<body>
671+
<body class="app-loading">
587672
<header class="topbar">
588673
<div class="brand">
589674
<h1 data-i18n="appTitle">策略切换</h1>
@@ -597,7 +682,16 @@ <h1 data-i18n="appTitle">策略切换</h1>
597682
</div>
598683
</header>
599684

600-
<main class="shell">
685+
<main class="boot-screen" id="boot-screen" role="status" aria-live="polite">
686+
<div class="boot-panel">
687+
<span class="boot-kicker" data-i18n="bootKicker">初始化控制台</span>
688+
<h2 data-i18n="bootTitle">读取策略配置</h2>
689+
<p id="boot-message" data-i18n="bootMessage">正在读取登录状态、账号配置和当前状态。</p>
690+
<div class="loading-track" aria-hidden="true"></div>
691+
</div>
692+
</main>
693+
694+
<main class="shell" id="app-shell">
601695
<nav class="platform-strip" id="platform-strip" aria-label="Platforms"></nav>
602696

603697
<section class="switch-surface">
@@ -734,6 +828,13 @@ <h2 data-i18n="summary">切换摘要</h2>
734828
zh: {
735829
appTitle: "策略切换",
736830
appSubtitle: "选平台、目标账号和策略,一次执行完成切换。",
831+
bootKicker: "初始化控制台",
832+
bootTitle: "读取策略配置",
833+
bootMessage: "正在读取登录状态、账号配置和当前状态。",
834+
bootStrategy: "正在读取策略目录。",
835+
bootSession: "正在验证登录状态。",
836+
bootConfig: "正在读取账号配置和当前状态。",
837+
bootPublic: "公开预览已就绪。",
737838
login: "登录",
738839
logout: "退出",
739840
signedInAs: "已登录 {login}",
@@ -787,6 +888,13 @@ <h2 data-i18n="summary">切换摘要</h2>
787888
en: {
788889
appTitle: "Strategy Switch",
789890
appSubtitle: "Pick platform, target account, and strategy. One action switches everything.",
891+
bootKicker: "Starting console",
892+
bootTitle: "Loading strategy config",
893+
bootMessage: "Reading session, account config, and current state.",
894+
bootStrategy: "Reading strategy catalog.",
895+
bootSession: "Checking sign-in status.",
896+
bootConfig: "Reading account config and current state.",
897+
bootPublic: "Public preview is ready.",
790898
login: "Sign in",
791899
logout: "Sign out",
792900
signedInAs: "Signed in as {login}",
@@ -848,6 +956,8 @@ <h2 data-i18n="summary">切换摘要</h2>
848956
const state = {
849957
selected: "longbridge",
850958
lang: initialLang,
959+
appReady: false,
960+
bootMessageKey: "bootMessage",
851961
auth: { available: false, allowed: false, admin: false, login: null },
852962
accountOptions: clone(defaultAccountOptions),
853963
currentStrategies: {},
@@ -1263,15 +1373,23 @@ <h2 data-i18n="summary">切换摘要</h2>
12631373
note.classList.toggle("warning", state.auth.allowed && !loadingConfig && (!hasPrivateAccounts || !hasValidStrategy));
12641374
}
12651375

1376+
function renderAppVisibility() {
1377+
document.body.classList.toggle("app-loading", !state.appReady);
1378+
el("boot-message").textContent = t(state.bootMessageKey);
1379+
}
1380+
12661381
function render() {
12671382
applyLanguage();
12681383
renderPlatforms();
12691384
renderControls();
12701385
renderSummary();
12711386
renderAuth();
1387+
renderAppVisibility();
12721388
}
12731389

12741390
async function refreshSession() {
1391+
state.bootMessageKey = "bootSession";
1392+
render();
12751393
try {
12761394
const response = await fetch("/api/session", { cache: "no-store" });
12771395
if (!response.ok) throw new Error("no backend");
@@ -1285,11 +1403,18 @@ <h2 data-i18n="summary">切换摘要</h2>
12851403
} catch {
12861404
state.auth = { available: false, allowed: false, admin: false, login: null };
12871405
}
1288-
render();
1289-
await refreshConfig();
1406+
if (state.auth.allowed) {
1407+
await refreshConfig();
1408+
} else {
1409+
state.bootMessageKey = "bootPublic";
1410+
state.appReady = true;
1411+
render();
1412+
}
12901413
}
12911414

12921415
async function refreshStrategyProfiles() {
1416+
state.bootMessageKey = "bootStrategy";
1417+
render();
12931418
try {
12941419
const response = await fetch("/api/strategy-profiles", { cache: "no-store" });
12951420
if (!response.ok) throw new Error("no strategy profiles");
@@ -1306,6 +1431,7 @@ <h2 data-i18n="summary">切换摘要</h2>
13061431
async function refreshConfig() {
13071432
if (!state.auth.available || !state.auth.allowed) return;
13081433
state.configSource = "loading";
1434+
state.bootMessageKey = "bootConfig";
13091435
render();
13101436
try {
13111437
const response = await fetch("/api/config", { cache: "no-store" });
@@ -1320,11 +1446,16 @@ <h2 data-i18n="summary">切换摘要</h2>
13201446
ensureAccountSelection(platform);
13211447
syncStrategyForAccount(platform);
13221448
}
1323-
render();
1449+
} else {
1450+
state.configSource = "default";
1451+
state.currentStrategies = {};
13241452
}
13251453
} catch {
13261454
state.configSource = "default";
13271455
state.currentStrategies = {};
1456+
} finally {
1457+
state.appReady = true;
1458+
render();
13281459
}
13291460
}
13301461

@@ -1454,8 +1585,12 @@ <h2 data-i18n="summary">切换摘要</h2>
14541585
applyStrategyProfiles(defaultStrategyProfiles);
14551586
for (const platform of Object.keys(platformMeta)) syncStrategyForAccount(platform);
14561587
render();
1457-
refreshStrategyProfiles();
1458-
refreshSession();
1588+
boot();
1589+
1590+
async function boot() {
1591+
await refreshStrategyProfiles();
1592+
await refreshSession();
1593+
}
14591594
</script>
14601595
</body>
14611596
</html>

web/strategy-switch-console/page_asset.js

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)