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
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>
|