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';