From 1e3cdff69903cdb1000086f0a683e4f64e1a2ecc Mon Sep 17 00:00:00 2001 From: Daybreak312 Date: Fri, 28 Jun 2024 16:06:41 +0900 Subject: [PATCH 1/2] =?UTF-8?q?FEAT=20::=20=EB=B2=84=ED=8A=BC=20=EC=B6=94?= =?UTF-8?q?=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- public/Share.svg | 3 + public/Zoom.svg | 3 + .../body/photocard/PhotoCardModal.tsx | 128 +++++++++++------- src/component/button/GotoDivIconButton.tsx | 24 ++++ 4 files changed, 109 insertions(+), 49 deletions(-) create mode 100644 public/Share.svg create mode 100644 public/Zoom.svg create mode 100644 src/component/button/GotoDivIconButton.tsx diff --git a/public/Share.svg b/public/Share.svg new file mode 100644 index 0000000..c9193e3 --- /dev/null +++ b/public/Share.svg @@ -0,0 +1,3 @@ + + + diff --git a/public/Zoom.svg b/public/Zoom.svg new file mode 100644 index 0000000..eef78d3 --- /dev/null +++ b/public/Zoom.svg @@ -0,0 +1,3 @@ + + + diff --git a/src/component/body/photocard/PhotoCardModal.tsx b/src/component/body/photocard/PhotoCardModal.tsx index 43a9c93..119f454 100644 --- a/src/component/body/photocard/PhotoCardModal.tsx +++ b/src/component/body/photocard/PhotoCardModal.tsx @@ -4,6 +4,8 @@ import {BlurModal} from "../modal/BlurModal"; import {HttpMethod} from "../../../module/request/ServerInfo"; import axios from "axios"; import {AnyRepository} from "../../../module/repository/AnyRepository"; +import {DivIconButton} from "../../button/DivIconButton"; +import {GotoDivIconButton} from "../../button/GotoDivIconButton"; export interface PhotoCardModalProps { id: string @@ -22,6 +24,82 @@ export const PhotoCardModal = ( setModel: (value: JSX.Element | undefined) => void, isLiked: AnyRepository ) => { + + const PhotoCardModalInfo = (props: PhotoCardModalProps) => { + return ( +
+

{props.name}

+

+ {`${props.groupName}, ${props.memberNickname != null ? (props.memberNickname + " ") : ""}${props.memberName}`} +

+

+ {props.createdAt} +

+
+ ) + } + + const PhotoCardModalImages = (props: PhotoCardModalProps) => { + return ( +
+ + +
+ ) + } + + const Buttons = (props: PhotoCardModalProps) => { + return ( +
+ {PhotoCardLikeApi(props)} + + +
+ ) + } + + const PhotoCardLikeApi = (props: PhotoCardModalProps) => { + + return ( +
{ + event.stopPropagation() + if (!isLiked.data) { + isLiked.data = true + + axios.request({ + method: HttpMethod.PATCH, + url: `/photo-cards/like/${props.id}` + }).catch(); + + (event.currentTarget.firstChild as HTMLImageElement).src = "/Filled Heart.svg"; + (event.currentTarget.lastChild as HTMLPreElement).innerHTML = + (++(props.likeCount)).toString(); + } + }}> + {"heart"}/ +

{props.likeCount}

+
+ ) + } + axios.request({ method: HttpMethod.GET, url: `/photo-cards/${props.id}` @@ -35,58 +113,10 @@ export const PhotoCardModal = ( setModel={setModel}> {PhotoCardModalInfo(props)} {PhotoCardModalImages(props)} - {PhotoCardLikeApi(props, isLiked)} + {Buttons(props)} ) }) } -const PhotoCardModalInfo = (props: PhotoCardModalProps) => { - return ( -
-

{props.name}

-

- {`${props.groupName}, ${props.memberNickname != null ? (props.memberNickname + " ") : ""}${props.memberName}`} -

-

- {props.createdAt} -

-
- ) -} - -const PhotoCardModalImages = (props: PhotoCardModalProps) => { - return ( -
- - -
- ) -} - -const PhotoCardLikeApi = (props: PhotoCardModalProps, isLiked: AnyRepository) => { - - return ( -
{ - event.stopPropagation() - if (!isLiked.data) { - isLiked.data = true - - axios.request({ - method: HttpMethod.PATCH, - url: `/photo-cards/like/${props.id}` - }).catch(); - - (event.currentTarget.firstChild as HTMLImageElement).src = "/Filled Heart.svg"; - (event.currentTarget.lastChild as HTMLPreElement).innerHTML = - (++(props.likeCount)).toString(); - } - }}> - {"heart"}/ -

{props.likeCount}

-
- ) -} \ No newline at end of file diff --git a/src/component/button/GotoDivIconButton.tsx b/src/component/button/GotoDivIconButton.tsx new file mode 100644 index 0000000..47f8aee --- /dev/null +++ b/src/component/button/GotoDivIconButton.tsx @@ -0,0 +1,24 @@ +import {DivIconButton, IconProps} from "./DivIconButton"; + +export interface GotoDivIconButtonProps { + iconProps: IconProps + gap?: string + text: string + textSize: string + gotoPath: string + className?: string +} + +export const GotoDivIconButton = (props: GotoDivIconButtonProps) => { + return ( + + + + ) +} \ No newline at end of file From 07e287bf9c9923cf24a1d0361e8510881afdb6d6 Mon Sep 17 00:00:00 2001 From: Daybreak312 Date: Fri, 28 Jun 2024 19:53:43 +0900 Subject: [PATCH 2/2] =?UTF-8?q?FEAT=20::=20=EB=A7=81=ED=81=AC=20=EB=B3=B5?= =?UTF-8?q?=EC=82=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- public/Check.svg | 3 + src/component/body/photocard/PhotoCard.tsx | 6 +- .../body/photocard/PhotoCardComponent.tsx | 7 +- .../body/photocard/PhotoCardModal.tsx | 45 +++++--- src/component/body/photocard/PhotoCards.tsx | 6 +- .../body/toast/CompleteCopyToast.tsx | 15 +++ src/component/body/toast/Toast.tsx | 33 ++++++ src/component/button/DivIconButton.tsx | 4 +- src/index.css | 28 +++++ src/initializer/CurrentPage.tsx | 3 + src/initializer/Initializers.tsx | 2 +- src/pages/DispatcherRouter.tsx | 2 + src/pages/photocard/PhotoCardPage.tsx | 103 ++++++++++++++++++ 13 files changed, 234 insertions(+), 23 deletions(-) create mode 100644 public/Check.svg create mode 100644 src/component/body/toast/CompleteCopyToast.tsx create mode 100644 src/component/body/toast/Toast.tsx create mode 100644 src/pages/photocard/PhotoCardPage.tsx diff --git a/public/Check.svg b/public/Check.svg new file mode 100644 index 0000000..f46aebf --- /dev/null +++ b/public/Check.svg @@ -0,0 +1,3 @@ + + + diff --git a/src/component/body/photocard/PhotoCard.tsx b/src/component/body/photocard/PhotoCard.tsx index 1da0246..f2ed8ba 100644 --- a/src/component/body/photocard/PhotoCard.tsx +++ b/src/component/body/photocard/PhotoCard.tsx @@ -9,7 +9,9 @@ export interface PhotoCardProps { export const PhotoCard = ( props: PhotoCardProps, - setModel: (value: JSX.Element | undefined) => void + setModel: (value: JSX.Element | undefined) => void, + toast: JSX.Element | undefined, + setToast: (value: JSX.Element | undefined) => void ) => { const isLiked = new AnyRepository(false) @@ -17,7 +19,7 @@ export const PhotoCard = ( return (
{ - PhotoCardModal(props, setModel, isLiked) + PhotoCardModal(props, setModel, isLiked, toast, setToast) }}>
diff --git a/src/component/body/photocard/PhotoCardComponent.tsx b/src/component/body/photocard/PhotoCardComponent.tsx index 4c1fdbf..164c2d0 100644 --- a/src/component/body/photocard/PhotoCardComponent.tsx +++ b/src/component/body/photocard/PhotoCardComponent.tsx @@ -14,6 +14,8 @@ export const PhotoCardComponent = () => { ); + const [toast, setToast] = + useState() const [modal, setModal] = useState() @@ -32,7 +34,9 @@ export const PhotoCardComponent = () => { props, (value: JSX.Element | undefined) => { setModal(value) - } + }, + toast, + setToast ) ) } @@ -45,6 +49,7 @@ export const PhotoCardComponent = () => { <> {component} {modal && modal} + {toast} ) } \ No newline at end of file diff --git a/src/component/body/photocard/PhotoCardModal.tsx b/src/component/body/photocard/PhotoCardModal.tsx index 119f454..badbe98 100644 --- a/src/component/body/photocard/PhotoCardModal.tsx +++ b/src/component/body/photocard/PhotoCardModal.tsx @@ -6,6 +6,8 @@ import axios from "axios"; import {AnyRepository} from "../../../module/repository/AnyRepository"; import {DivIconButton} from "../../button/DivIconButton"; import {GotoDivIconButton} from "../../button/GotoDivIconButton"; +import {PageName} from "../../../initializer/CurrentPage"; +import {CompleteCopyToast} from "../toast/CompleteCopyToast"; export interface PhotoCardModalProps { id: string @@ -22,7 +24,9 @@ export interface PhotoCardModalProps { export const PhotoCardModal = ( props: PhotoCardProps, setModel: (value: JSX.Element | undefined) => void, - isLiked: AnyRepository + isLiked: AnyRepository, + toast: JSX.Element | undefined, + setToast: (value: JSX.Element | undefined) => void ) => { const PhotoCardModalInfo = (props: PhotoCardModalProps) => { @@ -60,9 +64,17 @@ export const PhotoCardModal = ( }} gap={"10px"} className={`rounded-[10px] p-[10px] hover:bg-photocard-addicon animated`} text={"공유하기"} textSize={"32px"} + onClick={() => { + navigator.clipboard.writeText(location.host + `/${PageName.PHOTOCARD}/${props.id}`) + if (toast === undefined) { + setToast( + + ) + } + }} /> { return ( -
{ - event.stopPropagation() - if (!isLiked.data) { - isLiked.data = true +
{ + event.stopPropagation() + if (!isLiked.data) { + isLiked.data = true - axios.request({ - method: HttpMethod.PATCH, - url: `/photo-cards/like/${props.id}` - }).catch(); + axios.request({ + method: HttpMethod.PATCH, + url: `/sphoto-card/like/${props.id}` + }).catch(); - (event.currentTarget.firstChild as HTMLImageElement).src = "/Filled Heart.svg"; - (event.currentTarget.lastChild as HTMLPreElement).innerHTML = - (++(props.likeCount)).toString(); - } - }}> + (event.currentTarget.firstChild as HTMLImageElement).src = "/Filled Heart.svg"; + (event.currentTarget.lastChild as HTMLPreElement).innerHTML = + (++(props.likeCount)).toString(); + } + }}> {"heart"}/

{props.likeCount}

diff --git a/src/component/body/photocard/PhotoCards.tsx b/src/component/body/photocard/PhotoCards.tsx index f379de9..bdfaf45 100644 --- a/src/component/body/photocard/PhotoCards.tsx +++ b/src/component/body/photocard/PhotoCards.tsx @@ -6,7 +6,9 @@ export interface PhotoCardsProps { export const PhotoCards = ( props: PhotoCardsProps, - setModel: (value: JSX.Element | undefined) => void + setModel: (value: JSX.Element | undefined) => void, + toast: JSX.Element | undefined, + setToast: (value: JSX.Element | undefined) => void ) => { if (props.photoCardProps === undefined) { @@ -15,6 +17,6 @@ export const PhotoCards = ( } return
- {props.photoCardProps.map((value) => PhotoCard(value, setModel))} + {props.photoCardProps.map((value) => PhotoCard(value, setModel, toast, setToast))}
} \ No newline at end of file diff --git a/src/component/body/toast/CompleteCopyToast.tsx b/src/component/body/toast/CompleteCopyToast.tsx new file mode 100644 index 0000000..7fc71c4 --- /dev/null +++ b/src/component/body/toast/CompleteCopyToast.tsx @@ -0,0 +1,15 @@ +import {Toast} from "./Toast"; + +export interface CompleteCopyToastProps { + toast: JSX.Element | undefined + setToast: (toast: JSX.Element | undefined) => void +} + +export const CompleteCopyToast = (props: CompleteCopyToastProps) => { + return + {""}/ +

링크가 복사되었습니다!

+
+} \ No newline at end of file diff --git a/src/component/body/toast/Toast.tsx b/src/component/body/toast/Toast.tsx new file mode 100644 index 0000000..dc2a844 --- /dev/null +++ b/src/component/body/toast/Toast.tsx @@ -0,0 +1,33 @@ +export interface ToastProps { + children: JSX.Element[] + className?: string + currentToast: JSX.Element | undefined + setToast: (value: JSX.Element | undefined) => void +} + +export const Toast = (props: ToastProps) => { + + const animeId = setTimeout(() => { + try { + document.getElementById('toast')!.className += " toast-down-animation" + } catch (e) { + if (e instanceof TypeError) { + // 상관 없음 + return + } + console.error(e) + } + }, 4000) + const removeId = setTimeout(() => { + props.setToast(undefined) + }, 5000) + + + return ( +
+ {props.children} +
+ ) +} \ No newline at end of file diff --git a/src/component/button/DivIconButton.tsx b/src/component/button/DivIconButton.tsx index b358200..dca1edc 100644 --- a/src/component/button/DivIconButton.tsx +++ b/src/component/button/DivIconButton.tsx @@ -1,3 +1,5 @@ +import {MouseEvent} from "react"; + export interface IconProps { readonly iconUri: string, readonly iconWidth: string, @@ -7,7 +9,7 @@ export interface IconProps { export interface DivIconButtonProps { readonly iconProps: IconProps, readonly gap: string, - readonly onClick?: () => void, + readonly onClick?: ((event: React.MouseEvent) => void) | (() => void), readonly text?: string, readonly textSize?: string, readonly className?: string, diff --git a/src/index.css b/src/index.css index e852b0e..f16178c 100644 --- a/src/index.css +++ b/src/index.css @@ -50,6 +50,34 @@ } } +.toast-up-animation { + animation: toast-up-animation 0.5s ease-in-out; +} + +@keyframes toast-up-animation { + 0% { + opacity: 0; + transform: translate3d(0, 100%, 0); + } + to { + opacity: 1; + transform: translate3d(0, 0, 0); + } +} + +.toast-down-animation { + animation: toast-down-animation 2s ease-in-out; +} + +@keyframes toast-down-animation { + 0% { + transform: translate3d(0, 0, 0); + } + to { + transform: translate3d(0, 600%, 0); + } +} + html { background-color: var(--background-default); } diff --git a/src/initializer/CurrentPage.tsx b/src/initializer/CurrentPage.tsx index 1729f63..6993e3b 100644 --- a/src/initializer/CurrentPage.tsx +++ b/src/initializer/CurrentPage.tsx @@ -1,4 +1,5 @@ export enum PageName { + PHOTOCARD = "photocard", POPULAR = "popular", LATEST = "latest", GROUP = "group", @@ -41,6 +42,8 @@ export const CurrentPageInitializer = () => { // eslint-disable-next-line no-restricted-globals const paths = location.href.split('/') + console.log(paths) + paths.forEach((it) => { if (CurrentPage.isPageName(it)) { CurrentPage.initialCurrentPage(it as PageName) diff --git a/src/initializer/Initializers.tsx b/src/initializer/Initializers.tsx index 9ddb2e1..ebf4d76 100644 --- a/src/initializer/Initializers.tsx +++ b/src/initializer/Initializers.tsx @@ -8,7 +8,7 @@ export const Initializers = () => { return <> - + {/**/} } \ No newline at end of file diff --git a/src/pages/DispatcherRouter.tsx b/src/pages/DispatcherRouter.tsx index ff7d748..0631b04 100644 --- a/src/pages/DispatcherRouter.tsx +++ b/src/pages/DispatcherRouter.tsx @@ -5,6 +5,7 @@ import {SearchPage} from "./search/SearchPage"; import {AddPhotoCardPage} from "./add/AddPhotoCardPage"; import {SignupPage} from "./auth/SignupPage"; import {LoginPage} from "./auth/LoginPage"; +import {PhotoCardPage} from "./photocard/PhotoCardPage"; export const DispatcherRouter = () => { return ( @@ -18,6 +19,7 @@ export const DispatcherRouter = () => { }/> }/> + }/> }/> ) diff --git a/src/pages/photocard/PhotoCardPage.tsx b/src/pages/photocard/PhotoCardPage.tsx new file mode 100644 index 0000000..e694c00 --- /dev/null +++ b/src/pages/photocard/PhotoCardPage.tsx @@ -0,0 +1,103 @@ +import {PageBase} from "../PageBase"; +import {useEffect, useState} from "react"; +import axios from "axios"; +import {HttpMethod} from "../../module/request/ServerInfo"; +import {PhotoCardModalProps} from "../../component/body/photocard/PhotoCardModal"; +import {PhotoCardImage} from "../../component/body/photocard/PhotoCardImage"; +import {DivIconButton} from "../../component/button/DivIconButton"; +import {AnyRepository} from "../../module/repository/AnyRepository"; +import {CompleteCopyToast} from "../../component/body/toast/CompleteCopyToast"; + +let info: PhotoCardModalProps | undefined = undefined + +export const PhotoCardPage = () => { + + const [photoCardInfo, setPhotoCardInfo] + = useState() + const [toast, setToast] + = useState() + const isLiked = new AnyRepository(false) + + const getPhotoCardInfo = async () => { + + if (info === undefined) { + const paths = location.pathname.split('/') + const photoCardId = paths[paths.length - 1] + + info = (await axios.request({ + method: HttpMethod.GET, + url: `/photo-cards/${photoCardId}` + })).data as PhotoCardModalProps + } + + setPhotoCardInfo( +
+
+

{info.name}

+

{`${info.groupName}, ${info.memberNickname != null ? (info.memberNickname + " ") : ""}${info.memberName}`}

+

{info.createdAt}

+
+
+ + +
+
+
{ + event.stopPropagation() + if (!isLiked.data) { + isLiked.data = true + + axios.request({ + method: HttpMethod.PATCH, + url: `/photo-cards/like/${info!.id}` + }).catch(); + + (event.currentTarget.firstChild as HTMLImageElement).src = "/Filled Heart.svg"; + (event.currentTarget.lastChild as HTMLPreElement).innerHTML = + (++(info!.likeCount)).toString(); + } + }}> + {"heart"}/ +

{info.likeCount}

+
+ { + navigator.clipboard.writeText(location.href) + if (toast === undefined) { + setToast( + + ) + } + }} + /> +
+ {toast} +
+ ) + } + + useEffect(() => { + getPhotoCardInfo() + }, [toast]); + + return ( + + + <> +
+ {photoCardInfo} +
+ +
+ ) +} \ No newline at end of file