You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

184 lines
5.7 KiB

<template>
<transition
enter-active-class="transform ease-out duration-300 transition"
enter-from-class="translate-y-2 opacity-0 sm:translate-y-4"
enter-to-class="translate-y-0 opacity-100 sm:translate-y-0"
leave-active-class="transition ease-in duration-500"
leave-from-class="opacity-100"
leave-to-class="opacity-0"
>
<div
v-if="showInstallPrompt"
class="fixed z-[100] top-4 right-4 max-w-sm bg-white rounded-lg shadow-lg border border-gray-200"
>
<div class="p-4">
<div class="flex items-start">
<div class="flex-shrink-0">
<font-awesome
icon="download"
class="h-6 w-6 text-blue-600"
title="Install App"
/>
</div>
<div class="ml-3 flex-1">
<h3 class="text-sm font-medium text-gray-900">
Install Time Safari
</h3>
<p class="mt-1 text-sm text-gray-500">
Install this app on your device for a better experience
</p>
<div class="mt-4 flex space-x-3">
<button
@click="installPWA"
class="flex-1 bg-blue-600 text-white text-sm font-medium px-3 py-2 rounded-md hover:bg-blue-700 focus:outline-none focus:ring-2 focus:ring-blue-500 focus:ring-offset-2"
>
Install
</button>
<button
@click="dismissPrompt"
class="flex-1 bg-gray-100 text-gray-700 text-sm font-medium px-3 py-2 rounded-md hover:bg-gray-200 focus:outline-none focus:ring-2 focus:ring-gray-500 focus:ring-offset-2"
>
Later
</button>
</div>
</div>
<div class="ml-4 flex-shrink-0">
<button
@click="dismissPrompt"
class="text-gray-400 hover:text-gray-600 focus:outline-none"
>
<font-awesome icon="times" class="h-4 w-4" />
</button>
</div>
</div>
</div>
</div>
</transition>
</template>
<script lang="ts">
import { Component, Vue } from "vue-facing-decorator";
import { logger } from "@/utils/logger";
import { PlatformServiceFactory } from "@/services/PlatformServiceFactory";
interface BeforeInstallPromptEvent extends Event {
prompt(): Promise<void>;
userChoice: Promise<{ outcome: 'accepted' | 'dismissed' }>;
}
@Component({ name: "PWAInstallPrompt" })
export default class PWAInstallPrompt extends Vue {
$notify!: (notification: any, timeout?: number) => void;
private showInstallPrompt = false;
private deferredPrompt: BeforeInstallPromptEvent | null = null;
private dismissed = false;
mounted() {
if (!PlatformServiceFactory.getInstance().isPWAEnabled) return;
this.setupInstallPrompt();
}
private setupInstallPrompt() {
// Only show install prompt if PWA is enabled
if (process.env.VITE_PWA_ENABLED !== "true") {
logger.debug("[PWA] Install prompt disabled - PWA not enabled");
return;
}
// Check if already installed
if (this.isPWAInstalled()) {
logger.debug("[PWA] Install prompt disabled - PWA already installed");
return;
}
// Listen for the beforeinstallprompt event
window.addEventListener('beforeinstallprompt', (e) => {
logger.debug("[PWA] beforeinstallprompt event fired");
// Prevent the mini-infobar from appearing on mobile
e.preventDefault();
// Stash the event so it can be triggered later
this.deferredPrompt = e as BeforeInstallPromptEvent;
// Show the install prompt
this.showInstallPrompt = true;
});
// Listen for successful installation
window.addEventListener('appinstalled', () => {
logger.debug("[PWA] App installed successfully");
this.showInstallPrompt = false;
this.deferredPrompt = null;
// Show success notification
this.$notify({
group: "alert",
type: "success",
title: "App Installed!",
text: "Time Safari has been installed on your device.",
}, 5000);
});
}
private isPWAInstalled(): boolean {
// Check if running in standalone mode (installed PWA)
if (window.matchMedia('(display-mode: standalone)').matches) {
return true;
}
// Check if running in fullscreen mode (installed PWA)
if (window.matchMedia('(display-mode: fullscreen)').matches) {
return true;
}
// Check if running in minimal-ui mode (installed PWA)
if (window.matchMedia('(display-mode: minimal-ui)').matches) {
return true;
}
return false;
}
private async installPWA() {
if (!this.deferredPrompt) {
logger.warn("[PWA] No install prompt available");
return;
}
try {
// Show the install prompt
this.deferredPrompt.prompt();
// Wait for the user to respond to the prompt
const { outcome } = await this.deferredPrompt.userChoice;
logger.debug(`[PWA] User response to install prompt: ${outcome}`);
if (outcome === 'accepted') {
logger.debug("[PWA] User accepted the install prompt");
this.showInstallPrompt = false;
} else {
logger.debug("[PWA] User dismissed the install prompt");
this.dismissed = true;
this.showInstallPrompt = false;
}
// Clear the deferred prompt
this.deferredPrompt = null;
} catch (error) {
logger.error("[PWA] Error during install prompt:", error);
this.showInstallPrompt = false;
}
}
private dismissPrompt() {
this.dismissed = true;
this.showInstallPrompt = false;
// Don't show again for this session
sessionStorage.setItem('pwa-install-dismissed', 'true');
}
}
</script>