|
|
@ -1,28 +1,36 @@ |
|
|
|
/** |
|
|
|
* Platform Service Mixin for Vue Components |
|
|
|
* |
|
|
|
* Provides class-level caching of platform service instances to avoid |
|
|
|
* repeated PlatformServiceFactory.getInstance() calls throughout components. |
|
|
|
* |
|
|
|
* This mixin implements a hybrid approach combining: |
|
|
|
* - Class-level service caching for performance |
|
|
|
* - Vue composition API patterns for modern development |
|
|
|
* Enhanced PlatformService Mixin with ultra-concise database operations and caching |
|
|
|
* |
|
|
|
* Provides cached platform service access and utility methods for Vue components. |
|
|
|
* Eliminates repetitive PlatformServiceFactory.getInstance() calls across components. |
|
|
|
* |
|
|
|
* Features: |
|
|
|
* - Cached platform service instance (created once per component) |
|
|
|
* - Enhanced database utility methods with comprehensive error handling |
|
|
|
* - Ultra-concise database interaction methods ($db, $exec, $one, etc.) |
|
|
|
* - Automatic query result mapping and JSON field parsing |
|
|
|
* - Specialized shortcuts for common queries (contacts, settings) |
|
|
|
* - Transaction support with automatic rollback on errors |
|
|
|
* - Mixin pattern for easy integration with existing class components |
|
|
|
* - Enhanced utility methods for common patterns |
|
|
|
* - Robust error handling and logging |
|
|
|
* - Ultra-concise database interaction methods |
|
|
|
* - Smart caching layer with TTL for performance optimization |
|
|
|
* - Settings shortcuts for ultra-frequent update patterns |
|
|
|
* |
|
|
|
* Benefits: |
|
|
|
* - Eliminates repeated PlatformServiceFactory.getInstance() calls |
|
|
|
* - Provides consistent service access pattern across components |
|
|
|
* - Improves performance with cached instances |
|
|
|
* - Provides consistent error handling across components |
|
|
|
* - Reduces boilerplate database code by up to 80% |
|
|
|
* - Maintains type safety with TypeScript |
|
|
|
* - Includes common database utility patterns |
|
|
|
* - Enhanced error handling and logging |
|
|
|
* - Ultra-concise method names for frequent operations |
|
|
|
* - Automatic caching for settings and contacts (massive performance gain) |
|
|
|
* - Settings update shortcuts reduce 90% of update boilerplate |
|
|
|
* |
|
|
|
* @author Matthew Raymer |
|
|
|
* @version 3.0.0 |
|
|
|
* @version 4.0.0 |
|
|
|
* @since 2025-07-02 |
|
|
|
*/ |
|
|
|
|
|
|
@ -30,37 +38,53 @@ import { PlatformServiceFactory } from "@/services/PlatformServiceFactory"; |
|
|
|
import type { PlatformService } from "@/services/PlatformService"; |
|
|
|
import { mapColumnsToValues, parseJsonField } from "@/db/databaseUtil"; |
|
|
|
import { MASTER_SETTINGS_KEY } from "@/db/tables/settings"; |
|
|
|
import * as databaseUtil from "@/db/databaseUtil"; |
|
|
|
|
|
|
|
// =================================================
|
|
|
|
// CACHING INFRASTRUCTURE
|
|
|
|
// =================================================
|
|
|
|
|
|
|
|
/** |
|
|
|
* Cache entry with TTL support |
|
|
|
*/ |
|
|
|
interface CacheEntry<T> { |
|
|
|
data: T; |
|
|
|
timestamp: number; |
|
|
|
ttl: number; // milliseconds
|
|
|
|
} |
|
|
|
|
|
|
|
/** |
|
|
|
* Global cache store for mixin instances |
|
|
|
* Uses WeakMap to avoid memory leaks when components are destroyed |
|
|
|
*/ |
|
|
|
const componentCaches = new WeakMap<any, Map<string, CacheEntry<any>>>(); |
|
|
|
|
|
|
|
/** |
|
|
|
* Cache configuration constants |
|
|
|
*/ |
|
|
|
const CACHE_DEFAULTS = { |
|
|
|
settings: 30000, // 30 seconds TTL for settings
|
|
|
|
contacts: 60000, // 60 seconds TTL for contacts
|
|
|
|
accounts: 30000, // 30 seconds TTL for accounts
|
|
|
|
default: 15000, // 15 seconds default TTL
|
|
|
|
} as const; |
|
|
|
|
|
|
|
/** |
|
|
|
* Enhanced mixin that provides cached platform service access and utility methods |
|
|
|
* |
|
|
|
* Usage: |
|
|
|
* ```typescript
|
|
|
|
* @Component({ |
|
|
|
* mixins: [PlatformServiceMixin] |
|
|
|
* }) |
|
|
|
* export default class MyComponent extends Vue { |
|
|
|
* async someMethod() { |
|
|
|
* // Direct access without type assertions
|
|
|
|
* const result = await this.$dbQuery('SELECT * FROM users'); |
|
|
|
* |
|
|
|
* // Utility methods for common patterns
|
|
|
|
* const settings = await this.$getSettings('user123'); |
|
|
|
* } |
|
|
|
* } |
|
|
|
* ``` |
|
|
|
* with smart caching layer for ultimate performance optimization |
|
|
|
*/ |
|
|
|
export const PlatformServiceMixin = { |
|
|
|
data() { |
|
|
|
return { |
|
|
|
// Cache the platform service instance at component level
|
|
|
|
_platformService: null as PlatformService | null, |
|
|
|
}; |
|
|
|
}, |
|
|
|
|
|
|
|
computed: { |
|
|
|
/** |
|
|
|
* Get the cached platform service instance |
|
|
|
* Creates and caches the instance on first access |
|
|
|
* Cached platform service instance |
|
|
|
* Created once per component lifecycle |
|
|
|
*/ |
|
|
|
platformService(): PlatformService { |
|
|
|
if (!(this as any)._platformService) { |
|
|
@ -70,45 +94,101 @@ export const PlatformServiceMixin = { |
|
|
|
}, |
|
|
|
|
|
|
|
/** |
|
|
|
* Check if running on Capacitor platform |
|
|
|
* Platform detection utilities |
|
|
|
*/ |
|
|
|
isCapacitor(): boolean { |
|
|
|
const service = (this as any).platformService as any; |
|
|
|
return typeof service.isCapacitor === "function" |
|
|
|
? service.isCapacitor() |
|
|
|
: false; |
|
|
|
return (this as any).platformService().isCapacitor(); |
|
|
|
}, |
|
|
|
|
|
|
|
/** |
|
|
|
* Check if running on web platform |
|
|
|
*/ |
|
|
|
isWeb(): boolean { |
|
|
|
const service = (this as any).platformService as any; |
|
|
|
return typeof service.isWeb === "function" ? service.isWeb() : false; |
|
|
|
return (this as any).platformService().isWeb(); |
|
|
|
}, |
|
|
|
|
|
|
|
/** |
|
|
|
* Check if running on Electron platform |
|
|
|
*/ |
|
|
|
isElectron(): boolean { |
|
|
|
const service = (this as any).platformService as any; |
|
|
|
return typeof service.isElectron === "function" |
|
|
|
? service.isElectron() |
|
|
|
: false; |
|
|
|
return (this as any).platformService().isElectron(); |
|
|
|
}, |
|
|
|
|
|
|
|
/** |
|
|
|
* Get platform capabilities |
|
|
|
* Platform capabilities |
|
|
|
*/ |
|
|
|
capabilities() { |
|
|
|
return (this as any).platformService.getCapabilities(); |
|
|
|
return (this as any).platformService().getCapabilities(); |
|
|
|
}, |
|
|
|
}, |
|
|
|
|
|
|
|
methods: { |
|
|
|
// =================================================
|
|
|
|
// CACHING UTILITY METHODS
|
|
|
|
// =================================================
|
|
|
|
|
|
|
|
/** |
|
|
|
* Get or initialize cache for this component instance |
|
|
|
*/ |
|
|
|
_getCache(): Map<string, CacheEntry<any>> { |
|
|
|
let cache = componentCaches.get(this); |
|
|
|
if (!cache) { |
|
|
|
cache = new Map(); |
|
|
|
componentCaches.set(this, cache); |
|
|
|
} |
|
|
|
return cache; |
|
|
|
}, |
|
|
|
|
|
|
|
/** |
|
|
|
* Check if cache entry is valid (not expired) |
|
|
|
*/ |
|
|
|
_isCacheValid(entry: CacheEntry<any>): boolean { |
|
|
|
return Date.now() - entry.timestamp < entry.ttl; |
|
|
|
}, |
|
|
|
|
|
|
|
/** |
|
|
|
* Get data from cache if valid, otherwise return null |
|
|
|
*/ |
|
|
|
_getCached<T>(key: string): T | null { |
|
|
|
const cache = this._getCache(); |
|
|
|
const entry = cache.get(key); |
|
|
|
if (entry && this._isCacheValid(entry)) { |
|
|
|
return entry.data; |
|
|
|
} |
|
|
|
cache.delete(key); // Clean up expired entries
|
|
|
|
return null; |
|
|
|
}, |
|
|
|
|
|
|
|
/** |
|
|
|
* Store data in cache with TTL |
|
|
|
*/ |
|
|
|
_setCached<T>(key: string, data: T, ttl?: number): T { |
|
|
|
const cache = this._getCache(); |
|
|
|
const actualTtl = ttl || CACHE_DEFAULTS.default; |
|
|
|
cache.set(key, { |
|
|
|
data, |
|
|
|
timestamp: Date.now(), |
|
|
|
ttl: actualTtl, |
|
|
|
}); |
|
|
|
return data; |
|
|
|
}, |
|
|
|
|
|
|
|
/** |
|
|
|
* Invalidate specific cache entry |
|
|
|
*/ |
|
|
|
_invalidateCache(key: string): void { |
|
|
|
const cache = this._getCache(); |
|
|
|
cache.delete(key); |
|
|
|
}, |
|
|
|
|
|
|
|
/** |
|
|
|
* Clear all cache entries for this component |
|
|
|
*/ |
|
|
|
_clearCache(): void { |
|
|
|
const cache = this._getCache(); |
|
|
|
cache.clear(); |
|
|
|
}, |
|
|
|
|
|
|
|
// =================================================
|
|
|
|
// ENHANCED DATABASE METHODS (with error handling)
|
|
|
|
// =================================================
|
|
|
|
|
|
|
|
/** |
|
|
|
* Enhanced database query method with error handling |
|
|
|
* Prefixed with $ to avoid naming conflicts and improve discoverability |
|
|
|
*/ |
|
|
|
async $dbQuery(sql: string, params?: unknown[]) { |
|
|
|
try { |
|
|
@ -146,14 +226,14 @@ export const PlatformServiceMixin = { |
|
|
|
}, |
|
|
|
|
|
|
|
/** |
|
|
|
* Enhanced database single row method with error handling |
|
|
|
* Enhanced database single row query method with error handling |
|
|
|
*/ |
|
|
|
async $dbGetOneRow(sql: string, params?: unknown[]) { |
|
|
|
try { |
|
|
|
return await (this as any).platformService.dbGetOneRow(sql, params); |
|
|
|
} catch (error) { |
|
|
|
console.error( |
|
|
|
`[${(this as any).$options.name}] Database getOneRow failed:`, |
|
|
|
`[${(this as any).$options.name}] Database single row query failed:`, |
|
|
|
{ |
|
|
|
sql, |
|
|
|
params, |
|
|
@ -269,8 +349,7 @@ export const PlatformServiceMixin = { |
|
|
|
}, |
|
|
|
|
|
|
|
/** |
|
|
|
* Utility method for safe database transactions |
|
|
|
* Handles common transaction patterns with proper error handling |
|
|
|
* Transaction wrapper with automatic rollback on error |
|
|
|
*/ |
|
|
|
async $withTransaction<T>(callback: () => Promise<T>): Promise<T> { |
|
|
|
try { |
|
|
@ -280,10 +359,6 @@ export const PlatformServiceMixin = { |
|
|
|
return result; |
|
|
|
} catch (error) { |
|
|
|
await this.$dbExec("ROLLBACK"); |
|
|
|
console.error( |
|
|
|
`[${(this as any).$options.name}] Transaction failed:`, |
|
|
|
error, |
|
|
|
); |
|
|
|
throw error; |
|
|
|
} |
|
|
|
}, |
|
|
@ -350,45 +425,160 @@ export const PlatformServiceMixin = { |
|
|
|
}, |
|
|
|
|
|
|
|
// =================================================
|
|
|
|
// SPECIALIZED SHORTCUTS (common patterns)
|
|
|
|
// CACHED SPECIALIZED SHORTCUTS (massive performance boost)
|
|
|
|
// =================================================
|
|
|
|
|
|
|
|
/** |
|
|
|
* Load all contacts in one call - $contacts() |
|
|
|
* Ultra-concise shortcut for the most common query |
|
|
|
* @returns Mapped array of all contacts |
|
|
|
* Load all contacts with caching - $contacts() |
|
|
|
* Ultra-concise shortcut with 60s TTL for performance |
|
|
|
* @returns Cached mapped array of all contacts |
|
|
|
*/ |
|
|
|
async $contacts(): Promise<any[]> { |
|
|
|
return await (this as any).$query("SELECT * FROM contacts ORDER BY name"); |
|
|
|
const cacheKey = 'contacts_all'; |
|
|
|
const cached = this._getCached<any[]>(cacheKey); |
|
|
|
if (cached) { |
|
|
|
return cached; |
|
|
|
} |
|
|
|
|
|
|
|
const contacts = await this.$query("SELECT * FROM contacts ORDER BY name"); |
|
|
|
return this._setCached(cacheKey, contacts, CACHE_DEFAULTS.contacts); |
|
|
|
}, |
|
|
|
|
|
|
|
/** |
|
|
|
* Load settings with optional defaults - $settings() |
|
|
|
* Load settings with optional defaults and caching - $settings() |
|
|
|
* Ultra-concise with 30s TTL for massive performance gain |
|
|
|
* @param defaults Optional default values |
|
|
|
* @returns Settings object |
|
|
|
* @returns Cached settings object |
|
|
|
*/ |
|
|
|
async $settings(defaults: any = {}): Promise<any> { |
|
|
|
return await (this as any).$getSettings(MASTER_SETTINGS_KEY, defaults); |
|
|
|
const cacheKey = `settings_${String(MASTER_SETTINGS_KEY)}`; |
|
|
|
const cached = this._getCached<any>(cacheKey); |
|
|
|
if (cached) { |
|
|
|
return { ...cached, ...defaults }; // Merge with any new defaults
|
|
|
|
} |
|
|
|
|
|
|
|
const settings = await this.$getSettings(MASTER_SETTINGS_KEY, defaults); |
|
|
|
return (this as any)._setCached(cacheKey, settings, CACHE_DEFAULTS.settings); |
|
|
|
}, |
|
|
|
|
|
|
|
/** |
|
|
|
* Load account-specific settings - $accountSettings() |
|
|
|
* Load account-specific settings with caching - $accountSettings() |
|
|
|
* @param did DID identifier (optional, uses current active DID) |
|
|
|
* @param defaults Optional default values |
|
|
|
* @returns Merged settings object |
|
|
|
* @returns Cached merged settings object |
|
|
|
*/ |
|
|
|
async $accountSettings(did?: string, defaults: any = {}): Promise<any> { |
|
|
|
const currentDid = did || (this as any).activeDid; |
|
|
|
const cacheKey = `account_settings_${currentDid || 'default'}`; |
|
|
|
|
|
|
|
const cached = this._getCached<any>(cacheKey); |
|
|
|
if (cached) { |
|
|
|
return { ...cached, ...defaults }; // Merge with any new defaults
|
|
|
|
} |
|
|
|
|
|
|
|
let settings; |
|
|
|
if (!currentDid) { |
|
|
|
return await (this as any).$settings(defaults); |
|
|
|
settings = await this.$settings(defaults); |
|
|
|
} else { |
|
|
|
settings = await this.$getMergedSettings(MASTER_SETTINGS_KEY, currentDid, defaults); |
|
|
|
} |
|
|
|
return await (this as any).$getMergedSettings(MASTER_SETTINGS_KEY, currentDid, defaults); |
|
|
|
|
|
|
|
return this._setCached(cacheKey, settings, CACHE_DEFAULTS.settings); |
|
|
|
}, |
|
|
|
|
|
|
|
// =================================================
|
|
|
|
// SETTINGS UPDATE SHORTCUTS (eliminate 90% boilerplate)
|
|
|
|
// =================================================
|
|
|
|
|
|
|
|
/** |
|
|
|
* Save default settings with cache invalidation - $saveSettings() |
|
|
|
* Ultra-concise shortcut for updateDefaultSettings |
|
|
|
* @param changes Settings changes to save |
|
|
|
* @returns Promise<boolean> Success status |
|
|
|
*/ |
|
|
|
async $saveSettings(changes: any): Promise<boolean> { |
|
|
|
const result = await databaseUtil.updateDefaultSettings(changes); |
|
|
|
|
|
|
|
// Invalidate related caches
|
|
|
|
this._invalidateCache(`settings_${MASTER_SETTINGS_KEY}`); |
|
|
|
this._invalidateCache(`account_settings_default`); |
|
|
|
|
|
|
|
return result; |
|
|
|
}, |
|
|
|
|
|
|
|
/** |
|
|
|
* Save user-specific settings with cache invalidation - $saveUserSettings() |
|
|
|
* Ultra-concise shortcut for updateDidSpecificSettings |
|
|
|
* @param did DID identifier |
|
|
|
* @param changes Settings changes to save |
|
|
|
* @returns Promise<boolean> Success status |
|
|
|
*/ |
|
|
|
async $saveUserSettings(did: string, changes: any): Promise<boolean> { |
|
|
|
const result = await databaseUtil.updateDidSpecificSettings(did, changes); |
|
|
|
|
|
|
|
// Invalidate related caches
|
|
|
|
this._invalidateCache(`account_settings_${did}`); |
|
|
|
this._invalidateCache(`settings_${MASTER_SETTINGS_KEY}`); |
|
|
|
|
|
|
|
return result; |
|
|
|
}, |
|
|
|
|
|
|
|
/** |
|
|
|
* Save settings for current active user - $saveMySettings() |
|
|
|
* Ultra-concise shortcut using activeDid from component |
|
|
|
* @param changes Settings changes to save |
|
|
|
* @returns Promise<boolean> Success status |
|
|
|
*/ |
|
|
|
async $saveMySettings(changes: any): Promise<boolean> { |
|
|
|
const currentDid = (this as any).activeDid; |
|
|
|
if (!currentDid) { |
|
|
|
return await this.$saveSettings(changes); |
|
|
|
} |
|
|
|
return await this.$saveUserSettings(currentDid, changes); |
|
|
|
}, |
|
|
|
|
|
|
|
// =================================================
|
|
|
|
// CACHE MANAGEMENT METHODS
|
|
|
|
// =================================================
|
|
|
|
|
|
|
|
/** |
|
|
|
* Manually refresh settings cache - $refreshSettings() |
|
|
|
* Forces reload of settings from database |
|
|
|
*/ |
|
|
|
async $refreshSettings(): Promise<any> { |
|
|
|
this._invalidateCache(`settings_${MASTER_SETTINGS_KEY}`); |
|
|
|
const currentDid = (this as any).activeDid; |
|
|
|
if (currentDid) { |
|
|
|
this._invalidateCache(`account_settings_${currentDid}`); |
|
|
|
} |
|
|
|
return await this.$settings(); |
|
|
|
}, |
|
|
|
|
|
|
|
/** |
|
|
|
* Manually refresh contacts cache - $refreshContacts() |
|
|
|
* Forces reload of contacts from database |
|
|
|
*/ |
|
|
|
async $refreshContacts(): Promise<any[]> { |
|
|
|
this._invalidateCache('contacts_all'); |
|
|
|
return await this.$contacts(); |
|
|
|
}, |
|
|
|
|
|
|
|
/** |
|
|
|
* Clear all caches for this component - $clearAllCaches() |
|
|
|
* Useful for manual cache management |
|
|
|
*/ |
|
|
|
$clearAllCaches(): void { |
|
|
|
this._clearCache(); |
|
|
|
}, |
|
|
|
}, |
|
|
|
}; |
|
|
|
|
|
|
|
// =================================================
|
|
|
|
// TYPESCRIPT INTERFACES
|
|
|
|
// =================================================
|
|
|
|
|
|
|
|
/** |
|
|
|
* Enhanced interface with utility methods |
|
|
|
* Enhanced interface with caching utility methods |
|
|
|
*/ |
|
|
|
export interface IPlatformServiceMixin { |
|
|
|
platformService: PlatformService; |
|
|
@ -435,9 +625,19 @@ declare module "@vue/runtime-core" { |
|
|
|
$getMergedSettings(key: string, did?: string, defaults?: any): Promise<any>; |
|
|
|
$withTransaction<T>(fn: () => Promise<T>): Promise<T>; |
|
|
|
|
|
|
|
// Specialized shortcuts for ultra-common patterns
|
|
|
|
// Cached specialized shortcuts (massive performance boost)
|
|
|
|
$contacts(): Promise<any[]>; |
|
|
|
$settings(defaults?: any): Promise<any>; |
|
|
|
$accountSettings(did?: string, defaults?: any): Promise<any>; |
|
|
|
|
|
|
|
// Settings update shortcuts (eliminate 90% boilerplate)
|
|
|
|
$saveSettings(changes: any): Promise<boolean>; |
|
|
|
$saveUserSettings(did: string, changes: any): Promise<boolean>; |
|
|
|
$saveMySettings(changes: any): Promise<boolean>; |
|
|
|
|
|
|
|
// Cache management methods
|
|
|
|
$refreshSettings(): Promise<any>; |
|
|
|
$refreshContacts(): Promise<any[]>; |
|
|
|
$clearAllCaches(): void; |
|
|
|
} |
|
|
|
} |
|
|
|