Files
hipmi-mobile/screens/UserSeach/MainView_V2.tsx
bagasbanuna b3bfbc0f7e Fix Infinite Load Data
Forum & User Search – User
- app/(application)/(user)/forum/index.tsx
- app/(application)/(user)/user-search/index.tsx

Global & Core
- app/+not-found.tsx
- screens/RootLayout/AppRoot.tsx
- constants/constans-value.ts

Component
- components/Image/AvatarComp.tsx

API Client
- service/api-client/api-user.ts

Untracked Files
- QWEN.md
- helpers/
- hooks/use-pagination.tsx
- screens/Forum/ViewBeranda3.tsx
- screens/UserSeach/

### No Issue
2026-01-29 11:36:24 +08:00

175 lines
4.5 KiB
TypeScript

import {
AvatarComp,
ClickableCustom,
Grid,
NewWrapper,
StackCustom,
TextCustom,
TextInputCustom
} from "@/components";
import { MainColor } from "@/constants/color-palet";
import { ICON_SIZE_SMALL, PAGINATION_DEFAULT_TAKE } from "@/constants/constans-value";
import { usePagination } from "@/hooks/use-pagination";
import { apiAllUser } from "@/service/api-client/api-user";
import { Ionicons } from "@expo/vector-icons";
import { router, useFocusEffect } from "expo-router";
import _ from "lodash";
import { useCallback, useRef, useState } from "react";
import { RefreshControl, View } from "react-native";
import { createPaginationComponents } from "@/helpers/paginationHelpers";
export default function UserSearchMainView_V2() {
const isInitialMount = useRef(true);
const [search, setSearch] = useState("");
const {
listData,
loading,
refreshing,
hasMore,
onRefresh,
loadMore,
isInitialLoad
} = usePagination({
fetchFunction: async (page, searchQuery) => {
const response = await apiAllUser({
page: String(page),
search: searchQuery || "",
});
return response;
},
pageSize: PAGINATION_DEFAULT_TAKE,
searchQuery: search,
});
// 🔁 Refresh otomatis saat kembali ke halaman ini
useFocusEffect(
useCallback(() => {
if (isInitialMount.current) {
// Skip saat pertama kali mount
isInitialMount.current = false;
return;
}
// Hanya refresh saat kembali dari screen lain
onRefresh();
}, [onRefresh]),
);
const renderHeader = () => (
<>
<TextInputCustom
value={search}
onChangeText={setSearch}
iconLeft={
<Ionicons
name="search"
size={ICON_SIZE_SMALL}
color={MainColor.placeholder}
/>
}
placeholder="Cari Pengguna"
borderRadius={50}
containerStyle={{ marginBottom: 0 }}
/>
</>
);
const renderItem = ({ item }: { item: any }) => (
<View
style={{
backgroundColor: MainColor.soft_darkblue,
borderRadius: 8,
padding: 12,
marginBottom: 10,
elevation: 2,
shadowColor: '#000',
shadowOffset: { width: 0, height: 1 },
shadowOpacity: 0.2,
shadowRadius: 2,
// height: 100
}}
>
<ClickableCustom
onPress={() => {
console.log("Ke Profile");
router.push(`/profile/${item?.Profile?.id}`);
}}
>
<Grid>
<Grid.Col span={2}>
<AvatarComp fileId={item?.Profile?.imageId} size="base" />
</Grid.Col>
<Grid.Col span={9}>
<StackCustom gap={"sm"}>
<TextCustom size="large">{item?.username}</TextCustom>
<TextCustom size="small">+{item?.nomor}</TextCustom>
{item?.Profile?.businessField && (
<TextCustom size="small">
{item?.Profile?.businessField}
</TextCustom>
)}
</StackCustom>
</Grid.Col>
<Grid.Col
span={1}
style={{
justifyContent: "center",
alignItems: "flex-end",
}}
>
<Ionicons
name="chevron-forward"
size={ICON_SIZE_SMALL}
color={MainColor.placeholder}
/>
</Grid.Col>
</Grid>
</ClickableCustom>
</View>
);
const { ListEmptyComponent, ListFooterComponent } = createPaginationComponents({
loading,
refreshing,
listData,
searchQuery: search,
emptyMessage: "Tidak ada pengguna ditemukan",
emptySearchMessage: "Tidak ada hasil pencarian",
skeletonCount: 5,
skeletonHeight: 150,
loadingFooterText: "Memuat lebih banyak pengguna...",
isInitialLoad
});
return (
<>
<NewWrapper
headerComponent={renderHeader()}
listData={listData}
renderItem={renderItem}
onEndReached={loadMore}
refreshControl={
<RefreshControl
progressBackgroundColor={MainColor.yellow}
refreshing={refreshing}
onRefresh={onRefresh}
/>
}
ListFooterComponent={ListFooterComponent}
ListEmptyComponent={ListEmptyComponent}
/>
</>
);
// return (
// <>
// <ViewWrapper>
// <View style={{ padding: 16 }}>
// <TextCustom>{JSON.stringify(listData, null, 2)}</TextCustom>
// </View>
// </ViewWrapper>
// </>
// );
}