Files
crowd-funder-for-time-pwa/doc/notification-system-overview.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

12 KiB

Notification System Overview

Date: 2026-01-23
Purpose: Understanding notification architecture and implementation guide for daily-notification-plugin


Executive Summary

Your app has two separate notification systems that coexist:

  1. Web Push Notifications (Web/PWA platforms)

    • Uses service workers, VAPID keys, and a push server
    • Requires the "Notification Push Server" setting
    • Server-based delivery
  2. Native Notifications (iOS/Android via DailyNotificationPlugin)

    • Uses native OS notification APIs
    • On-device scheduling (no server needed)
    • The "Notification Push Server" setting is NOT used for native

The system automatically selects the correct implementation based on platform using Capacitor.isNativePlatform().


Notification Push Server Setting

Location

  • File: src/views/AccountViewView.vue (lines 506-549)
  • UI Section: Advanced Settings → "Notification Push Server"
  • Database Field: settings.webPushServer

Purpose

The "Notification Push Server" setting ONLY applies to Web Push notifications (web/PWA platforms). It configures:

  1. VAPID Key Retrieval: The server URL used to fetch VAPID (Voluntary Application Server Identification) keys
  2. Subscription Endpoint: Where push subscriptions are sent
  3. Push Message Delivery: The server that sends push messages to browsers

How It Works (Web Push Flow)

User enables notification
    ↓
PushNotificationPermission.vue opens
    ↓
Fetches VAPID key from: {webPushServer}/web-push/vapid
    ↓
Subscribes to browser push service
    ↓
Sends subscription + time + message to: {webPushServer}/web-push/subscribe
    ↓
Server stores subscription and schedules push messages
    ↓
Server sends push messages at scheduled time via browser push service

Key Code Locations

AccountViewView.vue (lines 1473-1479):

async onClickSavePushServer(): Promise<void> {
  await this.$saveSettings({
    webPushServer: this.webPushServerInput,
  });
  this.webPushServer = this.webPushServerInput;
  this.notify.warning(ACCOUNT_VIEW_CONSTANTS.INFO.RELOAD_VAPID);
}

PushNotificationPermission.vue (lines 177-221):

  • Retrieves webPushServer from settings
  • Fetches VAPID key from {webPushServer}/web-push/vapid
  • Uses VAPID key to subscribe to push notifications

PushNotificationPermission.vue (lines 556-575):

  • Sends subscription to /web-push/subscribe endpoint (relative URL, handled by service worker)

Important Notes

  • ⚠️ This setting is NOT used for native iOS/Android notifications
  • The setting defaults to DEFAULT_PUSH_SERVER if not configured
  • Changing the server requires reloading VAPID keys (hence the warning message)
  • Local development (http://localhost) skips VAPID key retrieval

Daily Notification Plugin Integration

Current Status

Infrastructure Complete:

  • Plugin registered (src/plugins/DailyNotificationPlugin.ts)
  • Service abstraction layer created (src/services/notifications/)
  • Platform detection working
  • Native implementation ready (NativeNotificationService.ts)

🔄 UI Integration Needed:

  • PushNotificationPermission.vue still uses web push logic
  • AccountViewView notification toggles need platform detection
  • Settings storage needs to handle both systems

Architecture

NotificationService.getInstance()
    ↓
Platform Detection (Capacitor.isNativePlatform())
    ↓
┌─────────────────────┬─────────────────────┐
│   Native Platform   │    Web Platform      │
│   (iOS/Android)     │    (Web/PWA)         │
├─────────────────────┼─────────────────────┤
│ NativeNotification  │ WebPushNotification  │
│ Service             │ Service              │
│                     │                      │
│ Uses:               │ Uses:                │
│ - DailyNotification │ - Service Workers   │
│   Plugin            │ - VAPID Keys         │
│ - Native OS APIs    │ - Push Server        │
│ - On-device alarms  │ - Server scheduling  │
└─────────────────────┴─────────────────────┘

Key Differences

Feature Native (Plugin) Web Push
Server Required No Yes (Notification Push Server)
Scheduling On-device Server-side
Offline Delivery Yes No (requires network)
Background Support Full ⚠️ Limited (browser-dependent)
Permission Model OS-level Browser-level
Settings Storage Local only Local + server subscription

Implementation Recommendations

1. Update PushNotificationPermission Component

Current State: Only handles web push

Recommended Changes:

// In PushNotificationPermission.vue
import { NotificationService } from '@/services/notifications';
import { Capacitor } from '@capacitor/core';

async open(pushType: string, callback?: ...) {
  const isNative = Capacitor.isNativePlatform();
  
  if (isNative) {
    // Use native notification service
    const service = NotificationService.getInstance();
    const granted = await service.requestPermissions();
    
    if (granted) {
      // Show time picker UI
      // Then schedule via service.scheduleDailyNotification()
    }
  } else {
    // Existing web push logic
    // ... current implementation ...
  }
}

2. Update AccountViewView Notification Toggles

Current State: Always uses PushNotificationPermission component (web push)

Recommended Changes:

// In AccountViewView.vue
import { NotificationService } from '@/services/notifications';
import { Capacitor } from '@capacitor/core';

async showNewActivityNotificationChoice(): Promise<void> {
  const isNative = Capacitor.isNativePlatform();
  
  if (isNative) {
    // Use native service directly
    const service = NotificationService.getInstance();
    // Show time picker, then schedule
  } else {
    // Use existing PushNotificationPermission component
    (this.$refs.pushNotificationPermission as PushNotificationPermission)
      .open(DAILY_CHECK_TITLE, ...);
  }
}

3. Settings Storage Strategy

Current Settings Fields (from src/db/tables/settings.ts):

  • notifyingNewActivityTime - Time string for daily check
  • notifyingReminderTime - Time string for reminder
  • notifyingReminderMessage - Reminder message text
  • webPushServer - Push server URL (web only)

Recommendation: These settings work for both systems:

  • notifyingNewActivityTime - Works for both (native stores locally, web sends to server)
  • notifyingReminderTime - Works for both
  • notifyingReminderMessage - Works for both
  • ⚠️ webPushServer - Only used for web push (hide on native platforms)

4. Platform-Aware UI

Recommendations:

  1. Hide "Notification Push Server" setting on native platforms:

    <h2 v-if="!isNativePlatform" class="text-slate-500 text-sm font-bold mb-2">
      Notification Push Server
    </h2>
    
  2. Update help text to explain platform differences

  3. Show different messaging based on platform:

    • Native: "Notifications are scheduled on your device"
    • Web: "Notifications are sent via push server"

Notification Types

Your app supports two notification types:

1. Daily Check (DAILY_CHECK_TITLE)

  • Purpose: Notify user of new activity/updates
  • Message: Auto-generated by server (web) or app (native)
  • Settings Field: notifyingNewActivityTime

2. Direct Push (DIRECT_PUSH_TITLE)

  • Purpose: Daily reminder with custom message
  • Message: User-provided (max 100 characters)
  • Settings Fields: notifyingReminderTime, notifyingReminderMessage

Both types can be enabled simultaneously.


Code Flow Examples

// 1. Get service instance
const service = NotificationService.getInstance();

// 2. Request permissions
const granted = await service.requestPermissions();
if (!granted) {
  // Show error, guide to settings
  return;
}

// 3. Schedule notification
await service.scheduleDailyNotification({
  time: '09:00', // HH:mm format (24-hour)
  title: 'Daily Check-In',
  body: 'Time to check your TimeSafari activity',
  priority: 'normal'
});

// 4. Save to settings
await this.$saveSettings({
  notifyingNewActivityTime: '09:00'
});

// 5. Check status
const status = await service.getStatus();
console.log('Enabled:', status.enabled);
console.log('Time:', status.scheduledTime);

Web Push Flow (Current Implementation)

// 1. Open PushNotificationPermission component
(this.$refs.pushNotificationPermission as PushNotificationPermission)
  .open(DAILY_CHECK_TITLE, async (success, timeText) => {
    if (success) {
      // Component handles:
      // - VAPID key retrieval from webPushServer
      // - Service worker subscription
      // - Sending subscription to server
      
      // Just save the time
      await this.$saveSettings({
        notifyingNewActivityTime: timeText
      });
    }
  });

Testing Checklist

Native (iOS/Android)

  • Request permissions works
  • Notification appears at scheduled time
  • Notification survives app close
  • Notification survives device reboot
  • Both notification types can be enabled
  • Cancellation works correctly

Web Push

  • VAPID key retrieval works
  • Service worker subscription works
  • Subscription sent to server
  • Push messages received at scheduled time
  • Works with different push server URLs

Platform Detection

  • Correct service selected on iOS
  • Correct service selected on Android
  • Correct service selected on web
  • Settings UI shows/hides appropriately

Key Files Reference

Core Notification Services

  • src/services/notifications/NotificationService.ts - Factory/selector
  • src/services/notifications/NativeNotificationService.ts - Native implementation
  • src/services/notifications/WebPushNotificationService.ts - Web implementation (stub)

UI Components

  • src/components/PushNotificationPermission.vue - Web push UI (needs update)
  • src/views/AccountViewView.vue - Settings UI (lines 506-549 for push server)

Settings & Constants

  • src/db/tables/settings.ts - Settings schema
  • src/constants/app.ts - DEFAULT_PUSH_SERVER constant
  • src/libs/util.ts - DAILY_CHECK_TITLE, DIRECT_PUSH_TITLE

Plugin

  • src/plugins/DailyNotificationPlugin.ts - Plugin registration

Next Steps

  1. Update PushNotificationPermission.vue to detect platform and use appropriate service
  2. Update AccountViewView.vue notification toggles to use platform detection
  3. Hide "Notification Push Server" setting on native platforms
  4. Test on real devices (iOS and Android)
  5. Update documentation with platform-specific instructions

Questions & Answers

Q: Do I need to configure the Notification Push Server for native apps?
A: No. The setting is only for web push. Native notifications are scheduled on-device.

Q: Can both notification systems be active at the same time?
A: No, they're mutually exclusive per platform. The app automatically selects the correct one.

Q: How do I test native notifications?
A: Use NotificationService.getInstance() and test on a real device (simulators have limitations).

Q: What happens if I change the push server URL?
A: Only affects web push. Users need to re-subscribe to push notifications with the new server.

Q: Can I use the same settings fields for both systems?
A: Yes! The time and message fields work for both. Only webPushServer is web-specific.


Last Updated: 2026-01-23