Files
crowd-funder-for-time-pwa/doc/daily-notification-plugin-integration-plan.md
Matthew Raymer 45eff4a9ac docs: add plugin state sync, time update logic, and component extraction
- Update initialization to sync with plugin state on mount (checks for pre-existing schedules)
- Add updateNotificationTime() method to update schedule when time changes (cancel old, schedule new)
- Extract DailyNotificationSection into dedicated component using vue-facing-decorator
- Update component architecture to show DailyNotificationSection.vue structure
- Update Phase 2 tasks to reflect component creation and AccountViewView integration
- Add acceptance criteria for plugin state sync and time update functionality
- Update verification checklist with new requirements
2025-11-05 06:34:53 +00:00

1090 lines
38 KiB
Markdown

# 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<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/
└── 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
<template>
<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"
>
<!-- Daily Notifications UI -->
</section>
</template>
<script lang="ts">
import { Component, Vue } from "vue-facing-decorator";
import { PlatformServiceMixin } from "@/utils/PlatformServiceMixin";
import { logger } from "@/utils/logger";
/**
* DailyNotificationSection Component
*
* A self-contained component for managing daily notification scheduling
* in AccountViewView. This component handles platform detection, permission
* requests, scheduling, and state management for daily notifications.
*
* Features:
* - Platform capability detection (hides on unsupported platforms)
* - Permission request flow
* - Schedule/cancel notifications
* - Time editing with HTML5 time input
* - Settings persistence
* - Plugin state synchronization
*/
@Component({
name: "DailyNotificationSection",
mixins: [PlatformServiceMixin],
})
export default class DailyNotificationSection extends Vue {
// Component implementation here
}
</script>
```
---
## 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<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!";
notificationsSupported: boolean = false; // Computed from PlatformService
```
#### 3. Template Section
```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>
```
#### 4. 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.
#### 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 <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 (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 `<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**: 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
<!-- Option 1: Conditional rendering with v-if -->
<section v-if="notificationsSupported">
<!-- Scheduling UI -->
</section>
<!-- Option 2: Early return in component logic -->
<template>
<div v-if="notificationsSupported">
<!-- Scheduling UI -->
</div>
<div v-else>
<!-- Optional: Show unsupported message -->
<p>Notifications are not supported on this platform.</p>
</div>
</template>
```
#### 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