Skip to content

Commit

Permalink
feat(suite): add empty state and refetch logic for Solana staking rew…
Browse files Browse the repository at this point in the history
…ards
  • Loading branch information
dev-pvl committed Jan 23, 2025
1 parent 1514ed3 commit 3cb4a06
Show file tree
Hide file tree
Showing 7 changed files with 108 additions and 28 deletions.
13 changes: 13 additions & 0 deletions packages/suite/src/support/messages.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8568,6 +8568,19 @@ export default defineMessages({
defaultMessage:
'An epoch in Solana is approximately {count, plural, one {# day} other {# days}} long.',
},
TR_STAKE_REFRESH_REWARDS_TOOLTIP: {
id: 'TR_STAKE_REFRESH_REWARDS_TOOLTIP',
defaultMessage: 'Refresh your rewards for this account.',
},
TR_STAKE_REWARDS_ARE_EMPTY: {
id: 'TR_STAKE_REWARDS_ARE_EMPTY',
defaultMessage: 'No Rewards',
},
TR_STAKE_WAIT_TO_CHECK_REWARDS: {
id: 'TR_STAKE_WAIT_TO_CHECK_REWARDS',
defaultMessage:
'Wait up to {count, plural, one {# day} other {# days}} to check your rewards',
},
TR_STAKE_ETH_CARD_TITLE: {
id: 'TR_STAKE_ETH_CARD_TITLE',
defaultMessage: 'The easiest way to earn {symbol}',
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ import { ApyCard } from '../StakingDashboard/components/ApyCard';
import { PayoutCard } from '../StakingDashboard/components/PayoutCard';
import { ClaimCard } from '../StakingDashboard/components/ClaimCard';
import { StakingCard } from '../StakingDashboard/components/StakingCard';
import { RewardsList } from './components/RewardsList';
import { RewardsList } from './components/Rewards/RewardsList';

interface SolStakingDashboardProps {
selectedAccount: SelectedAccountLoaded;
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
import { SOLANA_EPOCH_DAYS } from '@suite-common/wallet-constants';

import { Translation } from 'src/components/suite';
import { AccountExceptionLayout } from 'src/components/wallet';

export const RewardsEmpty = () => (
<AccountExceptionLayout
title={<Translation id="TR_STAKE_REWARDS_ARE_EMPTY" />}
description={
<Translation
id="TR_STAKE_WAIT_TO_CHECK_REWARDS"
values={{ count: SOLANA_EPOCH_DAYS }}
/>
}
iconName="arrowLineDown"
iconVariant="tertiary"
/>
);
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import React, { useEffect, useRef, useState } from 'react';
import React, { useCallback, useEffect, useRef, useState } from 'react';

import {
EverstakeRewardsEndpointType,
Expand All @@ -7,13 +7,24 @@ import {
StakeAccountRewards,
} from '@suite-common/wallet-core';
import { formatNetworkAmount } from '@suite-common/wallet-utils';
import { Badge, Card, Column, Icon, Row, SkeletonStack, Text, Tooltip } from '@trezor/components';
import {
Badge,
Card,
Column,
Icon,
IconButton,
Row,
SkeletonStack,
Text,
Tooltip,
} from '@trezor/components';
import { spacings } from '@trezor/theme';
import { SOLANA_EPOCH_DAYS } from '@suite-common/wallet-constants';
import { useDebounce } from '@trezor/react-utils';

import {
CoinBalance,
FiatValue,
FormattedCryptoAmount,
FormattedDate,
HiddenPlaceholder,
Translation,
Expand All @@ -25,21 +36,25 @@ import { Pagination } from 'src/components/wallet';
import SkeletonTransactionItem from 'src/views/wallet/transactions/TransactionList/SkeletonTransactionItem';
import { ColDate } from 'src/views/wallet/transactions/TransactionList/TransactionsGroup/CommonComponents';

import { RewardsEmpty } from './RewardsEmpty';

const PAGE_SIZE_DEFAULT = 10;

interface RewardsListProps {
account: Account;
}

export const RewardsList = ({ account }: RewardsListProps) => {
const anchor = useSelector(state => state.router.anchor);
const { data, isLoading } =
useSelector(state => selectStakingRewards(state, account?.symbol)) || {};
useSelector(state => selectStakingRewards(state, account.symbol)) || {};

const { rewards } = data ?? {};
const selectedAccountRewards = rewards?.[account.descriptor];

const sectionRef = useRef<HTMLDivElement>(null);

const dispatch = useDispatch();
const debounce = useDebounce();

const perPage = PAGE_SIZE_DEFAULT;
const startPage = 1;
Expand All @@ -50,32 +65,41 @@ export const RewardsList = ({ account }: RewardsListProps) => {
const startIndex = (currentPage - 1) * perPage;
const stopIndex = startIndex + perPage;

const isSolanaMainnet = account.symbol === 'sol';

const fetchRewards = useCallback(
async ({ symbol, descriptor }: Account) => {
await debounce(() => {
if (symbol !== 'sol') return;
dispatch(
fetchEverstakeRewards({
symbol,
endpointType: EverstakeRewardsEndpointType.GetRewards,
address: descriptor,
}),
);
});
},
[dispatch, debounce],
);

useEffect(() => {
// Fetch rewards only for the Solana mainnet
if (account.symbol === 'sol') {
dispatch(
fetchEverstakeRewards({
symbol: account.symbol,
endpointType: EverstakeRewardsEndpointType.GetRewards,
address: account.descriptor,
}),
);
}
}, [anchor, account, dispatch]);
fetchRewards(account);
}, [account, fetchRewards]);

useEffect(() => {
if (rewards) {
const slicedRewards = rewards?.slice(startIndex, stopIndex);
if (selectedAccountRewards) {
const slicedRewards = selectedAccountRewards?.slice(startIndex, stopIndex);
setSlicedRewards(slicedRewards);
}
}, [currentPage, rewards, startIndex, stopIndex]);
}, [currentPage, selectedAccountRewards, startIndex, stopIndex]);

useEffect(() => {
// reset page on account change
setSelectedPage(startPage);
}, [account.descriptor, account.symbol, startPage]);

const totalItems = rewards?.length ?? 0;
const totalItems = selectedAccountRewards?.length ?? 0;
const showPagination = totalItems > perPage;
const isLastPage = stopIndex >= totalItems;

Expand All @@ -86,10 +110,29 @@ export const RewardsList = ({ account }: RewardsListProps) => {
}
};

if (!isSolanaMainnet || !totalItems) {
return <RewardsEmpty />;
}

return (
<DashboardSection
ref={sectionRef}
heading={<Translation id="TR_REWARDS" />}
actions={
<Column>
<Tooltip
maxWidth={250}
content={<Translation id="TR_STAKE_REFRESH_REWARDS_TOOLTIP" />}
>
<IconButton
icon="arrowClockwise"
variant="tertiary"
size="small"
onClick={() => fetchRewards(account)}
/>
</Tooltip>
</Column>
}
data-testid="@wallet/accounts/rewards-list"
>
{isLoading ? (
Expand Down Expand Up @@ -147,7 +190,7 @@ export const RewardsList = ({ account }: RewardsListProps) => {
{reward?.amount && (
<Column alignItems="end">
<HiddenPlaceholder>
<CoinBalance
<FormattedCryptoAmount
value={formatNetworkAmount(
reward?.amount,
account.symbol,
Expand Down
6 changes: 3 additions & 3 deletions suite-common/wallet-core/src/stake/stakeReducer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import { cloneObject } from '@trezor/utils';
import { NetworkSymbol } from '@suite-common/wallet-config';

import { stakeActions } from './stakeActions';
import { ValidatorsQueue, StakeAccountRewards } from './stakeTypes';
import { ValidatorsQueue, StakeRewardsByAccount } from './stakeTypes';
import { fetchEverstakeAssetData, fetchEverstakeData, fetchEverstakeRewards } from './stakeThunks';
import { SerializedTx } from '../send/sendFormTypes';

Expand Down Expand Up @@ -40,7 +40,7 @@ export interface StakeState {
error: boolean | string;
isLoading: boolean;
lastSuccessfulFetchTimestamp: Timestamp;
data: { rewards?: StakeAccountRewards[] };
data: { rewards?: StakeRewardsByAccount };
};
};
};
Expand Down Expand Up @@ -174,7 +174,7 @@ export const prepareStakeReducer = createReducerWithExtraDeps(stakeInitialState,
.addCase(fetchEverstakeRewards.pending, (state, action) => {
const { symbol } = action.meta.arg;

if (!state.data[symbol]) {
if (!state.data[symbol]?.stakingRewards) {
state.data[symbol] = {
stakingRewards: {
error: false,
Expand Down
8 changes: 5 additions & 3 deletions suite-common/wallet-core/src/stake/stakeThunks.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ import {
EverstakeEndpointType,
EverstakeRewardsEndpointType,
ValidatorsQueue,
StakeAccountRewards,
StakeRewardsByAccount,
} from './stakeTypes';
import { EVERSTAKE_ENDPOINT_PREFIX, EVERSTAKE_REWARDS_SOLANA_ENPOINT } from './stakeConstants';
import { selectAllNetworkSymbolsOfVisibleAccounts } from '../accounts/accountsReducer';
Expand Down Expand Up @@ -108,7 +108,7 @@ export const fetchEverstakeAssetData = createThunk<
);

export const fetchEverstakeRewards = createThunk<
{ rewards: StakeAccountRewards[] },
{ rewards: StakeRewardsByAccount },
{
symbol: SupportedSolanaNetworkSymbols;
endpointType: EverstakeRewardsEndpointType;
Expand All @@ -132,7 +132,9 @@ export const fetchEverstakeRewards = createThunk<
const data = await response.json();

return fulfillWithValue({
rewards: data,
rewards: {
[address]: data,
},
});
} catch (error) {
return rejectWithValue(error.toString());
Expand Down
4 changes: 4 additions & 0 deletions suite-common/wallet-core/src/stake/stakeTypes.ts
Original file line number Diff line number Diff line change
Expand Up @@ -105,3 +105,7 @@ export type StakeAccountRewards = {
currency: string;
time: string;
};

export type StakeRewardsByAccount = {
[address: string]: StakeAccountRewards[];
};

0 comments on commit 3cb4a06

Please sign in to comment.