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)