You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

231 lines
7.0 KiB

import {
ImageResult,
PlatformService,
PlatformCapabilities,
} from "../PlatformService";
import { logger } from "../../utils/logger";
/**
* Platform service implementation for web browser platform.
* Implements the PlatformService interface with web-specific functionality.
*
* @remarks
* This service provides web-based implementations for:
* - Image capture using the browser's file input
* - Image selection from local filesystem
* - Image processing and conversion
*
* Note: File system operations are not available in the web platform
* due to browser security restrictions. These methods throw appropriate errors.
*/
export class WebPlatformService implements PlatformService {
/**
* Gets the capabilities of the web platform
* @returns Platform capabilities object
*/
getCapabilities(): PlatformCapabilities {
return {
hasFileSystem: false,
hasCamera: true, // Through file input with capture
isMobile: /iPhone|iPad|iPod|Android/i.test(navigator.userAgent),
isIOS: /iPad|iPhone|iPod/.test(navigator.userAgent),
hasFileDownload: true,
needsFileHandlingInstructions: false,
};
}
/**
* Not supported in web platform.
* @param _path - Unused path parameter
* @throws Error indicating file system access is not available
*/
async readFile(_path: string): Promise<string> {
throw new Error("File system access not available in web platform");
}
/**
* Not supported in web platform.
* @param _path - Unused path parameter
* @param _content - Unused content parameter
* @throws Error indicating file system access is not available
*/
async writeFile(_path: string, _content: string): Promise<void> {
throw new Error("File system access not available in web platform");
}
/**
* Not supported in web platform.
* @param _path - Unused path parameter
* @throws Error indicating file system access is not available
*/
async deleteFile(_path: string): Promise<void> {
throw new Error("File system access not available in web platform");
}
/**
* Not supported in web platform.
* @param _directory - Unused directory parameter
* @throws Error indicating file system access is not available
*/
async listFiles(_directory: string): Promise<string[]> {
throw new Error("File system access not available in web platform");
}
/**
* Opens a file input dialog configured for camera capture.
* Creates a temporary file input element to access the device camera.
*
* @returns Promise resolving to the captured image data
* @throws Error if image capture fails or no image is selected
*
* @remarks
* Uses the 'capture' attribute to prefer the device camera.
* Falls back to file selection if camera is not available.
* Processes the captured image to ensure consistent format.
*/
async takePicture(): Promise<ImageResult> {
return new Promise((resolve, reject) => {
const input = document.createElement("input");
input.type = "file";
input.accept = "image/*";
input.capture = "environment";
input.onchange = async (e) => {
const file = (e.target as HTMLInputElement).files?.[0];
if (file) {
try {
const blob = await this.processImageFile(file);
resolve({
blob,
fileName: file.name || "photo.jpg",
});
} catch (error) {
logger.error("Error processing camera image:", error);
reject(new Error("Failed to process camera image"));
}
} else {
reject(new Error("No image captured"));
}
};
input.click();
});
}
/**
* Opens a file input dialog for selecting an image file.
* Creates a temporary file input element to access local files.
*
* @returns Promise resolving to the selected image data
* @throws Error if image processing fails or no image is selected
*
* @remarks
* Allows selection of any image file type.
* Processes the selected image to ensure consistent format.
*/
async pickImage(): Promise<ImageResult> {
return new Promise((resolve, reject) => {
const input = document.createElement("input");
input.type = "file";
input.accept = "image/*";
input.onchange = async (e) => {
const file = (e.target as HTMLInputElement).files?.[0];
if (file) {
try {
const blob = await this.processImageFile(file);
resolve({
blob,
fileName: file.name || "photo.jpg",
});
} catch (error) {
logger.error("Error processing picked image:", error);
reject(new Error("Failed to process picked image"));
}
} else {
reject(new Error("No image selected"));
}
};
input.click();
});
}
/**
* Processes an image file to ensure consistent format.
* Converts the file to a data URL and then to a Blob.
*
* @param file - The image File object to process
* @returns Promise resolving to processed image Blob
* @throws Error if file reading or conversion fails
*
* @remarks
* This method ensures consistent image format across different
* input sources by converting through data URL to Blob.
*/
private async processImageFile(file: File): Promise<Blob> {
return new Promise((resolve, reject) => {
const reader = new FileReader();
reader.onload = (event) => {
const dataUrl = event.target?.result as string;
// Convert to blob to ensure consistent format
fetch(dataUrl)
.then((res) => res.blob())
.then((blob) => resolve(blob))
.catch((error) => {
logger.error("Error converting data URL to blob:", error);
reject(error);
});
};
reader.onerror = (error) => {
logger.error("Error reading file:", error);
reject(error);
};
reader.readAsDataURL(file);
});
}
/**
* Checks if running on Capacitor platform.
* @returns false, as this is not Capacitor
*/
isCapacitor(): boolean {
return false;
}
/**
* 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 true, as this is the web implementation
*/
isWeb(): boolean {
return true;
}
/**
* Handles deep link URLs in the web platform.
* Deep links are handled through URL parameters in the web environment.
*
* @param _url - The deep link URL to handle (unused in web implementation)
* @returns Promise that resolves immediately as web handles URLs naturally
*/
async handleDeepLink(_url: string): Promise<void> {
// Web platform can handle deep links through URL parameters
return Promise.resolve();
}
}