From a575cb057bc02fdba7a7b226b00b0d6f421472d0 Mon Sep 17 00:00:00 2001
From: 3252a8 <3252a8@proton.me>
Date: Mon, 11 May 2026 22:32:23 +0300
Subject: [PATCH] refactor: slice web app wip
---
bot/app/web/frontend/src/App.svelte | 435 +++---------------
.../src/lib/webapp/billingOptionCache.js | 4 +
.../frontend/src/lib/webapp/devicesLabels.js | 24 +
.../src/lib/webapp/stores/accountStore.js | 1 +
.../src/lib/webapp/stores/authStore.js | 3 +-
.../src/lib/webapp/stores/devicesStore.js | 25 -
.../web/frontend/src/lib/webapp/webappBoot.js | 87 ++++
.../frontend/src/webapp/TariffDialogs.svelte | 1 -
.../src/webapp/screens/DevicesScreen.svelte | 24 +-
9 files changed, 177 insertions(+), 427 deletions(-)
create mode 100644 bot/app/web/frontend/src/lib/webapp/billingOptionCache.js
create mode 100644 bot/app/web/frontend/src/lib/webapp/devicesLabels.js
create mode 100644 bot/app/web/frontend/src/lib/webapp/webappBoot.js
diff --git a/bot/app/web/frontend/src/App.svelte b/bot/app/web/frontend/src/App.svelte
index 1d38e27..eb6c111 100644
--- a/bot/app/web/frontend/src/App.svelte
+++ b/bot/app/web/frontend/src/App.svelte
@@ -1,5 +1,4 @@
@@ -1123,8 +799,8 @@
initialSection={adminSectionFromPath(window.location.pathname)}
initialUserId={adminUserIdFromPath(window.location.pathname)}
onSectionChange={handleAdminSectionChange}
- onSettingsSaved={handleSettingsSaved}
- onTariffsSaved={handleTariffsSaved}
+ onSettingsSaved={handleAdminPersistedSaved}
+ onTariffsSaved={handleAdminPersistedSaved}
brandTitle={brandTitle}
logoUrl={CFG.logoUrl}
logoEmoji={brandEmoji}
@@ -1313,7 +989,6 @@
{topupOptions}
{trafficMode}
{t}
- {termUnitLabel}
/>
{/if}
diff --git a/bot/app/web/frontend/src/lib/webapp/billingOptionCache.js b/bot/app/web/frontend/src/lib/webapp/billingOptionCache.js
new file mode 100644
index 0000000..91b0f1d
--- /dev/null
+++ b/bot/app/web/frontend/src/lib/webapp/billingOptionCache.js
@@ -0,0 +1,4 @@
+/** Drop cached topup / change-tariff option payloads so the next open refetches from /api. */
+export function invalidateWebappTariffOptionCaches(billingStore) {
+ billingStore.update((s) => ({ ...s, topupOptions: null, deviceTopupOptions: null, changeOptions: null }));
+}
diff --git a/bot/app/web/frontend/src/lib/webapp/devicesLabels.js b/bot/app/web/frontend/src/lib/webapp/devicesLabels.js
new file mode 100644
index 0000000..ab8894e
--- /dev/null
+++ b/bot/app/web/frontend/src/lib/webapp/devicesLabels.js
@@ -0,0 +1,24 @@
+/**
+ * Pure helpers for HWID / device limits UI (used by DevicesScreen).
+ * @param {Record} devicesData API payload from /api/devices
+ * @param {(key: string, vars?: Record, fallback?: string) => string} t i18n function
+ * @param {unknown} [maxDevicesOverride] optional max_devices override (defaults to devicesData.max_devices)
+ */
+export function devicesLimitLabel(devicesData, t, maxDevicesOverride) {
+ const value = maxDevicesOverride !== undefined ? maxDevicesOverride : devicesData?.max_devices;
+ const numeric = Number(value ?? 0);
+ if (!Number.isFinite(numeric) || numeric <= 0) return t("wa_devices_unlimited");
+ return String(Math.trunc(numeric));
+}
+
+export function devicesCountLabel(devicesData, t) {
+ const current = Number(devicesData?.current_devices ?? devicesData?.devices?.length ?? 0);
+ return t("wa_devices_count", { current, max: devicesLimitLabel(devicesData, t) });
+}
+
+export function devicesPercent(devicesData) {
+ const current = Number(devicesData?.current_devices ?? devicesData?.devices?.length ?? 0);
+ const max = Number(devicesData?.max_devices || 0);
+ if (!max || max <= 0) return 100;
+ return Math.max(0, Math.min(100, Math.round((current / max) * 100)));
+}
diff --git a/bot/app/web/frontend/src/lib/webapp/stores/accountStore.js b/bot/app/web/frontend/src/lib/webapp/stores/accountStore.js
index 81c9c62..55e8cb7 100644
--- a/bot/app/web/frontend/src/lib/webapp/stores/accountStore.js
+++ b/bot/app/web/frontend/src/lib/webapp/stores/accountStore.js
@@ -211,5 +211,6 @@ export function createAccountStore({ api, publicApi, setToken, loadData, t, show
linkTelegramAccount,
updateAccountLanguage,
logout,
+ clearLinkEmailResendTimer: clearCooldownTimer,
};
}
diff --git a/bot/app/web/frontend/src/lib/webapp/stores/authStore.js b/bot/app/web/frontend/src/lib/webapp/stores/authStore.js
index bbfd8c2..79fe5b7 100644
--- a/bot/app/web/frontend/src/lib/webapp/stores/authStore.js
+++ b/bot/app/web/frontend/src/lib/webapp/stores/authStore.js
@@ -269,6 +269,7 @@ export function createAuthStore({
verifyEmailCode,
openTelegramLogin,
clearCooldownTimer,
- setAuthStatus
+ stopTelegramLoginWatchdog,
+ setAuthStatus,
};
}
diff --git a/bot/app/web/frontend/src/lib/webapp/stores/devicesStore.js b/bot/app/web/frontend/src/lib/webapp/stores/devicesStore.js
index 2fa7641..852f69b 100644
--- a/bot/app/web/frontend/src/lib/webapp/stores/devicesStore.js
+++ b/bot/app/web/frontend/src/lib/webapp/stores/devicesStore.js
@@ -63,28 +63,6 @@ export function createDevicesStore({ api, t, showToast }) {
}
}
- function devicesLimitLabel() {
- const s = get(state);
- const value = s.devicesData?.max_devices;
- const numeric = Number(value ?? 0);
- if (!Number.isFinite(numeric) || numeric <= 0) return t("wa_devices_unlimited");
- return String(Math.trunc(numeric));
- }
-
- function devicesCountLabel() {
- const s = get(state);
- const current = Number(s.devicesData?.current_devices ?? s.devicesData?.devices?.length ?? 0);
- return t("wa_devices_count", { current, max: devicesLimitLabel() });
- }
-
- function devicesPercent() {
- const s = get(state);
- const current = Number(s.devicesData?.current_devices ?? s.devicesData?.devices?.length ?? 0);
- const max = Number(s.devicesData?.max_devices || 0);
- if (!max || max <= 0) return 100;
- return Math.max(0, Math.min(100, Math.round((current / max) * 100)));
- }
-
return {
subscribe: state.subscribe,
set: state.set,
@@ -93,8 +71,5 @@ export function createDevicesStore({ api, t, showToast }) {
openDeviceDisconnectDialog,
closeDeviceDisconnectDialog,
disconnectDevice,
- devicesLimitLabel,
- devicesCountLabel,
- devicesPercent,
};
}
diff --git a/bot/app/web/frontend/src/lib/webapp/webappBoot.js b/bot/app/web/frontend/src/lib/webapp/webappBoot.js
new file mode 100644
index 0000000..11f0c1d
--- /dev/null
+++ b/bot/app/web/frontend/src/lib/webapp/webappBoot.js
@@ -0,0 +1,87 @@
+import {
+ readMagicLoginToken,
+ readTelegramAuthStatus,
+ readTelegramLoginWidgetAuthData,
+ clearAuthQuery,
+} from "./authHelpers.js";
+import { TELEGRAM_SDK_BOOT_TIMEOUT_MS } from "./constants.js";
+
+/**
+ * Initial auth / session bootstrap for the subscription webapp (non-preview).
+ * Keeps side effects in App (mode, tg, token) via injected callbacks.
+ */
+export async function runWebappBoot({
+ MOCK,
+ setMode,
+ hasTelegramLaunchParams,
+ loadTelegramSdk,
+ prepareTelegramMiniApp,
+ loadData,
+ showLogin,
+ clearToken,
+ clearManualLogoutFlag,
+ isManuallyLoggedOut,
+ finalizeMagicLogin,
+ finalizeTelegramAuth,
+ setAuthStatus,
+ t,
+ getInitDataForBoot,
+ getToken,
+ getCsrfToken,
+}) {
+ setMode("loading");
+ if (hasTelegramLaunchParams()) await loadTelegramSdk(TELEGRAM_SDK_BOOT_TIMEOUT_MS);
+ prepareTelegramMiniApp();
+
+ if (MOCK) {
+ await loadData();
+ return;
+ }
+
+ const magicToken = readMagicLoginToken();
+ if (magicToken && (await finalizeMagicLogin(magicToken))) return;
+
+ const telegramAuthStatus = readTelegramAuthStatus();
+ if (telegramAuthStatus === "success") {
+ clearManualLogoutFlag();
+ clearAuthQuery();
+ try {
+ await loadData();
+ return;
+ } catch {
+ clearToken();
+ }
+ } else if (telegramAuthStatus) {
+ clearAuthQuery();
+ setAuthStatus(
+ telegramAuthStatus === "cancelled" ? t("wa_auth_telegram_cancelled") : t("wa_auth_telegram_not_confirmed"),
+ true,
+ );
+ }
+
+ if (isManuallyLoggedOut()) {
+ showLogin();
+ return;
+ }
+
+ const widgetAuthData = readTelegramLoginWidgetAuthData();
+ if (widgetAuthData && (await finalizeTelegramAuth(widgetAuthData, "auth_data"))) return;
+
+ const initData = getInitDataForBoot();
+ if (initData) {
+ try {
+ if (await finalizeTelegramAuth(initData, "init_data")) return;
+ } catch {}
+ }
+
+ if (getToken() || getCsrfToken()) {
+ try {
+ await loadData();
+ return;
+ } catch {
+ clearToken();
+ }
+ }
+
+ showLogin();
+}
diff --git a/bot/app/web/frontend/src/webapp/TariffDialogs.svelte b/bot/app/web/frontend/src/webapp/TariffDialogs.svelte
index a1aa8d3..83910bf 100644
--- a/bot/app/web/frontend/src/webapp/TariffDialogs.svelte
+++ b/bot/app/web/frontend/src/webapp/TariffDialogs.svelte
@@ -140,7 +140,6 @@
export let t = (key) => key;
- export let termUnitLabel = () => "";