From 33a9a281146338c143ffe3a5fe57a7ee554f7964 Mon Sep 17 00:00:00 2001 From: Szymon Lesisz Date: Wed, 9 Jan 2019 14:31:40 +0100 Subject: [PATCH] add destination tag to Ripple send form --- src/actions/ripple/SendFormActions.js | 19 ++++++ .../ripple/SendFormValidationActions.js | 14 +++++ src/reducers/SendFormRippleReducer.js | 2 + .../ripple/components/AdvancedForm/index.js | 59 +++++++++++++++++-- 4 files changed, 89 insertions(+), 5 deletions(-) diff --git a/src/actions/ripple/SendFormActions.js b/src/actions/ripple/SendFormActions.js index d737f53c..cb146d1b 100644 --- a/src/actions/ripple/SendFormActions.js +++ b/src/actions/ripple/SendFormActions.js @@ -244,6 +244,23 @@ export const onFeeChange = (fee: string): ThunkAction => (dispatch: Dispatch, ge }); }; +/* +* Called from UI on "advanced / destination tag" field change +*/ +export const onDestinationTagChange = (destinationTag: string): ThunkAction => (dispatch: Dispatch, getState: GetState): void => { + const state: State = getState().sendFormRipple; + dispatch({ + type: SEND.CHANGE, + networkType: 'ripple', + state: { + ...state, + untouched: false, + touched: { ...state.touched, destinationTag: true }, + destinationTag, + }, + }); +}; + /* * Called from UI from "send" button */ @@ -279,6 +296,7 @@ export const onSend = (): AsyncAction => async (dispatch: Dispatch, getState: Ge payment: { amount, destination: currentState.address, + destinationTag: currentState.destinationTag.length ? parseInt(currentState.destinationTag, 10) : undefined, }, }, }); @@ -346,5 +364,6 @@ export default { onFeeLevelChange, updateFeeLevels, onFeeChange, + onDestinationTagChange, onSend, }; \ No newline at end of file diff --git a/src/actions/ripple/SendFormValidationActions.js b/src/actions/ripple/SendFormValidationActions.js index db5131fa..b89bce95 100644 --- a/src/actions/ripple/SendFormValidationActions.js +++ b/src/actions/ripple/SendFormValidationActions.js @@ -73,6 +73,7 @@ export const validation = (): PayloadAction => (dispatch: Dispatch, getSt state = dispatch(addressLabel(state)); state = dispatch(amountValidation(state)); state = dispatch(feeValidation(state)); + state = dispatch(destinationTagValidation(state)); return state; }; @@ -229,6 +230,19 @@ export const feeValidation = ($state: State): PayloadAction => (dispatch: } return state; }; +/* +* Destination Tag value validation +*/ +export const destinationTagValidation = ($state: State): PayloadAction => (): State => { + const state = { ...$state }; + if (!state.touched.destinationTag) return state; + + const { destinationTag } = state; + if (destinationTag.length > 0 && !destinationTag.match(ABS_RE)) { + state.errors.destinationTag = 'Destination tag must be an absolute number'; + } + return state; +}; /* diff --git a/src/reducers/SendFormRippleReducer.js b/src/reducers/SendFormRippleReducer.js index eaedcfa1..f56083bc 100644 --- a/src/reducers/SendFormRippleReducer.js +++ b/src/reducers/SendFormRippleReducer.js @@ -27,6 +27,7 @@ export type State = { fee: string; feeNeedsUpdate: boolean; sequence: string; + destinationTag: string; total: string; errors: {[k: string]: string}; @@ -56,6 +57,7 @@ export const initialState: State = { fee: '0', feeNeedsUpdate: false, sequence: '0', + destinationTag: '', total: '0', errors: {}, diff --git a/src/views/Wallet/views/Account/Send/ripple/components/AdvancedForm/index.js b/src/views/Wallet/views/Account/Send/ripple/components/AdvancedForm/index.js index 578b0d2c..7890bac7 100644 --- a/src/views/Wallet/views/Account/Send/ripple/components/AdvancedForm/index.js +++ b/src/views/Wallet/views/Account/Send/ripple/components/AdvancedForm/index.js @@ -35,7 +35,7 @@ const AdvancedSettingsWrapper = styled.div` border-top: 1px solid ${colors.DIVIDER}; `; -const GasInputRow = styled.div` +const InputRow = styled.div` width: 100%; display: flex; @@ -44,7 +44,7 @@ const GasInputRow = styled.div` } `; -const GasInput = styled(Input)` +const StyledInput = styled(Input)` /* min-height: 85px; */ padding-bottom: 28px; &:first-child { @@ -75,6 +75,17 @@ const getFeeInputState = (feeErrors: string, feeWarnings: string): string => { return state; }; +const getDestinationTagInputState = (errors: string, warnings: string): string => { + let state = ''; + if (warnings && !errors) { + state = 'warning'; + } + if (errors) { + state = 'error'; + } + return state; +}; + const Left = styled.div` display: flex; align-items: center; @@ -91,15 +102,17 @@ const AdvancedForm = (props: Props) => { warnings, infos, fee, + destinationTag, } = props.sendForm; const { onFeeChange, + onDestinationTagChange, } = props.sendFormActions; return ( - - + { value={fee} onChange={event => onFeeChange(event.target.value)} /> - + + + + + + Destination tag + + An arbitrary unsigned 32-bit integer that identifies a reason for payment or a non-Ripple account. + + )} + maxWidth={410} + readMoreLink="https://developers.ripple.com/rippleapi-reference.html#payment" + placement="top" + > + + + + + )} + bottomText={errors.destinationTag || warnings.destinationTag || infos.destinationTag} + value={destinationTag} + onChange={event => onDestinationTagChange(event.target.value)} + /> + { props.children }