# VisibilityService Integration Guide for Module Developers ## Quick Start ### 1. Basic Service Integration ```typescript // src/modules/your-module/services/your-service.ts import { BaseService } from '@/core/base/BaseService' export class YourService extends BaseService { protected readonly metadata = { name: 'YourService', version: '1.0.0', dependencies: ['VisibilityService'] // Optional but recommended } private visibilityUnsubscribe?: () => void protected async onInitialize(): Promise { // Your initialization code await this.setupService() // Register with visibility service this.registerWithVisibilityService() } private registerWithVisibilityService(): void { if (!this.visibilityService) { this.debug('VisibilityService not available') return } this.visibilityUnsubscribe = this.visibilityService.registerService( this.metadata.name, async () => this.handleAppResume(), async () => this.handleAppPause() ) this.debug('Registered with VisibilityService') } private async handleAppResume(): Promise { this.debug('App resumed - checking connections') // 1. Check if reconnection is needed if (await this.needsReconnection()) { await this.reconnectService() } // 2. Resume normal operations this.resumeBackgroundTasks() } private async handleAppPause(): Promise { this.debug('App paused - reducing activity') // 1. Stop non-essential tasks this.pauseBackgroundTasks() // 2. Prepare for potential disconnection await this.prepareForPause() } protected async onDispose(): Promise { // Always clean up registration if (this.visibilityUnsubscribe) { this.visibilityUnsubscribe() } this.debug('Service disposed') } // Implement these methods based on your service needs private async needsReconnection(): Promise { // Check if your service connections are healthy return false } private async reconnectService(): Promise { // Reconnect your service } private resumeBackgroundTasks(): void { // Resume periodic tasks, polling, etc. } private pauseBackgroundTasks(): void { // Pause periodic tasks to save battery } private async prepareForPause(): Promise { // Save state, queue operations, etc. } } ``` ## Integration Patterns by Service Type ### Real-Time Connection Services (WebSocket, Nostr, etc.) ```typescript export class RealtimeService extends BaseService { private connections = new Map() private subscriptions = new Map() private async handleAppResume(): Promise { // 1. Check connection health const brokenConnections = await this.checkConnectionHealth() // 2. Reconnect failed connections for (const connectionId of brokenConnections) { await this.reconnectConnection(connectionId) } // 3. Restore subscriptions await this.restoreSubscriptions() // 4. Resume heartbeat/keepalive this.startHeartbeat() } private async handleAppPause(): Promise { // 1. Stop heartbeat to save battery this.stopHeartbeat() // 2. Don't disconnect immediately (for quick resume) // Connections will be checked when app resumes } private async checkConnectionHealth(): Promise { const broken: string[] = [] for (const [id, connection] of this.connections) { if (!connection.isConnected()) { broken.push(id) } } return broken } } ``` ### Data Sync Services ```typescript export class DataSyncService extends BaseService { private syncQueue: Operation[] = [] private lastSyncTime: number = 0 private async handleAppResume(): Promise { const hiddenTime = Date.now() - this.lastSyncTime // If hidden for > 5 minutes, do full sync if (hiddenTime > 300000) { await this.performFullSync() } else { await this.performIncrementalSync() } // Process queued operations await this.processQueue() // Resume periodic sync this.startPeriodicSync() } private async handleAppPause(): Promise { // Stop periodic sync this.stopPeriodicSync() // Save timestamp this.lastSyncTime = Date.now() // Enable operation queueing this.enableQueueMode() } } ``` ### Background Processing Services ```typescript export class BackgroundService extends BaseService { private processingInterval?: number private taskQueue: Task[] = [] private async handleAppResume(): Promise { // Resume background processing this.startProcessing() // Process any queued tasks await this.processQueuedTasks() } private async handleAppPause(): Promise { // Stop background processing to save CPU/battery if (this.processingInterval) { clearInterval(this.processingInterval) this.processingInterval = undefined } // Queue new tasks instead of processing immediately this.enableTaskQueueing() } } ``` ## Module Registration Pattern ### Module Index File ```typescript // src/modules/your-module/index.ts import type { App } from 'vue' import type { ModulePlugin } from '@/core/types' import { YourService } from './services/your-service' export const yourModule: ModulePlugin = { name: 'your-module', version: '1.0.0', dependencies: ['base'], // base module provides VisibilityService async install(app: App, options?: any) { console.log('🔧 Installing your module...') // Create and initialize service const yourService = new YourService() // Initialize service (this will register with VisibilityService) await yourService.initialize({ waitForDependencies: true, // Wait for VisibilityService maxRetries: 3 }) // Register service in DI container container.provide(YOUR_SERVICE_TOKEN, yourService) console.log('✅ Your module installed successfully') }, async uninstall() { console.log('🗑️ Uninstalling your module...') // Services will auto-dispose and unregister from VisibilityService } } ``` ## Best Practices Checklist ### ✅ Do's - **Always register during `onInitialize()`** ```typescript protected async onInitialize(): Promise { await this.setupService() this.registerWithVisibilityService() // ✅ } ``` - **Check hidden duration before expensive operations** ```typescript private async handleAppResume(): Promise { const state = this.visibilityService.getState() if (state.hiddenDuration && state.hiddenDuration > 30000) { await this.performFullReconnect() // ✅ Only if needed } } ``` - **Always clean up registrations** ```typescript protected async onDispose(): Promise { if (this.visibilityUnsubscribe) { this.visibilityUnsubscribe() // ✅ } } ``` - **Use graceful pause strategies** ```typescript private async handleAppPause(): Promise { this.stopHeartbeat() // ✅ Stop periodic tasks // Keep connections alive for quick resume } ``` ### ❌ Don'ts - **Don't immediately disconnect on pause** ```typescript private async handleAppPause(): Promise { this.disconnectAll() // ❌ Too aggressive } ``` - **Don't ignore the service availability check** ```typescript private registerWithVisibilityService(): void { // ❌ Missing availability check this.visibilityService.registerService(/*...*/) // ✅ Correct if (!this.visibilityService) return this.visibilityService.registerService(/*...*/) } ``` - **Don't forget dependencies in metadata** ```typescript protected readonly metadata = { name: 'MyService', dependencies: [] // ❌ Should include 'VisibilityService' } ``` ## Common Patterns ### Connection Health Checking ```typescript private async checkConnectionHealth(): Promise { try { // Perform a lightweight health check await this.ping() return true } catch (error) { this.debug('Connection health check failed:', error) return false } } ``` ### Subscription Restoration ```typescript private async restoreSubscriptions(): Promise { const subscriptionsToRestore = Array.from(this.subscriptionConfigs.values()) for (const config of subscriptionsToRestore) { try { await this.recreateSubscription(config) } catch (error) { this.debug(`Failed to restore subscription ${config.id}:`, error) } } } ``` ### Operation Queueing ```typescript private operationQueue: Operation[] = [] private queueingEnabled = false private async executeOrQueue(operation: Operation): Promise { if (this.queueingEnabled) { this.operationQueue.push(operation) } else { await operation.execute() } } private async processQueue(): Promise { const operations = this.operationQueue.splice(0) // Clear queue for (const operation of operations) { try { await operation.execute() } catch (error) { this.debug('Queued operation failed:', error) } } } ``` ## Testing Integration ### Mock VisibilityService for Tests ```typescript // tests/setup/mockVisibilityService.ts export const createMockVisibilityService = () => ({ isVisible: { value: true }, isOnline: { value: true }, registerService: vi.fn(() => vi.fn()), // Returns unregister function getState: vi.fn(() => ({ isVisible: true, isOnline: true, hiddenDuration: 0 })) }) // In your test describe('YourService', () => { it('should register with VisibilityService', async () => { const mockVisibility = createMockVisibilityService() const service = new YourService() service.visibilityService = mockVisibility await service.initialize() expect(mockVisibility.registerService).toHaveBeenCalledWith( 'YourService', expect.any(Function), expect.any(Function) ) }) }) ``` ### Test Visibility Events ```typescript it('should handle app resume correctly', async () => { const service = new YourService() const reconnectSpy = vi.spyOn(service, 'reconnect') // Simulate app resume after long pause await service.handleAppResume() expect(reconnectSpy).toHaveBeenCalled() }) ``` --- This integration guide provides everything a module developer needs to add visibility management to their services. The patterns are battle-tested and optimize for both user experience and device battery life.