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;