Skip to content

Commit

Permalink
Refactor#173: 이미지 수정 로직 보완 (#175)
Browse files Browse the repository at this point in the history
* refactor: image update 로직 보완

* images 하나의 상태로 파생된 상태 형식으로 로직 수정 적용

* style: JSX 닫는 태그 스타일 수정
  • Loading branch information
semnil5202 authored Jul 3, 2024
1 parent d62d30f commit 2a7c755
Show file tree
Hide file tree
Showing 3 changed files with 60 additions and 112 deletions.
43 changes: 15 additions & 28 deletions src/pages/WriteEdit/WriteEdit.page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -28,9 +28,11 @@ import TitleAndIntroduceSection from './components/TitleAndIntroduceSection';
import UpdateImages from './components/UpdateImages';
import { usePutIdea } from './hooks/mutations/usePutIdea';
import { useWritingEditInfoQuery } from './hooks/queries/useWritingInfoQuery';
import { ImageResponse, Info, PutFormData } from './types';
import { AddedImage, ImageResponse, Info, PutFormData } from './types';
import { get2DepthCountsBy1Depth } from './utils/get2DepthCountsBy1Depth';

type ImageType = AddedImage | ImageResponse;

const WriteEditPage = () => {
const openAlert = useAlert();
const location = useLocation();
Expand All @@ -42,11 +44,7 @@ const WriteEditPage = () => {
const [title, setTitle] = useState(ideaDetail.title);
const [introduce, setIntroduce] = useState(ideaDetail.introduce);
const [isOpenBottomSheet, setIsOpenBottomSheet] = useState(false);
const [images, setImages] = useState<ImageResponse[]>(ideaDetail.imageResponses); // 서버에서 받아온거 화면 보여주는용
const [imageFiles, setImageFiles] = useState<File[]>([]); // 새롭게 수정해서 올린거 서버에 보낼거
const [notDeletedImageIds, setNotDeletedImageIds] = useState<number[]>(
ideaDetail.imageResponses.map((image) => image.id),
); // images 에서 삭제되지 않은 id를 기록해서 서버로 보낼거
const [images, setImages] = useState<ImageType[]>(ideaDetail.imageResponses);
const [selectedTeamRecruitment1Depth, setSelectedTeamRecruitment1Depth] = useState(skillCategoryResponses[0].name);
const [selectedSkillResponses, setSelectedSkillResponses] = useState<Info[]>(
skillCategoryResponses
Expand Down Expand Up @@ -105,6 +103,9 @@ const WriteEditPage = () => {

const formData = new FormData();

const alreadyUploadedImages = images.filter((image) => image.id > 0) as ImageResponse[];
const justUploadedImages = images.filter((image) => image.id < 0) as AddedImage[];

const userData = {
title,
introduce,
Expand All @@ -113,10 +114,10 @@ const WriteEditPage = () => {
branchIds: selectedCheckboxId.branches,
purposeIds: selectedCheckboxId.purposes,
skillCategoryIds: selectedSkillResponses.map((selectedSkillResponse) => selectedSkillResponse.id),
imageIds: notDeletedImageIds,
imageIds: alreadyUploadedImages.map((image) => image.id),
};

imageFiles.forEach((imageFile) => {
justUploadedImages.forEach(({ imageFile }) => {
formData.append('images', imageFile);
});

Expand Down Expand Up @@ -153,22 +154,14 @@ const WriteEditPage = () => {
setSelectedSkillResponses((prev) => prev.filter((item) => item.id !== id));
};

const addImages = (addedImages: File[]) => {
setImageFiles([...imageFiles, ...addedImages]);
const addImages = (addedImages: AddedImage[]) => {
setImages([...images, ...addedImages]);
};

// server 에서 기존에 있던 이미지 제거 시
const deleteImage = (id: number) => {
const filteredImages = images.filter((image) => image.id !== id);
const filteredImageIds = filteredImages.map((image) => image.id);
const deleteImages = (id: number) => {
const deletedImages = images.filter((image) => image.id !== id);

setImages(filteredImages);
setNotDeletedImageIds(filteredImageIds);
};

// client 에서 새롭게 추가한 이미지 제거 시
const deleteImageFiles = (index: number) => {
setImageFiles(imageFiles.filter((_, idx) => idx + notDeletedImageIds.length !== index));
setImages(deletedImages);
};

return (
Expand All @@ -185,13 +178,7 @@ const WriteEditPage = () => {

<Divider color="bg1" height={8} />

<UpdateImages
images={images}
imageFiles={imageFiles}
onAddImages={addImages}
onDeleteImage={deleteImage}
onDeleteImageFiles={deleteImageFiles}
/>
<UpdateImages images={images} onAddImages={addImages} onDeleteImage={deleteImages} />

<Divider color="bg1" height={8} bottom={30} />

Expand Down
123 changes: 39 additions & 84 deletions src/pages/WriteEdit/components/UpdateImages.tsx
Original file line number Diff line number Diff line change
@@ -1,117 +1,72 @@
import styled from '@emotion/styled';
import { Box, Flex, SVGCancel, theme } from 'concept-be-design-system';
import { useState } from 'react';
import useAlert from '../../../hooks/useAlert';
import useCompressImage from '../../Write/hooks/useCompressImage';
import { ImageResponse } from '../types';
import { AddedImage, ImageResponse } from '../types';

interface Props {
images: ImageResponse[];
imageFiles: File[];
onAddImages: (images: File[]) => void;
onDeleteImage: (id: number) => void; // 서버에 있는 이미지
onDeleteImageFiles: (index: number) => void; // 클라이언트에서 추가한 이미지
}
type ImageType = AddedImage | ImageResponse;

type ImageUrls =
| {
id: number;
ideaId: number;
imageUrl: string;
}
| string;

interface DeleteProps {
id?: number;
index: number;
imageUrl: string;
interface Props {
images: ImageType[];
onAddImages: (images: AddedImage[]) => void;
onDeleteImage: (id: number) => void;
}

const FROM_SERVER_IMAGE = 'cloudfront';
let CLIENT_IMAGE_ADJUST_VALUE = -1;

// let addId = -1;

const UpdateImages = ({ images, imageFiles, onAddImages, onDeleteImage, onDeleteImageFiles }: Props) => {
const UpdateImages = ({ images, onAddImages, onDeleteImage }: Props) => {
const openAlert = useAlert();
const { compressImages } = useCompressImage();
const [imageUrls, setImageUrls] = useState<ImageUrls[]>(images);

const onChangeImage = async (e: React.ChangeEvent<HTMLInputElement>) => {
const onClickAddImage = async (e: React.ChangeEvent<HTMLInputElement>) => {
const imageFileList = e.target.files;

if (!imageFileList) return;
if (images.length + imageFiles.length + imageFileList.length > 3) {

if (images.length + imageFileList.length > 3) {
openAlert({ content: '이미지는 최대 3개까지 업로드 가능합니다.' });
}

const updatedImages = [...imageFileList].filter((_, idx) => idx < 3);
const compressedImages = await compressImages(updatedImages);
const imageObjectUrls = compressedImages.map((image) => URL.createObjectURL(image));

onAddImages(compressedImages);
setImageUrls([...imageUrls, ...imageObjectUrls]);
};
const willAddedImages = compressedImages.map((image, idx) => ({
id: CLIENT_IMAGE_ADJUST_VALUE--,
imageUrl: imageObjectUrls[idx],
imageFile: image,
}));

const onClickDeleteImage = ({ id, index, imageUrl }: DeleteProps) => {
if (imageUrl.includes(FROM_SERVER_IMAGE) && id) {
onDeleteImage(id);
setImageUrls(imageUrls.filter((_, idx) => idx !== index));
return;
}
onAddImages(willAddedImages);
};

onDeleteImageFiles(index);
setImageUrls(imageUrls.filter((_, idx) => idx !== index));
const onClickDeleteImage = (id: number) => {
onDeleteImage(id);
};

return (
<Wrapper width="100%" height="100%" padding="22px" overflow="scroll" boxSizing="border-box">
<Flex gap={8}>
<AddImageLabel htmlFor="add-image">+</AddImageLabel>
<AddImageInput id="add-image" type="file" multiple onChange={onChangeImage}></AddImageInput>
{imageUrls.map((image, index) => {
if (typeof image === 'string') {
return (
<Box position="relative" key={index}>
<Flex
position="absolute"
top="0"
right="0"
width={32}
height={32}
justifyContent="center"
alignItems="center"
backgroundColor="b6"
cursor="pointer"
>
<SVGCancel color="white" onClick={() => onClickDeleteImage({ index, imageUrl: image })} />
</Flex>
<Image src={image} alt={`이미지 ${index + 1}`} />
</Box>
);
}

return (
<Box position="relative" key={index}>
<Flex
position="absolute"
top="0"
right="0"
width={32}
height={32}
justifyContent="center"
alignItems="center"
backgroundColor="b6"
cursor="pointer"
>
<SVGCancel
color="white"
onClick={() => onClickDeleteImage({ id: image.id, index, imageUrl: image.imageUrl })}
/>
</Flex>
<Image src={image.imageUrl} alt={`이미지 ${index + 1}`} />
</Box>
);
})}
<AddImageInput id="add-image" type="file" multiple onChange={onClickAddImage} />
{images.map(({ id, imageUrl }, index) => (
<Box position="relative" key={id} onClick={() => onClickDeleteImage(id)}>
<Flex
position="absolute"
top="0"
right="0"
width={32}
height={32}
justifyContent="center"
alignItems="center"
backgroundColor="b6"
cursor="pointer"
>
<SVGCancel color="white" />
</Flex>
<Image src={imageUrl} alt={`이미지 ${index + 1}`} />
</Box>
))}
</Flex>
</Wrapper>
);
Expand Down
6 changes: 6 additions & 0 deletions src/pages/WriteEdit/types/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,12 @@ export interface ImageResponse {
imageUrl: string;
}

export interface AddedImage {
id: number;
imageUrl: string;
imageFile: File;
}

export type IdeaDetail = {
imageUrl: string;
nickname: string;
Expand Down

0 comments on commit 2a7c755

Please sign in to comment.