Skip to content

Commit

Permalink
feature(mobile): Animated battery icon
Browse files Browse the repository at this point in the history
  • Loading branch information
voloshinskii committed Apr 3, 2024
1 parent 05d1eb1 commit ff4c3dd
Show file tree
Hide file tree
Showing 9 changed files with 218 additions and 38 deletions.
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
41 changes: 24 additions & 17 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: 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
1 change: 1 addition & 0 deletions packages/shared/modals/RefillBatteryModal.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,7 @@ export const RefillBatteryModal = memo(() => {

const handleBack = useCallback(() => stepViewRef.current?.goBack(), []);

// TODO: rewrite to react-native-pager-view
return (
<>
<NavBar
Expand Down
4 changes: 1 addition & 3 deletions packages/shared/utils/battery.ts
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,5 @@ export function calculateAvailableNumOfTransactions(batteryBalance: string) {
const balance = new BigNumber(batteryBalance);

// return balance divided by mean fees rounded down
return balance
.div(config.get('batteryMeanFees'))
.decimalPlaces(0, BigNumber.ROUND_DOWN);
return balance.div(config.get('batteryMeanFees')).decimalPlaces(0, BigNumber.ROUND_UP);
}
Loading

0 comments on commit ff4c3dd

Please sign in to comment.