Browse Source

change the notification detection to our own variables, and save the selected time

Trent Larson 3 days ago
parent
commit
2b6f87f0aa
  1. 56
      src/App.vue
  2. 71
      src/components/PushNotificationPermission.vue
  3. 7
      src/components/UserNameDialog.vue
  4. 3
      src/constants/app.ts
  5. 6
      src/db/tables/settings.ts
  6. 5
      src/router/index.ts
  7. 166
      src/views/AccountViewView.vue
  8. 68
      src/views/HelpNotificationTypesView.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>

71
src/components/PushNotificationPermission.vue

@ -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: {

7
src/components/UserNameDialog.vue

@ -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 || "";

3
src/constants/app.ts

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

6
src/db/tables/settings.ts

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

5
src/router/index.ts

@ -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",

166
src/views/AccountViewView.vue

@ -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,67 +998,56 @@ export default class AccountViewView extends Vue {
} }
} }
async showNotificationChoice() { async showNewActivityNotificationInfo() {
if (!this.subscription) {
(
this.$refs.pushNotificationPermission as PushNotificationPermission
).open();
} else {
this.$notify( this.$notify(
{ {
group: "modal", group: "modal",
type: "notification-off", type: "confirm",
title: "", // unused, only here to satisfy type check title: "New Activity Notification",
text: "", // unused, only here to satisfy type check 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, -1,
); );
} }
this.notificationMaybeChanged = true;
}
public async updateShowContactAmounts() { async showNewActivityNotificationChoice() {
await db.open(); if (!this.notifyingNewActivity) {
await db.settings.update(MASTER_SETTINGS_KEY, { (
showContactGivesInline: this.showContactGives, this.$refs.pushNotificationPermission as PushNotificationPermission
}); ).open((success: boolean, time: string) => {
if (success) {
this.notifyingNewActivity = true;
this.notifyingNewActivityTime = time;
} }
public async updateShowGeneralAdvanced() {
await db.open();
await db.settings.update(MASTER_SETTINGS_KEY, {
showGeneralAdvanced: this.showGeneralAdvanced,
}); });
} } else {
public async updateWarnIfProdServer(newSetting: boolean) {
try {
await db.open();
await db.settings.update(MASTER_SETTINGS_KEY, {
warnIfProdServer: newSetting,
});
} catch (err) {
this.$notify( this.$notify(
{ {
group: "alert", group: "modal",
type: "danger", type: "notification-off",
title: "Error Updating Prod Warning", title: "", // unused, only here to satisfy type check
text: "The setting may not have saved. Try again, maybe after restarting the app.", text: "", // unused, only here to satisfy type check
callback: async (success) => {
if (success) {
this.notifyingNewActivity = false;
this.notifyingNewActivityTime = "";
}
},
}, },
-1, -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

@ -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>
Loading…
Cancel
Save