forked from trent_larson/crowd-funder-for-time-pwa
add minute to notification scheduling & fix a bug, plus other tweaks
This commit is contained in:
97
src/App.vue
97
src/App.vue
@@ -260,9 +260,16 @@
|
||||
<span class="mt-2">Yes, tell me at: </span>
|
||||
<input
|
||||
type="number"
|
||||
@change="checkHourInput"
|
||||
class="rounded-l border border-r-0 border-slate-400 ml-2 mt-2 px-2 py-2 text-center w-20"
|
||||
v-model="hourInput"
|
||||
/>
|
||||
<input
|
||||
type="number"
|
||||
@change="checkMinuteInput"
|
||||
class="border border-slate-400 mt-2 px-2 py-2 text-center w-20"
|
||||
v-model="minuteInput"
|
||||
/>
|
||||
<span
|
||||
class="rounded-r border border-slate-400 bg-slate-200 text-center text-blue-500 mt-2 px-2 py-2 w-20"
|
||||
@click="hourAm = !hourAm"
|
||||
@@ -377,7 +384,7 @@ import axios from "axios";
|
||||
import { Vue, Component } from "vue-facing-decorator";
|
||||
|
||||
import { DEFAULT_PUSH_SERVER, NotificationIface } from "@/constants/app";
|
||||
import { retrieveSettingsForActiveAccount } from "@/db/index";
|
||||
import { addLogMessage, retrieveSettingsForActiveAccount } from "@/db/index";
|
||||
import * as libsUtil from "@/libs/util";
|
||||
import { urlBase64ToUint8Array } from "@/libs/crypto/vc/util";
|
||||
|
||||
@@ -405,7 +412,8 @@ interface VapidResponse {
|
||||
}
|
||||
|
||||
interface PushSubscriptionWithTime extends PushSubscriptionJSON {
|
||||
notifyTime: { utcHour: number };
|
||||
notifyTime: { utcHour: number; minute: number };
|
||||
notifyType: string;
|
||||
}
|
||||
|
||||
@Component
|
||||
@@ -416,6 +424,7 @@ export default class App extends Vue {
|
||||
b64 = "";
|
||||
hourAm = true;
|
||||
hourInput = "8";
|
||||
minuteInput = "00";
|
||||
serviceWorkerReady = true;
|
||||
|
||||
async mounted() {
|
||||
@@ -429,14 +438,19 @@ export default class App extends Vue {
|
||||
if (pushUrl.startsWith("http://localhost")) {
|
||||
console.log("Not checking for VAPID in this local environment.");
|
||||
} else {
|
||||
let responseData = "";
|
||||
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");
|
||||
});
|
||||
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<unknown> {
|
||||
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<NotificationPermission> {
|
||||
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);
|
||||
})
|
||||
|
||||
@@ -131,3 +131,16 @@ export async function updateAccountSettings(
|
||||
settings.accountDid = accountDid;
|
||||
await updateSettings(settings);
|
||||
}
|
||||
|
||||
export async function addLogMessage(message: string): Promise<void> {
|
||||
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 });
|
||||
}
|
||||
|
||||
@@ -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";
|
||||
|
||||
|
||||
@@ -595,6 +595,7 @@
|
||||
|
||||
<div id="sectionPartnerServerURL" class="mt-2">
|
||||
<span class="text-slate-500 text-sm font-bold">Partner Server URL</span>
|
||||
|
||||
<span class="text-sm">{{ DEFAULT_PARTNER_API_SERVER }}</span>
|
||||
</div>
|
||||
|
||||
@@ -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.");
|
||||
|
||||
@@ -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.",
|
||||
|
||||
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user