Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(ux): Simplify pool list, fetch performance on More click #2070

Merged
merged 6 commits into from
Apr 9, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 5 additions & 4 deletions src/contexts/Pools/PoolPerformance/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,8 +24,9 @@ export interface PoolPerformanceContextInterface {
}

// Fetching status for keys.
export type PoolPerformanceTasks = Partial<
Record<PoolRewardPointsKey, PoolPerformanceTaskStatus>
export type PoolPerformanceTasks = Record<
PoolRewardPointsKey,
PoolPerformanceTaskStatus
>;

// Performance fetching status.
Expand All @@ -42,10 +43,10 @@ export interface PoolPerformanceTaskStatus {
*/

// Supported reward points batch keys.
export type PoolRewardPointsKey = 'pool_join' | 'pool_page';
export type PoolRewardPointsKey = string;

// Pool reward batches, keyed by batch key.
export type PoolRewardPointsMap = Partial<Record<string, PoolRewardPoints>>;
export type PoolRewardPointsMap = Record<PoolRewardPointsKey, PoolRewardPoints>;

// Pool reward points are keyed by era, then by pool address.

Expand Down
2 changes: 1 addition & 1 deletion src/library/List/defaults.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ export const defaultContext: ListContextInterface = {
};

// The amount of pools per page.
export const poolsPerPage = 21;
export const poolsPerPage = 30;

// The amount of validators per page.
export const validatorsPerPage = 30;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,30 +5,39 @@ import { faCaretRight } from '@fortawesome/free-solid-svg-icons';
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
import { useTranslation } from 'react-i18next';
import { useOverlay } from 'kits/Overlay/Provider';
import { usePoolPerformance } from 'contexts/Pools/PoolPerformance';
import type { BondedPool } from 'contexts/Pools/BondedPools/types';

export const JoinPool = ({
id,
export const More = ({
pool,
setActiveTab,
disabled,
}: {
id: number;
pool: BondedPool;
setActiveTab: (t: number) => void;
disabled: boolean;
}) => {
const { t } = useTranslation('tips');
const { openCanvas } = useOverlay().canvas;
const { startPoolRewardPointsFetch } = usePoolPerformance();

const { id, addresses } = pool;

// Define a unique pool performance data key
const performanceKey = `pool_page_standalone_${id}`;

return (
<div className="label button-with-text">
<button
type="button"
onClick={() => {
startPoolRewardPointsFetch(performanceKey, [addresses.stash]);
openCanvas({
key: 'JoinPool',
options: {
providedPool: {
id,
performanceBatchKey: 'pool_page',
performanceBatchKey: performanceKey,
},
onJoinCallback: () => setActiveTab(0),
},
Expand Down
89 changes: 28 additions & 61 deletions src/library/ListItem/Labels/PoolBonded.tsx
Original file line number Diff line number Diff line change
@@ -1,78 +1,45 @@
// Copyright 2024 @paritytech/polkadot-staking-dashboard authors & contributors
// SPDX-License-Identifier: GPL-3.0-only

import { capitalizeFirstLetter, planckToUnit, rmCommas } from '@w3ux/utils';
import { planckToUnit, rmCommas } from '@w3ux/utils';
import BigNumber from 'bignumber.js';
import { useEffect, useState } from 'react';
import { useTranslation } from 'react-i18next';
import { useBondedPools } from 'contexts/Pools/BondedPools';
import { useStaking } from 'contexts/Staking';
import { ValidatorStatusWrapper } from 'library/ListItem/Wrappers';
import type { Pool } from 'library/Pool/types';
import { useNetwork } from 'contexts/Network';
import type { Pool } from 'library/Pool/types';
import { TooltipTrigger } from '../Wrappers';
import { useTranslation } from 'react-i18next';
import { useTooltip } from 'contexts/Tooltip';

export const PoolBonded = ({ pool }: { pool: Pool }) => {
const { t } = useTranslation('library');
const {
networkData: { units, unit },
networkData: {
units,
brand: { token },
},
} = useNetwork();
const { getPoolNominationStatusCode, poolsNominations } = useBondedPools();
const { eraStakers, getNominationsStatusFromTargets } = useStaking();
const { addresses, points } = pool;

// get pool targets from nominations meta batch
const nominations = poolsNominations[pool.id];
const targets = nominations?.targets || [];
const { setTooltipTextAndOpen } = useTooltip();

// store nomination status in state
const [nominationsStatus, setNominationsStatus] =
useState<Record<string, string>>();
const tooltipText = t('bonded');

// update pool nomination status as nominations metadata becomes available.
// we cannot add effect dependencies here as this needs to trigger
// as soon as the component displays. (upon tab change).
const handleNominationsStatus = () => {
setNominationsStatus(
getNominationsStatusFromTargets(addresses.stash, targets)
);
};
const { points } = pool;
const TokenIcon = token;

// recalculate nominations status as app syncs
useEffect(() => {
if (
targets.length &&
nominationsStatus === null &&
eraStakers.stakers.length
) {
handleNominationsStatus();
}
});

// metadata has changed, which means pool items may have been added.
// recalculate nominations status
useEffect(() => {
handleNominationsStatus();
}, [pool, eraStakers.stakers.length, Object.keys(poolsNominations).length]);

// calculate total bonded pool amount
const poolBonded = planckToUnit(new BigNumber(rmCommas(points)), units);

// determine nominations status and display
const nominationStatus = getPoolNominationStatusCode(
nominationsStatus || null
);
// Format total bonded pool amount.
const bonded = planckToUnit(new BigNumber(rmCommas(points)), units);

return (
<ValidatorStatusWrapper $status={nominationStatus} $noMargin>
<h5>
{nominationStatus === null || !eraStakers.stakers.length
? `${t('syncing')}...`
: targets.length
? capitalizeFirstLetter(t(`${nominationStatus}`) ?? '')
: t('notNominating')}
{' / '}
{t('bonded')}: {poolBonded.decimalPlaces(3).toFormat()} {unit}
</h5>
</ValidatorStatusWrapper>
<div className="label pool">
<TooltipTrigger
className="tooltip-trigger-element"
data-tooltip-text={tooltipText}
onMouseMove={() => setTooltipTextAndOpen(tooltipText)}
/>

<TokenIcon
style={{ maxWidth: '1.25rem', height: '1.25rem' }}
className="token"
/>
{bonded.decimalPlaces(0).toFormat()}
</div>
);
};
70 changes: 70 additions & 0 deletions src/library/ListItem/Labels/PoolNominateStatus.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
// Copyright 2024 @paritytech/polkadot-staking-dashboard authors & contributors
// SPDX-License-Identifier: GPL-3.0-only

import { capitalizeFirstLetter } from '@w3ux/utils';
import { useEffect, useState } from 'react';
import { useTranslation } from 'react-i18next';
import { useBondedPools } from 'contexts/Pools/BondedPools';
import { useStaking } from 'contexts/Staking';
import { PoolStatusWrapper } from 'library/ListItem/Wrappers';
import type { Pool } from 'library/Pool/types';

export const PoolNominateStatus = ({ pool }: { pool: Pool }) => {
const { t } = useTranslation('library');
const { getPoolNominationStatusCode, poolsNominations } = useBondedPools();
const { eraStakers, getNominationsStatusFromTargets } = useStaking();
const { addresses } = pool;

// get pool targets from nominations meta batch
const nominations = poolsNominations[pool.id];
const targets = nominations?.targets || [];

// store nomination status in state
const [nominationsStatus, setNominationsStatus] =
useState<Record<string, string>>();

// update pool nomination status as nominations metadata becomes available.
// we cannot add effect dependencies here as this needs to trigger
// as soon as the component displays. (upon tab change).
const handleNominationsStatus = () => {
setNominationsStatus(
getNominationsStatusFromTargets(addresses.stash, targets)
);
};

// recalculate nominations status as app syncs
useEffect(() => {
if (
targets.length &&
nominationsStatus === null &&
eraStakers.stakers.length
) {
handleNominationsStatus();
}
});

// metadata has changed, which means pool items may have been added.
// recalculate nominations status
useEffect(() => {
handleNominationsStatus();
}, [pool, eraStakers.stakers.length, Object.keys(poolsNominations).length]);

// determine nominations status and display
const nominationStatus = getPoolNominationStatusCode(
nominationsStatus || null
);

return (
<PoolStatusWrapper $status={nominationStatus}>
<h4>
<span>
{nominationStatus === null || !eraStakers.stakers.length
? `${t('syncing')}...`
: targets.length
? capitalizeFirstLetter(t(`${nominationStatus}`) ?? '')
: t('notNominating')}
</span>
</h4>
</PoolStatusWrapper>
);
};
59 changes: 51 additions & 8 deletions src/library/ListItem/Wrappers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ export const Wrapper = styled.div`
--height-bottom-row: 2.75rem;
}
&.pool-more {
--height-bottom-row: 7.5rem;
--height-bottom-row: 5.75rem;
}

--height-total: calc(var(--height-top-row) + var(--height-bottom-row));
Expand Down Expand Up @@ -63,9 +63,14 @@ export const Wrapper = styled.div`
&.bottom {
height: var(--height-bottom-row);

&.pools {
align-items: flex-start;
}

&.lg {
display: flex;
align-items: center;

> div {
&:first-child {
flex-grow: 1;
Expand Down Expand Up @@ -94,6 +99,10 @@ export const Labels = styled.div`
padding: 0 0 0 0.25rem;
height: inherit;

&.yMargin {
margin-bottom: 0.9rem;
}

button {
background: var(--shimmer-foreground);
padding: 0 0.1rem;
Expand Down Expand Up @@ -129,16 +138,13 @@ export const Labels = styled.div`
align-items: center;
justify-content: center;
font-size: inherit;
margin: 0 0.4em;

@media (min-width: ${SmallFontSizeMaxWidth}px) {
margin: 0 0.35rem;
&.pool {
margin: 0 0.45rem;
}
> .token {
margin-right: 0.25rem;
}

&.button-with-text {
margin-right: 0;
margin: 0.25rem 0 0 0;

button {
color: var(--accent-color-secondary);
Expand Down Expand Up @@ -253,6 +259,43 @@ export const ValidatorStatusWrapper = styled.div<{
}
`;

export const PoolStatusWrapper = styled.div<{
$status: string;
}>`
h4,
h5 {
display: flex;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}

h4 {
color: var(--text-color-tertiary);
font-size: 1rem;

padding-top: ${(props) =>
props.$status === 'active' ? '0.15rem' : '0.25rem'};

> span {
color: ${(props) =>
props.$status === 'active'
? 'var(--status-success-color)'
: 'var(--text-color-tertiary)'};

border: 0.75px solid
${(props) =>
props.$status === 'active'
? 'var(--status-success-color)'
: 'transparent'};

padding: ${(props) => (props.$status === 'active' ? '0 0.5rem' : '0')};
border-radius: 0.3rem;
opacity: ${(props) => (props.$status === 'active' ? 1 : 0.6)};
}
}
`;

export const SelectWrapper = styled.button`
background: var(--background-input);
margin: 0 0.75rem 0 0.25rem;
Expand Down
Loading
Loading