# 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**: AccountViewView integration (settings UI) with optional supporting components - **Store**: No store needed - state managed locally in AccountViewView --- ## Complexity Assessment ### Technical Complexity: **Medium** #### Code Changes - **Medium**: AccountViewView modification, optional supporting components - **Pattern**: Following PlatformService interface pattern (like camera, filesystem methods) - all platforms implement, unsupported return null - **Integration**: Plugin API integration with error handling - **UI Integration**: AccountViewView modification with new notification section and optional supporting components #### 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) - AccountViewView UI integration ### Dependency Complexity #### Internal Dependencies - **Medium**: - Optional supporting components (only if AccountViewView exceeds length limits) - Logger integration (replace console.* with project logger) - AccountViewView modifications - Settings schema updates #### External Dependencies - **Medium**: - `@timesafari/daily-notification-plugin` (external package) - `@capacitor/core` (already in project) - 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**: State managed locally in AccountViewView - no store needed --- ## 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 **CRITICAL REQUIREMENT**: All notification scheduling components MUST hide themselves if the current device does not support scheduling. 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 **Component Visibility Requirements**: - **AccountViewView notification section**: Must use `v-if="notificationsSupported"` to hide section - **Supporting components** (if created): Must check platform support before rendering - **Any component providing scheduling UI**: Must verify `getDailyNotificationStatus() !== null` before showing scheduling controls ### 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; /** * Check notification permissions * @returns Promise resolving to permission status, or null if not supported */ checkNotificationPermissions(): Promise; /** * Request notification permissions * @returns Promise resolving to permission result, or null if not supported */ requestNotificationPermissions(): Promise; /** * Schedule a daily notification * @param options - Notification scheduling options * @returns Promise that resolves when scheduled, or rejects if not supported */ scheduleDailyNotification(options: ScheduleOptions): Promise; /** * Cancel scheduled daily notification * @returns Promise that resolves when cancelled, or rejects if not supported */ cancelDailyNotification(): Promise; /** * 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; /** * 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; } ``` **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; // Ignored - generated automatically by configureNativeFetcher starredPlanHandleIds: string[]; } ``` **Implementation Behavior**: - **Capacitor**: Full implementation, all methods functional - **Web/Electron**: Status/permission methods return `null`, scheduling methods throw errors with clear messages ### Authentication & Token Management #### Background Prefetch Authentication The daily notification plugin requires authentication tokens for background prefetch operations. The implementation uses a **hybrid token refresh strategy** that balances security with offline capability. **Token Generation** (`src/libs/crypto/index.ts`): - Function: `accessTokenForBackground(did, expirationMinutes?)` - Default expiration: **72 hours** (4320 minutes) - Token type: JWT with ES256K signing - Payload: `{ exp, iat, iss: did }` **Why 72 Hours?** - Balances security (read-only prefetch operations) with offline capability - Reduces need for app to wake itself for token refresh - Allows plugin to work offline for extended periods (e.g., weekend trips) - Longer than typical prefetch windows (5 minutes before notification) **Token Refresh Strategy (Hybrid Approach)**: 1. **Proactive Refresh Triggers**: - Component mount (`DailyNotificationSection.mounted()`) - App resume (Capacitor `resume` event) - Notification enabled (when user enables daily notifications) 2. **Refresh Implementation** (`DailyNotificationSection.refreshNativeFetcherConfig()`): - Checks if notifications are supported and enabled - Retrieves API server URL from settings - Retrieves starred plans from settings - Calls `configureNativeFetcher()` to generate fresh token - Errors are logged but don't interrupt user experience 3. **Offline Behavior**: - If token expires while offline → plugin uses cached content - Next time app opens → token automatically refreshed - No app wake-up required (refresh happens when app is already open) **Configuration Flow** (`CapacitorPlatformService.configureNativeFetcher()`): 1. Retrieves active DID from `active_identity` table (single source of truth) 2. Generates JWT token with 72-hour expiration using `accessTokenForBackground()` 3. Configures plugin with API server URL, active DID, and JWT token 4. Plugin stores token in its Room database for background workers **Security Considerations**: - Tokens are used only for read-only prefetch operations - Tokens are stored securely in plugin's Room database - Tokens are refreshed proactively to minimize exposure window - No private keys are exposed to native code - Token generation happens in TypeScript (no Java crypto compatibility issues) **Error Handling**: - Returns `null` if active DID not found (no user logged in) - Returns `null` if JWT generation fails - Logs errors but doesn't throw (allows graceful degradation) - Refresh failures don't interrupt user experience (plugin uses cached content) ### Component Architecture #### Views Structure ``` src/views/ └── AccountViewView.vue (existing - add DailyNotificationSection component) ``` #### Supporting Components ``` src/components/notifications/ └── DailyNotificationSection.vue (required - extracted section component) ``` **Component Structure**: `DailyNotificationSection.vue` will use vue-facing-decorator with ES6 classes ```vue ``` --- ## AccountViewView Integration Strategy ### Overview Integrate daily notification scheduling into `AccountViewView.vue`, allowing users to configure notification times directly from their account settings. ### Integration Approach ✅ **ACCEPTED** **Decision**: 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 ### UI Component Design #### 1. Platform Capability Detection ```typescript // In AccountViewView component async checkNotificationSupport(): Promise { 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!"; notificationsSupported: boolean = false; // Computed from PlatformService ``` #### 3. Template Section ```vue

Daily Notifications

Daily Notification
Scheduled for: {{ nativeNotificationTime }}
``` #### 4. Time Input ✅ **SELECTED: HTML5 Time Input** **Decision**: Use HTML5 `` for native mobile experience ```vue ``` **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. #### 5. 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 // 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 `` 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 (Sync with Plugin State) ```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 this.notificationsSupported = false; return; } this.notificationsSupported = true; // CRITICAL: Sync with plugin state first (source of truth) // Plugin may have an existing schedule even if settings don't if (status.isScheduled && status.scheduledTime) { // Plugin has a scheduled notification - sync UI to match this.nativeNotificationEnabled = true; this.nativeNotificationTimeStorage = status.scheduledTime; this.nativeNotificationTime = formatTimeForDisplay(status.scheduledTime); // Also sync settings to match plugin state const settings = await this.$accountSettings(); if (settings.nativeNotificationTime !== status.scheduledTime) { await this.$saveSettings({ nativeNotificationTime: status.scheduledTime, nativeNotificationTitle: settings.nativeNotificationTitle || this.nativeNotificationTitle, nativeNotificationMessage: settings.nativeNotificationMessage || this.nativeNotificationMessage, }); } } else { // No plugin schedule - check settings for user preference const settings = await this.$accountSettings(); const nativeNotificationTime = settings.nativeNotificationTime || ""; this.nativeNotificationEnabled = !!nativeNotificationTime; this.nativeNotificationTimeStorage = nativeNotificationTime; if (nativeNotificationTime) { this.nativeNotificationTime = formatTimeForDisplay(nativeNotificationTime); } } // Update UI with current status this.notificationStatus = status; } ``` **Key Points**: - `getDailyNotificationStatus()` is called on mount to check for pre-existing schedules - Plugin state is the source of truth - if plugin has a schedule, UI syncs to match - Settings are synced with plugin state if they differ - If no plugin schedule exists, fall back to settings #### 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 (Update Schedule) **Approach**: When time changes, immediately update the scheduled notification ```typescript async editNativeNotificationTime() { // Show inline HTML5 time input for quick changes this.showTimeEdit = true; } async updateNotificationTime(newTime: string) { // newTime is in "HH:mm" format from HTML5 time input if (!this.nativeNotificationEnabled) { // If notification is disabled, just save the time preference this.nativeNotificationTimeStorage = newTime; this.nativeNotificationTime = formatTimeForDisplay(newTime); await this.$saveSettings({ nativeNotificationTime: newTime, }); return; } // Notification is enabled - update the schedule try { const platformService = PlatformServiceFactory.getInstance(); // 1. Cancel existing notification await platformService.cancelDailyNotification(); // 2. Schedule with new time await platformService.scheduleDailyNotification({ time: newTime, // "09:00" in local time title: this.nativeNotificationTitle, body: this.nativeNotificationMessage, sound: true, priority: 'high' }); // 3. Update local state this.nativeNotificationTimeStorage = newTime; this.nativeNotificationTime = formatTimeForDisplay(newTime); // 4. Save to settings await this.$saveSettings({ nativeNotificationTime: newTime, }); this.notify.success("Notification time updated successfully", TIMEOUTS.SHORT); this.showTimeEdit = false; } catch (error) { logger.error("Failed to update notification time:", error); this.notify.error("Failed to update notification time. Please try again.", TIMEOUTS.LONG); } } ``` **Implementation Note**: HTML5 time input provides native mobile picker experience when shown inline, making it ideal for quick time adjustments. When the time changes, the notification schedule is immediately updated via PlatformService. ### 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 ### 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 Decisions ✅ #### Time Input Format ✅ - **Selected**: HTML5 `` 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**: All editing happens within AccountViewView - no separate views needed #### 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 --- ## 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. **Settings Schema Extension** - [ ] Add notification settings fields to Settings interface - [ ] Update settings persistence methods if needed #### Acceptance Criteria - [ ] PlatformService interface extended with notification methods - [ ] CapacitorPlatformService implements notification methods using plugin - [ ] WebPlatformService and ElectronPlatformService return null/throw errors appropriately - [ ] Settings schema extended with notification fields - [ ] No build errors in web/electron builds --- ### Phase 2: AccountViewView Integration **Complexity**: Medium **Goals**: Integrate notification scheduling into AccountViewView with optional supporting components #### Tasks 1. **DailyNotificationSection Component** - [ ] Create `src/components/notifications/DailyNotificationSection.vue` - [ ] Use vue-facing-decorator with ES6 class extending Vue - [ ] Add PlatformServiceMixin to component - [ ] Implement platform capability detection on mount - [ ] Implement initialization that syncs with plugin state (checks for pre-existing schedules) - [ ] Add toggle switch for enabling/disabling notifications - [ ] Add HTML5 time input for scheduling time - [ ] Integrate with PlatformService via PlatformServiceFactory - [ ] Implement time format conversion (display vs storage) - [ ] Add enable/disable notification methods - [ ] Add edit time functionality with schedule update (cancel old, schedule new) - [ ] Add permission request flow - [ ] Add error handling and user feedback - [ ] Save/load settings from `settings` table - [ ] Follow project styling patterns - [ ] Add TypeScript interfaces - [ ] Add file-level documentation 2. **AccountViewView Integration** - [ ] Import DailyNotificationSection component - [ ] Add component to template (minimal integration) - [ ] Verify component renders correctly - [ ] Test component hiding on unsupported platforms #### Acceptance Criteria - [ ] DailyNotificationSection component created using vue-facing-decorator - [ ] Component extends Vue class with PlatformServiceMixin - [ ] Component checks platform support on mount via `getDailyNotificationStatus()` - [ ] Component syncs with plugin state on initialization (checks for pre-existing schedules) - [ ] Component hidden on unsupported platforms (`v-if="notificationsSupported"`) - [ ] Toggle and time input functional - [ ] Time changes update notification schedule immediately (cancel old, schedule new) - [ ] Settings persist across app restarts - [ ] Plugin state syncs with settings on mount - [ ] All logging uses project logger - [ ] Error handling implemented - [ ] Loading states visible - [ ] UI matches existing design patterns - [ ] AccountViewView integration is minimal (just imports and uses component) --- ### Phase 3: Polish & Testing **Complexity**: Medium **Goals**: Complete AccountViewView integration, error handling, and testing #### Tasks 1. **Permission Management** - [ ] Implement permission request flow in AccountViewView - [ ] Handle permission denial gracefully - [ ] Update status after permission changes - [ ] Show appropriate user feedback 2. **Error Handling & User Feedback** - [ ] Add comprehensive error handling for all plugin operations - [ ] Implement loading states during async operations - [ ] Add success/error toast notifications - [ ] Handle edge cases (permission denied, plugin unavailable, etc.) 3. **Testing & Validation** - [ ] Test AccountViewView integration on Capacitor platforms - [ ] Verify component hiding on Web/Electron - [ ] Test all user workflows (enable, disable, edit time) - [ ] Verify settings persistence #### Acceptance Criteria - [ ] Permission requests handled properly - [ ] Status updates after permission changes - [ ] Error handling for all failure cases - [ ] User feedback (toasts, loading states) implemented - [ ] AccountViewView tested on all platforms - [ ] Component hiding verified on unsupported platforms --- --- ## Milestones ### Milestone 1: Foundation Complete **Success Criteria**: - [ ] PlatformService interface extended - [ ] Settings schema extended - [ ] No build regressions ### Milestone 2: AccountViewView Integration Complete **Success Criteria**: - [ ] AccountViewView notification section functional - [ ] Plugin integration working - [ ] Settings persistence working - [ ] Component hiding verified on unsupported platforms ### Milestone 3: Production Ready **Success Criteria**: - [ ] All tests passing - [ ] Cross-platform validation complete - [ ] Error handling robust - [ ] User feedback implemented - [ ] Documentation complete --- ## Testing Strategy ### Unit Tests - [ ] PlatformService platform detection - [ ] AccountViewView notification section rendering - [ ] Supporting component rendering (if created) ### Integration Tests - [ ] Plugin API calls from AccountViewView - [ ] Permission flows - [ ] Status updates - [ ] AccountViewView enable/disable/edit workflows - [ ] Settings persistence ### Platform Tests - [ ] **Capacitor Android**: Notification scheduling, permissions, status, AccountViewView UI - [ ] **Capacitor iOS**: Notification scheduling, permissions, status, AccountViewView UI - [ ] **Web**: Feature hidden, no errors, AccountViewView section hidden - [ ] **Electron**: Feature hidden, no errors, AccountViewView section hidden ### E2E Tests (Playwright) - [ ] AccountViewView notification configuration workflow - [ ] Permission request flow - [ ] Enable/disable notification workflow - [ ] Edit notification time workflow - [ ] Error handling scenarios --- ## Dependencies ### External Dependencies - `@timesafari/daily-notification-plugin` (to be added) - `@capacitor/core` (already in project) - `vue` (already in project) ### Internal Dependencies - Logger service (`@/utils/logger`) - Platform detection utilities - Existing component patterns - AccountViewView component - Settings schema and persistence ### Configuration Dependencies - **Settings Access**: Use `$accountSettings()` and `$saveSettings()` for persistence (existing) --- ## Implementation Notes ### Component Visibility Requirements **CRITICAL**: All components that provide notification scheduling UI MUST hide themselves if the current device does not support scheduling. #### Required Pattern for All Scheduling Components ```typescript // In component mounted/created lifecycle async mounted() { const platformService = PlatformServiceFactory.getInstance(); const status = await platformService.getDailyNotificationStatus(); if (status === null) { // Device does not support scheduling - hide component this.notificationsSupported = false; return; } // Device supports scheduling - proceed with initialization this.notificationsSupported = true; // ... rest of initialization } ``` #### Template Pattern ```vue
``` #### Components That Must Implement This Pattern 1. **DailyNotificationSection.vue**: Daily Notifications section uses `v-if="notificationsSupported"` and checks `getDailyNotificationStatus()` on mount 2. **Any component providing scheduling UI**: Must verify `getDailyNotificationStatus() !== null` before showing scheduling controls #### Verification Checklist - [ ] DailyNotificationSection checks platform support on mount and hides on unsupported platforms - [ ] DailyNotificationSection syncs with plugin state on initialization (checks for pre-existing schedules) - [ ] Component tested on Web/Electron to verify hiding works - [ ] No console errors when components are hidden - [ ] Time changes properly update notification schedule ### 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 - **Service Interface**: Abstract interface with platform implementations - **Component Organization**: Keep AccountViewView concise - extract supporting components if needed to maintain < 200 lines - **PlatformService Pattern**: Check capabilities via method results, not environment variables ### 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 - [ ] Verify AccountViewView section properly hidden ### Risk 4: AccountViewView Integration Issues **Mitigation**: - [ ] Use platform capability detection before showing UI - [ ] Test on all platforms to ensure proper hiding - [ ] Follow existing UI patterns for consistency - [ ] Add comprehensive error handling ### Risk 5: Components Visible on Unsupported Platforms **Mitigation**: - [ ] **REQUIRED**: All scheduling components must check `getDailyNotificationStatus()` and hide if `null` - [ ] Use `v-if="notificationsSupported"` pattern consistently - [ ] Add explicit verification in acceptance criteria - [ ] Test on Web/Electron builds to verify hiding works - [ ] Document required pattern in Implementation Notes section --- ## Success Criteria Summary - [ ] Plugin integrated using PlatformService architecture - [ ] Feature works on Capacitor (Android/iOS) - [ ] Feature hidden/graceful on Web/Electron - [ ] DailyNotificationSection component created and functional - [ ] **DailyNotificationSection hides itself on unsupported platforms** - [ ] Component syncs with plugin state on mount (checks for pre-existing schedules) - [ ] Time changes update notification schedule immediately - [ ] AccountViewView integration minimal (just imports component) - [ ] Settings persist across app restarts - [ ] Logging standardized (no console.*) - [ ] Error handling robust - [ ] Cross-platform testing complete - [ ] **Verified component hiding on Web/Electron platforms** - [ ] Documentation updated - [ ] No build regressions --- ## Next Steps - [ ] **Verify Plugin Package**: Confirm `@timesafari/daily-notification-plugin` availability - [ ] **Extend PlatformService**: Add notification methods to PlatformService interface and implement in all platform services - [ ] **Extend Settings Schema**: Add notification fields to Settings interface - [ ] **Begin Phase 1 Implementation**: Start with foundation tasks - [ ] **AccountViewView Integration**: Implement Daily Notifications section in Phase 2 --- **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 - `src/views/AccountViewView.vue` - Target component for integration