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
+1
View File
@@ -2,3 +2,4 @@
.idea
node_modules
package-lock.json
/.idea/
+1
View File
@@ -26,3 +26,4 @@ coverage
*.njsproj
*.sln
*.sw?
/dev-dist/
+36
View File
@@ -6,6 +6,8 @@ import {onMounted, ref} from "vue";
import {Auth} from "@/services/auth";
import DialogHost from "@/components/dialog/DialogHost.vue";
import {LoginService} from "@/services/LoginService";
import {Dialogs} from "@/services/DialogService";
import InstallPWADialog from "@/components/dialog/InstallPWADialog.vue";
let showPopup = ref(false)
let loggedIn = ref(false)
@@ -33,6 +35,40 @@ LoginService.subject.subscribe(() => {
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>
<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 { VitePWA } from 'vite-plugin-pwa'
import {VitePWA} from 'vite-plugin-pwa'
export default defineConfig({
plugins: [
vue(),
VitePWA({
registerType: 'autoUpdate',
includeAssets: ['favicon.ico', 'minis_logo.png'],
manifest: {
name: 'Miniplan',
short_name: 'Miniplan',
description: 'Miniplan',
theme_color: '#ffffff',
background_color: '#ffffff',
display: 'standalone',
scope: '/',
start_url: '/',
screenshots: [
{
src: 'screenshot1.png',
sizes: '505x710',
type: 'image/png',
form_factor: "narrow",
label: "Miniplan Liste"
},
{
src: 'screenshot2.png',
sizes: '505x711',
type: 'image/png',
form_factor: "narrow",
label: "Miniplan Tabelle"
},
{
src: "screenshot3.png",
sizes: '1003x709',
type: 'image/png',
form_factor: "wide",
label: "Miniplan Tabelle"
}
export default defineConfig(({mode}) => (
{
plugins: [
vue(),
VitePWA({
registerType: 'autoUpdate',
includeAssets: ['favicon.ico', 'minis_logo.png'],
manifest: {
name: 'Miniplan App',
short_name: 'Miniplan',
description: 'Lade deinen Miniplan direkt auf dein Handy. So hast du den Miniplan jederzeit und schnell erreichbar.',
theme_color: '#ffffff',
background_color: '#ffffff',
display: 'standalone',
scope: '/',
start_url: '/',
screenshots: [
{
src: 'screenshot1.png',
sizes: '505x710',
type: 'image/png',
form_factor: "narrow",
label: "Miniplan Liste"
},
{
src: 'screenshot2.png',
sizes: '505x711',
type: 'image/png',
form_factor: "narrow",
label: "Miniplan Tabelle"
},
{
src: "screenshot3.png",
sizes: '1003x709',
type: 'image/png',
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: [
{
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: false
}
resolve: {
alias: {
'@': fileURLToPath(new URL('./src', import.meta.url))
}
}
})
],
resolve: {
alias: {
'@': fileURLToPath(new URL('./src', import.meta.url))
}
}
})
)