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.
 
 
 
 
 
 

13 KiB

TimeSafari PWA - ActiveDid Change Integration Guide

Author: Matthew Raymer
Version: 1.0.0
Created: 2025-10-08 06:24:57 UTC

Overview

This guide explains how the DailyNotification plugin integrates with the existing TimeSafari PWA activeDid change detection system. The TimeSafari PWA has a sophisticated activeDid change management system that the plugin must integrate with.

TimeSafari PWA ActiveDid Change System

How ActiveDid Changes Work

1. Database Storage

-- ActiveDid is stored in active_identity table (single source of truth)
UPDATE active_identity SET activeDid = ?, lastUpdated = datetime('now') WHERE id = 1

2. PlatformServiceMixin Change Detection

// In PlatformServiceMixin.ts
watch: {
  currentActiveDid: {
    handler(newDid: string | null, oldDid: string | null) {
      if (newDid !== oldDid) {
        logger.debug(`[PlatformServiceMixin] ActiveDid changed from ${oldDid} to ${newDid}`);
        // Clear caches that might be affected by the change
      }
    },
    immediate: true,
  },
}

3. Update Method

// In PlatformServiceMixin.ts
async $updateActiveDid(newDid: string | null): Promise<void> {
  const oldDid = this._currentActiveDid;
  this._currentActiveDid = newDid;

  if (newDid !== oldDid) {
    // Write to active_identity table (single source of truth)
    await this.$dbExec(
      "UPDATE active_identity SET activeDid = ?, lastUpdated = datetime('now') WHERE id = 1",
      [newDid || ""]
    );
  }
}

4. Usage in Components

// In IdentitySwitcherView.vue
async switchAccount(did?: string) {
  // Update the active DID in the active_identity table
  await this.$updateActiveDid(did);
  
  // Check if we need to load user-specific settings for the new DID
  if (did) {
    const newSettings = await this.$accountSettings(did);
    // Update UI with new settings
  }
}

DailyNotification Plugin Integration

How the Plugin Handles ActiveDid Changes

1. Enhanced updateActiveDid Method

// In CapacitorPlatformService.ts
async updateActiveDid(did: string): Promise<void> {
  const oldDid = this.currentActiveDid;
  
  // Update the database (existing TimeSafari pattern)
  await this.dbExec(
    "UPDATE active_identity SET activeDid = ?, lastUpdated = datetime('now') WHERE id = 1",
    [did],
  );
  
  // Update local tracking
  this.currentActiveDid = did;
  
  // Notify listeners of the change
  this.notifyActiveDidChange(did, oldDid);
  
  // Update DailyNotification plugin if initialized
  if (this.dailyNotificationInitialized) {
    await this.updateDailyNotificationActiveDid(did, oldDid);
  }
}

2. Plugin Reconfiguration

// In CapacitorPlatformService.ts
private async updateDailyNotificationActiveDid(newDid: string, oldDid: string | null): Promise<void> {
  try {
    logger.log(`[CapacitorPlatformService] Updating DailyNotification plugin activeDid from ${oldDid} to ${newDid}`);
    
    // Get new settings for the new activeDid
    const newSettings = await this.getTimeSafariSettings();
    
    // Reconfigure DailyNotification plugin with new activeDid
    await DailyNotification.configure({
      timesafariConfig: {
        activeDid: newDid,
        endpoints: {
          projectsLastUpdated: `${newSettings.apiServer}/api/v2/report/plansLastUpdatedBetween`
        },
        starredProjectsConfig: {
          enabled: true,
          starredPlanHandleIds: newSettings.starredPlanHandleIds || [],
          lastAckedJwtId: newSettings.lastAckedStarredPlanChangesJwtId || '',
          fetchInterval: '0 8 * * *'
        }
      }
    });
    
    // Update TimeSafari Integration Service
    if (this.integrationService) {
      await this.integrationService.initialize({
        activeDid: newDid,
        storageAdapter: this.getTimeSafariStorageAdapter(),
        endorserApiBaseUrl: newSettings.apiServer || 'https://endorser.ch'
      });
    }
    
  } catch (error) {
    logger.error(`[CapacitorPlatformService] Failed to update DailyNotification plugin activeDid:`, error);
  }
}

3. Change Listener System

// In CapacitorPlatformService.ts
private activeDidChangeListeners: Array<(newDid: string | null, oldDid: string | null) => void> = [];

addActiveDidChangeListener(listener: (newDid: string | null, oldDid: string | null) => void): void {
  this.activeDidChangeListeners.push(listener);
}

removeActiveDidChangeListener(listener: (newDid: string | null, oldDid: string | null) => void): void {
  const index = this.activeDidChangeListeners.indexOf(listener);
  if (index > -1) {
    this.activeDidChangeListeners.splice(index, 1);
  }
}

private notifyActiveDidChange(newDid: string | null, oldDid: string | null): void {
  this.activeDidChangeListeners.forEach(listener => {
    try {
      listener(newDid, oldDid);
    } catch (error) {
      logger.error('[CapacitorPlatformService] Error in activeDid change listener:', error);
    }
  });
}

Integration Flow

1. User Switches Identity

User clicks "Switch Account" → IdentitySwitcherView.switchAccount() → 
$updateActiveDid() → CapacitorPlatformService.updateActiveDid() → 
DailyNotification plugin reconfiguration

2. Plugin Reconfiguration Process

1. Update database (existing TimeSafari pattern)
2. Update local tracking
3. Notify change listeners
4. Reconfigure DailyNotification plugin
5. Update TimeSafari Integration Service
6. Log the change

3. Settings Update Process

1. Get new settings for new activeDid
2. Update plugin configuration with new settings
3. Update integration service with new activeDid
4. Clear any cached data for old activeDid
5. Initialize new activeDid-specific data

Required Changes to Existing Code

File: src/services/platforms/CapacitorPlatformService.ts

1. Add New Properties

export class CapacitorPlatformService implements PlatformService {
  // ... existing properties ...
  
  // ADD THESE NEW PROPERTIES
  private dailyNotificationService: DailyNotification | null = null;
  private integrationService: TimeSafariIntegrationService | null = null;
  private dailyNotificationInitialized = false;
  
  // ActiveDid change tracking
  private currentActiveDid: string | null = null;
  private activeDidChangeListeners: Array<(newDid: string | null, oldDid: string | null) => void> = [];
}

2. Modify Existing updateActiveDid Method

async updateActiveDid(did: string): Promise<void> {
  const oldDid = this.currentActiveDid;
  
  // Update the database (existing TimeSafari pattern)
  await this.dbExec(
    "UPDATE active_identity SET activeDid = ?, lastUpdated = datetime('now') WHERE id = 1",
    [did],
  );
  
  // Update local tracking
  this.currentActiveDid = did;
  
  // Notify listeners of the change
  this.notifyActiveDidChange(did, oldDid);
  
  // Update DailyNotification plugin if initialized
  if (this.dailyNotificationInitialized) {
    await this.updateDailyNotificationActiveDid(did, oldDid);
  }
  
  logger.debug(`[CapacitorPlatformService] ActiveDid updated from ${oldDid} to ${did}`);
}

3. Add New Methods

// Add these new methods to the class
private async updateDailyNotificationActiveDid(newDid: string, oldDid: string | null): Promise<void> { /* ... */ }
private async getCurrentActiveDid(): Promise<string | null> { /* ... */ }
addActiveDidChangeListener(listener: (newDid: string | null, oldDid: string | null) => void): void { /* ... */ }
removeActiveDidChangeListener(listener: (newDid: string | null, oldDid: string | null) => void): void { /* ... */ }
private notifyActiveDidChange(newDid: string | null, oldDid: string | null): void { /* ... */ }

PlatformServiceMixin Integration

File: src/utils/PlatformServiceMixin.ts

1. Add DailyNotification Methods

methods: {
  // ... existing methods ...
  
  /**
   * Initialize DailyNotification plugin (Capacitor only)
   */
  async $initializeDailyNotification(): Promise<void> {
    if (this.isCapacitor) {
      await this.platformService.initializeDailyNotification();
    }
  },

  /**
   * Enhanced loadNewStarredProjectChanges method
   */
  async $loadNewStarredProjectChanges(): Promise<StarredProjectsResponse> {
    if (this.isCapacitor) {
      return await this.platformService.loadNewStarredProjectChanges();
    } else {
      // Fall back to existing web method
      return await this.$loadNewStarredProjectChangesWeb();
    }
  }
}

Vue Component Integration

File: src/views/HomeView.vue (or similar)

1. Add DailyNotification Initialization

export default defineComponent({
  name: 'HomeView',
  
  mixins: [PlatformServiceMixin],
  
  async mounted() {
    // Initialize DailyNotification (only on Capacitor)
    if (this.isCapacitor) {
      await this.$initializeDailyNotification();
    }
  },

  methods: {
    /**
     * Enhanced loadNewStarredProjectChanges method
     */
    async loadNewStarredProjectChanges(): Promise<void> {
      if (this.isCapacitor) {
        // Use plugin-enhanced method on Capacitor
        const result = await this.$loadNewStarredProjectChanges();
        this.numNewStarredProjectChanges = result.data.length;
        this.newStarredProjectChangesHitLimit = result.hitLimit;
      } else {
        // Use existing web method in browser
        await this.loadNewStarredProjectChangesWeb();
      }
    }
  }
});

Key Benefits

1. Seamless ActiveDid Change Handling

  • Automatic Reconfiguration: Plugin automatically reconfigures when activeDid changes
  • Settings Synchronization: New activeDid settings are automatically loaded and applied
  • Error Handling: Robust error handling for activeDid change failures
  • Logging: Comprehensive logging for debugging activeDid changes

2. Integration with Existing Patterns

  • Uses Existing Methods: Leverages existing updateActiveDid method
  • Database Consistency: Uses existing active_identity table pattern
  • Change Detection: Integrates with existing change detection system
  • Settings Management: Uses existing settings retrieval patterns

3. Enhanced Functionality

  • Background Fetching: Continues to work with new activeDid
  • Notification Scheduling: Updates notifications for new activeDid
  • Storage Management: Manages storage for multiple activeDids
  • Observability: Tracks activeDid changes in logs and metrics

Testing Strategy

1. ActiveDid Change Testing

// Test activeDid change handling
const testActiveDidChange = async () => {
  // Switch to new activeDid
  await platformService.updateActiveDid('new-did-123');
  
  // Verify plugin is reconfigured
  const status = await platformService.getDailyNotificationStatus();
  assert.equal(status.currentActiveDid, 'new-did-123');
  
  // Test starred projects fetch with new activeDid
  const result = await platformService.loadNewStarredProjectChanges();
  assert.ok(result.data !== undefined);
};

2. Settings Synchronization Testing

// Test settings synchronization
const testSettingsSynchronization = async () => {
  // Switch activeDid
  await platformService.updateActiveDid('new-did-123');
  
  // Verify settings are loaded for new activeDid
  const settings = await platformService.getTimeSafariSettings();
  assert.equal(settings.activeDid, 'new-did-123');
  
  // Verify plugin configuration is updated
  const config = await DailyNotification.getConfiguration();
  assert.equal(config.timesafariConfig.activeDid, 'new-did-123');
};

3. Error Handling Testing

// Test error handling during activeDid changes
const testErrorHandling = async () => {
  try {
    // Switch to invalid activeDid
    await platformService.updateActiveDid('invalid-did');
    
    // Verify error is handled gracefully
    const status = await platformService.getDailyNotificationStatus();
    assert.ok(status.initialized); // Plugin should remain initialized
    
  } catch (error) {
    // Verify error is logged
    assert.ok(error.message.includes('activeDid'));
  }
};

Common Issues and Solutions

Issue 1: Plugin Not Updating on ActiveDid Change

Solution: Ensure updateDailyNotificationActiveDid is called in the updateActiveDid method

Issue 2: Settings Not Loading for New ActiveDid

Solution: Verify getTimeSafariSettings uses the current activeDid from database

Issue 3: Cached Data from Old ActiveDid

Solution: Clear plugin caches when activeDid changes

Issue 4: Background Fetching with Wrong ActiveDid

Solution: Ensure plugin reconfiguration includes new activeDid in all requests

Conclusion

The DailyNotification plugin integrates seamlessly with the existing TimeSafari PWA activeDid change system by:

  • Extending the existing updateActiveDid method to handle plugin reconfiguration
  • Using the existing change detection patterns from PlatformServiceMixin
  • Maintaining database consistency with the existing active_identity table
  • Providing robust error handling for activeDid change failures
  • Supporting multiple activeDids with proper isolation and cleanup

The integration ensures that the plugin automatically adapts to activeDid changes while maintaining the same interface and behavior as the existing TimeSafari PWA code.


Key Takeaway: The CapacitorPlatformService does know when activeDid changes through the existing updateActiveDid method, and the DailyNotification plugin integrates with this system to automatically reconfigure when the user switches identities.