Browse Source

fix: Resolve contact export errors in DataExportSection

- Fix ref timing issue by always rendering download link element
- Convert notify helper to getter to ensure $notify availability
- Add proper error handling and resource cleanup for blob URLs
- Improve user feedback with better error messages
- Add comprehensive documentation and security considerations

Resolves: TypeError on downloadLink.click() and notify function errors
pull/142/head
Matthew Raymer 18 hours ago
parent
commit
400748b9a1
  1. 46
      src/components/DataExportSection.vue

46
src/components/DataExportSection.vue

@ -1,9 +1,12 @@
/** * Data Export Section Component * * Provides UI and functionality for /** * Data Export Section Component * * Provides UI and functionality for
exporting user data and backing up identifier seeds. * Includes buttons for seed exporting user data and backing up identifier seeds. * Includes buttons for seed
backup and database export, with platform-specific download instructions. * * backup and database export, with platform-specific download instructions. * *
@component * @displayName DataExportSection * @example * ```vue * Features: * - Platform-specific export handling (web vs. native) * - Proper
resource cleanup for blob URLs * - Robust error handling with user-friendly
messages * - Conditional UI based on platform capabilities * * @component *
@displayName DataExportSection * @example * ```vue *
<DataExportSection :active-did="currentDid" /> <DataExportSection :active-did="currentDid" />
* ``` */ * ``` * * @author Matthew Raymer * @since 2025-01-25 * @version 1.1.0 */
<template> <template>
<div <div
@ -26,11 +29,14 @@ backup and database export, with platform-specific download instructions. * *
> >
Download Contacts Download Contacts
</button> </button>
<!-- Hidden download link for web platform - always rendered for ref access -->
<a <a
v-if="isWebPlatform && downloadUrl" v-if="isWebPlatform"
ref="downloadLink" ref="downloadLink"
:href="downloadUrl" :href="downloadUrl"
:download="fileName" :download="fileName"
:class="{ hidden: !downloadUrl }"
class="block w-full text-center text-md bg-gradient-to-b from-green-500 to-green-800 shadow-[inset_0_-1px_0_0_rgba(0,0,0,0.5)] text-white px-1.5 py-2 rounded-md mb-6" class="block w-full text-center text-md bg-gradient-to-b from-green-500 to-green-800 shadow-[inset_0_-1px_0_0_rgba(0,0,0,0.5)] text-white px-1.5 py-2 rounded-md mb-6"
> >
If no download happened yet, click again here to download now. If no download happened yet, click again here to download now.
@ -97,8 +103,11 @@ export default class DataExportSection extends Vue {
/** /**
* Notification helper for consistent notification patterns * Notification helper for consistent notification patterns
* Created as a getter to ensure $notify is available when called
*/ */
notify = createNotifyHelpers(this.$notify); get notify() {
return createNotifyHelpers(this.$notify);
}
/** /**
* NOTE: PlatformServiceMixin provides both concise helpers (e.g. $contacts, capabilities) * NOTE: PlatformServiceMixin provides both concise helpers (e.g. $contacts, capabilities)
@ -135,6 +144,7 @@ export default class DataExportSection extends Vue {
beforeUnmount() { beforeUnmount() {
if (this.downloadUrl && this.isWebPlatform) { if (this.downloadUrl && this.isWebPlatform) {
URL.revokeObjectURL(this.downloadUrl); URL.revokeObjectURL(this.downloadUrl);
this.downloadUrl = "";
} }
} }
@ -183,9 +193,31 @@ export default class DataExportSection extends Vue {
*/ */
private async handleWebExport(blob: Blob): Promise<void> { private async handleWebExport(blob: Blob): Promise<void> {
this.downloadUrl = URL.createObjectURL(blob); this.downloadUrl = URL.createObjectURL(blob);
const downloadAnchor = this.$refs.downloadLink as HTMLAnchorElement;
downloadAnchor.click(); try {
setTimeout(() => URL.revokeObjectURL(this.downloadUrl), 1000); // Wait for next tick to ensure DOM is updated
await this.$nextTick();
const downloadAnchor = this.$refs.downloadLink as HTMLAnchorElement;
if (!downloadAnchor) {
throw new Error("Download link element not found. Please try again.");
}
downloadAnchor.click();
// Clean up the URL after a delay
setTimeout(() => {
URL.revokeObjectURL(this.downloadUrl);
this.downloadUrl = "";
}, 1000);
} catch (error) {
// Clean up the URL on error
if (this.downloadUrl) {
URL.revokeObjectURL(this.downloadUrl);
this.downloadUrl = "";
}
throw error;
}
} }
/** /**

Loading…
Cancel
Save