From d5def2407958a7beedb69d6a654dc741f8d1f4be Mon Sep 17 00:00:00 2001 From: Veado Date: Wed, 9 Nov 2022 21:13:49 +0100 Subject: [PATCH 1/5] ActionButton --- .../button/ActionButton.stories.tsx | 40 ++++++++ .../uielements/button/ActionButton.tsx | 94 +++++++++++++++++++ src/renderer/i18n/de/common.ts | 1 + src/renderer/i18n/en/common.ts | 1 + src/renderer/i18n/fr/common.ts | 1 + src/renderer/i18n/ru/common.ts | 1 + src/renderer/i18n/types.ts | 1 + 7 files changed, 139 insertions(+) create mode 100644 src/renderer/components/uielements/button/ActionButton.stories.tsx create mode 100644 src/renderer/components/uielements/button/ActionButton.tsx diff --git a/src/renderer/components/uielements/button/ActionButton.stories.tsx b/src/renderer/components/uielements/button/ActionButton.stories.tsx new file mode 100644 index 000000000..2ff121c62 --- /dev/null +++ b/src/renderer/components/uielements/button/ActionButton.stories.tsx @@ -0,0 +1,40 @@ +import { ComponentMeta, StoryFn } from '@storybook/react' +import { AssetBNB, AssetBTC, AssetETH, AssetLTC, AssetRuneB1A, AssetRuneNative } from '@xchainjs/xchain-util' + +import { Actionbutton as Component, Props } from './ActionButton' + +const Template: StoryFn = (args) => +export const Default = Template.bind({}) + +const meta: ComponentMeta = { + title: 'Components/button/ActionButton', + argTypes: { + size: { + control: { + type: 'select', + options: ['small', 'normal', 'large'] + } + } + }, + args: { + actions: [ + { type: 'swap', data: { target: AssetRuneNative, source: AssetBNB } }, + { type: 'manage', data: { asset: AssetBNB } }, + { type: 'savers', data: { asset: AssetBTC } }, + { type: 'send', data: { asset: AssetETH } }, + { type: 'deposit', data: { asset: AssetLTC } }, + { type: 'upgrade', data: { asset: AssetRuneB1A } } + ], + disabled: false, + size: 'normal' + }, + decorators: [ + (Story) => ( +
+ +
+ ) + ] +} + +export default meta diff --git a/src/renderer/components/uielements/button/ActionButton.tsx b/src/renderer/components/uielements/button/ActionButton.tsx new file mode 100644 index 000000000..e9a7a559f --- /dev/null +++ b/src/renderer/components/uielements/button/ActionButton.tsx @@ -0,0 +1,94 @@ +import React, { useCallback } from 'react' + +import { Popover } from '@headlessui/react' +import { ChevronDownIcon } from '@heroicons/react/24/outline' +import { Asset, assetToString } from '@xchainjs/xchain-util' +import * as A from 'fp-ts/lib/Array' +import * as FP from 'fp-ts/lib/function' +import { useIntl } from 'react-intl' +import { useNavigate } from 'react-router-dom' + +import * as poolsRoutes from '../../../routes/pools' +import * as walletRoutes from '../../../routes/wallet' +import type { Props as ButtonProps } from './FlatButton' +import { TextButton, FlatButton } from './index' + +export type Action = + | { type: 'swap'; data: { target: Asset; source: Asset } } + | { type: 'manage'; data: { asset: Asset } } + | { type: 'savers'; data: { asset: Asset } } + | { type: 'send'; data: { asset: Asset } } + | { type: 'deposit'; data: { asset: Asset } } + | { type: 'upgrade'; data: { asset: Asset } } + +export type Props = Omit & { + actions: Action[] + isTextView?: boolean +} + +export const Actionbutton: React.FC = (props): JSX.Element => { + const { size, actions, isTextView = true } = props + + const intl = useIntl() + const navigate = useNavigate() + + const getPath = useCallback((action: Action): string => { + const { type, data } = action + switch (type) { + case 'swap': + return poolsRoutes.swap.path({ source: assetToString(data.source), target: assetToString(data.target) }) + case 'manage': + return poolsRoutes.deposit.path({ asset: assetToString(data.asset) }) + case 'savers': + return poolsRoutes.savers.path({ asset: assetToString(data.asset) }) + case 'send': + // TODO(@veado) Update send path to accept an asset + return walletRoutes.send.path() + case 'upgrade': + // TODO(@veado) Update upgrade path to accept an asset + return walletRoutes.upgradeRune.path() + case 'deposit': + // TODO(@veado) Update interact path to accept an asset + return walletRoutes.interact.path({ interactType: 'bond' }) + } + }, []) + + return ( + + + {({ open }) => ( + + + {intl.formatMessage({ id: 'common.action' })} + + + + )} + + + {({ close }) => ( +
+ {FP.pipe( + actions, + A.map((action) => ( + { + console.log('action:', action) + close() + navigate(getPath(action)) + }}> + {/* TODO(@veado) i18n */} + {action.type} + + )) + )} +
+ )} +
+
+ ) +} diff --git a/src/renderer/i18n/de/common.ts b/src/renderer/i18n/de/common.ts index dda6a3bde..bfdf8f4f4 100644 --- a/src/renderer/i18n/de/common.ts +++ b/src/renderer/i18n/de/common.ts @@ -63,6 +63,7 @@ const common: CommonMessages = { 'common.searchAsset': 'Suche Asset', 'common.retry': 'Wiederholen', 'common.reload': 'Neuladen', + 'common.action': 'Aktion', 'common.add': 'Einzahlen', 'common.swap': 'Swap', 'common.savers': 'Savers', diff --git a/src/renderer/i18n/en/common.ts b/src/renderer/i18n/en/common.ts index 2851721dc..27c769fcb 100644 --- a/src/renderer/i18n/en/common.ts +++ b/src/renderer/i18n/en/common.ts @@ -63,6 +63,7 @@ const common: CommonMessages = { 'common.searchAsset': 'Search Asset', 'common.retry': 'Retry', 'common.reload': 'Reload', + 'common.action': 'Action', 'common.add': 'Add', 'common.swap': 'Swap', 'common.savers': 'Savers', diff --git a/src/renderer/i18n/fr/common.ts b/src/renderer/i18n/fr/common.ts index 5a5b40d60..c570134c9 100644 --- a/src/renderer/i18n/fr/common.ts +++ b/src/renderer/i18n/fr/common.ts @@ -63,6 +63,7 @@ const common: CommonMessages = { 'common.searchAsset': 'Rechercher un actif', 'common.retry': 'Réessayer', 'common.reload': 'Recharger', + 'common.action': 'Action - FR', 'common.add': 'Ajouter', 'common.swap': 'Échanger', 'common.savers': 'Savers - FR', diff --git a/src/renderer/i18n/ru/common.ts b/src/renderer/i18n/ru/common.ts index 59be9731c..91e29e9a3 100644 --- a/src/renderer/i18n/ru/common.ts +++ b/src/renderer/i18n/ru/common.ts @@ -63,6 +63,7 @@ const common: CommonMessages = { 'common.searchAsset': 'Поиск актива', 'common.retry': 'Повторить', 'common.reload': 'Обновить', + 'common.action': 'Action - RU', 'common.add': 'Добавить', 'common.swap': 'Обмен', 'common.savers': 'Savers - RU', diff --git a/src/renderer/i18n/types.ts b/src/renderer/i18n/types.ts index 5ee2e6f12..cd5e8ad46 100644 --- a/src/renderer/i18n/types.ts +++ b/src/renderer/i18n/types.ts @@ -62,6 +62,7 @@ export type CommonMessageKey = | 'common.searchAsset' | 'common.retry' | 'common.reload' + | 'common.action' | 'common.add' | 'common.swap' | 'common.savers' From 55b8540ab7cf99e3fe5e95c852fd091d99f45467 Mon Sep 17 00:00:00 2001 From: Veado Date: Thu, 10 Nov 2022 12:46:28 +0100 Subject: [PATCH 2/5] Simplify `ActionButton` --- .../button/ActionButton.stories.tsx | 53 ++++++++++++--- .../uielements/button/ActionButton.tsx | 66 ++++++------------- .../button/ManageButton.stories.tsx | 2 +- 3 files changed, 64 insertions(+), 57 deletions(-) diff --git a/src/renderer/components/uielements/button/ActionButton.stories.tsx b/src/renderer/components/uielements/button/ActionButton.stories.tsx index 2ff121c62..10a84cbe3 100644 --- a/src/renderer/components/uielements/button/ActionButton.stories.tsx +++ b/src/renderer/components/uielements/button/ActionButton.stories.tsx @@ -1,7 +1,6 @@ import { ComponentMeta, StoryFn } from '@storybook/react' -import { AssetBNB, AssetBTC, AssetETH, AssetLTC, AssetRuneB1A, AssetRuneNative } from '@xchainjs/xchain-util' -import { Actionbutton as Component, Props } from './ActionButton' +import { ActionButton as Component, Props } from './ActionButton' const Template: StoryFn = (args) => export const Default = Template.bind({}) @@ -12,18 +11,54 @@ const meta: ComponentMeta = { size: { control: { type: 'select', - options: ['small', 'normal', 'large'] + options: ['small', 'medium', 'normal', 'large'] } } }, args: { actions: [ - { type: 'swap', data: { target: AssetRuneNative, source: AssetBNB } }, - { type: 'manage', data: { asset: AssetBNB } }, - { type: 'savers', data: { asset: AssetBTC } }, - { type: 'send', data: { asset: AssetETH } }, - { type: 'deposit', data: { asset: AssetLTC } }, - { type: 'upgrade', data: { asset: AssetRuneB1A } } + { + label: 'swap', + callback: () => { + console.log('swap') + }, + disabled: false + }, + { + label: 'manage', + callback: () => { + console.log('manage') + }, + disabled: false + }, + { + label: 'savers', + callback: () => { + console.log('savers') + }, + disabled: true + }, + { + label: 'send', + callback: () => { + console.log('send') + }, + disabled: false + }, + { + label: 'deposit', + callback: () => { + console.log('deposit') + }, + disabled: false + }, + { + label: 'upgrade', + callback: () => { + console.log('upgrade') + }, + disabled: true + } ], disabled: false, size: 'normal' diff --git a/src/renderer/components/uielements/button/ActionButton.tsx b/src/renderer/components/uielements/button/ActionButton.tsx index e9a7a559f..d12016e7f 100644 --- a/src/renderer/components/uielements/button/ActionButton.tsx +++ b/src/renderer/components/uielements/button/ActionButton.tsx @@ -1,67 +1,38 @@ -import React, { useCallback } from 'react' +import React from 'react' import { Popover } from '@headlessui/react' import { ChevronDownIcon } from '@heroicons/react/24/outline' -import { Asset, assetToString } from '@xchainjs/xchain-util' import * as A from 'fp-ts/lib/Array' import * as FP from 'fp-ts/lib/function' import { useIntl } from 'react-intl' -import { useNavigate } from 'react-router-dom' -import * as poolsRoutes from '../../../routes/pools' -import * as walletRoutes from '../../../routes/wallet' import type { Props as ButtonProps } from './FlatButton' import { TextButton, FlatButton } from './index' -export type Action = - | { type: 'swap'; data: { target: Asset; source: Asset } } - | { type: 'manage'; data: { asset: Asset } } - | { type: 'savers'; data: { asset: Asset } } - | { type: 'send'; data: { asset: Asset } } - | { type: 'deposit'; data: { asset: Asset } } - | { type: 'upgrade'; data: { asset: Asset } } - +export type Action = { label: string; callback: FP.Lazy; disabled?: boolean } export type Props = Omit & { actions: Action[] isTextView?: boolean } -export const Actionbutton: React.FC = (props): JSX.Element => { - const { size, actions, isTextView = true } = props +export const ActionButton: React.FC = (props): JSX.Element => { + const { size, actions, isTextView = true, disabled = false } = props const intl = useIntl() - const navigate = useNavigate() - - const getPath = useCallback((action: Action): string => { - const { type, data } = action - switch (type) { - case 'swap': - return poolsRoutes.swap.path({ source: assetToString(data.source), target: assetToString(data.target) }) - case 'manage': - return poolsRoutes.deposit.path({ asset: assetToString(data.asset) }) - case 'savers': - return poolsRoutes.savers.path({ asset: assetToString(data.asset) }) - case 'send': - // TODO(@veado) Update send path to accept an asset - return walletRoutes.send.path() - case 'upgrade': - // TODO(@veado) Update upgrade path to accept an asset - return walletRoutes.upgradeRune.path() - case 'deposit': - // TODO(@veado) Update interact path to accept an asset - return walletRoutes.interact.path({ interactType: 'bond' }) - } - }, []) return ( {({ open }) => ( - + {intl.formatMessage({ id: 'common.action' })} - + )} @@ -70,19 +41,20 @@ export const Actionbutton: React.FC = (props): JSX.Element => {
{FP.pipe( actions, - A.map((action) => ( + A.mapWithIndex((index, { label, callback, disabled = false }) => ( { - console.log('action:', action) + key={index} + onClick={(event: React.MouseEvent) => { + event.preventDefault() + event.stopPropagation() + callback() close() - navigate(getPath(action)) }}> - {/* TODO(@veado) i18n */} - {action.type} + {label} )) )} diff --git a/src/renderer/components/uielements/button/ManageButton.stories.tsx b/src/renderer/components/uielements/button/ManageButton.stories.tsx index 7ddebb587..bd1c83bfb 100644 --- a/src/renderer/components/uielements/button/ManageButton.stories.tsx +++ b/src/renderer/components/uielements/button/ManageButton.stories.tsx @@ -12,7 +12,7 @@ const meta: ComponentMeta = { size: { control: { type: 'select', - options: ['small', 'normal', 'large'] + options: ['small', 'medium', 'normal', 'large'] } } }, From 34706fc1c52b96a910c77c35dd9e75a27ef236b3 Mon Sep 17 00:00:00 2001 From: Veado Date: Thu, 10 Nov 2022 12:46:51 +0100 Subject: [PATCH 3/5] Use ActionButton in ActivePools --- src/renderer/views/pools/ActivePools.tsx | 59 ++++++++++++++---------- 1 file changed, 35 insertions(+), 24 deletions(-) diff --git a/src/renderer/views/pools/ActivePools.tsx b/src/renderer/views/pools/ActivePools.tsx index c48170f7d..6b56002ae 100644 --- a/src/renderer/views/pools/ActivePools.tsx +++ b/src/renderer/views/pools/ActivePools.tsx @@ -1,7 +1,14 @@ import React, { useCallback, useMemo, useRef } from 'react' import * as RD from '@devexperts/remote-data-ts' -import { assetToString, baseToAsset, bn, formatAssetAmountCurrency, formatBN } from '@xchainjs/xchain-util' +import { + AssetRuneNative, + assetToString, + baseToAsset, + bn, + formatAssetAmountCurrency, + formatBN +} from '@xchainjs/xchain-util' import { Grid } from 'antd' import { ColumnsType, ColumnType } from 'antd/lib/table' import * as A from 'fp-ts/Array' @@ -13,7 +20,7 @@ import { useNavigate } from 'react-router-dom' import { Network } from '../../../shared/api/types' import { ProtocolLimit, IncentivePendulum } from '../../components/pool' -import { ManageButton, SaversButton, Size as ButtonSize, SwapButton } from '../../components/uielements/button' +import { Action as ActionButtonAction, ActionButton } from '../../components/uielements/button/ActionButton' import { Table } from '../../components/uielements/table' import { useAppContext } from '../../contexts/AppContext' import { useMidgardContext } from '../../contexts/MidgardContext' @@ -79,35 +86,39 @@ export const ActivePools: React.FC = ({ haltedChains, mimir mimirHalt }) - const buttonSize: ButtonSize = isDesktopView ? 'normal' : 'large' + const actions: ActionButtonAction[] = [ + { + label: intl.formatMessage({ id: 'common.swap' }), + disabled: disableAllPoolActions || disableTradingActions, + callback: () => { + console.log('swap', assetToString(asset)) + navigate(poolsRoutes.swap.path({ source: assetToString(AssetRuneNative), target: assetToString(asset) })) + } + }, + { + label: intl.formatMessage({ id: 'common.manage' }), + disabled: disableAllPoolActions || disablePoolActions || walletLocked, + callback: () => { + navigate(poolsRoutes.deposit.path({ asset: assetToString(asset) })) + } + }, + { + label: intl.formatMessage({ id: 'common.savers' }), + disabled: disableAllPoolActions || disableTradingActions, + callback: () => { + navigate(poolsRoutes.savers.path({ asset: assetToString(asset) })) + } + } + ] return ( - - - + ) }, - [haltedChains, mimirHalt, walletLocked, isDesktopView] + [haltedChains, mimirHalt, intl, walletLocked, navigate] ) const btnPoolsColumn = useMemo( From 87e0dbe7952c568c1e6ab1a65fa062010e419113 Mon Sep 17 00:00:00 2001 From: Veado Date: Thu, 10 Nov 2022 13:25:59 +0100 Subject: [PATCH 4/5] Use ActionButton in PoolTitle --- .../components/pool/PoolTitle.styles.ts | 9 --- src/renderer/components/pool/PoolTitle.tsx | 76 +++++++++++-------- 2 files changed, 45 insertions(+), 40 deletions(-) diff --git a/src/renderer/components/pool/PoolTitle.styles.ts b/src/renderer/components/pool/PoolTitle.styles.ts index 7d3b2b12a..6549a4fb4 100644 --- a/src/renderer/components/pool/PoolTitle.styles.ts +++ b/src/renderer/components/pool/PoolTitle.styles.ts @@ -38,15 +38,6 @@ export const RowItem = styled(A.Col)` align-items: center; ` -export const ButtonActions = styled.div` - display: flex; - align-items: center; - justify-content: center; - > :not(:first-child) { - margin-left: 10px; - } -` - export const TitleContainer = styled.div` display: flex; flex-direction: row; diff --git a/src/renderer/components/pool/PoolTitle.tsx b/src/renderer/components/pool/PoolTitle.tsx index 5c12e6d28..a52aaf405 100644 --- a/src/renderer/components/pool/PoolTitle.tsx +++ b/src/renderer/components/pool/PoolTitle.tsx @@ -1,14 +1,17 @@ import React, { useMemo } from 'react' import * as RD from '@devexperts/remote-data-ts' -import { Asset, AssetAmount, assetToString, formatAssetAmount } from '@xchainjs/xchain-util' +import { Asset, AssetAmount, AssetRuneNative, assetToString, formatAssetAmount } from '@xchainjs/xchain-util' import { Grid } from 'antd' import * as FP from 'fp-ts/lib/function' +import { useIntl } from 'react-intl' +import { useNavigate } from 'react-router-dom' import { Network } from '../../../shared/api/types' +import { Action as ActionButtonAction, ActionButton } from '../../components/uielements/button/ActionButton' import { loadingString } from '../../helpers/stringHelper' +import * as poolsRoutes from '../../routes/pools' import { AssetIcon } from '../uielements/assets/assetIcon' -import { SwapButton, ManageButton } from '../uielements/button' import * as Styled from './PoolTitle.styles' export type Props = { @@ -41,6 +44,9 @@ export const PoolTitle: React.FC = ({ }) => { const isDesktopView = Grid.useBreakpoint()?.md ?? false + const intl = useIntl() + const navigate = useNavigate() + const title = useMemo(() => { const Star = watched ? Styled.StarFilled : Styled.StarOutlined const starClickHandler = watched ? unwatch : watch @@ -68,35 +74,43 @@ export const PoolTitle: React.FC = ({ [priceRD] ) - const buttons = useMemo( - () => ( - - - {isAvailablePool && ( - - )} - - ), - [ - disableAllPoolActions, - disablePoolActions, - walletLocked, - asset, - isDesktopView, - isAvailablePool, - disableTradingPoolAction + const actionButton = useMemo(() => { + const actions: ActionButtonAction[] = [ + { + label: intl.formatMessage({ id: 'common.swap' }), + disabled: !isAvailablePool || disableAllPoolActions || disableTradingPoolAction, + callback: () => { + navigate(poolsRoutes.swap.path({ source: assetToString(AssetRuneNative), target: assetToString(asset) })) + } + }, + { + label: intl.formatMessage({ id: 'common.manage' }), + disabled: disableAllPoolActions || disablePoolActions || walletLocked, + callback: () => { + navigate(poolsRoutes.deposit.path({ asset: assetToString(asset) })) + } + }, + { + label: intl.formatMessage({ id: 'common.savers' }), + disabled: !isAvailablePool || disableAllPoolActions || disableTradingPoolAction, + callback: () => { + navigate(poolsRoutes.savers.path({ asset: assetToString(asset) })) + } + } ] - ) + + return + }, [ + intl, + isAvailablePool, + disableAllPoolActions, + disableTradingPoolAction, + disablePoolActions, + walletLocked, + isDesktopView, + navigate, + asset + ]) return ( @@ -104,7 +118,7 @@ export const PoolTitle: React.FC = ({ {title} {priceStr} - {buttons} + {actionButton} ) } From 1aa29b0fac962809e86a41faaec058b3e172cab6 Mon Sep 17 00:00:00 2001 From: Veado Date: Thu, 10 Nov 2022 13:26:43 +0100 Subject: [PATCH 5/5] Fix AssetIcon style to avoid `max-width: 100%` - for img by default, which might troubles layouts (e.g. in PoolTitle) --- .../components/uielements/assets/assetIcon/AssetIcon.styles.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/src/renderer/components/uielements/assets/assetIcon/AssetIcon.styles.ts b/src/renderer/components/uielements/assets/assetIcon/AssetIcon.styles.ts index cecb36a9f..f32019663 100644 --- a/src/renderer/components/uielements/assets/assetIcon/AssetIcon.styles.ts +++ b/src/renderer/components/uielements/assets/assetIcon/AssetIcon.styles.ts @@ -77,4 +77,5 @@ export const Icon = styled.img` width: ${({ size, isSynth }) => `${sizes[size] - (isSynth ? 2 : 0) * borders[size]}px`}; height: ${({ size, isSynth }) => `${sizes[size] - (isSynth ? 2 : 0) * borders[size]}px`}; border-radius: 50%; + max-width: auto; // overridden to avoid max-w-100% (default) `