mirror of
https://github.com/MetaFam/TheGame.git
synced 2026-04-24 03:00:09 -04:00
fetching count and players simultaneously
This commit is contained in:
@@ -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} />;
|
||||
};
|
||||
|
||||
@@ -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'}`}
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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';
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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">
|
||||
|
||||
@@ -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;
|
||||
|
||||
Reference in New Issue
Block a user