Add own stash jumps (#7259)
This commit is contained in:
parent
2c74cd3d70
commit
3ead92db53
@ -5,12 +5,14 @@ import type { u64 } from '@polkadot/types';
|
||||
import type { PalletBagsListListBag } from '@polkadot/types/lookup';
|
||||
import type { StashNode } from './types';
|
||||
|
||||
import React from 'react';
|
||||
import React, { useEffect, useState } from 'react';
|
||||
|
||||
import { AddressMini } from '@polkadot/react-components';
|
||||
import { AddressMini, Spinner } from '@polkadot/react-components';
|
||||
import { formatNumber } from '@polkadot/util';
|
||||
|
||||
// import useBagEntries from './useBagEntries';
|
||||
import Stash from './Stash';
|
||||
import useBagEntries from './useBagEntries';
|
||||
import useBonded from './useBonded';
|
||||
|
||||
interface Props {
|
||||
id: u64;
|
||||
@ -18,8 +20,22 @@ interface Props {
|
||||
stashNodes?: StashNode[];
|
||||
}
|
||||
|
||||
export default function Bag ({ id, info, stashNodes = [] }: Props): React.ReactElement<Props> {
|
||||
// const entries = useBagEntries(stashNodes.length ? info.head.unwrapOr(null) : null);
|
||||
const NO_NODES: StashNode[] = [];
|
||||
|
||||
export default function Bag ({ id, info, stashNodes = NO_NODES }: Props): React.ReactElement<Props> {
|
||||
const [isLoading, setLoading] = useState(true);
|
||||
const { isCompleted, list } = useBagEntries(stashNodes.length ? info.head.unwrapOr(null) : null);
|
||||
const bonded = useBonded(isCompleted && list);
|
||||
|
||||
useEffect((): void => {
|
||||
setLoading(
|
||||
stashNodes.length
|
||||
? isCompleted
|
||||
? !bonded
|
||||
: true
|
||||
: false
|
||||
);
|
||||
}, [bonded, isCompleted, list, stashNodes]);
|
||||
|
||||
return (
|
||||
<tr>
|
||||
@ -28,13 +44,21 @@ export default function Bag ({ id, info, stashNodes = [] }: Props): React.ReactE
|
||||
<td className='address'>{info.tail.isSome && <AddressMini value={info.tail} />}</td>
|
||||
<td className='address'>
|
||||
{stashNodes?.map(({ stashId }) => (
|
||||
<AddressMini
|
||||
<Stash
|
||||
key={stashId}
|
||||
value={stashId}
|
||||
withBonded
|
||||
list={bonded}
|
||||
stashId={stashId}
|
||||
/>
|
||||
))}
|
||||
</td>
|
||||
<td className='number'>
|
||||
{isLoading
|
||||
? <Spinner noLabel />
|
||||
: list.length
|
||||
? formatNumber(list.length)
|
||||
: null
|
||||
}
|
||||
</td>
|
||||
</tr>
|
||||
);
|
||||
}
|
||||
|
||||
47
packages/page-staking/src/Bags/Stash.tsx
Normal file
47
packages/page-staking/src/Bags/Stash.tsx
Normal file
@ -0,0 +1,47 @@
|
||||
// Copyright 2017-2022 @polkadot/app-staking authors & contributors
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
import type { ListNode } from './types';
|
||||
|
||||
import React, { useMemo } from 'react';
|
||||
|
||||
import { AddressMini, TxButton } from '@polkadot/react-components';
|
||||
import { useApi } from '@polkadot/react-hooks';
|
||||
|
||||
import { useTranslation } from '../translate';
|
||||
|
||||
interface Props {
|
||||
list?: ListNode[];
|
||||
stashId: string;
|
||||
}
|
||||
|
||||
function findEntry (stashId: string, list: ListNode[] = []): ListNode | null {
|
||||
return list.find((o) => o.stashId === stashId) || null;
|
||||
}
|
||||
|
||||
export default function Stash ({ list, stashId }: Props): React.ReactElement<Props> {
|
||||
const { t } = useTranslation();
|
||||
const { api } = useApi();
|
||||
const stashInfo = useMemo(
|
||||
() => findEntry(stashId, list),
|
||||
[list, stashId]
|
||||
);
|
||||
|
||||
return (
|
||||
<div>
|
||||
<AddressMini
|
||||
value={stashId}
|
||||
withBonded
|
||||
/>
|
||||
{stashInfo && stashInfo.jump && (
|
||||
<TxButton
|
||||
accountId={stashInfo?.stashId}
|
||||
icon='caret-up'
|
||||
label={t<string>('Jump')}
|
||||
params={[stashInfo?.jump]}
|
||||
tx={api.tx.bagsList.putInFrontOf}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@ -23,7 +23,7 @@ export default function Summary ({ className = '', ids }: Props): React.ReactEle
|
||||
<CardSummary label={t<string>('total bags')}>
|
||||
{ids
|
||||
? formatNumber(ids.length)
|
||||
: <Spinner />
|
||||
: <Spinner noLabel />
|
||||
}
|
||||
</CardSummary>
|
||||
</SummaryBox>
|
||||
|
||||
@ -34,7 +34,8 @@ export default function Bags ({ ownStashes }: Props): React.ReactElement<Props>
|
||||
[t('bags')],
|
||||
[t('head'), 'address'],
|
||||
[t('tail'), 'address'],
|
||||
[t('mine'), 'address']
|
||||
[t('mine'), 'address'],
|
||||
[]
|
||||
]);
|
||||
|
||||
const sorted = useMemo(
|
||||
|
||||
@ -2,8 +2,16 @@
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
import type { PalletBagsListListNode } from '@polkadot/types/lookup';
|
||||
import type { BN } from '@polkadot/util';
|
||||
|
||||
export interface StashNode {
|
||||
stashId: string;
|
||||
node: PalletBagsListListNode;
|
||||
}
|
||||
|
||||
export interface ListNode {
|
||||
bonded: BN;
|
||||
index: number;
|
||||
jump: string | null;
|
||||
stashId: string;
|
||||
}
|
||||
|
||||
@ -9,19 +9,24 @@ import { useEffect, useState } from 'react';
|
||||
|
||||
import { createNamedHook, useApi, useCall } from '@polkadot/react-hooks';
|
||||
|
||||
function useBagEntriesImpl (headId: AccountId32 | null): AccountId32[] {
|
||||
interface Result {
|
||||
isCompleted: boolean;
|
||||
list: AccountId32[];
|
||||
}
|
||||
|
||||
const EMPTY: [AccountId32 | null, Result] = [null, { isCompleted: false, list: [] }];
|
||||
|
||||
function useBagEntriesImpl (headId: AccountId32 | null): Result {
|
||||
const { api } = useApi();
|
||||
const [[currId, entries], setCurrent] = useState<[AccountId32 | null, AccountId32[]]>([null, []]);
|
||||
const [[currId, result], setCurrent] = useState<[AccountId32 | null, Result]>(EMPTY);
|
||||
const node = useCall<Option<PalletBagsListListNode>>(!!currId && api.query.bagsList.listNodes, [currId]);
|
||||
|
||||
useEffect(
|
||||
(): void => {
|
||||
setCurrent(
|
||||
headId
|
||||
? [headId, [headId]]
|
||||
: [null, []]
|
||||
);
|
||||
},
|
||||
() => setCurrent(
|
||||
headId
|
||||
? [headId, { isCompleted: false, list: [headId] }]
|
||||
: [null, { isCompleted: true, list: [] }]
|
||||
),
|
||||
[headId]
|
||||
);
|
||||
|
||||
@ -32,12 +37,14 @@ function useBagEntriesImpl (headId: AccountId32 | null): AccountId32[] {
|
||||
if (next.isSome) {
|
||||
const currId = next.unwrap();
|
||||
|
||||
setCurrent(([, entries]) => [currId, [...entries, currId]]);
|
||||
setCurrent(([, { list }]) => [currId, { isCompleted: false, list: [...list, currId] }]);
|
||||
} else {
|
||||
setCurrent(([currId, { list }]) => [currId, { isCompleted: true, list }]);
|
||||
}
|
||||
}
|
||||
}, [node]);
|
||||
|
||||
return entries;
|
||||
return result;
|
||||
}
|
||||
|
||||
export default createNamedHook('useBagEntries', useBagEntriesImpl);
|
||||
|
||||
@ -12,8 +12,8 @@ const multiOptions = {
|
||||
transform: ([[ids], opts]: [[u64[]], Option<PalletBagsListListBag>[]]): Result[] =>
|
||||
ids
|
||||
.map((id, index): [u64, Option<PalletBagsListListBag>] => [id, opts[index]])
|
||||
.filter(([, opt]) => opt.isSome)
|
||||
.map(([id, opt]): Result => [id.toString(), id, opt.unwrap()]),
|
||||
.filter(([, o]) => o.isSome)
|
||||
.map(([id, o]): Result => [id.toString(), id, o.unwrap()]),
|
||||
withParamsTransform: true
|
||||
};
|
||||
|
||||
|
||||
40
packages/page-staking/src/Bags/useBonded.ts
Normal file
40
packages/page-staking/src/Bags/useBonded.ts
Normal file
@ -0,0 +1,40 @@
|
||||
// Copyright 2017-2022 @polkadot/app-staking authors & contributors
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
import type { DeriveStakingAccount } from '@polkadot/api-derive/types';
|
||||
import type { AccountId32 } from '@polkadot/types/interfaces';
|
||||
import type { ListNode } from './types';
|
||||
|
||||
import { createNamedHook, useApi, useCall } from '@polkadot/react-hooks';
|
||||
|
||||
const deriveOpts = {
|
||||
transform: (all: DeriveStakingAccount[]): ListNode[] => {
|
||||
const infos = all.map(({ stakingLedger, stashId }, index): ListNode => ({
|
||||
bonded: stakingLedger.active.unwrap(),
|
||||
index,
|
||||
jump: null,
|
||||
stashId: stashId.toString()
|
||||
}));
|
||||
|
||||
return infos.map((info) => {
|
||||
const lower = infos.find(({ bonded, index }) =>
|
||||
index < info.index &&
|
||||
bonded.lt(info.bonded)
|
||||
);
|
||||
|
||||
if (lower) {
|
||||
info.jump = lower.stashId;
|
||||
}
|
||||
|
||||
return info;
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
function useBondedImpl (ids?: false | AccountId32[]): ListNode[] | undefined {
|
||||
const { api } = useApi();
|
||||
|
||||
return useCall(ids && ids.length !== 0 && api.derive.staking.accounts, [ids], deriveOpts);
|
||||
}
|
||||
|
||||
export default createNamedHook('useBonded', useBondedImpl);
|
||||
Loading…
Reference in New Issue
Block a user