Files
mobile-darmasaba/app/(application)/announcement/index.tsx
amaliadwiy ccf8ee1caf upd: caching data
Deskripsi:
- update caching pada fitur utama -yg fitur divisi belom
2026-04-20 14:23:14 +08:00

133 lines
4.8 KiB
TypeScript

import BorderBottomItem from "@/components/borderBottomItem";
import InputSearch from "@/components/inputSearch";
import SkeletonContent from "@/components/skeletonContent";
import Text from '@/components/Text';
import Styles from "@/constants/Styles";
import { apiGetAnnouncement } from "@/lib/api";
import { useAuthSession } from "@/providers/AuthProvider";
import { useTheme } from "@/providers/ThemeProvider";
import { MaterialIcons } from "@expo/vector-icons";
import { useInfiniteQuery } from "@tanstack/react-query";
import { router } from "expo-router";
import { useEffect, useMemo, useState } from "react";
import { RefreshControl, View, VirtualizedList } from "react-native";
import { useSelector } from "react-redux";
type Props = {
id: string,
title: string,
desc: string,
createdAt: string
}
export default function Announcement() {
const { token, decryptToken } = useAuthSession()
const { colors } = useTheme();
const [search, setSearch] = useState('')
const update = useSelector((state: any) => state.announcementUpdate)
const arrSkeleton = Array.from({ length: 5 }, (_, index) => index)
// TanStack Query Infinite Query
const {
data,
fetchNextPage,
hasNextPage,
isFetchingNextPage,
isLoading,
refetch,
isRefetching
} = useInfiniteQuery({
queryKey: ['announcements', search],
queryFn: async ({ pageParam = 1 }) => {
const hasil = await decryptToken(String(token?.current))
const response = await apiGetAnnouncement({
user: hasil,
search: search,
page: pageParam
})
return response.data
},
initialPageParam: 1,
getNextPageParam: (lastPage, allPages) => {
return lastPage.length > 0 ? allPages.length + 1 : undefined
},
})
// Trigger refetch when Redux state 'update' changes
useEffect(() => {
refetch()
}, [update, refetch])
// Flatten data from pages
const flattenedData = useMemo(() => {
return data?.pages.flat() || []
}, [data])
const loadMoreData = () => {
if (hasNextPage && !isFetchingNextPage) {
fetchNextPage()
}
};
const getItem = (_data: unknown, index: number): Props => ({
id: flattenedData[index].id,
title: flattenedData[index].title,
desc: flattenedData[index].desc,
createdAt: flattenedData[index].createdAt,
})
return (
<View style={[Styles.p15, Styles.flex1, { backgroundColor: colors.background }]}>
<View>
<InputSearch onChange={setSearch} />
</View>
<View style={[Styles.flex2, Styles.mt05]}>
{
isLoading && !flattenedData.length ?
arrSkeleton.map((item, index) => {
return (
<SkeletonContent key={index} />
)
})
:
flattenedData.length > 0
?
<VirtualizedList
data={flattenedData}
getItemCount={() => flattenedData.length}
getItem={getItem}
renderItem={({ item, index }: { item: Props, index: number }) => {
return (
<BorderBottomItem
key={index}
onPress={() => { router.push(`/announcement/${item.id}`) }}
borderType="bottom"
bgColor="transparent"
icon={
<MaterialIcons name="campaign" size={25} color={colors.text} />
}
title={item.title}
desc={item.desc.replace(/<[^>]*>?/gm, '').replace(/\r?\n|\r/g, ' ')}
rightTopInfo={item.createdAt}
/>
)
}}
keyExtractor={(item, index) => String(item.id || index)}
onEndReached={loadMoreData}
onEndReachedThreshold={0.5}
showsVerticalScrollIndicator={false}
refreshControl={
<RefreshControl
refreshing={isRefetching && !isFetchingNextPage}
onRefresh={refetch}
tintColor={colors.icon}
/>
}
/>
:
<Text style={[Styles.textDefault, Styles.textCenter, { color: colors.dimmed }]}>Tidak ada pengumuman</Text>
}
</View>
</View>
)
}