166 lines
6.5 KiB
JavaScript
166 lines
6.5 KiB
JavaScript
import { renderHeader } from '../components/header.js';
|
||
import { addAppLogEntry, authService, closeCurrentSessionAndSignOut, state } from '../state.js';
|
||
import {
|
||
canInstallPwa,
|
||
isStandalonePwaMode,
|
||
onPwaInstallAvailabilityChange,
|
||
promptPwaInstall,
|
||
} from '../services/pwa-install-service.js';
|
||
import { initPwaPush } from '../services/pwa-push-service.js';
|
||
|
||
export const pageMeta = { id: 'settings-view', title: 'Настройки' };
|
||
|
||
export function render({ navigate }) {
|
||
const screen = document.createElement('section');
|
||
screen.className = 'stack';
|
||
let isDisposed = false;
|
||
|
||
screen.append(
|
||
renderHeader({
|
||
title: 'Настройки',
|
||
leftAction: { label: '←', onClick: () => navigate('profile-view') },
|
||
}),
|
||
);
|
||
|
||
const card = document.createElement('div');
|
||
card.className = 'card stack';
|
||
card.innerHTML = `
|
||
<button class="text-btn" type="button" id="settings-device">Устройства</button>
|
||
<button class="text-btn" type="button" id="settings-servers">Настройки серверов</button>
|
||
<button class="text-btn" type="button" id="settings-language">Язык / Language</button>
|
||
<button class="text-btn" type="button" id="settings-app-log">Лог приложения</button>
|
||
<button class="text-btn" type="button" id="settings-pwa-diagnostics">Диагностика PWA / Push</button>
|
||
<button class="text-btn" type="button" id="settings-signout">Завершить текущий сеанс</button>
|
||
<button class="text-btn" type="button" id="settings-pwa-install">Зарегистрировать PWA</button>
|
||
`;
|
||
|
||
card.querySelector('#settings-device').addEventListener('click', () => navigate('device-view'));
|
||
card.querySelector('#settings-servers').addEventListener('click', () => navigate('server-settings-view'));
|
||
card.querySelector('#settings-language').addEventListener('click', () => navigate('language-view'));
|
||
card.querySelector('#settings-app-log').addEventListener('click', () => navigate('app-log-view'));
|
||
card.querySelector('#settings-pwa-diagnostics').addEventListener('click', () => navigate('pwa-diagnostics-view'));
|
||
|
||
const signOutBtn = card.querySelector('#settings-signout');
|
||
const pwaInstallBtn = card.querySelector('#settings-pwa-install');
|
||
|
||
const syncPwaButtonLabel = () => {
|
||
if (isStandalonePwaMode()) {
|
||
pwaInstallBtn.textContent = 'PWA установлено (проверить WebPush)';
|
||
return;
|
||
}
|
||
if (canInstallPwa()) {
|
||
pwaInstallBtn.textContent = 'Зарегистрировать PWA';
|
||
return;
|
||
}
|
||
pwaInstallBtn.textContent = 'Как установить PWA';
|
||
};
|
||
|
||
const unsubscribeInstallAvailability = onPwaInstallAvailabilityChange(() => {
|
||
syncPwaButtonLabel();
|
||
});
|
||
syncPwaButtonLabel();
|
||
|
||
signOutBtn.addEventListener('click', async () => {
|
||
const confirmed = window.confirm(
|
||
'Завершить текущую сессию на сервере, отключиться, очистить локальные данные и перейти на стартовый экран?'
|
||
);
|
||
if (!confirmed) return;
|
||
|
||
signOutBtn.disabled = true;
|
||
try {
|
||
addAppLogEntry({
|
||
level: 'info',
|
||
source: 'session',
|
||
message: `Запрошено завершение текущей сессии: ${state.session.sessionId || 'unknown'}`,
|
||
});
|
||
await closeCurrentSessionAndSignOut({
|
||
infoMessage: 'Сеанс завершён. Выполните вход заново.',
|
||
});
|
||
} finally {
|
||
signOutBtn.disabled = false;
|
||
}
|
||
});
|
||
|
||
pwaInstallBtn.addEventListener('click', async () => {
|
||
pwaInstallBtn.disabled = true;
|
||
try {
|
||
await initPwaPush({
|
||
authService,
|
||
onLog: (entry) => addAppLogEntry(entry),
|
||
});
|
||
|
||
if (canInstallPwa()) {
|
||
const result = await promptPwaInstall();
|
||
const accepted = result.outcome === 'accepted';
|
||
addAppLogEntry({
|
||
level: 'info',
|
||
source: 'pwa-install',
|
||
message: accepted ? 'Пользователь принял установку PWA' : 'Пользователь отклонил установку PWA',
|
||
details: { outcome: result.outcome || 'unknown' },
|
||
});
|
||
if (accepted) {
|
||
window.alert('Установка PWA подтверждена. Проверьте приложение на главном экране устройства.');
|
||
}
|
||
} else if (!isStandalonePwaMode()) {
|
||
window.alert('Для установки откройте меню браузера и выберите "Установить приложение" или "Добавить на главный экран".');
|
||
} else {
|
||
window.alert('PWA уже установлено. WebPush перерегистрирован.');
|
||
}
|
||
} catch (error) {
|
||
addAppLogEntry({
|
||
level: 'warn',
|
||
source: 'pwa-install',
|
||
message: 'Не удалось зарегистрировать PWA/WebPush',
|
||
details: { error: error?.message || 'unknown' },
|
||
});
|
||
window.alert(`Ошибка регистрации PWA: ${error?.message || 'unknown'}`);
|
||
} finally {
|
||
pwaInstallBtn.disabled = false;
|
||
syncPwaButtonLabel();
|
||
}
|
||
});
|
||
|
||
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 = `Клиент: ${String(window.__SHINE_CLIENT_VERSION__ || 'n/a').trim() || 'n/a'}`;
|
||
|
||
const uiBuild = document.createElement('p');
|
||
uiBuild.className = 'meta-muted';
|
||
uiBuild.textContent = `Сборка UI: ${String(window.__SHINE_BUILD_HASH__ || 'n/a').trim() || 'n/a'}`;
|
||
|
||
const serverVersion = document.createElement('p');
|
||
serverVersion.className = 'meta-muted';
|
||
serverVersion.textContent = 'Сервер: загружается...';
|
||
|
||
versionCard.append(title, clientVersion, uiBuild, serverVersion);
|
||
|
||
void (async () => {
|
||
try {
|
||
const resp = await authService.ws.request('GetServerInfo', {});
|
||
const value = String(resp?.payload?.version || '').trim() || 'n/a';
|
||
if (!isDisposed) {
|
||
serverVersion.textContent = `Сервер: ${value}`;
|
||
}
|
||
} catch {
|
||
if (!isDisposed) {
|
||
serverVersion.textContent = 'Сервер: недоступно';
|
||
}
|
||
}
|
||
})();
|
||
|
||
screen.append(card);
|
||
screen.cleanup = () => {
|
||
isDisposed = true;
|
||
unsubscribeInstallAvailability();
|
||
};
|
||
screen.append(versionCard);
|
||
return screen;
|
||
}
|