forked from jsnbuchanan/crowd-funder-for-time-pwa
- Add master settings functions implementation strategy - Correct IdentitySection.vue analysis (prop-based, no changes required) - Simplify ContactAmountsView.vue (phased-out method, separate refactoring) - Add new getMasterSettings() function with active_identity integration - Include helper methods _getSettingsWithoutActiveDid() and _getActiveIdentity() - Enhance evidence section with master settings architecture support - Update risk assessment for phased-out methods - Clean up migration timeline formatting This commit focuses the migration plan on components requiring immediate active_identity table changes, separating concerns from broader API refactoring.
813 lines
28 KiB
Markdown
813 lines
28 KiB
Markdown
# ActiveDid Migration Plan - Separate Table Architecture
|
|
|
|
**Author**: Matthew Raymer
|
|
**Date**: 2025-01-27T18:30Z
|
|
**Status**: 🎯 **PLANNING** - Active migration planning phase
|
|
|
|
## Objective
|
|
|
|
Move the `activeDid` field from the `settings` table to a dedicated
|
|
`active_identity` table to improve database architecture and separate
|
|
identity selection from user preferences.
|
|
|
|
## Result
|
|
|
|
This document serves as the comprehensive planning and implementation
|
|
guide for the ActiveDid migration.
|
|
|
|
## Use/Run
|
|
|
|
Reference this document during implementation to ensure all migration
|
|
steps are followed correctly and all stakeholders are aligned on the
|
|
approach.
|
|
|
|
## Context & Scope
|
|
|
|
- **In scope**:
|
|
- Database schema modification for active_identity table
|
|
- Migration of existing activeDid data
|
|
- Updates to all platform services and mixins
|
|
- Type definition updates
|
|
- Testing across all platforms
|
|
- **Out of scope**:
|
|
- Changes to user interface for identity selection
|
|
- Modifications to identity creation logic
|
|
- Changes to authentication flow
|
|
|
|
## Environment & Preconditions
|
|
|
|
- **OS/Runtime**: All platforms (Web, Electron, iOS, Android)
|
|
- **Versions/Builds**: Current development branch, SQLite database
|
|
- **Services/Endpoints**: Local database, PlatformServiceMixin
|
|
- **Auth mode**: Existing authentication system unchanged
|
|
|
|
## Architecture / Process Overview
|
|
|
|
The migration follows a phased approach to minimize risk and ensure
|
|
data integrity:
|
|
|
|
```mermaid
|
|
flowchart TD
|
|
A[Current State<br/>activeDid in settings] --> B[Phase 1: Schema Creation<br/>Add active_identity table]
|
|
B --> C[Phase 2: Data Migration<br/>Copy activeDid data]
|
|
C --> D[Phase 3: API Updates<br/>Update all access methods]
|
|
D --> E[Phase 4: Cleanup<br/>Remove activeDid from settings]
|
|
E --> F[Final State<br/>Separate active_identity table]
|
|
|
|
G[Rollback Plan<br/>Keep old field until verified] --> H[Data Validation<br/>Verify integrity at each step]
|
|
H --> I[Platform Testing<br/>Test all platforms]
|
|
I --> J[Production Deployment<br/>Gradual rollout]
|
|
```
|
|
|
|
## Interfaces & Contracts
|
|
|
|
### Database Schema Changes
|
|
|
|
| Table | Current Schema | New Schema | Migration Required |
|
|
|-------|----------------|------------|-------------------|
|
|
| `settings` | `activeDid TEXT` | Field removed | Yes - data migration |
|
|
| `active_identity` | Does not exist | New table with `activeDid TEXT` | Yes - table creation |
|
|
|
|
### API Contract Changes
|
|
|
|
| Method | Current Behavior | New Behavior | Breaking Change |
|
|
|---------|------------------|--------------|-----------------|
|
|
| `$accountSettings()` | Returns settings with activeDid | Returns settings without activeDid | No - backward compatible |
|
|
| `$saveSettings()` | Updates settings.activeDid | Updates active_identity.activeDid | Yes - requires updates |
|
|
| `$updateActiveDid()` | Updates internal tracking | Updates active_identity table | Yes - requires updates |
|
|
|
|
## Repro: End-to-End Procedure
|
|
|
|
### Phase 1: Schema Creation
|
|
|
|
```sql
|
|
-- Create new active_identity table
|
|
CREATE TABLE active_identity (
|
|
id INTEGER PRIMARY KEY CHECK (id = 1),
|
|
activeDid TEXT NOT NULL,
|
|
lastUpdated TEXT NOT NULL DEFAULT (datetime('now'))
|
|
);
|
|
|
|
-- Insert default record (will be updated during migration)
|
|
INSERT INTO active_identity (id, activeDid) VALUES (1, '');
|
|
```
|
|
|
|
### Phase 2: Data Migration
|
|
|
|
```typescript
|
|
// Migration script to copy existing activeDid values
|
|
async function migrateActiveDidToSeparateTable(): Promise<void> {
|
|
// Get current activeDid from settings
|
|
const currentSettings = await retrieveSettingsForDefaultAccount();
|
|
const activeDid = currentSettings.activeDid;
|
|
|
|
if (activeDid) {
|
|
// Insert into new table
|
|
await dbExec(
|
|
"UPDATE active_identity SET activeDid = ?, lastUpdated = datetime('now') WHERE id = 1",
|
|
[activeDid]
|
|
);
|
|
}
|
|
}
|
|
```
|
|
|
|
### Phase 3: API Updates
|
|
|
|
```typescript
|
|
// Updated PlatformServiceMixin method
|
|
async $accountSettings(did?: string, defaults: Settings = {}): Promise<Settings> {
|
|
// Get settings without activeDid
|
|
const settings = await this._getSettingsWithoutActiveDid();
|
|
|
|
// Get activeDid from separate table
|
|
const activeIdentity = await this._getActiveIdentity();
|
|
|
|
return { ...settings, activeDid: activeIdentity.activeDid };
|
|
}
|
|
|
|
// New method to get settings without activeDid
|
|
async _getSettingsWithoutActiveDid(): Promise<Settings> {
|
|
const result = await this.$dbQuery(
|
|
"SELECT id, accountDid, apiServer, filterFeedByNearby, filterFeedByVisible, " +
|
|
"finishedOnboarding, firstName, hideRegisterPromptOnNewContact, isRegistered, " +
|
|
"lastName, lastAckedOfferToUserJwtId, lastAckedOfferToUserProjectsJwtId, " +
|
|
"lastNotifiedClaimId, lastViewedClaimId, notifyingNewActivityTime, " +
|
|
"notifyingReminderMessage, notifyingReminderTime, partnerApiServer, " +
|
|
"passkeyExpirationMinutes, profileImageUrl, searchBoxes, showContactGivesInline, " +
|
|
"showGeneralAdvanced, showShortcutBvc, vapid, warnIfProdServer, warnIfTestServer, " +
|
|
"webPushServer FROM settings WHERE id = ?",
|
|
[MASTER_SETTINGS_KEY]
|
|
);
|
|
|
|
if (!result?.values?.length) {
|
|
return DEFAULT_SETTINGS;
|
|
}
|
|
|
|
return this._mapColumnsToValues(result.columns, result.values)[0] as Settings;
|
|
}
|
|
|
|
// New method to get active identity
|
|
async _getActiveIdentity(): Promise<{ activeDid: string | null }> {
|
|
const result = await this.$dbQuery(
|
|
"SELECT activeDid FROM active_identity WHERE id = 1"
|
|
);
|
|
|
|
if (!result?.values?.length) {
|
|
return { activeDid: null };
|
|
}
|
|
|
|
return { activeDid: result.values[0][0] as string };
|
|
}
|
|
```
|
|
|
|
### **Master Settings Functions Implementation Strategy**
|
|
|
|
#### **1. Update `retrieveSettingsForDefaultAccount()`**
|
|
|
|
```typescript
|
|
// Current implementation in src/db/databaseUtil.ts:148
|
|
export async function retrieveSettingsForDefaultAccount(): Promise<Settings> {
|
|
const platform = PlatformServiceFactory.getInstance();
|
|
const sql = "SELECT * FROM settings WHERE id = ?";
|
|
const result = await platform.dbQuery(sql, [MASTER_SETTINGS_KEY]);
|
|
// ... rest of implementation
|
|
}
|
|
|
|
// Updated implementation
|
|
export async function retrieveSettingsForDefaultAccount(): Promise<Settings> {
|
|
const platform = PlatformServiceFactory.getInstance();
|
|
|
|
// Get settings without activeDid
|
|
const sql = "SELECT id, accountDid, apiServer, filterFeedByNearby, filterFeedByVisible, " +
|
|
"finishedOnboarding, firstName, hideRegisterPromptOnNewContact, isRegistered, " +
|
|
"lastName, lastAckedOfferToUserJwtId, lastAckedOfferToUserProjectsJwtId, " +
|
|
"lastNotifiedClaimId, lastViewedClaimId, notifyingNewActivityTime, " +
|
|
"notifyingReminderMessage, notifyingReminderTime, partnerApiServer, " +
|
|
"passkeyExpirationMinutes, profileImageUrl, searchBoxes, showContactGivesInline, " +
|
|
"showGeneralAdvanced, showShortcutBvc, vapid, warnIfProdServer, warnIfTestServer, " +
|
|
"webPushServer FROM settings WHERE id = ?";
|
|
|
|
const result = await platform.dbQuery(sql, [MASTER_SETTINGS_KEY]);
|
|
|
|
if (!result) {
|
|
return DEFAULT_SETTINGS;
|
|
} else {
|
|
const settings = mapColumnsToValues(result.columns, result.values)[0] as Settings;
|
|
|
|
// Handle JSON parsing
|
|
if (settings.searchBoxes) {
|
|
settings.searchBoxes = JSON.parse(settings.searchBoxes);
|
|
}
|
|
|
|
// Get activeDid from separate table
|
|
const activeIdentityResult = await platform.dbQuery(
|
|
"SELECT activeDid FROM active_identity WHERE id = 1"
|
|
);
|
|
|
|
if (activeIdentityResult?.values?.length) {
|
|
settings.activeDid = activeIdentityResult.values[0][0] as string;
|
|
}
|
|
|
|
return settings;
|
|
}
|
|
}
|
|
```
|
|
|
|
|
|
|
|
#### **2. Update `$getMergedSettings()` Method**
|
|
|
|
```typescript
|
|
// Current implementation in PlatformServiceMixin.ts:485
|
|
async $getMergedSettings(defaultKey: string, accountDid?: string, defaultFallback: Settings = {}): Promise<Settings> {
|
|
// Get default settings
|
|
const defaultSettings = await this.$getSettings(defaultKey, defaultFallback);
|
|
// ... rest of implementation
|
|
}
|
|
|
|
// Updated implementation
|
|
async $getMergedSettings(defaultKey: string, accountDid?: string, defaultFallback: Settings = {}): Promise<Settings> {
|
|
try {
|
|
// Get default settings (now without activeDid)
|
|
const defaultSettings = await this.$getSettings(defaultKey, defaultFallback);
|
|
|
|
// If no account DID, return defaults with activeDid from separate table
|
|
if (!accountDid) {
|
|
if (defaultSettings) {
|
|
// Get activeDid from separate table
|
|
const activeIdentityResult = await this.$dbQuery(
|
|
"SELECT activeDid FROM active_identity WHERE id = 1"
|
|
);
|
|
|
|
if (activeIdentityResult?.values?.length) {
|
|
defaultSettings.activeDid = activeIdentityResult.values[0][0] as string;
|
|
}
|
|
}
|
|
return defaultSettings || defaultFallback;
|
|
}
|
|
|
|
// ... rest of existing implementation for account-specific settings
|
|
} catch (error) {
|
|
logger.error(`[Settings Trace] ❌ Failed to get merged settings:`, { defaultKey, accountDid, error });
|
|
return defaultFallback;
|
|
}
|
|
}
|
|
```
|
|
|
|
## What Works (Evidence)
|
|
|
|
- ✅ **Current activeDid storage** in settings table
|
|
- **Time**: 2025-01-27T18:30Z
|
|
- **Evidence**: `src/db/tables/settings.ts:25` - activeDid field exists
|
|
- **Verify at**: Current database schema and Settings type definition
|
|
|
|
- ✅ **PlatformServiceMixin integration** with activeDid
|
|
- **Time**: 2025-01-27T18:30Z
|
|
- **Evidence**: `src/utils/PlatformServiceMixin.ts:108` - activeDid tracking
|
|
- **Verify at**: Component usage across all platforms
|
|
|
|
- ✅ **Database migration infrastructure** exists
|
|
- **Time**: 2025-01-27T18:30Z
|
|
- **Evidence**: `src/db-sql/migration.ts:31` - migration system in place
|
|
- **Verify at**: Existing migration scripts and database versioning
|
|
|
|
- ✅ **Master settings functions architecture** supports migration
|
|
- **Time**: 2025-01-27T18:30Z
|
|
- **Evidence**: Functions use explicit field selection, not `SELECT *`
|
|
- **Verify at**: `src/db/databaseUtil.ts:148` and `src/utils/PlatformServiceMixin.ts:442`
|
|
|
|
## What Doesn't (Evidence & Hypotheses)
|
|
|
|
- ❌ **No separate active_identity table** exists
|
|
- **Time**: 2025-01-27T18:30Z
|
|
- **Evidence**: Database schema only shows settings table
|
|
- **Hypothesis**: Table needs to be created as part of migration
|
|
- **Next probe**: Create migration script for new table
|
|
|
|
- ❌ **Platform services hardcoded** to settings table
|
|
- **Time**: 2025-01-27T18:30Z
|
|
- **Evidence**: `src/services/platforms/*.ts` - direct settings table access
|
|
- **Hypothesis**: All platform services need updates
|
|
- **Next probe**: Audit all platform service files for activeDid usage
|
|
|
|
## Risks, Limits, Assumptions
|
|
|
|
- **Data Loss Risk**: Migration failure could lose activeDid values
|
|
- **Breaking Changes**: API updates required across all platform services
|
|
- **Rollback Complexity**: Schema changes make rollback difficult
|
|
- **Testing Overhead**: All platforms must be tested with new structure
|
|
- **Performance Impact**: Additional table join for activeDid retrieval
|
|
- **Migration Timing**: Must be coordinated with other database changes
|
|
|
|
## Next Steps
|
|
|
|
| Owner | Task | Exit Criteria | Target Date (UTC) |
|
|
|-------|------|---------------|-------------------|
|
|
| Development Team | Create migration script | Migration script tested and validated | 2025-01-28 |
|
|
| Development Team | Update type definitions | Settings type updated, ActiveIdentity type created | 2025-01-28 |
|
|
| Development Team | Update platform services | All services use new active_identity table | 2025-01-29 |
|
|
| Development Team | Update PlatformServiceMixin | Mixin methods updated and tested | 2025-01-29 |
|
|
| QA Team | Platform testing | All platforms tested and verified | 2025-01-30 |
|
|
| Development Team | Deploy migration | Production deployment successful | 2025-01-31 |
|
|
|
|
## References
|
|
|
|
- [Database Migration Guide](./database-migration-guide.md)
|
|
- [Dexie to SQLite Mapping](./dexie-to-sqlite-mapping.md)
|
|
- [PlatformServiceMixin Documentation](./component-communication-guide.md)
|
|
- [Migration Templates](./migration-templates/)
|
|
|
|
## Competence Hooks
|
|
|
|
- *Why this works*: Separates concerns between identity selection and
|
|
user preferences, improves database normalization, enables future
|
|
identity management features
|
|
- *Common pitfalls*: Forgetting to update all platform services, not
|
|
testing rollback scenarios, missing data validation during migration
|
|
- *Next skill unlock*: Advanced database schema design and migration
|
|
planning
|
|
- *Teach-back*: Explain the four-phase migration approach and why each
|
|
phase is necessary
|
|
|
|
## Collaboration Hooks
|
|
|
|
- **Sign-off checklist**:
|
|
- [ ] Migration script tested on development database
|
|
- [ ] All platform services updated and tested
|
|
- [ ] Rollback plan validated
|
|
- [ ] Performance impact assessed
|
|
- [ ] All stakeholders approve deployment timeline
|
|
|
|
## Assumptions & Limits
|
|
|
|
- Current activeDid values are valid and should be preserved
|
|
- All platforms can handle the additional database table
|
|
- Migration can be completed without user downtime
|
|
- Rollback to previous schema is acceptable if needed
|
|
- Performance impact of additional table join is minimal
|
|
|
|
## Component & View Impact Analysis
|
|
|
|
### **High Impact Components**
|
|
|
|
1. **`IdentitySection.vue`** - Receives `activeDid` as prop
|
|
- **Current**: Uses `activeDid` from parent component via prop
|
|
- **Impact**: **NO CHANGES REQUIRED** - Parent component handles migration
|
|
- **Risk**: **LOW** - No direct database access
|
|
|
|
**Current Implementation:**
|
|
|
|
```vue
|
|
<template>
|
|
<div class="text-sm text-slate-500">
|
|
<div class="font-bold">ID: </div>
|
|
<code class="truncate">{{ activeDid }}</code>
|
|
<!-- ... rest of template -->
|
|
</div>
|
|
</template>
|
|
|
|
<script lang="ts">
|
|
export default class IdentitySection extends Vue {
|
|
@Prop({ required: true }) activeDid!: string; // Received as prop from parent
|
|
// ... other props and methods
|
|
}
|
|
</script>
|
|
```
|
|
|
|
**Required Changes:**
|
|
|
|
```vue
|
|
<script lang="ts">
|
|
export default class IdentitySection extends Vue {
|
|
@Prop({ required: true }) activeDid!: string; // No changes needed
|
|
// ... other props and methods
|
|
}
|
|
</script>
|
|
```
|
|
|
|
**Key Insight**: This component requires **zero changes** since it receives
|
|
`activeDid` as a prop. The parent component that provides this prop will
|
|
handle the migration automatically through the API layer updates.
|
|
|
|
2. **`DIDView.vue`** - Heavy activeDid usage
|
|
- **Current**: Initializes `activeDid` in `mounted()` lifecycle
|
|
- **Impact**: Must update initialization logic to use new table
|
|
- **Risk**: **HIGH** - Primary DID viewing component
|
|
|
|
**Current Implementation:**
|
|
|
|
```vue
|
|
<script lang="ts">
|
|
export default class DIDView extends Vue {
|
|
activeDid = ""; // Component data
|
|
|
|
async mounted() {
|
|
await this.initializeSettings();
|
|
await this.determineDIDToDisplay();
|
|
// ... rest of initialization
|
|
}
|
|
|
|
private async initializeSettings() {
|
|
const settings = await this.$accountSettings();
|
|
this.activeDid = settings.activeDid || "";
|
|
this.apiServer = settings.apiServer || "";
|
|
}
|
|
|
|
private async determineDIDToDisplay() {
|
|
const pathParam = window.location.pathname.substring("/did/".length);
|
|
let showDid = pathParam;
|
|
|
|
if (!showDid) {
|
|
// No DID provided in URL, use active DID
|
|
showDid = this.activeDid; // Uses component data
|
|
this.notifyDefaultToActiveDID();
|
|
}
|
|
// ... rest of logic
|
|
}
|
|
}
|
|
</script>
|
|
```
|
|
|
|
**Required Changes:**
|
|
|
|
```vue
|
|
<script lang="ts">
|
|
export default class DIDView extends Vue {
|
|
activeDid = ""; // Component data still needed
|
|
|
|
async mounted() {
|
|
await this.initializeSettings();
|
|
await this.determineDIDToDisplay();
|
|
// ... rest of initialization
|
|
}
|
|
|
|
private async initializeSettings() {
|
|
// This will automatically work if $accountSettings() is updated
|
|
const settings = await this.$accountSettings();
|
|
this.activeDid = settings.activeDid || "";
|
|
this.apiServer = settings.apiServer || "";
|
|
}
|
|
|
|
// Add watcher for activeDid changes
|
|
async onActiveDidChanged(newDid: string | null) {
|
|
if (newDid && newDid !== this.activeDid) {
|
|
this.activeDid = newDid;
|
|
// Re-initialize if needed
|
|
await this.determineDIDToDisplay();
|
|
}
|
|
}
|
|
}
|
|
</script>
|
|
```
|
|
|
|
3. **`HomeView.vue`** - ActiveDid change detection
|
|
- **Current**: Has `onActiveDidChanged()` watcher method
|
|
- **Impact**: Watcher logic needs updates for new data source
|
|
- **Risk**: **MEDIUM** - Core navigation component
|
|
|
|
**Current Implementation:**
|
|
|
|
```vue
|
|
<script lang="ts">
|
|
export default class HomeView extends Vue {
|
|
async onActiveDidChanged(newDid: string | null, oldDid: string | null) {
|
|
if (newDid !== oldDid) {
|
|
// Re-initialize identity with new settings (loads settings internally)
|
|
await this.initializeIdentity();
|
|
} else {
|
|
logger.debug(
|
|
"[HomeView Settings Trace] 📍 DID unchanged, skipping re-initialization",
|
|
);
|
|
}
|
|
}
|
|
|
|
private async initializeIdentity() {
|
|
// ... identity initialization logic
|
|
const settings = await this.$accountSettings();
|
|
this.activeDid = settings.activeDid || "";
|
|
// ... rest of initialization
|
|
}
|
|
}
|
|
</script>
|
|
```
|
|
|
|
**Required Changes:**
|
|
|
|
```vue
|
|
<script lang="ts">
|
|
export default class HomeView extends Vue {
|
|
async onActiveDidChanged(newDid: string | null, oldDid: string | null) {
|
|
if (newDid !== oldDid) {
|
|
// This will automatically work if $accountSettings() is updated
|
|
await this.initializeIdentity();
|
|
} else {
|
|
logger.debug(
|
|
"[HomeView Settings Trace] 📍 DID unchanged, skipping re-initialization",
|
|
);
|
|
}
|
|
}
|
|
|
|
private async initializeIdentity() {
|
|
// ... identity initialization logic
|
|
// This will automatically work if $accountSettings() is updated
|
|
const settings = await this.$accountSettings();
|
|
this.activeDid = settings.activeDid || "";
|
|
// ... rest of initialization
|
|
}
|
|
}
|
|
</script>
|
|
```
|
|
|
|
**Key Insight**: HomeView will require minimal changes since it already uses
|
|
the `$accountSettings()` method, which will be updated to handle the new
|
|
table structure transparently.
|
|
|
|
### **Medium Impact Components**
|
|
|
|
1. **`InviteOneAcceptView.vue`** - Identity fallback logic
|
|
- **Current**: Creates identity if no `activeDid` exists
|
|
- **Impact**: Fallback logic needs to check new table
|
|
- **Risk**: **MEDIUM** - Invite processing component
|
|
|
|
**Current Implementation:**
|
|
|
|
```vue
|
|
<script lang="ts">
|
|
export default class InviteOneAcceptView extends Vue {
|
|
activeDid = ""; // Component data
|
|
|
|
async mounted() {
|
|
// Load or generate identity
|
|
const settings = await this.$accountSettings();
|
|
this.activeDid = settings.activeDid || "";
|
|
|
|
// Identity creation should be handled by router guard, but keep as fallback for deep links
|
|
if (!this.activeDid) {
|
|
logger.info(
|
|
"[InviteOneAcceptView] No active DID found, creating identity as fallback",
|
|
);
|
|
this.activeDid = await generateSaveAndActivateIdentity();
|
|
}
|
|
// ... rest of initialization
|
|
}
|
|
}
|
|
</script>
|
|
```
|
|
|
|
**Required Changes:**
|
|
|
|
```vue
|
|
<script lang="ts">
|
|
export default class InviteOneAcceptView extends Vue {
|
|
activeDid = ""; // Component data still needed
|
|
|
|
async mounted() {
|
|
// This will automatically work if $accountSettings() is updated
|
|
const settings = await this.$accountSettings();
|
|
this.activeDid = settings.activeDid || "";
|
|
|
|
// Fallback logic remains the same - the API layer handles the new table
|
|
if (!this.activeDid) {
|
|
logger.info(
|
|
"[InviteOneAcceptView] No active DID found, creating identity as fallback",
|
|
);
|
|
this.activeDid = await generateSaveAndActivateIdentity();
|
|
}
|
|
// ... rest of initialization
|
|
}
|
|
}
|
|
</script>
|
|
```
|
|
|
|
**Key Insight**: This component will work automatically since it uses
|
|
`$accountSettings()`. The fallback logic doesn't need changes.
|
|
|
|
2. **`ClaimView.vue`** - Settings retrieval
|
|
- **Current**: Gets `activeDid` from `$accountSettings()`
|
|
- **Impact**: Will automatically work if API is updated
|
|
- **Risk**: **LOW** - Depends on API layer updates
|
|
|
|
**Current Implementation:**
|
|
|
|
```vue
|
|
<script lang="ts">
|
|
export default class ClaimView extends Vue {
|
|
activeDid = ""; // Component data
|
|
|
|
async created() {
|
|
const settings = await this.$accountSettings();
|
|
this.activeDid = settings.activeDid || "";
|
|
this.apiServer = settings.apiServer || "";
|
|
// ... rest of initialization
|
|
}
|
|
|
|
// Helper method that uses activeDid
|
|
didInfo(did: string): string {
|
|
return serverUtil.didInfo(
|
|
did,
|
|
this.activeDid, // Uses component data
|
|
this.allMyDids,
|
|
this.allContacts,
|
|
);
|
|
}
|
|
}
|
|
</script>
|
|
```
|
|
|
|
**Required Changes:**
|
|
|
|
```vue
|
|
<script lang="ts">
|
|
export default class ClaimView extends Vue {
|
|
activeDid = ""; // Component data still needed
|
|
|
|
async created() {
|
|
// This will automatically work if $accountSettings() is updated
|
|
const settings = await this.$accountSettings();
|
|
this.activeDid = settings.activeDid || "";
|
|
this.apiServer = settings.apiServer || "";
|
|
// ... rest of initialization
|
|
}
|
|
|
|
// No changes needed - method will work automatically
|
|
didInfo(did: string): string {
|
|
return serverUtil.didInfo(
|
|
did,
|
|
this.activeDid, // Uses component data
|
|
this.allMyDids,
|
|
this.allContacts,
|
|
);
|
|
}
|
|
}
|
|
</script>
|
|
```
|
|
|
|
**Key Insight**: This component requires zero changes since it already
|
|
uses the proper API method. It's the lowest risk component.
|
|
|
|
3. **`ContactAmountsView.vue`** - Uses phased-out method
|
|
- **Current**: Uses `$getSettings(MASTER_SETTINGS_KEY)` (being phased out)
|
|
- **Impact**: **NO CHANGES REQUIRED** - Will be updated when migrating to `getMasterSettings`
|
|
- **Risk**: **LOW** - Part of planned refactoring, not migration-specific
|
|
|
|
**Note**: This component will be updated as part of the broader refactoring to
|
|
replace `$getSettings(MASTER_SETTINGS_KEY)` with `getMasterSettings()`, which
|
|
is separate from the activeDid migration. The migration plan focuses only on
|
|
components that require immediate changes for the active_identity table.
|
|
|
|
### **Service Layer Impact**
|
|
|
|
1. **`WebPlatformService.ts`**
|
|
- **Current**: Direct SQL queries to settings table
|
|
- **Impact**: Must add `active_identity` table queries
|
|
- **Risk**: **HIGH** - Core web platform service
|
|
|
|
2. **`CapacitorPlatformService.ts`**
|
|
- **Current**: Similar direct SQL access
|
|
- **Impact**: Same updates as web service
|
|
- **Risk**: **HIGH** - Mobile platform service
|
|
|
|
3. **`PlatformServiceMixin.ts`**
|
|
- **Current**: Core methods like `$accountSettings()`, `$saveSettings()`
|
|
- **Impact**: Major refactoring required
|
|
- **Risk**: **CRITICAL** - Used by 50+ components
|
|
|
|
### **API Contract Changes**
|
|
|
|
1. **`$saveSettings()` method**
|
|
- **Current**: Updates `settings.activeDid`
|
|
- **New**: Updates `active_identity.activeDid`
|
|
- **Impact**: All components using this method
|
|
|
|
2. **`$updateActiveDid()` method**
|
|
- **Current**: Internal tracking only
|
|
- **New**: Database persistence required
|
|
- **Impact**: Identity switching logic
|
|
|
|
### **Master Settings Functions Impact**
|
|
|
|
1. **`retrieveSettingsForDefaultAccount()` function**
|
|
- **Current**: Returns settings with `activeDid` field from master settings
|
|
- **New**: Returns settings without `activeDid` field
|
|
- **Impact**: **HIGH** - Used by migration scripts and core database utilities
|
|
- **Location**: `src/db/databaseUtil.ts:148`
|
|
|
|
2. **`$getMergedSettings()` method**
|
|
- **Current**: Merges default and account settings, includes `activeDid` from defaults
|
|
- **New**: Merges settings without `activeDid`, adds from `active_identity` table
|
|
- **Impact**: **HIGH** - Core method used by `$accountSettings()`
|
|
- **Location**: `src/utils/PlatformServiceMixin.ts:485`
|
|
|
|
**Note**: `$getSettings(MASTER_SETTINGS_KEY)` is being phased out in favor of `getMasterSettings`,
|
|
so it doesn't require updates for this migration.
|
|
|
|
### **New `getMasterSettings()` Function**
|
|
|
|
Since we're phasing out `$getSettings(MASTER_SETTINGS_KEY)`, this migration
|
|
provides an opportunity to implement the new `getMasterSettings()` function
|
|
that will handle the active_identity table integration from the start:
|
|
|
|
```typescript
|
|
// New getMasterSettings function to replace phased-out $getSettings
|
|
async getMasterSettings(): Promise<Settings> {
|
|
try {
|
|
// Get master settings without activeDid
|
|
const result = await this.$dbQuery(
|
|
"SELECT id, accountDid, apiServer, filterFeedByNearby, filterFeedByVisible, " +
|
|
"finishedOnboarding, firstName, hideRegisterPromptOnNewContact, isRegistered, " +
|
|
"lastName, lastAckedOfferToUserJwtId, lastAckedOfferToUserProjectsJwtId, " +
|
|
"lastNotifiedClaimId, lastViewedClaimId, notifyingNewActivityTime, " +
|
|
"notifyingReminderMessage, notifyingReminderTime, partnerApiServer, " +
|
|
"passkeyExpirationMinutes, profileImageUrl, searchBoxes, showContactGivesInline, " +
|
|
"showGeneralAdvanced, showShortcutBvc, vapid, warnIfProdServer, warnIfTestServer, " +
|
|
"webPushServer FROM settings WHERE id = ?",
|
|
[MASTER_SETTINGS_KEY]
|
|
);
|
|
|
|
if (!result?.values?.length) {
|
|
return DEFAULT_SETTINGS;
|
|
}
|
|
|
|
const settings = this._mapColumnsToValues(result.columns, result.values)[0] as Settings;
|
|
|
|
// Handle JSON field parsing
|
|
if (settings.searchBoxes) {
|
|
settings.searchBoxes = this._parseJsonField(settings.searchBoxes, []);
|
|
}
|
|
|
|
// Get activeDid from separate table
|
|
const activeIdentityResult = await this.$dbQuery(
|
|
"SELECT activeDid FROM active_identity WHERE id = 1"
|
|
);
|
|
|
|
if (activeIdentityResult?.values?.length) {
|
|
settings.activeDid = activeIdentityResult.values[0][0] as string;
|
|
}
|
|
|
|
return settings;
|
|
} catch (error) {
|
|
logger.error(`[Settings Trace] ❌ Failed to get master settings:`, { error });
|
|
return DEFAULT_SETTINGS;
|
|
}
|
|
}
|
|
```
|
|
|
|
### **Testing Impact**
|
|
|
|
1. **Unit Tests**
|
|
- All platform service methods
|
|
- PlatformServiceMixin methods
|
|
- Database migration scripts
|
|
|
|
2. **Integration Tests**
|
|
- Component behavior with new data source
|
|
- Identity switching workflows
|
|
- Settings persistence
|
|
|
|
3. **Platform Tests**
|
|
- Web, Electron, iOS, Android
|
|
- Cross-platform data consistency
|
|
- Migration success on all platforms
|
|
|
|
### **Performance Impact**
|
|
|
|
1. **Additional Table Join**
|
|
- Settings queries now require active_identity table
|
|
- Potential performance impact on frequent operations
|
|
- Need for proper indexing
|
|
|
|
2. **Caching Considerations**
|
|
- ActiveDid changes trigger cache invalidation
|
|
- Component re-rendering on identity switches
|
|
- Memory usage for additional table data
|
|
|
|
### **Risk Assessment by Component Type**
|
|
|
|
- **Critical Risk**: PlatformServiceMixin, Platform Services
|
|
- **High Risk**: Identity-related components, views using `$accountSettings()`
|
|
- **Medium Risk**: Components with direct settings access, identity management
|
|
- **Low Risk**: Components using only basic settings, utility components,
|
|
prop-based components, components using phased-out methods
|
|
|
|
### **Migration Timeline Impact**
|
|
|
|
- **Phase 1**: Schema Creation - No component impact
|
|
- **Phase 2**: Data Migration - No component impact
|
|
- **Phase 3**: API Updates - All components affected
|
|
- **Phase 4**: Cleanup - No component impact
|
|
|
|
### **Update Priority Order**
|
|
|
|
1. **PlatformServiceMixin** - Core dependency for most components
|
|
2. **Platform Services** - Ensure data access layer works
|
|
3. **Identity Components** - Verify core functionality
|
|
4. **Settings-Dependent Views** - Update in dependency order
|
|
5. **Utility Components** - Final cleanup and testing
|
|
|
|
## Deferred for depth
|
|
|
|
- Advanced identity management features enabled by this change
|
|
- Performance optimization strategies for the new table structure
|
|
- Future schema evolution planning
|
|
- Advanced rollback and recovery procedures
|