Skip to content

Commit

Permalink
refactor : 리스트 아이템 컴포넌트 수정&리팩토링 (#462)
Browse files Browse the repository at this point in the history
* chore : 메뉴 카테고리 반복되는 코드 map으로 정리

* asset : 누락된 svg 파일 추가

* chore : url 파라미터명을 selectedLetterType로 수정

* feat : 스토어 내부 편지 관련 타입 정의 수정

* chore : 변경된 url 파라미터명으로 코드 갱신

* feat : 카테고리 렌더링 부분을 분리

* chore : 타입명 수정

* refactor : 리스트 아이템 컴포넌트 리팩토링
  • Loading branch information
cmlim0070 authored Jan 3, 2025
1 parent 0d3887e commit b7b3bb9
Show file tree
Hide file tree
Showing 11 changed files with 210 additions and 129 deletions.
4 changes: 4 additions & 0 deletions src/assets/Location.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
3 changes: 3 additions & 0 deletions src/assets/rightArrow.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
24 changes: 24 additions & 0 deletions src/components/Common/LetterListItem/Category.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
type BoxType = 'SEND' | 'RECEIVE';
type LetterType = 'LETTER' | 'REPLY_LETTER';

interface CategoryProps {
boxType: BoxType;
letterType: LetterType;
}

const categoryStyle = 'text-sm';

export const Category = ({ boxType, letterType }: CategoryProps) => {
const classification = `${boxType}-${letterType}`;
switch (classification) {
case 'SEND-LETTER':
return <div className={categoryStyle}>보낸 편지</div>;
case 'SEND-REPLY_LETTER':
return <div className={categoryStyle}>보낸 답장</div>;
case 'RECEIVE-LETTER':
return <div className={categoryStyle}>받은 편지</div>;
case 'RECEIVE-REPLY_LETTER':
return <div className={categoryStyle}>받은 답장</div>;
default:
}
};
13 changes: 2 additions & 11 deletions src/components/StoragePage/LetterDateGroup.tsx
Original file line number Diff line number Diff line change
@@ -1,18 +1,9 @@
import { LetterListItem } from './LetterListItem';
import { DeleteLetterType } from '@/types/letter';

interface Letter {
letterId: number;
title: string;
label: string;
letterType: string;
boxType: string;
createdAt: string;
}
import { DeleteLetterType, StorageLetterDataType } from '@/types/letter';

type LetterDateGroupProps = {
date: string;
letters: Letter[];
letters: StorageLetterDataType[];
checkedItems: DeleteLetterType[];
handleSingleCheck: (
checked: boolean,
Expand Down
144 changes: 99 additions & 45 deletions src/components/StoragePage/LetterListItem.tsx
Original file line number Diff line number Diff line change
@@ -1,20 +1,22 @@
import { Itembox } from '@/components/Common/Itembox/Itembox';
import { BottleLetter } from '@/components/Common/BottleLetter/BottleLetter';
import { useNavigate, useParams, useSearchParams } from 'react-router-dom';
import { DeleteLetterType, storageLetterType } from '@/types/letter';
import { match } from 'ts-pattern';
import {
DeleteLetterType,
StorageKeywordLetter,
StorageLetterDataType,
StorageMapArchivedLetter,
StorageMapReceivedLetter,
StorageMapSentLetter
} from '@/types/letter';
import { Category } from '../Common/LetterListItem/Category';

interface Letter {
letterId: number;
title: string;
label: string;
letterType: string;
boxType: string;
createdAt: string;
}
export const ASSETS = {
LOCATION: '/src/assets/Location.svg',
RIGHT_ARROW: '/src/assets/rightArrow.svg'
} as const;

type LetterListItemProps = {
letter: Letter;
letter: StorageLetterDataType;
checked: boolean;
handleSingleCheck: (
checked: boolean,
Expand All @@ -28,39 +30,91 @@ export const LetterListItem = ({
handleSingleCheck
}: LetterListItemProps) => {
const navigate = useNavigate();

const { letterType } = useParams();
const { selectedLetterType } = useParams();
const [searchParams] = useSearchParams();
const filterType = searchParams.get('filtertype');

const renderCategory = (boxType: string, letterType: string) => {
const condition = `${boxType}-${letterType}`;
switch (condition) {
case 'SEND-LETTER':
return '보낸 편지';
case 'SEND-REPLY_LETTER':
return '보낸 답장';
case 'RECEIVE-LETTER':
return '받은 편지';
case 'RECEIVE-REPLY_LETTER':
return '받은 답장';
const getNavigatePath = (letter: StorageLetterDataType) => {
switch (selectedLetterType) {
case 'keyword':
return `/letter/keyword/${letter.letterType}/${filterType}/${letter.letterId}`;
case 'map':
return `/letter/map/${filterType}/${letter.letterId}`;
case 'bookmark':
return `/letter/map/${filterType}/bookmark/${letter.letterId}`;
default:
throw new Error(
`Unsupported letter type: ${selectedLetterType}`
);
}
};

const getNavigatePath = (letter: Letter) => {
return match<storageLetterType>(letterType as storageLetterType)
.with(
'keyword',
() =>
`/letter/keyword/${letter.letterType}/${filterType}/${letter.letterId}`
)
.with('map', () => `/letter/map/${filterType}/${letter.letterId}`)
.with(
'bookmark',
() => `/letter/map/${filterType}/bookmark/${letter.letterId}`
)
.exhaustive();
const renderListContent = () => {
switch (selectedLetterType) {
case 'keyword': {
const keywordLetter = letter as StorageKeywordLetter;
return (
<>
<Category
boxType={keywordLetter.boxType}
letterType={keywordLetter.letterType}
/>
<h3 className="text-md font-bold">
{keywordLetter.title}
</h3>
</>
);
}
case 'map': {
const mapLetter = letter as
| StorageMapSentLetter
| StorageMapReceivedLetter;

const getUserNicknameLabel = (
letter: StorageMapSentLetter | StorageMapReceivedLetter
) => {
if (letter.type === 'PUBLIC') return '';
// Target
const prefix =
'targetUserNickname' in letter ? 'To.' : 'From.';
const nickname =
'targetUserNickname' in letter
? letter.targetUserNickname
: letter.senderNickname;

return nickname ? `${prefix} ${nickname}` : '';
};
return (
<>
<div className="text-sm">
{getUserNicknameLabel(mapLetter)}
</div>
<div className="flex flex-row gap-1 items-center">
<h3 className="text-md font-bold">
{letter.title}
</h3>
</div>
<div className="flex flex-row gap-1">
<img src="/src/assets/Location.svg"></img>
<div className="text-sm">
서울특별시 동대문구 어디어디
</div>
</div>
</>
);
}
case 'bookmark': {
const bookmarkLetter = letter as StorageMapArchivedLetter;
return (
<>
<h3 className="text-md font-bold">
{bookmarkLetter.title}
</h3>
<div>샘플 위치가 들어갑니다.</div>
</>
);
}
}
};

return (
Expand All @@ -81,15 +135,15 @@ export const LetterListItem = ({
className="flex flex-row gap-4 w-full h-[90px] items-center p-4 rounded-lg bg-sample-gray cursor-pointer"
onClick={() => navigate(getNavigatePath(letter))}
>
<Itembox>
<BottleLetter Letter={letter} />
</Itembox>
<div className="flex flex-col h-full">
<div className="text-[12px] text-gray-500 mt-2">
{renderCategory(letter.boxType, letter.letterType)}
<div className="flex flex-row gap-4 w-full items-center">
<div className="w-[67px] h-[67px] p-2 bg-white rounded-full">
<BottleLetter Letter={letter} />
</div>
<div className="flex flex-col h-full">
{renderListContent()}
</div>
<h3 className="text-sm font-bold">{letter.title}</h3>
</div>
<img src={ASSETS.RIGHT_ARROW} />
</div>
</div>
);
Expand Down
4 changes: 2 additions & 2 deletions src/components/StoragePage/StorageList.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ const ROWS_PER_PAGE = 10;

export const StorageList = () => {
const queryClient = useQueryClient();
const { letterType } = useParams();
const { selectedLetterType } = useParams();
const [searchParams, setSearchParams] = useSearchParams();
const filterType = searchParams.get('filtertype');
const { ref, inView } = useInView();
Expand All @@ -24,7 +24,7 @@ export const StorageList = () => {
const [checkedItems, setCheckedItems] = useState<DeleteLetterType[]>([]);

const getApiEndpoint = () => {
return match<storageLetterType>(letterType as storageLetterType)
return match<storageLetterType>(selectedLetterType as storageLetterType)
.with('keyword', () => `/letters/saved/${filterType}`)
.with('map', () => `/map/${filterType}`)
.with('bookmark', () => '/map/archived')
Expand Down
12 changes: 2 additions & 10 deletions src/hooks/useInfiniteStorageFetch.ts
Original file line number Diff line number Diff line change
@@ -1,15 +1,7 @@
import { useMemo } from 'react';
import { useInfiniteQuery } from '@tanstack/react-query';
import { getLetter } from '@/service/storage/getLetter';

interface Letter {
letterId: number;
title: string;
label: string;
letterType: string;
boxType: string;
createdAt: string;
}
import { StorageLetterDataType } from '@/types/letter';

type useInfiniteStorageFetchParams = {
apiEndpoint: string;
Expand Down Expand Up @@ -65,7 +57,7 @@ export const useInfiniteStorageFetch = ({
}
const allLetters = data.pages.flatMap((page) => page.content);
const grouped = allLetters.reduce(
(acc: { [key: string]: Letter[] }, letter) => {
(acc: { [key: string]: StorageLetterDataType[] }, letter) => {
const date = new Date(letter.createdAt)
.toISOString()
.split('T')[0];
Expand Down
76 changes: 40 additions & 36 deletions src/pages/Storage/StoragePage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,32 +2,44 @@ import React, { useEffect } from 'react';
import { StorageList } from '@/components/StoragePage/StorageList';
import { useNavigate, useParams, useSearchParams } from 'react-router-dom';

type LetterType = 'keyword' | 'map' | 'bookmark';
export const StoragePage = () => {
const navigate = useNavigate();
const { selectedLetterType } = useParams();
const [searchParams, setSearchParams] = useSearchParams();

const getTranslateX = (path: LetterType) => {
const pathIndex =
const storageMenuList = [
{
type: 'keyword',
label: '키워드 편지'
},
{
keyword: 0,
map: 1,
bookmark: 2
}[path] || 0;
type: 'map',
label: '지도 편지'
},
{
type: 'bookmark',
label: '보관함'
}
];

return `${pathIndex * 100}%`;
};
const getTranslateX = (path: string) => {
const pathIndex =
{
keyword: 0,
map: 1,
bookmark: 2
}[path] || 0;

export const StoragePage = () => {
const navigate = useNavigate();
const { letterType } = useParams();
const [searchParams, setSearchParams] = useSearchParams();
return `${pathIndex * 100}%`;
};

const handleNavigate = (type: LetterType) => {
const handleNavigate = (type: string) => {
if (type === 'bookmark') return navigate(`/storage/${type}`);
navigate(`/storage/${type}?filtertype=sent`);
};

// 마운트 될 때 초기 필터타입을 지정합니다
useEffect(() => {
if (letterType === 'bookmark') return;
if (selectedLetterType === 'bookmark') return;
const currentFilter = searchParams.get('filtertype');
if (!currentFilter) {
setSearchParams({ filtertype: 'sent' });
Expand All @@ -41,30 +53,22 @@ export const StoragePage = () => {
<div
className="absolute bottom-0 w-1/3 h-[2px] transition-transform duration-500 ease-in-out bg-sample-blue"
style={{
transform: `translateX(${getTranslateX(letterType as LetterType)})`
transform: `translateX(${getTranslateX(selectedLetterType as string)})`
}}
></div>
<div
className="flex items-center justify-center flex-1 h-full cursor-pointer"
onClick={() => handleNavigate('keyword')}
>
<span>키워드 편지</span>
</div>
<div
className="flex items-center justify-center flex-1 h-full cursor-pointer"
onClick={() => handleNavigate('map')}
>
<span>내 지도 편지</span>
</div>
<div
className="flex items-center justify-center flex-1 h-full cursor-pointer"
onClick={() => handleNavigate('bookmark')}
>
<span>보관함</span>
</div>
{storageMenuList.map((category) => {
return (
<div
key={category.type}
className="flex items-center justify-center flex-1 h-full cursor-pointer"
onClick={() => handleNavigate(category.type)}
>
<span>{category.label}</span>
</div>
);
})}
</div>
</div>

<div className="flex flex-col gap-2 mt-[60px]">
<StorageList />
</div>
Expand Down
3 changes: 1 addition & 2 deletions src/router.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,6 @@ const ProfileSharePage = lazy(
import { tokenStorage } from './service/auth/tokenStorage';
import { AuthProvider } from './AuthProvider';
import { Container } from '@/components/Common/Container/Container';

import { CreateMapLetterPage } from './pages/Map/Create/CreateMapLetterPage';
import { MapSelectItemPage } from './pages/Map/Select/MapSelectItemPage';
import { ErrorBoundary } from './ErrorBoundary';
Expand Down Expand Up @@ -113,7 +112,7 @@ export const router = createBrowserRouter([
element: <MyPage />
},
{
path: 'storage/:letterType',
path: 'storage/:selectedLetterType',
element: <StoragePage />
},
{
Expand Down
Loading

0 comments on commit b7b3bb9

Please sign in to comment.