feat: lazy loaded tanks earch

This commit is contained in:
TrèsAbhi committed 2024-07-09 21:16:20 -05:00
1 parent 6a2631ea72
commit 2286a398cf
3 files changed
+51 -16

No files matched your search

@@ -1,10 +1,25 @@
import { Flex, Skeleton } from '@radix-ui/themes'; import { Flex, Skeleton } from '@radix-ui/themes';
import { memo, useRef } from 'react';
import { useIntersection } from '../../../../hooks/useIntersection';
export function SkeletonTankCard() { interface SkeletonTankCardProps {
return ( onIntersection?: () => void;
<Flex direction="column" gap="2">
<Skeleton height="64px" />
<Skeleton height="1em" />
</Flex>
);
} }
export const SkeletonTankCard = memo(
({ onIntersection }: SkeletonTankCardProps) => {
const card = useRef<HTMLDivElement>(null);
useIntersection(() => onIntersection?.(), card, {
disabled: onIntersection === undefined,
});
return (
<Flex direction="column" gap="2" ref={card}>
<Skeleton height="64px" />
<Skeleton height="1em" />
</Flex>
);
},
() => true,
);
@@ -1,7 +1,9 @@
'use client';
import { Callout, Flex, FlexProps, Link, Text } from '@radix-ui/themes'; import { Callout, Flex, FlexProps, Link, Text } from '@radix-ui/themes';
import { go } from 'fuzzysort'; import { go } from 'fuzzysort';
import { times } from 'lodash'; import { times } from 'lodash';
import { memo, use, useMemo } from 'react'; import { memo, use, useEffect, useMemo, useState } from 'react';
import { AdMidSectionResponsive } from '../../../../../components/AdMidSectionResponsive'; import { AdMidSectionResponsive } from '../../../../../components/AdMidSectionResponsive';
import { ExperimentIcon } from '../../../../../components/ExperimentIcon'; import { ExperimentIcon } from '../../../../../components/ExperimentIcon';
import { TANK_CLASSES } from '../../../../../components/Tanks/components/Item/constants'; import { TANK_CLASSES } from '../../../../../components/Tanks/components/Item/constants';
@@ -40,6 +42,9 @@ type TankSearchProps = Omit<FlexProps, 'onSelect'> & {
onSelectAll?: (tanks: TankDefinition[]) => void; onSelectAll?: (tanks: TankDefinition[]) => void;
}; };
const PREVIEW_COUNT = 25;
const DEFAULT_LOADED_CARDS = 75;
export const TankSearch = memo<TankSearchProps>( export const TankSearch = memo<TankSearchProps>(
({ compact, onSelect, onSelectAll, ...props }) => { ({ compact, onSelect, onSelectAll, ...props }) => {
const exempt = useAdExempt(); const exempt = useAdExempt();
@@ -50,7 +55,7 @@ export const TankSearch = memo<TankSearchProps>(
const awaitedTankNames = use(tankNames); const awaitedTankNames = use(tankNames);
const filters = TankFilters.use(); const filters = TankFilters.use();
const sort = TankopediaSort.use(); const sort = TankopediaSort.use();
const tanks = useMemo(() => { const tanksFiltered = useMemo(() => {
if (filters.search === undefined) { if (filters.search === undefined) {
const filtered = awaitedTanksDefinitionsArray.filter((tank) => const filtered = awaitedTanksDefinitionsArray.filter((tank) =>
filterTank(filters, tank), filterTank(filters, tank),
@@ -379,6 +384,12 @@ export const TankSearch = memo<TankSearchProps>(
return searchedTanks; return searchedTanks;
} }
}, [filters]); }, [filters]);
const [loadedRows, setLoadedRows] = useState(DEFAULT_LOADED_CARDS);
const tanks = tanksFiltered.slice(0, loadedRows);
useEffect(() => {
setLoadedRows(DEFAULT_LOADED_CARDS);
}, [filters, sort]);
return ( return (
<Flex direction="column" gap="4" flexGrow="1" {...props}> <Flex direction="column" gap="4" flexGrow="1" {...props}>
@@ -447,6 +458,22 @@ export const TankSearch = memo<TankSearchProps>(
)} )}
</> </>
))} ))}
{times(
Math.min(PREVIEW_COUNT, tanksFiltered.length - loadedRows),
(index) => {
return (
<SkeletonTankCard
key={index}
onIntersection={() => {
setLoadedRows((state) =>
Math.min(state + 2, tanksFiltered.length),
);
}}
/>
);
},
)}
</TankCardWrapper> </TankCardWrapper>
)} )}
-7
View File
@@ -1,19 +1,12 @@
'use client';
import { AdMidSectionResponsive } from '../../../components/AdMidSectionResponsive';
import PageWrapper from '../../../components/PageWrapper'; import PageWrapper from '../../../components/PageWrapper';
import { useAdExempt } from '../../../hooks/useAdExempt';
import * as TankFilters from '../../../stores/tankFilters'; import * as TankFilters from '../../../stores/tankFilters';
import { TankSearch } from './components/TankSearch'; import { TankSearch } from './components/TankSearch';
export default function Page() { export default function Page() {
const exempt = useAdExempt();
return ( return (
<TankFilters.Provider> <TankFilters.Provider>
<PageWrapper color="purple" size={1028}> <PageWrapper color="purple" size={1028}>
<TankSearch /> <TankSearch />
{!exempt && <AdMidSectionResponsive />}
</PageWrapper> </PageWrapper>
</TankFilters.Provider> </TankFilters.Provider>
); );