Compare commits
1
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
d1a3d24db5 |
@@ -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';
|
||||
|
||||
@@ -7,6 +7,7 @@ fragment TeamFields on Team {
|
||||
createdAt
|
||||
createdAtEpoch
|
||||
closed
|
||||
allowList
|
||||
}
|
||||
|
||||
fragment TeamStatsFields on TeamStatistics {
|
||||
|
||||
+5
-4
@@ -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`
|
||||
|
||||
+2
-2
@@ -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) =>
|
||||
+4
-3
@@ -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);
|
||||
};
|
||||
|
||||
@@ -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,
|
||||
};
|
||||
|
||||
@@ -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>;
|
||||
};
|
||||
|
||||
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user