forked from trent_larson/crowd-funder-for-time-pwa
docs: add daily notification plugin integration planning documents
- Add comprehensive integration plan following meta_feature_planning workflow - Add AccountViewView integration strategy with PlatformService approach - Document architecture decisions: PlatformService interface integration - Remove web/push notification references - Document implementation phases and acceptance criteria
This commit is contained in:
472
doc/daily-notification-accountview-integration-strategy.md
Normal file
472
doc/daily-notification-accountview-integration-strategy.md
Normal file
@@ -0,0 +1,472 @@
|
||||
# Daily Notification Plugin - AccountViewView Integration Strategy
|
||||
|
||||
**Author**: Matthew Raymer
|
||||
**Date**: 2025-11-03
|
||||
**Status**: 🎯 **PLANNING** - UI integration strategy
|
||||
**Feature**: Daily Notification Scheduling in Account Settings
|
||||
|
||||
---
|
||||
|
||||
## Overview
|
||||
|
||||
This document outlines the strategy for integrating daily notification scheduling into `AccountViewView.vue`, allowing users to configure notification times directly from their account settings.
|
||||
|
||||
---
|
||||
|
||||
## Current State Analysis
|
||||
|
||||
### Account Settings Context
|
||||
|
||||
**Location**: `AccountViewView.vue` - Settings view for user account configuration
|
||||
|
||||
**Integration Approach**: Add new "Daily Notifications" section for scheduling native daily notifications using PlatformService.
|
||||
|
||||
---
|
||||
|
||||
## Integration Strategy
|
||||
|
||||
**Approach**: Create a separate "Daily Notifications" section
|
||||
|
||||
This approach adds a dedicated "Daily Notifications" section that checks PlatformService capabilities. On Capacitor platforms, it provides full functionality. On other platforms, the UI is hidden when PlatformService returns `null` for notification methods.
|
||||
|
||||
**Key Benefits**:
|
||||
- Uses PlatformService interface pattern (consistent with camera, filesystem)
|
||||
- Platform-specific features properly isolated
|
||||
- Can use native time picker (better UX on mobile)
|
||||
- Future-proof: Easy to extend with additional notification features
|
||||
- Graceful degradation on unsupported platforms
|
||||
|
||||
**Implementation**:
|
||||
```vue
|
||||
<!-- New Daily Notifications section -->
|
||||
<section
|
||||
v-if="notificationsSupported"
|
||||
id="sectionDailyNotifications"
|
||||
class="bg-slate-100 rounded-md overflow-hidden px-4 py-4 mt-8 mb-8"
|
||||
aria-labelledby="dailyNotificationsHeading"
|
||||
>
|
||||
<h2 id="dailyNotificationsHeading" class="mb-2 font-bold">
|
||||
Daily Notifications
|
||||
<button
|
||||
class="text-slate-400 fa-fw cursor-pointer"
|
||||
aria-label="Learn more about native notifications"
|
||||
@click.stop="showNativeNotificationInfo"
|
||||
>
|
||||
<font-awesome icon="question-circle" aria-hidden="true" />
|
||||
</button>
|
||||
</h2>
|
||||
|
||||
<div class="flex items-center justify-between">
|
||||
<div>Daily Notification</div>
|
||||
<!-- Toggle switch -->
|
||||
<div
|
||||
class="relative ml-2 cursor-pointer"
|
||||
role="switch"
|
||||
:aria-checked="nativeNotificationEnabled"
|
||||
@click="toggleNativeNotification()"
|
||||
>
|
||||
<!-- Custom toggle UI -->
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Show current time when enabled -->
|
||||
<div v-if="nativeNotificationEnabled" class="mt-2">
|
||||
<div class="flex items-center justify-between">
|
||||
<span>Scheduled for: {{ nativeNotificationTime }}</span>
|
||||
<button
|
||||
class="text-blue-500 text-sm"
|
||||
@click="editNativeNotificationTime()"
|
||||
>
|
||||
Edit Time
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Approach Rationale
|
||||
|
||||
**Decision Date**: 2025-11-03
|
||||
**Status**: ✅ **ACCEPTED** - Will proceed with separate native notification section
|
||||
|
||||
1. **Clear Platform Distinction**: Users understand this is for mobile apps
|
||||
2. **No Conflicts**: Doesn't interfere with disabled web notifications
|
||||
3. **Better UX**: Can use native time picker on mobile
|
||||
4. **Future-Proof**: Easy to extend with additional native notification features
|
||||
|
||||
### Implementation Status
|
||||
|
||||
- [x] Approach decision finalized
|
||||
- [ ] Implementation begins (Phase 1)
|
||||
|
||||
---
|
||||
|
||||
## UI Component Design
|
||||
|
||||
### 1. Platform Capability Detection
|
||||
|
||||
```typescript
|
||||
// In AccountViewView component
|
||||
async checkNotificationSupport(): Promise<boolean> {
|
||||
const platformService = PlatformServiceFactory.getInstance();
|
||||
const status = await platformService.getDailyNotificationStatus();
|
||||
return status !== null; // null means not supported
|
||||
}
|
||||
```
|
||||
|
||||
### 2. State Management
|
||||
|
||||
```typescript
|
||||
// Component properties
|
||||
nativeNotificationEnabled: boolean = false;
|
||||
nativeNotificationTime: string = ""; // Display format: "9:00 AM"
|
||||
nativeNotificationTimeStorage: string = ""; // Plugin format: "09:00"
|
||||
nativeNotificationTitle: string = "Daily Update";
|
||||
nativeNotificationMessage: string = "Your daily notification is ready!";
|
||||
```
|
||||
|
||||
### 3. Time Input ✅ **SELECTED: HTML5 Time Input**
|
||||
|
||||
**Decision**: Use HTML5 `<input type="time">` for native mobile experience
|
||||
|
||||
```vue
|
||||
<input
|
||||
type="time"
|
||||
v-model="nativeNotificationTimeStorage"
|
||||
class="rounded border border-slate-400 px-2 py-2"
|
||||
/>
|
||||
```
|
||||
|
||||
**Benefits**:
|
||||
- Native mobile time picker UI on Capacitor platforms
|
||||
- Simpler implementation (no custom time parsing needed)
|
||||
- Automatic 24-hour format output (compatible with plugin)
|
||||
- System handles locale-specific time formatting
|
||||
- Better UX on mobile devices
|
||||
|
||||
**Note**: HTML5 time input provides time in "HH:mm" format (24-hour) which matches the plugin's expected format perfectly.
|
||||
|
||||
### 4. Time Format Conversion (Using System Time)
|
||||
|
||||
**Key Principle**: Use device's local system time - no timezone conversions needed. The plugin handles system time natively.
|
||||
|
||||
```typescript
|
||||
// Convert "09:00" (plugin storage format) to "9:00 AM" (display)
|
||||
function formatTimeForDisplay(time24: string): string {
|
||||
const [hours, minutes] = time24.split(':');
|
||||
const hourNum = parseInt(hours);
|
||||
const isPM = hourNum >= 12;
|
||||
const displayHour = hourNum === 0 ? 12 : hourNum > 12 ? hourNum - 12 : hourNum;
|
||||
return `${displayHour}:${minutes} ${isPM ? 'PM' : 'AM'}`;
|
||||
}
|
||||
|
||||
// HTML5 time input provides "HH:mm" in local time - use directly
|
||||
// No UTC conversion needed - plugin handles local timezone
|
||||
function getTimeFromInput(timeInput: string): string {
|
||||
// timeInput is already in "HH:mm" format from <input type="time">
|
||||
// This is in the user's local timezone - pass directly to plugin
|
||||
return timeInput; // e.g., "09:00" in user's local time
|
||||
}
|
||||
```
|
||||
|
||||
**Time Handling**:
|
||||
|
||||
- **PlatformService Integration**: Uses device's local system time directly - NO UTC conversion needed. The plugin schedules notifications on the device itself, using the device's timezone.
|
||||
|
||||
**Implementation Principles**:
|
||||
- HTML5 `<input type="time">` provides time in device's local timezone
|
||||
- Plugin receives time in "HH:mm" format and schedules relative to device's local time
|
||||
- No manual timezone conversion or UTC calculations needed
|
||||
- System automatically handles:
|
||||
- Timezone changes
|
||||
- Daylight saving time transitions
|
||||
- Device timezone updates
|
||||
- User sets "9:00 AM" in their local time → plugin schedules for 9:00 AM local time every day
|
||||
|
||||
---
|
||||
|
||||
## Data Flow
|
||||
|
||||
### 1. Initialization
|
||||
|
||||
```typescript
|
||||
async initializeState() {
|
||||
// ... existing initialization ...
|
||||
|
||||
const platformService = PlatformServiceFactory.getInstance();
|
||||
|
||||
// Check if notifications are supported on this platform
|
||||
const status = await platformService.getDailyNotificationStatus();
|
||||
if (status === null) {
|
||||
// Notifications not supported - don't initialize
|
||||
return;
|
||||
}
|
||||
|
||||
// Load from settings
|
||||
const nativeNotificationTime = settings.nativeNotificationTime || "";
|
||||
this.nativeNotificationEnabled = !!nativeNotificationTime;
|
||||
this.nativeNotificationTimeStorage = nativeNotificationTime;
|
||||
|
||||
if (nativeNotificationTime) {
|
||||
this.nativeNotificationTime = formatTimeForDisplay(nativeNotificationTime);
|
||||
}
|
||||
|
||||
// Update UI with current status
|
||||
this.notificationStatus = status;
|
||||
}
|
||||
```
|
||||
|
||||
### 2. Enable Notification
|
||||
|
||||
```typescript
|
||||
async enableNativeNotification() {
|
||||
try {
|
||||
const platformService = PlatformServiceFactory.getInstance();
|
||||
|
||||
// 1. Request permissions if needed
|
||||
const permissions = await platformService.checkNotificationPermissions();
|
||||
if (permissions === null || permissions.notifications !== 'granted') {
|
||||
const result = await platformService.requestNotificationPermissions();
|
||||
if (result === null || !result.notifications) {
|
||||
throw new Error("Notification permissions denied");
|
||||
}
|
||||
}
|
||||
|
||||
// 2. Schedule notification via PlatformService
|
||||
// Time is in device's local system time (from HTML5 time input)
|
||||
// PlatformService handles timezone and scheduling internally
|
||||
await platformService.scheduleDailyNotification({
|
||||
time: this.nativeNotificationTimeStorage, // "09:00" in local time
|
||||
title: this.nativeNotificationTitle,
|
||||
body: this.nativeNotificationMessage,
|
||||
sound: true,
|
||||
priority: 'high'
|
||||
});
|
||||
|
||||
// 3. Save to settings
|
||||
await this.$saveSettings({
|
||||
nativeNotificationTime: this.nativeNotificationTimeStorage,
|
||||
nativeNotificationTitle: this.nativeNotificationTitle,
|
||||
nativeNotificationMessage: this.nativeNotificationMessage,
|
||||
});
|
||||
|
||||
// 4. Update UI state
|
||||
this.nativeNotificationEnabled = true;
|
||||
|
||||
this.notify.success("Daily notification scheduled successfully", TIMEOUTS.SHORT);
|
||||
} catch (error) {
|
||||
logger.error("Failed to enable notification:", error);
|
||||
this.notify.error("Failed to schedule notification. Please try again.", TIMEOUTS.LONG);
|
||||
}
|
||||
}
|
||||
|
||||
### 3. Disable Notification
|
||||
|
||||
```typescript
|
||||
async disableNativeNotification() {
|
||||
try {
|
||||
const platformService = PlatformServiceFactory.getInstance();
|
||||
|
||||
// 1. Cancel notification via PlatformService
|
||||
await platformService.cancelDailyNotification();
|
||||
|
||||
// 2. Clear settings
|
||||
await this.$saveSettings({
|
||||
nativeNotificationTime: "",
|
||||
nativeNotificationTitle: "",
|
||||
nativeNotificationMessage: "",
|
||||
});
|
||||
|
||||
// 3. Update UI state
|
||||
this.nativeNotificationEnabled = false;
|
||||
this.nativeNotificationTime = "";
|
||||
this.nativeNotificationTimeStorage = "";
|
||||
|
||||
this.notify.success("Daily notification disabled", TIMEOUTS.SHORT);
|
||||
} catch (error) {
|
||||
logger.error("Failed to disable native notification:", error);
|
||||
this.notify.error("Failed to disable notification. Please try again.", TIMEOUTS.LONG);
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### 4. Edit Time
|
||||
|
||||
**Approach**: Use inline HTML5 time input for quick edits
|
||||
|
||||
```typescript
|
||||
async editNativeNotificationTime() {
|
||||
// Show inline HTML5 time input for quick changes
|
||||
// For complex editing (title, message), navigate to ScheduleView
|
||||
this.showTimeEdit = true;
|
||||
}
|
||||
```
|
||||
|
||||
**Implementation Note**: HTML5 time input provides native mobile picker experience when shown inline, making it ideal for quick time adjustments in AccountViewView.
|
||||
|
||||
---
|
||||
|
||||
## Settings Schema
|
||||
|
||||
### New Settings Fields
|
||||
|
||||
```typescript
|
||||
// Add to Settings interface in src/db/tables/settings.ts
|
||||
interface Settings {
|
||||
// ... existing fields ...
|
||||
|
||||
// Native notification settings (Capacitor only)
|
||||
nativeNotificationTime?: string; // "09:00" format (24-hour)
|
||||
nativeNotificationTitle?: string; // Default: "Daily Update"
|
||||
nativeNotificationMessage?: string; // Default message
|
||||
}
|
||||
```
|
||||
|
||||
### Settings Persistence
|
||||
|
||||
- Store in `settings` table via `$saveSettings()`
|
||||
- Use same pattern as `notifyingNewActivityTime`
|
||||
- Persist across app restarts
|
||||
- Sync with plugin state on component mount
|
||||
|
||||
---
|
||||
|
||||
## Plugin Integration
|
||||
|
||||
### PlatformService Usage
|
||||
|
||||
```typescript
|
||||
import { PlatformServiceFactory } from '@/services/PlatformServiceFactory';
|
||||
|
||||
const platformService = PlatformServiceFactory.getInstance();
|
||||
|
||||
// Check if notifications are supported
|
||||
const status = await platformService.getDailyNotificationStatus();
|
||||
if (status === null) {
|
||||
// Notifications not supported on this platform
|
||||
}
|
||||
```
|
||||
|
||||
### Key Operations (via PlatformService)
|
||||
|
||||
1. **Check Status**: `getDailyNotificationStatus()` - Returns status or `null` if unsupported
|
||||
2. **Check Permissions**: `checkNotificationPermissions()` - Returns permissions or `null` if unsupported
|
||||
3. **Request Permissions**: `requestNotificationPermissions()` - Requests permissions or returns `null` if unsupported
|
||||
4. **Schedule**: `scheduleDailyNotification(options)` - Schedules notification or throws error if unsupported
|
||||
5. **Cancel**: `cancelDailyNotification()` - Cancels notification or throws error if unsupported
|
||||
|
||||
---
|
||||
|
||||
## UI/UX Considerations
|
||||
|
||||
### Visual Design
|
||||
- **Section Style**: Match existing notification section (`bg-slate-100 rounded-md`)
|
||||
- **Toggle Switch**: Reuse existing custom toggle pattern
|
||||
- **Time Display**: Show in user-friendly format ("9:00 AM")
|
||||
- **Edit Button**: Small, subtle link/button to edit time
|
||||
|
||||
### User Feedback
|
||||
- **Success**: Toast notification when scheduled successfully
|
||||
- **Error**: Clear error message with troubleshooting guidance
|
||||
- **Loading**: Show loading state during plugin operations
|
||||
- **Permission Request**: Handle gracefully if denied
|
||||
|
||||
### Accessibility
|
||||
- **ARIA Labels**: Proper labels for all interactive elements
|
||||
- **Keyboard Navigation**: Full keyboard support
|
||||
- **Screen Reader**: Clear announcements for state changes
|
||||
|
||||
---
|
||||
|
||||
## Implementation Phases
|
||||
|
||||
### Phase 1: Basic Integration
|
||||
- [ ] Add platform detection property
|
||||
- [ ] Create native notification section in template
|
||||
- [ ] Add component state properties
|
||||
- [ ] Implement toggle functionality
|
||||
- [ ] Basic enable/disable operations
|
||||
|
||||
### Phase 2: Time Management
|
||||
- [ ] Add time input (HTML5 time picker)
|
||||
- [ ] Implement time format conversion
|
||||
- [ ] Add edit time functionality
|
||||
- [ ] Save/load time from settings
|
||||
|
||||
### Phase 3: Plugin Integration
|
||||
- [ ] Integrate with DailyNotificationFactory
|
||||
- [ ] Schedule notification on enable
|
||||
- [ ] Cancel notification on disable
|
||||
- [ ] Update notification on time change
|
||||
- [ ] Sync plugin state with settings
|
||||
|
||||
### Phase 4: Polish & Error Handling
|
||||
- [ ] Permission request flow
|
||||
- [ ] Error handling and user feedback
|
||||
- [ ] Status verification
|
||||
- [ ] Help/info dialogs
|
||||
- [ ] Accessibility improvements
|
||||
|
||||
---
|
||||
|
||||
## Implementation Decisions
|
||||
|
||||
### Time Input Format ✅
|
||||
- **Selected**: HTML5 `<input type="time">` for Capacitor platforms
|
||||
- **Rationale**: Native mobile experience, simpler code, automatic 24-hour format
|
||||
|
||||
### Edit Approach ✅
|
||||
- **Selected**: Inline HTML5 time input for quick edits in AccountViewView
|
||||
- **Note**: For complex editing (title, message changes), users can navigate to dedicated ScheduleView
|
||||
|
||||
### Settings Field Names ✅
|
||||
- **Selected**: `nativeNotificationTime`, `nativeNotificationTitle`, `nativeNotificationMessage`
|
||||
- **Rationale**: Clear distinction from web push notification fields
|
||||
|
||||
### Notification Title/Message ✅
|
||||
- **Selected**: Allow customization, default to "Daily Update" / "Your daily notification is ready!"
|
||||
- **Rationale**: Flexibility for users, sensible defaults
|
||||
|
||||
---
|
||||
|
||||
## Success Criteria
|
||||
|
||||
- [ ] Native notification section appears only on Capacitor platforms
|
||||
- [ ] Toggle enables/disables notifications via plugin
|
||||
- [ ] Time can be set and edited
|
||||
- [ ] Settings persist across app restarts
|
||||
- [ ] Plugin state syncs with settings
|
||||
- [ ] Error handling provides clear user feedback
|
||||
- [ ] UI matches existing design patterns
|
||||
- [ ] Accessibility requirements met
|
||||
|
||||
---
|
||||
|
||||
## Related Components
|
||||
|
||||
- **PlatformService**: Interface for platform capabilities (notification methods)
|
||||
- **PlatformServiceFactory**: Factory for getting platform service instance
|
||||
- **ScheduleView**: Dedicated scheduling interface (for complex editing)
|
||||
- **AccountViewView**: Main settings view (integration target)
|
||||
|
||||
---
|
||||
|
||||
## Next Steps
|
||||
|
||||
1. ~~**Decide on Approach**: Separate native notification section~~ ✅ **DECIDED**
|
||||
2. **Define Settings Schema**: Add native notification fields to Settings interface
|
||||
3. **Create UI Components**: Build notification section in AccountViewView
|
||||
4. **Integrate Plugin**: Connect UI to DailyNotificationFactory service
|
||||
5. **Test Flow**: Verify enable/disable/edit workflows
|
||||
6. **Add Help Content**: Create help documentation for native notifications
|
||||
|
||||
---
|
||||
|
||||
**See also**:
|
||||
- `doc/daily-notification-plugin-integration-plan.md` - Overall integration plan
|
||||
- `src/views/AccountViewView.vue` - Target component for integration
|
||||
- `src/services/PlatformService.ts` - PlatformService interface definition
|
||||
- `src/services/PlatformServiceFactory.ts` - Factory for platform service instances
|
||||
|
||||
658
doc/daily-notification-plugin-integration-plan.md
Normal file
658
doc/daily-notification-plugin-integration-plan.md
Normal file
@@ -0,0 +1,658 @@
|
||||
# Daily Notification Plugin Integration Plan
|
||||
|
||||
**Author**: Matthew Raymer
|
||||
**Date**: 2025-11-03
|
||||
**Status**: 🎯 **PLANNING** - Feature planning phase
|
||||
**Feature**: Daily Notification Plugin Integration
|
||||
**Platform Scope**: Capacitor-only (Android/iOS)
|
||||
|
||||
---
|
||||
|
||||
## Executive Summary
|
||||
|
||||
This plan outlines the integration of `@timesafari/daily-notification-plugin` into the TimeSafari application using the PlatformService interface pattern. The feature is implemented on all platforms via PlatformService, but only Capacitor platforms provide full functionality. Web and Electron platforms return `null` for unsupported operations.
|
||||
|
||||
### Key Requirements
|
||||
|
||||
- **Platform**: All platforms (Capacitor provides full functionality, Web/Electron return null)
|
||||
- **Architecture**: PlatformService interface integration (all platforms implement, unsupported return null)
|
||||
- **Components**: Home view (diagnostics/status) + Schedule view (time setting)
|
||||
- **Store**: Pinia store for notification state management
|
||||
- **Routes**: New routes for schedule, notifications, history, settings views
|
||||
|
||||
---
|
||||
|
||||
## Complexity Assessment
|
||||
|
||||
### Technical Complexity: **Medium**
|
||||
|
||||
#### Code Changes
|
||||
- **Medium**: New Vue components, Pinia store, router routes
|
||||
- **Pattern**: Following PlatformService interface pattern (like camera, filesystem methods) - all platforms implement, unsupported return null
|
||||
- **Integration**: Plugin API integration with error handling
|
||||
|
||||
#### Platform Impact
|
||||
- **Single Platform**: Capacitor-only (Android/iOS)
|
||||
- **Conditional Loading**: Feature only loads on Capacitor platforms
|
||||
- **Graceful Degradation**: Web/Electron builds should not break when plugin unavailable
|
||||
|
||||
#### Testing Requirements
|
||||
- **Comprehensive**:
|
||||
- Plugin availability detection
|
||||
- Permission request flows
|
||||
- Notification scheduling
|
||||
- Status checking
|
||||
- Cross-platform validation (ensure web/electron unaffected)
|
||||
|
||||
### Dependency Complexity
|
||||
|
||||
#### Internal Dependencies
|
||||
- **Medium**:
|
||||
- Router configuration (new routes)
|
||||
- Store creation (Pinia)
|
||||
- Component dependencies (ActionCard, StatusCard)
|
||||
- Logger integration (replace console.* with project logger)
|
||||
|
||||
#### External Dependencies
|
||||
- **Medium**:
|
||||
- `@timesafari/daily-notification-plugin` (external package)
|
||||
- Capacitor core APIs
|
||||
- Platform detection utilities
|
||||
|
||||
#### Infrastructure Dependencies
|
||||
- **Low**:
|
||||
- Package.json update (add plugin dependency)
|
||||
- Vite conditional imports for Capacitor builds only
|
||||
- No infrastructure changes required
|
||||
|
||||
### Risk Factors
|
||||
|
||||
1. **Plugin Availability**: Plugin may not be available in package registry
|
||||
- **Mitigation**: Verify package availability, consider local development setup
|
||||
|
||||
2. **Platform Implementation**: All platforms must implement interface methods
|
||||
- **Mitigation**: Follow PlatformService pattern - Capacitor provides full implementation, Web/Electron return null or throw errors
|
||||
|
||||
3. **Web/Electron Compatibility**: Feature must not break non-Capacitor builds
|
||||
- **Mitigation**: Use dynamic imports with platform checks, graceful fallbacks
|
||||
|
||||
4. **Store State Management**: Notification state persistence
|
||||
- **Mitigation**: Follow existing Pinia patterns in codebase
|
||||
|
||||
---
|
||||
|
||||
## Platform Analysis
|
||||
|
||||
### Target Platform: Capacitor Only
|
||||
|
||||
#### Capacitor Requirements
|
||||
- Android: API 21+ (already supported)
|
||||
- iOS: 13+ (already supported)
|
||||
- Native platform detection: `Capacitor.isNativePlatform()`
|
||||
|
||||
#### Build Configuration
|
||||
- **Vite Config**: `vite.config.capacitor.mts` (already exists)
|
||||
- **Build Command**: `npm run build:capacitor`
|
||||
- **Conditional Import Pattern**: Dynamic import based on `process.env.VITE_PLATFORM === 'capacitor'`
|
||||
|
||||
#### Platform Detection Strategy
|
||||
|
||||
**Pattern**: PlatformService interface - all platforms implement methods
|
||||
|
||||
Components check PlatformService capabilities by calling methods and checking for `null` returns:
|
||||
|
||||
```typescript
|
||||
// Components check capability via PlatformService
|
||||
const platformService = PlatformServiceFactory.getInstance();
|
||||
const status = await platformService.getDailyNotificationStatus();
|
||||
|
||||
if (status === null) {
|
||||
// Notifications not supported on this platform - hide UI
|
||||
return;
|
||||
}
|
||||
// Continue with notification features
|
||||
```
|
||||
|
||||
**Why PlatformService Pattern?**
|
||||
- Consistent with existing platform capabilities (camera, filesystem)
|
||||
- All platforms implement the interface (contract compliance)
|
||||
- Unsupported platforms return `null` or throw clear errors
|
||||
- Components handle capability detection via method results, not environment variables
|
||||
|
||||
### Web/Electron Implementation Strategy
|
||||
|
||||
#### Web Platform
|
||||
- **Implementation**: All notification methods implemented in `WebPlatformService`
|
||||
- **Behavior**: Methods return `null` for status/permissions, throw errors for scheduling
|
||||
- **UI**: Components check for `null` responses to hide notification UI
|
||||
- **Plugin Import**: No plugin imports - methods return null/throw errors directly
|
||||
|
||||
#### Electron Platform
|
||||
- **Implementation**: All notification methods implemented in `ElectronPlatformService`
|
||||
- **Behavior**: Methods return `null` for status/permissions, throw errors for scheduling
|
||||
- **UI**: Components check for `null` responses to hide notification UI
|
||||
- **Plugin Import**: No plugin imports - methods return null/throw errors directly
|
||||
|
||||
---
|
||||
|
||||
## Architecture Design
|
||||
|
||||
### PlatformService Integration
|
||||
|
||||
**Key Pattern**: Add notification methods directly to `PlatformService` interface, implemented on all platforms. Unsupported platforms return `null` or empty results.
|
||||
|
||||
This follows the same pattern as other platform capabilities (camera, filesystem) where all platforms implement the interface, but unsupported platforms return null/empty results.
|
||||
|
||||
```typescript
|
||||
// src/services/PlatformService.ts - Add to interface
|
||||
export interface PlatformService {
|
||||
// ... existing methods ...
|
||||
|
||||
// Daily notification operations
|
||||
/**
|
||||
* Get the status of scheduled daily notifications
|
||||
* @returns Promise resolving to notification status, or null if not supported
|
||||
*/
|
||||
getDailyNotificationStatus(): Promise<NotificationStatus | null>;
|
||||
|
||||
/**
|
||||
* Check notification permissions
|
||||
* @returns Promise resolving to permission status, or null if not supported
|
||||
*/
|
||||
checkNotificationPermissions(): Promise<PermissionStatus | null>;
|
||||
|
||||
/**
|
||||
* Request notification permissions
|
||||
* @returns Promise resolving to permission result, or null if not supported
|
||||
*/
|
||||
requestNotificationPermissions(): Promise<PermissionResult | null>;
|
||||
|
||||
/**
|
||||
* Schedule a daily notification
|
||||
* @param options - Notification scheduling options
|
||||
* @returns Promise that resolves when scheduled, or rejects if not supported
|
||||
*/
|
||||
scheduleDailyNotification(options: ScheduleOptions): Promise<void>;
|
||||
|
||||
/**
|
||||
* Cancel scheduled daily notification
|
||||
* @returns Promise that resolves when cancelled, or rejects if not supported
|
||||
*/
|
||||
cancelDailyNotification(): Promise<void>;
|
||||
|
||||
/**
|
||||
* Configure native fetcher for background operations
|
||||
* @param config - Native fetcher configuration
|
||||
* @returns Promise that resolves when configured, or null if not supported
|
||||
*/
|
||||
configureNativeFetcher(config: NativeFetcherConfig): Promise<void | null>;
|
||||
|
||||
/**
|
||||
* Update starred plans for background fetcher
|
||||
* @param plans - Starred plan IDs
|
||||
* @returns Promise that resolves when updated, or null if not supported
|
||||
*/
|
||||
updateStarredPlans(plans: { planIds: string[] }): Promise<void | null>;
|
||||
}
|
||||
```
|
||||
|
||||
**Implementation Pattern**:
|
||||
- **CapacitorPlatformService**: Full implementation using `@timesafari/daily-notification-plugin`
|
||||
- **WebPlatformService**: Returns `null` for status/permissions, throws errors for scheduling operations
|
||||
- **ElectronPlatformService**: Returns `null` for status/permissions, throws errors for scheduling operations
|
||||
|
||||
### PlatformService Interface Extensions
|
||||
|
||||
```typescript
|
||||
// Types/interfaces for notification operations
|
||||
export interface NotificationStatus {
|
||||
isScheduled: boolean;
|
||||
scheduledTime?: string; // "HH:mm" format
|
||||
lastTriggered?: string;
|
||||
permissions: PermissionStatus;
|
||||
}
|
||||
|
||||
export interface PermissionStatus {
|
||||
notifications: 'granted' | 'denied' | 'prompt';
|
||||
exactAlarms?: 'granted' | 'denied' | 'prompt'; // Android only
|
||||
}
|
||||
|
||||
export interface PermissionResult {
|
||||
notifications: boolean;
|
||||
exactAlarms?: boolean; // Android only
|
||||
}
|
||||
|
||||
export interface ScheduleOptions {
|
||||
time: string; // "HH:mm" format in local time
|
||||
title: string;
|
||||
body: string;
|
||||
sound?: boolean;
|
||||
priority?: 'high' | 'normal' | 'low';
|
||||
}
|
||||
|
||||
export interface NativeFetcherConfig {
|
||||
apiServer: string;
|
||||
jwt: string;
|
||||
starredPlanHandleIds: string[];
|
||||
}
|
||||
```
|
||||
|
||||
**Implementation Behavior**:
|
||||
- **Capacitor**: Full implementation, all methods functional
|
||||
- **Web/Electron**: Status/permission methods return `null`, scheduling methods throw errors with clear messages
|
||||
|
||||
### Component Architecture
|
||||
|
||||
#### Views Structure
|
||||
```
|
||||
src/views/
|
||||
├── HomeView.vue (existing - modify to add notification diagnostics)
|
||||
├── ScheduleView.vue (new - notification scheduling)
|
||||
├── NotificationsView.vue (new - view scheduled notifications)
|
||||
├── NotificationHistoryView.vue (new - notification history)
|
||||
└── NotificationSettingsView.vue (new - notification settings)
|
||||
```
|
||||
|
||||
#### Supporting Components
|
||||
```
|
||||
src/components/cards/
|
||||
├── ActionCard.vue (new - reusable action card)
|
||||
└── StatusCard.vue (new - reusable status card)
|
||||
```
|
||||
|
||||
#### Store Structure
|
||||
```
|
||||
src/stores/
|
||||
└── app.ts (new - Pinia store for app-wide state)
|
||||
- notificationStatus: NotificationStatus | null
|
||||
- platform: 'web' | 'capacitor' | 'electron'
|
||||
- setNotificationStatus(status): void
|
||||
```
|
||||
|
||||
### Router Integration
|
||||
|
||||
```typescript
|
||||
// src/router/index.ts - Add new routes
|
||||
{
|
||||
path: "/schedule",
|
||||
name: "schedule",
|
||||
component: () => import("../views/ScheduleView.vue"),
|
||||
},
|
||||
{
|
||||
path: "/notifications",
|
||||
name: "notifications",
|
||||
component: () => import("../views/NotificationsView.vue"),
|
||||
},
|
||||
{
|
||||
path: "/history",
|
||||
name: "notification-history",
|
||||
component: () => import("../views/NotificationHistoryView.vue"),
|
||||
},
|
||||
{
|
||||
path: "/settings",
|
||||
name: "settings",
|
||||
component: () => import("../views/NotificationSettingsView.vue"),
|
||||
},
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Phase Breakdown
|
||||
|
||||
### Phase 1: Foundation & Infrastructure
|
||||
|
||||
**Complexity**: Low-Medium
|
||||
**Goals**: Set up factory architecture, store, and conditional loading
|
||||
|
||||
#### Tasks
|
||||
1. **Package Dependency**
|
||||
- Add `@timesafari/daily-notification-plugin` to `package.json`
|
||||
- Verify package availability/version
|
||||
- Document in dependencies section
|
||||
|
||||
2. **PlatformService Interface Extension**
|
||||
- Add notification methods to `PlatformService` interface
|
||||
- Define notification types/interfaces (NotificationStatus, ScheduleOptions, etc.)
|
||||
- Implement in `CapacitorPlatformService` using `@timesafari/daily-notification-plugin`
|
||||
- Implement in `WebPlatformService` with null returns / error throws
|
||||
- Implement in `ElectronPlatformService` with null returns / error throws
|
||||
|
||||
3. **Pinia Store Setup**
|
||||
- Create `src/stores/app.ts` with notification state
|
||||
- Define `NotificationStatus` interface
|
||||
- Implement `setNotificationStatus()` action
|
||||
- Add platform detection to store
|
||||
|
||||
4. **Native Fetcher Configuration Integration**
|
||||
- Update HomeView `configureNativeFetcher()` to use active DID management
|
||||
- Replace `TEST_USER_ZERO_CONFIG` references with `$getActiveIdentity()`
|
||||
- Replace `generateEndorserJWT` with `createEndorserJwtForDid()` from `src/libs/endorserServer.ts`
|
||||
- Get `apiServer` and `starredPlanHandleIds` from `$accountSettings()`
|
||||
|
||||
5. **Router Routes**
|
||||
- Add route definitions for schedule, notifications, history, settings
|
||||
- Test route navigation
|
||||
|
||||
#### Acceptance Criteria
|
||||
- [ ] PlatformService interface extended with notification methods
|
||||
- [ ] CapacitorPlatformService implements notification methods using plugin
|
||||
- [ ] WebPlatformService and ElectronPlatformService return null/throw errors appropriately
|
||||
- [ ] Pinia store created and tested
|
||||
- [ ] HomeView `configureNativeFetcher()` updated to use active DID (no TEST_USER_ZERO_CONFIG)
|
||||
- [ ] Routes added and accessible
|
||||
- [ ] No build errors in web/electron builds
|
||||
|
||||
---
|
||||
|
||||
### Phase 2: Core Components
|
||||
|
||||
**Complexity**: Medium
|
||||
**Goals**: Create reusable components and main views
|
||||
|
||||
#### Tasks
|
||||
1. **Reusable Components**
|
||||
- Create `ActionCard.vue` component
|
||||
- Create `StatusCard.vue` component
|
||||
- Follow project styling patterns
|
||||
- Add TypeScript interfaces
|
||||
|
||||
2. **Home View Integration**
|
||||
- Modify existing `HomeView.vue` OR create new notification home view
|
||||
- Integrate plugin diagnostics
|
||||
- Add system status display
|
||||
- Connect to Pinia store
|
||||
- Replace `console.*` with project logger
|
||||
|
||||
3. **Schedule View**
|
||||
- Create `ScheduleView.vue` (provided code as reference)
|
||||
- Integrate with PlatformService via PlatformServiceFactory
|
||||
- Add error handling
|
||||
- Replace `console.*` with project logger
|
||||
- Add loading states
|
||||
|
||||
4. **AccountViewView Integration** ✅ **ACCEPTED: Option A**
|
||||
- Add separate "Daily Notifications" section
|
||||
- Check platform capabilities before showing UI
|
||||
- Add toggle switch for enabling/disabling notifications
|
||||
- Add HTML5 time input for scheduling time
|
||||
- Integrate with PlatformService via PlatformServiceFactory
|
||||
- Save/load settings from `settings` table
|
||||
|
||||
#### Acceptance Criteria
|
||||
- [ ] ActionCard and StatusCard components created
|
||||
- [ ] Home view shows notification diagnostics
|
||||
- [ ] Schedule view allows notification scheduling
|
||||
- [ ] AccountViewView has separate "Daily Notifications" section (Option A accepted)
|
||||
- [ ] Notification section checks PlatformService capabilities before showing
|
||||
- [ ] Toggle and time input functional in AccountViewView
|
||||
- [ ] All logging uses project logger
|
||||
- [ ] Error handling implemented
|
||||
- [ ] Loading states visible
|
||||
|
||||
---
|
||||
|
||||
### Phase 3: Supporting Views & Configuration
|
||||
|
||||
**Complexity**: Medium
|
||||
**Goals**: Complete all views and native fetcher configuration
|
||||
|
||||
#### Tasks
|
||||
1. **Supporting Views**
|
||||
- Create `NotificationsView.vue` (list scheduled notifications)
|
||||
- Create `NotificationHistoryView.vue` (notification history)
|
||||
- Create `NotificationSettingsView.vue` (settings/preferences)
|
||||
|
||||
2. **Native Fetcher Configuration**
|
||||
- Integrate `configureNativeFetcher()` in HomeView
|
||||
- Use `$getActiveIdentity()` to get active DID (replace TEST_USER_ZERO_CONFIG)
|
||||
- Use `createEndorserJwtForDid()` for JWT generation
|
||||
- Get `apiServer` and `starredPlanHandleIds` from `$accountSettings()`
|
||||
- Add error handling for configuration failures
|
||||
|
||||
3. **Permission Management**
|
||||
- Implement permission request flow
|
||||
- Handle permission denial gracefully
|
||||
- Update status after permission changes
|
||||
|
||||
#### Acceptance Criteria
|
||||
- [ ] All supporting views created and functional
|
||||
- [ ] Native fetcher configuration working
|
||||
- [ ] Permission requests handled properly
|
||||
- [ ] Status updates after permission changes
|
||||
- [ ] Error handling for all failure cases
|
||||
|
||||
---
|
||||
|
||||
### Phase 4: Testing & Validation
|
||||
|
||||
**Complexity**: Medium-High
|
||||
**Goals**: Comprehensive testing across platforms and scenarios
|
||||
|
||||
#### Tasks
|
||||
1. **Capacitor Testing**
|
||||
- Test plugin availability detection
|
||||
- Test notification scheduling on Android
|
||||
- Test notification scheduling on iOS
|
||||
- Test permission requests
|
||||
- Test status updates
|
||||
- Test native fetcher configuration
|
||||
|
||||
2. **Cross-Platform Validation**
|
||||
- Verify web build doesn't break
|
||||
- Verify Electron build doesn't break
|
||||
- Verify feature is hidden on non-Capacitor platforms
|
||||
- Test graceful degradation
|
||||
|
||||
3. **Integration Testing**
|
||||
- Test full scheduling workflow
|
||||
- Test status checking workflow
|
||||
- Test navigation between views
|
||||
- Test store state persistence
|
||||
|
||||
4. **Error Scenarios**
|
||||
- Test plugin unavailable scenarios
|
||||
- Test permission denied scenarios
|
||||
- Test network failures (for native fetcher)
|
||||
- Test invalid configuration scenarios
|
||||
|
||||
#### Acceptance Criteria
|
||||
- [ ] All Capacitor tests passing
|
||||
- [ ] Web/Electron builds unaffected
|
||||
- [ ] Integration tests passing
|
||||
- [ ] Error scenarios handled gracefully
|
||||
- [ ] Documentation updated
|
||||
|
||||
---
|
||||
|
||||
## Milestones
|
||||
|
||||
### Milestone 1: Foundation Complete
|
||||
**Success Criteria**:
|
||||
- Factory service operational
|
||||
- Store created and tested
|
||||
- Routes accessible
|
||||
- No build regressions
|
||||
|
||||
### Milestone 2: Core Features Operational
|
||||
**Success Criteria**:
|
||||
- Home view shows diagnostics
|
||||
- Schedule view functional
|
||||
- Plugin integration working
|
||||
- Logging standardized
|
||||
|
||||
### Milestone 3: Full Feature Set
|
||||
**Success Criteria**:
|
||||
- All views created and functional
|
||||
- Native fetcher configured
|
||||
- Permissions managed properly
|
||||
- Status updates working
|
||||
|
||||
### Milestone 4: Production Ready
|
||||
**Success Criteria**:
|
||||
- All tests passing
|
||||
- Cross-platform validation complete
|
||||
- Error handling robust
|
||||
- Documentation complete
|
||||
|
||||
---
|
||||
|
||||
## Testing Strategy
|
||||
|
||||
### Unit Tests
|
||||
- Factory service platform detection
|
||||
- Store actions and state management
|
||||
- Component rendering and interactions
|
||||
|
||||
### Integration Tests
|
||||
- Plugin API calls
|
||||
- Permission flows
|
||||
- Status updates
|
||||
- Navigation between views
|
||||
|
||||
### Platform Tests
|
||||
- **Capacitor Android**: Notification scheduling, permissions, status
|
||||
- **Capacitor iOS**: Notification scheduling, permissions, status
|
||||
- **Web**: Feature hidden, no errors
|
||||
- **Electron**: Feature hidden, no errors
|
||||
|
||||
### E2E Tests (Playwright)
|
||||
- Full notification scheduling workflow
|
||||
- Permission request flow
|
||||
- Status checking workflow
|
||||
- Error handling scenarios
|
||||
|
||||
---
|
||||
|
||||
## Dependencies
|
||||
|
||||
### External Dependencies
|
||||
- `@timesafari/daily-notification-plugin` (to be added)
|
||||
- `@capacitor/core` (already in project)
|
||||
- `vue` (already in project)
|
||||
- `vue-router` (already in project)
|
||||
- `pinia` (already in project)
|
||||
|
||||
### Internal Dependencies
|
||||
- Logger service (`@/utils/logger`)
|
||||
- Platform detection utilities
|
||||
- Router configuration
|
||||
- Existing component patterns
|
||||
|
||||
### Configuration Dependencies
|
||||
- **Active DID Management**: Use `$getActiveIdentity()` from `PlatformServiceMixin` (existing)
|
||||
- **JWT Generation**: Use `createEndorserJwtForDid(activeDid, payload)` from `src/libs/endorserServer.ts` (existing)
|
||||
- **Settings Access**: Use `$accountSettings()` for `apiServer` and `starredPlanHandleIds` (existing)
|
||||
- **No new config files needed**: Replace `TEST_USER_ZERO_CONFIG` references with active DID and settings
|
||||
|
||||
---
|
||||
|
||||
## Implementation Notes
|
||||
|
||||
### Code Quality Standards
|
||||
- **Logging**: Use `logger` from `@/utils/logger`, not `console.*`
|
||||
- **File Documentation**: Add file-level documentation headers
|
||||
- **Method Documentation**: Rich method-level documentation
|
||||
- **Type Safety**: Full TypeScript typing
|
||||
- **PEP8/Prettier**: Follow code style guidelines
|
||||
- **Line Length**: Keep methods < 80 columns when possible
|
||||
|
||||
### Architecture Patterns to Follow
|
||||
- **Factory Pattern**: Like `QRScannerFactory` for conditional loading
|
||||
- **Service Interface**: Abstract interface with platform implementations
|
||||
- **Store Pattern**: Pinia store for state management
|
||||
- **Composition API vs Class**: Use provided code style (Composition API for HomeView, Class for ScheduleView)
|
||||
|
||||
### PlatformService Integration Strategy
|
||||
|
||||
**Pattern**: Direct integration into PlatformService interface (like camera, filesystem methods)
|
||||
|
||||
```typescript
|
||||
// In components - use PlatformServiceFactory pattern
|
||||
import { PlatformServiceFactory } from '@/services/PlatformServiceFactory';
|
||||
|
||||
const platformService = PlatformServiceFactory.getInstance();
|
||||
|
||||
// Check if notifications are supported
|
||||
const status = await platformService.getDailyNotificationStatus();
|
||||
if (status === null) {
|
||||
// Notifications not supported on this platform - hide UI
|
||||
return;
|
||||
}
|
||||
|
||||
// Schedule notification
|
||||
await platformService.scheduleDailyNotification({
|
||||
time: "09:00",
|
||||
title: "Daily Update",
|
||||
body: "Your daily notification is ready!",
|
||||
});
|
||||
```
|
||||
|
||||
**Key Points**:
|
||||
- Methods available on all PlatformService implementations
|
||||
- CapacitorPlatformService provides full implementation
|
||||
- WebPlatformService/ElectronPlatformService return `null` or throw errors
|
||||
- Components check for `null` responses to hide/show UI appropriately
|
||||
- No separate factory needed - uses existing PlatformServiceFactory pattern
|
||||
|
||||
---
|
||||
|
||||
## Risk Mitigation
|
||||
|
||||
### Risk 1: Plugin Package Unavailable
|
||||
**Mitigation**:
|
||||
- Verify package exists and is accessible
|
||||
- Consider local development setup if needed
|
||||
- Document package installation requirements
|
||||
|
||||
### Risk 2: Platform Detection Failures
|
||||
**Mitigation**:
|
||||
- Use proven patterns from `QRScannerFactory`
|
||||
- Test on all platforms
|
||||
- Add fallback logic
|
||||
|
||||
### Risk 3: Web/Electron Build Breaks
|
||||
**Mitigation**:
|
||||
- Use dynamic imports exclusively
|
||||
- Test web/electron builds after each phase
|
||||
- Ensure no static plugin imports
|
||||
|
||||
### Risk 4: Configuration Dependencies (RESOLVED)
|
||||
**Mitigation**:
|
||||
- **Use existing active DID management**: Use `$getActiveIdentity()` from `PlatformServiceMixin` to get currently selected DID
|
||||
- **Use existing JWT generation**: Use `createEndorserJwtForDid(activeDid, payload)` from `src/libs/endorserServer.ts`
|
||||
- **Use existing settings**: Get `apiServer` and `starredPlanHandleIds` from `$accountSettings()`
|
||||
- **No config files needed**: The HomeView component code references `TEST_USER_ZERO_CONFIG`, but should instead use the currently active DID and settings
|
||||
|
||||
---
|
||||
|
||||
## Success Criteria Summary
|
||||
|
||||
- [ ] Plugin integrated using factory architecture
|
||||
- [ ] Feature works on Capacitor (Android/iOS)
|
||||
- [ ] Feature hidden/graceful on Web/Electron
|
||||
- [ ] All components created and functional
|
||||
- [ ] Store manages notification state
|
||||
- [ ] Router routes accessible
|
||||
- [ ] Logging standardized (no console.*)
|
||||
- [ ] Error handling robust
|
||||
- [ ] Cross-platform testing complete
|
||||
- [ ] Documentation updated
|
||||
- [ ] No build regressions
|
||||
|
||||
---
|
||||
|
||||
## Next Steps
|
||||
|
||||
1. **Verify Plugin Package**: Confirm `@timesafari/daily-notification-plugin` availability
|
||||
2. **Update HomeView Configuration**: Replace `TEST_USER_ZERO_CONFIG` references in HomeView with existing active DID management (`$getActiveIdentity()`, `createEndorserJwtForDid()`, `$accountSettings()`)
|
||||
3. **Extend PlatformService**: Add notification methods to PlatformService interface and implement in all platform services
|
||||
4. **Set Up Store**: Create Pinia store for notification state
|
||||
5. **Begin Phase 1 Implementation**: Start with foundation tasks
|
||||
|
||||
---
|
||||
|
||||
**See also**:
|
||||
- `.cursor/rules/meta_feature_planning.mdc` - Feature planning workflow
|
||||
- `.cursor/rules/app/architectural_patterns.mdc` - Architecture patterns
|
||||
- `.cursor/rules/app/timesafari_platforms.mdc` - Platform requirements
|
||||
- `src/services/QRScanner/QRScannerFactory.ts` - Factory pattern reference
|
||||
Reference in New Issue
Block a user