forked from jsnbuchanan/crowd-funder-for-time-pwa
feat: implement comprehensive camera state management
- Add CameraState type and CameraStateListener interface for standardized state handling - Implement camera state tracking in WebInlineQRScanner: - Add state management properties and methods - Update state transitions during camera operations - Add proper error state handling for different scenarios - Enhance QR scanner UI with improved state feedback: - Add color-coded status indicators - Implement state-specific messages and notifications - Add user-friendly error notifications for common issues - Improve error handling with specific states for: - Camera in use by another application - Permission denied - Camera not found - General errors This change improves the user experience by providing clear visual feedback about the camera's state and better error handling with actionable notifications.
This commit is contained in:
@@ -1,4 +1,4 @@
|
|||||||
import { QRScannerService, ScanListener, QRScannerOptions } from "./types";
|
import { QRScannerService, ScanListener, QRScannerOptions, CameraState, CameraStateListener } from "./types";
|
||||||
import { logger } from "@/utils/logger";
|
import { logger } from "@/utils/logger";
|
||||||
import { EventEmitter } from "events";
|
import { EventEmitter } from "events";
|
||||||
import jsQR from "jsqr";
|
import jsQR from "jsqr";
|
||||||
@@ -21,6 +21,9 @@ export class WebInlineQRScanner implements QRScannerService {
|
|||||||
private readonly TARGET_FPS = 15; // Target 15 FPS for scanning
|
private readonly TARGET_FPS = 15; // Target 15 FPS for scanning
|
||||||
private readonly FRAME_INTERVAL = 1000 / 15; // ~67ms between frames
|
private readonly FRAME_INTERVAL = 1000 / 15; // ~67ms between frames
|
||||||
private lastFrameTime = 0;
|
private lastFrameTime = 0;
|
||||||
|
private cameraStateListeners: Set<CameraStateListener> = new Set();
|
||||||
|
private currentState: CameraState = 'off';
|
||||||
|
private currentStateMessage?: string;
|
||||||
|
|
||||||
constructor(private options?: QRScannerOptions) {
|
constructor(private options?: QRScannerOptions) {
|
||||||
// Generate a short random ID for this scanner instance
|
// Generate a short random ID for this scanner instance
|
||||||
@@ -43,8 +46,35 @@ export class WebInlineQRScanner implements QRScannerService {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private updateCameraState(state: CameraState, message?: string) {
|
||||||
|
this.currentState = state;
|
||||||
|
this.currentStateMessage = message;
|
||||||
|
this.cameraStateListeners.forEach(listener => {
|
||||||
|
try {
|
||||||
|
listener.onStateChange(state, message);
|
||||||
|
logger.info(`[WebInlineQRScanner:${this.id}] Camera state changed to: ${state}`, {
|
||||||
|
state,
|
||||||
|
message,
|
||||||
|
});
|
||||||
|
} catch (error) {
|
||||||
|
logger.error(`[WebInlineQRScanner:${this.id}] Error in camera state listener:`, error);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
addCameraStateListener(listener: CameraStateListener): void {
|
||||||
|
this.cameraStateListeners.add(listener);
|
||||||
|
// Immediately notify the new listener of current state
|
||||||
|
listener.onStateChange(this.currentState, this.currentStateMessage);
|
||||||
|
}
|
||||||
|
|
||||||
|
removeCameraStateListener(listener: CameraStateListener): void {
|
||||||
|
this.cameraStateListeners.delete(listener);
|
||||||
|
}
|
||||||
|
|
||||||
async checkPermissions(): Promise<boolean> {
|
async checkPermissions(): Promise<boolean> {
|
||||||
try {
|
try {
|
||||||
|
this.updateCameraState('initializing', 'Checking camera permissions...');
|
||||||
logger.error(
|
logger.error(
|
||||||
`[WebInlineQRScanner:${this.id}] Checking camera permissions...`,
|
`[WebInlineQRScanner:${this.id}] Checking camera permissions...`,
|
||||||
);
|
);
|
||||||
@@ -55,7 +85,9 @@ export class WebInlineQRScanner implements QRScannerService {
|
|||||||
`[WebInlineQRScanner:${this.id}] Permission state:`,
|
`[WebInlineQRScanner:${this.id}] Permission state:`,
|
||||||
permissions.state,
|
permissions.state,
|
||||||
);
|
);
|
||||||
return permissions.state === "granted";
|
const granted = permissions.state === "granted";
|
||||||
|
this.updateCameraState(granted ? 'ready' : 'permission_denied');
|
||||||
|
return granted;
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
logger.error(
|
logger.error(
|
||||||
`[WebInlineQRScanner:${this.id}] Error checking camera permissions:`,
|
`[WebInlineQRScanner:${this.id}] Error checking camera permissions:`,
|
||||||
@@ -64,12 +96,14 @@ export class WebInlineQRScanner implements QRScannerService {
|
|||||||
stack: error instanceof Error ? error.stack : undefined,
|
stack: error instanceof Error ? error.stack : undefined,
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
this.updateCameraState('error', 'Error checking camera permissions');
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async requestPermissions(): Promise<boolean> {
|
async requestPermissions(): Promise<boolean> {
|
||||||
try {
|
try {
|
||||||
|
this.updateCameraState('initializing', 'Requesting camera permissions...');
|
||||||
logger.error(
|
logger.error(
|
||||||
`[WebInlineQRScanner:${this.id}] Requesting camera permissions...`,
|
`[WebInlineQRScanner:${this.id}] Requesting camera permissions...`,
|
||||||
);
|
);
|
||||||
@@ -107,10 +141,8 @@ export class WebInlineQRScanner implements QRScannerService {
|
|||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
logger.error(
|
this.updateCameraState('ready', 'Camera permissions granted');
|
||||||
`[WebInlineQRScanner:${this.id}] Camera stream obtained successfully`,
|
|
||||||
);
|
|
||||||
|
|
||||||
// Stop the test stream immediately
|
// Stop the test stream immediately
|
||||||
stream.getTracks().forEach((track) => {
|
stream.getTracks().forEach((track) => {
|
||||||
logger.error(`[WebInlineQRScanner:${this.id}] Stopping test track:`, {
|
logger.error(`[WebInlineQRScanner:${this.id}] Stopping test track:`, {
|
||||||
@@ -122,36 +154,20 @@ export class WebInlineQRScanner implements QRScannerService {
|
|||||||
});
|
});
|
||||||
return true;
|
return true;
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
const wrappedError =
|
const wrappedError = error instanceof Error ? error : new Error(String(error));
|
||||||
error instanceof Error ? error : new Error(String(error));
|
|
||||||
logger.error(
|
// Update state based on error type
|
||||||
`[WebInlineQRScanner:${this.id}] Error requesting camera permissions:`,
|
if (wrappedError.name === "NotFoundError" || wrappedError.name === "DevicesNotFoundError") {
|
||||||
{
|
this.updateCameraState('not_found', 'No camera found on this device');
|
||||||
error: wrappedError.message,
|
|
||||||
stack: wrappedError.stack,
|
|
||||||
name: wrappedError.name,
|
|
||||||
},
|
|
||||||
);
|
|
||||||
|
|
||||||
// Provide more specific error messages
|
|
||||||
if (
|
|
||||||
wrappedError.name === "NotFoundError" ||
|
|
||||||
wrappedError.name === "DevicesNotFoundError"
|
|
||||||
) {
|
|
||||||
throw new Error("No camera found on this device");
|
throw new Error("No camera found on this device");
|
||||||
} else if (
|
} else if (wrappedError.name === "NotAllowedError" || wrappedError.name === "PermissionDeniedError") {
|
||||||
wrappedError.name === "NotAllowedError" ||
|
this.updateCameraState('permission_denied', 'Camera access denied');
|
||||||
wrappedError.name === "PermissionDeniedError"
|
throw new Error("Camera access denied. Please grant camera permission and try again");
|
||||||
) {
|
} else if (wrappedError.name === "NotReadableError" || wrappedError.name === "TrackStartError") {
|
||||||
throw new Error(
|
this.updateCameraState('in_use', 'Camera is in use by another application');
|
||||||
"Camera access denied. Please grant camera permission and try again",
|
|
||||||
);
|
|
||||||
} else if (
|
|
||||||
wrappedError.name === "NotReadableError" ||
|
|
||||||
wrappedError.name === "TrackStartError"
|
|
||||||
) {
|
|
||||||
throw new Error("Camera is in use by another application");
|
throw new Error("Camera is in use by another application");
|
||||||
} else {
|
} else {
|
||||||
|
this.updateCameraState('error', wrappedError.message);
|
||||||
throw new Error(`Camera error: ${wrappedError.message}`);
|
throw new Error(`Camera error: ${wrappedError.message}`);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -390,6 +406,7 @@ export class WebInlineQRScanner implements QRScannerService {
|
|||||||
this.isScanning = true;
|
this.isScanning = true;
|
||||||
this.scanAttempts = 0;
|
this.scanAttempts = 0;
|
||||||
this.lastScanTime = Date.now();
|
this.lastScanTime = Date.now();
|
||||||
|
this.updateCameraState('initializing', 'Starting camera...');
|
||||||
logger.error(`[WebInlineQRScanner:${this.id}] Starting scan`);
|
logger.error(`[WebInlineQRScanner:${this.id}] Starting scan`);
|
||||||
|
|
||||||
// Get camera stream
|
// Get camera stream
|
||||||
@@ -404,6 +421,8 @@ export class WebInlineQRScanner implements QRScannerService {
|
|||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
|
this.updateCameraState('active', 'Camera is active');
|
||||||
|
|
||||||
logger.error(`[WebInlineQRScanner:${this.id}] Camera stream obtained:`, {
|
logger.error(`[WebInlineQRScanner:${this.id}] Camera stream obtained:`, {
|
||||||
tracks: this.stream.getTracks().map((t) => ({
|
tracks: this.stream.getTracks().map((t) => ({
|
||||||
kind: t.kind,
|
kind: t.kind,
|
||||||
@@ -429,13 +448,15 @@ export class WebInlineQRScanner implements QRScannerService {
|
|||||||
this.scanQRCode();
|
this.scanQRCode();
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
this.isScanning = false;
|
this.isScanning = false;
|
||||||
const wrappedError =
|
const wrappedError = error instanceof Error ? error : new Error(String(error));
|
||||||
error instanceof Error ? error : new Error(String(error));
|
|
||||||
logger.error(`[WebInlineQRScanner:${this.id}] Error starting scan:`, {
|
// Update state based on error type
|
||||||
error: wrappedError.message,
|
if (wrappedError.name === "NotReadableError" || wrappedError.name === "TrackStartError") {
|
||||||
stack: wrappedError.stack,
|
this.updateCameraState('in_use', 'Camera is in use by another application');
|
||||||
name: wrappedError.name,
|
} else {
|
||||||
});
|
this.updateCameraState('error', wrappedError.message);
|
||||||
|
}
|
||||||
|
|
||||||
if (this.scanListener?.onError) {
|
if (this.scanListener?.onError) {
|
||||||
this.scanListener.onError(wrappedError);
|
this.scanListener.onError(wrappedError);
|
||||||
}
|
}
|
||||||
@@ -492,14 +513,9 @@ export class WebInlineQRScanner implements QRScannerService {
|
|||||||
`[WebInlineQRScanner:${this.id}] Stream stopped event emitted`,
|
`[WebInlineQRScanner:${this.id}] Stream stopped event emitted`,
|
||||||
);
|
);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
const wrappedError =
|
logger.error(`[WebInlineQRScanner:${this.id}] Error stopping scan:`, error);
|
||||||
error instanceof Error ? error : new Error(String(error));
|
this.updateCameraState('error', 'Error stopping camera');
|
||||||
logger.error(`[WebInlineQRScanner:${this.id}] Error stopping scan:`, {
|
throw error;
|
||||||
error: wrappedError.message,
|
|
||||||
stack: wrappedError.stack,
|
|
||||||
name: wrappedError.name,
|
|
||||||
});
|
|
||||||
throw wrappedError;
|
|
||||||
} finally {
|
} finally {
|
||||||
this.isScanning = false;
|
this.isScanning = false;
|
||||||
logger.error(`[WebInlineQRScanner:${this.id}] Scan stopped successfully`);
|
logger.error(`[WebInlineQRScanner:${this.id}] Scan stopped successfully`);
|
||||||
@@ -541,10 +557,9 @@ export class WebInlineQRScanner implements QRScannerService {
|
|||||||
`[WebInlineQRScanner:${this.id}] Cleanup completed successfully`,
|
`[WebInlineQRScanner:${this.id}] Cleanup completed successfully`,
|
||||||
);
|
);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
logger.error(`[WebInlineQRScanner:${this.id}] Error during cleanup:`, {
|
logger.error(`[WebInlineQRScanner:${this.id}] Error during cleanup:`, error);
|
||||||
error: error instanceof Error ? error.message : String(error),
|
this.updateCameraState('error', 'Error during cleanup');
|
||||||
stack: error instanceof Error ? error.stack : undefined,
|
throw error;
|
||||||
});
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -22,6 +22,20 @@ export interface QRScannerOptions {
|
|||||||
playSound?: boolean;
|
playSound?: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export type CameraState =
|
||||||
|
| 'initializing' // Camera is being initialized
|
||||||
|
| 'ready' // Camera is ready to use
|
||||||
|
| 'active' // Camera is actively streaming
|
||||||
|
| 'in_use' // Camera is in use by another application
|
||||||
|
| 'permission_denied' // Camera permission was denied
|
||||||
|
| 'not_found' // No camera found on device
|
||||||
|
| 'error' // Generic error state
|
||||||
|
| 'off'; // Camera is off/stopped
|
||||||
|
|
||||||
|
export interface CameraStateListener {
|
||||||
|
onStateChange: (state: CameraState, message?: string) => void;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Interface for QR scanner service implementations
|
* Interface for QR scanner service implementations
|
||||||
*/
|
*/
|
||||||
@@ -44,6 +58,12 @@ export interface QRScannerService {
|
|||||||
/** Add a listener for scan events */
|
/** Add a listener for scan events */
|
||||||
addListener(listener: ScanListener): void;
|
addListener(listener: ScanListener): void;
|
||||||
|
|
||||||
|
/** Add a listener for camera state changes */
|
||||||
|
addCameraStateListener(listener: CameraStateListener): void;
|
||||||
|
|
||||||
|
/** Remove a camera state listener */
|
||||||
|
removeCameraStateListener(listener: CameraStateListener): void;
|
||||||
|
|
||||||
/** Clean up scanner resources */
|
/** Clean up scanner resources */
|
||||||
cleanup(): Promise<void>;
|
cleanup(): Promise<void>;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -88,7 +88,7 @@
|
|||||||
class="absolute top-0 left-0 right-0 bg-black bg-opacity-50 text-white text-sm text-center py-2 z-10"
|
class="absolute top-0 left-0 right-0 bg-black bg-opacity-50 text-white text-sm text-center py-2 z-10"
|
||||||
>
|
>
|
||||||
<div
|
<div
|
||||||
v-if="isInitializing"
|
v-if="cameraState === 'initializing'"
|
||||||
class="flex items-center justify-center space-x-2"
|
class="flex items-center justify-center space-x-2"
|
||||||
>
|
>
|
||||||
<svg
|
<svg
|
||||||
@@ -112,10 +112,10 @@
|
|||||||
3.042 1.135 5.824 3 7.938l3-2.647z"
|
3.042 1.135 5.824 3 7.938l3-2.647z"
|
||||||
></path>
|
></path>
|
||||||
</svg>
|
</svg>
|
||||||
<span>{{ initializationStatus }}</span>
|
<span>{{ cameraStateMessage || 'Initializing camera...' }}</span>
|
||||||
</div>
|
</div>
|
||||||
<p
|
<p
|
||||||
v-else-if="isScanning"
|
v-else-if="cameraState === 'active'"
|
||||||
class="flex items-center justify-center space-x-2"
|
class="flex items-center justify-center space-x-2"
|
||||||
>
|
>
|
||||||
<span
|
<span
|
||||||
@@ -125,8 +125,14 @@
|
|||||||
</p>
|
</p>
|
||||||
<p v-else-if="error" class="text-red-400">Error: {{ error }}</p>
|
<p v-else-if="error" class="text-red-400">Error: {{ error }}</p>
|
||||||
<p v-else class="flex items-center justify-center space-x-2">
|
<p v-else class="flex items-center justify-center space-x-2">
|
||||||
<span class="inline-block w-2 h-2 bg-blue-500 rounded-full"></span>
|
<span :class="{
|
||||||
<span>Ready to scan</span>
|
'inline-block w-2 h-2 rounded-full': true,
|
||||||
|
'bg-green-500': cameraState === 'ready',
|
||||||
|
'bg-yellow-500': cameraState === 'in_use',
|
||||||
|
'bg-red-500': cameraState === 'error' || cameraState === 'permission_denied' || cameraState === 'not_found',
|
||||||
|
'bg-blue-500': cameraState === 'off'
|
||||||
|
}"></span>
|
||||||
|
<span>{{ cameraStateMessage || 'Ready to scan' }}</span>
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@@ -202,6 +208,7 @@ import { retrieveAccountMetadata } from "../libs/util";
|
|||||||
import { Router } from "vue-router";
|
import { Router } from "vue-router";
|
||||||
import { logger } from "../utils/logger";
|
import { logger } from "../utils/logger";
|
||||||
import { QRScannerFactory } from "@/services/QRScanner/QRScannerFactory";
|
import { QRScannerFactory } from "@/services/QRScanner/QRScannerFactory";
|
||||||
|
import { CameraState } from "@/services/QRScanner/types";
|
||||||
|
|
||||||
interface QRScanResult {
|
interface QRScanResult {
|
||||||
rawValue?: string;
|
rawValue?: string;
|
||||||
@@ -239,7 +246,8 @@ export default class ContactQRScanShow extends Vue {
|
|||||||
initializationStatus = "Initializing camera...";
|
initializationStatus = "Initializing camera...";
|
||||||
useQRReader = __USE_QR_READER__;
|
useQRReader = __USE_QR_READER__;
|
||||||
preferredCamera: "user" | "environment" = "environment";
|
preferredCamera: "user" | "environment" = "environment";
|
||||||
cameraStatus = "Initializing";
|
cameraState: CameraState = 'off';
|
||||||
|
cameraStateMessage?: string;
|
||||||
|
|
||||||
ETHR_DID_PREFIX = ETHR_DID_PREFIX;
|
ETHR_DID_PREFIX = ETHR_DID_PREFIX;
|
||||||
|
|
||||||
@@ -303,19 +311,70 @@ export default class ContactQRScanShow extends Vue {
|
|||||||
try {
|
try {
|
||||||
this.error = null;
|
this.error = null;
|
||||||
this.isScanning = true;
|
this.isScanning = true;
|
||||||
this.isInitializing = true;
|
|
||||||
this.initializationStatus = "Initializing camera...";
|
|
||||||
this.lastScannedValue = "";
|
this.lastScannedValue = "";
|
||||||
this.lastScanTime = 0;
|
this.lastScanTime = 0;
|
||||||
|
|
||||||
const scanner = QRScannerFactory.getInstance();
|
const scanner = QRScannerFactory.getInstance();
|
||||||
|
|
||||||
|
// Add camera state listener
|
||||||
|
scanner.addCameraStateListener({
|
||||||
|
onStateChange: (state, message) => {
|
||||||
|
this.cameraState = state;
|
||||||
|
this.cameraStateMessage = message;
|
||||||
|
|
||||||
|
// Update UI based on camera state
|
||||||
|
switch (state) {
|
||||||
|
case 'in_use':
|
||||||
|
this.error = "Camera is in use by another application";
|
||||||
|
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,
|
||||||
|
);
|
||||||
|
break;
|
||||||
|
case 'permission_denied':
|
||||||
|
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,
|
||||||
|
);
|
||||||
|
break;
|
||||||
|
case 'not_found':
|
||||||
|
this.error = "No camera found";
|
||||||
|
this.isScanning = false;
|
||||||
|
this.$notify(
|
||||||
|
{
|
||||||
|
group: "alert",
|
||||||
|
type: "warning",
|
||||||
|
title: "No Camera",
|
||||||
|
text: "No camera was found on this device",
|
||||||
|
},
|
||||||
|
5000,
|
||||||
|
);
|
||||||
|
break;
|
||||||
|
case 'error':
|
||||||
|
this.error = this.cameraStateMessage || "Camera error";
|
||||||
|
this.isScanning = false;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
// Check if scanning is supported first
|
// Check if scanning is supported first
|
||||||
if (!(await scanner.isSupported())) {
|
if (!(await scanner.isSupported())) {
|
||||||
this.error =
|
this.error = "Camera access requires HTTPS. Please use a secure connection.";
|
||||||
"Camera access requires HTTPS. Please use a secure connection.";
|
|
||||||
this.isScanning = false;
|
this.isScanning = false;
|
||||||
this.isInitializing = false;
|
|
||||||
this.$notify(
|
this.$notify(
|
||||||
{
|
{
|
||||||
group: "alert",
|
group: "alert",
|
||||||
@@ -328,40 +387,11 @@ export default class ContactQRScanShow extends Vue {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check permissions first
|
|
||||||
if (!(await scanner.checkPermissions())) {
|
|
||||||
this.initializationStatus = "Requesting camera permission...";
|
|
||||||
const granted = await scanner.requestPermissions();
|
|
||||||
if (!granted) {
|
|
||||||
this.error = "Camera permission denied";
|
|
||||||
this.isScanning = false;
|
|
||||||
this.isInitializing = false;
|
|
||||||
// Show notification for better visibility
|
|
||||||
this.$notify(
|
|
||||||
{
|
|
||||||
group: "alert",
|
|
||||||
type: "warning",
|
|
||||||
title: "Camera Access Required",
|
|
||||||
text: "Camera permission denied",
|
|
||||||
},
|
|
||||||
5000,
|
|
||||||
);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// For native platforms, use the scanner service
|
|
||||||
scanner.addListener({
|
|
||||||
onScan: this.onScanDetect,
|
|
||||||
onError: this.onScanError,
|
|
||||||
});
|
|
||||||
|
|
||||||
// Start scanning
|
// Start scanning
|
||||||
await scanner.startScan();
|
await scanner.startScan();
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
this.error = error instanceof Error ? error.message : String(error);
|
this.error = error instanceof Error ? error.message : String(error);
|
||||||
this.isScanning = false;
|
this.isScanning = false;
|
||||||
this.isInitializing = false;
|
|
||||||
logger.error("Error starting scan:", {
|
logger.error("Error starting scan:", {
|
||||||
error: error instanceof Error ? error.message : String(error),
|
error: error instanceof Error ? error.message : String(error),
|
||||||
stack: error instanceof Error ? error.stack : undefined,
|
stack: error instanceof Error ? error.stack : undefined,
|
||||||
@@ -828,12 +858,12 @@ export default class ContactQRScanShow extends Vue {
|
|||||||
try {
|
try {
|
||||||
await promise;
|
await promise;
|
||||||
this.isInitializing = false;
|
this.isInitializing = false;
|
||||||
this.cameraStatus = "Ready";
|
this.cameraState = "ready";
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
const wrappedError =
|
const wrappedError =
|
||||||
error instanceof Error ? error : new Error(String(error));
|
error instanceof Error ? error : new Error(String(error));
|
||||||
this.error = wrappedError.message;
|
this.error = wrappedError.message;
|
||||||
this.cameraStatus = "Error";
|
this.cameraState = "error";
|
||||||
this.isInitializing = false;
|
this.isInitializing = false;
|
||||||
logger.error("Error during QR scanner initialization:", {
|
logger.error("Error during QR scanner initialization:", {
|
||||||
error: wrappedError.message,
|
error: wrappedError.message,
|
||||||
@@ -843,17 +873,17 @@ export default class ContactQRScanShow extends Vue {
|
|||||||
}
|
}
|
||||||
|
|
||||||
onCameraOn(): void {
|
onCameraOn(): void {
|
||||||
this.cameraStatus = "Active";
|
this.cameraState = "active";
|
||||||
this.isInitializing = false;
|
this.isInitializing = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
onCameraOff(): void {
|
onCameraOff(): void {
|
||||||
this.cameraStatus = "Off";
|
this.cameraState = "off";
|
||||||
}
|
}
|
||||||
|
|
||||||
onDetect(result: unknown): void {
|
onDetect(result: unknown): void {
|
||||||
this.isScanning = true;
|
this.isScanning = true;
|
||||||
this.cameraStatus = "Detecting";
|
this.cameraState = "detecting";
|
||||||
try {
|
try {
|
||||||
let rawValue: string | undefined;
|
let rawValue: string | undefined;
|
||||||
if (
|
if (
|
||||||
@@ -874,7 +904,7 @@ export default class ContactQRScanShow extends Vue {
|
|||||||
this.handleError(error);
|
this.handleError(error);
|
||||||
} finally {
|
} finally {
|
||||||
this.isScanning = false;
|
this.isScanning = false;
|
||||||
this.cameraStatus = "Active";
|
this.cameraState = "active";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -897,12 +927,12 @@ export default class ContactQRScanShow extends Vue {
|
|||||||
const wrappedError =
|
const wrappedError =
|
||||||
error instanceof Error ? error : new Error(String(error));
|
error instanceof Error ? error : new Error(String(error));
|
||||||
this.error = wrappedError.message;
|
this.error = wrappedError.message;
|
||||||
this.cameraStatus = "Error";
|
this.cameraState = "error";
|
||||||
}
|
}
|
||||||
|
|
||||||
onError(error: Error): void {
|
onError(error: Error): void {
|
||||||
this.error = error.message;
|
this.error = error.message;
|
||||||
this.cameraStatus = "Error";
|
this.cameraState = "error";
|
||||||
logger.error("QR code scan error:", {
|
logger.error("QR code scan error:", {
|
||||||
error: error.message,
|
error: error.message,
|
||||||
stack: error.stack,
|
stack: error.stack,
|
||||||
|
|||||||
Reference in New Issue
Block a user