Alliance announcements (#7922)

* Alliance announcements

* Add key field

* jest esm config

* type rename

* useRule & move useMembers to top-level
This commit is contained in:
Jaco 2022-07-28 08:02:43 +02:00 committed by GitHub
parent 583c81642a
commit 3e9716b93b
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
13 changed files with 217 additions and 29 deletions

View File

@ -27,5 +27,5 @@ module.exports = {
testTimeout: 90000,
// NOTE: While we can remove @polkadot/etc. react-markdown still has issues with Jest 28
// ... TL;DR We still end up with a massive list here
transformIgnorePatterns: ['/node_modules/(?!react-markdown|vfile|vfile-|unified|unist-|bail|is-plain-obj|trough|remark-parse|mdast-|micromark|decode-named-character-reference|character-entities|remark-rehype|property-information|hast-util-|hast-to-|space-separated-tokens|comma-separated-tokens|rehype-raw|hastscript|web-namespaces|zwitch|html-void-elements|multiformats|uint8arrays)']
transformIgnorePatterns: ['/node_modules/(?!react-markdown|vfile|vfile-|unified|unist-|bail|is-plain-obj|trough|remark-parse|mdast-|micromark|decode-named-character-reference|character-entities|remark-rehype|property-information|hast-util-|hast-to-|space-separated-tokens|comma-separated-tokens|rehype-raw|hastscript|web-namespaces|zwitch|html-void-elements|multiformats|is-ipfs|uint8arrays)']
};

View File

@ -13,6 +13,7 @@
"type": "module",
"version": "0.118.2-17-x",
"dependencies": {
"@babel/runtime": "^7.18.9"
"@babel/runtime": "^7.18.9",
"multiformats": "^9.7.1"
}
}

View File

@ -0,0 +1,30 @@
// Copyright 2017-2022 @polkadot/app-alliance authors & contributors
// SPDX-License-Identifier: Apache-2.0
import type { Cid } from '../types';
import React from 'react';
interface Props {
className?: string;
value: Cid;
}
function Website ({ className, value: { cid: { codec, version }, ipfs } }: Props): React.ReactElement<Props> {
// TODO IPFS link
return (
<tr className={className}>
<td className='start all'>
{ipfs}
</td>
<td className='number'>
{version.type}
</td>
<td className='number'>
{codec.toString()}
</td>
</tr>
);
}
export default React.memo(Website);

View File

@ -0,0 +1,44 @@
// Copyright 2017-2022 @polkadot/app-alliance authors & contributors
// SPDX-License-Identifier: Apache-2.0
import type { Cid } from '../types';
import React, { useRef } from 'react';
import { Table } from '@polkadot/react-components';
import { useTranslation } from '../translate';
import Accouncement from './Accouncement';
interface Props {
accouncements?: Cid[];
className?: string;
}
function Announcements ({ accouncements, className }: Props): React.ReactElement<Props> {
const { t } = useTranslation();
const annRef = useRef([
[t<string>('annoucements'), 'start'],
[t<string>('version'), 'number'],
[t<string>('codec'), 'number']
]);
return (
<div className={className}>
<Table
empty={accouncements && t<string>('No annoucements')}
header={annRef.current}
>
{accouncements && accouncements.map((a) => (
<Accouncement
key={a.key}
value={a}
/>
))}
</Table>
</div>
);
}
export default React.memo(Announcements);

View File

@ -1,7 +1,7 @@
// Copyright 2017-2022 @polkadot/app-alliance authors & contributors
// SPDX-License-Identifier: Apache-2.0
import type { Unscrupelous } from '../types';
import type { Member as MemberType, Unscrupelous } from '../types';
import React from 'react';
@ -9,20 +9,19 @@ import { Button } from '@polkadot/react-components';
import { useToggle } from '@polkadot/react-hooks';
import { useTranslation } from '../translate';
import useMembers from '../useMembers';
import Join from './Join';
import Members from './Members';
import Summary from './Summary';
interface Props {
className?: string;
members?: MemberType[];
unscrupelous?: Unscrupelous;
}
function Overview ({ className, unscrupelous }: Props): React.ReactElement<Props> {
function Overview ({ className, members, unscrupelous }: Props): React.ReactElement<Props> {
const { t } = useTranslation();
const [isJoinOpen, toggleJoin] = useToggle();
const members = useMembers();
return (
<div className={className}>

View File

@ -6,9 +6,12 @@ import { Route, Switch } from 'react-router';
import { Tabs } from '@polkadot/react-components';
import Announcements from './Announcements';
import Overview from './Overview';
import { useTranslation } from './translate';
import Unscrupelous from './Unscrupelous';
import useAnnoucements from './useAnnoucements';
import useMembers from './useMembers';
import useUnscrupelous from './useUnscrupelous';
interface Props {
@ -18,6 +21,8 @@ interface Props {
function AllianceApp ({ basePath, className }: Props): React.ReactElement<Props> {
const { t } = useTranslation();
const accouncements = useAnnoucements();
const members = useMembers();
const unscrupelous = useUnscrupelous();
const itemsRef = useRef([
@ -26,6 +31,10 @@ function AllianceApp ({ basePath, className }: Props): React.ReactElement<Props>
name: 'overview',
text: t<string>('Overview')
},
{
name: 'announcements',
text: t<string>('Announcements')
},
{
name: 'unscrupelous',
text: t<string>('Unscrupelous')
@ -39,11 +48,17 @@ function AllianceApp ({ basePath, className }: Props): React.ReactElement<Props>
items={itemsRef.current}
/>
<Switch>
<Route path={`${basePath}/announcements`}>
<Announcements accouncements={accouncements} />
</Route>
<Route path={`${basePath}/unscrupelous`}>
<Unscrupelous unscrupelous={unscrupelous} />
</Route>
<Route>
<Overview unscrupelous={unscrupelous} />
<Overview
members={members}
unscrupelous={unscrupelous}
/>
</Route>
</Switch>
</main>

View File

@ -1,7 +1,7 @@
// Copyright 2017-2022 @polkadot/app-alliance authors & contributors
// SPDX-License-Identifier: Apache-2.0
import type { PalletAllianceMemberRole } from '@polkadot/types/lookup';
import type { PalletAllianceCid, PalletAllianceMemberRole } from '@polkadot/types/lookup';
import type { BN } from '@polkadot/util';
export interface Member {
@ -19,3 +19,9 @@ export interface Unscrupelous {
accounts: string[];
websites: string[];
}
export interface Cid {
cid: PalletAllianceCid;
ipfs: string;
key: string;
}

View File

@ -0,0 +1,22 @@
// Copyright 2017-2022 @polkadot/app-alliance authors & contributors
// SPDX-License-Identifier: Apache-2.0
import type { PalletAllianceCid } from '@polkadot/types/lookup';
import type { Cid } from './types';
import { createNamedHook, useApi, useCall } from '@polkadot/react-hooks';
import { createCid } from './util';
const OPT_ANN = {
transform: (cids: PalletAllianceCid[]): Cid[] =>
cids.map(createCid)
};
function useAnnouncementsImpl (): Cid[] | undefined {
const { api } = useApi();
return useCall<Cid[]>(api.query.alliance.announcements, [], OPT_ANN);
}
export default createNamedHook('useAnnouncements', useAnnouncementsImpl);

View File

@ -8,6 +8,14 @@ import { useMemo } from 'react';
import { createNamedHook, useApi, useCall } from '@polkadot/react-hooks';
function transform (accountId: string, depositOf: Option<UInt>, upForKicking: bool): MemberInfo {
return {
accountId,
deposit: depositOf.unwrapOr(null),
isUpForKicking: upForKicking.isTrue
};
}
function useMemberInfoImpl (accountId: string): MemberInfo | undefined {
const { api } = useApi();
const upForKicking = useCall<bool>(api.query.alliance.upForKicking, [accountId]);
@ -16,11 +24,7 @@ function useMemberInfoImpl (accountId: string): MemberInfo | undefined {
return useMemo(
(): MemberInfo | undefined =>
upForKicking && depositOf
? {
accountId,
deposit: depositOf.unwrapOr(null),
isUpForKicking: upForKicking.isTrue
}
? transform(accountId, depositOf, upForKicking)
: undefined,
[accountId, upForKicking, depositOf]
);

View File

@ -0,0 +1,25 @@
// Copyright 2017-2022 @polkadot/app-alliance authors & contributors
// SPDX-License-Identifier: Apache-2.0
import type { Option } from '@polkadot/types';
import type { PalletAllianceCid } from '@polkadot/types/lookup';
import type { Cid } from './types';
import { createNamedHook, useApi, useCall } from '@polkadot/react-hooks';
import { createCid } from './util';
const OPT_RULE = {
transform: (opt: Option<PalletAllianceCid>): Cid | null =>
opt.isSome
? createCid(opt.unwrap())
: null
};
function useRuleImpl (): Cid | null | undefined {
const { api } = useApi();
return useCall<Cid | null>(api.query.alliance.rule, [], OPT_RULE);
}
export default createNamedHook('useRule', useRuleImpl);

View File

@ -5,28 +5,26 @@ import type { Bytes } from '@polkadot/types';
import type { AccountId32 } from '@polkadot/types/interfaces';
import type { Unscrupelous } from './types';
import { useMemo } from 'react';
import { createNamedHook, useApi, useCallMulti } from '@polkadot/react-hooks';
import { createNamedHook, useApi, useCall } from '@polkadot/react-hooks';
function getUnscrupelous (accounts: AccountId32[], websites: Bytes[]): Unscrupelous {
return {
accounts: accounts.map((a) => a.toString()),
websites: websites.filter((b) => b.isAscii).map((b) => b.toString())
};
function mapString (all: { toString: () => string }[]): string[] {
return all.map((a) => a.toString());
}
const OPT_MULTI = {
transform: ([accounts, websites]: [AccountId32[], Bytes[]]): Unscrupelous => ({
accounts: mapString(accounts),
websites: mapString(websites.filter((b) => b.isAscii))
})
};
function useUnscrupelousImpl (): Unscrupelous | undefined {
const { api } = useApi();
const accounts = useCall<AccountId32[]>(api.query.alliance.unscrupulousAccounts, []);
const websites = useCall<Bytes[]>(api.query.alliance.unscrupulousWebsites, []);
return useMemo(
(): Unscrupelous | undefined =>
accounts && websites &&
getUnscrupelous(accounts, websites),
[accounts, websites]
);
return useCallMulti([
api.query.alliance.unscrupulousAccounts,
api.query.alliance.unscrupulousWebsites
], OPT_MULTI);
}
export default createNamedHook('useUnscrupelous', useUnscrupelousImpl);

View File

@ -0,0 +1,36 @@
// Copyright 2017-2022 @polkadot/app-alliance authors & contributors
// SPDX-License-Identifier: Apache-2.0
import type { PalletAllianceCid } from '@polkadot/types/lookup';
import type { Cid } from './types';
import { CID } from 'multiformats/cid';
import { u8aToBn } from '@polkadot/util';
function createIpfs (cid: PalletAllianceCid): string {
try {
const { codec, hash_: { code, digest }, version } = cid;
return CID
.create(version.index as 0, codec.toNumber(), {
bytes: digest.subarray(4),
code: code.toNumber(),
digest: digest.toU8a(true),
size: u8aToBn(digest.subarray(0, 4)).toNumber()
})
.toString();
} catch (error) {
console.error(`createIpfs: ${(error as Error).message}::`, cid.toHuman());
return '';
}
}
export function createCid (cid: PalletAllianceCid): Cid {
return {
cid,
ipfs: createIpfs(cid),
key: cid.toHex()
};
}

View File

@ -2766,6 +2766,7 @@ __metadata:
resolution: "@polkadot/app-alliance@workspace:packages/page-alliance"
dependencies:
"@babel/runtime": ^7.18.9
multiformats: ^9.7.1
languageName: unknown
linkType: soft
@ -14835,6 +14836,13 @@ __metadata:
languageName: node
linkType: hard
"multiformats@npm:^9.7.1":
version: 9.7.1
resolution: "multiformats@npm:9.7.1"
checksum: 9193a4b55573dc88053a357e5f9562696d7cc47669e5ce5d7e147e3db1ec3f793567d7943c7e20271af9acd1e48c1679d0dfe932094333b16ed7f9f2ccf9197f
languageName: node
linkType: hard
"multihashes@npm:^0.4.15, multihashes@npm:~0.4.13, multihashes@npm:~0.4.15":
version: 0.4.21
resolution: "multihashes@npm:0.4.21"