Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
16 commits
Select commit Hold shift + click to select a range
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
95 changes: 90 additions & 5 deletions src/apis/share.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,72 @@
import client from './client';

//공유 게시글 상세 페이지 편지
interface ShareLetter {
id: number;
content: string;
writerZipCode: string;
receiverZipCode: string;
}

// 공유 게시글 목록 조회 타입
export interface SharePost {
writerZipCode: number;
receiverZipCode: number;
content: string;
createdAt: string;
active: boolean;
sharePostId: number;
sharePostContent: string;
letters: ShareLetter[];
}

// 페이징 포함
export interface SharePostResponse {
content: SharePost[];
currentPage: number;
size: number;
totalElements: number;
totalPages: number;
}

// 편지 공유 수락 / 거절
export interface SharePostApproval {
shareProposalId: number;
status: 'APPROVED' | 'REJECTED';
sharePostId: number;
}

// 공유 게시글 목록 조회
export const getSharePostList = async (
page: number = 1,
size: number = 10,
): Promise<SharePostResponse> => {
try {
const response = await client.get('/api/share-posts', {
params: { page, size },
});
console.log(`🌟공유 게시글 목록`, response.data);

return response.data;
} catch (error) {
console.error('❌ 편지 공유 게시글 목록을 조회하던 중 에러가 발생했습니다', error);
throw new Error('편지 공유 게시글 목록 조회 실패');
}
};

// 공유 게시글 상세 조회
export const getSharePostDetail = async (sharePostId: number): Promise<SharePost> => {
try {
const response = await client.get(`/api/share-posts/${sharePostId}`);
console.log(`🔥공유 게시글 상세 데이터`, response.data);
return response.data.data;
} catch (error) {
console.error('❌ 편지 공유 게시글을 상세 조회하던 중 에러가 발생했습니다', error);
throw new Error('편지 공유 게시글 상세 조회 실패');
}
};

// 공유 요청 보내기
export const postShareProposals = async (
letterIds: number[],
requesterId: number,
Expand All @@ -9,13 +76,31 @@ export const postShareProposals = async (
try {
const response = await client.post('/api/share-proposals', {
letterIds: letterIds,
requesterId: requesterId,
recipientId: recipientId,
message: message,
requesterId,
recipientId,
message,
});
if (!response) throw new Error('error while fetching mailbox data');
return response;
return response.data;
} catch (error) {
console.error('❌ 공유 요청 보내기 중 에러가 발생했습니다', error);
throw new Error('공유 요청 실패');
}
};

// 편지 공유 수락 / 거절
export const postShareProposalApproval = async (
shareProposalId: number,
action: 'approve' | 'reject',
): Promise<SharePostApproval> => {
try {
const response = await client.patch(`/api/share-proposal/${shareProposalId}/${action}`);
return response.data;
} catch (error) {
console.error(error);
console.error(
`❌ 편지 공유 ${action === 'approve' ? '수락' : '거절'} 중 에러가 발생했습니다`,
error,
);
throw new Error(`편지 공유 ${action === 'approve' ? '수락' : '거부'} 실패`);
}
};
2 changes: 1 addition & 1 deletion src/pages/Home/components/GoToLetterBoard.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import goToLetterBoard from '@/assets/images/go-to-letter-board.png';

const GoToLetterBoard = () => {
return (
<div className="absolute bottom-48 left-[calc(var(--vh)*36)] z-9 flex w-full">
<div className="absolute bottom-48 left-[calc(var(--vh)*33)] z-9 flex w-full">
<div className="text-left">
<p className="text-gray-60 body-r mb-1 ml-2">게시판</p>
<Link to="/board/letter">
Expand Down
9 changes: 5 additions & 4 deletions src/pages/Home/components/GoToLetterBox.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,17 +2,18 @@ import { Link } from 'react-router';

import goToLetterBoxNewLetters from '@/assets/images/go-to-letter-box-new-letters.png';
import goToLetterBox from '@/assets/images/go-to-letter-box.png';
import { useIncomingLettersStore } from '@/stores/incomingLettersStore';

const GoToLetterBox = () => {
//TODO : hasNewLetters 전역으로 상태 관리하기
const hasNewLetters = true;
const arrivedCount = useIncomingLettersStore((state) => state.arrivedCount);

return (
<div className="absolute bottom-10 left-5 z-9 flex w-fit">
<div className="absolute bottom-15 left-5 z-9 flex">
<div className="text-left">
<p className="text-gray-60 body-r mb-1 ml-2">내 편지함</p>
<Link to="/letter/box">
<img
src={hasNewLetters ? goToLetterBoxNewLetters : goToLetterBox}
src={arrivedCount ? goToLetterBoxNewLetters : goToLetterBox}
alt="go to letter box"
className="w-[206.5px]"
/>
Expand Down
2 changes: 1 addition & 1 deletion src/pages/Home/components/HomeRight.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ const HomeRight = () => {
const { arrivedCount, fetchIncomingLetters } = useIncomingLettersStore();
useEffect(() => {
fetchIncomingLetters();
}, []);
}, [fetchIncomingLetters]);

return (
<div className="flex h-screen w-full max-w-150 min-w-[300px] flex-shrink-0 grow snap-start flex-col items-center overflow-x-hidden pt-5">
Expand Down
8 changes: 1 addition & 7 deletions src/pages/Home/components/NewLetterModal.tsx
Original file line number Diff line number Diff line change
@@ -1,14 +1,8 @@
import { useEffect } from 'react';

import { useIncomingLettersStore } from '@/stores/incomingLettersStore';

//TODO: 내 편지함 상세 조회에서 해당 편지를 조회하면 arrivedCount가 1 감소하도록
const NewLetterModal = () => {
const { arrivedCount, fetchIncomingLetters } = useIncomingLettersStore();

useEffect(() => {
fetchIncomingLetters();
}, []);
const arrivedCount = useIncomingLettersStore((state) => state.arrivedCount);

return (
<p className="text-gray-60 body-b absolute top-30 mb-10 w-fit animate-pulse rounded-full bg-white px-6 py-4">
Expand Down
3 changes: 2 additions & 1 deletion src/pages/Home/components/ShowIncomingLettersModal.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import React, { useEffect } from 'react';
import { useNavigate } from 'react-router';

import ModalBackgroundWrapper from '@/components/ModalBackgroundWrapper';
import ModalOverlay from '@/components/ModalOverlay';
import { useIncomingLettersStore } from '@/stores/incomingLettersStore';
Expand All @@ -22,7 +23,7 @@ const ShowIncomingLettersModal = ({ onClose }: ShowIncomingLettersModalProps) =>

useEffect(() => {
fetchIncomingLetters();
});
}, []);

return (
<ModalOverlay closeOnOutsideClick onClose={onClose}>
Expand Down
50 changes: 33 additions & 17 deletions src/pages/Home/components/ShowShareAccessModal.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
import React from 'react';
import React, { useEffect, useState } from 'react';
import { useNavigate } from 'react-router';

import { getSharePostDetail, getSharePostList } from '@/apis/share';
import { SharePostResponse } from '@/apis/share';
import ModalBackgroundWrapper from '@/components/ModalBackgroundWrapper';
import ModalOverlay from '@/components/ModalOverlay';

Expand All @@ -9,21 +11,35 @@ interface ShowShareAccessModalProps {
onClose: () => void;
}

const DUMMY_SHARE_ACCESS = [
{ id: 1, zip_code: '235EA' },
{ id: 2, zip_code: '711PO' },
{ id: 3, zip_code: '105CF' },
{ id: 4, zip_code: '299EB' },
];

const ShowShareAccessModal = ({ onClose }: ShowShareAccessModalProps) => {
const navigate = useNavigate();

const handleNavigation = (accessId: number) => {
navigate(`/board/letter/${accessId}`, {
state: { isShareLetterPreview: true },
});
const [sharePosts, setSharePosts] = useState<SharePostResponse>();

useEffect(() => {
const fetchPosts = async () => {
try {
const data = await getSharePostList(1, 10);
setSharePosts(data);
} catch (error) {
console.error('❌ 게시글 목록을 불러오는 데 실패했습니다.', error);
}
};

fetchPosts();
}, []);

const handleNavigation = async (sharePostId: number) => {
try {
const postDetail = await getSharePostDetail(sharePostId);
navigate(`/board/letter/${sharePostId}`, {
state: { postDetail, isShareLetterPreview: true },
});
} catch (error) {
console.error('❌ 게시글 상세 페이지로 이동하는 데에 실패했습니다.', error);
}
};

return (
<ModalOverlay closeOnOutsideClick onClose={onClose}>
<div className="flex h-full flex-col items-center justify-center">
Expand All @@ -39,14 +55,14 @@ const ShowShareAccessModal = ({ onClose }: ShowShareAccessModalProps) => {
허락 여부를 체크해주세요!
</p>
</div>
<div className="mt-6 flex w-[251px] flex-col gap-[10px]">
{DUMMY_SHARE_ACCESS.map((access) => (
<div className="mt-6 flex max-h-60 min-h-auto w-[251px] flex-col gap-[10px] overflow-y-scroll [&::-webkit-scrollbar]:hidden">
{sharePosts?.content.map((post) => (
<button
className="text-gray-80 body-m flex h-10 w-full items-center justify-between gap-1 rounded-lg bg-white p-3"
key={access.id}
onClick={() => handleNavigation(access.id)}
key={post.sharePostId}
onClick={() => handleNavigation(post.sharePostId)}
>
<p>{access.zip_code}님의 공유 요청</p>
<p>{post.writerZipCode}님의 공유 요청</p>
</button>
))}
</div>
Expand Down
14 changes: 7 additions & 7 deletions src/pages/LetterBoardDetail/components/Letter.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,20 +2,20 @@ import MemoWrapper from '@/components/MemoWrapper';

interface LetterProps {
letter: {
receiver: string;
receiverZipCode: string;
content: string;
sender: string;
writerZipCode: string;
};
isSender?: boolean;
isWriter?: boolean;
}

const Letter = ({ letter, isSender = false }: LetterProps) => {
const Letter = ({ letter, isWriter = false }: LetterProps) => {
return (
<MemoWrapper isSender={isSender}>
<MemoWrapper isSender={isWriter}>
<div className="flex flex-col gap-2 text-black">
<p className="body-sb">To. {letter.receiver}</p>
<p className="body-sb">To. {letter.receiverZipCode}</p>
<p className="body-r leading-[26px] whitespace-pre-wrap">{letter.content}</p>
<p className="body-m place-self-end">From. {letter.sender}</p>
<p className="body-m place-self-end">From. {letter.writerZipCode}</p>
</div>
</MemoWrapper>
);
Expand Down
Loading