diff --git a/src/apis/message/apis.ts b/src/apis/message/apis.ts index 392bf9dd..06e22de2 100644 --- a/src/apis/message/apis.ts +++ b/src/apis/message/apis.ts @@ -1,5 +1,32 @@ -import type { CreateMessageRoomReq, CreateMessageRoomRes } from './types' +import type { + GetMessageRoomsReq, + GetMessageRoomsRes, + GetMessageReq, + GetMessageRes, + CreateMessageReq, + CreateMessageRes, + CreateMessageRoomReq, + CreateMessageRoomRes +} from './types' import { http } from '@utils/http' +export const getMessageRooms = (params: GetMessageRoomsReq) => + http.get('/msgrooms', params) + +export const getMessage = (params: GetMessageReq) => + http.get( + `/msgrooms/${params.msgRoomId}/msgs`, + params + ) + +export const createMessage = ({ messageRoomId, ...params }: CreateMessageReq) => + http.post, CreateMessageRes>( + `/msgrooms/${messageRoomId}/msgs`, + params + ) + +export const deleteMessageRoom = (messageRoomId: number) => + http.delete(`/msgrooms/${messageRoomId}`) + export const createMessageRoom = (params: CreateMessageRoomReq) => http.post('/msgrooms', params) diff --git a/src/apis/message/queries.ts b/src/apis/message/queries.ts index 4eb3b2ec..e126d5e0 100644 --- a/src/apis/message/queries.ts +++ b/src/apis/message/queries.ts @@ -1,6 +1,40 @@ -import { useMutation } from '@tanstack/react-query' -import { createMessageRoom } from './apis' -import type { CreateMessageRoomReq } from './types' +import { useMutation, useQuery } from '@tanstack/react-query' +import { + getMessageRooms, + getMessage, + createMessage, + deleteMessageRoom, + createMessageRoom +} from './apis' +import type { + CreateMessageReq, + GetMessageReq, + GetMessageRoomsReq, + CreateMessageRoomReq +} from './types' + +export const useGetMessageRoomsQuery = (params: GetMessageRoomsReq) => + useQuery({ + queryKey: ['getMessageRooms', params], + queryFn: () => getMessageRooms(params) + }) + +export const useGetMessageQuery = (params: GetMessageReq) => + useQuery({ + queryKey: ['getMessage', params], + queryFn: () => getMessage(params), + enabled: typeof params.msgRoomId === 'number' + }) + +export const useCreateMessageMutation = () => + useMutation({ + mutationFn: (params: CreateMessageReq) => createMessage(params) + }) + +export const useDeleteMessageRoomMutation = (messageRoomId: number) => + useMutation({ + mutationFn: () => deleteMessageRoom(messageRoomId) + }) export const useCreateMessageRoomMutation = () => useMutation({ diff --git a/src/apis/message/types.ts b/src/apis/message/types.ts index 8b54e077..18d8611a 100644 --- a/src/apis/message/types.ts +++ b/src/apis/message/types.ts @@ -1,6 +1,12 @@ -import type { CommonCreation, MessageRoomInfo, MessageInfo } from '@types' +import type { + CommonCreation, + MessageRoomInfo, + MessageInfo, + MessageSortTypeCodes +} from '@types' export type GetMessageRoomsReq = { + sort: MessageSortTypeCodes page: number } export type GetMessageRoomsRes = MessageRoomInfo[] @@ -17,7 +23,7 @@ export type GetMessageReq = { export type GetMessageRes = MessageInfo[] export type CreateMessageReq = { - msgRoomId: number + messageRoomId: number content: string } export type CreateMessageRes = CommonCreation diff --git a/src/components/common/Header/SideBar/index.tsx b/src/components/common/Header/SideBar/index.tsx index 11ec4478..49e3f681 100644 --- a/src/components/common/Header/SideBar/index.tsx +++ b/src/components/common/Header/SideBar/index.tsx @@ -1,5 +1,4 @@ import { Avatar, Divider, Icon, Badge } from '@offer-ui/react' -import Link from 'next/link' import { useRouter } from 'next/router' import type { ReactElement } from 'react' import React from 'react' @@ -21,7 +20,7 @@ const NAV_DATA: NavDataType = [ }, { content: '쪽지함', - url: '/message', + url: '/messagebox', iconType: 'message' } ] @@ -32,11 +31,19 @@ export const SideBar = ({ isOpen, onClose }: SideBarProps): ReactElement => { const handleClickLogin = () => { router.replace(OAUTH_URL.KAKAO) + + onClose() + } + + const handleClickButton = (url: string) => { + router.push(url) + onClose() } const handleClickLogout = () => { handleLogout() + onClose() } @@ -64,14 +71,14 @@ export const SideBar = ({ isOpen, onClose }: SideBarProps): ReactElement => { )} - {NAV_DATA.map((item, index) => { + {NAV_DATA.map(({ iconType, content, url }) => { return ( - - - - {item.content} - - + handleClickButton(url)}> + + {content} + ) })} diff --git a/src/components/common/Header/SideBar/styled.ts b/src/components/common/Header/SideBar/styled.ts index 660cce07..d6f22879 100644 --- a/src/components/common/Header/SideBar/styled.ts +++ b/src/components/common/Header/SideBar/styled.ts @@ -78,6 +78,8 @@ const SidebarMenu = styled.li` align-items: center; ${({ theme }): string => theme.fonts.body01B}; color: ${({ theme }): string => theme.colors.grayScale90}; + + cursor: pointer; ` const SidebarLogoutButton = styled.button` diff --git a/src/components/messagebox/Chatting/Chatting.stories.tsx b/src/components/messagebox/Chatting/Chatting.stories.tsx index 2aadcf1d..bea0fe67 100644 --- a/src/components/messagebox/Chatting/Chatting.stories.tsx +++ b/src/components/messagebox/Chatting/Chatting.stories.tsx @@ -12,61 +12,74 @@ export default meta const MESSAGES_MOCK = [ { - id: 10, content: 'offer 쪽지1', - receiverId: 1, - senderId: 2, - createdDate: '2021-11-11T00:12:43' + member: { + id: 1, + nickname: '주영', + imageUrl: '' + }, + sendTime: '2021-11-11T00:12:43' }, { - id: 8, content: 'offer 쪽지2', - receiverId: 1, - senderId: 2, - createdDate: '2021-12-15T00:13:49' + member: { + id: 2, + nickname: '수림', + imageUrl: '' + }, + sendTime: '2021-12-15T00:13:49' }, { - id: 6, content: ' offer 쪽지3 offer 쪽지3 offer 쪽지3 offer 쪽지3 offer 쪽지3 offer 쪽지3 offer 쪽지3 offer 쪽지3 offer 쪽지3 offer 쪽지3 offer 쪽지3 offer 쪽지3 offer 쪽지3 offer 쪽지3 offer 쪽지3 offer 쪽지3 offer 쪽지3 offer 쪽지3 offer 쪽지3 offer 쪽지3 offer 쪽지3 offer 쪽지3 offer 쪽지3 offer 쪽지3', - receiverId: 1, - senderId: 2, - createdDate: '2021-12-15T00:25:31' + member: { + id: 1, + nickname: '주영', + imageUrl: '' + }, + sendTime: '2021-12-15T00:25:31' }, { - id: 61, content: 'offer 쪽지3', - receiverId: 2, - senderId: 1, - createdDate: '2021-12-15T01:45:41' + member: { + id: 2, + nickname: '수림', + imageUrl: '' + }, + sendTime: '2021-12-15T01:45:41' }, { - id: 62, content: 'offer 쪽지3', - receiverId: 2, - senderId: 1, - createdDate: '2021-12-15T01:45:41' + member: { + id: 1, + nickname: '주영', + imageUrl: '' + }, + sendTime: '2021-12-15T01:45:41' }, { - id: 63, content: 'offer 쪽지3', - receiverId: 2, - senderId: 1, - createdDate: '2021-12-15T01:45:41' + member: { + id: 1, + nickname: '주영', + imageUrl: '' + }, + sendTime: '2021-12-15T01:45:41' }, { - id: 64, content: 'offer 쪽지3', - receiverId: 2, - senderId: 1, - createdDate: '2021-12-15T01:45:41' + member: { + id: 1, + nickname: '주영', + imageUrl: '' + }, + sendTime: '2021-12-15T01:45:41' } ] export const Default: StoryObj = { args: { userId: 1, - receiverImageUrl: 'https://picsum.photos/id/237/200/300', messages: MESSAGES_MOCK }, render: args => diff --git a/src/components/messagebox/Chatting/index.tsx b/src/components/messagebox/Chatting/index.tsx index d0d71969..58bf4142 100644 --- a/src/components/messagebox/Chatting/index.tsx +++ b/src/components/messagebox/Chatting/index.tsx @@ -4,53 +4,60 @@ import { Styled } from './styled' import type { ChattingProps } from './types' import { formatDate } from '@utils/format' -export const getDate = (createdDate = '') => createdDate.split('T')[0] - // TODO: 상품 정보 버블 추가 export const Chatting = ({ userId, messages, receiverImageUrl }: ChattingProps) => { - return ( - - {messages.map(({ id, content, senderId, createdDate }, idx, list) => { - const prevMessage = list.at(idx - 1) - const nextMessage = list.at(idx + 1) - const isSender = senderId === userId + const renderChattingList = () => + messages.map(({ content, member, sendTime }, idx, list) => { + const prevMessage = list.at(idx - 1) + const nextMessage = list.at(idx + 1) + const isSender = member.id === userId + + const currentDate = formatDate(sendTime, 'YYYY년 M월 D일 dddd') + const prevDate = formatDate( + prevMessage?.sendTime || '', + 'YYYY년 M월 D일 dddd' + ) + const nextDate = formatDate( + nextMessage?.sendTime || '', + 'YYYY년 M월 D일 dddd' + ) + const isPrevDateChanged = prevDate !== currentDate + const isNextDateChanged = nextDate !== currentDate + const currentTime = formatDate(sendTime, 'A H:mm') + const nextTime = formatDate(nextMessage?.sendTime || '', 'A H:mm') - const currentDate = getDate(createdDate) - const prevDate = getDate(prevMessage?.createdDate) - const nextDate = getDate(nextMessage?.createdDate) - const isPrevDateChanged = prevDate !== currentDate - const isNextDateChanged = nextDate !== currentDate + const commonProps = { + time: currentDate, + isSectionStart: + member.id !== prevMessage?.member.id || isPrevDateChanged, + isSectionLast: + member.id !== nextMessage?.member.id || + isNextDateChanged || + currentTime !== nextTime + } - const commonProps = { - time: formatDate(createdDate, 'A H:m'), - isSectionStart: - senderId !== prevMessage?.senderId || isPrevDateChanged, - isSectionLast: senderId !== nextMessage?.senderId || isNextDateChanged - } + return ( + // TODO: 메세지 키값 내려줄 수 있는지 확인하기 +
+ {isPrevDateChanged && ( + + {currentDate} + + )} + {isSender ? ( + {content} + ) : ( + + {content} + + )} +
+ ) + }) - return ( -
- {isPrevDateChanged && ( - - - {formatDate(createdDate, 'YYYY년 M월 D일 dddd')} - - - )} - {isSender ? ( - {content} - ) : ( - - {content} - - )} -
- ) - })} -
- ) + return {renderChattingList()} } diff --git a/src/components/messagebox/Chatting/types.ts b/src/components/messagebox/Chatting/types.ts index b365937d..918ee4b3 100644 --- a/src/components/messagebox/Chatting/types.ts +++ b/src/components/messagebox/Chatting/types.ts @@ -1,9 +1,11 @@ type Message = { - id: number + member: { + id: number + nickname: string + imageUrl: string + } content: string - receiverId: number - senderId: number - createdDate: string + sendTime: string } export type SendProps = { diff --git a/src/components/messagebox/ChattingRoom/ChattingRoom.stories.tsx b/src/components/messagebox/ChattingRoom/ChattingRoom.stories.tsx index f8f53e50..24acc76c 100644 --- a/src/components/messagebox/ChattingRoom/ChattingRoom.stories.tsx +++ b/src/components/messagebox/ChattingRoom/ChattingRoom.stories.tsx @@ -11,6 +11,6 @@ const meta: Meta = { export default meta export const Default: StoryObj = { - args: { id: 1 }, + args: { roomId: 1 }, render: args => } diff --git a/src/components/messagebox/ChattingRoom/index.tsx b/src/components/messagebox/ChattingRoom/index.tsx index 61d6affc..a1474991 100644 --- a/src/components/messagebox/ChattingRoom/index.tsx +++ b/src/components/messagebox/ChattingRoom/index.tsx @@ -1,114 +1,154 @@ -import { Image, IconButton, Input } from '@offer-ui/react' +import { Image, IconButton, Input, useMedia } from '@offer-ui/react' +import { useEffect, useRef, useState } from 'react' import { Styled } from './styled' import type { ChattingRoomProps } from './types' import { Chatting } from '../Chatting' +import type { ChattingProps } from '../Chatting/types' +import { Dialog } from '@components/common' import { toLocaleCurrency } from '@utils/format' +import { + useCreateMessageMutation, + useDeleteMessageRoomMutation, + useGetMessageQuery +} from '@apis' +import { useAuth, useModal } from '@hooks' -export const ChattingRoom = ({ id, onClose }: ChattingRoomProps) => { - // TODO: ChattingRoom Id를 통해서 컴포넌트 내부에서 패치하도록 +// TODO: messageRoom 정보조회 api 붙이고 제거 +const POST_MOCK = { + offerPrice: 10000, + post: { + title: '팔아요 !', + thumbnailImageUrl: '', + price: 12000 + } +} + +export const ChattingRoom = ({ roomId, onClose }: ChattingRoomProps) => { + const getMessageQuery = useGetMessageQuery({ + msgRoomId: roomId, + page: 0 + }) + const createMessageMutation = useCreateMessageMutation() + const deleteMessageRoomMutation = useDeleteMessageRoomMutation(roomId) + + const chattingBoxRef = useRef(null) + const inputRef = useRef(null) + + const [messages, setMessages] = useState([]) + const { desktop, tablet, mobile } = useMedia() + const { isOpen, openModal, closeModal } = useModal() + const { user } = useAuth() + + const senderInfo = { + id: user.id, + nickname: user.nickname, + imageUrl: user.profileImageUrl + } + + const scrollToBottom = () => { + if (!chattingBoxRef.current) { + return + } + + if (chattingBoxRef.current.scrollHeight > 0) { + chattingBoxRef.current.scrollTop = chattingBoxRef.current.scrollHeight + } + } const handleCloseRoom = () => { - onClose?.(id) + onClose?.(roomId) } + const handleDeleteRoom = async () => { + await deleteMessageRoomMutation.mutateAsync() + + handleCloseRoom() + } + + const handleSubmitMessage = async (content: string) => { + const res = await createMessageMutation.mutateAsync({ + messageRoomId: roomId, + content + }) + + if (inputRef.current) { + inputRef.current.value = '' + } + + setMessages(prev => [ + ...prev, + { + member: senderInfo, + content, + sendTime: res.createdAt + } + ]) + await getMessageQuery.refetch() + } + + useEffect(() => { + setMessages(getMessageQuery.data || []) + }, [getMessageQuery.data]) + + useEffect(() => { + scrollToBottom() + }, [messages, desktop, tablet, mobile]) + return ( - 황금 효정 + {user.nickname} - - + getMessageQuery.refetch()} + /> + + + {isOpen && ( + + + 쪽지함 나가기 + + + )} + - product + {`${POST_MOCK.post.title}-image`} - 마르니 플렛 로퍼(black) + {POST_MOCK.post.title} 시작가 - {toLocaleCurrency(15000)}원 + {toLocaleCurrency(POST_MOCK.post.price)}원 제안가 - {toLocaleCurrency(15000)}원 + + {toLocaleCurrency(POST_MOCK.offerPrice)}원 + - - + + - + ) } - -const MESSAGES_MOCK = [ - { - id: 10, - content: 'offer 쪽지1', - receiverId: 1, - senderId: 2, - createdDate: '2021-11-11T00:12:43' - }, - { - id: 8, - content: 'offer 쪽지2', - receiverId: 1, - senderId: 2, - createdDate: '2021-12-15T00:13:49' - }, - { - id: 6, - content: - ' offer 쪽지3 offer 쪽지3 offer 쪽지3 offer 쪽지3 offer 쪽지3 offer 쪽지3 offer 쪽지3 offer 쪽지3 offer 쪽지3 offer 쪽지3 offer 쪽지3 offer 쪽지3 offer 쪽지3 offer 쪽지3 offer 쪽지3 offer 쪽지3 offer 쪽지3 offer 쪽지3 offer 쪽지3 offer 쪽지3 offer 쪽지3 offer 쪽지3 offer 쪽지3 offer 쪽지3', - receiverId: 1, - senderId: 2, - createdDate: '2021-12-15T00:25:31' - }, - { - id: 61, - content: 'offer 쪽지3', - receiverId: 2, - senderId: 1, - createdDate: '2021-12-15T01:45:41' - }, - { - id: 62, - content: 'offer 쪽지3', - receiverId: 2, - senderId: 1, - createdDate: '2021-12-15T01:45:41' - }, - { - id: 63, - content: 'offer 쪽지3', - receiverId: 2, - senderId: 1, - createdDate: '2021-12-15T01:45:41' - }, - { - id: 64, - content: 'offer 쪽지3', - receiverId: 2, - senderId: 1, - createdDate: '2021-12-15T01:45:41' - }, - { - id: 612, - content: - 'offer 쪽지3 offer 쪽지3 offer 쪽지3 offer 쪽지3 offer 쪽지3 offer 쪽지3 offer 쪽지3 offer 쪽지3 offer 쪽지3 offer 쪽지3 offer 쪽지3 offer 쪽지3 offer 쪽지3 offer 쪽지3 offer 쪽지3 offer 쪽지3 offer 쪽지3 offer 쪽지3 offer 쪽지3 offer 쪽지3 offer 쪽지3 offer 쪽지3 offer 쪽지3 offer 쪽지3 offer 쪽지3 offer 쪽지3 offer 쪽지3 offer 쪽지3', - receiverId: 2, - senderId: 1, - createdDate: '2021-12-15T01:45:41' - }, - { - id: 699, - content: 'offer 쪽지3', - receiverId: 2, - senderId: 1, - createdDate: '2021-12-15T01:45:41' - } -] diff --git a/src/components/messagebox/ChattingRoom/styled.ts b/src/components/messagebox/ChattingRoom/styled.ts index 60bf7e83..f1cf228c 100644 --- a/src/components/messagebox/ChattingRoom/styled.ts +++ b/src/components/messagebox/ChattingRoom/styled.ts @@ -34,6 +34,18 @@ const IconButtonContainer = styled.div` gap: 20px; ` +const MoreButtonWrapper = styled.div` + position: relative; +` + +const DeleteButton = styled.button` + border: none; + + background-color: transparent; + + cursor: pointer; +` + const ProductInfo = styled.div` display: flex; gap: 8px; @@ -106,6 +118,8 @@ export const Styled = { Header, Nickname, IconButtonContainer, + MoreButtonWrapper, + DeleteButton, ProductInfo, ProductTextContainer, ProductName, diff --git a/src/components/messagebox/ChattingRoom/types.ts b/src/components/messagebox/ChattingRoom/types.ts index 9080221b..a059f55c 100644 --- a/src/components/messagebox/ChattingRoom/types.ts +++ b/src/components/messagebox/ChattingRoom/types.ts @@ -1,4 +1,4 @@ export type ChattingRoomProps = { - id: number + roomId: number onClose?(id: number): void } diff --git a/src/components/messagebox/MessagePreview/MessagePreview.stories.tsx b/src/components/messagebox/MessagePreview/MessagePreview.stories.tsx index 82f84d9e..1ae96898 100644 --- a/src/components/messagebox/MessagePreview/MessagePreview.stories.tsx +++ b/src/components/messagebox/MessagePreview/MessagePreview.stories.tsx @@ -12,20 +12,21 @@ export default meta export const Default: StoryObj = { args: { - userInfo: { + partner: { id: 1, nickname: 'offerer', - profileImageUrl: null + imageUrl: '' }, - productInfo: { - price: 123346, - productImageUrl: null + post: { + id: 1, + price: 10000, + thumbnailImageUrl: '' }, - latestTalk: { - content: - '구매 가능 할까요 ? 구매 가능 할까요 ? 구매 가능 할까요 ? 구매 가능 할까요 ? 구매 가능 할까요 ? 구매 가능 할까요 ? 구매 가능 할까요 ? 구매 가능 할까요 ? 구매 가능 할까요 ?', - createdDate: '2시간 전' - } + offerPrice: 123, + lastContent: + '구매 가능 할까요 ? 구매 가능 할까요 ? 구매 가능 할까요 ? 구매 가능 할까요 ? 구매 가능 할까요 ? 구매 가능 할까요 ? 구매 가능 할까요 ? 구매 가능 할까요 ? 구매 가능 할까요 ?', + lastSendTime: '2시간 전', + notReadCnt: 0 }, render: args => } diff --git a/src/components/messagebox/MessagePreview/index.tsx b/src/components/messagebox/MessagePreview/index.tsx index f291cb00..8bbda187 100644 --- a/src/components/messagebox/MessagePreview/index.tsx +++ b/src/components/messagebox/MessagePreview/index.tsx @@ -3,13 +3,16 @@ import type { ReactElement } from 'react' import { Styled } from './styled' import type { MessagePreviewProps } from './types' import { IMAGE } from '@constants' -import { toLocaleCurrency } from '@utils' +import { getTimeDiffText, toLocaleCurrency } from '@utils' export const MessagePreview = ({ id, - userInfo, - productInfo, - latestTalk, + partner, + post, + offerPrice, + lastContent, + lastSendTime, + notReadCnt, isSelected = false, onClick }: MessagePreviewProps): ReactElement => { @@ -23,31 +26,27 @@ export const MessagePreview = ({ role="button" onClick={handleClickPreview}> - + - {userInfo.nickname} - {latestTalk.createdDate} - {latestTalk.content} + {partner.nickname} + {getTimeDiffText(lastSendTime)} + {lastContent} - - 6 - - {`${toLocaleCurrency( - productInfo.price - )}원`} + {notReadCnt && ( + + {notReadCnt} + + )} + {`${toLocaleCurrency(offerPrice)}원`} product diff --git a/src/components/messagebox/MessagePreview/types.ts b/src/components/messagebox/MessagePreview/types.ts index 795a135c..8dde04a6 100644 --- a/src/components/messagebox/MessagePreview/types.ts +++ b/src/components/messagebox/MessagePreview/types.ts @@ -1,18 +1,6 @@ -export type MessagePreviewProps = { - id: number - userInfo: { - id: number - nickname: string - profileImageUrl: string | null - } - productInfo: { - price: number - productImageUrl: string | null - } - latestTalk: { - content: string - createdDate: string - } +import type { MessageRoomInfo } from '@types' + +export type MessagePreviewProps = MessageRoomInfo & { isSelected?: boolean onClick?(id: number): void } diff --git a/src/constants/app.ts b/src/constants/app.ts index 875fccc1..f69922df 100644 --- a/src/constants/app.ts +++ b/src/constants/app.ts @@ -34,6 +34,18 @@ export const TRADE_TYPES = [ } ] as const +export const MESSAGE_SORT_OPTIONS = [ + { code: 'ALL', name: '전체' }, + { + code: 'BUY', + name: '구매' + }, + { + code: 'SELL', + name: '판매' + } +] + export const TRADE_STATUS = [ { code: 'SELLING', diff --git a/src/pages/messagebox/index.tsx b/src/pages/messagebox/index.tsx index 75919c20..f7de2583 100644 --- a/src/pages/messagebox/index.tsx +++ b/src/pages/messagebox/index.tsx @@ -1,7 +1,11 @@ import { css } from '@emotion/react' import styled from '@emotion/styled' import { Modal, useMedia } from '@offer-ui/react' +import type { GetServerSideProps } from 'next' +import { useRouter } from 'next/router' import { useState, type ReactElement, useEffect } from 'react' +import { toQueryString } from '@utils/format' +import { useGetMessageRoomsQuery } from '@apis' import { MessagePreview, Tabs, @@ -9,35 +13,53 @@ import { ChattingRoom, MessageBoxPlaceholder } from '@components' -import { IMAGE } from '@constants' +import { IMAGE, MESSAGE_SORT_OPTIONS } from '@constants' import { useModal } from '@hooks' +import type { MessageSortTypeCodes } from '@types' -type TabType = 'all' | 'buy' | 'sell' +type RoomId = number | null +type Props = { + roomId: RoomId +} -const TABS = { - all: '전체', - buy: '구매', - sell: '판매' -} as const +export const getServerSideProps: GetServerSideProps = async ({ + query +}) => ({ + props: { + roomId: query.roomId ? Number(query.roomId) : null + } +}) -const TabKeys = Object.keys(TABS) as TabType[] -const TabEntries = Object.entries>(TABS) +const MessageBoxPage = ({ roomId: defaultRoomId }: Props): ReactElement => { + const [sortType, setSortType] = useState('ALL') + const getMessageRoomsQuery = useGetMessageRoomsQuery({ + page: 0, + sort: sortType + }) -const MessageBoxPage = (): ReactElement => { - const [tab, setTab] = useState('all') - const [roomId, setRoomId] = useState(null) + const [roomId, setRoomId] = useState(defaultRoomId) + const router = useRouter() const { isOpen, openModal, closeModal } = useModal() const { desktop, mobile, tablet } = useMedia() - const handleChangeTab = (currentIndex: number, nextIndex: number) => { - const nextTab = TabKeys[nextIndex] + const messageList = getMessageRoomsQuery.data || [] + const messagesCount = messageList.length + + const handleChangeSortType = (currentIndex: number, nextIndex: number) => { + const { code } = MESSAGE_SORT_OPTIONS[nextIndex] - setTab(nextTab) + setSortType(code) } const handleSelectRoom = (id: number) => { setRoomId(id) + router.push( + `/messagebox${toQueryString({ + roomId: String(id) + })}` + ) + if (!desktop) { openModal() } @@ -46,13 +68,23 @@ const MessageBoxPage = (): ReactElement => { const handleCloseRoom = () => { setRoomId(null) + getMessageRoomsQuery.refetch() + router.push(`/messagebox`) + if (!desktop) { closeModal() } } useEffect(() => { - setRoomId(null) + if (desktop) { + closeModal() + return + } + + if (roomId) { + openModal() + } }, [desktop, tablet, mobile]) return ( @@ -62,14 +94,16 @@ const MessageBoxPage = (): ReactElement => { - 내 쪽지함 {LIST_MOCK.length} + 내 쪽지함 {messagesCount}
- + - {TabEntries.map(([key, value]) => ( - - {value} + {MESSAGE_SORT_OPTIONS.map(({ code, name }) => ( + + + {name} + ))} @@ -77,14 +111,15 @@ const MessageBoxPage = (): ReactElement => {
- {LIST_MOCK.length > 0 ? ( - LIST_MOCK.map(({ id, ...messageInfo }) => ( + {messagesCount > 0 ? ( + messageList.map(({ id, post, ...resInfo }) => ( handleSelectRoom(id)} + {...resInfo} /> )) ) : ( @@ -101,7 +136,7 @@ const MessageBoxPage = (): ReactElement => {
{roomId ? ( - + ) : ( { {roomId && ( - + )} @@ -248,177 +283,4 @@ const DetailContainer = styled.div` `} ` -const LIST_MOCK = [ - { - id: 1, - userInfo: { - id: 1, - nickname: 'offerer', - profileImageUrl: null - }, - productInfo: { - price: 123346, - productImageUrl: null - }, - latestTalk: { - content: - '구매 가능 할까요 ? 구매 가능 할까요 ? 구매 가능 할까요 ? 구매 가능 할까요 ? 구매 가능 할까요 ? 구매 가능 할까요 ? 구매 가능 할까요 ? 구매 가능 할까요 ? 구매 가능 할까요 ?', - createdDate: '2시간 전' - } - }, - { - id: 2, - userInfo: { - id: 1, - nickname: 'offerer', - profileImageUrl: null - }, - productInfo: { - price: 123346, - productImageUrl: null - }, - latestTalk: { - content: - '구매 가능 할까요 ? 구매 가능 할까요 ? 구매 가능 할까요 ? 구매 가능 할까요 ? 구매 가능 할까요 ? 구매 가능 할까요 ? 구매 가능 할까요 ? 구매 가능 할까요 ? 구매 가능 할까요 ?', - createdDate: '2시간 전' - } - }, - { - id: 3, - userInfo: { - id: 1, - nickname: 'offerer', - profileImageUrl: null - }, - productInfo: { - price: 123346, - productImageUrl: null - }, - latestTalk: { - content: - '구매 가능 할까요 ? 구매 가능 할까요 ? 구매 가능 할까요 ? 구매 가능 할까요 ? 구매 가능 할까요 ? 구매 가능 할까요 ? 구매 가능 할까요 ? 구매 가능 할까요 ? 구매 가능 할까요 ?', - createdDate: '2시간 전' - } - }, - { - id: 4, - userInfo: { - id: 1, - nickname: 'offerer', - profileImageUrl: null - }, - productInfo: { - price: 123346, - productImageUrl: null - }, - latestTalk: { - content: - '구매 가능 할까요 ? 구매 가능 할까요 ? 구매 가능 할까요 ? 구매 가능 할까요 ? 구매 가능 할까요 ? 구매 가능 할까요 ? 구매 가능 할까요 ? 구매 가능 할까요 ? 구매 가능 할까요 ?', - createdDate: '2시간 전' - } - }, - { - id: 5, - userInfo: { - id: 1, - nickname: 'offerer', - profileImageUrl: null - }, - productInfo: { - price: 123346, - productImageUrl: null - }, - latestTalk: { - content: - '구매 가능 할까요 ? 구매 가능 할까요 ? 구매 가능 할까요 ? 구매 가능 할까요 ? 구매 가능 할까요 ? 구매 가능 할까요 ? 구매 가능 할까요 ? 구매 가능 할까요 ? 구매 가능 할까요 ?', - createdDate: '2시간 전' - } - }, - { - id: 6, - userInfo: { - id: 1, - nickname: 'offerer', - profileImageUrl: null - }, - productInfo: { - price: 123346, - productImageUrl: null - }, - latestTalk: { - content: - '구매 가능 할까요 ? 구매 가능 할까요 ? 구매 가능 할까요 ? 구매 가능 할까요 ? 구매 가능 할까요 ? 구매 가능 할까요 ? 구매 가능 할까요 ? 구매 가능 할까요 ? 구매 가능 할까요 ?', - createdDate: '2시간 전' - } - }, - { - id: 7, - userInfo: { - id: 1, - nickname: 'offerer', - profileImageUrl: null - }, - productInfo: { - price: 123346, - productImageUrl: null - }, - latestTalk: { - content: - '구매 가능 할까요 ? 구매 가능 할까요 ? 구매 가능 할까요 ? 구매 가능 할까요 ? 구매 가능 할까요 ? 구매 가능 할까요 ? 구매 가능 할까요 ? 구매 가능 할까요 ? 구매 가능 할까요 ?', - createdDate: '2시간 전' - } - }, - { - id: 8, - userInfo: { - id: 1, - nickname: 'offerer', - profileImageUrl: null - }, - productInfo: { - price: 123346, - productImageUrl: null - }, - latestTalk: { - content: - '구매 가능 할까요 ? 구매 가능 할까요 ? 구매 가능 할까요 ? 구매 가능 할까요 ? 구매 가능 할까요 ? 구매 가능 할까요 ? 구매 가능 할까요 ? 구매 가능 할까요 ? 구매 가능 할까요 ?', - createdDate: '2시간 전' - } - }, - { - id: 9, - userInfo: { - id: 1, - nickname: 'offerer', - profileImageUrl: null - }, - productInfo: { - price: 123346, - productImageUrl: null - }, - latestTalk: { - content: - '구매 가능 할까요 ? 구매 가능 할까요 ? 구매 가능 할까요 ? 구매 가능 할까요 ? 구매 가능 할까요 ? 구매 가능 할까요 ? 구매 가능 할까요 ? 구매 가능 할까요 ? 구매 가능 할까요 ?', - createdDate: '2시간 전' - } - }, - { - id: 10, - userInfo: { - id: 1, - nickname: 'offerer', - profileImageUrl: null - }, - productInfo: { - price: 123346, - productImageUrl: null - }, - latestTalk: { - content: - '구매 가능 할까요 ? 구매 가능 할까요 ? 구매 가능 할까요 ? 구매 가능 할까요 ? 구매 가능 할까요 ? 구매 가능 할까요 ? 구매 가능 할까요 ? 구매 가능 할까요 ? 구매 가능 할까요 ?', - createdDate: '2시간 전' - } - } -] - export default MessageBoxPage diff --git a/src/types/scheme.ts b/src/types/scheme.ts index d20dc631..a364f61f 100644 --- a/src/types/scheme.ts +++ b/src/types/scheme.ts @@ -154,7 +154,7 @@ export type OfferSummary = { export type MessageRoomInfo = { id: number partner: PartnerBrief - post: PostBrief + post: Pick offerPrice: number lastContent: string notReadCnt: number diff --git a/src/types/service.ts b/src/types/service.ts index 90b5791b..20d0109a 100644 --- a/src/types/service.ts +++ b/src/types/service.ts @@ -4,7 +4,8 @@ import type { SORT_TYPES, TRADE_TYPES, TRADE_STATUS, - PRODUCT_CONDITIONS + PRODUCT_CONDITIONS, + MESSAGE_SORT_OPTIONS } from '@constants' /** 정렬 옵션 */ @@ -19,6 +20,12 @@ export type SortType = ValueOf export type SortTypeCodes = SortType['code'] export type SortTypeNames = SortType['name'] +/** 정렬 타입 - 메세지 */ +export type MessageSortTypes = typeof MESSAGE_SORT_OPTIONS +export type MessageSortType = ValueOf +export type MessageSortTypeCodes = MessageSortType['code'] +export type MessageSortTypeNames = MessageSortType['name'] + /** 거래 방식 */ export type TradeTypes = typeof TRADE_TYPES export type TradeType = ValueOf diff --git a/src/utils/format/index.ts b/src/utils/format/index.ts index 9279eb2d..3c37b714 100644 --- a/src/utils/format/index.ts +++ b/src/utils/format/index.ts @@ -5,7 +5,7 @@ dayjs.locale('ko') const DATE_FORMAT = { 'YYYY년 M월 D일 dddd': 'YYYY년 M월 D일 dddd', - 'A H:m': 'A H:m' + 'A H:mm': 'A H:mm' } export const formatDate = (