Skip to content

Commit

Permalink
Merge pull request #960 from tonwhales/release/v2.3.7
Browse files Browse the repository at this point in the history
Release/v2.3.7
  • Loading branch information
vzhovnitsky authored Jun 24, 2024
2 parents ceafb6d + 41a2122 commit 0225321
Show file tree
Hide file tree
Showing 33 changed files with 484 additions and 180 deletions.
2 changes: 1 addition & 1 deletion VERSION_CODE
Original file line number Diff line number Diff line change
@@ -1 +1 @@
200
201
2 changes: 1 addition & 1 deletion app/Root.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ import { AddressBookLoader } from './engine/AddressBookContext';
import { ThemeProvider } from './engine/ThemeContext';
import { PriceLoader } from './engine/PriceContext';

const PERSISTANCE_VERSION = '22';
const PERSISTANCE_VERSION = '23';

LogBox.ignoreAllLogs()

Expand Down
2 changes: 1 addition & 1 deletion app/components/HintsPrefetcher.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,5 +8,5 @@ export function HintsPrefetcher() {

usePrefetchHints(client, selected?.addressString);

return <></>;
return null;
}
2 changes: 1 addition & 1 deletion app/components/PendingTxsWatcher.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,5 +4,5 @@ import { usePendingWatcher } from "../engine/hooks";
export const PendingTxsWatcher = memo(() => {
// clear pending txs on account change
usePendingWatcher();
return <></>;
return null;
});
2 changes: 1 addition & 1 deletion app/components/PriceComponent.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -73,7 +73,7 @@ export const PriceComponent = memo((
}

if (!price) {
return <></>;
return null;
}

return (
Expand Down
2 changes: 1 addition & 1 deletion app/components/SortedHintsWatcher.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,5 +3,5 @@ import { useSortedHintsWatcher } from "../engine/hooks/jettons/useSortedHintsWat

export const SortedHintsWatcher = memo(({ owner }: { owner?: string}) => {
useSortedHintsWatcher(owner);
return <></>;
return null;
});
117 changes: 80 additions & 37 deletions app/components/browser/BrowserExtensions.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { memo, useCallback } from "react";
import { View, Image, Text, Platform, Alert, NativeSyntheticEvent, NativeScrollEvent } from "react-native";
import { View, Image, Text, Alert, NativeSyntheticEvent, NativeScrollEvent } from "react-native";
import Animated, { FadeIn, FadeOut } from "react-native-reanimated";
import { ConnectionButton } from "../ConnectionButton";
import { useDisconnectApp, useExtensions, useNetwork, useRemoveExtension, useTheme, useTonConnectExtensions } from "../../engine/hooks";
Expand All @@ -12,6 +12,8 @@ import { useBottomTabBarHeight } from "@react-navigation/bottom-tabs";
import { useDimensions } from "@react-native-community/hooks";
import { useSafeAreaInsets } from "react-native-safe-area-context";
import { holdersUrl as resolveHoldersUrl } from '../../engine/api/holders/fetchAccountState';
import { Typography } from "../styles";
import { ConnectedApp } from "../../engine/hooks/dapps/useTonConnectExtenstions";

export const EmptyIllustrations = {
dark: require('@assets/empty-connections-dark.webp'),
Expand Down Expand Up @@ -55,6 +57,52 @@ export const BrowserExtensions = memo(({ onScroll }: { onScroll?: ((event: Nativ
}
}, []);

const openTonconnectApp = useCallback((item: ConnectedApp) => {
const domain = extractDomain(item.url);
const titleComponent = (
<View style={{ flexDirection: 'row', alignItems: 'center' }}>
<View style={{ marginRight: 8 }}>
<View style={{
width: 24, height: 24,
borderRadius: 12,
backgroundColor: theme.accent,
justifyContent: 'center', alignItems: 'center'
}}>
<Text style={[{ color: theme.textPrimary }, Typography.semiBold15_20]}>
{domain.charAt(0).toUpperCase()}
</Text>
</View>
</View>
<View style={{ justifyContent: 'center', alignItems: 'center' }}>
{item.name && (
<Text style={[{ color: theme.textPrimary }, Typography.semiBold15_20]}>
{item.name}
</Text>
)}
<Text style={[{ color: theme.textSecondary }, Typography.regular13_18]}>
{domain}
</Text>
</View>
</View>
);

navigation.navigateDAppWebView({
lockNativeBack: true,
safeMode: true,
url: item.url,
title: item.name ?? undefined,
header: { titleComponent },
useStatusBar: true,
engine: 'ton-connect',
controlls: {
refresh: true,
share: true,
back: true,
forward: true
}
})
}, []);

const onRemoveExtension = useCallback((key: string) => {
Alert.alert(t('auth.revoke.title'), t('auth.revoke.message'), [{ text: t('common.cancel') }, {
text: t('auth.revoke.action'),
Expand Down Expand Up @@ -108,45 +156,40 @@ export const BrowserExtensions = memo(({ onScroll }: { onScroll?: ((event: Nativ
<Animated.ScrollView
entering={FadeIn}
exiting={FadeOut}
contentInset={{ bottom: bottomBarHeight, top: 0.1 }}
style={{ flexGrow: 1, marginTop: 24 }}
style={{ flexGrow: 1 }}
onScroll={onScroll}
scrollEventThrottle={16}
contentContainerStyle={{ justifyContent: 'center', alignItems: 'center', gap: 8 }}
>
<View style={{
marginBottom: 16, marginTop: 0,
borderRadius: 14,
justifyContent: 'center',
alignItems: 'center',
flexShrink: 1,
}}>
{extensions.map((app, index) => (
<View key={`app-${app.url}`} style={{ width: '100%', marginTop: index === 0 ? 0 : 8, marginBottom: 8 }}>
<ConnectionButton
onPress={() => openExtension(app.url)}
onRevoke={() => onRemoveExtension(app.url)}
onLongPress={() => onRemoveExtension(app.url)}
url={app.url}
name={app.title}
/>
</View>
))}
{tonconnectApps.map((app, index) => (
<View
key={`app-${app.url}`}
style={{ width: '100%', marginTop: (index === 0 && extensions.length === 0) ? 0 : 8, marginBottom: 8 }}
>
<ConnectionButton
onRevoke={() => disconnectConnectApp(app.url)}
onPress={() => navigation.navigate('ConnectApp', { url: app.url })}
url={app.url}
name={app.name}
tonconnect
/>
</View>
))}
<View style={{ height: Platform.OS === 'android' ? 64 : safeArea.bottom }} />
</View>
{extensions.map((app) => (
<View
key={`ton-x-app-${app.url}`}
style={{ width: '100%' }}
>
<ConnectionButton
onPress={() => openExtension(app.url)}
onRevoke={() => onRemoveExtension(app.url)}
onLongPress={() => onRemoveExtension(app.url)}
url={app.url}
name={app.title}
/>
</View>
))}
{tonconnectApps.map((app) => (
<View
key={`connect-app-${app.url}`}
style={{ width: '100%' }}
>
<ConnectionButton
onRevoke={() => disconnectConnectApp(app.url)}
onPress={() => openTonconnectApp(app)}
url={app.url}
name={app.name}
tonconnect
/>
</View>
))}
<View style={{ height: bottomBarHeight + safeArea.top + safeArea.bottom + 256 }} />
</Animated.ScrollView>
);
});
36 changes: 33 additions & 3 deletions app/components/secure/AuthWalletKeys.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -13,11 +13,23 @@ import { useActionSheet } from '@expo/react-native-action-sheet';
import { PERMISSIONS, check, openSettings, request } from 'react-native-permissions';
import * as LocalAuthentication from 'expo-local-authentication'
import { useTypedNavigation } from '../../utils/useTypedNavigation';
import { useBiometricsState, useSelectedAccount, useSetBiometricsState, useTheme } from '../../engine/hooks';
import { useBiometricsState, useSetBiometricsState, useTheme } from '../../engine/hooks';
import { useLogoutAndReset } from '../../engine/hooks/accounts/useLogoutAndReset';
import { CloseButton } from '../navigation/CloseButton';
import { SelectedAccount } from '../../engine/types';

export const lastAuthKey = 'lastAuthenticationAt';

// Save last successful auth time
function updateLastAuthTimestamp() {
storage.set(lastAuthKey, Date.now());
}

// Get last successful auth time
export function getLastAuthTimestamp() {
return storage.getNumber(lastAuthKey);
}

type EnteringAnimation = BaseAnimationBuilder
| typeof BaseAnimationBuilder
| EntryExitAnimationFunction
Expand Down Expand Up @@ -149,6 +161,9 @@ export const AuthWalletKeysContextProvider = memo((props: { children?: any }) =>
if (biometricsState === null) {
setBiometricsState(BiometricsState.InUse);
}

updateLastAuthTimestamp();

return keys;
} catch (e) {
if (e instanceof SecureAuthenticationCancelledError) {
Expand Down Expand Up @@ -233,7 +248,13 @@ export const AuthWalletKeysContextProvider = memo((props: { children?: any }) =>
// Fallback to passcode if biometrics is not set or unavailable (checked before)
if (passcodeState === PasscodeState.Set) {
return new Promise<WalletKeys>((resolve, reject) => {
setAuth({ returns: 'keysOnly', promise: { resolve, reject }, params: { showResetOnMaxAttempts: true, ...style, useBiometrics, passcodeLength } });

const resolveWithTimestamp = async (keys: WalletKeys) => {
updateLastAuthTimestamp();
resolve(keys);
};

setAuth({ returns: 'keysOnly', promise: { resolve: resolveWithTimestamp, reject }, params: { showResetOnMaxAttempts: true, ...style, useBiometrics, passcodeLength } });
});
}

Expand All @@ -258,7 +279,16 @@ export const AuthWalletKeysContextProvider = memo((props: { children?: any }) =>
if (passcodeState !== PasscodeState.Set) {
reject();
}
setAuth({ returns: 'keysWithPasscode', promise: { resolve, reject }, params: { showResetOnMaxAttempts: true, ...style, useBiometrics: false, passcodeLength } });

const resolveWithTimestamp = async (res: {
keys: WalletKeys;
passcode: string;
}) => {
updateLastAuthTimestamp();
resolve(res);
}

setAuth({ returns: 'keysWithPasscode', promise: { resolve: resolveWithTimestamp, reject }, params: { showResetOnMaxAttempts: true, ...style, useBiometrics: false, passcodeLength } });
});
}, [auth]);

Expand Down
40 changes: 29 additions & 11 deletions app/components/transfer/HoldersOpView.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,20 +6,32 @@ import { Typography } from "../styles";
import { t } from "../../i18n/t";
import { ItemDivider } from "../ItemDivider";
import { ItemGroup } from "../ItemGroup";
import { ContractKind } from "../../engine/api/fetchContractInfo";
import { toNano } from "@ton/core";
import { fromBnWithDecimals } from "../../utils/withDecimals";

export type HoldersOpType = {
export type HoldersOp = {
type: 'topUp';
amount: string;
} | {
type: 'jettonTopUp';
} | {
type: 'limitsChange';
onetime: string | null;
daily: string | null;
monthly: string | null;
onetime?: string | null;
daily?: string | null;
monthly?: string | null;
}

export const HoldersOpView = memo(({ theme, op }: { theme: ThemeType, op: HoldersOpType }) => {
function formatAmount(amount: string | null | undefined, type: 'USDT' | 'TON'): string | null {
if (!amount) {
return null;
}

// convert back to nano and format with decimals
return `${fromBnWithDecimals(toNano(amount), type === 'USDT' ? 6 : 9)} ${type}`;
}

export const HoldersOpView = memo(({ theme, op, targetKind }: { theme: ThemeType, op: HoldersOp, targetKind?: ContractKind }) => {
if (op.type === 'jettonTopUp') { // Jetton account top up (amount is shown in the Tx preview title)
return null;
}
Expand All @@ -41,49 +53,55 @@ export const HoldersOpView = memo(({ theme, op }: { theme: ThemeType, op: Holder
);
}

// We have no jetton address so are forced to use built-in decimals
const type = targetKind === 'jetton-card' ? 'USDT' : 'TON'
const onetime = formatAmount(op.onetime, type);
const daily = formatAmount(op.daily, type);
const monthly = formatAmount(op.monthly, type);

return (
<ItemGroup style={{ marginTop: 16 }}>
<PerfView style={{ paddingHorizontal: 10, justifyContent: 'center' }}>
<PerfText style={[{ color: theme.textSecondary, marginBottom: 8 }, Typography.regular17_24]}>
{t('known.holders.limitsTitle')}
</PerfText>
{!!op.onetime && (
{!!onetime && (
<>
<PerfView style={{ justifyContent: 'space-between', flexDirection: 'row', width: '100%' }}>
<PerfText style={[{ color: theme.textPrimary }, Typography.regular15_20]}>
{t('known.holders.limitsOneTime')}
</PerfText>
<PerfText style={[{ color: theme.textPrimary }, Typography.regular17_24]}>
{`${op.onetime} TON`}
{onetime}
</PerfText>
</PerfView>
{(!!op.daily || !!op.monthly) && (
<ItemDivider marginHorizontal={0} />
)}
</>
)}
{!!op.daily && (
{!!daily && (
<>
<PerfView style={{ justifyContent: 'space-between', flexDirection: 'row', width: '100%' }}>
<PerfText style={[{ color: theme.textPrimary }, Typography.regular15_20]}>
{t('known.holders.limitsDaily')}
</PerfText>
<PerfText style={[{ color: theme.textPrimary }, Typography.regular17_24]}>
{`${op.daily} TON`}
{daily}
</PerfText>
</PerfView>
{!!op.monthly && (
<ItemDivider marginHorizontal={0} />
)}
</>
)}
{!!op.monthly && (
{!!monthly && (
<PerfView style={{ justifyContent: 'space-between', flexDirection: 'row', width: '100%' }}>
<PerfText style={[{ color: theme.textPrimary }, Typography.regular15_20]}>
{t('known.holders.limitsMonthly')}
</PerfText>
<PerfText style={[{ color: theme.textPrimary }, Typography.regular17_24]}>
{`${op.monthly} TON`}
{monthly}
</PerfText>
</PerfView>
)}
Expand Down
Loading

0 comments on commit 0225321

Please sign in to comment.