Add alliance join (#7915)
This commit is contained in:
parent
89771038c8
commit
6980e1ebeb
@ -19,7 +19,7 @@ const SHA_COL = 'rgba(34, 36, 38, 0.12)';
|
||||
const SHA_OFF = '5px';
|
||||
|
||||
function Grouping ({ className = '', isActive, name, routes }: Props): React.ReactElement<Props> {
|
||||
if (routes.length === 1) {
|
||||
if (routes.length === 1 && routes[0].group === 'settings') {
|
||||
return (
|
||||
<Item
|
||||
className={isActive ? 'isActive' : ''}
|
||||
|
||||
72
packages/page-alliance/src/Join.tsx
Normal file
72
packages/page-alliance/src/Join.tsx
Normal file
@ -0,0 +1,72 @@
|
||||
// Copyright 2017-2022 @polkadot/app-alliance authors & contributors
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
import type { Member } from './types';
|
||||
|
||||
import React, { useMemo, useState } from 'react';
|
||||
|
||||
import { InputAddress, InputBalance, Modal, TxButton } from '@polkadot/react-components';
|
||||
import { useAccounts, useApi } from '@polkadot/react-hooks';
|
||||
|
||||
import { useTranslation } from './translate';
|
||||
|
||||
interface Props {
|
||||
className?: string;
|
||||
members: Member[];
|
||||
onClose: () => void;
|
||||
}
|
||||
|
||||
function Join ({ className, members, onClose }: Props): React.ReactElement<Props> {
|
||||
const { t } = useTranslation();
|
||||
const { api } = useApi();
|
||||
const { allAccounts } = useAccounts();
|
||||
const [accountId, setAccountId] = useState<string | null>(null);
|
||||
|
||||
const available = useMemo(
|
||||
(): string[] =>
|
||||
allAccounts.filter((a) =>
|
||||
!members.some(({ accountId }) =>
|
||||
accountId === a
|
||||
)
|
||||
),
|
||||
[allAccounts, members]
|
||||
);
|
||||
|
||||
return (
|
||||
<Modal
|
||||
className={className}
|
||||
header={t<string>('Join alliance')}
|
||||
onClose={onClose}
|
||||
size='large'
|
||||
>
|
||||
<Modal.Content>
|
||||
<Modal.Columns hint={t<string>('This account will be submitted to join the aliance. It will be allocated one of the alliance roles upon joining, starting with Ally.')}>
|
||||
<InputAddress
|
||||
filter={available}
|
||||
help={t<string>('Select the account you wish to join with')}
|
||||
label={t<string>('alliance account')}
|
||||
onChange={setAccountId}
|
||||
type='account'
|
||||
/>
|
||||
</Modal.Columns>
|
||||
<Modal.Columns hint={t('The bond will be reserved for the duration of your alliance membership.')}>
|
||||
<InputBalance
|
||||
defaultValue={api.consts.alliance.allyDeposit}
|
||||
help={t<string>('The deposit that is reserved')}
|
||||
isDisabled
|
||||
label={t<string>('alliance deposit')}
|
||||
/>
|
||||
</Modal.Columns>
|
||||
</Modal.Content>
|
||||
<Modal.Actions>
|
||||
<TxButton
|
||||
accountId={accountId}
|
||||
onStart={onClose}
|
||||
tx={api.tx.alliance.joinAlliance}
|
||||
/>
|
||||
</Modal.Actions>
|
||||
</Modal>
|
||||
);
|
||||
}
|
||||
|
||||
export default React.memo(Join);
|
||||
@ -20,7 +20,7 @@ function Member ({ className, info: { accountId, role } }: Props): React.ReactEl
|
||||
<AddressSmall value={accountId} />
|
||||
</td>
|
||||
<td className='number'>
|
||||
{role.type}
|
||||
{role}
|
||||
</td>
|
||||
</tr>
|
||||
);
|
||||
|
||||
@ -3,8 +3,10 @@
|
||||
|
||||
import React, { useRef } from 'react';
|
||||
|
||||
import { Table } from '@polkadot/react-components';
|
||||
import { Button, Table } from '@polkadot/react-components';
|
||||
import { useToggle } from '@polkadot/react-hooks';
|
||||
|
||||
import Join from './Join';
|
||||
import Member from './Member';
|
||||
import Summary from './Summary';
|
||||
import { useTranslation } from './translate';
|
||||
@ -14,8 +16,9 @@ interface Props {
|
||||
className?: string;
|
||||
}
|
||||
|
||||
function Collators ({ className }: Props): React.ReactElement<Props> {
|
||||
function Members ({ className }: Props): React.ReactElement<Props> {
|
||||
const { t } = useTranslation();
|
||||
const [isJoinOpen, toggleJoin] = useToggle();
|
||||
const members = useMembers();
|
||||
|
||||
const hdrRef = useRef([
|
||||
@ -26,6 +29,20 @@ function Collators ({ className }: Props): React.ReactElement<Props> {
|
||||
return (
|
||||
<div className={className}>
|
||||
<Summary />
|
||||
<Button.Group>
|
||||
<Button
|
||||
icon='add'
|
||||
isDisabled={!members}
|
||||
label={t<string>('Join')}
|
||||
onClick={toggleJoin}
|
||||
/>
|
||||
{members && isJoinOpen && (
|
||||
<Join
|
||||
members={members}
|
||||
onClose={toggleJoin}
|
||||
/>
|
||||
)}
|
||||
</Button.Group>
|
||||
<Table
|
||||
empty={members && t<string>('No members')}
|
||||
header={hdrRef.current}
|
||||
@ -41,4 +58,4 @@ function Collators ({ className }: Props): React.ReactElement<Props> {
|
||||
);
|
||||
}
|
||||
|
||||
export default React.memo(Collators);
|
||||
export default React.memo(Members);
|
||||
|
||||
@ -5,5 +5,5 @@ import type { PalletAllianceMemberRole } from '@polkadot/types/lookup';
|
||||
|
||||
export interface Member {
|
||||
accountId: string;
|
||||
role: PalletAllianceMemberRole;
|
||||
role: PalletAllianceMemberRole['type'];
|
||||
}
|
||||
|
||||
@ -1,43 +1,47 @@
|
||||
// Copyright 2017-2022 @polkadot/app-alliance authors & contributors
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
import type { StorageKey } from '@polkadot/types';
|
||||
import type { AccountId32 } from '@polkadot/types/interfaces';
|
||||
import type { PalletAllianceMemberRole } from '@polkadot/types/lookup';
|
||||
import type { Member } from './types';
|
||||
|
||||
import { createNamedHook, useApi, useCall } from '@polkadot/react-hooks';
|
||||
import { useMemo } from 'react';
|
||||
|
||||
const OPT_MEM = {
|
||||
transform: (entries: [StorageKey<[PalletAllianceMemberRole]>, AccountId32[]][]) =>
|
||||
entries
|
||||
.reduce((all: Member[], [{ args: [role] }, accountIds]): Member[] => {
|
||||
for (let i = 0; i < accountIds.length; i++) {
|
||||
all.push({
|
||||
accountId: accountIds[i].toString(),
|
||||
role
|
||||
});
|
||||
}
|
||||
import { createNamedHook, useApi, useCallMulti } from '@polkadot/react-hooks';
|
||||
|
||||
return all;
|
||||
}, [])
|
||||
.sort((a, b) =>
|
||||
a.role.type === b.role.type
|
||||
? 0
|
||||
: a.role.type === 'Founder'
|
||||
? -1
|
||||
: b.role.type === 'Founder'
|
||||
? 1
|
||||
: a.role.type === 'Fellow'
|
||||
? -1
|
||||
: 1
|
||||
)
|
||||
};
|
||||
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
|
||||
});
|
||||
}
|
||||
|
||||
return all;
|
||||
}
|
||||
|
||||
function useMembersImpl (): Member[] | undefined {
|
||||
const { api } = useApi();
|
||||
const query = useCallMulti<AccountId32[][]>([
|
||||
[api.query.alliance.members, ROLES[0]],
|
||||
[api.query.alliance.members, ROLES[1]],
|
||||
[api.query.alliance.members, ROLES[2]]
|
||||
]);
|
||||
|
||||
return useCall<Member[]>(api.query.alliance.members.entries, [], OPT_MEM);
|
||||
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;
|
||||
}, [query]);
|
||||
}
|
||||
|
||||
export default createNamedHook('useMembersImpl', useMembersImpl);
|
||||
|
||||
@ -14,7 +14,7 @@ import { useModuleElections } from '../useModuleElections';
|
||||
function SubmitCandidacy ({ electionsInfo }: Props): React.ReactElement<Props> | null {
|
||||
const { api } = useApi();
|
||||
const { t } = useTranslation();
|
||||
const [accountId, setAcountId] = useState<string | null>(null);
|
||||
const [accountId, setAccountId] = useState<string | null>(null);
|
||||
const { isOpen, onClose, onOpen } = useModal();
|
||||
const modLocation = useModuleElections();
|
||||
|
||||
@ -35,7 +35,7 @@ function SubmitCandidacy ({ electionsInfo }: Props): React.ReactElement<Props> |
|
||||
<InputAddress
|
||||
help={t<string>('Select the account you wish to submit for candidacy.')}
|
||||
label={t<string>('candidate account')}
|
||||
onChange={setAcountId}
|
||||
onChange={setAccountId}
|
||||
type='account'
|
||||
/>
|
||||
</Modal.Columns>
|
||||
|
||||
@ -27,7 +27,7 @@ interface Turnout {
|
||||
voted: BN;
|
||||
}
|
||||
|
||||
const optMulti = {
|
||||
const OPT_MULTI = {
|
||||
defaultValue: [undefined, undefined] as MultiResult
|
||||
};
|
||||
|
||||
@ -38,7 +38,7 @@ function PollApp ({ basePath, className }: Props): React.ReactElement<Props> {
|
||||
const [totalIssuance, totals] = useCallMulti<MultiResult>([
|
||||
api.query.balances?.totalIssuance,
|
||||
api.query.poll.totals
|
||||
], optMulti);
|
||||
], OPT_MULTI);
|
||||
const [accountId, setAccountId] = useState<string | null>(null);
|
||||
const [turnout, setTurnout] = useState<Turnout | null>(null);
|
||||
const [opt10m, setOpt10m] = useState(false);
|
||||
|
||||
Loading…
Reference in New Issue
Block a user