NoteJS/public/pwa-debug.html
Fovway d6dc1d76a0 Обновлены инструкции и функциональность PWA
- Изменена структура инструкций по тестированию PWA, добавлена диагностическая страница для отладки.
- Обновлен manifest.json с добавлением поля "id".
- Реализована задержка при отображении кнопки установки для улучшения пользовательского опыта.
- Добавлены функции для проверки возможности установки PWA и отображения инструкций для мобильных устройств.
2025-10-20 09:42:36 +07:00

412 lines
18 KiB
HTML
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<!DOCTYPE html>
<html lang="ru">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Диагностика PWA - NoteJS</title>
<!-- PWA Meta Tags -->
<meta name="description" content="Диагностика PWA для NoteJS" />
<meta name="theme-color" content="#007bff" />
<meta name="mobile-web-app-capable" content="yes" />
<meta name="apple-mobile-web-app-capable" content="yes" />
<meta name="apple-mobile-web-app-status-bar-style" content="black-translucent" />
<meta name="apple-mobile-web-app-title" content="NoteJS" />
<meta name="apple-touch-fullscreen" content="yes" />
<meta name="msapplication-TileColor" content="#007bff" />
<meta name="msapplication-config" content="/browserconfig.xml" />
<meta name="msapplication-TileImage" content="/icons/icon-144x144.png" />
<meta name="application-name" content="NoteJS" />
<meta name="format-detection" content="telephone=no" />
<!-- Icons -->
<link rel="icon" type="image/svg+xml" href="/icon.svg" />
<link rel="icon" type="image/png" sizes="32x32" href="/icons/icon-32x32.png" />
<link rel="icon" type="image/png" sizes="16x16" href="/icons/icon-16x16.png" />
<link rel="apple-touch-icon" sizes="57x57" href="/icons/icon-48x48.png" />
<link rel="apple-touch-icon" sizes="60x60" href="/icons/icon-48x48.png" />
<link rel="apple-touch-icon" sizes="72x72" href="/icons/icon-72x72.png" />
<link rel="apple-touch-icon" sizes="76x76" href="/icons/icon-72x72.png" />
<link rel="apple-touch-icon" sizes="114x114" href="/icons/icon-128x128.png" />
<link rel="apple-touch-icon" sizes="120x120" href="/icons/icon-128x128.png" />
<link rel="apple-touch-icon" sizes="144x144" href="/icons/icon-144x144.png" />
<link rel="apple-touch-icon" sizes="152x152" href="/icons/icon-152x152.png" />
<link rel="apple-touch-icon" sizes="180x180" href="/icons/icon-192x192.png" />
<link rel="mask-icon" href="/icon.svg" color="#007bff" />
<!-- Manifest -->
<link rel="manifest" href="/manifest.json" />
<!-- Styles -->
<link rel="stylesheet" href="/style.css" />
<style>
.debug-container {
max-width: 800px;
margin: 0 auto;
padding: 20px;
font-family: Arial, sans-serif;
}
.debug-section {
background: #f8f9fa;
border: 1px solid #dee2e6;
border-radius: 8px;
padding: 20px;
margin: 20px 0;
}
.debug-item {
display: flex;
justify-content: space-between;
align-items: center;
padding: 10px 0;
border-bottom: 1px solid #e9ecef;
}
.debug-item:last-child {
border-bottom: none;
}
.status {
padding: 4px 8px;
border-radius: 4px;
font-size: 12px;
font-weight: bold;
}
.status.pass {
background: #d4edda;
color: #155724;
}
.status.fail {
background: #f8d7da;
color: #721c24;
}
.status.warning {
background: #fff3cd;
color: #856404;
}
.debug-code {
background: #f8f9fa;
border: 1px solid #dee2e6;
border-radius: 4px;
padding: 10px;
font-family: monospace;
font-size: 12px;
white-space: pre-wrap;
max-height: 300px;
overflow-y: auto;
margin: 10px 0;
}
.install-button {
background: #007bff;
color: white;
border: none;
padding: 12px 24px;
border-radius: 6px;
font-size: 16px;
cursor: pointer;
width: 100%;
margin: 10px 0;
}
.install-button:hover {
background: #0056b3;
}
.install-button:disabled {
background: #6c757d;
cursor: not-allowed;
}
.error-message {
background: #f8d7da;
color: #721c24;
padding: 10px;
border-radius: 4px;
margin: 10px 0;
}
.success-message {
background: #d4edda;
color: #155724;
padding: 10px;
border-radius: 4px;
margin: 10px 0;
}
</style>
</head>
<body>
<div class="debug-container">
<h1>🔍 Диагностика PWA</h1>
<div class="debug-section">
<h3>Основные требования PWA</h3>
<div id="basic-checks">
<div class="debug-item">
<span>HTTPS или localhost</span>
<span class="status" id="https-check">Проверка...</span>
</div>
<div class="debug-item">
<span>Service Worker</span>
<span class="status" id="sw-check">Проверка...</span>
</div>
<div class="debug-item">
<span>Manifest</span>
<span class="status" id="manifest-check">Проверка...</span>
</div>
<div class="debug-item">
<span>Иконки</span>
<span class="status" id="icons-check">Проверка...</span>
</div>
</div>
</div>
<div class="debug-section">
<h3>Детальная диагностика</h3>
<div id="detailed-checks">
<div class="debug-item">
<span>Manifest загружается</span>
<span class="status" id="manifest-load-check">Проверка...</span>
</div>
<div class="debug-item">
<span>Service Worker регистрируется</span>
<span class="status" id="sw-register-check">Проверка...</span>
</div>
<div class="debug-item">
<span>Иконки доступны</span>
<span class="status" id="icons-available-check">Проверка...</span>
</div>
<div class="debug-item">
<span>beforeinstallprompt событие</span>
<span class="status" id="install-prompt-check">Проверка...</span>
</div>
</div>
</div>
<div class="debug-section">
<h3>Установка приложения</h3>
<button id="install-button" class="install-button" disabled>
Установить приложение
</button>
<div id="install-status"></div>
</div>
<div class="debug-section">
<h3>Информация о манифесте</h3>
<div id="manifest-info" class="debug-code">Загрузка...</div>
</div>
<div class="debug-section">
<h3>Информация о Service Worker</h3>
<div id="sw-info" class="debug-code">Загрузка...</div>
</div>
<div class="debug-section">
<h3>Информация о браузере</h3>
<div id="browser-info" class="debug-code">Загрузка...</div>
</div>
<div class="debug-section">
<h3>Ошибки консоли</h3>
<div id="console-errors" class="debug-code">Нет ошибок</div>
</div>
</div>
<script>
let deferredPrompt;
let consoleErrors = [];
// Перехватываем ошибки консоли
const originalError = console.error;
console.error = function(...args) {
consoleErrors.push(args.join(' '));
originalError.apply(console, args);
};
// Проверка основных требований PWA
function checkBasicRequirements() {
// HTTPS или localhost
const isSecure = location.protocol === 'https:' || location.hostname === 'localhost';
document.getElementById('https-check').textContent = isSecure ? '✅ Да' : '❌ Нет';
document.getElementById('https-check').className = `status ${isSecure ? 'pass' : 'fail'}`;
// Service Worker
const hasSW = 'serviceWorker' in navigator;
document.getElementById('sw-check').textContent = hasSW ? '✅ Да' : '❌ Нет';
document.getElementById('sw-check').className = `status ${hasSW ? 'pass' : 'fail'}`;
// Manifest
const hasManifest = document.querySelector('link[rel="manifest"]') !== null;
document.getElementById('manifest-check').textContent = hasManifest ? '✅ Да' : '❌ Нет';
document.getElementById('manifest-check').className = `status ${hasManifest ? 'pass' : 'fail'}`;
// Иконки
const hasIcons = document.querySelector('link[rel="icon"]') !== null;
document.getElementById('icons-check').textContent = hasIcons ? '✅ Да' : '❌ Нет';
document.getElementById('icons-check').className = `status ${hasIcons ? 'pass' : 'fail'}`;
}
// Детальная диагностика
async function detailedDiagnostics() {
// Проверка загрузки манифеста
try {
const manifestResponse = await fetch('/manifest.json');
const manifest = await manifestResponse.json();
document.getElementById('manifest-load-check').textContent = '✅ Загружен';
document.getElementById('manifest-load-check').className = 'status pass';
// Отображаем информацию о манифесте
document.getElementById('manifest-info').textContent = JSON.stringify(manifest, null, 2);
} catch (error) {
document.getElementById('manifest-load-check').textContent = '❌ Ошибка загрузки';
document.getElementById('manifest-load-check').className = 'status fail';
document.getElementById('manifest-info').textContent = `Ошибка: ${error.message}`;
}
// Проверка регистрации Service Worker
if ('serviceWorker' in navigator) {
try {
const registration = await navigator.serviceWorker.getRegistration();
if (registration) {
document.getElementById('sw-register-check').textContent = '✅ Зарегистрирован';
document.getElementById('sw-register-check').className = 'status pass';
// Отображаем информацию о Service Worker
document.getElementById('sw-info').textContent = JSON.stringify({
scope: registration.scope,
state: registration.active ? registration.active.state : 'не активен',
scriptURL: registration.active ? registration.active.scriptURL : 'не доступен'
}, null, 2);
} else {
document.getElementById('sw-register-check').textContent = '❌ Не зарегистрирован';
document.getElementById('sw-register-check').className = 'status fail';
}
} catch (error) {
document.getElementById('sw-register-check').textContent = '❌ Ошибка';
document.getElementById('sw-register-check').className = 'status fail';
}
}
// Проверка доступности иконок
const iconSizes = ['192x192', '512x512'];
let iconsAvailable = 0;
for (const size of iconSizes) {
try {
const response = await fetch(`/icons/icon-${size}.png`);
if (response.ok) {
iconsAvailable++;
}
} catch (error) {
console.error(`Ошибка загрузки иконки ${size}:`, error);
}
}
if (iconsAvailable === iconSizes.length) {
document.getElementById('icons-available-check').textContent = '✅ Все доступны';
document.getElementById('icons-available-check').className = 'status pass';
} else {
document.getElementById('icons-available-check').textContent = `⚠️ ${iconsAvailable}/${iconSizes.length} доступны`;
document.getElementById('icons-available-check').className = 'status warning';
}
// Информация о браузере
document.getElementById('browser-info').textContent = JSON.stringify({
userAgent: navigator.userAgent,
platform: navigator.platform,
language: navigator.language,
onLine: navigator.onLine,
cookieEnabled: navigator.cookieEnabled,
maxTouchPoints: navigator.maxTouchPoints,
displayMode: window.matchMedia('(display-mode: standalone)').matches ? 'standalone' : 'browser',
standalone: window.navigator.standalone
}, null, 2);
}
// Регистрация Service Worker
function registerServiceWorker() {
if ('serviceWorker' in navigator) {
navigator.serviceWorker.register('/sw.js')
.then((registration) => {
console.log('SW зарегистрирован успешно:', registration.scope);
})
.catch((error) => {
console.log('Ошибка регистрации SW:', error);
});
}
}
// Обработка установки
window.addEventListener('beforeinstallprompt', (e) => {
console.log('beforeinstallprompt событие получено');
e.preventDefault();
deferredPrompt = e;
document.getElementById('install-prompt-check').textContent = '✅ Получено';
document.getElementById('install-prompt-check').className = 'status pass';
const installButton = document.getElementById('install-button');
installButton.disabled = false;
installButton.textContent = '📱 Установить приложение';
});
// Обработка успешной установки
window.addEventListener('appinstalled', () => {
console.log('PWA установлено успешно');
document.getElementById('install-status').innerHTML =
'<div class="success-message">✅ Приложение успешно установлено!</div>';
const installButton = document.getElementById('install-button');
installButton.disabled = true;
installButton.textContent = 'Приложение установлено';
});
// Установка приложения
document.getElementById('install-button').addEventListener('click', () => {
if (deferredPrompt) {
deferredPrompt.prompt();
deferredPrompt.userChoice.then((choiceResult) => {
console.log('Результат установки:', choiceResult.outcome);
if (choiceResult.outcome === 'accepted') {
document.getElementById('install-status').innerHTML =
'<div class="success-message">✅ Пользователь принял установку</div>';
} else {
document.getElementById('install-status').innerHTML =
'<div class="error-message">⚠️ Пользователь отклонил установку</div>';
}
deferredPrompt = null;
});
} else {
document.getElementById('install-status').innerHTML =
'<div class="error-message">❌ Установка недоступна. Попробуйте использовать меню браузера.</div>';
}
});
// Обновление ошибок консоли
function updateConsoleErrors() {
if (consoleErrors.length > 0) {
document.getElementById('console-errors').textContent = consoleErrors.join('\n');
} else {
document.getElementById('console-errors').textContent = 'Нет ошибок';
}
}
// Инициализация
document.addEventListener('DOMContentLoaded', () => {
checkBasicRequirements();
detailedDiagnostics();
registerServiceWorker();
// Обновляем ошибки каждые 2 секунды
setInterval(updateConsoleErrors, 2000);
});
</script>
</body>
</html>