Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
23 commits
Select commit Hold shift + click to select a range
dca90d6
feat: 임시저장된 편지 삭제 기능 구현 (#84)
tifsy Mar 7, 2025
70051d3
feat: 롤링페이퍼 배포된 api로 연결 수정 (#85)
AAminha Mar 7, 2025
aeab0e6
feat : 알림 2차 기능 구현 (#81)
wldnjs990 Mar 7, 2025
ae17500
fix: 자잘한 이슈 수정 (#86)
nirii00 Mar 7, 2025
62d67cd
feat : 재사용 가능한 페이지네이션 구현 (#92)
wldnjs990 Mar 7, 2025
266d0e5
feat: 편지 공유 요청 수신 조회 기능 구현 (#90)
tifsy Mar 7, 2025
a0f576b
feat : 편지작성, 랜덤편지, 상세페이지 3차 기능구현 (#94)
wldnjs990 Mar 8, 2025
5b4d4ba
feat: 롤링페이퍼 추가 기능 구현 (#95)
AAminha Mar 9, 2025
f0b5b4f
feat: 임시저장 편지 조회 기능 완성 (#97)
tifsy Mar 9, 2025
4aced28
feat : 토스트 UI 구현 + 알림 페이지 코드 작업 90% 완료 + 실시간 알림, 편지 작성 예외처리에 토스트UI 연결…
wldnjs990 Mar 9, 2025
6917b3b
fix: reissue 문제, 내 편지함 data 최신화 문제 해결 (#100)
nirii00 Mar 9, 2025
a8144d5
feat : 알림 페이지 알림 확인 처리 안되던 현상 수정 + 신고페이지 4차 구현 (#101)
wldnjs990 Mar 9, 2025
58df399
feat: 공유 요청 상세 조회 기능 구현 (#106)
tifsy Mar 10, 2025
74ef07b
fix: 임시저장 편지 작성 페이지 이동 경로 문제 해결 (#109)
tifsy Mar 10, 2025
6059368
feat : 신고 등록 API + ReportModal 수정 (#111)
wldnjs990 Mar 10, 2025
2fb1cc6
fix: QA 반영 (#112)
nirii00 Mar 10, 2025
40c2021
feat : 알림 기능 구현 + QA (#114)
wldnjs990 Mar 10, 2025
f55da9f
feat : 임시저장 데이터 바인딩 및 임시저장 덮어씌우기 작업 완료 (#115)
wldnjs990 Mar 10, 2025
5fd9543
refactor: 2차 QA 반영 (#116)
tifsy Mar 10, 2025
8e38f31
Merge branch 'main' of https://github.com/prgrms-web-devcourse-final-…
wldnjs990 Mar 10, 2025
5d2e9dc
deploy : 코드 정리
wldnjs990 Mar 10, 2025
b33b576
fix: 롤링페이퍼 QA 반영 (#120)
AAminha Mar 10, 2025
b40548e
Merge branch 'develop' of https://github.com/prgrms-web-devcourse-fin…
wldnjs990 Mar 10, 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
2 changes: 1 addition & 1 deletion src/hooks/useServerSentEvents.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -79,7 +79,6 @@ export const useServerSentEvents = () => {

sourceRef.current.onerror = () => {
// 에러 발생시 해당 에러가 45초를 넘어서 발생한 에러인지, 401에러인지 판단할 수 있는게 없어서 그냥 에러 발생하면 reissue 넣는걸로 때움
callReissue();
closeSSE();
recallCountRef.current += 1;
console.log('SSE연결 에러 발생');
Expand All @@ -92,6 +91,7 @@ export const useServerSentEvents = () => {
}
};
} catch (error) {
callReissue();
console.error(error);
}
};
Expand Down
9 changes: 7 additions & 2 deletions src/pages/Admin/components/AddRollingPaperModal.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import { ChangeEvent, FormEvent, useState } from 'react';

import { postNewRollingPaper } from '@/apis/rolling';
import ModalOverlay from '@/components/ModalOverlay';
import { AxiosError } from 'axios';

interface AddRollingPaperModalProps {
currentPage: number | string;
Expand All @@ -22,8 +23,12 @@ export default function AddRollingPaperModal({ currentPage, onClose }: AddRollin
onClose();
queryClient.invalidateQueries({ queryKey: ['admin-rolling-paper', currentPage] });
},
onError: () => {
setError('편지 작성에 실패했어요. 다시 시도해주세요.');
onError: (err: AxiosError<{ code: string; message: string }>) => {
if (err.response?.data.code === 'MOD-003') {
setError('금칙어가 포함되어 있어요. 다시 작성해주세요.');
} else {
setError('편지 작성에 실패했어요. 다시 시도해주세요.');
}
},
});

Expand Down
3 changes: 1 addition & 2 deletions src/pages/Home/components/ShowShareAccessModal.tsx
Original file line number Diff line number Diff line change
@@ -1,11 +1,10 @@
import React, { useEffect, useState } from 'react';
import { useNavigate } from 'react-router';

import { getShareProposalDetail } from '@/apis/share';
import { getShareProposalList } from '@/apis/share';
import { ShareProposal } from '@/apis/share';

import { getShareProposalDetail } from '@/apis/share';

import ModalBackgroundWrapper from '@/components/ModalBackgroundWrapper';
import ModalOverlay from '@/components/ModalOverlay';

Expand Down
20 changes: 10 additions & 10 deletions src/pages/Landing/constants/index.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
export const STYLE_CLASS = [
{
imagePosition: 'left-[calc(50%-200px)]',
mask: 'bottom-26 left-[calc(50%+65px)]',
circle: 'h-41 w-41',
textPosition: '-top-20 left-[calc(50%-30px)] text-right',
imagePosition: 'left-[calc(50%+130px)]',
mask: 'bottom-8 left-[calc(50%)]',
circle: 'h-65 w-65',
textPosition: '-top-14 left-1/2 text-center',
description:
'따뜻한 편지 사례들을 모아 볼 수 있어요.\n응원이 필요한 사람들에게\n단체로 롤링페이퍼를 쓸 수도 있습니다.',
'모르는 사람에게 고민을 털어 놓아 보세요.\n다른 사람에게도 위로, 응원, 축하를 보낼 수 있어요.',
},
{
imagePosition: 'left-[calc(50%-200px)]',
Expand All @@ -15,11 +15,11 @@ export const STYLE_CLASS = [
description: '주고받은 편지 내역을 확인해 보세요.\n보는 것 만으로도 마음이 따뜻해집니다.',
},
{
imagePosition: 'left-[calc(50%+130px)]',
mask: 'bottom-8 left-[calc(50%)]',
circle: 'h-65 w-65',
textPosition: '-top-14 left-1/2 text-center',
imagePosition: 'left-[calc(50%-200px)]',
mask: 'bottom-26 left-[calc(50%+65px)]',
circle: 'h-41 w-41',
textPosition: '-top-20 left-[calc(50%-30px)] text-right',
description:
'모르는 사람에게 고민을 털어 놓아 보세요.\n다른 사람에게도 위로, 응원, 축하를 보낼 수 있어요.',
'따뜻한 편지 사례들을 모아 볼 수 있어요.\n응원이 필요한 사람들에게\n단체로 롤링페이퍼를 쓸 수도 있습니다.',
},
];
38 changes: 3 additions & 35 deletions src/pages/LetterBoardDetail/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ import ReportModal from '@/components/ReportModal';

import Header from './components/Header';
import Letter from './components/Letter';
import { useLocation, useParams } from 'react-router';
import { useParams } from 'react-router';
import useAuthStore from '@/stores/authStore';

const LetterBoardDetailPage = () => {
Expand All @@ -20,8 +20,8 @@ const LetterBoardDetailPage = () => {
const [isWriter, setIsWriter] = useState(false);
const [postDetail, setPostDetail] = useState<SharePost>();
const [activeReportModal, setActiveReportModal] = useState(false);
const location = useLocation();
const sharePostId: string = location.pathname.split('/')[3];
// const location = useLocation();
// const sharePostId: string = location.pathname.split('/')[3];
// const isShareLetterPreview = location.state?.isShareLetterPreview || false;

const { id } = useParams();
Expand Down Expand Up @@ -84,38 +84,6 @@ const LetterBoardDetailPage = () => {
}
}, []);

useEffect(() => {
const fetchPostDetail = async (postId: string) => {
try {
const data = await getSharePostDetail(postId);
setPostDetail(data);
} catch (error) {
console.error('❌ 공유 게시글 상세 조회에 실패했습니다.', error);
}
};

const fetchLikeCounts = async (postId: string) => {
try {
const response = await getSharePostLikeCount(postId);
if (!response) throw new Error('error while fetching like count');
console.log('✅ 편지 좋아요 갯수:', response);
setLikeCount(response.likeCount);
setIsLike(response.liked);
} catch (error) {
console.error('❌ 편지 좋아요 갯수를 가져오는 중 에러가 발생했습니다', error);
throw new Error('편지 좋아요 갯수 가져오기 실패');
}
};

// if (location.state?.postDetail) {
fetchPostDetail(sharePostId);
fetchLikeCounts(sharePostId);
// } else {
// console.warn('postDetail not found in location.state');
// }
// }, [location.state]);
}, []);

return (
<>
{activeReportModal && (
Expand Down
2 changes: 1 addition & 1 deletion src/pages/RollingPaper/components/Comment.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ const Comment = ({ comment, onClick }: CommentProps) => {
return (
<MemoWrapper className="cursor-pointer text-black" onClick={onClick}>
<div className="z-1 h-full w-full">
<p className="caption-r whitespace-pre-wrap">{comment.content}</p>
<p className="caption-r break-all whitespace-pre-wrap">{comment.content}</p>
<p className="caption-m mt-1 place-self-end">From. {comment.zipCode}</p>
</div>
</MemoWrapper>
Expand Down
24 changes: 17 additions & 7 deletions src/pages/RollingPaper/components/CommentDetailModal.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,21 +6,31 @@ interface CommentDetailModalProps {
isWriter: boolean;
onClose: () => void;
onDelete: () => void;
onReport: () => void;
}

const CommentDetailModal = ({ comment, isWriter, onClose, onDelete }: CommentDetailModalProps) => {
const CommentDetailModal = ({
comment,
isWriter,
onClose,
onDelete,
onReport,
}: CommentDetailModalProps) => {
const handleButtonClick = () => {
if (isWriter) onDelete();
else onReport();
};

return (
<ModalOverlay closeOnOutsideClick onClose={onClose}>
<>
{isWriter && (
<button type="button" className="body-b ml-auto text-white" onClick={onDelete}>
삭제하기
</button>
)}
<button type="button" className="body-b ml-auto text-white" onClick={handleButtonClick}>
{isWriter ? '삭제하기' : '신고하기'}
</button>

<MemoWrapper className="mt-1 flex max-h-1/2 w-78 overflow-y-auto px-5 text-black">
<div className="z-1 flex flex-col gap-3">
<p className="body-r leading-[26px] whitespace-pre-wrap">{comment.content}</p>
<p className="body-r leading-[26px] break-all whitespace-pre-wrap">{comment.content}</p>
<p className="body-m place-self-end">From. {comment.zipCode}</p>
</div>
</MemoWrapper>
Expand Down
17 changes: 13 additions & 4 deletions src/pages/RollingPaper/components/WriteCommentButton.tsx
Original file line number Diff line number Diff line change
@@ -1,10 +1,11 @@
import { useMutation, useQueryClient } from '@tanstack/react-query';
import { useState } from 'react';
import { useEffect, useState } from 'react';

import { postRollingPaperComment } from '@/apis/rolling';
import EnvelopeImg from '@/assets/images/closed-letter.png';
import MessageModal from '@/components/MessageModal';
import useAuthStore from '@/stores/authStore';
import { AxiosError } from 'axios';

interface WriteCommentButtonProps {
rollingPaperId: string;
Expand All @@ -25,8 +26,12 @@ const WriteCommentButton = ({ rollingPaperId }: WriteCommentButtonProps) => {
setError(null);
setActiveMessageModal(false);
},
onError: () => {
setError('편지 작성에 실패했어요. 다시 시도해주세요.');
onError: (err: AxiosError<{ code: string; message: string }>) => {
if (err.response?.data.code === 'MOD-003') {
setError('금칙어가 포함되어 있어요. 다시 작성해주세요.');
} else {
setError('편지 작성에 실패했어요. 다시 시도해주세요.');
}
},
});

Expand All @@ -44,6 +49,10 @@ const WriteCommentButton = ({ rollingPaperId }: WriteCommentButtonProps) => {
mutate(newMessage.trim());
};

useEffect(() => {
setError(null);
}, [activeMessageModal]);

return (
<>
{activeMessageModal && (
Expand All @@ -62,7 +71,7 @@ const WriteCommentButton = ({ rollingPaperId }: WriteCommentButtonProps) => {
)}
<button
type="button"
className="sticky bottom-8 z-10 mt-4 -mb-4 self-start overflow-hidden rounded-sm"
className="sticky bottom-8 z-10 mt-auto -mb-4 self-start overflow-hidden rounded-sm"
onClick={() => setActiveMessageModal(true)}
>
<img src={EnvelopeImg} alt="편지지 이미지" className="h-12 w-auto opacity-70" />
Expand Down
15 changes: 14 additions & 1 deletion src/pages/RollingPaper/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import Comment from './components/Comment';
import CommentDetailModal from './components/CommentDetailModal';
import WriteCommentButton from './components/WriteCommentButton';
import useAuthStore from '@/stores/authStore';
import ReportModal from '@/components/ReportModal';

const MESSAGE_SIZE = 10;

Expand All @@ -22,6 +23,7 @@ const RollingPaperPage = () => {
const [activeComment, setActiveComment] = useState<RollingPaperComment | null>(null);
const [activeDetailModal, setActiveDetailModal] = useState(false);
const [activeDeleteModal, setActiveDeleteModal] = useState(false);
const [activeReportModal, setActiveReportModal] = useState(false);
const zipCode = useAuthStore((props) => props.zipCode);
const queryClient = useQueryClient();

Expand Down Expand Up @@ -69,6 +71,17 @@ const RollingPaperPage = () => {
setActiveDetailModal(false);
setActiveDeleteModal(true);
}}
onReport={() => {
setActiveDetailModal(false);
setActiveReportModal(true);
}}
/>
)}
{activeReportModal && (
<ReportModal
reportType="EVENT_COMMENT"
letterId={Number(activeComment?.commentId)}
onClose={() => setActiveReportModal(false)}
/>
)}
{activeDeleteModal && (
Expand All @@ -90,7 +103,7 @@ const RollingPaperPage = () => {
<main className="z-1 flex grow flex-col items-center px-5 pt-20 pb-12">
<PageTitle className="mb-18 max-w-73 text-center">{title}</PageTitle>
<p className="body-sb text-gray-60 mb-2 w-full">등록된 편지 {totalComments}</p>
<section className="w-full">
<section className="mb-4 w-full">
<MasonryInfiniteGrid column={2} align="stretch" gap={16} onRequestAppend={handleLoadMore}>
{isSuccess &&
allComments.map((comment) => (
Expand Down