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.
		
		
		
		
		
			
		
			
				
					
					
						
							621 lines
						
					
					
						
							21 KiB
						
					
					
				
			
		
		
		
			
			
			
				
					
				
				
					
				
			
		
		
	
	
							621 lines
						
					
					
						
							21 KiB
						
					
					
				
								<template>
							 | 
						|
								  <router-view />
							 | 
						|
								
							 | 
						|
								  <!-- https://github.com/emmanuelsw/notiwind -->
							 | 
						|
								  <NotificationGroup group="alert">
							 | 
						|
								    <div
							 | 
						|
								      class="fixed top-4 right-4 w-full max-w-sm flex flex-col items-start justify-end"
							 | 
						|
								    >
							 | 
						|
								      <Notification
							 | 
						|
								        v-slot="{ notifications, close }"
							 | 
						|
								        enter="transform ease-out duration-300 transition"
							 | 
						|
								        enter-from="translate-y-2 opacity-0 sm:translate-y-0 sm:translate-x-4"
							 | 
						|
								        enter-to="translate-y-0 opacity-100 sm:translate-x-0"
							 | 
						|
								        leave="transition ease-in duration-500"
							 | 
						|
								        leave-from="opacity-100"
							 | 
						|
								        leave-to="opacity-0"
							 | 
						|
								        move="transition duration-500"
							 | 
						|
								        move-delay="delay-300"
							 | 
						|
								      >
							 | 
						|
								        <div
							 | 
						|
								          v-for="notification in notifications"
							 | 
						|
								          :key="notification.id"
							 | 
						|
								          class="w-full"
							 | 
						|
								          role="alert"
							 | 
						|
								        >
							 | 
						|
								          <div
							 | 
						|
								            v-if="notification.type === 'toast'"
							 | 
						|
								            class="w-full max-w-sm mx-auto mb-3 overflow-hidden bg-slate-900/90 text-white rounded-lg shadow-md"
							 | 
						|
								          >
							 | 
						|
								            <div class="w-full px-4 py-3">
							 | 
						|
								              <span class="font-semibold">{{ notification.title }}</span>
							 | 
						|
								              <p class="text-sm">{{ notification.text }}</p>
							 | 
						|
								            </div>
							 | 
						|
								          </div>
							 | 
						|
								
							 | 
						|
								          <div
							 | 
						|
								            v-if="notification.type === 'info'"
							 | 
						|
								            class="flex w-full max-w-sm mx-auto mb-3 overflow-hidden bg-slate-100 rounded-lg shadow-md"
							 | 
						|
								          >
							 | 
						|
								            <div
							 | 
						|
								              class="flex items-center justify-center w-12 bg-slate-600 text-slate-100"
							 | 
						|
								            >
							 | 
						|
								              <fa icon="circle-info" class="fa-fw fa-xl"></fa>
							 | 
						|
								            </div>
							 | 
						|
								
							 | 
						|
								            <div class="relative w-full pl-4 pr-8 py-2 text-slate-900">
							 | 
						|
								              <span class="font-semibold">{{ notification.title }}</span>
							 | 
						|
								              <p class="text-sm">{{ notification.text }}</p>
							 | 
						|
								
							 | 
						|
								              <button
							 | 
						|
								                @click="close(notification.id)"
							 | 
						|
								                class="absolute top-2 right-2 px-0.5 py-0 rounded-full bg-slate-200 text-slate-600"
							 | 
						|
								              >
							 | 
						|
								                <fa icon="xmark" class="fa-fw"></fa>
							 | 
						|
								              </button>
							 | 
						|
								            </div>
							 | 
						|
								          </div>
							 | 
						|
								
							 | 
						|
								          <div
							 | 
						|
								            v-if="notification.type === 'success'"
							 | 
						|
								            class="flex w-full max-w-sm mx-auto mb-3 overflow-hidden bg-emerald-100 rounded-lg shadow-md"
							 | 
						|
								          >
							 | 
						|
								            <div
							 | 
						|
								              class="flex items-center justify-center w-12 bg-emerald-600 text-emerald-100"
							 | 
						|
								            >
							 | 
						|
								              <fa icon="circle-info" class="fa-fw fa-xl"></fa>
							 | 
						|
								            </div>
							 | 
						|
								
							 | 
						|
								            <div class="relative w-full pl-4 pr-8 py-2 text-emerald-900">
							 | 
						|
								              <span class="font-semibold">{{ notification.title }}</span>
							 | 
						|
								              <p class="text-sm">{{ notification.text }}</p>
							 | 
						|
								
							 | 
						|
								              <button
							 | 
						|
								                @click="close(notification.id)"
							 | 
						|
								                class="absolute top-2 right-2 px-0.5 py-0 rounded-full bg-emerald-200 text-emerald-600"
							 | 
						|
								              >
							 | 
						|
								                <fa icon="xmark" class="fa-fw"></fa>
							 | 
						|
								              </button>
							 | 
						|
								            </div>
							 | 
						|
								          </div>
							 | 
						|
								
							 | 
						|
								          <div
							 | 
						|
								            v-if="notification.type === 'warning'"
							 | 
						|
								            class="flex w-full max-w-sm mx-auto mb-3 overflow-hidden bg-amber-100 rounded-lg shadow-md"
							 | 
						|
								          >
							 | 
						|
								            <div
							 | 
						|
								              class="flex items-center justify-center w-12 bg-amber-600 text-amber-100"
							 | 
						|
								            >
							 | 
						|
								              <fa icon="triangle-exclamation" class="fa-fw fa-xl"></fa>
							 | 
						|
								            </div>
							 | 
						|
								
							 | 
						|
								            <div class="relative w-full pl-4 pr-8 py-2 text-amber-900">
							 | 
						|
								              <span class="font-semibold">{{ notification.title }}</span>
							 | 
						|
								              <p class="text-sm">{{ notification.text }}</p>
							 | 
						|
								
							 | 
						|
								              <button
							 | 
						|
								                @click="close(notification.id)"
							 | 
						|
								                class="absolute top-2 right-2 px-0.5 py-0 rounded-full bg-amber-200 text-amber-600"
							 | 
						|
								              >
							 | 
						|
								                <fa icon="xmark" class="fa-fw"></fa>
							 | 
						|
								              </button>
							 | 
						|
								            </div>
							 | 
						|
								          </div>
							 | 
						|
								
							 | 
						|
								          <div
							 | 
						|
								            v-if="notification.type === 'danger'"
							 | 
						|
								            class="flex w-full max-w-sm mx-auto mb-3 overflow-hidden bg-rose-100 rounded-lg shadow-md"
							 | 
						|
								          >
							 | 
						|
								            <div
							 | 
						|
								              class="flex items-center justify-center w-12 bg-rose-600 text-rose-100"
							 | 
						|
								            >
							 | 
						|
								              <fa icon="triangle-exclamation" class="fa-fw fa-xl"></fa>
							 | 
						|
								            </div>
							 | 
						|
								
							 | 
						|
								            <div class="relative w-full pl-4 pr-8 py-2 text-rose-900">
							 | 
						|
								              <span class="font-semibold">{{ notification.title }}</span>
							 | 
						|
								              <p class="text-sm">{{ notification.text }}</p>
							 | 
						|
								
							 | 
						|
								              <button
							 | 
						|
								                @click="close(notification.id)"
							 | 
						|
								                class="absolute top-2 right-2 px-0.5 py-0 rounded-full bg-rose-200 text-rose-600"
							 | 
						|
								              >
							 | 
						|
								                <fa icon="xmark" class="fa-fw"></fa>
							 | 
						|
								              </button>
							 | 
						|
								            </div>
							 | 
						|
								          </div>
							 | 
						|
								        </div>
							 | 
						|
								      </Notification>
							 | 
						|
								    </div>
							 | 
						|
								  </NotificationGroup>
							 | 
						|
								
							 | 
						|
								  <NotificationGroup group="modal">
							 | 
						|
								    <div class="fixed z-[100] top-0 inset-x-0 w-full">
							 | 
						|
								      <Notification
							 | 
						|
								        v-slot="{ notifications, close }"
							 | 
						|
								        enter="transform ease-out duration-300 transition"
							 | 
						|
								        enter-from="translate-y-2 opacity-0 sm:translate-y-4"
							 | 
						|
								        enter-to="translate-y-0 opacity-100 sm:translate-y-0"
							 | 
						|
								        leave="transition ease-in duration-500"
							 | 
						|
								        leave-from="opacity-100"
							 | 
						|
								        leave-to="opacity-0"
							 | 
						|
								        move="transition duration-500"
							 | 
						|
								        move-delay="delay-300"
							 | 
						|
								      >
							 | 
						|
								        <div
							 | 
						|
								          v-for="notification in notifications"
							 | 
						|
								          :key="notification.id"
							 | 
						|
								          class="w-full"
							 | 
						|
								          role="alert"
							 | 
						|
								        >
							 | 
						|
								          <div
							 | 
						|
								            v-if="notification.type === 'notification-permission'"
							 | 
						|
								            class="absolute inset-0 h-screen flex flex-col items-center justify-center bg-slate-900/50"
							 | 
						|
								          >
							 | 
						|
								            <div
							 | 
						|
								              class="flex w-11/12 max-w-sm mx-auto mb-3 overflow-hidden bg-white rounded-lg shadow-lg"
							 | 
						|
								            >
							 | 
						|
								              <div class="w-full px-6 py-6 text-slate-900 text-center">
							 | 
						|
								                <p v-if="serviceWorkerReady" class="text-lg mb-4">
							 | 
						|
								                  Would you like to <b>turn on</b> notifications for this app?
							 | 
						|
								                </p>
							 | 
						|
								                <p v-else class="text-lg mb-4">
							 | 
						|
								                  Waiting for system initialization, which may take up to 10
							 | 
						|
								                  seconds...
							 | 
						|
								                  <fa icon="spinner" spin />
							 | 
						|
								                </p>
							 | 
						|
								
							 | 
						|
								                <button
							 | 
						|
								                  v-if="serviceWorkerReady"
							 | 
						|
								                  class="block w-full text-center text-md font-bold uppercase bg-blue-600 text-white px-2 py-2 rounded-md mb-2"
							 | 
						|
								                  @click="
							 | 
						|
								                    close(notification.id);
							 | 
						|
								                    turnOnNotifications();
							 | 
						|
								                  "
							 | 
						|
								                >
							 | 
						|
								                  Turn on Notifications
							 | 
						|
								                </button>
							 | 
						|
								
							 | 
						|
								                <button
							 | 
						|
								                  @click="close(notification.id)"
							 | 
						|
								                  class="block w-full text-center text-md font-bold uppercase bg-slate-600 text-white px-2 py-2 rounded-md"
							 | 
						|
								                >
							 | 
						|
								                  Maybe Later
							 | 
						|
								                </button>
							 | 
						|
								              </div>
							 | 
						|
								            </div>
							 | 
						|
								          </div>
							 | 
						|
								          <div
							 | 
						|
								            v-if="notification.type === 'notification-mute'"
							 | 
						|
								            class="absolute inset-0 h-screen flex flex-col items-center justify-center bg-slate-900/50"
							 | 
						|
								          >
							 | 
						|
								            <div
							 | 
						|
								              class="flex w-11/12 max-w-sm mx-auto mb-3 overflow-hidden bg-white rounded-lg shadow-lg"
							 | 
						|
								            >
							 | 
						|
								              <div class="w-full px-6 py-6 text-slate-900 text-center">
							 | 
						|
								                <p class="text-lg mb-4">Mute app notifications:</p>
							 | 
						|
								
							 | 
						|
								                <button
							 | 
						|
								                  class="block w-full text-center text-md font-bold uppercase bg-blue-600 text-white px-2 py-2 rounded-md mb-2"
							 | 
						|
								                >
							 | 
						|
								                  For 1 Hour
							 | 
						|
								                </button>
							 | 
						|
								                <button
							 | 
						|
								                  class="block w-full text-center text-md font-bold uppercase bg-blue-600 text-white px-2 py-2 rounded-md mb-2"
							 | 
						|
								                >
							 | 
						|
								                  For 8 Hours
							 | 
						|
								                </button>
							 | 
						|
								                <button
							 | 
						|
								                  class="block w-full text-center text-md font-bold uppercase bg-blue-600 text-white px-2 py-2 rounded-md mb-2"
							 | 
						|
								                >
							 | 
						|
								                  For 24 Hours
							 | 
						|
								                </button>
							 | 
						|
								                <button
							 | 
						|
								                  class="block w-full text-center text-md font-bold uppercase bg-blue-600 text-white px-2 py-2 rounded-md mb-2"
							 | 
						|
								                >
							 | 
						|
								                  Until I turn it back on
							 | 
						|
								                </button>
							 | 
						|
								                <button
							 | 
						|
								                  @click="close(notification.id)"
							 | 
						|
								                  class="block w-full text-center text-md font-bold uppercase bg-slate-600 text-white px-2 py-2 rounded-md"
							 | 
						|
								                >
							 | 
						|
								                  Cancel
							 | 
						|
								                </button>
							 | 
						|
								              </div>
							 | 
						|
								            </div>
							 | 
						|
								          </div>
							 | 
						|
								          <div
							 | 
						|
								            v-if="notification.type === 'notification-off'"
							 | 
						|
								            class="absolute inset-0 h-screen flex flex-col items-center justify-center bg-slate-900/50"
							 | 
						|
								          >
							 | 
						|
								            <div
							 | 
						|
								              class="flex w-11/12 max-w-sm mx-auto mb-3 overflow-hidden bg-white rounded-lg shadow-lg"
							 | 
						|
								            >
							 | 
						|
								              <div class="w-full px-6 py-6 text-slate-900 text-center">
							 | 
						|
								                <p class="text-lg mb-4">
							 | 
						|
								                  Would you like to <b>turn off</b> notifications for this app?
							 | 
						|
								                </p>
							 | 
						|
								
							 | 
						|
								                <button
							 | 
						|
								                  @click="
							 | 
						|
								                    close(notification.id);
							 | 
						|
								                    turnOffNotifications();
							 | 
						|
								                  "
							 | 
						|
								                  class="block w-full text-center text-md font-bold uppercase bg-rose-600 text-white px-2 py-2 rounded-md mb-2"
							 | 
						|
								                >
							 | 
						|
								                  Turn Off Notifications
							 | 
						|
								                </button>
							 | 
						|
								                <button
							 | 
						|
								                  @click="close(notification.id)"
							 | 
						|
								                  class="block w-full text-center text-md font-bold uppercase bg-slate-600 text-white px-2 py-2 rounded-md"
							 | 
						|
								                >
							 | 
						|
								                  Leave it On
							 | 
						|
								                </button>
							 | 
						|
								              </div>
							 | 
						|
								            </div>
							 | 
						|
								          </div>
							 | 
						|
								        </div>
							 | 
						|
								      </Notification>
							 | 
						|
								    </div>
							 | 
						|
								  </NotificationGroup>
							 | 
						|
								</template>
							 | 
						|
								
							 | 
						|
								<style></style>
							 | 
						|
								
							 | 
						|
								<script lang="ts">
							 | 
						|
								import { Vue, Component } from "vue-facing-decorator";
							 | 
						|
								import axios from "axios";
							 | 
						|
								interface ServiceWorkerMessage {
							 | 
						|
								  type: string;
							 | 
						|
								  data: string;
							 | 
						|
								}
							 | 
						|
								
							 | 
						|
								interface ServiceWorkerResponse {
							 | 
						|
								  // Define the properties and their types
							 | 
						|
								  success: boolean;
							 | 
						|
								  message?: string;
							 | 
						|
								}
							 | 
						|
								
							 | 
						|
								// Example interface for error
							 | 
						|
								interface ErrorResponse {
							 | 
						|
								  message: string;
							 | 
						|
								  // Other properties as needed
							 | 
						|
								}
							 | 
						|
								
							 | 
						|
								interface VapidResponse {
							 | 
						|
								  data: {
							 | 
						|
								    vapidKey: string;
							 | 
						|
								  };
							 | 
						|
								}
							 | 
						|
								
							 | 
						|
								import { DEFAULT_PUSH_SERVER } from "@/constants/app";
							 | 
						|
								import { db } from "@/db/index";
							 | 
						|
								import { MASTER_SETTINGS_KEY } from "@/db/tables/settings";
							 | 
						|
								import { sendTestThroughPushServer } from "@/libs/util";
							 | 
						|
								
							 | 
						|
								interface Notification {
							 | 
						|
								  group: string;
							 | 
						|
								  type: string;
							 | 
						|
								  title: string;
							 | 
						|
								  text: string;
							 | 
						|
								}
							 | 
						|
								
							 | 
						|
								@Component
							 | 
						|
								export default class App extends Vue {
							 | 
						|
								  $notify!: (notification: Notification, timeout?: number) => void;
							 | 
						|
								
							 | 
						|
								  b64 = "";
							 | 
						|
								  serviceWorkerReady = false;
							 | 
						|
								
							 | 
						|
								  async mounted() {
							 | 
						|
								    try {
							 | 
						|
								      await db.open();
							 | 
						|
								      const settings = await db.settings.get(MASTER_SETTINGS_KEY);
							 | 
						|
								      let pushUrl = DEFAULT_PUSH_SERVER;
							 | 
						|
								      if (settings?.webPushServer) {
							 | 
						|
								        pushUrl = settings.webPushServer;
							 | 
						|
								      }
							 | 
						|
								
							 | 
						|
								      await axios
							 | 
						|
								        .get(pushUrl + "/web-push/vapid")
							 | 
						|
								        .then((response: VapidResponse) => {
							 | 
						|
								          this.b64 = response.data?.vapidKey || "";
							 | 
						|
								          console.log("Got vapid key:", this.b64);
							 | 
						|
								          navigator.serviceWorker.addEventListener("controllerchange", () => {
							 | 
						|
								            console.log("New service worker is now controlling the page");
							 | 
						|
								          });
							 | 
						|
								        });
							 | 
						|
								      if (!this.b64) {
							 | 
						|
								        this.$notify(
							 | 
						|
								          {
							 | 
						|
								            group: "alert",
							 | 
						|
								            type: "danger",
							 | 
						|
								            title: "Error Setting Notifications",
							 | 
						|
								            text: "Could not set notifications.",
							 | 
						|
								          },
							 | 
						|
								          -1,
							 | 
						|
								        );
							 | 
						|
								      }
							 | 
						|
								    } catch (error) {
							 | 
						|
								      if (window.location.host.startsWith("localhost")) {
							 | 
						|
								        console.log("Ignoring the error getting VAPID for local development.");
							 | 
						|
								      } else {
							 | 
						|
								        console.error("Got an error initializing notifications:", error);
							 | 
						|
								        this.$notify(
							 | 
						|
								          {
							 | 
						|
								            group: "alert",
							 | 
						|
								            type: "danger",
							 | 
						|
								            title: "Error Setting Notifications",
							 | 
						|
								            text: "Got an error setting notifications.",
							 | 
						|
								          },
							 | 
						|
								          -1,
							 | 
						|
								        );
							 | 
						|
								      }
							 | 
						|
								    }
							 | 
						|
								    // there may be a long pause here on first initialization
							 | 
						|
								    navigator.serviceWorker.ready.then(() => {
							 | 
						|
								      this.serviceWorkerReady = true;
							 | 
						|
								    });
							 | 
						|
								  }
							 | 
						|
								
							 | 
						|
								  private sendMessageToServiceWorker(
							 | 
						|
								    message: ServiceWorkerMessage,
							 | 
						|
								  ): Promise<unknown> {
							 | 
						|
								    return new Promise((resolve, reject) => {
							 | 
						|
								      if (navigator.serviceWorker.controller) {
							 | 
						|
								        const messageChannel = new MessageChannel();
							 | 
						|
								
							 | 
						|
								        messageChannel.port1.onmessage = (event: MessageEvent) => {
							 | 
						|
								          if (event.data.error) {
							 | 
						|
								            reject(event.data.error as ErrorResponse);
							 | 
						|
								          } else {
							 | 
						|
								            resolve(event.data as ServiceWorkerResponse);
							 | 
						|
								          }
							 | 
						|
								        };
							 | 
						|
								
							 | 
						|
								        navigator.serviceWorker.controller.postMessage(message, [
							 | 
						|
								          messageChannel.port2,
							 | 
						|
								        ]);
							 | 
						|
								      } else {
							 | 
						|
								        reject("Service worker controller not available");
							 | 
						|
								      }
							 | 
						|
								    });
							 | 
						|
								  }
							 | 
						|
								
							 | 
						|
								  private askPermission(): Promise<NotificationPermission> {
							 | 
						|
								    console.log("Requesting permission for notifications:", navigator);
							 | 
						|
								    if (!("serviceWorker" in navigator && navigator.serviceWorker.controller)) {
							 | 
						|
								      return Promise.reject("Service worker not available.");
							 | 
						|
								    }
							 | 
						|
								
							 | 
						|
								    const secret = localStorage.getItem("secret");
							 | 
						|
								    if (!secret) {
							 | 
						|
								      return Promise.reject("No secret found.");
							 | 
						|
								    }
							 | 
						|
								
							 | 
						|
								    return this.sendSecretToServiceWorker(secret)
							 | 
						|
								      .then(() => this.checkNotificationSupport())
							 | 
						|
								      .then(() => this.requestNotificationPermission())
							 | 
						|
								      .catch((error) => Promise.reject(error));
							 | 
						|
								  }
							 | 
						|
								
							 | 
						|
								  private sendSecretToServiceWorker(secret: string): Promise<void> {
							 | 
						|
								    const message: ServiceWorkerMessage = {
							 | 
						|
								      type: "SEND_LOCAL_DATA",
							 | 
						|
								      data: secret,
							 | 
						|
								    };
							 | 
						|
								
							 | 
						|
								    return this.sendMessageToServiceWorker(message).then((response) => {
							 | 
						|
								      console.log("Response from service worker:", response);
							 | 
						|
								    });
							 | 
						|
								  }
							 | 
						|
								
							 | 
						|
								  private checkNotificationSupport(): Promise<void> {
							 | 
						|
								    if (!("Notification" in window)) {
							 | 
						|
								      alert("This browser does not support notifications.");
							 | 
						|
								      return Promise.reject("This browser does not support notifications.");
							 | 
						|
								    }
							 | 
						|
								    if (Notification.permission === "granted") {
							 | 
						|
								      return Promise.resolve();
							 | 
						|
								    }
							 | 
						|
								    return Promise.resolve();
							 | 
						|
								  }
							 | 
						|
								
							 | 
						|
								  private requestNotificationPermission(): Promise<NotificationPermission> {
							 | 
						|
								    return Notification.requestPermission().then((permission) => {
							 | 
						|
								      if (permission !== "granted") {
							 | 
						|
								        alert(
							 | 
						|
								          "Allow this app permission to make notifications for personal reminders." +
							 | 
						|
								            " You can adjust them at any time in your settings.",
							 | 
						|
								        );
							 | 
						|
								        throw new Error("We weren't granted permission.");
							 | 
						|
								      }
							 | 
						|
								      return permission;
							 | 
						|
								    });
							 | 
						|
								  }
							 | 
						|
								
							 | 
						|
								  public async turnOnNotifications() {
							 | 
						|
								    return this.askPermission()
							 | 
						|
								      .then((permission) => {
							 | 
						|
								        console.log("Permission granted:", permission);
							 | 
						|
								
							 | 
						|
								        // Call the function and handle promises
							 | 
						|
								        this.subscribeToPush()
							 | 
						|
								          .then(() => {
							 | 
						|
								            console.log("Subscribed successfully.");
							 | 
						|
								            return navigator.serviceWorker.ready;
							 | 
						|
								          })
							 | 
						|
								          .then((registration) => {
							 | 
						|
								            return registration.pushManager.getSubscription();
							 | 
						|
								          })
							 | 
						|
								          .then(async (subscription) => {
							 | 
						|
								            if (subscription) {
							 | 
						|
								              await this.$notify(
							 | 
						|
								                {
							 | 
						|
								                  group: "alert",
							 | 
						|
								                  type: "info",
							 | 
						|
								                  title: "Notification Setup Underway",
							 | 
						|
								                  text: "Setting up notifications for interesting activity, which takes about 10 seconds. If you don't see a final confirmation, check the 'Troubleshoot' page.",
							 | 
						|
								                },
							 | 
						|
								                -1,
							 | 
						|
								              );
							 | 
						|
								              this.sendSubscriptionToServer(subscription);
							 | 
						|
								              return subscription;
							 | 
						|
								            } else {
							 | 
						|
								              throw new Error("Subscription object is not available.");
							 | 
						|
								            }
							 | 
						|
								          })
							 | 
						|
								          .then(async (subscription) => {
							 | 
						|
								            console.log(
							 | 
						|
								              "Subscription data sent to server and all finished successfully.",
							 | 
						|
								            );
							 | 
						|
								            await sendTestThroughPushServer(subscription, true);
							 | 
						|
								            this.$notify(
							 | 
						|
								              {
							 | 
						|
								                group: "alert",
							 | 
						|
								                type: "success",
							 | 
						|
								                title: "Notifications Turned On",
							 | 
						|
								                text: "Notifications are on. You should see at least one on your device; if not, check the 'Troubleshoot' page.",
							 | 
						|
								              },
							 | 
						|
								              -1,
							 | 
						|
								            );
							 | 
						|
								          })
							 | 
						|
								          .catch((error) => {
							 | 
						|
								            console.error(
							 | 
						|
								              "Subscription or server communication failed:",
							 | 
						|
								              error,
							 | 
						|
								            );
							 | 
						|
								            alert(
							 | 
						|
								              "Subscription or server communication failed. Try again in a while.",
							 | 
						|
								            );
							 | 
						|
								          });
							 | 
						|
								      })
							 | 
						|
								      .catch((error) => {
							 | 
						|
								        console.error(
							 | 
						|
								          "An error occurred setting notification permissions:",
							 | 
						|
								          error,
							 | 
						|
								        );
							 | 
						|
								        alert("Some error occurred setting notification permissions.");
							 | 
						|
								      });
							 | 
						|
								  }
							 | 
						|
								
							 | 
						|
								  private urlBase64ToUint8Array(base64String: string): Uint8Array {
							 | 
						|
								    const padding = "=".repeat((4 - (base64String.length % 4)) % 4);
							 | 
						|
								    const base64 = (base64String + padding)
							 | 
						|
								      .replace(/-/g, "+")
							 | 
						|
								      .replace(/_/g, "/");
							 | 
						|
								    const rawData = window.atob(base64);
							 | 
						|
								    const outputArray = new Uint8Array(rawData.length);
							 | 
						|
								
							 | 
						|
								    for (let i = 0; i < rawData.length; ++i) {
							 | 
						|
								      outputArray[i] = rawData.charCodeAt(i);
							 | 
						|
								    }
							 | 
						|
								    return outputArray;
							 | 
						|
								  }
							 | 
						|
								
							 | 
						|
								  private subscribeToPush(): Promise<void> {
							 | 
						|
								    return new Promise<void>((resolve, reject) => {
							 | 
						|
								      if (!("serviceWorker" in navigator && "PushManager" in window)) {
							 | 
						|
								        const errorMsg = "Push messaging is not supported";
							 | 
						|
								        console.warn(errorMsg);
							 | 
						|
								        return reject(new Error(errorMsg));
							 | 
						|
								      }
							 | 
						|
								
							 | 
						|
								      if (Notification.permission !== "granted") {
							 | 
						|
								        const errorMsg = "Notification permission not granted";
							 | 
						|
								        console.warn(errorMsg);
							 | 
						|
								        return reject(new Error(errorMsg));
							 | 
						|
								      }
							 | 
						|
								
							 | 
						|
								      const applicationServerKey = this.urlBase64ToUint8Array(this.b64);
							 | 
						|
								      const options: PushSubscriptionOptions = {
							 | 
						|
								        userVisibleOnly: true,
							 | 
						|
								        applicationServerKey: applicationServerKey,
							 | 
						|
								      };
							 | 
						|
								
							 | 
						|
								      navigator.serviceWorker.ready
							 | 
						|
								        .then((registration) => {
							 | 
						|
								          return registration.pushManager.subscribe(options);
							 | 
						|
								        })
							 | 
						|
								        .then((subscription) => {
							 | 
						|
								          console.log("Push subscription successful:", subscription);
							 | 
						|
								          resolve();
							 | 
						|
								        })
							 | 
						|
								        .catch((error) => {
							 | 
						|
								          console.error("Push subscription failed:", error, options);
							 | 
						|
								
							 | 
						|
								          // Inform the user about the issue
							 | 
						|
								          alert(
							 | 
						|
								            "We encountered an issue setting up push notifications. " +
							 | 
						|
								              "If you wish to revoke notification permissions, please do so in your browser settings.",
							 | 
						|
								          );
							 | 
						|
								
							 | 
						|
								          reject(error);
							 | 
						|
								        });
							 | 
						|
								    });
							 | 
						|
								  }
							 | 
						|
								
							 | 
						|
								  private sendSubscriptionToServer(
							 | 
						|
								    subscription: PushSubscription,
							 | 
						|
								  ): Promise<void> {
							 | 
						|
								    console.log("About to send subscription...", subscription);
							 | 
						|
								    return fetch("/web-push/subscribe", {
							 | 
						|
								      method: "POST",
							 | 
						|
								      headers: {
							 | 
						|
								        "Content-Type": "application/json",
							 | 
						|
								      },
							 | 
						|
								      body: JSON.stringify(subscription),
							 | 
						|
								    }).then((response) => {
							 | 
						|
								      if (!response.ok) {
							 | 
						|
								        throw new Error("Failed to send subscription to server");
							 | 
						|
								      }
							 | 
						|
								      console.log("Subscription sent to server successfully.");
							 | 
						|
								    });
							 | 
						|
								  }
							 | 
						|
								
							 | 
						|
								  async turnOffNotifications() {
							 | 
						|
								    let subscription;
							 | 
						|
								    const pushProviderSuccess = await navigator.serviceWorker.ready
							 | 
						|
								      .then((registration) => {
							 | 
						|
								        return registration.pushManager.getSubscription();
							 | 
						|
								      })
							 | 
						|
								      .then((subscript) => {
							 | 
						|
								        subscription = subscript;
							 | 
						|
								        if (subscription) {
							 | 
						|
								          return subscription.unsubscribe();
							 | 
						|
								        } else {
							 | 
						|
								          console.log("Subscription object is not available.");
							 | 
						|
								          return false;
							 | 
						|
								        }
							 | 
						|
								      })
							 | 
						|
								      .catch((error) => {
							 | 
						|
								        console.log("Push provider server communication failed:", error);
							 | 
						|
								        return false;
							 | 
						|
								      });
							 | 
						|
								
							 | 
						|
								    const pushServerSuccess = await fetch("/web-push/unsubscribe", {
							 | 
						|
								      method: "POST",
							 | 
						|
								      headers: {
							 | 
						|
								        "Content-Type": "application/json",
							 | 
						|
								      },
							 | 
						|
								      body: JSON.stringify(subscription),
							 | 
						|
								    })
							 | 
						|
								      .then((response) => {
							 | 
						|
								        return response.ok;
							 | 
						|
								      })
							 | 
						|
								      .catch((error) => {
							 | 
						|
								        console.log("Push server communication failed:", error);
							 | 
						|
								        return false;
							 | 
						|
								      });
							 | 
						|
								
							 | 
						|
								    alert(
							 | 
						|
								      "Notifications are off. Push provider unsubscribe " +
							 | 
						|
								        (pushProviderSuccess ? "succeeded" : "failed") +
							 | 
						|
								        (pushProviderSuccess === pushServerSuccess ? " and" : " but") +
							 | 
						|
								        " push server unsubscribe " +
							 | 
						|
								        (pushServerSuccess ? "succeeded" : "failed") +
							 | 
						|
								        ".",
							 | 
						|
								    );
							 | 
						|
								  }
							 | 
						|
								}
							 | 
						|
								</script>
							 | 
						|
								
							 |