@ -20,34 +20,33 @@ backup and database export, with platform-specific download instructions. * *
< / r o u t e r - l i n k >
< 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 ="platformC apabilities.needsFileHandlingInstructions" class="mt-4" >
< div v-if ="c apabilities.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 && !platformC apabilities.isIOS"
v - if = "capabilities.isMobile && !c apabilities.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 operatio ns
* Notification helper for consistent notification patter ns
* /
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 [ ] ;
}
/ / F e t c h c o n t a c t s f r o m d a t a b a s e u s i n g m i x i n ' s c a c h e d m e t h o d
const allContacts = await this . $contacts ( ) ;
/ / C o n v e r t c o n t a c t s t o e x p o r t f o r m a t
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 ) {
/ / W e b p l a t f o r m : U s e d o w n l o a d l i n k
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 ) {
/ / N a t i v e p l a t f o r m : W r i t e t o a p p d i r e c t o r y
await this . platformService . writeAndShareFile ( fileName , jsonStr ) ;
/ / H a n d l e e x p o r t b a s e d o n p l a t f o r m c a p a b i l i t i e s
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 >