Optimization (audited across src/, typecheck green): - Crash guards: PurchaseAuction data?.result + stray text-node, AuctionItemDetail length>0, LiveAuction user?.contractorId, KYC params guard - Hoisted render-defined components: Login MinimalInput (keyboard-focus bug), TenderFullDetail Section/InfoBox (subtree remount) - Memoized hot paths: Spreadsheet ColumnHeaderRow/DataRow, list makeStyles, shared infinite-query select (stops per-render flatMap) - Removed hot-path/in-list console logs; SkeletonList 10->1 shimmer UI/theme: themed AppToast, redesigned sidebar drawer, list empty states, KYC WebView cleanup Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
130 lines
5.2 KiB
TypeScript
130 lines
5.2 KiB
TypeScript
import React, { useState, useCallback, useRef } from 'react';
|
|
import { View, FlatList, RefreshControl, ActivityIndicator } from 'react-native';
|
|
import { useNavigation } from '@react-navigation/native';
|
|
import moment from 'moment';
|
|
import { useFetchLiveAuctionPage } from '../../Services/ApiCall/Hooks';
|
|
import PurchaseAuctionTile from './PurchaseAuctionTile';
|
|
import { useDispatch } from 'react-redux';
|
|
import { setAuction } from '../../Redux/Slices/SelectedAuction';
|
|
import { SearchInput } from '../../components/SearchInput';
|
|
import { Skeleton, EmptyState } from '../../components';
|
|
import { allBooleanProps } from './types/types';
|
|
import FilterModal from './Modal/FilterModal';
|
|
import NavigationString from '../../Constant/NavigationString';
|
|
import colors from '../../Constant/Color';
|
|
|
|
|
|
const PurchaseAuction = () => {
|
|
const navigation = useNavigation() as any;
|
|
const Dispatch = useDispatch()
|
|
const onEndReachedCalledDuringMomentum = useRef(true);
|
|
const [filter, setFilter] = useState({})
|
|
const { data, fetchNextPage, hasNextPage, isFetchingNextPage, isRefetching, refetch, isPending }: any = useFetchLiveAuctionPage(filter);
|
|
const [allBoolean, setAllBoolean] = useState<allBooleanProps>({ isShowFilter: false })
|
|
const format = 'DD-MM-YYYY HH:mm:ss';
|
|
|
|
function isAuctionActive(item: any): boolean {
|
|
const now = moment();
|
|
const bidding = item?.auctionStatus === 'Amended'
|
|
? item?.amendedkeyDates?.auctionBidding
|
|
: item?.keyDates?.auctionBidding;
|
|
const start = moment(bidding?.startDateTime, format);
|
|
const end = moment(bidding?.endDateTime, format);
|
|
|
|
if (!start.isValid() || !end.isValid()) return false;
|
|
|
|
return now.isBetween(start, end, undefined, '[]');
|
|
}
|
|
|
|
function isAuctionEnd(item: any): boolean {
|
|
const now = moment();
|
|
const end = moment(item.auctionStatus === "Amended" ? item?.amendedkeyDates?.auctionBidding?.endDateTime : item?.keyDates?.auctionBidding?.endDateTime, format);
|
|
|
|
if (!end.isValid()) return false;
|
|
|
|
return now.isAfter(end);
|
|
}
|
|
|
|
const loadMore = useCallback(() => {
|
|
if (onEndReachedCalledDuringMomentum.current) return;
|
|
|
|
if (!hasNextPage || isFetchingNextPage) return;
|
|
|
|
fetchNextPage();
|
|
onEndReachedCalledDuringMomentum.current = true;
|
|
}, [hasNextPage, isFetchingNextPage, fetchNextPage]);
|
|
|
|
const renderFooter = useCallback(() => {
|
|
if (!isFetchingNextPage) return null;
|
|
return (
|
|
<View style={{ paddingVertical: 12 }}>
|
|
<ActivityIndicator size="small" color={colors.primary} />
|
|
</View>
|
|
);
|
|
}, [isFetchingNextPage]);
|
|
|
|
|
|
return (
|
|
<View style={{ flex: 1 }}>
|
|
<SearchInput onChange={(val) => { setFilter(prev => ({ ...prev, auctionNumber: val })) }} placeholder='Search....' filterPress={() => { setAllBoolean((prev: any) => ({ ...prev, isShowFilter: !prev.isShowFilter })) }} filter={false} />
|
|
{isPending ? <Skeleton />
|
|
:
|
|
<FlatList
|
|
data={data?.result ?? []}
|
|
renderItem={({ item }) => (
|
|
<PurchaseAuctionTile
|
|
item={item}
|
|
onPressDetails={() => {
|
|
}}
|
|
onStartBidding={() => {
|
|
Dispatch(setAuction(item))
|
|
navigation.navigate(NavigationString.Emd)
|
|
}}
|
|
buttonEnable={isAuctionActive(item)}
|
|
isAuctionEnd={isAuctionEnd(item)}
|
|
/>
|
|
)}
|
|
showsVerticalScrollIndicator={false}
|
|
contentContainerStyle={{ flexGrow: 1, paddingBottom: 50, paddingTop: 10 }}
|
|
|
|
onEndReached={loadMore}
|
|
onEndReachedThreshold={0.1}
|
|
|
|
onMomentumScrollBegin={() => {
|
|
onEndReachedCalledDuringMomentum.current = false;
|
|
}}
|
|
|
|
ListFooterComponent={renderFooter}
|
|
ListEmptyComponent={
|
|
<EmptyState
|
|
title="No purchased auctions found"
|
|
subtitle="Auctions you purchase will appear here."
|
|
/>
|
|
}
|
|
|
|
refreshControl={
|
|
<RefreshControl
|
|
refreshing={isRefetching}
|
|
onRefresh={refetch}
|
|
tintColor="transparent"
|
|
colors={[colors.primary]}
|
|
/>
|
|
}
|
|
|
|
initialNumToRender={6}
|
|
maxToRenderPerBatch={6}
|
|
windowSize={7}
|
|
removeClippedSubviews
|
|
/>
|
|
}
|
|
{allBoolean.isShowFilter && <View style={{ flex: 1 }}>
|
|
<FilterModal visible={allBoolean.isShowFilter} onClose={() => { setAllBoolean((prev: any) => ({ ...prev, isShowFilter: false })) }} setFilter={setFilter} filter={filter} />
|
|
</View>}
|
|
|
|
</View>
|
|
);
|
|
};
|
|
|
|
export default PurchaseAuction;
|
|
|