start the SMS notifications, beginning with the UI

This commit is contained in:
2026-07-19 20:23:34 -06:00
parent cdf5c721a5
commit ee8fb80cfe
3 changed files with 210 additions and 25 deletions

View File

@@ -57,9 +57,12 @@ export const ACCOUNT_VIEW_CONSTANTS = {
// Notification messages
NOTIFICATIONS: {
NEW_ACTIVITY_INFO: `
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'.
This will notify you with an app message when there is new relevant activity for you personally.
Do you want more details?
`,
NEW_ACTIVITY_SMS_INFO: `
This is opt-in: by turning it on you agree to receive text messages when there is new relevant activity for you personally.
Standard message and data rates may apply, and you can turn it off at any time.
Do you want more details?
`,
REMINDER_INFO: `

View File

@@ -143,24 +143,48 @@
</button>
</div>
</div>
<div class="flex items-center justify-between mt-4 mb-2">
<!-- label -->
<div>
<!--
New Activity Notification: two delivery channels for the same alert.
Both let the user pick a time; they differ only in how the alert is
delivered (and therefore in the parameters passed to the notify-api):
- In-App: on-device notification via the daily-notification-plugin.
- SMS: text message sent by the notify-api (opt-in).
-->
<div class="mt-4 mb-1">
<div class="font-semibold">
New Activity Notification
<button
class="text-slate-400 fa-fw cursor-pointer"
aria-label="Learn more about New Activity notifications"
@click.stop="showNewActivityNotificationInfo"
@click.stop="showNewActivityNotificationInfo(false)"
>
<font-awesome icon="question-circle" aria-hidden="true" />
</button>
</div>
<p class="text-xs text-slate-500">
Get told about new relevant activity for you. Choose a delivery
channel below -- you can turn on either one, or both.
</p>
</div>
<!-- Channel 1: In-App notification -->
<div class="flex items-center justify-between mt-3 mb-2 pl-1">
<!-- label -->
<div>
<font-awesome
icon="comment"
class="fa-fw text-slate-400"
aria-hidden="true"
/>
In-App Notification
</div>
<!-- toggle -->
<div
class="relative ml-2 cursor-pointer"
role="switch"
:aria-checked="notifyingNewActivity"
aria-label="Toggle New Activity notifications"
aria-label="Toggle New Activity notifications in app"
tabindex="0"
@click.stop.prevent="showNewActivityNotificationChoice()"
>
@@ -178,7 +202,7 @@
></div>
</div>
</div>
<div v-if="notifyingNewActivity" class="w-full">
<div v-if="notifyingNewActivity" class="w-full pl-1">
<div
class="text-sm text-slate-500 mb-2 bg-white rounded px-3 py-2 border border-slate-200"
>
@@ -191,10 +215,75 @@
class="w-full text-md 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"
@click="editNewActivityNotification"
>
Edit New Activity Notification
Edit In-App Time
</button>
</div>
</div>
<!-- Channel 2: SMS text message (opt-in) -->
<div class="flex items-center justify-between mt-3 mb-2 pl-1">
<!-- label -->
<div>
<font-awesome
icon="message"
class="fa-fw text-slate-400"
aria-hidden="true"
/>
SMS Text Message
<button
class="text-slate-400 fa-fw cursor-pointer"
aria-label="Learn more about New Activity SMS notifications"
@click.stop="showNewActivityNotificationInfo(true)"
>
<font-awesome icon="question-circle" aria-hidden="true" />
</button>
</div>
<!-- toggle -->
<div
class="relative ml-2 cursor-pointer"
role="switch"
:aria-checked="notifyingNewActivitySms"
aria-label="Toggle New Activity notifications via SMS"
tabindex="0"
@click.stop.prevent="showNewActivitySmsNotificationChoice()"
>
<input
:checked="notifyingNewActivitySms"
type="checkbox"
class="sr-only"
readonly
@click.stop.prevent
@change.stop.prevent
/>
<div class="block bg-slate-500 w-14 h-8 rounded-full"></div>
<div
class="dot absolute left-1 top-1 bg-slate-400 w-6 h-6 rounded-full transition"
></div>
</div>
</div>
<div v-if="notifyingNewActivitySms" class="w-full pl-1">
<div
class="text-sm text-slate-500 mb-2 bg-white rounded px-3 py-2 border border-slate-200"
>
<div>
<b>Time:</b>
{{ notifyingNewActivitySmsTime.replace(" ", "&nbsp;") }}
</div>
<div>
<b>Text to:</b>
<i>{{ notifyingNewActivitySmsPhone || "(number not set)" }}</i>
</div>
</div>
<div class="mt-2 text-center">
<button
class="w-full text-md 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"
@click="editNewActivitySmsNotification"
>
Edit SMS Time
</button>
</div>
</div>
<div class="mt-2 text-center">
<router-link class="text-sm text-blue-500" to="/help-notifications">
Troubleshoot your notifications&hellip;
@@ -546,7 +635,8 @@
</div>
<!-- Notification Push Server setting - only show on web platforms -->
<div v-if="!isNativePlatform">
<!-- This will be removed because we're no longer using PWA notifications. -->
<div v-if="false">
<h2 class="text-slate-500 text-sm font-bold mb-2">
Notification Push Server
</h2>
@@ -931,6 +1021,12 @@ export default class AccountViewView extends Vue {
// Notification properties
notifyingNewActivity: boolean = false;
notifyingNewActivityTime: string = "";
// SMS delivery channel for New Activity (opt-in). MOCK-UP: state is currently
// component-local. TODO: persist via settings + register the number and time
// with the notify-api (SMS registration/verification flow).
notifyingNewActivitySms: boolean = false;
notifyingNewActivitySmsTime: string = "";
notifyingNewActivitySmsPhone: string = "";
notifyingReminder: boolean = false;
notifyingReminderMessage: string = "";
notifyingReminderTime: string = "";
@@ -1216,9 +1312,11 @@ export default class AccountViewView extends Vue {
}
}
async showNewActivityNotificationInfo(): Promise<void> {
async showNewActivityNotificationInfo(isSms: boolean): Promise<void> {
this.notify.confirm(
ACCOUNT_VIEW_CONSTANTS.NOTIFICATIONS.NEW_ACTIVITY_INFO,
isSms
? ACCOUNT_VIEW_CONSTANTS.NOTIFICATIONS.NEW_ACTIVITY_SMS_INFO
: ACCOUNT_VIEW_CONSTANTS.NOTIFICATIONS.NEW_ACTIVITY_INFO,
async () => {
await (this.$router as Router).push({
name: "help-notification-types",
@@ -1266,6 +1364,89 @@ export default class AccountViewView extends Vue {
}
}
/**
* Toggle the SMS delivery channel for New Activity notifications.
*
* MOCK-UP: reuses the time-picker dialog so the UI behaves like the in-app
* channel, but the picked time is only held in component state for now.
*
* TODO (notify-api wiring): on enable, ensure the user has a verified phone
* number, then POST the SMS-notification preference to the notify-api with
* SMS-specific params (channel="sms", phoneNumber, sendTime) -- the same
* alert as the in-app channel, delivered by a different transport. On
* disable, tell the notify-api to remove the SMS preference.
*/
async showNewActivitySmsNotificationChoice(): Promise<void> {
if (!this.notifyingNewActivitySms) {
(
this.$refs.pushNotificationPermission as PushNotificationPermission
).open(
DAILY_CHECK_TITLE,
async (success: boolean, timeText: string) => {
if (success) {
// TODO: verify phone number + register SMS preference with notify-api
this.notifyingNewActivitySms = true;
this.notifyingNewActivitySmsTime = timeText;
}
},
{ skipSchedule: true },
);
} else {
// TODO: remove SMS preference from notify-api
this.notifyingNewActivitySms = false;
this.notifyingNewActivitySmsTime = "";
}
}
/**
* Edit the time for the SMS delivery channel.
*
* MOCK-UP: updates component state only. TODO: PUT the updated sendTime to
* the notify-api SMS preference.
*/
async editNewActivitySmsNotification(): Promise<void> {
const dialog = this.$refs
.pushNotificationPermission as PushNotificationPermission;
dialog.open(
DAILY_CHECK_TITLE,
async (success: boolean, timeText: string) => {
if (!success) return;
// TODO: PUT updated sendTime to notify-api SMS preference
this.notifyingNewActivitySmsTime = timeText;
this.notify.success(
"New Activity SMS time updated.",
TIMEOUTS.STANDARD,
);
},
{ skipSchedule: true },
);
// Pre-populate the dialog with the current SMS time
setTimeout(() => {
const timeMatch = this.notifyingNewActivitySmsTime.match(
/(\d+):(\d+)\s*(AM|PM)/i,
);
if (timeMatch) {
let hour = parseInt(timeMatch[1], 10);
const minute = timeMatch[2];
const isAm = timeMatch[3].toUpperCase() === "AM";
if (hour === 12) {
hour = 12;
} else if (hour > 12) {
hour = hour - 12;
}
const dialogComponent =
dialog as unknown as PushNotificationPermissionRef;
if (dialogComponent) {
dialogComponent.hourInput = hour.toString();
dialogComponent.minuteInput = minute;
dialogComponent.hourAm = isAm;
}
}
}, 150);
}
/**
* Configure native fetcher, sync starred plans, and schedule API-driven dual notification.
*/

View File

@@ -39,31 +39,32 @@
something, so you can record thanks in here.
</p>
<p>
This is a reliable message, but it doesn't contain any details about
This is your own message activated in the app, but it doesn't contain any details about
activity that might be especially interesting to you.
Note that the timing is not precise: the device may deliver it a bit later than scheduled.
</p>
</div>
<h2 class="text-xl font-semibold mt-4">New Activity Notifications</h2>
<h2 class="text-xl font-semibold mt-4">New Activity Notifications -- In App</h2>
<div>
<p>
The New Activity Notification will be sent to you when there is new, relevant activity
The New Activity Notification will be activated in your app 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>
</div>
<h2 class="text-xl font-semibold mt-4">New Activity Notifications -- Texts From A Server</h2>
<div>
<p>
This type is not as reliable as a Reminder Notification because mobile devices often
suppress such notifications to save battery. (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. We are working on other
ways to notify you more reliably.
<router-link class="text-blue-500" to="/help">
go here to follow us or contact us
<font-awesome icon="chevron-right" class="fa-fw"></font-awesome>
</router-link>.)
This New Activity Notification SMS 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>
</div>
</div>
<!-- eslint-enable -->
</section>