Skip to content

Commit 2d4c5d0

Browse files
committed
chore(suite-native): Mobile Trade: types renaming, generic trade input
1 parent 05132b4 commit 2d4c5d0

File tree

58 files changed

+406
-375
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

58 files changed

+406
-375
lines changed

suite-native/forms/src/Form.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import { ReactNode, createContext } from 'react';
22
import { FieldValues, UseFormReturn } from 'react-hook-form';
33

4-
export type { FieldValues, UseFormReturn } from 'react-hook-form';
4+
export type { FieldPath, FieldValues, UseFormReturn } from 'react-hook-form';
55

66
export interface FormProps<TFieldValues extends FieldValues> {
77
children?: ReactNode;

suite-native/module-trading/src/__fixtures__/tradeableAssets.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ import { CryptoId } from 'invity-api';
22

33
import { TokenAddress } from '@suite-common/wallet-types';
44

5-
import { TradeableAsset } from '../types';
5+
import { TradeableAsset } from '../types/general';
66

77
export const btcAsset: TradeableAsset = {
88
symbol: 'btc',
Lines changed: 12 additions & 175 deletions
Original file line numberDiff line numberDiff line change
@@ -1,183 +1,20 @@
1-
import { forwardRef, useCallback, useImperativeHandle, useRef, useState } from 'react';
2-
import { LayoutChangeEvent, Pressable, TextInput, TextInputProps } from 'react-native';
1+
import { forwardRef } from 'react';
2+
import { TextInput } from 'react-native';
33

4-
import { useField } from '@suite-native/forms';
5-
import { prepareNativeStyle, useNativeStyles } from '@trezor/styles';
4+
import type { UseFormReturn } from '@suite-native/forms';
65

76
import { useBuyFormContext } from '../../hooks/buy/useBuyFormContext';
8-
import { truncateDecimals } from '../../utils/general/amountUtils';
7+
import { FocusableFormValues } from '../../types/general';
8+
import { AmountInput, AmountInputProps } from '../general/AmountInput';
99

10-
export type TradingAmountInputProps = {
11-
name: 'fiatValue' | 'cryptoValue';
12-
inputTransformer: (value: string) => string;
13-
maxDecimals?: number;
14-
} & Omit<
15-
TextInputProps,
16-
'value' | 'style' | 'onBlur' | 'onFocus' | 'onChangeText' | 'onLayout' | 'onContentSizeChange'
17-
>;
10+
type InputKey = 'fiatValue' | 'cryptoValue';
1811

19-
const MAX_FONT_SIZE = 34;
20-
const MIN_FONT_SIZE = Math.ceil(MAX_FONT_SIZE / 2);
21-
const FONT_TO_LINE_HEIGHT_RATIO = 1.235;
22-
const FONT_SIZE_SHRINK_THRESHOLD = 20;
23-
const FONT_SIZE_GROW_HYSTERESIS = 20;
12+
export type BuyAmountInputProps = Omit<AmountInputProps<InputKey>, 'form'>;
2413

25-
export const MIN_INPUT_WIDTH = 70;
26-
export const MAX_INPUT_HEIGHT = Math.floor(MAX_FONT_SIZE * FONT_TO_LINE_HEIGHT_RATIO);
14+
export { MAX_INPUT_HEIGHT, MIN_INPUT_WIDTH } from '../general/AmountInput';
2715

28-
const boxStyle = prepareNativeStyle(() => ({
29-
flex: 1,
30-
alignItems: 'flex-end',
31-
paddingLeft: 0,
32-
marginLeft: 0,
33-
overflow: 'visible',
34-
}));
16+
export const BuyAmountInput = forwardRef<TextInput, BuyAmountInputProps>((props, ref) => {
17+
const form = useBuyFormContext() as unknown as UseFormReturn<FocusableFormValues<string>>;
3518

36-
const inputStyle = prepareNativeStyle<{ hasError: boolean; fontSize: number }>(
37-
({ colors, typography }, { hasError, fontSize }) => ({
38-
...typography.body,
39-
color: hasError ? colors.textAlertRed : colors.textDefault,
40-
textAlign: 'right',
41-
fontSize,
42-
lineHeight: Math.floor(fontSize * FONT_TO_LINE_HEIGHT_RATIO),
43-
minWidth: MIN_INPUT_WIDTH,
44-
}),
45-
);
46-
47-
const useInputLayoutControls = () => {
48-
const [availableWidth, setAvailableWidth] = useState(
49-
MIN_INPUT_WIDTH + FONT_SIZE_SHRINK_THRESHOLD,
50-
);
51-
const [fontSize, setFontSize] = useState(MAX_FONT_SIZE);
52-
53-
const handleAvailableWith = useCallback(({ nativeEvent }: LayoutChangeEvent) => {
54-
const { width } = nativeEvent.layout;
55-
setAvailableWidth(width);
56-
}, []);
57-
58-
const handleFontSizeOnContentChange = useCallback(
59-
({ nativeEvent }: LayoutChangeEvent) => {
60-
const contentWidth = nativeEvent.layout.width;
61-
62-
if (contentWidth === 0 || availableWidth === 0) {
63-
setFontSize(MAX_FONT_SIZE);
64-
65-
return;
66-
}
67-
68-
const shrinkThreshold = availableWidth - FONT_SIZE_SHRINK_THRESHOLD;
69-
if (contentWidth > shrinkThreshold) {
70-
const newFontSize = Math.max(
71-
Math.floor((shrinkThreshold / contentWidth) * fontSize),
72-
MIN_FONT_SIZE,
73-
);
74-
setFontSize(newFontSize);
75-
}
76-
77-
const growThreshold = shrinkThreshold - FONT_SIZE_GROW_HYSTERESIS;
78-
if (contentWidth < growThreshold) {
79-
const newFontSize = Math.min(
80-
Math.floor((shrinkThreshold / contentWidth) * fontSize),
81-
MAX_FONT_SIZE,
82-
);
83-
setFontSize(newFontSize);
84-
}
85-
},
86-
[availableWidth, fontSize],
87-
);
88-
89-
return {
90-
fontSize,
91-
onBoxLayout: handleAvailableWith,
92-
onInputLayout: handleFontSizeOnContentChange,
93-
};
94-
};
95-
96-
const useInputFormControls = (
97-
name: 'fiatValue' | 'cryptoValue',
98-
inputTransformer: (value: string) => string,
99-
maxLength: number | undefined,
100-
maxDecimals: number | undefined,
101-
) => {
102-
const { getValues, setValue } = useBuyFormContext();
103-
// do not use `value` from `useField` here, because it does not work properly with `undefined`
104-
const value = getValues(name);
105-
const { onChange, onBlur, hasError } = useField({ name });
106-
107-
const setFocusedValue = useCallback(() => {
108-
setValue('focusedValue', name);
109-
}, [name, setValue]);
110-
111-
const handleTextChange = useCallback(
112-
(text: string) => {
113-
let transformedText = inputTransformer(text);
114-
transformedText = truncateDecimals(transformedText, maxDecimals);
115-
transformedText = transformedText.slice(0, maxLength);
116-
117-
return onChange(transformedText === '' ? undefined : transformedText);
118-
},
119-
[maxLength, maxDecimals, inputTransformer, onChange],
120-
);
121-
122-
const clearFocusedValueAndBlur = useCallback(() => {
123-
onBlur();
124-
setValue('focusedValue', undefined);
125-
}, [onBlur, setValue]);
126-
127-
return {
128-
value,
129-
hasError,
130-
onFocus: setFocusedValue,
131-
onChangeText: handleTextChange,
132-
onBlur: clearFocusedValueAndBlur,
133-
};
134-
};
135-
136-
export const BuyAmountInput = forwardRef<TextInput, TradingAmountInputProps>(
137-
({ name, inputTransformer, maxLength, maxDecimals, onPress, ...inputProps }, ref) => {
138-
const innerRef = useRef<TextInput>(null);
139-
useImperativeHandle(ref, () => innerRef.current!, []);
140-
141-
const { applyStyle, utils } = useNativeStyles();
142-
const { fontSize, onBoxLayout, onInputLayout } = useInputLayoutControls();
143-
const { value, hasError, onFocus, onChangeText, onBlur } = useInputFormControls(
144-
name,
145-
inputTransformer,
146-
maxLength,
147-
maxDecimals,
148-
);
149-
150-
const focusInputCallback = useCallback(() => {
151-
innerRef.current?.focus();
152-
}, [innerRef]);
153-
const wrapperOnPress = onPress ?? focusInputCallback;
154-
155-
// Note: it would be nice to use `onContentSizeChange` instead of `onLayout` on `<Pressable />` once this bug is fixed https://github.com/facebook/react-native/issues/29702.
156-
// It would also allow us to remove `innerRef` and `useImperativeHandle` logic.
157-
return (
158-
<Pressable
159-
style={applyStyle(boxStyle)}
160-
onLayout={onBoxLayout}
161-
testID="@trading/amountInput/wrapper"
162-
onPress={wrapperOnPress}
163-
>
164-
<TextInput
165-
ref={innerRef}
166-
style={applyStyle(inputStyle, { hasError, fontSize })}
167-
keyboardType="decimal-pad"
168-
inputMode="decimal"
169-
placeholder="0.0"
170-
placeholderTextColor={utils.colors.textDisabled}
171-
value={value ?? ''}
172-
maxLength={maxLength}
173-
onChangeText={onChangeText}
174-
onFocus={onFocus}
175-
onBlur={onBlur}
176-
onLayout={onInputLayout}
177-
onPress={onPress}
178-
{...inputProps}
179-
/>
180-
</Pressable>
181-
);
182-
},
183-
);
19+
return <AmountInput ref={ref} form={form} {...props} />;
20+
});

suite-native/module-trading/src/components/buy/BuyCountryOfResidencePicker.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ import { useTranslate } from '@suite-native/intl';
44

55
import { useBuyFormContext } from '../../hooks/buy/useBuyFormContext';
66
import { useSheetControls } from '../../hooks/general/useSheetControls';
7-
import { Country } from '../../types';
7+
import { Country } from '../../types/general';
88
import { CountrySheet } from '../general/CountrySheet/CountrySheet';
99
import { OverviewRow } from '../general/OverviewRow';
1010

suite-native/module-trading/src/components/buy/BuyFormFieldErrorBadge.tsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,10 @@
11
import { Badge } from '@suite-native/atoms';
22
import { useField } from '@suite-native/forms';
33

4-
import { TradingBuyFormValues } from '../../types';
4+
import { BuyFormValues } from '../../types/buy';
55

66
export type BuyFormFieldErrorBadgeProps = {
7-
fieldName: keyof TradingBuyFormValues;
7+
fieldName: keyof BuyFormValues;
88
};
99

1010
export const BuyFormFieldErrorBadge = ({ fieldName }: BuyFormFieldErrorBadgeProps) => {

suite-native/module-trading/src/components/buy/BuyTradeableAssetPicker.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ import { BuyTradeableAssetsSheet } from './BuyTradeableAssetsSheet';
1010
import { useBuyFormContext } from '../../hooks/buy/useBuyFormContext';
1111
import { useSheetControls } from '../../hooks/general/useSheetControls';
1212
import { selectBuyTradeableAssetsSorted } from '../../selectors/buySelectors';
13-
import { TradeableAsset } from '../../types';
13+
import { TradeableAsset } from '../../types/general';
1414
import { SelectTradeableAssetButton } from '../general/SelectTradeableAssetButton';
1515

1616
const ASSET_PICKER_TEST_ID = '@trading/buy/asset-button';

suite-native/module-trading/src/components/buy/__tests__/BuyAlert.comp.test.tsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,11 +6,11 @@ import {
66
} from '@suite-native/test-utils';
77

88
import { useBuyForm } from '../../../hooks/buy/useBuyForm';
9-
import { TradingBuyForm } from '../../../types';
9+
import { BuyForm } from '../../../types/buy';
1010
import { BuyAlert } from '../BuyAlert';
1111

1212
describe('BuyAlert', () => {
13-
let form: TradingBuyForm;
13+
let form: BuyForm;
1414

1515
const renderFormHook = () => renderHookWithStoreProviderAsync(() => useBuyForm());
1616

suite-native/module-trading/src/components/buy/__tests__/BuyAmountInput.comp.test.tsx

Lines changed: 3 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -9,15 +9,12 @@ import {
99
import { paletteV1 } from '@trezor/theme';
1010

1111
import { useBuyForm } from '../../../hooks/buy/useBuyForm';
12-
import { TradingBuyForm } from '../../../types';
13-
import { BuyAmountInput, TradingAmountInputProps } from '../BuyAmountInput';
12+
import { BuyForm } from '../../../types/buy';
13+
import { BuyAmountInput, BuyAmountInputProps } from '../BuyAmountInput';
1414

1515
describe('BuyAmountInput', () => {
1616
const renderBuyFormHook = () => renderHookWithStoreProviderAsync(() => useBuyForm());
17-
const renderTradingAmountInput = (
18-
props: Partial<TradingAmountInputProps>,
19-
form: TradingBuyForm,
20-
) =>
17+
const renderTradingAmountInput = (props: Partial<BuyAmountInputProps>, form: BuyForm) =>
2118
renderWithBasicProvider(
2219
<Form form={form}>
2320
<BuyAmountInput

suite-native/module-trading/src/components/buy/__tests__/BuyAssetNetworkInfo.comp.test.tsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,11 +7,11 @@ import {
77

88
import { btcAsset, ethOnBaseAsset, usdcAsset } from '../../../__fixtures__/tradeableAssets';
99
import { useBuyForm } from '../../../hooks/buy/useBuyForm';
10-
import { TradingBuyForm } from '../../../types';
10+
import { BuyForm } from '../../../types/buy';
1111
import { BuyAssetNetworkInfo } from '../BuyAssetNetworkInfo';
1212

1313
describe('BuyAssetNetworkInfo', () => {
14-
let form: TradingBuyForm;
14+
let form: BuyForm;
1515

1616
const renderNetworkIconForToken = () =>
1717
renderWithBasicProvider(

0 commit comments

Comments
 (0)