juni
커뮤니티 (게시글 고정) API 연동 본문
게시글 상세 조회 API 예시 -> 고정 게시글 리스트에서도 상세보기를 위해 사용
// 글 목록 조회
export const findFeeds = async (
domain: string,
tagKey?: string,
limit?: number,
lastId?: number
) => {
let searchParams;
if (!tagKey) {
searchParams = { limit, lastId };
} else {
searchParams = { tagKey, limit, lastId };
}
return await getApi(`/clubs/${domain}/feeds`, searchParams, false);
};
// 글 상세 조회
export const getFeedDetail = async (domain: string, feedId: number) => {
return await getApi(`/clubs/${domain}/feeds/${feedId}`, {}, false);
};
고정 게시글 예시
const resetFeeds = async () => {
setLoading(true);
const results = await findFeeds(
params.domain as string,
selectedTag,
FEED_LIMIT
);
if (results.statusCode === 500) {
toast({ state: "warning", description: results.message });
} else if (results.statusCode === 200) {
// 고정 피드
setPinnedFeeds(results.pinnedFeeds);
// 피드
setFeeds(results.feeds);
if (results.feeds.length < FEED_LIMIT) {
setHasMore(false); // 새로운 피드의 갯수가 FEED_LIMIT보다 적으면 더 이상 가져올 데이터가 없음
}
}
if (tagList.length == 0) {
fetchRankingAndTags();
}
setLoading(false);
};
<FixedFeedList
pinnedFeeds={pinnedFeeds}
onCompleted={() => {
resetFeeds();
}}
/>
고정 게시글 목록에서 사용된 상세 게시글 코드 예시
const [isPinned, setIsPinned] = useState(!!defaultValue?.pinnedTagKey); // 생성/수정 고정 상태
const openFeedDetails = async (feed: IFeedItem) => {
setLoading(true);
const result = await getFeedDetail(
params.domain as string,
feed.id
);
if (result.statusCode === 200) {
if (result.feed) {
const feedDetails = {
...result.feed,
images: result.feed.images || { feed_image: [] },
comments: result.feed.comments || [],
};
setSelectedFeed(feedDetails);
setDialogOpen(true);
}
if (result.challenge) {
// 챌린지 자동생성
router.push(`/${params.domain}/classes/${result.challenge.id}`);
return;
}
if (result.achievement) {
// console.log(result);
if (feed.userId) {
router.push(
`/${params.domain}/certificate/${result.achievement.id}`
);
}
return;
}
}
if (result.statusCode == 410) {
toast({
state: "warning",
description: "회원가입하지 않은 사용자입니다",
});
}
if (result.statusCode == 500) {
toast({ state: "warning", description: result.message });
}
setLoading(false);
};
<div className="flex justify-between">
<h4 className="text-title_l md:text-subhead_s grow-0 mb-6">
{defaultValue ? "게시글 수정하기" : "게시글 작성하기"}
</h4>
{role === "owner" && (
<Pin
className={`w-6 h-6 cursor-pointer ${
isPinned ? "text-icon-primary" : "text-icon-secondary"
}`}
onClick={() => setIsPinned(!isPinned)}
/>
)}
</div>
커스텀 DropDownMenu에서 DB반영을 위해 API 예시
// 게시물 고정
export const pinFeed = async (
domain: string,
feedId: number,
tagKey: string
) => {
const pinInfo = {
tagKey,
};
return await patchApi(`/clubs/${domain}/feeds/${feedId}/pin`, pinInfo);
};
// 게시물 고정 해제
export const unpinFeed = async (domain: string, feedId: number) => {
return await patchApi(`/clubs/${domain}/feeds/${feedId}/unpin`, {});
};
커스텀 DropDownMenu 게시글 고정 코드 예시
import { pinFeed, unpinFeed } from "@/lib/api/community";
const togglePinFeed = async () => {
const tagKey = item.tags[0]?.tagKey;
if (!tagKey) {
toast({
description: "고정할 유효한 태그 키가 없습니다.",
state: "warning",
});
return;
}
if (item.pinnedTagKey) {
const result = await unpinFeed(params.domain as string, item.id);
if (result.statusCode === 200) {
item.pinnedTagKey = null;
if (onCompleted) onCompleted();
} else {
toast({ description: "고정 해제 실패", state: "warning" });
}
} else {
const result = await pinFeed(
params.domain as string,
item.id,
tagKey
);
if (result.statusCode === 200) {
item.pinnedTagKey = tagKey;
if (onCompleted) onCompleted();
} else {
toast({ description: "고정 실패", state: "warning" });
}
}
};
{role == "owner" && (
<div
className="px-3 pt-4 pb-3.5 cursor-pointer"
onClick={togglePinFeed}
>
{item.pinnedTagKey ? "상단 고정 해제하기" : "상단 고정하기"}
</div>
)}
게시글 작성/수정 중 Pin 아이콘으로 고정 예약 on/off기능 예시
const [isPinned, setIsPinned] = useState(!!defaultValue?.pinnedTagKey); // 생성/수정 고정 상태
const [defaultImages, setDefaultImages] = useState<any>([]);
const onSubmit = async (data: FeedFormValues) => {
setLoading(true);
// const isPinned = Boolean(feed.pinnedTagKey);
const pinnedTagKey = isPinned && tags.length > 0 ? tags[0].tagKey : null;
let result;
// 일반 피드 생성
if (!submissionInfo) {
if (!defaultValue) {
result = await createFeed({
domain: params.domain as string,
...data,
tags,
pinnedTagKey: pinnedTagKey,
});
if (result.statusCode === 201) {
if (onCompleted) onCompleted(undefined);
form.reset();
setTags([]);
setIsPinned(false);
setOpenDialog(false);
}
} else {
result = await updateFeed({
feedId: defaultValue.id,
domain: params.domain as string,
...data,
tags,
imageIdsToDelete,
pinnedTagKey: pinnedTagKey,
});
if (result.statusCode === 200) {
if (onCompleted) onCompleted(result.feed);
form.reset();
setTags([]);
setOpenDialog(false);
}
}
toast({ description: result.message });
}
// 클래스용 피드 생성
if (submissionInfo) {
if (!defaultValue) {
result = await submitTask(
params.domain as string,
submissionInfo.challengeId as number,
submissionInfo.id as number,
{
domain: params.domain as string,
...data,
tags,
}
);
if (result.statusCode === 200) {
// toast({ description: result.message });
if (onCompleted) onCompleted();
form.reset();
setOpenDialog(false);
}
if (result.statusCode === 409) {
// toast({ description: result.message });
}
toast({ description: result.message });
}
}
setLoading(false);
};
<div className="flex justify-between">
<h4 className="text-title_l md:text-subhead_s grow-0 mb-6">
{defaultValue ? "게시글 수정하기" : "게시글 작성하기"}
</h4>
{role === "owner" && (
<Pin
className={`w-6 h-6 cursor-pointer ${
isPinned ? "text-icon-primary" : "text-icon-secondary"
}`}
onClick={() => setIsPinned(!isPinned)}
/>
)}
</div>
'인턴' 카테고리의 다른 글
푸터 (3) | 2024.11.15 |
---|---|
커뮤니티 게시글 관련 QA 및 리팩토링 (1) | 2024.11.14 |
게시글 고정 (1) | 2024.11.13 |
프로필 아바타 및 이미지 수정 (1) | 2024.11.12 |
게시글 삭제, 상세 게시글 DropDownMenu 연결 (0) | 2024.11.12 |