mirror of
https://github.com/trezor/trezor-wallet
synced 2024-11-27 10:48:22 +00:00
l10n support for xrp validation
This commit is contained in:
parent
8d1c01bc61
commit
934df38064
@ -6,6 +6,8 @@ import { findDevice, getPendingAmount } from 'reducers/utils';
|
||||
import { toDecimalAmount } from 'utils/formatUtils';
|
||||
import { toFiatCurrency } from 'utils/fiatConverter';
|
||||
import * as validators from 'utils/validators';
|
||||
import l10nMessages from 'views/Wallet/views/Account/Send/validation.messages';
|
||||
import l10nCommonMessages from 'views/common.messages';
|
||||
|
||||
import type {
|
||||
Dispatch,
|
||||
@ -150,11 +152,11 @@ const addressValidation = ($state: State): PayloadAction<State> => (
|
||||
const { address } = state;
|
||||
|
||||
if (address.length < 1) {
|
||||
state.errors.address = 'Address is not set';
|
||||
state.errors.address = l10nMessages.TR_ADDRESS_IS_NOT_SET;
|
||||
} else if (!AddressValidator.validate(address, 'XRP')) {
|
||||
state.errors.address = 'Address is not valid';
|
||||
state.errors.address = l10nMessages.TR_ADDRESS_IS_NOT_VALID;
|
||||
} else if (address.toLowerCase() === account.descriptor.toLowerCase()) {
|
||||
state.errors.address = 'Cannot send to myself';
|
||||
state.errors.address = l10nMessages.TR_CANNOT_SEND_TO_MYSELF;
|
||||
}
|
||||
return state;
|
||||
};
|
||||
@ -226,9 +228,13 @@ const addressLabel = ($state: State): PayloadAction<State> => (
|
||||
currentNetworkAccount.deviceState
|
||||
);
|
||||
if (device) {
|
||||
state.infos.address = `${
|
||||
device.instanceLabel
|
||||
} Account #${currentNetworkAccount.index + 1}`;
|
||||
state.infos.address = {
|
||||
...l10nCommonMessages.TR_DEVICE_LABEL_ACCOUNT_HASH,
|
||||
values: {
|
||||
deviceLabel: device.instanceLabel,
|
||||
number: currentNetworkAccount.index + 1,
|
||||
},
|
||||
};
|
||||
}
|
||||
} else {
|
||||
// corner-case: the same derivation path is used on different networks
|
||||
@ -241,11 +247,14 @@ const addressLabel = ($state: State): PayloadAction<State> => (
|
||||
const { networks } = getState().localStorage.config;
|
||||
const otherNetwork = networks.find(c => c.shortcut === otherNetworkAccount.network);
|
||||
if (device && otherNetwork) {
|
||||
state.warnings.address = `Looks like it's ${
|
||||
device.instanceLabel
|
||||
} Account #${otherNetworkAccount.index + 1} address of ${
|
||||
otherNetwork.name
|
||||
} network`;
|
||||
state.warnings.address = {
|
||||
...l10nCommonMessages.TR_LOOKS_LIKE_IT_IS_DEVICE_LABEL,
|
||||
values: {
|
||||
deviceLabel: device.instanceLabel,
|
||||
number: otherNetworkAccount.index + 1,
|
||||
network: otherNetwork.name,
|
||||
},
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -268,19 +277,22 @@ const amountValidation = ($state: State): PayloadAction<State> => (
|
||||
|
||||
const { amount } = state;
|
||||
if (amount.length < 1) {
|
||||
state.errors.amount = 'Amount is not set';
|
||||
state.errors.amount = l10nMessages.TR_AMOUNT_IS_NOT_SET;
|
||||
} else if (amount.length > 0 && !validators.isNumber(amount)) {
|
||||
state.errors.amount = 'Amount is not a number';
|
||||
state.errors.amount = l10nMessages.TR_AMOUNT_IS_NOT_A_NUMBER;
|
||||
} else {
|
||||
const pendingAmount: BigNumber = getPendingAmount(pending, state.networkSymbol);
|
||||
if (!validators.hasDecimals(state.amount, 6)) {
|
||||
state.errors.amount = 'Maximum 6 decimals allowed';
|
||||
state.errors.amount = {
|
||||
...l10nMessages.TR_MAXIMUM_DECIMALS_ALLOWED,
|
||||
values: { decimals: 6 },
|
||||
};
|
||||
} else if (
|
||||
new BigNumber(state.total).isGreaterThan(
|
||||
new BigNumber(account.balance).minus(pendingAmount)
|
||||
)
|
||||
) {
|
||||
state.errors.amount = 'Not enough funds';
|
||||
state.errors.amount = l10nMessages.TR_NOT_ENOUGH_FUNDS;
|
||||
}
|
||||
}
|
||||
|
||||
@ -288,15 +300,23 @@ const amountValidation = ($state: State): PayloadAction<State> => (
|
||||
!state.errors.amount &&
|
||||
new BigNumber(account.balance).minus(state.total).lt(account.reserve)
|
||||
) {
|
||||
state.errors.amount = `Not enough funds. Reserved amount for this account is ${
|
||||
account.reserve
|
||||
} ${state.networkSymbol}`;
|
||||
state.errors.amount = {
|
||||
...l10nMessages.TR_NOT_ENOUGH_FUNDS_RESERVED_AMOUNT,
|
||||
values: {
|
||||
reservedAmount: account.reserve,
|
||||
networkSymbol: state.networkSymbol,
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
if (!state.errors.amount && new BigNumber(state.amount).lt(state.minAmount)) {
|
||||
state.errors.amount = `Amount is too low. Minimum amount for creating a new account is ${
|
||||
state.minAmount
|
||||
} ${state.networkSymbol}`;
|
||||
state.errors.amount = {
|
||||
...l10nMessages.TR_AMOUNT_IS_TOO_LOW_MINIMUM_AMOUNT_FOR_CREATING,
|
||||
values: {
|
||||
reservedAmount: state.minAmount,
|
||||
networkSymbol: state.networkSymbol,
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
return state;
|
||||
@ -317,15 +337,15 @@ export const feeValidation = ($state: State): PayloadAction<State> => (
|
||||
|
||||
const { fee } = state;
|
||||
if (fee.length < 1) {
|
||||
state.errors.fee = 'Fee is not set';
|
||||
state.errors.fee = l10nMessages.TR_FEE_IS_NOT_SET;
|
||||
} else if (fee.length > 0 && !validators.isAbs(fee)) {
|
||||
state.errors.fee = 'Fee must be an absolute number';
|
||||
state.errors.fee = l10nMessages.TR_FEE_MUST_ME_AN_ABSOLUT_NUMBER;
|
||||
} else {
|
||||
const gl: BigNumber = new BigNumber(fee);
|
||||
if (gl.isLessThan(network.fee.minFee)) {
|
||||
state.errors.fee = 'Fee is below recommended';
|
||||
state.errors.fee = l10nMessages.TR_FEE_IS_BELOW_RECOMMENDED;
|
||||
} else if (gl.isGreaterThan(network.fee.maxFee)) {
|
||||
state.errors.fee = 'Fee is above recommended';
|
||||
state.errors.fee = l10nMessages.TR_FEE_IS_ABOVE_RECOMMENDED;
|
||||
}
|
||||
}
|
||||
return state;
|
||||
@ -340,11 +360,11 @@ export const destinationTagValidation = ($state: State): PayloadAction<State> =>
|
||||
const { destinationTag } = state;
|
||||
|
||||
if (destinationTag.length > 0 && !validators.isAbs(destinationTag)) {
|
||||
state.errors.destinationTag = 'Destination tag must be an absolute number';
|
||||
state.errors.destinationTag = l10nMessages.TR_DESTINATION_TAG_MUST_BE_AN_ABSOLUTE;
|
||||
}
|
||||
|
||||
if (parseInt(destinationTag, 10) > U_INT_32) {
|
||||
state.errors.destinationTag = 'Number is too big';
|
||||
state.errors.destinationTag = l10nMessages.TR_DESTINATION_TAG_IS_NOT_VALID;
|
||||
}
|
||||
|
||||
return state;
|
||||
@ -386,9 +406,16 @@ export const getFeeLevels = (
|
||||
const { network } = getState().selectedAccount;
|
||||
if (!network) return []; // flowtype fallback
|
||||
|
||||
const l10nFeeMap = {
|
||||
Low: l10nCommonMessages.TR_LOW_FEE,
|
||||
Normal: l10nCommonMessages.TR_NORMAL_FEE,
|
||||
High: l10nCommonMessages.TR_HIGH_FEE,
|
||||
};
|
||||
|
||||
// map BlockchainFeeLevel to SendFormReducer FeeLevel
|
||||
const levels = feeLevels.map(level => ({
|
||||
value: level.name,
|
||||
localizedValue: l10nFeeMap[level.value] || level.value,
|
||||
fee: level.value,
|
||||
label: `${toDecimalAmount(level.value, network.decimals)} ${network.symbol}`,
|
||||
}));
|
||||
@ -398,11 +425,13 @@ export const getFeeLevels = (
|
||||
selected && selected.value === 'Custom'
|
||||
? {
|
||||
value: 'Custom',
|
||||
localizedValue: l10nCommonMessages.TR_CUSTOM_FEE,
|
||||
fee: selected.fee,
|
||||
label: `${toDecimalAmount(selected.fee, network.decimals)} ${network.symbol}`,
|
||||
}
|
||||
: {
|
||||
value: 'Custom',
|
||||
localizedValue: l10nCommonMessages.TR_CUSTOM_FEE,
|
||||
fee: '0',
|
||||
label: '',
|
||||
};
|
||||
|
@ -3,12 +3,13 @@
|
||||
import * as SEND from 'actions/constants/send';
|
||||
import * as ACCOUNT from 'actions/constants/account';
|
||||
|
||||
import type { Action } from 'flowtype';
|
||||
import type { Action, MessageDescriptor } from 'flowtype';
|
||||
|
||||
export type FeeLevel = {
|
||||
label: string,
|
||||
fee: string,
|
||||
value: string,
|
||||
localizedValue?: MessageDescriptor,
|
||||
};
|
||||
|
||||
export type State = {
|
||||
@ -33,9 +34,9 @@ export type State = {
|
||||
destinationTag: string,
|
||||
total: string,
|
||||
|
||||
errors: { [k: string]: string },
|
||||
warnings: { [k: string]: string },
|
||||
infos: { [k: string]: string },
|
||||
errors: { [k: string]: MessageDescriptor },
|
||||
warnings: { [k: string]: MessageDescriptor },
|
||||
infos: { [k: string]: MessageDescriptor },
|
||||
|
||||
sending: boolean,
|
||||
};
|
||||
|
@ -52,7 +52,7 @@ const TooltipContainer = styled.div`
|
||||
margin-left: 6px;
|
||||
`;
|
||||
|
||||
const getFeeInputState = (feeErrors: string, feeWarnings: string): ?string => {
|
||||
const getFeeInputState = (feeErrors: boolean, feeWarnings: boolean): ?string => {
|
||||
let state = null;
|
||||
if (feeWarnings && !feeErrors) {
|
||||
state = 'warning';
|
||||
@ -63,7 +63,7 @@ const getFeeInputState = (feeErrors: string, feeWarnings: string): ?string => {
|
||||
return state;
|
||||
};
|
||||
|
||||
const getDestinationTagInputState = (errors: string, warnings: string): ?string => {
|
||||
const getDestinationTagInputState = (errors: boolean, warnings: boolean): ?string => {
|
||||
let state = null;
|
||||
if (warnings && !errors) {
|
||||
state = 'warning';
|
||||
@ -90,7 +90,7 @@ const AdvancedForm = (props: Props) => {
|
||||
<AdvancedSettingsWrapper>
|
||||
<InputRow>
|
||||
<StyledInput
|
||||
state={getFeeInputState(errors.fee, warnings.fee)}
|
||||
state={getFeeInputState(!!errors.fee, !!warnings.fee)}
|
||||
autoComplete="off"
|
||||
autoCorrect="off"
|
||||
autoCapitalize="off"
|
||||
@ -125,7 +125,13 @@ const AdvancedForm = (props: Props) => {
|
||||
</Left>
|
||||
</InputLabelWrapper>
|
||||
}
|
||||
bottomText={errors.fee || warnings.fee || infos.fee}
|
||||
bottomText={
|
||||
<>
|
||||
{(errors.fee && <FormattedMessage {...errors.fee} />) ||
|
||||
(warnings.fee && <FormattedMessage {...warnings.fee} />) ||
|
||||
(infos.fee && <FormattedMessage {...infos.fee} />)}
|
||||
</>
|
||||
}
|
||||
value={fee}
|
||||
onChange={event => onFeeChange(event.target.value)}
|
||||
/>
|
||||
@ -134,8 +140,8 @@ const AdvancedForm = (props: Props) => {
|
||||
<InputRow>
|
||||
<StyledInput
|
||||
state={getDestinationTagInputState(
|
||||
errors.destinationTag,
|
||||
warnings.destinationTag
|
||||
!!errors.destinationTag,
|
||||
!!warnings.destinationTag
|
||||
)}
|
||||
autoComplete="off"
|
||||
autoCorrect="off"
|
||||
@ -172,7 +178,17 @@ const AdvancedForm = (props: Props) => {
|
||||
</InputLabelWrapper>
|
||||
}
|
||||
bottomText={
|
||||
errors.destinationTag || warnings.destinationTag || infos.destinationTag
|
||||
<>
|
||||
{(errors.destinationTag && (
|
||||
<FormattedMessage {...errors.destinationTag} />
|
||||
)) ||
|
||||
(warnings.destinationTag && (
|
||||
<FormattedMessage {...warnings.destinationTag} />
|
||||
)) ||
|
||||
(infos.destinationTag && (
|
||||
<FormattedMessage {...infos.destinationTag} />
|
||||
))}
|
||||
</>
|
||||
}
|
||||
value={destinationTag}
|
||||
onChange={event => onDestinationTagChange(event.target.value)}
|
||||
|
@ -202,8 +202,8 @@ const StyledIcon = styled(Icon)`
|
||||
// render helpers
|
||||
const getAddressInputState = (
|
||||
address: string,
|
||||
addressErrors: string,
|
||||
addressWarnings: string
|
||||
addressErrors: boolean,
|
||||
addressWarnings: boolean
|
||||
): ?string => {
|
||||
let state = null;
|
||||
if (address && !addressErrors) {
|
||||
@ -218,7 +218,7 @@ const getAddressInputState = (
|
||||
return state;
|
||||
};
|
||||
|
||||
const getAmountInputState = (amountErrors: string, amountWarnings: string): ?string => {
|
||||
const getAmountInputState = (amountErrors: boolean, amountWarnings: boolean): ?string => {
|
||||
let state = null;
|
||||
if (amountWarnings && !amountErrors) {
|
||||
state = 'warning';
|
||||
@ -317,13 +317,19 @@ const AccountSend = (props: Props) => {
|
||||
</Title>
|
||||
<InputRow>
|
||||
<Input
|
||||
state={getAddressInputState(address, errors.address, warnings.address)}
|
||||
state={getAddressInputState(address, !!errors.address, !!warnings.address)}
|
||||
autoComplete="off"
|
||||
autoCorrect="off"
|
||||
autoCapitalize="off"
|
||||
spellCheck="false"
|
||||
topLabel={props.intl.formatMessage(l10nCommonMessages.TR_ADDRESS)}
|
||||
bottomText={errors.address || warnings.address || infos.address}
|
||||
bottomText={
|
||||
<>
|
||||
{(errors.address && <FormattedMessage {...errors.address} />) ||
|
||||
(warnings.address && <FormattedMessage {...warnings.address} />) ||
|
||||
(infos.address && <FormattedMessage {...infos.address} />)}
|
||||
</>
|
||||
}
|
||||
value={address}
|
||||
onChange={event => onAddressChange(event.target.value)}
|
||||
sideAddons={[
|
||||
@ -335,7 +341,7 @@ const AccountSend = (props: Props) => {
|
||||
</InputRow>
|
||||
<AmountRow>
|
||||
<Input
|
||||
state={getAmountInputState(errors.amount, warnings.amount)}
|
||||
state={getAmountInputState(!!errors.amount, !!warnings.amount)}
|
||||
autoComplete="off"
|
||||
autoCorrect="off"
|
||||
autoCapitalize="off"
|
||||
@ -357,7 +363,13 @@ const AccountSend = (props: Props) => {
|
||||
}
|
||||
value={amount}
|
||||
onChange={event => onAmountChange(event.target.value)}
|
||||
bottomText={errors.amount || warnings.amount || infos.amount}
|
||||
bottomText={
|
||||
<>
|
||||
{(errors.amount && <FormattedMessage {...errors.amount} />) ||
|
||||
(warnings.amount && <FormattedMessage {...warnings.amount} />) ||
|
||||
(infos.amount && <FormattedMessage {...infos.amount} />)}
|
||||
</>
|
||||
}
|
||||
sideAddons={[
|
||||
<SetMaxAmountButton key="icon" onClick={() => onSetMax()} isWhite={!setMax}>
|
||||
{!setMax && (
|
||||
@ -385,7 +397,7 @@ const AccountSend = (props: Props) => {
|
||||
<LocalAmountWrapper>
|
||||
<EqualsSign>=</EqualsSign>
|
||||
<LocalAmountInput
|
||||
state={getAmountInputState(errors.amount, warnings.amount)}
|
||||
state={getAmountInputState(!!errors.amount, !!warnings.amount)}
|
||||
autoComplete="off"
|
||||
autoCorrect="off"
|
||||
autoCapitalize="off"
|
||||
|
@ -91,6 +91,44 @@ const definedMessages: Messages = defineMessages({
|
||||
id: 'TR_DATA_IS_NOT_VALID_HEX',
|
||||
defaultMessage: 'Data is not valid hexadecimal',
|
||||
},
|
||||
TR_CANNOT_SEND_TO_MYSELF: {
|
||||
id: 'TR_CANNOT_SEND_TO_MYSELF',
|
||||
defaultMessage: 'Cannot send to myself',
|
||||
},
|
||||
TR_NOT_ENOUGH_FUNDS_RESERVED_AMOUNT: {
|
||||
id: 'TR_NOT_ENOUGH_FUNDS_RESERVED_AMOUNT',
|
||||
defaultMessage:
|
||||
'Not enough funds. Reserved amount for this account is {reservedAmount} {networkSymbol}',
|
||||
},
|
||||
TR_AMOUNT_IS_TOO_LOW_MINIMUM_AMOUNT_FOR_CREATING: {
|
||||
id: 'TR_AMOUNT_IS_TOO_LOW_MINIMUM_AMOUNT_FOR_CREATING',
|
||||
defaultMessage:
|
||||
'Amount is too low. Minimum amount for creating a new account is {minimalAmount} {networkSymbol}',
|
||||
},
|
||||
TR_FEE_IS_NOT_SET: {
|
||||
id: 'TR_FEE_IS_NOT_SET',
|
||||
defaultMessage: 'Fee is not set',
|
||||
},
|
||||
TR_FEE_MUST_ME_AN_ABSOLUT_NUMBER: {
|
||||
id: 'TR_FEE_MUST_ME_AN_ABSOLUT_NUMBER',
|
||||
defaultMessage: 'Fee must be an absolute number',
|
||||
},
|
||||
TR_FEE_IS_BELOW_RECOMMENDED: {
|
||||
id: 'TR_FEE_IS_BELOW_RECOMMENDED',
|
||||
defaultMessage: 'Fee is below recommended',
|
||||
},
|
||||
TR_FEE_IS_ABOVE_RECOMMENDED: {
|
||||
id: 'TR_FEE_IS_ABOVE_RECOMMENDED',
|
||||
defaultMessage: 'Fee is above recommended',
|
||||
},
|
||||
TR_DESTINATION_TAG_MUST_BE_AN_ABSOLUTE: {
|
||||
id: 'TR_DESTINATION_TAG_MUST_BE_AN_ABSOLUTE',
|
||||
defaultMessage: 'Destination tag must be an absolute number',
|
||||
},
|
||||
TR_DESTINATION_TAG_IS_NOT_VALID: {
|
||||
id: 'TR_DESTINATION_TAG_IS_NOT_VALID',
|
||||
defaultMessage: 'Destination tag is not valid',
|
||||
},
|
||||
});
|
||||
|
||||
export default definedMessages;
|
||||
|
@ -23,8 +23,9 @@ const definedMessages: Messages = defineMessages({
|
||||
},
|
||||
TR_LOOKS_LIKE_IT_IS_DEVICE_LABEL: {
|
||||
id: 'TR_LOOKS_LIKE_IT_IS_DEVICE_LABEL',
|
||||
defaultMessage: 'Looks like it is {deviceLabel} Account #{number} of {network}',
|
||||
description: 'Example: Looks like it is My Trezor Account #1 of ETH',
|
||||
defaultMessage:
|
||||
'Looks like it is {deviceLabel} Account #{number} address of {network} network',
|
||||
description: 'Example: Looks like it is My Trezor Account #1 address of ETH network',
|
||||
},
|
||||
TR_IMPORTED_ACCOUNT_HASH: {
|
||||
id: 'TR_IMPORTED_ACCOUNT_HASH',
|
||||
|
Loading…
Reference in New Issue
Block a user