-
Notifications
You must be signed in to change notification settings - Fork 1
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
FE-83 ✨ 피드 페이지 구현 #165
Merged
Merged
FE-83 ✨ 피드 페이지 구현 #165
Changes from all commits
Commits
File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,36 @@ | ||
import React from 'react'; | ||
import Image from 'next/image'; | ||
import { useRouter } from 'next/router'; | ||
import { Button } from '@/components/ui/button'; | ||
|
||
function AddEpigramFAB() { | ||
const router = useRouter(); | ||
|
||
const handleAddEpigramClick = () => { | ||
router.push('/addEpigram'); | ||
}; | ||
|
||
return ( | ||
<Button | ||
variant='default' | ||
size='lg' | ||
onClick={handleAddEpigramClick} | ||
className='z-10 h-12 lg:h-16 px-3.5 lg:px-5 py-3 lg:py-4 bg-[#2c394d] text-white rounded-[100px] shadow-lg justify-center items-center gap-1 inline-flex fixed bottom-40 right-6 cursor-pointer' | ||
role='button' | ||
aria-label='Add Epigram' | ||
tabIndex={0} | ||
onKeyPress={(e) => { | ||
if (e.key === 'Enter' || e.key === ' ') { | ||
handleAddEpigramClick(); | ||
} | ||
}} | ||
> | ||
<div className='w-6 h-6 relative'> | ||
<Image src='/icon/plus-icon.svg' alt='add icon' layout='fill' objectFit='contain' /> | ||
</div> | ||
<span className='text-sm lg:text-xl font-semibold font-pretendard leading-normal lg:leading-loose'>에피그램 만들기</span> | ||
</Button> | ||
); | ||
} | ||
|
||
export default AddEpigramFAB; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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<RecentEpigramType[]>([]); | ||
const [cursor, setCursor] = useState<number>(0); | ||
const [limit, setLimit] = useState<number>(6); | ||
const [isLoadingMore, setIsLoadingMore] = useState(false); | ||
const [shouldFetch, setShouldFetch] = useState<boolean>(true); | ||
const [isSingleColumn, setIsSingleColumn] = useState<boolean>(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<HTMLDivElement>) => { | ||
if (e.key === 'Enter' || e.key === ' ') { | ||
toggleLayout(); | ||
} | ||
}; | ||
|
||
if (isLoading && epigrams.length === 0) return <p>로딩 중...</p>; | ||
if (error) return <p>{error.message}</p>; | ||
|
||
return ( | ||
<div> | ||
<div className='w-full h-[26px] justify-between items-center inline-flex mt-[32px] lg:mt-[120px]'> | ||
<h1 className='text-[#373737] text-base lg:text-2xl font-semibold font-pretendard leading-relaxed'>피드</h1> | ||
<div className='w-6 h-6 relative block md:hidden' onClick={toggleLayout} onKeyPress={handleSortKeyPress} role='button' tabIndex={0} aria-label='Toggle layout'> | ||
<Image src={isSingleColumn ? '/icon/grid-icon.svg' : '/icon/sort-icon.svg'} alt={isSingleColumn ? 'grid layout' : 'sort layout'} width={24} height={24} className='w-full h-full' /> | ||
</div> | ||
</div> | ||
<div className={`mt-[24px] lg:mt-[40px] mb-[10px] gap-x-2 gap-y-4 md:gap-y-6 lg:gap-y-10 ${isSingleColumn ? 'grid grid-cols-1' : 'grid grid-cols-2'}`}> | ||
{epigrams.map((epigram: RecentEpigramType) => ( | ||
<div | ||
key={epigram.id} | ||
onClick={() => handleEpigramClick(epigram.id)} | ||
role='button' | ||
tabIndex={0} | ||
onKeyPress={(e) => { | ||
if (e.key === 'Enter' || e.key === ' ') { | ||
handleEpigramClick(epigram.id); | ||
} | ||
}} | ||
> | ||
<FeedCard content={epigram.content} author={epigram.author} tags={epigram.tags} size={isSingleColumn ? 'sm2' : 'sm1'} /> | ||
</div> | ||
))} | ||
</div> | ||
{isLoadingMore && ( | ||
<div className='w-full flex items-center justify-center lg:mt-[70px] md:mt-[50px]'> | ||
<Image src={spinner} alt='로딩중' width={50} height={50} /> | ||
</div> | ||
)} | ||
{!isLoadingMore && data?.nextCursor !== null && ( | ||
<div className='mt-10 mb-14 w-full flex justify-center'> | ||
<MoreEpigramButton onClick={loadMore} /> | ||
</div> | ||
)} | ||
</div> | ||
); | ||
} | ||
|
||
export default EpigramFeed; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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 ( | ||
<div className={`relative flex-col justify-start items-end gap-[16px] inline-flex ${sizeStyles[size]} ${sizeStyles.md} ${sizeStyles.lg}`}> | ||
<div | ||
className={`w-full ${paddingStyles[size]} ${paddingStyles.md} ${paddingStyles.lg} bg-white rounded-[14.67px] shadow border border-zinc-100 flex-col justify-start items-start flex relative overflow-hidden`} | ||
> | ||
<div className='absolute inset-0 bg-stripes w-full h-full'></div> {/* Background stripes */} | ||
<div className='relative w-full z-10 flex flex-col justify-start items-start flex-1'> | ||
<div className='self-stretch flex-col justify-start items-start gap-2 flex'> | ||
<div className={`self-stretch ${textSizeStyles[size]} ${textSizeStyles.md} ${textSizeStyles.lg} text-neutral-700 font-normal font-iropkeBatang leading-normal`}>{content}</div> | ||
<div className={`self-stretch ${textSizeStyles[size]} ${textSizeStyles.md} ${textSizeStyles.lg} text-right text-slate-400 font-normal font-iropkeBatang leading-normal`}>- {author}</div> | ||
</div> | ||
</div> | ||
</div> | ||
<div className='justify-start items-start gap-2 inline-flex'> | ||
{tags.map((tag) => ( | ||
<div key={tag.id} className={`text-right ${textSizeStyles[size]} ${textSizeStyles.md} ${textSizeStyles.lg} text-slate-400 font-normal font-iropkeBatang leading-normal`}> | ||
#{tag.name} | ||
</div> | ||
))} | ||
</div> | ||
</div> | ||
); | ||
} | ||
|
||
export default FeedCard; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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 ( | ||
<> | ||
<Header icon='search' isLogo insteadOfLogo='' isProfileIcon isShareIcon={false} isButton={false} textInButton='' disabled={false} onClick={() => {}} /> | ||
<main className='w-full h-auto flex-col justify-start items-center gap-[72px] inline-flex bg-blue-200'> | ||
<div className='w-[312px] md:w-[600px] lg:w-[1200px] h-auto flex-col justify-center items-center gap-14 inline-flex'> | ||
<div className='self-stretch'> | ||
<EpigramFeed /> | ||
</div> | ||
</div> | ||
</main> | ||
<AddEpigramFAB /> | ||
<FAB /> | ||
</> | ||
); | ||
} | ||
|
||
export default FeedLayout; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,29 @@ | ||
import React from 'react'; | ||
import Image from 'next/image'; | ||
|
||
interface LoadMoreButtonProps { | ||
onClick: () => void; | ||
} | ||
|
||
function MoreEpigramButton({ onClick }: LoadMoreButtonProps) { | ||
return ( | ||
<div | ||
onClick={onClick} | ||
className='h-12 lg:h-14 px-[18px] lg:px-10 py-3 bg-[#f5f7fa] rounded-[100px] border border-[#cfdbea] justify-center items-center gap-1 lg:gap-2 inline-flex cursor-pointer' | ||
role='button' | ||
tabIndex={0} | ||
onKeyPress={(e) => { | ||
if (e.key === 'Enter' || e.key === ' ') { | ||
onClick(); | ||
} | ||
}} | ||
> | ||
<div className='w-6 h-6 relative'> | ||
<Image src='/icon/plus-icon.svg' alt='plus icon' layout='fill' objectFit='contain' /> | ||
</div> | ||
<div className='text-blue-400 text-sm lg:text-xl font-normal lg:font-medium font-pretendard leading-normal lg:leading-loose'>에피그램 더보기</div> | ||
</div> | ||
); | ||
} | ||
|
||
export default MoreEpigramButton; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,7 @@ | ||
import FeedLayout from '@/pageLayout/Feed/FeedPageLayout'; | ||
|
||
function FeedPage() { | ||
return <FeedLayout />; | ||
} | ||
|
||
export default FeedPage; |
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
❓Question
버튼 태그에 role='button' 속성을 넣으신 이유가 궁금합니다!
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
처음엔 div였어서 넣었는데 button ui 사용하려고 button으로 바꿔서 남아있네요