From 6079e305791c3c9f8a45d0397a5a1c9d87c0a588 Mon Sep 17 00:00:00 2001 From: Jaco Greeff Date: Sun, 23 Aug 2020 15:22:31 +0200 Subject: [PATCH] Split tips into an own tab (#3487) --- packages/page-treasury/src/Overview/index.tsx | 39 +++------- .../src/{Overview => Tips}/Tip.tsx | 74 ++++++++++--------- .../src/{Overview => Tips}/TipClose.tsx | 0 .../src/{Overview => Tips}/TipCreate.tsx | 0 .../src/{Overview => Tips}/TipEndorse.tsx | 0 .../src/{Overview => Tips}/TipReason.tsx | 0 .../src/{Overview => Tips}/Tips.tsx | 42 ++++++----- packages/page-treasury/src/Tips/index.tsx | 37 ++++++++++ packages/page-treasury/src/index.tsx | 45 +++++++++-- 9 files changed, 149 insertions(+), 88 deletions(-) rename packages/page-treasury/src/{Overview => Tips}/Tip.tsx (75%) rename packages/page-treasury/src/{Overview => Tips}/TipClose.tsx (100%) rename packages/page-treasury/src/{Overview => Tips}/TipCreate.tsx (100%) rename packages/page-treasury/src/{Overview => Tips}/TipEndorse.tsx (100%) rename packages/page-treasury/src/{Overview => Tips}/TipReason.tsx (100%) rename packages/page-treasury/src/{Overview => Tips}/Tips.tsx (71%) create mode 100644 packages/page-treasury/src/Tips/index.tsx diff --git a/packages/page-treasury/src/Overview/index.tsx b/packages/page-treasury/src/Overview/index.tsx index 831e4b5ff1..0279a13616 100644 --- a/packages/page-treasury/src/Overview/index.tsx +++ b/packages/page-treasury/src/Overview/index.tsx @@ -3,36 +3,24 @@ // of the Apache-2.0 license. See the LICENSE file for details. import { DeriveTreasuryProposals } from '@polkadot/api-derive/types'; -import { BareProps as Props } from '@polkadot/react-components/types'; -import React, { useEffect, useState } from 'react'; +import React from 'react'; import { Button } from '@polkadot/react-components'; -import { useApi, useCall, useIncrement, useMembers, useIsMountedRef } from '@polkadot/react-hooks'; +import { useApi, useCall } from '@polkadot/react-hooks'; import ProposalCreate from './ProposalCreate'; import Proposals from './Proposals'; import Summary from './Summary'; -import TipCreate from './TipCreate'; -import Tips from './Tips'; -function Overview ({ className }: Props): React.ReactElement { +interface Props { + className?: string; + isMember: boolean; + members: string[]; +} + +function Overview ({ className, isMember, members }: Props): React.ReactElement { const { api } = useApi(); const info = useCall(api.derive.treasury.proposals); - const { isMember, members } = useMembers(); - - const mountedRef = useIsMountedRef(); - const [tipHashTrigger, triggerTipHashes] = useIncrement(); - const [tipHashes, setTipHashes] = useState(null); - - useEffect((): void => { - if (tipHashTrigger && mountedRef.current) { - api.query.treasury.tips.keys().then((keys) => - mountedRef.current && setTipHashes( - keys.map((key) => key.args[0].toHex()) - ) - ).catch(console.error); - } - }, [api, tipHashTrigger, mountedRef]); return (
@@ -41,10 +29,6 @@ function Overview ({ className }: Props): React.ReactElement { proposalCount={info?.proposals.length} /> - { members={members} proposals={info?.approvals} /> -
); } diff --git a/packages/page-treasury/src/Overview/Tip.tsx b/packages/page-treasury/src/Tips/Tip.tsx similarity index 75% rename from packages/page-treasury/src/Overview/Tip.tsx rename to packages/page-treasury/src/Tips/Tip.tsx index c9e1d79f90..1c017cc2e6 100644 --- a/packages/page-treasury/src/Overview/Tip.tsx +++ b/packages/page-treasury/src/Tips/Tip.tsx @@ -5,7 +5,7 @@ import { AccountId, Balance, BlockNumber, OpenTip, OpenTipTo225 } from '@polkadot/types/interfaces'; import BN from 'bn.js'; -import React, { useEffect, useState } from 'react'; +import React, { useMemo } from 'react'; import styled from 'styled-components'; import { AddressSmall, AddressMini, Expander, Icon, LinkExternal, TxButton } from '@polkadot/react-components'; import { useAccounts } from '@polkadot/react-hooks'; @@ -40,44 +40,48 @@ function isCurrentTip (tip: OpenTip | OpenTipTo225): tip is OpenTip { return !!(tip as OpenTip)?.findersFee; } +function extractTipState (tip: OpenTip | OpenTipTo225, hash: string, allAccounts: string[]): TipState { + const closesAt = tip.closes.unwrapOr(null); + let finder: AccountId | null = null; + let deposit: Balance | null = null; + + if (isCurrentTip(tip)) { + finder = tip.finder; + deposit = tip.deposit; + } else if (tip.finder.isSome) { + const finderInfo = tip.finder.unwrap(); + + finder = finderInfo[0]; + deposit = finderInfo[1]; + } + + const values = tip.tips.map(([, value]) => value).sort((a, b) => a.cmp(b)); + const midIndex = Math.floor(values.length / 2); + const median = values.length + ? values.length % 2 + ? values[midIndex] + : values[midIndex - 1].add(values[midIndex]).divn(2) + : BN_ZERO; + + return { + closesAt, + deposit, + finder, + isFinder: !!finder && allAccounts.includes(finder.toString()), + isTipped: !!values.length, + isTipper: tip.tips.some(([address]) => allAccounts.includes(address.toString())), + median + }; +} + function Tip ({ bestNumber, className = '', hash, isMember, members, tip }: Props): React.ReactElement | null { const { t } = useTranslation(); const { allAccounts } = useAccounts(); - const [{ closesAt, finder, isFinder, isTipped, isTipper, median }, setTipState] = useState({ closesAt: null, deposit: null, finder: null, isFinder: false, isTipped: false, isTipper: false, median: BN_ZERO }); - useEffect((): void => { - const closesAt = tip.closes.unwrapOr(null); - let finder: AccountId | null = null; - let deposit: Balance | null = null; - - if (isCurrentTip(tip)) { - finder = tip.finder; - deposit = tip.deposit; - } else if (tip.finder.isSome) { - const finderInfo = tip.finder.unwrap(); - - finder = finderInfo[0]; - deposit = finderInfo[1]; - } - - const values = tip.tips.map(([, value]) => value).sort((a, b) => a.cmp(b)); - const midIndex = Math.floor(values.length / 2); - const median = values.length - ? values.length % 2 - ? values[midIndex] - : values[midIndex - 1].add(values[midIndex]).divn(2) - : BN_ZERO; - - setTipState({ - closesAt, - deposit, - finder, - isFinder: !!finder && allAccounts.includes(finder.toString()), - isTipped: !!values.length, - isTipper: tip.tips.some(([address]) => allAccounts.includes(address.toString())), - median - }); - }, [allAccounts, hash, tip]); + const { closesAt, finder, isFinder, isTipped, isTipper, median } = useMemo( + () => extractTipState(tip, hash, allAccounts), + [allAccounts, hash, tip] + ); const { reason, tips, who } = tip; diff --git a/packages/page-treasury/src/Overview/TipClose.tsx b/packages/page-treasury/src/Tips/TipClose.tsx similarity index 100% rename from packages/page-treasury/src/Overview/TipClose.tsx rename to packages/page-treasury/src/Tips/TipClose.tsx diff --git a/packages/page-treasury/src/Overview/TipCreate.tsx b/packages/page-treasury/src/Tips/TipCreate.tsx similarity index 100% rename from packages/page-treasury/src/Overview/TipCreate.tsx rename to packages/page-treasury/src/Tips/TipCreate.tsx diff --git a/packages/page-treasury/src/Overview/TipEndorse.tsx b/packages/page-treasury/src/Tips/TipEndorse.tsx similarity index 100% rename from packages/page-treasury/src/Overview/TipEndorse.tsx rename to packages/page-treasury/src/Tips/TipEndorse.tsx diff --git a/packages/page-treasury/src/Overview/TipReason.tsx b/packages/page-treasury/src/Tips/TipReason.tsx similarity index 100% rename from packages/page-treasury/src/Overview/TipReason.tsx rename to packages/page-treasury/src/Tips/TipReason.tsx diff --git a/packages/page-treasury/src/Overview/Tips.tsx b/packages/page-treasury/src/Tips/Tips.tsx similarity index 71% rename from packages/page-treasury/src/Overview/Tips.tsx rename to packages/page-treasury/src/Tips/Tips.tsx index 5246558879..39d1608891 100644 --- a/packages/page-treasury/src/Overview/Tips.tsx +++ b/packages/page-treasury/src/Tips/Tips.tsx @@ -4,7 +4,7 @@ import { BlockNumber, OpenTip, OpenTipTo225 } from '@polkadot/types/interfaces'; -import React, { useEffect, useRef, useState } from 'react'; +import React, { useMemo, useRef } from 'react'; import { Table } from '@polkadot/react-components'; import { useApi, useCall } from '@polkadot/react-hooks'; import { Option } from '@polkadot/types'; @@ -21,29 +21,35 @@ interface Props { type Tip = [string, OpenTip | OpenTipTo225]; +function extractTips (optTips?: Option[], hashes?: string[] | null): Tip[] | undefined { + if (!hashes || !optTips) { + return undefined; + } + + return optTips + .map((opt, index): [string, OpenTip | null] => [hashes[index], opt.unwrapOr(null)]) + .filter((val): val is [string, OpenTip] => !!val[1]) + .sort((a, b) => + a[1].closes.isNone + ? b[1].closes.isNone + ? 0 + : -1 + : b[1].closes.isSome + ? b[1].closes.unwrap().cmp(a[1].closes.unwrap()) + : 1 + ); +} + function Tips ({ className = '', hashes, isMember, members }: Props): React.ReactElement { const { t } = useTranslation(); const { api } = useApi(); const bestNumber = useCall(api.derive.chain.bestNumber); - const [tips, setTips] = useState(); const optTips = useCall[]>(hashes && api.query.treasury.tips.multi, [hashes]); - useEffect((): void => { - hashes && optTips && setTips( - optTips - .map((opt, index): [string, OpenTip | null] => [hashes[index], opt.unwrapOr(null)]) - .filter((val): val is [string, OpenTip] => !!val[1]) - .sort((a, b) => - a[1].closes.isNone - ? b[1].closes.isNone - ? 0 - : -1 - : b[1].closes.isSome - ? b[1].closes.unwrap().cmp(a[1].closes.unwrap()) - : 1 - ) - ); - }, [hashes, optTips]); + const tips = useMemo( + () => extractTips(optTips, hashes), + [hashes, optTips] + ); const headerRef = useRef([ [t('tips'), 'start'], diff --git a/packages/page-treasury/src/Tips/index.tsx b/packages/page-treasury/src/Tips/index.tsx new file mode 100644 index 0000000000..49be29139f --- /dev/null +++ b/packages/page-treasury/src/Tips/index.tsx @@ -0,0 +1,37 @@ +// Copyright 2017-2020 @polkadot/app-treasury 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 { Button } from '@polkadot/react-components'; + +import TipCreate from './TipCreate'; +import Tips from './Tips'; + +interface Props { + className?: string; + hashes?: string[] | null; + isMember: boolean; + members: string[]; + trigger: () => void; +} + +function TipsEntry ({ className, hashes, isMember, members, trigger }: Props): React.ReactElement { + return ( +
+ + + + +
+ ); +} + +export default React.memo(TipsEntry); diff --git a/packages/page-treasury/src/index.tsx b/packages/page-treasury/src/index.tsx index b377bb651e..8fc94a9737 100644 --- a/packages/page-treasury/src/index.tsx +++ b/packages/page-treasury/src/index.tsx @@ -2,12 +2,14 @@ // This software may be modified and distributed under the terms // of the Apache-2.0 license. See the LICENSE file for details. -import React, { useRef } from 'react'; +import React, { useEffect, useMemo, useState } from 'react'; import { Route, Switch } from 'react-router'; import { HelpOverlay, Tabs } from '@polkadot/react-components'; +import { useApi, useIncrement, useIsMountedRef, useMembers } from '@polkadot/react-hooks'; import basicMd from './md/basic.md'; import Overview from './Overview'; +import Tips from './Tips'; import { useTranslation } from './translate'; @@ -19,14 +21,34 @@ interface Props { function TreasuryApp ({ basePath }: Props): React.ReactElement { const { t } = useTranslation(); + const { api } = useApi(); + const mountedRef = useIsMountedRef(); + const [tipHashTrigger, triggerTipHashes] = useIncrement(); + const { isMember, members } = useMembers(); + const [tipHashes, setTipHashes] = useState(null); - const itemsRef = useRef([ + useEffect((): void => { + if (tipHashTrigger && mountedRef.current) { + api.query.treasury.tips.keys().then((keys) => + mountedRef.current && setTipHashes( + keys.map((key) => key.args[0].toHex()) + ) + ).catch(console.error); + } + }, [api, tipHashTrigger, mountedRef]); + + const items = useMemo(() => [ { isRoot: true, name: 'overview', text: t('Treasury overview') + }, + { + count: tipHashes?.length, + name: 'tips', + text: t('Tips') } - ]); + ], [t, tipHashes]); return (
@@ -34,11 +56,24 @@ function TreasuryApp ({ basePath }: Props): React.ReactElement {
- + + + + + +
);