Browse Source

simplify: Use activeDid-only approach for notifications

- Simplified authentication to use single activeDid instead of complex user management
- Updated plugin interface to require only setActiveDid() method
- Modified API requests to use activeDid as both issuer and recipient
- Streamlined configuration to activeDid: string instead of complex credential object
- Aligned JWT generation with simple DID-based authentication pattern
- Reduced complexity while maintaining security through DID signing

This assumption significantly simplifies the host application integration.
research/notification-plugin-enhancement
Matthew Raymer 19 hours ago
parent
commit
7dd0e41ab7
  1. 117
      doc/BACKGROUND_DATA_FETCHING_PLAN.md

117
doc/BACKGROUND_DATA_FETCHING_PLAN.md

@ -3,7 +3,7 @@
**Author**: Matthew Raymer **Author**: Matthew Raymer
**Version**: 1.0.0 **Version**: 1.0.0
**Created**: 2025-10-02 07:47:04 UTC **Created**: 2025-10-02 07:47:04 UTC
**Last Updated**: 2025-10-02 10:15:00 UTC **Last Updated**: 2025-10-02 10:45:00 UTC
## Overview ## Overview
@ -70,7 +70,7 @@ class JWTHelper {
// Background HTTP Worker // Background HTTP Worker
class DataFetchWorker : CoroutineWorker { class DataFetchWorker : CoroutineWorker {
suspend fun doWork(): Result { suspend fun doWork(): Result {
val jwt = generateJWT(userDid, 60) val jwt = generateJWT(activeDid, 60)
val headers = mapOf( val headers = mapOf(
"Authorization" to "Bearer $jwt", "Authorization" to "Bearer $jwt",
"Content-Type" to "application/json" "Content-Type" to "application/json"
@ -78,7 +78,7 @@ class DataFetchWorker : CoroutineWorker {
val offersResponse = httpClient.get("$apiServer/api/v2/report/offers") { val offersResponse = httpClient.get("$apiServer/api/v2/report/offers") {
headers { headers.forEach { append(it.key, it.value) } } headers { headers.forEach { append(it.key, it.value) } }
parameter("recipientDid", userDid) parameter("recipientId", activeDid) // Use activeDid for recipient filtering
parameter("afterId", lastKnownOfferId) parameter("afterId", lastKnownOfferId)
} }
@ -118,7 +118,7 @@ class JWTHelper {
// Background HTTP Task // Background HTTP Task
class DataFetchTask { class DataFetchTask {
func fetchData() async { func fetchData() async {
let jwt = generateJWT(userDid: userDid, expiresInSeconds: 60) let jwt = generateJWT(userDid: activeDid, expiresInSeconds: 60)
var request = URLRequest(url: apiURL) var request = URLRequest(url: apiURL)
request.setValue("Bearer \(jwt)", forHTTPHeaderField: "Authorization") request.setValue("Bearer \(jwt)", forHTTPHeaderField: "Authorization")
request.setValue("application/json", forHTTPHeaderField: "Content-Type") request.setValue("application/json", forHTTPHeaderField: "Content-Type")
@ -173,7 +173,7 @@ interface PluginConfig {
jwtExpirationSeconds: number; jwtExpirationSeconds: number;
requestTimeoutMs: number; requestTimeoutMs: number;
retryAttempts: number; retryAttempts: number;
userDid: string; activeDid: string; // Simplified to single active DID
lastKnownOfferId?: string; lastKnownOfferId?: string;
lastKnownPlanId?: string; lastKnownPlanId?: string;
} }
@ -203,11 +203,22 @@ interface PluginConfig {
### Data Persistence ### Data Persistence
- **Android Room database** for caching API responses #### **Platform-Specific Storage Architecture**
- **iOS Core Data** for persistent storage
- **Web IndexedDB** for web platform caching **Android/Electron Platforms:**
- **TTL enforcement** for cached data freshness - **@capacitor-community/sqlite** plugin integration for native SQLite access
- **SQLite integration** via @capacitor-community/sqlite for unified storage across platforms - **Shared plugin database** - Plugin manages its own SQLite database instance
- **Direct SQL execution** via plugin's `dbExec()` methods for complex queries
- **Background worker integration** for database operations during content fetch
**Web Platform:**
- **absurd-sql** for SQLite support in browser (managed by host application)
- **Plugin delegation pattern** - Plugin provides SQL queries, host executes them
- **IndexedDB fallback** for basic caching when SQLite unavailable
**iOS Platform:**
- **Core Data integration** via native Swift implementation
- **Background task compatibility** with iOS background refresh constraints
### State Synchronization ### State Synchronization
@ -243,11 +254,78 @@ Based on TimeSafari's optimization patterns:
## Integration Points ## Integration Points
### Existing Plugin APIs ### Enhanced Plugin Interface for Host Application Integration
#### **Database Integration Patterns**
**Android/Electron: Plugin-Managed SQLite**
```typescript
// Plugin handles database operations directly
await plugin.configure({
dbPath: 'daily_notifications.db',
storage: 'shared'
});
// Plugin provides database access methods
const results = await plugin.executeContentFetch(config);
await plugin.cacheContentData(results);
// Host application accesses cached data via plugin
const cachedData = await plugin.getContentCache();
```
**Web: Host-Managed Database**
```typescript
// Host application manages absurd-sql database
import { openDatabase } from 'absurd-sql';
const db = await openDatabase('daily_notifications.db');
// Plugin provides SQL queries, host executes them
const query = await plugin.getContentFetchQuery(apiEndpoint, credentials);
const results = await db.exec(query);
- **Extend** `DailyNotification.configure()` to include API server settings // Plugin receives results for processing
- **Add** new methods: `setUserCredentials()`, `fetchActivityData()`, `getCachedData()` await plugin.processContentData(results);
- **Trigger** notifications based on fetched data differences ```
**iOS: Hybrid Approach**
```typescript
// Plugin manages Core Data operations on background thread
await plugin.scheduleContentFetch(config);
// Host application accesses stored data via plugin APIs
const notificationData = await plugin.getLastNotification();
const cachedContent = await plugin.getContentHistory();
```
#### **New Plugin Methods Required**
```typescript
interface EnhancedDailyNotificationPlugin {
// Database configuration
configureDatabase(options: {
storageType: 'plugin-managed' | 'host-managed';
dbPath?: string;
encryption?: boolean;
}): Promise<void>;
// Content fetch with database integration
fetchAndStoreContent(config: ContentFetchConfig): Promise<ContentFetchResult>;
// Credential management - simplified to activeDid only
setActiveDid(activeDid: string): Promise<void>;
// Data access for host application
getStoredContent(query: string, params?: any[]): Promise<any[]>;
clearStoredContent(options?: { olderThan?: number }): Promise<void>;
// Background task coordination
getBackgroundTaskStatus(): Promise<BackgroundTaskStatus>;
pauseBackgroundTasks(): Promise<void>;
resumeBackgroundTasks(): Promise<void>;
}
```
### Background Scheduling ### Background Scheduling
@ -343,8 +421,9 @@ GET {apiServer}/api/v2/report/offersToPlansOwnedByMe?afterId={jwtId}&beforeId={j
### Token Generation ### Token Generation
- Accept user DID as input - Accept activeDid as input
- Generate appropriate authentication token based on user's credential type - Generate JWT authentication token using DID signing
- Include activeDid as both issuer (`iss`) and subject (`sub`)
- Return token for immediate use or caching - Return token for immediate use or caching
### Request Execution ### Request Execution
@ -374,7 +453,11 @@ GET {apiServer}/api/v2/report/offersToPlansOwnedByMe?afterId={jwtId}&beforeId={j
- Implement DID-based authentication - Implement DID-based authentication
- Integrate API endpoint calls - Integrate API endpoint calls
- Add response parsing and validation - Add response parsing and validation
- Implement basic caching - Implement platform-specific database integration:
- **Android/Electron**: Direct @capacitor-community/sqlite integration
- **Web**: Plugin delegation pattern with absurd-sql coordination
- **iOS**: Core Data background thread integration
- Implement simplified activeDid-based authentication and API calls
### Phase 3: Background Integration (Weeks 5-6) ### Phase 3: Background Integration (Weeks 5-6)

Loading…
Cancel
Save