From eb675a8ad4f5dc4e6c95c55c7bbb6e36efe9f812 Mon Sep 17 00:00:00 2001 From: Trent Larson Date: Fri, 15 Nov 2024 20:39:08 -0700 Subject: [PATCH] add minute to notification scheduling & fix a bug, plus other tweaks --- src/App.vue | 97 ++++++++++++++++++++++++----- src/db/index.ts | 13 ++++ src/libs/util.ts | 2 +- src/views/AccountViewView.vue | 3 +- src/views/HelpNotificationsView.vue | 6 +- sw_scripts/additional-scripts.js | 7 ++- 6 files changed, 106 insertions(+), 22 deletions(-) diff --git a/src/App.vue b/src/App.vue index a18e36c76..af8a66ed3 100644 --- a/src/App.vue +++ b/src/App.vue @@ -260,9 +260,16 @@ Yes, tell me at: + { 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"); - }); + responseData = JSON.stringify(response.data); + navigator.serviceWorker?.addEventListener( + "controllerchange", + () => { + console.log("New service worker is now controlling the page"); + }, + ); }); if (!this.b64) { this.$notify( @@ -446,7 +460,11 @@ export default class App extends Vue { title: "Error Setting Notifications", text: "Could not set notifications.", }, - -1, + 5000, + ); + await addLogMessage( + "Error Setting Notifications: web push server response didn't have vapidKey: " + + responseData, ); } } @@ -462,7 +480,11 @@ export default class App extends Vue { title: "Error Setting Notifications", text: "Got an error setting notifications.", }, - -1, + 5000, + ); + await addLogMessage( + "Error Setting Notifications: some error occurred: " + + JSON.stringify(error), ); } } @@ -476,7 +498,7 @@ export default class App extends Vue { message: ServiceWorkerMessage, ): Promise { return new Promise((resolve, reject) => { - if (navigator.serviceWorker.controller) { + if (navigator.serviceWorker?.controller) { const messageChannel = new MessageChannel(); messageChannel.port1.onmessage = (event: MessageEvent) => { @@ -487,7 +509,7 @@ export default class App extends Vue { } }; - navigator.serviceWorker.controller.postMessage(message, [ + navigator.serviceWorker?.controller.postMessage(message, [ messageChannel.port2, ]); } else { @@ -498,7 +520,9 @@ export default class App extends Vue { private askPermission(): Promise { console.log("Requesting permission for notifications:", navigator); - if (!("serviceWorker" in navigator && navigator.serviceWorker.controller)) { + if ( + !("serviceWorker" in navigator && navigator.serviceWorker?.controller) + ) { return Promise.reject("Service worker not available."); } @@ -548,6 +572,36 @@ export default class App extends Vue { }); } + private checkHourInput() { + const hourNum = parseInt(this.hourInput); + if (isNaN(hourNum)) { + this.hourInput = "12"; + } else if (hourNum < 1) { + this.hourInput = "12"; + this.hourAm = !this.hourAm; + } else if (hourNum > 12) { + this.hourInput = "1"; + this.hourAm = !this.hourAm; + } else { + this.hourInput = hourNum.toString(); + } + } + + private checkMinuteInput() { + const minuteNum = parseInt(this.minuteInput); + if (isNaN(minuteNum)) { + this.minuteInput = "00"; + } else if (minuteNum < 0) { + this.minuteInput = "59"; + } else if (minuteNum < 10) { + this.minuteInput = "0" + minuteNum; + } else if (minuteNum > 59) { + this.minuteInput = "00"; + } else { + this.minuteInput = minuteNum.toString(); + } + } + // this allows us to show an error without closing the dialog checkHour() { if (!libsUtil.isNumeric(this.hourInput)) { @@ -617,14 +671,29 @@ export default class App extends Vue { ); // we already checked that this is a valid hour number const rawHourNum = libsUtil.numberOrZero(this.hourInput); - const adjHourNum = rawHourNum + (this.hourAm ? 0 : 12); - const hourNum = adjHourNum % 24; + const adjHourNum = this.hourAm + ? // If it's AM, then we'll change it to 0 for 12 AM but otherwise use rawHourNum + rawHourNum === 12 + ? 0 + : rawHourNum + : // Otherwise it's PM, so keep a 12 but otherwise add 12 + rawHourNum === 12 + ? 12 + : rawHourNum + 12; + const hourNum = adjHourNum % 24; // probably unnecessary now const utcHour = hourNum + Math.round(new Date().getTimezoneOffset() / 60); const finalUtcHour = (utcHour + (utcHour < 0 ? 24 : 0)) % 24; + console.log("utc hour:", utcHour, "final utc:", finalUtcHour); + const minuteNum = libsUtil.numberOrZero(this.minuteInput); + const utcMinute = + minuteNum + Math.round(new Date().getTimezoneOffset() % 60); + const finalUtcMinute = + (utcMinute + (utcMinute < 0 ? 60 : 0)) % 60; const subscriptionWithTime: PushSubscriptionWithTime = { - notifyTime: { utcHour: finalUtcHour }, + notifyTime: { utcHour: finalUtcHour, minute: finalUtcMinute }, + notifyType: "DAILY_CHECK", ...subscription.toJSON(), }; await this.sendSubscriptionToServer(subscriptionWithTime); @@ -687,7 +756,7 @@ export default class App extends Vue { applicationServerKey: applicationServerKey, }; - navigator.serviceWorker.ready + navigator.serviceWorker?.ready .then((registration) => { return registration.pushManager.subscribe(options); }) diff --git a/src/db/index.ts b/src/db/index.ts index 442979fb0..2fd8f4ef2 100644 --- a/src/db/index.ts +++ b/src/db/index.ts @@ -131,3 +131,16 @@ export async function updateAccountSettings( settings.accountDid = accountDid; await updateSettings(settings); } + +export async function addLogMessage(message: string): Promise { + await db.open(); + const todayKey = new Date().toDateString(); + // only keep one day's worth of logs + const previous = await db.logs.get(todayKey); + if (!previous) { + // when this is today's first log, clear out everything previous + await db.logs.clear(); + } + const fullMessage = (previous && previous.message) || ""; + await db.logs.update(todayKey, { message: fullMessage + "\n" + message }); +} diff --git a/src/libs/util.ts b/src/libs/util.ts index 07a3b15f0..52b7e5cf6 100644 --- a/src/libs/util.ts +++ b/src/libs/util.ts @@ -374,8 +374,8 @@ export const sendTestThroughPushServer = async ( pushUrl = settings.webPushServer; } - // This is a special value that tells the service worker to send a direct notification to the device, skipping filters. // This is shared with the service worker and should be a constant. Look for the same name in additional-scripts.js + // This is a special value that tells the service worker to send a direct notification to the device, skipping filters. // Use something other than "Daily Update" https://gitea.anomalistdesign.com/trent_larson/py-push-server/src/commit/3c0e196c11bc98060ec5934e99e7dbd591b5da4d/app.py#L213 const DIRECT_PUSH_TITLE = "DIRECT_NOTIFICATION"; diff --git a/src/views/AccountViewView.vue b/src/views/AccountViewView.vue index dee34262d..fe1bb6739 100644 --- a/src/views/AccountViewView.vue +++ b/src/views/AccountViewView.vue @@ -595,6 +595,7 @@
Partner Server URL +   {{ DEFAULT_PARTNER_API_SERVER }}
@@ -830,7 +831,7 @@ export default class AccountViewView extends Vue { /** * Beware! I've seen where this "ready" never resolves. */ - const registration = await navigator.serviceWorker.ready; + const registration = await navigator.serviceWorker?.ready; this.subscription = await registration.pushManager.getSubscription(); this.isSubscribed = !!this.subscription; // console.log("Got to the end of 'mounted' call in AccountViewView."); diff --git a/src/views/HelpNotificationsView.vue b/src/views/HelpNotificationsView.vue index 313e88b6a..6e913a1af 100644 --- a/src/views/HelpNotificationsView.vue +++ b/src/views/HelpNotificationsView.vue @@ -305,8 +305,8 @@ export default class HelpNotificationsView extends Vue { async mounted() { try { - const registration = await navigator.serviceWorker.ready; - const fullSub = await registration.pushManager.getSubscription(); + const registration = await navigator.serviceWorker?.ready; + const fullSub = await registration?.pushManager.getSubscription(); this.subscriptionJSON = fullSub?.toJSON(); } catch (error) { console.error("Mount error:", error); @@ -366,7 +366,7 @@ export default class HelpNotificationsView extends Vue { showTestNotification() { const TEST_NOTIFICATION_TITLE = "It Worked"; - navigator.serviceWorker.ready + navigator.serviceWorker?.ready .then((registration) => { return registration.showNotification(TEST_NOTIFICATION_TITLE, { body: "This is your test notification.", diff --git a/sw_scripts/additional-scripts.js b/sw_scripts/additional-scripts.js index ebdff59c4..c0cce22c5 100644 --- a/sw_scripts/additional-scripts.js +++ b/sw_scripts/additional-scripts.js @@ -63,9 +63,9 @@ self.addEventListener("push", function (event) { // See https://gitea.anomalistdesign.com/trent_larson/py-push-server/src/commit/c1ed026662e754348a5f91542680bd4f57e5b81e/app.py#L217 const DAILY_UPDATE_TITLE = "DAILY_CHECK"; - // This is a special value that tells the service worker to send a direct notification to the device, skipping filters. // This is shared with the notification-test code and should be a constant. Look for the same name in HelpNotificationsView.vue - // Make sure it is something other than the DAILY_UPDATE_TITLE. + // This is a special value that tells the service worker to send a direct notification to the device, skipping filters. + // Make sure it is something different from the DAILY_UPDATE_TITLE. const DIRECT_PUSH_TITLE = "DIRECT_NOTIFICATION"; let title; @@ -133,7 +133,8 @@ self.addEventListener("notificationclick", (event) => { // This is invoked when the user chooses this as a share_target, mapped to share-target in the manifest. self.addEventListener("fetch", (event) => { - logConsoleAndDb("Service worker got fetch event.", event); + // Skipping this because we get so many of them, at startup and other times. + //logConsoleAndDb("Service worker got fetch event.", event); // Bypass any regular requests not related to Web Share Target // and also requests that are not exactly to the timesafari.app