Skip to content

Commit 0572d20

Browse files
committed
[feat] 댓글 클릭시 해당 글로 보내주는 기능
1 parent 0a4742e commit 0572d20

File tree

11 files changed

+185
-81
lines changed

11 files changed

+185
-81
lines changed

src/app/mypage/my-active/my-like/page.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,6 @@ export const metadata: Metadata = {
88
};
99

1010
function Page() {
11-
// return <MyLike />;
11+
return <MyLike />;
1212
}
1313
export default Page;

src/domains/mypage/components/pages/my-active/MyComment.tsx

Lines changed: 11 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -7,11 +7,11 @@ import { useEffect, useState } from 'react';
77
import { useShallow } from 'zustand/shallow';
88

99
function MyComment() {
10-
const { user } = useAuthStore(
11-
useShallow((state) => ({
12-
user: state.user,
13-
}))
14-
);
10+
const { user } = useAuthStore(
11+
useShallow((state) => ({
12+
user: state.user,
13+
}))
14+
);
1515

1616
const [myComment, setMyComment] = useState<CommentType[]>([]);
1717
const [isLoading] = useState<boolean>(false);
@@ -32,7 +32,12 @@ const { user } = useAuthStore(
3232
return (
3333
<section>
3434
{CommentList.length !== 0 ? (
35-
<CommentList comments={myComment} isLoading={isLoading} myPage={true} currentUserNickname={user?.nickname} />
35+
<CommentList
36+
comments={myComment}
37+
isLoading={isLoading}
38+
myPage={true}
39+
currentUserNickname={user?.nickname}
40+
/>
3641
) : (
3742
<div className="flex justify-center">
3843
<p>작성한 댓글이 없습니다.</p>

src/domains/mypage/components/pages/my-active/MyLike.tsx

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,14 @@ function MyLike() {
2020
fetchLike();
2121
}, []);
2222

23-
return <PostCard posts={myLike} isLoading={isLoading} />;
23+
return (
24+
<section className="flex justify-center">
25+
{myLike.length > 0 ? (
26+
<PostCard posts={myLike} isLoading={isLoading} />
27+
) : (
28+
<div>아직 좋아요를 누른 글이 없습니다</div>
29+
)}
30+
</section>
31+
);
2432
}
2533
export default MyLike;

src/domains/recipe/api/RecipeFetch.tsx

Lines changed: 23 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -23,8 +23,7 @@ export const RecipeFetch = ({
2323
setHasNextPage,
2424
SIZE = 20,
2525
}: Props) => {
26-
27-
const user = useAuthStore()
26+
const user = useAuthStore();
2827
const fetchData = useCallback(async () => {
2928
// 쿼리파라미터에 값 넣기
3029
if (!hasNextPage) return;
@@ -35,32 +34,41 @@ export const RecipeFetch = ({
3534
}
3635
url.searchParams.set('LastValue', String(lastId));
3736

38-
3937
const recipeRes = await fetch(url.toString(), {
40-
method:'GET'
41-
})
42-
if(!recipeRes.ok) throw new Error('데이터 요청 실패')
43-
const recipeJson = await recipeRes.json()
44-
const list: Cocktail[] = recipeJson.data ?? []
38+
method: 'GET',
39+
});
40+
if (!recipeRes.ok) throw new Error('데이터 요청 실패');
41+
const recipeJson = await recipeRes.json();
42+
const list: Cocktail[] = recipeJson.data ?? [];
4543

4644
if (user) {
4745
const keepRes = await fetch(`${getApi}/me/bar`, {
4846
method: 'GET',
49-
credentials:'include'
50-
})
51-
const bars = keepRes.ok ? (await keepRes.json()).data ?? [] : []
47+
credentials: 'include',
48+
});
49+
const bars = keepRes.ok ? ((await keepRes.json()).data ?? []) : [];
5250
const favoriteIds = new Set(bars.map((m: { cocktailId: number }) => m.cocktailId));
53-
const merged = list.map(item => ({ ...item, isFavorited: favoriteIds.has(item.cocktailId) }))
54-
setData(prev => Array.from(new Map<number,Cocktail>([...prev,...merged].map(i => [i.cocktailId,i])).values()))
51+
const merged = list.map((item) => ({
52+
...item,
53+
isFavorited: favoriteIds.has(item.cocktailId),
54+
}));
55+
setData((prev) =>
56+
Array.from(
57+
new Map<number, Cocktail>([...prev, ...merged].map((i) => [i.cocktailId, i])).values()
58+
)
59+
);
5560
} else {
56-
setData(prev => Array.from(new Map<number,Cocktail>([...prev,...list].map(i=>[i.cocktailId,i])).values()))
61+
setData((prev) =>
62+
Array.from(
63+
new Map<number, Cocktail>([...prev, ...list].map((i) => [i.cocktailId, i])).values()
64+
)
65+
);
5766
}
5867

5968
if (list.length > 0) {
6069
setLastId(list[list.length - 1].cocktailId);
6170
}
6271
setHasNextPage(list.length === SIZE);
63-
6472
}, [hasNextPage, lastId, setData, setLastId, setHasNextPage, SIZE]);
6573
return { fetchData };
6674
};

src/domains/recipe/components/main/CocktailFilter.tsx

Lines changed: 20 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -9,28 +9,27 @@ interface Props {
99
setData: Dispatch<SetStateAction<Cocktail[]>>;
1010
}
1111

12-
function CocktailFilter({ cocktailsEA,setData }:Props) {
12+
function CocktailFilter({ cocktailsEA, setData }: Props) {
13+
const sortMap = {
14+
최신순: 'recent',
15+
인기순: 'popular',
16+
댓글순: 'comments',
17+
};
18+
const searchParams = useSearchParams();
19+
const query = searchParams.get('sortBy');
20+
const router = useRouter();
21+
const handleChange = async (selectTitle: string) => {
22+
if (!query) return;
23+
try {
24+
const res = await fetch(`${getApi}/cocktails`);
25+
const json = await res.json();
26+
setData(json.data);
27+
} catch {
28+
console.error();
29+
console.log(selectTitle);
30+
}
31+
};
1332

14-
const sortMap = {
15-
최신순: 'recent',
16-
인기순: 'popular',
17-
댓글순: 'comments',
18-
}
19-
const searchParams = useSearchParams();
20-
const query = searchParams.get('sortBy');
21-
const router = useRouter();
22-
const handleChange = async (selectTitle: string) => {
23-
if (!query) return;
24-
try {
25-
const res = await fetch(`${getApi}/cocktails`)
26-
const json = await res.json()
27-
setData(json.data)
28-
} catch {
29-
console.error()
30-
console.log(selectTitle)
31-
}
32-
};
33-
3433
return (
3534
<div className="h-10 flex justify-between items-center mt-3 border-b-1 border-gray-light">
3635
<p>{cocktailsEA}</p>

src/domains/recipe/details/DetailMain.tsx

Lines changed: 12 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -13,17 +13,17 @@ import RecipeComment from '../components/details/RecipeComment';
1313
import { getApi } from '@/app/api/config/appConfig';
1414
import { useAuthStore } from '@/domains/shared/store/auth';
1515

16-
interface Kept{
17-
cocktailId: number,
18-
id: number,
19-
keptAt:Date
16+
interface Kept {
17+
cocktailId: number;
18+
id: number;
19+
keptAt: Date;
2020
}
2121

2222
function DetailMain({ id }: { id: number }) {
23-
const user = useAuthStore()
23+
const user = useAuthStore();
2424
const [cocktail, setCocktail] = useState();
25-
const [isKept, setIsKept] = useState<boolean|null>(null)
26-
25+
const [isKept, setIsKept] = useState<boolean | null>(null);
26+
2727
const fetchData = async () => {
2828
const res = await fetch(`${getApi}/cocktails/${id}`);
2929
const json = await res.json();
@@ -33,19 +33,17 @@ function DetailMain({ id }: { id: number }) {
3333
if (user) {
3434
const keepRes = await fetch(`${getApi}/me/bar`, {
3535
method: 'GET',
36-
credentials:'include'
37-
})
38-
const keepjson = await keepRes.json()
39-
if (!keepRes.ok) throw new Error('킵 한 아이템 호출 에러')
36+
credentials: 'include',
37+
});
38+
const keepjson = await keepRes.json();
39+
if (!keepRes.ok) throw new Error('킵 한 아이템 호출 에러');
4040
const keepIds = keepjson.data.map((a: Kept) => String(a.cocktailId));
4141
setIsKept(keepIds.includes(String(id)));
4242
} else {
43-
setIsKept(false)
43+
setIsKept(false);
4444
}
4545
};
4646

47-
48-
4947
useEffect(() => {
5048
fetchData();
5149
}, []);

src/domains/recipe/details/DetailsHeader.tsx

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -12,22 +12,20 @@ interface Meta {
1212
url: string;
1313
}
1414

15-
function DetailsHeader({ id, favor }: { id: number, favor: boolean | null }) {
16-
15+
function DetailsHeader({ id, favor }: { id: number; favor: boolean | null }) {
1716
const [isShare, setIsShare] = useState(false);
1817
const [meta, setMeta] = useState<Meta | null>(null);
19-
18+
2019
const url = async () => {
2120
const res = await fetch(`${getApi}/cocktails/${id}/share`);
2221
const json = await res.json();
2322
setMeta(json.data);
2423
};
25-
24+
2625
useEffect(() => {
2726
url();
2827
}, []);
2928

30-
3129
return (
3230
<div className="flex items-center justify-between pb-5 sm:pb-12">
3331
{isShare && meta && (

src/domains/shared/components/comment/CommentList.tsx

Lines changed: 96 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,8 @@ import { useInfiniteScrollObserver } from '@/shared/hook/useInfiniteScrollObserv
66
import { useItemVirtualizer } from '@/domains/community/hook/useItemVirtualizer';
77
import { useCommentEnterAnimation } from '@/domains/community/hook/useCommentAnimation';
88
import { usePrevious } from 'react-use';
9+
import Link from 'next/link';
10+
import { getApi } from '@/app/api/config/appConfig';
911

1012
type Props = {
1113
comments: CommentType[] | null;
@@ -27,7 +29,6 @@ function CommentList({
2729
isEnd,
2830
myPage = false,
2931
}: Props) {
30-
3132
const parentRef = useRef<HTMLDivElement | null>(null);
3233
const [editCommentId, setEditCommentId] = useState<number | null>(null);
3334
const [editedContentMap, setEditedContentMap] = useState<Record<number, string>>({});
@@ -71,12 +72,100 @@ function CommentList({
7172
>
7273
<ul style={{ height: rowVirtualizer.getTotalSize(), position: 'relative' }}>
7374
{rowVirtualizer.getVirtualItems().map(({ index, key, start }) => {
74-
const { commentId, content, userNickName, createdAt } = comments[index];
75+
const { commentId, content, userNickName, createdAt, postId } = comments[index];
7576
const isEditing = editCommentId === commentId;
7677
const isMyComment = comments && currentUserNickname === userNickName;
7778
const isLast = index === comments.length - 1;
7879

79-
return (
80+
return myPage ? (
81+
<Link href={`/community/${postId}`} key={key}>
82+
<li
83+
className="border-b-1 border-gray py-3"
84+
data-index={index}
85+
ref={(el) => {
86+
if (el) {
87+
requestAnimationFrame(() => {
88+
try {
89+
rowVirtualizer.measureElement(el);
90+
} catch (e) {
91+
console.error('measureElement failed', e);
92+
}
93+
});
94+
if (index === 0) firstItemRef.current = el;
95+
if (isLast) observeLastItem(el);
96+
}
97+
}}
98+
style={{
99+
position: 'absolute',
100+
top: 0,
101+
left: 0,
102+
width: '100%',
103+
transform: `translateY(${start}px)`,
104+
minHeight: '60px', // ← 최소 보장
105+
}}
106+
>
107+
<article>
108+
<CommentTitle
109+
userNickname={myPage ? currentUserNickname! : userNickName}
110+
commentTime={createdAt}
111+
isMyComment={isMyComment}
112+
isEditing={isEditing}
113+
myPage={myPage}
114+
onSubmitEdit={() => {
115+
const updatedContent = editedContentMap[commentId];
116+
if (!updatedContent) return;
117+
if (!onUpdateComment) return;
118+
onUpdateComment(commentId, updatedContent).then(() => {
119+
setEditCommentId(null);
120+
setEditedContentMap((prev) => {
121+
const next = { ...prev };
122+
delete next[commentId];
123+
return next;
124+
});
125+
});
126+
}}
127+
onDelete={() => {
128+
if (!onDeleteComment) return;
129+
onDeleteComment(commentId);
130+
}}
131+
onEdit={() => {
132+
setEditCommentId(commentId);
133+
setEditedContentMap((prev) => ({
134+
...prev,
135+
[commentId]: content, // 기존 내용 세팅
136+
}));
137+
}}
138+
onCancelEdit={() => {
139+
setEditCommentId(null);
140+
setEditedContentMap((prev) => {
141+
const next = { ...prev };
142+
delete next[commentId];
143+
return next;
144+
});
145+
}}
146+
/>
147+
<article className="mt-4 h-full">
148+
{isEditing ? (
149+
<AutoGrowingTextarea
150+
value={editedContentMap[commentId] ?? content}
151+
rowVirtualize={rowVirtualizer}
152+
onChange={(e) =>
153+
setEditedContentMap((prev) => ({
154+
...prev,
155+
[commentId]: e.target.value,
156+
}))
157+
}
158+
/>
159+
) : (
160+
<div className="mt-4">
161+
<p className="whitespace-pre-wrap">{content}</p>
162+
</div>
163+
)}
164+
</article>
165+
</article>
166+
</li>
167+
</Link>
168+
) : (
80169
<li
81170
className="border-b-1 border-gray py-3"
82171
key={key}
@@ -105,15 +194,15 @@ function CommentList({
105194
>
106195
<article>
107196
<CommentTitle
108-
userNickname={ myPage ? currentUserNickname! : userNickName}
197+
userNickname={myPage ? currentUserNickname! : userNickName}
109198
commentTime={createdAt}
110199
isMyComment={isMyComment}
111200
isEditing={isEditing}
112201
myPage={myPage}
113202
onSubmitEdit={() => {
114203
const updatedContent = editedContentMap[commentId];
115204
if (!updatedContent) return;
116-
if (!onUpdateComment) return
205+
if (!onUpdateComment) return;
117206
onUpdateComment(commentId, updatedContent).then(() => {
118207
setEditCommentId(null);
119208
setEditedContentMap((prev) => {
@@ -123,10 +212,9 @@ function CommentList({
123212
});
124213
});
125214
}}
126-
127215
onDelete={() => {
128-
if (!onDeleteComment) return
129-
onDeleteComment(commentId)
216+
if (!onDeleteComment) return;
217+
onDeleteComment(commentId);
130218
}}
131219
onEdit={() => {
132220
setEditCommentId(commentId);

0 commit comments

Comments
 (0)