import { FlashList, FlashListProps, MasonryFlashList, MasonryFlashListProps, } from "@shopify/flash-list"; import React, { useEffect, useRef } from "react"; import { FlatList, RefreshControl } from "react-native"; import { HFlatList } from "react-native-head-tab-view"; import Animated from "react-native-reanimated"; import { IPropsSwipeListView, SwipeListView, } from "react-native-swipe-list-view"; import { Platform_iOS, useColors, vs } from "../commons"; import { Spinner, View } from "../components"; import { LoadingParams, LoadingType } from "../types"; export type ListProps = FlashListProps & Partial> & MasonryFlashListProps & { refFlatList?: any; status?: LoadingType; reanimated?: boolean; useFlatList?: boolean; useSwipeList?: boolean; useMasonryList?: boolean; hideOverflow?: boolean; showIndicator?: boolean; listWidth?: number; listHeight?: number; hViewIndex?: number; onGetting?: (type: LoadingType, params: LoadingParams) => void; renderSkeleton?: (item: any, index: number) => any; }; export const DEFAULT_LOADING_PARAMS: LoadingParams = { page: 1, size: 10 }; const LOADING_DATA = new Array(Platform_iOS ? 6 : 4).fill(0); const AFlatList = Animated.createAnimatedComponent(FlatList); const AHFlatList = Animated.createAnimatedComponent(HFlatList); const AFlashList = Animated.createAnimatedComponent(FlashList); const AMasonryList = Animated.createAnimatedComponent(MasonryFlashList); const ASwipeListView = Animated.createAnimatedComponent(SwipeListView); export const List = (props: ListProps) => { const { refFlatList, data, status, reanimated, horizontal, useFlatList, useSwipeList, useMasonryList, hideOverflow, showIndicator, listWidth, listHeight, hViewIndex, numColumns = 1, style, onGetting, onEndReached, renderSkeleton, ListEmptyComponent, ListFooterComponent, ItemSeparatorComponent, } = props; const dataLength = data?.length || 0; const _isNormal = !status || status === "normal"; const _isLoading = status === "loading"; const _isRefreshing = status === "refreshing"; const _isLoadingMore = status === "loadingmore"; const enableGrid = !horizontal && numColumns > 1; const enableRefresh = !horizontal && !!status; const colors = useColors(); const isEndOfListRef = useRef(true); const paramsRef = useRef(DEFAULT_LOADING_PARAMS); const _keyExtractor = (item: any, index: number) => { return String(item.id || item.Id || item.ID || index); }; const _onRefresh = () => { if (!_isNormal) return; paramsRef.current = DEFAULT_LOADING_PARAMS; onGetting?.("refreshing", paramsRef.current); }; const _onEndReached = () => { onEndReached?.(); if ( _isNormal && !isEndOfListRef.current && dataLength >= DEFAULT_LOADING_PARAMS.size ) { paramsRef.current = { page: paramsRef.current.page + DEFAULT_LOADING_PARAMS.page, size: paramsRef.current.size + DEFAULT_LOADING_PARAMS.size, }; onGetting?.("loadingmore", paramsRef.current); isEndOfListRef.current = true; } }; const renderRefresh = () => { if (!enableRefresh) return undefined; return ( ); }; const renderSpinner = () => { return ( ); }; const renderLoadingItem = (item: any, index: number) => { return ( <> {!!index && (typeof ItemSeparatorComponent === "function" ? // @ts-ignore ItemSeparatorComponent() : ItemSeparatorComponent)} {enableGrid ? new Array(numColumns).fill(0).map(renderSkeleton!) : renderSkeleton!(item, index)} ); }; const renderLoading = () => { if (renderSkeleton) return <>{LOADING_DATA.map(renderLoadingItem)}; return ( {renderSpinner()} ); }; useEffect(() => { onGetting?.("loading", paramsRef.current); }, []); useEffect(() => { isEndOfListRef.current = false; }, [dataLength]); const _ListEmptyComponent = _isLoading ? renderLoading() : ListEmptyComponent; const _ListFooterComponent = _isLoadingMore ? renderSpinner() : ListFooterComponent; const MyList = typeof hViewIndex === "number" ? reanimated ? AHFlatList : HFlatList : useSwipeList ? reanimated ? ASwipeListView : SwipeListView : useFlatList || (horizontal && !dataLength) ? reanimated ? AFlatList : FlatList : useMasonryList ? reanimated ? AMasonryList : MasonryFlashList : reanimated ? AFlashList : FlashList; return ( ); };