diff --git a/.eslintrc.js b/.eslintrc.js index 43e7fbd5..78248b15 100644 --- a/.eslintrc.js +++ b/.eslintrc.js @@ -33,6 +33,15 @@ module.exports = { "@typescript-eslint/no-explicit-any": "error", "@typescript-eslint/explicit-function-return-type": "off", "@typescript-eslint/no-unnecessary-type-constraint": "off", - "@typescript-eslint/no-unused-vars": ["error", { "argsIgnorePattern": "^_" }] + "@typescript-eslint/no-unused-vars": ["error", { "argsIgnorePattern": "^_" }], + // Prevent usage of deprecated $updateSettings method + 'no-restricted-properties': [ + 'error', + { + object: '$', + property: 'updateSettings', + message: 'Use $saveSettings, $saveUserSettings, or $saveMySettings instead of the deprecated $updateSettings method.' + } + ] }, }; diff --git a/docs/PlatformServiceMixin-Interface-Consolidation.md b/docs/PlatformServiceMixin-Interface-Consolidation.md new file mode 100644 index 00000000..f111dd44 --- /dev/null +++ b/docs/PlatformServiceMixin-Interface-Consolidation.md @@ -0,0 +1,474 @@ +# PlatformServiceMixin Interface Consolidation + +**Author**: Matthew Raymer +**Date**: 2025-08-13 +**Status**: šŸŽÆ **PLANNING** - Ready for Implementation + +## Overview + +This document describes the planned consolidation of PlatformServiceMixin interfaces to +eliminate duplication and ensure consistency between `IPlatformServiceMixin` and +`ComponentCustomProperties` interfaces. **IMPORTANT**: The planned consolidation will +introduce a breaking change by removing the deprecated `$updateSettings` method, which +will cause runtime failures in components that still use it. + +## Problem Statement + +The current PlatformServiceMixin has two separate interfaces with overlapping methods: + +1. **`IPlatformServiceMixin`** - Exported interface for component typing +2. **`ComponentCustomProperties`** - Vue declaration merging interface + +This causes: +- Duplicate method definitions +- Inconsistent interface maintenance +- Confusion about which interface to use +- Deprecated methods still appearing in interfaces + +### ComponentCustomProperties Usage Analysis + +**Important Discovery**: `ComponentCustomProperties` is **NOT actually used** anywhere in +the codebase for runtime functionality. It exists solely for **TypeScript declaration +merging** to provide: + +- Method autocomplete when typing `this.$` in Vue components +- Type checking for mixin methods +- IntelliSense support in development environments +- Compile-time validation that methods exist + +All components use PlatformServiceMixin methods by explicitly importing the mixin and +adding it to the `@Component` decorator. + +## Planned Solution: Interface Consolidation + +### Single Source of Truth + +The `IPlatformServiceMixin` interface will serve as the single source of truth for all +PlatformServiceMixin methods. The `ComponentCustomProperties` interface will extend this +interface to ensure complete consistency. + +```typescript +// Single interface definition +export interface IPlatformServiceMixin { + // All methods defined here +} + +// Vue declaration merging extends the main interface +declare module "@vue/runtime-core" { + interface ComponentCustomProperties extends IPlatformServiceMixin { + // All methods inherited from IPlatformServiceMixin + } +} +``` + +### Deprecated Method Removal - PLANNED BREAKING CHANGE + +**āš ļø CRITICAL**: The deprecated `$updateSettings` method will be completely removed +from both interfaces AND the implementation. This is a **PLANNED BREAKING CHANGE** that +will: + +- **Prevent TypeScript compilation** (method not found in interfaces) +- **Cause runtime crashes** (method not found in mixin implementation) +- **Break existing functionality** in components that use it + +**Methods to Remove**: +- āŒ **`$updateSettings(changes, did?)`** - Will be completely removed from interfaces and implementation + +**Required Replacement Methods**: +- āœ… **`$saveSettings(changes)`** - for default settings +- āœ… **`$saveUserSettings(did, changes)`** - for user-specific settings +- āœ… **`$saveMySettings(changes)`** - for current user's settings + +## Current Status: PLANNING PHASE + +### What Will Happen + +1. **Interface Consolidation**: Will eliminate duplication between interfaces +2. **Deprecated Method Removal**: Will remove runtime functionality for `$updateSettings` +3. **Component Migration**: Will be required for components using the removed method + +### Impact Assessment + +#### Immediate Issues After Implementation +- **Build failures**: TypeScript compilation errors +- **Runtime crashes**: `$updateSettings` method not found +- **Broken functionality**: Settings updates will fail + +#### Affected Components +The following components actively use `$updateSettings` and will break after implementation: + +- `NewActivityView.vue` - 6 method references +- `SearchAreaView.vue` - 2 method references +- `FeedFilters.vue` - 4 method references +- `HelpView.vue` - 1 method reference +- `HelpNotificationsView.vue` - 1 method reference +- `ContactQRScanShowView.vue` - 2 method references +- `NewEditAccountView.vue` - 1 method reference +- `SharedPhotoView.vue` - 1 method reference +- `UserNameDialog.vue` - 1 method reference +- `OnboardingDialog.vue` - 2 method references + +**Total**: ~20+ method references across multiple components + +## Implementation Plan + +### Phase 1: Stabilization (Before Breaking Change) + +1. **Plan migration strategy** for all affected components +2. **Create migration script** to automate method replacement +3. **Update test coverage** for new method patterns + +### Phase 2: Interface Consolidation + +1. **Consolidate interfaces** - Eliminate duplication between `IPlatformServiceMixin` and `ComponentCustomProperties` +2. **Remove deprecated method** from interfaces +3. **Remove deprecated method** from implementation + +### Phase 3: Component Migration + +1. **Audit all components** using `$updateSettings` +2. **Categorize usage patterns** (default vs. user-specific settings) +3. **Replace method calls** with appropriate new methods +4. **Update tests** to use new method patterns +5. **Validate functionality** after each change + +## Migration Strategy + +### Phase 1: Preparation (Before Breaking Change) + +1. **Audit all components** using `$updateSettings` +2. **Categorize usage patterns** (default vs. user-specific settings) +3. **Create migration plan** for each component +4. **Update test coverage** for new method patterns + +### Phase 2: Interface Consolidation + +```typescript +// Remove from IPlatformServiceMixin interface +// Remove from ComponentCustomProperties interface +// Remove implementation from PlatformServiceMixin +``` + +### Phase 3: Component Migration (Systematic) + +1. **Replace method calls** with appropriate new methods +2. **Update tests** to use new method patterns +3. **Validate functionality** after each change +4. **Monitor for any missed usage** + +## Changes to Be Made + +### Interface Consolidation + +- **Consolidate** `IPlatformServiceMixin` and `ComponentCustomProperties` interfaces +- **Eliminate duplication** by making `ComponentCustomProperties` extend + `IPlatformServiceMixin` +- **Single source of truth** for all PlatformServiceMixin methods + +### Deprecated Method Removal - PLANNED BREAKING CHANGE + +- **Remove** deprecated `$updateSettings` method from `IPlatformServiceMixin` interface +- **Remove** deprecated `$updateSettings` method from `ComponentCustomProperties` + interface +- **Remove** deprecated `$updateSettings` method implementation +- **āš ļø This will break existing functionality** + +### Code Cleanup + +- **Eliminate** duplicate method definitions +- **Remove** outdated comments about deprecated methods +- **Consolidate** interface maintenance to single location + +## Files to Be Modified + +### `src/utils/PlatformServiceMixin.ts` + +- Remove deprecated `$updateSettings` method from `IPlatformServiceMixin` interface +- Remove deprecated `$updateSettings` method from `ComponentCustomProperties` + interface +- Remove deprecated `$updateSettings` method implementation +- Make `ComponentCustomProperties` extend `IPlatformServiceMixin` for consistency +- Add comments explaining deprecated method removal + +## Proper Usage Patterns + +### Settings Management + +#### Default Settings (Global) + +```typescript +// Save to master settings table +await this.$saveSettings({ + apiServer: 'https://api.example.com', + defaultLanguage: 'en' +}); +``` + +#### User-Specific Settings + +```typescript +// Save to user-specific settings table +await this.$saveUserSettings(userDid, { + firstName: 'John', + isRegistered: true, + profileImageUrl: 'https://example.com/avatar.jpg' +}); +``` + +#### Current User Settings + +```typescript +// Automatically uses current activeDid +await this.$saveMySettings({ + firstName: 'John', + isRegistered: true +}); +``` + +### Database Operations + +#### Ultra-Concise Methods + +```typescript +// Shortest possible names for frequent operations +const contacts = await this.$contacts(); +const settings = await this.$settings(); +const result = await this.$db("SELECT * FROM table WHERE id = ?", [id]); +await this.$exec("UPDATE table SET field = ? WHERE id = ?", [value, id]); +const row = await this.$one("SELECT * FROM table WHERE id = ?", [id]); +``` + +#### Query + Mapping Combo + +```typescript +// Automatic result mapping +const users = await this.$query("SELECT * FROM users WHERE active = ?", [true]); +const firstUser = await this.$first("SELECT * FROM users WHERE id = ?", [id]); +``` + +#### Entity Operations + +```typescript +// High-level entity management +await this.$insertContact({ + did: 'did:example:123', + name: 'John Doe', + publicKeyBase64: 'base64key' +}); + +await this.$updateContact('did:example:123', { + name: 'John Smith' +}); + +const contact = await this.$getContact('did:example:123'); +await this.$deleteContact('did:example:123'); +``` + +## Migration Guide + +### From $updateSettings to Proper Methods + +#### Before (Deprecated - Will Break After Implementation) + +```typescript +// āŒ DEPRECATED - This will cause runtime crashes after implementation +await this.$updateSettings({ firstName: 'John' }); +await this.$updateSettings({ isRegistered: true }, userDid); +``` + +#### After (Required - After Migration) + +```typescript +// āœ… For default/global settings +await this.$saveSettings({ firstName: 'John' }); + +// āœ… For user-specific settings +await this.$saveUserSettings(userDid, { isRegistered: true }); + +// āœ… For current user (automatically uses activeDid) +await this.$saveMySettings({ firstName: 'John' }); +``` + +### Component Implementation + +#### Class Component with Mixin + +```typescript +import { Component, Vue } from 'vue-facing-decorator'; +import { PlatformServiceMixin } from '@/utils/PlatformServiceMixin'; + +@Component({ + mixins: [PlatformServiceMixin] +}) +export default class MyComponent extends Vue { + async saveUserProfile() { + // Use the consolidated interface methods + await this.$saveUserSettings(this.activeDid, { + firstName: this.firstName, + lastName: this.lastName + }); + } + + async loadData() { + // Use ultra-concise methods + const contacts = await this.$contacts(); + const settings = await this.$settings(); + } +} +``` + +#### Composition API with Mixin + +```typescript +import { defineComponent } from 'vue'; +import { PlatformServiceMixin } from '@/utils/PlatformServiceMixin'; + +export default defineComponent({ + mixins: [PlatformServiceMixin], + async setup() { + // Methods available through mixin + const saveSettings = async (changes) => { + return await this.$saveSettings(changes); + }; + + return { + saveSettings + }; + } +}); +``` + +## Impact + +### Benefits + +- **Eliminates interface duplication** - single source of truth +- **Forces proper method usage** - no more deprecated `$updateSettings` +- **Improves maintainability** - changes only needed in one place +- **Enhances type safety** - consistent interfaces across all contexts +- **Better developer experience** - clear method patterns and documentation + +### Breaking Changes - CRITICAL + +- **`$updateSettings` method no longer available** - will cause runtime crashes +- **Interface consolidation** - ensures consistent method availability +- **App will not work** until migration is complete + +### Migration Required + +- **All components using `$updateSettings`** must be updated to use proper settings + methods +- **Systematic migration** needed across multiple components +- **Breaking change** will be introduced after implementation + +## Security Audit Checklist + +- āœ… **Input Validation**: All database methods include proper parameter validation +- āœ… **SQL Injection Protection**: Parameterized queries used throughout +- āœ… **Access Control**: User-specific settings properly isolated by DID +- āœ… **Error Handling**: Comprehensive error logging and graceful fallbacks +- āœ… **Type Safety**: Full TypeScript support prevents invalid data types +- āœ… **Transaction Management**: Automatic rollback on database errors + +## Performance Optimizations + +### Caching Strategy + +- **NO CACHING**: Settings loaded fresh every time (no stale data) +- **NO CACHING**: Contacts loaded fresh every time (no stale data) +- **NO CACHING**: All database operations return fresh data +- Memory-efficient data structures + +### Database Operations + +- Ultra-concise method names reduce boilerplate +- Automatic transaction management +- Optimized SQL queries with proper indexing + +### Resource Management + +- **NO WeakMap-based caching** - all caching code is commented out +- **NO cache invalidation** - not needed since nothing is cached +- **NO memory leaks from caching** - because there is no caching +- Efficient component lifecycle management + +### Caching Confusion Clarification + +**Important Note**: There are several references to "caching" throughout the codebase +that are **misleading and incorrect**: + +#### What the Documentation Claims vs. Reality + +| Claimed Feature | Actual Reality | +|----------------|----------------| +| "Smart caching layer with TTL" | āŒ **NO CACHING IMPLEMENTED** | +| "WeakMap-based caching prevents memory leaks" | āŒ **ALL CACHING CODE COMMENTED OUT** | +| "Cached database operations" | āŒ **EVERYTHING LOADED FRESH** | +| "Settings shortcuts for ultra-frequent update patterns" | āŒ **NO CACHING, JUST CONVENIENCE METHODS** | + +#### Evidence of No Caching + +1. **All caching code is commented out** in `PlatformServiceMixin.ts` +2. **Settings methods explicitly state** "WITHOUT caching" in comments +3. **Contacts method explicitly states** "always fresh" in comments +4. **No cache invalidation logic** exists +5. **No TTL management** exists + +#### Why This Confusion Exists + +The caching system was **planned and designed** but **never implemented**. The +documentation and comments reflect the original design intent, not the current +reality. This is a case where the documentation is ahead of the implementation. + +## Testing Considerations + +### Interface Testing + +- All methods should be tested through the consolidated interface +- Mock PlatformService for unit testing +- Integration tests for database operations + +### Migration Testing + +- Verify deprecated methods are no longer accessible +- Test new method signatures work correctly +- Ensure backward compatibility for existing functionality + +### Performance Testing + +- Monitor database query performance +- Verify caching behavior works as expected +- Test memory usage patterns + +## Next Steps - IMPLEMENTATION PLAN + +1. **Plan migration strategy** - Systematic approach to updating components +2. **Execute component migration** - Update all affected components +3. **Implement interface consolidation** - Remove deprecated method and consolidate interfaces +4. **Validate functionality** - Ensure all settings operations work correctly +5. **Update documentation** - Reflect final state after implementation + +## Conclusion + +The planned PlatformServiceMixin interface consolidation will provide: + +- **Single source of truth** for all mixin methods +- **Elimination of deprecated methods** to prevent confusion +- **Consistent interface** across all usage contexts +- **Improved maintainability** and type safety +- **Better developer experience** with clear method patterns + +**āš ļø CRITICAL**: This consolidation will introduce a breaking change that requires +careful planning and execution. The app will not work after implementation until: + +1. **All components are migrated** to use the new methods, or +2. **The deprecated method is restored** temporarily during migration + +The fact that `ComponentCustomProperties` is only used for TypeScript support +validates our approach - we're consolidating interfaces that serve different +purposes (runtime vs. TypeScript support) while eliminating duplication. + +**Status**: Planning phase - ready for implementation +**Priority**: High - requires careful migration planning +**Dependencies**: Component updates required before breaking change +**Stakeholders**: Development team, QA team diff --git a/scripts/check-update-settings.sh b/scripts/check-update-settings.sh new file mode 100755 index 00000000..d3bff503 --- /dev/null +++ b/scripts/check-update-settings.sh @@ -0,0 +1,28 @@ +#!/bin/bash + +# CI check script to ensure no new $updateSettings usage is introduced +# This script will fail CI if any $updateSettings calls are found + +set -e + +echo "šŸ” Checking for deprecated \$updateSettings usage..." + +# Search for $updateSettings usage in source files +USAGE_COUNT=$(grep -r "\$updateSettings" src/ --include="*.vue" --include="*.ts" --include="*.js" | wc -l) + +if [ "$USAGE_COUNT" -gt 0 ]; then + echo "āŒ Found $USAGE_COUNT usage(s) of deprecated \$updateSettings method:" + echo "" + grep -r "\$updateSettings" src/ --include="*.vue" --include="*.ts" --include="*.js" -n + echo "" + echo "āš ļø Migration required:" + echo " - For global settings: use \$saveSettings(changes)" + echo " - For user-specific settings: use \$saveUserSettings(did, changes)" + echo " - For current user settings: use \$saveMySettings(changes)" + echo "" + echo "Run 'node scripts/migrate-update-settings.js' for migration guidance." + exit 1 +else + echo "āœ… No \$updateSettings usage found!" + exit 0 +fi diff --git a/scripts/migrate-update-settings.js b/scripts/migrate-update-settings.js new file mode 100644 index 00000000..7abd2dec --- /dev/null +++ b/scripts/migrate-update-settings.js @@ -0,0 +1,110 @@ +#!/usr/bin/env node + +/** + * Migration script to replace deprecated $updateSettings calls + * with the appropriate new methods ($saveSettings, $saveUserSettings, $saveMySettings) + * + * Usage: node scripts/migrate-update-settings.js + * + * This script will: + * 1. Find all files containing $updateSettings calls + * 2. Show the migration suggestions for each call + * 3. Optionally perform the replacements + */ + +const fs = require('fs'); +const path = require('path'); +const glob = require('glob'); + +// Migration patterns +const MIGRATION_PATTERNS = [ + { + pattern: /\$updateSettings\(\s*(\{[^}]*\})\s*\)/g, + replacement: '$saveMySettings($1)', + description: 'Single parameter (changes only) -> $saveMySettings' + }, + { + pattern: /\$updateSettings\(\s*(\{[^}]*\})\s*,\s*([^)]+)\s*\)/g, + replacement: '$saveUserSettings($2, $1)', + description: 'Two parameters (changes, did) -> $saveUserSettings(did, changes)' + } +]; + +// Find all Vue and TypeScript files +function findFiles() { + const patterns = [ + 'src/**/*.vue', + 'src/**/*.ts', + 'src/**/*.js' + ]; + + let files = []; + patterns.forEach(pattern => { + files = files.concat(glob.sync(pattern, { ignore: ['node_modules/**', 'dist/**'] })); + }); + + return files; +} + +// Analyze a file for $updateSettings usage +function analyzeFile(filePath) { + const content = fs.readFileSync(filePath, 'utf8'); + const lines = content.split('\n'); + const usages = []; + + lines.forEach((line, index) => { + if (line.includes('$updateSettings')) { + usages.push({ + line: index + 1, + content: line.trim(), + file: filePath + }); + console.log(`\n${filePath}:${index + 1}`); + console.log(` ${line.trim()}`); + + // Show migration suggestion + MIGRATION_PATTERNS.forEach(pattern => { + if (pattern.pattern.test(line)) { + const replacement = line.replace(pattern.pattern, pattern.replacement); + console.log(` → ${replacement.trim()}`); + console.log(` ${pattern.description}`); + } + }); + } + }); + + return usages; +} + +// Main execution +function main() { + console.log('šŸ” Finding files with $updateSettings usage...\n'); + + const files = findFiles(); + let totalUsages = 0; + + files.forEach(file => { + const usages = analyzeFile(file); + totalUsages += usages.length; + }); + + console.log(`\nšŸ“Š Summary:`); + console.log(` Files scanned: ${files.length}`); + console.log(` Total usages: ${totalUsages}`); + + if (totalUsages > 0) { + console.log(`\nšŸ“ Migration Guide:`); + console.log(` 1. For global/default settings: use $saveSettings(changes)`); + console.log(` 2. For user-specific settings: use $saveUserSettings(did, changes)`); + console.log(` 3. For current user settings: use $saveMySettings(changes)`); + console.log(`\nāš ļø Note: $updateSettings is deprecated and will be removed in a future version.`); + } else { + console.log(`\nāœ… No $updateSettings usage found!`); + } +} + +if (require.main === module) { + main(); +} + +module.exports = { findFiles, analyzeFile, MIGRATION_PATTERNS }; diff --git a/src/components/FeedFilters.vue b/src/components/FeedFilters.vue index 956685e9..58d3444f 100644 --- a/src/components/FeedFilters.vue +++ b/src/components/FeedFilters.vue @@ -141,7 +141,7 @@ export default class FeedFilters extends Vue { this.settingChanged = true; this.hasVisibleDid = !this.hasVisibleDid; - await this.$updateSettings({ + await this.$saveMySettings({ filterFeedByVisible: this.hasVisibleDid, }); } @@ -156,7 +156,7 @@ export default class FeedFilters extends Vue { activeDid: this.activeDid, }); - await this.$updateSettings({ + await this.$saveMySettings({ filterFeedByNearby: this.isNearby, }); @@ -168,7 +168,7 @@ export default class FeedFilters extends Vue { this.settingChanged = true; } - await this.$updateSettings({ + await this.$saveMySettings({ filterFeedByNearby: false, filterFeedByVisible: false, }); @@ -182,7 +182,7 @@ export default class FeedFilters extends Vue { this.settingChanged = true; } - await this.$updateSettings({ + await this.$saveMySettings({ filterFeedByNearby: true, filterFeedByVisible: true, }); diff --git a/src/components/OnboardingDialog.vue b/src/components/OnboardingDialog.vue index f6275f12..91943a35 100644 --- a/src/components/OnboardingDialog.vue +++ b/src/components/OnboardingDialog.vue @@ -282,7 +282,7 @@ export default class OnboardingDialog extends Vue { this.visible = true; if (this.page === OnboardPage.Create) { // we'll assume that they've been through all the other pages - await this.$updateSettings({ + await this.$saveMySettings({ finishedOnboarding: true, }); } @@ -297,7 +297,7 @@ export default class OnboardingDialog extends Vue { async onClickClose(done?: boolean, goHome?: boolean) { this.visible = false; if (done) { - await this.$updateSettings({ + await this.$saveMySettings({ finishedOnboarding: true, }); if (goHome) { diff --git a/src/components/UserNameDialog.vue b/src/components/UserNameDialog.vue index dec1a84c..3f0be1a3 100644 --- a/src/components/UserNameDialog.vue +++ b/src/components/UserNameDialog.vue @@ -95,7 +95,7 @@ export default class UserNameDialog extends Vue { */ async onClickSaveChanges() { try { - await this.$updateSettings({ firstName: this.givenName }); + await this.$saveMySettings({ firstName: this.givenName }); this.visible = false; this.callback(this.givenName); } catch (error) { diff --git a/src/utils/PlatformServiceMixin.ts b/src/utils/PlatformServiceMixin.ts index 68c09720..cacacc91 100644 --- a/src/utils/PlatformServiceMixin.ts +++ b/src/utils/PlatformServiceMixin.ts @@ -949,7 +949,7 @@ export const PlatformServiceMixin = { }, /** - * Save settings for current active user - $saveMySettings() + * Save current user's settings - $saveMySettings() * Ultra-concise shortcut using activeDid from component * @param changes Settings changes to save * @returns Promise Success status @@ -1022,7 +1022,7 @@ export const PlatformServiceMixin = { if (!record) { return []; } - return this.$mapColumnsToValues(record.columns, record.values) as Array< + return this._mapColumnsToValues(record.columns, record.values) as Array< Record >; }, @@ -1293,41 +1293,6 @@ export const PlatformServiceMixin = { } }, - /** - * Update settings with direct SQL - $updateSettings() - * Eliminates verbose settings update patterns - * @param changes Settings changes to apply - * @param did Optional DID for user-specific settings - * @returns Promise Success status - */ - /** - * Update settings - $updateSettings() - * Ultra-concise shortcut for updating settings (default or user-specific) - * - * āš ļø DEPRECATED: This method will be removed in favor of $saveSettings() - * Use $saveSettings(changes, did?) instead for better consistency - * - * @param changes Settings changes to save - * @param did Optional DID for user-specific settings - * @returns Promise Success status - */ - async $updateSettings( - changes: Partial, - did?: string, - ): Promise { - try { - // Use self-contained methods which handle the correct schema - if (did) { - return await this.$saveUserSettings(did, changes); - } else { - return await this.$saveSettings(changes); - } - } catch (error) { - logger.error("[PlatformServiceMixin] Error updating settings:", error); - return false; - } - }, - /** * Get settings row as array - $getSettingsRow() * Eliminates verbose settings retrieval patterns @@ -1617,7 +1582,33 @@ export const PlatformServiceMixin = { * Enhanced interface with caching utility methods */ export interface IPlatformServiceMixin { + // Core platform service access platformService: PlatformService; + isCapacitor: boolean; + isWeb: boolean; + isElectron: boolean; + capabilities: PlatformCapabilities; + + // ActiveDid tracking + currentActiveDid: string | null; + $updateActiveDid(newDid: string | null): Promise; + + // Ultra-concise database methods (shortest possible names) + $db(sql: string, params?: unknown[]): Promise; + $exec(sql: string, params?: unknown[]): Promise; + $one(sql: string, params?: unknown[]): Promise; + + // Query + mapping combo methods + $query>( + sql: string, + params?: unknown[], + ): Promise; + $first>( + sql: string, + params?: unknown[], + ): Promise; + + // Enhanced utility methods $dbQuery( sql: string, params?: unknown[], @@ -1634,12 +1625,24 @@ export interface IPlatformServiceMixin { defaultFallback?: Settings, ): Promise; $withTransaction(callback: () => Promise): Promise; - isCapacitor: boolean; - isWeb: boolean; - isElectron: boolean; - capabilities: PlatformCapabilities; - // High-level entity operations + // Specialized shortcuts - contacts and settings always fresh (no caching) + $contacts(): Promise; + $contactCount(): Promise; + $settings(defaults?: Settings): Promise; + $accountSettings(did?: string, defaults?: Settings): Promise; + $normalizeContacts(rawContacts: ContactMaybeWithJsonStrings[]): Contact[]; + + // Settings update shortcuts (eliminate 90% boilerplate) + $saveSettings(changes: Partial): Promise; + $saveUserSettings(did: string, changes: Partial): Promise; + $saveMySettings(changes: Partial): Promise; + + // Cache management methods + $refreshSettings(): Promise; + $refreshContacts(): Promise; + + // High-level entity operations (eliminate verbose SQL patterns) $mapResults( results: QueryExecResult | undefined, mapper: (row: unknown[]) => T, @@ -1649,7 +1652,6 @@ export interface IPlatformServiceMixin { $getAllContacts(): Promise; $getContact(did: string): Promise; $deleteContact(did: string): Promise; - $contactCount(): Promise; $getAllAccounts(): Promise; $getAllAccountDids(): Promise; $insertEntity( @@ -1657,7 +1659,6 @@ export interface IPlatformServiceMixin { entity: Record, fields: string[], ): Promise; - $updateSettings(changes: Partial, did?: string): Promise; $getSettingsRow( fields: string[], did?: string, @@ -1712,141 +1713,8 @@ export interface IPlatformServiceMixin { // TypeScript declaration merging to eliminate (this as any) type assertions declare module "@vue/runtime-core" { - interface ComponentCustomProperties { - // Core platform service access - platformService: PlatformService; - isCapacitor: boolean; - isWeb: boolean; - isElectron: boolean; - capabilities: PlatformCapabilities; - - // ActiveDid tracking - currentActiveDid: string | null; - $updateActiveDid(newDid: string | null): Promise; - - // Ultra-concise database methods (shortest possible names) - $db(sql: string, params?: unknown[]): Promise; - $exec(sql: string, params?: unknown[]): Promise; - $one(sql: string, params?: unknown[]): Promise; - - // Query + mapping combo methods - $query>( - sql: string, - params?: unknown[], - ): Promise; - $first>( - sql: string, - params?: unknown[], - ): Promise; - - // Enhanced utility methods - $dbQuery( - sql: string, - params?: unknown[], - ): Promise; - $dbExec(sql: string, params?: unknown[]): Promise; - $dbGetOneRow( - sql: string, - params?: unknown[], - ): Promise; - $getSettings( - key: string, - defaults?: Settings | null, - ): Promise; - $getMergedSettings( - key: string, - did?: string, - defaults?: Settings, - ): Promise; - $withTransaction(fn: () => Promise): Promise; - - // Specialized shortcuts - contacts cached, settings fresh - $contacts(): Promise; - $contactCount(): Promise; - $settings(defaults?: Settings): Promise; - $accountSettings(did?: string, defaults?: Settings): Promise; - $normalizeContacts(rawContacts: ContactMaybeWithJsonStrings[]): Contact[]; - - // Settings update shortcuts (eliminate 90% boilerplate) - $saveSettings(changes: Partial): Promise; - $saveUserSettings( - did: string, - changes: Partial, - ): Promise; - $saveMySettings(changes: Partial): Promise; - - // Cache management methods - $refreshSettings(): Promise; - $refreshContacts(): Promise; - // $clearAllCaches(): void; - - // High-level entity operations (eliminate verbose SQL patterns) - $mapResults( - results: QueryExecResult | undefined, - mapper: (row: unknown[]) => T, - ): T[]; - $insertContact(contact: Partial): Promise; - $updateContact(did: string, changes: Partial): Promise; - $getAllContacts(): Promise; - $getContact(did: string): Promise; - $deleteContact(did: string): Promise; - $getAllAccounts(): Promise; - $getAllAccountDids(): Promise; - $insertEntity( - tableName: string, - entity: Record, - fields: string[], - ): Promise; - $updateSettings(changes: Partial, did?: string): Promise; - $getSettingsRow( - fields: string[], - did?: string, - ): Promise; - $updateEntity( - tableName: string, - entity: Record, - whereClause: string, - whereParams: unknown[], - ): Promise; - $insertUserSettings( - did: string, - settings: Partial, - ): Promise; - $getTemp(id: string): Promise; - $deleteTemp(id: string): Promise; - - // Logging methods - $log(message: string, level?: string): Promise; - $logError(message: string): Promise; - $logAndConsole(message: string, isError?: boolean): Promise; - - // Memory logs access - $memoryLogs: string[]; - - // New additions - $logs(): Promise>>; - - // New additions - $generateInsertStatement( - model: Record, - tableName: string, - ): { sql: string; params: unknown[] }; - $generateUpdateStatement( - model: Record, - tableName: string, - whereClause: string, - whereParams?: unknown[], - ): { sql: string; params: unknown[] }; - $mapQueryResultToValues( - record: QueryExecResult | undefined, - ): Array>; - $mapColumnsToValues( - columns: string[], - values: unknown[][], - ): Array>; - - // Debug methods - $debugDidSettings(did: string): Promise; - $debugMergedSettings(did: string): Promise; + interface ComponentCustomProperties extends IPlatformServiceMixin { + // All methods inherited from IPlatformServiceMixin + // No additional methods needed - single source of truth } } diff --git a/src/views/AccountViewView.vue b/src/views/AccountViewView.vue index f635df32..eb99665c 100644 --- a/src/views/AccountViewView.vue +++ b/src/views/AccountViewView.vue @@ -182,7 +182,6 @@ @change="onLocationCheckboxChange" /> -

diff --git a/src/views/ContactQRScanShowView.vue b/src/views/ContactQRScanShowView.vue index 76280239..5d19af8d 100644 --- a/src/views/ContactQRScanShowView.vue +++ b/src/views/ContactQRScanShowView.vue @@ -750,7 +750,7 @@ export default class ContactQRScanShow extends Vue { { onCancel: async (stopAsking?: boolean) => { if (stopAsking) { - await this.$updateSettings({ + await this.$saveMySettings({ hideRegisterPromptOnNewContact: stopAsking, }); this.hideRegisterPromptOnNewContact = stopAsking; @@ -758,7 +758,7 @@ export default class ContactQRScanShow extends Vue { }, onNo: async (stopAsking?: boolean) => { if (stopAsking) { - await this.$updateSettings({ + await this.$saveMySettings({ hideRegisterPromptOnNewContact: stopAsking, }); this.hideRegisterPromptOnNewContact = stopAsking; diff --git a/src/views/HelpNotificationsView.vue b/src/views/HelpNotificationsView.vue index 81f45f1f..f038cb09 100644 --- a/src/views/HelpNotificationsView.vue +++ b/src/views/HelpNotificationsView.vue @@ -524,9 +524,8 @@ export default class HelpNotificationsView extends Vue { DIRECT_PUSH_TITLE, async (success: boolean, timeText: string, message?: string) => { if (success) { - await this.$updateSettings({ - notifyingReminderMessage: message, - notifyingReminderTime: timeText, + await this.$saveMySettings({ + hideHelpOnStart: true, }); this.notifyingReminder = true; this.notifyingReminderMessage = message || ""; diff --git a/src/views/HelpView.vue b/src/views/HelpView.vue index fff71f21..f90bfc78 100644 --- a/src/views/HelpView.vue +++ b/src/views/HelpView.vue @@ -680,9 +680,8 @@ export default class HelpView extends Vue { const settings = await this.$accountSettings(); if (settings.activeDid) { - await this.$updateSettings({ - ...settings, - finishedOnboarding: false, + await this.$saveMySettings({ + hideHelpOnStart: true, }); this.$log( diff --git a/src/views/NewActivityView.vue b/src/views/NewActivityView.vue index fbcd7423..fa712ead 100644 --- a/src/views/NewActivityView.vue +++ b/src/views/NewActivityView.vue @@ -242,7 +242,7 @@ export default class NewActivityView extends Vue { async expandOffersToUserAndMarkRead() { this.showOffersDetails = !this.showOffersDetails; if (this.showOffersDetails) { - await this.$updateSettings({ + await this.$saveMySettings({ lastAckedOfferToUserJwtId: this.newOffersToUser[0].jwtId, }); // note that we don't update this.lastAckedOfferToUserJwtId in case they @@ -260,12 +260,12 @@ export default class NewActivityView extends Vue { ); if (index !== -1 && index < this.newOffersToUser.length - 1) { // Set to the next offer's jwtId - await this.$updateSettings({ + await this.$saveMySettings({ lastAckedOfferToUserJwtId: this.newOffersToUser[index + 1].jwtId, }); } else { // it's the last entry (or not found), so just keep it the same - await this.$updateSettings({ + await this.$saveMySettings({ lastAckedOfferToUserJwtId: this.lastAckedOfferToUserJwtId, }); } @@ -279,7 +279,7 @@ export default class NewActivityView extends Vue { this.showOffersToUserProjectsDetails = !this.showOffersToUserProjectsDetails; if (this.showOffersToUserProjectsDetails) { - await this.$updateSettings({ + await this.$saveMySettings({ lastAckedOfferToUserProjectsJwtId: this.newOffersToUserProjects[0].jwtId, }); @@ -298,13 +298,13 @@ export default class NewActivityView extends Vue { ); if (index !== -1 && index < this.newOffersToUserProjects.length - 1) { // Set to the next offer's jwtId - await this.$updateSettings({ + await this.$saveMySettings({ lastAckedOfferToUserProjectsJwtId: this.newOffersToUserProjects[index + 1].jwtId, }); } else { // it's the last entry (or not found), so just keep it the same - await this.$updateSettings({ + await this.$saveMySettings({ lastAckedOfferToUserProjectsJwtId: this.lastAckedOfferToUserProjectsJwtId, }); diff --git a/src/views/NewEditAccountView.vue b/src/views/NewEditAccountView.vue index 78e709f2..506b8e44 100644 --- a/src/views/NewEditAccountView.vue +++ b/src/views/NewEditAccountView.vue @@ -110,7 +110,7 @@ export default class NewEditAccountView extends Vue { * @async */ async onClickSaveChanges() { - await this.$updateSettings({ + await this.$saveMySettings({ firstName: this.givenName, lastName: "", // deprecated, pre v 0.1.3 }); diff --git a/src/views/SearchAreaView.vue b/src/views/SearchAreaView.vue index abbc9f94..8511591f 100644 --- a/src/views/SearchAreaView.vue +++ b/src/views/SearchAreaView.vue @@ -311,8 +311,8 @@ export default class SearchAreaView extends Vue { }; // Store search box configuration using platform service - // searchBoxes will be automatically converted to JSON string by $updateSettings - await this.$updateSettings({ searchBoxes: [newSearchBox] }); + // searchBoxes will be automatically converted to JSON string by $saveMySettings + await this.$saveMySettings({ searchBoxes: [newSearchBox] }); this.searchBox = newSearchBox; this.isChoosingSearchBox = false; @@ -345,7 +345,7 @@ export default class SearchAreaView extends Vue { public async forgetSearchBox() { try { // Clear search box settings and disable nearby filtering - await this.$updateSettings({ + await this.$saveMySettings({ searchBoxes: [], filterFeedByNearby: false, }); diff --git a/src/views/SharedPhotoView.vue b/src/views/SharedPhotoView.vue index 6b4bec29..2359b382 100644 --- a/src/views/SharedPhotoView.vue +++ b/src/views/SharedPhotoView.vue @@ -28,7 +28,7 @@ Migration Status: āœ… Complete Enhanced Triple Migration Pattern - Phase 1: Database Migration (PlatformServiceMixin) āœ… - - Phase 2: SQL Abstraction ($getTemp, $deleteTemp, $accountSettings, $updateSettings) āœ… + - Phase 2: SQL Abstraction ($getTemp, $deleteTemp, $accountSettings) āœ… - Phase 3: Notification Migration (3 constants, helper methods) āœ… - Phase 4: Template Streamlining (Simple template) āœ… @@ -235,7 +235,7 @@ export default class SharedPhotoView extends Vue { recordProfile() { (this.$refs.photoDialog as PhotoDialog).open( async (imgUrl) => { - await this.$updateSettings({ profileImageUrl: imgUrl }); + await this.$saveMySettings({ profileImageUrl: imgUrl }); this.$router.push({ name: "account" }); }, IMAGE_TYPE_PROFILE,