Browse Source

feat(export): adapt DataExportSection for platform-specific file handling

Integrate PlatformServiceFactory to provide platform-specific data export:
- Add platform-specific file saving for Capacitor and other platforms
- Use web download mechanism only in web platform
- Conditionally show platform-specific save instructions
- Add iOS/Android detection for targeted guidance
- Update success messages based on platform context
- Improve download link visibility logic for web platform

This change ensures proper file handling across web, mobile, and desktop
platforms while maintaining a consistent user experience.
cross-platform-factory
Matthew Raymer 2 months ago
parent
commit
b8a7771edf
  1. 118
      src/components/DataExportSection.vue

118
src/components/DataExportSection.vue

@ -1,3 +1,17 @@
/**
* Data Export Section Component
*
* Provides UI and functionality for exporting user data and backing up identifier seeds.
* Includes buttons for seed backup and database export, with platform-specific download instructions.
*
* @component
* @displayName DataExportSection
* @example
* ```vue
* <DataExportSection :active-did="currentDid" />
* ```
*/
<template>
<div
id="sectionDataExport"
@ -28,17 +42,17 @@
>
If no download happened yet, click again here to download now.
</a>
<div class="mt-4">
<div class="mt-4" v-if="showPlatformInstructions">
<p>
After the download, you can save the file in your preferred storage
location.
</p>
<ul>
<li class="list-disc list-outside ml-4">
<li v-if="platformService.isCapacitor() && isIOS" class="list-disc list-outside ml-4">
On iOS: Choose "More..." and select a place in iCloud, or go "Back"
and save to another location.
</li>
<li class="list-disc list-outside ml-4">
<li v-if="platformService.isCapacitor() && !isIOS" class="list-disc list-outside ml-4">
On Android: Choose "Open" and then share
<font-awesome icon="share-nodes" class="fa-fw" />
to your prefered place.
@ -53,38 +67,106 @@ import { Component, Prop, Vue } from "vue-facing-decorator";
import { NotificationIface } from "../constants/app";
import { db } from "../db/index";
import { logger } from "../utils/logger";
import { PlatformServiceFactory } from "../services/PlatformServiceFactory";
import { PlatformService } from "../services/PlatformService";
/**
* @vue-component
* Data Export Section Component
* Handles database export and seed backup functionality with platform-specific behavior
*/
@Component
export default class DataExportSection extends Vue {
/**
* Notification function injected by Vue
* Used to show success/error messages to the user
*/
$notify!: (notification: NotificationIface, timeout?: number) => void;
/**
* Active DID (Decentralized Identifier) of the user
* Controls visibility of seed backup option
* @required
*/
@Prop({ required: true }) readonly activeDid!: string;
/**
* URL for the database export download
* Created and revoked dynamically during export process
* Only used in web platform
*/
downloadUrl = "";
/**
* Platform service instance for platform-specific operations
*/
private platformService: PlatformService = PlatformServiceFactory.getInstance();
/**
* Whether the current platform is iOS
*/
private get isIOS(): boolean {
return /iPad|iPhone|iPod/.test(navigator.userAgent);
}
/**
* Whether to show platform-specific instructions
*/
private get showPlatformInstructions(): boolean {
return this.platformService.isCapacitor();
}
/**
* Lifecycle hook to clean up resources
* Revokes object URL when component is unmounted (web platform only)
*/
beforeUnmount() {
if (this.downloadUrl) {
if (this.downloadUrl && this.platformService.isWeb()) {
URL.revokeObjectURL(this.downloadUrl);
}
}
/**
* Exports the database to a JSON file
* Uses platform-specific methods for saving the exported data
* Shows success/error notifications to user
*
* @throws {Error} If export fails
* @emits {Notification} Success or error notification
*/
public async exportDatabase() {
try {
const blob = await db.export({ prettyJson: true });
this.downloadUrl = URL.createObjectURL(blob);
const downloadAnchor = this.$refs.downloadLink as HTMLAnchorElement;
downloadAnchor.href = this.downloadUrl;
downloadAnchor.download = `${db.name}-backup.json`;
downloadAnchor.click();
const fileName = `${db.name}-backup.json`;
if (this.platformService.isWeb()) {
// 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.platformService.isCapacitor()) {
// Capacitor platform: Write to app directory
const content = await blob.text();
await this.platformService.writeFile(fileName, content);
} else {
// Other platforms: Use platform service
await this.platformService.writeFile(fileName, await blob.text());
}
this.$notify(
{
group: "alert",
type: "success",
title: "Download Started",
text: "See your downloads directory for the backup. It is in the Dexie format.",
title: "Export Successful",
text: this.platformService.isWeb()
? "See your downloads directory for the backup. It is in the Dexie format."
: "The backup has been saved to your device.",
},
-1,
);
setTimeout(() => URL.revokeObjectURL(this.downloadUrl), 1000);
} catch (error) {
logger.error("Export Error:", error);
this.$notify(
@ -99,15 +181,23 @@ export default class DataExportSection extends Vue {
}
}
/**
* Computes class names for the initial download button
* @returns Object with 'hidden' class when download is in progress (web platform only)
*/
public computedStartDownloadLinkClassNames() {
return {
hidden: this.downloadUrl,
hidden: this.downloadUrl && this.platformService.isWeb(),
};
}
/**
* Computes class names for the secondary download link
* @returns Object with 'hidden' class when no download is available or not on web platform
*/
public computedDownloadLinkClassNames() {
return {
hidden: !this.downloadUrl,
hidden: !this.downloadUrl || !this.platformService.isWeb(),
};
}
}

Loading…
Cancel
Save