mirror of
https://github.com/mudabbir-ahmad/peworkshop.git
synced 2026-10-08 03:50:21 +00:00
added more clue buttons and positioned them correctly, need to fix elapsed timer on clue page....
This commit is contained in:
1 parent
e6f2c5446a
commit
8a06fd4966
3 files changed
+102
-21
No files matched your search
@@ -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}
|
||||||
|
|||||||
Binary file not shown.
@@ -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}
|
||||||
|
|||||||
Reference in new issue
Block a user