feat: all coins on commissions

This commit is contained in:
Taranto 2020-11-05 17:06:16 +00:00 committed by Josh Harvey
parent 913d281798
commit 9595afb63a
12 changed files with 258 additions and 152 deletions

View file

@ -31,12 +31,24 @@ const getCommissions = (cryptoCode, deviceId, config) => {
const commissions = fromNamespace(namespaces.COMMISSIONS)(config)
const overrides = commissions.overrides
const filter = it => it.machine === deviceId && _.includes(cryptoCode)(it.cryptoCurrencies)
const allFilter = it => it.machine === 'ALL_MACHINES' && _.includes(cryptoCode)(it.cryptoCurrencies)
if (_.isEmpty(overrides)) return _.omit('overrides', commissions)
return _.omit('overrides', _.assignAll([commissions, ..._.filter(allFilter)(overrides), ..._.filter(filter)(overrides)]))
const specificFilter = it => it.machine === deviceId && _.includes(cryptoCode)(it.cryptoCurrencies)
const specificAllCoinsFilter = it => it.machine === deviceId && _.includes('ALL_COINS')(it.cryptoCurrencies)
const allMachinesFilter = it => it.machine === 'ALL_MACHINES' && _.includes(cryptoCode)(it.cryptoCurrencies)
const specificOverrides = _.filter(specificFilter)(overrides)
const specificAllCoinsOverrides = _.filter(specificAllCoinsFilter)(overrides)
const allMachinesOverrides = _.filter(allMachinesFilter)(overrides)
const priorityOrderOverrides = [
commissions,
...allMachinesOverrides,
...specificAllCoinsOverrides,
...specificOverrides
]
return _.omit('overrides', _.assignAll(priorityOrderOverrides))
}
const getLocale = (deviceId, it) => {

View file

@ -15526,9 +15526,9 @@
}
},
"fn-name": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/fn-name/-/fn-name-2.0.1.tgz",
"integrity": "sha1-UhTXU3pNBqSjAcDMJi/rhBiAAuc="
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/fn-name/-/fn-name-3.0.0.tgz",
"integrity": "sha512-eNMNr5exLoavuAMhIUVsOKF79SWd/zG104ef6sxBTSw+cZc6BXdQXDvYcGvp0VbxVVSp1XDUNoz7mg1xMtSznA=="
},
"follow-redirects": {
"version": "1.5.10",
@ -24740,9 +24740,9 @@
}
},
"property-expr": {
"version": "1.5.1",
"resolved": "https://registry.npmjs.org/property-expr/-/property-expr-1.5.1.tgz",
"integrity": "sha512-CGuc0VUTGthpJXL36ydB6jnbyOf/rAHFvmVrJlH+Rg0DqqLFQGAP6hIaxD/G0OAmBJPhXDHuEJigrp0e0wFV6g=="
"version": "2.0.4",
"resolved": "https://registry.npmjs.org/property-expr/-/property-expr-2.0.4.tgz",
"integrity": "sha512-sFPkHQjVKheDNnPvotjQmm3KD3uk1fWKUN7CrpdbwmUx3CrG3QiM8QpTSimvig5vTXmTvjz7+TDvXOI9+4rkcg=="
},
"property-information": {
"version": "5.6.0",
@ -30753,9 +30753,9 @@
}
},
"synchronous-promise": {
"version": "2.0.9",
"resolved": "https://registry.npmjs.org/synchronous-promise/-/synchronous-promise-2.0.9.tgz",
"integrity": "sha512-LO95GIW16x69LuND1nuuwM4pjgFGupg7pZ/4lU86AmchPKrhk0o2tpMU2unXRrqo81iAFe1YJ0nAGEVwsrZAgg=="
"version": "2.0.15",
"resolved": "https://registry.npmjs.org/synchronous-promise/-/synchronous-promise-2.0.15.tgz",
"integrity": "sha512-k8uzYIkIVwmT+TcglpdN50pS2y1BDcUnBPK9iJeGu0Pl1lOI8pD6wtzgw91Pjpe+RxtTncw32tLxs/R0yNL2Mg=="
},
"table": {
"version": "5.4.6",
@ -33772,16 +33772,32 @@
}
},
"yup": {
"version": "0.27.0",
"resolved": "https://registry.npmjs.org/yup/-/yup-0.27.0.tgz",
"integrity": "sha512-v1yFnE4+u9za42gG/b/081E7uNW9mUj3qtkmelLbW5YPROZzSH/KUUyJu9Wt8vxFJcT9otL/eZopS0YK1L5yPQ==",
"version": "0.29.3",
"resolved": "https://registry.npmjs.org/yup/-/yup-0.29.3.tgz",
"integrity": "sha512-RNUGiZ/sQ37CkhzKFoedkeMfJM0vNQyaz+wRZJzxdKE7VfDeVKH8bb4rr7XhRLbHJz5hSjoDNwMEIaKhuMZ8gQ==",
"requires": {
"@babel/runtime": "^7.0.0",
"fn-name": "~2.0.1",
"lodash": "^4.17.11",
"property-expr": "^1.5.0",
"synchronous-promise": "^2.0.6",
"@babel/runtime": "^7.10.5",
"fn-name": "~3.0.0",
"lodash": "^4.17.15",
"lodash-es": "^4.17.11",
"property-expr": "^2.0.2",
"synchronous-promise": "^2.0.13",
"toposort": "^2.0.2"
},
"dependencies": {
"@babel/runtime": {
"version": "7.12.5",
"resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.12.5.tgz",
"integrity": "sha512-plcc+hbExy3McchJCEQG3knOsuh3HH+Prx1P6cLIkET/0dLuQDEnrT+s27Axgc9bqfsmNUNHfscgMUdBpC9xfg==",
"requires": {
"regenerator-runtime": "^0.13.4"
}
},
"regenerator-runtime": {
"version": "0.13.7",
"resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.7.tgz",
"integrity": "sha512-a54FxoJDIr27pgf7IgeQGxmqUNYrcV338lf/6gH456HZ/PhX+5BcwHXG9ajESmwe6WRO0tAzRUrRmNONWgkrew=="
}
}
},
"zen-observable": {

View file

@ -36,7 +36,7 @@
"react-virtualized": "^9.21.2",
"sanctuary": "^2.0.1",
"uuid": "^7.0.2",
"yup": "0.27.0"
"yup": "0.29.3"
},
"devDependencies": {
"@storybook/addon-actions": "6.0.26",

View file

@ -195,10 +195,13 @@ const ETable = ({
)}
{innerData.map((it, idx) => {
const nextElement = innerData[idx + 1]
const canGroup = !!groupBy && nextElement
const isFunction = R.type(groupBy) === 'Function'
const groupFunction = isFunction ? groupBy : R.prop(groupBy)
const isLastOfGroup =
groupBy &&
nextElement &&
nextElement[groupBy] !== it[groupBy]
canGroup && groupFunction(it) !== groupFunction(nextElement)
return (
<Formik

View file

@ -20,11 +20,13 @@ const AutocompleteFormik = ({ options, onChange, ...props }) => {
onBlur && onBlur(event)
}
const onChangeHandler = value => setFieldValue(name, value)
return (
<Autocomplete
name={name}
onChange={(event, item) => {
onChange && onChange(value, item, () => setFieldValue(name, value))
if (onChange) return onChange(value, item, onChangeHandler)
setFieldValue(name, item)
}}
onBlur={innerOnBlur}

View file

@ -168,7 +168,7 @@ const DataTable = ({
// this has to be in a style because of how the component works
style={{ overflow: 'inherit', outline: 'none' }}
{...props}
height={height}
height={loading ? 0 : height}
width={width}
rowCount={data.length}
rowHeight={cache.rowHeight}

View file

@ -12,9 +12,10 @@ import {
mainFields,
overrides,
schema,
OverridesSchema,
getOverridesSchema,
defaults,
overridesDefaults
overridesDefaults,
getOrder
} from './helper'
const GET_DATA = gql`
@ -54,7 +55,7 @@ const Commissions = ({ name: SCREEN_KEY }) => {
const commissionOverrides = commission?.overrides ?? []
const orderedCommissionsOverrides = R.sortWith([
R.ascend(it => (R.propEq('machine', 'ALL_MACHINES')(it) ? 0 : 1)),
R.ascend(getOrder),
R.ascend(R.prop('machine'))
])(commissionOverrides)
@ -100,9 +101,13 @@ const Commissions = ({ name: SCREEN_KEY }) => {
enableDelete
enableEdit
enableCreate
groupBy={getOrder}
initialValues={overridesDefaults}
save={saveOverrides}
validationSchema={OverridesSchema}
validationSchema={getOverridesSchema(
orderedCommissionsOverrides,
data
)}
data={orderedCommissionsOverrides}
elements={overrides(data, currency, orderedCommissionsOverrides)}
setEditing={onEditingOverrides}

View file

@ -12,6 +12,11 @@ const ALL_MACHINES = {
deviceId: 'ALL_MACHINES'
}
const ALL_COINS = {
display: 'All Coins',
code: 'ALL_COINS'
}
const cashInAndOutHeaderStyle = { marginLeft: 6 }
const cashInHeader = (
@ -28,41 +33,39 @@ const cashOutHeader = (
</div>
)
const getOverridesFields = (getData, currency, auxElements) => {
const getView = (data, code, compare) => it => {
const getView = (data, code, compare) => it => {
if (!data) return ''
return R.compose(
R.prop(code),
R.find(R.propEq(compare ?? 'code', it))
)(data)
}
return R.compose(R.prop(code), R.find(R.propEq(compare ?? 'code', it)))(data)
}
const displayCodeArray = data => it => {
const displayCodeArray = data => it => {
if (!it) return it
return R.compose(R.join(', '), R.map(getView(data, 'code')))(it)
return R.compose(R.join(', '), R.map(getView(data, 'display')))(it)
}
const onCryptoChange = (prev, curr, setValue) => {
const hasAllCoins = R.includes(ALL_COINS.code)(curr)
const hadAllCoins = R.includes(ALL_COINS.code)(prev)
if (hasAllCoins && hadAllCoins && R.length(curr) > 1) {
return setValue(R.reject(R.equals(ALL_COINS.code))(curr))
}
var overridenMachineCoins = R.reduceBy(
(acc, { cryptoCurrencies }) => acc.concat(cryptoCurrencies),
[],
R.prop('machine'),
auxElements
)
const suggestionFilter = (it, cryptoData) => {
if (!it?.machine) return cryptoData
return R.differenceWith(
(x, y) => x.code === y && !it?.cryptoCurrencies.includes(x.code),
cryptoData,
overridenMachineCoins[it?.machine] ?? []
)
if (hasAllCoins && !hadAllCoins) {
return setValue([ALL_COINS.code])
}
setValue(curr)
}
const getOverridesFields = (getData, currency, auxElements) => {
const machineData = [ALL_MACHINES].concat(getData(['machines']))
const cryptoData = getData(['cryptoCurrencies'])
const rawCryptos = getData(['cryptoCurrencies'])
const cryptoData = [ALL_COINS].concat(
R.map(it => ({ display: it.code, code: it.code }))(rawCryptos ?? [])
)
return [
{
@ -84,10 +87,11 @@ const getOverridesFields = (getData, currency, auxElements) => {
view: displayCodeArray(cryptoData),
input: Autocomplete,
inputProps: {
options: (...[, it]) => suggestionFilter(it, cryptoData),
options: cryptoData,
valueProp: 'code',
getLabel: R.path(['code']),
multiple: true
getLabel: R.path(['display']),
multiple: true,
onChange: onCryptoChange
}
},
{
@ -225,12 +229,70 @@ const schema = Yup.object().shape({
.required()
})
const OverridesSchema = Yup.object().shape({
const getAlreadyUsed = (id, machine, values) => {
const getCrypto = R.prop('cryptoCurrencies')
const getMachineId = R.prop('machine')
const filteredOverrides = R.filter(R.propEq('machine', machine))(values)
const originalValue = R.find(R.propEq('id', id))(values)
const originalCryptos = getCrypto(originalValue)
const originalMachineId = getMachineId(originalValue)
const alreadyUsed = R.compose(
R.uniq,
R.flatten,
R.map(getCrypto)
)(filteredOverrides)
if (machine !== originalMachineId) return alreadyUsed ?? []
return R.difference(alreadyUsed, originalCryptos)
}
const getOverridesSchema = (values, rawData) => {
const getData = R.path(R.__, rawData)
const machineData = [ALL_MACHINES].concat(getData(['machines']))
const rawCryptos = getData(['cryptoCurrencies'])
const cryptoData = [ALL_COINS].concat(
R.map(it => ({ display: it.code, code: it.code }))(rawCryptos ?? [])
)
return Yup.object().shape({
machine: Yup.string()
.nullable()
.label('Machine')
.required(),
cryptoCurrencies: Yup.array()
.test({
test() {
const { id, machine, cryptoCurrencies } = this.parent
const alreadyUsed = getAlreadyUsed(id, machine, values)
const isAllMachines = machine === ALL_MACHINES.deviceId
const isAllCoins = R.includes(ALL_COINS.code, cryptoCurrencies)
if (isAllMachines && isAllCoins) {
return this.createError({
message: `All machines and all coins should be configured in the default setup table`
})
}
const repeated = R.intersection(alreadyUsed, cryptoCurrencies)
if (!R.isEmpty(repeated)) {
const codes = displayCodeArray(cryptoData)(repeated)
const machineView = getView(
machineData,
'name',
'deviceId'
)(machine)
const message = `${codes} already overriden for machine: ${machineView}`
return this.createError({ message })
}
return true
}
})
.label('Crypto Currencies')
.required(),
cashIn: Yup.number()
@ -253,7 +315,8 @@ const OverridesSchema = Yup.object().shape({
.min(0)
.max(currencyMax)
.required()
})
})
}
const defaults = {
cashIn: '',
@ -271,11 +334,23 @@ const overridesDefaults = {
minimumTx: ''
}
const getOrder = ({ machine, cryptoCurrencies }) => {
const isAllMachines = machine === ALL_MACHINES.deviceId
const isAllCoins = R.contains(ALL_COINS.code, cryptoCurrencies)
if (isAllMachines && isAllCoins) return 0
if (isAllMachines) return 1
if (isAllCoins) return 2
return 3
}
export {
mainFields,
overrides,
schema,
OverridesSchema,
getOverridesSchema,
defaults,
overridesDefaults
overridesDefaults,
getOrder
}

View file

@ -99,9 +99,7 @@ const FiatCurrencyChangeAlert = ({ open, close, save }) => {
const Locales = ({ name: SCREEN_KEY }) => {
const [wizard, setWizard] = useState(false)
const [cancelCryptoConfiguration, setCancelCryptoConfiguration] = useState(
null
)
const [onChangeFunction, setOnChangeFunction] = useState(null)
const [isEditingDefault, setEditingDefault] = useState(false)
const [isEditingOverrides, setEditingOverrides] = useState(false)
const { data } = useQuery(GET_DATA)
@ -143,19 +141,30 @@ const Locales = ({ name: SCREEN_KEY }) => {
return saveConfig({ variables: { config } })
}
const configureCoin = (it, prev, cancel) => {
if (!it) return
const onChangeCoin = (prev, curr, setValue) => {
const coin = R.difference(curr, prev)[0]
if (!coin) return setValue(curr)
const namespaced = fromNamespace(it)(wallets)
const namespaced = fromNamespace(coin)(wallets)
if (!WalletSchema.isValidSync(namespaced)) {
setCancelCryptoConfiguration(() => () => cancel())
setWizard(it)
setOnChangeFunction(() => () => setValue(curr))
setWizard(coin)
return
}
setValue(curr)
}
const onEditingDefault = (it, editing) => setEditingDefault(editing)
const onEditingOverrides = (it, editing) => setEditingOverrides(editing)
const wizardSave = it =>
save(toNamespace(namespaces.WALLETS)(it)).then(it => {
onChangeFunction()
setOnChangeFunction(null)
return it
})
return (
<>
<FiatCurrencyChangeAlert
@ -175,7 +184,7 @@ const Locales = ({ name: SCREEN_KEY }) => {
save={handleSave}
validationSchema={LocaleSchema}
data={R.of(locale)}
elements={mainFields(data, configureCoin)}
elements={mainFields(data, onChangeCoin)}
setEditing={onEditingDefault}
forceDisable={isEditingOverrides}
/>
@ -193,7 +202,7 @@ const Locales = ({ name: SCREEN_KEY }) => {
save={saveOverrides}
validationSchema={OverridesSchema}
data={localeOverrides ?? []}
elements={overrides(data, localeOverrides, configureCoin)}
elements={overrides(data, localeOverrides, onChangeCoin)}
disableAdd={R.compose(R.isEmpty, R.difference)(
data?.machines.map(m => m.deviceId) ?? [],
localeOverrides?.map(o => o.machine) ?? []
@ -205,14 +214,8 @@ const Locales = ({ name: SCREEN_KEY }) => {
{wizard && (
<Wizard
coin={R.find(R.propEq('code', wizard))(cryptoCurrencies)}
onClose={() => {
cancelCryptoConfiguration && cancelCryptoConfiguration()
setWizard(false)
}}
save={rawConfig => {
save(toNamespace(namespaces.WALLETS)(rawConfig))
setCancelCryptoConfiguration(null)
}}
onClose={() => setWizard(false)}
save={wizardSave}
error={error?.message}
cryptoCurrencies={cryptoCurrencies}
userAccounts={data?.config?.accounts}

View file

@ -3,14 +3,14 @@ import * as Yup from 'yup'
import Autocomplete from 'src/components/inputs/formik/Autocomplete.js'
const getFields = (getData, names, configureCoin, auxElements = []) => {
const getFields = (getData, names, onChange, auxElements = []) => {
return R.filter(
it => R.includes(it.name, names),
allFields(getData, configureCoin, auxElements)
allFields(getData, onChange, auxElements)
)
}
const allFields = (getData, configureCoin, auxElements = []) => {
const allFields = (getData, onChange, auxElements = []) => {
const getView = (data, code, compare) => it => {
if (!data) return ''
@ -105,8 +105,7 @@ const allFields = (getData, configureCoin, auxElements = []) => {
getLabel: R.path(['code']),
multiple: true,
optionsLimit: null,
onChange: (prev, curr, cancel) =>
configureCoin(R.difference(curr, prev)[0], prev, cancel)
onChange
}
}
]

View file

@ -45,9 +45,7 @@ const GET_INFO = gql`
const Wallet = ({ name: SCREEN_KEY }) => {
const [editingSchema, setEditingSchema] = useState(null)
const [cancelServiceConfiguration, setCancelServiceConfiguration] = useState(
null
)
const [onChangeFunction, setOnChangeFunction] = useState(null)
const [wizard, setWizard] = useState(false)
const { data } = useQuery(GET_INFO)
@ -71,13 +69,16 @@ const Wallet = ({ name: SCREEN_KEY }) => {
const cryptoCurrencies = data?.cryptoCurrencies ?? []
const accounts = data?.accounts ?? []
const configureThirdPartyService = (it, cancel) => {
if (!it) return
const onChange = (previous, current, setValue) => {
if (!current) return setValue(current)
if (!accounts[it]) {
setEditingSchema(schemas[it])
setCancelServiceConfiguration(() => () => cancel())
if (!accounts[current] && schemas[current]) {
setEditingSchema(schemas[current])
setOnChangeFunction(() => () => setValue(current))
return
}
setValue(current)
}
const shouldOverrideEdit = it => {
@ -85,6 +86,15 @@ const Wallet = ({ name: SCREEN_KEY }) => {
return !WalletSchema.isValidSync(namespaced)
}
const wizardSave = it =>
saveAccount({
variables: { accounts: { [editingSchema.code]: it } }
}).then(it => {
onChangeFunction()
setOnChangeFunction(null)
return it
})
return (
<>
<TitleSection title="Wallet Settings" />
@ -100,11 +110,7 @@ const Wallet = ({ name: SCREEN_KEY }) => {
editWidth={174}
save={save}
validationSchema={WalletSchema}
elements={getElements(
cryptoCurrencies,
accountsConfig,
configureThirdPartyService
)}
elements={getElements(cryptoCurrencies, accountsConfig, onChange)}
/>
{wizard && (
<Wizard
@ -122,17 +128,10 @@ const Wallet = ({ name: SCREEN_KEY }) => {
<Modal
title={`Edit ${editingSchema.name}`}
width={478}
handleClose={() => {
cancelServiceConfiguration && cancelServiceConfiguration()
setEditingSchema(null)
}}
handleClose={() => setEditingSchema(null)}
open={true}>
<FormRenderer
save={it =>
saveAccount({
variables: { accounts: { [editingSchema.code]: it } }
})
}
save={wizardSave}
elements={editingSchema.elements}
validationSchema={editingSchema.validationSchema}
value={accounts[editingSchema.code]}

View file

@ -13,12 +13,7 @@ const WalletSchema = Yup.object().shape({
zeroConf: Yup.string().required()
})
const getElements = (
cryptoCurrencies,
accounts,
configureThirdPartyService,
wizard = false
) => {
const getElements = (cryptoCurrencies, accounts, onChange, wizard = false) => {
const widthAdjust = wizard ? 11 : 0
const viewCryptoCurrency = it =>
R.compose(
@ -38,9 +33,6 @@ const getElements = (
filterCoins(it)(filterOptions(option))
)
const onChange = (prev, curr, cancel) =>
configureThirdPartyService(curr, cancel)
return [
{
name: 'id',