added more clue buttons and positioned them correctly, need to fix elapsed timer on clue page....

This commit is contained in:
bobbert committed 2025-03-17 13:28:55 +00:00
1 parent e6f2c5446a
commit 8a06fd4966
3 files changed
+102 -21

No files matched your search

+14 -3
View File
@@ -359,6 +359,12 @@ export default function CluePage({ params }) {
"✅ Clue verified! You can now proceed to the next clue."
);
// Make next clue available immediately
setNextClueAvailable(true);
// Force a refresh of the clue data to update availability
setLastRefresh(0);
// Show success notification
setShowSuccessNotification(true);
@@ -395,8 +401,8 @@ export default function CluePage({ params }) {
// Navigate to the next clue
const handleNextClue = () => {
if (nextClueId) {
// Check if next clue is available before navigating
if (nextClueAvailable || isAdmin) {
// Always allow navigation if current clue is found
if (clueFound || nextClueAvailable || isAdmin) {
router.push(`/clue/${nextClueId}`);
} else {
// Show notification that the next clue is locked
@@ -561,7 +567,12 @@ export default function CluePage({ params }) {
{/* QR Scanner Section */}
<div className="scanner-section">
<h2>Scan QR Code</h2>
<EnhancedQRScanner onScan={handleScan} width={280} height={280} />
<EnhancedQRScanner
onScan={handleScan}
width={280}
height={280}
disableTorch={true} // Disable torch functionality
/>
{scanMessage && (
<div className={`scan-message ${clueFound ? "success" : ""}`}>
{scanMessage}
BIN
View File
Binary file not shown.
+88 -18
View File
@@ -7,6 +7,7 @@ export default function EnhancedQRScanner({
onScan,
width = 300,
height = 300,
disableTorch = false, // Add option to disable torch
}) {
const [error, setError] = useState(null);
const [isSecureContext, setIsSecureContext] = useState(true);
@@ -15,9 +16,13 @@ export default function EnhancedQRScanner({
const [hasCameras, setHasCameras] = useState(true);
const [scanSuccess, setScanSuccess] = useState(false);
const [manualInputValue, setManualInputValue] = useState("");
const [cameraPermissionDenied, setCameraPermissionDenied] = useState(false);
const cameraStreamRef = useRef(null);
const [retryCount, setRetryCount] = useState(0);
// We're keeping torchEnabled state but only using it if torch is enabled
const [torchEnabled, setTorchEnabled] = useState(false);
const inputRef = useRef(null);
const [cameraPermissionDenied, setCameraPermissionDenied] = useState(false);
// Define video constraints explicitly
const videoConstraints = {
@@ -40,6 +45,28 @@ export default function EnhancedQRScanner({
paused: !cameraActive,
});
// Function to release any existing camera streams
const releaseCameraResources = () => {
try {
// Release any stream that might be stored in our ref
if (cameraStreamRef.current) {
cameraStreamRef.current.getTracks().forEach(track => {
track.stop();
});
cameraStreamRef.current = null;
console.log("Released existing camera resources");
}
// Also try to find and release any active video tracks that might be running
navigator.mediaDevices.getUserMedia({ audio: false, video: false })
.catch(() => {
// This intentionally fails but helps release resources in some browsers
});
} catch (err) {
console.error("Error releasing camera:", err);
}
};
// Check if we're in a secure context and if camera is accessible
useEffect(() => {
// Ensure scanner compatibility
@@ -57,15 +84,20 @@ export default function EnhancedQRScanner({
throw new Error("MediaDevices API is not supported in this browser");
}
// Release any existing camera streams first
releaseCameraResources();
// Explicitly request video permission - important to avoid the error
const stream = await navigator.mediaDevices.getUserMedia({
video: true,
audio: false,
});
// If we got a stream, we have permission - release it immediately
// If we got a stream, we have permission - store it for later release if needed
if (stream) {
stream.getTracks().forEach((track) => track.stop());
cameraStreamRef.current = stream;
// We'll keep this stream active to prevent other apps from taking the camera
// It will be released when component unmounts or when we retry camera access
}
// Now enumerate devices to confirm we have video devices
@@ -110,11 +142,16 @@ export default function EnhancedQRScanner({
} else {
setInitialized(true); // Still mark as initialized even if not secure
}
// Cleanup function to release camera when component unmounts
return () => {
releaseCameraResources();
};
}, []);
// Toggle torch/flashlight
// Toggle torch/flashlight - only used if torch isn't disabled
const toggleTorch = () => {
if (torch) {
if (!disableTorch && torch) {
setTorchEnabled(!torchEnabled);
torch.toggled = !torchEnabled;
}
@@ -138,12 +175,19 @@ export default function EnhancedQRScanner({
}
};
// Manually request camera access
// Manually request camera access with robust error handling
const requestCameraAccess = async () => {
try {
setError(null);
// Explicitly request video permission
setCameraActive(false);
// First release any existing camera resources
releaseCameraResources();
// Add a small delay to ensure resources are properly released
await new Promise(resolve => setTimeout(resolve, 500));
// Now try to get fresh camera access
const stream = await navigator.mediaDevices.getUserMedia({
video: true,
audio: false,
@@ -151,15 +195,39 @@ export default function EnhancedQRScanner({
// If we got a stream, we have permission
if (stream) {
// Release the stream since useZxing will request it again
// Store the stream reference for potential future release
cameraStreamRef.current = stream;
// Release this stream since useZxing will request its own
stream.getTracks().forEach((track) => track.stop());
setCameraActive(true);
setCameraPermissionDenied(false);
setRetryCount(0); // Reset retry count on success
}
} catch (err) {
console.error("Failed to get camera permission:", err);
setError(`Camera error: ${err.message}`);
setCameraPermissionDenied(true);
// Increment retry count
setRetryCount(prev => prev + 1);
if (
err.name === "NotAllowedError" ||
err.name === "PermissionDeniedError"
) {
setCameraPermissionDenied(true);
setError(
"Camera permission denied. Please allow camera access in your browser settings."
);
} else if (
err.name === "NotReadableError" ||
err.name === "TrackStartError" ||
retryCount < 2 // Try a few times before showing hardware error
) {
setError(`Camera temporarily unavailable. Please try again in a few moments.`);
} else {
setError(`Camera error: ${err.message}. You may need to restart your browser or device.`);
}
}
};
@@ -174,7 +242,12 @@ export default function EnhancedQRScanner({
// Handle camera errors
const handleError = (error) => {
console.error("QR Scanner error:", error);
setCameraActive(false);
// Don't immediately deactivate camera on all errors
// Some errors can be temporary
if (error.name === "NotAllowedError" || error.name === "PermissionDeniedError") {
setCameraActive(false);
}
// More descriptive error messages
let errorMessage = "Camera access error. ";
@@ -256,10 +329,7 @@ export default function EnhancedQRScanner({
) : (
<button
className="retry-button"
onClick={() => {
setError(null);
requestCameraAccess();
}}
onClick={requestCameraAccess}
>
Retry Camera Access
</button>
@@ -286,8 +356,8 @@ export default function EnhancedQRScanner({
{/* Scanning frame to help user focus */}
<div className="scanning-frame"></div>
{/* Add flashlight toggle button if available */}
{torch && (
{/* Only show torch button if torch is available and not disabled */}
{torch && !disableTorch && (
<button
className={`torch-button ${torchEnabled ? "active" : ""}`}
onClick={toggleTorch}