SHA256
feat(update): server push-команда на массовое обновление UI и формат версий
This commit is contained in:
+60
-4
@@ -118,6 +118,7 @@ let connectionNextRetryAtMs = 0;
|
||||
let connectionCheckInFlight = false;
|
||||
let wsSessionRestoreInFlight = null;
|
||||
let uiUpdateReloadScheduled = false;
|
||||
let pwaUpdateCheckAttempted = false;
|
||||
|
||||
setClientErrorTransport((payload) => authService.reportClientError(payload));
|
||||
initPwaInstallPromptHandling();
|
||||
@@ -235,20 +236,62 @@ async function triggerImmediateConnectionRetry() {
|
||||
}
|
||||
|
||||
function checkAndReloadIfUiUpdated(remoteHashRaw) {
|
||||
if (uiUpdateReloadScheduled) return;
|
||||
const remoteHash = String(remoteHashRaw || '').trim();
|
||||
if (!remoteHash || !CURRENT_BUILD_HASH || remoteHash === CURRENT_BUILD_HASH) return;
|
||||
|
||||
scheduleUiReload({
|
||||
source: 'version-check',
|
||||
message: `Обнаружена новая версия UI (через Ping): ${CURRENT_BUILD_HASH} -> ${remoteHash}`,
|
||||
delayMs: 600,
|
||||
activateWaitingWorker: true,
|
||||
});
|
||||
}
|
||||
|
||||
async function refreshServiceWorkers({ activateWaitingWorker = false } = {}) {
|
||||
if (!('serviceWorker' in navigator)) return;
|
||||
try {
|
||||
const registrations = await navigator.serviceWorker.getRegistrations();
|
||||
await Promise.all(registrations.map(async (registration) => {
|
||||
try {
|
||||
await registration.update();
|
||||
} catch {}
|
||||
if (activateWaitingWorker && registration.waiting) {
|
||||
try {
|
||||
registration.waiting.postMessage({ type: 'SKIP_WAITING' });
|
||||
} catch {}
|
||||
}
|
||||
}));
|
||||
} catch {
|
||||
// ignore service worker update failures
|
||||
}
|
||||
}
|
||||
|
||||
function scheduleUiReload({
|
||||
source = 'ui-update',
|
||||
message = 'Запрошено обновление интерфейса',
|
||||
delayMs = 700,
|
||||
activateWaitingWorker = true,
|
||||
} = {}) {
|
||||
if (uiUpdateReloadScheduled) return;
|
||||
uiUpdateReloadScheduled = true;
|
||||
addAppLogEntry({
|
||||
level: 'info',
|
||||
source: 'version-check',
|
||||
message: `Обнаружена новая версия UI (через Ping): ${CURRENT_BUILD_HASH} -> ${remoteHash}`,
|
||||
source,
|
||||
message,
|
||||
});
|
||||
setConnectionStatus('updating');
|
||||
void refreshServiceWorkers({ activateWaitingWorker });
|
||||
|
||||
const ms = Math.min(15_000, Math.max(200, Number(delayMs || 700)));
|
||||
window.setTimeout(() => {
|
||||
window.location.reload();
|
||||
}, 600);
|
||||
}, ms);
|
||||
}
|
||||
|
||||
async function tryUpdatePwaOnFirstConnectedPing() {
|
||||
if (pwaUpdateCheckAttempted) return;
|
||||
pwaUpdateCheckAttempted = true;
|
||||
await refreshServiceWorkers({ activateWaitingWorker: false });
|
||||
}
|
||||
|
||||
async function checkConnectionHealth() {
|
||||
@@ -271,6 +314,7 @@ async function checkConnectionHealth() {
|
||||
const pingResp = await authService.ws.request('Ping', { ts: Date.now() }, 7000);
|
||||
const remoteUiBuildHash = pingResp?.payload?.uiBuildHash || pingResp?.uiBuildHash || '';
|
||||
checkAndReloadIfUiUpdated(remoteUiBuildHash);
|
||||
await tryUpdatePwaOnFirstConnectedPing();
|
||||
setConnectionStatus('connected');
|
||||
} catch {
|
||||
connectionStatusText = '';
|
||||
@@ -618,6 +662,18 @@ async function init() {
|
||||
await terminateCurrentSession({ infoMessage: 'Сессия закрыта с другого устройства.' });
|
||||
});
|
||||
|
||||
authService.onEvent('ForceUiReload', async (evt) => {
|
||||
const payload = evt?.payload || {};
|
||||
const reason = String(payload.reason || 'server_debug_api').trim() || 'server_debug_api';
|
||||
const reloadAfterMs = Number(payload.reloadAfterMs || 700);
|
||||
scheduleUiReload({
|
||||
source: 'server-ui-reload',
|
||||
message: `Сервер запросил обновление UI (${reason})`,
|
||||
delayMs: reloadAfterMs,
|
||||
activateWaitingWorker: true,
|
||||
});
|
||||
});
|
||||
|
||||
authService.onEvent('SignedMessageArrived', async (evt) => {
|
||||
const payload = evt?.payload || {};
|
||||
const messageKey = String(payload.messageKey || '').trim();
|
||||
|
||||
@@ -10,6 +10,28 @@ import { initPwaPush } from '../services/pwa-push-service.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 }) {
|
||||
const screen = document.createElement('section');
|
||||
screen.className = 'stack';
|
||||
@@ -129,11 +151,11 @@ export function render({ navigate }) {
|
||||
|
||||
const clientVersion = document.createElement('p');
|
||||
clientVersion.className = 'meta-muted';
|
||||
clientVersion.textContent = `Клиент: ${String(window.__SHINE_CLIENT_VERSION__ || 'n/a').trim() || 'n/a'}`;
|
||||
clientVersion.textContent = `Клиент: ${formatVersionForUi(window.__SHINE_CLIENT_VERSION__)}`;
|
||||
|
||||
const uiBuild = document.createElement('p');
|
||||
uiBuild.className = 'meta-muted';
|
||||
uiBuild.textContent = `Сборка UI: ${String(window.__SHINE_BUILD_HASH__ || 'n/a').trim() || 'n/a'}`;
|
||||
uiBuild.textContent = `Сборка UI: ${formatVersionForUi(window.__SHINE_BUILD_HASH__)}`;
|
||||
|
||||
const serverVersion = document.createElement('p');
|
||||
serverVersion.className = 'meta-muted';
|
||||
@@ -143,11 +165,19 @@ export function render({ navigate }) {
|
||||
|
||||
void (async () => {
|
||||
try {
|
||||
const resp = await authService.ws.request('GetServerInfo', {});
|
||||
const value = String(resp?.payload?.version || '').trim() || 'n/a';
|
||||
if (!isDisposed) {
|
||||
serverVersion.textContent = `Сервер: ${value}`;
|
||||
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 = 'Сервер: недоступно';
|
||||
|
||||
Reference in New Issue
Block a user