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
This commit is contained in:
@@ -20,34 +20,33 @@ backup and database export, with platform-specific download instructions. * *
|
||||
</router-link>
|
||||
|
||||
<button
|
||||
:class="computedStartDownloadLinkClassNames()"
|
||||
:class="{ hidden: isDownloadInProgress }"
|
||||
class="block w-full text-center text-md bg-gradient-to-b from-blue-400 to-blue-700 shadow-[inset_0_-1px_0_0_rgba(0,0,0,0.5)] text-white px-1.5 py-2 rounded-md"
|
||||
@click="exportDatabase()"
|
||||
>
|
||||
Download Contacts
|
||||
</button>
|
||||
<a
|
||||
v-if="isWebPlatform && downloadUrl"
|
||||
ref="downloadLink"
|
||||
:class="computedDownloadLinkClassNames()"
|
||||
:href="downloadUrl"
|
||||
:download="fileName"
|
||||
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.
|
||||
</a>
|
||||
<div v-if="platformCapabilities.needsFileHandlingInstructions" class="mt-4">
|
||||
<div v-if="capabilities.needsFileHandlingInstructions" class="mt-4">
|
||||
<p>
|
||||
After the download, you can save the file in your preferred storage
|
||||
location.
|
||||
</p>
|
||||
<ul>
|
||||
<li
|
||||
v-if="platformCapabilities.isIOS"
|
||||
class="list-disc list-outside ml-4"
|
||||
>
|
||||
<li v-if="capabilities.isIOS" class="list-disc list-outside ml-4">
|
||||
On iOS: You will be prompted to choose a location to save your backup
|
||||
file.
|
||||
</li>
|
||||
<li
|
||||
v-if="platformCapabilities.isMobile && !platformCapabilities.isIOS"
|
||||
v-if="capabilities.isMobile && !capabilities.isIOS"
|
||||
class="list-disc list-outside ml-4"
|
||||
>
|
||||
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<void> {
|
||||
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<void> {
|
||||
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<void> {
|
||||
await this.platformService.writeAndShareFile(this.fileName, jsonStr);
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
Reference in New Issue
Block a user