Major PWA and mobile UI improvements
- 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.
This commit is contained in:
121
validate-pwa.html
Normal file
121
validate-pwa.html
Normal file
@@ -0,0 +1,121 @@
|
||||
<!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>
|
Reference in New Issue
Block a user