You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

15 KiB

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:

<!-- 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

  • Approach decision finalized
  • Implementation begins (Phase 1)

UI Component Design

1. Platform Capability Detection

// 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

// 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

<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.

// 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

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

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

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

// 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

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

  • 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