diff --git a/package-lock.json b/package-lock.json index 54066a48..75729d15 100644 --- a/package-lock.json +++ b/package-lock.json @@ -17,6 +17,7 @@ "luxon": "^3.3.0", "pinia": "2.0.36", "pinia-plugin-persistedstate": "^3.1.0", + "register-service-worker": "^1.7.2", "vue": "^3.3.4", "vue-barcode-reader": "^1.0.3", "vue-i18n": "^9.2.2" @@ -4055,6 +4056,11 @@ } ] }, + "node_modules/register-service-worker": { + "version": "1.7.2", + "resolved": "https://registry.npmjs.org/register-service-worker/-/register-service-worker-1.7.2.tgz", + "integrity": "sha512-CiD3ZSanZqcMPRhtfct5K9f7i3OLCcBBWsJjLh1gW9RO/nS94sVzY59iS+fgYBOBqaBpf4EzfqUF3j9IG+xo8A==" + }, "node_modules/require-directory": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", @@ -7829,6 +7835,11 @@ "integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==", "dev": true }, + "register-service-worker": { + "version": "1.7.2", + "resolved": "https://registry.npmjs.org/register-service-worker/-/register-service-worker-1.7.2.tgz", + "integrity": "sha512-CiD3ZSanZqcMPRhtfct5K9f7i3OLCcBBWsJjLh1gW9RO/nS94sVzY59iS+fgYBOBqaBpf4EzfqUF3j9IG+xo8A==" + }, "require-directory": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", diff --git a/package.json b/package.json index ea5d30f2..bfde7fa9 100644 --- a/package.json +++ b/package.json @@ -32,6 +32,7 @@ "luxon": "^3.3.0", "pinia": "2.0.36", "pinia-plugin-persistedstate": "^3.1.0", + "register-service-worker": "^1.7.2", "vue": "^3.3.4", "vue-barcode-reader": "^1.0.3", "vue-i18n": "^9.2.2" diff --git a/src/components/AppVersionInfo.vue b/src/components/AppVersionInfo.vue deleted file mode 100644 index 5aa4793a..00000000 --- a/src/components/AppVersionInfo.vue +++ /dev/null @@ -1,19 +0,0 @@ - - - \ No newline at end of file diff --git a/src/components/DxpAppVersionInfo.vue b/src/components/DxpAppVersionInfo.vue new file mode 100644 index 00000000..4e32c00d --- /dev/null +++ b/src/components/DxpAppVersionInfo.vue @@ -0,0 +1,34 @@ + + + \ No newline at end of file diff --git a/src/components/index.ts b/src/components/index.ts index 4d9bdf0f..eb8d6781 100644 --- a/src/components/index.ts +++ b/src/components/index.ts @@ -16,7 +16,7 @@ import '@ionic/vue/css/display.css'; export { default as DxpImage } from './DxpImage.vue'; export { default as DxpUserProfile } from './DxpUserProfile.vue' -export { default as AppVersionInfo } from './AppVersionInfo.vue'; +export { default as DxpAppVersionInfo } from './DxpAppVersionInfo.vue'; export { default as LanguageSwitcher } from './LanguageSwitcher.vue'; export { default as DxpMenuFooterNavigation } from './DxpMenuFooterNavigation.vue'; export { default as DxpLogin } from './DxpLogin.vue'; diff --git a/src/index.ts b/src/index.ts index bebfe966..2de5a192 100644 --- a/src/index.ts +++ b/src/index.ts @@ -3,13 +3,15 @@ declare var process: any; import { createPinia } from "pinia"; import { useProductIdentificationStore } from "./store/productIdentification"; import { useAuthStore } from "./store/auth"; -import { AppVersionInfo, DxpImage, DxpLogin, DxpUserProfile, LanguageSwitcher, DxpMenuFooterNavigation, OmsInstanceNavigator, ProductIdentifier, Scanner, ShopifyImg } from "./components"; +import { DxpAppVersionInfo, DxpImage, DxpLogin, DxpUserProfile, LanguageSwitcher, DxpMenuFooterNavigation, OmsInstanceNavigator, ProductIdentifier, Scanner, ShopifyImg } from "./components"; import { goToOms, getProductIdentificationValue } from "./utils"; import { initialiseFirebaseApp } from "./utils/firebase" import piniaPluginPersistedstate from 'pinia-plugin-persistedstate' import { createI18n } from 'vue-i18n' import { useUserStore } from "./store/user"; +import "./service-worker" + // TODO: handle cases when the store from app or pinia store are not available // creating a pinia store for the plugin const pinia = createPinia(); @@ -24,6 +26,23 @@ let appContext = {} as any let productIdentificationContext = {} as any let notificationContext = {} as any let userContext = {} as any +let showToast = {} as any + +let refreshing = false; + +const updateAvailable = ($event: any) => { + const registration = $event.detail; + const updateExists = true; + appContext.config.globalProperties.$store.dispatch('user/updatePwaState', { registration, updateExists }); + showToast(translate("New version available, please update the app.")); +} + +document.addEventListener('swUpdated', updateAvailable, { once: true }) +navigator.serviceWorker.addEventListener('controllerchange', () => { + if (refreshing) return + refreshing = true + window.location.reload() +}) // executed on app initialization export let dxpComponents = { @@ -42,7 +61,7 @@ export let dxpComponents = { app.use(pinia); app.use(i18n); - app.component('AppVersionInfo', AppVersionInfo) + app.component('DxpAppVersionInfo', DxpAppVersionInfo) app.component('DxpImage', DxpImage) app.component('DxpUserProfile', DxpUserProfile) app.component('LanguageSwitcher', LanguageSwitcher) @@ -53,6 +72,8 @@ export let dxpComponents = { app.component('Scanner', Scanner) app.component('ShopifyImg', ShopifyImg) + showToast = options.showToast + loginContext.login = options.login loginContext.logout = options.logout loginContext.loader = options.loader diff --git a/src/service-worker.ts b/src/service-worker.ts new file mode 100644 index 00000000..2d5d50cf --- /dev/null +++ b/src/service-worker.ts @@ -0,0 +1,36 @@ +import { register } from 'register-service-worker' + +declare var process: any; + +if (process.env.NODE_ENV === 'production') { + register(`${process.env.BASE_URL}service-worker.js`, { + ready () { + console.log( + 'App is being served from cache by a service worker.\n' + + 'For more details, visit https://goo.gl/AFskqB' + ) + }, + registered (registration: any) { + console.log('Service worker has been registered.') + setInterval(() => { + registration.update(); + }, 1000 * 60 * 60); // check for service worker update hourly + }, + cached () { + console.log('Content has been cached for offline use.') + }, + updatefound () { + console.log('New content is downloading.') + }, + updated (registration: any) { + console.log('New content is available; please refresh.') + document.dispatchEvent(new CustomEvent('swUpdated', { detail: registration })) + }, + offline () { + console.log('No internet connection found. App is running in offline mode.') + }, + error (error: any) { + console.error('Error during service worker registration:', error) + } + }) +} diff --git a/src/utils/index.ts b/src/utils/index.ts index 3c585de0..f164e2f9 100644 --- a/src/utils/index.ts +++ b/src/utils/index.ts @@ -1,3 +1,6 @@ +import { toastController } from "@ionic/vue"; +import { translate } from "src"; + const goToOms = (token: string, oms: string) => { const link = (oms.startsWith('http') ? oms.replace(/api\/?/, "") : `https://${oms}.hotwax.io/`) + `commerce/control/main?token=${token}` @@ -24,7 +27,26 @@ const getProductIdentificationValue = (productIdentifier: string, product: any) return value; } +const showToast = async (message: string, configButtons?: any) => { + const defaultButtons = [{ + text: 'Dismiss', + role: 'cancel' + }] + + if (configButtons) defaultButtons.push(...configButtons); + + const toast = await toastController + .create({ + message: message, + duration: 3000, + position: 'bottom', + buttons: defaultButtons + }) + return toast.present(); +} + export { getProductIdentificationValue, - goToOms + goToOms, + showToast } \ No newline at end of file