feat: implement Active Pointer + Smart Deletion Pattern for accounts
- Consolidate migrations: merge 002/003 into 001_initial with UNIQUE did constraint - Add foreign key: active_identity.activeDid REFERENCES accounts.did ON DELETE RESTRICT - Replace empty string defaults with NULL for proper empty state handling - Implement atomic smart deletion with auto-switch logic in IdentitySwitcherView - Add DAL methods: $getAllAccountDids, $getActiveDid, $setActiveDid, $pickNextAccountDid - Add migration bootstrapping to auto-select first account if none selected - Block deletion of last remaining account with user notification Refs: doc/active-pointer-smart-deletion-pattern.md
This commit is contained in:
@@ -737,6 +737,87 @@ export const PlatformServiceMixin = {
|
||||
}
|
||||
},
|
||||
|
||||
// =================================================
|
||||
// SMART DELETION PATTERN DAL METHODS
|
||||
// =================================================
|
||||
|
||||
/**
|
||||
* Get all account DIDs ordered by creation date
|
||||
* Required for smart deletion pattern
|
||||
*/
|
||||
async $getAllAccountDids(): Promise<string[]> {
|
||||
const result = await this.$dbQuery(
|
||||
"SELECT did FROM accounts ORDER BY dateCreated, did",
|
||||
);
|
||||
return result?.values?.map((row) => row[0] as string) || [];
|
||||
},
|
||||
|
||||
/**
|
||||
* Get account DID by ID
|
||||
* Required for smart deletion pattern
|
||||
*/
|
||||
async $getAccountDidById(id: number): Promise<string> {
|
||||
const result = await this.$dbQuery(
|
||||
"SELECT did FROM accounts WHERE id = ?",
|
||||
[id],
|
||||
);
|
||||
return result?.values?.[0]?.[0] as string;
|
||||
},
|
||||
|
||||
/**
|
||||
* Get active DID (returns null if none selected)
|
||||
* Required for smart deletion pattern
|
||||
*/
|
||||
async $getActiveDid(): Promise<string | null> {
|
||||
const result = await this.$dbQuery(
|
||||
"SELECT activeDid FROM active_identity WHERE id = 1",
|
||||
);
|
||||
return (result?.values?.[0]?.[0] as string) || null;
|
||||
},
|
||||
|
||||
/**
|
||||
* Set active DID (can be null for no selection)
|
||||
* Required for smart deletion pattern
|
||||
*/
|
||||
async $setActiveDid(did: string | null): Promise<void> {
|
||||
await this.$dbExec(
|
||||
"UPDATE active_identity SET activeDid = ?, lastUpdated = datetime('now') WHERE id = 1",
|
||||
[did],
|
||||
);
|
||||
},
|
||||
|
||||
/**
|
||||
* Count total accounts
|
||||
* Required for smart deletion pattern
|
||||
*/
|
||||
async $countAccounts(): Promise<number> {
|
||||
const result = await this.$dbQuery("SELECT COUNT(*) FROM accounts");
|
||||
return (result?.values?.[0]?.[0] as number) || 0;
|
||||
},
|
||||
|
||||
/**
|
||||
* Deterministic "next" picker for account selection
|
||||
* Required for smart deletion pattern
|
||||
*/
|
||||
$pickNextAccountDid(all: string[], current?: string): string {
|
||||
const sorted = [...all].sort();
|
||||
if (!current) return sorted[0];
|
||||
const i = sorted.indexOf(current);
|
||||
return sorted[(i + 1) % sorted.length];
|
||||
},
|
||||
|
||||
/**
|
||||
* Ensure an active account is selected (repair hook)
|
||||
* Required for smart deletion pattern bootstrapping
|
||||
*/
|
||||
async $ensureActiveSelected(): Promise<void> {
|
||||
const active = await this.$getActiveDid();
|
||||
const all = await this.$getAllAccountDids();
|
||||
if (active === null && all.length > 0) {
|
||||
await this.$setActiveDid(this.$pickNextAccountDid(all));
|
||||
}
|
||||
},
|
||||
|
||||
// =================================================
|
||||
// ULTRA-CONCISE DATABASE METHODS (shortest names)
|
||||
// =================================================
|
||||
|
||||
Reference in New Issue
Block a user