diff --git a/src/services/platforms/CapacitorPlatformService.ts b/src/services/platforms/CapacitorPlatformService.ts index c1374f25..f31ac2e2 100644 --- a/src/services/platforms/CapacitorPlatformService.ts +++ b/src/services/platforms/CapacitorPlatformService.ts @@ -1319,6 +1319,13 @@ export class CapacitorPlatformService implements PlatformService { 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 { await this.dbExec("INSERT INTO settings (accountDid) VALUES (?)", [did]); } diff --git a/src/utils/PlatformServiceMixin.ts b/src/utils/PlatformServiceMixin.ts index 958478b8..0e9bebec 100644 --- a/src/utils/PlatformServiceMixin.ts +++ b/src/utils/PlatformServiceMixin.ts @@ -195,80 +195,6 @@ export const PlatformServiceMixin = { // SELF-CONTAINED UTILITY METHODS (no databaseUtil dependency) // ================================================= - /** - * Ensure active_identity table is populated with data from settings - * This is a one-time fix for the migration gap - */ - async $ensureActiveIdentityPopulated(): Promise { - try { - logger.info( - "[PlatformServiceMixin] $ensureActiveIdentityPopulated() called", - ); - - // Check if active_identity has data - const activeIdentity = await this.$dbQuery( - "SELECT activeDid FROM active_identity WHERE id = 1", - ); - - const currentActiveDid = activeIdentity?.values?.[0]?.[0] as string; - logger.info( - "[PlatformServiceMixin] Current active_identity table state:", - { currentActiveDid, hasData: !!currentActiveDid }, - ); - - if (!currentActiveDid) { - logger.info( - "[PlatformServiceMixin] Active identity table empty, populating from settings", - ); - - // Get activeDid from settings (any row with accountDid) - const settings = await this.$dbQuery( - "SELECT accountDid FROM settings WHERE accountDid IS NOT NULL LIMIT 1", - ); - - const settingsAccountDid = settings?.values?.[0]?.[0] as string; - logger.info("[PlatformServiceMixin] Found settings accountDid:", { - settingsAccountDid, - }); - - if (settingsAccountDid) { - await this.$dbExec( - "UPDATE active_identity SET activeDid = ?, lastUpdated = datetime('now') WHERE id = 1", - [settingsAccountDid], - ); - logger.info( - `[PlatformServiceMixin] Populated active_identity with: ${settingsAccountDid}`, - ); - } else { - // If no settings found, try to get any account DID - const accounts = await this.$dbQuery( - "SELECT did FROM accounts LIMIT 1", - ); - const accountDid = accounts?.values?.[0]?.[0] as string; - - if (accountDid) { - await this.$dbExec( - "UPDATE active_identity SET activeDid = ?, lastUpdated = datetime('now') WHERE id = 1", - [accountDid], - ); - logger.info( - `[PlatformServiceMixin] Populated active_identity with account DID: ${accountDid}`, - ); - } else { - logger.warn( - "[PlatformServiceMixin] No accountDid found in settings or accounts table", - ); - } - } - } - } catch (error) { - logger.warn( - "[PlatformServiceMixin] Failed to populate active_identity:", - error, - ); - } - }, - /** * Update the current activeDid and trigger change detection * This method should be called when the user switches identities @@ -306,6 +232,69 @@ export const PlatformServiceMixin = { } }, + /** + * Check if active identity needs user intervention + * Returns true if active_identity table is empty but accounts exist + * This allows components to show appropriate UI for user selection + */ + async $needsActiveIdentitySelection(): Promise { + try { + // Check if active_identity table has a valid activeDid + const activeIdentity = await this.$dbQuery( + "SELECT activeDid FROM active_identity WHERE id = 1", + ); + + const currentActiveDid = activeIdentity?.values?.[0]?.[0] as string; + + // If we have an activeDid, validate it exists in accounts + if (currentActiveDid) { + const accountExists = await this.$dbQuery( + "SELECT did FROM accounts WHERE did = ?", + [currentActiveDid], + ); + return !accountExists?.values?.length; + } + + // If no activeDid, check if there are any accounts available + const availableAccounts = await this.$dbQuery( + "SELECT COUNT(*) FROM accounts", + ); + const accountCount = availableAccounts?.values?.[0]?.[0] as number; + + return accountCount > 0; + } catch (error) { + logger.error( + "[PlatformServiceMixin] Error checking if active identity selection needed:", + error, + ); + return false; + } + }, + + /** + * Get available account DIDs for user selection + * Returns array of DIDs that can be set as active identity + */ + async $getAvailableAccountDids(): Promise { + try { + const result = await this.$dbQuery( + "SELECT did FROM accounts ORDER BY did", + ); + + if (!result?.values?.length) { + return []; + } + + return result.values.map((row: unknown[]) => row[0] as string); + } catch (error) { + logger.error( + "[PlatformServiceMixin] Error getting available account DIDs:", + error, + ); + return []; + } + }, + /** * Map database columns to values with proper type conversion * Handles boolean conversion from SQLite integers (0/1) to boolean values @@ -655,9 +644,6 @@ export const PlatformServiceMixin = { "[PlatformServiceMixin] $getActiveIdentity() called - API layer verification", ); - // Ensure the table is populated before reading - await this.$ensureActiveIdentityPopulated(); - logger.debug( "[PlatformServiceMixin] Getting active identity from active_identity table", ); @@ -700,6 +686,29 @@ export const PlatformServiceMixin = { } } + // Handle empty active_identity table - this indicates a migration issue + // Instead of auto-fixing, we log the issue for user awareness + logger.warn( + "[PlatformServiceMixin] Active identity table is empty - this may indicate a migration issue", + ); + + // Check if there are any accounts available for user selection + const availableAccounts = await this.$dbQuery( + "SELECT did FROM accounts ORDER BY did LIMIT 5", + ); + + if (availableAccounts?.values?.length) { + const accountDids = availableAccounts.values.map( + (row: unknown[]) => row[0] as string, + ); + logger.info( + "[PlatformServiceMixin] Available accounts for user selection:", + { accountDids }, + ); + } else { + logger.warn("[PlatformServiceMixin] No accounts found in database"); + } + logger.debug( "[PlatformServiceMixin] No active identity found, returning empty", ); @@ -1837,6 +1846,8 @@ export interface IPlatformServiceMixin { ): Promise; $getActiveIdentity(): Promise<{ activeDid: string }>; $withTransaction(callback: () => Promise): Promise; + $needsActiveIdentitySelection(): Promise; + $getAvailableAccountDids(): Promise; isCapacitor: boolean; isWeb: boolean; isElectron: boolean; @@ -1958,7 +1969,10 @@ declare module "@vue/runtime-core" { did?: string, defaults?: Settings, ): Promise; + $getActiveIdentity(): Promise<{ activeDid: string }>; $withTransaction(fn: () => Promise): Promise; + $needsActiveIdentitySelection(): Promise; + $getAvailableAccountDids(): Promise; // Specialized shortcuts - contacts cached, settings fresh $contacts(): Promise; diff --git a/src/views/ContactsView.vue b/src/views/ContactsView.vue index 777e8cca..2496c66e 100644 --- a/src/views/ContactsView.vue +++ b/src/views/ContactsView.vue @@ -359,9 +359,6 @@ export default class ContactsView extends Vue { activeDid: this.activeDid, }); - // Ensure active_identity is populated before processing invite - await this.$ensureActiveIdentityPopulated(); - // Re-fetch settings after ensuring active_identity is populated const updatedSettings = await this.$accountSettings(); this.activeDid = updatedSettings.activeDid || ""; @@ -448,17 +445,28 @@ export default class ContactsView extends Vue { this.$logAndConsole(fullError, true); let message = "Got an error sending the invite."; if ( + error && + typeof error === "object" && + "response" in error && error.response && + typeof error.response === "object" && + "data" in error.response && error.response.data && - error.response.data.error + typeof error.response.data === "object" && + "error" in error.response.data ) { - if (error.response.data.error.message) { - message = error.response.data.error.message; + const responseData = error.response.data as { error: unknown }; + if ( + responseData.error && + typeof responseData.error === "object" && + "message" in responseData.error + ) { + message = (responseData.error as { message: string }).message; } else { - message = error.response.data.error; + message = String(responseData.error); } - } else if (error.message) { - message = error.message; + } else if (error && typeof error === "object" && "message" in error) { + message = (error as { message: string }).message; } this.notify.error(message, TIMEOUTS.MODAL); } diff --git a/src/views/HomeView.vue b/src/views/HomeView.vue index 19e27022..67de90ba 100644 --- a/src/views/HomeView.vue +++ b/src/views/HomeView.vue @@ -478,7 +478,7 @@ export default class HomeView extends Vue { await this.initializeIdentity(); // Settings already loaded in initializeIdentity() - await this.loadContacts(); + // Contacts already loaded in initializeIdentity() // Registration check already handled in initializeIdentity() await this.loadFeedData(); @@ -600,7 +600,7 @@ export default class HomeView extends Vue { // Load contacts with graceful fallback try { - this.loadContacts(); + await this.loadContacts(); } catch (error) { this.$logAndConsole( `[HomeView] Failed to retrieve contacts: ${error}`,