fix: preserve admin view on language change

This commit is contained in:
3252a8
2026-05-28 09:54:27 +03:00
parent 6c0d2932c3
commit 12e60629fe
3 changed files with 105 additions and 28 deletions
+91 -23
View File
@@ -187,7 +187,9 @@
let adminBundleError = "";
let adminMountTarget = null;
let adminMountHandle = null;
let adminMountedTarget = null;
let adminPanelProps = {};
let adminActiveSection = "stats";
let tg = null;
const telegramSdk = createTelegramSdk({
scriptUrl: TELEGRAM_WEBAPP_SCRIPT_URL,
@@ -767,7 +769,11 @@
void boot();
return;
}
const section = sectionFromPath(window.location.pathname);
const currentQuery = currentSearchParams();
const section =
isDocsDemo && currentQuery.get("screen")
? normalizeSection(currentQuery.get("screen"))
: sectionFromPath(window.location.pathname);
if (mode === "login") {
setPasswordLoginMode(isPasswordLoginPath(), true);
screen = "login";
@@ -775,6 +781,9 @@
}
if (mode === "app") {
if (section === "admin" && isAdmin) {
adminActiveSection = isDocsDemo
? initialAdminSectionFromLocation()
: adminSectionFromPath(window.location.pathname);
const pathAtStart = window.location.pathname;
void Promise.all([ensureI18nScope("admin"), ensureAdminBundle()])
.then(() => {
@@ -1022,20 +1031,54 @@
if (!adminMountHandle) return;
adminMountHandle.destroy?.();
adminMountHandle = null;
adminMountedTarget = null;
}
function currentSearchParams() {
return new URLSearchParams(window.location.search);
}
function initialAdminSectionFromLocation() {
if (MOCK && query.get("admin_section")) {
return normalizeAdminSection(query.get("admin_section"));
const currentQuery = currentSearchParams();
if (MOCK && currentQuery.get("admin_section")) {
return normalizeAdminSection(currentQuery.get("admin_section"));
}
return adminSectionFromPath(window.location.pathname);
}
function syncDocsDemoSection(section, replace = false, adminSection = null) {
if (!isDocsDemo || window.location.protocol === "file:") return false;
const normalized = normalizeSection(section);
const currentQuery = currentSearchParams();
currentQuery.set("screen", normalized);
if (normalized === "admin") {
currentQuery.set(
"admin_section",
normalizeAdminSection(
adminSection || adminActiveSection || initialAdminSectionFromLocation()
)
);
} else {
currentQuery.delete("admin_section");
}
const nextSearch = currentQuery.toString();
const nextUrl = `${window.location.pathname}${nextSearch ? `?${nextSearch}` : ""}${window.location.hash}`;
if (nextUrl !== `${window.location.pathname}${window.location.search}${window.location.hash}`) {
window.history[replace ? "replaceState" : "pushState"](null, "", nextUrl);
}
return true;
}
function syncAppSectionPath(section, replace = false, adminSection = null, adminUserId = null) {
if (syncDocsDemoSection(section, replace, adminSection)) return;
syncSectionPath(section, replace, adminSection, adminUserId);
}
$: adminPanelProps = {
api,
onClose: closeAdminPanel,
onToast: (text) => showToast(text),
initialSection: initialAdminSectionFromLocation(),
initialSection: screen === "admin" ? adminActiveSection : initialAdminSectionFromLocation(),
initialPaymentId: adminPaymentIdFromPath(window.location.pathname),
initialPaymentUserId: adminPaymentsUserIdFromPath(window.location.pathname),
initialUserId: adminUserIdFromPath(window.location.pathname),
@@ -1063,11 +1106,13 @@
if (shouldMountAdmin) {
try {
if (adminMountHandle) {
if (adminMountHandle && adminMountedTarget === adminMountTarget) {
adminMountHandle.update?.(props);
} else {
destroyAdminMount();
adminMountTarget.replaceChildren();
adminMountHandle = adminBundleApi.mount(adminMountTarget, props);
adminMountedTarget = adminMountTarget;
}
} catch (error) {
adminBundleError = error?.message || "admin_bundle_mount_failed";
@@ -1161,6 +1206,16 @@
}
async function loadData(options = {}) {
const preserveView = options?.preserveView === true;
const preservedSection = preserveView
? normalizeSection(options?.section || screen || activeTab)
: null;
const preservedAdminSection =
preserveView && preservedSection === "admin"
? normalizeAdminSection(
options?.adminSection || adminActiveSection || initialAdminSectionFromLocation()
)
: null;
const payload = await api(options?.fresh ? "/me?fresh=1" : "/me");
if (!payload.ok) throw new Error(payload.error || "load_failed");
data = payload;
@@ -1171,9 +1226,11 @@
paymentStep: "tariff",
selectedMethod: payload.payment_methods?.[0]?.id || "",
}));
let section =
MOCK && query.get("screen")
? normalizeSection(query.get("screen"))
const currentQuery = currentSearchParams();
let section = preserveView
? preservedSection
: MOCK && currentQuery.get("screen")
? normalizeSection(currentQuery.get("screen"))
: sectionFromPath(window.location.pathname);
if (section === "admin" && !payload.user?.is_admin) section = "settings";
if (section === "devices" && !payload.settings?.my_devices_enabled) section = "home";
@@ -1186,11 +1243,13 @@
) {
section = "home";
}
const initialAdminSection = section === "admin" ? initialAdminSectionFromLocation() : null;
const initialAdminSection =
section === "admin" ? preservedAdminSection || initialAdminSectionFromLocation() : null;
if (section === "admin" && payload.user?.is_admin) {
try {
await ensureI18nScope("admin");
await ensureAdminBundle();
adminActiveSection = initialAdminSection || "stats";
} catch (_error) {
void _error;
section = "settings";
@@ -1228,7 +1287,7 @@
);
}
} else {
syncSectionPath(section, true, initialAdminSection);
syncAppSectionPath(section, true, initialAdminSection);
}
if (section === "devices" && payload.settings?.my_devices_enabled) {
await devicesStore.loadDevices(true);
@@ -1443,12 +1502,12 @@
activeTab = "home";
if (useInstallGuides) {
screen = "install";
syncSectionPath("install", replace);
syncAppSectionPath("install", replace);
installGuidesStore.load(true);
return;
}
screen = "home";
syncSectionPath("home", replace);
syncAppSectionPath("home", replace);
}
async function handleSubscriptionActivated(context = {}) {
@@ -1551,7 +1610,7 @@
billingStore.closePaymentModal();
activeTab = "home";
screen = "home";
syncSectionPath("home");
syncAppSectionPath("home");
}
function goInstall() {
@@ -1562,7 +1621,7 @@
billingStore.closePaymentModal();
activeTab = "home";
screen = "install";
syncSectionPath("install");
syncAppSectionPath("install");
installGuidesStore.load(true);
}
@@ -1570,7 +1629,7 @@
billingStore.closePaymentModal();
activeTab = "invite";
screen = "invite";
syncSectionPath("invite");
syncAppSectionPath("invite");
}
function goDevices() {
@@ -1578,7 +1637,7 @@
billingStore.closePaymentModal();
activeTab = "devices";
screen = "devices";
syncSectionPath("devices");
syncAppSectionPath("devices");
devicesStore.loadDevices(devicesEnabled);
}
@@ -1587,7 +1646,7 @@
billingStore.closePaymentModal();
activeTab = "support";
screen = "support";
syncSectionPath("support");
syncAppSectionPath("support");
supportStore.loadList();
supportStore.startPolling({ includeList: true });
}
@@ -1643,13 +1702,16 @@
billingStore.closePaymentModal();
activeTab = "settings";
screen = "settings";
syncSectionPath("settings");
syncAppSectionPath("settings");
}
async function openAdminPanel() {
if (!isAdmin) return;
clearLanguageClickGuard();
billingStore.closePaymentModal();
const nextAdminSection = normalizeAdminSection(
adminActiveSection || adminSectionFromPath(window.location.pathname)
);
try {
await ensureI18nScope("admin");
await ensureAdminBundle();
@@ -1660,23 +1722,29 @@
}
activeTab = "settings";
screen = "admin";
syncSectionPath("admin", false, adminSectionFromPath(window.location.pathname));
adminActiveSection = nextAdminSection;
syncAppSectionPath("admin", false, adminActiveSection);
}
function closeAdminPanel() {
screen = "settings";
activeTab = "settings";
syncSectionPath("settings");
syncAppSectionPath("settings");
}
function handleAdminSectionChange(adminSection, adminUserId = null) {
if (screen !== "admin") return;
const nextAdminSection = normalizeAdminSection(adminSection);
adminActiveSection = nextAdminSection;
if (window.location.protocol === "file:") return;
if (isDocsDemo) return;
if (isDocsDemo) {
syncDocsDemoSection("admin", false, nextAdminSection);
return;
}
const targetPath =
adminSection === "users" && adminUserId
nextAdminSection === "users" && adminUserId
? `/admin/users/${adminUserId}`
: `/admin/${adminSection}`;
: `/admin/${nextAdminSection}`;
if (window.location.pathname === targetPath) return;
window.history.pushState(
null,
+12 -3
View File
@@ -211,8 +211,13 @@
const normalizeSection = (value) => ((VALID_SECTIONS || []).includes(value) ? value : "stats");
let active = normalizeSection(initialSection);
$: if (initialSection) {
active = normalizeSection(initialSection);
let lastInitialSection = active;
$: {
const nextInitialSection = normalizeSection(initialSection);
if (nextInitialSection !== lastInitialSection) {
active = nextInitialSection;
lastInitialSection = nextInitialSection;
}
}
let sidebarOpen = false;
let isCompact = false;
@@ -287,6 +292,10 @@
onSectionChange(next);
}
function changeLanguage(value) {
onLanguageChange(value, { section: "admin", adminSection: active });
}
function readSectionFromPath() {
if (typeof window === "undefined") return "stats";
const match = window.location.pathname.match(/^\/admin\/([a-z0-9_-]+)(?:\/.*)?$/i);
@@ -584,7 +593,7 @@
items={languageOptions}
disabled={languageBusy}
onOpenChange={setAdminLanguageMenuOpen}
onValueChange={onLanguageChange}
onValueChange={changeLanguage}
>
<Select.Trigger
class="admin-language-trigger"
@@ -346,7 +346,7 @@ export function createAccountStore({
window.location.assign(buildTelegramOAuthStartUrl("link", getTg()));
}
async function updateAccountLanguage(nextValue) {
async function updateAccountLanguage(nextValue, options = {}) {
const s = get(state);
const normalize = typeof normalizeLangCode === "function" ? normalizeLangCode : (v) => v;
const language = normalize(nextValue);
@@ -361,7 +361,7 @@ export function createAccountStore({
if (typeof updateLocalData === "function") {
updateLocalData(normalize(response.language || language));
}
await loadData({ fresh: true });
await loadData({ fresh: true, preserveView: true, ...options });
} catch {
showToast(t("wa_settings_language_update_failed"));
} finally {