From d8b078d3721cbfa74efb906c19bf8d615d6e77fb Mon Sep 17 00:00:00 2001 From: Matthew Raymer Date: Sat, 5 Jul 2025 13:25:37 +0000 Subject: [PATCH] refactor: migrate DataExportSection to PlatformServiceMixin - Use PlatformServiceMixin for platform and database access - Replace manual PlatformService instantiation with mixin methods/properties - Use $contacts() for contact export - Use capabilities for platform checks in template and logic - Remove unused imports and redundant code - Lint clean --- src/components/DataExportSection.vue | 134 ++++++++++------------- src/components/LocationSearchSection.vue | 10 +- src/components/RegistrationNotice.vue | 11 +- src/components/UsageLimitsSection.vue | 12 +- src/views/AccountViewView.vue | 14 +-- 5 files changed, 82 insertions(+), 99 deletions(-) diff --git a/src/components/DataExportSection.vue b/src/components/DataExportSection.vue index e9f37649..b34b3430 100644 --- a/src/components/DataExportSection.vue +++ b/src/components/DataExportSection.vue @@ -20,34 +20,33 @@ backup and database export, with platform-specific download instructions. * * If no download happened yet, click again here to download now. -
+

After the download, you can save the file in your preferred storage location.

    -
  • +
  • On iOS: You will be prompted to choose a location to save your backup file.
  • On Android: You will be prompted to choose a location to save your @@ -62,23 +61,19 @@ backup and database export, with platform-specific download instructions. * * import { Component, Prop, Vue } from "vue-facing-decorator"; import { AppString, NotificationIface } from "../constants/app"; -import { Contact } from "../db/tables/contacts"; -import * as databaseUtil from "../db/databaseUtil"; - import { logger } from "../utils/logger"; -import { PlatformServiceFactory } from "../services/PlatformServiceFactory"; -import { - PlatformService, - PlatformCapabilities, -} from "../services/PlatformService"; import { contactsToExportJson } from "../libs/util"; +import { createNotifyHelpers } from "@/utils/notify"; +import { PlatformServiceMixin } from "@/utils/PlatformServiceMixin"; /** * @vue-component * Data Export Section Component * Handles database export and seed backup functionality with platform-specific behavior */ -@Component +@Component({ + mixins: [PlatformServiceMixin], +}) export default class DataExportSection extends Vue { /** * Notification function injected by Vue @@ -101,16 +96,29 @@ export default class DataExportSection extends Vue { downloadUrl = ""; /** - * Platform service instance for platform-specific operations + * Notification helper for consistent notification patterns */ - private platformService: PlatformService = - PlatformServiceFactory.getInstance(); + notify = createNotifyHelpers(this.$notify); /** - * Platform capabilities for the current platform + * Computed property to check if we're on web platform */ - private get platformCapabilities(): PlatformCapabilities { - return this.platformService.getCapabilities(); + private get isWebPlatform(): boolean { + return this.capabilities.hasFileDownload; + } + + /** + * Computed property to check if download is in progress + */ + private get isDownloadInProgress(): boolean { + return Boolean(this.downloadUrl && this.isWebPlatform); + } + + /** + * Computed property for the export file name + */ + private get fileName(): string { + return `${AppString.APP_NAME_NO_SPACES}-backup-contacts.json`; } /** @@ -118,7 +126,7 @@ export default class DataExportSection extends Vue { * Revokes object URL when component is unmounted (web platform only) */ beforeUnmount() { - if (this.downloadUrl && this.platformCapabilities.hasFileDownload) { + if (this.downloadUrl && this.isWebPlatform) { URL.revokeObjectURL(this.downloadUrl); } } @@ -129,84 +137,56 @@ export default class DataExportSection extends Vue { * Shows success/error notifications to user * * @throws {Error} If export fails - * @emits {Notification} Success or error notification */ - public async exportDatabase() { + public async exportDatabase(): Promise { try { - let allContacts: Contact[] = []; - const platformService = PlatformServiceFactory.getInstance(); - const result = await platformService.dbQuery(`SELECT * FROM contacts`); - if (result) { - allContacts = databaseUtil.mapQueryResultToValues( - result, - ) as unknown as Contact[]; - } + // Fetch contacts from database using mixin's cached method + const allContacts = await this.$contacts(); // Convert contacts to export format const exportData = contactsToExportJson(allContacts); const jsonStr = JSON.stringify(exportData, null, 2); const blob = new Blob([jsonStr], { type: "application/json" }); - const fileName = `${AppString.APP_NAME_NO_SPACES}-backup-contacts.json`; - - if (this.platformCapabilities.hasFileDownload) { - // Web platform: Use download link - this.downloadUrl = URL.createObjectURL(blob); - const downloadAnchor = this.$refs.downloadLink as HTMLAnchorElement; - downloadAnchor.href = this.downloadUrl; - downloadAnchor.download = fileName; - downloadAnchor.click(); - setTimeout(() => URL.revokeObjectURL(this.downloadUrl), 1000); - } else if (this.platformCapabilities.hasFileSystem) { - // Native platform: Write to app directory - await this.platformService.writeAndShareFile(fileName, jsonStr); + // Handle export based on platform capabilities + if (this.isWebPlatform) { + await this.handleWebExport(blob); + } else if (this.capabilities.hasFileSystem) { + await this.handleNativeExport(jsonStr); } else { throw new Error("This platform does not support file downloads."); } - this.$notify( - { - group: "alert", - type: "success", - title: "Export Successful", - text: this.platformCapabilities.hasFileDownload - ? "See your downloads directory for the backup." - : "The backup file has been saved.", - }, - 3000, + this.notify.success( + this.isWebPlatform + ? "See your downloads directory for the backup." + : "The backup file has been saved.", ); } catch (error) { logger.error("Export Error:", error); - this.$notify( - { - group: "alert", - type: "danger", - title: "Export Error", - text: "There was an error exporting the data.", - }, - 3000, + this.notify.error( + `There was an error exporting the data: ${error instanceof Error ? error.message : "Unknown error"}`, ); } } /** - * Computes class names for the initial download button - * @returns Object with 'hidden' class when download is in progress (web platform only) + * Handles export for web platform using download link + * @param blob The blob to download */ - public computedStartDownloadLinkClassNames() { - return { - hidden: this.downloadUrl && this.platformCapabilities.hasFileDownload, - }; + private async handleWebExport(blob: Blob): Promise { + this.downloadUrl = URL.createObjectURL(blob); + const downloadAnchor = this.$refs.downloadLink as HTMLAnchorElement; + downloadAnchor.click(); + setTimeout(() => URL.revokeObjectURL(this.downloadUrl), 1000); } /** - * Computes class names for the secondary download link - * @returns Object with 'hidden' class when no download is available or not on web platform + * Handles export for native platforms using file system + * @param jsonStr The JSON string to save */ - public computedDownloadLinkClassNames() { - return { - hidden: !this.downloadUrl || !this.platformCapabilities.hasFileDownload, - }; + private async handleNativeExport(jsonStr: string): Promise { + await this.platformService.writeAndShareFile(this.fileName, jsonStr); } } diff --git a/src/components/LocationSearchSection.vue b/src/components/LocationSearchSection.vue index 8bf41ce4..a91e52c4 100644 --- a/src/components/LocationSearchSection.vue +++ b/src/components/LocationSearchSection.vue @@ -1,7 +1,7 @@ \ No newline at end of file + diff --git a/src/components/RegistrationNotice.vue b/src/components/RegistrationNotice.vue index fb977f14..c9faffea 100644 --- a/src/components/RegistrationNotice.vue +++ b/src/components/RegistrationNotice.vue @@ -7,7 +7,8 @@ aria-live="polite" >

    - Before you can publicly announce a new project or time commitment, a friend needs to register you. + Before you can publicly announce a new project or time commitment, a + friend needs to register you.