Skip to content

Commit 2abdb8c

Browse files
committed
feat:최초편지, 최초답장 구현 완료(디테일 처리 필요)
1 parent bf7f2f5 commit 2abdb8c

File tree

13 files changed

+125
-135
lines changed

13 files changed

+125
-135
lines changed

src/apis/randomLetter.ts

Lines changed: 1 addition & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -11,18 +11,14 @@ const getRandomLetters = async (category: string | null) => {
1111
}
1212
};
1313

14-
interface ApproveRequest {
15-
letterId: string;
16-
writerId: string;
17-
}
1814
const postRandomLettersApprove = async (approveRequest: ApproveRequest, callBack?: () => void) => {
1915
try {
2016
console.log('엔드포인트 : /api/random-letters/approve');
2117
console.log('request', approveRequest);
2218
const res = await client.post('/api/random-letters/approve', approveRequest);
2319
if (!res) throw new Error('랜덤편지 매칭수락 도중 에러가 발생했습니다.');
2420
if (callBack) callBack();
25-
console.log(res);
21+
return res;
2622
} catch (error) {
2723
console.error(error);
2824
}

src/components/ResultLetter.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ export default function ResultLetter({
1515
const today = `${date.getFullYear()}${date.getMonth() + 1}${date.getDate()}일`;
1616

1717
return (
18-
<LetterWrapper>
18+
<LetterWrapper className="min-w-[300px]">
1919
<div className="flex w-full flex-col gap-[35px]">
2020
<div className="flex justify-between gap-3">
2121
<div className="flex flex-col gap-2.5">

src/layouts/Header.tsx

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,16 @@
1-
import { Link } from 'react-router';
1+
import { Link, useNavigate } from 'react-router';
22

33
import { AlarmIcon, ArrowLeftIcon, PersonIcon } from '@/assets/icons';
44

55
const Header = () => {
66
// TODO: 뒤로 가기 버튼이 보이는 조건 추가
77
// TODO: 스크롤 발생 시, 어떻게 보여져야 하는지
8+
const navigate = useNavigate();
89
return (
910
<header className="fixed top-0 z-40 flex h-16 w-full max-w-150 items-center justify-between p-5">
10-
<ArrowLeftIcon className="h-6 w-6 text-white" />
11+
<button onClick={() => navigate(-1)}>
12+
<ArrowLeftIcon className="h-6 w-6 text-white" />
13+
</button>
1114
<div className="flex items-center gap-3">
1215
<Link to="/mypage/notifications">
1316
<AlarmIcon className="h-6 w-6 text-white" />

src/pages/RandomLetters/components/CoolTime.tsx

Lines changed: 0 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -52,49 +52,6 @@ export default function CoolTime({
5252
};
5353
});
5454

55-
// const [endTimes, setEndTimes] = useState({
56-
// hours: Math.floor((endTime / (1000 * 60 * 60)) % 24),
57-
// minutes: Math.floor((endTime / (1000 * 60)) % 60),
58-
// seconds: Math.floor((endTime / 1000) % 60),
59-
// });
60-
61-
// useEffect(() => {
62-
// if (endTimes.hours < 0 || endTimes.minutes < 0 || endTimes.seconds < 0) {
63-
// setEndTimes({ hours: 0, minutes: 0, seconds: 0 });
64-
// }
65-
// if (endTimes.hours === 0 && endTimes.minutes === 0 && endTimes.seconds === 0) {
66-
// setCoolTime(false);
67-
// return;
68-
// }
69-
// const endTimeFlow = setInterval(() => {
70-
// setEndTimes((currentTime) => {
71-
// if (currentTime.seconds > 0) {
72-
// return { ...currentTime, seconds: currentTime.seconds - 1 };
73-
// } //
74-
// else {
75-
// if (currentTime.minutes > 0) {
76-
// return { ...currentTime, minutes: currentTime.minutes - 1, seconds: 59 };
77-
// } //
78-
// else {
79-
// if (currentTime.hours > 0) {
80-
// return { hours: currentTime.hours - 1, minutes: 59, seconds: 59 };
81-
// } //
82-
// else {
83-
// setCoolTime(false);
84-
// return { ...currentTime };
85-
// }
86-
// }
87-
// }
88-
// });
89-
// if (endTimes.hours === 0 && endTimes.minutes === 0 && endTimes.seconds === 0) {
90-
// clearInterval(endTimeFlow);
91-
// }
92-
// }, 1000);
93-
94-
// return () => {
95-
// clearInterval(endTimeFlow);
96-
// };
97-
// }, [endTimes, setCoolTime]);
9855
return (
9956
<div className="mt-20 flex flex-col items-center justify-center">
10057
<div className="body-m flex flex-col items-center justify-center">

src/pages/RandomLetters/components/Matched.tsx

Lines changed: 19 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -7,32 +7,32 @@ import { formatNumber } from '@/utils/formatNumber';
77
import { timeFormatter } from '@/utils/timeFormatter';
88

99
export default function Matched({
10-
selectedLetter,
10+
matchedLetter,
1111
setIsMatched,
1212
setIsCoolTime,
13+
setOpenSelectedDetailModal,
1314
}: {
14-
selectedLetter: RandomLetters;
15+
matchedLetter: MatchedLetter;
1516
setIsMatched: React.Dispatch<React.SetStateAction<boolean>>;
1617
setIsCoolTime: React.Dispatch<React.SetStateAction<boolean>>;
18+
setOpenSelectedDetailModal: React.Dispatch<React.SetStateAction<boolean>>;
1719
}) {
1820
const navigate = useNavigate();
1921

2022
const [isDisabled, setIsDisabled] = useState<boolean>(false);
2123

22-
const TIME_STAMP = '2025-03-03T19:35:05';
24+
const TIME_STAMP = matchedLetter.replyDeadLine;
25+
const MATCH_DURATION = 1000 * 60 * 60 * 24;
26+
const MATCH_GRACE = 1000 * 60 * 5;
2327

24-
const MATCHED_DATE = new Date(TIME_STAMP);
28+
const END_DATE = new Date(TIME_STAMP);
2529

26-
const END_DATE = new Date(MATCHED_DATE);
27-
END_DATE.setHours(MATCHED_DATE.getHours() + 24);
28-
29-
const GRACE_DATE = new Date(MATCHED_DATE);
30-
GRACE_DATE.setMinutes(MATCHED_DATE.getMinutes() + 5);
30+
const MATCHED_DATE = new Date(END_DATE.getTime() - MATCH_DURATION + MATCH_GRACE);
3131

3232
const NOW_DATE = new Date();
3333

3434
const endTime = END_DATE.getTime() - NOW_DATE.getTime();
35-
const graceTime = GRACE_DATE.getTime() - NOW_DATE.getTime();
35+
const graceTime = MATCHED_DATE.getTime() - NOW_DATE.getTime();
3636

3737
const [endTimeSeconds, setEndTimeSeconds] = useState(Math.floor(endTime / 1000));
3838
const [graceTimeSeconds, setGraceTimeSeconds] = useState(Math.floor(graceTime / 1000));
@@ -90,11 +90,16 @@ export default function Matched({
9090
{formatNumber(FormatedEndTimes.hours)} : {formatNumber(FormatedEndTimes.minutes)} :{' '}
9191
{formatNumber(FormatedEndTimes.seconds)}
9292
</p>
93-
<div className="mt-2 w-75">
93+
<div
94+
className="mt-2 w-75"
95+
onClick={() => {
96+
setOpenSelectedDetailModal(true);
97+
}}
98+
>
9499
<ResultLetter
95-
categoryName={selectedLetter.category}
96-
title={selectedLetter.title}
97-
zipCode={selectedLetter.zipCode}
100+
categoryName={matchedLetter.category}
101+
title={matchedLetter.title}
102+
zipCode={matchedLetter.zipCode}
98103
/>
99104
</div>
100105
<button

src/pages/RandomLetters/components/MatchedLetter.tsx

Lines changed: 11 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,48 +1,48 @@
1-
import { useState } from 'react';
1+
import { useEffect, useState } from 'react';
22
import { useNavigate } from 'react-router';
33
import { twMerge } from 'tailwind-merge';
44

55
import BackButton from '@/components/BackButton';
66
import ReportModal from '@/components/ReportModal';
77
import { FONT_TYPE_OBJ, PAPER_TYPE_OBJ } from '@/pages/Write/constants';
88

9-
const MatchedLetter = ({ selectedLetter }: { selectedLetter: RandomLetters }) => {
9+
const MatchedLetter = ({ matchedLetter }: { matchedLetter: MatchedLetter }) => {
1010
const navigate = useNavigate();
11-
// 상대방의 우편번호도 데이터에 포함되어야 할 거 같음!!!
12-
const [letterDetail] = useState<LetterDetail | null>(null);
1311

1412
const [reportModalOpen, setReportModalOpen] = useState<boolean>(false);
1513

14+
useEffect(() => {}, [matchedLetter]);
15+
1616
return (
1717
<>
1818
{reportModalOpen && <ReportModal onClose={() => setReportModalOpen(false)} />}
1919
<div
2020
className={twMerge(
2121
`flex grow flex-col gap-3 px-5 pb-7.5`,
22-
letterDetail && PAPER_TYPE_OBJ[letterDetail.paperType],
22+
PAPER_TYPE_OBJ[matchedLetter.paperType],
2323
)}
2424
>
2525
<div className="absolute top-5 left-0 flex w-full justify-between px-5">
2626
<BackButton />
2727
</div>
2828
<div className="flex flex-col gap-3 px-5">
2929
<span className="body-b mt-[55px]">TO. 따숨이</span>
30-
<span className="body-sb">{selectedLetter?.title}</span>
30+
<span className="body-sb">{matchedLetter?.title}</span>
3131
</div>
3232
<textarea
3333
readOnly
34-
value={letterDetail?.content}
34+
value={matchedLetter?.content}
3535
className={twMerge(
3636
`body-r basic-theme min-h-full w-full grow px-6`,
37-
letterDetail && FONT_TYPE_OBJ[letterDetail.fontType],
37+
FONT_TYPE_OBJ[matchedLetter.fontType],
3838
)}
3939
></textarea>
40-
<span className="body-sb mt-10 flex justify-end">FROM. {selectedLetter.zipCode}</span>
40+
<span className="body-sb mt-10 flex justify-end">FROM. {matchedLetter.zipCode}</span>
4141
<button
4242
className="bg-primary-3 disabled:bg-gray-30 body-m mt-3 w-full rounded-lg py-2 disabled:text-white"
4343
onClick={() => {
44-
navigate(`/letter/write?letterId=${selectedLetter?.letterId}`, {
45-
state: { randomMatched: true, selectedLetter: selectedLetter },
44+
navigate(`/letter/write?letterId=${matchedLetter?.letterId}`, {
45+
state: { randomMatched: true, matchedLetter: matchedLetter },
4646
});
4747
}}
4848
aria-label="편지 작성 버튼"

src/pages/RandomLetters/components/MatchingSelectModal.tsx

Lines changed: 15 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -7,12 +7,23 @@ import ResultLetter from '@/components/ResultLetter';
77
function MatchingSelectModal({
88
setOpenModal,
99
selectedLetter,
10+
setMatchedLetter,
1011
setOpenSelectedDetailModal,
1112
}: {
1213
setOpenModal: React.Dispatch<React.SetStateAction<boolean>>;
1314
selectedLetter: RandomLetters;
15+
setMatchedLetter: React.Dispatch<React.SetStateAction<MatchedLetter>>;
1416
setOpenSelectedDetailModal: React.Dispatch<React.SetStateAction<boolean>>;
1517
}) {
18+
const handlePostRandomLettersApprove = async (approveRequest: ApproveRequest) => {
19+
const res = await postRandomLettersApprove(approveRequest);
20+
if (res?.status === 200) {
21+
setOpenModal(false);
22+
// MEMO : 이제 랜덤 편지 승인하기 데이터에 랜덤 편지 최종 매칭 시간 검증과 동일한 response 값이 담겨서 그 값을 matchedLetter의 상태 업데이트 값으로 사용하면 됨
23+
setMatchedLetter(res.data.data);
24+
setOpenSelectedDetailModal(true);
25+
}
26+
};
1627
// const navigate = useNavigate();
1728
return (
1829
<ModalOverlay>
@@ -40,14 +51,10 @@ function MatchingSelectModal({
4051
<button
4152
className="bg-primary-3 body-m h-10 flex-1 basis-1/2 rounded-lg"
4253
onClick={() => {
43-
postRandomLettersApprove(
44-
// MEMO 여기서 writerId는 나의 ID인가?
45-
{ letterId: `${selectedLetter.letterId}`, writerId: '1' },
46-
() => {
47-
setOpenModal(false);
48-
setOpenSelectedDetailModal(true);
49-
},
50-
);
54+
handlePostRandomLettersApprove({
55+
letterId: `${selectedLetter.letterId}`,
56+
writerId: `${selectedLetter.writerId}`,
57+
});
5158
}}
5259
>
5360
승인하기

src/pages/RandomLetters/index.tsx

Lines changed: 9 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -20,18 +20,19 @@ const RandomLettersPage = () => {
2020
category: 'ETC',
2121
title: 'error',
2222
zipCode: 'error',
23-
paperType: 'BASIC',
24-
fontType: 'DEFAULT',
23+
writerId: 0,
2524
createdAt: new Date(),
2625
});
2726
const [matchedLetter, setMatchedLetter] = useState<MatchedLetter>({
2827
letterId: 0,
2928
category: 'ETC',
3029
title: 'error',
30+
content: 'error',
3131
zipCode: 'error',
3232
paperType: 'BASIC',
3333
fontType: 'DEFAULT',
3434
createdAt: new Date(),
35+
writerId: 0,
3536
replyDeadLine: new Date(),
3637
temporary: false,
3738
});
@@ -43,30 +44,10 @@ const RandomLettersPage = () => {
4344
const res = await getRandomLetterMatched();
4445
if (res?.status === 200) {
4546
const data: MatchedLetter = res.data.data;
47+
console.log(data);
4648
if (data?.temporary === true) {
4749
setIsMatched(true);
48-
setMatchedLetter({
49-
letterId: data.letterId,
50-
category: data.category,
51-
title: data.title,
52-
zipCode: data.zipCode,
53-
paperType: data.paperType,
54-
fontType: data.fontType,
55-
createdAt: data.createdAt,
56-
replyDeadLine: data.replyDeadLine,
57-
temporary: data.temporary,
58-
});
59-
console.log({
60-
letterId: data.letterId,
61-
category: data.category,
62-
title: data.title,
63-
zipCode: data.zipCode,
64-
paperType: data.paperType,
65-
fontType: data.fontType,
66-
createdAt: data.createdAt,
67-
replyDeadLine: data.replyDeadLine,
68-
temporary: data.temporary,
69-
});
50+
setMatchedLetter(data);
7051
}
7152
}
7253
};
@@ -94,7 +75,7 @@ const RandomLettersPage = () => {
9475
return (
9576
<>
9677
{openSelectedDetailModal ? (
97-
<MatchedLetter selectedLetter={selectedLetter} />
78+
<MatchedLetter matchedLetter={matchedLetter} />
9879
) : (
9980
<>
10081
<div className="z-10 flex grow flex-col items-center overflow-hidden">
@@ -115,9 +96,10 @@ const RandomLettersPage = () => {
11596
) : (
11697
<Matched
11798
// MEMO : 여기 selectedLetter가 아니라 MatchedLetter 들어가야함
118-
selectedLetter={selectedLetter}
99+
matchedLetter={matchedLetter}
119100
setIsMatched={setIsMatched}
120101
setIsCoolTime={setIsCoolTime}
102+
setOpenSelectedDetailModal={setOpenSelectedDetailModal}
121103
/>
122104
)}
123105

@@ -126,6 +108,7 @@ const RandomLettersPage = () => {
126108
setOpenModal={setOpenSelectModal}
127109
selectedLetter={selectedLetter}
128110
setOpenSelectedDetailModal={setOpenSelectedDetailModal}
111+
setMatchedLetter={setMatchedLetter}
129112
/>
130113
)}
131114
</div>

src/pages/Write/LetterEditor.tsx

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,7 @@ export default function LetterEditor({
6060

6161
useEffect(() => {
6262
if (isReply) {
63+
console.log('prevLetter', prevLetter);
6364
setLetterRequest({
6465
receiverId: prevLetter[0].memberId,
6566
parentLetterId: Number(searchParams.get('letterId')),
@@ -80,7 +81,7 @@ export default function LetterEditor({
8081
onClick={() => {
8182
if (letterRequest.title.trim() !== '' && letterRequest.content.trim() !== '') {
8283
if (randomMatched) {
83-
const firstReplyRequest = removeProperty(letterRequest, 'matchingId');
84+
const firstReplyRequest = removeProperty(letterRequest, ['matchingId']);
8485
console.log(firstReplyRequest);
8586
handlePostFirstReply(firstReplyRequest);
8687
} else {
@@ -105,7 +106,7 @@ export default function LetterEditor({
105106
)}
106107
</div>
107108
<div className="flex flex-col gap-3 px-6">
108-
<div className="body-b mt-15">TO. {'12EE1'}</div>
109+
<div className="body-b mt-15">TO. 따숨이에게</div>
109110
<input
110111
type="text"
111112
placeholder="제목을 입력해주세요."

0 commit comments

Comments
 (0)