Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

리뷰 작성 페이지 성능 개선 및 QR 기능 추가 #87

Open
wants to merge 5 commits into
base: develop
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion src/components/Common/OptionButton.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -80,7 +80,7 @@ const CircleInner = styled.div<{ size: string }>`
case "small":
return tw`w-2 h-2 left-[4px] top-[4px]`;
case "large":
return tw`w-3 h-3 left-[8px] top-[8px]`;
return tw`w-3 h-3 left-[9px] top-[9px]`;
default:
return tw`w-2.5 h-2.5 left-[6px] top-[6px]`;
}
Expand Down
21 changes: 18 additions & 3 deletions src/components/WriteReview/UploadImageSection.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import tw from "twin.macro";
import styled from "styled-components";
import CameraSvg from "../../assets/images/camera.svg?react";
import { MdDeleteOutline } from "react-icons/md";
import {useEffect, useMemo} from "react";
type UploadImageProps = {
imageFiles: File[];
setImageFiles: React.Dispatch<React.SetStateAction<File[]>>;
Expand All @@ -14,14 +15,28 @@ function UploadImageSection({ imageFiles, setImageFiles }: UploadImageProps) {
const newFiles = [...prevFiles, ...filesArray];
return newFiles;
});
// 파일 선택 후 input의 값을 초기화하여 동일 파일 업로드 가능하게 만듦
event.target.value = "";
}
};
const handleImageDelete = (index: number) => {
// File 객체 삭제
const updatedFiles = imageFiles.filter((_, i) => i !== index);
setImageFiles(updatedFiles);
};


// imageFiles가 변경될 때만 URL 생성
const imageUrls = useMemo(() => {
return imageFiles.map((file) => URL.createObjectURL(file));
}, [imageFiles]);

// Blob URL 해제
useEffect(() => {
return () => {
imageUrls.forEach((url) => URL.revokeObjectURL(url));
};
}, [imageUrls]);

return (
<Container>
<label htmlFor="image-upload">
Expand All @@ -40,9 +55,9 @@ function UploadImageSection({ imageFiles, setImageFiles }: UploadImageProps) {
disabled={Boolean(status && status !== "PENDING")}
/>

{imageFiles.map((file, index) => (
{imageUrls.map((url, index) => (
<ImageWrapper key={index}>
<img src={URL.createObjectURL(file)} alt={`Uploaded ${index}`} />
<img src={url} alt={`Uploaded ${index}`} />
<DeleteIconWrapper onClick={() => handleImageDelete(index)}>
<MdDeleteOutline size={20} color="#A1A6B5" />
</DeleteIconWrapper>
Expand Down
30 changes: 23 additions & 7 deletions src/pages/PhotoUpload/CheckPhoto.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,14 +6,31 @@ import NextButton from "../../components/Common/NextButton.tsx";

function CheckPhoto() {
const navigate = useNavigate();

const location = useLocation();
const { imageFile } = location.state;

const { imageFile, qrLink } = location.state;
const handleNextClick = () => {
navigate("/photo-review", { state: { imageFile: imageFile } });
navigate("/photo-review", { state: { imageFile: imageFile , qrLink: qrLink } });
};


console.log(qrLink);

if (!imageFile) {
// imageFile이 없는 경우 처리
return (
<Container>
<Header>
<Title>QR 사진 확인</Title>
<button className="absolute left-[10px] top-[35%]" onClick={() => navigate(-1)}>
<BackIcon color="white" />
</button>
</Header>
<img src={qrLink} alt="QR 사진" className="img-container" />
<NextButton onClick={handleNextClick} text="다음" />
</Container>
);
}

return (
<Container>
<Header>
Expand All @@ -22,8 +39,7 @@ function CheckPhoto() {
<BackIcon color="white" />
</button>
</Header>

<img src={URL.createObjectURL(imageFile)} alt="QR 사진" className="img-container" />
<img src={URL.createObjectURL(imageFile)} alt="업로드 사진" className="img-container" />
<NextButton onClick={handleNextClick} text="다음" />
</Container>
);
Expand Down
5 changes: 3 additions & 2 deletions src/pages/PhotoUpload/ReviewPhoto.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -26,17 +26,18 @@ function ReviewPhoto() {
const navigate = useNavigate();
const location = useLocation();

const { imageFile } = location.state;
const { imageFile, qrLink } = location.state;

console.log(imageFile);
console.log(qrLink);

const handleNext = () => {
navigate("/write-detail", {
state: {
year: year,
month: month,
day: day,
qrLink: location.state.qrLink,
qrLink: qrLink,
imageFile: imageFile,
boothId: boothId,
},
Expand Down
38 changes: 29 additions & 9 deletions src/pages/PhotoUpload/WriteDetail.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,6 @@ function WriteDetail() {
const [isHashModalOpen, setIsHashModalOpen] = useState(false);
const [isRecordModalOpen, setIsRecordModalOpen] = useState(false);
const [countHash, setCountHash] = useState(0);
// const [imageFiles, setImageFiles] = useState<File[]>([imgSrc]);
const { accessToken } = useAuthStore();
const location = useLocation();

Expand Down Expand Up @@ -46,12 +45,29 @@ function WriteDetail() {
const count = hashTags.filter((tag) => tag.length > 0);
setCountHash(count.length);
}, [hashTags]);

const getUploadedFilePaths = async () => {
const presignedData = await getPresignedUrl("/images/album", imageFile.name, accessToken!);
if (presignedData) {
//s3에 이미지 업로드
await uploadToS3(presignedData.url, imageFile);
let imageName = "";
let imageFileToUpload: File | undefined;

if (qrLink) {
const url = new URL(qrLink);
imageName = url.pathname.split("/").pop() || "qr-photo";
// QR URL에서 이미지 Blob 가져오기
const response = await fetch(qrLink);
const blob = await response.blob();
imageFileToUpload = new File([blob], imageName, { type: blob.type }); // Blob을 File 객체로 변환
//TODO 인생네컷으로 받아오는 QR 링크가 실제로 blob화가 가능한지 검증이 필요함
} else if (imageFile) {
imageName = imageFile.name;
imageFileToUpload = imageFile;
}

// presigned URL 가져오기
const presignedData = await getPresignedUrl("/images/album", imageName, accessToken!);
if (presignedData && imageFileToUpload) {
// S3에 이미지 업로드
await uploadToS3(presignedData.url, imageFileToUpload);
return presignedData.filePath;
}
};
Expand Down Expand Up @@ -81,10 +97,8 @@ function WriteDetail() {

// 메인 로직
const handleUpload = async () => {
console.log(imageFile);
// Step 2: 이미지 업로드 및 filePaths 저장
const filePath = await getUploadedFilePaths();
console.log(filePath);
// Step 3: 리뷰등록 api 호출
if (filePath) {
await uploadImage(accessToken!, boothId, year, month, day, hashTags, records, filePath);
Expand Down Expand Up @@ -152,7 +166,13 @@ function WriteDetail() {
</div>

{isHashModalOpen && <HashTagModal hashTags={hashTags} closeModal={closeHashModal} setHashTags={setHashTags} />}
<img className="w-[80%]" src={URL.createObjectURL(imageFile)} alt="QR 사진" />

{!imageFile ? (
<img className="w-[80%] h-[400px]" src={qrLink} alt="QR 사진" />
) : (
<img className="w-[80%]" src={URL.createObjectURL(imageFile)} alt="업로드 사진" />
)}


<button
className="w-[90%] p-[10px] rounded-lg bg-[#e9eaee] text-[#676f7b] text-base font-normal font-['Pretendard']"
Expand Down