10 changed files with 444 additions and 1468 deletions
@ -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<boolean>; |
|
||||
requestPermissions(): Promise<boolean>; |
|
||||
isSupported(): Promise<boolean>; |
|
||||
startScan(options?: QRScannerOptions): Promise<void>; |
|
||||
stopScan(): Promise<void>; |
|
||||
addListener(listener: ScanListener): void; |
|
||||
onStream(callback: (stream: MediaStream | null) => void): void; |
|
||||
cleanup(): Promise<void>; |
|
||||
} |
|
||||
|
|
||||
interface ScanListener { |
|
||||
onScan: (result: string) => void; |
|
||||
onError?: (error: Error) => void; |
|
||||
} |
|
||||
|
|
||||
interface QRScannerOptions { |
|
||||
camera?: "front" | "back"; |
|
||||
showPreview?: boolean; |
|
||||
playSound?: boolean; |
|
||||
} |
|
@ -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<boolean>; |
|
||||
requestPermissions(): Promise<boolean>; |
|
||||
isSupported(): Promise<boolean>; |
|
||||
startScan(options?: QRScannerOptions): Promise<void>; |
|
||||
stopScan(): Promise<void>; |
|
||||
addListener(listener: ScanListener): void; |
|
||||
onStream(callback: (stream: MediaStream | null) => void): void; |
|
||||
cleanup(): Promise<void>; |
|
||||
} |
|
||||
``` |
|
||||
|
|
||||
## 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<void> { |
|
||||
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<void>> = []; |
|
||||
private cleanupPromise: Promise<void> | null = null; |
|
||||
|
|
||||
async checkPermissions(): Promise<boolean> { |
|
||||
try { |
|
||||
const { camera } = await BarcodeScanner.checkPermissions(); |
|
||||
return camera === "granted"; |
|
||||
} catch (error) { |
|
||||
logger.error("Error checking camera permissions:", error); |
|
||||
return false; |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
async requestPermissions(): Promise<boolean> { |
|
||||
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<boolean> { |
|
||||
try { |
|
||||
const { supported } = await BarcodeScanner.isSupported(); |
|
||||
return supported; |
|
||||
} catch (error) { |
|
||||
logger.error("Error checking scanner support:", error); |
|
||||
return false; |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
async startScan(options?: QRScannerOptions): Promise<void> { |
|
||||
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<void> { |
|
||||
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<void> { |
|
||||
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<boolean> { |
|
||||
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<boolean> { |
|
||||
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<boolean> { |
|
||||
return 'mediaDevices' in navigator && 'getUserMedia' in navigator.mediaDevices; |
|
||||
} |
|
||||
|
|
||||
async startScan(): Promise<void> { |
|
||||
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<void> { |
|
||||
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<void> { |
|
||||
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 |
|
@ -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<boolean>; |
||||
|
requestPermissions(): Promise<boolean>; |
||||
|
isSupported(): Promise<boolean>; |
||||
|
startScan(options?: QRScannerOptions): Promise<void>; |
||||
|
stopScan(): Promise<void>; |
||||
|
addListener(listener: ScanListener): void; |
||||
|
onStream(callback: (stream: MediaStream | null) => void): void; |
||||
|
cleanup(): Promise<void>; |
||||
|
} |
||||
|
|
||||
|
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 |
@ -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 |
|
@ -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 |
|
Loading…
Reference in new issue