From 8770390a58ff4cf93f5be0b935ea5ddfe2023c11 Mon Sep 17 00:00:00 2001 From: Jaco Date: Sun, 24 Oct 2021 15:15:00 +0200 Subject: [PATCH] Display tip warning on no recipient balance (#6393) --- packages/page-treasury/src/Tips/Tip.tsx | 13 +++++++---- .../page-treasury/src/Tips/TipEndorse.tsx | 23 +++++++++++++++---- packages/page-treasury/src/Tips/Tips.tsx | 13 ++++++----- 3 files changed, 33 insertions(+), 16 deletions(-) diff --git a/packages/page-treasury/src/Tips/Tip.tsx b/packages/page-treasury/src/Tips/Tip.tsx index dbeb84e34e..1139803dd4 100644 --- a/packages/page-treasury/src/Tips/Tip.tsx +++ b/packages/page-treasury/src/Tips/Tip.tsx @@ -2,7 +2,8 @@ // SPDX-License-Identifier: Apache-2.0 import type BN from 'bn.js'; -import type { AccountId, Balance, BlockNumber, OpenTip, OpenTipTo225 } from '@polkadot/types/interfaces'; +import type { AccountId, Balance, BlockNumber, OpenTipTo225 } from '@polkadot/types/interfaces'; +import type { PalletTipsOpenTip } from '@polkadot/types/lookup'; import React, { useEffect, useMemo, useState } from 'react'; import styled from 'styled-components'; @@ -25,7 +26,7 @@ interface Props { members: string[]; onSelect: (hash: string, isSelected: boolean, value: BN) => void; onlyUntipped: boolean; - tip: OpenTip | OpenTipTo225; + tip: PalletTipsOpenTip | OpenTipTo225; } interface TipState { @@ -38,11 +39,11 @@ interface TipState { median: BN; } -function isCurrentTip (tip: OpenTip | OpenTipTo225): tip is OpenTip { - return !!(tip as OpenTip)?.findersFee; +function isCurrentTip (tip: PalletTipsOpenTip | OpenTipTo225): tip is PalletTipsOpenTip { + return !!(tip as PalletTipsOpenTip)?.findersFee; } -function extractTipState (tip: OpenTip | OpenTipTo225, allAccounts: string[]): TipState { +function extractTipState (tip: PalletTipsOpenTip | OpenTipTo225, allAccounts: string[]): TipState { const closesAt = tip.closes.unwrapOr(null); let finder: AccountId | null = null; let deposit: Balance | null = null; @@ -106,6 +107,7 @@ function Tip ({ bestNumber, className = '', defaultId, hash, isMember, members, } const { reason, tips, who } = tip; + const recipient = who.toString(); return ( @@ -166,6 +168,7 @@ function Tip ({ bestNumber, className = '', defaultId, hash, isMember, members, isTipped={isTipped} median={median} members={members} + recipient={recipient} /> ) : ( diff --git a/packages/page-treasury/src/Tips/TipEndorse.tsx b/packages/page-treasury/src/Tips/TipEndorse.tsx index 5645777a54..375426b532 100644 --- a/packages/page-treasury/src/Tips/TipEndorse.tsx +++ b/packages/page-treasury/src/Tips/TipEndorse.tsx @@ -2,11 +2,12 @@ // SPDX-License-Identifier: Apache-2.0 import type BN from 'bn.js'; +import type { DeriveBalancesAll } from '@polkadot/api-derive/types'; import React, { useState } from 'react'; -import { Button, InputAddress, InputBalance, Modal, TxButton } from '@polkadot/react-components'; -import { useApi, useToggle } from '@polkadot/react-hooks'; +import { Button, InputAddress, InputBalance, MarkWarning, Modal, TxButton } from '@polkadot/react-components'; +import { useApi, useCall, useToggle } from '@polkadot/react-hooks'; import { useTranslation } from '../translate'; @@ -17,14 +18,23 @@ interface Props { isTipped: boolean; median: BN; members: string[]; + recipient: string; } -function TipEndorse ({ defaultId, hash, isMember, isTipped, median, members }: Props): React.ReactElement { +const transformBalance = { + transform: ({ freeBalance, reservedBalance }: DeriveBalancesAll): BN => + freeBalance.add(reservedBalance) +}; + +function TipEndorse ({ defaultId, hash, isMember, isTipped, median, members, recipient }: Props): React.ReactElement { const { t } = useTranslation(); const { api } = useApi(); const [isOpen, toggleOpen] = useToggle(); const [accountId, setAccountId] = useState(defaultId); const [value, setValue] = useState(); + const totalBalance = useCall(api.derive.balances?.all, [recipient], transformBalance); + + const tipTx = (api.tx.tips || api.tx.treasury).tip; return ( <> @@ -41,7 +51,7 @@ function TipEndorse ({ defaultId, hash, isMember, isTipped, median, members }: P isDisabled={!isMember || !isTipped} isIcon params={[hash, median]} - tx={(api.tx.tips || api.tx.treasury).tip} + tx={tipTx} withoutLink /> {isOpen && ( @@ -70,6 +80,9 @@ function TipEndorse ({ defaultId, hash, isMember, isTipped, median, members }: P label={t('value')} onChange={setValue} /> + {totalBalance && totalBalance.isZero() && ( + ('The recipient account has no balance, ensure the tip is more than the existential deposit to create the account.')} /> + )} @@ -80,7 +93,7 @@ function TipEndorse ({ defaultId, hash, isMember, isTipped, median, members }: P label={t('Submit tip')} onStart={toggleOpen} params={[hash, value]} - tx={(api.tx.tips || api.tx.treasury).tip} + tx={tipTx} /> diff --git a/packages/page-treasury/src/Tips/Tips.tsx b/packages/page-treasury/src/Tips/Tips.tsx index adb4c1dee1..fb01493df3 100644 --- a/packages/page-treasury/src/Tips/Tips.tsx +++ b/packages/page-treasury/src/Tips/Tips.tsx @@ -3,7 +3,8 @@ import type BN from 'bn.js'; import type { Option } from '@polkadot/types'; -import type { OpenTip, OpenTipTo225 } from '@polkadot/types/interfaces'; +import type { OpenTipTo225 } from '@polkadot/types/interfaces'; +import type { PalletTipsOpenTip } from '@polkadot/types/lookup'; import React, { useMemo, useRef, useState } from 'react'; import styled from 'styled-components'; @@ -23,11 +24,11 @@ interface Props { onSelectTip: (hash: string, isSelected: boolean, value: BN) => void, } -type Tip = [string, OpenTip | OpenTipTo225]; +type Tip = [string, PalletTipsOpenTip | OpenTipTo225]; const TIP_OPTS = { withParams: true }; -function extractTips (tipsWithHashes?: [[string[]], Option[]], inHashes?: string[] | null): Tip[] | undefined { +function extractTips (tipsWithHashes?: [[string[]], Option[]], inHashes?: string[] | null): Tip[] | undefined { if (!tipsWithHashes || !inHashes) { return undefined; } @@ -35,8 +36,8 @@ function extractTips (tipsWithHashes?: [[string[]], Option[]], inHashes const [[hashes], optTips] = tipsWithHashes; return optTips - .map((opt, index): [string, OpenTip | null] => [hashes[index], opt.unwrapOr(null)]) - .filter((val): val is [string, OpenTip] => inHashes.includes(val[0]) && !!val[1]) + .map((opt, index): [string, PalletTipsOpenTip | null] => [hashes[index], opt.unwrapOr(null)]) + .filter((val): val is [string, PalletTipsOpenTip] => inHashes.includes(val[0]) && !!val[1]) .sort((a, b) => a[1].closes.isNone ? b[1].closes.isNone @@ -53,7 +54,7 @@ function Tips ({ className = '', defaultId, hashes, isMember, members, onSelectT const { api } = useApi(); const [onlyUntipped, setOnlyUntipped] = useState(false); const bestNumber = useBestNumber(); - const tipsWithHashes = useCall<[[string[]], Option[]]>(hashes && (api.query.tips || api.query.treasury).tips.multi, [hashes], TIP_OPTS); + const tipsWithHashes = useCall<[[string[]], Option[]]>(hashes && (api.query.tips || api.query.treasury).tips.multi, [hashes], TIP_OPTS); const tips = useMemo( () => extractTips(tipsWithHashes, hashes),