forked from trent_larson/crowd-funder-for-time-pwa
fix(notifications): New Activity vs Daily Reminder separation and copy
- PushNotificationPermission: show "Turn on New Activity Notifications" when enabling New Activity; use NOTIFY_PUSH_SUCCESS_NEW_ACTIVITY for success toast so copy says "New Activity notifications are now enabled." - App.vue: on native, turnOffNotifications invokes the modal's callback only (fixes turn-off not updating state); add comment that callback is per notification type. - AccountViewView: handle plugin UNIMPLEMENTED for scheduleDualNotification on iOS with friendlier message; add New Activity time block and "Edit New Activity Notification…"; rename Daily Reminder button to "Edit Daily Reminder…". - Constants: add NOTIFY_PUSH_SUCCESS_NEW_ACTIVITY. Reminder IDs and Option A (skip single reminder for New Activity) from earlier commit.
This commit is contained in:
19
src/App.vue
19
src/App.vue
@@ -360,6 +360,7 @@
|
|||||||
|
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import { Vue, Component } from "vue-facing-decorator";
|
import { Vue, Component } from "vue-facing-decorator";
|
||||||
|
import { Capacitor } from "@capacitor/core";
|
||||||
|
|
||||||
import { NotificationIface } from "./constants/app";
|
import { NotificationIface } from "./constants/app";
|
||||||
import { PlatformServiceMixin } from "@/utils/PlatformServiceMixin";
|
import { PlatformServiceMixin } from "@/utils/PlatformServiceMixin";
|
||||||
@@ -382,6 +383,24 @@ export default class App extends Vue {
|
|||||||
async turnOffNotifications(
|
async turnOffNotifications(
|
||||||
notification: NotificationIface,
|
notification: NotificationIface,
|
||||||
): Promise<boolean> {
|
): Promise<boolean> {
|
||||||
|
// On native (iOS/Android) we don't use web push; the callback handles cancel + state in the view.
|
||||||
|
// The callback is the one passed for this specific modal (New Activity or Daily Reminder), so we only turn off that one.
|
||||||
|
if (Capacitor.isNativePlatform()) {
|
||||||
|
if (notification.callback) {
|
||||||
|
await notification.callback(true);
|
||||||
|
}
|
||||||
|
this.$notify(
|
||||||
|
{
|
||||||
|
group: "alert",
|
||||||
|
type: "info",
|
||||||
|
title: "Finished",
|
||||||
|
text: "Notifications are off.",
|
||||||
|
},
|
||||||
|
5000,
|
||||||
|
);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
let subscription: PushSubscriptionJSON | null = null;
|
let subscription: PushSubscriptionJSON | null = null;
|
||||||
let allGoingOff = false;
|
let allGoingOff = false;
|
||||||
|
|
||||||
|
|||||||
@@ -67,7 +67,7 @@
|
|||||||
class="block w-full text-center text-md font-bold uppercase bg-blue-600 text-white mt-2 px-2 py-2 rounded-md"
|
class="block w-full text-center text-md font-bold uppercase bg-blue-600 text-white mt-2 px-2 py-2 rounded-md"
|
||||||
@click="handleTurnOnNotifications"
|
@click="handleTurnOnNotifications"
|
||||||
>
|
>
|
||||||
Turn on Daily Reminder
|
{{ isDailyCheck ? "Turn on New Activity Notifications" : "Turn on Daily Reminder" }}
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@@ -95,6 +95,7 @@ import {
|
|||||||
NOTIFY_PUSH_PERMISSION_ERROR,
|
NOTIFY_PUSH_PERMISSION_ERROR,
|
||||||
NOTIFY_PUSH_SETUP_UNDERWAY,
|
NOTIFY_PUSH_SETUP_UNDERWAY,
|
||||||
NOTIFY_PUSH_SUCCESS,
|
NOTIFY_PUSH_SUCCESS,
|
||||||
|
NOTIFY_PUSH_SUCCESS_NEW_ACTIVITY,
|
||||||
NOTIFY_PUSH_SETUP_ERROR,
|
NOTIFY_PUSH_SETUP_ERROR,
|
||||||
NOTIFY_PUSH_SUBSCRIPTION_ERROR,
|
NOTIFY_PUSH_SUBSCRIPTION_ERROR,
|
||||||
PUSH_NOTIFICATION_TIMEOUT_SHORT,
|
PUSH_NOTIFICATION_TIMEOUT_SHORT,
|
||||||
@@ -775,8 +776,8 @@ export default class PushNotificationPermission extends Vue {
|
|||||||
{
|
{
|
||||||
group: "alert",
|
group: "alert",
|
||||||
type: "success",
|
type: "success",
|
||||||
title: NOTIFY_PUSH_SUCCESS.title,
|
title: NOTIFY_PUSH_SUCCESS_NEW_ACTIVITY.title,
|
||||||
text: NOTIFY_PUSH_SUCCESS.message,
|
text: NOTIFY_PUSH_SUCCESS_NEW_ACTIVITY.message,
|
||||||
},
|
},
|
||||||
PUSH_NOTIFICATION_TIMEOUT_LONG,
|
PUSH_NOTIFICATION_TIMEOUT_LONG,
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -1640,12 +1640,18 @@ export const NOTIFY_PUSH_SETUP_UNDERWAY = {
|
|||||||
"Setting up notifications for interesting activity, which takes about 10 seconds. If you don't see a final confirmation, check the 'Troubleshoot' page.",
|
"Setting up notifications for interesting activity, which takes about 10 seconds. If you don't see a final confirmation, check the 'Troubleshoot' page.",
|
||||||
};
|
};
|
||||||
|
|
||||||
// Used in: PushNotificationPermission.vue (turnOnNotifications method - success)
|
// Used in: PushNotificationPermission.vue (turnOnNotifications method - success, Daily Reminder)
|
||||||
export const NOTIFY_PUSH_SUCCESS = {
|
export const NOTIFY_PUSH_SUCCESS = {
|
||||||
title: "Notifications On",
|
title: "Notifications On",
|
||||||
message: "Daily Reminder notifications are now enabled.",
|
message: "Daily Reminder notifications are now enabled.",
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// Used in: PushNotificationPermission.vue (turnOnNotifications method - success, New Activity only)
|
||||||
|
export const NOTIFY_PUSH_SUCCESS_NEW_ACTIVITY = {
|
||||||
|
title: "Notifications On",
|
||||||
|
message: "New Activity notifications are now enabled.",
|
||||||
|
};
|
||||||
|
|
||||||
// Used in: PushNotificationPermission.vue (turnOnNotifications method - general error)
|
// Used in: PushNotificationPermission.vue (turnOnNotifications method - general error)
|
||||||
export const NOTIFY_PUSH_SETUP_ERROR = {
|
export const NOTIFY_PUSH_SETUP_ERROR = {
|
||||||
title: "Error Setting Notification Permissions",
|
title: "Error Setting Notification Permissions",
|
||||||
|
|||||||
@@ -139,11 +139,11 @@
|
|||||||
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"
|
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="editReminderNotification"
|
@click="editReminderNotification"
|
||||||
>
|
>
|
||||||
Edit Notification Details…
|
Edit Daily Reminder…
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="mt-4 flex items-center justify-between">
|
<div class="flex items-center justify-between mt-4 mb-2">
|
||||||
<!-- label -->
|
<!-- label -->
|
||||||
<div>
|
<div>
|
||||||
New Activity Notification
|
New Activity Notification
|
||||||
@@ -178,8 +178,22 @@
|
|||||||
></div>
|
></div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div v-if="notifyingNewActivityTime" class="w-full text-right">
|
<div v-if="notifyingNewActivity" class="w-full">
|
||||||
{{ notifyingNewActivityTime.replace(" ", " ") }}
|
<div
|
||||||
|
class="text-sm text-slate-500 mb-2 bg-white rounded px-3 py-2 border border-slate-200"
|
||||||
|
>
|
||||||
|
<div>
|
||||||
|
<b>Time:</b> {{ notifyingNewActivityTime.replace(" ", " ") }}
|
||||||
|
</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="editNewActivityNotification"
|
||||||
|
>
|
||||||
|
Edit New Activity Notification…
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="mt-2 text-center">
|
<div class="mt-2 text-center">
|
||||||
<router-link class="text-sm text-blue-500" to="/help-notifications">
|
<router-link class="text-sm text-blue-500" to="/help-notifications">
|
||||||
@@ -1244,6 +1258,19 @@ export default class AccountViewView extends Vue {
|
|||||||
* Configure native fetcher, sync starred plans, and schedule API-driven dual notification.
|
* Configure native fetcher, sync starred plans, and schedule API-driven dual notification.
|
||||||
*/
|
*/
|
||||||
async scheduleNewActivityDualNotification(notifyTime: string): Promise<void> {
|
async scheduleNewActivityDualNotification(notifyTime: string): Promise<void> {
|
||||||
|
const plugin = DailyNotification as unknown as {
|
||||||
|
scheduleDualNotification?: (opts: { config: unknown }) => Promise<void>;
|
||||||
|
};
|
||||||
|
if (!plugin.scheduleDualNotification) {
|
||||||
|
logger.warn(
|
||||||
|
"[AccountViewView] scheduleDualNotification not available on this device",
|
||||||
|
);
|
||||||
|
this.notify.error(
|
||||||
|
"New Activity scheduling is not available on this device. Please update the app.",
|
||||||
|
TIMEOUTS.STANDARD,
|
||||||
|
);
|
||||||
|
return;
|
||||||
|
}
|
||||||
try {
|
try {
|
||||||
await configureNativeFetcherIfReady(this.activeDid);
|
await configureNativeFetcherIfReady(this.activeDid);
|
||||||
const settings = await this.$accountSettings();
|
const settings = await this.$accountSettings();
|
||||||
@@ -1252,24 +1279,26 @@ export default class AccountViewView extends Vue {
|
|||||||
await DailyNotification.updateStarredPlans({ planIds });
|
await DailyNotification.updateStarredPlans({ planIds });
|
||||||
}
|
}
|
||||||
const config = buildDualScheduleConfig({ notifyTime });
|
const config = buildDualScheduleConfig({ notifyTime });
|
||||||
await (
|
await plugin.scheduleDualNotification!({ config });
|
||||||
DailyNotification as unknown as {
|
|
||||||
scheduleDualNotification: (opts: {
|
|
||||||
config: unknown;
|
|
||||||
}) => Promise<void>;
|
|
||||||
}
|
|
||||||
).scheduleDualNotification({ config });
|
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
logger.error(
|
logger.error(
|
||||||
"[AccountViewView] scheduleNewActivityDualNotification failed:",
|
"[AccountViewView] scheduleNewActivityDualNotification failed:",
|
||||||
error,
|
error,
|
||||||
);
|
);
|
||||||
|
const code = (error as { code?: string })?.code;
|
||||||
|
if (code === "UNIMPLEMENTED") {
|
||||||
|
this.notify.error(
|
||||||
|
"New Activity scheduling is not yet available on this device. Please update the app when support is added.",
|
||||||
|
TIMEOUTS.STANDARD,
|
||||||
|
);
|
||||||
|
} else {
|
||||||
this.notify.error(
|
this.notify.error(
|
||||||
"Could not schedule New Activity notification. Please try again.",
|
"Could not schedule New Activity notification. Please try again.",
|
||||||
TIMEOUTS.STANDARD,
|
TIMEOUTS.STANDARD,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
async showReminderNotificationInfo(): Promise<void> {
|
async showReminderNotificationInfo(): Promise<void> {
|
||||||
this.notify.confirm(
|
this.notify.confirm(
|
||||||
@@ -1468,6 +1497,56 @@ export default class AccountViewView extends Vue {
|
|||||||
}, 150);
|
}, 150);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Edit existing New Activity notification time.
|
||||||
|
* Opens the dialog with current time; on success reschedules dual notification.
|
||||||
|
*/
|
||||||
|
async editNewActivityNotification(): Promise<void> {
|
||||||
|
const dialog = this.$refs
|
||||||
|
.pushNotificationPermission as PushNotificationPermission;
|
||||||
|
|
||||||
|
dialog.open(
|
||||||
|
DAILY_CHECK_TITLE,
|
||||||
|
async (success: boolean, timeText: string) => {
|
||||||
|
if (!success) return;
|
||||||
|
if (Capacitor.isNativePlatform()) {
|
||||||
|
await this.scheduleNewActivityDualNotification(timeText);
|
||||||
|
}
|
||||||
|
await this.$saveSettings({ notifyingNewActivityTime: timeText });
|
||||||
|
this.notifyingNewActivityTime = timeText;
|
||||||
|
this.notify.success(
|
||||||
|
"New Activity notification time updated.",
|
||||||
|
TIMEOUTS.STANDARD,
|
||||||
|
);
|
||||||
|
},
|
||||||
|
{ skipSchedule: true },
|
||||||
|
);
|
||||||
|
|
||||||
|
// Pre-populate the dialog with current New Activity time
|
||||||
|
setTimeout(() => {
|
||||||
|
const timeMatch = this.notifyingNewActivityTime.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);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Toggle dev-only 10-minute rollover for daily reminder. Saves the setting and,
|
* Toggle dev-only 10-minute rollover for daily reminder. Saves the setting and,
|
||||||
* if reminder is already on, reschedules so the plugin uses the new interval.
|
* if reminder is already on, reschedules so the plugin uses the new interval.
|
||||||
|
|||||||
Reference in New Issue
Block a user