feat: show notification to install miniplan as app
Deploy Miniplan / build (push) Successful in 59s

This commit is contained in:
2026-07-21 16:38:05 +02:00
parent 1104280c0c
commit 87812d02d3
5 changed files with 181 additions and 75 deletions
+2 -1
View File
@@ -1,4 +1,5 @@
.directory .directory
.idea .idea
node_modules node_modules
package-lock.json package-lock.json
/.idea/
+1
View File
@@ -26,3 +26,4 @@ coverage
*.njsproj *.njsproj
*.sln *.sln
*.sw? *.sw?
/dev-dist/
+36
View File
@@ -6,6 +6,8 @@ import {onMounted, ref} from "vue";
import {Auth} from "@/services/auth"; import {Auth} from "@/services/auth";
import DialogHost from "@/components/dialog/DialogHost.vue"; import DialogHost from "@/components/dialog/DialogHost.vue";
import {LoginService} from "@/services/LoginService"; import {LoginService} from "@/services/LoginService";
import {Dialogs} from "@/services/DialogService";
import InstallPWADialog from "@/components/dialog/InstallPWADialog.vue";
let showPopup = ref(false) let showPopup = ref(false)
let loggedIn = ref(false) let loggedIn = ref(false)
@@ -33,6 +35,40 @@ LoginService.subject.subscribe(() => {
showPopup.value = true showPopup.value = true
}) })
const installEvent = ref<Event>()
window.addEventListener('beforeinstallprompt', (e) => {
e.preventDefault()
console.log("beforeinstallprompt")
installEvent.value = e
scheduleInstallDialog()
})
function scheduleInstallDialog() {
if (isPwa()) return
if (localStorage.getItem('installPromptDismissed')) return
if (!installEvent.value) return
Dialogs.createDialog(InstallPWADialog, {
onPositive(dontShowAgain: boolean) {
if (dontShowAgain) localStorage.setItem('installPromptDismissed', 'true')
;(installEvent.value as any).prompt()
},
onNegative(dontShowAgain: boolean) {
if (dontShowAgain) localStorage.setItem('installPromptDismissed', 'true')
},
onDismiss() {}
}, {})
}
function isPwa() {
return window.matchMedia('(display-mode: standalone)').matches
}
window.addEventListener('appinstalled', () => {
localStorage.setItem('installPromptDismissed', 'true')
})
</script> </script>
<template> <template>
@@ -0,0 +1,66 @@
<script setup lang="ts">
import {ref} from "vue";
import Dialog from "@/components/dialog/Dialog.vue";
import type {DialogControls} from "@/components/dialog/dialog";
interface InstallPWADialogProps extends DialogControls {
}
defineProps<InstallPWADialogProps>()
const dontShowAgain = ref<boolean>(false)
</script>
<template>
<Dialog class="dialog">
<h3 style="margin-bottom: 10px; display: flex; gap: 1rem; align-items: center">
Zum Startbildschirm hinzufügen
</h3>
<p style="line-height: 1.7rem">
Dein Miniplan kann jetzt auf Ihrem Gerät installiert werden, um schnell und einfach darauf zuzugreifen.
</p>
<label class="checkbox">
<input type="checkbox" v-model="dontShowAgain" />
Nicht mehr anzeigen
</label>
<div class="buttons">
<button @click="onNegative(dontShowAgain)" class="dismiss">Später</button>
<button @click="onPositive(dontShowAgain)" ><i>browser_updated</i>Jetzt hinzufügen</button>
</div>
</Dialog>
</template>
<style scoped lang="less">
.dialog {
display: flex;
flex-direction: column;
align-items: center;
text-align: center;
width: 500px
}
.checkbox {
display: flex;
align-items: center;
gap: 8px;
margin-top: 16px;
font-size: 14px;
cursor: pointer;
}
.buttons {
display: flex;
justify-content: end;
margin-top: 20px;
.dismiss {
background: transparent;
}
}
</style>
+76 -74
View File
@@ -1,79 +1,81 @@
import { fileURLToPath, URL } from 'node:url' import {fileURLToPath, URL} from 'node:url'
import { defineConfig } from 'vite' import {defineConfig} from 'vite'
import vue from '@vitejs/plugin-vue' import vue from '@vitejs/plugin-vue'
import { VitePWA } from 'vite-plugin-pwa' import {VitePWA} from 'vite-plugin-pwa'
export default defineConfig({ export default defineConfig(({mode}) => (
plugins: [ {
vue(), plugins: [
VitePWA({ vue(),
registerType: 'autoUpdate', VitePWA({
includeAssets: ['favicon.ico', 'minis_logo.png'], registerType: 'autoUpdate',
manifest: { includeAssets: ['favicon.ico', 'minis_logo.png'],
name: 'Miniplan', manifest: {
short_name: 'Miniplan', name: 'Miniplan App',
description: 'Miniplan', short_name: 'Miniplan',
theme_color: '#ffffff', description: 'Lade deinen Miniplan direkt auf dein Handy. So hast du den Miniplan jederzeit und schnell erreichbar.',
background_color: '#ffffff', theme_color: '#ffffff',
display: 'standalone', background_color: '#ffffff',
scope: '/', display: 'standalone',
start_url: '/', scope: '/',
screenshots: [ start_url: '/',
{ screenshots: [
src: 'screenshot1.png', {
sizes: '505x710', src: 'screenshot1.png',
type: 'image/png', sizes: '505x710',
form_factor: "narrow", type: 'image/png',
label: "Miniplan Liste" form_factor: "narrow",
}, label: "Miniplan Liste"
{ },
src: 'screenshot2.png', {
sizes: '505x711', src: 'screenshot2.png',
type: 'image/png', sizes: '505x711',
form_factor: "narrow", type: 'image/png',
label: "Miniplan Tabelle" form_factor: "narrow",
}, label: "Miniplan Tabelle"
{ },
src: "screenshot3.png", {
sizes: '1003x709', src: "screenshot3.png",
type: 'image/png', sizes: '1003x709',
form_factor: "wide", type: 'image/png',
label: "Miniplan Tabelle" form_factor: "wide",
} label: "Miniplan Tabelle"
}
],
icons: [
{
src: "pwa-64x64.png",
sizes: "64x64",
type: "image/png"
},
{
src: "pwa-192x192.png",
sizes: "192x192",
type: "image/png"
},
{
src: "pwa-512x512.png",
sizes: "512x512",
type: "image/png"
},
{
src: "maskable-icon-512x512.png",
sizes: "512x512",
type: "image/png",
purpose: "maskable"
}
]
},
devOptions: {
enabled: mode === "development"
}
})
], ],
icons: [ resolve: {
{ alias: {
src: "pwa-64x64.png", '@': fileURLToPath(new URL('./src', import.meta.url))
sizes: "64x64", }
type: "image/png" }
},
{
src: "pwa-192x192.png",
sizes: "192x192",
type: "image/png"
},
{
src: "pwa-512x512.png",
sizes: "512x512",
type: "image/png"
},
{
src: "maskable-icon-512x512.png",
sizes: "512x512",
type: "image/png",
purpose: "maskable"
}
]
},
devOptions: {
enabled: false
}
}) })
], )
resolve: {
alias: {
'@': fileURLToPath(new URL('./src', import.meta.url))
}
}
})