116 lines
3.5 KiB
TypeScript
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,
|
|
},
|
|
});
|