Compare commits

...
Author SHA1 Message Date
asiaznik d1a3d24db5 feat(trading): competition update team 2024-01-25 22:47:05 +01:00
23 changed files with 442 additions and 233 deletions
+1
View File
@@ -26,6 +26,7 @@ NX_ICEBERG_ORDERS=true
# NX_PRODUCT_PERPETUALS
NX_METAMASK_SNAPS=true
NX_REFERRALS=true
NX_TEAM_COMPETITION=true
NX_CHARTING_LIBRARY_PATH=https://assets.vega.community/trading-view-bundle/v0.0.1/
NX_CHARTING_LIBRARY_HASH=PDjWaqPFndDp+LCvqbKvntWriaqNzNpZ5i9R/BULzCg=
@@ -1,28 +1,9 @@
import { useSearchParams } from 'react-router-dom';
import { Controller, useForm } from 'react-hook-form';
import {
Intent,
TextArea,
TradingAnchorButton,
TradingButton,
TradingCheckbox,
TradingFormGroup,
TradingInput,
TradingInputError,
} from '@vegaprotocol/ui-toolkit';
import {
useVegaWallet,
type CreateReferralSet,
type Status,
useVegaWalletDialogStore,
} from '@vegaprotocol/wallet';
import {
addDecimalsFormatNumber,
isValidVegaPublicKey,
URL_REGEX,
} from '@vegaprotocol/utils';
import { Intent, TradingAnchorButton } from '@vegaprotocol/ui-toolkit';
import { useVegaWallet, useVegaWalletDialogStore } from '@vegaprotocol/wallet';
import { addDecimalsFormatNumber } from '@vegaprotocol/utils';
import { useT } from '../../lib/use-t';
import { useCreateReferralSet } from '../../lib/hooks/use-create-referral-set';
import { useReferralSetTransaction } from '../../lib/hooks/use-referral-set-transaction';
import { DApp, TokenStaticLinks, useLinks } from '@vegaprotocol/environment';
import { RainbowButton } from '../../components/rainbow-button';
import { usePageTitle } from '../../lib/hooks/use-page-title';
@@ -30,14 +11,7 @@ import { ErrorBoundary } from '../../components/error-boundary';
import { Box } from '../../components/competitions/box';
import { LayoutWithGradient } from '../../components/layouts-inner';
import { Links } from '../../lib/links';
interface FormFields {
name: string;
url: string;
avatarUrl: string;
private: boolean;
allowList: string;
}
import { TeamForm, TransactionType } from './team-form';
export const CompetitionsCreateTeam = () => {
const [searchParams] = useSearchParams();
@@ -85,7 +59,7 @@ const CreateTeamFormContainer = ({ isSolo }: { isSolo: boolean }) => {
const createLink = useLinks(DApp.Governance);
const { err, status, code, isEligible, requiredStake, onSubmit } =
useCreateReferralSet({
useReferralSetTransaction({
onSuccess: (code) => {
// For some reason team creation takes a long time, too long even to make
// polling viable, so its not feasible to navigate to the team page
@@ -146,7 +120,8 @@ const CreateTeamFormContainer = ({ isSolo }: { isSolo: boolean }) => {
}
return (
<CreateTeamForm
<TeamForm
type={TransactionType.CreateReferralSet}
onSubmit={onSubmit}
status={status}
err={err}
@@ -154,180 +129,3 @@ const CreateTeamFormContainer = ({ isSolo }: { isSolo: boolean }) => {
/>
);
};
const CreateTeamForm = ({
status,
err,
isSolo,
onSubmit,
}: {
status: ReturnType<typeof useCreateReferralSet>['status'];
err: ReturnType<typeof useCreateReferralSet>['err'];
isSolo: boolean;
onSubmit: (tx: CreateReferralSet) => void;
}) => {
const t = useT();
const {
register,
handleSubmit,
control,
watch,
formState: { errors },
} = useForm<FormFields>({
defaultValues: {
private: isSolo,
},
});
const isPrivate = watch('private');
const createTeam = (fields: FormFields) => {
onSubmit({
createReferralSet: {
isTeam: true,
team: {
name: fields.name,
teamUrl: fields.url,
avatarUrl: fields.avatarUrl,
closed: fields.private,
allowList: fields.private ? parseAllowListText(fields.allowList) : [],
},
},
});
};
return (
<form onSubmit={handleSubmit(createTeam)}>
<TradingFormGroup label={t('Team name')} labelFor="name">
<TradingInput {...register('name', { required: t('Required') })} />
{errors.name?.message && (
<TradingInputError forInput="name">
{errors.name.message}
</TradingInputError>
)}
</TradingFormGroup>
<TradingFormGroup
label={t('URL')}
labelFor="url"
labelDescription={t(
'Provide a link so users can learn more about your team'
)}
>
<TradingInput
{...register('url', {
pattern: { value: URL_REGEX, message: t('Invalid URL') },
})}
/>
{errors.url?.message && (
<TradingInputError forInput="url">
{errors.url.message}
</TradingInputError>
)}
</TradingFormGroup>
<TradingFormGroup
label={t('Avatar URL')}
labelFor="avatarUrl"
labelDescription={t('Provide a URL to a hosted image')}
>
<TradingInput
{...register('avatarUrl', {
pattern: {
value: URL_REGEX,
message: t('Invalid image URL'),
},
})}
/>
{errors.avatarUrl?.message && (
<TradingInputError forInput="avatarUrl">
{errors.avatarUrl.message}
</TradingInputError>
)}
</TradingFormGroup>
<TradingFormGroup
label={t('Make team private')}
labelFor="private"
hideLabel={true}
>
<Controller
name="private"
control={control}
render={({ field }) => {
return (
<TradingCheckbox
label={t('Make team private')}
checked={field.value}
onCheckedChange={(value) => {
field.onChange(value);
}}
disabled={isSolo}
/>
);
}}
/>
</TradingFormGroup>
{isPrivate && (
<TradingFormGroup
label={t('Public key allow list')}
labelFor="allowList"
labelDescription={t(
'Use a comma separated list to allow only specific public keys to join the team'
)}
>
<TextArea
{...register('allowList', {
required: t('Required'),
disabled: isSolo,
validate: {
allowList: (value) => {
const publicKeys = parseAllowListText(value);
if (publicKeys.every((pk) => isValidVegaPublicKey(pk))) {
return true;
}
return t('Invalid public key found in allow list');
},
},
})}
/>
{errors.allowList?.message && (
<TradingInputError forInput="avatarUrl">
{errors.allowList.message}
</TradingInputError>
)}
</TradingFormGroup>
)}
{err && (
<p className="text-danger text-xs mb-4 first-letter:capitalize">
{err}
</p>
)}
<SubmitButton status={status} />
</form>
);
};
const SubmitButton = ({ status }: { status: Status }) => {
const t = useT();
const disabled = status === 'pending' || status === 'requested';
let text = t('Create');
if (status === 'requested') {
text = t('Confirm in wallet...');
} else if (status === 'pending') {
text = t('Confirming transaction...');
}
return (
<TradingButton type="submit" intent={Intent.Info} disabled={disabled}>
{text}
</TradingButton>
);
};
const parseAllowListText = (str: string) => {
return str
.split(',')
.map((v) => v.trim())
.filter(Boolean);
};
@@ -3,7 +3,7 @@ import { ErrorBoundary } from '@sentry/react';
import { CompetitionsHeader } from '../../components/competitions/competitions-header';
import { Intent, Loader, TradingButton } from '@vegaprotocol/ui-toolkit';
import { useGames } from './hooks/use-games';
import { useGames } from '../../lib/hooks/use-games';
import { useCurrentEpochInfoQuery } from '../referrals/hooks/__generated__/Epoch';
import { Link, useNavigate } from 'react-router-dom';
import { Links } from '../../lib/links';
@@ -13,7 +13,7 @@ import {
} from '../../components/competitions/competitions-cta';
import { GamesContainer } from '../../components/competitions/games-container';
import { CompetitionsLeaderboard } from '../../components/competitions/competitions-leaderboard';
import { useTeams } from './hooks/use-teams';
import { useTeams } from '../../lib/hooks/use-teams';
import take from 'lodash/take';
import { usePageTitle } from '../../lib/hooks/use-page-title';
@@ -22,6 +22,7 @@ import { ErrorBoundary } from '../../components/error-boundary';
import { LayoutWithGradient } from '../../components/layouts-inner';
import { useVegaWallet } from '@vegaprotocol/wallet';
import { JoinTeam } from './join-team';
import { UpdateTeamButton } from './update-team-button';
export const CompetitionsTeam = () => {
const t = useT();
@@ -95,6 +96,7 @@ const TeamPage = ({
<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>
<JoinTeam team={team} partyTeam={partyTeam} refetch={refetch} />
<UpdateTeamButton team={team} />
</div>
</header>
<TeamStats stats={stats} members={members} games={games} />
@@ -2,7 +2,7 @@ import { ErrorBoundary } from '@sentry/react';
import { CompetitionsHeader } from '../../components/competitions/competitions-header';
import { useRef, useState } from 'react';
import { useT } from '../../lib/use-t';
import { useTeams } from './hooks/use-teams';
import { useTeams } from '../../lib/hooks/use-teams';
import { CompetitionsLeaderboard } from '../../components/competitions/competitions-leaderboard';
import {
Input,
@@ -0,0 +1,106 @@
import { ErrorBoundary } from '../../components/error-boundary';
import { usePageTitle } from '../../lib/hooks/use-page-title';
import { Box } from '../../components/competitions/box';
import { useT } from '../../lib/use-t';
import { useVegaWallet, useVegaWalletDialogStore } from '@vegaprotocol/wallet';
import { Loader, Splash } from '@vegaprotocol/ui-toolkit';
import { RainbowButton } from '../../components/rainbow-button';
import { Link, Navigate, useParams } from 'react-router-dom';
import { Links } from '../../lib/links';
import { useReferralSetTransaction } from '../../lib/hooks/use-referral-set-transaction';
import { type FormFields, TeamForm, TransactionType } from './team-form';
import { useTeam } from '../../lib/hooks/use-team';
import { LayoutWithGradient } from '../../components/layouts-inner';
export const CompetitionsUpdateTeam = () => {
const t = useT();
usePageTitle([t('Competitions'), t('Update a team')]);
const { pubKey, isReadOnly } = useVegaWallet();
const openWalletDialog = useVegaWalletDialogStore(
(store) => store.openVegaWalletDialog
);
const { teamId } = useParams<{ teamId: string }>();
if (!teamId) {
return <Navigate to={Links.COMPETITIONS()} />;
}
return (
<ErrorBoundary feature="update-team">
<LayoutWithGradient>
<div className="mx-auto md:w-2/3 max-w-xl">
<Box className="flex flex-col gap-4">
<h1 className="calt text-2xl lg:text-3xl xl:text-5xl">
{t('Update a team')}
</h1>
{pubKey && !isReadOnly ? (
<UpdateTeamFormContainer teamId={teamId} pubKey={pubKey} />
) : (
<>
<p>{t('Connect to update the details of your team.')}</p>
<RainbowButton variant="border" onClick={openWalletDialog}>
{t('Connect wallet')}
</RainbowButton>
</>
)}
</Box>
</div>
</LayoutWithGradient>
</ErrorBoundary>
);
};
const UpdateTeamFormContainer = ({
teamId,
pubKey,
}: {
teamId: string;
pubKey: string;
}) => {
const t = useT();
const { team, loading, error } = useTeam(teamId, pubKey);
const { err, status, onSubmit } = useReferralSetTransaction({
onSuccess: () => {
// NOOP
},
});
if (loading) {
return <Loader size="small" />;
}
if (error) {
return (
<Splash className="gap-1">
<span>{t('Something went wrong.')}</span>
<Link to={Links.COMPETITIONS_TEAM(teamId)} className="underline">
{t("Go back to the team's profile")}
</Link>
</Splash>
);
}
const isMyTeam = team?.referrer === pubKey;
if (!isMyTeam) {
return <Navigate to={Links.COMPETITIONS_TEAM(teamId)} />;
}
const defaultValues: FormFields = {
id: team.teamId,
name: team.name,
url: team.teamUrl,
avatarUrl: team.avatarUrl,
private: team.closed,
allowList: team.allowList.join(','),
};
return (
<TeamForm
type={TransactionType.UpdateReferralSet}
status={status}
err={err}
isSolo={team.closed}
onSubmit={onSubmit}
defaultValues={defaultValues}
/>
);
};
@@ -0,0 +1,254 @@
import {
TradingFormGroup,
TradingInput,
TradingInputError,
TradingCheckbox,
TextArea,
TradingButton,
Intent,
} from '@vegaprotocol/ui-toolkit';
import { URL_REGEX, isValidVegaPublicKey } from '@vegaprotocol/utils';
import { type useReferralSetTransaction } from '../../lib/hooks/use-referral-set-transaction';
import { useT } from '../../lib/use-t';
import { useForm, Controller } from 'react-hook-form';
import type {
CreateReferralSet,
UpdateReferralSet,
Status,
} from '@vegaprotocol/wallet';
export type FormFields = {
id: string;
name: string;
url: string;
avatarUrl: string;
private: boolean;
allowList: string;
};
export enum TransactionType {
CreateReferralSet,
UpdateReferralSet,
}
const prepareTransaction = (
type: TransactionType,
fields: FormFields
): CreateReferralSet | UpdateReferralSet => {
switch (type) {
case TransactionType.CreateReferralSet:
return {
createReferralSet: {
isTeam: true,
team: {
name: fields.name,
teamUrl: fields.url,
avatarUrl: fields.avatarUrl,
closed: fields.private,
allowList: fields.private
? parseAllowListText(fields.allowList)
: [],
},
},
};
case TransactionType.UpdateReferralSet:
return {
updateReferralSet: {
id: fields.id,
isTeam: true,
team: {
name: fields.name,
teamUrl: fields.url,
avatarUrl: fields.avatarUrl,
closed: fields.private,
allowList: fields.private
? parseAllowListText(fields.allowList)
: [],
},
},
};
}
};
export const TeamForm = ({
type,
status,
err,
isSolo,
onSubmit,
defaultValues,
}: {
type: TransactionType;
status: ReturnType<typeof useReferralSetTransaction>['status'];
err: ReturnType<typeof useReferralSetTransaction>['err'];
isSolo: boolean;
onSubmit: ReturnType<typeof useReferralSetTransaction>['onSubmit'];
defaultValues?: FormFields;
}) => {
const t = useT();
const {
register,
handleSubmit,
control,
watch,
formState: { errors },
} = useForm<FormFields>({
defaultValues: {
private: isSolo,
...defaultValues,
},
});
const isPrivate = watch('private');
const sendTransaction = (fields: FormFields) => {
onSubmit(prepareTransaction(type, fields));
};
return (
<form onSubmit={handleSubmit(sendTransaction)}>
<input
type="hidden"
{...register('id', {
disabled: true,
})}
/>
<TradingFormGroup label={t('Team name')} labelFor="name">
<TradingInput {...register('name', { required: t('Required') })} />
{errors.name?.message && (
<TradingInputError forInput="name">
{errors.name.message}
</TradingInputError>
)}
</TradingFormGroup>
<TradingFormGroup
label={t('URL')}
labelFor="url"
labelDescription={t(
'Provide a link so users can learn more about your team'
)}
>
<TradingInput
{...register('url', {
pattern: { value: URL_REGEX, message: t('Invalid URL') },
})}
/>
{errors.url?.message && (
<TradingInputError forInput="url">
{errors.url.message}
</TradingInputError>
)}
</TradingFormGroup>
<TradingFormGroup
label={t('Avatar URL')}
labelFor="avatarUrl"
labelDescription={t('Provide a URL to a hosted image')}
>
<TradingInput
{...register('avatarUrl', {
pattern: {
value: URL_REGEX,
message: t('Invalid image URL'),
},
})}
/>
{errors.avatarUrl?.message && (
<TradingInputError forInput="avatarUrl">
{errors.avatarUrl.message}
</TradingInputError>
)}
</TradingFormGroup>
<TradingFormGroup
label={t('Make team private')}
labelFor="private"
hideLabel={true}
>
<Controller
name="private"
control={control}
render={({ field }) => {
return (
<TradingCheckbox
label={t('Make team private')}
checked={field.value}
onCheckedChange={(value) => {
field.onChange(value);
}}
disabled={isSolo}
/>
);
}}
/>
</TradingFormGroup>
{isPrivate && (
<TradingFormGroup
label={t('Public key allow list')}
labelFor="allowList"
labelDescription={t(
'Use a comma separated list to allow only specific public keys to join the team'
)}
>
<TextArea
{...register('allowList', {
required: t('Required'),
disabled: isSolo,
validate: {
allowList: (value) => {
const publicKeys = parseAllowListText(value);
if (publicKeys.every((pk) => isValidVegaPublicKey(pk))) {
return true;
}
return t('Invalid public key found in allow list');
},
},
})}
/>
{errors.allowList?.message && (
<TradingInputError forInput="avatarUrl">
{errors.allowList.message}
</TradingInputError>
)}
</TradingFormGroup>
)}
{err && <p className="text-danger text-xs mb-4 capitalize">{err}</p>}
<SubmitButton type={type} status={status} />
</form>
);
};
const SubmitButton = ({
type,
status,
}: {
type?: TransactionType;
status: Status;
}) => {
const t = useT();
const disabled = status === 'pending' || status === 'requested';
let text = t('Create');
if (type === TransactionType.UpdateReferralSet) {
text = t('Update');
}
if (status === 'requested') {
text = t('Confirm in wallet...');
} else if (status === 'pending') {
text = t('Confirming transaction...');
}
return (
<TradingButton type="submit" intent={Intent.Info} disabled={disabled}>
{text}
</TradingButton>
);
};
const parseAllowListText = (str: string) => {
return str
.split(',')
.map((v) => v.trim())
.filter(Boolean);
};
@@ -0,0 +1,20 @@
import { useVegaWallet } from '@vegaprotocol/wallet';
import { type Team } from '../../lib/hooks/use-team';
import { Intent, TradingAnchorButton } from '@vegaprotocol/ui-toolkit';
import { Links } from '../../lib/links';
export const UpdateTeamButton = ({ team }: { team: Team }) => {
const { pubKey, isReadOnly } = useVegaWallet();
if (pubKey && !isReadOnly && pubKey === team.referrer) {
return (
<TradingAnchorButton
data-testid="update-team-button"
href={Links.COMPETITIONS_UPDATE_TEAM(team.teamId)}
intent={Intent.Info}
/>
);
}
return null;
};
@@ -21,7 +21,7 @@ import { useT } from '../../lib/use-t';
import { Link, Navigate, useNavigate } from 'react-router-dom';
import { Links, Routes } from '../../lib/links';
import { useReferralProgram } from './hooks/use-referral-program';
import { useCreateReferralSet } from '../../lib/hooks/use-create-referral-set';
import { useReferralSetTransaction } from '../../lib/hooks/use-referral-set-transaction';
import { Trans } from 'react-i18next';
export const CreateCodeContainer = () => {
@@ -155,7 +155,7 @@ const CreateCodeDialog = ({
stakeAvailable: currentStakeAvailable,
requiredStake,
onSubmit,
} = useCreateReferralSet();
} = useReferralSetTransaction();
const { details: programDetails } = useReferralProgram();
@@ -1,7 +1,7 @@
import { Link } from 'react-router-dom';
import { Splash } from '@vegaprotocol/ui-toolkit';
import { getNumberFormat } from '@vegaprotocol/utils';
import { type useTeams } from '../../client-pages/competitions/hooks/use-teams';
import { type useTeams } from '../../lib/hooks/use-teams';
import { useT } from '../../lib/use-t';
import { Table } from '../table';
import { Rank } from './graphics/rank';
+1
View File
@@ -7,6 +7,7 @@ fragment TeamFields on Team {
createdAt
createdAtEpoch
closed
allowList
}
fragment TeamStatsFields on TeamStatistics {
+5 -4
View File
@@ -3,15 +3,15 @@ import * as Types from '@vegaprotocol/types';
import { gql } from '@apollo/client';
import * as Apollo from '@apollo/client';
const defaultOptions = {} as const;
export type TeamFieldsFragment = { __typename?: 'Team', teamId: string, referrer: string, name: string, teamUrl: string, avatarUrl: string, createdAt: any, createdAtEpoch: number, closed: boolean };
export type TeamFieldsFragment = { __typename?: 'Team', teamId: string, referrer: string, name: string, teamUrl: string, avatarUrl: string, createdAt: any, createdAtEpoch: number, closed: boolean, allowList: Array<string> };
export type TeamStatsFieldsFragment = { __typename?: 'TeamStatistics', teamId: string, totalQuantumVolume: string, totalQuantumRewards: string, totalGamesPlayed: number, gamesPlayed: Array<string>, quantumRewards: Array<{ __typename?: 'QuantumRewardsPerEpoch', epoch: number, total_quantum_rewards: string }> };
export type TeamRefereeFieldsFragment = { __typename?: 'TeamReferee', teamId: string, referee: string, joinedAt: any, joinedAtEpoch: number };
export type TeamEntityFragment = { __typename?: 'TeamGameEntity', rank: number, volume: string, rewardMetric: string, rewardEarned: string, totalRewardsEarned: string, team: { __typename?: 'TeamParticipation', teamId: string } };
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: string, 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 TeamQueryVariables = Types.Exact<{
teamId: Types.Scalars['ID'];
@@ -19,7 +19,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 } }> } | 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 } }> } | 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, total_quantum_rewards: 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: string, rewardEarned: string, totalRewardsEarned: string, team: { __typename?: 'TeamParticipation', teamId: string } }> } } | null> | 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, total_quantum_rewards: 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 } };
export const TeamFieldsFragmentDoc = gql`
fragment TeamFields on Team {
@@ -31,6 +31,7 @@ export const TeamFieldsFragmentDoc = gql`
createdAt
createdAtEpoch
closed
allowList
}
`;
export const TeamStatsFieldsFragmentDoc = gql`
@@ -1,6 +1,6 @@
import compact from 'lodash/compact';
import { useActiveRewardsQuery } from '../../../components/rewards-container/__generated__/Rewards';
import { isActiveReward } from '../../../components/rewards-container/active-rewards';
import { useActiveRewardsQuery } from '../../components/rewards-container/__generated__/Rewards';
import { isActiveReward } from '../../components/rewards-container/active-rewards';
import { EntityScope, type TransferNode } from '@vegaprotocol/types';
const isScopedToTeams = (node: TransferNode) =>
@@ -1,14 +1,15 @@
import {
useSimpleTransaction,
type CreateReferralSet,
type Options,
type CreateReferralSet,
type UpdateReferralSet,
} from '@vegaprotocol/wallet';
import { useStakeAvailable } from './use-stake-available';
/**
* Manages state for creating a referral set or team
*/
export const useCreateReferralSet = (opts?: Options) => {
export const useReferralSetTransaction = (opts?: Options) => {
const { stakeAvailable, requiredStake, isEligible } = useStakeAvailable();
const { status, result, error, send } = useSimpleTransaction({
@@ -16,7 +17,7 @@ export const useCreateReferralSet = (opts?: Options) => {
onError: opts?.onError,
});
const onSubmit = (tx: CreateReferralSet) => {
const onSubmit = (tx: CreateReferralSet | UpdateReferralSet) => {
send(tx);
};
+6 -3
View File
@@ -18,9 +18,10 @@ export const Routes = {
REFERRALS_CREATE_CODE: '/referrals/create-code',
COMPETITIONS: '/competitions',
COMPETITIONS_TEAMS: '/competitions/teams',
COMPETITIONS_TEAM: '/competitions/teams/:teamId',
COMPETITIONS_CREATE_TEAM: '/competitions/teams/create',
COMPETITIONS_CREATE_TEAM_SOLO: '/competitions/teams/create?solo=true',
COMPETITIONS_TEAM: '/competitions/teams/:teamId',
COMPETITIONS_UPDATE_TEAM: '/competitions/teams/:teamId/update',
FEES: '/fees',
REWARDS: '/rewards',
} as const;
@@ -47,10 +48,12 @@ export const Links: ConsoleLinks = {
REFERRALS_CREATE_CODE: () => Routes.REFERRALS_CREATE_CODE,
COMPETITIONS: () => Routes.COMPETITIONS,
COMPETITIONS_TEAMS: () => Routes.COMPETITIONS_TEAMS,
COMPETITIONS_CREATE_TEAM: () => Routes.COMPETITIONS_CREATE_TEAM,
COMPETITIONS_CREATE_TEAM_SOLO: () => Routes.COMPETITIONS_CREATE_TEAM_SOLO,
COMPETITIONS_TEAM: (teamId: string) =>
Routes.COMPETITIONS_TEAM.replace(':teamId', teamId),
COMPETITIONS_CREATE_TEAM: () => Routes.COMPETITIONS_CREATE_TEAM,
COMPETITIONS_CREATE_TEAM_SOLO: () => Routes.COMPETITIONS_CREATE_TEAM_SOLO,
COMPETITIONS_UPDATE_TEAM: (teamId: string) =>
Routes.COMPETITIONS_UPDATE_TEAM.replace(':teamId', teamId),
FEES: () => Routes.FEES,
REWARDS: () => Routes.REWARDS,
};
+7 -2
View File
@@ -32,6 +32,7 @@ import { CompetitionsHome } from '../client-pages/competitions/competitions-home
import { CompetitionsTeams } from '../client-pages/competitions/competitions-teams';
import { CompetitionsTeam } from '../client-pages/competitions/competitions-team';
import { CompetitionsCreateTeam } from '../client-pages/competitions/competitions-create-team';
import { CompetitionsUpdateTeam } from '../client-pages/competitions/competitions-update-team';
// These must remain dynamically imported as pennant cannot be compiled by nextjs due to ESM
// Using dynamic imports is a workaround for this until pennant is published as ESM
@@ -112,13 +113,17 @@ export const useRouterConfig = (): RouteObject[] => {
],
},
// pages with blurred background
{
path: AppRoutes.COMPETITIONS_TEAM,
element: <CompetitionsTeam />,
},
{
path: AppRoutes.COMPETITIONS_CREATE_TEAM,
element: <CompetitionsCreateTeam />,
},
{
path: AppRoutes.COMPETITIONS_TEAM,
element: <CompetitionsTeam />,
path: AppRoutes.COMPETITIONS_UPDATE_TEAM,
element: <CompetitionsUpdateTeam />,
},
],
}
@@ -3,12 +3,14 @@ import type { ReactNode } from 'react';
export interface SplashProps {
children: ReactNode;
className?: classNames.Argument;
}
export const Splash = ({ children }: SplashProps) => {
export const Splash = ({ children, className }: SplashProps) => {
const splashClasses = classNames(
'w-full h-full text-xs text-center text-gray-800 dark:text-gray-200',
'flex items-center justify-center'
'flex items-center justify-center',
className
);
return <div className={splashClasses}>{children}</div>;
};
+16 -1
View File
@@ -458,6 +458,20 @@ export type CreateReferralSet = {
};
};
export type UpdateReferralSet = {
updateReferralSet: {
id: string;
isTeam: boolean;
team?: {
name: string;
teamUrl?: string;
avatarUrl?: string;
closed: boolean;
allowList: string[];
};
};
};
export enum MarginMode {
MARGIN_MODE_CROSS_MARGIN = 1,
MARGIN_MODE_ISOLATED_MARGIN,
@@ -489,7 +503,8 @@ export type Transaction =
| LiquidityProvisionSubmission
| ApplyReferralCode
| JoinTeam
| CreateReferralSet;
| CreateReferralSet
| UpdateReferralSet;
export const isMarginModeUpdateTransaction = (
transaction: Transaction