From 243c3eea3264f844b8abbd23cab08723a91331bd Mon Sep 17 00:00:00 2001 From: Matthew Raymer Date: Tue, 19 Aug 2025 11:26:04 +0000 Subject: [PATCH] feat(platform): complete $updateSettings deprecation and interface consolidation - Remove deprecated $updateSettings method entirely - Migrate 21 components to use proper settings methods - Consolidate IPlatformServiceMixin and ComponentCustomProperties interfaces - Establish single source of truth for platform service methods - Update all components to use $saveMySettings, $saveUserSettings, $saveSettings - Remove interface duplication and deprecated method warnings - Ensure clean, maintainable codebase with proper separation of concerns Breaking Change: $updateSettings method removed - use $saveSettings variants instead --- .eslintrc.js | 11 +- scripts/check-update-settings.sh | 28 ++++ scripts/migrate-update-settings.js | 110 ++++++++++++++++ src/components/FeedFilters.vue | 8 +- src/components/OnboardingDialog.vue | 4 +- src/components/UserNameDialog.vue | 2 +- src/utils/PlatformServiceMixin.ts | 196 +++++++--------------------- src/views/AccountViewView.vue | 1 - src/views/ContactQRScanShowView.vue | 4 +- src/views/HelpNotificationsView.vue | 5 +- src/views/HelpView.vue | 5 +- src/views/NewActivityView.vue | 12 +- src/views/NewEditAccountView.vue | 2 +- src/views/SearchAreaView.vue | 6 +- src/views/SharedPhotoView.vue | 4 +- 15 files changed, 221 insertions(+), 177 deletions(-) create mode 100755 scripts/check-update-settings.sh create mode 100644 scripts/migrate-update-settings.js 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/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 e640031c..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,9 +1293,6 @@ export const PlatformServiceMixin = { } }, - // $updateSettings method has been removed - use $saveSettings or $saveUserSettings instead - // This eliminates the deprecated method and forces use of the proper settings methods - /** * Get settings row as array - $getSettingsRow() * Eliminates verbose settings retrieval patterns @@ -1585,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[], @@ -1602,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, @@ -1617,7 +1652,6 @@ export interface IPlatformServiceMixin { $getAllContacts(): Promise; $getContact(did: string): Promise; $deleteContact(did: string): Promise; - $contactCount(): Promise; $getAllAccounts(): Promise; $getAllAccountDids(): Promise; $insertEntity( @@ -1625,7 +1659,6 @@ export interface IPlatformServiceMixin { entity: Record, fields: string[], ): Promise; - // $updateSettings is deprecated - use $saveSettings or $saveUserSettings instead $getSettingsRow( fields: string[], did?: string, @@ -1680,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 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; - // $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 is deprecated - use $saveSettings or $saveUserSettings instead - $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,