-
- {{ decryptionErrorMessage() }}
-
-
-
- You are not currently admitted by the organizer.
-
-
- Your name is not set, so others may not recognize you. Reload this page
- to set it.
+
+
+
+
+
-
-
- Click
-
+
+
+
+ {{ decryptionErrorMessage() }}
+
+
+
+ You are not currently admitted by the organizer.
+
+
+ Your name is not set, so others may not recognize you. Reload this
+ page to set it.
+
+
+
+
-
-
- /
-
+ /
+
+ to add/remove them to/from the meeting.
+
+
-
-
- to add/remove them to/from the meeting.
-
-
- Click
-
-
-
- to add them to your contacts.
-
-
-
-
-
-
-
- Refresh
- ({{ countdownTimer }}s)
-
-
-
-
+
+ Refresh
+ ({{ countdownTimer }}s)
+
+
+
-
-
-
- {{ member.name || unnamedMember }}
-
-
+
+
+
+
+
+
+ {{ member.name || unnamedMember }}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
-
+
-
+
-
+
-
-
-
-
-
-
-
-
-
-
-
- {{ member.did }}
-
-
-
-
-
-
-
-
- Refresh
- ({{ countdownTimer }}s)
-
+
+
+ Refresh
+ ({{ countdownTimer }}s)
+
+
+
+
+ No members have joined this meeting yet
+
-
-
- No members have joined this meeting yet
-
-
-
-
+
+
+
diff --git a/src/constants/notifications.ts b/src/constants/notifications.ts
index 5cc75bd4..33d36e5d 100644
--- a/src/constants/notifications.ts
+++ b/src/constants/notifications.ts
@@ -510,14 +510,6 @@ export const NOTIFY_REGISTER_CONTACT = {
text: "Do you want to register them?",
};
-// Used in: ContactsView.vue (showOnboardMeetingDialog method - complex modal for onboarding meeting)
-export const NOTIFY_ONBOARDING_MEETING = {
- title: "Onboarding Meeting",
- text: "Would you like to start a new meeting?",
- yesText: "Start New Meeting",
- noText: "Join Existing Meeting",
-};
-
// TestView.vue specific constants
// Used in: TestView.vue (executeSql method - SQL error handling)
export const NOTIFY_SQL_ERROR = {
diff --git a/src/interfaces/common.ts b/src/interfaces/common.ts
index f1f172e2..ec5226a7 100644
--- a/src/interfaces/common.ts
+++ b/src/interfaces/common.ts
@@ -70,15 +70,6 @@ export interface AxiosErrorResponse {
[key: string]: unknown;
}
-export interface UserInfo {
- did: string;
- name: string;
- publicEncKey: string;
- registered: boolean;
- profileImageUrl?: string;
- nextPublicEncKeyHash?: string;
-}
-
export interface CreateAndSubmitClaimResult {
success: boolean;
embeddedRecordError?: string;
diff --git a/src/interfaces/index.ts b/src/interfaces/index.ts
index c4b12191..8197df86 100644
--- a/src/interfaces/index.ts
+++ b/src/interfaces/index.ts
@@ -4,3 +4,4 @@ export * from "./common";
export * from "./deepLinks";
export * from "./limits";
export * from "./records";
+export * from "./user";
diff --git a/src/interfaces/user.ts b/src/interfaces/user.ts
index a79d6a9c..c290dcb6 100644
--- a/src/interfaces/user.ts
+++ b/src/interfaces/user.ts
@@ -6,3 +6,12 @@ export interface UserInfo {
profileImageUrl?: string;
nextPublicEncKeyHash?: string;
}
+
+export interface MemberData {
+ did: string;
+ name: string;
+ isContact: boolean;
+ member: {
+ memberId: string;
+ };
+}
diff --git a/src/libs/endorserServer.ts b/src/libs/endorserServer.ts
index 08a65934..36bfa223 100644
--- a/src/libs/endorserServer.ts
+++ b/src/libs/endorserServer.ts
@@ -42,9 +42,6 @@ import {
PlanActionClaim,
RegisterActionClaim,
TenureClaim,
-} from "../interfaces/claims";
-
-import {
GenericCredWrapper,
GenericVerifiableCredential,
AxiosErrorResponse,
@@ -55,14 +52,12 @@ import {
QuantitativeValue,
KeyMetaWithPrivate,
KeyMetaMaybeWithPrivate,
-} from "../interfaces/common";
-import {
OfferSummaryRecord,
OfferToPlanSummaryRecord,
PlanSummaryAndPreviousClaim,
PlanSummaryRecord,
-} from "../interfaces/records";
-import { logger } from "../utils/logger";
+} from "../interfaces";
+import { logger, safeStringify } from "../utils/logger";
import { PlatformServiceFactory } from "@/services/PlatformServiceFactory";
import { APP_SERVER } from "@/constants/app";
import { SOMEONE_UNNAMED } from "@/constants/entities";
@@ -702,7 +697,7 @@ export function serverMessageForUser(error: unknown): string | undefined {
export function errorStringForLog(error: unknown) {
let stringifiedError = "" + error;
try {
- stringifiedError = JSON.stringify(error);
+ stringifiedError = safeStringify(error);
} catch (e) {
// can happen with Dexie, eg:
// TypeError: Converting circular structure to JSON
@@ -714,7 +709,7 @@ export function errorStringForLog(error: unknown) {
if (error && typeof error === "object" && "response" in error) {
const err = error as AxiosErrorResponse;
- const errorResponseText = JSON.stringify(err.response);
+ const errorResponseText = safeStringify(err.response);
// for some reason, error.response is not included in stringify result (eg. for 400 errors on invite redemptions)
if (!R.empty(errorResponseText) && !fullError.includes(errorResponseText)) {
// add error.response stuff
@@ -724,7 +719,7 @@ export function errorStringForLog(error: unknown) {
R.equals(err.config, err.response.config)
) {
// but exclude "config" because it's already in there
- const newErrorResponseText = JSON.stringify(
+ const newErrorResponseText = safeStringify(
R.omit(["config"] as never[], err.response),
);
fullError +=
@@ -1662,30 +1657,35 @@ export async function register(
message?: string;
}>(url, { jwtEncoded: vcJwt });
- if (resp.data?.success?.handleId) {
- return { success: true };
- } else if (resp.data?.success?.embeddedRecordError) {
+ if (resp.data?.success?.embeddedRecordError) {
let message =
"There was some problem with the registration and so it may not be complete.";
if (typeof resp.data.success.embeddedRecordError === "string") {
message += " " + resp.data.success.embeddedRecordError;
}
return { error: message };
+ } else if (resp.data?.success?.handleId) {
+ return { success: true };
} else {
- logger.error("Registration error:", JSON.stringify(resp.data));
- return { error: "Got a server error when registering." };
+ logger.error("Registration non-thrown error:", JSON.stringify(resp.data));
+ return {
+ error:
+ (resp.data?.error as { message?: string })?.message ||
+ (resp.data?.error as string) ||
+ "Got a server error when registering.",
+ };
}
} catch (error: unknown) {
if (error && typeof error === "object") {
const err = error as AxiosErrorResponse;
const errorMessage =
- err.message ||
- (err.response?.data &&
- typeof err.response.data === "object" &&
- "message" in err.response.data
- ? (err.response.data as { message: string }).message
- : undefined);
- logger.error("Registration error:", errorMessage || JSON.stringify(err));
+ err.response?.data?.error?.message ||
+ err.response?.data?.error ||
+ err.message;
+ logger.error(
+ "Registration thrown error:",
+ errorMessage || JSON.stringify(err),
+ );
return { error: errorMessage || "Got a server error when registering." };
}
return { error: "Got a server error when registering." };
diff --git a/src/libs/fontawesome.ts b/src/libs/fontawesome.ts
index efd8ff03..947833e6 100644
--- a/src/libs/fontawesome.ts
+++ b/src/libs/fontawesome.ts
@@ -29,6 +29,7 @@ import {
faCircle,
faCircleCheck,
faCircleInfo,
+ faCircleMinus,
faCirclePlus,
faCircleQuestion,
faCircleRight,
@@ -37,6 +38,7 @@ import {
faCoins,
faComment,
faCopy,
+ faCrown,
faDollar,
faDownload,
faEllipsis,
@@ -58,6 +60,7 @@ import {
faHand,
faHandHoldingDollar,
faHandHoldingHeart,
+ faHourglassHalf,
faHouseChimney,
faImage,
faImagePortrait,
@@ -123,6 +126,7 @@ library.add(
faCircle,
faCircleCheck,
faCircleInfo,
+ faCircleMinus,
faCirclePlus,
faCircleQuestion,
faCircleRight,
@@ -131,6 +135,7 @@ library.add(
faCoins,
faComment,
faCopy,
+ faCrown,
faDollar,
faDownload,
faEllipsis,
@@ -152,6 +157,7 @@ library.add(
faHand,
faHandHoldingDollar,
faHandHoldingHeart,
+ faHourglassHalf,
faHouseChimney,
faImage,
faImagePortrait,
diff --git a/src/libs/util.ts b/src/libs/util.ts
index 72dbf164..4790714d 100644
--- a/src/libs/util.ts
+++ b/src/libs/util.ts
@@ -988,11 +988,6 @@ export async function importFromMnemonic(
): Promise
{
const mne: string = mnemonic.trim().toLowerCase();
- // Check if this is Test User #0
- const TEST_USER_0_MNEMONIC =
- "rigid shrug mobile smart veteran half all pond toilet brave review universe ship congress found yard skate elite apology jar uniform subway slender luggage";
- const isTestUser0 = mne === TEST_USER_0_MNEMONIC;
-
// Derive address and keys from mnemonic
const [address, privateHex, publicHex] = deriveAddress(mne, derivationPath);
@@ -1007,90 +1002,6 @@ export async function importFromMnemonic(
// Save the new identity
await saveNewIdentity(newId, mne, derivationPath);
-
- // Set up Test User #0 specific settings
- if (isTestUser0) {
- // Set up Test User #0 specific settings with enhanced error handling
- const platformService = await getPlatformService();
-
- try {
- // First, ensure the DID-specific settings record exists
- await platformService.insertNewDidIntoSettings(newId.did);
-
- // Then update with Test User #0 specific settings
- await platformService.updateDidSpecificSettings(newId.did, {
- firstName: "User Zero",
- isRegistered: true,
- });
-
- // Verify the settings were saved correctly
- const verificationResult = await platformService.dbQuery(
- "SELECT firstName, isRegistered FROM settings WHERE accountDid = ?",
- [newId.did],
- );
-
- if (verificationResult?.values?.length) {
- const settings = verificationResult.values[0];
- const firstName = settings[0];
- const isRegistered = settings[1];
-
- logger.debug(
- "[importFromMnemonic] Test User #0 settings verification",
- {
- did: newId.did,
- firstName,
- isRegistered,
- expectedFirstName: "User Zero",
- expectedIsRegistered: true,
- },
- );
-
- // If settings weren't saved correctly, try individual updates
- if (firstName !== "User Zero" || isRegistered !== 1) {
- logger.warn(
- "[importFromMnemonic] Test User #0 settings not saved correctly, retrying with individual updates",
- );
-
- await platformService.dbExec(
- "UPDATE settings SET firstName = ? WHERE accountDid = ?",
- ["User Zero", newId.did],
- );
-
- await platformService.dbExec(
- "UPDATE settings SET isRegistered = ? WHERE accountDid = ?",
- [1, newId.did],
- );
-
- // Verify again
- const retryResult = await platformService.dbQuery(
- "SELECT firstName, isRegistered FROM settings WHERE accountDid = ?",
- [newId.did],
- );
-
- if (retryResult?.values?.length) {
- const retrySettings = retryResult.values[0];
- logger.debug(
- "[importFromMnemonic] Test User #0 settings after retry",
- {
- firstName: retrySettings[0],
- isRegistered: retrySettings[1],
- },
- );
- }
- }
- } else {
- logger.error(
- "[importFromMnemonic] Failed to verify Test User #0 settings - no record found",
- );
- }
- } catch (error) {
- logger.error(
- "[importFromMnemonic] Error setting up Test User #0 settings:",
- error,
- );
- // Don't throw - allow the import to continue even if settings fail
- }
- }
}
/**
diff --git a/src/services/platforms/BaseDatabaseService.ts b/src/services/platforms/BaseDatabaseService.ts
new file mode 100644
index 00000000..9f995c13
--- /dev/null
+++ b/src/services/platforms/BaseDatabaseService.ts
@@ -0,0 +1,297 @@
+/**
+ * @fileoverview Base Database Service for Platform Services
+ * @author Matthew Raymer
+ *
+ * This abstract base class provides common database operations that are
+ * identical across all platform implementations. It eliminates code
+ * duplication and ensures consistency in database operations.
+ *
+ * Key Features:
+ * - Common database utility methods
+ * - Consistent settings management
+ * - Active identity management
+ * - Abstract methods for platform-specific database operations
+ *
+ * Architecture:
+ * - Abstract base class with common implementations
+ * - Platform services extend this class
+ * - Platform-specific database operations remain abstract
+ *
+ * @since 1.1.1-beta
+ */
+
+import { logger } from "../../utils/logger";
+import { QueryExecResult } from "@/interfaces/database";
+
+/**
+ * Abstract base class for platform-specific database services.
+ *
+ * This class provides common database operations that are identical
+ * across all platform implementations (Web, Capacitor, Electron).
+ * Platform-specific services extend this class and implement the
+ * abstract database operation methods.
+ *
+ * Common Operations:
+ * - Settings management (update, retrieve, insert)
+ * - Active identity management
+ * - Database utility methods
+ *
+ * @abstract
+ * @example
+ * ```typescript
+ * export class WebPlatformService extends BaseDatabaseService {
+ * async dbQuery(sql: string, params?: unknown[]): Promise {
+ * // Web-specific implementation
+ * }
+ * }
+ * ```
+ */
+export abstract class BaseDatabaseService {
+ /**
+ * Generate an INSERT statement for a model object.
+ *
+ * Creates a parameterized INSERT statement with placeholders for
+ * all properties in the model object. This ensures safe SQL
+ * execution and prevents SQL injection.
+ *
+ * @param model - Object containing the data to insert
+ * @param tableName - Name of the target table
+ * @returns Object containing the SQL statement and parameters
+ *
+ * @example
+ * ```typescript
+ * const { sql, params } = this.generateInsertStatement(
+ * { name: 'John', age: 30 },
+ * 'users'
+ * );
+ * // sql: "INSERT INTO users (name, age) VALUES (?, ?)"
+ * // params: ['John', 30]
+ * ```
+ */
+ generateInsertStatement(
+ model: Record,
+ tableName: string,
+ ): { sql: string; params: unknown[] } {
+ const keys = Object.keys(model);
+ const placeholders = keys.map(() => "?").join(", ");
+ const sql = `INSERT INTO ${tableName} (${keys.join(", ")}) VALUES (${placeholders})`;
+ const params = keys.map((key) => model[key]);
+ return { sql, params };
+ }
+
+ /**
+ * Update default settings for the currently active account.
+ *
+ * Retrieves the active DID from the active_identity table and updates
+ * the corresponding settings record. This ensures settings are always
+ * updated for the correct account.
+ *
+ * @param settings - Object containing the settings to update
+ * @returns Promise that resolves when settings are updated
+ *
+ * @throws {Error} If no active DID is found or database operation fails
+ *
+ * @example
+ * ```typescript
+ * await this.updateDefaultSettings({
+ * theme: 'dark',
+ * notifications: true
+ * });
+ * ```
+ */
+ async updateDefaultSettings(
+ settings: Record,
+ ): Promise {
+ // Get current active DID and update that identity's settings
+ const activeIdentity = await this.getActiveIdentity();
+ const activeDid = activeIdentity.activeDid;
+
+ if (!activeDid) {
+ logger.warn(
+ "[BaseDatabaseService] No active DID found, cannot update default settings",
+ );
+ return;
+ }
+
+ const keys = Object.keys(settings);
+ const setClause = keys.map((key) => `${key} = ?`).join(", ");
+ const sql = `UPDATE settings SET ${setClause} WHERE accountDid = ?`;
+ const params = [...keys.map((key) => settings[key]), activeDid];
+ await this.dbExec(sql, params);
+ }
+
+ /**
+ * Update the active DID in the active_identity table.
+ *
+ * Sets the active DID and updates the lastUpdated timestamp.
+ * This is used when switching between different accounts/identities.
+ *
+ * @param did - The DID to set as active
+ * @returns Promise that resolves when the update is complete
+ *
+ * @example
+ * ```typescript
+ * await this.updateActiveDid('did:example:123');
+ * ```
+ */
+ async updateActiveDid(did: string): Promise {
+ await this.dbExec(
+ "UPDATE active_identity SET activeDid = ?, lastUpdated = datetime('now') WHERE id = 1",
+ [did],
+ );
+ }
+
+ /**
+ * Get the currently active DID from the active_identity table.
+ *
+ * Retrieves the active DID that represents the currently selected
+ * account/identity. This is used throughout the application to
+ * ensure operations are performed on the correct account.
+ *
+ * @returns Promise resolving to object containing the active DID
+ *
+ * @example
+ * ```typescript
+ * const { activeDid } = await this.getActiveIdentity();
+ * console.log('Current active DID:', activeDid);
+ * ```
+ */
+ async getActiveIdentity(): Promise<{ activeDid: string }> {
+ const result = (await this.dbQuery(
+ "SELECT activeDid FROM active_identity WHERE id = 1",
+ )) as QueryExecResult;
+ return {
+ activeDid: (result?.values?.[0]?.[0] as string) || "",
+ };
+ }
+
+ /**
+ * Insert a new DID into the settings table with default values.
+ *
+ * Creates a new settings record for a DID with default configuration
+ * values. Uses INSERT OR REPLACE to handle cases where settings
+ * already exist for the DID.
+ *
+ * @param did - The DID to create settings for
+ * @returns Promise that resolves when settings are created
+ *
+ * @example
+ * ```typescript
+ * await this.insertNewDidIntoSettings('did:example:123');
+ * ```
+ */
+ async insertNewDidIntoSettings(did: string): Promise {
+ // Import constants dynamically to avoid circular dependencies
+ const { DEFAULT_ENDORSER_API_SERVER, DEFAULT_PARTNER_API_SERVER } =
+ await import("@/constants/app");
+
+ // Use INSERT OR REPLACE to handle case where settings already exist for this DID
+ // This prevents duplicate accountDid entries and ensures data integrity
+ await this.dbExec(
+ "INSERT OR REPLACE INTO settings (accountDid, finishedOnboarding, apiServer, partnerApiServer) VALUES (?, ?, ?, ?)",
+ [did, false, DEFAULT_ENDORSER_API_SERVER, DEFAULT_PARTNER_API_SERVER],
+ );
+ }
+
+ /**
+ * Update settings for a specific DID.
+ *
+ * Updates settings for a particular DID rather than the active one.
+ * This is useful for bulk operations or when managing multiple accounts.
+ *
+ * @param did - The DID to update settings for
+ * @param settings - Object containing the settings to update
+ * @returns Promise that resolves when settings are updated
+ *
+ * @example
+ * ```typescript
+ * await this.updateDidSpecificSettings('did:example:123', {
+ * theme: 'light',
+ * notifications: false
+ * });
+ * ```
+ */
+ async updateDidSpecificSettings(
+ did: string,
+ settings: Record,
+ ): Promise {
+ const keys = Object.keys(settings);
+ const setClause = keys.map((key) => `${key} = ?`).join(", ");
+ const sql = `UPDATE settings SET ${setClause} WHERE accountDid = ?`;
+ const params = [...keys.map((key) => settings[key]), did];
+ await this.dbExec(sql, params);
+ }
+
+ /**
+ * Retrieve settings for the currently active account.
+ *
+ * Gets the active DID and retrieves all settings for that account.
+ * Excludes the 'id' column from the returned settings object.
+ *
+ * @returns Promise resolving to settings object or null if no active DID
+ *
+ * @example
+ * ```typescript
+ * const settings = await this.retrieveSettingsForActiveAccount();
+ * if (settings) {
+ * console.log('Theme:', settings.theme);
+ * console.log('Notifications:', settings.notifications);
+ * }
+ * ```
+ */
+ async retrieveSettingsForActiveAccount(): Promise | null> {
+ // Get current active DID from active_identity table
+ const activeIdentity = await this.getActiveIdentity();
+ const activeDid = activeIdentity.activeDid;
+
+ if (!activeDid) {
+ return null;
+ }
+
+ const result = (await this.dbQuery(
+ "SELECT * FROM settings WHERE accountDid = ?",
+ [activeDid],
+ )) as QueryExecResult;
+ if (result?.values?.[0]) {
+ // Convert the row to an object
+ const row = result.values[0];
+ const columns = result.columns || [];
+ const settings: Record = {};
+
+ columns.forEach((column: string, index: number) => {
+ if (column !== "id") {
+ // Exclude the id column
+ settings[column] = row[index];
+ }
+ });
+
+ return settings;
+ }
+ return null;
+ }
+
+ // Abstract methods that must be implemented by platform-specific services
+
+ /**
+ * Execute a database query (SELECT operations).
+ *
+ * @abstract
+ * @param sql - SQL query string
+ * @param params - Optional parameters for prepared statements
+ * @returns Promise resolving to query results
+ */
+ abstract dbQuery(sql: string, params?: unknown[]): Promise;
+
+ /**
+ * Execute a database statement (INSERT, UPDATE, DELETE operations).
+ *
+ * @abstract
+ * @param sql - SQL statement string
+ * @param params - Optional parameters for prepared statements
+ * @returns Promise resolving to execution results
+ */
+ abstract dbExec(sql: string, params?: unknown[]): Promise;
+}
diff --git a/src/services/platforms/CapacitorPlatformService.ts b/src/services/platforms/CapacitorPlatformService.ts
index 2db74656..51fb9ce5 100644
--- a/src/services/platforms/CapacitorPlatformService.ts
+++ b/src/services/platforms/CapacitorPlatformService.ts
@@ -22,6 +22,7 @@ import {
PlatformCapabilities,
} from "../PlatformService";
import { logger } from "../../utils/logger";
+import { BaseDatabaseService } from "./BaseDatabaseService";
interface QueuedOperation {
type: "run" | "query" | "rawQuery";
@@ -39,7 +40,10 @@ interface QueuedOperation {
* - Platform-specific features
* - SQLite database operations
*/
-export class CapacitorPlatformService implements PlatformService {
+export class CapacitorPlatformService
+ extends BaseDatabaseService
+ implements PlatformService
+{
/** Current camera direction */
private currentDirection: CameraDirection = CameraDirection.Rear;
@@ -52,6 +56,7 @@ export class CapacitorPlatformService implements PlatformService {
private isProcessingQueue: boolean = false;
constructor() {
+ super();
this.sqlite = new SQLiteConnection(CapacitorSQLite);
}
@@ -1328,79 +1333,8 @@ export class CapacitorPlatformService implements PlatformService {
// --- PWA/Web-only methods (no-op for Capacitor) ---
public registerServiceWorker(): void {}
- // Database utility methods
- generateInsertStatement(
- model: Record,
- tableName: string,
- ): { sql: string; params: unknown[] } {
- const keys = Object.keys(model);
- const placeholders = keys.map(() => "?").join(", ");
- const sql = `INSERT INTO ${tableName} (${keys.join(", ")}) VALUES (${placeholders})`;
- const params = keys.map((key) => model[key]);
- return { sql, params };
- }
-
- async updateDefaultSettings(
- settings: Record,
- ): Promise {
- const keys = Object.keys(settings);
- const setClause = keys.map((key) => `${key} = ?`).join(", ");
- const sql = `UPDATE settings SET ${setClause} WHERE id = 1`;
- const params = keys.map((key) => settings[key]);
- await this.dbExec(sql, params);
- }
-
- async updateActiveDid(did: string): Promise {
- await this.dbExec(
- "UPDATE active_identity SET activeDid = ?, lastUpdated = datetime('now') WHERE id = 1",
- [did],
- );
- }
-
- async insertNewDidIntoSettings(did: string): Promise {
- // Import constants dynamically to avoid circular dependencies
- const { DEFAULT_ENDORSER_API_SERVER, DEFAULT_PARTNER_API_SERVER } =
- await import("@/constants/app");
-
- // Use INSERT OR REPLACE to handle case where settings already exist for this DID
- // This prevents duplicate accountDid entries and ensures data integrity
- await this.dbExec(
- "INSERT OR REPLACE INTO settings (accountDid, finishedOnboarding, apiServer, partnerApiServer) VALUES (?, ?, ?, ?)",
- [did, false, DEFAULT_ENDORSER_API_SERVER, DEFAULT_PARTNER_API_SERVER],
- );
- }
-
- async updateDidSpecificSettings(
- did: string,
- settings: Record,
- ): Promise {
- const keys = Object.keys(settings);
- const setClause = keys.map((key) => `${key} = ?`).join(", ");
- const sql = `UPDATE settings SET ${setClause} WHERE accountDid = ?`;
- const params = [...keys.map((key) => settings[key]), did];
- await this.dbExec(sql, params);
- }
-
- async retrieveSettingsForActiveAccount(): Promise | null> {
- const result = await this.dbQuery("SELECT * FROM settings WHERE id = 1");
- if (result?.values?.[0]) {
- // Convert the row to an object
- const row = result.values[0];
- const columns = result.columns || [];
- const settings: Record = {};
-
- columns.forEach((column, index) => {
- if (column !== "id") {
- // Exclude the id column
- settings[column] = row[index];
- }
- });
-
- return settings;
- }
- return null;
- }
+ // Database utility methods - inherited from BaseDatabaseService
+ // generateInsertStatement, updateDefaultSettings, updateActiveDid,
+ // getActiveIdentity, insertNewDidIntoSettings, updateDidSpecificSettings,
+ // retrieveSettingsForActiveAccount are all inherited from BaseDatabaseService
}
diff --git a/src/services/platforms/WebPlatformService.ts b/src/services/platforms/WebPlatformService.ts
index b5b25622..da573837 100644
--- a/src/services/platforms/WebPlatformService.ts
+++ b/src/services/platforms/WebPlatformService.ts
@@ -5,6 +5,7 @@ import {
} from "../PlatformService";
import { logger } from "../../utils/logger";
import { QueryExecResult } from "@/interfaces/database";
+import { BaseDatabaseService } from "./BaseDatabaseService";
// Dynamic import of initBackend to prevent worker context errors
import type {
WorkerRequest,
@@ -29,7 +30,10 @@ import type {
* Note: File system operations are not available in the web platform
* due to browser security restrictions. These methods throw appropriate errors.
*/
-export class WebPlatformService implements PlatformService {
+export class WebPlatformService
+ extends BaseDatabaseService
+ implements PlatformService
+{
private static instanceCount = 0; // Debug counter
private worker: Worker | null = null;
private workerReady = false;
@@ -46,6 +50,7 @@ export class WebPlatformService implements PlatformService {
private readonly messageTimeout = 30000; // 30 seconds
constructor() {
+ super();
WebPlatformService.instanceCount++;
logger.debug("[WebPlatformService] Initializing web platform service");
@@ -668,105 +673,8 @@ export class WebPlatformService implements PlatformService {
// SharedArrayBuffer initialization is handled by initBackend call in initializeWorker
}
- // Database utility methods
- generateInsertStatement(
- model: Record,
- tableName: string,
- ): { sql: string; params: unknown[] } {
- const keys = Object.keys(model);
- const placeholders = keys.map(() => "?").join(", ");
- const sql = `INSERT INTO ${tableName} (${keys.join(", ")}) VALUES (${placeholders})`;
- const params = keys.map((key) => model[key]);
- return { sql, params };
- }
-
- async updateDefaultSettings(
- settings: Record,
- ): Promise {
- // Get current active DID and update that identity's settings
- const activeIdentity = await this.getActiveIdentity();
- const activeDid = activeIdentity.activeDid;
-
- if (!activeDid) {
- logger.warn(
- "[WebPlatformService] No active DID found, cannot update default settings",
- );
- return;
- }
-
- const keys = Object.keys(settings);
- const setClause = keys.map((key) => `${key} = ?`).join(", ");
- const sql = `UPDATE settings SET ${setClause} WHERE accountDid = ?`;
- const params = [...keys.map((key) => settings[key]), activeDid];
- await this.dbExec(sql, params);
- }
-
- async updateActiveDid(did: string): Promise {
- await this.dbExec(
- "INSERT OR REPLACE INTO active_identity (id, activeDid, lastUpdated) VALUES (1, ?, ?)",
- [did, new Date().toISOString()],
- );
- }
-
- async getActiveIdentity(): Promise<{ activeDid: string }> {
- const result = await this.dbQuery(
- "SELECT activeDid FROM active_identity WHERE id = 1",
- );
- return {
- activeDid: (result?.values?.[0]?.[0] as string) || "",
- };
- }
-
- async insertNewDidIntoSettings(did: string): Promise {
- // Import constants dynamically to avoid circular dependencies
- const { DEFAULT_ENDORSER_API_SERVER, DEFAULT_PARTNER_API_SERVER } =
- await import("@/constants/app");
-
- // Use INSERT OR REPLACE to handle case where settings already exist for this DID
- // This prevents duplicate accountDid entries and ensures data integrity
- await this.dbExec(
- "INSERT OR REPLACE INTO settings (accountDid, finishedOnboarding, apiServer, partnerApiServer) VALUES (?, ?, ?, ?)",
- [did, false, DEFAULT_ENDORSER_API_SERVER, DEFAULT_PARTNER_API_SERVER],
- );
- }
-
- async updateDidSpecificSettings(
- did: string,
- settings: Record,
- ): Promise {
- const keys = Object.keys(settings);
- const setClause = keys.map((key) => `${key} = ?`).join(", ");
- const sql = `UPDATE settings SET ${setClause} WHERE accountDid = ?`;
- const params = [...keys.map((key) => settings[key]), did];
- // Log update operation for debugging
- logger.debug(
- "[WebPlatformService] updateDidSpecificSettings",
- sql,
- JSON.stringify(params, null, 2),
- );
- await this.dbExec(sql, params);
- }
-
- async retrieveSettingsForActiveAccount(): Promise | null> {
- const result = await this.dbQuery("SELECT * FROM settings WHERE id = 1");
- if (result?.values?.[0]) {
- // Convert the row to an object
- const row = result.values[0];
- const columns = result.columns || [];
- const settings: Record = {};
-
- columns.forEach((column, index) => {
- if (column !== "id") {
- // Exclude the id column
- settings[column] = row[index];
- }
- });
-
- return settings;
- }
- return null;
- }
+ // Database utility methods - inherited from BaseDatabaseService
+ // generateInsertStatement, updateDefaultSettings, updateActiveDid,
+ // getActiveIdentity, insertNewDidIntoSettings, updateDidSpecificSettings,
+ // retrieveSettingsForActiveAccount are all inherited from BaseDatabaseService
}
diff --git a/src/views/AccountViewView.vue b/src/views/AccountViewView.vue
index e7f698a3..9b7efd3e 100644
--- a/src/views/AccountViewView.vue
+++ b/src/views/AccountViewView.vue
@@ -1488,18 +1488,21 @@ export default class AccountViewView extends Vue {
status?: number;
};
};
- logger.error("[Server Limits] Error retrieving limits:", {
- error: error instanceof Error ? error.message : String(error),
- did: did,
- apiServer: this.apiServer,
- imageServer: this.DEFAULT_IMAGE_API_SERVER,
- partnerApiServer: this.partnerApiServer,
- errorCode: axiosError?.response?.data?.error?.code,
- errorMessage: axiosError?.response?.data?.error?.message,
- httpStatus: axiosError?.response?.status,
- needsUserMigration: true,
- timestamp: new Date().toISOString(),
- });
+ logger.warn(
+ "[Server Limits] Error retrieving limits, expected for unregistered users:",
+ {
+ error: error instanceof Error ? error.message : String(error),
+ did: did,
+ apiServer: this.apiServer,
+ imageServer: this.DEFAULT_IMAGE_API_SERVER,
+ partnerApiServer: this.partnerApiServer,
+ errorCode: axiosError?.response?.data?.error?.code,
+ errorMessage: axiosError?.response?.data?.error?.message,
+ httpStatus: axiosError?.response?.status,
+ needsUserMigration: true,
+ timestamp: new Date().toISOString(),
+ },
+ );
// this.notify.error(this.limitsMessage, TIMEOUTS.STANDARD);
} finally {
diff --git a/src/views/ContactEditView.vue b/src/views/ContactEditView.vue
index 51687b5b..a3ec73ce 100644
--- a/src/views/ContactEditView.vue
+++ b/src/views/ContactEditView.vue
@@ -346,9 +346,7 @@ export default class ContactEditView extends Vue {
// Notify success and redirect
this.notify.success(NOTIFY_CONTACT_SAVED.message, TIMEOUTS.STANDARD);
- (this.$router as Router).push({
- path: "/did/" + encodeURIComponent(this.contact?.did || ""),
- });
+ this.$router.back();
}
}
diff --git a/src/views/ContactsView.vue b/src/views/ContactsView.vue
index e31cb708..eebd8049 100644
--- a/src/views/ContactsView.vue
+++ b/src/views/ContactsView.vue
@@ -171,9 +171,11 @@ import {
CONTACT_IMPORT_ONE_URL_PATH_TIME_SAFARI,
CONTACT_URL_PATH_ENDORSER_CH_OLD,
} from "../libs/endorserServer";
-import { GiveSummaryRecord } from "@/interfaces/records";
-import { UserInfo } from "@/interfaces/common";
-import { VerifiableCredential } from "@/interfaces/claims-result";
+import {
+ GiveSummaryRecord,
+ UserInfo,
+ VerifiableCredential,
+} from "@/interfaces";
import * as libsUtil from "../libs/util";
import {
generateSaveAndActivateIdentity,
diff --git a/src/views/DIDView.vue b/src/views/DIDView.vue
index f6acf31c..8d67961c 100644
--- a/src/views/DIDView.vue
+++ b/src/views/DIDView.vue
@@ -12,20 +12,20 @@
-
-
+
-
-
+
@@ -476,7 +476,7 @@ export default class DIDView extends Vue {
* Navigation helper methods
*/
goBack() {
- this.$router.go(-1);
+ this.$router.back();
}
/**
diff --git a/src/views/HomeView.vue b/src/views/HomeView.vue
index 9f087d85..718a731f 100644
--- a/src/views/HomeView.vue
+++ b/src/views/HomeView.vue
@@ -706,7 +706,7 @@ export default class HomeView extends Vue {
};
logger.warn(
- "[HomeView Settings Trace] ⚠️ Registration check failed",
+ "[HomeView Settings Trace] ⚠️ Registration check failed, expected for unregistered users.",
{
error: errorMessage,
did: this.activeDid,
diff --git a/src/views/OnboardMeetingListView.vue b/src/views/OnboardMeetingListView.vue
index 452b64a9..3c9aef71 100644
--- a/src/views/OnboardMeetingListView.vue
+++ b/src/views/OnboardMeetingListView.vue
@@ -77,7 +77,7 @@
v-if="meetings.length === 0 && !isRegistered"
class="text-center text-gray-500 py-8"
>
- No onboarding meetings available
+ No onboarding meetings are available