diff --git a/android/.gradle/buildOutputCleanup/buildOutputCleanup.lock b/android/.gradle/buildOutputCleanup/buildOutputCleanup.lock index 6f5da8a4..c7300913 100644 Binary files a/android/.gradle/buildOutputCleanup/buildOutputCleanup.lock and b/android/.gradle/buildOutputCleanup/buildOutputCleanup.lock differ diff --git a/android/.gradle/file-system.probe b/android/.gradle/file-system.probe index d0da6143..d8388669 100644 Binary files a/android/.gradle/file-system.probe and b/android/.gradle/file-system.probe differ diff --git a/src/components/PhotoDialog.vue b/src/components/PhotoDialog.vue index 76fd948a..9ffba49e 100644 --- a/src/components/PhotoDialog.vue +++ b/src/components/PhotoDialog.vue @@ -40,11 +40,6 @@ }" class="max-h-[90vh] max-w-[90vw] object-contain" /> -
@@ -74,87 +69,67 @@
-
- - -
+
+ -
-
+ + -
-
- -
- + + +
@@ -438,12 +304,4 @@ export default class PhotoDialog extends Vue { width: 100%; max-width: 700px; } - -.mirror-video { - transform: scaleX(-1); - -webkit-transform: scaleX(-1); /* For Safari */ - -moz-transform: scaleX(-1); /* For Firefox */ - -ms-transform: scaleX(-1); /* For IE */ - -o-transform: scaleX(-1); /* For Opera */ -} diff --git a/src/services/PlatformService.ts b/src/services/PlatformService.ts index 8cb6c8e4..ab3c0a60 100644 --- a/src/services/PlatformService.ts +++ b/src/services/PlatformService.ts @@ -1,3 +1,8 @@ +export interface ImageResult { + blob: Blob; + fileName: string; +} + export interface PlatformService { // File system operations readFile(path: string): Promise; @@ -6,8 +11,8 @@ export interface PlatformService { listFiles(directory: string): Promise; // Camera operations - takePicture(): Promise; - pickImage(): Promise; + takePicture(): Promise; + pickImage(): Promise; // Platform specific features isCapacitor(): boolean; diff --git a/src/services/platforms/CapacitorPlatformService.ts b/src/services/platforms/CapacitorPlatformService.ts index ae1aa2ab..ad232f3a 100644 --- a/src/services/platforms/CapacitorPlatformService.ts +++ b/src/services/platforms/CapacitorPlatformService.ts @@ -1,8 +1,9 @@ -import { PlatformService } from "../PlatformService"; +import { ImageResult, PlatformService } from "../PlatformService"; import { Capacitor } from "@capacitor/core"; import { Filesystem, Directory } from "@capacitor/filesystem"; import { Camera, CameraResultType, CameraSource } from "@capacitor/camera"; import { App } from "@capacitor/app"; +import { logger } from "../../utils/logger"; export class CapacitorPlatformService implements PlatformService { async readFile(path: string): Promise { @@ -36,24 +37,64 @@ export class CapacitorPlatformService implements PlatformService { return result.files; } - async takePicture(): Promise { - const image = await Camera.getPhoto({ - quality: 90, - allowEditing: true, - resultType: CameraResultType.Uri, - source: CameraSource.Camera, - }); - return image.webPath || ""; + async takePicture(): Promise { + 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"); + } } - async pickImage(): Promise { - const image = await Camera.getPhoto({ - quality: 90, - allowEditing: true, - resultType: CameraResultType.Uri, - source: CameraSource.Photos, - }); - return image.webPath || ""; + async pickImage(): Promise { + 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"); + } + } + + private async processImageData(base64String?: string): Promise { + 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' }); } isCapacitor(): boolean { diff --git a/src/services/platforms/ElectronPlatformService.ts b/src/services/platforms/ElectronPlatformService.ts index 8595d390..00d41174 100644 --- a/src/services/platforms/ElectronPlatformService.ts +++ b/src/services/platforms/ElectronPlatformService.ts @@ -1,28 +1,28 @@ -import { PlatformService } from '../PlatformService'; +import { PlatformService } from "../PlatformService"; export class ElectronPlatformService implements PlatformService { async readFile(path: string): Promise { - throw new Error('Not implemented'); + throw new Error("Not implemented"); } async writeFile(path: string, content: string): Promise { - throw new Error('Not implemented'); + throw new Error("Not implemented"); } async deleteFile(path: string): Promise { - throw new Error('Not implemented'); + throw new Error("Not implemented"); } async listFiles(directory: string): Promise { - throw new Error('Not implemented'); + throw new Error("Not implemented"); } async takePicture(): Promise { - throw new Error('Not implemented'); + throw new Error("Not implemented"); } async pickImage(): Promise { - throw new Error('Not implemented'); + throw new Error("Not implemented"); } isCapacitor(): boolean { @@ -42,6 +42,6 @@ export class ElectronPlatformService implements PlatformService { } async handleDeepLink(url: string): Promise { - throw new Error('Not implemented'); + throw new Error("Not implemented"); } -} \ No newline at end of file +} diff --git a/src/services/platforms/PyWebViewPlatformService.ts b/src/services/platforms/PyWebViewPlatformService.ts index 4d10a285..907241fe 100644 --- a/src/services/platforms/PyWebViewPlatformService.ts +++ b/src/services/platforms/PyWebViewPlatformService.ts @@ -1,28 +1,28 @@ -import { PlatformService } from '../PlatformService'; +import { PlatformService } from "../PlatformService"; export class PyWebViewPlatformService implements PlatformService { async readFile(path: string): Promise { - throw new Error('Not implemented'); + throw new Error("Not implemented"); } async writeFile(path: string, content: string): Promise { - throw new Error('Not implemented'); + throw new Error("Not implemented"); } async deleteFile(path: string): Promise { - throw new Error('Not implemented'); + throw new Error("Not implemented"); } async listFiles(directory: string): Promise { - throw new Error('Not implemented'); + throw new Error("Not implemented"); } async takePicture(): Promise { - throw new Error('Not implemented'); + throw new Error("Not implemented"); } async pickImage(): Promise { - throw new Error('Not implemented'); + throw new Error("Not implemented"); } isCapacitor(): boolean { @@ -42,6 +42,6 @@ export class PyWebViewPlatformService implements PlatformService { } async handleDeepLink(url: string): Promise { - throw new Error('Not implemented'); + throw new Error("Not implemented"); } -} \ No newline at end of file +} diff --git a/src/services/platforms/WebPlatformService.ts b/src/services/platforms/WebPlatformService.ts index befb7cbc..9ae1de83 100644 --- a/src/services/platforms/WebPlatformService.ts +++ b/src/services/platforms/WebPlatformService.ts @@ -1,4 +1,5 @@ -import { PlatformService } from "../PlatformService"; +import { ImageResult, PlatformService } from "../PlatformService"; +import { logger } from "../../utils/logger"; export class WebPlatformService implements PlatformService { async readFile(path: string): Promise { @@ -17,23 +18,28 @@ export class WebPlatformService implements PlatformService { throw new Error("File system access not available in web platform"); } - async takePicture(): Promise { + async takePicture(): Promise { return new Promise((resolve, reject) => { const input = document.createElement("input"); input.type = "file"; input.accept = "image/*"; input.capture = "environment"; - input.onchange = (e) => { + input.onchange = async (e) => { const file = (e.target as HTMLInputElement).files?.[0]; if (file) { - const reader = new FileReader(); - reader.onload = (event) => { - resolve(event.target?.result as string); - }; - reader.readAsDataURL(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 file selected")); + reject(new Error("No image captured")); } }; @@ -41,22 +47,27 @@ export class WebPlatformService implements PlatformService { }); } - async pickImage(): Promise { + async pickImage(): Promise { return new Promise((resolve, reject) => { const input = document.createElement("input"); input.type = "file"; input.accept = "image/*"; - input.onchange = (e) => { + input.onchange = async (e) => { const file = (e.target as HTMLInputElement).files?.[0]; if (file) { - const reader = new FileReader(); - reader.onload = (event) => { - resolve(event.target?.result as string); - }; - reader.readAsDataURL(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 file selected")); + reject(new Error("No image selected")); } }; @@ -64,6 +75,28 @@ export class WebPlatformService implements PlatformService { }); } + private async processImageFile(file: File): Promise { + 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); + }); + } + isCapacitor(): boolean { return false; }