From ebfbf49760eedbe78368ca7fb6810244d7c861f3 Mon Sep 17 00:00:00 2001 From: Sean <83432253+MegaTheLEGEND@users.noreply.github.com> Date: Sun, 18 Aug 2024 02:41:47 -0700 Subject: [PATCH 1/2] Images from camera add the option to take images with the camera --- client/pages/post/index.tsx | 5 +- client/pages/post/post-with-camera/index.tsx | 202 ++++++++++++++++++ .../post-with-camera/postcamera.module.scss | 183 ++++++++++++++++ client/pages/post/post.module.scss | 12 ++ 4 files changed, 401 insertions(+), 1 deletion(-) create mode 100644 client/pages/post/post-with-camera/index.tsx create mode 100644 client/pages/post/post-with-camera/postcamera.module.scss diff --git a/client/pages/post/index.tsx b/client/pages/post/index.tsx index 056d7ad..a3a240c 100644 --- a/client/pages/post/index.tsx +++ b/client/pages/post/index.tsx @@ -131,6 +131,9 @@ export default function Post() { return (
+
@@ -158,7 +161,7 @@ export default function Post() { disabled >
{ handleSubmission() }}> - submit + Post
*some photos taken on an iphone (.heic) may not work. if there is an error try taking a screenshot of the image and uploading that instead.
diff --git a/client/pages/post/post-with-camera/index.tsx b/client/pages/post/post-with-camera/index.tsx new file mode 100644 index 0000000..783862d --- /dev/null +++ b/client/pages/post/post-with-camera/index.tsx @@ -0,0 +1,202 @@ +import { useState, useRef, useEffect } from "react"; +import s from './postcamera.module.scss'; +import useCheck from "@/utils/check"; +import { useRouter } from "next/router"; + +export default function Post() { + useCheck(); + const router = useRouter(); + + const [loading, setLoading] = useState(false); + const [failure, setFailure] = useState(""); + const [success, setSuccess] = useState(""); + + const [caption, setCaption] = useState(''); + const [primaryBase64, setPrimaryBase64] = useState(''); + const [secondaryBase64, setSecondaryBase64] = useState(''); + const [cameraActive, setCameraActive] = useState(false); + const [isPrimaryCaptured, setIsPrimaryCaptured] = useState(false); + const [isSecondaryCaptured, setIsSecondaryCaptured] = useState(false); + const [selectedCameraId, setSelectedCameraId] = useState(null); + const [currentCapture, setCurrentCapture] = useState<'primary' | 'secondary' | null>(null); + + const videoRef = useRef(null); + const canvasRef = useRef(null); + + useEffect(() => { + let stream: MediaStream | null = null; + + const getMediaStream = async () => { + try { + const constraints: MediaStreamConstraints = { + video: { deviceId: selectedCameraId ? { exact: selectedCameraId } : undefined } + }; + stream = await navigator.mediaDevices.getUserMedia(constraints); + if (videoRef.current) { + videoRef.current.srcObject = stream; + videoRef.current.play(); + } + } catch (err) { + console.error("Error accessing the camera: ", err); + } + }; + + if (cameraActive) { + getMediaStream(); + } + + return () => { + if (stream) { + stream.getTracks().forEach(track => track.stop()); + } + }; + }, [cameraActive, selectedCameraId]); + + useEffect(() => { + const getCameraList = async () => { + const devices = await navigator.mediaDevices.enumerateDevices(); + const videoDevices = devices.filter(device => device.kind === 'videoinput'); + if (videoDevices.length > 0) { + setSelectedCameraId(videoDevices[0].deviceId); // Default to the first camera + } + }; + + getCameraList(); + }, []); + + const captureImage = () => { + if (canvasRef.current && videoRef.current) { + const context = canvasRef.current.getContext('2d'); + if (context) { + canvasRef.current.width = 1500; + canvasRef.current.height = 2000; + context.drawImage(videoRef.current, 0, 0, 1500, 2000); + const imageBase64 = canvasRef.current.toDataURL('image/png'); + + if (currentCapture === 'primary') { + setPrimaryBase64(imageBase64); + setIsPrimaryCaptured(true); + } else if (currentCapture === 'secondary') { + setSecondaryBase64(imageBase64); + setIsSecondaryCaptured(true); + } + + stopCamera(); + } + } + }; + + const stopCamera = () => { + const stream = videoRef.current?.srcObject as MediaStream; + if (stream) { + stream.getTracks().forEach(track => track.stop()); + } + setCameraActive(false); + }; + + const handleCameraToggle = async () => { + const devices = await navigator.mediaDevices.enumerateDevices(); + const videoDevices = devices.filter(device => device.kind === 'videoinput'); + if (videoDevices.length > 0) { + const currentIndex = videoDevices.findIndex(device => device.deviceId === selectedCameraId); + const nextIndex = (currentIndex + 1) % videoDevices.length; + setSelectedCameraId(videoDevices[nextIndex].deviceId); + } + }; + + const handleCameraModalOpen = (type: 'primary' | 'secondary') => { + setCurrentCapture(type); + setIsPrimaryCaptured(type === 'primary' ? false : isPrimaryCaptured); + setIsSecondaryCaptured(type === 'secondary' ? false : isSecondaryCaptured); + setCameraActive(true); + }; + + const handleSubmission = () => { + setLoading(true); + + const authorization_token = localStorage.getItem("token"); + + fetch("/api/add/post", { + method: "POST", + headers: { + 'Content-Type': 'application/json' + }, + body: JSON.stringify({ + primaryb64: primaryBase64, + secondaryb64: secondaryBase64, + caption: caption, + token: authorization_token + }) + }) + .then(response => { + if (response.ok) { + setLoading(false); + setSuccess("Successfully posted!"); + setTimeout(() => { setSuccess(""); router.push("/feed") }, 3000); + } else { + throw new Error("Error: " + response.statusText); + } + }) + .catch(error => { + console.log(error); + setLoading(false); + setFailure(error.message); + setTimeout(() => { setFailure("") }, 5000); + }); + }; + + return ( +
+ +
+
+ + {isPrimaryCaptured && ( +
+ Back Image +
+ )} +
+
+ + {isSecondaryCaptured && ( +
+ Front Image +
+ )} +
+
+ setCaption(txt.target.value)} + disabled + /> +
Post
+
+ *The photos taken here won't look perfect in the app for everyone else, but it's close.
+
+ {failure &&
{failure}
} + {loading &&
loading...
} + {success &&
{success}
} + + {cameraActive && ( +
+ +
+ + + +
+ +
+ )} +
+ ); +} diff --git a/client/pages/post/post-with-camera/postcamera.module.scss b/client/pages/post/post-with-camera/postcamera.module.scss new file mode 100644 index 0000000..d27dabb --- /dev/null +++ b/client/pages/post/post-with-camera/postcamera.module.scss @@ -0,0 +1,183 @@ +@use "../../../styles/common.scss" as c; + +.images { + display: flex; + flex-wrap: wrap; + .img { + position: relative; + width: 300px; + min-width: 300px; + height: 400px; + border: 2px solid white; + margin-right: 18px; + border-radius: 12px; + display: flex; + justify-content: center; + align-items: center; + margin-bottom: 14px; + input[type=file] { + display: none; + } + .upload { + position: absolute; + border: 2px solid #fff; + display: inline-block; + cursor: pointer; + border-radius: 12px; + padding-left: 14px; padding-right: 14px; + height: 40px; + @include c.center(); + background-color: rgb(0, 0, 0, 0.5); + color: white; + } + .sub { + height: 100%; + width: 100%; + img { + height: 100%; + width: 100%; + object-fit: cover; + border-radius: 12px; + } + .data { + position: absolute; + top: 0px; + } + } + } +} +.caption { + padding-left: 14px; padding-right: 14px; + width: 100%; + min-width: 300px; + max-width: 620px; + height: 40px; + border-radius: 12px; + background-color: white; + color: black; + + @media screen and (max-width: 675px) { + margin-left: 0px !important; + width: 300px !important; + } +} + +.switchMode { + border: 2px solid #fff; + display: inline-block; + cursor: pointer; + border-radius: 12px; + padding-left: 14px; padding-right: 14px; + height: 40px; + @include c.center(); + background-color: rgb(0, 0, 0, 0.5); + color: white; + } + +.submit { + margin-top: 14px; + cursor: pointer; + @include c.center(); + border: 2px solid white; + min-width: 144px; + width: 144px; + height: 40px; + border-radius: 12px; + padding-left: 14px; padding-right: 14px; +} +.info { + max-width: 600px; + margin-top: 12px; + color: white; + font-size: 12px; +} +.failure { + color: rgb(165, 0, 0); + font-size: 14px; + margin-top: 12px; +} +.success { + color: cyan; + font-size: 14px; + margin-top: 12px; +} +.loading { + margin-top: 12px; + color: green; + font-size: 14px; +} +.video { + width: 100%; + height: auto; + display: block; +} + +.canvas { + display: none; +} + +.captureButton { + background-color: #007bff; + color: #fff; + padding: 10px 20px; + text-align: center; + cursor: pointer; + margin-top: 10px; + margin-right: 10px; + border: none; + border-radius: 5px; +} + +.swapButton { + background-color: grey; + color: #fff; + padding: 10px 20px; + text-align: center; + cursor: pointer; + margin-top: 10px; + margin-right: 10px; + border: none; + border-radius: 5px; +} + +.closeButton { + background-color: maroon; + color: #fff; + padding: 10px 20px; + text-align: center; + cursor: pointer; + margin-top: 10px; + margin-right: 10px; + border: none; + border-radius: 5px; +} + + +.cameraModal { + position: fixed; + top: 50%; + left: 50%; + transform: translate(-50%, -50%); + background: rgba(0, 0, 0, 0.8); + padding: 20px; + border-radius: 10px; + z-index: 1000; + display: flex; + flex-direction: column; + align-items: center; +} + +.cameraControls { + display: flex; + justify-content: center; + margin-top: 20px; +} + +.preview { + margin-top: 10px; + + img { + max-width: 100%; + height: auto; + } +} diff --git a/client/pages/post/post.module.scss b/client/pages/post/post.module.scss index 52c2625..3c68b01 100644 --- a/client/pages/post/post.module.scss +++ b/client/pages/post/post.module.scss @@ -91,4 +91,16 @@ margin-top: 12px; color: green; font-size: 14px; +} + +.switchMode { + border: 2px solid #fff; + display: inline-block; + cursor: pointer; + border-radius: 12px; + padding-left: 14px; padding-right: 14px; + height: 40px; + @include c.center(); + background-color: rgb(0, 0, 0, 0.5); + color: white; } \ No newline at end of file From e9ce56db59447dc3e2438ba43fc014396f5d5744 Mon Sep 17 00:00:00 2001 From: Sean <83432253+MegaTheLEGEND@users.noreply.github.com> Date: Sun, 18 Aug 2024 02:51:04 -0700 Subject: [PATCH 2/2] fix button margin --- client/pages/post/post-with-camera/postcamera.module.scss | 1 + client/pages/post/post.module.scss | 2 ++ 2 files changed, 3 insertions(+) diff --git a/client/pages/post/post-with-camera/postcamera.module.scss b/client/pages/post/post-with-camera/postcamera.module.scss index d27dabb..a529225 100644 --- a/client/pages/post/post-with-camera/postcamera.module.scss +++ b/client/pages/post/post-with-camera/postcamera.module.scss @@ -72,6 +72,7 @@ @include c.center(); background-color: rgb(0, 0, 0, 0.5); color: white; + margin-bottom: 14px; } .submit { diff --git a/client/pages/post/post.module.scss b/client/pages/post/post.module.scss index 3c68b01..228d9f5 100644 --- a/client/pages/post/post.module.scss +++ b/client/pages/post/post.module.scss @@ -103,4 +103,6 @@ @include c.center(); background-color: rgb(0, 0, 0, 0.5); color: white; + margin-bottom: 14px; + } \ No newline at end of file