Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
56 commits
Select commit Hold shift + click to select a range
dbec029
[feat] ๊ธ€์“ฐ๊ธฐ ๊ธฐ๋Šฅ
EunbinJung Oct 10, 2025
64d9a8e
[feat] ํฌ์ŠคํŠธ ์ž‘์„ฑ ๊ธฐ๋Šฅ
EunbinJung Oct 10, 2025
0692f9d
Merge branch 'dev' into feat/write#19
EunbinJung Oct 10, 2025
c63aa72
Feat/communityscroll#23 (#114)
EunbinJung Oct 10, 2025
b381dc0
[feat] ๊ธ€์“ฐ๊ธฐ ๊ธฐ๋Šฅ
EunbinJung Oct 10, 2025
b1edcc9
์นดํ…Œ๊ณ ๋ฆฌํ•„์ˆ˜
EunbinJung Oct 10, 2025
5cf0cc4
[feat] ํฌ์ŠคํŠธ ๋ฌดํ•œ์Šคํฌ๋กค + ๊ธ€์“ฐ๊ธฐ๊ธฐ๋Šฅ ์ด๋ฏธ์ง€์ถ”๊ฐ€
EunbinJung Oct 10, 2025
37e6b64
[feat] ์ด๋ฏธ์ง€ ์Šค์™€์ดํผ
EunbinJung Oct 11, 2025
0df3876
[feat] ํ”„๋กœํ•„ ์‘ค๋ฆฌ ์ด๋ฏธ์ง€
EunbinJung Oct 11, 2025
c03685b
[feat] ๋Œ“๊ธ€ ๋ˆ„๋ฅด๋ฉด ๋Œ“๊ธ€ ์„น์…˜์œผ๋กœ ๊ฐ€๊ธฐ
EunbinJung Oct 11, 2025
0b9ff6a
[feat] ์ข‹์•„์š”๊ธฐ๋Šฅ(์•„์ง ์ข‹์•„์š”๋ฐ›์•„์˜ค๋Š”๊ฑด ๋ชปํ•จ apiํ•„์š”)
EunbinJung Oct 11, 2025
06f4218
[feat] ๊ฒŒ์‹œ๋ฌผ ์ˆ˜์ •
EunbinJung Oct 11, 2025
41bfd9f
[feat] ๊ธ€ ์ˆ˜์ •
EunbinJung Oct 12, 2025
a9013e6
[refactor] ์ฝ”๋“œ ์กฐ๊ธˆ์ •๋ฆฌ
EunbinJung Oct 12, 2025
7ca7f8b
[feat] ์ž‘์„ฑ์ž๋ณธ์ธ๋งŒ ๊ธ€์ˆ˜์ •์‚ญ์ œ
EunbinJung Oct 12, 2025
d555f39
[feat]๊ธ€ ์‚ญ์ œ๊ธฐ๋Šฅ
EunbinJung Oct 12, 2025
9439bfc
[feat] ์นตํ…Œ์ผํƒœ๊ทธ
EunbinJung Oct 12, 2025
5b72ae4
[fix]์นตํ…Œ์ผ, ์‰์–ด ๊ธฐ๋Šฅ
EunbinJung Oct 12, 2025
5801561
์ˆ˜ ๋ผ์šฐํ„ฐ, ๋น„๋กœ๊ทธ์ธ์ฒ˜๋ฆฌ
EunbinJung Oct 12, 2025
fc5d719
ํƒ€์ž… ์ˆ˜์ •
EunbinJung Oct 12, 2025
18a7eeb
ํƒ€์ž… ์ˆ˜์ •
EunbinJung Oct 12, 2025
9b385db
ํƒ€์ž…์ˆ˜์ •
EunbinJung Oct 12, 2025
379e6e2
ํƒ€์ž…์ˆ˜์ •
EunbinJung Oct 12, 2025
f5a3870
ํƒ€์ž…์ˆ˜์ •
EunbinJung Oct 12, 2025
f1dd851
์˜ค๋ฅ˜์ˆ˜์ •
EunbinJung Oct 12, 2025
1975647
์˜ค๋ฅ˜์ˆ˜์ •
EunbinJung Oct 12, 2025
8beb817
์˜ค๋ฅ˜์ˆ˜์ •
EunbinJung Oct 12, 2025
f811379
์˜ค๋ฅ˜์ˆ˜์ •
EunbinJung Oct 12, 2025
fabb821
์˜ค๋ฅ˜์ˆ˜์ •
EunbinJung Oct 12, 2025
e3f356b
Merge branch 'dev' into feat/write#19
EunbinJung Oct 12, 2025
1cba59c
์ถฉ๋Œํ•ด๊ฒฐ
EunbinJung Oct 12, 2025
2a783d6
์˜ค๋ฅ˜ ์ˆ˜์ •
EunbinJung Oct 12, 2025
641b8e8
์˜ค๋ฅ˜ ์ˆ˜์ •
EunbinJung Oct 12, 2025
d9ffb6f
์˜ค๋ฅ˜ ์ˆ˜์ •
EunbinJung Oct 12, 2025
c32ae58
[fix] ์ด๋ฏธ์ง€ ์นด์šดํŠธ, 10๊ฐœ ์ œํ•œ
EunbinJung Oct 12, 2025
74462d0
[fix] ๊ธ€์“ฐ๊ธฐ placeholder
EunbinJung Oct 12, 2025
03b3ab9
[fix] ๊ณต์œ  url ์ˆ˜์ •
EunbinJung Oct 12, 2025
01575c1
[fix] ํ”„๋กœํ•„๋ฐฐ๊ฒฝ ์ง€์šฐ๊ธฐ
EunbinJung Oct 12, 2025
aa5d0ef
[fix] ํ”Œ๋กœํŒ…ํƒญ ๋ฏธ๋””์–ด์ฟผ๋ฆฌ ์ˆ˜์ •
EunbinJung Oct 12, 2025
e59ce26
[fix] ์ˆ˜์ • ๋ชจ๋‹ฌ
EunbinJung Oct 12, 2025
d8bae3c
๋Œ“๊ธ€์‹ค์‹œ๊ฐ„๋ฐ˜์˜ ์‹œ๋„
EunbinJung Oct 12, 2025
18647b5
์ˆ˜์ •
EunbinJung Oct 12, 2025
b51e65b
Merge branch 'dev' into feat/write#19
EunbinJung Oct 12, 2025
0ce9b6d
[fix] ์ˆ˜์ •๋ชจ๋‹ฌ ๋กœ์ง ์ˆ˜์ •
EunbinJung Oct 12, 2025
3c5bb99
์ˆ˜์ •๋กœ์ง ์ˆ˜์ •
EunbinJung Oct 12, 2025
491ba44
[feat] edit์ˆ˜์ •
EunbinJung Oct 14, 2025
0fc9e12
ํƒ€์ž…์˜ค๋ฅ˜
EunbinJung Oct 14, 2025
c464136
[feat] ์ข‹์•„์š”๋กœ์ง ์ˆ˜์ •
EunbinJung Oct 14, 2025
3216b01
[fix] ์นตํ…Œ์ผํƒœ๊ทธ ๋„ˆ๋น„ ์ˆ˜์ •
EunbinJung Oct 14, 2025
e0806a7
๊ธ€์“ฐ๊ธฐ ๋กœ๊ทธ์ธ๊ฒ€์‚ฌ
EunbinJung Oct 14, 2025
4a5291a
Merge branch 'dev' into feat/write#19
EunbinJung Oct 15, 2025
a2e5539
Merge branch 'dev' into feat/write#19
EunbinJung Oct 15, 2025
c378ff4
๋ฒ„๊ทธ๋“ค ์ˆ˜์ •
EunbinJung Oct 15, 2025
d08e422
Merge branch 'dev' into feat/write#19
EunbinJung Oct 15, 2025
f5f7093
๋ฐ˜์‘ํ˜•
EunbinJung Oct 15, 2025
f7cd698
ํŒŒ์ผ๋ช… ๋Œ€๋ฌธ์ž์ˆ˜์ •
EunbinJung Oct 15, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions next.config.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
import type { NextConfig } from 'next';

const nextConfig: NextConfig = {
experimental: {
scrollRestoration: false,
},
images: {
domains: ['team2-app-s3-bucket.s3.ap-northeast-2.amazonaws.com'],
remotePatterns: [
Expand All @@ -9,6 +12,7 @@ const nextConfig: NextConfig = {
hostname: 'www.thecocktaildb.com',
},
],
qualities: [25, 50, 75, 90, 100],
},
env: {
NPUBLIC_API_URL: process.env.NEXT_PUBLIC_API_URL,
Expand Down
Binary file removed public/1Stars.png
Binary file not shown.
Binary file removed public/1Stars.webp
Binary file not shown.
Binary file added public/1star.webp
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file removed public/2Stars.png
Binary file not shown.
Binary file removed public/2Stars.webp
Binary file not shown.
Binary file added public/2star.webp
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added public/CocktailDrop.webp
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file removed public/CocktailDrop_4x.webp
Binary file not shown.
11 changes: 10 additions & 1 deletion src/app/(no-layout)/layout.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,13 @@
import FooterWrapper from '@/shared/components/footer/FooterWrapper';
import Header from '@/shared/components/header/Header';

function NoLayout({ children }: { children: React.ReactNode }) {
return <main className="flex flex-1">{children}</main>;
return (
<>
<Header className="bg-transparent w-full h-[44px] md:h-[60px] flex items-center justify-between px-[12px] fixed top-0 left-0 z-50 transition-transform duration-200 ease-in-ou" />
<main className="flex flex-1">{children}</main>
<FooterWrapper />
</>
);
}
export default NoLayout;
2 changes: 1 addition & 1 deletion src/app/(no-layout)/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import FinalLanding from '@/domains/main/components/FinalLanding';

export default function Home() {
return (
<div className="page-layout max-w-full">
<div className="max-w-full flex-1">
<FinalLanding />
</div>
);
Expand Down
1 change: 0 additions & 1 deletion src/app/(with-layout)/community/[id]/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,6 @@ export async function generateMetadata({
cache: 'no-store',
});
const post = await res.json();
console.log(post);
return {
title: post.title,
description: post.content?.slice(0, 80),
Expand Down
1 change: 0 additions & 1 deletion src/app/(with-layout)/community/edit/[postId]/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@ import { useParams } from 'next/navigation';

function Page() {
const params = useParams();
console.log(params);

return (
<div className="w-full mb-20 flex relative">
Expand Down
9 changes: 9 additions & 0 deletions src/domains/community/api/fetchPost.ts
Original file line number Diff line number Diff line change
Expand Up @@ -95,3 +95,12 @@ export async function likePost(postId: number | ParamValue) {
});
if (!res.ok) throw new Error('์ข‹์•„์š” ์‹คํŒจ');
}

export async function getLikePost(postId: number | ParamValue) {
const res = await fetch(`${getApi}/posts/${postId}/like`, {
method: 'GET',
});
if (!res.ok) throw new Error('์ข‹์•„์š” ์‹คํŒจ');
const data = await res.json();
return data.data;
}
4 changes: 2 additions & 2 deletions src/domains/community/components/like/LikeBtn.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import { useState } from 'react';
type Props = {
size: 'sm' | 'md';
onClick?: () => void;
isClick?: boolean; // ์™ธ๋ถ€์—์„œ ์ œ์–ด
isClick?: boolean | null; // ์™ธ๋ถ€์—์„œ ์ œ์–ด
};

function LikeBtn({ size, onClick, isClick = false }: Props) {
Expand All @@ -13,7 +13,7 @@ function LikeBtn({ size, onClick, isClick = false }: Props) {
type="button"
className={`${size === 'md' ? 'w-13.75 h-13.75 flex-center border-1 border-white rounded-full' : ''} bg-primary`}
aria-label="์ข‹์•„์š” ๋ฒ„ํŠผ"
aria-pressed={isClick}
aria-pressed={isClick ? isClick : false}
onClick={() => {
if (onClick) onClick();
}}
Expand Down
18 changes: 16 additions & 2 deletions src/domains/community/components/tag/TagList.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -13,12 +13,26 @@ function TagList({ hasDelete, tags, setTags }: Props) {

if (!tags) return;
return (
<ul className="flex text-sm gap-2 items-center text-primary font-light flex-wrap">
<ul
className="flex
flex-nowrap
md:flex-wrap
overflow-x-scroll
md:overflow-visible
text-sm
gap-2
items-center
text-primary
font-light
no-scrollbar
max-w-full
"
>
{tags?.length > 0 &&
tags.map((tag) => (
<li
key={tag}
className={`bg-[#FFE4E6] px-2 py-[1px] rounded-md flex gap-2 ${hasDelete && 'hover:opacity-90 pl-2 pr-1'}`}
className={`bg-[#FFE4E6] px-2 py-[1px] rounded-md flex gap-2 w-fit whitespace-nowrap ${hasDelete && 'hover:opacity-90 pl-2 pr-1'}`}
>
<p>{tag}</p>
{hasDelete && (
Expand Down
Original file line number Diff line number Diff line change
@@ -1,14 +1,12 @@
import { Virtualizer } from '@tanstack/react-virtual';
import gsap from 'gsap';
import { useEffect, useRef } from 'react';

type Props = {
value: string;
onChange: (e: React.ChangeEvent<HTMLTextAreaElement>) => void;
rowVirtualize: Virtualizer<HTMLElement, Element>;
};

function AutoGrowingTextarea({ value, onChange, rowVirtualize }: Props) {
function AutoGrowingTextarea({ value, onChange }: Props) {
const textareaRef = useRef<HTMLTextAreaElement | null>(null);

useEffect(() => {
Expand All @@ -29,17 +27,6 @@ function AutoGrowingTextarea({ value, onChange, rowVirtualize }: Props) {
};
}, []);

useEffect(() => {
if (textareaRef.current) {
requestAnimationFrame(() => {
const li = textareaRef.current?.closest('li') as HTMLElement | null;
if (li) {
rowVirtualize.measureElement(li);
}
});
}
}, [value]);

useEffect(() => {
if (!textareaRef.current) return;
gsap.fromTo(
Expand Down
2 changes: 1 addition & 1 deletion src/domains/community/detail/DetailContent.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ type Props = {
content: string;
prevLikeCount: number;
commentCount: number;
like: boolean;
like: boolean | null;
onLikeToggle: () => void;
imageUrls: string[];
title: string;
Expand Down
30 changes: 21 additions & 9 deletions src/domains/community/detail/DetailPage.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
'use client';

import { fetchPostById, likePost } from '@/domains/community/api/fetchPost';
import { fetchPostById, getLikePost, likePost } from '@/domains/community/api/fetchPost';
import DetailContent from '@/domains/community/detail/DetailContent';
import DetailHeader from '@/domains/community/detail/DetailHeader';
import DetailTitle from '@/domains/community/detail/DetailTitle';
Expand All @@ -24,7 +24,7 @@ function DetailPage() {

const [postDetail, setPostDetail] = useState<Post | null>(null);
const [isLoading, setIsLoading] = useState(false);
const [like, setLike] = useState(false);
const [like, setLike] = useState<boolean | null>(null);
const [prevLikeCount, setPrevLikeCount] = useState<number | undefined>(0);

const isLoggedIn = useAuthStore((state) => state.isLoggedIn);
Expand All @@ -46,6 +46,18 @@ function DetailPage() {
fetchData();
}, [postId, setPostDetail]);

useEffect(() => {
const fetchLikeStatus = async () => {
try {
const liked = await getLikePost(postId);
setLike(liked);
} catch (err) {
console.error('์ข‹์•„์š” ์ƒํƒœ ๋ถˆ๋Ÿฌ์˜ค๊ธฐ ์‹คํŒจ', err);
}
};
fetchLikeStatus();
}, [postId]);

useEffect(() => {
if (postDetail) {
setPrevLikeCount(postDetail.likeCount);
Expand All @@ -68,17 +80,17 @@ function DetailPage() {
} = postDetail;

const handleLike = async () => {
setLike((prev) => !prev);
setPrevLikeCount((prev) => {
return like ? prev! - 1 : prev! + 1;
});
const newLike = !like; // ํ˜„์žฌ ์ƒํƒœ ๊ธฐ์ค€์œผ๋กœ ๋จผ์ € ๊ณ„์‚ฐ
setLike(newLike); // ์ข‹์•„์š” ์ƒํƒœ ๋จผ์ € ๋ฐ˜์˜
setPrevLikeCount((count) => (newLike ? (count ?? 0) + 1 : (count ?? 0) - 1)); // count๋„ ๋ฐ”๋กœ ๊ณ„์‚ฐ

try {
await likePost(postId); // POST ์š”์ฒญ ํ•œ ๋ฒˆ์œผ๋กœ ํ† ๊ธ€ ์ฒ˜๋ฆฌ
} catch (err) {
console.error('์ข‹์•„์š” ํ† ๊ธ€ ์‹คํŒจ', err);
setLike((prev) => !prev);
setPrevLikeCount((prev) => (like ? prev! + 1 : prev! - 1));
// ๋กค๋ฐฑ
setLike(!newLike);
setPrevLikeCount((count) => (newLike ? (count ?? 0) - 1 : (count ?? 0) + 1));
}
};

Expand Down Expand Up @@ -126,7 +138,7 @@ function DetailPage() {
{isLoggedIn && (
<div className="hidden lg:block">
<DetailTabDesktop
likeCount={prevLikeCount ?? 0}
likeCount={prevLikeCount}
commentCount={commentCount}
commentRef={commentRef}
like={like}
Expand Down
40 changes: 31 additions & 9 deletions src/domains/community/detail/ImageSlide.tsx
Original file line number Diff line number Diff line change
@@ -1,27 +1,49 @@
import Image from 'next/image';
import { Swiper, SwiperSlide } from 'swiper/react';
import { Navigation, Pagination } from 'swiper/modules';
import { useState } from 'react';

function ImageSlide({ imageUrls }: { imageUrls: string[] }) {
const shouldLoop = imageUrls.length > 1;
const [loadedImages, setLoadedImages] = useState<Set<string>>(new Set());

const handleImageLoad = (imgUrl: string) => {
setLoadedImages((prev) => new Set(prev).add(imgUrl));
};

return (
<Swiper
spaceBetween={20}
modules={[Navigation, Pagination]}
navigation
navigation={shouldLoop}
pagination={{ clickable: true, type: 'bullets' }}
loop
loop={shouldLoop}
className="w-full max-h-100 flex justify-center items-center"
>
{imageUrls.length > 0 &&
imageUrls.map((img) => (
<SwiperSlide className="w-full flex justify-center items-center" key={img}>
<Image
src={encodeURI(img)}
alt="์ด๋ฏธ์ง€"
width={150}
height={150}
className="object-contain w-full max-h-[400px]"
/>
<div className="relative w-full h-[400px] flex items-center justify-center">
{!loadedImages.has(img) && (
<div className="absolute inset-0 flex items-center justify-center bg-gray/80 rounded-lg">
<div className="w-8 h-8 border-4 border-secondary border-t-tertiary rounded-full animate-spin"></div>
</div>
)}
<Image
src={img}
alt="์ด๋ฏธ์ง€"
width={800}
height={600}
quality={90}
priority
onLoad={() => handleImageLoad(img)}
onError={() => handleImageLoad(img)}
className={`object-contain w-full max-h-[400px] transition-opacity duration-300 ${
loadedImages.has(img) ? 'opacity-100' : 'opacity-0'
}`}
style={{ width: 'auto', height: 'auto' }}
/>
</div>
</SwiperSlide>
))}
</Swiper>
Expand Down
5 changes: 2 additions & 3 deletions src/domains/community/detail/tab/DetailTabDesktop.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,10 +9,10 @@ import { useParams } from 'next/navigation';
import { CommentType } from '../../types/post';

type Props = {
likeCount: number;
likeCount: number | undefined;
commentCount: number;
commentRef: RefObject<HTMLElement | null>;
like: boolean;
like: boolean | null;
onLikeToggle: () => void;
title: string;
imageUrls: string[];
Expand All @@ -27,7 +27,6 @@ interface Meta {

function DetailTabDesktop({
likeCount,
commentCount,
commentRef,
like,
onLikeToggle,
Expand Down
2 changes: 1 addition & 1 deletion src/domains/community/detail/tab/DetailTabMobile.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import { useParams } from 'next/navigation';

type Props = {
likeCount: number;
like: boolean;
like: boolean | null;
onLikeToggle: () => void;
title: string;
imageUrls: string[];
Expand Down
16 changes: 11 additions & 5 deletions src/domains/community/main/Community.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -45,8 +45,6 @@ function Community() {
const newPosts = await fetchPostByTab({
category,
filter,
lastLikeCount,
lastCommentCount,
});

if (!newPosts || newPosts.length === 0) {
Expand All @@ -55,6 +53,9 @@ function Community() {
} else {
setPosts(newPosts);
}
} catch (error) {
console.error('๊ฒŒ์‹œ๊ธ€ ๋กœ๋”ฉ ์‹คํŒจ:', error);
setPosts([]);
} finally {
setIsLoading(false);
}
Expand All @@ -63,7 +64,6 @@ function Community() {
const loadMorePosts = async (lastPostId: number) => {
if (isEnd || isLoading) return;
if (!posts || posts.length === 0) return;
console.log('์‹œ์ž‘', lastPostId);

if (lastLoadedId === lastPostId) return;
setLastLoadedId(lastPostId);
Expand All @@ -81,8 +81,14 @@ function Community() {
if (!newPosts || newPosts?.length === 0) {
setIsEnd(true);
} else {
setPosts((prev) => [...(prev ?? []), ...(newPosts ?? [])]);
setPosts((prev) => {
const existingIds = new Set(prev?.map((p) => p.postId));
const filtered = newPosts.filter((p) => !existingIds.has(p.postId));
return [...(prev || []), ...filtered];
});
}
} catch (error) {
console.error('์ถ”๊ฐ€ ๊ฒŒ์‹œ๊ธ€ ๋กœ๋”ฉ ์‹คํŒจ:', error);
} finally {
setIsLoading(false);
}
Expand All @@ -94,7 +100,7 @@ function Community() {
aria-label="ํƒญ๊ณผ ๊ธ€์“ฐ๊ธฐ"
className="flex justify-between item-center sm:flex-row flex-col gap-4 mt-1"
>
<CommunityTab setPosts={setPosts} setIsLoading={setIsLoading} setIsEnd={setIsEnd} />
<CommunityTab />
<WriteBtn />
</section>

Expand Down
9 changes: 1 addition & 8 deletions src/domains/community/main/CommunityTab.tsx
Original file line number Diff line number Diff line change
@@ -1,16 +1,9 @@
'use client';

import tw from '@/shared/utills/tw';
import { Post } from '../types/post';
import { useState } from 'react';
import { useRouter, useSearchParams } from 'next/navigation';

type Props = {
setPosts: (value: Post[] | null) => void;
setIsLoading: (value: boolean) => void;
setIsEnd: (value: boolean) => void;
};

export const tabItem = [
{ key: 'all', label: '์ „์ฒด' },
{ key: 'recipe', label: '๋ ˆ์‹œํ”ผ' },
Expand All @@ -19,7 +12,7 @@ export const tabItem = [
{ key: 'chat', label: '์ž์œ ' },
];

function CommunityTab({ setPosts, setIsLoading, setIsEnd }: Props) {
function CommunityTab() {
const searchParams = useSearchParams();
const router = useRouter();

Expand Down
Loading