forked from jsnbuchanan/crowd-funder-for-time-pwa
change the notification detection to our own variables, and save the selected time
This commit is contained in:
56
src/App.vue
56
src/App.vue
@@ -229,7 +229,7 @@
|
|||||||
? notification.onCancel(stopAsking)
|
? notification.onCancel(stopAsking)
|
||||||
: null;
|
: null;
|
||||||
close(notification.id);
|
close(notification.id);
|
||||||
stopAsking = false; // reset value
|
stopAsking = false; // reset value for next time they open this modal
|
||||||
"
|
"
|
||||||
class="block w-full text-center text-md font-bold uppercase bg-slate-600 text-white px-2 py-2 rounded-md"
|
class="block w-full text-center text-md font-bold uppercase bg-slate-600 text-white px-2 py-2 rounded-md"
|
||||||
>
|
>
|
||||||
@@ -292,7 +292,7 @@
|
|||||||
<button
|
<button
|
||||||
@click="
|
@click="
|
||||||
close(notification.id);
|
close(notification.id);
|
||||||
turnOffNotifications();
|
turnOffNotifications(notification);
|
||||||
"
|
"
|
||||||
class="block w-full text-center text-md font-bold uppercase bg-rose-600 text-white px-2 py-2 rounded-md mb-2"
|
class="block w-full text-center text-md font-bold uppercase bg-rose-600 text-white px-2 py-2 rounded-md mb-2"
|
||||||
>
|
>
|
||||||
@@ -318,22 +318,26 @@
|
|||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import { Vue, Component } from "vue-facing-decorator";
|
import { Vue, Component } from "vue-facing-decorator";
|
||||||
|
|
||||||
import { logConsoleAndDb } from "@/db/index";
|
import { db, logConsoleAndDb } from "@/db/index";
|
||||||
|
import { NotificationIface } from "./constants/app";
|
||||||
|
import { MASTER_SETTINGS_KEY } from "@/db/tables/settings";
|
||||||
|
|
||||||
@Component
|
@Component
|
||||||
export default class App extends Vue {
|
export default class App extends Vue {
|
||||||
|
$notify!: (notification: NotificationIface, timeout?: number) => void;
|
||||||
|
|
||||||
stopAsking = false;
|
stopAsking = false;
|
||||||
|
|
||||||
async turnOffNotifications() {
|
async turnOffNotifications(notification: NotificationIface) {
|
||||||
let subscription;
|
let subscription;
|
||||||
const pushProviderSuccess = await navigator.serviceWorker?.ready
|
const pushProviderSuccess: boolean = await navigator.serviceWorker?.ready
|
||||||
.then((registration) => {
|
.then((registration) => {
|
||||||
return registration.pushManager.getSubscription();
|
return registration.pushManager.getSubscription();
|
||||||
})
|
})
|
||||||
.then((subscript) => {
|
.then((subscript: PushSubscription | null) => {
|
||||||
subscription = subscript;
|
subscription = subscript;
|
||||||
if (subscription) {
|
if (subscript) {
|
||||||
return subscription.unsubscribe();
|
return subscript.unsubscribe();
|
||||||
} else {
|
} else {
|
||||||
logConsoleAndDb("Subscription object is not available.");
|
logConsoleAndDb("Subscription object is not available.");
|
||||||
return false;
|
return false;
|
||||||
@@ -347,7 +351,7 @@ export default class App extends Vue {
|
|||||||
return false;
|
return false;
|
||||||
});
|
});
|
||||||
|
|
||||||
const pushServerSuccess = await fetch("/web-push/unsubscribe", {
|
const pushServerSuccess: boolean = await fetch("/web-push/unsubscribe", {
|
||||||
method: "POST",
|
method: "POST",
|
||||||
headers: {
|
headers: {
|
||||||
"Content-Type": "application/json",
|
"Content-Type": "application/json",
|
||||||
@@ -365,14 +369,36 @@ export default class App extends Vue {
|
|||||||
return false;
|
return false;
|
||||||
});
|
});
|
||||||
|
|
||||||
alert(
|
let message;
|
||||||
"Notifications are off. Push provider unsubscribe " +
|
if (pushProviderSuccess === pushServerSuccess) {
|
||||||
|
message = "Both local and server notifications ";
|
||||||
|
if (pushProviderSuccess) {
|
||||||
|
message += "are off.";
|
||||||
|
} else {
|
||||||
|
message += "failed to turn off.";
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
message =
|
||||||
|
"Local unsubscribe " +
|
||||||
(pushProviderSuccess ? "succeeded" : "failed") +
|
(pushProviderSuccess ? "succeeded" : "failed") +
|
||||||
(pushProviderSuccess === pushServerSuccess ? " and" : " but") +
|
" but server unsubscribe " +
|
||||||
" push server unsubscribe " +
|
|
||||||
(pushServerSuccess ? "succeeded" : "failed") +
|
(pushServerSuccess ? "succeeded" : "failed") +
|
||||||
".",
|
".";
|
||||||
);
|
}
|
||||||
|
this.$notify({
|
||||||
|
group: "alert",
|
||||||
|
type: "info",
|
||||||
|
title: "Finished",
|
||||||
|
text: message,
|
||||||
|
});
|
||||||
|
|
||||||
|
await db.open();
|
||||||
|
await db.settings.update(MASTER_SETTINGS_KEY, {
|
||||||
|
notifyingNewActivity: false,
|
||||||
|
});
|
||||||
|
if (notification.callback) {
|
||||||
|
notification.callback(pushProviderSuccess && pushServerSuccess);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|||||||
@@ -19,8 +19,7 @@
|
|||||||
Would you like to be notified of new activity once a day?
|
Would you like to be notified of new activity once a day?
|
||||||
</p>
|
</p>
|
||||||
<p v-else class="text-lg mb-4">
|
<p v-else class="text-lg mb-4">
|
||||||
Waiting for system initialization, which may take up to 5
|
Waiting for system initialization, which may take up to 5 seconds...
|
||||||
seconds...
|
|
||||||
<fa icon="spinner" spin />
|
<fa icon="spinner" spin />
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
@@ -74,7 +73,12 @@
|
|||||||
import { Component, Vue } from "vue-facing-decorator";
|
import { Component, Vue } from "vue-facing-decorator";
|
||||||
|
|
||||||
import { DEFAULT_PUSH_SERVER, NotificationIface } from "@/constants/app";
|
import { DEFAULT_PUSH_SERVER, NotificationIface } from "@/constants/app";
|
||||||
import { logConsoleAndDb, retrieveSettingsForActiveAccount } from "@/db/index";
|
import {
|
||||||
|
db,
|
||||||
|
logConsoleAndDb,
|
||||||
|
retrieveSettingsForActiveAccount,
|
||||||
|
} from "@/db/index";
|
||||||
|
import { MASTER_SETTINGS_KEY } from "@/db/tables/settings";
|
||||||
import { urlBase64ToUint8Array } from "@/libs/crypto/vc/util";
|
import { urlBase64ToUint8Array } from "@/libs/crypto/vc/util";
|
||||||
import * as libsUtil from "@/libs/util";
|
import * as libsUtil from "@/libs/util";
|
||||||
|
|
||||||
@@ -108,8 +112,10 @@ interface VapidResponse {
|
|||||||
|
|
||||||
@Component
|
@Component
|
||||||
export default class PushNotificationPermission extends Vue {
|
export default class PushNotificationPermission extends Vue {
|
||||||
$notify!: (notification: NotificationIface, timeout?: number) => void;
|
// eslint-disable-next-line
|
||||||
|
$notify!: (notification: NotificationIface, timeout?: number) => Promise<() => void>;
|
||||||
|
|
||||||
|
callback: (success: boolean, time: string) => void = () => {};
|
||||||
hourAm = true;
|
hourAm = true;
|
||||||
hourInput = "8";
|
hourInput = "8";
|
||||||
isVisible = false;
|
isVisible = false;
|
||||||
@@ -117,8 +123,9 @@ export default class PushNotificationPermission extends Vue {
|
|||||||
serviceWorkerReady = false;
|
serviceWorkerReady = false;
|
||||||
vapidKey = "";
|
vapidKey = "";
|
||||||
|
|
||||||
async open() {
|
async open(callback?: (success: boolean, time: string) => void) {
|
||||||
this.isVisible = true;
|
this.isVisible = true;
|
||||||
|
this.callback = callback || this.callback;
|
||||||
try {
|
try {
|
||||||
const settings = await retrieveSettingsForActiveAccount();
|
const settings = await retrieveSettingsForActiveAccount();
|
||||||
let pushUrl = DEFAULT_PUSH_SERVER;
|
let pushUrl = DEFAULT_PUSH_SERVER;
|
||||||
@@ -253,7 +260,15 @@ export default class PushNotificationPermission extends Vue {
|
|||||||
|
|
||||||
private checkNotificationSupport(): Promise<void> {
|
private checkNotificationSupport(): Promise<void> {
|
||||||
if (!("Notification" in window)) {
|
if (!("Notification" in window)) {
|
||||||
alert("This browser does not support notifications.");
|
this.$notify(
|
||||||
|
{
|
||||||
|
group: "alert",
|
||||||
|
type: "danger",
|
||||||
|
title: "Browser Notifications Not Supported",
|
||||||
|
text: "This browser does not support notifications.",
|
||||||
|
},
|
||||||
|
3000,
|
||||||
|
);
|
||||||
return Promise.reject("This browser does not support notifications.");
|
return Promise.reject("This browser does not support notifications.");
|
||||||
}
|
}
|
||||||
if (window.Notification.permission === "granted") {
|
if (window.Notification.permission === "granted") {
|
||||||
@@ -266,9 +281,16 @@ export default class PushNotificationPermission extends Vue {
|
|||||||
return window.Notification.requestPermission().then(
|
return window.Notification.requestPermission().then(
|
||||||
(permission: string) => {
|
(permission: string) => {
|
||||||
if (permission !== "granted") {
|
if (permission !== "granted") {
|
||||||
alert(
|
this.$notify(
|
||||||
|
{
|
||||||
|
group: "alert",
|
||||||
|
type: "danger",
|
||||||
|
title: "Error Requesting Notification Permission",
|
||||||
|
text:
|
||||||
"Allow this app permission to make notifications for personal reminders." +
|
"Allow this app permission to make notifications for personal reminders." +
|
||||||
" You can adjust them at any time in your settings.",
|
" You can adjust them at any time in your settings.",
|
||||||
|
},
|
||||||
|
-1,
|
||||||
);
|
);
|
||||||
throw new Error("We weren't granted permission.");
|
throw new Error("We weren't granted permission.");
|
||||||
}
|
}
|
||||||
@@ -308,6 +330,7 @@ export default class PushNotificationPermission extends Vue {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public async turnOnNotifications() {
|
public async turnOnNotifications() {
|
||||||
|
let notifyCloser = () => {};
|
||||||
return this.askPermission()
|
return this.askPermission()
|
||||||
.then((permission) => {
|
.then((permission) => {
|
||||||
logConsoleAndDb("Permission granted: " + JSON.stringify(permission));
|
logConsoleAndDb("Permission granted: " + JSON.stringify(permission));
|
||||||
@@ -324,7 +347,7 @@ export default class PushNotificationPermission extends Vue {
|
|||||||
})
|
})
|
||||||
.then(async (subscription) => {
|
.then(async (subscription) => {
|
||||||
if (subscription) {
|
if (subscription) {
|
||||||
await this.$notify(
|
notifyCloser = await this.$notify(
|
||||||
{
|
{
|
||||||
group: "alert",
|
group: "alert",
|
||||||
type: "info",
|
type: "info",
|
||||||
@@ -374,6 +397,7 @@ export default class PushNotificationPermission extends Vue {
|
|||||||
"Subscription data sent to server and all finished successfully.",
|
"Subscription data sent to server and all finished successfully.",
|
||||||
);
|
);
|
||||||
await libsUtil.sendTestThroughPushServer(subscription, true);
|
await libsUtil.sendTestThroughPushServer(subscription, true);
|
||||||
|
notifyCloser();
|
||||||
this.$notify(
|
this.$notify(
|
||||||
{
|
{
|
||||||
group: "alert",
|
group: "alert",
|
||||||
@@ -383,6 +407,14 @@ export default class PushNotificationPermission extends Vue {
|
|||||||
},
|
},
|
||||||
-1,
|
-1,
|
||||||
);
|
);
|
||||||
|
const timeText =
|
||||||
|
// eslint-disable-next-line
|
||||||
|
this.hourInput + ":" + this.minuteInput + " " + (this.hourAm ? "AM" : "PM");
|
||||||
|
await db.settings.update(MASTER_SETTINGS_KEY, {
|
||||||
|
notifyingNewActivity: true,
|
||||||
|
notifyingNewActivityTime: timeText,
|
||||||
|
});
|
||||||
|
this.callback(true, timeText);
|
||||||
})
|
})
|
||||||
.catch((error) => {
|
.catch((error) => {
|
||||||
logConsoleAndDb(
|
logConsoleAndDb(
|
||||||
@@ -393,7 +425,15 @@ export default class PushNotificationPermission extends Vue {
|
|||||||
JSON.stringify(error),
|
JSON.stringify(error),
|
||||||
true,
|
true,
|
||||||
);
|
);
|
||||||
alert("Some error occurred setting notification permissions.");
|
this.$notify(
|
||||||
|
{
|
||||||
|
group: "alert",
|
||||||
|
type: "danger",
|
||||||
|
title: "Error Setting Notification Permissions",
|
||||||
|
text: "Could not set notification permissions.",
|
||||||
|
},
|
||||||
|
3000,
|
||||||
|
);
|
||||||
// unsubscribe just in case we failed after getting a subscription
|
// unsubscribe just in case we failed after getting a subscription
|
||||||
navigator.serviceWorker?.ready
|
navigator.serviceWorker?.ready
|
||||||
.then((registration) => registration.pushManager.getSubscription())
|
.then((registration) => registration.pushManager.getSubscription())
|
||||||
@@ -445,9 +485,16 @@ export default class PushNotificationPermission extends Vue {
|
|||||||
);
|
);
|
||||||
|
|
||||||
// Inform the user about the issue
|
// Inform the user about the issue
|
||||||
alert(
|
this.$notify(
|
||||||
|
{
|
||||||
|
group: "alert",
|
||||||
|
type: "danger",
|
||||||
|
title: "Error Setting Push Notifications",
|
||||||
|
text:
|
||||||
"We encountered an issue setting up push notifications. " +
|
"We encountered an issue setting up push notifications. " +
|
||||||
"If you wish to revoke notification permissions, please do so in your browser settings.",
|
"If you wish to revoke notification permissions, please do so in your browser settings.",
|
||||||
|
},
|
||||||
|
-1,
|
||||||
);
|
);
|
||||||
|
|
||||||
reject(error);
|
reject(error);
|
||||||
@@ -458,7 +505,9 @@ export default class PushNotificationPermission extends Vue {
|
|||||||
private sendSubscriptionToServer(
|
private sendSubscriptionToServer(
|
||||||
subscription: PushSubscriptionWithTime,
|
subscription: PushSubscriptionWithTime,
|
||||||
): Promise<void> {
|
): Promise<void> {
|
||||||
logConsoleAndDb("About to send subscription... " + subscription);
|
logConsoleAndDb(
|
||||||
|
"About to send subscription... " + JSON.stringify(subscription),
|
||||||
|
);
|
||||||
return fetch("/web-push/subscribe", {
|
return fetch("/web-push/subscribe", {
|
||||||
method: "POST",
|
method: "POST",
|
||||||
headers: {
|
headers: {
|
||||||
|
|||||||
@@ -46,11 +46,14 @@ import { MASTER_SETTINGS_KEY } from "@/db/tables/settings";
|
|||||||
export default class UserNameDialog extends Vue {
|
export default class UserNameDialog extends Vue {
|
||||||
$notify!: (notification: NotificationIface, timeout?: number) => void;
|
$notify!: (notification: NotificationIface, timeout?: number) => void;
|
||||||
|
|
||||||
callback: (name?: string) => void = () => {};
|
callback: (name: string) => void = () => {};
|
||||||
givenName = "";
|
givenName = "";
|
||||||
visible = false;
|
visible = false;
|
||||||
|
|
||||||
async open(aCallback?: (name?: string) => void) {
|
/**
|
||||||
|
* @param aCallback - callback function for name, which may be ""
|
||||||
|
*/
|
||||||
|
async open(aCallback?: (name: string) => void) {
|
||||||
this.callback = aCallback || this.callback;
|
this.callback = aCallback || this.callback;
|
||||||
const settings = await retrieveSettingsForActiveAccount();
|
const settings = await retrieveSettingsForActiveAccount();
|
||||||
this.givenName = settings.firstName || "";
|
this.givenName = settings.firstName || "";
|
||||||
|
|||||||
@@ -52,13 +52,14 @@ export const PASSKEYS_ENABLED =
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* The possible values for "group" and "type" are in App.vue.
|
* The possible values for "group" and "type" are in App.vue.
|
||||||
* From the notiwind package
|
* Some of this comes from the notiwind package, some is custom.
|
||||||
*/
|
*/
|
||||||
export interface NotificationIface {
|
export interface NotificationIface {
|
||||||
group: string; // "alert" | "modal"
|
group: string; // "alert" | "modal"
|
||||||
type: string; // "toast" | "info" | "success" | "warning" | "danger"
|
type: string; // "toast" | "info" | "success" | "warning" | "danger"
|
||||||
title: string;
|
title: string;
|
||||||
text?: string;
|
text?: string;
|
||||||
|
callback?: (success: boolean) => Promise<void>; // if this triggered an action
|
||||||
noText?: string;
|
noText?: string;
|
||||||
onCancel?: (stopAsking?: boolean) => Promise<void>;
|
onCancel?: (stopAsking?: boolean) => Promise<void>;
|
||||||
onNo?: (stopAsking?: boolean) => Promise<void>;
|
onNo?: (stopAsking?: boolean) => Promise<void>;
|
||||||
|
|||||||
@@ -39,10 +39,12 @@ export type Settings = {
|
|||||||
lastNotifiedClaimId?: string;
|
lastNotifiedClaimId?: string;
|
||||||
lastViewedClaimId?: string;
|
lastViewedClaimId?: string;
|
||||||
|
|
||||||
|
notifyingNewActivity?: boolean; // set if they have turned on daily check for new activity via the push server
|
||||||
|
notifyingNewActivityTime?: string; // set to their chosen time if they have turned on daily check for new activity via the push server
|
||||||
|
|
||||||
passkeyExpirationMinutes?: number; // passkey access token time-to-live in minutes
|
passkeyExpirationMinutes?: number; // passkey access token time-to-live in minutes
|
||||||
|
|
||||||
profileImageUrl?: string; // may be null if unwanted for a particular account
|
profileImageUrl?: string; // may be null if unwanted for a particular account
|
||||||
reminderTime?: number; // Time in milliseconds since UNIX epoch for reminders
|
|
||||||
reminderOn?: boolean; // Toggle to enable or disable reminders
|
|
||||||
|
|
||||||
// Array of named search boxes defined by bounding boxes
|
// Array of named search boxes defined by bounding boxes
|
||||||
searchBoxes?: Array<{
|
searchBoxes?: Array<{
|
||||||
|
|||||||
@@ -103,6 +103,11 @@ const routes: Array<RouteRecordRaw> = [
|
|||||||
name: "help-notifications",
|
name: "help-notifications",
|
||||||
component: () => import("../views/HelpNotificationsView.vue"),
|
component: () => import("../views/HelpNotificationsView.vue"),
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
path: "/help-notification-types",
|
||||||
|
name: "help-notification-types",
|
||||||
|
component: () => import("../views/HelpNotificationTypesView.vue"),
|
||||||
|
},
|
||||||
{
|
{
|
||||||
path: "/help-onboarding",
|
path: "/help-onboarding",
|
||||||
name: "help-onboarding",
|
name: "help-onboarding",
|
||||||
|
|||||||
@@ -54,7 +54,10 @@
|
|||||||
>
|
>
|
||||||
<button
|
<button
|
||||||
@click="
|
@click="
|
||||||
() => $refs.userNameDialog.open((name) => (this.givenName = name))
|
() =>
|
||||||
|
(this.$refs.userNameDialog as UserNameDialog).open(
|
||||||
|
(name) => (this.givenName = name),
|
||||||
|
)
|
||||||
"
|
"
|
||||||
class="inline-block text-md uppercase bg-gradient-to-b from-blue-400 to-blue-700 shadow-[inset_0_-1px_0_0_rgba(0,0,0,0.5)] text-white px-4 py-2 rounded-md"
|
class="inline-block text-md uppercase bg-gradient-to-b from-blue-400 to-blue-700 shadow-[inset_0_-1px_0_0_rgba(0,0,0,0.5)] text-white px-4 py-2 rounded-md"
|
||||||
>
|
>
|
||||||
@@ -178,19 +181,25 @@
|
|||||||
>
|
>
|
||||||
<!-- label -->
|
<!-- label -->
|
||||||
<div class="mb-2 font-bold">Notifications</div>
|
<div class="mb-2 font-bold">Notifications</div>
|
||||||
<div
|
<div class="flex items-center justify-between">
|
||||||
v-if="!notificationMaybeChanged"
|
|
||||||
class="flex items-center justify-between cursor-pointer"
|
|
||||||
@click="showNotificationChoice()"
|
|
||||||
>
|
|
||||||
<!-- label -->
|
<!-- label -->
|
||||||
<div>App Notifications</div>
|
<div>
|
||||||
|
New Activity Notifications
|
||||||
|
<fa
|
||||||
|
icon="question-circle"
|
||||||
|
class="text-slate-400 fa-fw ml-2 cursor-pointer"
|
||||||
|
@click.stop="showNewActivityNotificationInfo"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
<!-- toggle -->
|
<!-- toggle -->
|
||||||
<div class="relative ml-2">
|
<div
|
||||||
|
class="relative ml-2 cursor-pointer"
|
||||||
|
@click="showNewActivityNotificationChoice()"
|
||||||
|
>
|
||||||
<!-- input -->
|
<!-- input -->
|
||||||
<input
|
<input
|
||||||
type="checkbox"
|
type="checkbox"
|
||||||
v-model="isSubscribed"
|
v-model="notifyingNewActivity"
|
||||||
name="toggleNotificationsInput"
|
name="toggleNotificationsInput"
|
||||||
class="sr-only"
|
class="sr-only"
|
||||||
/>
|
/>
|
||||||
@@ -202,9 +211,8 @@
|
|||||||
></div>
|
></div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div v-else>
|
<div v-if="notifyingNewActivityTime" class="w-full text-right">
|
||||||
Notification status may have changed. Refresh this page to see the
|
{{ notifyingNewActivityTime }}
|
||||||
latest setting.
|
|
||||||
</div>
|
</div>
|
||||||
<router-link class="pl-4 text-sm text-blue-500" to="/help-notifications">
|
<router-link class="pl-4 text-sm text-blue-500" to="/help-notifications">
|
||||||
Troubleshoot your notification setup.
|
Troubleshoot your notification setup.
|
||||||
@@ -803,10 +811,10 @@ export default class AccountViewView extends Vue {
|
|||||||
imageLimits: ImageRateLimits | null = null;
|
imageLimits: ImageRateLimits | null = null;
|
||||||
imageServer = "";
|
imageServer = "";
|
||||||
isRegistered = false;
|
isRegistered = false;
|
||||||
isSubscribed = false;
|
|
||||||
limitsMessage = "";
|
limitsMessage = "";
|
||||||
loadingLimits = false;
|
loadingLimits = false;
|
||||||
notificationMaybeChanged = false;
|
notifyingNewActivity = false;
|
||||||
|
notifyingNewActivityTime = "";
|
||||||
passkeyExpirationDescription = "";
|
passkeyExpirationDescription = "";
|
||||||
passkeyExpirationMinutes = DEFAULT_PASSKEY_EXPIRATION_MINUTES;
|
passkeyExpirationMinutes = DEFAULT_PASSKEY_EXPIRATION_MINUTES;
|
||||||
previousPasskeyExpirationMinutes = DEFAULT_PASSKEY_EXPIRATION_MINUTES;
|
previousPasskeyExpirationMinutes = DEFAULT_PASSKEY_EXPIRATION_MINUTES;
|
||||||
@@ -849,7 +857,12 @@ export default class AccountViewView extends Vue {
|
|||||||
*/
|
*/
|
||||||
const registration = await navigator.serviceWorker?.ready;
|
const registration = await navigator.serviceWorker?.ready;
|
||||||
this.subscription = await registration.pushManager.getSubscription();
|
this.subscription = await registration.pushManager.getSubscription();
|
||||||
this.isSubscribed = !!this.subscription;
|
if (!this.subscription) {
|
||||||
|
if (this.notifyingNewActivity) {
|
||||||
|
// the app thought there was a subscription but there isn't, so fix the settings
|
||||||
|
this.turnOffNotifyingFlags();
|
||||||
|
}
|
||||||
|
}
|
||||||
// console.log("Got to the end of 'mounted' call in AccountViewView.");
|
// console.log("Got to the end of 'mounted' call in AccountViewView.");
|
||||||
/**
|
/**
|
||||||
* Beware! I've seen where we never get to this point because "ready" never resolves.
|
* Beware! I've seen where we never get to this point because "ready" never resolves.
|
||||||
@@ -902,6 +915,8 @@ export default class AccountViewView extends Vue {
|
|||||||
this.showContactGives = !!settings.showContactGivesInline;
|
this.showContactGives = !!settings.showContactGivesInline;
|
||||||
this.hideRegisterPromptOnNewContact =
|
this.hideRegisterPromptOnNewContact =
|
||||||
!!settings.hideRegisterPromptOnNewContact;
|
!!settings.hideRegisterPromptOnNewContact;
|
||||||
|
this.notifyingNewActivity = !!settings.notifyingNewActivity;
|
||||||
|
this.notifyingNewActivityTime = settings.notifyingNewActivityTime || "";
|
||||||
this.passkeyExpirationMinutes =
|
this.passkeyExpirationMinutes =
|
||||||
settings.passkeyExpirationMinutes ?? DEFAULT_PASSKEY_EXPIRATION_MINUTES;
|
settings.passkeyExpirationMinutes ?? DEFAULT_PASSKEY_EXPIRATION_MINUTES;
|
||||||
this.previousPasskeyExpirationMinutes = this.passkeyExpirationMinutes;
|
this.previousPasskeyExpirationMinutes = this.passkeyExpirationMinutes;
|
||||||
@@ -921,29 +936,44 @@ export default class AccountViewView extends Vue {
|
|||||||
.then(() => setTimeout(fn, 2000));
|
.then(() => setTimeout(fn, 2000));
|
||||||
}
|
}
|
||||||
|
|
||||||
toggleShowContactAmounts() {
|
async toggleShowContactAmounts() {
|
||||||
this.showContactGives = !this.showContactGives;
|
this.showContactGives = !this.showContactGives;
|
||||||
this.updateShowContactAmounts();
|
await db.open();
|
||||||
|
await db.settings.update(MASTER_SETTINGS_KEY, {
|
||||||
|
showContactGivesInline: this.showContactGives,
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
toggleShowGeneralAdvanced() {
|
async toggleShowGeneralAdvanced() {
|
||||||
this.showGeneralAdvanced = !this.showGeneralAdvanced;
|
this.showGeneralAdvanced = !this.showGeneralAdvanced;
|
||||||
this.updateShowGeneralAdvanced();
|
await db.open();
|
||||||
|
await db.settings.update(MASTER_SETTINGS_KEY, {
|
||||||
|
showGeneralAdvanced: this.showGeneralAdvanced,
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
toggleProdWarning() {
|
async toggleProdWarning() {
|
||||||
this.warnIfProdServer = !this.warnIfProdServer;
|
this.warnIfProdServer = !this.warnIfProdServer;
|
||||||
this.updateWarnIfProdServer(this.warnIfProdServer);
|
await db.open();
|
||||||
|
await db.settings.update(MASTER_SETTINGS_KEY, {
|
||||||
|
warnIfProdServer: this.warnIfProdServer,
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
toggleTestWarning() {
|
async toggleTestWarning() {
|
||||||
this.warnIfTestServer = !this.warnIfTestServer;
|
this.warnIfTestServer = !this.warnIfTestServer;
|
||||||
this.updateWarnIfTestServer(this.warnIfTestServer);
|
await db.open();
|
||||||
|
await db.settings.update(MASTER_SETTINGS_KEY, {
|
||||||
|
warnIfTestServer: this.warnIfTestServer,
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
toggleShowShortcutBvc() {
|
async toggleShowShortcutBvc() {
|
||||||
this.showShortcutBvc = !this.showShortcutBvc;
|
this.showShortcutBvc = !this.showShortcutBvc;
|
||||||
this.updateShowShortcutBvc(this.showShortcutBvc);
|
await db.open();
|
||||||
|
await db.settings.update(MASTER_SETTINGS_KEY, {
|
||||||
|
showShortcutBvc: this.showShortcutBvc,
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
readableDate(timeStr: string) {
|
readableDate(timeStr: string) {
|
||||||
@@ -968,11 +998,39 @@ export default class AccountViewView extends Vue {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async showNotificationChoice() {
|
async showNewActivityNotificationInfo() {
|
||||||
if (!this.subscription) {
|
this.$notify(
|
||||||
|
{
|
||||||
|
group: "modal",
|
||||||
|
type: "confirm",
|
||||||
|
title: "New Activity Notification",
|
||||||
|
text: `
|
||||||
|
This will only notify you when there is new relevant activity for you personally.
|
||||||
|
Note that it runs on your device and many factors may affect delivery,
|
||||||
|
so if you want a reliable but simple daily notification then choose a 'Reminder'.
|
||||||
|
Do you want more details?
|
||||||
|
`,
|
||||||
|
onYes: async () => {
|
||||||
|
await (this.$router as Router).push({
|
||||||
|
name: "help-notification-types",
|
||||||
|
});
|
||||||
|
},
|
||||||
|
yesText: "tell me more.",
|
||||||
|
},
|
||||||
|
-1,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
async showNewActivityNotificationChoice() {
|
||||||
|
if (!this.notifyingNewActivity) {
|
||||||
(
|
(
|
||||||
this.$refs.pushNotificationPermission as PushNotificationPermission
|
this.$refs.pushNotificationPermission as PushNotificationPermission
|
||||||
).open();
|
).open((success: boolean, time: string) => {
|
||||||
|
if (success) {
|
||||||
|
this.notifyingNewActivity = true;
|
||||||
|
this.notifyingNewActivityTime = time;
|
||||||
|
}
|
||||||
|
});
|
||||||
} else {
|
} else {
|
||||||
this.$notify(
|
this.$notify(
|
||||||
{
|
{
|
||||||
@@ -980,55 +1038,16 @@ export default class AccountViewView extends Vue {
|
|||||||
type: "notification-off",
|
type: "notification-off",
|
||||||
title: "", // unused, only here to satisfy type check
|
title: "", // unused, only here to satisfy type check
|
||||||
text: "", // unused, only here to satisfy type check
|
text: "", // unused, only here to satisfy type check
|
||||||
|
callback: async (success) => {
|
||||||
|
if (success) {
|
||||||
|
this.notifyingNewActivity = false;
|
||||||
|
this.notifyingNewActivityTime = "";
|
||||||
|
}
|
||||||
|
},
|
||||||
},
|
},
|
||||||
-1,
|
-1,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
this.notificationMaybeChanged = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
public async updateShowContactAmounts() {
|
|
||||||
await db.open();
|
|
||||||
await db.settings.update(MASTER_SETTINGS_KEY, {
|
|
||||||
showContactGivesInline: this.showContactGives,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
public async updateShowGeneralAdvanced() {
|
|
||||||
await db.open();
|
|
||||||
await db.settings.update(MASTER_SETTINGS_KEY, {
|
|
||||||
showGeneralAdvanced: this.showGeneralAdvanced,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
public async updateWarnIfProdServer(newSetting: boolean) {
|
|
||||||
try {
|
|
||||||
await db.open();
|
|
||||||
await db.settings.update(MASTER_SETTINGS_KEY, {
|
|
||||||
warnIfProdServer: newSetting,
|
|
||||||
});
|
|
||||||
} catch (err) {
|
|
||||||
this.$notify(
|
|
||||||
{
|
|
||||||
group: "alert",
|
|
||||||
type: "danger",
|
|
||||||
title: "Error Updating Prod Warning",
|
|
||||||
text: "The setting may not have saved. Try again, maybe after restarting the app.",
|
|
||||||
},
|
|
||||||
-1,
|
|
||||||
);
|
|
||||||
console.error(
|
|
||||||
"Telling user to try again after prod-server-warning setting update because:",
|
|
||||||
err,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public async updateWarnIfTestServer(newSetting: boolean) {
|
|
||||||
await db.open();
|
|
||||||
await db.settings.update(MASTER_SETTINGS_KEY, {
|
|
||||||
warnIfTestServer: newSetting,
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public async toggleHideRegisterPromptOnNewContact() {
|
public async toggleHideRegisterPromptOnNewContact() {
|
||||||
@@ -1049,11 +1068,14 @@ export default class AccountViewView extends Vue {
|
|||||||
this.passkeyExpirationDescription = tokenExpiryTimeDescription();
|
this.passkeyExpirationDescription = tokenExpiryTimeDescription();
|
||||||
}
|
}
|
||||||
|
|
||||||
public async updateShowShortcutBvc(newSetting: boolean) {
|
public async turnOffNotifyingFlags() {
|
||||||
await db.open();
|
await db.open();
|
||||||
await db.settings.update(MASTER_SETTINGS_KEY, {
|
await db.settings.update(MASTER_SETTINGS_KEY, {
|
||||||
showShortcutBvc: newSetting,
|
notifyingNewActivity: false,
|
||||||
|
notifyingNewActivityTime: "",
|
||||||
});
|
});
|
||||||
|
this.notifyingNewActivity = false;
|
||||||
|
this.notifyingNewActivityTime = "";
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
68
src/views/HelpNotificationTypesView.vue
Normal file
68
src/views/HelpNotificationTypesView.vue
Normal file
@@ -0,0 +1,68 @@
|
|||||||
|
<template>
|
||||||
|
<QuickNav />
|
||||||
|
|
||||||
|
<!-- CONTENT -->
|
||||||
|
<section id="Content" class="p-6 pb-24 max-w-3xl mx-auto">
|
||||||
|
<!-- Breadcrumb -->
|
||||||
|
<div class="mb-8">
|
||||||
|
<!-- Back -->
|
||||||
|
<div class="text-lg text-center font-light relative px-7">
|
||||||
|
<h1
|
||||||
|
class="text-lg text-center px-2 py-1 absolute -left-2 -top-1"
|
||||||
|
@click="$router.back()"
|
||||||
|
>
|
||||||
|
<fa icon="chevron-left" class="fa-fw"></fa>
|
||||||
|
</h1>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Heading -->
|
||||||
|
<h1 id="ViewHeading" class="text-4xl text-center font-light pt-4 mb-8">
|
||||||
|
Notification Types
|
||||||
|
</h1>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- eslint-disable prettier/prettier -->
|
||||||
|
<div>
|
||||||
|
<p>There are two types of notifications:</p>
|
||||||
|
|
||||||
|
<h2 class="text-xl font-semibold mt-4">Reminder Notifications</h2>
|
||||||
|
<div>
|
||||||
|
<p>
|
||||||
|
The Reminder Notification will be sent to you daily with a specific message,
|
||||||
|
at whatever time you choose. Use it to remind
|
||||||
|
yourself to act, for example: pause and consider who has given you
|
||||||
|
something, so you can record thanks in here.
|
||||||
|
</p>
|
||||||
|
<p>
|
||||||
|
This is a reliable message, but it doesn't contain any details about
|
||||||
|
activity that might be especially interesting to you.
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<h2 class="text-xl font-semibold mt-4">New Activity Notifications</h2>
|
||||||
|
<div>
|
||||||
|
<p>
|
||||||
|
The New Activity Notification will be sent to you when there is new, relevant activity for you.
|
||||||
|
It will only trigger if something involves you or a project of interest; it will not
|
||||||
|
bug you for other, general activity.
|
||||||
|
</p>
|
||||||
|
<p>
|
||||||
|
This type is not as reliable as a Reminder Notification because mobile devices often suppress
|
||||||
|
such notifications to save battery. (We are working on other ways to notify you more
|
||||||
|
reliably. If you want to quickly check for relevant activity daily, use the Reminder
|
||||||
|
Notification and open the app and look for a large green button that points out new
|
||||||
|
activity that is personal to you.)
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<!-- eslint-enable -->
|
||||||
|
</section>
|
||||||
|
</template>
|
||||||
|
<script lang="ts">
|
||||||
|
import { Component, Vue } from "vue-facing-decorator";
|
||||||
|
|
||||||
|
import QuickNav from "@/components/QuickNav.vue";
|
||||||
|
|
||||||
|
@Component({ components: { QuickNav } })
|
||||||
|
export default class HelpNotificationTypesView extends Vue {}
|
||||||
|
</script>
|
||||||
Reference in New Issue
Block a user