- Fixed PWA installation on Android by correcting manifest.json icon configuration - Made UI mobile-friendly with compact layout and sticky record button - Implemented auto-translation after transcription stops - Updated branding from 'Voice Translator' to 'Talk2Me' throughout - Added reverse proxy support with ProxyFix middleware - Created diagnostic tools for PWA troubleshooting - Added proper HTTP headers for service worker and manifest - Improved mobile CSS with responsive design - Fixed JavaScript bundling with webpack configuration - Updated service worker cache versioning - Added comprehensive PWA documentation These changes ensure the app works properly as a PWA on Android devices and provides a better mobile user experience.
218 lines
8.0 KiB
HTML
218 lines
8.0 KiB
HTML
<!DOCTYPE html>
|
|
<html lang="en">
|
|
<head>
|
|
<meta charset="UTF-8">
|
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
<title>PWA Status Check - Talk2Me</title>
|
|
<style>
|
|
body {
|
|
font-family: Arial, sans-serif;
|
|
padding: 20px;
|
|
max-width: 600px;
|
|
margin: 0 auto;
|
|
}
|
|
.status {
|
|
padding: 10px;
|
|
margin: 10px 0;
|
|
border-radius: 5px;
|
|
}
|
|
.success {
|
|
background: #d4edda;
|
|
color: #155724;
|
|
}
|
|
.error {
|
|
background: #f8d7da;
|
|
color: #721c24;
|
|
}
|
|
.warning {
|
|
background: #fff3cd;
|
|
color: #856404;
|
|
}
|
|
.info {
|
|
background: #d1ecf1;
|
|
color: #0c5460;
|
|
}
|
|
button {
|
|
padding: 10px 20px;
|
|
margin: 10px 0;
|
|
font-size: 16px;
|
|
cursor: pointer;
|
|
}
|
|
pre {
|
|
background: #f5f5f5;
|
|
padding: 10px;
|
|
overflow-x: auto;
|
|
}
|
|
</style>
|
|
</head>
|
|
<body>
|
|
<h1>Talk2Me PWA Status Check</h1>
|
|
|
|
<div id="results"></div>
|
|
|
|
<h2>Actions</h2>
|
|
<button onclick="testInstall()">Test Install Prompt</button>
|
|
<button onclick="clearPWA()">Clear PWA Data</button>
|
|
<button onclick="location.reload()">Refresh Page</button>
|
|
|
|
<script>
|
|
const results = document.getElementById('results');
|
|
|
|
function addResult(message, type = 'info') {
|
|
const div = document.createElement('div');
|
|
div.className = `status ${type}`;
|
|
div.textContent = message;
|
|
results.appendChild(div);
|
|
}
|
|
|
|
// Check HTTPS
|
|
if (location.protocol === 'https:' || location.hostname === 'localhost') {
|
|
addResult('✓ HTTPS enabled', 'success');
|
|
} else {
|
|
addResult('✗ HTTPS required for PWA', 'error');
|
|
}
|
|
|
|
// Check Service Worker support
|
|
if ('serviceWorker' in navigator) {
|
|
addResult('✓ Service Worker supported', 'success');
|
|
|
|
// Check registration
|
|
navigator.serviceWorker.getRegistration().then(reg => {
|
|
if (reg) {
|
|
addResult(`✓ Service Worker registered (scope: ${reg.scope})`, 'success');
|
|
if (reg.active) {
|
|
addResult('✓ Service Worker is active', 'success');
|
|
} else if (reg.installing) {
|
|
addResult('⚠ Service Worker is installing', 'warning');
|
|
} else if (reg.waiting) {
|
|
addResult('⚠ Service Worker is waiting', 'warning');
|
|
}
|
|
} else {
|
|
addResult('✗ Service Worker not registered', 'error');
|
|
}
|
|
});
|
|
} else {
|
|
addResult('✗ Service Worker not supported', 'error');
|
|
}
|
|
|
|
// Check manifest
|
|
const manifestLink = document.querySelector('link[rel="manifest"]');
|
|
if (manifestLink) {
|
|
addResult('✓ Manifest link found', 'success');
|
|
|
|
fetch(manifestLink.href)
|
|
.then(r => r.json())
|
|
.then(manifest => {
|
|
addResult('✓ Manifest loaded successfully', 'success');
|
|
|
|
// Check required fields
|
|
const required = ['name', 'short_name', 'start_url', 'display', 'icons'];
|
|
required.forEach(field => {
|
|
if (manifest[field]) {
|
|
addResult(`✓ Manifest has ${field}`, 'success');
|
|
} else {
|
|
addResult(`✗ Manifest missing ${field}`, 'error');
|
|
}
|
|
});
|
|
|
|
// Check icons
|
|
if (manifest.icons && manifest.icons.length > 0) {
|
|
const has192 = manifest.icons.some(i => i.sizes && i.sizes.includes('192'));
|
|
const has512 = manifest.icons.some(i => i.sizes && i.sizes.includes('512'));
|
|
|
|
if (has192) addResult('✓ Has 192x192 icon', 'success');
|
|
else addResult('✗ Missing 192x192 icon', 'error');
|
|
|
|
if (has512) addResult('✓ Has 512x512 icon', 'success');
|
|
else addResult('⚠ Missing 512x512 icon (recommended)', 'warning');
|
|
|
|
// Check icon purposes
|
|
manifest.icons.forEach((icon, i) => {
|
|
if (icon.purpose) {
|
|
addResult(`Icon ${i+1}: purpose="${icon.purpose}"`, 'info');
|
|
}
|
|
});
|
|
}
|
|
|
|
// Show manifest content
|
|
const pre = document.createElement('pre');
|
|
pre.textContent = JSON.stringify(manifest, null, 2);
|
|
results.appendChild(pre);
|
|
})
|
|
.catch(err => {
|
|
addResult(`✗ Failed to load manifest: ${err}`, 'error');
|
|
});
|
|
} else {
|
|
addResult('✗ No manifest link found', 'error');
|
|
}
|
|
|
|
// Check if already installed
|
|
if (window.matchMedia('(display-mode: standalone)').matches) {
|
|
addResult('✓ App is running in standalone mode (already installed)', 'success');
|
|
} else {
|
|
addResult('App is running in browser mode', 'info');
|
|
}
|
|
|
|
// Listen for install prompt
|
|
let deferredPrompt;
|
|
window.addEventListener('beforeinstallprompt', (e) => {
|
|
e.preventDefault();
|
|
deferredPrompt = e;
|
|
addResult('✓ Install prompt is available!', 'success');
|
|
addResult('Chrome recognizes this as an installable PWA', 'success');
|
|
});
|
|
|
|
// Check Chrome version
|
|
const userAgent = navigator.userAgent;
|
|
const chromeMatch = userAgent.match(/Chrome\/(\d+)/);
|
|
if (chromeMatch) {
|
|
const version = parseInt(chromeMatch[1]);
|
|
addResult(`Chrome version: ${version}`, 'info');
|
|
if (version < 90) {
|
|
addResult('⚠ Chrome version is old, consider updating', 'warning');
|
|
}
|
|
}
|
|
|
|
// Test install
|
|
function testInstall() {
|
|
if (deferredPrompt) {
|
|
deferredPrompt.prompt();
|
|
deferredPrompt.userChoice.then((choiceResult) => {
|
|
if (choiceResult.outcome === 'accepted') {
|
|
addResult('✓ User accepted the install prompt', 'success');
|
|
} else {
|
|
addResult('User dismissed the install prompt', 'warning');
|
|
}
|
|
deferredPrompt = null;
|
|
});
|
|
} else {
|
|
addResult('No install prompt available. Chrome may not recognize this as installable.', 'error');
|
|
addResult('Try: Menu (⋮) → Add to Home screen', 'info');
|
|
}
|
|
}
|
|
|
|
// Clear PWA data
|
|
function clearPWA() {
|
|
if ('serviceWorker' in navigator) {
|
|
navigator.serviceWorker.getRegistrations().then(function(registrations) {
|
|
for(let registration of registrations) {
|
|
registration.unregister();
|
|
}
|
|
addResult('Service Workers unregistered', 'info');
|
|
});
|
|
}
|
|
|
|
if ('caches' in window) {
|
|
caches.keys().then(function(names) {
|
|
for (let name of names) {
|
|
caches.delete(name);
|
|
}
|
|
addResult('Caches cleared', 'info');
|
|
});
|
|
}
|
|
|
|
addResult('PWA data cleared. Reload the page to re-register.', 'info');
|
|
}
|
|
</script>
|
|
</body>
|
|
</html> |