From a65aa97b5f50ec7d279b21f5485db4232ac9e992 Mon Sep 17 00:00:00 2001 From: Jaco Greeff Date: Sun, 1 Mar 2020 13:29:58 +0100 Subject: [PATCH] Support multiple explorers (#2316) * Support multiple explorers * LinkExplorer -> LinkExternal * Add externals to council, treasury & democracy --- packages/app-accounts/src/Account.tsx | 4 +- packages/app-address-book/src/Address.tsx | 4 +- packages/app-council/src/Motions/Motion.tsx | 6 +- .../src/Overview/DispatchEntry.tsx | 5 + .../app-democracy/src/Overview/Proposal.tsx | 6 +- .../app-democracy/src/Overview/Referendum.tsx | 6 +- packages/app-explorer/src/BlockHeader.tsx | 4 +- .../app-explorer/src/BlockInfo/Extrinsics.tsx | 4 +- .../app-treasury/src/Overview/Proposal.tsx | 8 +- .../react-components/src/LinkExternal.tsx | 152 ++++++++++++++++++ .../react-components/src/LinkPolkascan.tsx | 65 -------- packages/react-components/src/index.tsx | 2 +- 12 files changed, 187 insertions(+), 79 deletions(-) create mode 100644 packages/react-components/src/LinkExternal.tsx delete mode 100644 packages/react-components/src/LinkPolkascan.tsx diff --git a/packages/app-accounts/src/Account.tsx b/packages/app-accounts/src/Account.tsx index d62be808c9..3c66b2ce56 100644 --- a/packages/app-accounts/src/Account.tsx +++ b/packages/app-accounts/src/Account.tsx @@ -9,7 +9,7 @@ import { RecoveryConfig } from '@polkadot/types/interfaces'; import React, { useState, useEffect } from 'react'; import { Label } from 'semantic-ui-react'; import styled from 'styled-components'; -import { AddressInfo, AddressSmall, Badge, Button, ChainLock, Forget, Icon, IdentityIcon, InputTags, LinkPolkascan, Menu, Popup, Input } from '@polkadot/react-components'; +import { AddressInfo, AddressSmall, Badge, Button, ChainLock, Forget, Icon, IdentityIcon, InputTags, LinkExternal, Menu, Popup, Input } from '@polkadot/react-components'; import { useApi, useCall, useToggle } from '@polkadot/react-hooks'; import { Option } from '@polkadot/types'; import keyring from '@polkadot/ui-keyring'; @@ -414,7 +414,7 @@ function Account ({ address, className, filter, isFavorite, toggleFavorite }: Pr - - + ); diff --git a/packages/app-democracy/src/Overview/DispatchEntry.tsx b/packages/app-democracy/src/Overview/DispatchEntry.tsx index c3cb419be0..a894a28995 100644 --- a/packages/app-democracy/src/Overview/DispatchEntry.tsx +++ b/packages/app-democracy/src/Overview/DispatchEntry.tsx @@ -6,6 +6,7 @@ import { AccountId, Balance, BlockNumber, Hash, Proposal, ReferendumIndex } from import { ITuple } from '@polkadot/types/types'; import React, { useEffect, useState } from 'react'; +import { LinkExternal } from '@polkadot/react-components'; import { useApi, useCall } from '@polkadot/react-hooks'; import { Bytes, Option } from '@polkadot/types'; import { formatNumber } from '@polkadot/util'; @@ -54,6 +55,10 @@ export default function DispatchEntry ({ blockNumber, hash, referendumIndex }: P isImminent proposal={proposal} /> + ); diff --git a/packages/app-democracy/src/Overview/Proposal.tsx b/packages/app-democracy/src/Overview/Proposal.tsx index c778be8138..4755391095 100644 --- a/packages/app-democracy/src/Overview/Proposal.tsx +++ b/packages/app-democracy/src/Overview/Proposal.tsx @@ -6,7 +6,7 @@ import { DeriveProposal } from '@polkadot/api-derive/types'; import React from 'react'; import styled from 'styled-components'; -import { AddressMini, AddressSmall, Button } from '@polkadot/react-components'; +import { AddressMini, AddressSmall, Button, LinkExternal } from '@polkadot/react-components'; import { FormatBalance } from '@polkadot/react-query'; import { formatNumber } from '@polkadot/util'; @@ -68,6 +68,10 @@ function Proposal ({ className, value: { balance, hash, index, proposal, propose proposal={proposal} /> + ); diff --git a/packages/app-democracy/src/Overview/Referendum.tsx b/packages/app-democracy/src/Overview/Referendum.tsx index 5ef61df7a5..7178c74044 100644 --- a/packages/app-democracy/src/Overview/Referendum.tsx +++ b/packages/app-democracy/src/Overview/Referendum.tsx @@ -9,7 +9,7 @@ import { BlockNumber } from '@polkadot/types/interfaces'; import BN from 'bn.js'; import React, { useEffect, useState } from 'react'; import styled from 'styled-components'; -import { Button } from '@polkadot/react-components'; +import { Button, LinkExternal } from '@polkadot/react-components'; import { useApi, useCall } from '@polkadot/react-hooks'; import { FormatBalance } from '@polkadot/react-query'; import { formatNumber } from '@polkadot/util'; @@ -126,6 +126,10 @@ function Referendum ({ className, idNumber, value }: Props): React.ReactElement< proposal={value.proposal} /> + ); diff --git a/packages/app-explorer/src/BlockHeader.tsx b/packages/app-explorer/src/BlockHeader.tsx index 4695877d59..cbc3e56943 100644 --- a/packages/app-explorer/src/BlockHeader.tsx +++ b/packages/app-explorer/src/BlockHeader.tsx @@ -8,7 +8,7 @@ import React from 'react'; import { Link } from 'react-router-dom'; import styled from 'styled-components'; import { HeaderExtended } from '@polkadot/api-derive'; -import { AddressMini, LinkPolkascan } from '@polkadot/react-components'; +import { AddressMini, LinkExternal } from '@polkadot/react-components'; import { formatNumber } from '@polkadot/util'; interface Props extends BareProps { @@ -75,7 +75,7 @@ function BlockHeader ({ className, isSummary, value, withExplorer, withLink }: P } { withExplorer - ? + ? : undefined } diff --git a/packages/app-explorer/src/BlockInfo/Extrinsics.tsx b/packages/app-explorer/src/BlockInfo/Extrinsics.tsx index c60e89ae8a..1456d34414 100644 --- a/packages/app-explorer/src/BlockInfo/Extrinsics.tsx +++ b/packages/app-explorer/src/BlockInfo/Extrinsics.tsx @@ -8,7 +8,7 @@ import { I18nProps } from '@polkadot/react-components/types'; import React from 'react'; import styled from 'styled-components'; import { registry } from '@polkadot/react-api'; -import { AddressMini, Call, Column, LinkPolkascan } from '@polkadot/react-components'; +import { AddressMini, Call, Column, LinkExternal } from '@polkadot/react-components'; import { formatNumber } from '@polkadot/util'; import translate from '../translate'; @@ -73,7 +73,7 @@ function renderExtrinsic (props: Props, extrinsic: Extrinsic, index: number): Re { extrinsic.isSigned - ? + ? : null } diff --git a/packages/app-treasury/src/Overview/Proposal.tsx b/packages/app-treasury/src/Overview/Proposal.tsx index fca9ecca68..05df9b8817 100644 --- a/packages/app-treasury/src/Overview/Proposal.tsx +++ b/packages/app-treasury/src/Overview/Proposal.tsx @@ -5,7 +5,7 @@ import { DerivedTreasuryProposal } from '@polkadot/api-derive/types'; import React from 'react'; -import { AddressMini, AddressSmall } from '@polkadot/react-components'; +import { AddressMini, AddressSmall, LinkExternal } from '@polkadot/react-components'; import { FormatBalance } from '@polkadot/react-query'; import { formatNumber } from '@polkadot/util'; @@ -29,7 +29,7 @@ export default function ProposalDisplay ({ className, isMember, proposal: { coun

{formatNumber(id)}

- + @@ -64,6 +64,10 @@ export default function ProposalDisplay ({ className, isMember, proposal: { coun /> )} + ); diff --git a/packages/react-components/src/LinkExternal.tsx b/packages/react-components/src/LinkExternal.tsx new file mode 100644 index 0000000000..704d05caec --- /dev/null +++ b/packages/react-components/src/LinkExternal.tsx @@ -0,0 +1,152 @@ +// Copyright 2017-2020 @polkadot/react-components authors & contributors +// This software may be modified and distributed under the terms +// of the Apache-2.0 license. See the LICENSE file for details. + +import BN from 'bn.js'; +import React, { useMemo } from 'react'; +import styled from 'styled-components'; +import { useApi } from '@polkadot/react-hooks'; + +import { useTranslation } from './translate'; +import Icon from './Icon'; +import Tooltip from './Tooltip'; + +export type LinkTypes = 'address' | 'block' | 'council' | 'extrinsic' | 'proposal' | 'referendum' | 'treasury'; + +interface Props { + className?: string; + data: BN | number | string; + type: LinkTypes; + withShort?: boolean; +} + +interface Explorer { + isActive: boolean; + chains: Record; + paths: Partial>; + create: (chain: string, path: string, data: BN | number | string) => string; +} + +const EXPLORERS: Record = { + Commonwealth: { + isActive: true, + chains: { + Edgeware: 'edgeware', + Kusama: 'kusama', + 'Kusama CC3': 'kusama' + }, + paths: { + council: 'proposal/councilmotion', + proposal: 'proposal/democracyproposal', + referendum: 'proposal/referendum', + treasury: 'proposal/treasuryproposal' + }, + create: (chain: string, path: string, data: BN | number | string): string => + `https://commonwealth.im/${chain}/${path}/${data.toString()}` + }, + Polkascan: { + isActive: true, + chains: { + Edgeware: 'edgeware', + Kulupu: 'kulupu', + Kusama: 'kusama', + 'Kusama CC3': 'kusama', + Westend: 'westend' + }, + paths: { + address: 'module/account', + block: 'system/block', + council: 'council/motion', + extrinsic: 'system/extrinsic', + proposal: 'democracy/proposal', + referendum: 'democracy/referendum', + treasury: 'treasury/proposal' + }, + create: (chain: string, path: string, data: BN | number | string): string => + `https://polkascan.io/pre/${chain}/${path}/${data.toString()}` + }, + Subscan: { + isActive: false, + chains: { + Kusama: 'kusama', + 'Kusama CC3': 'kusama' + }, + paths: { + address: 'account', + block: 'block', + extrinsic: 'extrinsic' + }, + create: (chain: string, path: string, data: BN | number | string): string => + `https://${chain}.subscan.io/${path}/${data.toString()}` + } +}; + +function genLinks (systemChain: string, { data, type, withShort }: Props): React.ReactNode[] { + return Object + .entries(EXPLORERS) + .map(([name, { isActive, chains, paths, create }]): React.ReactNode | null => { + const extChain = chains[systemChain]; + const extPath = paths[type]; + + if (!isActive || !extChain || !extPath) { + return null; + } + + const trigger = `${name}-${type}-${data}`; + const link = create(extChain, extPath, data); + + return ( + + {withShort + ? + : name + } + + + ); + }) + .filter((node): node is React.ReactNode => !!node); +} + +function LinkExternal ({ className, data, type, withShort }: Props): React.ReactElement | null { + const { t } = useTranslation(); + const { systemChain } = useApi(); + const links = useMemo((): React.ReactNode[] => { + return genLinks(systemChain, { data, type, withShort }); + }, [systemChain, data, type, withShort]); + + if (!links.length) { + return null; + } + + return ( +
+ {!withShort &&
{t('View this externally')}
}
{links.map((link, index) => {link})}
+
+ ); +} + +export default styled(LinkExternal)` + margin-top: 0.75rem; + text-align: right; + + > div { + display: block; + whitespace: nowrap; + + > span+span { + margin-left: 0.3rem; + } + } +`; diff --git a/packages/react-components/src/LinkPolkascan.tsx b/packages/react-components/src/LinkPolkascan.tsx deleted file mode 100644 index 573cf81717..0000000000 --- a/packages/react-components/src/LinkPolkascan.tsx +++ /dev/null @@ -1,65 +0,0 @@ -// Copyright 2017-2020 @polkadot/react-components authors & contributors -// This software may be modified and distributed under the terms -// of the Apache-2.0 license. See the LICENSE file for details. - -import React from 'react'; -import styled from 'styled-components'; -import { useApi } from '@polkadot/react-hooks'; - -import { useTranslation } from './translate'; -import Icon from './Icon'; - -export type LinkTypes = 'address' | 'block' | 'extrinsic'; - -interface Props { - className?: string; - data: string; - type: LinkTypes; - withShort?: boolean; -} - -const BASE = 'https://polkascan.io/pre/'; - -const CHAINS: Record = { - Kulupu: 'kulupu', - Kusama: 'kusama', - 'Kusama CC3': 'kusama', - Westend: 'westend' -}; - -const TYPES: Record = { - address: '/module/account/', - block: '/system/block/', - extrinsic: '/system/extrinsic/' -}; - -function LinkPolkascan ({ className, data, type, withShort }: Props): React.ReactElement | null { - const { t } = useTranslation(); - const { systemChain } = useApi(); - const extChain = CHAINS[systemChain]; - const extType = TYPES[type]; - - if (!extChain || !extType) { - return null; - } - - return ( - - ); -} - -export default styled(LinkPolkascan)` - margin-top: 1rem; - text-align: right; -`; diff --git a/packages/react-components/src/index.tsx b/packages/react-components/src/index.tsx index fe22bcb89d..ee0d45fe0b 100644 --- a/packages/react-components/src/index.tsx +++ b/packages/react-components/src/index.tsx @@ -60,7 +60,7 @@ export { default as Inset } from './Inset'; export { default as Label } from './Label'; export { default as LabelHelp } from './LabelHelp'; export { default as Labelled } from './Labelled'; -export { default as LinkPolkascan } from './LinkPolkascan'; +export { default as LinkExternal } from './LinkExternal'; export { default as LockedVote } from './LockedVote'; export { default as Menu } from './Menu'; export { default as Messages } from './Messages';