Allow alliance retire (& display member-specific info) (#7916)

This commit is contained in:
Jaco 2022-07-27 10:58:47 +02:00 committed by GitHub
parent 1d44d9a060
commit ad27ddb80c
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 118 additions and 24 deletions

View File

@ -3,9 +3,14 @@
import type { Member as MemberType } from './types';
import React from 'react';
import React, { useCallback, useContext, useMemo } from 'react';
import { AddressSmall } from '@polkadot/react-components';
import { AddressSmall, Badge, Menu, Popup, StatusContext } from '@polkadot/react-components';
import { useAccounts, useApi } from '@polkadot/react-hooks';
import { FormatBalance } from '@polkadot/react-query';
import { useTranslation } from './translate';
import useMemberInfo from './useMemberInfo';
interface Props {
className?: string;
@ -13,15 +18,62 @@ interface Props {
}
function Member ({ className, info: { accountId, role } }: Props): React.ReactElement<Props> {
const { t } = useTranslation();
const { api } = useApi();
const { allAccounts } = useAccounts();
const info = useMemberInfo(accountId);
const { queueExtrinsic } = useContext(StatusContext);
const hasActions = useMemo(
() => allAccounts.includes(accountId),
[allAccounts, accountId]
);
const doRetire = useCallback(
() => queueExtrinsic({
accountId,
extrinsic: api.tx.alliance.retire()
}),
[accountId, api, queueExtrinsic]
);
return (
<tr className={className}>
<td className='badge' />
<td className='badge'>
{info && info.isUpForKicking && (
<Badge
color='red'
icon='ban'
/>
)}
</td>
<td className='address all'>
<AddressSmall value={accountId} />
</td>
<td className='number'>
{info && info.deposit && (
<FormatBalance value={info.deposit} />
)}
</td>
<td className='number'>
{role}
</td>
<td className='button'>
{hasActions && (
<Popup
key='settings'
value={
<Menu>
<Menu.Item
isDisabled={info && info.isUpForKicking}
label={t<string>('Retire')}
onClick={doRetire}
/>
</Menu>
}
/>
)}
</td>
</tr>
);
}

View File

@ -23,7 +23,9 @@ function Members ({ className }: Props): React.ReactElement<Props> {
const hdrRef = useRef([
[t<string>('members'), 'start', 2],
[t<string>('role'), 'number']
[t<string>('deposit'), 'number'],
[t<string>('role'), 'number'],
[]
]);
return (

View File

@ -2,8 +2,15 @@
// SPDX-License-Identifier: Apache-2.0
import type { PalletAllianceMemberRole } from '@polkadot/types/lookup';
import type { BN } from '@polkadot/util';
export interface Member {
accountId: string;
role: PalletAllianceMemberRole['type'];
}
export interface MemberInfo {
accountId: string;
deposit?: BN | null;
isUpForKicking: boolean;
}

View File

@ -0,0 +1,29 @@
// Copyright 2017-2022 @polkadot/app-alliance authors & contributors
// SPDX-License-Identifier: Apache-2.0
import type { bool, Option, UInt } from '@polkadot/types';
import type { MemberInfo } from './types';
import { useMemo } from 'react';
import { createNamedHook, useApi, useCall } from '@polkadot/react-hooks';
function useMemberInfoImpl (accountId: string): MemberInfo | undefined {
const { api } = useApi();
const upForKicking = useCall<bool>(api.query.alliance.upForKicking, [accountId]);
const depositOf = useCall<Option<UInt>>(api.query.alliance.depositOf, [accountId]);
return useMemo((): MemberInfo | undefined => {
if (upForKicking && depositOf) {
return {
accountId,
deposit: depositOf.unwrapOr(null),
isUpForKicking: upForKicking.isTrue
};
}
return undefined;
}, [accountId, upForKicking, depositOf]);
}
export default createNamedHook('useMemberInfo', useMemberInfoImpl);

View File

@ -4,18 +4,28 @@
import type { AccountId32 } from '@polkadot/types/interfaces';
import type { Member } from './types';
import { useMemo } from 'react';
import { useEffect, useState } from 'react';
import { createNamedHook, useApi, useCallMulti } from '@polkadot/react-hooks';
const ROLES = <const> ['Founder', 'Fellow', 'Ally'];
function addMembers (all: Member[], role: Member['role'], accountIds: AccountId32[] = []): Member[] {
for (let i = 0; i < accountIds.length; i++) {
all.push({
accountId: accountIds[i].toString(),
role
});
function addMembers (prev: Member[] = [], query: AccountId32[][]): Member[] {
const all: Member[] = [];
for (let i = 0; i < ROLES.length; i++) {
const role = ROLES[i];
const accountIds = query[i];
for (let j = 0; j < accountIds.length; j++) {
const accountId = accountIds[j].toString();
const existing = prev.find((p) => p.accountId === accountId && p.role === role);
all.push(existing || {
accountId,
role
});
}
}
return all;
@ -23,25 +33,19 @@ function addMembers (all: Member[], role: Member['role'], accountIds: AccountId3
function useMembersImpl (): Member[] | undefined {
const { api } = useApi();
const [state, setState] = useState<Member[] | undefined>();
const query = useCallMulti<AccountId32[][]>([
[api.query.alliance.members, ROLES[0]],
[api.query.alliance.members, ROLES[1]],
[api.query.alliance.members, ROLES[2]]
]);
return useMemo((): Member[] | undefined => {
if (query && query.length === ROLES.length) {
const all: Member[] = [];
for (let i = 0; i < ROLES.length; i++) {
addMembers(all, ROLES[i], query[i]);
}
return all;
}
return undefined;
useEffect((): void => {
query && query.length === ROLES.length &&
setState((prev) => addMembers(prev, query));
}, [query]);
return state;
}
export default createNamedHook('useMembersImpl', useMembersImpl);
export default createNamedHook('useMembers', useMembersImpl);