diff --git a/frontend/src/constants/index.ts b/frontend/src/constants/index.ts index 29843193..702210b6 100644 --- a/frontend/src/constants/index.ts +++ b/frontend/src/constants/index.ts @@ -49,6 +49,13 @@ const RECIPIENT = { MEMBER: "MEMBER", } as const; +const ROLLINGPAPER_STATE_TYPE = { + WRITE: "WRITE", + EDIT: "EDIT", + NORMAL: "NORMAL", + LOADING: "LOADING", +} as const; + export { REGEX, COLORS, @@ -59,4 +66,5 @@ export { KAKAO_OAUTH_URL, GOOGLE_OAUTH_URL, RECIPIENT, + ROLLINGPAPER_STATE_TYPE, }; diff --git a/frontend/src/pages/RollingpaperPage/components/LetterPaper.tsx b/frontend/src/pages/RollingpaperPage/components/LetterPaper.tsx index 129976e7..78abbd5d 100644 --- a/frontend/src/pages/RollingpaperPage/components/LetterPaper.tsx +++ b/frontend/src/pages/RollingpaperPage/components/LetterPaper.tsx @@ -9,22 +9,35 @@ import PencilIcon from "@/assets/icons/bx-pencil.svg"; import MessageCreateForm from "@/pages/RollingpaperPage/components/MessageCreateForm"; import MessageBox from "@/pages/RollingpaperPage/components/MessageBox"; -import useMessageWrite from "@/pages/RollingpaperPage/hooks/useMessageWrite"; -import useSliceMessageList from "../hooks/useSliceMessageList"; +import useSliceMessageList from "@/pages/RollingpaperPage/hooks/useSliceMessageList"; + +import { ROLLINGPAPER_STATE_TYPE, RECIPIENT } from "@/constants"; interface LetterPaperProp { to: string; recipientType: Recipient; messageList: Message[]; + rollingpaperState: string; + handleWriteButtonClick: () => void; + handleEditButtonClick: () => void; + handleWriteEnd: () => void; } -const LetterPaper = ({ to, recipientType, messageList }: LetterPaperProp) => { - const { isWrite, handleWriteButtonClick, handleWriteEnd } = useMessageWrite(); - +const LetterPaper = ({ + to, + recipientType, + messageList, + rollingpaperState, + handleWriteButtonClick, + handleEditButtonClick, + handleWriteEnd, +}: LetterPaperProp) => { const elementList = useMemo(() => { const elementList = messageList .map((message) => ( { )) .reverse(); - return isWrite + return rollingpaperState === ROLLINGPAPER_STATE_TYPE.WRITE ? [ , ...elementList, ] : [...elementList]; - }, [messageList, isWrite]); + }, [rollingpaperState]); const slicedMessageLists = useSliceMessageList(elementList); @@ -49,7 +62,7 @@ const LetterPaper = ({ to, recipientType, messageList }: LetterPaperProp) => { To. {to} - {!isWrite && ( + {rollingpaperState !== ROLLINGPAPER_STATE_TYPE.WRITE && ( diff --git a/frontend/src/pages/RollingpaperPage/components/MessageBox.tsx b/frontend/src/pages/RollingpaperPage/components/MessageBox.tsx index 0c5e6fd7..a9d21daf 100644 --- a/frontend/src/pages/RollingpaperPage/components/MessageBox.tsx +++ b/frontend/src/pages/RollingpaperPage/components/MessageBox.tsx @@ -3,6 +3,8 @@ import styled from "@emotion/styled"; import IconButton from "@/components/IconButton"; +import useValidatedParam from "@/hooks/useValidatedParam"; + import TrashIcon from "@/assets/icons/bx-trash.svg"; import Pencil from "@/assets/icons/bx-pencil.svg"; import LockIcon from "@/assets/icons/bx-lock-alt.svg"; @@ -12,7 +14,6 @@ import useMessageBox from "@/pages/RollingpaperPage/hooks/useMessageBox"; import SecretMessage from "@/pages/RollingpaperPage/components/SecretMessage"; import { Message, Recipient } from "@/types"; -import useValidatedParam from "@/hooks/useValidatedParam"; const MessageBox = ({ id, @@ -24,7 +25,13 @@ const MessageBox = ({ editable, visible, recipientType, -}: Message & { recipientType: Recipient }) => { + handleEditButtonClick, + handleWriteEnd, +}: Message & { + recipientType: Recipient; + handleEditButtonClick: () => void; + handleWriteEnd: () => void; +}) => { const rollingpaperId = useValidatedParam("rollingpaperId"); const { @@ -32,7 +39,11 @@ const MessageBox = ({ handleWriteButtonClick, handleDeleteButtonClick, handleEditEnd, - } = useMessageBox({ id, rollingpaperId: rollingpaperId }); + } = useMessageBox({ + id, + rollingpaperId: rollingpaperId, + handleEditButtonClick, + }); if (!visible) { return ; @@ -47,6 +58,7 @@ const MessageBox = ({ anonymous={anonymous} secret={secret} onEditEnd={handleEditEnd} + handleWriteEnd={handleWriteEnd} enableSecretMessage={recipientType === "MEMBER"} /> ); diff --git a/frontend/src/pages/RollingpaperPage/components/MessageCreateForm.tsx b/frontend/src/pages/RollingpaperPage/components/MessageCreateForm.tsx index f174337b..0bcc65d4 100644 --- a/frontend/src/pages/RollingpaperPage/components/MessageCreateForm.tsx +++ b/frontend/src/pages/RollingpaperPage/components/MessageCreateForm.tsx @@ -25,7 +25,6 @@ export const MessageCreateForm = ({ handleColorClick, handleAnonymousCheckBoxChange, handleSecretCheckBoxChange, - initMessage, } = useMessageForm({}); const rollingpaperId = useValidatedParam("rollingpaperId"); @@ -38,13 +37,10 @@ export const MessageCreateForm = ({ anonymous, secret: enableSecretMessage && secret, }); - initMessage(); - onEditEnd(); }; const handleMessageCancel = () => { if (confirm("메시지 작성을 취소하시겠습니까?")) { - initMessage(); onEditEnd(); } }; diff --git a/frontend/src/pages/RollingpaperPage/components/MessageUpdateForm.tsx b/frontend/src/pages/RollingpaperPage/components/MessageUpdateForm.tsx index bf780718..fdf59312 100644 --- a/frontend/src/pages/RollingpaperPage/components/MessageUpdateForm.tsx +++ b/frontend/src/pages/RollingpaperPage/components/MessageUpdateForm.tsx @@ -10,6 +10,7 @@ import { Message } from "@/types"; type MessageUpdateFormProps = { enableSecretMessage: boolean; onEditEnd: () => void; + handleWriteEnd: () => void; } & Pick; export const MessageUpdateForm = ({ @@ -20,6 +21,7 @@ export const MessageUpdateForm = ({ anonymous, secret, onEditEnd, + handleWriteEnd, }: MessageUpdateFormProps) => { const { content: newContent, @@ -30,7 +32,6 @@ export const MessageUpdateForm = ({ handleMessageChange, handleAnonymousCheckBoxChange, handleSecretCheckBoxChange, - initMessage, } = useMessageForm({ initContent: content, initColor: color, @@ -47,13 +48,12 @@ export const MessageUpdateForm = ({ anonymous: newAnonymous, secret: enableSecretMessage && newSecret, }); - initMessage(); onEditEnd(); }; const handleMessageCancel = () => { if (confirm("메시지 작성을 취소하시겠습니까?")) { - initMessage(); + handleWriteEnd(); onEditEnd(); } }; diff --git a/frontend/src/pages/RollingpaperPage/hooks/useMessageBox.tsx b/frontend/src/pages/RollingpaperPage/hooks/useMessageBox.tsx index 4d58703b..9fe53c46 100644 --- a/frontend/src/pages/RollingpaperPage/hooks/useMessageBox.tsx +++ b/frontend/src/pages/RollingpaperPage/hooks/useMessageBox.tsx @@ -4,20 +4,27 @@ import useDeleteMessage from "@/pages/RollingpaperPage/hooks/useDeleteMessage"; interface UseMessageProps { id: number; rollingpaperId: number; + handleEditButtonClick: () => void; } -const useMessage = ({ id, rollingpaperId }: UseMessageProps) => { +const useMessageBox = ({ + id, + rollingpaperId, + handleEditButtonClick, +}: UseMessageProps) => { const [isEdit, setIsEdit] = useState(false); const { deleteRollingpaperMessage } = useDeleteMessage(rollingpaperId); const handleWriteButtonClick: React.MouseEventHandler< HTMLButtonElement > = () => { + handleEditButtonClick(); setIsEdit(true); }; const handleDeleteButtonClick = () => { if (id) { + handleEditButtonClick(); deleteRollingpaperMessage(id); } }; @@ -34,4 +41,4 @@ const useMessage = ({ id, rollingpaperId }: UseMessageProps) => { }; }; -export default useMessage; +export default useMessageBox; diff --git a/frontend/src/pages/RollingpaperPage/hooks/useMessageForm.tsx b/frontend/src/pages/RollingpaperPage/hooks/useMessageForm.tsx index 86854758..d7edd6ee 100644 --- a/frontend/src/pages/RollingpaperPage/hooks/useMessageForm.tsx +++ b/frontend/src/pages/RollingpaperPage/hooks/useMessageForm.tsx @@ -36,11 +36,6 @@ const useMessageForm = ({ setColor(color); }; - const initMessage = () => { - setContent(""); - setColor(INIT_COLOR); - }; - return { color, content, @@ -50,7 +45,6 @@ const useMessageForm = ({ handleColorClick, handleAnonymousCheckBoxChange, handleSecretCheckBoxChange, - initMessage, }; }; diff --git a/frontend/src/pages/RollingpaperPage/hooks/useMessageWrite.tsx b/frontend/src/pages/RollingpaperPage/hooks/useMessageWrite.tsx index e5b930b7..8334f59d 100644 --- a/frontend/src/pages/RollingpaperPage/hooks/useMessageWrite.tsx +++ b/frontend/src/pages/RollingpaperPage/hooks/useMessageWrite.tsx @@ -1,17 +1,31 @@ import React, { useState } from "react"; +import { ValueOf } from "@/types"; +import { ROLLINGPAPER_STATE_TYPE } from "@/constants"; + const useMessageWrite = () => { - const [isWrite, setIsWrite] = useState(false); + const [rollingpaperState, setRollingpaperState] = useState< + ValueOf + >(ROLLINGPAPER_STATE_TYPE.LOADING); const handleWriteButtonClick = () => { - setIsWrite(true); + setRollingpaperState(ROLLINGPAPER_STATE_TYPE.WRITE); + }; + + const handleEditButtonClick = () => { + setRollingpaperState(ROLLINGPAPER_STATE_TYPE.EDIT); }; const handleWriteEnd = () => { - setIsWrite(false); + setRollingpaperState(ROLLINGPAPER_STATE_TYPE.NORMAL); }; - return { isWrite, handleWriteButtonClick, handleWriteEnd }; + return { + rollingpaperState, + handleWriteButtonClick, + handleEditButtonClick, + handleWriteEnd, + }; }; export default useMessageWrite; diff --git a/frontend/src/pages/RollingpaperPage/hooks/useReadRollingpaper.tsx b/frontend/src/pages/RollingpaperPage/hooks/useReadRollingpaper.tsx index b613d9ad..ae5d9eb5 100644 --- a/frontend/src/pages/RollingpaperPage/hooks/useReadRollingpaper.tsx +++ b/frontend/src/pages/RollingpaperPage/hooks/useReadRollingpaper.tsx @@ -8,14 +8,24 @@ import { GetRollingpaperResponse } from "@/types/apiResponse"; interface UseReadRollingpaperArgs { teamId: number; rollingpaperId: number; + handleWriteEnd: () => void; } -export const useReadRollingpaper = ({ +const useReadRollingpaper = ({ teamId, rollingpaperId, + handleWriteEnd, }: UseReadRollingpaperArgs) => useQuery( ["rollingpaper", rollingpaperId], () => getRollingpaper({ teamId, id: rollingpaperId }), - { useErrorBoundary: true } + { + useErrorBoundary: true, + refetchOnWindowFocus: false, + onSuccess: () => { + handleWriteEnd(); + }, + } ); + +export default useReadRollingpaper; diff --git a/frontend/src/pages/RollingpaperPage/index.tsx b/frontend/src/pages/RollingpaperPage/index.tsx index b8506966..1a34d6b9 100644 --- a/frontend/src/pages/RollingpaperPage/index.tsx +++ b/frontend/src/pages/RollingpaperPage/index.tsx @@ -1,38 +1,146 @@ +import { useMemo } from "react"; +import styled from "@emotion/styled"; + import PageTitleWithBackButton from "@/components/PageTitleWithBackButton"; -import LetterPaper from "@/pages/RollingpaperPage/components/LetterPaper"; +import IconButton from "@/components/IconButton"; +import PencilIcon from "@/assets/icons/bx-pencil.svg"; import useValidatedParam from "@/hooks/useValidatedParam"; -import { useReadRollingpaper } from "@/pages/RollingpaperPage/hooks/useReadRollingpaper"; + +import MessageBox from "@/pages/RollingpaperPage/components/MessageBox"; +import MessageCreateForm from "@/pages/RollingpaperPage/components/MessageCreateForm"; +import useReadRollingpaper from "@/pages/RollingpaperPage/hooks/useReadRollingpaper"; +import useMessageWrite from "@/pages/RollingpaperPage/hooks/useMessageWrite"; +import useSliceMessageList from "@/pages/RollingpaperPage/hooks/useSliceMessageList"; + +import { RECIPIENT, ROLLINGPAPER_STATE_TYPE } from "@/constants"; const RollingpaperPage = () => { const teamId = useValidatedParam("teamId"); const rollingpaperId = useValidatedParam("rollingpaperId"); + const { + rollingpaperState, + handleWriteButtonClick, + handleWriteEnd, + handleEditButtonClick, + } = useMessageWrite(); - const { isLoading, data: rollingpaper } = useReadRollingpaper({ + const { data: rollingpaper } = useReadRollingpaper({ teamId, rollingpaperId, + handleWriteEnd, }); - if (isLoading) { - return
로딩 중
; - } + const elementList = useMemo(() => { + if (!rollingpaper) { + return []; + } + + const elementList = [...rollingpaper.messages] + .map((message) => ( + + )) + .reverse(); - if (!rollingpaper) { - return
에러
; + return rollingpaperState === ROLLINGPAPER_STATE_TYPE.WRITE + ? [ + , + ...elementList, + ] + : [...elementList]; + }, [rollingpaperState]); + + const slicedMessageLists = useSliceMessageList(elementList); + + if (rollingpaperState === ROLLINGPAPER_STATE_TYPE.LOADING) { + return
loading..
; } return ( <> - {rollingpaper.title} + {rollingpaper?.title}
- + + + To. {rollingpaper?.to} + {rollingpaperState !== ROLLINGPAPER_STATE_TYPE.WRITE && ( + + + + )} + + + {slicedMessageLists.map((messageList, index) => ( + {messageList} + ))} + +
); }; +const StyledLetterPaper = styled.div` + width: 100%; + height: 100%; + padding: 20px; + + background: ${({ theme }) => theme.colors.GRAY_100}; + border-radius: 8px; +`; + +const StyledLetterPaperTop = styled.div` + display: flex; + align-items: center; + justify-content: space-between; + + height: 20px; + + margin-bottom: 20px; +`; + +const StyledTo = styled.h3` + font-size: 18px; + font-weight: 600; +`; + +const StyledMessageList = styled.div` + display: flex; + flex-direction: column; + gap: 20px; + + width: 100%; + + a { + display: inline-block; + } +`; + +const StyledSlicedMessageLists = styled.div` + display: grid; + grid-template-columns: repeat(1, 1fr); + grid-row-gap: 20px; + grid-column-gap: 20px; + justify-items: center; + + height: calc(100% - 40px); + + @media only screen and (min-width: 960px) { + grid-template-columns: repeat(2, 1fr); + } + + @media only screen and (min-width: 1280px) { + grid-template-columns: repeat(3, 1fr); + } +`; + export default RollingpaperPage;