From bf103a11562f113b99721080aa4013d40d6e28fe Mon Sep 17 00:00:00 2001 From: GC-Park Date: Thu, 5 Oct 2023 14:34:24 +0900 Subject: [PATCH 1/7] =?UTF-8?q?feat:=20=ED=95=80=20=EC=9D=B4=EB=AF=B8?= =?UTF-8?q?=EC=A7=80=20=EC=82=AD=EC=A0=9C=20=EA=B8=B0=EB=8A=A5=20=EA=B5=AC?= =?UTF-8?q?=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- frontend/src/assets/remove_image_icon.svg | 9 +++ .../components/PinImageContainer/index.tsx | 78 ++++++++++++++----- 2 files changed, 69 insertions(+), 18 deletions(-) create mode 100644 frontend/src/assets/remove_image_icon.svg diff --git a/frontend/src/assets/remove_image_icon.svg b/frontend/src/assets/remove_image_icon.svg new file mode 100644 index 00000000..f4f48825 --- /dev/null +++ b/frontend/src/assets/remove_image_icon.svg @@ -0,0 +1,9 @@ + + + + + + + + + diff --git a/frontend/src/components/PinImageContainer/index.tsx b/frontend/src/components/PinImageContainer/index.tsx index 5e25a276..7aa91d80 100644 --- a/frontend/src/components/PinImageContainer/index.tsx +++ b/frontend/src/components/PinImageContainer/index.tsx @@ -2,6 +2,9 @@ import styled from 'styled-components'; import { ImageProps } from '../../types/Pin'; import Image from '../common/Image'; +import RemoveImageButton from '../../assets/remove_image_icon.svg'; +import useDelete from '../../apiHooks/useDelete'; +import useToast from '../../hooks/useToast'; interface PinImageContainerProps { images: ImageProps[]; @@ -10,26 +13,50 @@ interface PinImageContainerProps { const NOT_FOUND_IMAGE = 'https://dr702blqc4x5d.cloudfront.net/2023-map-be-fine/icon/notFound_image.svg'; -function PinImageContainer({ images }: PinImageContainerProps) { +const PinImageContainer = ({ images }: PinImageContainerProps) => { + const { fetchDelete } = useDelete(); + const { showToast } = useToast(); + + const onRemovePinIcon = (imageId: number) => { + const isRemoveImage = confirm('해당 이미지를 삭제하시겠습니까?'); + + if (isRemoveImage) { + fetchDelete({ + url: `/pins/images/${imageId}`, + errorMessage: '이미지 제거에 실패했습니다.', + isThrow: true, + onSuccess: () => { + showToast('info', '핀에서 이미지가 삭제 되었습니다.'); + }, + }); + } + }; return ( - - {images.map( - (image, index) => - index < 3 && ( - - - - ), - )} - + <> + + {images.map( + (image, index) => + index < 3 && ( + + + onRemovePinIcon(image.id)} + > + + + + ), + )} + + ); -} +}; const FilmList = styled.ul` width: 330px; @@ -38,7 +65,22 @@ const FilmList = styled.ul` `; const ImageWrapper = styled.li` + position: relative; margin-right: 10px; `; +const RemoveImageIconWrapper = styled.div` + opacity: 0.6; + position: absolute; + right: 1px; + top: 1px; + line-height: 0; + background-color: #ffffff; + cursor: pointer; + + &:hover { + opacity: 1; + } +`; + export default PinImageContainer; From fd71bdb9c871e57cfdb09f543dc9fcb439414076 Mon Sep 17 00:00:00 2001 From: GC-Park Date: Tue, 10 Oct 2023 01:28:36 +0900 Subject: [PATCH 2/7] =?UTF-8?q?feat:=20=ED=86=A0=ED=94=BD=20=EC=82=AC?= =?UTF-8?q?=EC=A7=84=20=EC=88=98=EC=A0=95=20=EA=B8=B0=EB=8A=A5=20=EC=98=88?= =?UTF-8?q?=EB=B9=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../components/TopicInfo/UpdatedTopicInfo.tsx | 106 +++++++++++++++++- 1 file changed, 105 insertions(+), 1 deletion(-) diff --git a/frontend/src/components/TopicInfo/UpdatedTopicInfo.tsx b/frontend/src/components/TopicInfo/UpdatedTopicInfo.tsx index 91b70f0e..872714eb 100644 --- a/frontend/src/components/TopicInfo/UpdatedTopicInfo.tsx +++ b/frontend/src/components/TopicInfo/UpdatedTopicInfo.tsx @@ -12,6 +12,10 @@ import Button from '../common/Button'; import Flex from '../common/Flex'; import Space from '../common/Space'; import InputContainer from '../InputContainer'; +import { postApi } from '../../apis/postApi'; +import useCompressImage from '../../hooks/useCompressImage'; +import Image from '../common/Image'; +import { DEFAULT_TOPIC_IMAGE } from '../../constants'; interface UpdatedTopicInfoProp { id: number; @@ -51,6 +55,7 @@ function UpdatedTopicInfo({ const [isPrivate, setIsPrivate] = useState(false); // 혼자 볼 지도 : 같이 볼 지도 const [isAllPermissioned, setIsAllPermissioned] = useState(true); // 모두 : 지정 인원 const [authorizedMemberIds, setAuthorizedMemberIds] = useState([]); + const { compressImage } = useCompressImage(); const updateTopicInfo = async () => { try { @@ -123,8 +128,65 @@ function UpdatedTopicInfo({ ); }, []); + const onTopicImageFileChange = async ( + event: React.ChangeEvent, + ) => { + const file = event.target.files && event.target.files[0]; + const formData = new FormData(); + + if (!file) { + showToast( + 'error', + '이미지를 선택하지 않았거나 추가하신 이미지를 찾을 수 없습니다. 다시 선택해 주세요.', + ); + return; + } + + const compressedFile = await compressImage(file); + + formData.append('image', compressedFile); + + const data = JSON.stringify(id); + const jsonBlob = new Blob([data], { type: 'application/json' }); + + formData.append('topicId', jsonBlob); + + await postApi('/topic/images', formData); + + // getTopicImage(); + }; + + // const [topicImage, setTopicImage] = useState(null); + + // const getTopicImage = async () => { + // const newTopicImage = await getApi(`/topic/${id}`); + // setTopicImage(newTopicImage); + // }; + return ( + + ) => { + e.currentTarget.src = DEFAULT_TOPIC_IMAGE; + }} + /> + 수정 + + + + + theme.color.black}; + background-color: ${({ theme }) => theme.color.lightGray}; + + font-size: ${({ theme }) => theme.fontSize.extraSmall}; + font-weight: ${({ theme }) => theme.fontWeight.bold}; + text-align: center; + + border-radius: ${({ theme }) => theme.radius.small}; + cursor: pointer; + + position: absolute; + left: 40%; + bottom: -25px; + + box-shadow: rgba(0, 0, 0, 0.3) 0px 0px 5px 0px; + + &:hover { + filter: brightness(0.95); + } +`; + +const ImageInputButton = styled.input` + display: none; +`; + +const TopicImage = styled(Image)` + border-radius: ${({ theme }) => theme.radius.medium}; +`; export default UpdatedTopicInfo; +function compressImage(file: File) { + throw new Error('Function not implemented.'); +} From 87df2cc84427f58d5d39530a35cbe776ac0637cb Mon Sep 17 00:00:00 2001 From: GC-Park Date: Wed, 11 Oct 2023 14:22:12 +0900 Subject: [PATCH 3/7] =?UTF-8?q?refactor:=20=ED=95=80=20=EC=9D=B4=EB=AF=B8?= =?UTF-8?q?=EC=A7=80=20=EC=82=AD=EC=A0=9C=20=EC=8B=9C=20=EC=9E=AC=EB=A0=8C?= =?UTF-8?q?=EB=8D=94=EB=A7=81=20=EC=95=88=EB=90=98=EB=8D=98=20=EB=AC=B8?= =?UTF-8?q?=EC=A0=9C=20=ED=95=B4=EA=B2=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- frontend/src/components/PinImageContainer/index.tsx | 11 ++++++----- frontend/src/pages/PinDetail.tsx | 2 +- 2 files changed, 7 insertions(+), 6 deletions(-) diff --git a/frontend/src/components/PinImageContainer/index.tsx b/frontend/src/components/PinImageContainer/index.tsx index 7aa91d80..8f0422e2 100644 --- a/frontend/src/components/PinImageContainer/index.tsx +++ b/frontend/src/components/PinImageContainer/index.tsx @@ -8,12 +8,12 @@ import useToast from '../../hooks/useToast'; interface PinImageContainerProps { images: ImageProps[]; -} + getPinData: () => void; + +}const NOT_FOUND_IMAGE = +'https://dr702blqc4x5d.cloudfront.net/2023-map-be-fine/icon/notFound_image.svg'; -const NOT_FOUND_IMAGE = - 'https://dr702blqc4x5d.cloudfront.net/2023-map-be-fine/icon/notFound_image.svg'; - -const PinImageContainer = ({ images }: PinImageContainerProps) => { +const PinImageContainer = ({ images, getPinData }: PinImageContainerProps) => { const { fetchDelete } = useDelete(); const { showToast } = useToast(); @@ -27,6 +27,7 @@ const PinImageContainer = ({ images }: PinImageContainerProps) => { isThrow: true, onSuccess: () => { showToast('info', '핀에서 이미지가 삭제 되었습니다.'); + getPinData(); }, }); } diff --git a/frontend/src/pages/PinDetail.tsx b/frontend/src/pages/PinDetail.tsx index ef5f2179..f4f43ba1 100644 --- a/frontend/src/pages/PinDetail.tsx +++ b/frontend/src/pages/PinDetail.tsx @@ -175,7 +175,7 @@ function PinDetail({ onChange={onPinImageFileChange} /> - + From e534c046a54febc7b1b0ef1b89356f8457b2962f Mon Sep 17 00:00:00 2001 From: GC-Park Date: Wed, 11 Oct 2023 15:13:45 +0900 Subject: [PATCH 4/7] =?UTF-8?q?refactor:=20=ED=95=80=20=EC=88=98=EC=A0=95?= =?UTF-8?q?=20=EA=B8=B0=EB=8A=A5=20=EB=AA=85=EC=84=B8=20=EC=97=B0=EA=B2=B0?= =?UTF-8?q?=20=EB=B0=8F=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- frontend/src/apis/putApi.ts | 23 ++++++++++++++++++- .../components/TopicInfo/UpdatedTopicInfo.tsx | 19 ++++----------- 2 files changed, 26 insertions(+), 16 deletions(-) diff --git a/frontend/src/apis/putApi.ts b/frontend/src/apis/putApi.ts index bdcf46f3..e84978e7 100644 --- a/frontend/src/apis/putApi.ts +++ b/frontend/src/apis/putApi.ts @@ -4,12 +4,33 @@ import withTokenRefresh from './utils'; export const putApi = async ( url: string, - payload: {}, + payload: {} | FormData, contentType?: ContentTypeType, ) => { const data = await withTokenRefresh(async () => { const apiUrl = `${DEFAULT_PROD_URL + url}`; const userToken = localStorage.getItem('userToken'); + + if (payload instanceof FormData) { + const headers: any = {}; + + if (userToken) { + headers.Authorization = `Bearer ${userToken}`; + } + + const response = await fetch(apiUrl, { + method: 'PUT', + headers, + body: payload, + }); + + if (response.status >= 400) { + throw new Error('[SERVER] POST 요청에 실패했습니다.'); + } + + return response; + } + const headers: any = { 'content-type': 'application/json', }; diff --git a/frontend/src/components/TopicInfo/UpdatedTopicInfo.tsx b/frontend/src/components/TopicInfo/UpdatedTopicInfo.tsx index 872714eb..2ee0831b 100644 --- a/frontend/src/components/TopicInfo/UpdatedTopicInfo.tsx +++ b/frontend/src/components/TopicInfo/UpdatedTopicInfo.tsx @@ -16,6 +16,7 @@ import { postApi } from '../../apis/postApi'; import useCompressImage from '../../hooks/useCompressImage'; import Image from '../common/Image'; import { DEFAULT_TOPIC_IMAGE } from '../../constants'; +import { putApi } from '../../apis/putApi'; interface UpdatedTopicInfoProp { id: number; @@ -146,23 +147,11 @@ function UpdatedTopicInfo({ formData.append('image', compressedFile); - const data = JSON.stringify(id); - const jsonBlob = new Blob([data], { type: 'application/json' }); + await putApi(`/topics/images/${id}`, formData); - formData.append('topicId', jsonBlob); - - await postApi('/topic/images', formData); - - // getTopicImage(); + setTopicsFromServer(); }; - // const [topicImage, setTopicImage] = useState(null); - - // const getTopicImage = async () => { - // const newTopicImage = await getApi(`/topic/${id}`); - // setTopicImage(newTopicImage); - // }; - return ( @@ -291,4 +280,4 @@ const TopicImage = styled(Image)` export default UpdatedTopicInfo; function compressImage(file: File) { throw new Error('Function not implemented.'); -} +} \ No newline at end of file From 7c47f31034220502aa6980038803aa7c23aa0cd7 Mon Sep 17 00:00:00 2001 From: GC-Park Date: Sat, 14 Oct 2023 01:34:49 +0900 Subject: [PATCH 5/7] =?UTF-8?q?refactor:=20=EC=A7=80=EB=8F=84=20=EC=82=AC?= =?UTF-8?q?=EC=A7=84=20=EC=88=98=EC=A0=95=ED=95=A0=20=EB=95=8C=20=EC=82=AC?= =?UTF-8?q?=EC=A7=84=EB=A7=8C=20=EC=88=98=EC=A0=95=EB=90=98=EB=8F=84?= =?UTF-8?q?=EB=A1=9D=20=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../src/components/TopicInfo/UpdatedTopicInfo.tsx | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/frontend/src/components/TopicInfo/UpdatedTopicInfo.tsx b/frontend/src/components/TopicInfo/UpdatedTopicInfo.tsx index 2ee0831b..ea921a7d 100644 --- a/frontend/src/components/TopicInfo/UpdatedTopicInfo.tsx +++ b/frontend/src/components/TopicInfo/UpdatedTopicInfo.tsx @@ -12,7 +12,7 @@ import Button from '../common/Button'; import Flex from '../common/Flex'; import Space from '../common/Space'; import InputContainer from '../InputContainer'; -import { postApi } from '../../apis/postApi'; +import Text from '../common/Text'; import useCompressImage from '../../hooks/useCompressImage'; import Image from '../common/Image'; import { DEFAULT_TOPIC_IMAGE } from '../../constants'; @@ -56,6 +56,7 @@ function UpdatedTopicInfo({ const [isPrivate, setIsPrivate] = useState(false); // 혼자 볼 지도 : 같이 볼 지도 const [isAllPermissioned, setIsAllPermissioned] = useState(true); // 모두 : 지정 인원 const [authorizedMemberIds, setAuthorizedMemberIds] = useState([]); + const [changedImages, setChangedImages] = useState(null); const { compressImage } = useCompressImage(); const updateTopicInfo = async () => { @@ -144,12 +145,12 @@ function UpdatedTopicInfo({ } const compressedFile = await compressImage(file); - formData.append('image', compressedFile); await putApi(`/topics/images/${id}`, formData); - setTopicsFromServer(); + const updatedImageUrl = URL.createObjectURL(compressedFile); + setChangedImages(updatedImageUrl); }; return ( @@ -158,7 +159,7 @@ function UpdatedTopicInfo({ ) => { @@ -280,4 +281,4 @@ const TopicImage = styled(Image)` export default UpdatedTopicInfo; function compressImage(file: File) { throw new Error('Function not implemented.'); -} \ No newline at end of file +} From 2383f968616fe217d96b1c8e49abb54bcbe1af5d Mon Sep 17 00:00:00 2001 From: GC-Park Date: Sat, 14 Oct 2023 01:35:46 +0900 Subject: [PATCH 6/7] =?UTF-8?q?refactor:=20=ED=95=A8=EC=88=98=20=EC=9D=B4?= =?UTF-8?q?=EB=A6=84=20=EB=B3=80=EA=B2=BD=20=EB=B0=8F=20=EC=A7=80=EB=8F=84?= =?UTF-8?q?=20=EC=82=AC=EC=A7=84=20=EC=88=98=EC=A0=95=20=ED=83=80=EC=9D=B4?= =?UTF-8?q?=ED=8B=80=20=EC=9E=91=EC=84=B1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- frontend/src/components/PinImageContainer/index.tsx | 4 ++-- frontend/src/components/TopicInfo/UpdatedTopicInfo.tsx | 7 +++++++ 2 files changed, 9 insertions(+), 2 deletions(-) diff --git a/frontend/src/components/PinImageContainer/index.tsx b/frontend/src/components/PinImageContainer/index.tsx index 8f0422e2..fbc83065 100644 --- a/frontend/src/components/PinImageContainer/index.tsx +++ b/frontend/src/components/PinImageContainer/index.tsx @@ -17,7 +17,7 @@ const PinImageContainer = ({ images, getPinData }: PinImageContainerProps) => { const { fetchDelete } = useDelete(); const { showToast } = useToast(); - const onRemovePinIcon = (imageId: number) => { + const onRemovePinImage = (imageId: number) => { const isRemoveImage = confirm('해당 이미지를 삭제하시겠습니까?'); if (isRemoveImage) { @@ -47,7 +47,7 @@ const PinImageContainer = ({ images, getPinData }: PinImageContainerProps) => { $errorDefaultSrc={NOT_FOUND_IMAGE} /> onRemovePinIcon(image.id)} + onClick={() => onRemovePinImage(image.id)} > diff --git a/frontend/src/components/TopicInfo/UpdatedTopicInfo.tsx b/frontend/src/components/TopicInfo/UpdatedTopicInfo.tsx index ea921a7d..e70135c3 100644 --- a/frontend/src/components/TopicInfo/UpdatedTopicInfo.tsx +++ b/frontend/src/components/TopicInfo/UpdatedTopicInfo.tsx @@ -156,6 +156,13 @@ function UpdatedTopicInfo({ return ( + + 지도 사진 + + + 지도를 대표하는 사진을 변경할 수 있습니다. + + Date: Sat, 14 Oct 2023 02:47:19 +0900 Subject: [PATCH 7/7] =?UTF-8?q?refactor:=20=EC=A7=80=EB=8F=84=20=EC=88=98?= =?UTF-8?q?=EC=A0=95=20=EB=B2=84=ED=8A=BC=20=EC=9C=84=EC=B9=98=20=EC=9D=B4?= =?UTF-8?q?=EB=8F=99?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../src/components/TopicInfo/UpdatedTopicInfo.tsx | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/frontend/src/components/TopicInfo/UpdatedTopicInfo.tsx b/frontend/src/components/TopicInfo/UpdatedTopicInfo.tsx index e70135c3..930a089a 100644 --- a/frontend/src/components/TopicInfo/UpdatedTopicInfo.tsx +++ b/frontend/src/components/TopicInfo/UpdatedTopicInfo.tsx @@ -267,14 +267,21 @@ const ImageInputLabel = styled.label` cursor: pointer; position: absolute; - left: 40%; - bottom: -25px; + right: 5px; + bottom: -5px; box-shadow: rgba(0, 0, 0, 0.3) 0px 0px 5px 0px; &:hover { filter: brightness(0.95); } + + @media (max-width: 372px) { + width: 40px; + height: 30px; + + font-size: 8px; + } `; const ImageInputButton = styled.input`