Files
crowd-funder-for-time-pwa/doc/notification-integration-changes-outline.md
Jose Olarte III 5a4ab84bfe feat(notifications): integrate DailyNotificationPlugin with UI for native platforms
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)
2026-01-23 19:06:16 +08:00

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

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() or turnOnNotifications() based on platform

G. Update Computed Properties

  • isSystemReady: For native, return true immediately (no VAPID needed)
  • canShowNotificationForm: For native, return true immediately

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 PushNotificationPermission component
  • Change: Add platform check
    • If native: Use NotificationService directly (or still use component - it will handle platform)
    • If web: Keep existing logic
    • Note: Since we're updating PushNotificationPermission to handle both, this might not need changes, but we could add direct native path for cleaner code

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)

  1. Update PushNotificationPermission.vue with platform detection
  2. Update AccountViewView.vue to hide push server on native
  3. Test on native platforms

Phase 2: Polish (Optional)

  1. Complete WebPushNotificationService.ts implementation
  2. Add platform-specific UI messaging
  3. 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

  1. Backward Compatibility: All changes are additive - existing web push flow remains unchanged
  2. Feature Flags: Could add feature flag to enable/disable native notifications
  3. Gradual Rollout: Test on one platform first (e.g., Android), then iOS
  4. 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:

  1. Quick Rollback: Revert changes to PushNotificationPermission.vue and AccountViewView.vue
  2. Partial Rollback: Keep platform detection but disable native path (feature flag)
  3. No Data Migration Needed: Settings structure unchanged

Questions to Consider

  1. Should we keep using PushNotificationPermission component for native, or create separate native flow?

    • Recommendation: Keep using component (simpler, less code duplication)
  2. Should we show different UI messaging for native vs web?

    • Recommendation: Optional enhancement, not required for MVP
  3. Should we complete WebPushNotificationService now or later?

    • Recommendation: Later (not blocking, existing component works)
  4. How to handle notification cancellation on native?

    • Recommendation: Use NotificationService.cancelDailyNotification() in existing turn-off logic

Next Steps After Implementation

  1. Update documentation with platform-specific instructions
  2. Add error handling for edge cases
  3. Consider adding notification status display in UI
  4. Test on real devices (critical for native notifications)
  5. Monitor for any platform-specific issues

Last Updated: 2026-01-23