From 6d85c54a020b5a95966b249fad1eec9bf37f24ff Mon Sep 17 00:00:00 2001 From: Matthew Raymer Date: Sun, 6 Jul 2025 11:56:12 +0000 Subject: [PATCH] refactor: standardize notify helper usage and document migration workflow MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Refactor notify usage in GiftedDialog.vue, AccountViewView.vue, ClaimView.vue, and DataExportSection.vue: • Use notify as a property initialized in created() with createNotifyHelpers(this.$notify) • Remove getter-based notify patterns for consistency and lifecycle safety • Fix linter/type errors related to notify property initialization - Add mandatory per-file migration workflow to doc/migration-progress-tracker.md: • For each file: (1) migrate to PlatformServiceMixin, (2) immediately standardize notify usage and fix linter/type errors • Clarifies this two-step process is required for every file, not as a global sweep All migrated files are now consistent, maintainable, and ready for further migration work. --- doc/migration-progress-tracker.md | 20 +++- doc/migration-to-wa-sqlite.md | 2 +- src/components/DataExportSection.vue | 8 +- src/components/GiftedDialog.vue | 148 +++++++-------------------- src/components/IdentitySection.vue | 1 + src/utils/notificationUtils.ts | 1 + src/views/AccountViewView.vue | 16 ++- src/views/ClaimView.vue | 5 +- 8 files changed, 76 insertions(+), 125 deletions(-) diff --git a/doc/migration-progress-tracker.md b/doc/migration-progress-tracker.md index 838d1587..796b1f97 100644 --- a/doc/migration-progress-tracker.md +++ b/doc/migration-progress-tracker.md @@ -1,5 +1,17 @@ # Migration Progress Tracker: PlatformServiceMixin & 52-File Migration +## Per-File Migration Workflow (MANDATORY) + +For each file migrated: +1. **First**, migrate to PlatformServiceMixin (replace all databaseUtil usage, etc.). +2. **Immediately after**, standardize notify helper usage (property + created() pattern) and fix any related linter/type errors. + +**This two-step process is to be followed for every file, not as a global sweep at the end.** + +Anyone picking up this migration should follow this workflow for consistency and completeness. + +--- + ## Overview This document tracks the progress of the 2-day sprint to complete PlatformServiceMixin implementation and migrate all 52 files from databaseUtil imports to PlatformServiceMixin usage. @@ -176,7 +188,7 @@ export default class ComponentName extends Vue { - [ ] UserProfileView.vue ### **Components (15 files) - Priority 2** -**Progress**: 2/15 (13%) +**Progress**: 3/15 (20%) - [x] UserNameDialog.vue ✅ **MIGRATED** - [ ] ActivityListItem.vue @@ -190,7 +202,7 @@ export default class ComponentName extends Vue { - [ ] EntitySummaryButton.vue - [x] FeedFilters.vue ✅ **MIGRATED** - [ ] GiftDetailsStep.vue -- [ ] GiftedDialog.vue +- [x] GiftedDialog.vue ✅ **MIGRATED** - [ ] GiftedPrompts.vue - [ ] HiddenDidDialog.vue - [ ] IconRenderer.vue @@ -273,8 +285,8 @@ find src -name "*.vue" -o -name "*.ts" | xargs grep -l "import.*databaseUtil" | ### **Overall Progress** - **Total files to migrate**: 52 -- **Files migrated**: 2 -- **Progress**: 4% +- **Files migrated**: 3 +- **Progress**: 6% --- diff --git a/doc/migration-to-wa-sqlite.md b/doc/migration-to-wa-sqlite.md index b17ad89f..34f6a632 100644 --- a/doc/migration-to-wa-sqlite.md +++ b/doc/migration-to-wa-sqlite.md @@ -231,7 +231,7 @@ import { logger } from '../utils/logger'; logger.debug('[Migration] Starting migration process...'); const result = await migrateAll(); logger.debug('[Migration] Migration completed:', result); -``` + ``` ## Benefits of PlatformServiceMixin Approach diff --git a/src/components/DataExportSection.vue b/src/components/DataExportSection.vue index 1a9dc167..96f0aade 100644 --- a/src/components/DataExportSection.vue +++ b/src/components/DataExportSection.vue @@ -98,9 +98,7 @@ export default class DataExportSection extends Vue { * Notification helper for consistent notification patterns * Created as a getter to ensure $notify is available when called */ - get notify() { - return createNotifyHelpers(this.$notify); - } + notify!: ReturnType; /** * NOTE: PlatformServiceMixin provides both concise helpers (e.g. $contacts, capabilities) @@ -156,5 +154,9 @@ export default class DataExportSection extends Vue { this.isExporting = false; } } + + created() { + this.notify = createNotifyHelpers(this.$notify); + } } diff --git a/src/components/GiftedDialog.vue b/src/components/GiftedDialog.vue index 8237bbe8..264e3f41 100644 --- a/src/components/GiftedDialog.vue +++ b/src/components/GiftedDialog.vue @@ -18,7 +18,7 @@ :to-project-id="toProjectId" :giver="giver" :receiver="receiver" - :notify="$notify" + :notify="notify" @entity-selected="handleEntitySelected" @cancel="cancel" /> @@ -62,17 +62,16 @@ import { getHeaders, } from "../libs/endorserServer"; import * as libsUtil from "../libs/util"; -// Removed unused imports: db, retrieveSettingsForActiveAccount import { Contact } from "../db/tables/contacts"; -import * as databaseUtil from "../db/databaseUtil"; import { retrieveAccountDids } from "../libs/util"; import { logger } from "../utils/logger"; -import { PlatformServiceFactory } from "@/services/PlatformServiceFactory"; import EntityIcon from "../components/EntityIcon.vue"; import ProjectIcon from "../components/ProjectIcon.vue"; import EntitySelectionStep from "../components/EntitySelectionStep.vue"; import GiftDetailsStep from "../components/GiftDetailsStep.vue"; import { PlanData } from "../interfaces/records"; +import { PlatformServiceMixin } from "@/utils/PlatformServiceMixin"; +import { createNotifyHelpers, TIMEOUTS } from "@/utils/notify"; @Component({ components: { @@ -81,9 +80,10 @@ import { PlanData } from "../interfaces/records"; EntitySelectionStep, GiftDetailsStep, }, + mixins: [PlatformServiceMixin], }) export default class GiftedDialog extends Vue { - $notify!: (notification: NotificationIface, timeout?: number) => void; + notify!: ReturnType; @Prop() fromProjectId = ""; @Prop() toProjectId = ""; @@ -230,17 +230,11 @@ export default class GiftedDialog extends Vue { this.updateEntityTypes(); try { - const settings = await databaseUtil.retrieveSettingsForActiveAccount(); + const settings = await this.$settings(); this.apiServer = settings.apiServer || ""; this.activeDid = settings.activeDid || ""; - const platformService = PlatformServiceFactory.getInstance(); - const result = await platformService.dbQuery(`SELECT * FROM contacts`); - if (result) { - this.allContacts = databaseUtil.mapQueryResultToValues( - result, - ) as unknown as Contact[]; - } + this.allContacts = await this.$contacts(); this.allMyDids = await retrieveAccountDids(); @@ -264,17 +258,11 @@ export default class GiftedDialog extends Vue { } } catch (err: unknown) { logger.error("Error retrieving settings from database:", err); - this.$notify( - { - group: "alert", - type: "danger", - title: "Error", - text: - err instanceof Error - ? err.message - : "There was an error retrieving your settings.", - }, - -1, + this.notify.error( + err instanceof Error + ? err.message + : "There was an error retrieving your settings.", + TIMEOUTS.MODAL, ); } @@ -319,68 +307,37 @@ export default class GiftedDialog extends Vue { async confirm() { if (!this.activeDid) { - this.$notify( - { - group: "alert", - type: "danger", - title: "Error", - text: "You must select an identifier before you can record a give.", - }, - 3000, + this.notify.error( + "You must select an identifier before you can record a give.", + TIMEOUTS.STANDARD, ); return; } if (parseFloat(this.amountInput) < 0) { - this.$notify( - { - group: "alert", - type: "danger", - text: "You may not send a negative number.", - title: "", - }, - 2000, - ); + this.notify.error("You may not send a negative number.", TIMEOUTS.SHORT); return; } if (!this.description && !parseFloat(this.amountInput)) { - this.$notify( - { - group: "alert", - type: "danger", - title: "Error", - text: `You must enter a description or some number of ${ - this.libsUtil.UNIT_LONG[this.unitCode] - }.`, - }, - 2000, + this.notify.error( + `You must enter a description or some number of ${ + this.libsUtil.UNIT_LONG[this.unitCode] + }.`, + TIMEOUTS.SHORT, ); return; } // Check for person conflict if (this.hasPersonConflict) { - this.$notify( - { - group: "alert", - type: "danger", - title: "Error", - text: "You cannot select the same person as both giver and recipient.", - }, - 3000, + this.notify.error( + "You cannot select the same person as both giver and recipient.", + TIMEOUTS.STANDARD, ); return; } this.close(); - this.$notify( - { - group: "alert", - type: "toast", - text: "Recording the give...", - title: "", - }, - 1000, - ); + this.notify.toast("Recording the give...", undefined, TIMEOUTS.BRIEF); // this is asynchronous, but we don't need to wait for it to complete await this.recordGive( (this.giver?.did as string) || null, @@ -460,25 +417,12 @@ export default class GiftedDialog extends Vue { if (!result.success) { const errorMessage = this.getGiveCreationErrorMessage(result); logger.error("Error with give creation result:", result); - this.$notify( - { - group: "alert", - type: "danger", - title: "Error", - text: errorMessage || "There was an error creating the give.", - }, - -1, + this.notify.error( + errorMessage || "There was an error creating the give.", + TIMEOUTS.MODAL, ); } else { - this.$notify( - { - group: "alert", - type: "success", - title: "Success", - text: `That gift was recorded.`, - }, - 7000, - ); + this.notify.success("That gift was recorded.", TIMEOUTS.VERY_LONG); if (this.callbackOnSuccess) { this.callbackOnSuccess(amount); } @@ -490,15 +434,7 @@ export default class GiftedDialog extends Vue { error.userMessage || serverMessageForUser(error) || "There was an error recording the give."; - this.$notify( - { - group: "alert", - type: "danger", - title: "Error", - text: errorMessage, - }, - -1, - ); + this.notify.error(errorMessage, TIMEOUTS.MODAL); } } @@ -518,15 +454,7 @@ export default class GiftedDialog extends Vue { } explainData() { - this.$notify( - { - group: "alert", - type: "success", - title: "Data Sharing", - text: libsUtil.PRIVACY_MESSAGE, - }, - -1, - ); + this.notify.info(libsUtil.PRIVACY_MESSAGE, TIMEOUTS.MODAL); } selectGiver(contact?: Contact) { @@ -566,15 +494,7 @@ export default class GiftedDialog extends Vue { } } catch (error) { logger.error("Error loading projects:", error); - this.$notify( - { - group: "alert", - type: "danger", - title: "Error", - text: "Failed to load projects", - }, - 3000, - ); + this.notify.error("Failed to load projects", TIMEOUTS.STANDARD); } } @@ -696,6 +616,10 @@ export default class GiftedDialog extends Vue { amountInput: this.amountInput, }); } + + created() { + this.notify = createNotifyHelpers(this.$notify); + } } diff --git a/src/components/IdentitySection.vue b/src/components/IdentitySection.vue index f9e9fc53..4491a3c4 100644 --- a/src/components/IdentitySection.vue +++ b/src/components/IdentitySection.vue @@ -185,3 +185,4 @@ export default class IdentitySection extends Vue { } } + \ No newline at end of file diff --git a/src/utils/notificationUtils.ts b/src/utils/notificationUtils.ts index 496df3de..73d7b9a1 100644 --- a/src/utils/notificationUtils.ts +++ b/src/utils/notificationUtils.ts @@ -276,3 +276,4 @@ export const NotificationMixin = { }, }, }; + \ No newline at end of file diff --git a/src/views/AccountViewView.vue b/src/views/AccountViewView.vue index 9afa58a7..85a4cc78 100644 --- a/src/views/AccountViewView.vue +++ b/src/views/AccountViewView.vue @@ -817,6 +817,10 @@ import { const inputImportFileNameRef = ref(); +interface UserNameDialogRef { + open: (cb: (name?: string) => void) => void; +} + @Component({ components: { EntityIcon, @@ -908,6 +912,10 @@ export default class AccountViewView extends Vue { private profileService!: ProfileService; private notify!: ReturnType; + created() { + this.notify = createNotifyHelpers(this.$notify); + } + /** * Async function executed when the component is mounted. * Initializes the component's state with values from the database, @@ -916,7 +924,6 @@ export default class AccountViewView extends Vue { * @throws Will display specific messages to the user based on different errors. */ async mounted(): Promise { - this.notify = createNotifyHelpers(this.$notify); this.profileService = createProfileService( this.axios, this.partnerApiServer, @@ -1601,7 +1608,7 @@ export default class AccountViewView extends Vue { // IdentitySection event handlers onEditName() { - const dialog = this.$refs.userNameDialog as any; + const dialog = this.$refs.userNameDialog as UserNameDialogRef | undefined; if (dialog && typeof dialog.open === "function") { dialog.open((name?: string) => { if (name) this.givenName = name; @@ -1613,7 +1620,10 @@ export default class AccountViewView extends Vue { title: "Dialog Error", text: "Name dialog not available.", }); - console.error("UserNameDialog ref is missing or open() is not a function", dialog); + logger.error( + "UserNameDialog ref is missing or open() is not a function", + dialog, + ); } } onShowQrCode() { diff --git a/src/views/ClaimView.vue b/src/views/ClaimView.vue index 17f7a20f..8c638bf6 100644 --- a/src/views/ClaimView.vue +++ b/src/views/ClaimView.vue @@ -578,8 +578,7 @@ export default class ClaimView extends Vue { libsUtil = libsUtil; serverUtil = serverUtil; - // Add notification helpers - private notify = createNotifyHelpers(this.$notify); + notify!: ReturnType; // ================================================= // COMPUTED PROPERTIES @@ -746,6 +745,8 @@ export default class ClaimView extends Vue { this.windowDeepLink = `${APP_SERVER}/deep-link/claim/${claimId}`; this.canShare = !!navigator.share; + + this.notify = createNotifyHelpers(this.$notify); } // insert a space before any capital letters except the initial letter