Browse Source

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
pull/142/head
Matthew Raymer 3 days ago
parent
commit
be61ba1bce
  1. 60
      src/components/TopMessage.vue
  2. 15
      src/db/databaseUtil.ts
  3. 8
      src/main.web.ts
  4. 4
      src/registerSQLWorker.js
  5. 6
      src/services/AbsurdSqlDatabaseService.ts
  6. 5
      src/services/PlatformService.ts
  7. 6
      src/services/PlatformServiceFactory.ts
  8. 162
      src/services/platforms/CapacitorPlatformService.ts
  9. 26
      src/services/platforms/WebPlatformService.ts
  10. 136
      src/utils/PlatformServiceMixin.ts
  11. 70
      src/utils/usePlatformService.ts

60
src/components/TopMessage.vue

@ -18,11 +18,23 @@ import { Component, Vue, Prop } from "vue-facing-decorator";
import { AppString, NotificationIface } from "../constants/app";
import { MASTER_SETTINGS_KEY } from "../db/tables/settings";
import { DEFAULT_ENDORSER_API_SERVER } from "../constants/app";
import { usePlatformService } from "../utils/usePlatformService";
import {
PlatformServiceMixin,
IPlatformServiceMixin,
} from "../utils/PlatformServiceMixin";
import { mapColumnsToValues, parseJsonField } from "../db/databaseUtil";
@Component
@Component({
mixins: [PlatformServiceMixin],
})
export default class TopMessage extends Vue {
// NOTE: This component uses PlatformServiceMixin which provides:
// - this.dbQuery(), this.dbExec(), this.dbGetOneRow() methods
// - this.platformService computed property
// - this.isCapacitor, this.isWeb, this.isElectron computed properties
// - this.capabilities computed property
// TypeScript requires (this as any) for mixin methods due to compile-time limitations
$notify!: (notification: NotificationIface, timeout?: number) => void;
@Prop selected = "";
@ -59,24 +71,24 @@ export default class TopMessage extends Vue {
}
/**
* Get settings for the active account using the platform service composable.
* This replaces the direct call to databaseUtil.retrieveSettingsForActiveAccount()
* and demonstrates the new composable pattern.
* Get settings for the active account using the platform service mixin.
* This demonstrates the concise mixin pattern with direct database access.
*/
private async getActiveAccountSettings() {
const { dbQuery } = usePlatformService();
// Declare defaultSettings outside try block for proper scope
let defaultSettings;
try {
// Get default settings first
const defaultSettings = await this.getDefaultSettings();
defaultSettings = await this.getDefaultSettings();
// If no active DID, return defaults
if (!defaultSettings.activeDid) {
return defaultSettings;
}
// Get account-specific settings using the composable
const result = await dbQuery(
// Get account-specific settings using the mixin (much more concise!)
const result = await (this as any).dbQuery(
"SELECT * FROM settings WHERE accountDid = ?",
[defaultSettings.activeDid],
);
@ -105,22 +117,29 @@ export default class TopMessage extends Vue {
return settings;
} catch (error) {
console.error(`Failed to retrieve account settings for ${defaultSettings.activeDid}:`, error);
return defaultSettings;
console.error(
`Failed to retrieve account settings for ${defaultSettings?.activeDid}:`,
error,
);
return (
defaultSettings || {
id: MASTER_SETTINGS_KEY,
activeDid: undefined,
apiServer: DEFAULT_ENDORSER_API_SERVER,
}
);
}
}
/**
* Get default settings using the platform service composable
* Get default settings using the platform service mixin
*/
private async getDefaultSettings() {
const { dbQuery } = usePlatformService();
try {
const result = await dbQuery(
"SELECT * FROM settings WHERE id = ?",
[MASTER_SETTINGS_KEY],
);
// Direct database access via mixin - no destructuring needed!
const result = await (this as any).dbQuery("SELECT * FROM settings WHERE id = ?", [
MASTER_SETTINGS_KEY,
]);
if (!result?.values?.length) {
return {
@ -130,7 +149,10 @@ export default class TopMessage extends Vue {
};
}
const settings = mapColumnsToValues(result.columns, result.values)[0] as any;
const settings = mapColumnsToValues(
result.columns,
result.values,
)[0] as any;
// Handle searchBoxes parsing
if (settings.searchBoxes) {

15
src/db/databaseUtil.ts

@ -210,12 +210,21 @@ export async function logToDb(
const sevenDaysAgo = new Date(
new Date().getTime() - 7 * 24 * 60 * 60 * 1000,
).toDateString(); // Use date string to match schema
memoryLogs = memoryLogs.filter((log) => log.split(" ")[0] > sevenDaysAgo);
await platform.dbExec("DELETE FROM logs WHERE date < ?", [sevenDaysAgo]);
memoryLogs = memoryLogs.filter(
(log) => log.split(" ")[0] > sevenDaysAgo,
);
await platform.dbExec("DELETE FROM logs WHERE date < ?", [
sevenDaysAgo,
]);
lastCleanupDate = todayKey;
}
} catch (error) {
console.error("Error logging to database:", error, " ... for original message:", message);
console.error(
"Error logging to database:",
error,
" ... for original message:",
message,
);
}
} finally {
// Always reset the flag to prevent permanent blocking of database logging

8
src/main.web.ts

@ -5,9 +5,13 @@ const platform = process.env.VITE_PLATFORM;
const pwa_enabled = process.env.VITE_PWA_ENABLED === "true";
// Debug: Check SharedArrayBuffer availability
console.log(`[SharedArrayBuffer] Available: ${typeof SharedArrayBuffer !== 'undefined'}`);
console.log(
`[SharedArrayBuffer] Available: ${typeof SharedArrayBuffer !== "undefined"}`,
);
console.log(`[Browser] User Agent: ${navigator.userAgent}`);
console.log(`[Headers] Check COOP/COEP in Network tab if SharedArrayBuffer is false`);
console.log(
`[Headers] Check COOP/COEP in Network tab if SharedArrayBuffer is false`,
);
// Only import service worker for web builds
if (pwa_enabled) {

4
src/registerSQLWorker.js

@ -29,7 +29,9 @@ let databaseService = null;
async function getDatabaseService() {
if (!databaseService) {
// Dynamic import to prevent circular dependency
const { default: service } = await import("./services/AbsurdSqlDatabaseService");
const { default: service } = await import(
"./services/AbsurdSqlDatabaseService"
);
databaseService = service;
}
return databaseService;

6
src/services/AbsurdSqlDatabaseService.ts

@ -59,7 +59,7 @@ class AbsurdSqlDatabaseService implements DatabaseService {
await this.initializationPromise;
} catch (error) {
// logger.error(`AbsurdSqlDatabaseService initialize method failed:`, error); // DISABLED
console.error(`AbsurdSqlDatabaseService initialize method failed:`, error);
logger.error(`AbsurdSqlDatabaseService initialize method failed:`, error);
this.initializationPromise = null; // Reset on failure
throw error;
}
@ -153,7 +153,7 @@ class AbsurdSqlDatabaseService implements DatabaseService {
// " ... with params:",
// operation.params,
// );
console.error(
logger.error(
"Error while processing SQL queue:",
error,
" ... for sql:",
@ -208,7 +208,7 @@ class AbsurdSqlDatabaseService implements DatabaseService {
// logger.error( // DISABLED
// `Database not properly initialized after await waitForInitialization() - initialized flag is true but db is null`,
// );
console.error(
logger.error(
`Database not properly initialized after await waitForInitialization() - initialized flag is true but db is null`,
);
throw new Error(

5
src/services/PlatformService.ts

@ -137,8 +137,5 @@ export interface PlatformService {
* @param params - The parameters to pass to the query
* @returns Promise resolving to the first row as an array, or undefined if no results
*/
dbGetOneRow(
sql: string,
params?: unknown[],
): Promise<unknown[] | undefined>;
dbGetOneRow(sql: string, params?: unknown[]): Promise<unknown[] | undefined>;
}

6
src/services/PlatformServiceFactory.ts

@ -40,7 +40,9 @@ export class PlatformServiceFactory {
const platform = process.env.VITE_PLATFORM || "web";
if (!PlatformServiceFactory.creationLogged) {
console.log(`[PlatformServiceFactory] Creating singleton instance for platform: ${platform}`);
console.log(
`[PlatformServiceFactory] Creating singleton instance for platform: ${platform}`,
);
PlatformServiceFactory.creationLogged = true;
}
@ -63,7 +65,7 @@ export class PlatformServiceFactory {
public static getStats(): { callCount: number; instanceExists: boolean } {
return {
callCount: PlatformServiceFactory.callCount,
instanceExists: PlatformServiceFactory.instance !== null
instanceExists: PlatformServiceFactory.instance !== null,
};
}
}

162
src/services/platforms/CapacitorPlatformService.ts

@ -187,7 +187,10 @@ export class CapacitorPlatformService implements PlatformService {
params: unknown[] = [],
): Promise<R> {
// Log incoming parameters for debugging (HIGH PRIORITY)
logger.warn(`[CapacitorPlatformService] queueOperation - SQL: ${sql}, Params:`, params);
logger.warn(
`[CapacitorPlatformService] queueOperation - SQL: ${sql}, Params:`,
params,
);
// Convert parameters to SQLite-compatible types with robust serialization
const convertedParams = params.map((param, index) => {
@ -196,50 +199,74 @@ export class CapacitorPlatformService implements PlatformService {
}
if (typeof param === "object" && param !== null) {
// Enhanced debug logging for all objects (HIGH PRIORITY)
logger.warn(`[CapacitorPlatformService] Object param at index ${index}:`, {
logger.warn(
`[CapacitorPlatformService] Object param at index ${index}:`,
{
type: typeof param,
toString: param.toString(),
constructorName: param.constructor?.name,
isArray: Array.isArray(param),
keys: Object.keys(param),
stringRep: String(param)
});
stringRep: String(param),
},
);
// Special handling for Proxy objects (common cause of "An object could not be cloned")
const isProxy = this.isProxyObject(param);
logger.warn(`[CapacitorPlatformService] isProxy result for index ${index}:`, isProxy);
logger.warn(
`[CapacitorPlatformService] isProxy result for index ${index}:`,
isProxy,
);
// AGGRESSIVE: If toString contains "Proxy", treat as Proxy even if isProxyObject returns false
const stringRep = String(param);
const forceProxyDetection = stringRep.includes('Proxy(') || stringRep.startsWith('Proxy');
logger.warn(`[CapacitorPlatformService] Force proxy detection for index ${index}:`, forceProxyDetection);
const forceProxyDetection =
stringRep.includes("Proxy(") || stringRep.startsWith("Proxy");
logger.warn(
`[CapacitorPlatformService] Force proxy detection for index ${index}:`,
forceProxyDetection,
);
if (isProxy || forceProxyDetection) {
logger.warn(`[CapacitorPlatformService] Proxy object detected at index ${index} (method: ${isProxy ? 'isProxyObject' : 'stringDetection'}), toString: ${stringRep}`);
logger.warn(
`[CapacitorPlatformService] Proxy object detected at index ${index} (method: ${isProxy ? "isProxyObject" : "stringDetection"}), toString: ${stringRep}`,
);
try {
// AGGRESSIVE EXTRACTION: Try multiple methods to extract actual values
if (Array.isArray(param)) {
// Method 1: Array.from() to extract from Proxy(Array)
const actualArray = Array.from(param);
logger.info(`[CapacitorPlatformService] Extracted array from Proxy via Array.from():`, actualArray);
logger.info(
`[CapacitorPlatformService] Extracted array from Proxy via Array.from():`,
actualArray,
);
// Method 2: Manual element extraction for safety
const manualArray: unknown[] = [];
for (let i = 0; i < param.length; i++) {
manualArray.push(param[i]);
}
logger.info(`[CapacitorPlatformService] Manual array extraction:`, manualArray);
logger.info(
`[CapacitorPlatformService] Manual array extraction:`,
manualArray,
);
// Use the manual extraction as it's more reliable
return manualArray;
} else {
// For Proxy(Object), try to extract actual object
const actualObject = Object.assign({}, param);
logger.info(`[CapacitorPlatformService] Extracted object from Proxy:`, actualObject);
logger.info(
`[CapacitorPlatformService] Extracted object from Proxy:`,
actualObject,
);
return actualObject;
}
} catch (proxyError) {
logger.error(`[CapacitorPlatformService] Failed to extract from Proxy at index ${index}:`, proxyError);
logger.error(
`[CapacitorPlatformService] Failed to extract from Proxy at index ${index}:`,
proxyError,
);
// FALLBACK: Try to extract primitive values manually
if (Array.isArray(param)) {
@ -248,10 +275,16 @@ export class CapacitorPlatformService implements PlatformService {
for (let i = 0; i < param.length; i++) {
fallbackArray.push(param[i]);
}
logger.info(`[CapacitorPlatformService] Fallback array extraction successful:`, fallbackArray);
logger.info(
`[CapacitorPlatformService] Fallback array extraction successful:`,
fallbackArray,
);
return fallbackArray;
} catch (fallbackError) {
logger.error(`[CapacitorPlatformService] Fallback array extraction failed:`, fallbackError);
logger.error(
`[CapacitorPlatformService] Fallback array extraction failed:`,
fallbackError,
);
return `[Proxy Array - Could not extract]`;
}
}
@ -264,14 +297,20 @@ export class CapacitorPlatformService implements PlatformService {
return JSON.stringify(param);
} catch (error) {
// Handle non-serializable objects
logger.error(`[CapacitorPlatformService] Failed to serialize parameter at index ${index}:`, error);
logger.error(`[CapacitorPlatformService] Problematic parameter:`, param);
logger.error(
`[CapacitorPlatformService] Failed to serialize parameter at index ${index}:`,
error,
);
logger.error(
`[CapacitorPlatformService] Problematic parameter:`,
param,
);
// Fallback: Convert to string representation
if (Array.isArray(param)) {
return `[Array(${param.length})]`;
}
return `[Object ${param.constructor?.name || 'Unknown'}]`;
return `[Object ${param.constructor?.name || "Unknown"}]`;
}
}
if (typeof param === "boolean") {
@ -280,12 +319,16 @@ export class CapacitorPlatformService implements PlatformService {
}
if (typeof param === "function") {
// Functions can't be serialized - convert to string representation
logger.warn(`[CapacitorPlatformService] Function parameter detected and converted to string at index ${index}`);
return `[Function ${param.name || 'Anonymous'}]`;
logger.warn(
`[CapacitorPlatformService] Function parameter detected and converted to string at index ${index}`,
);
return `[Function ${param.name || "Anonymous"}]`;
}
if (typeof param === "symbol") {
// Symbols can't be serialized - convert to string representation
logger.warn(`[CapacitorPlatformService] Symbol parameter detected and converted to string at index ${index}`);
logger.warn(
`[CapacitorPlatformService] Symbol parameter detected and converted to string at index ${index}`,
);
return param.toString();
}
// Numbers, strings, bigints are supported, but ensure bigints are converted to strings
@ -296,7 +339,10 @@ export class CapacitorPlatformService implements PlatformService {
});
// Log converted parameters for debugging (HIGH PRIORITY)
logger.warn(`[CapacitorPlatformService] Converted params:`, convertedParams);
logger.warn(
`[CapacitorPlatformService] Converted params:`,
convertedParams,
);
return new Promise<R>((resolve, reject) => {
// Create completely plain objects that Vue cannot make reactive
@ -316,9 +362,18 @@ export class CapacitorPlatformService implements PlatformService {
Object.freeze(operation);
// Add enhanced logging to verify our fix
logger.warn(`[CapacitorPlatformService] Final operation.params type:`, typeof operation.params);
logger.warn(`[CapacitorPlatformService] Final operation.params toString:`, operation.params.toString());
logger.warn(`[CapacitorPlatformService] Final operation.params constructor:`, operation.params.constructor?.name);
logger.warn(
`[CapacitorPlatformService] Final operation.params type:`,
typeof operation.params,
);
logger.warn(
`[CapacitorPlatformService] Final operation.params toString:`,
operation.params.toString(),
);
logger.warn(
`[CapacitorPlatformService] Final operation.params constructor:`,
operation.params.constructor?.name,
);
this.operationQueue.push(operation);
@ -367,33 +422,42 @@ export class CapacitorPlatformService implements PlatformService {
try {
// Method 1: Check toString representation
const objString = obj.toString();
if (objString.includes('Proxy(') || objString.startsWith('Proxy')) {
logger.debug("[CapacitorPlatformService] Proxy detected via toString:", objString);
if (objString.includes("Proxy(") || objString.startsWith("Proxy")) {
logger.debug(
"[CapacitorPlatformService] Proxy detected via toString:",
objString,
);
return true;
}
// Method 2: Check constructor name
const constructorName = obj.constructor?.name;
if (constructorName === 'Proxy') {
logger.debug("[CapacitorPlatformService] Proxy detected via constructor name");
if (constructorName === "Proxy") {
logger.debug(
"[CapacitorPlatformService] Proxy detected via constructor name",
);
return true;
}
// Method 3: Check Object.prototype.toString
const objToString = Object.prototype.toString.call(obj);
if (objToString.includes('Proxy')) {
logger.debug("[CapacitorPlatformService] Proxy detected via Object.prototype.toString");
if (objToString.includes("Proxy")) {
logger.debug(
"[CapacitorPlatformService] Proxy detected via Object.prototype.toString",
);
return true;
}
// Method 4: Vue/Reactive Proxy detection - check for __v_ properties
if (typeof obj === 'object' && obj !== null) {
if (typeof obj === "object" && obj !== null) {
// Check for Vue reactive proxy indicators
const hasVueProxy = Object.getOwnPropertyNames(obj).some(prop =>
prop.startsWith('__v_') || prop.startsWith('__r_')
const hasVueProxy = Object.getOwnPropertyNames(obj).some(
(prop) => prop.startsWith("__v_") || prop.startsWith("__r_"),
);
if (hasVueProxy) {
logger.debug("[CapacitorPlatformService] Vue reactive Proxy detected");
logger.debug(
"[CapacitorPlatformService] Vue reactive Proxy detected",
);
return true;
}
}
@ -401,15 +465,24 @@ export class CapacitorPlatformService implements PlatformService {
// Method 5: Try JSON.stringify and check for Proxy in error or result
try {
const jsonString = JSON.stringify(obj);
if (jsonString.includes('Proxy')) {
logger.debug("[CapacitorPlatformService] Proxy detected in JSON serialization");
if (jsonString.includes("Proxy")) {
logger.debug(
"[CapacitorPlatformService] Proxy detected in JSON serialization",
);
return true;
}
} catch (jsonError) {
// If JSON.stringify fails, it might be a non-serializable Proxy
const errorMessage = jsonError instanceof Error ? jsonError.message : String(jsonError);
if (errorMessage.includes('Proxy') || errorMessage.includes('circular') || errorMessage.includes('clone')) {
logger.debug("[CapacitorPlatformService] Proxy detected via JSON serialization error");
const errorMessage =
jsonError instanceof Error ? jsonError.message : String(jsonError);
if (
errorMessage.includes("Proxy") ||
errorMessage.includes("circular") ||
errorMessage.includes("clone")
) {
logger.debug(
"[CapacitorPlatformService] Proxy detected via JSON serialization error",
);
return true;
}
}
@ -417,7 +490,10 @@ export class CapacitorPlatformService implements PlatformService {
return false;
} catch (error) {
// If we can't inspect the object, it might be a Proxy causing issues
logger.warn("[CapacitorPlatformService] Could not inspect object for Proxy detection:", error);
logger.warn(
"[CapacitorPlatformService] Could not inspect object for Proxy detection:",
error,
);
return true; // Assume it's a Proxy if we can't inspect it
}
}
@ -1268,7 +1344,11 @@ export class CapacitorPlatformService implements PlatformService {
params?: unknown[],
): Promise<unknown[] | undefined> {
await this.waitForInitialization();
const result = await this.queueOperation<QueryExecResult>("query", sql, params || []);
const result = await this.queueOperation<QueryExecResult>(
"query",
sql,
params || [],
);
// Return the first row from the result, or undefined if no results
if (result && result.values && result.values.length > 0) {

26
src/services/platforms/WebPlatformService.ts

@ -50,7 +50,9 @@ export class WebPlatformService implements PlatformService {
// Only warn if multiple instances (which shouldn't happen with singleton)
if (WebPlatformService.instanceCount > 1) {
console.error(`[WebPlatformService] ERROR: Multiple instances created! Count: ${WebPlatformService.instanceCount}`);
console.error(
`[WebPlatformService] ERROR: Multiple instances created! Count: ${WebPlatformService.instanceCount}`,
);
} else {
console.log(`[WebPlatformService] Initializing web platform service`);
}
@ -74,19 +76,26 @@ export class WebPlatformService implements PlatformService {
// This is required for Safari compatibility with nested workers
// It installs a handler that proxies web worker creation through the main thread
// CRITICAL: Only call initBackend from main thread, not from worker context
const isMainThread = typeof window !== 'undefined';
const isMainThread = typeof window !== "undefined";
if (isMainThread) {
// We're in the main thread - safe to dynamically import and call initBackend
try {
const { initBackend } = await import("absurd-sql/dist/indexeddb-main-thread");
const { initBackend } = await import(
"absurd-sql/dist/indexeddb-main-thread"
);
initBackend(this.worker);
} catch (error) {
console.error("[WebPlatformService] Failed to import/call initBackend:", error);
console.error(
"[WebPlatformService] Failed to import/call initBackend:",
error,
);
throw error;
}
} else {
// We're in a worker context - skip initBackend call
console.log("[WebPlatformService] Skipping initBackend call in worker context");
console.log(
"[WebPlatformService] Skipping initBackend call in worker context",
);
}
this.worker.onmessage = (event) => {
@ -120,13 +129,16 @@ export class WebPlatformService implements PlatformService {
const { id, type } = message;
// Handle absurd-sql internal messages (these are normal, don't log)
if (!id && message.type?.startsWith('__absurd:')) {
if (!id && message.type?.startsWith("__absurd:")) {
return; // Internal absurd-sql message, ignore silently
}
if (!id) {
// logger.warn("[WebPlatformService] Received message without ID:", message); // DISABLED
console.warn("[WebPlatformService] Received message without ID:", message);
console.warn(
"[WebPlatformService] Received message without ID:",
message,
);
return;
}

136
src/utils/PlatformServiceMixin.ts

@ -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;
}

70
src/utils/usePlatformService.ts

@ -18,11 +18,11 @@
* @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
@ -86,28 +86,31 @@ 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 {
@ -118,13 +121,19 @@ export function usePlatformService() {
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;
}
};
@ -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,7 +332,10 @@ 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 {
@ -355,7 +379,7 @@ export function usePlatformService() {
handleDeepLink,
// Raw service access for cases not covered above
service
service,
};
}

Loading…
Cancel
Save