-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge branch 'main' into file_upload
- Loading branch information
Showing
18 changed files
with
554 additions
and
22 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1 +1,20 @@ | ||
* text eol=lf | ||
*.png binary | ||
*.jpg binary | ||
*.jpeg binary | ||
*.gif binary | ||
*.ico binary | ||
*.mov binary | ||
*.mp4 binary | ||
*.mp3 binary | ||
*.flv binary | ||
*.fla binary | ||
*.swf binary | ||
*.gz binary | ||
*.zip binary | ||
*.7z binary | ||
*.ttf binary | ||
*.eot binary | ||
*.woff binary | ||
*.pyc binary | ||
*.pdf binary |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,9 @@ | ||
import styles from '../components/BackButton.module.css'; | ||
|
||
export default function ArrowButton({ onClick, children }) { | ||
return ( | ||
<button className={styles.backButton} onClick={onClick}> | ||
<span className={styles.arrow}>←</span> {children} | ||
</button> | ||
); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,25 @@ | ||
.backButton { | ||
align-self: flex-start; | ||
padding: 5px 10px; | ||
margin: 10px; | ||
border-radius: 8px; | ||
border: none; | ||
background-color: #c5f687; | ||
color: white; | ||
font-size: 16px; | ||
cursor: pointer; | ||
transition: background-color 0.3s; | ||
|
||
position: relative; | ||
display: flex; | ||
align-items: center; | ||
} | ||
|
||
.backButton:hover { | ||
background-color: #a5d667; | ||
} | ||
|
||
.arrow { | ||
display: inline-block; | ||
font-size: 1.5em; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,116 @@ | ||
import React, { useState, useEffect, useRef } from 'react'; | ||
import styles from './Camera.module.css'; | ||
|
||
const Camera = () => { | ||
const videoRef = useRef(null); | ||
const canvasRef = useRef(null); | ||
const [devices, setDevices] = useState([]); | ||
const [selectedDeviceId, setSelectedDeviceId] = useState(''); | ||
const [photo, setPhoto] = useState(''); | ||
|
||
useEffect(() => { | ||
const getDevices = async () => { | ||
const allDevices = await navigator.mediaDevices.enumerateDevices(); | ||
const videoDevices = allDevices.filter(device => device.kind === 'videoinput'); | ||
setDevices(videoDevices); | ||
if (videoDevices.length > 0) { | ||
setSelectedDeviceId(videoDevices[0].deviceId); | ||
} | ||
}; | ||
|
||
getDevices(); | ||
}, []); | ||
|
||
const startCamera = async () => { | ||
if (navigator.mediaDevices && navigator.mediaDevices.getUserMedia) { | ||
try { | ||
const stream = await navigator.mediaDevices.getUserMedia({ | ||
video: { deviceId: selectedDeviceId ? { exact: selectedDeviceId } : undefined } | ||
}); | ||
if (videoRef.current) { | ||
videoRef.current.srcObject = stream; | ||
videoRef.current.play(); | ||
} | ||
} catch (err) { | ||
console.error('Error accessing the camera: ', err); | ||
} | ||
} | ||
}; | ||
|
||
useEffect(() => { | ||
if (selectedDeviceId) { | ||
startCamera(); | ||
} | ||
|
||
return () => { | ||
if (videoRef.current && videoRef.current.srcObject) { | ||
videoRef.current.srcObject.getTracks().forEach((track) => track.stop()); | ||
} | ||
}; | ||
}, [selectedDeviceId]); | ||
|
||
const takePhoto = () => { | ||
const width = videoRef.current.videoWidth; | ||
const height = videoRef.current.videoHeight; | ||
|
||
const context = canvasRef.current.getContext('2d'); | ||
canvasRef.current.width = width; | ||
canvasRef.current.height = height; | ||
context.drawImage(videoRef.current, 0, 0, width, height); | ||
|
||
const dataUrl = canvasRef.current.toDataURL('image/png'); | ||
setPhoto(dataUrl); | ||
}; | ||
|
||
const uploadPhoto = async () => { | ||
const response = await fetch('/api/upload', { | ||
method: 'POST', | ||
headers: { | ||
'Content-Type': 'application/json', | ||
}, | ||
body: JSON.stringify({ image: photo }), | ||
}); | ||
|
||
const result = await response.json(); | ||
console.log(result); | ||
}; | ||
|
||
const retakePhoto = () => { | ||
setPhoto(''); | ||
startCamera(); // Restart the camera when retaking a photo | ||
}; | ||
|
||
return ( | ||
<div className={styles.container}> | ||
{!photo && ( | ||
<div className={styles.wrapper}> | ||
<select | ||
onChange={(e) => setSelectedDeviceId(e.target.value)} | ||
value={selectedDeviceId} | ||
className={styles.select} | ||
> | ||
{devices.map(device => ( | ||
<option key={device.deviceId} value={device.deviceId}> | ||
{device.label || `Camera ${device.deviceId}`} | ||
</option> | ||
))} | ||
</select> | ||
<video ref={videoRef} className={styles.video} /> | ||
<button className={styles.button} onClick={takePhoto}>Take Photo</button> | ||
</div> | ||
)} | ||
{photo && ( | ||
<div className={styles.wrapper}> | ||
<img src={photo} alt="Captured" className={styles.image} /> | ||
<div className={styles.buttonWrapper}> | ||
<button className={styles.button} onClick={uploadPhoto}>Upload Photo</button> | ||
<button className={styles.button} onClick={retakePhoto}>Retake Photo</button> | ||
</div> | ||
</div> | ||
)} | ||
<canvas ref={canvasRef} style={{ display: 'none' }}></canvas> | ||
</div> | ||
); | ||
}; | ||
|
||
export default Camera; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,57 @@ | ||
.container { | ||
display: flex; | ||
flex-direction: column; | ||
align-items: center; | ||
justify-content: center; | ||
} | ||
|
||
.wrapper { | ||
display: flex; | ||
flex-direction: column; | ||
align-items: center; | ||
justify-content: center; | ||
} | ||
|
||
.video { | ||
width: 100%; | ||
max-width: 80vw; | ||
border: 1px solid #ccc; | ||
border-radius: 8px; | ||
} | ||
|
||
.select { | ||
padding: 10px; | ||
margin: 10px 0; | ||
border-radius: 8px; | ||
border: 1px solid #ccc; | ||
font-size: 16px; | ||
} | ||
|
||
.buttonWrapper { | ||
display: flex; | ||
align-items: center; | ||
justify-content: center; | ||
} | ||
|
||
.button { | ||
padding: 10px 20px; | ||
margin: 20px 10px; | ||
border-radius: 8px; | ||
border: none; | ||
background-color: #c5f687; | ||
color: white; | ||
font-size: 16px; | ||
cursor: pointer; | ||
transition: background-color 0.3s; | ||
} | ||
|
||
.button:hover { | ||
background-color: #a5d667; | ||
} | ||
|
||
.image { | ||
width: 100%; | ||
max-width: 600px; | ||
border: 1px solid #ccc; | ||
border-radius: 8px; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,44 @@ | ||
.container { | ||
display: flex; | ||
flex-direction: column; | ||
align-items: center; | ||
justify-content: center; | ||
height: 100%; | ||
background-color: #fff; | ||
padding: 20px; | ||
font-family: 'Inter'; | ||
} | ||
|
||
.title { | ||
font-size: 24px; | ||
margin: 20px 0; | ||
color: #B3E576; | ||
} | ||
|
||
.imageContainer { | ||
display: flex; | ||
align-items: center; | ||
justify-content: center; | ||
width: 350px; | ||
border: 1px solid #ccc; | ||
border-radius: 10px; | ||
} | ||
|
||
.selectedImage { | ||
width: 100%; | ||
height: auto; | ||
} | ||
|
||
.placeholder { | ||
display: flex; | ||
flex-direction: column; | ||
align-items: center; | ||
justify-content: flex-start; | ||
width: 100%; | ||
color: #B3E576; | ||
} | ||
|
||
.fileInput { | ||
margin-top: 20px; | ||
} | ||
|
Empty file.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,28 @@ | ||
// pages/api/upload.js | ||
import fs from 'fs'; | ||
import path from 'path'; | ||
|
||
export default async (req, res) => { | ||
if (req.method === 'POST') { | ||
const { image } = req.body; | ||
|
||
// Create a buffer from the base64 image string | ||
const buffer = Buffer.from(image.split(',')[1], 'base64'); | ||
|
||
// Define the path where the image will be saved | ||
const uploadDir = path.join(process.cwd(), 'public', 'uploads'); | ||
const filePath = path.join(uploadDir, `${Date.now()}.png`); | ||
|
||
// Ensure the uploads directory exists | ||
if (!fs.existsSync(uploadDir)) { | ||
fs.mkdirSync(uploadDir, { recursive: true }); | ||
} | ||
|
||
// Write the buffer to a file | ||
fs.writeFileSync(filePath, buffer); | ||
|
||
return res.status(200).json({ message: 'Upload successful', filePath }); | ||
} | ||
|
||
return res.status(405).json({ message: 'Method not allowed' }); | ||
}; |
Oops, something went wrong.