Files
info-target-mobile/screens/announcements/ui/AnnouncementsList.tsx
Samandar Turgunboyev 124798419b fitst commit
2026-01-28 18:26:50 +05:00

116 lines
3.5 KiB
TypeScript

import { useTheme } from '@/components/ThemeContext';
import { useInfiniteQuery, useQueryClient } from '@tanstack/react-query';
import React, { useEffect, useRef, useState } from 'react';
import { useTranslation } from 'react-i18next';
import { ActivityIndicator, Animated, RefreshControl, StyleSheet, Text, View } from 'react-native';
import { SafeAreaView } from 'react-native-safe-area-context';
import { announcement_api } from '../lib/api';
import { AnnouncementListBodyRes } from '../lib/type';
import AnnouncementCard from './AnnouncementCard';
import EmptyState from './EmptyState';
export default function DashboardScreen() {
const [announcements, setAnnouncements] = useState<AnnouncementListBodyRes[]>([]);
const queryClient = useQueryClient();
const fadeAnim = useRef(new Animated.Value(0)).current;
const { isDark } = useTheme();
const { t } = useTranslation();
const theme = {
background: isDark ? '#0f172a' : '#f8fafc',
primary: '#2563eb',
loaderBg: isDark ? '#0f172a' : '#ffffff',
};
const { data, isLoading, isRefetching, fetchNextPage, hasNextPage } = useInfiniteQuery({
queryKey: ['announcements_list'],
queryFn: async ({ pageParam = 1 }) => {
const res = await announcement_api.list({ page: pageParam, page_size: 10 });
return res.data.data;
},
getNextPageParam: (lastPage) =>
lastPage.current_page < lastPage.total_pages ? lastPage.current_page + 1 : undefined,
initialPageParam: 1,
});
const allAnnouncements = data?.pages.flatMap((p) => p.results) ?? [];
useEffect(() => {
setAnnouncements(allAnnouncements);
fadeAnim.setValue(0);
Animated.timing(fadeAnim, {
toValue: 1,
duration: 300,
useNativeDriver: true,
}).start();
}, [allAnnouncements]);
const onRefresh = () => {
queryClient.refetchQueries({ queryKey: ['announcements_list'] });
};
const loadMore = () => {
if (hasNextPage) fetchNextPage();
};
if (isLoading) {
return (
<SafeAreaView style={[styles.container, { backgroundColor: theme.background }]}>
<View style={[styles.loaderBox, { backgroundColor: theme.loaderBg }]}>
<ActivityIndicator size="large" color={theme.primary} />
</View>
</SafeAreaView>
);
}
return (
<View style={[styles.container, { backgroundColor: theme.background }]}>
<Text style={{ color: 'white', fontSize: 20, marginBottom: 10 }}>
{t("E'lonlar ro'yxati")}
</Text>
{announcements.length > 0 ? (
<Animated.FlatList
style={{ flex: 1 }}
data={announcements}
keyExtractor={(item) => item.id.toString()}
numColumns={2}
columnWrapperStyle={styles.columnWrapper}
renderItem={({ item }) => <AnnouncementCard announcement={item} />}
refreshControl={
<RefreshControl
refreshing={isRefetching}
onRefresh={onRefresh}
colors={[theme.primary]}
tintColor={theme.primary}
progressBackgroundColor={theme.background}
/>
}
onEndReached={loadMore}
onEndReachedThreshold={0.3}
showsVerticalScrollIndicator={false}
/>
) : (
<EmptyState onRefresh={onRefresh} isRefreshing={isRefetching} />
)}
</View>
);
}
const styles = StyleSheet.create({
container: {
flex: 1,
paddingHorizontal: 16,
marginTop: 20,
},
loaderBox: {
flex: 1,
justifyContent: 'center',
alignItems: 'center',
},
columnWrapper: {
justifyContent: 'space-between',
gap: 12,
},
});