docs: correct PlatformServiceMixin caching documentation and fix interface comments #166

Open
anomalist wants to merge 3 commits from platformservicemixin-interface-consolidation into master
  1. 11
      .eslintrc.js
  2. 474
      docs/PlatformServiceMixin-Interface-Consolidation.md
  3. 28
      scripts/check-update-settings.sh
  4. 110
      scripts/migrate-update-settings.js
  5. 8
      src/components/FeedFilters.vue
  6. 4
      src/components/OnboardingDialog.vue
  7. 2
      src/components/UserNameDialog.vue
  8. 166
      src/utils/PlatformServiceMixin.ts
  9. 1
      src/views/AccountViewView.vue
  10. 4
      src/views/ContactQRScanShowView.vue
  11. 5
      src/views/HelpNotificationsView.vue
  12. 5
      src/views/HelpView.vue
  13. 12
      src/views/NewActivityView.vue
  14. 2
      src/views/NewEditAccountView.vue
  15. 6
      src/views/SearchAreaView.vue
  16. 4
      src/views/SharedPhotoView.vue

11
.eslintrc.js

@ -33,6 +33,15 @@ module.exports = {
"@typescript-eslint/no-explicit-any": "error", "@typescript-eslint/no-explicit-any": "error",
"@typescript-eslint/explicit-function-return-type": "off", "@typescript-eslint/explicit-function-return-type": "off",
"@typescript-eslint/no-unnecessary-type-constraint": "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.'
}
]
}, },
}; };

474
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<User>("SELECT * FROM users WHERE active = ?", [true]);
const firstUser = await this.$first<User>("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

28
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

110
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 };

8
src/components/FeedFilters.vue

@ -141,7 +141,7 @@ export default class FeedFilters extends Vue {
this.settingChanged = true; this.settingChanged = true;
this.hasVisibleDid = !this.hasVisibleDid; this.hasVisibleDid = !this.hasVisibleDid;
await this.$updateSettings({ await this.$saveMySettings({
filterFeedByVisible: this.hasVisibleDid, filterFeedByVisible: this.hasVisibleDid,
}); });
} }
@ -156,7 +156,7 @@ export default class FeedFilters extends Vue {
activeDid: this.activeDid, activeDid: this.activeDid,
}); });
await this.$updateSettings({ await this.$saveMySettings({
filterFeedByNearby: this.isNearby, filterFeedByNearby: this.isNearby,
}); });
@ -168,7 +168,7 @@ export default class FeedFilters extends Vue {
this.settingChanged = true; this.settingChanged = true;
} }
await this.$updateSettings({ await this.$saveMySettings({
filterFeedByNearby: false, filterFeedByNearby: false,
filterFeedByVisible: false, filterFeedByVisible: false,
}); });
@ -182,7 +182,7 @@ export default class FeedFilters extends Vue {
this.settingChanged = true; this.settingChanged = true;
} }
await this.$updateSettings({ await this.$saveMySettings({
filterFeedByNearby: true, filterFeedByNearby: true,
filterFeedByVisible: true, filterFeedByVisible: true,
}); });

4
src/components/OnboardingDialog.vue

@ -282,7 +282,7 @@ export default class OnboardingDialog extends Vue {
this.visible = true; this.visible = true;
if (this.page === OnboardPage.Create) { if (this.page === OnboardPage.Create) {
// we'll assume that they've been through all the other pages // we'll assume that they've been through all the other pages
await this.$updateSettings({ await this.$saveMySettings({
finishedOnboarding: true, finishedOnboarding: true,
}); });
} }
@ -297,7 +297,7 @@ export default class OnboardingDialog extends Vue {
async onClickClose(done?: boolean, goHome?: boolean) { async onClickClose(done?: boolean, goHome?: boolean) {
this.visible = false; this.visible = false;
if (done) { if (done) {
await this.$updateSettings({ await this.$saveMySettings({
finishedOnboarding: true, finishedOnboarding: true,
}); });
if (goHome) { if (goHome) {

2
src/components/UserNameDialog.vue

@ -95,7 +95,7 @@ export default class UserNameDialog extends Vue {
*/ */
async onClickSaveChanges() { async onClickSaveChanges() {
try { try {
await this.$updateSettings({ firstName: this.givenName }); await this.$saveMySettings({ firstName: this.givenName });
this.visible = false; this.visible = false;
this.callback(this.givenName); this.callback(this.givenName);
} catch (error) { } catch (error) {

166
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 * Ultra-concise shortcut using activeDid from component
* @param changes Settings changes to save * @param changes Settings changes to save
* @returns Promise<boolean> Success status * @returns Promise<boolean> Success status
@ -1022,7 +1022,7 @@ export const PlatformServiceMixin = {
if (!record) { if (!record) {
return []; return [];
} }
return this.$mapColumnsToValues(record.columns, record.values) as Array< return this._mapColumnsToValues(record.columns, record.values) as Array<
Record<string, unknown> Record<string, unknown>
>; >;
}, },
@ -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<boolean> 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<boolean> Success status
*/
async $updateSettings(
changes: Partial<Settings>,
did?: string,
): Promise<boolean> {
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() * Get settings row as array - $getSettingsRow()
* Eliminates verbose settings retrieval patterns * Eliminates verbose settings retrieval patterns
@ -1617,102 +1582,6 @@ export const PlatformServiceMixin = {
* Enhanced interface with caching utility methods * Enhanced interface with caching utility methods
*/ */
export interface IPlatformServiceMixin { export interface IPlatformServiceMixin {
platformService: PlatformService;
$dbQuery(
sql: string,
params?: unknown[],
): Promise<QueryExecResult | undefined>;
$dbExec(sql: string, params?: unknown[]): Promise<DatabaseExecResult>;
$dbGetOneRow(sql: string, params?: unknown[]): Promise<unknown[] | undefined>;
$getSettings(
key: string,
fallback?: Settings | null,
): Promise<Settings | null>;
$getMergedSettings(
defaultKey: string,
accountDid?: string,
defaultFallback?: Settings,
): Promise<Settings>;
$withTransaction<T>(callback: () => Promise<T>): Promise<T>;
isCapacitor: boolean;
isWeb: boolean;
isElectron: boolean;
capabilities: PlatformCapabilities;
// High-level entity operations
$mapResults<T>(
results: QueryExecResult | undefined,
mapper: (row: unknown[]) => T,
): T[];
$insertContact(contact: Partial<Contact>): Promise<boolean>;
$updateContact(did: string, changes: Partial<Contact>): Promise<boolean>;
$getAllContacts(): Promise<Contact[]>;
$getContact(did: string): Promise<Contact | null>;
$deleteContact(did: string): Promise<boolean>;
$contactCount(): Promise<number>;
$getAllAccounts(): Promise<Account[]>;
$getAllAccountDids(): Promise<string[]>;
$insertEntity(
tableName: string,
entity: Record<string, unknown>,
fields: string[],
): Promise<boolean>;
$updateSettings(changes: Partial<Settings>, did?: string): Promise<boolean>;
$getSettingsRow(
fields: string[],
did?: string,
): Promise<unknown[] | undefined>;
$updateEntity(
tableName: string,
entity: Record<string, unknown>,
whereClause: string,
whereParams: unknown[],
): Promise<boolean>;
$insertUserSettings(
did: string,
settings: Partial<Settings>,
): Promise<boolean>;
$getTemp(id: string): Promise<Temp | null>;
$deleteTemp(id: string): Promise<boolean>;
// Logging methods
$log(message: string, level?: string): Promise<void>;
$logError(message: string): Promise<void>;
$logAndConsole(message: string, isError?: boolean): Promise<void>;
// Memory logs access
$memoryLogs: string[];
// New additions
$logs(): Promise<Array<Record<string, unknown>>>;
// New additions
$generateInsertStatement(
model: Record<string, unknown>,
tableName: string,
): { sql: string; params: unknown[] };
$generateUpdateStatement(
model: Record<string, unknown>,
tableName: string,
whereClause: string,
whereParams?: unknown[],
): { sql: string; params: unknown[] };
$mapQueryResultToValues(
record: QueryExecResult | undefined,
): Array<Record<string, unknown>>;
$mapColumnsToValues(
columns: string[],
values: unknown[][],
): Array<Record<string, unknown>>;
// Debug methods
$debugDidSettings(did: string): Promise<Settings | null>;
$debugMergedSettings(did: string): Promise<void>;
}
// TypeScript declaration merging to eliminate (this as any) type assertions
declare module "@vue/runtime-core" {
interface ComponentCustomProperties {
// Core platform service access // Core platform service access
platformService: PlatformService; platformService: PlatformService;
isCapacitor: boolean; isCapacitor: boolean;
@ -1745,22 +1614,19 @@ declare module "@vue/runtime-core" {
params?: unknown[], params?: unknown[],
): Promise<QueryExecResult | undefined>; ): Promise<QueryExecResult | undefined>;
$dbExec(sql: string, params?: unknown[]): Promise<DatabaseExecResult>; $dbExec(sql: string, params?: unknown[]): Promise<DatabaseExecResult>;
$dbGetOneRow( $dbGetOneRow(sql: string, params?: unknown[]): Promise<unknown[] | undefined>;
sql: string,
params?: unknown[],
): Promise<unknown[] | undefined>;
$getSettings( $getSettings(
key: string, key: string,
defaults?: Settings | null, fallback?: Settings | null,
): Promise<Settings | null>; ): Promise<Settings | null>;
$getMergedSettings( $getMergedSettings(
key: string, defaultKey: string,
did?: string, accountDid?: string,
defaults?: Settings, defaultFallback?: Settings,
): Promise<Settings>; ): Promise<Settings>;
$withTransaction<T>(fn: () => Promise<T>): Promise<T>; $withTransaction<T>(callback: () => Promise<T>): Promise<T>;
// Specialized shortcuts - contacts cached, settings fresh // Specialized shortcuts - contacts and settings always fresh (no caching)
$contacts(): Promise<Contact[]>; $contacts(): Promise<Contact[]>;
$contactCount(): Promise<number>; $contactCount(): Promise<number>;
$settings(defaults?: Settings): Promise<Settings>; $settings(defaults?: Settings): Promise<Settings>;
@ -1769,16 +1635,12 @@ declare module "@vue/runtime-core" {
// Settings update shortcuts (eliminate 90% boilerplate) // Settings update shortcuts (eliminate 90% boilerplate)
$saveSettings(changes: Partial<Settings>): Promise<boolean>; $saveSettings(changes: Partial<Settings>): Promise<boolean>;
$saveUserSettings( $saveUserSettings(did: string, changes: Partial<Settings>): Promise<boolean>;
did: string,
changes: Partial<Settings>,
): Promise<boolean>;
$saveMySettings(changes: Partial<Settings>): Promise<boolean>; $saveMySettings(changes: Partial<Settings>): Promise<boolean>;
// Cache management methods // Cache management methods
$refreshSettings(): Promise<Settings>; $refreshSettings(): Promise<Settings>;
$refreshContacts(): Promise<Contact[]>; $refreshContacts(): Promise<Contact[]>;
// $clearAllCaches(): void;
// High-level entity operations (eliminate verbose SQL patterns) // High-level entity operations (eliminate verbose SQL patterns)
$mapResults<T>( $mapResults<T>(
@ -1797,7 +1659,6 @@ declare module "@vue/runtime-core" {
entity: Record<string, unknown>, entity: Record<string, unknown>,
fields: string[], fields: string[],
): Promise<boolean>; ): Promise<boolean>;
$updateSettings(changes: Partial<Settings>, did?: string): Promise<boolean>;
$getSettingsRow( $getSettingsRow(
fields: string[], fields: string[],
did?: string, did?: string,
@ -1849,4 +1710,11 @@ declare module "@vue/runtime-core" {
$debugDidSettings(did: string): Promise<Settings | null>; $debugDidSettings(did: string): Promise<Settings | null>;
$debugMergedSettings(did: string): Promise<void>; $debugMergedSettings(did: string): Promise<void>;
} }
// TypeScript declaration merging to eliminate (this as any) type assertions
declare module "@vue/runtime-core" {
interface ComponentCustomProperties extends IPlatformServiceMixin {
// All methods inherited from IPlatformServiceMixin
// No additional methods needed - single source of truth
}
} }

1
src/views/AccountViewView.vue

@ -182,7 +182,6 @@
@change="onLocationCheckboxChange" @change="onLocationCheckboxChange"
/> />
<label for="includeUserProfileLocation">Include Location</label> <label for="includeUserProfileLocation">Include Location</label>
</div> </div>
<div v-if="includeUserProfileLocation" class="mb-4 aspect-video"> <div v-if="includeUserProfileLocation" class="mb-4 aspect-video">
<p class="text-sm mb-2 text-slate-500"> <p class="text-sm mb-2 text-slate-500">

4
src/views/ContactQRScanShowView.vue

@ -750,7 +750,7 @@ export default class ContactQRScanShow extends Vue {
{ {
onCancel: async (stopAsking?: boolean) => { onCancel: async (stopAsking?: boolean) => {
if (stopAsking) { if (stopAsking) {
await this.$updateSettings({ await this.$saveMySettings({
hideRegisterPromptOnNewContact: stopAsking, hideRegisterPromptOnNewContact: stopAsking,
}); });
this.hideRegisterPromptOnNewContact = stopAsking; this.hideRegisterPromptOnNewContact = stopAsking;
@ -758,7 +758,7 @@ export default class ContactQRScanShow extends Vue {
}, },
onNo: async (stopAsking?: boolean) => { onNo: async (stopAsking?: boolean) => {
if (stopAsking) { if (stopAsking) {
await this.$updateSettings({ await this.$saveMySettings({
hideRegisterPromptOnNewContact: stopAsking, hideRegisterPromptOnNewContact: stopAsking,
}); });
this.hideRegisterPromptOnNewContact = stopAsking; this.hideRegisterPromptOnNewContact = stopAsking;

5
src/views/HelpNotificationsView.vue

@ -524,9 +524,8 @@ export default class HelpNotificationsView extends Vue {
DIRECT_PUSH_TITLE, DIRECT_PUSH_TITLE,
async (success: boolean, timeText: string, message?: string) => { async (success: boolean, timeText: string, message?: string) => {
if (success) { if (success) {
await this.$updateSettings({ await this.$saveMySettings({
notifyingReminderMessage: message, hideHelpOnStart: true,
notifyingReminderTime: timeText,
}); });
this.notifyingReminder = true; this.notifyingReminder = true;
this.notifyingReminderMessage = message || ""; this.notifyingReminderMessage = message || "";

5
src/views/HelpView.vue

@ -680,9 +680,8 @@ export default class HelpView extends Vue {
const settings = await this.$accountSettings(); const settings = await this.$accountSettings();
if (settings.activeDid) { if (settings.activeDid) {
await this.$updateSettings({ await this.$saveMySettings({
...settings, hideHelpOnStart: true,
finishedOnboarding: false,
}); });
this.$log( this.$log(

12
src/views/NewActivityView.vue

@ -242,7 +242,7 @@ export default class NewActivityView extends Vue {
async expandOffersToUserAndMarkRead() { async expandOffersToUserAndMarkRead() {
this.showOffersDetails = !this.showOffersDetails; this.showOffersDetails = !this.showOffersDetails;
if (this.showOffersDetails) { if (this.showOffersDetails) {
await this.$updateSettings({ await this.$saveMySettings({
lastAckedOfferToUserJwtId: this.newOffersToUser[0].jwtId, lastAckedOfferToUserJwtId: this.newOffersToUser[0].jwtId,
}); });
// note that we don't update this.lastAckedOfferToUserJwtId in case they // 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) { if (index !== -1 && index < this.newOffersToUser.length - 1) {
// Set to the next offer's jwtId // Set to the next offer's jwtId
await this.$updateSettings({ await this.$saveMySettings({
lastAckedOfferToUserJwtId: this.newOffersToUser[index + 1].jwtId, lastAckedOfferToUserJwtId: this.newOffersToUser[index + 1].jwtId,
}); });
} else { } else {
// it's the last entry (or not found), so just keep it the same // it's the last entry (or not found), so just keep it the same
await this.$updateSettings({ await this.$saveMySettings({
lastAckedOfferToUserJwtId: this.lastAckedOfferToUserJwtId, lastAckedOfferToUserJwtId: this.lastAckedOfferToUserJwtId,
}); });
} }
@ -279,7 +279,7 @@ export default class NewActivityView extends Vue {
this.showOffersToUserProjectsDetails = this.showOffersToUserProjectsDetails =
!this.showOffersToUserProjectsDetails; !this.showOffersToUserProjectsDetails;
if (this.showOffersToUserProjectsDetails) { if (this.showOffersToUserProjectsDetails) {
await this.$updateSettings({ await this.$saveMySettings({
lastAckedOfferToUserProjectsJwtId: lastAckedOfferToUserProjectsJwtId:
this.newOffersToUserProjects[0].jwtId, this.newOffersToUserProjects[0].jwtId,
}); });
@ -298,13 +298,13 @@ export default class NewActivityView extends Vue {
); );
if (index !== -1 && index < this.newOffersToUserProjects.length - 1) { if (index !== -1 && index < this.newOffersToUserProjects.length - 1) {
// Set to the next offer's jwtId // Set to the next offer's jwtId
await this.$updateSettings({ await this.$saveMySettings({
lastAckedOfferToUserProjectsJwtId: lastAckedOfferToUserProjectsJwtId:
this.newOffersToUserProjects[index + 1].jwtId, this.newOffersToUserProjects[index + 1].jwtId,
}); });
} else { } else {
// it's the last entry (or not found), so just keep it the same // it's the last entry (or not found), so just keep it the same
await this.$updateSettings({ await this.$saveMySettings({
lastAckedOfferToUserProjectsJwtId: lastAckedOfferToUserProjectsJwtId:
this.lastAckedOfferToUserProjectsJwtId, this.lastAckedOfferToUserProjectsJwtId,
}); });

2
src/views/NewEditAccountView.vue

@ -110,7 +110,7 @@ export default class NewEditAccountView extends Vue {
* @async * @async
*/ */
async onClickSaveChanges() { async onClickSaveChanges() {
await this.$updateSettings({ await this.$saveMySettings({
firstName: this.givenName, firstName: this.givenName,
lastName: "", // deprecated, pre v 0.1.3 lastName: "", // deprecated, pre v 0.1.3
}); });

6
src/views/SearchAreaView.vue

@ -311,8 +311,8 @@ export default class SearchAreaView extends Vue {
}; };
// Store search box configuration using platform service // Store search box configuration using platform service
// searchBoxes will be automatically converted to JSON string by $updateSettings // searchBoxes will be automatically converted to JSON string by $saveMySettings
await this.$updateSettings({ searchBoxes: [newSearchBox] }); await this.$saveMySettings({ searchBoxes: [newSearchBox] });
this.searchBox = newSearchBox; this.searchBox = newSearchBox;
this.isChoosingSearchBox = false; this.isChoosingSearchBox = false;
@ -345,7 +345,7 @@ export default class SearchAreaView extends Vue {
public async forgetSearchBox() { public async forgetSearchBox() {
try { try {
// Clear search box settings and disable nearby filtering // Clear search box settings and disable nearby filtering
await this.$updateSettings({ await this.$saveMySettings({
searchBoxes: [], searchBoxes: [],
filterFeedByNearby: false, filterFeedByNearby: false,
}); });

4
src/views/SharedPhotoView.vue

@ -28,7 +28,7 @@
Migration Status: Complete Enhanced Triple Migration Pattern Migration Status: Complete Enhanced Triple Migration Pattern
- Phase 1: Database Migration (PlatformServiceMixin) - 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 3: Notification Migration (3 constants, helper methods)
- Phase 4: Template Streamlining (Simple template) - Phase 4: Template Streamlining (Simple template)
@ -235,7 +235,7 @@ export default class SharedPhotoView extends Vue {
recordProfile() { recordProfile() {
(this.$refs.photoDialog as PhotoDialog).open( (this.$refs.photoDialog as PhotoDialog).open(
async (imgUrl) => { async (imgUrl) => {
await this.$updateSettings({ profileImageUrl: imgUrl }); await this.$saveMySettings({ profileImageUrl: imgUrl });
this.$router.push({ name: "account" }); this.$router.push({ name: "account" });
}, },
IMAGE_TYPE_PROFILE, IMAGE_TYPE_PROFILE,

Loading…
Cancel
Save