fix(platform): remove auto-fix identity selection and fix feed loading race condition
- Remove problematic $ensureActiveIdentityPopulated() that auto-selected identities - Add user-friendly $needsActiveIdentitySelection() and $getAvailableAccountDids() methods - Fix missing updateActiveDid implementation in CapacitorPlatformService - Resolve race condition in HomeView initialization causing feed loading failures - Improve TypeScript error handling in ContactsView invite processing Addresses team concerns about data consistency and user control for identity selection.
This commit is contained in:
@@ -1319,6 +1319,13 @@ export class CapacitorPlatformService implements PlatformService {
|
||||
await this.dbExec(sql, params);
|
||||
}
|
||||
|
||||
async updateActiveDid(did: string): Promise<void> {
|
||||
await this.dbExec(
|
||||
"UPDATE active_identity SET activeDid = ?, lastUpdated = datetime('now') WHERE id = 1",
|
||||
[did],
|
||||
);
|
||||
}
|
||||
|
||||
async insertNewDidIntoSettings(did: string): Promise<void> {
|
||||
await this.dbExec("INSERT INTO settings (accountDid) VALUES (?)", [did]);
|
||||
}
|
||||
|
||||
@@ -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<void> {
|
||||
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<boolean> {
|
||||
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<string[]> {
|
||||
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<Settings>;
|
||||
$getActiveIdentity(): Promise<{ activeDid: string }>;
|
||||
$withTransaction<T>(callback: () => Promise<T>): Promise<T>;
|
||||
$needsActiveIdentitySelection(): Promise<boolean>;
|
||||
$getAvailableAccountDids(): Promise<string[]>;
|
||||
isCapacitor: boolean;
|
||||
isWeb: boolean;
|
||||
isElectron: boolean;
|
||||
@@ -1958,7 +1969,10 @@ declare module "@vue/runtime-core" {
|
||||
did?: string,
|
||||
defaults?: Settings,
|
||||
): Promise<Settings>;
|
||||
$getActiveIdentity(): Promise<{ activeDid: string }>;
|
||||
$withTransaction<T>(fn: () => Promise<T>): Promise<T>;
|
||||
$needsActiveIdentitySelection(): Promise<boolean>;
|
||||
$getAvailableAccountDids(): Promise<string[]>;
|
||||
|
||||
// Specialized shortcuts - contacts cached, settings fresh
|
||||
$contacts(): Promise<Contact[]>;
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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}`,
|
||||
|
||||
Reference in New Issue
Block a user