feat(pwa): installable pwa widget

This commit is contained in:
userquin 2023-01-18 20:45:00 +01:00
parent 5785047856
commit b1876bc160
7 changed files with 70 additions and 0 deletions

View file

@ -38,6 +38,7 @@ defineProps<{
<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 />
<slot /> <slot />
</div> </div>
</template> </template>

View file

@ -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 op10 class="-z-1 rtl-flip" />
</div>
</template>

View file

@ -64,6 +64,7 @@ const isGrayscale = usePreferences('grayscaleMode')
<slot name="right"> <slot name="right">
<div flex-auto /> <div flex-auto />
<PwaPrompt /> <PwaPrompt />
<PwaInstallPrompt />
<NavFooter /> <NavFooter />
</slot> </slot>
</div> </div>

View file

@ -178,6 +178,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"

View file

@ -241,6 +241,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",

View file

@ -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",

View file

@ -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,49 @@ 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 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')
window.removeEventListener('beforeinstallprompt', beforeInstallPrompt)
}
const cancelInstall = () => {
showInstallPrompt.value = false
}
return { return {
provide: { provide: {
pwa: reactive({ pwa: reactive({
isInstalled, isInstalled,
showInstallPrompt,
cancelInstall,
install,
swActivated, swActivated,
registrationError, registrationError,
needRefresh, needRefresh,