Integrate DailyNotificationPlugin with notification UI to enable native notifications on iOS/Android while maintaining web push for web/PWA. - Add platform detection to PushNotificationPermission component - Implement native notification flow via NotificationService - Hide push server setting on native platforms (not needed) - Add time conversion (AM/PM to 24-hour) for native plugin - Add comprehensive documentation Breaking Changes: None (backward compatible)
13 KiB
Notification Integration Changes - Implementation Outline
Date: 2026-01-23
Purpose: Detailed outline of changes needed to integrate DailyNotificationPlugin with UI
Overview
This document outlines all changes required to integrate the DailyNotificationPlugin with the existing notification UI, making it work seamlessly on both native (iOS/Android) and web platforms.
Estimated Complexity: Medium
Estimated Files Changed: 3-4 files
Breaking Changes: None (backward compatible)
Change Summary
| File | Changes | Complexity | Risk |
|---|---|---|---|
PushNotificationPermission.vue |
Add platform detection, native flow | Medium | Low |
AccountViewView.vue |
Platform detection in toggles, hide push server on native | Low | Low |
WebPushNotificationService.ts |
Complete stub implementation (optional) | Medium | Low |
Detailed Changes
1. PushNotificationPermission.vue
File: src/components/PushNotificationPermission.vue
Current Lines: ~656 lines
Estimated New Lines: +50-80 lines
Complexity: Medium
Changes Required
A. Add Imports (Top of script section)
import { Capacitor } from "@capacitor/core";
import { NotificationService } from "@/services/notifications";
B. Add Platform Detection Property
// Add to class properties
private get isNativePlatform(): boolean {
return Capacitor.isNativePlatform();
}
C. Modify open() Method (Lines 170-258)
- Current: Always initializes web push (VAPID key, service worker)
- Change: Add platform check at start
- If native: Skip VAPID/service worker, show UI immediately
- If web: Keep existing logic
D. Modify turnOnNotifications() Method (Lines 393-499)
- Current: Web push subscription flow
- Change: Split into two paths:
- Native path: Use
NotificationService.getInstance()→requestPermissions()→scheduleDailyNotification() - Web path: Keep existing logic
- Native path: Use
E. Add New Method: turnOnNativeNotifications()
- Request permissions via
NotificationService - Convert time input (AM/PM) to 24-hour format (HH:mm)
- Call
scheduleDailyNotification()with proper options - Save to settings
- Call callback with success/time/message
F. Update handleTurnOnNotifications() Method (Line 643)
- Add platform check
- Route to
turnOnNativeNotifications()orturnOnNotifications()based on platform
G. Update Computed Properties
isSystemReady: For native, returntrueimmediately (no VAPID needed)canShowNotificationForm: For native, returntrueimmediately
H. Update Template (Optional - for better UX)
- Add platform-specific messaging if desired
- Native: "Notifications will be scheduled on your device"
- Web: Keep existing messaging
Code Structure Preview
async open(pushType: string, callback?: ...) {
this.callback = callback || this.callback;
this.isVisible = true;
this.pushType = pushType;
// Platform detection
if (this.isNativePlatform) {
// Native: No VAPID/service worker needed
this.serviceWorkerReady = true; // Fake it for UI
this.vapidKey = "native"; // Placeholder
return; // Skip web push initialization
}
// Existing web push initialization...
// (keep all existing code)
}
async turnOnNotifications() {
if (this.isNativePlatform) {
return this.turnOnNativeNotifications();
}
// Existing web push logic...
}
private async turnOnNativeNotifications(): Promise<void> {
const service = NotificationService.getInstance();
// Request permissions
const granted = await service.requestPermissions();
if (!granted) {
// Handle permission denial
return;
}
// Convert time to 24-hour format
const time24h = this.convertTo24HourFormat();
// Determine title and body based on pushType
const title = this.pushType === this.DAILY_CHECK_TITLE
? "Daily Check-In"
: "Daily Reminder";
const body = this.pushType === this.DIRECT_PUSH_TITLE
? this.messageInput
: "Time to check your TimeSafari activity";
// Schedule notification
const success = await service.scheduleDailyNotification({
time: time24h,
title,
body,
priority: 'normal'
});
if (success) {
// Save to settings
const timeText = this.notificationTimeText;
await this.$saveSettings({
[this.pushType === this.DAILY_CHECK_TITLE
? 'notifyingNewActivityTime'
: 'notifyingReminderTime']: timeText,
...(this.pushType === this.DIRECT_PUSH_TITLE && {
notifyingReminderMessage: this.messageInput
})
});
// Call callback
this.callback(true, timeText, this.messageInput);
}
}
private convertTo24HourFormat(): string {
const hour = parseInt(this.hourInput);
const minute = parseInt(this.minuteInput);
let hour24 = hour;
if (!this.hourAm && hour !== 12) {
hour24 = hour + 12;
} else if (this.hourAm && hour === 12) {
hour24 = 0;
}
return `${hour24.toString().padStart(2, '0')}:${minute.toString().padStart(2, '0')}`;
}
Testing Considerations
- Test on iOS device
- Test on Android device
- Test on web (should still work as before)
- Test permission denial flow
- Test time conversion (AM/PM → 24-hour)
2. AccountViewView.vue
File: src/views/AccountViewView.vue
Current Lines: 2124 lines
Estimated New Lines: +20-30 lines
Complexity: Low
Changes Required
A. Add Import (Top of script section, around line 739)
import { Capacitor } from "@capacitor/core";
B. Add Computed Property (In class, around line 888)
private get isNativePlatform(): boolean {
return Capacitor.isNativePlatform();
}
C. Modify Notification Toggle Methods (Lines 1134-1202)
showNewActivityNotificationChoice() (Lines 1134-1158)
- Current: Always uses
PushNotificationPermissioncomponent - Change: Add platform check
- If native: Use
NotificationServicedirectly (or still use component - it will handle platform) - If web: Keep existing logic
- Note: Since we're updating
PushNotificationPermissionto handle both, this might not need changes, but we could add direct native path for cleaner code
- If native: Use
showReminderNotificationChoice() (Lines 1171-1202)
- Same as above
D. Conditionally Hide Push Server Setting (Lines 506-549)
- Wrap the entire "Notification Push Server" section in
v-if="!isNativePlatform" - This hides it on iOS/Android where it's not needed
E. Update Status Display (Optional)
- When showing notification status, could add platform indicator
- "Native notification scheduled" vs "Web push subscription active"
Code Structure Preview
// Add computed property
private get isNativePlatform(): boolean {
return Capacitor.isNativePlatform();
}
// In template, wrap push server section:
<section v-if="!isNativePlatform" id="sectionPushServer">
<h2 class="text-slate-500 text-sm font-bold mb-2">
Notification Push Server
</h2>
<!-- ... existing push server UI ... -->
</section>
// Optional: Update notification choice methods
async showNewActivityNotificationChoice(): Promise<void> {
if (!this.notifyingNewActivity) {
// Component now handles platform detection, so this can stay the same
// OR we could add direct native path here for cleaner separation
(this.$refs.pushNotificationPermission as PushNotificationPermission)
.open(DAILY_CHECK_TITLE, async (success: boolean, timeText: string) => {
// ... existing callback ...
});
} else {
// ... existing turn-off logic ...
}
}
Testing Considerations
- Verify push server section hidden on iOS
- Verify push server section hidden on Android
- Verify push server section visible on web
- Test notification toggles work on all platforms
3. WebPushNotificationService.ts (Optional Enhancement)
File: src/services/notifications/WebPushNotificationService.ts
Current Lines: 213 lines
Estimated New Lines: +100-150 lines
Complexity: Medium
Priority: Low (can be done later)
Changes Required
A. Complete scheduleDailyNotification() Implementation
- Extract logic from
PushNotificationPermission.vue - Subscribe to push service
- Send subscription to server
- Return success status
B. Complete cancelDailyNotification() Implementation
- Get current subscription
- Unsubscribe from push service
- Notify server to stop sending
C. Complete getStatus() Implementation
- Check settings for
notifyingNewActivityTime/notifyingReminderTime - Check service worker subscription status
- Return combined status
Note: This is optional because PushNotificationPermission.vue already handles web push. Completing this would allow using NotificationService directly for web too, but it's not required for the integration to work.
Implementation Order
Phase 1: Core Integration (Required)
- ✅ Update
PushNotificationPermission.vuewith platform detection - ✅ Update
AccountViewView.vueto hide push server on native - ✅ Test on native platforms
Phase 2: Polish (Optional)
- Complete
WebPushNotificationService.tsimplementation - Add platform-specific UI messaging
- Add status indicators
Risk Assessment
Low Risk Changes
- ✅ Adding platform detection (read-only check)
- ✅ Conditionally hiding UI elements
- ✅ Adding new code paths (not modifying existing)
Medium Risk Changes
- ⚠️ Modifying
turnOnNotifications()flow (but we're adding, not replacing) - ⚠️ Time format conversion (need to test edge cases)
Mitigation Strategies
- Backward Compatibility: All changes are additive - existing web push flow remains unchanged
- Feature Flags: Could add feature flag to enable/disable native notifications
- Gradual Rollout: Test on one platform first (e.g., Android), then iOS
- Fallback: If native service fails, could fall back to showing error message
Testing Checklist
Functional Testing
- Native iOS: Request permissions → Schedule notification → Verify scheduled
- Native Android: Request permissions → Schedule notification → Verify scheduled
- Web: Existing flow still works (no regression)
- Permission denial: Shows appropriate error message
- Time conversion: AM/PM correctly converts to 24-hour format
- Both notification types: Daily Check and Direct Push work on native
- Settings persistence: Times saved correctly to database
UI Testing
- Push server setting hidden on iOS
- Push server setting hidden on Android
- Push server setting visible on web
- Notification toggles work on all platforms
- Time picker UI works on native (same as web)
Edge Cases
- 12:00 AM conversion (should be 00:00)
- 12:00 PM conversion (should be 12:00)
- Invalid time input handling
- App restart: Notifications still scheduled
- Device reboot: Notifications still scheduled (Android)
Dependencies
Required
- ✅
@capacitor/core- Already in project - ✅
@timesafari/daily-notification-plugin- Already installed - ✅
NotificationService- Already created
No New Dependencies Needed
Estimated Effort
| Task | Time Estimate |
|---|---|
| Update PushNotificationPermission.vue | 2-3 hours |
| Update AccountViewView.vue | 30 minutes - 1 hour |
| Testing on iOS | 1-2 hours |
| Testing on Android | 1-2 hours |
| Bug fixes & polish | 1-2 hours |
| Total | 5-10 hours |
Rollback Plan
If issues arise:
- Quick Rollback: Revert changes to
PushNotificationPermission.vueandAccountViewView.vue - Partial Rollback: Keep platform detection but disable native path (feature flag)
- No Data Migration Needed: Settings structure unchanged
Questions to Consider
-
Should we keep using
PushNotificationPermissioncomponent for native, or create separate native flow?- Recommendation: Keep using component (simpler, less code duplication)
-
Should we show different UI messaging for native vs web?
- Recommendation: Optional enhancement, not required for MVP
-
Should we complete
WebPushNotificationServicenow or later?- Recommendation: Later (not blocking, existing component works)
-
How to handle notification cancellation on native?
- Recommendation: Use
NotificationService.cancelDailyNotification()in existing turn-off logic
- Recommendation: Use
Next Steps After Implementation
- Update documentation with platform-specific instructions
- Add error handling for edge cases
- Consider adding notification status display in UI
- Test on real devices (critical for native notifications)
- Monitor for any platform-specific issues
Last Updated: 2026-01-23