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.
This commit is contained in:
@@ -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>
|
<template>
|
||||||
<div
|
<div
|
||||||
id="sectionDataExport"
|
id="sectionDataExport"
|
||||||
@@ -28,17 +42,17 @@
|
|||||||
>
|
>
|
||||||
If no download happened yet, click again here to download now.
|
If no download happened yet, click again here to download now.
|
||||||
</a>
|
</a>
|
||||||
<div class="mt-4">
|
<div class="mt-4" v-if="showPlatformInstructions">
|
||||||
<p>
|
<p>
|
||||||
After the download, you can save the file in your preferred storage
|
After the download, you can save the file in your preferred storage
|
||||||
location.
|
location.
|
||||||
</p>
|
</p>
|
||||||
<ul>
|
<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"
|
On iOS: Choose "More..." and select a place in iCloud, or go "Back"
|
||||||
and save to another location.
|
and save to another location.
|
||||||
</li>
|
</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
|
On Android: Choose "Open" and then share
|
||||||
<font-awesome icon="share-nodes" class="fa-fw" />
|
<font-awesome icon="share-nodes" class="fa-fw" />
|
||||||
to your prefered place.
|
to your prefered place.
|
||||||
@@ -53,38 +67,106 @@ import { Component, Prop, Vue } from "vue-facing-decorator";
|
|||||||
import { NotificationIface } from "../constants/app";
|
import { NotificationIface } from "../constants/app";
|
||||||
import { db } from "../db/index";
|
import { db } from "../db/index";
|
||||||
import { logger } from "../utils/logger";
|
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
|
@Component
|
||||||
export default class DataExportSection extends Vue {
|
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;
|
$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;
|
@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 = "";
|
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() {
|
beforeUnmount() {
|
||||||
if (this.downloadUrl) {
|
if (this.downloadUrl && this.platformService.isWeb()) {
|
||||||
URL.revokeObjectURL(this.downloadUrl);
|
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() {
|
public async exportDatabase() {
|
||||||
try {
|
try {
|
||||||
const blob = await db.export({ prettyJson: true });
|
const blob = await db.export({ prettyJson: true });
|
||||||
|
const fileName = `${db.name}-backup.json`;
|
||||||
|
|
||||||
|
if (this.platformService.isWeb()) {
|
||||||
|
// Web platform: Use download link
|
||||||
this.downloadUrl = URL.createObjectURL(blob);
|
this.downloadUrl = URL.createObjectURL(blob);
|
||||||
const downloadAnchor = this.$refs.downloadLink as HTMLAnchorElement;
|
const downloadAnchor = this.$refs.downloadLink as HTMLAnchorElement;
|
||||||
downloadAnchor.href = this.downloadUrl;
|
downloadAnchor.href = this.downloadUrl;
|
||||||
downloadAnchor.download = `${db.name}-backup.json`;
|
downloadAnchor.download = fileName;
|
||||||
downloadAnchor.click();
|
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(
|
this.$notify(
|
||||||
{
|
{
|
||||||
group: "alert",
|
group: "alert",
|
||||||
type: "success",
|
type: "success",
|
||||||
title: "Download Started",
|
title: "Export Successful",
|
||||||
text: "See your downloads directory for the backup. It is in the Dexie format.",
|
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,
|
-1,
|
||||||
);
|
);
|
||||||
setTimeout(() => URL.revokeObjectURL(this.downloadUrl), 1000);
|
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
logger.error("Export Error:", error);
|
logger.error("Export Error:", error);
|
||||||
this.$notify(
|
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() {
|
public computedStartDownloadLinkClassNames() {
|
||||||
return {
|
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() {
|
public computedDownloadLinkClassNames() {
|
||||||
return {
|
return {
|
||||||
hidden: !this.downloadUrl,
|
hidden: !this.downloadUrl || !this.platformService.isWeb(),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user