chore(trading): team page refactor, games with specified epoch from (#5772)

This commit is contained in:
Art 2024-02-12 15:03:21 +01:00 committed by GitHub
parent c7dd5e846a
commit 0d3bcf05a1
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
19 changed files with 303 additions and 150 deletions

View File

@ -3,8 +3,8 @@ import { ErrorBoundary } from '@sentry/react';
import { CompetitionsHeader } from '../../components/competitions/competitions-header';
import { Intent, Loader, TradingButton } from '@vegaprotocol/ui-toolkit';
import { useGames } from '../../lib/hooks/use-games';
import { useCurrentEpochInfoQuery } from '../referrals/hooks/__generated__/Epoch';
import { useGameCards } from '../../lib/hooks/use-game-cards';
import { useCurrentEpochInfoQuery } from '../../lib/hooks/__generated__/Epoch';
import { Link, useNavigate } from 'react-router-dom';
import { Links } from '../../lib/links';
import {
@ -28,7 +28,7 @@ export const CompetitionsHome = () => {
const { data: epochData } = useCurrentEpochInfoQuery();
const currentEpoch = Number(epochData?.epoch.id);
const { data: gamesData, loading: gamesLoading } = useGames({
const { data: gamesData, loading: gamesLoading } = useGameCards({
onlyActive: true,
currentEpoch,
});

View File

@ -12,7 +12,6 @@ import {
type TeamStats as ITeamStats,
type Team as TeamType,
type Member,
type TeamGame,
} from '../../lib/hooks/use-team';
import { DApp, EXPLORER_PARTIES, useLinks } from '@vegaprotocol/environment';
import { TeamAvatar } from '../../components/competitions/team-avatar';
@ -23,6 +22,11 @@ import { LayoutWithGradient } from '../../components/layouts-inner';
import { useVegaWallet } from '@vegaprotocol/wallet';
import { JoinTeam } from './join-team';
import { UpdateTeamButton } from './update-team-button';
import {
type TeamGame,
useGames,
areTeamGames,
} from '../../lib/hooks/use-games';
export const CompetitionsTeam = () => {
const t = useT();
@ -38,8 +42,12 @@ export const CompetitionsTeam = () => {
const TeamPageContainer = ({ teamId }: { teamId: string | undefined }) => {
const t = useT();
const { pubKey } = useVegaWallet();
const { data, team, partyTeam, stats, members, games, loading, refetch } =
useTeam(teamId, pubKey || undefined);
const { data, team, partyTeam, stats, members, loading, refetch } = useTeam(
teamId,
pubKey || undefined
);
const { data: games, loading: gamesLoading } = useGames(teamId);
// only show spinner on first load so when users join teams its smoother
if (!data && loading) {
@ -64,7 +72,8 @@ const TeamPageContainer = ({ teamId }: { teamId: string | undefined }) => {
partyTeam={partyTeam}
stats={stats}
members={members}
games={games}
games={areTeamGames(games) ? games : undefined}
gamesLoading={gamesLoading}
refetch={refetch}
/>
);
@ -76,6 +85,7 @@ const TeamPage = ({
stats,
members,
games,
gamesLoading,
refetch,
}: {
team: TeamType;
@ -83,6 +93,7 @@ const TeamPage = ({
stats?: ITeamStats;
members?: Member[];
games?: TeamGame[];
gamesLoading?: boolean;
refetch: () => void;
}) => {
const t = useT();
@ -113,7 +124,11 @@ const TeamPage = ({
onClick={() => setShowGames(true)}
data-testid="games-toggle"
>
{t('Games ({{count}})', { count: games ? games.length : 0 })}
{t('Games {{games}}', {
replace: {
games: gamesLoading ? '' : games ? `(${games.length})` : '(0)',
},
})}
</ToggleButton>
<ToggleButton
active={!showGames}
@ -125,15 +140,33 @@ const TeamPage = ({
})}
</ToggleButton>
</div>
{showGames ? <Games games={games} /> : <Members members={members} />}
{showGames ? (
<Games games={games} gamesLoading={gamesLoading} />
) : (
<Members members={members} />
)}
</section>
</LayoutWithGradient>
);
};
const Games = ({ games }: { games?: TeamGame[] }) => {
const Games = ({
games,
gamesLoading,
}: {
games?: TeamGame[];
gamesLoading?: boolean;
}) => {
const t = useT();
if (gamesLoading) {
return (
<div className="w-[15px]">
<Loader size="small" />
</div>
);
}
if (!games?.length) {
return <p>{t('No games')}</p>;
}

View File

@ -11,7 +11,7 @@ import { useEffect } from 'react';
import { useT } from '../../../lib/use-t';
import { matchPath, useLocation, useNavigate } from 'react-router-dom';
import { Routes } from '../../../lib/links';
import { useCurrentEpochInfoQuery } from './__generated__/Epoch';
import { useCurrentEpochInfoQuery } from '../../../lib/hooks/__generated__/Epoch';
const REFETCH_INTERVAL = 60 * 60 * 1000; // 1h
const NON_ELIGIBLE_REFERRAL_SET_TOAST_ID = 'non-eligible-referral-set';

View File

@ -34,9 +34,10 @@ import {
} from './hooks/use-referral';
import { ApplyCodeForm, ApplyCodeFormContainer } from './apply-code-form';
import { useReferralProgram } from './hooks/use-referral-program';
import { useCurrentEpochInfoQuery } from './hooks/__generated__/Epoch';
import { useCurrentEpochInfoQuery } from '../../lib/hooks/__generated__/Epoch';
import { QUSDTooltip } from './qusd-tooltip';
import { CodeTile, StatTile, Tile } from './tile';
import { areTeamGames, useGames } from '../../lib/hooks/use-games';
export const ReferralStatistics = () => {
const { pubKey } = useVegaWallet();
@ -576,7 +577,8 @@ export const RefereesTable = ({
};
const Team = ({ teamId }: { teamId?: string }) => {
const { team, games, members } = useTeam(teamId);
const { team, members } = useTeam(teamId);
const { data: games } = useGames(teamId);
if (!team) return null;
@ -585,7 +587,10 @@ const Team = ({ teamId }: { teamId?: string }) => {
<TeamAvatar teamId={team.teamId} imgUrl={team.avatarUrl} />
<div className="flex flex-col items-start gap-1 lg:gap-3">
<h1 className="calt text-2xl lg:text-3xl xl:text-5xl">{team.name}</h1>
<TeamStats members={members} games={games} />
<TeamStats
members={members}
games={areTeamGames(games) ? games : undefined}
/>
</div>
</Tile>
);

View File

@ -153,5 +153,8 @@ const cacheConfig: InMemoryCacheConfig = {
OrderUpdate: {
keyFields: false,
},
Game: {
keyFields: false,
},
},
};

View File

@ -1,4 +1,4 @@
import { type TeamGame, type TeamStats } from '../../lib/hooks/use-team';
import { type TeamStats } from '../../lib/hooks/use-team';
import { type TeamsFieldsFragment } from '../../lib/hooks/__generated__/Teams';
import { TeamAvatar, getFallbackAvatar } from './team-avatar';
import { FavoriteGame, Stat } from './team-stats';
@ -13,6 +13,7 @@ import { take } from 'lodash';
import { DispatchMetricLabels } from '@vegaprotocol/types';
import classNames from 'classnames';
import { UpdateTeamButton } from '../../client-pages/competitions/update-team-button';
import { type TeamGame } from '../../lib/hooks/use-games';
export const TeamCard = ({
rank,

View File

@ -11,11 +11,11 @@ import { formatNumberRounded } from '@vegaprotocol/utils';
import {
type TeamStats as ITeamStats,
type Member,
type TeamGame,
} from '../../lib/hooks/use-team';
import { useT } from '../../lib/use-t';
import { DispatchMetricLabels, type DispatchMetric } from '@vegaprotocol/types';
import classNames from 'classnames';
import { type TeamGame } from '../../lib/hooks/use-games';
export const TeamStats = ({
stats,

View File

@ -213,12 +213,12 @@ def create_team(vega: VegaServiceNull):
def test_team_page_games_table(team_page: Page):
team_page.get_by_test_id("games-toggle").click()
expect(team_page.get_by_test_id("games-toggle")).to_have_text("Games (1)")
expect(team_page.get_by_test_id("games-toggle")).to_have_text("Games (10)")
expect(team_page.get_by_test_id("rank-0")).to_have_text("2")
expect(team_page.get_by_test_id("epoch-0")).to_have_text("19")
expect(team_page.get_by_test_id("type-0")
).to_have_text("Price maker fees paid")
expect(team_page.get_by_test_id("amount-0")).to_have_text("74")
expect(team_page.get_by_test_id("amount-0")).to_have_text("74") # 7,438,330 on preview.11
expect(team_page.get_by_test_id("participatingTeams-0")).to_have_text("2")
expect(team_page.get_by_test_id("participatingMembers-0")).to_have_text("4")

View File

@ -0,0 +1,35 @@
fragment TeamEntity on TeamGameEntity {
rank
volume
rewardMetric
rewardEarned
totalRewardsEarned
team {
teamId
membersParticipating {
individual
rank
}
}
}
fragment GameFields on Game {
id
epoch
numberOfParticipants
entities {
... on TeamGameEntity {
...TeamEntity
}
}
}
query Games($epochFrom: Int) {
games(epochFrom: $epochFrom, entityScope: ENTITY_SCOPE_TEAMS) {
edges {
node {
...GameFields
}
}
}
}

View File

@ -29,28 +29,6 @@ fragment TeamRefereeFields on TeamReferee {
joinedAtEpoch
}
fragment TeamEntity on TeamGameEntity {
rank
volume
rewardMetric
rewardEarned
totalRewardsEarned
team {
teamId
}
}
fragment TeamGameFields on Game {
id
epoch
numberOfParticipants
entities {
... on TeamGameEntity {
...TeamEntity
}
}
}
fragment TeamMemberStatsFields on TeamMemberStatistics {
partyId
totalQuantumVolume
@ -87,13 +65,6 @@ query Team($teamId: ID!, $partyId: ID, $aggregationEpochs: Int) {
}
}
}
games(entityScope: ENTITY_SCOPE_TEAMS) {
edges {
node {
...TeamGameFields
}
}
}
teamMembersStatistics(
teamId: $teamId
aggregationEpochs: $aggregationEpochs

View File

@ -0,0 +1,83 @@
import * as Types from '@vegaprotocol/types';
import { gql } from '@apollo/client';
import * as Apollo from '@apollo/client';
const defaultOptions = {} as const;
export type TeamEntityFragment = { __typename?: 'TeamGameEntity', rank: number, volume: string, rewardMetric: Types.DispatchMetric, rewardEarned: string, totalRewardsEarned: string, team: { __typename?: 'TeamParticipation', teamId: string, membersParticipating: Array<{ __typename?: 'IndividualGameEntity', individual: string, rank: number }> } };
export type GameFieldsFragment = { __typename?: 'Game', id: string, epoch: number, numberOfParticipants: number, entities: Array<{ __typename?: 'IndividualGameEntity' } | { __typename?: 'TeamGameEntity', rank: number, volume: string, rewardMetric: Types.DispatchMetric, rewardEarned: string, totalRewardsEarned: string, team: { __typename?: 'TeamParticipation', teamId: string, membersParticipating: Array<{ __typename?: 'IndividualGameEntity', individual: string, rank: number }> } }> };
export type GamesQueryVariables = Types.Exact<{
epochFrom?: Types.InputMaybe<Types.Scalars['Int']>;
}>;
export type GamesQuery = { __typename?: 'Query', games: { __typename?: 'GamesConnection', edges?: Array<{ __typename?: 'GameEdge', node: { __typename?: 'Game', id: string, epoch: number, numberOfParticipants: number, entities: Array<{ __typename?: 'IndividualGameEntity' } | { __typename?: 'TeamGameEntity', rank: number, volume: string, rewardMetric: Types.DispatchMetric, rewardEarned: string, totalRewardsEarned: string, team: { __typename?: 'TeamParticipation', teamId: string, membersParticipating: Array<{ __typename?: 'IndividualGameEntity', individual: string, rank: number }> } }> } } | null> | null } };
export const TeamEntityFragmentDoc = gql`
fragment TeamEntity on TeamGameEntity {
rank
volume
rewardMetric
rewardEarned
totalRewardsEarned
team {
teamId
membersParticipating {
individual
rank
}
}
}
`;
export const GameFieldsFragmentDoc = gql`
fragment GameFields on Game {
id
epoch
numberOfParticipants
entities {
... on TeamGameEntity {
...TeamEntity
}
}
}
${TeamEntityFragmentDoc}`;
export const GamesDocument = gql`
query Games($epochFrom: Int) {
games(epochFrom: $epochFrom, entityScope: ENTITY_SCOPE_TEAMS) {
edges {
node {
...GameFields
}
}
}
}
${GameFieldsFragmentDoc}`;
/**
* __useGamesQuery__
*
* To run a query within a React component, call `useGamesQuery` and pass it any options that fit your needs.
* When your component renders, `useGamesQuery` returns an object from Apollo Client that contains loading, error, and data properties
* you can use to render your UI.
*
* @param baseOptions options that will be passed into the query, supported options are listed on: https://www.apollographql.com/docs/react/api/react-hooks/#options;
*
* @example
* const { data, loading, error } = useGamesQuery({
* variables: {
* epochFrom: // value for 'epochFrom'
* },
* });
*/
export function useGamesQuery(baseOptions?: Apollo.QueryHookOptions<GamesQuery, GamesQueryVariables>) {
const options = {...defaultOptions, ...baseOptions}
return Apollo.useQuery<GamesQuery, GamesQueryVariables>(GamesDocument, options);
}
export function useGamesLazyQuery(baseOptions?: Apollo.LazyQueryHookOptions<GamesQuery, GamesQueryVariables>) {
const options = {...defaultOptions, ...baseOptions}
return Apollo.useLazyQuery<GamesQuery, GamesQueryVariables>(GamesDocument, options);
}
export type GamesQueryHookResult = ReturnType<typeof useGamesQuery>;
export type GamesLazyQueryHookResult = ReturnType<typeof useGamesLazyQuery>;
export type GamesQueryResult = Apollo.QueryResult<GamesQuery, GamesQueryVariables>;

View File

@ -9,10 +9,6 @@ export type TeamStatsFieldsFragment = { __typename?: 'TeamStatistics', teamId: s
export type TeamRefereeFieldsFragment = { __typename?: 'TeamReferee', teamId: string, referee: string, joinedAt: any, joinedAtEpoch: number };
export type TeamEntityFragment = { __typename?: 'TeamGameEntity', rank: number, volume: string, rewardMetric: Types.DispatchMetric, rewardEarned: string, totalRewardsEarned: string, team: { __typename?: 'TeamParticipation', teamId: string } };
export type TeamGameFieldsFragment = { __typename?: 'Game', id: string, epoch: number, numberOfParticipants: number, entities: Array<{ __typename?: 'IndividualGameEntity' } | { __typename?: 'TeamGameEntity', rank: number, volume: string, rewardMetric: Types.DispatchMetric, rewardEarned: string, totalRewardsEarned: string, team: { __typename?: 'TeamParticipation', teamId: string } }> };
export type TeamMemberStatsFieldsFragment = { __typename?: 'TeamMemberStatistics', partyId: string, totalQuantumVolume: string, totalQuantumRewards: string, totalGamesPlayed: number };
export type TeamQueryVariables = Types.Exact<{
@ -22,7 +18,7 @@ export type TeamQueryVariables = Types.Exact<{
}>;
export type TeamQuery = { __typename?: 'Query', teams?: { __typename?: 'TeamConnection', edges: Array<{ __typename?: 'TeamEdge', node: { __typename?: 'Team', teamId: string, referrer: string, name: string, teamUrl: string, avatarUrl: string, createdAt: any, createdAtEpoch: number, closed: boolean, allowList: Array<string> } }> } | null, partyTeams?: { __typename?: 'TeamConnection', edges: Array<{ __typename?: 'TeamEdge', node: { __typename?: 'Team', teamId: string, referrer: string, name: string, teamUrl: string, avatarUrl: string, createdAt: any, createdAtEpoch: number, closed: boolean, allowList: Array<string> } }> } | null, teamsStatistics?: { __typename?: 'TeamsStatisticsConnection', edges: Array<{ __typename?: 'TeamStatisticsEdge', node: { __typename?: 'TeamStatistics', teamId: string, totalQuantumVolume: string, totalQuantumRewards: string, totalGamesPlayed: number, gamesPlayed: Array<string>, quantumRewards: Array<{ __typename?: 'QuantumRewardsPerEpoch', epoch: number, totalQuantumRewards: string }> } }> } | null, teamReferees?: { __typename?: 'TeamRefereeConnection', edges: Array<{ __typename?: 'TeamRefereeEdge', node: { __typename?: 'TeamReferee', teamId: string, referee: string, joinedAt: any, joinedAtEpoch: number } }> } | null, games: { __typename?: 'GamesConnection', edges?: Array<{ __typename?: 'GameEdge', node: { __typename?: 'Game', id: string, epoch: number, numberOfParticipants: number, entities: Array<{ __typename?: 'IndividualGameEntity' } | { __typename?: 'TeamGameEntity', rank: number, volume: string, rewardMetric: Types.DispatchMetric, rewardEarned: string, totalRewardsEarned: string, team: { __typename?: 'TeamParticipation', teamId: string } }> } } | null> | null }, teamMembersStatistics?: { __typename?: 'TeamMembersStatisticsConnection', edges: Array<{ __typename?: 'TeamMemberStatisticsEdge', node: { __typename?: 'TeamMemberStatistics', partyId: string, totalQuantumVolume: string, totalQuantumRewards: string, totalGamesPlayed: number } }> } | null };
export type TeamQuery = { __typename?: 'Query', teams?: { __typename?: 'TeamConnection', edges: Array<{ __typename?: 'TeamEdge', node: { __typename?: 'Team', teamId: string, referrer: string, name: string, teamUrl: string, avatarUrl: string, createdAt: any, createdAtEpoch: number, closed: boolean, allowList: Array<string> } }> } | null, partyTeams?: { __typename?: 'TeamConnection', edges: Array<{ __typename?: 'TeamEdge', node: { __typename?: 'Team', teamId: string, referrer: string, name: string, teamUrl: string, avatarUrl: string, createdAt: any, createdAtEpoch: number, closed: boolean, allowList: Array<string> } }> } | null, teamsStatistics?: { __typename?: 'TeamsStatisticsConnection', edges: Array<{ __typename?: 'TeamStatisticsEdge', node: { __typename?: 'TeamStatistics', teamId: string, totalQuantumVolume: string, totalQuantumRewards: string, totalGamesPlayed: number, gamesPlayed: Array<string>, quantumRewards: Array<{ __typename?: 'QuantumRewardsPerEpoch', epoch: number, totalQuantumRewards: string }> } }> } | null, teamReferees?: { __typename?: 'TeamRefereeConnection', edges: Array<{ __typename?: 'TeamRefereeEdge', node: { __typename?: 'TeamReferee', teamId: string, referee: string, joinedAt: any, joinedAtEpoch: number } }> } | null, teamMembersStatistics?: { __typename?: 'TeamMembersStatisticsConnection', edges: Array<{ __typename?: 'TeamMemberStatisticsEdge', node: { __typename?: 'TeamMemberStatistics', partyId: string, totalQuantumVolume: string, totalQuantumRewards: string, totalGamesPlayed: number } }> } | null };
export const TeamFieldsFragmentDoc = gql`
fragment TeamFields on Team {
@ -58,30 +54,6 @@ export const TeamRefereeFieldsFragmentDoc = gql`
joinedAtEpoch
}
`;
export const TeamEntityFragmentDoc = gql`
fragment TeamEntity on TeamGameEntity {
rank
volume
rewardMetric
rewardEarned
totalRewardsEarned
team {
teamId
}
}
`;
export const TeamGameFieldsFragmentDoc = gql`
fragment TeamGameFields on Game {
id
epoch
numberOfParticipants
entities {
... on TeamGameEntity {
...TeamEntity
}
}
}
${TeamEntityFragmentDoc}`;
export const TeamMemberStatsFieldsFragmentDoc = gql`
fragment TeamMemberStatsFields on TeamMemberStatistics {
partyId
@ -120,13 +92,6 @@ export const TeamDocument = gql`
}
}
}
games(entityScope: ENTITY_SCOPE_TEAMS) {
edges {
node {
...TeamGameFields
}
}
}
teamMembersStatistics(teamId: $teamId, aggregationEpochs: $aggregationEpochs) {
edges {
node {
@ -138,7 +103,6 @@ export const TeamDocument = gql`
${TeamFieldsFragmentDoc}
${TeamStatsFieldsFragmentDoc}
${TeamRefereeFieldsFragmentDoc}
${TeamGameFieldsFragmentDoc}
${TeamMemberStatsFieldsFragmentDoc}`;
/**

View File

@ -0,0 +1,48 @@
import compact from 'lodash/compact';
import { useActiveRewardsQuery } from '../../components/rewards-container/__generated__/Rewards';
import { isActiveReward } from '../../components/rewards-container/active-rewards';
import {
EntityScope,
IndividualScope,
type TransferNode,
} from '@vegaprotocol/types';
const isScopedToTeams = (node: TransferNode) =>
node.transfer.kind.__typename === 'RecurringTransfer' &&
// scoped to teams
(node.transfer.kind.dispatchStrategy?.entityScope ===
EntityScope.ENTITY_SCOPE_TEAMS ||
// or to individuals
(node.transfer.kind.dispatchStrategy?.entityScope ===
EntityScope.ENTITY_SCOPE_INDIVIDUALS &&
// but they have to be in a team
node.transfer.kind.dispatchStrategy.individualScope ===
IndividualScope.INDIVIDUAL_SCOPE_IN_TEAM));
export const useGameCards = ({
currentEpoch,
onlyActive,
}: {
currentEpoch: number;
onlyActive: boolean;
}) => {
const { data, loading, error } = useActiveRewardsQuery({
variables: {
isReward: true,
},
fetchPolicy: 'cache-and-network',
});
const games = compact(data?.transfersConnection?.edges?.map((n) => n?.node))
.map((n) => n as TransferNode)
.filter((node) => {
const active = onlyActive ? isActiveReward(node, currentEpoch) : true;
return active && isScopedToTeams(node);
});
return {
data: games,
loading,
error,
};
};

View File

@ -1,48 +1,80 @@
import compact from 'lodash/compact';
import { useActiveRewardsQuery } from '../../components/rewards-container/__generated__/Rewards';
import { isActiveReward } from '../../components/rewards-container/active-rewards';
import {
EntityScope,
IndividualScope,
type TransferNode,
} from '@vegaprotocol/types';
useGamesQuery,
type GameFieldsFragment,
type TeamEntityFragment,
} from './__generated__/Games';
import orderBy from 'lodash/orderBy';
import { removePaginationWrapper } from '@vegaprotocol/utils';
import { useCurrentEpochInfoQuery } from './__generated__/Epoch';
import { type ApolloError } from '@apollo/client';
const isScopedToTeams = (node: TransferNode) =>
node.transfer.kind.__typename === 'RecurringTransfer' &&
// scoped to teams
(node.transfer.kind.dispatchStrategy?.entityScope ===
EntityScope.ENTITY_SCOPE_TEAMS ||
// or to individuals
(node.transfer.kind.dispatchStrategy?.entityScope ===
EntityScope.ENTITY_SCOPE_INDIVIDUALS &&
// but they have to be in a team
node.transfer.kind.dispatchStrategy.individualScope ===
IndividualScope.INDIVIDUAL_SCOPE_IN_TEAM));
const TAKE_EPOCHS = 30; // TODO: should this be DEFAULT_AGGREGATION_EPOCHS?
export const useGames = ({
currentEpoch,
onlyActive,
}: {
currentEpoch: number;
onlyActive: boolean;
}) => {
const { data, loading, error } = useActiveRewardsQuery({
variables: {
isReward: true,
},
fetchPolicy: 'cache-and-network',
const findTeam = (entities: GameFieldsFragment['entities'], teamId: string) => {
const team = entities.find(
(ent) => ent.__typename === 'TeamGameEntity' && ent.team.teamId === teamId
);
if (team?.__typename === 'TeamGameEntity') return team; // drops __typename === 'IndividualGameEntity' from team object
return undefined;
};
export type Game = GameFieldsFragment & {
/** The team entity data accessible only if scoped to particular team. */
team?: TeamEntityFragment;
};
export type TeamGame = Game & { team: NonNullable<Game['team']> };
const isTeamGame = (game: Game): game is TeamGame => game.team !== undefined;
export const areTeamGames = (games?: Game[]): games is TeamGame[] =>
Boolean(games && games.filter((g) => isTeamGame(g)).length > 0);
type GamesData = {
data?: Game[];
loading: boolean;
error?: ApolloError;
};
export const useGames = (teamId?: string, epochFrom?: number): GamesData => {
const {
data: epochData,
loading: epochLoading,
error: epochError,
} = useCurrentEpochInfoQuery({
skip: Boolean(epochFrom),
});
const games = compact(data?.transfersConnection?.edges?.map((n) => n?.node))
.map((n) => n as TransferNode)
.filter((node) => {
const active = onlyActive ? isActiveReward(node, currentEpoch) : true;
return active && isScopedToTeams(node);
let from = epochFrom;
if (!from && epochData) {
from = Number(epochData.epoch.id) - TAKE_EPOCHS;
if (from < 1) from = 1; // make sure it's not negative
}
const { data, loading, error } = useGamesQuery({
variables: {
epochFrom: from,
},
skip: !from,
fetchPolicy: 'cache-and-network',
context: { isEnlargedTimeout: true },
});
const allGames = removePaginationWrapper(data?.games.edges);
const allOrScoped = allGames
.map((g) => ({
...g,
team: teamId ? findTeam(g.entities, teamId) : undefined,
}))
.filter((g) => {
// passthrough if not scoped to particular team
if (!teamId) return true;
return isTeamGame(g);
});
const games = orderBy(allOrScoped, 'epoch', 'desc');
return {
data: games,
loading,
error,
loading: loading || epochLoading,
error: error || epochError,
};
};

View File

@ -4,6 +4,7 @@ import first from 'lodash/first';
import { useTeamsQuery } from './__generated__/Teams';
import { useTeam } from './use-team';
import { useTeams } from './use-teams';
import { areTeamGames, useGames } from './use-games';
export const useMyTeam = () => {
const { pubKey } = useVegaWallet();
@ -19,7 +20,8 @@ export const useMyTeam = () => {
const team = first(compact(maybeMyTeam?.teams?.edges.map((n) => n.node)));
const rank = teams.findIndex((t) => t.teamId === team?.teamId) + 1;
const { games, stats } = useTeam(team?.teamId);
const { stats } = useTeam(team?.teamId);
const { data: games } = useGames(team?.teamId);
return { team, stats, games, rank };
return { team, stats, games: areTeamGames(games) ? games : undefined, rank };
};

View File

@ -1,11 +1,8 @@
import compact from 'lodash/compact';
import orderBy from 'lodash/orderBy';
import {
useTeamQuery,
type TeamFieldsFragment,
type TeamStatsFieldsFragment,
type TeamRefereeFieldsFragment,
type TeamEntityFragment,
type TeamMemberStatsFieldsFragment,
} from './__generated__/Team';
import { DEFAULT_AGGREGATION_EPOCHS } from './use-teams';
@ -18,8 +15,6 @@ export type Member = TeamRefereeFieldsFragment & {
totalQuantumVolume: string;
totalQuantumRewards: string;
};
export type TeamEntity = TeamEntityFragment;
export type TeamGame = ReturnType<typeof useTeam>['games'][number];
export type MemberStats = TeamMemberStatsFieldsFragment;
export const useTeam = (teamId?: string, partyId?: string) => {
@ -80,33 +75,11 @@ export const useTeam = (teamId?: string, partyId?: string) => {
});
}
// Find games where the current team participated in
const gamesWithTeam = compact(data?.games.edges).map((edge) => {
const team = edge.node.entities.find((e) => {
if (e.__typename !== 'TeamGameEntity') return false;
if (e.team.teamId !== teamId) return false;
return true;
});
if (!team) return null;
return {
id: edge.node.id,
epoch: edge.node.epoch,
numberOfParticipants: edge.node.numberOfParticipants,
entities: edge.node.entities,
team: team as TeamEntity, // TS can't infer that all the game entities are teams
};
});
const games = orderBy(compact(gamesWithTeam), 'epoch', 'desc');
return {
...queryResult,
stats: teamStatsEdge?.node,
team,
members,
games,
partyTeam,
};
};

View File

@ -48,5 +48,8 @@ export const DEFAULT_CACHE_CONFIG: InMemoryCacheConfig = {
statistics: {
keyFields: false,
},
Game: {
keyFields: false,
},
},
};