Skip to content

Commit

Permalink
fix(mobile): Battery QA fixes (#784)
Browse files Browse the repository at this point in the history
* fix(mobile): Don't reset battery balance in case of error

* fix(mobile): Touch zone

* fix(mobile): supported transaction setting from battery config

* fix disable send with relayer for SingRaw

* feature(mobile): Animated battery icon

* fix(mobile): switch attemptWithRelayer default value to false

* fix(mobile): update order
  • Loading branch information
voloshinskii authored Apr 9, 2024
1 parent 14bdc8f commit b2b52ec
Show file tree
Hide file tree
Showing 18 changed files with 249 additions and 61 deletions.
8 changes: 7 additions & 1 deletion packages/mobile/src/blockchain/wallet.ts
Original file line number Diff line number Diff line change
Expand Up @@ -487,7 +487,13 @@ export class TonWallet {
let feeNano: BigNumber;
let isBattery = false;
try {
const [fee, battery] = await this.calcFee(boc);
const [fee, battery] = await this.calcFee(
boc,
undefined,
tk.wallet.battery.state.data.supportedTransactions[
BatterySupportedTransaction.Jetton
],
);
feeNano = fee;
isBattery = battery;
} catch (e) {
Expand Down
10 changes: 10 additions & 0 deletions packages/mobile/src/config/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -43,9 +43,15 @@ export type AppConfigVars = {
tonapiTestnetHost: string;
tronapiHost: string;
tronapiTestnetHost: string;

batteryHost: string;
batteryTestnetHost: string;
batteryMeanFees: string;
batteryReservedAmount: string;
batteryMeanPrice_swap: string;
batteryMeanPrice_jetton: string;
batteryMeanPrice_nft: string;

holdersAppEndpoint: string;
holdersService: string;
aptabaseEndpoint: string;
Expand Down Expand Up @@ -83,6 +89,10 @@ const defaultConfig: Partial<AppConfigVars> = {
batteryHost: 'https://battery.tonkeeper.com',
batteryTestnetHost: 'https://testnet-battery.tonkeeper.com',
batteryMeanFees: '0.0055',
batteryReservedAmount: '0.3',
batteryMeanPrice_swap: '0.22',
batteryMeanPrice_jetton: '0.06',
batteryMeanPrice_nft: '0.03',
disable_battery: true,
disable_battery_iap_module: Platform.OS !== 'android', // Enable for iOS, disable for Android
disable_battery_send: true,
Expand Down
Original file line number Diff line number Diff line change
@@ -1,20 +1,17 @@
import React, { memo, useCallback, useMemo } from 'react';
import { t } from '@tonkeeper/shared/i18n';
import { Modal, Spacer } from '@tonkeeper/uikit';
import { openExploreTab } from '$navigation';
import { openExploreTab, openRefillBatteryModal } from '$navigation';
import { SheetActions, useNavigation } from '@tonkeeper/router';
import { Button, Icon, Text } from '$uikit';
import * as S from './InsufficientFunds.style';
import { delay, fromNano } from '$utils';
import { debugLog } from '$utils/debugLog';
import BigNumber from 'bignumber.js';
import { store } from '$store';
import { formatter } from '$utils/formatter';
import { push } from '$navigation/imperative';
import { useBatteryBalance } from '@tonkeeper/shared/query/hooks/useBatteryBalance';
import { config } from '$config';
import { openRefillBatteryModal } from '@tonkeeper/shared/modals/RefillBatteryModal';
import { tk } from '$wallet';
import { Wallet } from '$wallet/Wallet';
import { AmountFormatter } from '@tonkeeper/core';

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -327,6 +327,7 @@ export const openSignRawModal = async (

const { emulateResult, battery } = await emulateBoc(
boc,
undefined,
options.experimentalWithBattery,
);
consequences = emulateResult;
Expand Down
18 changes: 11 additions & 7 deletions packages/mobile/src/uikit/TransitionOpacity.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ interface TransitionOpacity {
style?: StyleProp<ViewStyle>;
duration?: number;
children: React.ReactNode;
}
}

export const TransitionOpacity: React.FC<TransitionOpacity> = (props) => {
const {
Expand All @@ -32,12 +32,16 @@ export const TransitionOpacity: React.FC<TransitionOpacity> = (props) => {

React.useEffect(() => {
if (isVisible) {
setIsShown(true);
opacity.value = withDelay(
250,
withTiming(1, {
opacity.value = withTiming(
1,
{
duration,
}),
},
(isComplete) => {
if (isComplete) {
runOnJS(setIsShown)(true);
}
},
);
} else {
opacity.value = withTiming(
Expand All @@ -52,7 +56,7 @@ export const TransitionOpacity: React.FC<TransitionOpacity> = (props) => {
},
);
}
}, [isVisible]);
}, [duration, isVisible, opacity]);

const opacityStyle = useAnimatedStyle(() => ({
opacity: opacity.value,
Expand Down
10 changes: 5 additions & 5 deletions packages/mobile/src/wallet/managers/BatteryManager.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,9 @@ import { TonProofManager } from '$wallet/managers/TonProofManager';
import { logger, NamespacedLogger } from '$logger';

export enum BatterySupportedTransaction {
NFT = 'nft',
Jetton = 'jetton',
Swap = 'swap',
Jetton = 'jetton',
NFT = 'nft',
}

export interface BatteryState {
Expand All @@ -22,9 +22,9 @@ export class BatteryManager {
isLoading: false,
balance: undefined,
supportedTransactions: {
[BatterySupportedTransaction.NFT]: true,
[BatterySupportedTransaction.Jetton]: true,
[BatterySupportedTransaction.Swap]: true,
[BatterySupportedTransaction.Jetton]: true,
[BatterySupportedTransaction.NFT]: true,
},
});

Expand Down Expand Up @@ -63,7 +63,7 @@ export class BatteryManager {
);
this.state.set({ isLoading: false, balance: data.balance });
} catch (err) {
this.state.set({ isLoading: false, balance: '0' });
this.state.set({ isLoading: false });
return null;
}
}
Expand Down
43 changes: 25 additions & 18 deletions packages/shared/components/BatteryIcon/BatteryIcon.tsx
Original file line number Diff line number Diff line change
@@ -1,36 +1,43 @@
import React, { memo } from 'react';
import { useBatteryBalance } from '../../query/hooks/useBatteryBalance';
import { Icon, IconNames, Steezy, TouchableOpacity } from '@tonkeeper/uikit';
import {
AnimatedBatteryIcon,
AnimatedBatterySize,
Icon,
Steezy,
TouchableOpacity,
} from '@tonkeeper/uikit';
import { BatteryState, getBatteryState } from '../../utils/battery';
import { config } from '@tonkeeper/mobile/src/config';
import { useBatteryUIStore } from '@tonkeeper/mobile/src/store/zustand/batteryUI';
import { openRefillBatteryModal } from '@tonkeeper/mobile/src/navigation';

const iconNames: { [key: string]: ((isViewed: boolean) => IconNames) | IconNames } = {
[BatteryState.Empty]: (isViewed) =>
isViewed ? 'ic-empty-battery-flash-34' : 'ic-empty-battery-accent-flash-34',
[BatteryState.AlmostEmpty]: 'ic-almost-empty-battery-34',
[BatteryState.Medium]: 'ic-medium-battery-34',
[BatteryState.Full]: 'ic-full-battery-34',
};

const hitSlop = { top: 8, bottom: 8, right: 8, left: 8 };
const hitSlop = { top: 12, bottom: 12, right: 24, left: 8 };

export const BatteryIcon = memo(() => {
const { balance } = useBatteryBalance();
const isViewedBatteryScreen = useBatteryUIStore((state) => state.isViewedBatteryScreen);
if (config.get('disable_battery')) return null;

const iconName = iconNames[getBatteryState(balance)];

return (
<TouchableOpacity onPress={openRefillBatteryModal} hitSlop={hitSlop}>
<Icon
imageStyle={styles.iconSize.static}
style={styles.iconSize.static}
colorless
name={typeof iconName === 'string' ? iconName : iconName(isViewedBatteryScreen)}
/>
{getBatteryState(balance) === BatteryState.Empty ? (
<Icon
imageStyle={styles.iconSize.static}
style={styles.iconSize.static}
colorless
name={
isViewedBatteryScreen
? 'ic-empty-battery-flash-34'
: 'ic-empty-battery-accent-flash-34'
}
/>
) : (
<AnimatedBatteryIcon
progress={parseFloat(balance)}
size={AnimatedBatterySize.Small}
/>
)}
</TouchableOpacity>
);
});
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,32 +7,29 @@ import { capitalizeFirstLetter } from '../../utils/date';
import { useExternalState } from '../../hooks/useExternalState';
import { tk } from '@tonkeeper/mobile/src/wallet';
import { BatterySupportedTransaction } from '@tonkeeper/mobile/src/wallet/managers/BatteryManager';
import { Platform } from 'react-native';

export interface SupportedTransaction {
type: BatterySupportedTransaction;
name: string;
nameSingle: string;
meanPrice: string;
}

export const supportedTransactions: SupportedTransaction[] = [
{
type: BatterySupportedTransaction.Swap,
name: 'battery.transactions.types.swap',
nameSingle: 'battery.transactions.type.swap',
meanPrice: '0.22',
},
{
type: BatterySupportedTransaction.NFT,
name: 'battery.transactions.types.nft',
nameSingle: 'battery.transactions.type.transfer',
meanPrice: '0.025',
},
{
type: BatterySupportedTransaction.Jetton,
name: 'battery.transactions.types.jetton',
nameSingle: 'battery.transactions.type.transfer',
meanPrice: '0.055',
},
];

Expand Down Expand Up @@ -72,28 +69,29 @@ export const BatterySupportedTransactions = memo<BatterySupportedTransactionsPro
<List>
{supportedTransactions.map((transaction) => (
<List.Item
key={transaction.type}
disabled={!props.editable}
onPress={() =>
handleSwitchSupport(transaction.type)(
!supportedTransactionsValues[transaction.type],
)
}
key={transaction.type}
title={capitalizeFirstLetter(t(transaction.name))}
subtitle={t('battery.transactions.charges_per_action', {
count: calculateChargesAmount(
transaction.meanPrice,
config.get(`batteryMeanPrice_${transaction.type}`),
config.get('batteryMeanFees'),
),
transactionName: t(transaction.nameSingle),
})}
rightContent={
props.editable && (
props.editable ? (
<Switch
disabled={Platform.OS === 'android'} // Temp fix. Should refactor screen with react-native-pager-view
onChange={handleSwitchSupport(transaction.type)}
value={supportedTransactionsValues[transaction.type]}
/>
)
) : null
}
/>
))}
Expand Down
27 changes: 17 additions & 10 deletions packages/shared/components/RefillBattery/RefillBattery.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,9 @@ import {
import { memo } from 'react';
import { useBatteryBalance } from '../../query/hooks/useBatteryBalance';
import {
AnimatedBatteryIcon,
AnimatedBatterySize,
Icon,
IconNames,
Spacer,
Steezy,
Text,
Expand All @@ -24,21 +25,13 @@ import { RefillBatterySettingsWidget } from './RefillBatterySettingsWidget';
import Animated from 'react-native-reanimated';
import { useSafeAreaInsets } from 'react-native-safe-area-context';

const iconNames: { [key: string]: IconNames } = {
[BatteryState.Empty]: 'ic-empty-battery-128',
[BatteryState.AlmostEmpty]: 'ic-almost-empty-battery-128',
[BatteryState.Medium]: 'ic-medium-battery-128',
[BatteryState.Full]: 'ic-full-battery-128',
};

export interface RefillBatteryProps {
navigateToTransactions: () => void;
}

export const RefillBattery = memo<RefillBatteryProps>((props) => {
const { balance } = useBatteryBalance();
const batteryState = getBatteryState(balance ?? '0');
const iconName = iconNames[batteryState];
const availableNumOfTransactionsCount = calculateAvailableNumOfTransactions(
balance ?? '0',
);
Expand All @@ -52,7 +45,16 @@ export const RefillBattery = memo<RefillBatteryProps>((props) => {
contentContainerStyle={{ paddingBottom: bottomInsets + 16 }}
>
<View style={styles.contentContainer}>
<Icon colorless name={iconName} />
{batteryState === BatteryState.Empty ? (
<Icon colorless name={'ic-empty-battery-128'} />
) : (
<View style={styles.animatedBatteryContainer}>
<AnimatedBatteryIcon
progress={parseFloat(balance)}
size={AnimatedBatterySize.Large}
/>
</View>
)}
<Spacer y={16} />
<Text textAlign="center" type="h2">
{t(`battery.title`)}
Expand Down Expand Up @@ -100,4 +102,9 @@ export const styles = Steezy.create({
indent: {
paddingHorizontal: 16,
},
animatedBatteryContainer: {
paddingHorizontal: 30,
paddingTop: 6,
paddingBottom: 8,
},
});
6 changes: 6 additions & 0 deletions packages/shared/components/RefillBattery/RefillBatteryIAP.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ import { useTokenPrice } from '@tonkeeper/mobile/src/hooks/useTokenPrice';
import { CryptoCurrencies } from '@tonkeeper/mobile/src/shared/constants';
import BigNumber from 'bignumber.js';
import { config } from '@tonkeeper/mobile/src/config';
import { useExternalState } from '../../hooks/useExternalState';

export interface InAppPackage {
icon: IconNames;
Expand Down Expand Up @@ -53,6 +54,10 @@ export const RefillBatteryIAP = memo(() => {
const [purchaseInProgress, setPurchaseInProgress] = useState<boolean>(false);
const { products, getProducts, requestPurchase, finishTransaction } = useIAP();
const tonPriceInUsd = useTokenPrice(CryptoCurrencies.Ton).usd;
const batteryBalance = useExternalState(
tk.wallet.battery.state,
(state) => state.balance,
);

useEffect(() => {
getProducts({
Expand Down Expand Up @@ -140,6 +145,7 @@ export const RefillBatteryIAP = memo(() => {
{t(`battery.packages.subtitle`, {
count: new BigNumber(item.userProceed)
.div(tonPriceInUsd)
.minus(!batteryBalance ? config.get('batteryReservedAmount') : 0)
.div(config.get('batteryMeanFees'))
.decimalPlaces(0)
.toNumber(),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@ export const RestorePurchases = memo(() => {
onPress={handleRestorePurchases}
type="body2"
textAlign="center"
color="textPrimary"
color="textSecondary"
>
{t('battery.packages.restore')}
</Text>
Expand Down
2 changes: 1 addition & 1 deletion packages/shared/i18n/locales/tonkeeper/en.json
Original file line number Diff line number Diff line change
Expand Up @@ -131,7 +131,7 @@
"types": {
"nft": "NFT transfers",
"swap": "swaps",
"jetton": "jetton transfers",
"jetton": "token transfers",
"ton": "TON transfers"
},
"type": {
Expand Down
4 changes: 2 additions & 2 deletions packages/shared/i18n/locales/tonkeeper/ru-RU.json
Original file line number Diff line number Diff line change
Expand Up @@ -1090,7 +1090,7 @@
"ok": "OK",
"title": "Батарейка Tonkeeper",
"description": {
"empty": "Обменивайте и отправляйте токены",
"empty": "Обменивайте и отправляйте токены.",
"other": {
"few": "В вашей батарейке %{count} заряда",
"many": "В вашей батарейке %{count} зарядов",
Expand All @@ -1109,7 +1109,7 @@
"title": {
"large": "Большой",
"medium": "Средний",
"small": "Маленький"
"small": "Малый"
},
"subtitle": {
"few": "%{count} заряда",
Expand Down
Loading

0 comments on commit b2b52ec

Please sign in to comment.