From ca66a6995fe78ae569163423fb1effe1a60c009a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?PontualTech=20/=20Karl=C3=A3o?= Date: Wed, 29 Apr 2026 10:39:02 -0300 Subject: [PATCH] fix(boot): try/catch defensivo + migration btDevices + alert crash v1.10.16 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Karlão reportou: APK v1.10.15 crasha no boot mesmo após desinstalar e reinstalar. Significa bug no código, não state corrompido. Fix preventivo: - Boot IIFE wrapped em try/catch master - Cada init (loadState, tracking, anchor, battery, sw, sensors, rt, gcal) agora em try individual — falha de um não derruba o resto - Migration defensiva de state.btDevices: filtra entries inválidas (null, sem id, etc) - Se loadState crashar (state corrupto), reseta localStorage - Crash master no try-catch chama alert() nativo + localStorage.clear() pra recovery automático na próxima abertura Quando Karlão atualizar pra v1.10.16, vai aparecer mensagem específica do erro (se ainda houver) — daí descubro causa exata em vez de adivinhar. Co-Authored-By: Claude Opus 4.7 (1M context) --- app/diario-bordo.html | 51 ++++++++++++++++++++++----------- mobile/android/app/build.gradle | 4 +-- mobile/package.json | 2 +- server/public/index.html | 51 ++++++++++++++++++++++----------- server/public/sw.js | 2 +- server/src/index.js | 2 +- 6 files changed, 73 insertions(+), 39 deletions(-) diff --git a/app/diario-bordo.html b/app/diario-bordo.html index d7d7385..39fe82b 100644 --- a/app/diario-bordo.html +++ b/app/diario-bordo.html @@ -3747,22 +3747,39 @@ async function updateStorageInfo(){ } (async()=>{ - await openDB();loadState();bindHeader();await renderAll(); - document.getElementById('fab').style.display='none'; - loadTrackingState(); - loadAnchorState(); - initBattery(); - initServiceWorker(); - initSensorWidget(); - // Realtime sync: conecta WebSocket se cloud configurada - setSyncStatus(cloudConfigured()?'syncing':'disabled'); - if(cloudConfigured()){rtConnect();refreshGoogleStatus()} - // tenta auto-fetch do tempo após pequeno delay - setTimeout(maybeAutoFetchWeather,3000); - // Welcome screen — só pra usuários sem login - setTimeout(maybeShowWelcome,300); - // Retoma polling do OAuth se app foi morto durante login Google - setTimeout(resumePollingIfPending,500); + // Wrapper try/catch pra capturar crash no boot (Capacitor WebView pode fechar silenciosamente) + try{ + await openDB(); + try{loadState()}catch(e){console.error('[boot] loadState',e);try{localStorage.removeItem(STORAGE_KEY)}catch{}/* corrupt state — reset */} + // Migration defensiva: limpa entries inválidas em state.btDevices + if(state.btDevices&&Array.isArray(state.btDevices)){ + state.btDevices=state.btDevices.filter(d=>d&&typeof d==='object'&&d.id); + }else{ + state.btDevices=[]; + } + bindHeader(); + await renderAll(); + try{document.getElementById('fab').style.display='none'}catch(e){} + try{loadTrackingState()}catch(e){console.error('[boot] tracking',e)} + try{loadAnchorState()}catch(e){console.error('[boot] anchor',e)} + try{initBattery()}catch(e){console.error('[boot] battery',e)} + try{initServiceWorker()}catch(e){console.error('[boot] sw',e)} + try{initSensorWidget()}catch(e){console.error('[boot] sensors',e)} + try{setSyncStatus(cloudConfigured()?'syncing':'disabled')}catch(e){} + if(cloudConfigured()){ + try{rtConnect()}catch(e){console.error('[boot] rt',e)} + try{refreshGoogleStatus()}catch(e){console.error('[boot] gcal',e)} + } + setTimeout(()=>{try{maybeAutoFetchWeather()}catch(e){}},3000); + setTimeout(()=>{try{maybeShowWelcome()}catch(e){}},300); + setTimeout(()=>{try{resumePollingIfPending()}catch(e){}},500); + }catch(e){ + // Crash no boot — mostra alert nativo (sobrevive Capacitor crash) + tenta auto-recovery + const msg='Boot error: '+(e.message||e)+'\n'+(e.stack||'').slice(0,300); + console.error('[BOOT CRASH]',e); + try{alert(msg)}catch{} + try{localStorage.clear()}catch{} + } })(); // Re-tenta init Google Sign-In quando o script async carrega window.addEventListener('load',()=>setTimeout(()=>{if(document.getElementById('welcome-screen').style.display==='flex')initGoogleSignIn()},500)); @@ -6457,7 +6474,7 @@ async function removeBluetoothDevice(id){ renderBluetoothCard(); } -const APP_VERSION='1.10.15'; +const APP_VERSION='1.10.16'; function renderBluetoothCard(){ const el=document.getElementById('bt-list'); const supportEl=document.getElementById('bt-support'); diff --git a/mobile/android/app/build.gradle b/mobile/android/app/build.gradle index 722fa27..c80f021 100644 --- a/mobile/android/app/build.gradle +++ b/mobile/android/app/build.gradle @@ -7,8 +7,8 @@ android { applicationId "br.com.pontualtech.shivao" minSdkVersion rootProject.ext.minSdkVersion targetSdkVersion rootProject.ext.targetSdkVersion - versionCode 28 - versionName "1.10.15" + versionCode 29 + versionName "1.10.16" testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" aaptOptions { // Files and dirs to omit from the packaged assets dir, modified to accommodate modern web apps. diff --git a/mobile/package.json b/mobile/package.json index 551c8e0..0f13f43 100644 --- a/mobile/package.json +++ b/mobile/package.json @@ -1,6 +1,6 @@ { "name": "shivao-mobile", - "version": "1.10.15", + "version": "1.10.16", "description": "Shivao app nativo (Capacitor wrapper Android/iOS)", "main": "index.js", "type": "module", diff --git a/server/public/index.html b/server/public/index.html index d7d7385..39fe82b 100644 --- a/server/public/index.html +++ b/server/public/index.html @@ -3747,22 +3747,39 @@ async function updateStorageInfo(){ } (async()=>{ - await openDB();loadState();bindHeader();await renderAll(); - document.getElementById('fab').style.display='none'; - loadTrackingState(); - loadAnchorState(); - initBattery(); - initServiceWorker(); - initSensorWidget(); - // Realtime sync: conecta WebSocket se cloud configurada - setSyncStatus(cloudConfigured()?'syncing':'disabled'); - if(cloudConfigured()){rtConnect();refreshGoogleStatus()} - // tenta auto-fetch do tempo após pequeno delay - setTimeout(maybeAutoFetchWeather,3000); - // Welcome screen — só pra usuários sem login - setTimeout(maybeShowWelcome,300); - // Retoma polling do OAuth se app foi morto durante login Google - setTimeout(resumePollingIfPending,500); + // Wrapper try/catch pra capturar crash no boot (Capacitor WebView pode fechar silenciosamente) + try{ + await openDB(); + try{loadState()}catch(e){console.error('[boot] loadState',e);try{localStorage.removeItem(STORAGE_KEY)}catch{}/* corrupt state — reset */} + // Migration defensiva: limpa entries inválidas em state.btDevices + if(state.btDevices&&Array.isArray(state.btDevices)){ + state.btDevices=state.btDevices.filter(d=>d&&typeof d==='object'&&d.id); + }else{ + state.btDevices=[]; + } + bindHeader(); + await renderAll(); + try{document.getElementById('fab').style.display='none'}catch(e){} + try{loadTrackingState()}catch(e){console.error('[boot] tracking',e)} + try{loadAnchorState()}catch(e){console.error('[boot] anchor',e)} + try{initBattery()}catch(e){console.error('[boot] battery',e)} + try{initServiceWorker()}catch(e){console.error('[boot] sw',e)} + try{initSensorWidget()}catch(e){console.error('[boot] sensors',e)} + try{setSyncStatus(cloudConfigured()?'syncing':'disabled')}catch(e){} + if(cloudConfigured()){ + try{rtConnect()}catch(e){console.error('[boot] rt',e)} + try{refreshGoogleStatus()}catch(e){console.error('[boot] gcal',e)} + } + setTimeout(()=>{try{maybeAutoFetchWeather()}catch(e){}},3000); + setTimeout(()=>{try{maybeShowWelcome()}catch(e){}},300); + setTimeout(()=>{try{resumePollingIfPending()}catch(e){}},500); + }catch(e){ + // Crash no boot — mostra alert nativo (sobrevive Capacitor crash) + tenta auto-recovery + const msg='Boot error: '+(e.message||e)+'\n'+(e.stack||'').slice(0,300); + console.error('[BOOT CRASH]',e); + try{alert(msg)}catch{} + try{localStorage.clear()}catch{} + } })(); // Re-tenta init Google Sign-In quando o script async carrega window.addEventListener('load',()=>setTimeout(()=>{if(document.getElementById('welcome-screen').style.display==='flex')initGoogleSignIn()},500)); @@ -6457,7 +6474,7 @@ async function removeBluetoothDevice(id){ renderBluetoothCard(); } -const APP_VERSION='1.10.15'; +const APP_VERSION='1.10.16'; function renderBluetoothCard(){ const el=document.getElementById('bt-list'); const supportEl=document.getElementById('bt-support'); diff --git a/server/public/sw.js b/server/public/sw.js index 1ad12bf..0327b41 100644 --- a/server/public/sw.js +++ b/server/public/sw.js @@ -1,7 +1,7 @@ // Shivao Service Worker — offline real // Estratégia: shell precachado, tiles cache-first, windy network-first, /api passa direto. // Versão usada nos cache names — bumpa essa string pra invalidar caches antigos em deploys. -const VERSION = 'shivao-v1.10.15'; +const VERSION = 'shivao-v1.10.16'; const SHELL_CACHE = `shivao-shell-${VERSION}`; const TILES_CACHE = 'shivao-tiles-v1'; // separado pra não invalidar tiles em update do shell const WINDY_CACHE = `shivao-windy-${VERSION}`; diff --git a/server/src/index.js b/server/src/index.js index 1854406..d6423a2 100644 --- a/server/src/index.js +++ b/server/src/index.js @@ -413,7 +413,7 @@ app.get('/.well-known/assetlinks.json', (req, res) => { }); // Atalho: /apk redireciona pra última APK release no Forgejo -const LATEST_APK_URL = 'https://git.pontualtech.work/karlao/shivao-projeto/releases/download/v1.10.15/Shivao-v1.10.15.apk'; +const LATEST_APK_URL = 'https://git.pontualtech.work/karlao/shivao-projeto/releases/download/v1.10.16/Shivao-v1.10.16.apk'; app.get('/apk', (req, res) => res.redirect(302, LATEST_APK_URL)); // Página A4 imprimível com QR Code + instruções (cola no barco/marina)