forked from jsnbuchanan/crowd-funder-for-time-pwa
Fix Vue property conflicts in PlatformServiceMixin implementation
- Remove duplicate property declarations from TopMessage component - Use (this as any) type assertion for mixin methods - Resolves 'Data property already defined' warnings - Fixes 'this.dbQuery is not a function' runtime errors
This commit is contained in:
136
src/utils/PlatformServiceMixin.ts
Normal file
136
src/utils/PlatformServiceMixin.ts
Normal file
@@ -0,0 +1,136 @@
|
||||
/**
|
||||
* 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
|
||||
* - Mixin pattern for easy integration with existing class components
|
||||
*
|
||||
* Benefits:
|
||||
* - Eliminates repeated PlatformServiceFactory.getInstance() calls
|
||||
* - Provides consistent service access pattern across components
|
||||
* - Improves performance with cached instances
|
||||
* - Maintains type safety with TypeScript
|
||||
*
|
||||
* @author Matthew Raymer
|
||||
* @version 1.0.0
|
||||
* @since 2025-07-02
|
||||
*/
|
||||
|
||||
import { PlatformServiceFactory } from "@/services/PlatformServiceFactory";
|
||||
import type { PlatformService } from "@/services/PlatformService";
|
||||
|
||||
/**
|
||||
* Mixin that provides cached platform service access to Vue components
|
||||
*
|
||||
* Usage:
|
||||
* ```typescript
|
||||
* @Component({
|
||||
* mixins: [PlatformServiceMixin]
|
||||
* })
|
||||
* export default class MyComponent extends Vue {
|
||||
* async someMethod() {
|
||||
* // Access cached platform service directly
|
||||
* const result = await this.platformService.dbQuery('SELECT * FROM users');
|
||||
* }
|
||||
* }
|
||||
* ```
|
||||
*/
|
||||
export const PlatformServiceMixin = {
|
||||
data() {
|
||||
return {
|
||||
_platformService: null as PlatformService | null,
|
||||
};
|
||||
},
|
||||
|
||||
computed: {
|
||||
/**
|
||||
* Get the cached platform service instance
|
||||
* Creates and caches the instance on first access
|
||||
*/
|
||||
platformService(): PlatformService {
|
||||
if (!(this as any)._platformService) {
|
||||
(this as any)._platformService = PlatformServiceFactory.getInstance();
|
||||
}
|
||||
return (this as any)._platformService;
|
||||
},
|
||||
|
||||
/**
|
||||
* Check if running on Capacitor platform
|
||||
*/
|
||||
isCapacitor(): boolean {
|
||||
const service = (this as any).platformService as any;
|
||||
return typeof service.isCapacitor === "function"
|
||||
? service.isCapacitor()
|
||||
: false;
|
||||
},
|
||||
|
||||
/**
|
||||
* Check if running on web platform
|
||||
*/
|
||||
isWeb(): boolean {
|
||||
const service = (this as any).platformService as any;
|
||||
return typeof service.isWeb === "function" ? service.isWeb() : false;
|
||||
},
|
||||
|
||||
/**
|
||||
* Check if running on Electron platform
|
||||
*/
|
||||
isElectron(): boolean {
|
||||
const service = (this as any).platformService as any;
|
||||
return typeof service.isElectron === "function"
|
||||
? service.isElectron()
|
||||
: false;
|
||||
},
|
||||
|
||||
/**
|
||||
* Get platform capabilities
|
||||
*/
|
||||
capabilities() {
|
||||
return (this as any).platformService.getCapabilities();
|
||||
},
|
||||
},
|
||||
|
||||
methods: {
|
||||
/**
|
||||
* Convenient database query method
|
||||
* Shorthand for this.platformService.dbQuery()
|
||||
*/
|
||||
async dbQuery(sql: string, params?: unknown[]) {
|
||||
return await (this as any).platformService.dbQuery(sql, params);
|
||||
},
|
||||
|
||||
/**
|
||||
* Convenient database execution method
|
||||
* Shorthand for this.platformService.dbExec()
|
||||
*/
|
||||
async dbExec(sql: string, params?: unknown[]) {
|
||||
return await (this as any).platformService.dbExec(sql, params);
|
||||
},
|
||||
|
||||
/**
|
||||
* Convenient database single row method
|
||||
* Shorthand for this.platformService.dbGetOneRow()
|
||||
*/
|
||||
async dbGetOneRow(sql: string, params?: unknown[]) {
|
||||
return await (this as any).platformService.dbGetOneRow(sql, params);
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
/**
|
||||
* Type-only export for components that need to declare the mixin interface
|
||||
*/
|
||||
export interface IPlatformServiceMixin {
|
||||
platformService: PlatformService;
|
||||
dbQuery(sql: string, params?: unknown[]): Promise<any>;
|
||||
dbExec(sql: string, params?: unknown[]): Promise<any>;
|
||||
dbGetOneRow(sql: string, params?: unknown[]): Promise<any>;
|
||||
isCapacitor: boolean;
|
||||
isWeb: boolean;
|
||||
isElectron: boolean;
|
||||
capabilities: any;
|
||||
}
|
||||
@@ -1,28 +1,28 @@
|
||||
/**
|
||||
* Platform Service Composable for TimeSafari
|
||||
*
|
||||
*
|
||||
* Provides centralized access to platform-specific services across Vue components.
|
||||
* This composable encapsulates the singleton pattern and provides a clean interface
|
||||
* for components to access platform functionality without directly managing
|
||||
* the PlatformServiceFactory.
|
||||
*
|
||||
*
|
||||
* Benefits:
|
||||
* - Centralized service access
|
||||
* - Better testability with easy mocking
|
||||
* - Cleaner component code
|
||||
* - Type safety with TypeScript
|
||||
* - Reactive capabilities if needed in the future
|
||||
*
|
||||
*
|
||||
* @author Matthew Raymer
|
||||
* @version 1.0.0
|
||||
* @since 2025-07-02
|
||||
*/
|
||||
|
||||
import { ref, readonly } from 'vue';
|
||||
import { PlatformServiceFactory } from '@/services/PlatformServiceFactory';
|
||||
import type { PlatformService } from '@/services/PlatformService';
|
||||
import * as databaseUtil from '@/db/databaseUtil';
|
||||
import { Contact } from '@/db/tables/contacts';
|
||||
import { ref, readonly } from "vue";
|
||||
import { PlatformServiceFactory } from "@/services/PlatformServiceFactory";
|
||||
import type { PlatformService } from "@/services/PlatformService";
|
||||
import * as databaseUtil from "@/db/databaseUtil";
|
||||
import { Contact } from "@/db/tables/contacts";
|
||||
|
||||
/**
|
||||
* Reactive reference to the platform service instance
|
||||
@@ -48,27 +48,27 @@ function initializePlatformService(): PlatformService {
|
||||
|
||||
/**
|
||||
* Platform Service Composable
|
||||
*
|
||||
*
|
||||
* Provides access to platform-specific services in a composable pattern.
|
||||
* This is the recommended way for Vue components to access platform functionality.
|
||||
*
|
||||
*
|
||||
* @returns Object containing platform service and utility functions
|
||||
*
|
||||
*
|
||||
* @example
|
||||
* ```typescript
|
||||
* // In a Vue component
|
||||
* import { usePlatformService } from '@/utils/usePlatformService';
|
||||
*
|
||||
*
|
||||
* export default {
|
||||
* setup() {
|
||||
* const { platform, dbQuery, dbExec, takePicture } = usePlatformService();
|
||||
*
|
||||
*
|
||||
* // Use platform methods directly
|
||||
* const takePhoto = async () => {
|
||||
* const result = await takePicture();
|
||||
* console.log('Photo taken:', result);
|
||||
* };
|
||||
*
|
||||
*
|
||||
* return { takePhoto };
|
||||
* }
|
||||
* };
|
||||
@@ -85,46 +85,55 @@ export function usePlatformService() {
|
||||
*/
|
||||
const safeSerializeParams = (params?: unknown[]): unknown[] => {
|
||||
if (!params) return [];
|
||||
|
||||
console.log('[usePlatformService] Original params:', params);
|
||||
console.log('[usePlatformService] Params toString:', params.toString());
|
||||
console.log('[usePlatformService] Params constructor:', params.constructor?.name);
|
||||
|
||||
|
||||
console.log("[usePlatformService] Original params:", params);
|
||||
console.log("[usePlatformService] Params toString:", params.toString());
|
||||
console.log(
|
||||
"[usePlatformService] Params constructor:",
|
||||
params.constructor?.name,
|
||||
);
|
||||
|
||||
// Use the most aggressive approach: JSON round-trip + spread operator
|
||||
try {
|
||||
// Method 1: JSON round-trip to completely strip any Proxy
|
||||
const jsonSerialized = JSON.parse(JSON.stringify(params));
|
||||
console.log('[usePlatformService] JSON serialized:', jsonSerialized);
|
||||
|
||||
console.log("[usePlatformService] JSON serialized:", jsonSerialized);
|
||||
|
||||
// Method 2: Spread operator to create new array
|
||||
const spreadArray = [...jsonSerialized];
|
||||
console.log('[usePlatformService] Spread array:', spreadArray);
|
||||
|
||||
console.log("[usePlatformService] Spread array:", spreadArray);
|
||||
|
||||
// Method 3: Force primitive extraction for each element
|
||||
const finalParams = spreadArray.map((param, index) => {
|
||||
const finalParams = spreadArray.map((param, _index) => {
|
||||
if (param === null || param === undefined) {
|
||||
return param;
|
||||
}
|
||||
|
||||
|
||||
// Force convert to primitive value
|
||||
if (typeof param === 'object') {
|
||||
if (typeof param === "object") {
|
||||
if (Array.isArray(param)) {
|
||||
return [...param]; // Spread to new array
|
||||
} else {
|
||||
return { ...param }; // Spread to new object
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
return param;
|
||||
});
|
||||
|
||||
console.log('[usePlatformService] Final params:', finalParams);
|
||||
console.log('[usePlatformService] Final params toString:', finalParams.toString());
|
||||
console.log('[usePlatformService] Final params constructor:', finalParams.constructor?.name);
|
||||
|
||||
|
||||
console.log("[usePlatformService] Final params:", finalParams);
|
||||
console.log(
|
||||
"[usePlatformService] Final params toString:",
|
||||
finalParams.toString(),
|
||||
);
|
||||
console.log(
|
||||
"[usePlatformService] Final params constructor:",
|
||||
finalParams.constructor?.name,
|
||||
);
|
||||
|
||||
return finalParams;
|
||||
} catch (error) {
|
||||
console.error('[usePlatformService] Serialization error:', error);
|
||||
console.error("[usePlatformService] Serialization error:", error);
|
||||
// Fallback: manual extraction
|
||||
const fallbackParams: unknown[] = [];
|
||||
for (let i = 0; i < params.length; i++) {
|
||||
@@ -133,11 +142,16 @@ export function usePlatformService() {
|
||||
const value = params[i];
|
||||
fallbackParams.push(value);
|
||||
} catch (accessError) {
|
||||
console.error('[usePlatformService] Access error for param', i, ':', accessError);
|
||||
console.error(
|
||||
"[usePlatformService] Access error for param",
|
||||
i,
|
||||
":",
|
||||
accessError,
|
||||
);
|
||||
fallbackParams.push(String(params[i]));
|
||||
}
|
||||
}
|
||||
console.log('[usePlatformService] Fallback params:', fallbackParams);
|
||||
console.log("[usePlatformService] Fallback params:", fallbackParams);
|
||||
return fallbackParams;
|
||||
}
|
||||
};
|
||||
@@ -194,12 +208,12 @@ export function usePlatformService() {
|
||||
const capabilities = service.getCapabilities();
|
||||
return !capabilities.isNativeApp;
|
||||
};
|
||||
|
||||
|
||||
const isCapacitor = () => {
|
||||
const capabilities = service.getCapabilities();
|
||||
return capabilities.isNativeApp && capabilities.isMobile;
|
||||
};
|
||||
|
||||
|
||||
const isElectron = () => {
|
||||
const capabilities = service.getCapabilities();
|
||||
return capabilities.isNativeApp && !capabilities.isMobile;
|
||||
@@ -263,9 +277,13 @@ export function usePlatformService() {
|
||||
* @param showBlocked Whether to include blocked contacts
|
||||
* @returns Promise<Contact[]> Filtered contacts
|
||||
*/
|
||||
const getContactsWithFilter = async (showBlocked = true): Promise<Contact[]> => {
|
||||
const getContactsWithFilter = async (
|
||||
showBlocked = true,
|
||||
): Promise<Contact[]> => {
|
||||
const contacts = await getContacts();
|
||||
return showBlocked ? contacts : contacts.filter(c => c.iViewContent !== false);
|
||||
return showBlocked
|
||||
? contacts
|
||||
: contacts.filter((c) => c.iViewContent !== false);
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -300,7 +318,10 @@ export function usePlatformService() {
|
||||
*/
|
||||
const getAccount = async (did?: string) => {
|
||||
if (!did) return null;
|
||||
const result = await dbQuery("SELECT * FROM accounts WHERE did = ? LIMIT 1", [did]);
|
||||
const result = await dbQuery(
|
||||
"SELECT * FROM accounts WHERE did = ? LIMIT 1",
|
||||
[did],
|
||||
);
|
||||
const mappedResults = databaseUtil.mapQueryResultToValues(result);
|
||||
return mappedResults.length > 0 ? mappedResults[0] : null;
|
||||
};
|
||||
@@ -311,19 +332,22 @@ export function usePlatformService() {
|
||||
*/
|
||||
const logActivity = async (message: string) => {
|
||||
const timestamp = new Date().toISOString();
|
||||
await dbExec("INSERT INTO logs (date, message) VALUES (?, ?)", [timestamp, message]);
|
||||
await dbExec("INSERT INTO logs (date, message) VALUES (?, ?)", [
|
||||
timestamp,
|
||||
message,
|
||||
]);
|
||||
};
|
||||
|
||||
return {
|
||||
// Direct service access (for advanced use cases)
|
||||
platform: readonly(platformService),
|
||||
isInitialized: readonly(isInitialized),
|
||||
|
||||
|
||||
// Database operations (low-level)
|
||||
dbQuery,
|
||||
dbExec,
|
||||
dbExec,
|
||||
dbGetOneRow,
|
||||
|
||||
|
||||
// Database operations (high-level)
|
||||
getContacts,
|
||||
getContactsWithFilter,
|
||||
@@ -332,34 +356,34 @@ export function usePlatformService() {
|
||||
saveDidSettings,
|
||||
getAccount,
|
||||
logActivity,
|
||||
|
||||
|
||||
// Media operations
|
||||
takePicture,
|
||||
pickImage,
|
||||
rotateCamera,
|
||||
|
||||
|
||||
// Platform detection
|
||||
isWeb,
|
||||
isCapacitor,
|
||||
isCapacitor,
|
||||
isElectron,
|
||||
getCapabilities,
|
||||
|
||||
|
||||
// File operations
|
||||
readFile,
|
||||
writeFile,
|
||||
deleteFile,
|
||||
listFiles,
|
||||
writeAndShareFile,
|
||||
|
||||
|
||||
// Navigation
|
||||
handleDeepLink,
|
||||
|
||||
|
||||
// Raw service access for cases not covered above
|
||||
service
|
||||
service,
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Type helper for the composable return type
|
||||
*/
|
||||
export type PlatformServiceComposable = ReturnType<typeof usePlatformService>;
|
||||
export type PlatformServiceComposable = ReturnType<typeof usePlatformService>;
|
||||
|
||||
Reference in New Issue
Block a user