import { createTopBar } from '../components/topbar.js'; import { addAppLogEntry, authService, closeSavedProfile, state } from '../state.js'; import { isDeveloperToolsEnabled } from '../services/feature-settings.js'; export const pageMeta = { id: 'settings-view', title: 'Настройки' }; function formatBuildStamp(rawValue) { const value = String(rawValue || '').trim(); if (!/^\d{14}$/.test(value)) return value; const yyyy = value.slice(0, 4); const mm = value.slice(4, 6); const dd = value.slice(6, 8); const hh = value.slice(8, 10); const min = value.slice(10, 12); const ss = value.slice(12, 14); return `${yyyy}-${mm}-${dd}/${hh}:${min}__${ss}`; } function formatVersionForUi(rawValue) { const value = String(rawValue || '').trim(); if (!value) return 'n/a'; const formatted = formatBuildStamp(value); if (formatted && formatted !== value) { return `${formatted} (${value})`; } return value; } export function render({navigate, chrome}) { const screen = document.createElement('section'); screen.className = 'stack'; let isDisposed = false; chrome?.setTopbar(createTopBar({ title: 'Настройки', back: { label: '←', onClick: () => navigate('profile-view') }, })); const logoButton = document.createElement('button'); logoButton.type = 'button'; logoButton.className = 'settings-shine-logo-button'; logoButton.setAttribute('aria-label', 'Логотип SHiNE'); logoButton.title = 'SHiNE'; logoButton.innerHTML = ''; let logoTapCount = 0; let lastLogoTapAt = 0; logoButton.addEventListener('click', () => { const now = Date.now(); logoTapCount = now - lastLogoTapAt <= 900 ? logoTapCount + 1 : 1; lastLogoTapAt = now; if (logoTapCount < 5) return; logoTapCount = 0; navigate('advanced-settings-view'); }); const card = document.createElement('div'); card.className = 'card stack'; card.innerHTML = ` `; card.querySelector('#settings-device').addEventListener('click', () => navigate('device-view')); card.querySelector('#settings-remote-addblock').addEventListener('click', () => navigate('remote-addblock-session-view')); card.querySelector('#settings-access-servers').addEventListener('click', () => navigate('access-servers-view')); card.querySelector('#settings-blockchain-servers').addEventListener('click', () => navigate('server-settings-view')); card.querySelector('#settings-arweave-uploads').addEventListener('click', () => navigate('arweave-uploads-view')); card.querySelector('#settings-language').addEventListener('click', () => { sessionStorage.setItem('shine-language-return-page', 'settings-view'); navigate('language-view'); }); const signOutBtn = card.querySelector('#settings-signout'); signOutBtn.addEventListener('click', async () => { const confirmed = window.confirm( 'Завершить текущий профиль на этом устройстве? Если есть другие сохранённые профили, приложение переключится на следующий.' ); if (!confirmed) return; signOutBtn.disabled = true; try { addAppLogEntry({ level: 'info', source: 'session', message: 'Запрошено завершение текущей сессии', }); const result = await closeSavedProfile(state.session.login); window.location.assign(result?.nextProfile ? '/profile' : '/start'); } finally { signOutBtn.disabled = false; } }); screen.append(logoButton, card); if (isDeveloperToolsEnabled()) { const versionCard = document.createElement('div'); versionCard.className = 'card stack'; const title = document.createElement('p'); title.className = 'field-label'; title.textContent = 'Версии'; const clientVersion = document.createElement('p'); clientVersion.className = 'meta-muted'; clientVersion.textContent = `Клиент: ${formatVersionForUi(window.__SHINE_CLIENT_VERSION__)}`; const uiBuild = document.createElement('p'); uiBuild.className = 'meta-muted'; uiBuild.textContent = `Сборка UI: ${formatVersionForUi(window.__SHINE_BUILD_HASH__)}`; const serverVersion = document.createElement('p'); serverVersion.className = 'meta-muted'; serverVersion.textContent = 'Сервер: загружается...'; versionCard.append(title, clientVersion, uiBuild, serverVersion); const developerCard = document.createElement('div'); developerCard.className = 'card stack'; developerCard.innerHTML = ` `; developerCard.querySelector('#settings-developer').addEventListener('click', () => navigate('developer-settings-view')); void (async () => { try { let value = ''; try { const pingResp = await authService.ws.request('Ping', { ts: Date.now() }, 7000); value = String(pingResp?.payload?.serverVersion || pingResp?.serverVersion || '').trim(); } catch { // fallback below } if (!value) { const infoResp = await authService.ws.request('GetServerInfo', {}); value = String(infoResp?.payload?.version || '').trim(); } if (!isDisposed) serverVersion.textContent = `Сервер: ${formatVersionForUi(value)}`; } catch { if (!isDisposed) { serverVersion.textContent = 'Сервер: недоступно'; } } })(); screen.append(versionCard, developerCard); } screen.cleanup = () => { isDisposed = true; }; return screen; }