Skip to content
Merged
Show file tree
Hide file tree
Changes from 13 commits
Commits
Show all changes
14 commits
Select commit Hold shift + click to select a range
7e6ea74
feat:편지 작성 페이지 전역변수 matchingId값 추가 + removeProperty 유틸함수 추가 + 답장 api …
wldnjs990 Feb 28, 2025
20fca5f
feat:랜덤편지 승인 api 연결(405오류 뜸) + 임시매칭상태, 쿨타임 상태 확인 api 생성(연결안함) + 랜덤편지에…
wldnjs990 Feb 28, 2025
ca17d65
feat:랜덤편지 매칭 여부 api 연결
wldnjs990 Mar 1, 2025
45c3331
refactor:매칭 제한시간, 쿨타임 타이머 리팩토링
wldnjs990 Mar 1, 2025
de81b51
refactor:편지작성, 편지상세 페이지 api코드 수정
wldnjs990 Mar 2, 2025
de15a93
Merge branch '49-feat-write/randomLetter/letterDetail-nd' of https://…
wldnjs990 Mar 2, 2025
aa02f24
Merge branch 'develop' of https://github.com/prgrms-web-devcourse-fin…
wldnjs990 Mar 3, 2025
e6385d8
refactor : api response값 호출 컴포넌트에서 후처리 하도록 코드 수정 + 랜덤편지, 편지작성 배포api 연…
wldnjs990 Mar 3, 2025
4cdad07
feat : 랜덤편지 매칭취소시 뒤로가기 처리(임시)
wldnjs990 Mar 3, 2025
a78f3ee
feat : 랜덤편지 편지 승인시 매칭 후 받는 랜덤 편지 테이블 유무 확인 response의 data값 matchedLet…
wldnjs990 Mar 3, 2025
bf7f2f5
Merge branch 'develop' of https://github.com/prgrms-web-devcourse-fin…
wldnjs990 Mar 4, 2025
2abdb8c
feat:최초편지, 최초답장 구현 완료(디테일 처리 필요)
wldnjs990 Mar 4, 2025
0f00424
chore : 더미 데이터 삭제
wldnjs990 Mar 4, 2025
d0a3845
chore : 코드리뷰 수정사항 반영
wldnjs990 Mar 4, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 0 additions & 1 deletion src/apis/client.ts
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,6 @@ const processQueue = (error: unknown, token: string | null = null) => {
client.interceptors.request.use(
(config) => {
const accessToken = useAuthStore.getState().accessToken;

if (config.url !== '/auth/reissue' && accessToken) {
config.headers.Authorization = `Bearer ${accessToken}`;
}
Expand Down
13 changes: 4 additions & 9 deletions src/apis/letterDetail.ts
Original file line number Diff line number Diff line change
@@ -1,28 +1,23 @@
import client from './client';

const getLetter = async (
letterId: string,
setLetterState: React.Dispatch<React.SetStateAction<LetterDetail | null>>,
callBack?: () => void,
) => {
const getLetter = async (letterId: string) => {
try {
const res = await client.get(`/api/letters/${letterId}`);
if (!res) throw new Error('편지 데이터를 가져오는 도중 에러가 발생했습니다.');
setLetterState(res.data.data);
if (callBack) callBack();
console.log(res);
return res;
} catch (error) {
console.error(error);
}
};

const deleteLetter = async (letterId: string, callBack?: () => void) => {
const deleteLetter = async (letterId: string) => {
try {
console.log(`/api/letters/${letterId}`);
const res = await client.delete(`/api/letters/${letterId}`);
if (!res) throw new Error('편지 삭제 요청 도중 에러가 발생했습니다.');
if (callBack) callBack();
console.log(res);
return res;
} catch (error) {
console.error(error);
}
Expand Down
67 changes: 60 additions & 7 deletions src/apis/randomLetter.ts
Original file line number Diff line number Diff line change
@@ -1,17 +1,70 @@
import client from './client';

const getRandomLetters = async (
setRandomLettersState: React.Dispatch<React.SetStateAction<RandomLetters[]>>,
category: string | null,
) => {
const getRandomLetters = async (category: string | null) => {
try {
const res = await client.get(`/api/random/${category}`);
const res = await client.get(`/api/random-letters/${category}`);
if (!res) throw new Error('랜덤 편지 데이터를 가져오는 도중 에러가 발생했습니다.');
setRandomLettersState(res.data.data);
console.log(res);
return res;
} catch (error) {
console.error(error);
}
};

export { getRandomLetters };
const postRandomLettersApprove = async (approveRequest: ApproveRequest, callBack?: () => void) => {
try {
console.log('엔드포인트 : /api/random-letters/approve');
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

테스트용 console.log는 나중에 한 번에 삭제하거나 해야겠네용

console.log('request', approveRequest);
const res = await client.post('/api/random-letters/approve', approveRequest);
if (!res) throw new Error('랜덤편지 매칭수락 도중 에러가 발생했습니다.');
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

!res 로는 어떤 에러인지 알기가 어려우니 res.status 로 확인하는 건 어떨까요?

if (res.status !== 200) { throw new Error('랜덤편지 매칭수락 도중 에러가 발생했습니다.'); }

이런 식으로요!

if (callBack) callBack();
return res;
} catch (error) {
console.error(error);
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

throw error; 까지 처리해주시면 좋을 것 같습니다!

}
};

const getRandomLetterMatched = async (callBack?: () => void) => {
try {
const res = await client.post('/api/random-letters/valid-table');
if (!res)
throw new Error('랜덤 편지 최종 매칭 시간 검증 데이터를 가자오는 도중 에러가 발생했습니다.');
if (callBack) callBack();
console.log(res);
return res;
} catch (error) {
console.error(error);
}
};

const getRandomLetterCoolTime = async (callBack?: () => void) => {
try {
const res = await client.post('/api/random-letters/valid');
if (!res)
throw new Error('랜덤 편지 최종 매칭 시간 검증 데이터를 가자오는 도중 에러가 발생했습니다.');
if (callBack) callBack();
console.log(res);
return res;
} catch (error) {
console.error(error);
}
};

const deleteRandomLetterMatching = async () => {
try {
const res = await client.delete('/api/random-letters/matching/cancel');
if (!res) throw new Error('매칭 취소 도중 에러가 발생했습니다.');
console.log(res);
return res;
} catch (error) {
console.log(error);
}
};

export {
getRandomLetters,
postRandomLettersApprove,
getRandomLetterCoolTime,
getRandomLetterMatched,
deleteRandomLetterMatching,
};
29 changes: 19 additions & 10 deletions src/apis/write.ts
Original file line number Diff line number Diff line change
@@ -1,28 +1,37 @@
// import { AxiosResponse } from 'axios';
import client from './client';

const postLetter = async (data: LetterRequest, callBack?: () => void) => {
const postLetter = async (data: LetterRequest) => {
try {
const res = await client.post('/api/letters', data);
if (callBack) callBack();
if (!res) throw new Error('편지 전송과정중에서 오류가 발생했습니다.');
console.log(`api 주소 : /api/letters, 전송타입 : post`);
return res;
} catch (error) {
console.error(error);
}
};

const postFirstReply = async (data: FirstReplyRequest) => {
try {
const res = await client.post('/api/random-letters/matching', data);
if (!res) throw new Error('최초 답장 전송과정중에서 오류가 발생했습니다.');
console.log(`api 주소 : /api/random-letters/matching, 전송타입 : post`);
console.log(res);
return res;
} catch (error) {
console.error(error);
}
};

const getPrevLetter = async (
letterId: string,
setPrevLetterState: React.Dispatch<React.SetStateAction<PrevLetter[]>>,
callBack?: () => void,
) => {
const getPrevLetter = async (letterId: string) => {
try {
const res = await client.get(`/api/letters/${letterId}/previous`);
setPrevLetterState(res.data.data);
if (callBack) callBack();
console.log(res);
return res;
} catch (error) {
console.error(error);
}
};

export { postLetter, getPrevLetter };
export { postLetter, postFirstReply, getPrevLetter };
4 changes: 2 additions & 2 deletions src/components/ResultLetter.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,13 +9,13 @@ export default function ResultLetter({
}: {
categoryName: Category;
title: string;
zipCode?: string;
zipCode: string;
}) {
const date = new Date();
const today = `${date.getFullYear()}년 ${date.getMonth() + 1}월 ${date.getDate()}일`;

return (
<LetterWrapper>
<LetterWrapper className="min-w-[300px]">
<div className="flex w-full flex-col gap-[35px]">
<div className="flex justify-between gap-3">
<div className="flex flex-col gap-2.5">
Expand Down
7 changes: 5 additions & 2 deletions src/layouts/Header.tsx
Original file line number Diff line number Diff line change
@@ -1,13 +1,16 @@
import { Link } from 'react-router';
import { Link, useNavigate } from 'react-router';

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

const Header = () => {
// TODO: 뒤로 가기 버튼이 보이는 조건 추가
// TODO: 스크롤 발생 시, 어떻게 보여져야 하는지
const navigate = useNavigate();
return (
<header className="fixed top-0 z-40 flex h-16 w-full max-w-150 items-center justify-between p-5">
<ArrowLeftIcon className="h-6 w-6 text-white" />
<button onClick={() => navigate(-1)}>
<ArrowLeftIcon className="h-6 w-6 text-white" />
</button>
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

저도 늘 까먹지만 aria-label 붙여주시면 좋을 것 같습니다!

<div className="flex items-center gap-3">
<Link to="/mypage/notifications">
<AlarmIcon className="h-6 w-6 text-white" />
Expand Down
28 changes: 25 additions & 3 deletions src/pages/LetterDetail/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -39,16 +39,38 @@ const LetterDetailPage = () => {
}
setDegreeModalOpen(false);
};

const handleDeleteLetter = async (letterId: string) => {
const res = await deleteLetter(letterId);
if (res?.status === 200) {
navigate(-1);
} else {
alert('편지 삭제 도중 오류 발생(임시)');
}
};

useEffect(() => {
document.body.addEventListener('click', handleOutsideClick);

const handleGetLetter = async (letterId: string) => {
const res = await getLetter(letterId);
if (res?.status === 200) {
setLetterDetail(res.data.data);
} else {
alert(
'에러가 발생했거나 존재하지 않거나 따숨님의 편지가 아니에요(임시) - 이거 에러코드 따른 처리 달리해야할듯',
);
navigate(-1);
}
};
if (params.id) {
getLetter(params.id, setLetterDetail);
handleGetLetter(params.id);
}

return () => {
document.body.removeEventListener('click', handleOutsideClick);
};
}, [params.id]);
}, [params.id, navigate]);
return (
<>
{reportModalOpen && <ReportModal onClose={() => setReportModalOpen(false)} />}
Expand Down Expand Up @@ -137,7 +159,7 @@ const LetterDetailPage = () => {
setDeleteModalOpen(false);
}}
onConfirm={() => {
if (params.id) deleteLetter(params.id);
if (params.id) handleDeleteLetter(params.id);
navigate(-1);
}}
/>
Expand Down
67 changes: 26 additions & 41 deletions src/pages/RandomLetters/components/CoolTime.tsx
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

확실히 코드가 짧아졌네요! 아주 좋습니다 💃🏻🕺🏻

Original file line number Diff line number Diff line change
Expand Up @@ -3,17 +3,22 @@ import { useNavigate } from 'react-router';

import LetterWrapper from '@/components/LetterWrapper';
import { formatNumber } from '@/utils/formatNumber';
import { timeFormatter } from '@/utils/timeFormatter';

// import letterPink from '@/assets/images/letter-pink.png';

export default function CoolTime({
setCoolTime,
setIsCoolTime,
coolTime,
}: {
setCoolTime: React.Dispatch<React.SetStateAction<boolean>>;
setIsCoolTime: React.Dispatch<React.SetStateAction<boolean>>;
coolTime: CoolTime;
}) {
const navigate = useNavigate();

const TIME_STAMP = '2025-02-26T22:13:25.262045608';
const TIME_STAMP = coolTime?.lastMatchedAt
? coolTime.lastMatchedAt
: '2025-03-01T21:15:25.262045608';

const COMPLETED_DATE = new Date(TIME_STAMP);

Expand All @@ -24,56 +29,36 @@ export default function CoolTime({

const endTime = END_DATE.getTime() - NOW_DATE.getTime();

const [endTimes, setEndTimes] = useState({
hours: Math.floor((endTime / (1000 * 60 * 60)) % 24),
minutes: Math.floor((endTime / (1000 * 60)) % 60),
seconds: Math.floor((endTime / 1000) % 60),
});
const [endTimeSeconds, setEndTimeSeconds] = useState(endTime / 1000);

const formatedEndTime = timeFormatter(endTimeSeconds);

useEffect(() => {
if (endTimes.hours < 0 || endTimes.minutes < 0 || endTimes.seconds < 0) {
setEndTimes({ hours: 0, minutes: 0, seconds: 0 });
}
if (endTimes.hours === 0 && endTimes.minutes === 0 && endTimes.seconds === 0) {
setCoolTime(false);
return;
}
const endTimeFlow = setInterval(() => {
setEndTimes((currentTime) => {
if (currentTime.seconds > 0) {
return { ...currentTime, seconds: currentTime.seconds - 1 };
} //
else {
if (currentTime.minutes > 0) {
return { ...currentTime, minutes: currentTime.minutes - 1, seconds: 59 };
} //
else {
if (currentTime.hours > 0) {
return { hours: currentTime.hours - 1, minutes: 59, seconds: 59 };
} //
else {
setCoolTime(false);
return { ...currentTime };
}
}
}
});
if (endTimes.hours === 0 && endTimes.minutes === 0 && endTimes.seconds === 0) {
clearInterval(endTimeFlow);
const endTargetTime = Date.now() + endTime;

const count = setInterval(() => {
const now = Date.now();
const newEndTimeSeconds = Math.max(0, Math.floor((endTargetTime - now) / 1000));

if (endTimeSeconds <= 0) {
setIsCoolTime(false);
}

setEndTimeSeconds(newEndTimeSeconds);
}, 1000);

return () => {
clearInterval(endTimeFlow);
clearInterval(count);
};
}, [endTimes, setCoolTime]);
});

return (
<div className="mt-20 flex flex-col items-center justify-center">
<div className="body-m flex flex-col items-center justify-center">
<p className="text-gray-60">랜덤 편지 활성화 까지</p>
<p className="text-gray-80">
{formatNumber(endTimes.hours)} : {formatNumber(endTimes.minutes)} :{' '}
{formatNumber(endTimes.seconds)}
{formatNumber(formatedEndTime.hours)} : {formatNumber(formatedEndTime.minutes)} :{' '}
{formatNumber(formatedEndTime.seconds)}
</p>
<div className="mt-2 w-75">
<LetterWrapper>
Expand Down
Loading