From bd306ab6f4682364c7096b8e498b7aee88757e92 Mon Sep 17 00:00:00 2001 From: NEWJIN <109906670+newjinlee@users.noreply.github.com> Date: Fri, 2 Aug 2024 15:41:07 +0900 Subject: [PATCH] =?UTF-8?q?FE-83=20=E2=9C=A8=20=ED=94=BC=EB=93=9C=20?= =?UTF-8?q?=ED=8E=98=EC=9D=B4=EC=A7=80=20=EA=B5=AC=ED=98=84=20(#164)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * FE-83 :sparkles: ui 구현 (sm 기준) * FE-83 :sparkles: grid/sort 구분 구현 * FE-83 :lipstick: 더보기버튼 반응형 구현 * FE-83 :sparkles: FAB 버튼 적용 * FE-83 :sparkles: 에피그램 만들기 버튼 구현 --- public/icon/grid-icon.svg | 8 ++ public/icon/sort-icon.svg | 7 ++ src/components/main/FAB.tsx | 2 +- src/pageLayout/Feed/AddEpigramFAB.tsx | 36 +++++++++ src/pageLayout/Feed/EpigramFeed.tsx | 98 +++++++++++++++++++++++ src/pageLayout/Feed/FeedCard.tsx | 61 ++++++++++++++ src/pageLayout/Feed/FeedPageLayout.tsx | 24 ++++++ src/pageLayout/Feed/MoreEpigramButton.tsx | 29 +++++++ src/pages/feed/index.tsx | 7 ++ 9 files changed, 271 insertions(+), 1 deletion(-) create mode 100644 public/icon/grid-icon.svg create mode 100644 public/icon/sort-icon.svg create mode 100644 src/pageLayout/Feed/AddEpigramFAB.tsx create mode 100644 src/pageLayout/Feed/EpigramFeed.tsx create mode 100644 src/pageLayout/Feed/FeedCard.tsx create mode 100644 src/pageLayout/Feed/FeedPageLayout.tsx create mode 100644 src/pageLayout/Feed/MoreEpigramButton.tsx create mode 100644 src/pages/feed/index.tsx diff --git a/public/icon/grid-icon.svg b/public/icon/grid-icon.svg new file mode 100644 index 00000000..af95c880 --- /dev/null +++ b/public/icon/grid-icon.svg @@ -0,0 +1,8 @@ + + + + + + + + diff --git a/public/icon/sort-icon.svg b/public/icon/sort-icon.svg new file mode 100644 index 00000000..d0a92737 --- /dev/null +++ b/public/icon/sort-icon.svg @@ -0,0 +1,7 @@ + + + + + + + diff --git a/src/components/main/FAB.tsx b/src/components/main/FAB.tsx index 740e35d5..2d064dc6 100644 --- a/src/components/main/FAB.tsx +++ b/src/components/main/FAB.tsx @@ -10,7 +10,7 @@ function FAB() { + ); +} + +export default AddEpigramFAB; diff --git a/src/pageLayout/Feed/EpigramFeed.tsx b/src/pageLayout/Feed/EpigramFeed.tsx new file mode 100644 index 00000000..e26a6b79 --- /dev/null +++ b/src/pageLayout/Feed/EpigramFeed.tsx @@ -0,0 +1,98 @@ +import React, { useState, useEffect } from 'react'; +import { useRouter } from 'next/router'; +import useGetRecentEpigrams from '@/hooks/useGetRecentEpigrams'; +import { RecentEpigramType } from '@/schema/recentEpigram'; +import Image from 'next/image'; +import MoreEpigramButton from './MoreEpigramButton'; +import FeedCard from './FeedCard'; +import spinner from '../../../public/spinner.svg'; + +function EpigramFeed() { + const router = useRouter(); + const [epigrams, setEpigrams] = useState([]); + const [cursor, setCursor] = useState(0); + const [limit, setLimit] = useState(6); + const [isLoadingMore, setIsLoadingMore] = useState(false); + const [shouldFetch, setShouldFetch] = useState(true); + const [isSingleColumn, setIsSingleColumn] = useState(false); + + const { data, error, isLoading } = useGetRecentEpigrams({ + cursor, + limit, + enabled: shouldFetch, + }); + + useEffect(() => { + if (data) { + setEpigrams((prevEpigrams) => [...prevEpigrams, ...data.list]); + if (data.list.length > 0) { + setCursor(data.list[data.list.length - 1].id); + } + setIsLoadingMore(false); + setShouldFetch(false); + } + }, [data]); + + const handleEpigramClick = (id: number) => { + router.push(`/epigrams/${id}`); + }; + + const loadMore = () => { + setIsLoadingMore(true); + setLimit(10); + setShouldFetch(true); + }; + + const toggleLayout = () => { + setIsSingleColumn(!isSingleColumn); + }; + + const handleSortKeyPress = (e: React.KeyboardEvent) => { + if (e.key === 'Enter' || e.key === ' ') { + toggleLayout(); + } + }; + + if (isLoading && epigrams.length === 0) return

로딩 중...

; + if (error) return

{error.message}

; + + return ( +
+
+

피드

+
+ {isSingleColumn +
+
+
+ {epigrams.map((epigram: RecentEpigramType) => ( +
handleEpigramClick(epigram.id)} + role='button' + tabIndex={0} + onKeyPress={(e) => { + if (e.key === 'Enter' || e.key === ' ') { + handleEpigramClick(epigram.id); + } + }} + > + +
+ ))} +
+ {isLoadingMore && ( +
+ 로딩중 +
+ )} + {!isLoadingMore && data?.nextCursor !== null && ( +
+ +
+ )} +
+ ); +} + +export default EpigramFeed; diff --git a/src/pageLayout/Feed/FeedCard.tsx b/src/pageLayout/Feed/FeedCard.tsx new file mode 100644 index 00000000..b848e107 --- /dev/null +++ b/src/pageLayout/Feed/FeedCard.tsx @@ -0,0 +1,61 @@ +import React from 'react'; + +interface Tag { + name: string; + id: number; +} + +interface EpigramCardProps { + content: string; + author: string; + tags: Tag[]; + size?: 'sm1' | 'sm2' | 'md' | 'lg'; +} + +const sizeStyles = { + sm1: 'w-[152px] max-h-[154px]', + sm2: 'w-[312px] max-h-[172px]', + md: 'md:w-[294px] md:max-h-[214px]', + lg: 'lg:w-[585px] lg:max-h-[307px]', +}; + +const textSizeStyles = { + sm1: 'text-xs', + sm2: 'text-sm', + md: 'md:text-base', + lg: 'lg:text-2xl', +}; + +const paddingStyles = { + sm1: 'p-4', + sm2: 'p-6', + md: 'md:p-6', + lg: 'lg:p-6', +}; + +function FeedCard({ content, author, tags, size = 'sm1' }: EpigramCardProps) { + return ( +
+
+
{/* Background stripes */} +
+
+
{content}
+
- {author}
+
+
+
+
+ {tags.map((tag) => ( +
+ #{tag.name} +
+ ))} +
+
+ ); +} + +export default FeedCard; diff --git a/src/pageLayout/Feed/FeedPageLayout.tsx b/src/pageLayout/Feed/FeedPageLayout.tsx new file mode 100644 index 00000000..fc331fe3 --- /dev/null +++ b/src/pageLayout/Feed/FeedPageLayout.tsx @@ -0,0 +1,24 @@ +import React from 'react'; +import Header from '@/components/Header/Header'; +import FAB from '@/components/main/FAB'; +import EpigramFeed from './EpigramFeed'; +import AddEpigramFAB from './AddEpigramFAB'; + +function FeedLayout() { + return ( + <> +
{}} /> +
+
+
+ +
+
+
+ + + + ); +} + +export default FeedLayout; diff --git a/src/pageLayout/Feed/MoreEpigramButton.tsx b/src/pageLayout/Feed/MoreEpigramButton.tsx new file mode 100644 index 00000000..2e623e2a --- /dev/null +++ b/src/pageLayout/Feed/MoreEpigramButton.tsx @@ -0,0 +1,29 @@ +import React from 'react'; +import Image from 'next/image'; + +interface LoadMoreButtonProps { + onClick: () => void; +} + +function MoreEpigramButton({ onClick }: LoadMoreButtonProps) { + return ( +
{ + if (e.key === 'Enter' || e.key === ' ') { + onClick(); + } + }} + > +
+ plus icon +
+
에피그램 더보기
+
+ ); +} + +export default MoreEpigramButton; diff --git a/src/pages/feed/index.tsx b/src/pages/feed/index.tsx new file mode 100644 index 00000000..05178305 --- /dev/null +++ b/src/pages/feed/index.tsx @@ -0,0 +1,7 @@ +import FeedLayout from '@/pageLayout/Feed/FeedPageLayout'; + +function FeedPage() { + return ; +} + +export default FeedPage;