feat(pwa): add install PWA widget (#1451)
parent
3c888d3914
commit
f96cdae1ba
|
@ -42,6 +42,7 @@ const wideLayout = computed(() => route.meta.wideLayout ?? false)
|
||||||
<slot name="header" />
|
<slot name="header" />
|
||||||
</div>
|
</div>
|
||||||
<div :class="{ 'xl:block': $route.name !== 'tag' }" hidden h-6 />
|
<div :class="{ 'xl:block': $route.name !== 'tag' }" hidden h-6 />
|
||||||
|
<PwaInstallPrompt lg:hidden />
|
||||||
<div :class="isHydrated && wideLayout ? 'xl:w-full sm:max-w-600px' : 'sm:max-w-600px md:shrink-0'" m-auto>
|
<div :class="isHydrated && wideLayout ? 'xl:w-full sm:max-w-600px' : 'sm:max-w-600px md:shrink-0'" m-auto>
|
||||||
<slot />
|
<slot />
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -0,0 +1,22 @@
|
||||||
|
<template>
|
||||||
|
<div
|
||||||
|
v-if="$pwa?.showInstallPrompt && !$pwa?.needRefresh"
|
||||||
|
m-2 p5 bg="primary-fade" relative
|
||||||
|
rounded-lg of-hidden
|
||||||
|
flex="~ col gap-3"
|
||||||
|
v-bind="$attrs"
|
||||||
|
>
|
||||||
|
<h2 flex="~ gap-2" items-center>
|
||||||
|
{{ $t('pwa.install_title') }}
|
||||||
|
</h2>
|
||||||
|
<div flex="~ gap-1">
|
||||||
|
<button type="button" btn-solid px-4 py-1 text-center text-sm @click="$pwa.install()">
|
||||||
|
{{ $t('pwa.install') }}
|
||||||
|
</button>
|
||||||
|
<button type="button" btn-text filter-saturate-0 px-4 py-1 text-center text-sm @click="$pwa.cancelInstall()">
|
||||||
|
{{ $t('pwa.dismiss') }}
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
<div i-material-symbols:install-desktop-rounded absolute text-6em bottom--2 inset-ie--2 text-primary dark:text-white op10 dark:op45 class="-z-1 rtl-flip" />
|
||||||
|
</div>
|
||||||
|
</template>
|
|
@ -16,6 +16,6 @@
|
||||||
{{ $t('pwa.dismiss') }}
|
{{ $t('pwa.dismiss') }}
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
<div i-ri-arrow-down-circle-line absolute text-8em bottom--10 inset-ie--10 text-primary op10 class="-z-1" />
|
<div i-ri-arrow-down-circle-line absolute text-8em bottom--10 inset-ie--10 text-primary dark:text-white op10 dark:op45 class="-z-1" />
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
|
@ -65,6 +65,7 @@ const isGrayscale = usePreferences('grayscaleMode')
|
||||||
<div flex-auto />
|
<div flex-auto />
|
||||||
|
|
||||||
<PwaPrompt />
|
<PwaPrompt />
|
||||||
|
<PwaInstallPrompt />
|
||||||
<LazyCommonPreviewPrompt v-if="info.env === 'preview'" />
|
<LazyCommonPreviewPrompt v-if="info.env === 'preview'" />
|
||||||
<NavFooter />
|
<NavFooter />
|
||||||
</slot>
|
</slot>
|
||||||
|
|
|
@ -248,6 +248,8 @@
|
||||||
},
|
},
|
||||||
"pwa": {
|
"pwa": {
|
||||||
"dismiss": "Dismiss",
|
"dismiss": "Dismiss",
|
||||||
|
"install": "Install",
|
||||||
|
"install_title": "Install Elk",
|
||||||
"title": "New Elk update available!",
|
"title": "New Elk update available!",
|
||||||
"update": "Update",
|
"update": "Update",
|
||||||
"update_available_short": "Update Elk",
|
"update_available_short": "Update Elk",
|
||||||
|
|
|
@ -204,6 +204,8 @@
|
||||||
},
|
},
|
||||||
"pwa": {
|
"pwa": {
|
||||||
"dismiss": "Descartar",
|
"dismiss": "Descartar",
|
||||||
|
"install": "Instalar",
|
||||||
|
"install_title": "Instalar Elk",
|
||||||
"title": "Nueva versión de Elk disponible",
|
"title": "Nueva versión de Elk disponible",
|
||||||
"update": "Actualizar",
|
"update": "Actualizar",
|
||||||
"update_available_short": "Actualiza Elk",
|
"update_available_short": "Actualiza Elk",
|
||||||
|
|
|
@ -253,6 +253,8 @@
|
||||||
},
|
},
|
||||||
"pwa": {
|
"pwa": {
|
||||||
"dismiss": "Fermer",
|
"dismiss": "Fermer",
|
||||||
|
"install": "Installer",
|
||||||
|
"install_title": "Installer Elk",
|
||||||
"title": "Nouvelle mise à jour Elk disponible !",
|
"title": "Nouvelle mise à jour Elk disponible !",
|
||||||
"update": "Mettre à jour",
|
"update": "Mettre à jour",
|
||||||
"update_available_short": "Mettre à jour Elk",
|
"update_available_short": "Mettre à jour Elk",
|
||||||
|
|
|
@ -4,6 +4,7 @@ export default defineNuxtPlugin(() => {
|
||||||
const online = useOnline()
|
const online = useOnline()
|
||||||
const registrationError = ref(false)
|
const registrationError = ref(false)
|
||||||
const swActivated = ref(false)
|
const swActivated = ref(false)
|
||||||
|
const showInstallPrompt = ref(false)
|
||||||
|
|
||||||
// https://thomashunter.name/posts/2021-12-11-detecting-if-pwa-twa-is-installed
|
// https://thomashunter.name/posts/2021-12-11-detecting-if-pwa-twa-is-installed
|
||||||
const ua = navigator.userAgent
|
const ua = navigator.userAgent
|
||||||
|
@ -57,10 +58,51 @@ export default defineNuxtPlugin(() => {
|
||||||
needRefresh.value = false
|
needRefresh.value = false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type InstallPromptEvent = Event & {
|
||||||
|
prompt: () => void
|
||||||
|
userChoice: Promise<{ outcome: 'dismissed' | 'accepted' }>
|
||||||
|
}
|
||||||
|
|
||||||
|
let deferredPrompt: InstallPromptEvent | undefined
|
||||||
|
|
||||||
|
const beforeInstallPrompt = (e: Event) => {
|
||||||
|
e.preventDefault()
|
||||||
|
deferredPrompt = e as InstallPromptEvent
|
||||||
|
showInstallPrompt.value = true
|
||||||
|
}
|
||||||
|
window.addEventListener('beforeinstallprompt', beforeInstallPrompt)
|
||||||
|
window.addEventListener('appinstalled', () => {
|
||||||
|
deferredPrompt = undefined
|
||||||
|
showInstallPrompt.value = false
|
||||||
|
})
|
||||||
|
|
||||||
|
const cancelInstall = () => {
|
||||||
|
deferredPrompt = undefined
|
||||||
|
showInstallPrompt.value = false
|
||||||
|
window.removeEventListener('beforeinstallprompt', beforeInstallPrompt)
|
||||||
|
}
|
||||||
|
|
||||||
|
const install = async () => {
|
||||||
|
if (!showInstallPrompt.value || !deferredPrompt) {
|
||||||
|
showInstallPrompt.value = false
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
showInstallPrompt.value = false
|
||||||
|
await nextTick()
|
||||||
|
deferredPrompt.prompt()
|
||||||
|
const { outcome } = await deferredPrompt.userChoice
|
||||||
|
if (outcome === 'dismissed')
|
||||||
|
cancelInstall()
|
||||||
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
provide: {
|
provide: {
|
||||||
pwa: reactive({
|
pwa: reactive({
|
||||||
isInstalled,
|
isInstalled,
|
||||||
|
showInstallPrompt,
|
||||||
|
cancelInstall,
|
||||||
|
install,
|
||||||
swActivated,
|
swActivated,
|
||||||
registrationError,
|
registrationError,
|
||||||
needRefresh,
|
needRefresh,
|
||||||
|
|
Loading…
Reference in New Issue