- 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.
121 lines
5.1 KiB
HTML
121 lines
5.1 KiB
HTML
<!DOCTYPE html>
|
|
<html>
|
|
<head>
|
|
<title>PWA Validation - Talk2Me</title>
|
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
<style>
|
|
body { font-family: Arial, sans-serif; padding: 20px; }
|
|
.status { padding: 10px; margin: 10px 0; border-radius: 5px; }
|
|
.success { background: #d4edda; color: #155724; }
|
|
.error { background: #f8d7da; color: #721c24; }
|
|
.info { background: #d1ecf1; color: #0c5460; }
|
|
img { max-width: 100px; height: auto; margin: 10px; }
|
|
</style>
|
|
</head>
|
|
<body>
|
|
<h1>Talk2Me PWA Validation</h1>
|
|
|
|
<h2>Manifest Check</h2>
|
|
<div id="manifest-status"></div>
|
|
|
|
<h2>Icon Check</h2>
|
|
<div id="icon-status"></div>
|
|
|
|
<h2>Service Worker Check</h2>
|
|
<div id="sw-status"></div>
|
|
|
|
<h2>Installation Test</h2>
|
|
<button id="install-btn" style="display:none; padding: 10px 20px; font-size: 16px;">Install Talk2Me</button>
|
|
<div id="install-status"></div>
|
|
|
|
<script>
|
|
// Check manifest
|
|
fetch('/static/manifest.json')
|
|
.then(res => res.json())
|
|
.then(manifest => {
|
|
const status = document.getElementById('manifest-status');
|
|
status.innerHTML = `
|
|
<div class="status success">✓ Manifest loaded successfully</div>
|
|
<div class="status info">Name: ${manifest.name}</div>
|
|
<div class="status info">Short Name: ${manifest.short_name}</div>
|
|
<div class="status info">Icons: ${manifest.icons.length} defined</div>
|
|
`;
|
|
|
|
// Check icons
|
|
const iconStatus = document.getElementById('icon-status');
|
|
manifest.icons.forEach(icon => {
|
|
const img = new Image();
|
|
img.onload = () => {
|
|
const div = document.createElement('div');
|
|
div.className = 'status success';
|
|
div.innerHTML = `✓ ${icon.src} (${icon.sizes}) - ${icon.purpose}`;
|
|
iconStatus.appendChild(div);
|
|
iconStatus.appendChild(img);
|
|
};
|
|
img.onerror = () => {
|
|
const div = document.createElement('div');
|
|
div.className = 'status error';
|
|
div.innerHTML = `✗ ${icon.src} (${icon.sizes}) - Failed to load`;
|
|
iconStatus.appendChild(div);
|
|
};
|
|
img.src = icon.src;
|
|
img.style.maxWidth = '50px';
|
|
});
|
|
})
|
|
.catch(err => {
|
|
document.getElementById('manifest-status').innerHTML =
|
|
`<div class="status error">✗ Failed to load manifest: ${err.message}</div>`;
|
|
});
|
|
|
|
// Check service worker
|
|
if ('serviceWorker' in navigator) {
|
|
navigator.serviceWorker.getRegistration()
|
|
.then(reg => {
|
|
const status = document.getElementById('sw-status');
|
|
if (reg) {
|
|
status.innerHTML = `
|
|
<div class="status success">✓ Service Worker is registered</div>
|
|
<div class="status info">Scope: ${reg.scope}</div>
|
|
<div class="status info">State: ${reg.active ? 'Active' : 'Not Active'}</div>
|
|
`;
|
|
} else {
|
|
status.innerHTML = '<div class="status error">✗ Service Worker not registered</div>';
|
|
}
|
|
})
|
|
.catch(err => {
|
|
document.getElementById('sw-status').innerHTML =
|
|
`<div class="status error">✗ Service Worker error: ${err.message}</div>`;
|
|
});
|
|
} else {
|
|
document.getElementById('sw-status').innerHTML =
|
|
'<div class="status error">✗ Service Workers not supported</div>';
|
|
}
|
|
|
|
// Check install prompt
|
|
let deferredPrompt;
|
|
window.addEventListener('beforeinstallprompt', (e) => {
|
|
e.preventDefault();
|
|
deferredPrompt = e;
|
|
document.getElementById('install-btn').style.display = 'block';
|
|
document.getElementById('install-status').innerHTML =
|
|
'<div class="status success">✓ App is installable</div>';
|
|
});
|
|
|
|
document.getElementById('install-btn').addEventListener('click', async () => {
|
|
if (deferredPrompt) {
|
|
deferredPrompt.prompt();
|
|
const { outcome } = await deferredPrompt.userChoice;
|
|
document.getElementById('install-status').innerHTML +=
|
|
`<div class="status info">User ${outcome} the install</div>`;
|
|
deferredPrompt = null;
|
|
}
|
|
});
|
|
|
|
// Check if already installed
|
|
if (window.matchMedia('(display-mode: standalone)').matches) {
|
|
document.getElementById('install-status').innerHTML =
|
|
'<div class="status success">✓ App is already installed</div>';
|
|
}
|
|
</script>
|
|
</body>
|
|
</html> |