Добавить автообновление UI и нижний статус соединения
This commit is contained in:
parent
d07602b0a9
commit
8be56192cb
@ -94,14 +94,131 @@ const routes = {
|
||||
|
||||
const screenEl = document.getElementById('app-screen');
|
||||
const toolbarEl = document.getElementById('toolbar-slot');
|
||||
const appShellEl = document.querySelector('.app-shell');
|
||||
|
||||
const VERSION_CHECK_INTERVAL_MS = 10 * 60 * 1000;
|
||||
const CONNECTION_CHECK_INTERVAL_MS = 20 * 1000;
|
||||
const CURRENT_BUILD_HASH = String(window.__SHINE_BUILD_HASH__ || '').trim();
|
||||
|
||||
let currentCleanup = null;
|
||||
let pingIntervalId = null;
|
||||
let versionCheckIntervalId = null;
|
||||
let versionCheckInFlight = false;
|
||||
let sessionRuntimeStarted = false;
|
||||
let connectionStatusEl = null;
|
||||
let connectionState = '';
|
||||
|
||||
setClientErrorTransport((payload) => authService.reportClientError(payload));
|
||||
initPwaInstallPromptHandling();
|
||||
|
||||
function ensureConnectionStatusEl() {
|
||||
if (connectionStatusEl) return connectionStatusEl;
|
||||
if (!appShellEl) return null;
|
||||
const el = document.createElement('div');
|
||||
el.id = 'connection-status-slot';
|
||||
el.className = 'connection-status-slot is-connecting';
|
||||
el.textContent = 'Подключение к серверу...';
|
||||
appShellEl.append(el);
|
||||
connectionStatusEl = el;
|
||||
return el;
|
||||
}
|
||||
|
||||
function setConnectionStatus(nextState, text = '') {
|
||||
const el = ensureConnectionStatusEl();
|
||||
if (!el) return;
|
||||
const state = String(nextState || '').trim();
|
||||
if (!state) return;
|
||||
if (state === connectionState && !text) return;
|
||||
connectionState = state;
|
||||
el.classList.remove('is-connected', 'is-connecting', 'is-disconnected', 'is-updating');
|
||||
el.classList.add(`is-${state}`);
|
||||
|
||||
if (text) {
|
||||
el.textContent = text;
|
||||
return;
|
||||
}
|
||||
if (state === 'connected') {
|
||||
el.textContent = 'Подключено к серверу';
|
||||
return;
|
||||
}
|
||||
if (state === 'disconnected') {
|
||||
el.textContent = 'Нет соединения с сервером';
|
||||
return;
|
||||
}
|
||||
if (state === 'updating') {
|
||||
el.textContent = 'Найдена новая версия, обновляю приложение...';
|
||||
return;
|
||||
}
|
||||
el.textContent = 'Подключение к серверу...';
|
||||
}
|
||||
|
||||
function parseBuildHashFromHtml(html) {
|
||||
const text = String(html || '');
|
||||
const m = text.match(/window\.__SHINE_BUILD_HASH__\s*=\s*'([^']+)'/);
|
||||
return String(m?.[1] || '').trim();
|
||||
}
|
||||
|
||||
async function checkUiVersionAndReload() {
|
||||
if (versionCheckInFlight) return;
|
||||
versionCheckInFlight = true;
|
||||
try {
|
||||
const resp = await fetch(`./index.html?versionCheckTs=${Date.now()}`, { cache: 'no-store' });
|
||||
if (!resp.ok) return;
|
||||
const html = await resp.text();
|
||||
const remoteHash = parseBuildHashFromHtml(html);
|
||||
if (!remoteHash || !CURRENT_BUILD_HASH) return;
|
||||
if (remoteHash === CURRENT_BUILD_HASH) return;
|
||||
|
||||
addAppLogEntry({
|
||||
level: 'info',
|
||||
source: 'version-check',
|
||||
message: `Обнаружена новая версия UI: ${CURRENT_BUILD_HASH} -> ${remoteHash}`,
|
||||
});
|
||||
setConnectionStatus('updating');
|
||||
window.setTimeout(() => {
|
||||
window.location.reload();
|
||||
}, 600);
|
||||
} catch {
|
||||
// ignore transient network/version-check errors
|
||||
} finally {
|
||||
versionCheckInFlight = false;
|
||||
}
|
||||
}
|
||||
|
||||
function startVersionMonitor() {
|
||||
if (versionCheckIntervalId) {
|
||||
window.clearInterval(versionCheckIntervalId);
|
||||
versionCheckIntervalId = null;
|
||||
}
|
||||
void checkUiVersionAndReload();
|
||||
versionCheckIntervalId = window.setInterval(() => {
|
||||
void checkUiVersionAndReload();
|
||||
}, VERSION_CHECK_INTERVAL_MS);
|
||||
}
|
||||
|
||||
async function checkConnectionHealth() {
|
||||
if (connectionState !== 'connected') {
|
||||
setConnectionStatus('connecting');
|
||||
}
|
||||
try {
|
||||
await authService.ws.request('Ping', { ts: Date.now() }, 7000);
|
||||
setConnectionStatus('connected');
|
||||
} catch {
|
||||
setConnectionStatus('disconnected');
|
||||
}
|
||||
}
|
||||
|
||||
function startConnectionMonitor() {
|
||||
if (pingIntervalId) {
|
||||
window.clearInterval(pingIntervalId);
|
||||
pingIntervalId = null;
|
||||
}
|
||||
void checkConnectionHealth();
|
||||
pingIntervalId = window.setInterval(() => {
|
||||
void checkConnectionHealth();
|
||||
}, CONNECTION_CHECK_INTERVAL_MS);
|
||||
}
|
||||
|
||||
function showGlobalErrorAlert(title, details = {}) {
|
||||
const lines = [title];
|
||||
if (details.message) lines.push(`Сообщение: ${details.message}`);
|
||||
@ -293,18 +410,7 @@ async function ensureSessionRuntimeStarted() {
|
||||
onLog: (entry) => addAppLogEntry(entry),
|
||||
});
|
||||
|
||||
if (pingIntervalId) {
|
||||
window.clearInterval(pingIntervalId);
|
||||
pingIntervalId = null;
|
||||
}
|
||||
pingIntervalId = window.setInterval(async () => {
|
||||
if (!state.session.isAuthorized) return;
|
||||
try {
|
||||
await authService.ws.request('Ping', { ts: Date.now() });
|
||||
} catch {
|
||||
// silent keep-alive
|
||||
}
|
||||
}, 60_000);
|
||||
startConnectionMonitor();
|
||||
}
|
||||
|
||||
async function init() {
|
||||
@ -316,10 +422,7 @@ async function init() {
|
||||
|
||||
setSessionResetHandler(() => {
|
||||
sessionRuntimeStarted = false;
|
||||
if (pingIntervalId) {
|
||||
window.clearInterval(pingIntervalId);
|
||||
pingIntervalId = null;
|
||||
}
|
||||
startConnectionMonitor();
|
||||
navigate('start-view');
|
||||
});
|
||||
|
||||
@ -463,6 +566,8 @@ async function init() {
|
||||
|
||||
await tryAutoLogin();
|
||||
await hydrateMessagesFromStore();
|
||||
startVersionMonitor();
|
||||
startConnectionMonitor();
|
||||
await ensureSessionRuntimeStarted();
|
||||
|
||||
if (!window.location.hash) {
|
||||
@ -472,6 +577,11 @@ async function init() {
|
||||
}
|
||||
|
||||
window.addEventListener('hashchange', renderApp);
|
||||
document.addEventListener('visibilitychange', () => {
|
||||
if (document.visibilityState !== 'visible') return;
|
||||
void checkUiVersionAndReload();
|
||||
void checkConnectionHealth();
|
||||
});
|
||||
}
|
||||
|
||||
init();
|
||||
|
||||
@ -41,6 +41,44 @@ body {
|
||||
background: linear-gradient(180deg, rgba(7, 12, 23, 0) 0%, rgba(6, 11, 22, 0.96) 44%);
|
||||
}
|
||||
|
||||
.connection-status-slot {
|
||||
position: absolute;
|
||||
left: 12px;
|
||||
right: 12px;
|
||||
bottom: calc(62px + env(safe-area-inset-bottom));
|
||||
z-index: 5;
|
||||
border-radius: 11px;
|
||||
border: 1px solid rgba(133, 156, 201, 0.3);
|
||||
background: rgba(10, 19, 37, 0.86);
|
||||
color: #c6d6f7;
|
||||
font-size: 12px;
|
||||
line-height: 1.2;
|
||||
text-align: center;
|
||||
padding: 7px 10px;
|
||||
pointer-events: none;
|
||||
backdrop-filter: blur(10px);
|
||||
}
|
||||
|
||||
.connection-status-slot.is-connected {
|
||||
border-color: rgba(124, 235, 171, 0.4);
|
||||
color: #d8ffe9;
|
||||
}
|
||||
|
||||
.connection-status-slot.is-connecting {
|
||||
border-color: rgba(238, 196, 107, 0.42);
|
||||
color: #ffe8bb;
|
||||
}
|
||||
|
||||
.connection-status-slot.is-disconnected {
|
||||
border-color: rgba(228, 127, 145, 0.44);
|
||||
color: #ffdce3;
|
||||
}
|
||||
|
||||
.connection-status-slot.is-updating {
|
||||
border-color: rgba(144, 201, 255, 0.44);
|
||||
color: #d9eeff;
|
||||
}
|
||||
|
||||
@media (min-width: 900px) {
|
||||
.app-shell {
|
||||
margin: 16px 0;
|
||||
|
||||
Loading…
Reference in New Issue
Block a user