Skip to content

Commit

Permalink
feat: Pool performance data to batches. Per-page fetching, pool join …
Browse files Browse the repository at this point in the history
…subset. (#2057)

Co-authored-by: Ting A Lin <linshaoting6@gmail.com>
  • Loading branch information
Ross Bulat and TingALin authored Apr 4, 2024
1 parent c4749c6 commit 965d3e1
Show file tree
Hide file tree
Showing 47 changed files with 785 additions and 459 deletions.
2 changes: 2 additions & 0 deletions src/Providers.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@ import type { Provider } from 'hooks/withProviders';
import { withProviders } from 'hooks/withProviders';
import { CommunityProvider } from 'contexts/Community';
import { OverlayProvider } from 'kits/Overlay/Provider';
import { JoinPoolsProvider } from 'contexts/Pools/JoinPools';

export const Providers = () => {
const {
Expand Down Expand Up @@ -83,6 +84,7 @@ export const Providers = () => {
FastUnstakeProvider,
PayoutsProvider,
PoolPerformanceProvider,
JoinPoolsProvider,
SetupProvider,
MenuProvider,
TooltipProvider,
Expand Down
11 changes: 6 additions & 5 deletions src/canvas/JoinPool/Header.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -25,19 +25,20 @@ export const Header = ({
autoSelected,
setActiveTab,
setSelectedPoolId,
setSelectedPoolCount,
}: JoinPoolHeaderProps) => {
const { t } = useTranslation();
const { closeCanvas } = useOverlay().canvas;

// Randomly select a new pool to display.
const handleChooseNewPool = () => {
// Trigger refresh of memoied selected bonded pool.
setSelectedPoolCount((prev: number) => prev + 1);
// Remove current pool from filtered so it is not selected again.
const filteredPools = filteredBondedPools.filter(
(pool) => String(pool.id) !== String(bondedPool.id)
);

// Randomly select a filtered bonded pool and set it as the selected pool.
const index = Math.ceil(Math.random() * filteredBondedPools.length - 1);
setSelectedPoolId(filteredBondedPools[index].id);
const index = Math.ceil(Math.random() * filteredPools.length - 1);
setSelectedPoolId(filteredPools[index].id);
};

return (
Expand Down
13 changes: 10 additions & 3 deletions src/canvas/JoinPool/Overview/PerformanceGraph.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ import {
} from 'chart.js';
import { useNetwork } from 'contexts/Network';
import { GraphWrapper, HeadingWrapper } from '../Wrappers';
import { Bar } from 'react-chartjs-2';
import { Line } from 'react-chartjs-2';
import BigNumber from 'bignumber.js';
import type { AnyJson } from 'types';
import { graphColors } from 'theme/graphs';
Expand Down Expand Up @@ -44,7 +44,9 @@ export const PerformanceGraph = ({ bondedPool }: OverviewSectionProps) => {
const { mode } = useTheme();
const { openHelp } = useHelp();
const { colors } = useNetwork().networkData;
const { poolRewardPoints } = usePoolPerformance();
const { getPoolRewardPoints } = usePoolPerformance();

const poolRewardPoints = getPoolRewardPoints('pool_join');
const rawEraRewardPoints = poolRewardPoints[bondedPool.addresses.stash] || {};

// Ref to the graph container.
Expand Down Expand Up @@ -100,6 +102,7 @@ export const PerformanceGraph = ({ bondedPool }: OverviewSectionProps) => {
},
y: {
stacked: true,
beginAtZero: true,
ticks: {
font: {
size: 10,
Expand Down Expand Up @@ -133,6 +136,10 @@ export const PerformanceGraph = ({ bondedPool }: OverviewSectionProps) => {
label: (context: AnyJson) =>
`${new BigNumber(context.parsed.y).decimalPlaces(0).toFormat()} ${t('eraPoints', { ns: 'library' })}`,
},
intersect: false,
interaction: {
mode: 'nearest',
},
},
},
};
Expand Down Expand Up @@ -172,7 +179,7 @@ export const PerformanceGraph = ({ bondedPool }: OverviewSectionProps) => {
height,
}}
>
<Bar options={options} data={data} />
<Line options={options} data={data} />
</div>
</GraphWrapper>
</div>
Expand Down
50 changes: 30 additions & 20 deletions src/canvas/JoinPool/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,35 +5,35 @@ import { CanvasFullScreenWrapper } from 'canvas/Wrappers';
import { useOverlay } from 'kits/Overlay/Provider';
import { JoinPoolInterfaceWrapper } from './Wrappers';
import { useBondedPools } from 'contexts/Pools/BondedPools';
import { useMemo, useState } from 'react';
import { useEffect, useMemo, useState } from 'react';
import { Header } from './Header';
import { Overview } from './Overview';
import { Nominations } from './Nominations';
import { usePoolPerformance } from 'contexts/Pools/PoolPerformance';
import { MaxEraRewardPointsEras } from 'consts';
import { useStaking } from 'contexts/Staking';
import { useJoinPools } from 'contexts/Pools/JoinPools';

export const JoinPool = () => {
const {
closeCanvas,
config: { options },
} = useOverlay().canvas;
const { eraStakers } = useStaking();
const { poolRewardPoints } = usePoolPerformance();
const { poolsForJoin } = useJoinPools();
const { getPoolRewardPoints } = usePoolPerformance();
const { poolsMetaData, bondedPools } = useBondedPools();
const poolRewardPoints = getPoolRewardPoints('pool_join');

// The active canvas tab.
const [activeTab, setActiveTab] = useState<number>(0);

// Trigger re-render when chosen selected pool is incremented.
const [selectedPoolCount, setSelectedPoolCount] = useState<number>(0);

// Filter bonded pools to only those that are open and that have active daily rewards for the last
// `MaxEraRewardPointsEras` eras. The second filter checks if the pool is in `eraStakers` for the
// active era.
const filteredBondedPools = useMemo(
() =>
bondedPools
poolsForJoin
.filter((pool) => {
// Fetch reward point data for the pool.
const rawEraRewardPoints =
Expand All @@ -53,28 +53,39 @@ export const JoinPool = () => {
staker.others.find(({ who }) => who !== pool.addresses.stash)
)
),
[bondedPools, poolRewardPoints]
[poolsForJoin, poolRewardPoints]
);

// The bonded pool to display. Use the provided `poolId`, or assign a random eligible filtered
// pool otherwise. Re-fetches when the selected pool count is incremented.
const bondedPool = useMemo(
const initialSelectedPoolId = useMemo(
() =>
options?.poolId
? bondedPools.find(({ id }) => id === options.poolId)
: filteredBondedPools[
(filteredBondedPools.length * Math.random()) << 0
],
[selectedPoolCount]
options?.poolId ||
filteredBondedPools[(filteredBondedPools.length * Math.random()) << 0]
.id ||
0,
[]
);

// The selected bonded pool id.
// The selected bonded pool id. Assigns a random id if one is not provided.
const [selectedPoolId, setSelectedPoolId] = useState<number>(
bondedPool?.id || 0
initialSelectedPoolId
);

// The bonded pool to display. Use the provided `poolId`, or assign a random eligible filtered
// pool otherwise. Re-fetches when the selected pool count is incremented.
const bondedPool = useMemo(
() => bondedPools.find(({ id }) => id === selectedPoolId),
[selectedPoolId]
);

// Close canvas if no pool id is selected.
useEffect(() => {
if (selectedPoolId === 0) {
closeCanvas();
}
}, [selectedPoolId]);

// Ensure bonded pool exists before rendering. Canvas should close if this is the case.
if (!bondedPool) {
closeCanvas();
return null;
}

Expand All @@ -86,7 +97,6 @@ export const JoinPool = () => {
setSelectedPoolId={setSelectedPoolId}
bondedPool={bondedPool}
metadata={poolsMetaData[selectedPoolId]}
setSelectedPoolCount={setSelectedPoolCount}
autoSelected={options?.poolId === undefined}
filteredBondedPools={filteredBondedPools}
/>
Expand Down
1 change: 0 additions & 1 deletion src/canvas/JoinPool/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,6 @@ export interface JoinPoolHeaderProps {
autoSelected: boolean;
setActiveTab: (tab: number) => void;
setSelectedPoolId: Dispatch<SetStateAction<number>>;
setSelectedPoolCount: Dispatch<SetStateAction<number>>;
}

export interface NominationsProps {
Expand Down
38 changes: 6 additions & 32 deletions src/canvas/PoolMembers/Lists/Default.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,9 @@
// SPDX-License-Identifier: GPL-3.0-only

import { isNotZero } from '@w3ux/utils';
import { useEffect, useRef, useState } from 'react';
import { useEffect, useState } from 'react';
import { useTranslation } from 'react-i18next';
import { listItemsPerBatch, listItemsPerPage } from 'library/List/defaults';
import { poolMembersPerPage } from 'library/List/defaults';
import { useApi } from 'contexts/Api';
import { usePoolMembers } from 'contexts/Pools/PoolMembers';
import { List, ListStatusHeader, Wrapper as ListWrapper } from 'library/List';
Expand All @@ -20,7 +20,6 @@ export const MembersListInner = ({
pagination,
batchKey,
members: initialMembers,
disableThrottle = false,
}: DefaultMembersListProps) => {
const { t } = useTranslation('pages');
const { isReady, activeEra } = useApi();
Expand All @@ -29,9 +28,6 @@ export const MembersListInner = ({
// current page
const [page, setPage] = useState<number>(1);

// current render iteration
const [renderIteration, setRenderIterationState] = useState<number>(1);

// default list of validators
const [membersDefault, setMembersDefault] =
useState<PoolMember[]>(initialMembers);
Expand All @@ -42,26 +38,13 @@ export const MembersListInner = ({
// is this the initial fetch
const [fetched, setFetched] = useState<Sync>('unsynced');

// render throttle iteration
const renderIterationRef = useRef(renderIteration);
const setRenderIteration = (iter: number) => {
renderIterationRef.current = iter;
setRenderIterationState(iter);
};

// pagination
const totalPages = Math.ceil(members.length / listItemsPerPage);
const pageEnd = page * listItemsPerPage - 1;
const pageStart = pageEnd - (listItemsPerPage - 1);

// render batch
const batchEnd = Math.min(
renderIteration * listItemsPerBatch - 1,
listItemsPerPage
);
const totalPages = Math.ceil(members.length / poolMembersPerPage);
const pageEnd = page * poolMembersPerPage - 1;
const pageStart = pageEnd - (poolMembersPerPage - 1);

// get throttled subset or entire list
const listMembers = members.slice(pageStart).slice(0, listItemsPerPage);
const listMembers = members.slice(pageStart).slice(0, poolMembersPerPage);

// handle validator list bootstrapping
const setupMembersList = () => {
Expand All @@ -85,15 +68,6 @@ export const MembersListInner = ({
}
}, [isReady, fetched, activeEra.index]);

// Render throttle.
useEffect(() => {
if (!(batchEnd >= pageEnd || disableThrottle)) {
setTimeout(() => {
setRenderIteration(renderIterationRef.current + 1);
}, 500);
}
}, [renderIterationRef.current]);

return !members.length ? null : (
<ListWrapper>
<List $flexBasisLarge={'33.33%'}>
Expand Down
36 changes: 5 additions & 31 deletions src/canvas/PoolMembers/Lists/FetchPage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@

import { useEffect, useRef, useState } from 'react';
import { useTranslation } from 'react-i18next';
import { listItemsPerBatch, listItemsPerPage } from 'library/List/defaults';
import { poolMembersPerPage } from 'library/List/defaults';
import { usePlugins } from 'contexts/Plugins';
import { useActivePool } from 'contexts/Pools/ActivePool';
import { usePoolMembers } from 'contexts/Pools/PoolMembers';
Expand All @@ -21,7 +21,6 @@ import { SubscanController } from 'controllers/SubscanController';
export const MembersListInner = ({
pagination,
batchKey,
disableThrottle = false,
memberCount,
}: FetchpageMembersListProps) => {
const { t } = useTranslation('pages');
Expand All @@ -40,26 +39,10 @@ export const MembersListInner = ({
// current page.
const [page, setPage] = useState<number>(1);

// current render iteration.
const [renderIteration, setRenderIterationState] = useState<number>(1);

// render throttle iteration.
const renderIterationRef = useRef(renderIteration);
const setRenderIteration = (iter: number) => {
renderIterationRef.current = iter;
setRenderIterationState(iter);
};

// pagination
const totalPages = Math.ceil(Number(memberCount) / listItemsPerPage);
const pageEnd = listItemsPerPage - 1;
const pageStart = pageEnd - (listItemsPerPage - 1);

// render batch
const batchEnd = Math.min(
renderIteration * listItemsPerBatch - 1,
listItemsPerPage
);
const totalPages = Math.ceil(Number(memberCount) / poolMembersPerPage);
const pageEnd = poolMembersPerPage - 1;
const pageStart = pageEnd - (poolMembersPerPage - 1);

// handle validator list bootstrapping
const fetchingMemberList = useRef<boolean>(false);
Expand All @@ -85,7 +68,7 @@ export const MembersListInner = ({
// get throttled subset or entire list
const listMembers = poolMembersApi
.slice(pageStart)
.slice(0, listItemsPerPage);
.slice(0, poolMembersPerPage);

// Refetch list when page changes.
useEffect(() => {
Expand All @@ -109,15 +92,6 @@ export const MembersListInner = ({
}
}, [fetchedPoolMembersApi, activePool]);

// Render throttle.
useEffect(() => {
if (!(batchEnd >= pageEnd || disableThrottle)) {
setTimeout(() => {
setRenderIteration(renderIterationRef.current + 1);
}, 500);
}
}, [renderIterationRef.current]);

return (
<ListWrapper>
<List $flexBasisLarge={'33.33%'}>
Expand Down
1 change: 0 additions & 1 deletion src/canvas/PoolMembers/Lists/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@ import type { AnyJson } from 'types';
export interface MembersListProps {
pagination: boolean;
batchKey: string;
disableThrottle?: boolean;
selectToggleable?: boolean;
}

Expand Down
2 changes: 1 addition & 1 deletion src/consts.ts
Original file line number Diff line number Diff line change
Expand Up @@ -37,4 +37,4 @@ export const TipsThresholdMedium = 1200;
* Misc Values
*/
export const MaxPayoutDays = 60;
export const MaxEraRewardPointsEras = 14;
export const MaxEraRewardPointsEras = 10;
16 changes: 15 additions & 1 deletion src/contexts/Filters/defaults.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
// SPDX-License-Identifier: GPL-3.0-only
/* eslint-disable @typescript-eslint/no-unused-vars, @typescript-eslint/no-empty-function */

import type { FiltersContextInterface } from './types';
import type { FilterItem, FiltersContextInterface } from './types';

export const defaultFiltersInterface: FiltersContextInterface = {
getFilters: (type, group) => [],
Expand All @@ -18,3 +18,17 @@ export const defaultFiltersInterface: FiltersContextInterface = {
applyFilters: (type, g, l, f) => {},
applyOrder: (g, l, f) => {},
};

export const defaultIncludes: FilterItem[] = [
{
key: 'pools',
filters: ['active'],
},
];

export const defaultExcludes: FilterItem[] = [
{
key: 'pools',
filters: ['locked', 'destroying'],
},
];
Loading

0 comments on commit 965d3e1

Please sign in to comment.