diff --git a/package.json b/package.json index 7323da4..9514795 100644 --- a/package.json +++ b/package.json @@ -21,6 +21,7 @@ "gsap": "^3.12.7", "react": "^18.3.1", "react-dom": "^18.3.1", + "react-intersection-observer": "^9.15.1", "react-router": "^7.1.5", "swiper": "^11.2.4", "tailwind-merge": "^3.0.1", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index c0758e6..59ed6ed 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -41,6 +41,9 @@ importers: react-dom: specifier: ^18.3.1 version: 18.3.1(react@18.3.1) + react-intersection-observer: + specifier: ^9.15.1 + version: 9.15.1(react-dom@18.3.1(react@18.3.1))(react@18.3.1) react-router: specifier: ^7.1.5 version: 7.1.5(react-dom@18.3.1(react@18.3.1))(react@18.3.1) @@ -2011,6 +2014,15 @@ packages: peerDependencies: react: ^18.3.1 + react-intersection-observer@9.15.1: + resolution: {integrity: sha512-vGrqYEVWXfH+AGu241uzfUpNK4HAdhCkSAyFdkMb9VWWXs6mxzBLpWCxEy9YcnDNY2g9eO6z7qUtTBdA9hc8pA==} + peerDependencies: + react: ^17.0.0 || ^18.0.0 || ^19.0.0 + react-dom: ^17.0.0 || ^18.0.0 || ^19.0.0 + peerDependenciesMeta: + react-dom: + optional: true + react-is@16.13.1: resolution: {integrity: sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==} @@ -4255,6 +4267,12 @@ snapshots: react: 18.3.1 scheduler: 0.23.2 + react-intersection-observer@9.15.1(react-dom@18.3.1(react@18.3.1))(react@18.3.1): + dependencies: + react: 18.3.1 + optionalDependencies: + react-dom: 18.3.1(react@18.3.1) + react-is@16.13.1: {} react-is@19.0.0: {} diff --git a/src/apis/mailBox.ts b/src/apis/mailBox.ts index 7c8b120..7990b34 100644 --- a/src/apis/mailBox.ts +++ b/src/apis/mailBox.ts @@ -10,9 +10,11 @@ export const getMailbox = async () => { } }; -export const getMailboxDetail = async (id: number) => { +export const getMailboxDetail = async (id: number, pageParam: number) => { try { - const response = await client.get(`/api/mailbox/${id}`); + const response = await client.get(`/api/mailbox/${id}?page=${pageParam}&size=20`); + // const response = await client.get(`/api/mailbox/${id}`); + if (!response) throw new Error('error while fetching mailbox detail data'); return response.data; } catch (error) { diff --git a/src/components/LetterWrapper.tsx b/src/components/LetterWrapper.tsx index 365a8b2..7373ea2 100644 --- a/src/components/LetterWrapper.tsx +++ b/src/components/LetterWrapper.tsx @@ -1,3 +1,4 @@ +import { forwardRef } from 'react'; import { twMerge } from 'tailwind-merge'; interface LetterWrapperProps { @@ -7,20 +8,23 @@ interface LetterWrapperProps { onClick?: (e: React.MouseEvent) => void; } -const LetterWrapper = ({ isSender = false, className, children, onClick }: LetterWrapperProps) => { - return ( -
-
{children}
-
-
- ); -}; +const LetterWrapper = forwardRef( + ({ isSender = false, className, children, onClick }, ref) => { + return ( +
+
{children}
+
+
+ ); + }, +); export default LetterWrapper; diff --git a/src/pages/LetterBox/components/LetterBoxItem.tsx b/src/pages/LetterBox/components/LetterBoxItem.tsx index 8f7d13e..f493ffb 100644 --- a/src/pages/LetterBox/components/LetterBoxItem.tsx +++ b/src/pages/LetterBox/components/LetterBoxItem.tsx @@ -30,14 +30,15 @@ const LetterBoxItem = ({ className="flex h-fit w-fit flex-col items-center" onClick={() => handleClickItem(boxId)} > -
+

{zipCode} @@ -48,7 +49,7 @@ const LetterBoxItem = ({

{letterCount}통

diff --git a/src/pages/LetterBox/index.tsx b/src/pages/LetterBox/index.tsx index a7aa199..892cb3e 100644 --- a/src/pages/LetterBox/index.tsx +++ b/src/pages/LetterBox/index.tsx @@ -1,4 +1,5 @@ -import { useEffect, useState } from 'react'; +import { useQuery } from '@tanstack/react-query'; +import { useNavigate } from 'react-router'; import { getMailbox } from '@/apis/mailBox'; import DoorImg from '@/assets/images/door.png'; @@ -13,25 +14,34 @@ interface LetterBoxData { oppositeZipCode: string; active: boolean; oppositeRead: boolean; - // totalLetters: number; + letterCount: number; } + +const fetchMailLists = async () => { + const response = await getMailbox(); + if (!response) throw new Error(); + const data: LetterBoxData[] = response.data; + // 정렬? + return data; +}; + const LetterBoxPage = () => { - const [letterBox, setLetterBox] = useState([]); + const { + data: letterBox = [], + isLoading, + isError, + } = useQuery({ + queryKey: ['mailbox'], + queryFn: fetchMailLists, + staleTime: 1000 * 60 * 5, + gcTime: 1000 * 60 * 10, + }); + + const navigate = useNavigate(); - const fetchMailLists = async () => { - try { - const response = await getMailbox(); - if (!response) throw new Error(); - const data: LetterBoxData[] = response.data; - // 정렬? - setLetterBox(data); - } catch (error) { - console.error(error); - } - }; - useEffect(() => { - fetchMailLists(); - }, []); + if (isError) { + navigate('/NotFound'); + } return (
@@ -42,14 +52,16 @@ const LetterBoxPage = () => {

- {letterBox.length > 0 ? ( + {isLoading ? ( +

로딩중..

+ ) : letterBox.length > 0 ? ( chunkBox( letterBox.map((data: LetterBoxData, index) => ( diff --git a/src/pages/LetterBoxDetail/components/LetterPreview.tsx b/src/pages/LetterBoxDetail/components/LetterPreview.tsx index 4737242..98967fc 100644 --- a/src/pages/LetterBoxDetail/components/LetterPreview.tsx +++ b/src/pages/LetterBoxDetail/components/LetterPreview.tsx @@ -1,6 +1,8 @@ +import { forwardRef } from 'react'; import { useNavigate } from 'react-router'; import LetterWrapper from '@/components/LetterWrapper'; +import formatDate from '@/utils/formatDate'; interface LetterPreviewProps { id: number; @@ -13,18 +15,18 @@ interface LetterPreviewProps { isClosed: boolean; zipCode: string; } -const LetterPreview = ({ - id, - date, - title, - isSend, - checked, - isShareMode = false, - onToggle, - isClosed, - zipCode, -}: LetterPreviewProps) => { - // 차단된 편지인경우 편지 보내기 disable +const LetterPreview = forwardRef((props, ref) => { + const { + id, + date, + title, + isSend, + checked, + isShareMode = false, + onToggle, + isClosed, + zipCode, + } = props; const navigate = useNavigate(); const handleItemClick = (id: number) => { @@ -39,9 +41,9 @@ const LetterPreview = ({ if (isShareMode) return ( - +
-

{date}

+

{formatDate(date)}

- {mailLists.map((letter) => ( - toggleSelected(letter.letterId)} - zipCode={userInfo.zipCode} - /> - ))} + {isLoading ? ( + //TODO: skeleton +
Loading
+ ) : ( + mailLists.map((letter, index) => ( + toggleSelected(letter.letterId)} + zipCode={userInfo.zipCode} + ref={index === mailLists.length - 1 ? ref : null} + /> + )) + )}
- {!isShareMode && !userInfo.isClosed && ( + {!isShareMode && !userInfo.isClosed && !isLoading && (