Skip to content

Commit e4a6062

Browse files
authored
fix(tangle-cloud): Fixes & Improvements (#3038)
1 parent 54540fa commit e4a6062

File tree

9 files changed

+259
-75
lines changed

9 files changed

+259
-75
lines changed

apps/tangle-cloud/src/pages/operators/page.tsx

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,15 +3,23 @@ import useRestakeDelegatorInfo from '@tangle-network/tangle-shared-ui/data/resta
33
import useRestakeOperatorMap from '@tangle-network/tangle-shared-ui/data/restake/useRestakeOperatorMap';
44
import useRestakeTvl from '@tangle-network/tangle-shared-ui/data/restake/useRestakeTvl';
55
import { DelegatorInfo } from '@tangle-network/tangle-shared-ui/types/restake';
6-
import { FC, useCallback, useEffect } from 'react';
6+
import { FC, useCallback, useEffect, useState } from 'react';
77

88
const Page: FC = () => {
99
const { result: delegatorInfo } = useRestakeDelegatorInfo();
10+
11+
const [refreshTrigger, setRefreshTrigger] = useState(0);
12+
13+
const handleOperatorJoined = useCallback(() => {
14+
setTimeout(() => {
15+
setRefreshTrigger((v) => v + 1);
16+
}, 2000);
17+
}, []);
1018
const {
1119
result: operatorMap,
1220
isLoading: isLoadingOperators,
1321
error: operatorMapError,
14-
} = useRestakeOperatorMap();
22+
} = useRestakeOperatorMap(refreshTrigger);
1523
const { operatorConcentration, operatorTvl } = useRestakeTvl(
1624
delegatorInfo as DelegatorInfo | null,
1725
);
@@ -30,6 +38,7 @@ const Page: FC = () => {
3038
return (
3139
<div className="!mt-16">
3240
<OperatorsTableContainer
41+
onOperatorJoined={handleOperatorJoined}
3342
operatorConcentration={operatorConcentration}
3443
operatorMap={operatorMap}
3544
operatorTvl={operatorTvl}

apps/tangle-dapp/src/components/account/ProtocolStatisticCard/VaultsHightlightCard.tsx

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -197,7 +197,7 @@ const VaultHighlight = ({
197197
<Typography
198198
variant="h4"
199199
fw="bold"
200-
className="text-mono-0 dark:text-mono-0"
200+
className="text-mono-180 dark:text-mono-0"
201201
>
202202
{vaultName}
203203
</Typography>
@@ -208,8 +208,8 @@ const VaultHighlight = ({
208208
result={vaultTvl || EMPTY_VALUE_PLACEHOLDER}
209209
isLoading={isVaultTvlLoading}
210210
error={null}
211-
labelClassName="text-mono-60 dark:text-mono-60"
212-
valueClassName="text-mono-0 dark:text-mono-0"
211+
labelClassName="text-mono-180 dark:text-mono-60"
212+
valueClassName="text-mono-200 dark:text-mono-0"
213213
/>
214214

215215
<StatsItem
@@ -221,8 +221,8 @@ const VaultHighlight = ({
221221
}
222222
isLoading={isVaultParticipantsLoading}
223223
error={vaultParticipantsError}
224-
labelClassName="text-mono-60 dark:text-mono-60"
225-
valueClassName="text-mono-0 dark:text-mono-0"
224+
labelClassName="text-mono-180 dark:text-mono-60"
225+
valueClassName="text-mono-200 dark:text-mono-0"
226226
/>
227227
</div>
228228
</div>

apps/tangle-dapp/src/features/claimCredits/components/ClaimCreditsButton.tsx

Lines changed: 36 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,8 @@ import {
1919
import { useCallback, useMemo, useState } from 'react';
2020
import useCredits from '../../../data/credits/useCredits';
2121
import useClaimCreditsTx from '../../../data/credits/useClaimCreditsTx';
22+
import { meetsMinimumClaimThreshold } from '../../../utils/creditConstraints';
23+
import CreditVelocityTooltip from './CreditVelocityTooltip';
2224

2325
const ClaimCreditsButton = () => {
2426
const { data, error, refetch, isPending } = useCredits();
@@ -37,6 +39,10 @@ const ClaimCreditsButton = () => {
3739
);
3840
}, [data]);
3941

42+
const meetsMinimumThreshold = useMemo(() => {
43+
return meetsMinimumClaimThreshold(data?.amount);
44+
}, [data?.amount]);
45+
4046
// Hide if there's no data
4147
if (data === undefined) {
4248
return;
@@ -67,21 +73,35 @@ const ClaimCreditsButton = () => {
6773
</DropdownButton>
6874

6975
<DropdownBody align="start" sideOffset={8} className="p-4 space-y-3">
70-
<Typography variant="body3" fw="bold" className="!text-muted uppercase">
71-
Unclaimed AI Credits
72-
</Typography>
76+
<div className="flex items-center justify-between">
77+
<div className="flex items-center gap-2">
78+
<Typography variant="body1" fw="bold">
79+
Unclaimed AI Credits
80+
</Typography>
81+
<CreditVelocityTooltip currentAmount={data?.amount} />
82+
</div>
83+
84+
<Typography variant="body1" fw="bold">
85+
{formattedCredits}
86+
</Typography>
87+
</div>
7388

74-
<Typography variant="h4" component="p" fw="bold">
75-
{formattedCredits}
76-
</Typography>
89+
{data?.amount && !data.amount.isZero() && !meetsMinimumThreshold && (
90+
<Typography
91+
variant="body2"
92+
className="text-blue-600 dark:text-blue-400"
93+
>
94+
Minimum 0.01 required to claim
95+
</Typography>
96+
)}
7797

7898
<Typography variant="body2" fw="semibold" className="!text-muted">
79-
Associate your account to claim these credits.
99+
Associate your AI app account to claim these credits.
80100
</Typography>
81101

82102
<div className="space-y-2">
83103
<Typography variant="body2" fw="semibold">
84-
Account ID
104+
AI App User ID
85105
</Typography>
86106
<TextField.Root
87107
error={inputError}
@@ -94,7 +114,7 @@ const ClaimCreditsButton = () => {
94114
setOffchainAccountId(e.target.value);
95115
setInputError('');
96116
}}
97-
placeholder="Enter your account ID"
117+
placeholder="Enter your AI app user ID"
98118
/>
99119
</TextField.Root>
100120
</div>
@@ -162,11 +182,17 @@ const CreditsButton = ({
162182

163183
const isLoading = useMemo(() => status === TxStatus.PROCESSING, [status]);
164184
const hasCredits = useMemo(() => credits && !credits.isZero(), [credits]);
185+
const meetsMinimumThreshold = useMemo(() => {
186+
return meetsMinimumClaimThreshold(credits);
187+
}, [credits]);
188+
const canClaim = useMemo(() => {
189+
return hasCredits && meetsMinimumThreshold;
190+
}, [hasCredits, meetsMinimumThreshold]);
165191

166192
return (
167193
<Button
168194
isFullWidth
169-
isDisabled={!hasCredits || isLoading || !offchainAccountId.trim()}
195+
isDisabled={!canClaim || isLoading || !offchainAccountId.trim()}
170196
onClick={handleClick}
171197
isLoading={isLoading}
172198
>

apps/tangle-dapp/src/features/claimCredits/components/ClaimCreditsModal.tsx

Lines changed: 26 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,8 @@ import {
1818
} from '@tangle-network/ui-components';
1919
import useCredits from '../../../data/credits/useCredits';
2020
import useClaimCreditsTx from '../../../data/credits/useClaimCreditsTx';
21+
import { meetsMinimumClaimThreshold } from '../../../utils/creditConstraints';
22+
import CreditVelocityTooltip from './CreditVelocityTooltip';
2123

2224
type Props = {
2325
isOpen: boolean;
@@ -63,6 +65,8 @@ const ClaimCreditsModal: FC<Props> = ({ isOpen, setIsOpen }) => {
6365

6466
const isLoading = status === TxStatus.PROCESSING;
6567
const hasCredits = data?.amount && !data.amount.isZero();
68+
const meetsMinimumThreshold = meetsMinimumClaimThreshold(data?.amount);
69+
const canClaim = hasCredits && meetsMinimumThreshold;
6670

6771
return (
6872
<Modal open={isOpen} onOpenChange={setIsOpen}>
@@ -79,24 +83,39 @@ const ClaimCreditsModal: FC<Props> = ({ isOpen, setIsOpen }) => {
7983
) : hasCredits ? (
8084
<>
8185
<div className="flex flex-col items-center justify-center p-4 bg-glass dark:bg-glass_dark rounded-xl border border-mono-0 dark:border-mono-180">
82-
<Typography variant="body1" fw="bold">
83-
Available Credits
84-
</Typography>
86+
<div className="flex items-center gap-2">
87+
<Typography variant="body1" fw="bold">
88+
Available Credits
89+
</Typography>
90+
<CreditVelocityTooltip
91+
currentAmount={data?.amount}
92+
tokenSymbol={nativeTokenSymbol}
93+
/>
94+
</div>
8595

8696
<Typography variant="h3" fw="bold">
8797
{formattedAmount} {nativeTokenSymbol}
8898
</Typography>
99+
100+
{!meetsMinimumThreshold && (
101+
<Typography
102+
variant="body2"
103+
className="text-yellow-600 dark:text-yellow-400 mt-2"
104+
>
105+
Minimum 0.01 {nativeTokenSymbol} required to claim
106+
</Typography>
107+
)}
89108
</div>
90109

91110
<div className="space-y-2">
92111
<Typography variant="body1" fw="bold">
93-
Account ID
112+
AI App User ID
94113
</Typography>
95114
<Typography
96115
variant="body2"
97116
className="text-mono-120 dark:text-mono-80"
98117
>
99-
Enter your account ID to associate with these credits
118+
Enter your AI app user ID to associate with these credits
100119
</Typography>
101120

102121
<TextField.Root
@@ -110,14 +129,14 @@ const ClaimCreditsModal: FC<Props> = ({ isOpen, setIsOpen }) => {
110129
setOffchainAccountId(e.target.value);
111130
setInputError('');
112131
}}
113-
placeholder="Enter your account ID"
132+
placeholder="Enter your AI app user ID"
114133
/>
115134
</TextField.Root>
116135
</div>
117136

118137
<Button
119138
isFullWidth
120-
isDisabled={!hasCredits || isLoading}
139+
isDisabled={!canClaim || isLoading}
121140
isLoading={isLoading}
122141
loadingText="Claiming credits..."
123142
onClick={handleClaimCredits}
Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,72 @@
1+
import { FC } from 'react';
2+
import { BN } from '@polkadot/util';
3+
import { InfoCircledIcon } from '@radix-ui/react-icons';
4+
import { Typography } from '@tangle-network/ui-components/typography/Typography';
5+
import {
6+
Tooltip,
7+
TooltipBody,
8+
TooltipTrigger,
9+
} from '@tangle-network/ui-components';
10+
import { TANGLE_TOKEN_DECIMALS } from '@tangle-network/dapp-config';
11+
import {
12+
formatDisplayAmount,
13+
AmountFormatStyle,
14+
} from '@tangle-network/ui-components';
15+
import {
16+
getCreditsNeededForMinimum,
17+
MINIMUM_CLAIMABLE_CREDITS,
18+
} from '../../../utils/creditConstraints';
19+
20+
type Props = {
21+
currentAmount: BN | null | undefined;
22+
tokenSymbol?: string;
23+
};
24+
25+
const CreditVelocityTooltip: FC<Props> = ({
26+
currentAmount,
27+
tokenSymbol = 'TNT',
28+
}) => {
29+
const creditsNeeded = getCreditsNeededForMinimum(currentAmount);
30+
31+
const formattedMinimum = formatDisplayAmount(
32+
MINIMUM_CLAIMABLE_CREDITS,
33+
TANGLE_TOKEN_DECIMALS,
34+
AmountFormatStyle.SHORT,
35+
);
36+
37+
const formattedCreditsNeeded = formatDisplayAmount(
38+
creditsNeeded,
39+
TANGLE_TOKEN_DECIMALS,
40+
AmountFormatStyle.SHORT,
41+
);
42+
43+
return (
44+
<Tooltip>
45+
<TooltipTrigger asChild>
46+
<InfoCircledIcon className="w-4 h-4 text-mono-120 dark:text-mono-80 cursor-help" />
47+
</TooltipTrigger>
48+
49+
<TooltipBody className="max-w-xs p-3 space-y-2">
50+
<Typography variant="body2" fw="bold">
51+
Minimum Claim Requirement
52+
</Typography>
53+
54+
<Typography variant="body2" className="text-mono-120 dark:text-mono-80">
55+
You need at least {formattedMinimum} {tokenSymbol} to claim credits.
56+
</Typography>
57+
58+
{!creditsNeeded.isZero() && (
59+
<Typography
60+
variant="body2"
61+
className="text-mono-120 dark:text-mono-80"
62+
>
63+
You need {formattedCreditsNeeded} {tokenSymbol} more to reach the
64+
minimum.
65+
</Typography>
66+
)}
67+
</TooltipBody>
68+
</Tooltip>
69+
);
70+
};
71+
72+
export default CreditVelocityTooltip;
Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
import { BN } from '@polkadot/util';
2+
import { TANGLE_TOKEN_DECIMALS } from '@tangle-network/dapp-config';
3+
4+
/**
5+
* Minimum claimable credit amount (0.01 tokens).
6+
*/
7+
export const MINIMUM_CLAIMABLE_CREDITS = new BN(10).pow(
8+
new BN(TANGLE_TOKEN_DECIMALS - 2),
9+
);
10+
11+
/**
12+
* Checks if the credit amount meets the minimum claimable threshold.
13+
*/
14+
export const meetsMinimumClaimThreshold = (
15+
amount: BN | null | undefined,
16+
): boolean => {
17+
if (!amount || amount.isZero()) {
18+
return false;
19+
}
20+
21+
return amount.gte(MINIMUM_CLAIMABLE_CREDITS);
22+
};
23+
24+
/**
25+
* Calculates how much more credits are needed to reach the minimum threshold.
26+
*/
27+
export const getCreditsNeededForMinimum = (
28+
amount: BN | null | undefined,
29+
): BN => {
30+
if (!amount) {
31+
return MINIMUM_CLAIMABLE_CREDITS;
32+
}
33+
34+
if (amount.gte(MINIMUM_CLAIMABLE_CREDITS)) {
35+
return new BN(0);
36+
}
37+
38+
return MINIMUM_CLAIMABLE_CREDITS.sub(amount);
39+
};

libs/tangle-shared-ui/src/components/Restaking/JoinOperatorsModal.tsx

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -20,9 +20,10 @@ import { TxStatus } from '../../hooks/useSubstrateTx';
2020

2121
type Props = {
2222
setIsOpen: (isOpen: boolean) => void;
23+
onSuccess?: () => void;
2324
};
2425

25-
const JoinOperatorsModal: FC<Props> = ({ setIsOpen }) => {
26+
const JoinOperatorsModal: FC<Props> = ({ setIsOpen, onSuccess }) => {
2627
const [bondAmount, setBondAmount] = useState<BN | null>(null);
2728
const { nativeTokenSymbol } = useNetworkStore();
2829
const { execute, status } = useJoinOperatorsTx();
@@ -73,8 +74,9 @@ const JoinOperatorsModal: FC<Props> = ({ setIsOpen }) => {
7374
if (status === TxStatus.COMPLETE) {
7475
setBondAmount(null);
7576
setIsOpen(false);
77+
onSuccess?.();
7678
}
77-
}, [setIsOpen, status]);
79+
}, [setIsOpen, status, onSuccess]);
7880

7981
return (
8082
<ModalContent size="sm">

0 commit comments

Comments
 (0)