@ -41,7 +41,7 @@ interface QueuedOperation {
* /
export class CapacitorPlatformService implements PlatformService {
/** Current camera direction */
private currentDirection : CameraDirection = "BACK" ;
private currentDirection : CameraDirection = "BACK" as CameraDirection ;
private sqlite : SQLiteConnection ;
private db : SQLiteDBConnection | null = null ;
@ -379,213 +379,169 @@ export class CapacitorPlatformService implements PlatformService {
}
/ * *
* Writes content to a file in the app ' s safe storage and offers sharing .
*
* Platform - specific behavior :
* - Saves to app ' s Documents directory
* - Offers sharing functionality to move file elsewhere
*
* The method handles :
* 1 . Writing to app - safe storage
* 2 . Sharing the file with user ' s preferred app
* 3 . Error handling and logging
*
* Enhanced file save and share functionality with location selection .
*
* Provides multiple options for saving files :
* 1 . Save to app - private storage and share ( current behavior )
* 2 . Save to device Downloads folder ( Android ) or Documents ( iOS )
* 3 . Allow user to choose save location via file picker
* 4 . Direct share without saving locally
*
* @param fileName - The name of the file to create ( e . g . "backup.json" )
* @param content - The content to write to the file
*
* @throws Error if :
* - File writing fails
* - Sharing fails
*
* @example
* ` ` ` typescript
* // Save and share a JSON file
* await platformService . writeFile (
* "backup.json" ,
* JSON . stringify ( data )
* ) ;
* ` ` `
* @param options - Additional options for file saving behavior
* @returns Promise resolving to save / share result
* /
async writeFile ( fileName : string , content : string ) : Promise < void > {
async writeAndShareFile (
fileName : string ,
content : string ,
options : {
allowLocationSelection? : boolean ;
saveToDownloads? : boolean ;
mimeType? : string ;
} = { }
) : Promise < { saved : boolean ; uri? : string ; shared : boolean ; error? : string } > {
const timestamp = new Date ( ) . toISOString ( ) ;
const logData = {
action : "writeAndShareFile" ,
fileName ,
contentLength : content.length ,
options ,
timestamp ,
} ;
logger . log ( "[CapacitorPlatformService]" , JSON . stringify ( logData , null , 2 ) ) ;
try {
// Check storage permissions before proceeding
await this . checkStoragePermissions ( ) ;
const logData = {
targetFileName : fileName ,
contentLength : content.length ,
platform : this.getCapabilities ( ) . isIOS ? "iOS" : "Android" ,
timestamp : new Date ( ) . toISOString ( ) ,
} ;
logger . log (
"Starting writeFile operation" ,
JSON . stringify ( logData , null , 2 ) ,
) ;
// For Android, we need to handle content URIs differently
if ( this . getCapabilities ( ) . isIOS ) {
// Write to app's Documents directory for iOS
const writeResult = await Filesystem . writeFile ( {
path : fileName ,
data : content ,
directory : Directory.Data ,
encoding : Encoding.UTF8 ,
} ) ;
const writeSuccessLogData = {
path : writeResult.uri ,
timestamp : new Date ( ) . toISOString ( ) ,
} ;
logger . log (
"File write successful" ,
JSON . stringify ( writeSuccessLogData , null , 2 ) ,
) ;
// Offer to share the file
try {
await Share . share ( {
title : "TimeSafari Backup" ,
text : "Here is your TimeSafari backup file." ,
url : writeResult.uri ,
dialogTitle : "Share your backup" ,
} ) ;
logger . log (
"Share dialog shown" ,
JSON . stringify ( { timestamp : new Date ( ) . toISOString ( ) } , null , 2 ) ,
) ;
} catch ( shareError ) {
// Log share error but don't fail the operation
logger . error (
"Share dialog failed" ,
JSON . stringify (
{
error : shareError ,
timestamp : new Date ( ) . toISOString ( ) ,
} ,
null ,
2 ,
) ,
) ;
}
let fileUri : string ;
let saved = false ;
// Determine save strategy based on options and platform
if ( options . allowLocationSelection ) {
// Use file picker to let user choose location
fileUri = await this . saveFileWithPicker ( fileName , content , options . mimeType ) ;
saved = true ;
} else if ( options . saveToDownloads ) {
// Save directly to Downloads folder
fileUri = await this . saveToDownloads ( fileName , content ) ;
saved = true ;
} else {
// For Android, first write to app's Documents directory
const w riteR esult = await Filesystem . writeFile ( {
// Fallback to app-private storage (current behavior)
const result = await Filesystem . writeFile ( {
path : fileName ,
data : content ,
directory : Directory.Data ,
encoding : Encoding.UTF8 ,
recursive : true ,
} ) ;
fileUri = result . uri ;
saved = true ;
}
const writeSuccessLogData = {
path : writeResult.uri ,
timestamp : new Date ( ) . toISOString ( ) ,
} ;
logger . log (
"File write successful to app storage" ,
JSON . stringify ( writeSuccessLogData , null , 2 ) ,
) ;
// Then share the file to let user choose where to save it
try {
await Share . share ( {
title : "TimeSafari Backup" ,
text : "Here is your TimeSafari backup file." ,
url : writeResult.uri ,
dialogTitle : "Save your backup" ,
} ) ;
logger . log ( "[CapacitorPlatformService] File write successful:" , {
uri : fileUri ,
saved ,
timestamp : new Date ( ) . toISOString ( ) ,
} ) ;
logger . log (
"Share dialog shown for Android" ,
JSON . stringify ( { timestamp : new Date ( ) . toISOString ( ) } , null , 2 ) ,
) ;
} catch ( shareError ) {
// Log share error but don't fail the operation
logger . error (
"Share dialog failed for Android" ,
JSON . stringify (
{
error : shareError ,
timestamp : new Date ( ) . toISOString ( ) ,
} ,
null ,
2 ,
) ,
) ;
}
// Share the file
let shared = false ;
try {
await Share . share ( {
title : "TimeSafari Backup" ,
text : "Here is your backup file." ,
url : fileUri ,
dialogTitle : "Share your backup file" ,
} ) ;
shared = true ;
logger . log ( "[CapacitorPlatformService] File shared successfully" ) ;
} catch ( shareError ) {
logger . warn ( "[CapacitorPlatformService] Share failed, but file was saved:" , shareError ) ;
// Don't throw error if sharing fails, file is still saved
}
} catch ( error : unknown ) {
return { saved , uri : fileUri , shared } ;
} catch ( error ) {
const err = error as Error ;
const finalErrorLogData = {
error : {
message : err.message ,
name : err.name ,
stack : err.stack ,
} ,
const errLog = {
message : err.message ,
stack : err.stack ,
timestamp : new Date ( ) . toISOString ( ) ,
} ;
logger . error (
"Error in writeFile operation :" ,
JSON . stringify ( finalErrorLogData , null , 2 ) ,
"[CapacitorPlatformService] Error writing or sharing file:" ,
JSON . stringify ( errLog , null , 2 ) ,
) ;
throw new Error ( ` Failed to save file: ${ err . message } ` ) ;
return { saved : false , shared : false , error : err.message } ;
}
}
/ * *
* Writes content to a file in the device ' s app - private storage .
* Then shares the file using the system share dialog .
*
* Works on both Android and iOS without needing external storage permissions .
*
* @param fileName - The name of the file to create ( e . g . "backup.json" )
* @param content - The content to write to the file
* Saves a file using the file picker to let user choose location .
* @param fileName - Name of the file to save
* @param content - File content
* @param mimeType - MIME type of the file
* @returns Promise resolving to the saved file URI
* /
async writeAndShareFile ( fileName : string , content : string ) : Promise < void > {
const timestamp = new Date ( ) . toISOString ( ) ;
const logData = {
action : "writeAndShareFile" ,
fileName ,
contentLength : content.length ,
timestamp ,
} ;
logger . log ( "[CapacitorPlatformService]" , JSON . stringify ( logData , null , 2 ) ) ;
private async saveFileWithPicker (
fileName : string ,
content : string ,
mimeType : string = "application/json"
) : Promise < string > {
try {
// Check storage permissions before proceeding
await this . checkStoragePermissions ( ) ;
const { uri } = await Filesystem . writeFile ( {
// For now, fallback to regular save since file picker save API is complex
// Save to app-private storage and let user share to choose location
const result = await Filesystem . writeFile ( {
path : fileName ,
data : content ,
directory : Directory.Data ,
encoding : Encoding.UTF8 ,
recursive : true ,
} ) ;
logger . log ( "[CapacitorPlatformService] File write successful :" , {
uri ,
logger . log ( "[CapacitorPlatformService] File saved to app storage for picker fallback :" , {
uri : result.uri ,
timestamp : new Date ( ) . toISOString ( ) ,
} ) ;
await Share . share ( {
title : "TimeSafari Backup" ,
text : "Here is your backup file." ,
url : uri ,
dialogTitle : "Share your backup file" ,
} ) ;
return result . uri ;
} catch ( error ) {
const err = error as Error ;
const errLog = {
message : err.message ,
stack : err.stack ,
timestamp : new Date ( ) . toISOString ( ) ,
} ;
logger . error (
"[CapacitorPlatformService] Error writing or sharing file:" ,
JSON . stringify ( errLog , null , 2 ) ,
) ;
throw new Error ( ` Failed to write or share file: ${ err . message } ` ) ;
logger . error ( "[CapacitorPlatformService] File picker save failed:" , error ) ;
throw new Error ( ` Failed to save file with picker: ${ error } ` ) ;
}
}
/ * *
* Saves a file directly to the Downloads folder ( Android ) or Documents ( iOS ) .
* @param fileName - Name of the file to save
* @param content - File content
* @returns Promise resolving to the saved file URI
* /
private async saveToDownloads ( fileName : string , content : string ) : Promise < string > {
try {
if ( this . getCapabilities ( ) . isIOS ) {
// iOS: Save to Documents directory
const result = await Filesystem . writeFile ( {
path : fileName ,
data : content ,
directory : Directory.Documents ,
encoding : Encoding.UTF8 ,
} ) ;
return result . uri ;
} else {
// Android: Save to Downloads directory
const result = await Filesystem . writeFile ( {
path : fileName ,
data : content ,
directory : Directory.External ,
encoding : Encoding.UTF8 ,
} ) ;
return result . uri ;
}
} catch ( error ) {
logger . error ( "[CapacitorPlatformService] Save to downloads failed:" , error ) ;
throw new Error ( ` Failed to save to downloads: ${ error } ` ) ;
}
}
@ -701,7 +657,9 @@ export class CapacitorPlatformService implements PlatformService {
* @returns Promise that resolves when the camera is rotated
* /
async rotateCamera ( ) : Promise < void > {
this . currentDirection = this . currentDirection === "BACK" ? "FRONT" : "BACK" ;
this . currentDirection = this . currentDirection === "BACK" as CameraDirection
? "FRONT" as CameraDirection
: "BACK" as CameraDirection ;
logger . debug ( ` Camera rotated to ${ this . currentDirection } camera ` ) ;
}
@ -738,4 +696,60 @@ export class CapacitorPlatformService implements PlatformService {
params || [ ] ,
) ;
}
/ * *
* Writes content to a file in the app ' s safe storage and offers sharing .
* @param path - The path where the file should be written
* @param content - The content to write to the file
* @returns Promise that resolves when the write is complete
* /
async writeFile ( path : string , content : string ) : Promise < void > {
try {
// Check storage permissions before proceeding
await this . checkStoragePermissions ( ) ;
const logData = {
targetPath : path ,
contentLength : content.length ,
platform : this.getCapabilities ( ) . isIOS ? "iOS" : "Android" ,
timestamp : new Date ( ) . toISOString ( ) ,
} ;
logger . log (
"Starting writeFile operation" ,
JSON . stringify ( logData , null , 2 ) ,
) ;
// Write to app's Data directory
const writeResult = await Filesystem . writeFile ( {
path ,
data : content ,
directory : Directory.Data ,
encoding : Encoding.UTF8 ,
} ) ;
const writeSuccessLogData = {
path : writeResult.uri ,
timestamp : new Date ( ) . toISOString ( ) ,
} ;
logger . log (
"File write successful" ,
JSON . stringify ( writeSuccessLogData , null , 2 ) ,
) ;
} catch ( error : unknown ) {
const err = error as Error ;
const finalErrorLogData = {
error : {
message : err.message ,
name : err.name ,
stack : err.stack ,
} ,
timestamp : new Date ( ) . toISOString ( ) ,
} ;
logger . error (
"Error in writeFile operation:" ,
JSON . stringify ( finalErrorLogData , null , 2 ) ,
) ;
throw new Error ( ` Failed to save file: ${ err . message } ` ) ;
}
}
}