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)
This commit is contained in:
Jose Olarte III
2026-01-23 19:06:16 +08:00
parent 84c3f79c57
commit 5a4ab84bfe
5 changed files with 1290 additions and 44 deletions

View File

@@ -0,0 +1,378 @@
# 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):
```typescript
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**:
```typescript
// 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**:
```typescript
// 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**:
```vue
<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
### Native Notification Flow (Recommended Implementation)
```typescript
// 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)
```typescript
// 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