Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Simplify SwapInputController animated reaction logic for responding to input value changes #5923

Merged
merged 8 commits into from
Jul 15, 2024
165 changes: 45 additions & 120 deletions src/__swaps__/screens/Swap/hooks/useSwapInputsController.ts
Original file line number Diff line number Diff line change
Expand Up @@ -547,64 +547,35 @@ export function useSwapInputsController({
{ leading: false, trailing: true }
);

const onTypedNumber = useDebouncedCallback(
(amount: number, inputKey: inputKeys, preserveAmount = true) => {
lastTypedInput.value = inputKey;

if (amount > 0) {
const updateWorklet = () => {
'worklet';
// If the user enters a new inputAmount, update the slider position ahead of the quote fetch, because
// we can derive the slider position directly from the entered amount.
if (inputKey === 'inputAmount') {
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This code was only used for when the inputKey is the inputAmount and is already done on lines 741-752 before the function is called, so it is redundant.

const inputAssetBalance = internalSelectedInputAsset.value?.maxSwappableAmount || '0';
if (equalWorklet(inputAssetBalance, 0)) {
sliderXPosition.value = withSpring(0, snappySpringConfig);
} else {
const updatedSliderPosition = clamp(Number(divWorklet(amount, inputAssetBalance)) * SLIDER_WIDTH, 0, SLIDER_WIDTH);
sliderXPosition.value = withSpring(updatedSliderPosition, snappySpringConfig);
}
}
fetchQuoteAndAssetPrices();
};
const resetValuesToZeroWorklet = (inputKey?: inputKeys) => {
'worklet';
quoteFetchingInterval.stop();
if (isFetching.value) isFetching.value = false;
if (isQuoteStale.value !== 0) isQuoteStale.value = 0;

const resetValues = {
inputAmount: 0,
inputNativeValue: 0,
outputAmount: 0,
outputNativeValue: 0,
};

if (!inputKey) {
inputValues.modify(values => ({ ...values, ...resetValues }));
return;
}

runOnUI(updateWorklet)();
} else {
const resetValuesToZero = () => {
if (isFetching.value) isFetching.value = false;
if (isQuoteStale.value !== 0) isQuoteStale.value = 0;

const updateWorklet = () => {
'worklet';
const keysToReset = ['inputAmount', 'inputNativeValue', 'outputAmount', 'outputNativeValue'];
const updatedValues = keysToReset.reduce(
(acc, key) => {
const castedKey = key as keyof typeof inputValues.value;
acc[castedKey] = castedKey === inputKey && preserveAmount ? inputValues.value[castedKey] : 0;
return acc;
},
{} as Partial<typeof inputValues.value>
);
inputValues.modify(values => {
return {
...values,
...updatedValues,
};
});
sliderXPosition.value = withSpring(0, snappySpringConfig);
isQuoteStale.value = 0;
quoteFetchingInterval.stop();
};
const inputKeyValue = inputValues.value[inputKey];
const hasDecimal = inputKeyValue.toString().includes('.');

runOnUI(updateWorklet)();
};
inputValues.modify(values => ({
...values,
...resetValues,
[inputKey]: hasDecimal ? inputKeyValue : 0,
}));

resetValuesToZero();
}
},
300,
{ leading: false, trailing: true }
);
sliderXPosition.value = withSpring(0, snappySpringConfig);
};

const debouncedFetchQuote = useDebouncedCallback(
() => {
Expand Down Expand Up @@ -695,20 +666,7 @@ export function useSwapInputsController({
if (inputMethod.value === 'slider' && internalSelectedInputAsset.value && current.sliderXPosition !== previous.sliderXPosition) {
// If the slider position changes
if (percentageToSwap.value === 0) {
// If the change set the slider position to 0
quoteFetchingInterval.stop();
isQuoteStale.value = 0;
isFetching.value = false;

inputValues.modify(values => {
return {
...values,
inputAmount: 0,
inputNativeValue: 0,
outputAmount: 0,
outputNativeValue: 0,
};
});
resetValuesToZeroWorklet();
} else {
// If the change set the slider position to > 0
if (!internalSelectedInputAsset.value) return;
Expand Down Expand Up @@ -748,29 +706,10 @@ export function useSwapInputsController({
}
if (inputMethod.value === 'inputAmount' && !equalWorklet(current.values.inputAmount, previous.values.inputAmount)) {
// If the number in the input field changes
lastTypedInput.value = 'inputAmount';
if (equalWorklet(current.values.inputAmount, 0)) {
// If the input amount was set to 0
quoteFetchingInterval.stop();
isQuoteStale.value = 0;
isFetching.value = false;

const hasDecimal = current.values.inputAmount.toString().includes('.');

sliderXPosition.value = withSpring(0, snappySpringConfig);
inputValues.modify(values => {
return {
...values,
inputAmount: hasDecimal ? current.values.inputAmount : 0,
inputNativeValue: 0,
outputAmount: 0,
outputNativeValue: 0,
};
});
if (hasDecimal) {
runOnJS(onTypedNumber)(0, 'inputAmount', true);
} else {
runOnJS(onTypedNumber)(0, 'inputAmount');
}
resetValuesToZeroWorklet('inputAmount');
} else {
// If the input amount was set to a non-zero value
if (!internalSelectedInputAsset.value) return;
Expand Down Expand Up @@ -798,35 +737,15 @@ export function useSwapInputsController({
sliderXPosition.value = withSpring(updatedSliderPosition, snappySpringConfig);
}

runOnJS(onTypedNumber)(Number(current.values.inputAmount), 'inputAmount', true);
runOnJS(debouncedFetchQuote)();
}
}
if (inputMethod.value === 'outputAmount' && !equalWorklet(current.values.outputAmount, previous.values.outputAmount)) {
// If the number in the output field changes
lastTypedInput.value = 'outputAmount';
if (equalWorklet(current.values.outputAmount, 0)) {
// If the output amount was set to 0
quoteFetchingInterval.stop();
isQuoteStale.value = 0;
isFetching.value = false;

const hasDecimal = current.values.outputAmount.toString().includes('.');

sliderXPosition.value = withSpring(0, snappySpringConfig);
inputValues.modify(values => {
return {
...values,
inputAmount: 0,
inputNativeValue: 0,
outputAmount: hasDecimal ? current.values.outputAmount : 0,
outputNativeValue: 0,
};
});

if (hasDecimal) {
runOnJS(onTypedNumber)(0, 'outputAmount', true);
} else {
runOnJS(onTypedNumber)(0, 'outputAmount');
}
resetValuesToZeroWorklet('outputAmount');
} else if (greaterThanWorklet(current.values.outputAmount, 0)) {
// If the output amount was set to a non-zero value
if (isQuoteStale.value !== 1) isQuoteStale.value = 1;
Expand All @@ -840,7 +759,7 @@ export function useSwapInputsController({
};
});

runOnJS(onTypedNumber)(Number(current.values.outputAmount), 'outputAmount');
runOnJS(debouncedFetchQuote)();
}
}
}
Expand Down Expand Up @@ -913,25 +832,31 @@ export function useSwapInputsController({
const inputNativePrice = internalSelectedInputAsset.value?.nativePrice || internalSelectedInputAsset.value?.price?.value || 0;
const outputNativePrice = internalSelectedOutputAsset.value?.nativePrice || internalSelectedOutputAsset.value?.price?.value || 0;

const prevInputNativeValue = inputValues.value.inputNativeValue;
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pulling these variables out for readability as it can get confusing when you've flipped assets that the native price corresponds to the "new" input / output assets while the inputValues correspond to the previous input/output assets.

const prevOutputAmount = inputValues.value.outputAmount;
const newInputAmount = inputNativePrice > 0 ? divWorklet(prevInputNativeValue, inputNativePrice) : prevOutputAmount;

const inputAmount = Number(
valueBasedDecimalFormatter({
amount:
inputNativePrice > 0 ? divWorklet(inputValues.value.inputNativeValue, inputNativePrice) : inputValues.value.outputAmount,
amount: newInputAmount,
nativePrice: inputNativePrice,
roundingMode: 'up',
isStablecoin: internalSelectedInputAsset.value?.type === 'stablecoin' ?? false,
stripSeparators: true,
})
);

const prevOutputNativeValue = inputValues.value.outputNativeValue;
const prevInputAmount = inputValues.value.inputAmount;
const newOutputAmount = outputNativePrice > 0 ? divWorklet(prevOutputNativeValue, outputNativePrice) : prevInputAmount;

inputValues.modify(values => {
return {
...values,
inputAmount,
inputNativeValue: inputValues.value.inputNativeValue,
outputAmount:
outputNativePrice > 0 ? divWorklet(inputValues.value.outputNativeValue, outputNativePrice) : inputValues.value.inputAmount,
outputNativeValue: inputValues.value.outputNativeValue,
inputNativeValue: mulWorklet(newInputAmount, inputNativePrice),
Copy link
Member Author

@jinchung jinchung Jul 10, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Updated input/outputNativeValue to use the mulWorklet so that it is more precise and based on the new inputAmount and the new native price.

outputAmount: newOutputAmount,
outputNativeValue: mulWorklet(newOutputAmount, outputNativePrice),
};
});
}
Expand Down
Loading