Fix forum detail

Forum (User)
- app/(application)/(user)/forum/[id]/index.tsx
- screens/Forum/ViewForumku2.tsx
- service/api-client/api-forum.ts

Forum – New / Refactor
- screens/Forum/DetailForum.tsx
- screens/Forum/DetailForum2.tsx

Documentation
- docs/

Removed
- hipmi-note.md

### No Issue'
This commit is contained in:
2026-01-29 17:36:17 +08:00
parent d693550a1f
commit ed16f1b204
8 changed files with 644 additions and 359 deletions

View File

@@ -1,258 +1,11 @@
import {
ButtonCustom,
DrawerCustom,
LoaderCustom,
Spacing,
TextAreaCustom,
TextCustom,
ViewWrapper,
} from "@/components";
import AlertWarning from "@/components/Alert/AlertWarning";
import { useAuth } from "@/hooks/use-auth";
import Forum_CommentarBoxSection from "@/screens/Forum/CommentarBoxSection";
import Forum_BoxDetailSection from "@/screens/Forum/DiscussionBoxSection";
import Forum_MenuDrawerBerandaSection from "@/screens/Forum/MenuDrawerSection.tsx/MenuBeranda";
import Forum_MenuDrawerCommentar from "@/screens/Forum/MenuDrawerSection.tsx/MenuCommentar";
import {
apiForumCreateComment,
apiForumGetComment,
apiForumGetOne,
apiForumUpdateStatus,
} from "@/service/api-client/api-forum";
import { TypeForum_CommentProps } from "@/types/type-forum";
import { isBadContent } from "@/utils/badWordsIndonesia";
import { useFocusEffect, useLocalSearchParams } from "expo-router";
import _ from "lodash";
import { useCallback, useEffect, useState } from "react";
import DetailForum from "@/screens/Forum/DetailForum";
import DetailForum2 from "@/screens/Forum/DetailForum2";
export default function ForumDetail() {
const { id } = useLocalSearchParams();
const { user } = useAuth();
const [openDrawer, setOpenDrawer] = useState(false);
const [data, setData] = useState<any | null>(null);
const [listComment, setListComment] = useState<TypeForum_CommentProps[] | null>(null);
const [isLoadingComment, setLoadingComment] = useState(false);
// Status
const [status, setStatus] = useState("");
const [text, setText] = useState("");
const [authorId, setAuthorId] = useState("");
const [dataId, setDataId] = useState("");
// Comentar
const [openDrawerCommentar, setOpenDrawerCommentar] = useState(false);
const [commentId, setCommentId] = useState("");
const [commentAuthorId, setCommentAuthorId] = useState("");
useFocusEffect(
useCallback(() => {
onLoadData(id as string);
}, [id])
);
const onLoadData = async (id: string) => {
try {
const response = await apiForumGetOne({ id });
setData(response.data);
} catch (error) {
console.log("[ERROR]", error);
}
};
useEffect(() => {
onLoadListComment(id as string);
}, [id]);
const onLoadListComment = async (id: string) => {
try {
const response = await apiForumGetComment({
id: id as string,
});
setListComment(response.data);
} catch (error) {
console.log("[ERROR]", error);
}
};
// Update Status
const handlerUpdateStatus = async (value: any) => {
try {
const response = await apiForumUpdateStatus({
id: id as string,
data: value,
});
if (response.success) {
setStatus(response.data);
setData({
...data,
ForumMaster_StatusPosting: {
status: response.data,
},
});
}
} catch (error) {
console.log("[ERROR]", error);
}
};
// Create Commentar
const handlerCreateCommentar = async () => {
if (isBadContent(text)) {
AlertWarning({});
return;
}
const newData = {
comment: text,
authorId: user?.id,
};
try {
setLoadingComment(true);
const response = await apiForumCreateComment({
id: id as string,
data: newData,
});
if (response.success) {
setText("");
const newComment = {
id: response.data.id,
isActive: response.data.isActive,
komentar: response.data.komentar,
createdAt: response.data.createdAt,
authorId: response.data.authorId,
Author: response.data.Author,
};
setListComment((prev) => [newComment, ...(prev || [])]);
setData({
...data,
count: data.count + 1,
});
}
} catch (error) {
console.log("[ERROR]", error);
} finally {
setLoadingComment(false);
}
};
return (
<>
<ViewWrapper>
{!data && !listComment ? (
<LoaderCustom />
) : (
<>
{/* Box Posting */}
<Forum_BoxDetailSection
data={data}
onSetData={() => {
setOpenDrawer(true);
setStatus(data.ForumMaster_StatusPosting?.status);
setAuthorId(data.Author?.id);
setDataId(data.id);
}}
/>
{/* Area Commentar */}
{data?.ForumMaster_StatusPosting?.status === "Open" && (
<>
<TextAreaCustom
placeholder="Ketik diskusi anda..."
maxLength={1000}
showCount
value={text}
onChangeText={setText}
style={{
marginBottom: 0,
}}
/>
<ButtonCustom
isLoading={isLoadingComment}
style={{
alignSelf: "flex-end",
}}
onPress={() => {
handlerCreateCommentar();
}}
>
Balas
</ButtonCustom>
</>
)}
<Spacing height={40} />
{/* List Commentar */}
{_.isEmpty(listComment) ? (
<TextCustom align="center" color="gray" size={"small"}>
Tidak ada komentar
</TextCustom>
) : (
<TextCustom color="gray">Komentar :</TextCustom>
)}
<Spacing height={5} />
{listComment?.map((item: any, index: number) => (
<Forum_CommentarBoxSection
key={index}
data={item}
onSetData={(value) => {
setCommentId(value.setCommentId);
setOpenDrawerCommentar(value.setOpenDrawer);
setCommentAuthorId(value.setCommentAuthorId);
}}
/>
))}
</>
)}
</ViewWrapper>
{/* Posting Drawer */}
<DrawerCustom
height={"auto"}
isVisible={openDrawer}
closeDrawer={() => setOpenDrawer(false)}
>
<Forum_MenuDrawerBerandaSection
id={dataId}
authorUsername={data?.Author?.username as string}
status={status}
setIsDrawerOpen={() => {
setOpenDrawer(false);
}}
authorId={authorId}
handlerUpdateStatus={(value: any) => {
handlerUpdateStatus(value);
}}
/>
</DrawerCustom>
{/* Commentar Drawer */}
<DrawerCustom
height={"auto"}
isVisible={openDrawerCommentar}
closeDrawer={() => setOpenDrawerCommentar(false)}
>
<Forum_MenuDrawerCommentar
id={commentId as string}
commentId={commentId}
commentAuthorId={commentAuthorId}
setIsDrawerOpen={() => {
setOpenDrawerCommentar(false);
}}
listComment={listComment}
setListComment={setListComment}
countComment={data?.count}
setCountComment={(val: any) => {
setData((prev: any) => ({
...prev,
count: val,
}));
}}
/>
</DrawerCustom>
{/* <DetailForum />; */}
<DetailForum2 />
</>
);
)
}

View File

@@ -0,0 +1,14 @@
<!-- Start Penerapan Pagination -->
Terapkan pagination pada file: screens/Forum/DetailForum.tsx
Component yang digunakan: hooks/use-pagination.tsx dan helpers/paginationHelpers.tsx
Perbaiki fetch pada file: service/api-client/api-forum.ts
Jika tidak ada props page maka tambahkan props page dan default page: "1"
Gunakan bahasa indonesia pada cli agar saya mudah membacanya.
<!-- End Penerapan Pagination -->
<!-- Start Penerapan NewWrapper -->
Terapkan NewWrapper pada file: screens/Forum/DetailForum.tsx
Component yang digunakan: components/_ShareComponent/NewWrapper.tsx , karena ini adalah halaman detail saya ingin anda fokus pada props pada NewWrapper. Seperti

View File

@@ -127,8 +127,6 @@ export default function LoginView() {
return <Redirect href={"/(application)/(user)/home"} />;
}
console.log("load term", loadingTerm);
return (
<NewWrapper
withBackground

View File

@@ -0,0 +1,276 @@
import {
BoxButtonOnFooter,
ButtonCustom,
DrawerCustom,
LoaderCustom,
NewWrapper,
Spacing,
TextAreaCustom,
TextCustom,
TextInputCustom,
ViewWrapper,
} from "@/components";
import CustomSkeleton from "@/components/_ShareComponent/SkeletonCustom";
import AlertWarning from "@/components/Alert/AlertWarning";
import { useAuth } from "@/hooks/use-auth";
import Forum_CommentarBoxSection from "@/screens/Forum/CommentarBoxSection";
import Forum_BoxDetailSection from "@/screens/Forum/DiscussionBoxSection";
import Forum_MenuDrawerBerandaSection from "@/screens/Forum/MenuDrawerSection.tsx/MenuBeranda";
import Forum_MenuDrawerCommentar from "@/screens/Forum/MenuDrawerSection.tsx/MenuCommentar";
import {
apiForumCreateComment,
apiForumGetComment,
apiForumGetOne,
apiForumUpdateStatus,
} from "@/service/api-client/api-forum";
import { TypeForum_CommentProps } from "@/types/type-forum";
import { isBadContent } from "@/utils/badWordsIndonesia";
import { useFocusEffect, useLocalSearchParams } from "expo-router";
import _ from "lodash";
import { useCallback, useEffect, useState } from "react";
export default function DetailForum() {
const { id } = useLocalSearchParams();
const { user } = useAuth();
const [openDrawer, setOpenDrawer] = useState(false);
const [data, setData] = useState<any | null>(null);
const [listComment, setListComment] = useState<
TypeForum_CommentProps[] | null
>(null);
const [isLoadingComment, setLoadingComment] = useState(false);
// Status
const [status, setStatus] = useState("");
const [text, setText] = useState("");
const [authorId, setAuthorId] = useState("");
const [dataId, setDataId] = useState("");
// Comentar
const [openDrawerCommentar, setOpenDrawerCommentar] = useState(false);
const [commentId, setCommentId] = useState("");
const [commentAuthorId, setCommentAuthorId] = useState("");
useFocusEffect(
useCallback(() => {
setTimeout(() => {
onLoadData(id as string);
}, 3000);
}, [id]),
);
useEffect(() => {
setTimeout(() => {
onLoadListComment(id as string);
}, 3000);
}, [id]);
const onLoadData = async (id: string) => {
try {
const response = await apiForumGetOne({ id });
setData(response.data);
} catch (error) {
console.log("[ERROR]", error);
}
};
const onLoadListComment = async (id: string) => {
try {
const response = await apiForumGetComment({
id: id as string,
});
setListComment(response.data);
} catch (error) {
console.log("[ERROR]", error);
}
};
// Update Status
const handlerUpdateStatus = async (value: any) => {
try {
const response = await apiForumUpdateStatus({
id: id as string,
data: value,
});
if (response.success) {
setStatus(response.data);
setData({
...data,
ForumMaster_StatusPosting: {
status: response.data,
},
});
}
} catch (error) {
console.log("[ERROR]", error);
}
};
// Create Commentar
const handlerCreateCommentar = async () => {
if (isBadContent(text)) {
AlertWarning({});
return;
}
const newData = {
comment: text,
authorId: user?.id,
};
try {
setLoadingComment(true);
const response = await apiForumCreateComment({
id: id as string,
data: newData,
});
if (response.success) {
setText("");
const newComment = {
id: response.data.id,
isActive: response.data.isActive,
komentar: response.data.komentar,
createdAt: response.data.createdAt,
authorId: response.data.authorId,
Author: response.data.Author,
};
setListComment((prev) => [newComment, ...(prev || [])]);
setData({
...data,
count: data.count + 1,
});
}
} catch (error) {
console.log("[ERROR]", error);
} finally {
setLoadingComment(false);
}
};
const headerComponent = () =>
// Box Posting
!data && !listComment ? (
<CustomSkeleton height={200} />
) : (
<>
<Forum_BoxDetailSection
data={data}
onSetData={() => {
setOpenDrawer(true);
setStatus(data.ForumMaster_StatusPosting?.status);
setAuthorId(data.Author?.id);
setDataId(data.id);
}}
/>
{data?.ForumMaster_StatusPosting?.status === "Open" && (
<>
<TextAreaCustom
placeholder="Ketik diskusi anda..."
maxLength={1000}
showCount
value={text}
onChangeText={setText}
style={{
marginBottom: 0,
}}
/>
<ButtonCustom
isLoading={isLoadingComment}
style={{
alignSelf: "flex-end",
}}
onPress={() => {
handlerCreateCommentar();
}}
>
Balas
</ButtonCustom>
</>
)}
</>
);
return (
<>
<NewWrapper
// headerComponent={headerComponent()}
>
{!data && !listComment ? (
<LoaderCustom />
) : (
<>
{/* Area Commentar */}
{headerComponent()}
<Spacing height={40} />
{/* List Commentar */}
{_.isEmpty(listComment) ? (
<TextCustom align="center" color="gray" size={"small"}>
Tidak ada komentar
</TextCustom>
) : (
<TextCustom color="gray">Komentar :</TextCustom>
)}
<Spacing height={5} />
{listComment?.map((item: any, index: number) => (
<Forum_CommentarBoxSection
key={index}
data={item}
onSetData={(value) => {
setCommentId(value.setCommentId);
setOpenDrawerCommentar(value.setOpenDrawer);
setCommentAuthorId(value.setCommentAuthorId);
}}
/>
))}
</>
)}
</NewWrapper>
{/* Posting Drawer */}
<DrawerCustom
height={"auto"}
isVisible={openDrawer}
closeDrawer={() => setOpenDrawer(false)}
>
<Forum_MenuDrawerBerandaSection
id={dataId}
authorUsername={data?.Author?.username as string}
status={status}
setIsDrawerOpen={() => {
setOpenDrawer(false);
}}
authorId={authorId}
handlerUpdateStatus={(value: any) => {
handlerUpdateStatus(value);
}}
/>
</DrawerCustom>
{/* Commentar Drawer */}
<DrawerCustom
height={"auto"}
isVisible={openDrawerCommentar}
closeDrawer={() => setOpenDrawerCommentar(false)}
>
<Forum_MenuDrawerCommentar
id={commentId as string}
commentId={commentId}
commentAuthorId={commentAuthorId}
setIsDrawerOpen={() => {
setOpenDrawerCommentar(false);
}}
listComment={listComment}
setListComment={setListComment}
countComment={data?.count}
setCountComment={(val: any) => {
setData((prev: any) => ({
...prev,
count: val,
}));
}}
/>
</DrawerCustom>
</>
);
}

View File

@@ -0,0 +1,306 @@
import {
BoxButtonOnFooter,
ButtonCustom,
DrawerCustom,
LoaderCustom,
NewWrapper,
Spacing,
TextAreaCustom,
TextCustom,
TextInputCustom,
ViewWrapper,
} from "@/components";
import CustomSkeleton from "@/components/_ShareComponent/SkeletonCustom";
import AlertWarning from "@/components/Alert/AlertWarning";
import { MainColor } from "@/constants/color-palet";
import { useAuth } from "@/hooks/use-auth";
import { usePagination } from "@/hooks/use-pagination";
import { createPaginationComponents } from "@/helpers/paginationHelpers";
import Forum_CommentarBoxSection from "@/screens/Forum/CommentarBoxSection";
import Forum_BoxDetailSection from "@/screens/Forum/DiscussionBoxSection";
import Forum_MenuDrawerBerandaSection from "@/screens/Forum/MenuDrawerSection.tsx/MenuBeranda";
import Forum_MenuDrawerCommentar from "@/screens/Forum/MenuDrawerSection.tsx/MenuCommentar";
import {
apiForumCreateComment,
apiForumGetComment,
apiForumGetOne,
apiForumUpdateStatus,
} from "@/service/api-client/api-forum";
import { TypeForum_CommentProps } from "@/types/type-forum";
import { censorText, isBadContent } from "@/utils/badWordsIndonesia";
import { useFocusEffect, useLocalSearchParams } from "expo-router";
import _ from "lodash";
import { useCallback, useEffect, useState } from "react";
import { RefreshControl } from "react-native";
import ListSkeletonComponent from "@/components/_ShareComponent/ListSkeletonComponent";
export default function DetailForum2() {
const { id } = useLocalSearchParams();
const { user } = useAuth();
const [openDrawer, setOpenDrawer] = useState(false);
const [data, setData] = useState<any | null>(null);
const [isLoadingComment, setLoadingComment] = useState(false);
// Status
const [status, setStatus] = useState("");
const [text, setText] = useState("");
const [authorId, setAuthorId] = useState("");
const [dataId, setDataId] = useState("");
// Comentar
const [openDrawerCommentar, setOpenDrawerCommentar] = useState(false);
const [commentId, setCommentId] = useState("");
const [commentAuthorId, setCommentAuthorId] = useState("");
// Initialize pagination for comments
const commentPagination = usePagination({
fetchFunction: async (page) => {
return await apiForumGetComment({
id: id as string,
page: String(page), // API expects string
});
},
pageSize: 5,
dependencies: [id],
onError: (error) => console.error("[ERROR] Fetch forum comment:", error),
});
useFocusEffect(
useCallback(() => {
setTimeout(() => {
onLoadData(id as string);
}, 3000);
}, [id]),
);
useEffect(() => {
// Reset and load first page of comments when id changes
commentPagination.reset();
commentPagination.onRefresh();
}, [id]);
const onLoadData = async (id: string) => {
try {
const response = await apiForumGetOne({ id });
setData(response.data);
} catch (error) {
console.log("[ERROR]", error);
}
};
// Update Status
const handlerUpdateStatus = async (value: any) => {
try {
const response = await apiForumUpdateStatus({
id: id as string,
data: value,
});
if (response.success) {
setStatus(response.data);
setData({
...data,
ForumMaster_StatusPosting: {
status: response.data,
},
});
}
} catch (error) {
console.log("[ERROR]", error);
}
};
// Create Commentar
const handlerCreateCommentar = async () => {
const cencorContent = censorText(text);
const newData = {
comment: cencorContent,
authorId: user?.id,
};
try {
setLoadingComment(true);
const response = await apiForumCreateComment({
id: id as string,
data: newData,
});
if (response.success) {
setText("");
const newComment = {
id: response.data.id,
isActive: response.data.isActive,
komentar: response.data.komentar,
createdAt: response.data.createdAt,
authorId: response.data.authorId,
Author: response.data.Author,
};
// Add new comment to the top of the list
commentPagination.setListData((prev) => [newComment, ...prev]);
setData({
...data,
count: data.count + 1,
});
}
} catch (error) {
console.log("[ERROR]", error);
} finally {
setLoadingComment(false);
}
};
const headerComponent = () =>
// Box Posting
!data ? (
<CustomSkeleton height={200} />
) : (
<>
{/* Area Posting */}
<Forum_BoxDetailSection
data={data}
onSetData={() => {
setOpenDrawer(true);
setStatus(data.ForumMaster_StatusPosting?.status);
setAuthorId(data.Author?.id);
setDataId(data.id);
}}
/>
{/* Area Commentar */}
{data?.ForumMaster_StatusPosting?.status === "Open" && (
<>
<TextAreaCustom
placeholder="Ketik diskusi anda..."
maxLength={1000}
showCount
value={text}
onChangeText={setText}
style={{
marginBottom: 0,
}}
/>
<ButtonCustom
isLoading={isLoadingComment}
style={{
alignSelf: "flex-end",
}}
onPress={() => {
handlerCreateCommentar();
}}
>
Balas
</ButtonCustom>
</>
)}
</>
);
// Render individual comment item
const renderCommentItem = ({ item }: { item: TypeForum_CommentProps }) =>
!data && !commentPagination.listData ? (
<ListSkeletonComponent />
) : (
<Forum_CommentarBoxSection
key={item.id}
data={item}
onSetData={(value) => {
setCommentId(value.setCommentId);
setOpenDrawerCommentar(value.setOpenDrawer);
setCommentAuthorId(value.setCommentAuthorId);
}}
/>
);
// Generate pagination components using helper
const { ListEmptyComponent, ListFooterComponent } =
createPaginationComponents({
loading: commentPagination.loading,
refreshing: commentPagination.refreshing,
listData: commentPagination.listData,
isInitialLoad: commentPagination.isInitialLoad,
emptyMessage: "Tidak ada komentar",
skeletonCount: 3,
skeletonHeight: 120,
});
return (
<>
<NewWrapper
// headerComponent={}
listData={commentPagination.listData}
renderItem={renderCommentItem}
refreshControl={
<RefreshControl
// IOS
tintColor={MainColor.yellow}
// Android
colors={[MainColor.yellow]}
progressBackgroundColor={MainColor.yellow}
refreshing={commentPagination.refreshing}
onRefresh={commentPagination.onRefresh}
/>
}
onEndReached={commentPagination.loadMore}
ListHeaderComponent={
<>
{/* <Spacing height={40} />
<TextCustom color="gray">Komentar :</TextCustom>
<Spacing height={5} /> */}
{headerComponent()}
<Spacing />
</>
}
ListFooterComponent={ListFooterComponent}
ListEmptyComponent={ListEmptyComponent}
/>
{/* Posting Drawer */}
<DrawerCustom
height={"auto"}
isVisible={openDrawer}
closeDrawer={() => setOpenDrawer(false)}
>
<Forum_MenuDrawerBerandaSection
id={dataId}
authorUsername={data?.Author?.username as string}
status={status}
setIsDrawerOpen={() => {
setOpenDrawer(false);
}}
authorId={authorId}
handlerUpdateStatus={(value: any) => {
handlerUpdateStatus(value);
}}
/>
</DrawerCustom>
{/* Commentar Drawer */}
<DrawerCustom
height={"auto"}
isVisible={openDrawerCommentar}
closeDrawer={() => setOpenDrawerCommentar(false)}
>
<Forum_MenuDrawerCommentar
id={commentId as string}
commentId={commentId}
commentAuthorId={commentAuthorId}
setIsDrawerOpen={() => {
setOpenDrawerCommentar(false);
}}
listComment={commentPagination.listData}
setListComment={commentPagination.setListData}
countComment={data?.count}
setCountComment={(val: any) => {
setData((prev: any) => ({
...prev,
count: val,
}));
}}
/>
</DrawerCustom>
</>
);
}

View File

@@ -15,38 +15,49 @@ import NoDataText from "@/components/_ShareComponent/NoDataText";
import CustomSkeleton from "@/components/_ShareComponent/SkeletonCustom";
import { MainColor } from "@/constants/color-palet";
import { useAuth } from "@/hooks/use-auth";
import { usePagination } from "@/hooks/use-pagination";
import { createPaginationComponents } from "@/helpers/paginationHelpers";
import Forum_BoxDetailSection from "@/screens/Forum/DiscussionBoxSection";
import { apiForumGetAll } from "@/service/api-client/api-forum";
import { apiUser } from "@/service/api-client/api-user";
import { router, useLocalSearchParams } from "expo-router";
import _ from "lodash";
import { useCallback, useEffect, useState } from "react";
import { useEffect, useState } from "react";
import { RefreshControl, View } from "react-native";
const PAGE_SIZE = 5;
export default function View_Forumku2() {
const { id } = useLocalSearchParams();
const { user } = useAuth();
const [listData, setListData] = useState<any[]>([]);
const [dataUser, setDataUser] = useState<any | null>(null);
const [loading, setLoading] = useState(false);
const [refreshing, setRefreshing] = useState(false);
const [hasMore, setHasMore] = useState(true);
const [page, setPage] = useState(1);
const [count, setCount] = useState(0);
// Initialize pagination hook
const pagination = usePagination({
fetchFunction: async (page) => {
if (!user?.id) throw new Error("User not authenticated");
const response = await apiForumGetAll({
category: "forumku",
authorId: id as string,
userLoginId: user.id,
page: String(page), // API terima string
});
// Update count when fetching page 1
if (page === 1) {
setCount(response.data.count);
}
return response.data;
},
pageSize: 5,
dependencies: [user?.id],
});
useEffect(() => {
onLoadDataProfile(id as string);
}, [id]);
useEffect(() => {
setPage(1);
setListData([]);
setHasMore(true);
fetchData(1, true);
}, [user?.id]);
const onLoadDataProfile = async (id: string) => {
try {
const response = await apiUser(id);
@@ -58,54 +69,6 @@ export default function View_Forumku2() {
}
};
// 🔹 Fungsi fetch data
const fetchData = async (pageNumber: number, clear: boolean) => {
if (!user?.id) return;
// Cegah multiple call
if (!clear && (loading || refreshing)) return;
const isRefresh = clear;
if (isRefresh) setRefreshing(true);
if (!isRefresh) setLoading(true);
try {
const response = await apiForumGetAll({
category: "forumku",
authorId: id as string,
userLoginId: user.id,
page: String(pageNumber), // API terima string
});
const newData = response.data.data || [];
setListData((prev) => {
const current = Array.isArray(prev) ? prev : [];
return clear ? newData : [...current, ...newData];
});
setHasMore(newData.length === PAGE_SIZE);
setPage(pageNumber);
setCount(response.data.count);
} catch (error) {
console.error("[ERROR] Fetch forum:", error);
setHasMore(false);
} finally {
setRefreshing(false);
setLoading(false);
}
};
// 🔹 Pull-to-refresh
const onRefresh = useCallback(() => {
fetchData(1, true);
}, [user?.id]);
// 🔹 Infinite scroll
const loadMore = useCallback(() => {
if (hasMore && !loading && !refreshing) {
fetchData(page + 1, false);
}
}, [hasMore, loading, refreshing, page, user?.id]);
const randerHeaderComponent = () => (
<>
<CenterCustom>
@@ -144,39 +107,16 @@ export default function View_Forumku2() {
/>
);
// Skeleton List (untuk initial load)
const SkeletonListComponent = () => (
<View style={{ flex: 1 }}>
<StackCustom>
{Array.from({ length: 5 }).map((_, i) => (
<CustomSkeleton height={200} key={i} />
))}
</StackCustom>
</View>
);
// Komponen Empty
const EmptyComponent = () => (
<View
style={{
flex: 1,
justifyContent: "center",
alignItems: "center",
padding: 20,
}}
>
<NoDataText />
</View>
);
// 🔹 Komponen Footer List (loading indicator)
const ListFooterComponent =
loading && !refreshing && listData.length > 0 ? (
<View style={{ paddingVertical: 16, alignItems: "center" }}>
{/* <Text style={{ color: "#aaa", fontSize: 14 }}>Memuat diskusi...</Text> */}
<LoaderCustom />
</View>
) : null;
// Generate pagination components using helper
const { ListEmptyComponent, ListFooterComponent } = createPaginationComponents({
loading: pagination.loading,
refreshing: pagination.refreshing,
listData: pagination.listData,
isInitialLoad: pagination.isInitialLoad,
emptyMessage: "Tidak ada postingan",
skeletonCount: 5,
skeletonHeight: 200,
});
return (
<>
@@ -190,7 +130,7 @@ export default function View_Forumku2() {
/>
)
}
listData={listData}
listData={pagination.listData}
renderItem={renderList}
refreshControl={
<RefreshControl
@@ -199,16 +139,14 @@ export default function View_Forumku2() {
// Android
colors={[MainColor.yellow]}
progressBackgroundColor={MainColor.yellow}
refreshing={refreshing}
onRefresh={onRefresh}
refreshing={pagination.refreshing}
onRefresh={pagination.onRefresh}
/>
}
onEndReached={loadMore}
onEndReached={pagination.loadMore}
ListHeaderComponent={randerHeaderComponent()}
ListFooterComponent={ListFooterComponent}
ListEmptyComponent={
loading && _.isEmpty(listData) ? <SkeletonListComponent /> : <EmptyComponent />
}
ListEmptyComponent={ListEmptyComponent}
/>
</>
);

View File

@@ -102,9 +102,9 @@ export async function apiForumCreateComment({
}
}
export async function apiForumGetComment({ id }: { id: string }) {
export async function apiForumGetComment({ id, page = "1" }: { id: string, page?: string }) {
try {
const response = await apiConfig.get(`/mobile/forum/${id}/comment`);
const response = await apiConfig.get(`/mobile/forum/${id}/comment?page=${page}`);
return response.data;
} catch (error) {
throw error;