fetching count and players simultaneously

This commit is contained in:
dan13ram
2021-07-07 11:51:28 +05:30
committed by Alec LaLonde
parent 11286feb44
commit 354dc22228
8 changed files with 121 additions and 206 deletions

View File

@@ -2,22 +2,22 @@ import { Avatar, AvatarProps } from '@metafam/ds';
import { PlayerFragmentFragment } from 'graphql/autogen/types';
import React from 'react';
import {
getPlayerImage,
getPlayerName,
hasPlayerImage,
getPlayerImage,
getPlayerName,
hasPlayerImage,
} from 'utils/playerHelpers';
type PlayerAvatarProps = AvatarProps & { player: PlayerFragmentFragment };
export const PlayerAvatar: React.FC<PlayerAvatarProps> = ({
player,
...props
player,
...props
}) => {
const attrs = {
src: getPlayerImage(player),
name: getPlayerName(player),
...props,
};
if (hasPlayerImage(player)) attrs.bg = 'transparent';
const attrs = {
src: getPlayerImage(player),
name: getPlayerName(player),
...props,
};
if (hasPlayerImage(player)) attrs.bg = 'transparent';
return <Avatar {...attrs} />;
return <Avatar {...attrs} />;
};

View File

@@ -87,8 +87,7 @@ const styles: typeof selectStyles = {
};
type Props = {
fetchingPlayers: boolean;
fetchingCount: boolean;
fetching: boolean;
aggregates: PlayerAggregates;
queryVariables: GetPlayersQueryVariables;
setQueryVariable: QueryVariableSetter;
@@ -97,8 +96,7 @@ type Props = {
};
export const PlayerFilter: React.FC<Props> = ({
fetchingPlayers,
fetchingCount,
fetching,
aggregates,
queryVariables,
setQueryVariable,
@@ -117,11 +115,13 @@ export const PlayerFilter: React.FC<Props> = ({
if (search.length >= 2) {
setQueryVariable('search', `%${search}%`);
} else {
setSearch('');
setQueryVariable('search', `%%`);
}
};
const { filtersUsed, onlySearchFilterUsed } = useFiltersUsed(queryVariables);
const filtersUsed = useFiltersUsed(queryVariables);
const isSearchUsed = search.length >= 2 && queryVariables.search !== '%%';
const [isElementSticky, setIsSticky] = useState<boolean>(false);
const ref = useRef<HTMLDivElement>(null);
@@ -212,12 +212,7 @@ export const PlayerFilter: React.FC<Props> = ({
</InputRightElement>
)}
</InputGroup>
<MetaButton
type="submit"
size="lg"
isDisabled={fetchingPlayers || fetchingCount}
px="16"
>
<MetaButton type="submit" size="lg" isDisabled={fetching} px="16">
SEARCH
</MetaButton>
</Stack>
@@ -300,60 +295,67 @@ export const PlayerFilter: React.FC<Props> = ({
{filtersUsed && (
<Flex w="100%" maxW="79rem" justify="space-between">
<Wrap flex="1">
{!onlySearchFilterUsed && (
<>
<WrapItem>
<Flex w="100%" h="100%" justify="center" align="center">
<Text> {`Selected Filters: `}</Text>
</Flex>
</WrapItem>
{playerTypes.map(({ value, label }, index) => (
<WrapItem key={value}>
<FilterTag
label={label}
onRemove={() => {
const newPlayerTypes = playerTypes.slice();
newPlayerTypes.splice(index, 1);
setPlayerTypes(newPlayerTypes);
}}
/>
</WrapItem>
))}
{skills.map(({ value, label }, index) => (
<WrapItem key={value}>
<FilterTag
label={label}
onRemove={() => {
const newSkills = skills.slice();
newSkills.splice(index, 1);
setSkills(newSkills);
}}
/>
</WrapItem>
))}
{timezones.map(({ value, label }, index) => (
<WrapItem key={value}>
<FilterTag
label={label}
onRemove={() => {
const newTimezones = timezones.slice();
newTimezones.splice(index, 1);
setTimezones(newTimezones);
}}
/>
</WrapItem>
))}
{availability && (
<WrapItem>
<FilterTag
label={`Available >${availability.value} h/week`}
onRemove={() => {
setAvailability(null);
}}
/>
</WrapItem>
)}
</>
<WrapItem>
<Flex w="100%" h="100%" justify="center" align="center">
<Text> {`Selected Filters: `}</Text>
</Flex>
</WrapItem>
{isSearchUsed && (
<WrapItem>
<FilterTag
label={search}
onRemove={() => {
setSearch('');
setQueryVariable('search', `%%`);
}}
/>
</WrapItem>
)}
{playerTypes.map(({ value, label }, index) => (
<WrapItem key={value}>
<FilterTag
label={label}
onRemove={() => {
const newPlayerTypes = playerTypes.slice();
newPlayerTypes.splice(index, 1);
setPlayerTypes(newPlayerTypes);
}}
/>
</WrapItem>
))}
{skills.map(({ value, label }, index) => (
<WrapItem key={value}>
<FilterTag
label={label}
onRemove={() => {
const newSkills = skills.slice();
newSkills.splice(index, 1);
setSkills(newSkills);
}}
/>
</WrapItem>
))}
{timezones.map(({ value, label }, index) => (
<WrapItem key={value}>
<FilterTag
label={label}
onRemove={() => {
const newTimezones = timezones.slice();
newTimezones.splice(index, 1);
setTimezones(newTimezones);
}}
/>
</WrapItem>
))}
{availability && (
<WrapItem>
<FilterTag
label={`Available >${availability.value} h/week`}
onRemove={() => {
setAvailability(null);
}}
/>
</WrapItem>
)}
</Wrap>
<Button
@@ -372,7 +374,7 @@ export const PlayerFilter: React.FC<Props> = ({
</Button>
</Flex>
)}
{!fetchingCount && (
{!fetching && (
<Flex justify="space-between" w="100%" maxW="80rem" px="4">
<Text fontWeight="bold" fontSize="xl" w="100%" maxW="79rem">
{`${totalCount} player${totalCount === 1 ? '' : 's'}`}

View File

@@ -18,10 +18,7 @@ import { PlayerTileMemberships } from 'components/Player/PlayerTileMemberships';
import { SkillsTags } from 'components/Skills';
import { PlayerFragmentFragment, Skill } from 'graphql/autogen/types';
import React from 'react';
import {
getPlayerCoverImage,
getPlayerName,
} from 'utils/playerHelpers';
import { getPlayerCoverImage, getPlayerName } from 'utils/playerHelpers';
type Props = {
player: PlayerFragmentFragment;

View File

@@ -4,10 +4,7 @@ import { PlayerFragmentFragment } from 'graphql/autogen/types';
import { getPersonalityInfo } from 'graphql/getPersonalityInfo';
import { PersonalityOption } from 'graphql/types';
import React, { useEffect } from 'react';
import {
getPlayerDescription,
getPlayerName,
} from 'utils/playerHelpers';
import { getPlayerDescription, getPlayerName } from 'utils/playerHelpers';
import { FlexContainer } from '../../Container';
import { ProfileSection } from '../../ProfileSection';

View File

@@ -5,9 +5,6 @@ import {
GetPlayerFiltersDocument,
GetPlayerFiltersQuery,
GetPlayerFiltersQueryVariables,
GetPlayersCountDocument,
GetPlayersCountQuery,
GetPlayersCountQueryVariables,
GetPlayersDocument,
GetPlayersQuery,
GetPlayersQueryVariables,
@@ -46,23 +43,6 @@ gql`
) {
...PlayerFragment
}
}
${PlayerFragment}
`;
export const PLAYER_LIMIT = 9;
// eslint-disable-next-line @typescript-eslint/no-unused-expressions
gql`
query GetPlayersCount(
$offset: Int
$limit: Int
$skillIds: [uuid!]
$playerTypeIds: [Int!]
$availability: Int
$timezones: [String!]
$search: String
) {
player_aggregate(
where: {
availability_hours: { _gte: $availability }
@@ -80,8 +60,11 @@ gql`
}
}
}
${PlayerFragment}
`;
export const PLAYER_LIMIT = 9;
export const defaultQueryVariables: GetPlayersQueryVariables = {
offset: 0,
limit: PLAYER_LIMIT,
@@ -94,10 +77,11 @@ export const defaultQueryVariables: GetPlayersQueryVariables = {
export type PlayersResponse = {
error: Error | undefined;
count: number;
players: PlayerFragmentFragment[];
};
export const getPlayers = async (
export const getPlayersWithCount = async (
queryVariables = defaultQueryVariables,
client: Client = defaultClient,
): Promise<PlayersResponse> => {
@@ -108,26 +92,11 @@ export const getPlayers = async (
)
.toPromise();
return { players: data?.player || [], error };
};
export type PlayersCountResponse = {
error: Error | undefined;
count: number;
};
export const getPlayersCount = async (
queryVariables = defaultQueryVariables,
client: Client = defaultClient,
): Promise<PlayersCountResponse> => {
const { data, error } = await client
.query<GetPlayersCountQuery, GetPlayersCountQueryVariables>(
GetPlayersCountDocument,
queryVariables,
)
.toPromise();
return { count: data?.player_aggregate.aggregate?.count || 0, error };
return {
players: data?.player || [],
count: data?.player_aggregate.aggregate?.count || 0,
error,
};
};
const playerUsernamesQuery = gql`
@@ -190,9 +159,9 @@ export const getPlayerFilters = async (client: Client = defaultClient) => {
.toPromise();
if (error) {
// eslint-disable-next-line no-console
console.error(error);
throw error;
// eslint-disable-next-line no-console
console.error(error);
throw error;
}
return data;

View File

@@ -2,7 +2,6 @@ import {
GetPlayersQueryVariables,
PlayerFragmentFragment,
useGetPlayerFiltersQuery,
useGetPlayersCountQuery,
useGetPlayersQuery,
} from 'graphql/autogen/types';
import { defaultQueryVariables, PLAYER_LIMIT } from 'graphql/getPlayers';
@@ -21,8 +20,7 @@ export interface PlayerAggregates {
interface PlayerFilter {
players: PlayerFragmentFragment[];
totalCount: number;
fetchingPlayers: boolean;
fetchingCount: boolean;
fetching: boolean;
fetchingMore: boolean;
aggregates: PlayerAggregates;
queryVariables: GetPlayersQueryVariables;
@@ -43,20 +41,13 @@ const usePlayerAggregates = () => {
};
};
const useTotalPlayersCount = (variables: GetPlayersQueryVariables) => {
const [{ fetching, data, error }] = useGetPlayersCountQuery({
variables: { ...variables, offset: 0, limit: PLAYER_LIMIT },
});
const totalCount = data?.player_aggregate.aggregate?.count || 0;
return { fetching, totalCount, error };
};
const useFilteredPlayers = (variables: GetPlayersQueryVariables) => {
const [{ fetching, data, error }] = useGetPlayersQuery({
variables,
});
const players = data?.player || [];
return { fetching, players, error };
const totalCount = data?.player_aggregate.aggregate?.count || 0;
return { fetching, players, totalCount, error };
};
export const usePlayerFilter = (): PlayerFilter => {
@@ -87,8 +78,7 @@ export const usePlayerFilter = (): PlayerFilter => {
const {
players,
fetchingPlayers,
fetchingCount,
fetching,
fetchingMore,
error,
nextPage,
@@ -98,8 +88,7 @@ export const usePlayerFilter = (): PlayerFilter => {
return {
players,
fetchingPlayers,
fetchingCount,
fetching,
fetchingMore,
error,
aggregates,
@@ -114,7 +103,7 @@ export const usePlayerFilter = (): PlayerFilter => {
export const useFiltersUsed = (
queryVariables: GetPlayersQueryVariables,
): { filtersUsed: boolean; onlySearchFilterUsed: boolean } => {
): boolean => {
const playerTypesFilterUsed = useMemo(
() => (queryVariables.playerTypeIds as number[])?.length > 0,
[queryVariables.playerTypeIds],
@@ -151,27 +140,7 @@ export const useFiltersUsed = (
],
);
const onlySearchFilterUsed = useMemo(
() =>
searchFilterUsed &&
!(
playerTypesFilterUsed ||
availabilityFilterUsed ||
skillIdsFilterUsed ||
timezonesFilterUsed
),
[
playerTypesFilterUsed,
searchFilterUsed,
availabilityFilterUsed,
skillIdsFilterUsed,
timezonesFilterUsed,
],
);
return {
filtersUsed,
onlySearchFilterUsed,
};
return filtersUsed;
};
const usePaginatedPlayers = (
@@ -179,19 +148,11 @@ const usePaginatedPlayers = (
setQueryVariable: QueryVariableSetter,
) => {
const {
fetching: fetchingPlayers,
fetching,
players: fetchedPlayers,
error: errorPlayers,
} = useFilteredPlayers(queryVariables);
const {
fetching: fetchingCount,
totalCount,
error: errorCount,
} = useTotalPlayersCount(queryVariables);
const fetching = fetchingPlayers || fetchingCount;
const error = errorPlayers || errorCount;
error,
} = useFilteredPlayers(queryVariables);
const itemsPerPage = PLAYER_LIMIT;
const maxPage = Math.ceil(totalCount / itemsPerPage);
@@ -225,7 +186,11 @@ const usePaginatedPlayers = (
);
useEffect(() => {
if (fetching || error) return;
if (fetching) return;
if (error) {
setPlayers([]);
return;
}
if (shouldAppend.current) {
setPlayers((_players) => [..._players, ...fetchedPlayers]);
shouldAppend.current = false;
@@ -238,8 +203,7 @@ const usePaginatedPlayers = (
nextPage,
players,
totalCount,
fetchingCount,
fetchingPlayers,
fetching,
error,
fetchingMore: shouldAppend.current,
moreAvailable: currentPage < maxPage,

View File

@@ -4,11 +4,7 @@ import { PlayerFilter } from 'components/Player/PlayerFilter';
import { PlayerList } from 'components/Player/PlayerList';
import { HeadComponent } from 'components/Seo';
import { getSsrClient } from 'graphql/client';
import {
getPlayerFilters,
getPlayers,
getPlayersCount,
} from 'graphql/getPlayers';
import { getPlayerFilters, getPlayersWithCount } from 'graphql/getPlayers';
import { usePlayerFilter } from 'lib/hooks/players';
import { InferGetStaticPropsType } from 'next';
import React, { useCallback, useEffect, useRef } from 'react';
@@ -19,18 +15,10 @@ export const getStaticProps = async () => {
const [ssrClient, ssrCache] = getSsrClient();
// This populates the cache server-side
const { error: errorPlayers } = await getPlayers(undefined, ssrClient);
if (errorPlayers) {
const { error } = await getPlayersWithCount(undefined, ssrClient);
if (error) {
// eslint-disable-next-line no-console
console.error('getPlayers error', errorPlayers);
}
const { error: errorPlayersCount } = await getPlayersCount(
undefined,
ssrClient,
);
if (errorPlayersCount) {
// eslint-disable-next-line no-console
console.error('getPlayersCount error', errorPlayersCount);
console.error('getPlayers error', error);
}
await getPlayerFilters(ssrClient);
@@ -46,8 +34,7 @@ const Players: React.FC<Props> = () => {
const {
players,
aggregates,
fetchingPlayers,
fetchingCount,
fetching,
fetchingMore,
error,
queryVariables,
@@ -86,8 +73,7 @@ const Players: React.FC<Props> = () => {
<HeadComponent url="https://my.metagame.wtf/players" />
<VStack w="100%" spacing="8" pb={{ base: '16', lg: '0' }}>
<PlayerFilter
fetchingPlayers={fetchingPlayers}
fetchingCount={fetchingCount}
fetching={fetching}
aggregates={aggregates}
queryVariables={queryVariables}
setQueryVariable={setQueryVariable}
@@ -95,11 +81,11 @@ const Players: React.FC<Props> = () => {
totalCount={totalCount}
/>
{error && <Text>{`Error: ${error.message}`}</Text>}
{!error && players.length && (fetchingMore || !fetchingPlayers) && (
{!error && players.length && (fetchingMore || !fetching) && (
<PlayerList players={players} />
)}
<VStack ref={loaderRef} w="100%">
{fetchingPlayers || fetchingMore || moreAvailable ? (
{fetching || fetchingMore || moreAvailable ? (
<LoadingState color="white" />
) : (
<Text color="white">

View File

@@ -22,5 +22,5 @@ export const formatAddress = (address = ''): string =>
export const formatUsernameIfAddress = (username = ''): string =>
ethers.utils.isAddress(username) ? formatAddress(username) : username;
export const hasPlayerImage = (player: PlayerFragmentFragment): boolean =>
!!player.box_profile?.imageUrl;
export const hasPlayerImage = (player: PlayerFragmentFragment): boolean =>
!!player.box_profile?.imageUrl;