NoteJS/public/mobile-pwa-test.html
Fovway 95401328c4 Улучшена поддержка PWA и мобильных устройств
- Обновлены инструкции по тестированию PWA для мобильных и десктопных устройств.
- Добавлены новые мета-теги и улучшены иконки для поддержки iOS и Windows.
- Оптимизирован Service Worker для кэширования и обработки ошибок.
- Реализована кнопка установки, отображающаяся только на мобильных устройствах, с различными инструкциями для разных браузеров.
- Обновлен manifest.json с добавлением категорий и скриншотов.
2025-10-20 09:35:32 +07:00

416 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 на мобильных устройствах" />
<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>
.test-container {
max-width: 600px;
margin: 0 auto;
padding: 20px;
font-family: Arial, sans-serif;
}
.test-section {
background: #f8f9fa;
border: 1px solid #dee2e6;
border-radius: 8px;
padding: 20px;
margin: 20px 0;
}
.test-item {
display: flex;
justify-content: space-between;
align-items: center;
padding: 10px 0;
border-bottom: 1px solid #e9ecef;
}
.test-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;
}
.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;
}
.instructions {
background: #e7f3ff;
border: 1px solid #b3d9ff;
border-radius: 6px;
padding: 15px;
margin: 15px 0;
}
.instructions h4 {
margin-top: 0;
color: #0066cc;
}
.instructions ol {
margin: 10px 0;
padding-left: 20px;
}
.instructions li {
margin: 5px 0;
}
.debug-info {
background: #f8f9fa;
border: 1px solid #dee2e6;
border-radius: 4px;
padding: 10px;
font-family: monospace;
font-size: 12px;
white-space: pre-wrap;
max-height: 200px;
overflow-y: auto;
}
</style>
</head>
<body>
<div class="test-container">
<h1>📱 Тест PWA для мобильных устройств</h1>
<div class="test-section">
<h3>Проверка требований PWA</h3>
<div id="pwa-checks">
<div class="test-item">
<span>HTTPS или localhost</span>
<span class="status" id="https-check">Проверка...</span>
</div>
<div class="test-item">
<span>Service Worker</span>
<span class="status" id="sw-check">Проверка...</span>
</div>
<div class="test-item">
<span>Manifest</span>
<span class="status" id="manifest-check">Проверка...</span>
</div>
<div class="test-item">
<span>Иконки</span>
<span class="status" id="icons-check">Проверка...</span>
</div>
<div class="test-item">
<span>Уже установлено</span>
<span class="status" id="installed-check">Проверка...</span>
</div>
</div>
</div>
<div class="test-section">
<h3>Установка приложения</h3>
<div id="device-info" style="margin-bottom: 10px; padding: 10px; background: #e7f3ff; border-radius: 4px; font-size: 14px;"></div>
<button id="install-button" class="install-button" disabled>
Установить приложение
</button>
<div id="install-status"></div>
</div>
<div class="test-section">
<h3>Инструкции по установке</h3>
<div class="instructions">
<h4>Android Chrome/Edge:</h4>
<ol>
<li>Откройте сайт в Chrome или Edge</li>
<li>Нажмите кнопку "Установить приложение" выше</li>
<li>Или нажмите меню (⋮) → "Установить приложение"</li>
<li>Следуйте инструкциям браузера</li>
</ol>
</div>
<div class="instructions">
<h4>iOS Safari:</h4>
<ol>
<li>Откройте сайт в Safari</li>
<li>Нажмите кнопку "Поделиться" (□↗)</li>
<li>Выберите "На экран Домой"</li>
<li>Нажмите "Добавить"</li>
</ol>
</div>
<div class="instructions">
<h4>Другие браузеры:</h4>
<ol>
<li>Найдите опцию "Установить" в меню браузера</li>
<li>Или используйте "Добавить на главный экран"</li>
</ol>
</div>
</div>
<div class="test-section">
<h3>Отладочная информация</h3>
<button onclick="showDebugInfo()" class="install-button">Показать отладочную информацию</button>
<div id="debug-info" class="debug-info" style="display: none;"></div>
</div>
<div class="test-section">
<h3>Действия</h3>
<button onclick="clearCache()" class="install-button">Очистить кэш</button>
<button onclick="forceUpdate()" class="install-button">Принудительное обновление</button>
<button onclick="location.reload()" class="install-button">Перезагрузить страницу</button>
</div>
</div>
<script>
let deferredPrompt;
// Проверка требований PWA
function checkPWARequirements() {
// 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'}`;
// Уже установлено
const isInstalled = window.matchMedia('(display-mode: standalone)').matches ||
window.navigator.standalone === true;
document.getElementById('installed-check').textContent = isInstalled ? '✅ Да' : '❌ Нет';
document.getElementById('installed-check').className = `status ${isInstalled ? 'warning' : 'pass'}`;
}
// Регистрация 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;
// Проверяем, является ли устройство мобильным
const isMobile = /Android|webOS|iPhone|iPad|iPod|BlackBerry|IEMobile|Opera Mini/i.test(navigator.userAgent) ||
(navigator.maxTouchPoints && navigator.maxTouchPoints > 2) ||
window.matchMedia('(max-width: 768px)').matches;
const installButton = document.getElementById('install-button');
if (isMobile) {
installButton.disabled = false;
installButton.textContent = '📱 Установить приложение';
} else {
installButton.disabled = true;
installButton.textContent = '💻 Установка доступна через меню браузера';
}
});
// Обработка успешной установки
window.addEventListener('appinstalled', () => {
console.log('PWA установлено успешно');
document.getElementById('install-status').innerHTML =
'<div style="color: green; margin-top: 10px;">✅ Приложение успешно установлено!</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 style="color: green; margin-top: 10px;">✅ Пользователь принял установку</div>';
} else {
document.getElementById('install-status').innerHTML =
'<div style="color: orange; margin-top: 10px;">⚠️ Пользователь отклонил установку</div>';
}
deferredPrompt = null;
});
} else {
document.getElementById('install-status').innerHTML =
'<div style="color: red; margin-top: 10px;">❌ Установка недоступна. Попробуйте использовать меню браузера.</div>';
}
});
// Показать отладочную информацию
function showDebugInfo() {
const debugInfo = document.getElementById('debug-info');
const info = {
userAgent: navigator.userAgent,
platform: navigator.platform,
language: navigator.language,
onLine: navigator.onLine,
cookieEnabled: navigator.cookieEnabled,
displayMode: window.matchMedia('(display-mode: standalone)').matches ? 'standalone' : 'browser',
standalone: window.navigator.standalone,
hasServiceWorker: 'serviceWorker' in navigator,
hasDeferredPrompt: deferredPrompt !== null,
location: {
href: location.href,
protocol: location.protocol,
hostname: location.hostname,
port: location.port
}
};
debugInfo.textContent = JSON.stringify(info, null, 2);
debugInfo.style.display = debugInfo.style.display === 'none' ? 'block' : 'none';
}
// Очистка кэша
async function clearCache() {
if ('serviceWorker' in navigator && navigator.serviceWorker.controller) {
try {
navigator.serviceWorker.controller.postMessage({
type: 'CLEAR_ALL_CACHE'
});
alert('Запрос на очистку кэша отправлен');
} catch (error) {
alert('Ошибка при очистке кэша: ' + error.message);
}
} else {
alert('Service Worker не доступен');
}
}
// Принудительное обновление
async function forceUpdate() {
if ('serviceWorker' in navigator) {
try {
const registration = await navigator.serviceWorker.getRegistration();
if (registration) {
await registration.update();
alert('Проверка обновлений завершена');
}
} catch (error) {
alert('Ошибка при проверке обновлений: ' + error.message);
}
} else {
alert('Service Worker не поддерживается');
}
}
// Проверка типа устройства
function checkDeviceType() {
const isMobile = /Android|webOS|iPhone|iPad|iPod|BlackBerry|IEMobile|Opera Mini/i.test(navigator.userAgent) ||
(navigator.maxTouchPoints && navigator.maxTouchPoints > 2) ||
window.matchMedia('(max-width: 768px)').matches;
const deviceInfo = document.getElementById('device-info');
if (isMobile) {
deviceInfo.innerHTML = '📱 <strong>Мобильное устройство</strong> - кнопка установки доступна';
deviceInfo.style.background = '#d4edda';
deviceInfo.style.color = '#155724';
} else {
deviceInfo.innerHTML = '💻 <strong>ПК/Десктоп</strong> - кнопка установки скрыта (PWA доступно через меню браузера)';
deviceInfo.style.background = '#fff3cd';
deviceInfo.style.color = '#856404';
}
}
// Инициализация
document.addEventListener('DOMContentLoaded', () => {
checkDeviceType();
checkPWARequirements();
registerServiceWorker();
});
</script>
</body>
</html>