Files
mayagen/src/Camera.js
everythingonblack 7d999a17c3 ok
2025-07-10 11:25:57 +07:00

300 lines
7.0 KiB
JavaScript

import React, { useState, useRef, useEffect } from 'react';
const CameraPage = ({ handleClose, handleUploadImage }) => {
const [image, setImage] = useState(null);
const [isCameraActive, setIsCameraActive] = useState(true);
const [uploading, setUploading] = useState(false);
const [isMobile, setIsMobile] = useState(false);
const [isUploadedFile, setIsUploadedFile] = useState(false); // ✅ NEW STATE
const videoRef = useRef(null);
const canvasRef = useRef(null);
const fileInputRef = useRef(null);
useEffect(() => {
const checkMobile = () => {
setIsMobile(window.innerWidth <= 768);
};
checkMobile();
window.addEventListener('resize', checkMobile);
return () => window.removeEventListener('resize', checkMobile);
}, []);
useEffect(() => {
let stream;
const startCamera = async () => {
try {
stream = await navigator.mediaDevices.getUserMedia({
video: { facingMode: 'environment' },
});
if (videoRef.current) {
videoRef.current.srcObject = stream;
}
} catch (error) {
console.error('Error accessing camera:', error);
}
};
startCamera();
// Clean-up logic (seperti socket.off)
return () => {
if (stream) {
stream.getTracks().forEach(track => track.stop());
}
};
}, []);
const captureImage = () => {
const canvas = canvasRef.current;
const video = videoRef.current;
if (video && canvas) {
canvas.width = video.videoWidth;
canvas.height = video.videoHeight;
const ctx = canvas.getContext('2d');
ctx.drawImage(video, 0, 0, canvas.width, canvas.height);
const capturedImage = canvas.toDataURL('image/jpeg');
setImage(capturedImage);
setIsCameraActive(false);
setIsUploadedFile(false); // ✅ from camera
}
};
const handleFileUpload = e => {
const file = e.target.files[0];
if (file) {
const reader = new FileReader();
reader.onloadend = () => {
setImage(reader.result);
setIsCameraActive(false);
setIsUploadedFile(true); // ✅ from file
};
reader.readAsDataURL(file);
}
};
const cancelCapture = () => {
setImage(null);
setIsCameraActive(true);
setIsUploadedFile(false); // ✅ reset
const startCamera = async () => {
try {
const stream = await navigator.mediaDevices.getUserMedia({
video: { facingMode: 'environment' },
});
if (videoRef.current) {
videoRef.current.srcObject = stream;
}
} catch (error) {
console.error('Error accessing camera:', error);
}
};
startCamera();
};
const triggerFileInput = () => {
fileInputRef.current?.click();
};
const mainContent = (
<div style={containerStyle}>
<div style={cameraContainerStyle}>
{isCameraActive && (
<video ref={videoRef} autoPlay playsInline style={videoStyle} />
)}
{!isCameraActive && image && (
<img
src={image}
alt="Captured or Uploaded"
style={{
...imageStyle,
objectFit: isUploadedFile ? 'contain' : 'cover',
backgroundColor: '#000',
}}
/>
)}
<div style={controlsStyle}>
{isCameraActive ? (
<>
<button
onClick={() => {
handleClose();
}}
style={baseButtonStyle}
aria-label="Cancel and retake"
>
<img
src="/back.png"
alt="Kamera"
style={{ height: '26px' }}
/>
</button>
<button
onClick={captureImage}
style={baseButtonStyle}
aria-label="Capture photo"
>
<img
src="/camera.png"
alt="Kamera"
style={{ height: '24px' }}
/>
</button>
<button
onClick={triggerFileInput}
style={baseButtonStyle}
aria-label="Upload from gallery"
>
<img
src="/upload.png"
alt="Kamera"
style={{ height: '24px' }}
/>
</button>
</>
) : (
<>
<button
onClick={cancelCapture}
style={baseButtonStyle}
aria-label="Cancel and retake"
>
<img
src="/back.png"
alt="Kamera"
style={{ height: '26px' }}
/>
</button>
<button
onClick={() => handleUploadImage(image)}
style={baseButtonStyle}
disabled={uploading}
aria-label={uploading ? 'Uploading...' : 'Confirm upload'}
>
<img
src="/send.png"
alt="Kamera"
style={{ height: '24px' }}
/> </button>
</>
)}
</div>
<input
ref={fileInputRef}
type="file"
accept="image/*"
style={{ display: 'none' }}
onChange={handleFileUpload}
/>
</div>
<canvas ref={canvasRef} style={{ display: 'none' }}></canvas>
</div>
);
if (!isMobile) {
return (
<div style={desktopLayoutStyle}>
<div style={sidebarStyle}></div>
<div style={mainContentStyle}>{mainContent}</div>
<div style={sidebarStyle}></div>
</div>
);
}
return mainContent;
};
// Styles
const containerStyle = {
position: 'absolute',
width: '100%',
height: '100%',
backgroundColor: '#000',
overflow: 'hidden',
};
const cameraContainerStyle = {
position: 'relative',
width: '100%',
height: '100%',
display: 'flex',
alignItems: 'center',
justifyContent: 'center',
};
const videoStyle = {
width: '100%',
height: '100%',
objectFit: 'cover',
};
const imageStyle = {
width: '100%',
height: '100%',
};
const controlsStyle = {
position: 'fixed',
bottom: '30px',
left: '50%',
transform: 'translateX(-50%)',
display: 'flex',
gap: '20px',
alignItems: 'center',
};
const baseButtonStyle = {
width: '60px',
height: '60px',
borderRadius: '50%',
border: 'none',
fontSize: '24px',
cursor: 'pointer',
display: 'flex',
alignItems: 'center',
justifyContent: 'center',
boxShadow: '0 4px 12px rgba(0, 0, 0, 0.3)',
transition: 'all 0.2s ease',
};
const confirmButtonStyle = {
...baseButtonStyle,
backgroundColor: '#4CAF50',
color: '#fff',
};
const desktopLayoutStyle = {
display: 'flex',
height: '100vh',
fontFamily: 'Arial, sans-serif',
};
const sidebarStyle = {
width: '250px',
backgroundColor: '#fff',
color: '#333',
padding: '20px',
boxShadow: '0 0 10px rgba(0, 0, 0, 0.1)',
borderRight: '1px solid #e0e0e0',
};
const mainContentStyle = {
flex: 1,
position: 'relative',
};
export default CameraPage;