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:
		
							
								
								
									
										218
									
								
								static/check-pwa-status.html
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										218
									
								
								static/check-pwa-status.html
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,218 @@ | ||||
| <!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> | ||||
		Reference in New Issue
	
	Block a user