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
+101 -20

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." "✅ 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 // Show success notification
setShowSuccessNotification(true); setShowSuccessNotification(true);
@@ -395,8 +401,8 @@ export default function CluePage({ params }) {
// Navigate to the next clue // Navigate to the next clue
const handleNextClue = () => { const handleNextClue = () => {
if (nextClueId) { if (nextClueId) {
// Check if next clue is available before navigating // Always allow navigation if current clue is found
if (nextClueAvailable || isAdmin) { if (clueFound || nextClueAvailable || isAdmin) {
router.push(`/clue/${nextClueId}`); router.push(`/clue/${nextClueId}`);
} else { } else {
// Show notification that the next clue is locked // Show notification that the next clue is locked
@@ -561,7 +567,12 @@ export default function CluePage({ params }) {
{/* QR Scanner Section */} {/* QR Scanner Section */}
<div className="scanner-section"> <div className="scanner-section">
<h2>Scan QR Code</h2> <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 && ( {scanMessage && (
<div className={`scan-message ${clueFound ? "success" : ""}`}> <div className={`scan-message ${clueFound ? "success" : ""}`}>
{scanMessage} {scanMessage}
BIN
View File
Binary file not shown.
+87 -17
View File
@@ -7,6 +7,7 @@ export default function EnhancedQRScanner({
onScan, onScan,
width = 300, width = 300,
height = 300, height = 300,
disableTorch = false, // Add option to disable torch
}) { }) {
const [error, setError] = useState(null); const [error, setError] = useState(null);
const [isSecureContext, setIsSecureContext] = useState(true); const [isSecureContext, setIsSecureContext] = useState(true);
@@ -15,9 +16,13 @@ export default function EnhancedQRScanner({
const [hasCameras, setHasCameras] = useState(true); const [hasCameras, setHasCameras] = useState(true);
const [scanSuccess, setScanSuccess] = useState(false); const [scanSuccess, setScanSuccess] = useState(false);
const [manualInputValue, setManualInputValue] = useState(""); 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 [torchEnabled, setTorchEnabled] = useState(false);
const inputRef = useRef(null); const inputRef = useRef(null);
const [cameraPermissionDenied, setCameraPermissionDenied] = useState(false);
// Define video constraints explicitly // Define video constraints explicitly
const videoConstraints = { const videoConstraints = {
@@ -40,6 +45,28 @@ export default function EnhancedQRScanner({
paused: !cameraActive, 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 // Check if we're in a secure context and if camera is accessible
useEffect(() => { useEffect(() => {
// Ensure scanner compatibility // Ensure scanner compatibility
@@ -57,15 +84,20 @@ export default function EnhancedQRScanner({
throw new Error("MediaDevices API is not supported in this browser"); 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 // Explicitly request video permission - important to avoid the error
const stream = await navigator.mediaDevices.getUserMedia({ const stream = await navigator.mediaDevices.getUserMedia({
video: true, video: true,
audio: false, 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) { 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 // Now enumerate devices to confirm we have video devices
@@ -110,11 +142,16 @@ export default function EnhancedQRScanner({
} else { } else {
setInitialized(true); // Still mark as initialized even if not secure 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 = () => { const toggleTorch = () => {
if (torch) { if (!disableTorch && torch) {
setTorchEnabled(!torchEnabled); setTorchEnabled(!torchEnabled);
torch.toggled = !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 () => { const requestCameraAccess = async () => {
try { try {
setError(null); setError(null);
setCameraActive(false);
// Explicitly request video permission // 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({ const stream = await navigator.mediaDevices.getUserMedia({
video: true, video: true,
audio: false, audio: false,
@@ -151,15 +195,39 @@ export default function EnhancedQRScanner({
// If we got a stream, we have permission // If we got a stream, we have permission
if (stream) { 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()); stream.getTracks().forEach((track) => track.stop());
setCameraActive(true); setCameraActive(true);
setCameraPermissionDenied(false); setCameraPermissionDenied(false);
setRetryCount(0); // Reset retry count on success
} }
} catch (err) { } catch (err) {
console.error("Failed to get camera permission:", 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 // Handle camera errors
const handleError = (error) => { const handleError = (error) => {
console.error("QR Scanner error:", 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 // More descriptive error messages
let errorMessage = "Camera access error. "; let errorMessage = "Camera access error. ";
@@ -256,10 +329,7 @@ export default function EnhancedQRScanner({
) : ( ) : (
<button <button
className="retry-button" className="retry-button"
onClick={() => { onClick={requestCameraAccess}
setError(null);
requestCameraAccess();
}}
> >
Retry Camera Access Retry Camera Access
</button> </button>
@@ -286,8 +356,8 @@ export default function EnhancedQRScanner({
{/* Scanning frame to help user focus */} {/* Scanning frame to help user focus */}
<div className="scanning-frame"></div> <div className="scanning-frame"></div>
{/* Add flashlight toggle button if available */} {/* Only show torch button if torch is available and not disabled */}
{torch && ( {torch && !disableTorch && (
<button <button
className={`torch-button ${torchEnabled ? "active" : ""}`} className={`torch-button ${torchEnabled ? "active" : ""}`}
onClick={toggleTorch} onClick={toggleTorch}