docs: add TimeSafari PWA CapacitorPlatformService integration example
- Add comprehensive example showing DailyNotification plugin integration with existing TimeSafari PWA architecture - Show how to extend CapacitorPlatformService and PlatformServiceMixin patterns - Provide Vue.js component integration with existing TimeSafari patterns - Include settings management, database operations, and platform detection - Add migration strategy and testing approach for gradual adoption - Show how to maintain existing interfaces while adding plugin features This demonstrates how the plugin integrates with the actual TimeSafari PWA CapacitorPlatformService and PlatformServiceMixin architecture patterns.
This commit is contained in:
490
docs/timesafari-capacitor-integration-guide.md
Normal file
490
docs/timesafari-capacitor-integration-guide.md
Normal file
@@ -0,0 +1,490 @@
|
|||||||
|
# TimeSafari PWA - DailyNotification Plugin Integration Guide
|
||||||
|
|
||||||
|
**Author**: Matthew Raymer
|
||||||
|
**Version**: 1.0.0
|
||||||
|
**Created**: 2025-10-08 06:24:57 UTC
|
||||||
|
|
||||||
|
## Overview
|
||||||
|
|
||||||
|
This guide shows how to integrate the DailyNotification plugin with the existing TimeSafari PWA architecture, specifically the `CapacitorPlatformService` and `PlatformServiceMixin` patterns. The integration maintains the same interfaces and patterns while adding enhanced functionality.
|
||||||
|
|
||||||
|
## TimeSafari PWA Architecture Analysis
|
||||||
|
|
||||||
|
### Existing Architecture Components
|
||||||
|
|
||||||
|
#### 1. CapacitorPlatformService
|
||||||
|
- **Location**: `src/services/platforms/CapacitorPlatformService.ts`
|
||||||
|
- **Purpose**: Provides native mobile functionality through Capacitor plugins
|
||||||
|
- **Features**: File system, camera, SQLite database operations, platform detection
|
||||||
|
- **Key Methods**: `dbQuery`, `dbExec`, `takePicture`, `writeFile`, `getCapabilities`
|
||||||
|
|
||||||
|
#### 2. PlatformServiceFactory
|
||||||
|
- **Location**: `src/services/PlatformServiceFactory.ts`
|
||||||
|
- **Purpose**: Factory pattern for creating platform-specific service implementations
|
||||||
|
- **Pattern**: Singleton pattern with environment-based platform detection
|
||||||
|
- **Platforms**: `capacitor`, `electron`, `web` (default)
|
||||||
|
|
||||||
|
#### 3. PlatformServiceMixin
|
||||||
|
- **Location**: `src/utils/PlatformServiceMixin.ts`
|
||||||
|
- **Purpose**: Vue.js mixin providing cached platform service access and utility methods
|
||||||
|
- **Features**: Database operations, settings management, contact management, logging
|
||||||
|
- **Key Methods**: `$db`, `$exec`, `$settings`, `$contacts`, `$saveSettings`
|
||||||
|
|
||||||
|
### Existing TimeSafari Patterns
|
||||||
|
|
||||||
|
#### 1. Settings Management
|
||||||
|
```typescript
|
||||||
|
// Existing TimeSafari settings pattern
|
||||||
|
const settings = await this.$settings();
|
||||||
|
const activeDid = settings.activeDid;
|
||||||
|
const starredPlanHandleIds = settings.starredPlanHandleIds;
|
||||||
|
const apiServer = settings.apiServer;
|
||||||
|
```
|
||||||
|
|
||||||
|
#### 2. Database Operations
|
||||||
|
```typescript
|
||||||
|
// Existing TimeSafari database pattern
|
||||||
|
const result = await this.$dbQuery("SELECT * FROM settings WHERE id = 1");
|
||||||
|
const contacts = await this.$contacts();
|
||||||
|
await this.$saveSettings({ starredPlanHandleIds: newIds });
|
||||||
|
```
|
||||||
|
|
||||||
|
#### 3. Platform Detection
|
||||||
|
```typescript
|
||||||
|
// Existing TimeSafari platform detection
|
||||||
|
if (this.isCapacitor) {
|
||||||
|
// Native mobile functionality
|
||||||
|
} else {
|
||||||
|
// Web browser functionality
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## DailyNotification Plugin Integration
|
||||||
|
|
||||||
|
### Integration Strategy
|
||||||
|
|
||||||
|
The DailyNotification plugin integrates with the existing TimeSafari PWA architecture by:
|
||||||
|
|
||||||
|
1. **Extending CapacitorPlatformService**: Adding DailyNotification functionality to the existing platform service
|
||||||
|
2. **Using PlatformServiceMixin**: Leveraging existing database and settings patterns
|
||||||
|
3. **Maintaining Interfaces**: Keeping the same method signatures and behavior
|
||||||
|
4. **Platform Detection**: Only initializing on Capacitor platforms
|
||||||
|
|
||||||
|
### Enhanced CapacitorPlatformService
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
export class EnhancedCapacitorPlatformService {
|
||||||
|
private platformService = PlatformServiceFactory.getInstance();
|
||||||
|
private dailyNotificationService: DailyNotification | null = null;
|
||||||
|
private integrationService: TimeSafariIntegrationService | null = null;
|
||||||
|
|
||||||
|
async initializeDailyNotification(): Promise<void> {
|
||||||
|
// Get existing TimeSafari settings
|
||||||
|
const settings = await this.getTimeSafariSettings();
|
||||||
|
|
||||||
|
// Configure plugin with TimeSafari data
|
||||||
|
await DailyNotification.configure({
|
||||||
|
timesafariConfig: {
|
||||||
|
activeDid: settings.activeDid,
|
||||||
|
endpoints: {
|
||||||
|
projectsLastUpdated: `${settings.apiServer}/api/v2/report/plansLastUpdatedBetween`
|
||||||
|
},
|
||||||
|
starredProjectsConfig: {
|
||||||
|
enabled: true,
|
||||||
|
starredPlanHandleIds: settings.starredPlanHandleIds,
|
||||||
|
lastAckedJwtId: settings.lastAckedStarredPlanChangesJwtId,
|
||||||
|
fetchInterval: '0 8 * * *'
|
||||||
|
}
|
||||||
|
},
|
||||||
|
networkConfig: {
|
||||||
|
baseURL: settings.apiServer,
|
||||||
|
timeout: 30000
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
async loadNewStarredProjectChanges(): Promise<StarredProjectsResponse> {
|
||||||
|
// Enhanced version of existing TimeSafari method
|
||||||
|
const settings = await this.getTimeSafariSettings();
|
||||||
|
|
||||||
|
if (!settings.activeDid || !settings.starredPlanHandleIds?.length) {
|
||||||
|
return { data: [], hitLimit: false };
|
||||||
|
}
|
||||||
|
|
||||||
|
// Use plugin's enhanced fetching
|
||||||
|
return await this.integrationService!.getStarredProjectsWithChanges(
|
||||||
|
settings.activeDid,
|
||||||
|
settings.starredPlanHandleIds,
|
||||||
|
settings.lastAckedStarredPlanChangesJwtId
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### Vue.js Component Integration
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
export const TimeSafariDailyNotificationMixin = {
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
// Existing TimeSafari data
|
||||||
|
activeDid: '',
|
||||||
|
starredPlanHandleIds: [] as string[],
|
||||||
|
lastAckedStarredPlanChangesJwtId: '',
|
||||||
|
numNewStarredProjectChanges: 0,
|
||||||
|
newStarredProjectChangesHitLimit: false,
|
||||||
|
|
||||||
|
// Plugin integration
|
||||||
|
enhancedPlatformService: null as EnhancedCapacitorPlatformService | null
|
||||||
|
};
|
||||||
|
},
|
||||||
|
|
||||||
|
async mounted() {
|
||||||
|
// Initialize DailyNotification when component mounts (only on Capacitor)
|
||||||
|
if (this.isCapacitor) {
|
||||||
|
await this.initializeDailyNotification();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
methods: {
|
||||||
|
async initializeDailyNotification(): Promise<void> {
|
||||||
|
this.enhancedPlatformService = new EnhancedCapacitorPlatformService();
|
||||||
|
await this.enhancedPlatformService.initializeDailyNotification();
|
||||||
|
},
|
||||||
|
|
||||||
|
async loadNewStarredProjectChanges(): Promise<void> {
|
||||||
|
if (this.isCapacitor && this.enhancedPlatformService) {
|
||||||
|
// Use plugin-enhanced method on Capacitor
|
||||||
|
const result = await this.enhancedPlatformService.loadNewStarredProjectChanges();
|
||||||
|
this.numNewStarredProjectChanges = result.data.length;
|
||||||
|
this.newStarredProjectChangesHitLimit = result.hitLimit;
|
||||||
|
} else {
|
||||||
|
// Use existing web method in browser
|
||||||
|
await this.loadNewStarredProjectChangesWeb();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
```
|
||||||
|
|
||||||
|
## Integration Steps
|
||||||
|
|
||||||
|
### Step 1: Install the Plugin
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# In TimeSafari PWA project
|
||||||
|
npm install ssh://git@173.199.124.46:222/trent_larson/daily-notification-plugin.git
|
||||||
|
```
|
||||||
|
|
||||||
|
### Step 2: Create Enhanced Platform Service
|
||||||
|
|
||||||
|
Create `src/services/platforms/EnhancedCapacitorPlatformService.ts`:
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
import { DailyNotification } from '@timesafari/daily-notification-plugin';
|
||||||
|
import { TimeSafariIntegrationService } from '@timesafari/daily-notification-plugin';
|
||||||
|
import { PlatformServiceFactory } from '@/services/PlatformServiceFactory';
|
||||||
|
|
||||||
|
export class EnhancedCapacitorPlatformService {
|
||||||
|
// Implementation from the example above
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### Step 3: Extend PlatformServiceMixin
|
||||||
|
|
||||||
|
Add to `src/utils/PlatformServiceMixin.ts`:
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
export const PlatformServiceMixin = {
|
||||||
|
// ... existing mixin code ...
|
||||||
|
|
||||||
|
methods: {
|
||||||
|
// ... existing methods ...
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Initialize DailyNotification plugin (Capacitor only)
|
||||||
|
*/
|
||||||
|
async $initializeDailyNotification(): Promise<void> {
|
||||||
|
if (this.isCapacitor) {
|
||||||
|
const enhancedService = new EnhancedCapacitorPlatformService();
|
||||||
|
await enhancedService.initializeDailyNotification();
|
||||||
|
this._enhancedPlatformService = enhancedService;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Enhanced loadNewStarredProjectChanges method
|
||||||
|
*/
|
||||||
|
async $loadNewStarredProjectChanges(): Promise<StarredProjectsResponse> {
|
||||||
|
if (this.isCapacitor && this._enhancedPlatformService) {
|
||||||
|
return await this._enhancedPlatformService.loadNewStarredProjectChanges();
|
||||||
|
} else {
|
||||||
|
// Fall back to existing web method
|
||||||
|
return await this.$loadNewStarredProjectChangesWeb();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
```
|
||||||
|
|
||||||
|
### Step 4: Update Vue Components
|
||||||
|
|
||||||
|
In your existing TimeSafari Vue components:
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
export default defineComponent({
|
||||||
|
name: 'TimeSafariHomeView',
|
||||||
|
|
||||||
|
mixins: [PlatformServiceMixin],
|
||||||
|
|
||||||
|
async mounted() {
|
||||||
|
// Initialize DailyNotification (only on Capacitor)
|
||||||
|
if (this.isCapacitor) {
|
||||||
|
await this.$initializeDailyNotification();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
methods: {
|
||||||
|
async loadNewStarredProjectChanges() {
|
||||||
|
// Use enhanced method
|
||||||
|
const result = await this.$loadNewStarredProjectChanges();
|
||||||
|
this.numNewStarredProjectChanges = result.data.length;
|
||||||
|
this.newStarredProjectChangesHitLimit = result.hitLimit;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
```
|
||||||
|
|
||||||
|
## Configuration Mapping
|
||||||
|
|
||||||
|
### TimeSafari Settings → Plugin Configuration
|
||||||
|
|
||||||
|
| TimeSafari Setting | Plugin Configuration | Purpose |
|
||||||
|
|-------------------|---------------------|---------|
|
||||||
|
| `settings.activeDid` | `timesafariConfig.activeDid` | User authentication |
|
||||||
|
| `settings.apiServer` | `networkConfig.baseURL` | API endpoint |
|
||||||
|
| `settings.starredPlanHandleIds` | `starredProjectsConfig.starredPlanHandleIds` | Project IDs to fetch |
|
||||||
|
| `settings.lastAckedStarredPlanChangesJwtId` | `starredProjectsConfig.lastAckedJwtId` | Pagination token |
|
||||||
|
|
||||||
|
### Existing Methods → Enhanced Methods
|
||||||
|
|
||||||
|
| Existing Method | Enhanced Method | Enhancement |
|
||||||
|
|----------------|----------------|-------------|
|
||||||
|
| `loadNewStarredProjectChanges()` | `$loadNewStarredProjectChanges()` | Background fetching, structured logging |
|
||||||
|
| `getStarredProjectsWithChanges()` | `integrationService.getStarredProjectsWithChanges()` | Retry logic, circuit breaker |
|
||||||
|
| Settings storage | Plugin storage adapter | Tiered storage, TTL management |
|
||||||
|
|
||||||
|
## Platform-Specific Behavior
|
||||||
|
|
||||||
|
### Capacitor (Android/iOS)
|
||||||
|
- **Plugin Enabled**: Full DailyNotification functionality
|
||||||
|
- **Background Fetching**: WorkManager (Android), BGTaskScheduler (iOS)
|
||||||
|
- **Notifications**: Native notification channels and categories
|
||||||
|
- **Storage**: SQLite with plugin's tiered storage system
|
||||||
|
|
||||||
|
### Web Browser
|
||||||
|
- **Plugin Disabled**: Uses existing TimeSafari web code
|
||||||
|
- **No Background Fetching**: Limited to browser capabilities
|
||||||
|
- **No Native Notifications**: Uses web notifications API
|
||||||
|
- **Storage**: Existing TimeSafari storage patterns
|
||||||
|
|
||||||
|
### Electron
|
||||||
|
- **Plugin Enabled**: Desktop-specific functionality
|
||||||
|
- **Background Tasks**: Electron main process
|
||||||
|
- **Desktop Notifications**: Native desktop notifications
|
||||||
|
- **Storage**: SQLite with plugin's tiered storage system
|
||||||
|
|
||||||
|
## Benefits of Integration
|
||||||
|
|
||||||
|
### 1. Same Interface, Enhanced Functionality
|
||||||
|
- Existing `loadNewStarredProjectChanges()` method works exactly the same
|
||||||
|
- Enhanced with background fetching, structured logging, and error handling
|
||||||
|
- No changes required to existing UI code
|
||||||
|
|
||||||
|
### 2. Leverages Existing TimeSafari Patterns
|
||||||
|
- Uses existing `PlatformServiceMixin` for database operations
|
||||||
|
- Integrates with existing settings management
|
||||||
|
- Maintains existing platform detection patterns
|
||||||
|
|
||||||
|
### 3. Platform-Optimized Performance
|
||||||
|
- **Capacitor**: Native mobile optimizations with WorkManager/BGTaskScheduler
|
||||||
|
- **Web**: Falls back to existing web code seamlessly
|
||||||
|
- **Electron**: Desktop-specific optimizations
|
||||||
|
|
||||||
|
### 4. Enhanced Observability
|
||||||
|
- Structured logging with event IDs
|
||||||
|
- Performance metrics and monitoring
|
||||||
|
- Error tracking and analysis
|
||||||
|
- Health checks and status monitoring
|
||||||
|
|
||||||
|
## Migration Strategy
|
||||||
|
|
||||||
|
### Phase 1: Parallel Implementation
|
||||||
|
1. **Keep existing code unchanged**
|
||||||
|
2. **Add enhanced platform service alongside existing code**
|
||||||
|
3. **Test both implementations in parallel**
|
||||||
|
4. **Compare results to ensure compatibility**
|
||||||
|
|
||||||
|
### Phase 2: Gradual Migration
|
||||||
|
1. **Replace individual methods one by one**
|
||||||
|
2. **Use enhanced error handling and logging**
|
||||||
|
3. **Maintain existing UI and user experience**
|
||||||
|
4. **Add plugin-specific features gradually**
|
||||||
|
|
||||||
|
### Phase 3: Full Integration
|
||||||
|
1. **Replace all TimeSafari request patterns with plugin**
|
||||||
|
2. **Remove duplicate code**
|
||||||
|
3. **Leverage plugin's advanced features**
|
||||||
|
4. **Optimize performance with plugin's caching and batching**
|
||||||
|
|
||||||
|
## Testing Strategy
|
||||||
|
|
||||||
|
### 1. Platform-Specific Testing
|
||||||
|
```typescript
|
||||||
|
// Test on different platforms
|
||||||
|
const testPlatforms = async () => {
|
||||||
|
if (Capacitor.getPlatform() === 'android') {
|
||||||
|
// Test Android-specific functionality
|
||||||
|
} else if (Capacitor.getPlatform() === 'ios') {
|
||||||
|
// Test iOS-specific functionality
|
||||||
|
} else {
|
||||||
|
// Test web fallback
|
||||||
|
}
|
||||||
|
};
|
||||||
|
```
|
||||||
|
|
||||||
|
### 2. Parallel Testing
|
||||||
|
```typescript
|
||||||
|
// Test both implementations
|
||||||
|
const testBothImplementations = async () => {
|
||||||
|
// Existing TimeSafari implementation
|
||||||
|
const existingResult = await this.loadNewStarredProjectChangesOriginal();
|
||||||
|
|
||||||
|
// Plugin-enhanced implementation
|
||||||
|
const pluginResult = await this.$loadNewStarredProjectChanges();
|
||||||
|
|
||||||
|
// Compare results
|
||||||
|
assert.deepEqual(existingResult, pluginResult);
|
||||||
|
};
|
||||||
|
```
|
||||||
|
|
||||||
|
### 3. Performance Testing
|
||||||
|
```typescript
|
||||||
|
// Compare performance
|
||||||
|
const performanceTest = async () => {
|
||||||
|
const start = Date.now();
|
||||||
|
await this.$loadNewStarredProjectChanges();
|
||||||
|
const pluginTime = Date.now() - start;
|
||||||
|
|
||||||
|
console.log('Plugin performance:', pluginTime);
|
||||||
|
};
|
||||||
|
```
|
||||||
|
|
||||||
|
## Common Integration Patterns
|
||||||
|
|
||||||
|
### 1. Settings Integration
|
||||||
|
```typescript
|
||||||
|
// Get TimeSafari settings
|
||||||
|
const settings = await this.$settings();
|
||||||
|
|
||||||
|
// Configure plugin with settings
|
||||||
|
await DailyNotification.configure({
|
||||||
|
timesafariConfig: {
|
||||||
|
activeDid: settings.activeDid,
|
||||||
|
starredPlanHandleIds: settings.starredPlanHandleIds
|
||||||
|
}
|
||||||
|
});
|
||||||
|
```
|
||||||
|
|
||||||
|
### 2. Database Integration
|
||||||
|
```typescript
|
||||||
|
// Store plugin results in TimeSafari database
|
||||||
|
await this.$dbExec(
|
||||||
|
"INSERT OR REPLACE INTO temp (id, data) VALUES (?, ?)",
|
||||||
|
['starred_projects_latest', JSON.stringify(result)]
|
||||||
|
);
|
||||||
|
```
|
||||||
|
|
||||||
|
### 3. Error Handling Integration
|
||||||
|
```typescript
|
||||||
|
// Use existing TimeSafari error handling patterns
|
||||||
|
try {
|
||||||
|
const result = await this.$loadNewStarredProjectChanges();
|
||||||
|
// Handle success
|
||||||
|
} catch (error) {
|
||||||
|
// Use existing TimeSafari error logging
|
||||||
|
await this.$logError(`Failed to load starred projects: ${error.message}`);
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## Troubleshooting
|
||||||
|
|
||||||
|
### Common Issues
|
||||||
|
|
||||||
|
#### 1. Plugin Not Initializing
|
||||||
|
```typescript
|
||||||
|
// Check platform detection
|
||||||
|
if (!this.isCapacitor) {
|
||||||
|
console.log('Plugin only works on Capacitor platforms');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
#### 2. Settings Not Found
|
||||||
|
```typescript
|
||||||
|
// Ensure settings are loaded
|
||||||
|
const settings = await this.$settings();
|
||||||
|
if (!settings.activeDid) {
|
||||||
|
console.log('No active DID found');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
#### 3. Database Connection Issues
|
||||||
|
```typescript
|
||||||
|
// Check database connection
|
||||||
|
try {
|
||||||
|
await this.$dbQuery("SELECT 1");
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Database connection failed:', error);
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### Debug Methods
|
||||||
|
|
||||||
|
#### 1. Plugin Status
|
||||||
|
```typescript
|
||||||
|
const status = await this.getDailyNotificationStatus();
|
||||||
|
console.log('Plugin status:', status);
|
||||||
|
```
|
||||||
|
|
||||||
|
#### 2. Settings Debug
|
||||||
|
```typescript
|
||||||
|
await this.$debugMergedSettings(this.activeDid);
|
||||||
|
```
|
||||||
|
|
||||||
|
#### 3. Performance Debug
|
||||||
|
```typescript
|
||||||
|
const metrics = await DailyNotification.getMetrics();
|
||||||
|
console.log('Plugin metrics:', metrics);
|
||||||
|
```
|
||||||
|
|
||||||
|
## Conclusion
|
||||||
|
|
||||||
|
The DailyNotification plugin integrates seamlessly with the existing TimeSafari PWA architecture by:
|
||||||
|
|
||||||
|
- **Extending CapacitorPlatformService** with enhanced functionality
|
||||||
|
- **Using PlatformServiceMixin** for database and settings operations
|
||||||
|
- **Maintaining existing interfaces** and method signatures
|
||||||
|
- **Providing platform-specific optimizations** for Capacitor, Web, and Electron
|
||||||
|
- **Enhancing observability** with structured logging and metrics
|
||||||
|
|
||||||
|
The integration maintains the same developer experience while adding powerful new features like background fetching, native notifications, and comprehensive monitoring.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
**Next Steps:**
|
||||||
|
1. Review the integration example and understand the architecture
|
||||||
|
2. Test the enhanced platform service with existing TimeSafari code
|
||||||
|
3. Gradually migrate individual methods to use the plugin
|
||||||
|
4. Leverage the plugin's advanced features for enhanced user experience
|
||||||
594
examples/timesafari-capacitor-integration-example.ts
Normal file
594
examples/timesafari-capacitor-integration-example.ts
Normal file
@@ -0,0 +1,594 @@
|
|||||||
|
/**
|
||||||
|
* TimeSafari PWA - DailyNotification Plugin Integration Example
|
||||||
|
*
|
||||||
|
* This example shows how to integrate the DailyNotification plugin with the existing
|
||||||
|
* TimeSafari PWA architecture, specifically the CapacitorPlatformService and
|
||||||
|
* PlatformServiceMixin patterns.
|
||||||
|
*
|
||||||
|
* @author Matthew Raymer
|
||||||
|
* @version 1.0.0
|
||||||
|
*/
|
||||||
|
|
||||||
|
import { DailyNotification } from '@timesafari/daily-notification-plugin';
|
||||||
|
import { TimeSafariIntegrationService } from '@timesafari/daily-notification-plugin';
|
||||||
|
import { PlatformServiceFactory } from '@/services/PlatformServiceFactory';
|
||||||
|
import { Capacitor } from '@capacitor/core';
|
||||||
|
|
||||||
|
// TimeSafari PWA existing interfaces (from the actual project)
|
||||||
|
interface PlanSummaryAndPreviousClaim {
|
||||||
|
id: string;
|
||||||
|
title: string;
|
||||||
|
description: string;
|
||||||
|
lastUpdated: string;
|
||||||
|
previousClaim?: unknown;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface StarredProjectsResponse {
|
||||||
|
data: Array<PlanSummaryAndPreviousClaim>;
|
||||||
|
hitLimit: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface Settings {
|
||||||
|
accountDid?: string;
|
||||||
|
activeDid?: string;
|
||||||
|
apiServer?: string;
|
||||||
|
starredPlanHandleIds?: string[];
|
||||||
|
lastAckedStarredPlanChangesJwtId?: string;
|
||||||
|
[key: string]: unknown;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Enhanced CapacitorPlatformService with DailyNotification integration
|
||||||
|
*
|
||||||
|
* This extends the existing TimeSafari PWA CapacitorPlatformService to include
|
||||||
|
* DailyNotification plugin functionality while maintaining the same interface.
|
||||||
|
*/
|
||||||
|
export class EnhancedCapacitorPlatformService {
|
||||||
|
private platformService = PlatformServiceFactory.getInstance();
|
||||||
|
private dailyNotificationService: DailyNotification | null = null;
|
||||||
|
private integrationService: TimeSafariIntegrationService | null = null;
|
||||||
|
private initialized = false;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Initialize DailyNotification plugin with TimeSafari PWA configuration
|
||||||
|
*/
|
||||||
|
async initializeDailyNotification(): Promise<void> {
|
||||||
|
if (this.initialized) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
// Get current TimeSafari settings
|
||||||
|
const settings = await this.getTimeSafariSettings();
|
||||||
|
|
||||||
|
// Configure DailyNotification plugin with TimeSafari data
|
||||||
|
await DailyNotification.configure({
|
||||||
|
// Basic plugin configuration
|
||||||
|
storage: 'tiered',
|
||||||
|
ttlSeconds: 1800,
|
||||||
|
enableETagSupport: true,
|
||||||
|
enableErrorHandling: true,
|
||||||
|
enablePerformanceOptimization: true,
|
||||||
|
|
||||||
|
// TimeSafari-specific configuration
|
||||||
|
timesafariConfig: {
|
||||||
|
// Use existing TimeSafari activeDid
|
||||||
|
activeDid: settings.activeDid || '',
|
||||||
|
|
||||||
|
// Use existing TimeSafari API endpoints
|
||||||
|
endpoints: {
|
||||||
|
offersToPerson: `${settings.apiServer}/api/v2/offers/person`,
|
||||||
|
offersToPlans: `${settings.apiServer}/api/v2/offers/plans`,
|
||||||
|
projectsLastUpdated: `${settings.apiServer}/api/v2/report/plansLastUpdatedBetween`
|
||||||
|
},
|
||||||
|
|
||||||
|
// Configure starred projects fetching (matches existing TimeSafari pattern)
|
||||||
|
starredProjectsConfig: {
|
||||||
|
enabled: true,
|
||||||
|
starredPlanHandleIds: settings.starredPlanHandleIds || [],
|
||||||
|
lastAckedJwtId: settings.lastAckedStarredPlanChangesJwtId || '',
|
||||||
|
fetchInterval: '0 8 * * *', // Daily at 8 AM
|
||||||
|
maxResults: 50,
|
||||||
|
hitLimitHandling: 'warn' // Same as existing TimeSafari error handling
|
||||||
|
},
|
||||||
|
|
||||||
|
// Sync configuration (optimized for TimeSafari use case)
|
||||||
|
syncConfig: {
|
||||||
|
enableParallel: true,
|
||||||
|
maxConcurrent: 3,
|
||||||
|
batchSize: 10,
|
||||||
|
timeout: 30000,
|
||||||
|
retryAttempts: 3
|
||||||
|
},
|
||||||
|
|
||||||
|
// Error policy (matches existing TimeSafari error handling)
|
||||||
|
errorPolicy: {
|
||||||
|
maxRetries: 3,
|
||||||
|
backoffMultiplier: 2,
|
||||||
|
activeDidChangeRetries: 5,
|
||||||
|
starredProjectsRetries: 3
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
// Network configuration using existing TimeSafari patterns
|
||||||
|
networkConfig: {
|
||||||
|
// Use existing TimeSafari HTTP client (if available)
|
||||||
|
baseURL: settings.apiServer || 'https://endorser.ch',
|
||||||
|
timeout: 30000,
|
||||||
|
retryAttempts: 3,
|
||||||
|
retryDelay: 1000,
|
||||||
|
maxConcurrent: 5,
|
||||||
|
|
||||||
|
// Headers matching TimeSafari pattern
|
||||||
|
defaultHeaders: {
|
||||||
|
'Content-Type': 'application/json',
|
||||||
|
'Accept': 'application/json',
|
||||||
|
'User-Agent': 'TimeSafari-PWA/1.0.0'
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
// Content fetch configuration (replaces existing loadNewStarredProjectChanges)
|
||||||
|
contentFetch: {
|
||||||
|
enabled: true,
|
||||||
|
schedule: '0 8 * * *', // Daily at 8 AM
|
||||||
|
|
||||||
|
// Use existing TimeSafari request pattern
|
||||||
|
requestConfig: {
|
||||||
|
method: 'POST',
|
||||||
|
url: `${settings.apiServer}/api/v2/report/plansLastUpdatedBetween`,
|
||||||
|
headers: {
|
||||||
|
'Authorization': 'Bearer ${jwt}',
|
||||||
|
'X-User-DID': '${activeDid}',
|
||||||
|
'Content-Type': 'application/json'
|
||||||
|
},
|
||||||
|
body: {
|
||||||
|
planIds: '${starredPlanHandleIds}',
|
||||||
|
afterId: '${lastAckedJwtId}'
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
// Callbacks that match TimeSafari error handling
|
||||||
|
callbacks: {
|
||||||
|
onSuccess: this.handleStarredProjectsSuccess.bind(this),
|
||||||
|
onError: this.handleStarredProjectsError.bind(this),
|
||||||
|
onComplete: this.handleStarredProjectsComplete.bind(this)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
// Authentication configuration
|
||||||
|
authentication: {
|
||||||
|
jwt: {
|
||||||
|
secret: process.env.JWT_SECRET || 'timesafari-jwt-secret',
|
||||||
|
algorithm: 'HS256',
|
||||||
|
expirationMinutes: 60,
|
||||||
|
refreshThresholdMinutes: 10
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
// Observability configuration
|
||||||
|
logging: {
|
||||||
|
level: 'INFO',
|
||||||
|
enableRequestLogging: true,
|
||||||
|
enableResponseLogging: true,
|
||||||
|
enableErrorLogging: true,
|
||||||
|
redactSensitiveData: true
|
||||||
|
},
|
||||||
|
|
||||||
|
// Security configuration
|
||||||
|
security: {
|
||||||
|
certificatePinning: {
|
||||||
|
enabled: true,
|
||||||
|
pins: [
|
||||||
|
{
|
||||||
|
hostname: 'endorser.ch',
|
||||||
|
pins: ['sha256/YOUR_PIN_HERE']
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// Initialize TimeSafari Integration Service
|
||||||
|
this.integrationService = TimeSafariIntegrationService.getInstance();
|
||||||
|
await this.integrationService.initialize({
|
||||||
|
activeDid: settings.activeDid || '',
|
||||||
|
storageAdapter: this.getTimeSafariStorageAdapter(),
|
||||||
|
endorserApiBaseUrl: settings.apiServer || 'https://endorser.ch',
|
||||||
|
|
||||||
|
// Use existing TimeSafari request patterns
|
||||||
|
requestConfig: {
|
||||||
|
baseURL: settings.apiServer || 'https://endorser.ch',
|
||||||
|
timeout: 30000,
|
||||||
|
retryAttempts: 3
|
||||||
|
},
|
||||||
|
|
||||||
|
// Configure starred projects fetching
|
||||||
|
starredProjectsConfig: {
|
||||||
|
enabled: true,
|
||||||
|
starredPlanHandleIds: settings.starredPlanHandleIds || [],
|
||||||
|
lastAckedJwtId: settings.lastAckedStarredPlanChangesJwtId || '',
|
||||||
|
fetchInterval: '0 8 * * *',
|
||||||
|
maxResults: 50
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// Schedule daily notifications
|
||||||
|
await DailyNotification.scheduleDailyNotification({
|
||||||
|
title: 'TimeSafari Community Update',
|
||||||
|
body: 'You have new offers and project updates',
|
||||||
|
time: '09:00',
|
||||||
|
channel: 'timesafari_community_updates'
|
||||||
|
});
|
||||||
|
|
||||||
|
this.initialized = true;
|
||||||
|
console.log('DailyNotification plugin initialized successfully with TimeSafari PWA');
|
||||||
|
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Failed to initialize DailyNotification plugin:', error);
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Enhanced version of existing TimeSafari loadNewStarredProjectChanges method
|
||||||
|
*
|
||||||
|
* This replaces the existing method with plugin-enhanced functionality
|
||||||
|
* while maintaining the same interface and behavior.
|
||||||
|
*/
|
||||||
|
async loadNewStarredProjectChanges(): Promise<StarredProjectsResponse> {
|
||||||
|
if (!this.initialized) {
|
||||||
|
await this.initializeDailyNotification();
|
||||||
|
}
|
||||||
|
|
||||||
|
const settings = await this.getTimeSafariSettings();
|
||||||
|
|
||||||
|
if (!settings.activeDid || !settings.starredPlanHandleIds?.length) {
|
||||||
|
return { data: [], hitLimit: false };
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
// Use plugin's enhanced fetching with same interface as existing TimeSafari code
|
||||||
|
const starredProjectChanges = await this.integrationService!.getStarredProjectsWithChanges(
|
||||||
|
settings.activeDid,
|
||||||
|
settings.starredPlanHandleIds,
|
||||||
|
settings.lastAckedStarredPlanChangesJwtId
|
||||||
|
);
|
||||||
|
|
||||||
|
// Enhanced logging (optional)
|
||||||
|
console.log('Starred projects loaded successfully:', {
|
||||||
|
count: starredProjectChanges.data.length,
|
||||||
|
hitLimit: starredProjectChanges.hitLimit,
|
||||||
|
planIds: settings.starredPlanHandleIds.length
|
||||||
|
});
|
||||||
|
|
||||||
|
return starredProjectChanges;
|
||||||
|
|
||||||
|
} catch (error) {
|
||||||
|
// Same error handling as existing TimeSafari code
|
||||||
|
console.warn('[TimeSafari] Failed to load starred project changes:', error);
|
||||||
|
return { data: [], hitLimit: false };
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get TimeSafari settings using existing PlatformServiceMixin pattern
|
||||||
|
*/
|
||||||
|
private async getTimeSafariSettings(): Promise<Settings> {
|
||||||
|
try {
|
||||||
|
// Use existing TimeSafari settings retrieval pattern
|
||||||
|
const result = await this.platformService.dbQuery(
|
||||||
|
"SELECT * FROM settings WHERE id = 1"
|
||||||
|
);
|
||||||
|
|
||||||
|
if (!result?.values?.length) {
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
// Map database columns to values (existing TimeSafari pattern)
|
||||||
|
const settings: Settings = {};
|
||||||
|
result.columns.forEach((column, index) => {
|
||||||
|
if (column !== 'id') {
|
||||||
|
settings[column] = result.values[0][index];
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// Handle JSON field parsing (existing TimeSafari pattern)
|
||||||
|
if (settings.starredPlanHandleIds && typeof settings.starredPlanHandleIds === 'string') {
|
||||||
|
try {
|
||||||
|
settings.starredPlanHandleIds = JSON.parse(settings.starredPlanHandleIds);
|
||||||
|
} catch {
|
||||||
|
settings.starredPlanHandleIds = [];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return settings;
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Error getting TimeSafari settings:', error);
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get TimeSafari storage adapter using existing patterns
|
||||||
|
*/
|
||||||
|
private getTimeSafariStorageAdapter(): unknown {
|
||||||
|
// Return existing TimeSafari storage adapter
|
||||||
|
return {
|
||||||
|
// Use existing TimeSafari storage patterns
|
||||||
|
store: async (key: string, value: unknown) => {
|
||||||
|
await this.platformService.dbExec(
|
||||||
|
"INSERT OR REPLACE INTO temp (id, data) VALUES (?, ?)",
|
||||||
|
[key, JSON.stringify(value)]
|
||||||
|
);
|
||||||
|
},
|
||||||
|
|
||||||
|
retrieve: async (key: string) => {
|
||||||
|
const result = await this.platformService.dbQuery(
|
||||||
|
"SELECT data FROM temp WHERE id = ?",
|
||||||
|
[key]
|
||||||
|
);
|
||||||
|
|
||||||
|
if (result?.values?.length) {
|
||||||
|
try {
|
||||||
|
return JSON.parse(result.values[0][0] as string);
|
||||||
|
} catch {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Callback handlers that match existing TimeSafari error handling patterns
|
||||||
|
*/
|
||||||
|
private async handleStarredProjectsSuccess(data: StarredProjectsResponse): Promise<void> {
|
||||||
|
// Enhanced logging (optional)
|
||||||
|
console.log('Starred projects success callback:', {
|
||||||
|
count: data.data.length,
|
||||||
|
hitLimit: data.hitLimit
|
||||||
|
});
|
||||||
|
|
||||||
|
// Store results in TimeSafari temp table for UI access
|
||||||
|
await this.platformService.dbExec(
|
||||||
|
"INSERT OR REPLACE INTO temp (id, data) VALUES (?, ?)",
|
||||||
|
['starred_projects_latest', JSON.stringify(data)]
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
private async handleStarredProjectsError(error: Error): Promise<void> {
|
||||||
|
// Same error handling as existing TimeSafari code
|
||||||
|
console.warn('[TimeSafari] Failed to load starred project changes:', error);
|
||||||
|
|
||||||
|
// Store error in TimeSafari temp table for UI access
|
||||||
|
await this.platformService.dbExec(
|
||||||
|
"INSERT OR REPLACE INTO temp (id, data) VALUES (?, ?)",
|
||||||
|
['starred_projects_error', JSON.stringify({ error: error.message, timestamp: Date.now() })]
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
private async handleStarredProjectsComplete(result: unknown): Promise<void> {
|
||||||
|
// Handle completion
|
||||||
|
console.log('Starred projects fetch completed:', result);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get plugin status for debugging
|
||||||
|
*/
|
||||||
|
async getDailyNotificationStatus(): Promise<{
|
||||||
|
initialized: boolean;
|
||||||
|
platform: string;
|
||||||
|
capabilities: unknown;
|
||||||
|
}> {
|
||||||
|
return {
|
||||||
|
initialized: this.initialized,
|
||||||
|
platform: Capacitor.getPlatform(),
|
||||||
|
capabilities: this.platformService.getCapabilities()
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Vue.js Mixin for DailyNotification integration with TimeSafari PWA
|
||||||
|
*
|
||||||
|
* This mixin extends the existing PlatformServiceMixin to include
|
||||||
|
* DailyNotification functionality while maintaining the same patterns.
|
||||||
|
*/
|
||||||
|
export const TimeSafariDailyNotificationMixin = {
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
// Existing TimeSafari data
|
||||||
|
activeDid: '',
|
||||||
|
starredPlanHandleIds: [] as string[],
|
||||||
|
lastAckedStarredPlanChangesJwtId: '',
|
||||||
|
numNewStarredProjectChanges: 0,
|
||||||
|
newStarredProjectChangesHitLimit: false,
|
||||||
|
|
||||||
|
// Plugin integration
|
||||||
|
dailyNotificationService: null as DailyNotification | null,
|
||||||
|
integrationService: null as TimeSafariIntegrationService | null,
|
||||||
|
enhancedPlatformService: null as EnhancedCapacitorPlatformService | null
|
||||||
|
};
|
||||||
|
},
|
||||||
|
|
||||||
|
computed: {
|
||||||
|
// Existing TimeSafari computed properties
|
||||||
|
isCapacitor(): boolean {
|
||||||
|
return this.platformService.isCapacitor();
|
||||||
|
},
|
||||||
|
|
||||||
|
isWeb(): boolean {
|
||||||
|
return this.platformService.isWeb();
|
||||||
|
},
|
||||||
|
|
||||||
|
isElectron(): boolean {
|
||||||
|
return this.platformService.isElectron();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
async mounted() {
|
||||||
|
// Initialize DailyNotification when component mounts (only on Capacitor)
|
||||||
|
if (this.isCapacitor) {
|
||||||
|
await this.initializeDailyNotification();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
methods: {
|
||||||
|
/**
|
||||||
|
* Initialize DailyNotification plugin with TimeSafari configuration
|
||||||
|
*/
|
||||||
|
async initializeDailyNotification(): Promise<void> {
|
||||||
|
try {
|
||||||
|
// Create enhanced platform service
|
||||||
|
this.enhancedPlatformService = new EnhancedCapacitorPlatformService();
|
||||||
|
|
||||||
|
// Initialize the plugin
|
||||||
|
await this.enhancedPlatformService.initializeDailyNotification();
|
||||||
|
|
||||||
|
console.log('DailyNotification initialized successfully in TimeSafari PWA');
|
||||||
|
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Failed to initialize DailyNotification in TimeSafari PWA:', error);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Enhanced version of existing TimeSafari loadNewStarredProjectChanges method
|
||||||
|
*/
|
||||||
|
async loadNewStarredProjectChanges(): Promise<void> {
|
||||||
|
if (this.isCapacitor && this.enhancedPlatformService) {
|
||||||
|
// Use plugin-enhanced method on Capacitor
|
||||||
|
const result = await this.enhancedPlatformService.loadNewStarredProjectChanges();
|
||||||
|
this.numNewStarredProjectChanges = result.data.length;
|
||||||
|
this.newStarredProjectChangesHitLimit = result.hitLimit;
|
||||||
|
} else {
|
||||||
|
// Use existing web method in browser
|
||||||
|
await this.loadNewStarredProjectChangesWeb();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Existing web-only method (unchanged)
|
||||||
|
*/
|
||||||
|
async loadNewStarredProjectChangesWeb(): Promise<void> {
|
||||||
|
// Your existing web-only implementation
|
||||||
|
console.log('Using web-only method for starred projects');
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get DailyNotification status for debugging
|
||||||
|
*/
|
||||||
|
async getDailyNotificationStatus(): Promise<unknown> {
|
||||||
|
if (this.enhancedPlatformService) {
|
||||||
|
return await this.enhancedPlatformService.getDailyNotificationStatus();
|
||||||
|
}
|
||||||
|
return { initialized: false, platform: 'web' };
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Usage example in a TimeSafari PWA Vue component
|
||||||
|
*/
|
||||||
|
export const TimeSafariHomeViewExample = {
|
||||||
|
name: 'TimeSafariHomeView',
|
||||||
|
|
||||||
|
mixins: [TimeSafariDailyNotificationMixin],
|
||||||
|
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
// Existing TimeSafari data
|
||||||
|
activeDid: '',
|
||||||
|
starredPlanHandleIds: [] as string[],
|
||||||
|
lastAckedStarredPlanChangesJwtId: '',
|
||||||
|
numNewStarredProjectChanges: 0,
|
||||||
|
newStarredProjectChangesHitLimit: false
|
||||||
|
};
|
||||||
|
},
|
||||||
|
|
||||||
|
async mounted() {
|
||||||
|
// Load existing TimeSafari data
|
||||||
|
await this.loadTimeSafariData();
|
||||||
|
|
||||||
|
// Initialize DailyNotification (only on Capacitor)
|
||||||
|
if (this.isCapacitor) {
|
||||||
|
await this.initializeDailyNotification();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
methods: {
|
||||||
|
/**
|
||||||
|
* Load existing TimeSafari data using PlatformServiceMixin
|
||||||
|
*/
|
||||||
|
async loadTimeSafariData(): Promise<void> {
|
||||||
|
try {
|
||||||
|
// Use existing TimeSafari settings pattern
|
||||||
|
const settings = await this.$settings();
|
||||||
|
|
||||||
|
this.activeDid = settings.activeDid || '';
|
||||||
|
this.starredPlanHandleIds = settings.starredPlanHandleIds || [];
|
||||||
|
this.lastAckedStarredPlanChangesJwtId = settings.lastAckedStarredPlanChangesJwtId || '';
|
||||||
|
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Error loading TimeSafari data:', error);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Enhanced loadNewStarredProjectChanges method
|
||||||
|
*/
|
||||||
|
async loadNewStarredProjectChanges(): Promise<void> {
|
||||||
|
if (this.isCapacitor && this.enhancedPlatformService) {
|
||||||
|
// Use plugin-enhanced method on Capacitor
|
||||||
|
const result = await this.enhancedPlatformService.loadNewStarredProjectChanges();
|
||||||
|
this.numNewStarredProjectChanges = result.data.length;
|
||||||
|
this.newStarredProjectChangesHitLimit = result.hitLimit;
|
||||||
|
} else {
|
||||||
|
// Use existing web method in browser
|
||||||
|
await this.loadNewStarredProjectChangesWeb();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Existing web-only method (unchanged)
|
||||||
|
*/
|
||||||
|
async loadNewStarredProjectChangesWeb(): Promise<void> {
|
||||||
|
// Your existing web-only implementation
|
||||||
|
console.log('Using web-only method for starred projects');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Factory function to create enhanced platform service
|
||||||
|
*/
|
||||||
|
export function createEnhancedPlatformService(): EnhancedCapacitorPlatformService {
|
||||||
|
return new EnhancedCapacitorPlatformService();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Utility function to check if DailyNotification should be used
|
||||||
|
*/
|
||||||
|
export function shouldUseDailyNotification(): boolean {
|
||||||
|
return Capacitor.isNativePlatform();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Utility function to get platform-specific configuration
|
||||||
|
*/
|
||||||
|
export function getPlatformConfig(): {
|
||||||
|
usePlugin: boolean;
|
||||||
|
platform: string;
|
||||||
|
capabilities: unknown;
|
||||||
|
} {
|
||||||
|
const platform = Capacitor.getPlatform();
|
||||||
|
const platformService = PlatformServiceFactory.getInstance();
|
||||||
|
|
||||||
|
return {
|
||||||
|
usePlugin: Capacitor.isNativePlatform(),
|
||||||
|
platform,
|
||||||
|
capabilities: platformService.getCapabilities()
|
||||||
|
};
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user