- check updates
- risolto problema della generazione dei PDF, avevo modificato in CMyPageElem , se si cambia qualcosa occorre stare attenti a mettere !hideHeader
This commit is contained in:
46
src/components/CCheckUpdatesPWA/CCheckUpdatesPWA.scss
Executable file
46
src/components/CCheckUpdatesPWA/CCheckUpdatesPWA.scss
Executable file
@@ -0,0 +1,46 @@
|
||||
.pwa-updater {
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.pwa-update-badge {
|
||||
position: fixed;
|
||||
bottom: 20px;
|
||||
right: 20px;
|
||||
z-index: 9999;
|
||||
padding: 8px 16px;
|
||||
font-size: 14px;
|
||||
font-weight: 500;
|
||||
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15);
|
||||
animation: pulse 2s infinite;
|
||||
|
||||
&:hover {
|
||||
transform: scale(1.05);
|
||||
box-shadow: 0 6px 16px rgba(0, 0, 0, 0.2);
|
||||
}
|
||||
}
|
||||
|
||||
.pwa-manual-check {
|
||||
position: fixed;
|
||||
bottom: 20px;
|
||||
left: 20px;
|
||||
z-index: 9998;
|
||||
}
|
||||
|
||||
@keyframes pulse {
|
||||
0%, 100% {
|
||||
opacity: 1;
|
||||
}
|
||||
50% {
|
||||
opacity: 0.8;
|
||||
}
|
||||
}
|
||||
|
||||
/* Responsive */
|
||||
@media (max-width: 600px) {
|
||||
.pwa-update-badge {
|
||||
bottom: 80px; // Sposta su per evitare FAB buttons
|
||||
right: 16px;
|
||||
font-size: 12px;
|
||||
padding: 6px 12px;
|
||||
}
|
||||
}
|
||||
401
src/components/CCheckUpdatesPWA/CCheckUpdatesPWA.ts
Executable file
401
src/components/CCheckUpdatesPWA/CCheckUpdatesPWA.ts
Executable file
@@ -0,0 +1,401 @@
|
||||
import { defineComponent, ref, onMounted, onBeforeUnmount } from 'vue';
|
||||
import { useQuasar } from 'quasar';
|
||||
import { tools } from '@tools'
|
||||
import { useGlobalStore } from 'app/src/store';
|
||||
|
||||
export default defineComponent({
|
||||
name: 'CCheckUpdatesPWA',
|
||||
|
||||
props: {
|
||||
// Mostra pulsante per check manuale
|
||||
showManualCheckButton: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
},
|
||||
// Intervallo check automatico (in minuti)
|
||||
autoCheckInterval: {
|
||||
type: Number,
|
||||
default: 60 // 60 minuti
|
||||
},
|
||||
// Mostra notifica successo dopo reload
|
||||
showSuccessNotification: {
|
||||
type: Boolean,
|
||||
default: true
|
||||
}
|
||||
},
|
||||
|
||||
setup(props) {
|
||||
const $q = useQuasar();
|
||||
|
||||
const globalStore = useGlobalStore();
|
||||
|
||||
// State
|
||||
const refreshing = ref(false);
|
||||
const swRegistration = ref<ServiceWorkerRegistration | null>(null);
|
||||
const updateAvailable = ref(false);
|
||||
const dialogVisible = ref(false);
|
||||
let updateCheckInterval: NodeJS.Timeout | null = null;
|
||||
|
||||
/**
|
||||
* Mostra dialog di conferma aggiornamento
|
||||
*/
|
||||
const showUpdateDialog = () => {
|
||||
if (!swRegistration.value) {
|
||||
console.error('❌ No service worker registration available');
|
||||
return;
|
||||
}
|
||||
|
||||
dialogVisible.value = true;
|
||||
|
||||
$q.dialog({
|
||||
title: '🎉 Nuova Versione Disponibile',
|
||||
message: 'È disponibile un aggiornamento dell\'applicazione. Vuoi installarlo ora?',
|
||||
html: true,
|
||||
persistent: false,
|
||||
ok: {
|
||||
label: 'Aggiorna Ora',
|
||||
color: 'primary',
|
||||
icon: 'system_update',
|
||||
noCaps: true
|
||||
},
|
||||
cancel: {
|
||||
label: 'Più Tardi',
|
||||
color: 'grey',
|
||||
flat: true,
|
||||
noCaps: true
|
||||
}
|
||||
}).onOk(() => {
|
||||
applyUpdate();
|
||||
}).onCancel(() => {
|
||||
dialogVisible.value = false;
|
||||
console.log('ℹ️ Aggiornamento rimandato dall\'utente');
|
||||
showPersistentNotification();
|
||||
}).onDismiss(() => {
|
||||
dialogVisible.value = false;
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* Mostra notifica persistente per aggiornamento rimandato
|
||||
*/
|
||||
const showPersistentNotification = () => {
|
||||
$q.notify({
|
||||
message: 'Aggiornamento disponibile',
|
||||
caption: 'Clicca per installare',
|
||||
icon: 'info',
|
||||
color: 'info',
|
||||
position: 'bottom-right',
|
||||
timeout: 0, // Persistente
|
||||
actions: [
|
||||
{
|
||||
label: 'Aggiorna',
|
||||
color: 'white',
|
||||
handler: () => {
|
||||
applyUpdate();
|
||||
}
|
||||
},
|
||||
{
|
||||
icon: 'close',
|
||||
color: 'white',
|
||||
round: true
|
||||
}
|
||||
]
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* Applica l'aggiornamento
|
||||
*/
|
||||
const applyUpdate = () => {
|
||||
if (!swRegistration.value?.waiting) {
|
||||
console.error('❌ No waiting service worker found');
|
||||
$q.notify({
|
||||
message: 'Errore: aggiornamento non disponibile',
|
||||
icon: 'error',
|
||||
color: 'negative',
|
||||
timeout: 3000
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
console.log('✅ Applying update...');
|
||||
dialogVisible.value = false;
|
||||
|
||||
// Mostra loading
|
||||
$q.loading.show({
|
||||
message: 'Aggiornamento in corso...<br/>Non chiudere l\'app',
|
||||
html: true,
|
||||
spinnerColor: 'primary',
|
||||
backgroundColor: 'dark',
|
||||
messageColor: 'white'
|
||||
});
|
||||
|
||||
// Salva stato prima del reload
|
||||
saveStateBeforeUpdate();
|
||||
|
||||
// Invia messaggio al SW per attivare skipWaiting
|
||||
swRegistration.value.waiting.postMessage({
|
||||
action: 'skipWaiting',
|
||||
type: 'SKIP_WAITING'
|
||||
});
|
||||
|
||||
console.log('📤 Skip waiting message sent to service worker');
|
||||
};
|
||||
|
||||
/**
|
||||
* Salva stato dell'app prima dell'aggiornamento
|
||||
*/
|
||||
const saveStateBeforeUpdate = () => {
|
||||
try {
|
||||
sessionStorage.setItem('pwa_updating', 'true');
|
||||
sessionStorage.setItem('pwa_update_timestamp', Date.now().toString());
|
||||
|
||||
// Salva posizione scroll
|
||||
const scrollPos = window.scrollY || document.documentElement.scrollTop;
|
||||
sessionStorage.setItem('pwa_scroll_position', scrollPos.toString());
|
||||
|
||||
// Salva percorso corrente
|
||||
sessionStorage.setItem('pwa_current_path', window.location.pathname);
|
||||
|
||||
console.log('💾 State saved before update');
|
||||
} catch (e) {
|
||||
console.error('Failed to save state:', e);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Ripristina stato dopo l'aggiornamento
|
||||
*/
|
||||
const restoreStateAfterUpdate = () => {
|
||||
try {
|
||||
if (sessionStorage.getItem('pwa_updating') === 'true') {
|
||||
sessionStorage.removeItem('pwa_updating');
|
||||
|
||||
if (props.showSuccessNotification) {
|
||||
$q.notify({
|
||||
message: 'App aggiornata con successo!',
|
||||
caption: 'Stai usando l\'ultima versione',
|
||||
icon: 'check_circle',
|
||||
color: 'positive',
|
||||
timeout: 4000,
|
||||
position: 'top'
|
||||
});
|
||||
}
|
||||
|
||||
// Ripristina scroll position con delay
|
||||
const scrollPos = sessionStorage.getItem('pwa_scroll_position');
|
||||
if (scrollPos) {
|
||||
setTimeout(() => {
|
||||
window.scrollTo({
|
||||
top: parseInt(scrollPos),
|
||||
behavior: 'smooth'
|
||||
});
|
||||
sessionStorage.removeItem('pwa_scroll_position');
|
||||
}, 300);
|
||||
}
|
||||
|
||||
sessionStorage.removeItem('pwa_current_path');
|
||||
sessionStorage.removeItem('pwa_update_timestamp');
|
||||
}
|
||||
} catch (e) {
|
||||
console.error('Failed to restore state:', e);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Handler per evento swUpdated
|
||||
*/
|
||||
const handleSwUpdated = (event: Event) => {
|
||||
const customEvent = event as CustomEvent<ServiceWorkerRegistration>;
|
||||
console.log('🔄 SW Update detected:', customEvent.detail);
|
||||
|
||||
swRegistration.value = customEvent.detail;
|
||||
updateAvailable.value = true;
|
||||
|
||||
// Mostra dialog automaticamente
|
||||
setTimeout(() => {
|
||||
showUpdateDialog();
|
||||
}, 500);
|
||||
};
|
||||
|
||||
/**
|
||||
* Handler per cambio controller (SW attivato)
|
||||
*/
|
||||
const handleControllerChange = () => {
|
||||
if (refreshing.value) {
|
||||
console.log('⏭️ Already refreshing, skipping...');
|
||||
return;
|
||||
}
|
||||
|
||||
console.log('🔄 Service Worker controller changed, reloading...');
|
||||
refreshing.value = true;
|
||||
|
||||
// Nascondi loading
|
||||
$q.loading.hide();
|
||||
|
||||
// Reload con delay per smooth transition
|
||||
setTimeout(() => {
|
||||
window.location.reload();
|
||||
}, 300);
|
||||
};
|
||||
|
||||
/**
|
||||
* Check manuale per aggiornamenti
|
||||
*/
|
||||
const checkForUpdates = async () => {
|
||||
if (!('serviceWorker' in navigator)) {
|
||||
console.warn('⚠️ Service Worker not supported in this browser');
|
||||
$q.notify({
|
||||
message: 'Service Worker non supportato',
|
||||
icon: 'warning',
|
||||
color: 'warning',
|
||||
timeout: 3000
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
console.log('🔍 Checking for updates...');
|
||||
|
||||
$q.loading.show({
|
||||
message: 'Controllo aggiornamenti...',
|
||||
spinnerColor: 'primary'
|
||||
});
|
||||
|
||||
const registration = await navigator.serviceWorker.ready;
|
||||
|
||||
if (!registration) {
|
||||
throw new Error('No service worker registration found');
|
||||
}
|
||||
|
||||
// Forza check aggiornamenti
|
||||
await registration.update();
|
||||
|
||||
$q.loading.hide();
|
||||
|
||||
// Se c'è un SW in waiting, mostra dialog
|
||||
if (registration.waiting) {
|
||||
swRegistration.value = registration;
|
||||
updateAvailable.value = true;
|
||||
showUpdateDialog();
|
||||
} else {
|
||||
$q.notify({
|
||||
message: 'Nessun aggiornamento disponibile',
|
||||
caption: 'Stai usando l\'ultima versione',
|
||||
icon: 'check_circle',
|
||||
color: 'positive',
|
||||
timeout: 3000
|
||||
});
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('❌ Error checking for updates:', error);
|
||||
|
||||
$q.loading.hide();
|
||||
|
||||
$q.notify({
|
||||
message: 'Errore nel controllo aggiornamenti',
|
||||
caption: (error as Error).message || 'Errore sconosciuto',
|
||||
icon: 'error',
|
||||
color: 'negative',
|
||||
timeout: 4000
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Handler per messaggi dal Service Worker
|
||||
*/
|
||||
const handleSwMessage = (event: MessageEvent) => {
|
||||
console.log('📨 Message from Service Worker:', event.data);
|
||||
|
||||
if (event.data?.type === 'SW_UPDATED') {
|
||||
console.log('✅ SW updated to version:', event.data.version);
|
||||
}
|
||||
|
||||
if (event.data?.type === 'SW_INSTALLED') {
|
||||
console.log('✅ SW installed for the first time');
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Setup automatico check aggiornamenti
|
||||
*/
|
||||
const setupAutoUpdateCheck = () => {
|
||||
if (props.autoCheckInterval <= 0) return;
|
||||
|
||||
const intervalMs = props.autoCheckInterval * 60 * 1000;
|
||||
console.log(`⏰ Setting up auto-update check every ${props.autoCheckInterval} minutes`);
|
||||
|
||||
updateCheckInterval = setInterval(() => {
|
||||
console.log('⏰ Periodic update check triggered');
|
||||
checkForUpdates();
|
||||
}, intervalMs);
|
||||
};
|
||||
|
||||
/**
|
||||
* Cleanup
|
||||
*/
|
||||
const cleanup = () => {
|
||||
console.log('🧹 Cleaning up PWA Updater');
|
||||
|
||||
// Rimuovi listeners
|
||||
window.removeEventListener('swUpdated', handleSwUpdated);
|
||||
|
||||
if ('serviceWorker' in navigator) {
|
||||
navigator.serviceWorker.removeEventListener('controllerchange', handleControllerChange);
|
||||
navigator.serviceWorker.removeEventListener('message', handleSwMessage);
|
||||
}
|
||||
|
||||
// Clearinterval
|
||||
if (updateCheckInterval) {
|
||||
clearInterval(updateCheckInterval);
|
||||
updateCheckInterval = null;
|
||||
}
|
||||
};
|
||||
|
||||
// Lifecycle Hooks
|
||||
onMounted(() => {
|
||||
console.log('🚀 PWA Updater component mounted');
|
||||
|
||||
// Listener per evento custom swUpdated
|
||||
window.addEventListener('swUpdated', handleSwUpdated);
|
||||
|
||||
// Setup Service Worker listeners
|
||||
if ('serviceWorker' in navigator) {
|
||||
navigator.serviceWorker.addEventListener('controllerchange', handleControllerChange);
|
||||
navigator.serviceWorker.addEventListener('message', handleSwMessage);
|
||||
|
||||
// Check se c'è già un SW in waiting al mount
|
||||
navigator.serviceWorker.ready.then((registration) => {
|
||||
if (registration.waiting) {
|
||||
console.log('⚠️ Service Worker already waiting at mount');
|
||||
swRegistration.value = registration;
|
||||
updateAvailable.value = true;
|
||||
showUpdateDialog();
|
||||
}
|
||||
});
|
||||
} else {
|
||||
console.warn('⚠️ Service Workers not supported in this browser');
|
||||
}
|
||||
|
||||
// Ripristina stato dopo aggiornamento
|
||||
restoreStateAfterUpdate();
|
||||
|
||||
// Setup check automatico
|
||||
setupAutoUpdateCheck();
|
||||
});
|
||||
|
||||
onBeforeUnmount(() => {
|
||||
cleanup();
|
||||
});
|
||||
|
||||
return {
|
||||
updateAvailable,
|
||||
dialogVisible,
|
||||
checkForUpdates,
|
||||
showUpdateDialog,
|
||||
tools,
|
||||
globalStore,
|
||||
};
|
||||
}
|
||||
});
|
||||
35
src/components/CCheckUpdatesPWA/CCheckUpdatesPWA.vue
Executable file
35
src/components/CCheckUpdatesPWA/CCheckUpdatesPWA.vue
Executable file
@@ -0,0 +1,35 @@
|
||||
|
||||
<template>
|
||||
<div v-if="globalStore.showHeader" class="pwa-updater">
|
||||
<!-- Badge floating per aggiornamento disponibile -->
|
||||
<q-badge
|
||||
v-if="updateAvailable && !dialogVisible"
|
||||
color="primary"
|
||||
floating
|
||||
class="pwa-update-badge cursor-pointer"
|
||||
@click="showUpdateDialog"
|
||||
>
|
||||
<q-icon name="system_update" size="sm" class="q-mr-xs" />
|
||||
Aggiorna
|
||||
</q-badge>
|
||||
|
||||
<!-- Pulsante manuale (opzionale - rimuovi se non serve) -->
|
||||
<q-btn
|
||||
v-if="showManualCheckButton"
|
||||
round
|
||||
dense
|
||||
icon="refresh"
|
||||
color="grey-7"
|
||||
class="pwa-manual-check"
|
||||
@click="checkForUpdates"
|
||||
>
|
||||
<q-tooltip>Controlla Aggiornamenti</q-tooltip>
|
||||
</q-btn>
|
||||
</div>
|
||||
</template>
|
||||
<script lang="ts" src="./CCheckUpdatesPWA.ts">
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
@import './CCheckUpdatesPWA.scss';
|
||||
</style>
|
||||
1
src/components/CCheckUpdatesPWA/index.ts
Executable file
1
src/components/CCheckUpdatesPWA/index.ts
Executable file
@@ -0,0 +1 @@
|
||||
export {default as CCheckUpdatesPWA} from './CCheckUpdatesPWA.vue'
|
||||
@@ -1,5 +1,14 @@
|
||||
import type { PropType } from 'vue';
|
||||
import { computed, defineComponent, onMounted, ref, toRef, watch, nextTick } from 'vue';
|
||||
import {
|
||||
computed,
|
||||
defineComponent,
|
||||
onMounted,
|
||||
ref,
|
||||
toRef,
|
||||
watch,
|
||||
nextTick,
|
||||
onUnmounted,
|
||||
} from 'vue';
|
||||
|
||||
import type { IOptCatalogo, ICoordGPS, IMyElem, ISocial } from '@src/model';
|
||||
import { IMyCard, IMyPage, IOperators } from '@src/model';
|
||||
@@ -195,6 +204,8 @@ export default defineComponent({
|
||||
|
||||
const newtype = ref(<any>'');
|
||||
|
||||
const canShowVersion = ref(false);
|
||||
|
||||
const isAppRunning = computed(() => globalStore.isAppRunning);
|
||||
|
||||
const cardGroupMaxWidth = computed(() => {
|
||||
@@ -307,6 +318,10 @@ export default defineComponent({
|
||||
myel.value = props.myelem;
|
||||
neworder.value = props.myelem.order;
|
||||
|
||||
setTimeout(() => {
|
||||
canShowVersion.value = true;
|
||||
}, 60000);
|
||||
|
||||
if (props.myelem) newtype.value = props.myelem.type;
|
||||
|
||||
nextTick(() => {
|
||||
@@ -394,8 +409,23 @@ export default defineComponent({
|
||||
}
|
||||
}
|
||||
|
||||
const updateApp = async () => {
|
||||
// Invia il messaggio al Service Worker per saltare l'attesa
|
||||
const registration = await navigator.serviceWorker.getRegistration();
|
||||
if (registration?.waiting) {
|
||||
registration.waiting.postMessage({ type: 'SKIP_WAITING' });
|
||||
}
|
||||
|
||||
// Ricarica la pagina
|
||||
window.location.reload();
|
||||
};
|
||||
|
||||
onMounted(mounted);
|
||||
|
||||
const isNewVersionAvailable = computed(() => {
|
||||
return canShowVersion.value ? globalStore.isNewVersionAvailable : false;
|
||||
});
|
||||
|
||||
return {
|
||||
tools,
|
||||
shared_consts,
|
||||
@@ -444,6 +474,7 @@ export default defineComponent({
|
||||
cardGroupMaxWidth,
|
||||
cardScroller,
|
||||
scrollCards,
|
||||
isNewVersionAvailable,
|
||||
};
|
||||
},
|
||||
});
|
||||
|
||||
@@ -196,7 +196,7 @@
|
||||
>
|
||||
<CTitle
|
||||
:imgbackground="myel.imgback"
|
||||
:headtitle="myel.title"
|
||||
:headtitle="myel.container"
|
||||
:sizes="myel.size"
|
||||
:styleadd="myel.styleadd"
|
||||
>
|
||||
@@ -1086,7 +1086,7 @@
|
||||
Controllo Nuova Versione
|
||||
</div>
|
||||
<q-banner
|
||||
v-if="globalStore.isNewVersionAvailable"
|
||||
v-if="isNewVersionAvailable"
|
||||
rounded
|
||||
dense
|
||||
class="bg-green text-white"
|
||||
@@ -1107,7 +1107,7 @@
|
||||
</div>
|
||||
<div v-else>
|
||||
<span class="mybanner"
|
||||
>Aggiornamento APP in corso ... Se dopo 1 minuto non dovesse scomparire
|
||||
>* Aggiornamento APP in corso ... Se dopo 1 minuto non dovesse scomparire
|
||||
questo messaggio, chiudere e riaprire la pagina.</span
|
||||
>
|
||||
</div>
|
||||
|
||||
@@ -590,6 +590,7 @@ export default defineComponent({
|
||||
openAdd,
|
||||
addAtEnd,
|
||||
showOrder,
|
||||
globalStore,
|
||||
};
|
||||
},
|
||||
});
|
||||
|
||||
@@ -65,42 +65,44 @@
|
||||
|
||||
<!-- Contenuto pagina -->
|
||||
<div
|
||||
:class="[{ 'q-gutter-xs': !hideHeader }, 'q-mx-auto', 'q-pb-lg']"
|
||||
:style="containerStyle"
|
||||
:class="[{ 'q-gutter-xs': !hideHeader, 'q-mx-auto': !hideHeader, 'q-pb-lg': !hideHeader}]"
|
||||
:style="hideHeader ? [{ 'margin-left': 0, 'margin-right': 0 }] : containerStyle"
|
||||
>
|
||||
<!-- Media/Content blocks (1..3) -->
|
||||
<section
|
||||
v-for="(blk, i) in mediaBlocks"
|
||||
:key="`mblk-${i}`"
|
||||
class="q-mb-md"
|
||||
>
|
||||
<div
|
||||
v-if="blk.img"
|
||||
class="text-center q-mb-sm"
|
||||
>
|
||||
<q-img
|
||||
:src="blk.img"
|
||||
class="page-img"
|
||||
:ratio="16 / 9"
|
||||
loading="lazy"
|
||||
/>
|
||||
</div>
|
||||
<div
|
||||
v-if="blk.html"
|
||||
v-html="blk.html"
|
||||
class="q-mb-sm content-html"
|
||||
></div>
|
||||
<q-video
|
||||
v-if="blk.video"
|
||||
:src="blk.video"
|
||||
:ratio="blk.ratio || 16 / 9"
|
||||
<div v-if="globalStore.showHeader">
|
||||
<!-- Media/Content blocks (1..3) -->
|
||||
<section
|
||||
v-for="(blk, i) in mediaBlocks"
|
||||
:key="`mblk-${i}`"
|
||||
class="q-mb-md"
|
||||
/>
|
||||
</section>
|
||||
>
|
||||
<div
|
||||
v-if="blk.img"
|
||||
class="text-center q-mb-sm"
|
||||
>
|
||||
<q-img
|
||||
:src="blk.img"
|
||||
class="page-img"
|
||||
:ratio="16 / 9"
|
||||
loading="lazy"
|
||||
/>
|
||||
</div>
|
||||
<div
|
||||
v-if="blk.html"
|
||||
v-html="blk.html"
|
||||
class="q-mb-sm content-html"
|
||||
></div>
|
||||
<q-video
|
||||
v-if="blk.video"
|
||||
:src="blk.video"
|
||||
:ratio="blk.ratio || 16 / 9"
|
||||
class="q-mb-md"
|
||||
/>
|
||||
</section>
|
||||
</div>
|
||||
|
||||
<!-- Contenuti extra HTML -->
|
||||
<div
|
||||
v-if="rec.content4"
|
||||
v-if="rec.content4 && globalStore.showHeader"
|
||||
v-html="rec.content4"
|
||||
class="q-mb-md content-html"
|
||||
></div>
|
||||
@@ -109,7 +111,6 @@
|
||||
<div
|
||||
v-for="myelem in myelems"
|
||||
:key="myelem._id"
|
||||
class="q-mb-lg"
|
||||
>
|
||||
<transition
|
||||
appear
|
||||
|
||||
@@ -23,7 +23,7 @@
|
||||
:strfido="t('account.fido_casa', { fido: account ? account.fidoConcesso : '', symbol })"
|
||||
:color="color"
|
||||
v-model="saldo"
|
||||
:before_str="t('account.saldo') + `:`"
|
||||
:before_str="$q.screen.lt.sm ? '' : t('account.saldo') + `:`"
|
||||
:valueextra="
|
||||
account && account.notifspending && account.notifspending.length > 0
|
||||
? `* `
|
||||
|
||||
@@ -85,6 +85,8 @@ export default defineComponent({
|
||||
const products = useProducts();
|
||||
const notifStore = useNotifStore();
|
||||
|
||||
const canShowVersion = ref(false);
|
||||
|
||||
const { getnumItemsCart } = MixinUsers();
|
||||
|
||||
const site = computed(() => globalStore.site);
|
||||
@@ -116,7 +118,8 @@ export default defineComponent({
|
||||
});
|
||||
|
||||
const getColorText = computed(() => {
|
||||
if (globalStore.site && globalStore.site.confpages?.col_toolbar === 'white') return 'black';
|
||||
if (globalStore.site && globalStore.site.confpages?.col_toolbar === 'white')
|
||||
return 'black';
|
||||
else return 'white';
|
||||
});
|
||||
|
||||
@@ -158,11 +161,46 @@ export default defineComponent({
|
||||
|
||||
const stateconn = ref(globalStore.stateConnection);
|
||||
|
||||
function refreshApp() {
|
||||
// ✅ 1. Se siamo su iOS: NON toccare il Service Worker!
|
||||
if (Platform.is.ios) {
|
||||
// Su iOS, Safari non risponde bene a unregister() o skipWaiting()
|
||||
// L'unico modo affidabile è: forzare un reload con cache busting
|
||||
console.log('[PWA] iOS: Forzo reload senza toccare il Service Worker...');
|
||||
|
||||
// Aggiungi un parametro casuale per bypassare la cache del browser
|
||||
const url = new URL(window.location.href);
|
||||
url.searchParams.set('refresh', Date.now().toString());
|
||||
window.location.href = url.toString();
|
||||
|
||||
return; // ✅ Esce subito — non esegue altro
|
||||
}
|
||||
|
||||
// ✅ 2. Se siamo su Android / Chrome / Desktop: usa SKIP_WAITING
|
||||
if (data.value.registration && data.value.registration.waiting) {
|
||||
console.log('[PWA] Invio SKIP_WAITING al Service Worker...');
|
||||
data.value.registration.waiting.postMessage({ type: 'SKIP_WAITING' });
|
||||
|
||||
// ✅ Aspetta che il nuovo SW diventi attivo prima di ricaricare
|
||||
navigator.serviceWorker.addEventListener('controllerchange', () => {
|
||||
console.log('[PWA] Nuovo Service Worker attivo — ricarico la pagina...');
|
||||
window.location.reload();
|
||||
});
|
||||
|
||||
// ✅ Imposta lo stato come risolto
|
||||
data.value.updateExists = false;
|
||||
} else {
|
||||
// Se non c'è un SW in attesa, forza un semplice reload (caso raro)
|
||||
console.log('[PWA] Nessun SW in attesa — ricarico comunque...');
|
||||
window.location.reload();
|
||||
}
|
||||
}
|
||||
|
||||
function updateAvailable(event: any) {
|
||||
console.log(event);
|
||||
data.value.registration = event.detail;
|
||||
data.value.updateExists = true;
|
||||
RefreshApp(); // update automatically
|
||||
refreshApp(); // update automatically
|
||||
}
|
||||
|
||||
function created() {
|
||||
@@ -173,16 +211,19 @@ export default defineComponent({
|
||||
try {
|
||||
if (window) {
|
||||
// Ascolta evento custom 'swUpdated' per notifica aggiornamento
|
||||
/*
|
||||
window.addEventListener(
|
||||
'swUpdated',
|
||||
async (event) => {
|
||||
async (event: any) => {
|
||||
// Chiedi conferma all’utente (qui con confirm, sostituisci con dialog Quasar se vuoi)
|
||||
const doUpdate = confirm('È disponibile una nuova versione. Vuoi aggiornare ora?');
|
||||
const doUpdate = confirm(
|
||||
'È disponibile una nuova versione. Vuoi aggiornare ora?'
|
||||
);
|
||||
|
||||
if (doUpdate) {
|
||||
// Invia messaggio al service worker per skipWaiting
|
||||
if (event.detail?.swWaiting) {
|
||||
event.detail.swWaiting.postMessage({ action: 'skipWaiting' });
|
||||
if (event.detail?.waiting) {
|
||||
event.detail.waiting.postMessage({ action: 'skipWaiting' });
|
||||
}
|
||||
}
|
||||
},
|
||||
@@ -196,9 +237,10 @@ export default defineComponent({
|
||||
window.location.reload();
|
||||
});
|
||||
}
|
||||
*/
|
||||
}
|
||||
} catch (e) {
|
||||
console.error('Err', e.message);
|
||||
console.error('Error in created() function:', e.message ? e.message : e);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -281,11 +323,15 @@ export default defineComponent({
|
||||
});
|
||||
}
|
||||
|
||||
function isNewVersionAvailable() {
|
||||
return globalStore.isNewVersionAvailable;
|
||||
}
|
||||
const isNewVersionAvailable = computed(() => {
|
||||
return canShowVersion.value ? globalStore.isNewVersionAvailable : false;
|
||||
});
|
||||
|
||||
function closeAll() {
|
||||
/*if (timeoutId) {
|
||||
window.clearTimeout(timeoutId);
|
||||
timeoutId = null;
|
||||
}*/
|
||||
globalStore.rightNotifOpen = false;
|
||||
globalStore.rightCartOpen = false;
|
||||
globalStore.rightDrawerOpen = false;
|
||||
@@ -363,10 +409,14 @@ export default defineComponent({
|
||||
);
|
||||
|
||||
watch(conndata_changed, (value, oldValue) => {
|
||||
clCloudUpload.value = value.uploading_server === 1 ? 'clCloudUpload send' : 'clCloudUpload';
|
||||
clCloudUpload.value = value.downloading_server === 1 ? 'clCloudUpload receive' : 'clCloudUpload';
|
||||
clCloudUp_Indexeddb.value = value.uploading_indexeddb === 1 ? 'clIndexeddb send' : 'clIndexeddb';
|
||||
clCloudUp_Indexeddb.value = value.downloading_indexeddb === 1 ? 'clIndexeddb receive' : 'clIndexeddb';
|
||||
clCloudUpload.value =
|
||||
value.uploading_server === 1 ? 'clCloudUpload send' : 'clCloudUpload';
|
||||
clCloudUpload.value =
|
||||
value.downloading_server === 1 ? 'clCloudUpload receive' : 'clCloudUpload';
|
||||
clCloudUp_Indexeddb.value =
|
||||
value.uploading_indexeddb === 1 ? 'clIndexeddb send' : 'clIndexeddb';
|
||||
clCloudUp_Indexeddb.value =
|
||||
value.downloading_indexeddb === 1 ? 'clIndexeddb receive' : 'clIndexeddb';
|
||||
|
||||
/* clCloudUpload.value = (value.uploading_server === -1) ? 'clCloudUpload error' : clCloudUpload
|
||||
clCloudUpload.value = (value.downloading_server === -1) ? 'clCloudUpload error' : clCloudDownload
|
||||
@@ -402,30 +452,10 @@ export default defineComponent({
|
||||
|
||||
*/
|
||||
|
||||
function RefreshApp() {
|
||||
if (Platform.is.ios) {
|
||||
// Unregister Service Worker
|
||||
if ('serviceWorker' in navigator) {
|
||||
navigator.serviceWorker.getRegistrations().then((registrations) => {
|
||||
for (const registration of registrations) {
|
||||
registration.unregister();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// window.location.reload()
|
||||
} else {
|
||||
data.value.updateExists = false;
|
||||
// Make sure we only send a 'skip waiting' message if the SW is waiting
|
||||
if (!data.value.registration || !data.value.registration.waiting) return;
|
||||
// Send message to SW to skip the waiting and activate the new SW
|
||||
data.value.registration.waiting.postMessage({ type: 'SKIP_WAITING' });
|
||||
}
|
||||
}
|
||||
|
||||
function changeIconConn() {
|
||||
iconConn.value = globalStore.stateConnection === 'online' ? 'wifi' : 'wifi_off';
|
||||
clIconConn.value = globalStore.stateConnection === 'online' ? 'clIconOnline' : 'clIconOffline';
|
||||
clIconConn.value =
|
||||
globalStore.stateConnection === 'online' ? 'clIconOnline' : 'clIconOffline';
|
||||
}
|
||||
|
||||
function getAppVersion() {
|
||||
@@ -475,6 +505,10 @@ export default defineComponent({
|
||||
// Test this by running the code snippet below and then
|
||||
// use the "TableOnlyView" checkbox in DevTools Network panel
|
||||
|
||||
setTimeout(() => {
|
||||
canShowVersion.value = true;
|
||||
}, 60000);
|
||||
|
||||
// console.log('Event LOAD')
|
||||
if (window) {
|
||||
window.addEventListener('load', () => {
|
||||
@@ -560,11 +594,6 @@ export default defineComponent({
|
||||
return 0;
|
||||
}
|
||||
|
||||
function getcart() {
|
||||
// return Products.cart
|
||||
return null;
|
||||
}
|
||||
|
||||
function toHome() {
|
||||
$router.push('/');
|
||||
}
|
||||
@@ -581,7 +610,6 @@ export default defineComponent({
|
||||
tools.setCookie('menu3oriz', globalStore.leftDrawerOpen ? '1' : '0');
|
||||
}
|
||||
|
||||
|
||||
onBeforeMount(BeforeMount);
|
||||
onMounted(mounted);
|
||||
|
||||
@@ -608,13 +636,12 @@ export default defineComponent({
|
||||
getcolormenu,
|
||||
isNewVersionAvailable,
|
||||
getAppVersion,
|
||||
RefreshApp,
|
||||
refreshApp,
|
||||
changecmd,
|
||||
imglogo,
|
||||
getappname,
|
||||
toggleanimation,
|
||||
getClassColorHeader,
|
||||
getcart,
|
||||
getnumItemsCart,
|
||||
isFacilitatore,
|
||||
isZoomeri,
|
||||
@@ -654,6 +681,7 @@ export default defineComponent({
|
||||
clickMenu3Orizz,
|
||||
isEditor,
|
||||
editOn,
|
||||
canShowVersion,
|
||||
};
|
||||
},
|
||||
});
|
||||
|
||||
@@ -38,12 +38,12 @@
|
||||
<q-btn
|
||||
size="md"
|
||||
id="newvers"
|
||||
v-if="isNewVersionAvailable() || data.updateExists"
|
||||
v-if="isNewVersionAvailable || data.updateExists"
|
||||
color="secondary"
|
||||
rounded
|
||||
icon="refresh"
|
||||
class="btnNewVersShow"
|
||||
@click="RefreshApp()"
|
||||
@click="refreshApp()"
|
||||
:label="t('notification.newVersionAvailable')"
|
||||
>
|
||||
</q-btn>
|
||||
|
||||
Reference in New Issue
Block a user