From 4120d58a1b72a42c828d136365e9ba6b8d6c42a8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jos=C3=A9=20Oliveira?= Date: Thu, 17 Feb 2022 01:40:02 +0000 Subject: [PATCH] feat: advanced wallet settings up to spec --- lib/new-config-manager.js | 5 +- ...5010873828-add-advanced-wallet-settings.js | 22 +++ .../src/pages/Wallet/AdvancedWallet.js | 125 ++++++++++++--- new-lamassu-admin/src/pages/Wallet/Wallet.js | 66 +------- .../src/pages/Wallet/Wallet.styles.js | 9 -- new-lamassu-admin/src/pages/Wallet/helper.js | 142 +++++++++++++++--- 6 files changed, 246 insertions(+), 123 deletions(-) create mode 100644 migrations/1645010873828-add-advanced-wallet-settings.js diff --git a/lib/new-config-manager.js b/lib/new-config-manager.js index 0e721736..c8196495 100644 --- a/lib/new-config-manager.js +++ b/lib/new-config-manager.js @@ -54,7 +54,10 @@ const getLocale = (deviceId, it) => { const getGlobalLocale = it => getLocale(null, it) -const getWalletSettings = (key, it) => _.compose(fromNamespace(key), fromNamespace(namespaces.WALLETS))(it) +const getWalletSettings = (key, it) => { + const result = _.compose(fromNamespace(key), fromNamespace(namespaces.WALLETS))(it) + return result +} const getCashOut = (key, it) => _.compose(fromNamespace(key), fromNamespace(namespaces.CASH_OUT))(it) const getGlobalCashOut = fromNamespace(namespaces.CASH_OUT) const getOperatorInfo = fromNamespace(namespaces.OPERATOR_INFO) diff --git a/migrations/1645010873828-add-advanced-wallet-settings.js b/migrations/1645010873828-add-advanced-wallet-settings.js new file mode 100644 index 00000000..34252cc6 --- /dev/null +++ b/migrations/1645010873828-add-advanced-wallet-settings.js @@ -0,0 +1,22 @@ +const uuid = require('uuid') +const { saveConfig, loadLatest } = require('../lib/new-settings-loader') + +exports.up = function (next) { + const newConfig = {} + return loadLatest() + .then(config => { + newConfig[`wallets_advanced_feeMultiplier`] = '1' + newConfig[`wallets_advanced_cryptoUnits`] = 'full' + newConfig[`wallets_advanced_allowTransactionBatching`] = false + newConfig[`wallets_advanced_id`] = uuid.v4() + return saveConfig(newConfig) + }) + .then(next) + .catch(err => { + return next(err) + }) +} + +module.exports.down = function (next) { + next() +} diff --git a/new-lamassu-admin/src/pages/Wallet/AdvancedWallet.js b/new-lamassu-admin/src/pages/Wallet/AdvancedWallet.js index 8897c534..e93aee84 100644 --- a/new-lamassu-admin/src/pages/Wallet/AdvancedWallet.js +++ b/new-lamassu-admin/src/pages/Wallet/AdvancedWallet.js @@ -2,27 +2,28 @@ import { useQuery, useMutation } from '@apollo/react-hooks' import gql from 'graphql-tag' import { utils as coinUtils } from 'lamassu-coins' import * as R from 'ramda' -import React from 'react' +import React, { useState } from 'react' -import { NamespacedTable as EditableTable } from 'src/components/editableTable' +import { Table as EditableTable } from 'src/components/editableTable' +import Section from 'src/components/layout/Section' import { fromNamespace, toNamespace, namespaces } from 'src/utils/config' import { - WalletSchema, AdvancedWalletSchema, - getAdvancedWalletElements + getAdvancedWalletElements, + getAdvancedWalletElementsOverrides, + OverridesDefaults, + OverridesSchema } from './helper' const SAVE_CONFIG = gql` - mutation Save($config: JSONObject, $accounts: JSONObject) { + mutation Save($config: JSONObject) { saveConfig(config: $config) - saveAccounts(accounts: $accounts) } ` const GET_INFO = gql` query getData { config - accounts cryptoCurrencies { code display @@ -31,34 +32,112 @@ const GET_INFO = gql` ` const AdvancedWallet = () => { + const ADVANCED = 'advanced' + const CRYPTOCURRENCY_KEY = 'cryptoCurrency' const SCREEN_KEY = namespaces.WALLETS const { data } = useQuery(GET_INFO) + const [isEditingDefault, setEditingDefault] = useState(false) + const [isEditingOverrides, setEditingOverrides] = useState(false) + const [saveConfig, { error }] = useMutation(SAVE_CONFIG, { refetchQueries: () => ['getData'] }) - const save = (rawConfig, accounts) => { - const config = toNamespace(SCREEN_KEY)(rawConfig) - return saveConfig({ variables: { config, accounts } }) + const mapConfigKeys = R.curry((fn, obj) => + R.zipObj(R.map(fn, R.keys(obj)), R.values(obj)) + ) + + const save = rawConfig => { + const config = toNamespace(SCREEN_KEY)( + mapConfigKeys(it => `${ADVANCED}_` + it, rawConfig.wallets[0]) + ) + + return saveConfig({ variables: { config } }) } - const config = data?.config && fromNamespace(SCREEN_KEY)(data.config) + const saveOverrides = rawConfig => { + const config = toNamespace(SCREEN_KEY)( + mapConfigKeys(it => `${ADVANCED}_` + it, rawConfig) + ) + return saveConfig({ variables: { config } }) + } + + const onEditingDefault = (it, editing) => setEditingDefault(editing) + const onEditingOverrides = (it, editing) => setEditingOverrides(editing) + const cryptoCurrencies = data?.cryptoCurrencies ?? [] + const AdvancedWalletSettings = mapConfigKeys( + it => R.tail(R.split('_', it)), + R.pickBy( + (val, key) => R.head(R.split('_', key)) === ADVANCED, + data?.config && fromNamespace(SCREEN_KEY)(data.config) + ) + ) + + const AdvancedWalletSettingsOverrides = AdvancedWalletSettings.overrides ?? [] + + const overridenCryptos = R.map(R.prop(CRYPTOCURRENCY_KEY))( + AdvancedWalletSettingsOverrides + ) + const suggestionFilter = R.filter( + it => !R.contains(it.code, overridenCryptos) + ) + const coinSuggestions = suggestionFilter(cryptoCurrencies) + + const findSuggestion = it => { + const coin = R.compose(R.find(R.propEq('code', it?.cryptoCurrency)))( + cryptoCurrencies + ) + return coin ? [coin] : [] + } + return ( - !WalletSchema.isValidSync(it)} - validationSchema={AdvancedWalletSchema} - elements={getAdvancedWalletElements(cryptoCurrencies, coinUtils, config)} - /> + <> +
+ !AdvancedWalletSchema.isValidSync(it)} + inialValues={R.of(AdvancedWalletSettings)} + validationSchema={AdvancedWalletSchema} + elements={getAdvancedWalletElements( + coinUtils, + AdvancedWalletSettings + )} + setEditing={onEditingDefault} + forceDisable={isEditingOverrides} + /> +
+
+ +
+ ) } diff --git a/new-lamassu-admin/src/pages/Wallet/Wallet.js b/new-lamassu-admin/src/pages/Wallet/Wallet.js index 1fff8153..b0a9c0c8 100644 --- a/new-lamassu-admin/src/pages/Wallet/Wallet.js +++ b/new-lamassu-admin/src/pages/Wallet/Wallet.js @@ -1,18 +1,14 @@ import { useQuery, useMutation } from '@apollo/react-hooks' -import { DialogActions, makeStyles, Box } from '@material-ui/core' +import { makeStyles } from '@material-ui/core' import gql from 'graphql-tag' import * as R from 'ramda' import React, { useState } from 'react' import Modal from 'src/components/Modal' -import { IconButton, Button } from 'src/components/buttons' import { NamespacedTable as EditableTable } from 'src/components/editableTable' -import { RadioGroup } from 'src/components/inputs' import TitleSection from 'src/components/layout/TitleSection' -import { P, Label1 } from 'src/components/typography' import FormRenderer from 'src/pages/Services/FormRenderer' import schemas from 'src/pages/Services/schemas' -import { ReactComponent as EditIcon } from 'src/styling/icons/action/edit/enabled.svg' import { ReactComponent as ReverseSettingsIcon } from 'src/styling/icons/circle buttons/settings/white.svg' import { ReactComponent as SettingsIcon } from 'src/styling/icons/circle buttons/settings/zodiac.svg' import { fromNamespace, toNamespace } from 'src/utils/config' @@ -59,8 +55,6 @@ const useStyles = makeStyles(styles) const Wallet = ({ name: SCREEN_KEY }) => { const classes = useStyles() - const [editingFeeDiscount, setEditingFeeDiscount] = useState(null) - const [selectedDiscount, setSelectedDiscount] = useState(null) const [editingSchema, setEditingSchema] = useState(null) const [onChangeFunction, setOnChangeFunction] = useState(null) const [wizard, setWizard] = useState(false) @@ -116,25 +110,6 @@ const Wallet = ({ name: SCREEN_KEY }) => { return it }) - const saveFeeDiscount = rawConfig => { - const config = toNamespace(SCREEN_KEY)(rawConfig) - setEditingFeeDiscount(false) - return saveConfig({ variables: { config } }) - } - - const handleRadioButtons = evt => { - const selectedDiscount = R.path(['target', 'value'])(evt) - setSelectedDiscount(selectedDiscount) - } - - const radioButtonOptions = [ - { display: '+20%', code: '1.2' }, - { display: 'Default', code: '1' }, - { display: '-20%', code: '0.8' }, - { display: '-40%', code: '0.6' }, - { display: '-60%', code: '0.4' } - ] - return ( <>
@@ -149,19 +124,6 @@ const Wallet = ({ name: SCREEN_KEY }) => { } ]} /> - - Fee discount - -

{selectedDiscount}

- setEditingFeeDiscount(true)}> - - -
-
{!advancedSettings && ( <> @@ -209,32 +171,6 @@ const Wallet = ({ name: SCREEN_KEY }) => { )} {advancedSettings && } - {editingFeeDiscount && ( - setEditingFeeDiscount(null)} - open={true}> -

- Set a priority level for your outgoing BTC transactions, selecting a - percentage off of the fee estimate your wallet uses. -

- - - - -
- )} ) } diff --git a/new-lamassu-admin/src/pages/Wallet/Wallet.styles.js b/new-lamassu-admin/src/pages/Wallet/Wallet.styles.js index f9a6bb52..c7f1bfb8 100644 --- a/new-lamassu-admin/src/pages/Wallet/Wallet.styles.js +++ b/new-lamassu-admin/src/pages/Wallet/Wallet.styles.js @@ -1,16 +1,7 @@ -import { offColor } from 'src/styling/variables' - export default { header: { display: 'flex', alignItems: 'center', justifyContent: 'space-between' - }, - feeDiscountLabel: { - color: offColor, - margin: [[13, 0, -5, 20]] - }, - selection: { - marginRight: 12 } } diff --git a/new-lamassu-admin/src/pages/Wallet/helper.js b/new-lamassu-admin/src/pages/Wallet/helper.js index 3d913107..510a4ac1 100644 --- a/new-lamassu-admin/src/pages/Wallet/helper.js +++ b/new-lamassu-admin/src/pages/Wallet/helper.js @@ -33,32 +33,50 @@ const WalletSchema = Yup.object().shape({ const AdvancedWalletSchema = Yup.object().shape({ cryptoUnits: Yup.string().required(), + feeMultiplier: Yup.string().required(), allowTransactionBatching: Yup.boolean() }) -const getAdvancedWalletElements = (cryptoCurrencies, coinUtils, config) => { - const viewCryptoCurrency = it => - R.compose( - R.prop(['display']), - R.find(R.propEq('code', it)) - )(cryptoCurrencies) +const OverridesSchema = Yup.object().shape({ + cryptoUnits: Yup.string().required(), + feeMultiplier: Yup.string().required(), + cryptoCurrency: Yup.string().required(), + allowTransactionBatching: Yup.boolean() +}) - const getOptions = R.curry((coinUtils, it) => { - const options = R.keys(coinUtils.getCryptoCurrency(it.id).units) - return R.map(option => { - return { code: option, display: option } - })(options) - }) +const OverridesDefaults = { + cryptoUnits: '', + feeMultiplier: '', + cryptoCurrency: '', + allowTransactionBatching: null +} +const viewFeeMultiplier = it => + R.compose(R.prop(['display']), R.find(R.propEq('code', it)))(feeOptions) + +const feeOptions = [ + { display: '+20%', code: '1.2' }, + { display: 'Default', code: '1' }, + { display: '-20%', code: '0.8' }, + { display: '-40%', code: '0.6' }, + { display: '-60%', code: '0.4' } +] + +const cryptoUnitsDefaultOptions = [ + { display: 'mili', code: 'mili' }, + { display: 'full', code: 'full' } +] + +const getCryptoUnitsOptions = R.curry((coinUtils, it) => { + if (R.isNil(it.cryptoCurrency)) return cryptoUnitsDefaultOptions + const options = R.keys(coinUtils.getCryptoCurrency(it.cryptoCurrency).units) + return R.map(option => { + return { code: option, display: option } + })(options) +}) + +const getAdvancedWalletElements = () => { return [ - { - name: 'id', - header: 'Cryptocurrency', - width: 180, - view: viewCryptoCurrency, - size: 'sm', - editable: false - }, { name: 'cryptoUnits', size: 'sm', @@ -66,7 +84,7 @@ const getAdvancedWalletElements = (cryptoCurrencies, coinUtils, config) => { width: 190, input: Autocomplete, inputProps: { - options: getOptions(coinUtils), + options: cryptoUnitsDefaultOptions, valueProp: 'code', labelProp: 'display' } @@ -77,12 +95,83 @@ const getAdvancedWalletElements = (cryptoCurrencies, coinUtils, config) => { stripe: true, width: 250, view: (_, ite) => { - if (ite.id !== 'BTC') + return ite.allowTransactionBatching ? 'Yes (Only BTC supports)' : `No` + }, + input: Checkbox + }, + { + name: 'feeMultiplier', + header: `Miner's Fee`, + size: 'sm', + stripe: true, + width: 250, + view: viewFeeMultiplier, + input: Autocomplete, + inputProps: { + options: feeOptions, + valueProp: 'code', + labelProp: 'display' + } + } + ] +} + +const getAdvancedWalletElementsOverrides = ( + coinSuggestions, + findSuggestion, + coinUtils +) => { + return [ + { + name: 'cryptoCurrency', + width: 180, + input: Autocomplete, + inputProps: { + options: it => R.concat(coinSuggestions, findSuggestion(it)), + optionsLimit: null, + valueProp: 'code', + labelProp: 'display' + }, + size: 'sm' + }, + { + name: 'cryptoUnits', + size: 'sm', + stripe: true, + width: 190, + input: Autocomplete, + inputProps: { + options: getCryptoUnitsOptions(coinUtils), + valueProp: 'code', + labelProp: 'display' + } + }, + { + name: 'allowTransactionBatching', + size: 'sm', + stripe: true, + width: 250, + view: (_, ite) => { + if (ite.cryptoCurrency !== 'BTC') return {`No`} - return config[`${ite.id}_allowTransactionBatching`] ? 'Yes' : 'No' + return ite.allowTransactionBatching ? 'Yes' : 'No' }, input: Checkbox, - editable: it => it.id === 'BTC' + editable: it => it.cryptoCurrency === 'BTC' + }, + { + name: 'feeMultiplier', + header: `Miner's Fee`, + size: 'sm', + stripe: true, + width: 250, + view: viewFeeMultiplier, + input: Autocomplete, + inputProps: { + options: feeOptions, + valueProp: 'code', + labelProp: 'display' + } } ] } @@ -199,5 +288,8 @@ export { AdvancedWalletSchema, getElements, filterClass, - getAdvancedWalletElements + getAdvancedWalletElements, + getAdvancedWalletElementsOverrides, + OverridesDefaults, + OverridesSchema }