diff --git a/.cursor/rules/qr-code-handling-rule.mdc b/.cursor/rules/qr-code-handling-rule.mdc deleted file mode 100644 index d78e2e28..00000000 --- a/.cursor/rules/qr-code-handling-rule.mdc +++ /dev/null @@ -1,177 +0,0 @@ ---- -description: -globs: -alwaysApply: true ---- -# QR Code Handling Rule - -## Architecture Overview - -The QR code scanning functionality follows a platform-agnostic design using a factory pattern that provides different implementations for web and mobile platforms. - -### Core Components - -1. **Factory Pattern** -- `QRScannerFactory` - Creates appropriate scanner instance based on platform -- Common interface `QRScannerService` implemented by all scanners -- Platform detection via Capacitor and build flags - -2. **Platform-Specific Implementations** -- `CapacitorQRScanner` - Native mobile implementation using MLKit -- `WebInlineQRScanner` - Web browser implementation using MediaDevices API -- `QRScannerDialog.vue` - Shared UI component - -## Mobile Implementation (Capacitor) - -### Technology Stack -- Uses `@capacitor-mlkit/barcode-scanning` plugin -- Configured in `capacitor.config.ts` -- Native camera access through platform APIs - -### Key Features -- Direct camera access via native APIs -- Optimized for mobile performance -- Supports both iOS and Android -- Real-time QR code detection -- Back camera preferred for scanning - -### Configuration -```typescript -MLKitBarcodeScanner: { - formats: ['QR_CODE'], - detectorSize: 1.0, - lensFacing: 'back', - googleBarcodeScannerModuleInstallState: true -} -``` - -### Permissions Handling -1. Check permissions via `BarcodeScanner.checkPermissions()` -2. Request permissions if needed -3. Handle permission states (granted/denied) -4. Graceful fallbacks for permission issues - -## Web Implementation - -### Technology Stack -- Uses browser's MediaDevices API -- Vue.js components for UI -- EventEmitter for stream management - -### Key Features -- Browser-based camera access -- Inline camera preview -- Responsive design -- Cross-browser compatibility -- Progressive enhancement - -### Permissions Handling -1. Uses browser's permission API -2. MediaDevices API for camera access -3. Handles secure context requirements -4. Provides user feedback for permission states - -## Shared Features - -### Error Handling -1. Permission denied scenarios -2. Device compatibility checks -3. Camera access failures -4. QR code validation -5. Network connectivity issues - -### User Experience -1. Clear feedback during scanning -2. Loading states -3. Error messages -4. Success confirmations -5. Camera preview - -### Security -1. HTTPS requirement for web -2. Permission validation -3. Data validation -4. Safe error handling - -## Usage Guidelines - -### Platform Detection -```typescript -const isNative = QRScannerFactory.isNativePlatform(); -if (isNative) { - // Use native scanner -} else { - // Use web scanner -} -``` - -### Implementation Example -```typescript -const scanner = QRScannerFactory.getInstance(); -await scanner.checkPermissions(); -await scanner.startScan(); -scanner.addListener({ - onScan: (result) => { - // Handle scan result - }, - onError: (error) => { - // Handle error - } -}); -``` - -### Best Practices -1. Always check permissions before starting scan -2. Clean up resources after scanning -3. Handle all error cases -4. Provide clear user feedback -5. Test on multiple devices/browsers - -## Platform-Specific Notes - -### Mobile (Capacitor) -1. Use native camera API when available -2. Handle device rotation -3. Support both front/back cameras -4. Manage system permissions properly -5. Handle app lifecycle events - -### Web -1. Check browser compatibility -2. Handle secure context requirement -3. Manage memory usage -4. Clean up MediaStream -5. Handle tab visibility changes - -## Testing Requirements - -1. Test on multiple devices -2. Verify permission flows -3. Check error handling -4. Validate cleanup -5. Verify cross-platform behavior - -## Service Interface - -```typescript -interface QRScannerService { - checkPermissions(): Promise; - requestPermissions(): Promise; - isSupported(): Promise; - startScan(options?: QRScannerOptions): Promise; - stopScan(): Promise; - addListener(listener: ScanListener): void; - onStream(callback: (stream: MediaStream | null) => void): void; - cleanup(): Promise; -} - -interface ScanListener { - onScan: (result: string) => void; - onError?: (error: Error) => void; -} - -interface QRScannerOptions { - camera?: "front" | "back"; - showPreview?: boolean; - playSound?: boolean; -} \ No newline at end of file diff --git a/.cursor/rules/qr-code-implementation-guide.mdc b/.cursor/rules/qr-code-implementation-guide.mdc deleted file mode 100644 index fd488f96..00000000 --- a/.cursor/rules/qr-code-implementation-guide.mdc +++ /dev/null @@ -1,533 +0,0 @@ ---- -description: -globs: -alwaysApply: true ---- -# QR Code Implementation Guide - -## Directory Structure - -``` -src/ -├── services/ -│ └── QRScanner/ -│ ├── types.ts # Core interfaces and types -│ ├── QRScannerFactory.ts # Factory for creating scanner instances -│ ├── CapacitorQRScanner.ts # Mobile implementation using MLKit -│ ├── WebInlineQRScanner.ts # Web implementation using MediaDevices API -│ └── interfaces.ts # Additional interfaces -├── components/ -│ └── QRScanner/ -│ └── QRScannerDialog.vue # Shared UI component -``` - -## Core Interfaces - -```typescript -// types.ts -export interface ScanListener { - onScan: (result: string) => void; - onError?: (error: Error) => void; -} - -export interface QRScannerOptions { - camera?: "front" | "back"; - showPreview?: boolean; - playSound?: boolean; -} - -export interface QRScannerService { - checkPermissions(): Promise; - requestPermissions(): Promise; - isSupported(): Promise; - startScan(options?: QRScannerOptions): Promise; - stopScan(): Promise; - addListener(listener: ScanListener): void; - onStream(callback: (stream: MediaStream | null) => void): void; - cleanup(): Promise; -} -``` - -## Configuration Files - -### Vite Configuration -```typescript -// vite.config.common.mts -export function createBuildConfig(mode: string) { - return { - define: { - 'process.env.VITE_PLATFORM': JSON.stringify(mode), - 'process.env.VITE_PWA_ENABLED': JSON.stringify(!isNative), - __IS_MOBILE__: JSON.stringify(isCapacitor), - __USE_QR_READER__: JSON.stringify(!isCapacitor) - } - }; -} -``` - -### Capacitor Configuration -```typescript -// capacitor.config.ts -const config: CapacitorConfig = { - plugins: { - MLKitBarcodeScanner: { - formats: ['QR_CODE'], - detectorSize: 1.0, - lensFacing: 'back', - googleBarcodeScannerModuleInstallState: true - } - } -}; -``` - -## Implementation Steps - -1. **Install Dependencies** -```bash -npm install @capacitor-mlkit/barcode-scanning -``` - -2. **Create Core Types** -Create the interface files as shown above. - -3. **Implement Factory** -```typescript -// QRScannerFactory.ts -export class QRScannerFactory { - private static instance: QRScannerService | null = null; - - private static isNativePlatform(): boolean { - const capacitorNative = Capacitor.isNativePlatform(); - const isMobile = typeof __IS_MOBILE__ !== "undefined" ? __IS_MOBILE__ : capacitorNative; - const platform = Capacitor.getPlatform(); - - // Always use native scanner on Android/iOS - if (platform === "android" || platform === "ios") { - return true; - } - - // For other platforms, use native if available - return capacitorNative || isMobile; - } - - static getInstance(): QRScannerService { - if (!this.instance) { - const isNative = this.isNativePlatform(); - - if (isNative) { - this.instance = new CapacitorQRScanner(); - } else { - this.instance = new WebInlineQRScanner(); - } - } - return this.instance!; - } - - static async cleanup(): Promise { - if (this.instance) { - await this.instance.cleanup(); - this.instance = null; - } - } -} -``` - -4. **Implement Mobile Scanner** -```typescript -// CapacitorQRScanner.ts -export class CapacitorQRScanner implements QRScannerService { - private scanListener: ScanListener | null = null; - private isScanning = false; - private listenerHandles: Array<() => Promise> = []; - private cleanupPromise: Promise | null = null; - - async checkPermissions(): Promise { - try { - const { camera } = await BarcodeScanner.checkPermissions(); - return camera === "granted"; - } catch (error) { - logger.error("Error checking camera permissions:", error); - return false; - } - } - - async requestPermissions(): Promise { - try { - if (await this.checkPermissions()) { - return true; - } - const { camera } = await BarcodeScanner.requestPermissions(); - return camera === "granted"; - } catch (error) { - logger.error("Error requesting camera permissions:", error); - return false; - } - } - - async isSupported(): Promise { - try { - const { supported } = await BarcodeScanner.isSupported(); - return supported; - } catch (error) { - logger.error("Error checking scanner support:", error); - return false; - } - } - - async startScan(options?: QRScannerOptions): Promise { - if (this.isScanning) return; - if (this.cleanupPromise) { - await this.cleanupPromise; - } - - try { - if (!(await this.checkPermissions())) { - const granted = await this.requestPermissions(); - if (!granted) { - throw new Error("Camera permission denied"); - } - } - - if (!(await this.isSupported())) { - throw new Error("QR scanning not supported on this device"); - } - - this.isScanning = true; - - const scanOptions: StartScanOptions = { - formats: [BarcodeFormat.QrCode], - lensFacing: options?.camera === "front" ? LensFacing.Front : LensFacing.Back, - }; - - const handle = await BarcodeScanner.addListener("barcodeScanned", (result) => { - if (this.scanListener && result.barcode?.rawValue) { - this.scanListener.onScan(result.barcode.rawValue); - } - }); - this.listenerHandles.push(handle.remove); - - await BarcodeScanner.startScan(scanOptions); - } catch (error) { - this.isScanning = false; - await this.cleanup(); - this.scanListener?.onError?.(error instanceof Error ? error : new Error(String(error))); - throw error; - } - } - - async stopScan(): Promise { - if (!this.isScanning) return; - this.isScanning = false; - - try { - await BarcodeScanner.stopScan(); - } catch (error) { - logger.error("Error stopping scan:", error); - throw error; - } - } - - addListener(listener: ScanListener): void { - this.scanListener = listener; - } - - onStream(callback: (stream: MediaStream | null) => void): void { - // No-op for native scanner - callback(null); - } - - async cleanup(): Promise { - await this.stopScan(); - for (const handle of this.listenerHandles) { - await handle(); - } - this.listenerHandles = []; - this.scanListener = null; - } -} -``` - -5. **Implement Web Scanner** -```typescript -// WebInlineQRScanner.ts -export class WebInlineQRScanner implements QRScannerService { - private scanListener: ScanListener | null = null; - private isScanning = false; - private stream: MediaStream | null = null; - private events = new EventEmitter(); - - constructor(private options?: QRScannerOptions) {} - - async checkPermissions(): Promise { - try { - const permissions = await navigator.permissions.query({ - name: "camera" as PermissionName, - }); - return permissions.state === "granted"; - } catch (error) { - logger.error("Error checking camera permissions:", error); - return false; - } - } - - async requestPermissions(): Promise { - try { - const stream = await navigator.mediaDevices.getUserMedia({ - video: { - facingMode: "environment", - width: { ideal: 1280 }, - height: { ideal: 720 }, - }, - }); - stream.getTracks().forEach(track => track.stop()); - return true; - } catch (error) { - logger.error("Error requesting camera permissions:", error); - return false; - } - } - - async isSupported(): Promise { - return 'mediaDevices' in navigator && 'getUserMedia' in navigator.mediaDevices; - } - - async startScan(): Promise { - if (this.isScanning) return; - - try { - this.isScanning = true; - this.stream = await navigator.mediaDevices.getUserMedia({ - video: { - facingMode: "environment", - width: { ideal: 1280 }, - height: { ideal: 720 }, - }, - }); - this.events.emit("stream", this.stream); - } catch (error) { - this.isScanning = false; - const wrappedError = error instanceof Error ? error : new Error(String(error)); - this.scanListener?.onError?.(wrappedError); - throw wrappedError; - } - } - - async stopScan(): Promise { - if (!this.isScanning) return; - - try { - if (this.stream) { - this.stream.getTracks().forEach(track => track.stop()); - this.stream = null; - } - this.events.emit("stream", null); - } catch (error) { - logger.error("Error stopping scan:", error); - throw error; - } finally { - this.isScanning = false; - } - } - - addListener(listener: ScanListener): void { - this.scanListener = listener; - } - - onStream(callback: (stream: MediaStream | null) => void): void { - this.events.on("stream", callback); - } - - async cleanup(): Promise { - try { - await this.stopScan(); - this.events.removeAllListeners(); - } catch (error) { - logger.error("Error during cleanup:", error); - } - } -} -``` - -## Usage Example - -```typescript -// Example usage in a Vue component -import { QRScannerFactory } from '@/services/QRScanner/QRScannerFactory'; - -export default defineComponent({ - async mounted() { - const scanner = QRScannerFactory.getInstance(); - - try { - // Check and request permissions - if (!(await scanner.checkPermissions())) { - const granted = await scanner.requestPermissions(); - if (!granted) { - throw new Error('Camera permission denied'); - } - } - - // Add scan listener - scanner.addListener({ - onScan: (result) => { - console.log('QR Code scanned:', result); - }, - onError: (error) => { - console.error('Scan error:', error); - } - }); - - // Start scanning - await scanner.startScan({ - camera: 'back', - showPreview: true - }); - - // Handle stream for preview - scanner.onStream((stream) => { - if (stream) { - // Update video element with stream - this.videoElement.srcObject = stream; - } - }); - } catch (error) { - console.error('Failed to start scanner:', error); - } - }, - - async beforeUnmount() { - // Clean up scanner - await QRScannerFactory.cleanup(); - } -}); -``` - -## Best Practices - -1. **Error Handling** - - Always implement error handlers in scan listeners - - Handle permission denials gracefully - - Provide user feedback for errors - - Clean up resources on errors - -2. **Resource Management** - - Always call cleanup when done - - Stop camera streams properly - - Remove event listeners - - Handle component unmounting - -3. **Performance** - - Use appropriate camera resolution - - Clean up resources promptly - - Handle platform-specific optimizations - - Monitor memory usage - -4. **Security** - - Require HTTPS for web implementation - - Validate scanned data - - Handle permissions properly - - Sanitize user input - -5. **Testing** - - Test on multiple devices - - Verify permission flows - - Check error scenarios - - Validate cleanup - - Test cross-platform behavior - -## Platform-Specific Notes - -### Mobile (Capacitor) -- Uses MLKit for optimal performance -- Handles native permissions -- Supports both iOS and Android -- Uses back camera by default -- Handles device rotation -- Provides native UI for scanning - -### Web -- Uses MediaDevices API -- Requires HTTPS for camera access -- Handles browser compatibility -- Manages memory and resources -- Provides fallback UI -- Uses vue-qrcode-reader for web scanning - -## Testing - -1. **Unit Tests** -- Test factory pattern -- Test platform detection -- Test error handling -- Test cleanup procedures -- Test permission flows - -2. **Integration Tests** -- Test camera access -- Test QR code detection -- Test cross-platform behavior -- Test UI components -- Test error scenarios - -3. **E2E Tests** -- Test complete scanning flow -- Test permission handling -- Test cross-platform compatibility -- Test error recovery -- Test cleanup procedures - -## Best Practices - -1. **Error Handling** -- Always handle permission errors gracefully -- Provide clear error messages to users -- Implement proper cleanup on errors -- Log errors for debugging - -2. **Performance** -- Clean up resources when not in use -- Handle device rotation properly -- Optimize camera usage -- Manage memory efficiently - -3. **Security** -- Request minimum required permissions -- Handle sensitive data securely -- Validate scanned data -- Implement proper cleanup - -4. **User Experience** -- Provide clear feedback -- Handle edge cases gracefully -- Support both platforms seamlessly -- Implement proper loading states - -## Troubleshooting - -1. **Common Issues** -- Camera permissions denied -- Device not supported -- Scanner not working -- Memory leaks -- UI glitches - -2. **Solutions** -- Check permissions -- Verify device support -- Debug scanner implementation -- Monitor memory usage -- Test UI components - -## Maintenance - -1. **Regular Updates** -- Keep dependencies updated -- Monitor platform changes -- Update documentation -- Review security patches - -2. **Performance Monitoring** -- Track memory usage -- Monitor camera performance -- Check error rates -- Analyze user feedback diff --git a/doc/camera-implementation.md b/doc/camera-implementation.md deleted file mode 100644 index bd00e268..00000000 --- a/doc/camera-implementation.md +++ /dev/null @@ -1,507 +0,0 @@ -# Camera Implementation Documentation - -## Overview - -This document describes how camera functionality is implemented across the TimeSafari application. The application uses cameras for several purposes: - -1. QR Code scanning for contact sharing and verification -2. Photo capture for gift records -3. Profile photo management -4. Shared photo handling -5. Image upload and processing - -## Components - -### QRScannerDialog.vue - -Primary component for QR code scanning in web browsers. - -**Key Features:** - -- Uses `qrcode-stream` for web-based QR scanning -- Supports both front and back cameras -- Provides real-time camera status feedback -- Implements error handling with user-friendly messages -- Includes camera switching functionality - -**Camera Access Flow:** - -1. Checks for camera API availability -2. Enumerates available video devices -3. Requests camera permissions -4. Initializes camera stream with preferred settings -5. Handles various error conditions with specific messages - -### PhotoDialog.vue - -Component for photo capture and selection. - -**Key Features:** - -- Cross-platform photo capture interface -- Image cropping capabilities -- File selection fallback -- Unified interface for different platforms -- Progress feedback during upload -- Comprehensive error handling - -**Camera Access Flow:** - -1. User initiates photo capture -2. Platform-specific camera access is requested -3. Image is captured or selected -4. Optional cropping is performed -5. Image is processed and uploaded -6. URL is returned to caller - -### ImageMethodDialog.vue - -Component for selecting image input method. - -**Key Features:** -- Multiple input methods (camera, file upload, URL) -- Unified interface for image selection -- Integration with PhotoDialog for processing -- Support for image cropping -- URL-based image handling - -**Camera Access Flow:** - -1. User selects camera option -2. PhotoDialog is opened for capture -3. Captured image is processed -4. Image is returned to parent component - -### SharedPhotoView.vue - -Component for handling shared photos. - -**Key Features:** -- Processes incoming shared photos -- Options to use photo for gifts or profile -- Image preview and confirmation -- Server upload integration -- Temporary storage management - -**Photo Processing Flow:** - -1. Photo is shared to application -2. Stored temporarily in IndexedDB -3. User chooses usage (gift/profile) -4. Image is processed accordingly -5. Server upload is performed - -### ContactQRScanShowView.vue - -Component for QR code scanning in contact sharing. - -**Key Features:** -- QR code scanning interface -- Camera controls (start/stop) -- Platform-specific implementations -- Error handling and status feedback - -**Camera Access Flow:** - -1. User initiates scanning -2. Camera permissions are checked -3. Camera stream is initialized -4. QR codes are detected in real-time -5. Results are processed - -## Services - -### QRScanner Services - -#### WebDialogQRScanner - -Web-based implementation of QR scanning. - -**Key Methods:** - -- `checkPermissions()`: Verifies camera permission status -- `requestPermissions()`: Requests camera access -- `isSupported()`: Checks for camera API support -- Handles various error conditions with specific messages - -#### CapacitorQRScanner - -Native implementation using Capacitor's MLKit. - -**Key Features:** - -- Uses `@capacitor-mlkit/barcode-scanning` -- Supports both front and back cameras -- Implements permission management -- Provides continuous scanning capability - -### Platform Services - -#### WebPlatformService - -Web-specific implementation of platform features. - -**Camera Capabilities:** - -- Uses HTML5 file input with capture attribute for mobile -- Uses getUserMedia API for desktop webcam access -- Falls back to file selection if camera unavailable -- Processes captured images for consistent format -- Handles both mobile and desktop browser environments - -#### CapacitorPlatformService - -Native implementation using Capacitor. - -**Camera Features:** - -- Uses `Camera.getPhoto()` for native camera access -- Supports image editing -- Configures high-quality image capture -- Handles base64 image processing -- Provides native camera UI - -#### ElectronPlatformService - -Desktop implementation (currently unimplemented). - -**Status:** - -- Camera functionality not yet implemented -- Planned to use Electron's media APIs -- Will support desktop camera access - -## Camera Usage Scenarios - -### Gift Photo Capture - -**Implementation:** -- Uses PhotoDialog for capture/selection -- Supports multiple input methods -- Optional image cropping -- Server upload with authentication -- Integration with gift records - -**Flow:** -1. User initiates photo capture from gift details -2. ImageMethodDialog presents input options -3. PhotoDialog handles capture/selection -4. Image is processed and uploaded -5. URL is attached to gift record - -### Profile Photo Management - -**Implementation:** -- Uses same PhotoDialog component -- Enforces square aspect ratio -- Requires image cropping -- Updates user profile settings -- Handles profile image updates - -**Flow:** -1. User initiates profile photo update -2. PhotoDialog opens with cropping enabled -3. Image is captured/selected -4. User crops to square aspect ratio -5. Image is uploaded and profile updated - -### Shared Photo Processing - -**Implementation:** -- Handles incoming shared photos -- Temporary storage in IndexedDB -- Options for photo usage -- Server upload integration -- Cleanup after processing - -**Flow:** -1. Photo is shared to application -2. Stored temporarily in IndexedDB -3. SharedPhotoView presents options -4. User chooses usage (gift/profile) -5. Image is processed accordingly - -### QR Code Scanning - -**Implementation:** -- Platform-specific scanning components -- Real-time camera feed processing -- QR code detection and validation -- Contact information processing -- Error handling and retry - -**Flow:** -1. User initiates QR scanning -2. Camera permissions are checked -3. Camera stream is initialized -4. QR codes are detected -5. Contact information is processed - -### QR Code Workflow - -**Implementation Details:** - -The QR code scanning workflow is implemented across multiple components and services to provide a seamless experience for contact sharing and verification. The system supports both web-based and native implementations through platform-specific services. - -#### QR Code Generation - -**Contact QR Codes:** -- Generated using `qrcode.vue` component -- Contains encrypted contact information -- Includes user ID and verification data -- Supports offline sharing -- Implements error correction - -**QR Code Format:** -```json -{ - "type": "contact", - "userId": "encrypted_user_id", - "timestamp": "creation_time", - "version": "qr_code_version", - "data": "encrypted_contact_data" -} -``` - -#### QR Code Scanning Workflow - -**1. Initiation:** -- User selects "Scan QR Code" option -- Platform-specific scanner is initialized -- Camera permissions are verified -- Appropriate scanner component is loaded - -**2. Platform-Specific Implementation:** - -*Web Implementation:* -- Uses `qrcode-stream` for real-time scanning -- Supports both front and back cameras -- Implements continuous scanning -- Provides visual feedback for scanning status -- Handles browser compatibility issues - -*Native Implementation (Capacitor):* -- Uses `@capacitor-mlkit/barcode-scanning` -- Leverages native camera capabilities -- Provides optimized scanning performance -- Supports multiple barcode formats -- Implements native permission handling - -**3. Scanning Process:** -- Camera stream is initialized -- Real-time frame analysis begins -- QR codes are detected and decoded -- Validation of QR code format -- Processing of contact information - -**4. Contact Processing:** -- Decryption of contact data -- Validation of user information -- Verification of timestamp -- Check for duplicate contacts -- Processing of shared data - -**5. Error Handling:** -- Invalid QR code format -- Expired QR codes -- Duplicate contact attempts -- Network connectivity issues -- Permission denials -- Camera access problems - -**6. Success Flow:** -- Contact information is extracted -- User is prompted for confirmation -- Contact is added to user's list -- Success notification is displayed -- Camera resources are cleaned up - -#### Security Measures - -**QR Code Security:** -- Encryption of contact data -- Timestamp validation -- Version checking -- User verification -- Rate limiting for scans - -**Data Protection:** -- Secure transmission of contact data -- Validation of QR code authenticity -- Prevention of duplicate scans -- Protection against malicious codes -- Secure storage of contact information - -#### User Experience - -**Scanning Interface:** -- Clear visual feedback -- Camera preview -- Scanning status indicators -- Error messages -- Success confirmations - -**Accessibility:** -- Support for different screen sizes -- Clear instructions -- Error recovery options -- Alternative input methods -- Offline capability - -#### Performance Considerations - -**Optimization:** -- Efficient camera resource usage -- Quick QR code detection -- Minimal processing overhead -- Battery usage optimization -- Memory management - -**Platform-Specific Optimizations:** -- Web: Optimized for browser performance -- Native: Leverages device capabilities -- Desktop: Efficient resource usage -- Mobile: Battery and performance balance - -## Platform-Specific Considerations - -### iOS - -- Requires `NSCameraUsageDescription` in Info.plist -- Supports both front and back cameras -- Implements proper permission handling -- Uses native camera UI through Capacitor -- Handles photo library access - -### Android - -- Requires camera permissions in manifest -- Supports both front and back cameras -- Handles permission requests through Capacitor -- Uses native camera UI -- Manages photo library access - -### Web - -- Requires HTTPS for camera access -- Implements fallback mechanisms -- Handles browser compatibility issues -- Uses getUserMedia API on desktop -- Uses file input with capture on mobile -- Supports multiple input methods - -## Error Handling - -### Common Error Scenarios - -1. No camera found -2. Permission denied -3. Camera in use by another application -4. HTTPS required -5. Browser compatibility issues -6. Upload failures -7. Image processing errors - -### Error Response - -- User-friendly error messages -- Troubleshooting tips -- Clear instructions for resolution -- Platform-specific guidance -- Graceful fallbacks - -## Security Considerations - -### Permission Management - -- Explicit permission requests -- Permission state tracking -- Graceful handling of denied permissions -- Platform-specific permission handling -- Secure permission storage - -### Data Handling - -- Secure image processing -- Proper cleanup of camera resources -- No persistent storage of camera data -- Secure server upload -- Temporary storage management - -## Best Practices - -### Camera Access - -1. Always check for camera availability -2. Request permissions explicitly -3. Handle all error conditions -4. Provide clear user feedback -5. Implement proper cleanup -6. Use platform-specific optimizations - -### Performance - -1. Optimize camera resolution -2. Implement proper resource cleanup -3. Handle camera switching efficiently -4. Manage memory usage -5. Optimize image processing -6. Handle upload efficiently - -### User Experience - -1. Clear status indicators -2. Intuitive camera controls -3. Helpful error messages -4. Smooth camera switching -5. Responsive UI feedback -6. Platform-appropriate UI - -## Future Improvements - -### Planned Enhancements - -1. Implement Electron camera support -2. Add advanced camera features -3. Improve error handling -4. Enhance user feedback -5. Optimize performance -6. Add image compression options - -### Known Issues - -1. Electron camera implementation pending -2. Some browser compatibility limitations -3. Platform-specific quirks to address -4. Mobile browser camera access limitations -5. Image upload performance on slow connections - -## Dependencies - -### Key Packages - -- `@capacitor-mlkit/barcode-scanning` -- `qrcode-stream` -- `vue-picture-cropper` -- `@capacitor/camera` -- Platform-specific camera APIs - -## Testing - -### Test Scenarios - -1. Permission handling -2. Camera switching -3. Error conditions -4. Platform compatibility -5. Performance metrics -6. Upload scenarios -7. Image processing - -### Test Environment - -- Multiple browsers -- iOS and Android devices -- Desktop platforms -- Various network conditions -- Different camera configurations diff --git a/doc/qr-code-implementation-guide.md b/doc/qr-code-implementation-guide.md new file mode 100644 index 00000000..bf7e448d --- /dev/null +++ b/doc/qr-code-implementation-guide.md @@ -0,0 +1,284 @@ +# QR Code Implementation Guide + +## Overview + +This document describes the QR code scanning and generation implementation in the TimeSafari application. The system uses a platform-agnostic design with specific implementations for web and mobile platforms. + +## Architecture + +### Directory Structure +``` +src/ +├── services/ +│ └── QRScanner/ +│ ├── types.ts # Core interfaces and types +│ ├── QRScannerFactory.ts # Factory for creating scanner instances +│ ├── CapacitorQRScanner.ts # Mobile implementation using MLKit +│ ├── WebInlineQRScanner.ts # Web implementation using MediaDevices API +│ └── interfaces.ts # Additional interfaces +├── components/ +│ └── QRScanner/ +│ └── QRScannerDialog.vue # Shared UI component +└── views/ + ├── ContactQRScanView.vue # Dedicated scanning view + └── ContactQRScanShowView.vue # Combined QR display and scanning view +``` + +### Core Components + +1. **Factory Pattern** + - `QRScannerFactory` - Creates appropriate scanner instance based on platform + - Common interface `QRScannerService` implemented by all scanners + - Platform detection via Capacitor and build flags + +2. **Platform-Specific Implementations** + - `CapacitorQRScanner` - Native mobile implementation using MLKit + - `WebInlineQRScanner` - Web browser implementation using MediaDevices API + - `QRScannerDialog.vue` - Shared UI component + +3. **View Components** + - `ContactQRScanView` - Dedicated view for scanning QR codes + - `ContactQRScanShowView` - Combined view for displaying and scanning QR codes + +## Implementation Details + +### Core Interfaces + +```typescript +interface QRScannerService { + checkPermissions(): Promise; + requestPermissions(): Promise; + isSupported(): Promise; + startScan(options?: QRScannerOptions): Promise; + stopScan(): Promise; + addListener(listener: ScanListener): void; + onStream(callback: (stream: MediaStream | null) => void): void; + cleanup(): Promise; +} + +interface ScanListener { + onScan: (result: string) => void; + onError?: (error: Error) => void; +} + +interface QRScannerOptions { + camera?: "front" | "back"; + showPreview?: boolean; + playSound?: boolean; +} +``` + +### Platform-Specific Implementations + +#### Mobile (Capacitor) +- Uses `@capacitor-mlkit/barcode-scanning` +- Native camera access through platform APIs +- Optimized for mobile performance +- Supports both iOS and Android +- Real-time QR code detection +- Back camera preferred for scanning + +Configuration: +```typescript +// capacitor.config.ts +const config: CapacitorConfig = { + plugins: { + MLKitBarcodeScanner: { + formats: ['QR_CODE'], + detectorSize: 1.0, + lensFacing: 'back', + googleBarcodeScannerModuleInstallState: true + } + } +}; +``` + +#### Web +- Uses browser's MediaDevices API +- Vue.js components for UI +- EventEmitter for stream management +- Browser-based camera access +- Inline camera preview +- Responsive design +- Cross-browser compatibility + +### View Components + +#### ContactQRScanView +- Dedicated view for scanning QR codes +- Full-screen camera interface +- Simple UI focused on scanning +- Used primarily on native platforms +- Streamlined scanning experience + +#### ContactQRScanShowView +- Combined view for QR code display and scanning +- Shows user's own QR code +- Handles user registration status +- Provides options to copy contact information +- Platform-specific scanning implementation: + - Native: Button to navigate to ContactQRScanView + - Web: Built-in scanning functionality + +### QR Code Workflow + +1. **Initiation** + - User selects "Scan QR Code" option + - Platform-specific scanner is initialized + - Camera permissions are verified + - Appropriate scanner component is loaded + +2. **Platform-Specific Implementation** + - Web: Uses `qrcode-stream` for real-time scanning + - Native: Uses `@capacitor-mlkit/barcode-scanning` + +3. **Scanning Process** + - Camera stream initialization + - Real-time frame analysis + - QR code detection and decoding + - Validation of QR code format + - Processing of contact information + +4. **Contact Processing** + - Decryption of contact data + - Validation of user information + - Verification of timestamp + - Check for duplicate contacts + - Processing of shared data + +## Build Configuration + +### Common Vite Configuration +```typescript +// vite.config.common.mts +export async function createBuildConfig(mode: string) { + const isCapacitor = mode === "capacitor"; + + return defineConfig({ + define: { + 'process.env.VITE_PLATFORM': JSON.stringify(mode), + 'process.env.VITE_PWA_ENABLED': JSON.stringify(!isNative), + __IS_MOBILE__: JSON.stringify(isCapacitor), + __USE_QR_READER__: JSON.stringify(!isCapacitor) + }, + optimizeDeps: { + include: [ + '@capacitor-mlkit/barcode-scanning', + 'vue-qrcode-reader' + ] + } + }); +} +``` + +### Platform-Specific Builds +```json +{ + "scripts": { + "build:web": "vite build --config vite.config.web.mts", + "build:capacitor": "vite build --config vite.config.capacitor.mts", + "build:all": "npm run build:web && npm run build:capacitor" + } +} +``` + +## Error Handling + +### Common Error Scenarios +1. No camera found +2. Permission denied +3. Camera in use by another application +4. HTTPS required +5. Browser compatibility issues +6. Invalid QR code format +7. Expired QR codes +8. Duplicate contact attempts +9. Network connectivity issues + +### Error Response +- User-friendly error messages +- Troubleshooting tips +- Clear instructions for resolution +- Platform-specific guidance + +## Security Considerations + +### QR Code Security +- Encryption of contact data +- Timestamp validation +- Version checking +- User verification +- Rate limiting for scans + +### Data Protection +- Secure transmission of contact data +- Validation of QR code authenticity +- Prevention of duplicate scans +- Protection against malicious codes +- Secure storage of contact information + +## Best Practices + +### Camera Access +1. Always check for camera availability +2. Request permissions explicitly +3. Handle all error conditions +4. Provide clear user feedback +5. Implement proper cleanup + +### Performance +1. Optimize camera resolution +2. Implement proper resource cleanup +3. Handle camera switching efficiently +4. Manage memory usage +5. Battery usage optimization + +### User Experience +1. Clear visual feedback +2. Camera preview +3. Scanning status indicators +4. Error messages +5. Success confirmations +6. Intuitive camera controls +7. Smooth camera switching +8. Responsive UI feedback + +## Testing + +### Test Scenarios +1. Permission handling +2. Camera switching +3. Error conditions +4. Platform compatibility +5. Performance metrics +6. QR code detection +7. Contact processing +8. Security validation + +### Test Environment +- Multiple browsers +- iOS and Android devices +- Various network conditions +- Different camera configurations + +## Dependencies + +### Key Packages +- `@capacitor-mlkit/barcode-scanning` +- `qrcode-stream` +- `vue-qrcode-reader` +- Platform-specific camera APIs + +## Maintenance + +### Regular Updates +- Keep dependencies updated +- Monitor platform changes +- Update documentation +- Review security patches + +### Performance Monitoring +- Track memory usage +- Monitor camera performance +- Check error rates +- Analyze user feedback \ No newline at end of file diff --git a/web-push.md b/doc/web-push.md similarity index 100% rename from web-push.md rename to doc/web-push.md diff --git a/qr-code-implementation-guide.md b/qr-code-implementation-guide.md deleted file mode 100644 index ef25a90b..00000000 --- a/qr-code-implementation-guide.md +++ /dev/null @@ -1,156 +0,0 @@ -## Build Configuration - -### Common Vite Configuration -```typescript -// vite.config.common.mts -export async function createBuildConfig(mode: string) { - const isCapacitor = mode === "capacitor"; - - return defineConfig({ - build: { - rollupOptions: { - output: { - manualChunks: { - 'vue-vendor': ['vue', 'vue-router', 'vue-facing-decorator'] - } - } - } - }, - define: { - __USE_QR_READER__: JSON.stringify(!isCapacitor), - __IS_MOBILE__: JSON.stringify(isCapacitor), - }, - optimizeDeps: { - include: [ - '@capacitor-mlkit/barcode-scanning', - 'vue-qrcode-reader' - ] - }, - resolve: { - alias: { - '@capacitor/app': path.resolve(__dirname, 'node_modules/@capacitor/app') - } - } - }); -} -``` - -### Web-Specific Configuration -```typescript -// vite.config.web.mts -import { defineConfig, mergeConfig } from "vite"; -import { createBuildConfig } from "./vite.config.common.mts"; - -export default defineConfig(async () => { - const baseConfig = await createBuildConfig('web'); - - return mergeConfig(baseConfig, { - define: { - __USE_QR_READER__: true, - __IS_MOBILE__: false, - } - }); -}); -``` - -### Capacitor-Specific Configuration -```typescript -// vite.config.capacitor.mts -import { defineConfig, mergeConfig } from "vite"; -import { createBuildConfig } from "./vite.config.common.mts"; - -export default defineConfig(async () => { - const baseConfig = await createBuildConfig('capacitor'); - - return mergeConfig(baseConfig, { - define: { - __USE_QR_READER__: false, - __IS_MOBILE__: true, - }, - build: { - rollupOptions: { - external: ['vue-qrcode-reader'], // Exclude web QR reader from mobile builds - output: { - entryFileNames: '[name]-mobile.js', - chunkFileNames: '[name]-mobile.js', - assetFileNames: '[name]-mobile.[ext]' - } - } - } - }); -}); -``` - -### Build Scripts -Add these scripts to your `package.json`: -```json -{ - "scripts": { - "build:web": "vite build --config vite.config.web.mts", - "build:capacitor": "vite build --config vite.config.capacitor.mts", - "build:all": "npm run build:web && npm run build:capacitor" - } -} -``` - -### Environment Variables -Create a `.env` file: -```bash -# QR Scanner Configuration -VITE_QR_SCANNER_ENABLED=true -VITE_DEFAULT_CAMERA=back -``` - -### Build Process - -1. **Web Build** -```bash -npm run build:web -``` -This will: -- Include vue-qrcode-reader -- Set __USE_QR_READER__ to true -- Set __IS_MOBILE__ to false -- Build for web browsers - -2. **Capacitor Build** -```bash -npm run build:capacitor -``` -This will: -- Exclude vue-qrcode-reader -- Set __USE_QR_READER__ to false -- Set __IS_MOBILE__ to true -- Build for mobile platforms - -3. **Build Both** -```bash -npm run build:all -``` - -### Important Notes - -1. **Dependencies** -- Ensure all QR-related dependencies are properly listed in package.json -- Use exact versions to avoid compatibility issues -- Consider using peer dependencies for shared libraries - -2. **Bundle Size** -- Web build includes vue-qrcode-reader (~100KB) -- Mobile build includes @capacitor-mlkit/barcode-scanning (~50KB) -- Consider using dynamic imports for lazy loading - -3. **Platform Detection** -- Build flags determine which implementation to use -- Runtime checks provide fallback options -- Environment variables can override defaults - -4. **Performance** -- Mobile builds optimize for native performance -- Web builds include necessary polyfills -- Chunk splitting improves load times - -5. **Debugging** -- Source maps are enabled for development -- Build artifacts are properly named for identification -- Console logs help track initialization \ No newline at end of file diff --git a/src/views/ContactQRScanShowView.vue b/src/views/ContactQRScanShowView.vue index da7cc538..cd4d6246 100644 --- a/src/views/ContactQRScanShowView.vue +++ b/src/views/ContactQRScanShowView.vue @@ -326,29 +326,29 @@ export default class ContactQRScanShow extends Vue { switch (state) { case 'in_use': this.error = "Camera is in use by another application"; - this.isScanning = false; - this.$notify( - { - group: "alert", - type: "warning", + this.isScanning = false; + this.$notify( + { + group: "alert", + type: "warning", title: "Camera in Use", text: "Please close other applications using the camera and try again", - }, - 5000, - ); + }, + 5000, + ); break; case 'permission_denied': - this.error = "Camera permission denied"; - this.isScanning = false; - this.$notify( - { - group: "alert", - type: "warning", - title: "Camera Access Required", + this.error = "Camera permission denied"; + this.isScanning = false; + this.$notify( + { + group: "alert", + type: "warning", + title: "Camera Access Required", text: "Please grant camera permission to scan QR codes", - }, - 5000, - ); + }, + 5000, + ); break; case 'not_found': this.error = "No camera found";