forked from jsnbuchanan/crowd-funder-for-time-pwa
Add detailed TypeScript JSDoc documentation to core service modules: - api.ts: Document error handling utilities and platform-specific logging - plan.ts: Document plan/claim loading with retry mechanism - deepLinks.ts: Document URL parsing and routing functionality - Platform services: - CapacitorPlatformService: Document mobile platform capabilities - ElectronPlatformService: Document desktop placeholder implementation - PyWebViewPlatformService: Document Python bridge placeholder - WebPlatformService: Document web platform limitations and features Key improvements: - Add detailed @remarks sections explaining implementation details - Include usage examples with TypeScript code snippets - Document error handling and platform-specific behaviors - Add @todo tags for unimplemented features - Fix PlanResponse interface to include headers property This documentation enhances code maintainability and developer experience by providing clear guidance on service layer functionality and usage.
192 lines
5.7 KiB
TypeScript
192 lines
5.7 KiB
TypeScript
import { ImageResult, PlatformService } from "../PlatformService";
|
|
import { Filesystem, Directory } from "@capacitor/filesystem";
|
|
import { Camera, CameraResultType, CameraSource } from "@capacitor/camera";
|
|
import { logger } from "../../utils/logger";
|
|
|
|
/**
|
|
* Platform service implementation for Capacitor (mobile) platform.
|
|
* Provides native mobile functionality through Capacitor plugins for:
|
|
* - File system operations
|
|
* - Camera and image picker
|
|
* - Platform-specific features
|
|
*/
|
|
export class CapacitorPlatformService implements PlatformService {
|
|
/**
|
|
* Reads a file from the app's data directory.
|
|
* @param path - Relative path to the file in the app's data directory
|
|
* @returns Promise resolving to the file contents as string
|
|
* @throws Error if file cannot be read or doesn't exist
|
|
*/
|
|
async readFile(path: string): Promise<string> {
|
|
const file = await Filesystem.readFile({
|
|
path,
|
|
directory: Directory.Data,
|
|
});
|
|
if (file.data instanceof Blob) {
|
|
return await file.data.text();
|
|
}
|
|
return file.data;
|
|
}
|
|
|
|
/**
|
|
* Writes content to a file in the app's data directory.
|
|
* @param path - Relative path where to write the file
|
|
* @param content - Content to write to the file
|
|
* @throws Error if write operation fails
|
|
*/
|
|
async writeFile(path: string, content: string): Promise<void> {
|
|
await Filesystem.writeFile({
|
|
path,
|
|
data: content,
|
|
directory: Directory.Data,
|
|
});
|
|
}
|
|
|
|
/**
|
|
* Deletes a file from the app's data directory.
|
|
* @param path - Relative path to the file to delete
|
|
* @throws Error if deletion fails or file doesn't exist
|
|
*/
|
|
async deleteFile(path: string): Promise<void> {
|
|
await Filesystem.deleteFile({
|
|
path,
|
|
directory: Directory.Data,
|
|
});
|
|
}
|
|
|
|
/**
|
|
* Lists files in the specified directory within app's data directory.
|
|
* @param directory - Relative path to the directory to list
|
|
* @returns Promise resolving to array of filenames
|
|
* @throws Error if directory cannot be read or doesn't exist
|
|
*/
|
|
async listFiles(directory: string): Promise<string[]> {
|
|
const result = await Filesystem.readdir({
|
|
path: directory,
|
|
directory: Directory.Data,
|
|
});
|
|
return result.files.map(file => typeof file === 'string' ? file : file.name);
|
|
}
|
|
|
|
/**
|
|
* Opens the device camera to take a picture.
|
|
* Configures camera for high quality images with editing enabled.
|
|
* @returns Promise resolving to the captured image data
|
|
* @throws Error if camera access fails or user cancels
|
|
*/
|
|
async takePicture(): Promise<ImageResult> {
|
|
try {
|
|
const image = await Camera.getPhoto({
|
|
quality: 90,
|
|
allowEditing: true,
|
|
resultType: CameraResultType.Base64,
|
|
source: CameraSource.Camera,
|
|
});
|
|
|
|
const blob = await this.processImageData(image.base64String);
|
|
return {
|
|
blob,
|
|
fileName: `photo_${Date.now()}.${image.format || "jpg"}`,
|
|
};
|
|
} catch (error) {
|
|
logger.error("Error taking picture with Capacitor:", error);
|
|
throw new Error("Failed to take picture");
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Opens the device photo gallery to pick an existing image.
|
|
* Configures picker for high quality images with editing enabled.
|
|
* @returns Promise resolving to the selected image data
|
|
* @throws Error if gallery access fails or user cancels
|
|
*/
|
|
async pickImage(): Promise<ImageResult> {
|
|
try {
|
|
const image = await Camera.getPhoto({
|
|
quality: 90,
|
|
allowEditing: true,
|
|
resultType: CameraResultType.Base64,
|
|
source: CameraSource.Photos,
|
|
});
|
|
|
|
const blob = await this.processImageData(image.base64String);
|
|
return {
|
|
blob,
|
|
fileName: `photo_${Date.now()}.${image.format || "jpg"}`,
|
|
};
|
|
} catch (error) {
|
|
logger.error("Error picking image with Capacitor:", error);
|
|
throw new Error("Failed to pick image");
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Converts base64 image data to a Blob.
|
|
* @param base64String - Base64 encoded image data
|
|
* @returns Promise resolving to image Blob
|
|
* @throws Error if conversion fails
|
|
*/
|
|
private async processImageData(base64String?: string): Promise<Blob> {
|
|
if (!base64String) {
|
|
throw new Error("No image data received");
|
|
}
|
|
|
|
// Convert base64 to blob
|
|
const byteCharacters = atob(base64String);
|
|
const byteArrays = [];
|
|
for (let offset = 0; offset < byteCharacters.length; offset += 512) {
|
|
const slice = byteCharacters.slice(offset, offset + 512);
|
|
const byteNumbers = new Array(slice.length);
|
|
for (let i = 0; i < slice.length; i++) {
|
|
byteNumbers[i] = slice.charCodeAt(i);
|
|
}
|
|
const byteArray = new Uint8Array(byteNumbers);
|
|
byteArrays.push(byteArray);
|
|
}
|
|
return new Blob(byteArrays, { type: "image/jpeg" });
|
|
}
|
|
|
|
/**
|
|
* Checks if running on Capacitor platform.
|
|
* @returns true, as this is the Capacitor implementation
|
|
*/
|
|
isCapacitor(): boolean {
|
|
return true;
|
|
}
|
|
|
|
/**
|
|
* Checks if running on Electron platform.
|
|
* @returns false, as this is not Electron
|
|
*/
|
|
isElectron(): boolean {
|
|
return false;
|
|
}
|
|
|
|
/**
|
|
* Checks if running on PyWebView platform.
|
|
* @returns false, as this is not PyWebView
|
|
*/
|
|
isPyWebView(): boolean {
|
|
return false;
|
|
}
|
|
|
|
/**
|
|
* Checks if running on web platform.
|
|
* @returns false, as this is not web
|
|
*/
|
|
isWeb(): boolean {
|
|
return false;
|
|
}
|
|
|
|
/**
|
|
* Handles deep link URLs for the application.
|
|
* Note: Capacitor handles deep links automatically.
|
|
* @param _url - The deep link URL (unused)
|
|
*/
|
|
async handleDeepLink(_url: string): Promise<void> {
|
|
// Capacitor handles deep links automatically
|
|
// This is just a placeholder for the interface
|
|
return Promise.resolve();
|
|
}
|
|
}
|