forked from trent_larson/crowd-funder-for-time-pwa
Merge branch 'qrcode-reboot' of ssh://173.199.124.46:222/trent_larson/crowd-funder-for-time-pwa into qrcode-reboot
This commit is contained in:
@@ -67,8 +67,8 @@
|
|||||||
<path
|
<path
|
||||||
class="opacity-75"
|
class="opacity-75"
|
||||||
fill="currentColor"
|
fill="currentColor"
|
||||||
d="M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0
|
d="M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0
|
||||||
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>{{ initializationStatus }}</span>
|
||||||
@@ -164,6 +164,40 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<!-- Error Banner -->
|
||||||
|
<div
|
||||||
|
v-if="error"
|
||||||
|
class="bg-red-100 border border-red-400 text-red-700 px-4 py-3 rounded relative mb-4"
|
||||||
|
role="alert"
|
||||||
|
>
|
||||||
|
<strong class="font-bold">Camera Error:</strong>
|
||||||
|
<span class="block sm:inline">{{ error }}</span>
|
||||||
|
<ul class="mt-2 text-sm text-red-600 list-disc list-inside">
|
||||||
|
<li v-if="error.includes('No camera found')">
|
||||||
|
Check if your device has a camera and it is enabled.
|
||||||
|
</li>
|
||||||
|
<li v-if="error.includes('denied')">
|
||||||
|
Allow camera access in your browser settings and reload the page.
|
||||||
|
</li>
|
||||||
|
<li v-if="error.includes('in use')">
|
||||||
|
Close other applications that may be using the camera.
|
||||||
|
</li>
|
||||||
|
<li v-if="error.includes('HTTPS')">
|
||||||
|
Ensure you are using a secure (HTTPS) connection.
|
||||||
|
</li>
|
||||||
|
<li
|
||||||
|
v-if="
|
||||||
|
!error.includes('No camera found') &&
|
||||||
|
!error.includes('denied') &&
|
||||||
|
!error.includes('in use') &&
|
||||||
|
!error.includes('HTTPS')
|
||||||
|
"
|
||||||
|
>
|
||||||
|
Try refreshing the page or using a different browser/device.
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
|
||||||
<!-- Footer -->
|
<!-- Footer -->
|
||||||
<div class="p-4 border-t border-gray-200">
|
<div class="p-4 border-t border-gray-200">
|
||||||
<div class="flex flex-col space-y-4">
|
<div class="flex flex-col space-y-4">
|
||||||
@@ -275,7 +309,8 @@ export default class QRScannerDialog extends Vue {
|
|||||||
|
|
||||||
async onInit(promise: Promise<void>): Promise<void> {
|
async onInit(promise: Promise<void>): Promise<void> {
|
||||||
if (this.isNativePlatform) {
|
if (this.isNativePlatform) {
|
||||||
logger.log("Skipping web scanner initialization on native platform");
|
logger.log("Closing QR dialog on native platform");
|
||||||
|
this.$nextTick(() => this.close());
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -291,14 +326,28 @@ export default class QRScannerDialog extends Vue {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Check for video devices
|
||||||
|
const devices = await navigator.mediaDevices.enumerateDevices();
|
||||||
|
const videoDevices = devices.filter(
|
||||||
|
(device) => device.kind === "videoinput",
|
||||||
|
);
|
||||||
|
|
||||||
|
if (videoDevices.length === 0) {
|
||||||
|
throw new Error("No camera found on this device");
|
||||||
|
}
|
||||||
|
|
||||||
logger.log("Starting QR scanner initialization...", {
|
logger.log("Starting QR scanner initialization...", {
|
||||||
mediaDevices: !!navigator.mediaDevices,
|
mediaDevices: !!navigator.mediaDevices,
|
||||||
getUserMedia: !!(
|
getUserMedia: !!(
|
||||||
navigator.mediaDevices && navigator.mediaDevices.getUserMedia
|
navigator.mediaDevices && navigator.mediaDevices.getUserMedia
|
||||||
),
|
),
|
||||||
|
videoDevices: videoDevices.length,
|
||||||
constraints: {
|
constraints: {
|
||||||
video: true,
|
video: {
|
||||||
facingMode: this.preferredCamera,
|
facingMode: this.preferredCamera,
|
||||||
|
width: { ideal: 1280 },
|
||||||
|
height: { ideal: 720 },
|
||||||
|
},
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -308,6 +357,8 @@ export default class QRScannerDialog extends Vue {
|
|||||||
const stream = await navigator.mediaDevices.getUserMedia({
|
const stream = await navigator.mediaDevices.getUserMedia({
|
||||||
video: {
|
video: {
|
||||||
facingMode: this.preferredCamera,
|
facingMode: this.preferredCamera,
|
||||||
|
width: { ideal: 1280 },
|
||||||
|
height: { ideal: 720 },
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -350,7 +401,7 @@ export default class QRScannerDialog extends Vue {
|
|||||||
// Now initialize the QR scanner
|
// Now initialize the QR scanner
|
||||||
this.initializationStatus = "Starting QR scanner...";
|
this.initializationStatus = "Starting QR scanner...";
|
||||||
logger.log("Initializing QR scanner...");
|
logger.log("Initializing QR scanner...");
|
||||||
// await promise; // <-- comment this out for debugging
|
await promise;
|
||||||
|
|
||||||
this.isInitializing = false;
|
this.isInitializing = false;
|
||||||
this.cameraStatus = "Ready";
|
this.cameraStatus = "Ready";
|
||||||
@@ -401,8 +452,8 @@ export default class QRScannerDialog extends Vue {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
// Handle both promise and non-promise results
|
// Use instanceof Promise for type narrowing
|
||||||
if (result && typeof result.then === "function") {
|
if (result instanceof Promise) {
|
||||||
result
|
result
|
||||||
.then(processResult)
|
.then(processResult)
|
||||||
.catch((error: Error) => this.handleError(error))
|
.catch((error: Error) => this.handleError(error))
|
||||||
|
|||||||
@@ -28,11 +28,90 @@ export class WebDialogQRScanner implements QRScannerService {
|
|||||||
|
|
||||||
async requestPermissions(): Promise<boolean> {
|
async requestPermissions(): Promise<boolean> {
|
||||||
try {
|
try {
|
||||||
const stream = await navigator.mediaDevices.getUserMedia({ video: true });
|
// First check if we have any video devices
|
||||||
|
const devices = await navigator.mediaDevices.enumerateDevices();
|
||||||
|
const videoDevices = devices.filter(
|
||||||
|
(device) => device.kind === "videoinput",
|
||||||
|
);
|
||||||
|
|
||||||
|
if (videoDevices.length === 0) {
|
||||||
|
logger.error("No video devices found");
|
||||||
|
throw new Error("No camera found on this device");
|
||||||
|
}
|
||||||
|
|
||||||
|
// Try to get a stream with specific constraints
|
||||||
|
const stream = await navigator.mediaDevices.getUserMedia({
|
||||||
|
video: {
|
||||||
|
facingMode: "environment",
|
||||||
|
width: { ideal: 1280 },
|
||||||
|
height: { ideal: 720 },
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
// Stop the test stream immediately
|
||||||
stream.getTracks().forEach((track) => track.stop());
|
stream.getTracks().forEach((track) => track.stop());
|
||||||
return true;
|
return true;
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
|
const wrappedError =
|
||||||
|
error instanceof Error ? error : new Error(String(error));
|
||||||
logger.error("Error requesting camera permissions:", {
|
logger.error("Error requesting camera permissions:", {
|
||||||
|
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");
|
||||||
|
} else if (
|
||||||
|
wrappedError.name === "NotAllowedError" ||
|
||||||
|
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("Camera is in use by another application");
|
||||||
|
} else {
|
||||||
|
throw new Error(`Camera error: ${wrappedError.message}`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async isSupported(): Promise<boolean> {
|
||||||
|
try {
|
||||||
|
// Check for secure context first
|
||||||
|
if (!window.isSecureContext) {
|
||||||
|
logger.warn("Camera access requires HTTPS (secure context)");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check for camera API support
|
||||||
|
if (!navigator.mediaDevices || !navigator.mediaDevices.getUserMedia) {
|
||||||
|
logger.warn("Camera API not supported in this browser");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check if we have any video devices
|
||||||
|
const devices = await navigator.mediaDevices.enumerateDevices();
|
||||||
|
const hasVideoDevices = devices.some(
|
||||||
|
(device) => device.kind === "videoinput",
|
||||||
|
);
|
||||||
|
|
||||||
|
if (!hasVideoDevices) {
|
||||||
|
logger.warn("No video devices found");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
} catch (error) {
|
||||||
|
logger.error("Error checking camera support:", {
|
||||||
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,
|
||||||
});
|
});
|
||||||
@@ -40,16 +119,6 @@ export class WebDialogQRScanner implements QRScannerService {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async isSupported(): Promise<boolean> {
|
|
||||||
// Check for secure context first
|
|
||||||
if (!window.isSecureContext) {
|
|
||||||
logger.warn("Camera access requires HTTPS (secure context)");
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
// Then check for camera API support
|
|
||||||
return !!(navigator.mediaDevices && navigator.mediaDevices.getUserMedia);
|
|
||||||
}
|
|
||||||
|
|
||||||
async startScan(): Promise<void> {
|
async startScan(): Promise<void> {
|
||||||
if (this.isScanning) {
|
if (this.isScanning) {
|
||||||
return;
|
return;
|
||||||
|
|||||||
Reference in New Issue
Block a user