diff --git a/packages/mobile/android/app/build.gradle b/packages/mobile/android/app/build.gradle index 8c0ffa385..86deb18c9 100644 --- a/packages/mobile/android/app/build.gradle +++ b/packages/mobile/android/app/build.gradle @@ -92,7 +92,7 @@ android { minSdkVersion rootProject.ext.minSdkVersion targetSdkVersion rootProject.ext.targetSdkVersion versionCode 433 - versionName "4.3.0" + versionName "4.4.0" missingDimensionStrategy 'react-native-camera', 'general' missingDimensionStrategy 'store', 'play' } diff --git a/packages/mobile/ios/ton_keeper.xcodeproj/project.pbxproj b/packages/mobile/ios/ton_keeper.xcodeproj/project.pbxproj index ed578fecd..360b519f9 100644 --- a/packages/mobile/ios/ton_keeper.xcodeproj/project.pbxproj +++ b/packages/mobile/ios/ton_keeper.xcodeproj/project.pbxproj @@ -1056,7 +1056,7 @@ outputFileListPaths = ( ); outputPaths = ( - "$SRCROOT/$PROJECT_NAME/Resources/Generated/R.generated.swift", + $SRCROOT/$PROJECT_NAME/Resources/Generated/R.generated.swift, ); runOnlyForDeploymentPostprocessing = 0; shellPath = /bin/sh; @@ -1298,7 +1298,7 @@ "$(inherited)", "@executable_path/Frameworks", ); - MARKETING_VERSION = 4.3.0; + MARKETING_VERSION = 4.4.0; OTHER_LDFLAGS = ( "$(inherited)", "-ObjC", @@ -1332,7 +1332,7 @@ "$(inherited)", "@executable_path/Frameworks", ); - MARKETING_VERSION = 4.3.0; + MARKETING_VERSION = 4.4.0; OTHER_LDFLAGS = ( "$(inherited)", "-ObjC", diff --git a/packages/mobile/ios/ton_keeper/SupportingFiles/Info.plist b/packages/mobile/ios/ton_keeper/SupportingFiles/Info.plist index b2717dd15..47c0122ee 100644 --- a/packages/mobile/ios/ton_keeper/SupportingFiles/Info.plist +++ b/packages/mobile/ios/ton_keeper/SupportingFiles/Info.plist @@ -151,8 +151,6 @@ UIInterfaceOrientationPortraitUpsideDown UIInterfaceOrientationPortrait - UIUserInterfaceStyle - Dark UIViewControllerBasedStatusBarAppearance diff --git a/packages/mobile/package.json b/packages/mobile/package.json index 3c24d549c..85b3016d8 100644 --- a/packages/mobile/package.json +++ b/packages/mobile/package.json @@ -33,7 +33,7 @@ "@expo/react-native-action-sheet": "^4.0.1", "@gorhom/bottom-sheet": "^4.6.0", "@rainbow-me/animated-charts": "https://github.com/tonkeeper/react-native-animated-charts#737b1633c41e13da437c8e111c4aedd15bd10558", - "@react-native-async-storage/async-storage": "^1.15.5", + "@react-native-async-storage/async-storage": "^1.23.1", "@react-native-community/clipboard": "^1.5.1", "@react-native-community/netinfo": "^9.3.2", "@react-native-firebase/analytics": "18.5.0", diff --git a/packages/mobile/src/components/CardsWidget/components/CardsList.tsx b/packages/mobile/src/components/CardsWidget/components/CardsList.tsx index bf546230a..2a5d5240a 100644 --- a/packages/mobile/src/components/CardsWidget/components/CardsList.tsx +++ b/packages/mobile/src/components/CardsWidget/components/CardsList.tsx @@ -5,7 +5,7 @@ import { formatter } from '@tonkeeper/shared/formatter'; import { MainStackRouteNames } from '$navigation'; import { useNavigation } from '@tonkeeper/router'; import { Platform, Text } from 'react-native'; -import { DarkTheme } from '@tonkeeper/uikit/src/styles/themes/dark'; +import { BlueTheme } from '@tonkeeper/uikit/src/styles/themes/blue'; import { CryptoCurrencies } from '$shared/constants'; import { useGetTokenPrice } from '$hooks/useTokenPrice'; import { capitalizeFirstLetter } from '@tonkeeper/shared/utils/date'; @@ -15,11 +15,11 @@ export interface CardsListProps { } const colorsForCardIconBackground = [ - DarkTheme.accentGreen, - DarkTheme.accentOrange, - DarkTheme.accentBlue, - DarkTheme.accentRed, - DarkTheme.accentPurple, + BlueTheme.accentGreen, + BlueTheme.accentOrange, + BlueTheme.accentBlue, + BlueTheme.accentRed, + BlueTheme.accentPurple, ]; function getColorByFourDigits(fourDigits: string | null | undefined) { diff --git a/packages/mobile/src/core/AccessConfirmation/AccessConfirmation.tsx b/packages/mobile/src/core/AccessConfirmation/AccessConfirmation.tsx index 16e1a0da7..316db2832 100644 --- a/packages/mobile/src/core/AccessConfirmation/AccessConfirmation.tsx +++ b/packages/mobile/src/core/AccessConfirmation/AccessConfirmation.tsx @@ -24,6 +24,7 @@ import { t } from '@tonkeeper/shared/i18n'; import { tk, vault } from '$wallet'; import { CanceledActionError } from '$core/Send/steps/ConfirmStep/ActionErrors'; import { useBiometrySettings, useWallet } from '@tonkeeper/shared/hooks'; +import { Screen } from '@tonkeeper/uikit'; export const AccessConfirmation: FC = () => { const route = useRoute(); @@ -215,7 +216,7 @@ export const AccessConfirmation: FC = () => { } return ( - + { onBiometryPress={handleBiometry} /> - + ); }; diff --git a/packages/mobile/src/core/AddWatchOnly/AddWatchOnly.tsx b/packages/mobile/src/core/AddWatchOnly/AddWatchOnly.tsx index 971956831..616e6a43e 100644 --- a/packages/mobile/src/core/AddWatchOnly/AddWatchOnly.tsx +++ b/packages/mobile/src/core/AddWatchOnly/AddWatchOnly.tsx @@ -152,8 +152,8 @@ export const AddWatchOnly: FC = () => { const { spacerStyle } = useReanimatedKeyboardHeight(); return ( - - + + diff --git a/packages/mobile/src/core/AddressUpdateInfo/AddressUpdateInfo.tsx b/packages/mobile/src/core/AddressUpdateInfo/AddressUpdateInfo.tsx index d881bf55d..7f99d9881 100644 --- a/packages/mobile/src/core/AddressUpdateInfo/AddressUpdateInfo.tsx +++ b/packages/mobile/src/core/AddressUpdateInfo/AddressUpdateInfo.tsx @@ -3,8 +3,7 @@ import { Spacer } from '$uikit'; import { Address } from '@tonkeeper/core'; import { t } from '@tonkeeper/shared/i18n'; import { tk } from '$wallet'; -import { Pressable, Screen, Steezy, Text, View } from '@tonkeeper/uikit'; -import { DarkTheme } from '@tonkeeper/uikit/src/styles/themes/dark'; +import { Pressable, Screen, Steezy, Text, View, useTheme } from '@tonkeeper/uikit'; import React, { FC } from 'react'; import { Platform } from 'react-native'; import { SafeAreaView } from 'react-native-safe-area-context'; @@ -34,9 +33,11 @@ export const AddressUpdateInfo: FC = () => { const oldStyle = splitAddress(oldAddress); const newStyle = splitAddress(newAddress); + const theme = useTheme(); + return ( - - + + @@ -51,8 +52,8 @@ export const AddressUpdateInfo: FC = () => { {t('address_update.your_wallet')} copyText(oldAddress)} > @@ -71,8 +72,8 @@ export const AddressUpdateInfo: FC = () => { copyText(newAddress)} > diff --git a/packages/mobile/src/core/App.tsx b/packages/mobile/src/core/App.tsx index 202f13b14..db72319ce 100644 --- a/packages/mobile/src/core/App.tsx +++ b/packages/mobile/src/core/App.tsx @@ -1,10 +1,17 @@ import React from 'react'; import { SafeAreaProvider } from 'react-native-safe-area-context'; import { Provider as StoreProvider, useSelector } from 'react-redux'; -import { ThemeProvider } from 'styled-components/native'; +import { ThemeProvider as StyledThemeProvider } from 'styled-components/native'; import { store } from '$store'; -import { AppearanceAccents, DarkTheme, TonTheme } from '$styled'; +import { + AccentKey, + AppearanceAccents, + BlueTheme, + DarkTheme, + LightTheme, + TonTheme, +} from '$styled'; import { AppNavigator } from '$navigation/AppNavigator'; import { ScrollPositionProvider } from '$uikit'; import { useMemo } from 'react'; @@ -19,24 +26,70 @@ import { HideableAmountProvider } from '$core/HideableAmount/HideableAmountProvi import { queryClient } from '@tonkeeper/shared/queryClient'; import { WalletProvider } from '../context'; -import { BlockingLoaderView } from '@tonkeeper/uikit'; +import { BlockingLoaderView, ThemeName, ThemeProvider } from '@tonkeeper/uikit'; +import { getThemeByName } from '@tonkeeper/uikit/src/styles/utils'; +import { useThemeName } from '$hooks/useThemeName'; + +const getLegacyThemeByName = (name: ThemeName): TonTheme => { + const uikitTheme = getThemeByName(name); + + if (name === ThemeName.Dark) { + return { ...DarkTheme, colors: { ...DarkTheme.colors, ...uikitTheme } }; + } + + if (name === ThemeName.Light) { + return { ...LightTheme, colors: { ...LightTheme.colors, ...uikitTheme } }; + } + + return { ...BlueTheme, colors: { ...BlueTheme.colors, ...uikitTheme } }; +}; const TonThemeProvider = ({ children }) => { const accent = useSelector(accentSelector); + const themeName = useThemeName(); + + const accentColors = + accent !== AccentKey.default ? AppearanceAccents[accent].colors : undefined; + + const uiThemeAccentColors = useMemo(() => { + if (!accentColors) { + return {}; + } + return { + textAccent: accentColors.accentPrimary, + buttonPrimaryBackground: accentColors.accentPrimary, + buttonPrimaryBackgroundDisabled: accentColors.accentPrimaryLight, + buttonPrimaryBackgroundHighlighted: accentColors.accentPrimaryDark, + fieldActiveBorder: accentColors.accentPrimary, + accentBlue: accentColors.accentPrimary, + tabBarActiveIcon: accentColors.accentPrimary, + }; + }, [accentColors]); + + const uitheme = useMemo( + () => ({ ...getThemeByName(themeName), ...uiThemeAccentColors }), + [themeName, uiThemeAccentColors], + ); - const accentColors = AppearanceAccents[accent].colors; + const legacyTheme = useMemo(() => getLegacyThemeByName(themeName), [themeName]); const theme = useMemo( (): TonTheme => ({ - ...DarkTheme, - colors: { ...DarkTheme.colors, ...accentColors }, + ...legacyTheme, + colors: { + ...legacyTheme.colors, + ...accentColors, + ...uiThemeAccentColors, + }, }), - [accentColors], + [accentColors, legacyTheme, uiThemeAccentColors], ); return ( - {children} + + {children} + ); }; diff --git a/packages/mobile/src/core/BuyFiat/BuyFiat.tsx b/packages/mobile/src/core/BuyFiat/BuyFiat.tsx index a12a0182c..5640f5964 100644 --- a/packages/mobile/src/core/BuyFiat/BuyFiat.tsx +++ b/packages/mobile/src/core/BuyFiat/BuyFiat.tsx @@ -16,6 +16,7 @@ import { trackEvent } from '$utils/stats'; import { useWallet, useWalletCurrency } from '@tonkeeper/shared/hooks'; import { config } from '$config'; import { tk } from '$wallet'; +import { useTheme } from '@tonkeeper/uikit'; export const BuyFiat: FC = ({ route }) => { const currency = route.params.currency; @@ -141,6 +142,8 @@ export const BuyFiat: FC = ({ route }) => { }, 500); }, []); + const theme = useTheme(); + return ( {renderHeader()} @@ -163,7 +166,7 @@ export const BuyFiat: FC = ({ route }) => { thirdPartyCookiesEnabled={true} onNavigationStateChange={handleNavigationChange} allowFileAccess - forceDarkOn={methodId !== 'onramp'} + forceDarkOn={theme.isDark && methodId !== 'onramp'} allowsInlineMediaPlayback allowsFullscreenVideo keyboardDisplayRequiresUserAction={false} diff --git a/packages/mobile/src/core/Colectibles/NFTCardItem.tsx b/packages/mobile/src/core/Colectibles/NFTCardItem.tsx index adade1249..44d1f6811 100644 --- a/packages/mobile/src/core/Colectibles/NFTCardItem.tsx +++ b/packages/mobile/src/core/Colectibles/NFTCardItem.tsx @@ -1,6 +1,6 @@ import { t } from '@tonkeeper/shared/i18n'; import { openNFT } from '$navigation'; -import { DarkTheme, Steezy } from '$styles'; +import { Steezy } from '$styles'; import { Icon, Pressable, View } from '$uikit'; import { checkIsTonDiamondsNFT } from '$utils'; import { useFlags } from '$utils/flags'; @@ -12,6 +12,7 @@ import { AnimationDirection, HideableAmount } from '$core/HideableAmount/Hideabl import { HideableImage } from '$core/HideableAmount/HideableImage'; import { Address } from '@tonkeeper/shared/Address'; import { DNS, KnownTLDs } from '@tonkeeper/core'; +import { useTheme } from '@tonkeeper/uikit'; interface NFTCardItemProps { item: any; @@ -46,10 +47,12 @@ export const NFTCardItem = memo((props) => { const nftRawAddress = useMemo(() => Address.parse(item.address).toRaw(), []); + const theme = useTheme(); + return ( @@ -60,7 +63,7 @@ export const NFTCardItem = memo((props) => { {isTonDiamondsNft && !flags.disable_apperance ? ( - + ) : null} @@ -122,6 +125,6 @@ const styles = Steezy.create(({ colors, corners }) => ({ height: '100%', borderTopLeftRadius: 16, borderTopRightRadius: 16, - background: DarkTheme.backgroundTertiary, + background: colors.backgroundTertiary, }, })); diff --git a/packages/mobile/src/core/CreatePin/CreatePin.tsx b/packages/mobile/src/core/CreatePin/CreatePin.tsx index 07c69aa1f..6b392beea 100644 --- a/packages/mobile/src/core/CreatePin/CreatePin.tsx +++ b/packages/mobile/src/core/CreatePin/CreatePin.tsx @@ -9,7 +9,7 @@ import { CreatePinForm } from '$shared/components'; import { tk } from '$wallet'; import { popToTop } from '$navigation/imperative'; import { useParams } from '@tonkeeper/router/src/imperative'; -import { BlockingLoader } from '@tonkeeper/uikit'; +import { BlockingLoader, Screen } from '@tonkeeper/uikit'; export const CreatePin: FC = () => { const params = useParams<{ isImport?: boolean }>(); @@ -44,9 +44,11 @@ export const CreatePin: FC = () => { ); return ( - - - - + + + + + + ); }; diff --git a/packages/mobile/src/core/CustomizeWallet/CustomizeWallet.tsx b/packages/mobile/src/core/CustomizeWallet/CustomizeWallet.tsx index a12a629a1..fdb3f146b 100644 --- a/packages/mobile/src/core/CustomizeWallet/CustomizeWallet.tsx +++ b/packages/mobile/src/core/CustomizeWallet/CustomizeWallet.tsx @@ -39,6 +39,8 @@ import LinearGradient from 'react-native-linear-gradient'; import { RouteProp } from '@react-navigation/native'; import { AppStackParamList } from '$navigation/AppStack'; import { AppStackRouteNames } from '$navigation'; +import { convertHexToRGBA } from '$utils'; +import { useThemeName } from '$hooks/useThemeName'; const COLORS_LIST = Object.values(WalletColor); @@ -57,6 +59,7 @@ export const CustomizeWallet: FC = memo((props) => { const wallet = useWallet(); const nav = useNavigation(); const theme = useTheme(); + const themeName = useThemeName(); const [name, setName] = useState( identifiers.length > 1 ? wallet.config.name.slice(0, -5) : wallet.config.name, @@ -133,7 +136,7 @@ export const CustomizeWallet: FC = memo((props) => { }, []); return ( - + @@ -161,10 +164,15 @@ export const CustomizeWallet: FC = memo((props) => { - + @@ -189,7 +197,7 @@ export const CustomizeWallet: FC = memo((props) => { {color === selectedColor ? ( @@ -207,7 +215,10 @@ export const CustomizeWallet: FC = memo((props) => { ({ width: 30, height: 30, borderRadius: 30 / 2, - borderColor: colors.backgroundPage, + borderColor: colors.backgroundPageAlternate, borderWidth: 5, }, buttonContainer: { diff --git a/packages/mobile/src/core/CustomizeWallet/EmojiPicker/EmojiPicker.tsx b/packages/mobile/src/core/CustomizeWallet/EmojiPicker/EmojiPicker.tsx index 8a14f9842..9beb17c8d 100644 --- a/packages/mobile/src/core/CustomizeWallet/EmojiPicker/EmojiPicker.tsx +++ b/packages/mobile/src/core/CustomizeWallet/EmojiPicker/EmojiPicker.tsx @@ -1,6 +1,6 @@ -import { isAndroid } from '$utils'; +import { changeAlphaValue, convertHexToRGBA, isAndroid } from '$utils'; import { FlashList } from '@shopify/flash-list'; -import { Steezy, View, WalletIcon, ns } from '@tonkeeper/uikit'; +import { Steezy, View, WalletIcon, ns, useTheme } from '@tonkeeper/uikit'; import { WALLET_ICONS } from '@tonkeeper/uikit/src/utils/walletIcons'; import React, { memo, useCallback } from 'react'; import { TouchableOpacity } from 'react-native'; @@ -36,6 +36,10 @@ export const EmojiPicker: React.FC = memo(({ onChange }) => { [onChange], ); + const theme = useTheme(); + + const rgbaColor = convertHexToRGBA(theme.backgroundPageAlternate); + return ( = memo(({ onChange }) => { /> @@ -136,6 +106,6 @@ const styles = Steezy.create(({ colors }) => ({ left: 0, right: 0, height: 20, - backgroundColor: colors.backgroundPage, + backgroundColor: colors.backgroundPageAlternate, }, })); diff --git a/packages/mobile/src/core/DAppBrowser/DAppBrowser.tsx b/packages/mobile/src/core/DAppBrowser/DAppBrowser.tsx index 53093a6c5..7fae81bef 100644 --- a/packages/mobile/src/core/DAppBrowser/DAppBrowser.tsx +++ b/packages/mobile/src/core/DAppBrowser/DAppBrowser.tsx @@ -2,7 +2,7 @@ import { useDeeplinking } from '$libs/deeplinking'; import { openDAppsSearch } from '$navigation'; import { getCorrectUrl, getSearchQuery, getUrlWithoutTonProxy } from '$utils'; import React, { FC, memo, useCallback, useState } from 'react'; -import { Linking, useWindowDimensions } from 'react-native'; +import { Linking, StatusBar, useWindowDimensions } from 'react-native'; import { useAnimatedStyle, useSharedValue, withTiming } from 'react-native-reanimated'; import { ShouldStartLoadRequest, @@ -16,6 +16,7 @@ import { useDAppBridge } from './hooks/useDAppBridge'; import { useWallet } from '@tonkeeper/shared/hooks'; import { Address } from '@tonkeeper/shared/Address'; import { config } from '$config'; +import { Screen, isIOS } from '@tonkeeper/uikit'; export interface DAppBrowserProps { url: string; @@ -144,7 +145,8 @@ const DAppBrowserComponent: FC = (props) => { }, [currentUrl, initialUrl, openUrl]); return ( - + + {isIOS ? : null} = (props) => { /> - + ); }; diff --git a/packages/mobile/src/core/DAppBrowser/components/BrowserNavBar/BrowserNavBar.style.ts b/packages/mobile/src/core/DAppBrowser/components/BrowserNavBar/BrowserNavBar.style.ts index 52f26393c..3651d22af 100644 --- a/packages/mobile/src/core/DAppBrowser/components/BrowserNavBar/BrowserNavBar.style.ts +++ b/packages/mobile/src/core/DAppBrowser/components/BrowserNavBar/BrowserNavBar.style.ts @@ -76,7 +76,7 @@ export const ActionsContainer = styled.View` flex-direction: row; flex: 1; height: ${ns(32)}px; - background: ${({ theme }) => theme.colors.backgroundSecondary}; + background: ${({ theme }) => theme.colors.buttonSecondaryBackground}; border-radius: ${ns(16)}px; overflow: hidden; `; @@ -101,7 +101,7 @@ export const ActionItemContent = styled.View` export const ActionsDivider = styled.View` width: 1px; margin: ${ns(8)}px 0; - background: ${({ theme }) => theme.colors.backgroundTertiary}; + background: ${({ theme }) => theme.colors.separatorCommon}; `; export const BackButtonTouchable = styled.TouchableOpacity.attrs({ @@ -109,7 +109,7 @@ export const BackButtonTouchable = styled.TouchableOpacity.attrs({ })``; export const BackButton = styled.View` - background: ${({ theme }) => theme.colors.backgroundSecondary}; + background: ${({ theme }) => theme.colors.buttonSecondaryBackground}; width: ${ns(32)}px; height: ${ns(32)}px; border-radius: ${ns(32 / 2)}px; diff --git a/packages/mobile/src/core/DAppsCategory/DAppsCategory.tsx b/packages/mobile/src/core/DAppsCategory/DAppsCategory.tsx index cb3e7963a..9e6f4b90d 100644 --- a/packages/mobile/src/core/DAppsCategory/DAppsCategory.tsx +++ b/packages/mobile/src/core/DAppsCategory/DAppsCategory.tsx @@ -58,11 +58,12 @@ const DAppsCategoryComponent: FC = (props) => { export const DAppsCategory = memo(DAppsCategoryComponent); -const styles = Steezy.create(() => ({ +const styles = Steezy.create(({ colors }) => ({ contentContainerStyle: { paddingBottom: 0, }, searchBarContainer: { + backgroundColor: colors.backgroundPageAlternate, padding: 16, position: 'relative', }, diff --git a/packages/mobile/src/core/DAppsExplore/DAppsExplore.tsx b/packages/mobile/src/core/DAppsExplore/DAppsExplore.tsx index ccaffa18b..0f7931c0f 100644 --- a/packages/mobile/src/core/DAppsExplore/DAppsExplore.tsx +++ b/packages/mobile/src/core/DAppsExplore/DAppsExplore.tsx @@ -19,7 +19,7 @@ import { AppsCategory, } from './components'; import type { NativeStackScreenProps } from '@react-navigation/native-stack'; -import { Screen, SegmentedControl, Steezy, View } from '@tonkeeper/uikit'; +import { Screen, SegmentedControl, Spacer, Steezy, View } from '@tonkeeper/uikit'; import { shallow } from 'zustand/shallow'; import { BrowserStackParamList } from '$navigation/BrowserStack/BrowserStack.interface'; import { t } from '@tonkeeper/shared/i18n'; @@ -109,7 +109,7 @@ const DAppsExploreComponent: FC = () => { }); return ( - + @@ -119,6 +119,7 @@ const DAppsExploreComponent: FC = () => { /> } + alternateBackground={segmentIndex === 1} > setSegmentIndex(segment)} @@ -147,8 +148,9 @@ const DAppsExploreComponent: FC = () => { + - + @@ -182,6 +184,7 @@ const styles = Steezy.create(({ colors }) => ({ overflow: 'hidden', }, searchBarContainer: { + backgroundColor: colors.backgroundPageAlternate, padding: 16, position: 'relative', }, diff --git a/packages/mobile/src/core/DAppsExplore/components/FeaturedApps/FeaturedApps.tsx b/packages/mobile/src/core/DAppsExplore/components/FeaturedApps/FeaturedApps.tsx index 8dfaa19e6..83c9fabb2 100644 --- a/packages/mobile/src/core/DAppsExplore/components/FeaturedApps/FeaturedApps.tsx +++ b/packages/mobile/src/core/DAppsExplore/components/FeaturedApps/FeaturedApps.tsx @@ -37,6 +37,7 @@ const CarouselItem = memo(({ metadata }) => { {metadata.name} @@ -44,6 +45,7 @@ const CarouselItem = memo(({ metadata }) => { void; @@ -10,14 +12,30 @@ interface Props { const SearchButtonComponent: FC = (props) => { const { onPress } = props; + const containerStyle = Steezy.useStyle(styles.container); + return ( - - + + - {t('browser.search_label')} - - + + {t('browser.search_label')} + + ); }; export const SearchButton = memo(SearchButtonComponent); + +const styles = Steezy.create(({ colors, corners }) => ({ + container: { + backgroundColor: colors.backgroundContentAlternate, + borderRadius: corners.medium, + }, + wrap: { + height: 48, + paddingHorizontal: 16, + flexDirection: 'row', + alignItems: 'center', + }, +})); diff --git a/packages/mobile/src/core/DAppsSearch/components/SearchBar/SearchBar.tsx b/packages/mobile/src/core/DAppsSearch/components/SearchBar/SearchBar.tsx index 755e0c988..883fab055 100644 --- a/packages/mobile/src/core/DAppsSearch/components/SearchBar/SearchBar.tsx +++ b/packages/mobile/src/core/DAppsSearch/components/SearchBar/SearchBar.tsx @@ -51,6 +51,7 @@ const SearchBarComponent: FC = (props) => { onSubmitEditing={handleSubmit} selectTextOnFocus={true} selectionColor={theme.colors.accentPrimary} + keyboardAppearance={theme.isDark ? 'dark' : 'light'} /> diff --git a/packages/mobile/src/core/HideableAmount/ShowBalance.tsx b/packages/mobile/src/core/HideableAmount/ShowBalance.tsx index b6890c3c1..78b99613d 100644 --- a/packages/mobile/src/core/HideableAmount/ShowBalance.tsx +++ b/packages/mobile/src/core/HideableAmount/ShowBalance.tsx @@ -4,27 +4,51 @@ import { usePrivacyStore } from '$store/zustand/privacy/usePrivacyStore'; import { Steezy } from '$styles'; import { Pressable, View } from '$uikit'; import { Haptics, isAndroid } from '$utils'; -import { DarkTheme } from '$styled'; -import { Text } from '@tonkeeper/uikit'; +import { Icon, Text, useTheme } from '@tonkeeper/uikit'; +import { DangerLevel } from '@tonkeeper/shared/hooks'; +import { useNavigation } from '@tonkeeper/router'; +import { SettingsStackRouteNames } from '$navigation'; +import { BatteryIcon } from '@tonkeeper/shared/components/BatteryIcon/BatteryIcon'; const TouchableComponent = isAndroid ? Pressable : TouchableHighlight; -export const ShowBalance: React.FC<{ amount: string }> = ({ amount }) => { +const getColorByDangerLevel = ( + dangerLevel: DangerLevel, +): undefined | 'accentOrange' | 'accentRed' => { + switch (dangerLevel) { + case DangerLevel.Normal: + return undefined; + case DangerLevel.Medium: + return 'accentOrange'; + case DangerLevel.High: + return 'accentRed'; + } +}; + +export const ShowBalance: React.FC<{ + amount: string; + dangerLevel: DangerLevel; + isWatchOnly: boolean; +}> = ({ amount, dangerLevel, isWatchOnly }) => { const hideAmounts = usePrivacyStore((state) => state.actions.toggleHiddenAmounts); const isHidden = usePrivacyStore((state) => state.hiddenAmounts); + const nav = useNavigation(); const handleToggleHideAmounts = useCallback(() => { hideAmounts(); Haptics.impactHeavy(); }, [hideAmounts]); + const handleNavigateToBackup = () => nav.navigate(SettingsStackRouteNames.Backup); + const theme = useTheme(); + return ( {isHidden ? ( @@ -34,7 +58,23 @@ export const ShowBalance: React.FC<{ amount: string }> = ({ amount }) => { ) : ( - {amount} + + {amount} + + + )} + {!isWatchOnly && } + {dangerLevel !== DangerLevel.Normal && ( + + )} @@ -46,6 +86,7 @@ const styles = Steezy.create(({ colors }) => ({ flexDirection: 'row', height: 54, alignItems: 'center', + gap: 8, }, starsContainer: { height: 40, @@ -59,4 +100,8 @@ const styles = Steezy.create(({ colors }) => ({ stars: { paddingTop: 8, }, + dangerButton: { + paddingTop: 21, + paddingBottom: 11, + }, })); diff --git a/packages/mobile/src/core/ImportWallet/ImportWallet.tsx b/packages/mobile/src/core/ImportWallet/ImportWallet.tsx index 588dd9151..020d5523c 100644 --- a/packages/mobile/src/core/ImportWallet/ImportWallet.tsx +++ b/packages/mobile/src/core/ImportWallet/ImportWallet.tsx @@ -13,6 +13,7 @@ import { useImportWallet } from '$hooks/useImportWallet'; import { tk } from '$wallet'; import { ImportWalletInfo } from '$wallet/WalletTypes'; import { DEFAULT_WALLET_VERSION } from '$wallet/constants'; +import { Screen } from '@tonkeeper/uikit'; export const ImportWallet: FC<{ route: RouteProp; @@ -60,9 +61,11 @@ export const ImportWallet: FC<{ ); return ( - - - - + + + + + + ); }; diff --git a/packages/mobile/src/core/Jetton/Jetton.style.ts b/packages/mobile/src/core/Jetton/Jetton.style.ts index 06077fa30..188d4925d 100644 --- a/packages/mobile/src/core/Jetton/Jetton.style.ts +++ b/packages/mobile/src/core/Jetton/Jetton.style.ts @@ -122,5 +122,5 @@ export const HeaderViewDetailsButton = styled(TouchableOpacity).attrs({ border-radius: ${ns(32 / 2)}px; align-items: center; justify-content: center; - background: ${({ theme }) => theme.colors.backgroundSecondary}; + background: ${({ theme }) => theme.colors.buttonSecondaryBackground}; `; diff --git a/packages/mobile/src/core/ModalContainer/AppearanceModal/AppearanceModal.tsx b/packages/mobile/src/core/ModalContainer/AppearanceModal/AppearanceModal.tsx index 212519c0c..6b50ea522 100644 --- a/packages/mobile/src/core/ModalContainer/AppearanceModal/AppearanceModal.tsx +++ b/packages/mobile/src/core/ModalContainer/AppearanceModal/AppearanceModal.tsx @@ -1,6 +1,5 @@ import React, { memo, useCallback, useEffect, useMemo, useRef, useState } from 'react'; import { useDimensions } from '$hooks/useDimensions'; -import { accentSelector } from '$store/main'; import { NFTModel, TonDiamondMetadata } from '$store/models'; import { AccentKey, @@ -179,7 +178,6 @@ const AppearanceModal = memo((props) => { /> = (props) => { - const { accents, selectedAccentIndex, isUnavailableAccent, ...buttonProps } = props; - - const theme = useTheme(); + const { accents, selectedAccentIndex, ...buttonProps } = props; const colors = useMemo( () => [ @@ -55,16 +52,12 @@ const CustomButtonComponent: FC = (props) => { default: accent.colors.accentPrimary, pressed: accent.colors.accentPrimaryLight, })), - { - default: theme.colors.backgroundSecondary, - pressed: theme.colors.backgroundTertiary, - }, ], - [accents, theme.colors], + [accents], ); const animation = useDerivedValue( - () => [...accents.map((_, i) => i === selectedAccentIndex), isUnavailableAccent], + () => [...accents.map((_, i) => i === selectedAccentIndex)], [selectedAccentIndex], ); const [isPressed, setPressed] = useState(false); diff --git a/packages/mobile/src/core/NFT/ImageWithTitle/ImageWithTitle.tsx b/packages/mobile/src/core/NFT/ImageWithTitle/ImageWithTitle.tsx index 119e43b62..3bb9002bf 100644 --- a/packages/mobile/src/core/NFT/ImageWithTitle/ImageWithTitle.tsx +++ b/packages/mobile/src/core/NFT/ImageWithTitle/ImageWithTitle.tsx @@ -1,7 +1,7 @@ import React, { useCallback, useMemo } from 'react'; import * as S from './ImageWithTitle.style'; import { ImageWithTitleProps } from '$core/NFT/ImageWithTitle/ImageWithTitle.interface'; -import { Badge, Icon, Separator, ShowMore, Spacer, Text, View } from '$uikit'; +import { Badge, Separator, ShowMore, Spacer, Text, View } from '$uikit'; import { t } from '@tonkeeper/shared/i18n'; import { isIOS, ns } from '$utils'; import Clipboard from '@react-native-community/clipboard'; @@ -9,6 +9,7 @@ import { Toast } from '$store'; import { HideableImage } from '$core/HideableAmount/HideableImage'; import { Steezy } from '$styles'; import { HideableAmount } from '$core/HideableAmount/HideableAmount'; +import { Icon } from '@tonkeeper/uikit'; export const ImageWithTitle: React.FC = ({ uri, @@ -108,7 +109,7 @@ export const ImageWithTitle: React.FC = ({ : collection || t('nft_unnamed_collection')} - {isVerified && } + {isVerified && } {description ? ( diff --git a/packages/mobile/src/core/NFT/RenewDomainButton.tsx b/packages/mobile/src/core/NFT/RenewDomainButton.tsx index 9b15e3815..666b1454e 100644 --- a/packages/mobile/src/core/NFT/RenewDomainButton.tsx +++ b/packages/mobile/src/core/NFT/RenewDomainButton.tsx @@ -16,6 +16,7 @@ import { Base64 } from '$utils'; import { Address } from '@tonkeeper/core'; import { useWallet } from '@tonkeeper/shared/hooks'; import { tk } from '$wallet'; +import { Spacer } from '@tonkeeper/uikit'; export type RenewDomainButtonRef = { renewUpdated: () => void; @@ -121,6 +122,7 @@ export const RenewDomainButton = forwardRef {t('dns_renew_valid_caption', { count: days })} + ); }, diff --git a/packages/mobile/src/core/NFT/TonDiamondFeature/FeatureButton/FeatureButton.style.ts b/packages/mobile/src/core/NFT/TonDiamondFeature/FeatureButton/FeatureButton.style.ts index 3edc3eab6..94b6c3ad0 100644 --- a/packages/mobile/src/core/NFT/TonDiamondFeature/FeatureButton/FeatureButton.style.ts +++ b/packages/mobile/src/core/NFT/TonDiamondFeature/FeatureButton/FeatureButton.style.ts @@ -1,5 +1,5 @@ import styled from '$styled'; -import { Highlight, Text } from '$uikit'; +import { Highlight } from '$uikit'; import { nfs, ns } from '$utils'; export const Wrap = styled.View` @@ -23,7 +23,3 @@ export const InnerContainer = styled.View<{ withIcon?: boolean }>` export const IconContainer = styled.View` margin-right: ${ns(8)}px; `; - -export const Title = styled(Text).attrs({ variant: 'label2' })` - color: ${({ theme }) => theme.colors.foregroundPrimary}; -`; diff --git a/packages/mobile/src/core/NFT/TonDiamondFeature/FeatureButton/FeatureButton.tsx b/packages/mobile/src/core/NFT/TonDiamondFeature/FeatureButton/FeatureButton.tsx index 0d678f29f..da6ed1b9c 100644 --- a/packages/mobile/src/core/NFT/TonDiamondFeature/FeatureButton/FeatureButton.tsx +++ b/packages/mobile/src/core/NFT/TonDiamondFeature/FeatureButton/FeatureButton.tsx @@ -2,6 +2,7 @@ import React from 'react'; import { FeatureButtonProps } from './FeatureButton.interface'; import * as S from './FeatureButton.style'; import { Icon } from '$uikit'; +import { Text } from '@tonkeeper/uikit'; export const FeatureButton: React.FC = (props) => { const { title, iconName, color, highlightColor, onPress } = props; @@ -11,14 +12,11 @@ export const FeatureButton: React.FC = (props) => { - {iconName ? ( - - ) : null} + {iconName ? : null} - {title} + + {title} + diff --git a/packages/mobile/src/core/ScanQR/ScanQR.tsx b/packages/mobile/src/core/ScanQR/ScanQR.tsx index 50c59e567..4b9bc9cd4 100644 --- a/packages/mobile/src/core/ScanQR/ScanQR.tsx +++ b/packages/mobile/src/core/ScanQR/ScanQR.tsx @@ -8,7 +8,7 @@ import { request, openSettings, } from 'react-native-permissions'; -import { Platform } from 'react-native'; +import { Platform, StatusBar } from 'react-native'; import { ScanQRProps } from './ScanQR.interface'; import * as S from './ScanQR.style'; import { Button, Icon, NavBar, Text } from '$uikit'; @@ -32,7 +32,6 @@ export const ScanQR: FC = ({ route }) => { const scannerRef = useRef(null); const theme = useTheme(); - const [isFlashlightOn, setFlashlightOn] = useState(false); const [isCameraBlocked, setCameraBlocked] = useState(false); @@ -122,14 +121,11 @@ export const ScanQR: FC = ({ route }) => { return ( + {isIOS ? : null} {isIOS && } - + {isHasPermission && ( @@ -160,7 +156,7 @@ export const ScanQR: FC = ({ route }) => { textAlign="center" numberOfLines={1} allowFontScaling={false} - color="foregroundPrimary" + color="constantLight" variant="h2" > {t('scan_qr_title')} @@ -207,7 +203,7 @@ export const ScanQR: FC = ({ route }) => { {isIOS && } diff --git a/packages/mobile/src/core/Security/Security.tsx b/packages/mobile/src/core/Security/Security.tsx index 8e704f244..40f48eba3 100644 --- a/packages/mobile/src/core/Security/Security.tsx +++ b/packages/mobile/src/core/Security/Security.tsx @@ -9,7 +9,7 @@ import { MainStackRouteNames, openChangePin } from '$navigation'; import { getBiometryName, ns } from '$utils'; import { Toast } from '$store'; import { t } from '@tonkeeper/shared/i18n'; -import { useBiometrySettings, useWallet } from '@tonkeeper/shared/hooks'; +import { useBiometrySettings, useLockSettings, useWallet } from '@tonkeeper/shared/hooks'; import { useNavigation } from '@tonkeeper/router'; import { vault } from '$wallet'; import { Haptics, Switch } from '@tonkeeper/uikit'; @@ -22,6 +22,8 @@ export const Security: FC = () => { const biometry = useBiometrySettings(); + const { lockScreenEnabled, toggleLock } = useLockSettings(); + const handleCopyLockupConfig = useCallback(() => { try { Clipboard.setString(JSON.stringify(wallet.getLockupConfig())); @@ -98,6 +100,19 @@ export const Security: FC = () => { scrollEventThrottle={16} > {renderBiometryToggler()} + + } + > + {t('security_lock_screen_switch')} + + + + + {t('security_lock_screen_tip')} + + {t('security_change_passcode')} diff --git a/packages/mobile/src/core/Send/steps/AddressStep/AddressStep.tsx b/packages/mobile/src/core/Send/steps/AddressStep/AddressStep.tsx index cad9853d0..47786802b 100644 --- a/packages/mobile/src/core/Send/steps/AddressStep/AddressStep.tsx +++ b/packages/mobile/src/core/Send/steps/AddressStep/AddressStep.tsx @@ -97,10 +97,11 @@ const AddressStepComponent: FC = (props) => { if (resolvedDomain === 'aborted') { return 'aborted'; } else if (resolvedDomain?.wallet?.address) { + const isWallet = !!resolvedDomain?.wallet?.account?.is_wallet; return new TonWeb.Address(resolvedDomain.wallet.address).toString( true, true, - false, + !isWallet, ) as string; } diff --git a/packages/mobile/src/core/Send/steps/AddressStep/components/AddressInput/AddressInput.tsx b/packages/mobile/src/core/Send/steps/AddressStep/components/AddressInput/AddressInput.tsx index df6355eaf..8f8abe28d 100644 --- a/packages/mobile/src/core/Send/steps/AddressStep/components/AddressInput/AddressInput.tsx +++ b/packages/mobile/src/core/Send/steps/AddressStep/components/AddressInput/AddressInput.tsx @@ -192,7 +192,9 @@ const AddressInputComponent: FC = (props) => { const preparedAddress = recipient && (recipient.name || recipient.domain) - ? Address.parse(recipient.address, { bounceable: false }).toShort() + ? Address.parse(recipient.address, { + bounceable: Address.isBounceable(recipient.address), + }).toShort() : ''; const isFirstRender = useRef(true); diff --git a/packages/mobile/src/core/Settings/Settings.tsx b/packages/mobile/src/core/Settings/Settings.tsx index ba607996e..7aea35bb9 100644 --- a/packages/mobile/src/core/Settings/Settings.tsx +++ b/packages/mobile/src/core/Settings/Settings.tsx @@ -25,17 +25,11 @@ import { openRefillBatteryModal, } from '$navigation'; import { walletActions } from '$store/wallet'; -import { - APPLE_STORE_ID, - GOOGLE_PACKAGE_NAME, - LargeNavBarHeight, - IsTablet, -} from '$shared/constants'; -import { checkIsTonDiamondsNFT, hNs, ns, throttle } from '$utils'; -import { LargeNavBarInteractiveDistance } from '$uikit/LargeNavBar/LargeNavBar'; +import { APPLE_STORE_ID, GOOGLE_PACKAGE_NAME } from '$shared/constants'; +import { checkIsTonDiamondsNFT, throttle } from '$utils'; import { CellSectionItem } from '$shared/components'; import { useFlags } from '$utils/flags'; -import { SearchEngine, useBrowserStore } from '$store'; +import { SearchEngine, ThemeOptions, useAppThemeStore, useBrowserStore } from '$store'; import AnimatedLottieView from 'lottie-react-native'; import { Steezy } from '$styles'; import { i18n, t } from '@tonkeeper/shared/i18n'; @@ -85,6 +79,9 @@ export const Settings: FC = () => { const searchEngine = useBrowserStore((state) => state.searchEngine); const setSearchEngine = useBrowserStore((state) => state.actions.setSearchEngine); + const selectedTheme = useAppThemeStore((state) => state.selectedTheme); + const setSelectedTheme = useAppThemeStore((state) => state.actions.setSelectedTheme); + // eslint-disable-next-line react-hooks/exhaustive-deps const handleAnimateDiamond = useCallback( throttle(() => { @@ -152,6 +149,8 @@ export const Settings: FC = () => { const searchEngineVariants = Object.values(SearchEngine); + const themeVariants = Object.values(ThemeOptions); + const handleSwitchLanguage = useCallback(() => { if (Platform.OS === 'android') { return openSelectLanguage(); @@ -374,6 +373,25 @@ export const Settings: FC = () => { onPress={handleSecurity} /> )} + item} + width={176} + renderItem={(item) => ( + {t(`settings_theme_names.${item}`)} + )} + > + + {t(`settings_theme_names.${selectedTheme}`)} + + } + title={t('settings_theme')} + /> + = (props) => { }, [handleDone, identifiers]); return ( - + = (props) => { onPress={handleDone} /> } + alternateBackground /> diff --git a/packages/mobile/src/core/Staking/Staking.tsx b/packages/mobile/src/core/Staking/Staking.tsx index dec0220fa..da22d693f 100644 --- a/packages/mobile/src/core/Staking/Staking.tsx +++ b/packages/mobile/src/core/Staking/Staking.tsx @@ -18,7 +18,7 @@ import { t } from '@tonkeeper/shared/i18n'; import { Address } from '@tonkeeper/shared/Address'; import { PoolImplementationType } from '@tonkeeper/core/src/TonAPI'; import { CryptoCurrencies, Decimals } from '$shared/constants'; -import { Flash } from '@tonkeeper/uikit'; +import { Flash, Screen } from '@tonkeeper/uikit'; import { Ton } from '$libs/Ton'; import { useBalancesState, useJettons, useStakingState } from '@tonkeeper/shared/hooks'; import { StakingManager, StakingProvider } from '$wallet/managers/StakingManager'; @@ -205,11 +205,10 @@ export const Staking: FC = () => { ); return ( - - + = () => { /> ) : null } + /> + } + showsVerticalScrollIndicator={false} > - } - showsVerticalScrollIndicator={false} - > - - {notificationsStore?.showRestakeBanner && - notificationsStore?.stakingAddressToMigrateFrom && ( - <> - - - - )} - {!hasActivePools ? ( - - {t('staking.title_large')} - - - {t('staking.desc_large')}{' '} - - {t('staking.learn_more')} - - - - - ) : null} - {hasActivePools ? ( + + {notificationsStore?.showRestakeBanner && + notificationsStore?.stakingAddressToMigrateFrom && ( <> - - {t('staking.active')} - - - {activePools.map((pool, index) => ( - - ))} - + - ) : null} - {hasActivePools ? ( + )} + {!hasActivePools ? ( + + {t('staking.title_large')} + + + {t('staking.desc_large')}{' '} + + {t('staking.learn_more')} + + + + + ) : null} + {hasActivePools ? ( + <> - {t('staking.other')} + {t('staking.active')} - ) : null} - {data.recommendedList.length > 0 ? ( - <> - - {data.recommendedList.map((provider, index) => ( - = 2}> - - - ))} - - - - ) : null} - {data.otherList.length > 0 ? ( - <> - - {data.otherList.map((provider, index) => ( + + {activePools.map((pool, index) => ( + + ))} + + + + ) : null} + {hasActivePools ? ( + + {t('staking.other')} + + ) : null} + {data.recommendedList.length > 0 ? ( + <> + + {data.recommendedList.map((provider, index) => ( + = 2}> = () => { highestApy={ highestApyPool && highestApyPool.implementation === provider.id } - separator={index < data.otherList.length - 1} + message={getEstimateProfitMessage(provider)} + separator={index < data.recommendedList.length - 1} onPress={handleProviderPress} /> - ))} - - - - ) : null} - - - - + + ))} + + + + ) : null} + {data.otherList.length > 0 ? ( + <> + + {data.otherList.map((provider, index) => ( + + ))} + + + + ) : null} + + + ); }; diff --git a/packages/mobile/src/core/Swap/Swap.style.ts b/packages/mobile/src/core/Swap/Swap.style.ts index 69c7bf324..47c7e52d8 100644 --- a/packages/mobile/src/core/Swap/Swap.style.ts +++ b/packages/mobile/src/core/Swap/Swap.style.ts @@ -30,7 +30,7 @@ export const BackButtonContainer = styled.TouchableOpacity.attrs({ })``; export const BackButton = styled.View` - background: ${({ theme }) => theme.colors.backgroundSecondary}; + background: ${({ theme }) => theme.colors.buttonSecondaryBackground}; height: ${hNs(32)}px; width: ${ns(32)}px; border-radius: ${ns(32 / 2)}px; diff --git a/packages/mobile/src/core/TonConnect/TonConnect.style.ts b/packages/mobile/src/core/TonConnect/TonConnect.style.ts index d8ce5fb6f..09671d2a5 100644 --- a/packages/mobile/src/core/TonConnect/TonConnect.style.ts +++ b/packages/mobile/src/core/TonConnect/TonConnect.style.ts @@ -62,7 +62,7 @@ export const VerticalDivider = styled.View` `; export const Logo = styled.View` - background-color: ${({ theme }) => theme.colors.backgroundSecondary}; + background-color: #1d2633; border-radius: ${({ theme }) => theme.radius.large}px; width: ${ns(72)}px; height: ${ns(72)}px; diff --git a/packages/mobile/src/core/TonConnect/TonConnectModal.tsx b/packages/mobile/src/core/TonConnect/TonConnectModal.tsx index 3bbe8e2c6..4b1b3d098 100644 --- a/packages/mobile/src/core/TonConnect/TonConnectModal.tsx +++ b/packages/mobile/src/core/TonConnect/TonConnectModal.tsx @@ -6,6 +6,7 @@ import { Linking, StyleSheet } from 'react-native'; import { useTheme } from '$hooks/useTheme'; import { Button, List, Loader, Spacer, Text, TransitionOpacity } from '$uikit'; import { + convertHexToRGBA, delay, getDomainFromURL, triggerNotificationSuccess, @@ -280,12 +281,15 @@ export const TonConnectModal = (props: TonConnectModalProps) => { - + @@ -308,7 +312,10 @@ export const TonConnectModal = (props: TonConnectModalProps) => { diff --git a/packages/mobile/src/core/Wallet/Wallet.style.ts b/packages/mobile/src/core/Wallet/Wallet.style.ts index f7de96af4..23c0bf1ce 100644 --- a/packages/mobile/src/core/Wallet/Wallet.style.ts +++ b/packages/mobile/src/core/Wallet/Wallet.style.ts @@ -77,7 +77,7 @@ export const IconWrapper = styled.View` export const Divider = styled.View` height: ${ns(0.5)}px; width: ${deviceWidth}px; - background: rgba(79, 90, 112, 0.24); + background: ${({ theme }) => theme.colors.separatorCommon}; margin-bottom: ${ns(24)}px; `; @@ -133,5 +133,5 @@ export const HeaderViewDetailsButton = styled(TouchableOpacity).attrs({ border-radius: ${ns(32 / 2)}px; align-items: center; justify-content: center; - background: ${({ theme }) => theme.colors.backgroundSecondary}; + background: ${({ theme }) => theme.colors.buttonSecondaryBackground}; `; diff --git a/packages/mobile/src/core/WebView/WebView.tsx b/packages/mobile/src/core/WebView/WebView.tsx index 81cb25fb5..cdd249679 100644 --- a/packages/mobile/src/core/WebView/WebView.tsx +++ b/packages/mobile/src/core/WebView/WebView.tsx @@ -100,7 +100,7 @@ export const WebView: FC = ({ route }) => { hideKeyboardAccessoryView thirdPartyCookiesEnabled={true} allowFileAccess - forceDarkOn + forceDarkOn={theme.isDark} allowsInlineMediaPlayback allowsFullscreenVideo keyboardDisplayRequiresUserAction={false} diff --git a/packages/mobile/src/hooks/useThemeName.ts b/packages/mobile/src/hooks/useThemeName.ts new file mode 100644 index 000000000..de1451253 --- /dev/null +++ b/packages/mobile/src/hooks/useThemeName.ts @@ -0,0 +1,38 @@ +import { ThemeOptions, useAppThemeStore } from '$store/zustand/appTheme'; +import { ThemeName } from '@tonkeeper/uikit'; +import { useEffect, useMemo, useState } from 'react'; +import { useColorScheme } from 'react-native'; + +export const useThemeName = (): ThemeName => { + const { selectedTheme } = useAppThemeStore(); + const colorScheme = useColorScheme(); + const [systemAppearance, setSystemAppearance] = useState(colorScheme); + + useEffect(() => { + const timeout = setTimeout(() => { + setSystemAppearance(colorScheme); + }, 300); + + return () => { + clearTimeout(timeout); + }; + }, [colorScheme]); + + const themeName = useMemo(() => { + if (selectedTheme === ThemeOptions.System) { + return systemAppearance === 'light' ? ThemeName.Light : ThemeName.Dark; + } + + if (selectedTheme === ThemeOptions.Light) { + return ThemeName.Light; + } + + if (selectedTheme === ThemeOptions.Dark) { + return ThemeName.Dark; + } + + return ThemeName.Blue; + }, [selectedTheme, systemAppearance]); + + return themeName; +}; diff --git a/packages/mobile/src/modals/ExchangeModal.tsx b/packages/mobile/src/modals/ExchangeModal.tsx index 7f1653d21..d55622851 100644 --- a/packages/mobile/src/modals/ExchangeModal.tsx +++ b/packages/mobile/src/modals/ExchangeModal.tsx @@ -1,11 +1,11 @@ import React, { useCallback, useMemo, useState } from 'react'; -import { Button, Loader, Spacer, View } from '$uikit'; +import { Loader, Spacer, View } from '$uikit'; import * as S from '../core/Exchange/Exchange.style'; import { ExchangeItem } from '../core/Exchange/ExchangeItem/ExchangeItem'; import { t } from '@tonkeeper/shared/i18n'; import { LayoutAnimation } from 'react-native'; -import { Modal, SegmentedControl, Text } from '@tonkeeper/uikit'; +import { Button, Modal, SegmentedControl, Text } from '@tonkeeper/uikit'; import { Steezy } from '$styles'; import { useMethodsToBuyStore } from '$store/zustand/methodsToBuy/useMethodsToBuyStore'; import { CategoryType } from '$store/zustand/methodsToBuy/types'; @@ -124,12 +124,13 @@ export const ExchangeModal = () => { + /> ) : null} diff --git a/packages/mobile/src/navigation/AppNavigator.tsx b/packages/mobile/src/navigation/AppNavigator.tsx index 33f7f47cd..e75fa7b00 100644 --- a/packages/mobile/src/navigation/AppNavigator.tsx +++ b/packages/mobile/src/navigation/AppNavigator.tsx @@ -6,8 +6,8 @@ import { StatusBar } from 'react-native'; import { setNavigationRef, onNavigationReady } from './imperative'; import { AppStack } from './MainStack'; import { mainSelector } from '$store/main'; -import { useTheme } from '$hooks/useTheme'; import { ProvidersWithoutNavigation } from './Providers'; +import { isAndroid, useTheme } from '@tonkeeper/uikit'; export const AppNavigator: FC = () => { const theme = useTheme(); @@ -17,11 +17,11 @@ export const AppNavigator: FC = () => { () => ({ dark: true, colors: { - primary: theme.colors.accentPrimary, - background: theme.colors.backgroundPrimary, - card: theme.colors.backgroundPrimary, - text: theme.colors.constantLight, - notification: theme.colors.backgroundPrimary, + primary: theme.accentBlue, + background: theme.backgroundPage, + card: theme.backgroundPage, + text: theme.textPrimary, + notification: theme.backgroundPage, border: 'none', }, }), @@ -40,8 +40,8 @@ export const AppNavigator: FC = () => { > diff --git a/packages/mobile/src/navigation/CreateWalletStack/CreateWalletStack.tsx b/packages/mobile/src/navigation/CreateWalletStack/CreateWalletStack.tsx index 797abf20c..eb3b8a974 100644 --- a/packages/mobile/src/navigation/CreateWalletStack/CreateWalletStack.tsx +++ b/packages/mobile/src/navigation/CreateWalletStack/CreateWalletStack.tsx @@ -17,7 +17,7 @@ export const CreateWalletStack = memo(() => { headerShown: false, gestureEnabled: true, contentStyle: { - backgroundColor: theme.colors.backgroundPrimary, + backgroundColor: theme.colors.backgroundPage, }, fullScreenGestureEnabled: true, }} diff --git a/packages/mobile/src/navigation/MainStack/MainStack.tsx b/packages/mobile/src/navigation/MainStack/MainStack.tsx index 4260e3b72..d09f04240 100644 --- a/packages/mobile/src/navigation/MainStack/MainStack.tsx +++ b/packages/mobile/src/navigation/MainStack/MainStack.tsx @@ -1,4 +1,4 @@ -import React, { FC, useMemo } from 'react'; +import React, { FC } from 'react'; import { createNativeStackNavigator } from '@react-navigation/native-stack'; import { MainStackParamList } from './MainStack.interface'; @@ -24,7 +24,7 @@ import { withModalStack } from '@tonkeeper/router'; import { ToncoinScreen } from '$core/Wallet/ToncoinScreen'; import { InscriptionScreen } from '$core/InscriptionScreen'; import { useDiamondsChecker } from '$hooks/useDiamondsChecker'; -import { useWallet } from '@tonkeeper/shared/hooks'; +import { useLockSettings, useWallet } from '@tonkeeper/shared/hooks'; import { StartScreen, HoldersWebView, @@ -61,12 +61,20 @@ export const MainStack: FC = () => { const hasWallet = !!wallet; + const { lockScreenEnabled } = useLockSettings(); + + const shouldObtainTonProof = + hasWallet && !wallet.isWatchOnly && !wallet.tonProof.tonProofToken; + const showLockScreen = - tk.lockEnabled && !isUnlocked && hasWallet && !attachedScreen.pathname; + (lockScreenEnabled || shouldObtainTonProof) && + !isUnlocked && + hasWallet && + !attachedScreen.pathname; const isMigrated = useExternalState(tk.migrationStore, (state) => state.isMigrated); - const root = useMemo(() => { + const renderRoot = () => { if (hasWallet) { if (showLockScreen) { return ( @@ -90,7 +98,7 @@ export const MainStack: FC = () => { } return ; - }, [hasWallet, isMigrated, showLockScreen]); + }; return ( { fullScreenGestureEnabled: true, }} > - {root} - - - - - - - - - - - - - - - - - - - - - - + {renderRoot()} + + + + + + + + + + + + + + + + + + + + + + + + + + ); }; diff --git a/packages/mobile/src/navigation/MainStack/TabStack/TabStack.tsx b/packages/mobile/src/navigation/MainStack/TabStack/TabStack.tsx index 812f77c1a..3f1bcfd49 100644 --- a/packages/mobile/src/navigation/MainStack/TabStack/TabStack.tsx +++ b/packages/mobile/src/navigation/MainStack/TabStack/TabStack.tsx @@ -84,7 +84,11 @@ export const TabStack: FC = () => { ]} /> ) : ( - + { const nav = useNavigation(); const isValid = useMemo(() => { - const word1 = values['0']; - const word2 = values['1']; - const word3 = values['2']; + const word1 = values['0'] ? values['0'].trim() : ''; + const word2 = values['1'] ? values['1'].trim() : ''; + const word3 = values['2'] ? values['2'].trim() : ''; return word1 === words[0].word && word2 === words[1].word && word3 === words[2].word; }, [values, words]); @@ -72,8 +72,8 @@ export const BackupCheckPhraseScreen = memo(() => { ); return ( - - + + & ExtractMediaVars<{ isTablet: unknown }> + >; +} + +const defaultSizes: Sizes = { + title: 'h2', + caption: 'body1', + index: 'body2', + word: 'body1', + styles: Steezy.create({ + line: { + width: 151, + flexDirection: 'row', + marginBottom: 8, + height: 24, + }, + }), +}; + +const smallSizes: Sizes = { + title: 'h3', + caption: 'body2', + index: 'body3', + word: 'body2', + styles: Steezy.create({ + line: { + width: 151, + flexDirection: 'row', + marginBottom: 4, + height: 18, + }, + }), +}; + +const sizesConfig = deviceHeight >= 650 ? defaultSizes : smallSizes; function getRandIndexes(length: number, indexes: number[] = []) { if (indexes.length === length) { @@ -23,7 +70,6 @@ function getRandIndexes(length: number, indexes: number[] = []) { export const BackupPhraseScreen = memo(() => { const params = useParams<{ mnemonic: string; isBackupAgain?: boolean }>(); - const safeArea = useSafeAreaInsets(); const nav = useNavigation(); const mnemonic = params.mnemonic!; @@ -39,15 +85,15 @@ export const BackupPhraseScreen = memo(() => { }, [phrase]); return ( - - + + - + {t('recovery_phrase.title')} - + {t('recovery_phrase.caption')} @@ -56,21 +102,32 @@ export const BackupPhraseScreen = memo(() => { {leftColumn.map((word, index) => ( - - + + {index + 1}. - {word} + {word} ))} {rightColumn.map((word, index) => ( - - + + {index + 1 + 12}. - {word} + {word} ))} diff --git a/packages/mobile/src/screens/BackupScreen/BackupScreen.tsx b/packages/mobile/src/screens/BackupScreen/BackupScreen.tsx index d5632b91b..2bbf48667 100644 --- a/packages/mobile/src/screens/BackupScreen/BackupScreen.tsx +++ b/packages/mobile/src/screens/BackupScreen/BackupScreen.tsx @@ -4,16 +4,51 @@ import { memo } from 'react'; import { format } from 'date-fns'; import { getLocale } from '$utils/date'; import { i18n, t } from '@tonkeeper/shared/i18n'; -import { useWalletSetup } from '@tonkeeper/shared/hooks'; +import { + DangerLevel, + useDangerLevel, + useWalletCurrency, + useWalletSetup, +} from '@tonkeeper/shared/hooks'; +import { tk } from '$wallet'; +import { formatter } from '@tonkeeper/shared/formatter'; export const BackupScreen = memo(() => { const { lastBackupAt } = useWalletSetup(); const nav = useNavigation(); + const currency = useWalletCurrency(); + const dangerLevel = useDangerLevel(tk.wallet.totalTon); + return ( + {dangerLevel !== DangerLevel.Normal && ( + <> + + + {t('backup_screen.backup_warning', { + totalFiat: formatter.format(tk.wallet.totalFiat, { currency }), + })} + + + + + )} {t('backup_screen.manual_title')} @@ -27,7 +62,7 @@ export const BackupScreen = memo(() => { + + + {!hasTextInAnyOfInputs && ( + + + + )} + + ); }; + +const styles = Steezy.create({ + pasteButtonContainer: { + position: 'absolute', + alignItems: 'center', + left: 0, + right: 0, + }, +}); diff --git a/packages/mobile/src/shared/components/ImportWalletForm/InputItem.tsx b/packages/mobile/src/shared/components/ImportWalletForm/InputItem.tsx index ca060eb61..90902c002 100644 --- a/packages/mobile/src/shared/components/ImportWalletForm/InputItem.tsx +++ b/packages/mobile/src/shared/components/ImportWalletForm/InputItem.tsx @@ -52,6 +52,8 @@ export const InputItem = forwardRef((props, ref) = const handleChangeText = useCallback( (text) => { let newText = text.trim(); + newText = newText.replace(/\r\n|\r|\n/g, ' '); + if (newText.split(' ').length > 1) { onMultipleWords(index, newText); } else if (text.slice(-1) === ' ') { diff --git a/packages/mobile/src/shared/components/SwapButton/SwapButton.tsx b/packages/mobile/src/shared/components/SwapButton/SwapButton.tsx index b6c43c0ac..f59112620 100644 --- a/packages/mobile/src/shared/components/SwapButton/SwapButton.tsx +++ b/packages/mobile/src/shared/components/SwapButton/SwapButton.tsx @@ -85,8 +85,8 @@ const SwapButtonComponent: FC = (props) => { hitSlop={HIT_SLOP_INSETS} > - - + + ); diff --git a/packages/mobile/src/store/wallet/sagas.ts b/packages/mobile/src/store/wallet/sagas.ts index e91f47ddb..eb0ae288b 100644 --- a/packages/mobile/src/store/wallet/sagas.ts +++ b/packages/mobile/src/store/wallet/sagas.ts @@ -444,8 +444,6 @@ export function* walletGetUnlockedVault(action?: WalletGetUnlockedVaultAction) { ? tk.wallets.get(action.payload.walletIdentifier)! : tk.wallet; - const generatedVault = yield select(walletGeneratedVaultSelector); - let withoutBiometryOnOpen = false; if (tk.biometryEnabled) { @@ -464,9 +462,7 @@ export function* walletGetUnlockedVault(action?: WalletGetUnlockedVaultAction) { mnemonic, ); - if (generatedVault) { - setLastEnteredPasscode(passcode); - } + setLastEnteredPasscode(passcode); action?.payload?.onDone?.(unlockedVault); return unlockedVault; diff --git a/packages/mobile/src/store/zustand/appTheme/index.ts b/packages/mobile/src/store/zustand/appTheme/index.ts new file mode 100644 index 000000000..dd55148e3 --- /dev/null +++ b/packages/mobile/src/store/zustand/appTheme/index.ts @@ -0,0 +1,2 @@ +export * from './useAppTheme'; +export * from './types'; diff --git a/packages/mobile/src/store/zustand/appTheme/types.ts b/packages/mobile/src/store/zustand/appTheme/types.ts new file mode 100644 index 000000000..f28394a3f --- /dev/null +++ b/packages/mobile/src/store/zustand/appTheme/types.ts @@ -0,0 +1,13 @@ +export enum ThemeOptions { + Blue = 'blue', + Dark = 'dark', + Light = 'light', + System = 'system', +} + +export interface IAppThemeStore { + selectedTheme: ThemeOptions; + actions: { + setSelectedTheme: (selectedTheme: ThemeOptions) => void; + }; +} diff --git a/packages/mobile/src/store/zustand/appTheme/useAppTheme.ts b/packages/mobile/src/store/zustand/appTheme/useAppTheme.ts new file mode 100644 index 000000000..e68cfb590 --- /dev/null +++ b/packages/mobile/src/store/zustand/appTheme/useAppTheme.ts @@ -0,0 +1,26 @@ +import AsyncStorage from '@react-native-async-storage/async-storage'; +import { create } from 'zustand'; +import { createJSONStorage, persist } from 'zustand/middleware'; +import { IAppThemeStore, ThemeOptions } from './types'; + +const initialState: Omit = { + selectedTheme: ThemeOptions.Blue, +}; + +export const useAppThemeStore = create( + persist( + (set) => ({ + ...initialState, + actions: { + setSelectedTheme: (selectedTheme) => { + set({ selectedTheme }); + }, + }, + }), + { + name: 'app-theme', + storage: createJSONStorage(() => AsyncStorage), + partialize: ({ selectedTheme }) => ({ selectedTheme } as IAppThemeStore), + }, + ), +); diff --git a/packages/mobile/src/store/zustand/index.ts b/packages/mobile/src/store/zustand/index.ts index fec3a7fc0..4eda0220c 100644 --- a/packages/mobile/src/store/zustand/index.ts +++ b/packages/mobile/src/store/zustand/index.ts @@ -7,3 +7,4 @@ export * from './flash'; export * from './notifications'; export * from './encryptedComments'; export * from './addressUpdate'; +export * from './appTheme'; diff --git a/packages/mobile/src/styled/colors.ts b/packages/mobile/src/styled/colors.ts index 03fab7129..36700c2cd 100644 --- a/packages/mobile/src/styled/colors.ts +++ b/packages/mobile/src/styled/colors.ts @@ -1,4 +1,4 @@ -export enum DARK_COLORS { +export enum BLUE_COLORS { textPrimary = '#FFFFFF', textSecondary = '#8994A3', textTertiary = '#556170', @@ -17,11 +17,64 @@ export enum DARK_COLORS { backgroundTertiary = '#2E3847', backgroundHighlighted = 'rgba(79, 90, 112, 0.24)', backgroundQuaternary = '#3E4A5C', - backgroundCamera = '#0c121f', border = 'rgba(255, 255, 255, 0.08)', separatorCommon = 'rgba(79, 90, 112, 0.24)', } +export enum DARK_COLORS { + textPrimary = '#D9D9D9', + textSecondary = '#8D8D93', + textTertiary = '#4E4E52', + textLink = '#45AEF5', + + iconPrimary = '#EBEBEB', + iconSecondary = '#8D8D93', + iconTertiary = '#4E4E52', + + foregroundPrimary = '#FFFFFF', + foregroundSecondary = '#8D8D93', + foregroundTertiary = '#4E4E52', + backgroundPrimary = '#000000', + backgroundPrimaryTrans = 'rgba(0, 0, 0, 0.92)', + backgroundSecondary = '#1B1B1F', + backgroundTertiary = '#2B2B2E', + backgroundHighlighted = 'rgba(255, 255, 255, 0.04)', + backgroundQuaternary = '#353538', + border = 'rgba(255, 255, 255, 0.12)', + separatorCommon = 'rgba(255, 255, 255, 0.12)', +} + +export enum LIGHT_COLORS { + textPrimary = '#000000', + textSecondary = '#818C99', + textTertiary = '#95A0AD', + textLink = '#007AFF', + + iconPrimary = '#000000', + iconSecondary = '#818C99', + iconTertiary = '#95A0AD', + + foregroundPrimary = '#000000', + foregroundSecondary = '#818C99', + foregroundTertiary = '#95A0AD', + backgroundPrimary = '#EFEEF3', + backgroundPrimaryTrans = 'rgba(255, 255, 255, 0.92)', + backgroundSecondary = '#FFFFFF', + backgroundTertiary = '#F0F0F0', + backgroundHighlighted = 'rgba(0, 0, 0, 0.04)', + backgroundQuaternary = 'rgba(129, 140, 153, 0.20)', + border = 'rgba(60, 60, 67, 0.08)', + separatorCommon = 'rgba(60, 60, 67, 0.08)', + + accentPrimary = '#007AFF', + accentPrimaryLight = '#3D9AFF', + accentPrimaryDark = '#1F8AFF', + accentPositive = '#25B86F', + accentNegative = '#FF3B30', + accentBlue = '#007AFF', + accentOrange = '#F5A73B', +} + export enum CONSTANT_COLORS { constantDark = '#000000', constantLight = '#FFFFFF', diff --git a/packages/mobile/src/styled/theme.ts b/packages/mobile/src/styled/theme.ts index 5a5069a39..a6600f174 100644 --- a/packages/mobile/src/styled/theme.ts +++ b/packages/mobile/src/styled/theme.ts @@ -1,7 +1,15 @@ -import { DARK_COLORS, COMMON_COLORS, CONSTANT_COLORS } from './colors'; +import { Theme, ThemeName } from '@tonkeeper/uikit'; +import { + DARK_COLORS, + COMMON_COLORS, + CONSTANT_COLORS, + BLUE_COLORS, + LIGHT_COLORS, +} from './colors'; import { FONT } from './fonts'; import { DARK_GRADIENTS } from './gradients'; import { RADIUS } from './radius'; +import { getThemeByName } from '@tonkeeper/uikit/src/styles/utils'; type ThemeSpecificColor = keyof typeof DARK_COLORS; type ConstantColor = keyof typeof CONSTANT_COLORS; @@ -12,7 +20,11 @@ export enum ThemeNames { dark = 'dark', } -export type TonThemeColor = ThemeSpecificColor | ConstantColor | CommonColor; +export type TonThemeColor = + | ThemeSpecificColor + | ConstantColor + | CommonColor + | keyof Theme; const CommonTheme = { font: FONT, @@ -29,7 +41,17 @@ export const LightTheme = { gradients: DARK_GRADIENTS, colors: { ...CommonTheme.colors, - ...DARK_COLORS, + ...LIGHT_COLORS, + }, +}; + +export const BlueTheme = { + ...CommonTheme, + isDark: true, + gradients: DARK_GRADIENTS, + colors: { + ...CommonTheme.colors, + ...BLUE_COLORS, }, }; diff --git a/packages/mobile/src/styles/Steezy.ts b/packages/mobile/src/styles/Steezy.ts index 08b17ad3a..9606a25b7 100644 --- a/packages/mobile/src/styles/Steezy.ts +++ b/packages/mobile/src/styles/Steezy.ts @@ -1,8 +1,11 @@ -import { createSteezy, createDynamicStyleVars, createMediaStyleVars } from "@bogoslavskiy/react-native-steezy"; -import { useSafeAreaInsets } from "react-native-safe-area-context"; -import { ns } from "$utils"; -import { DarkTheme } from "./DarkTheme"; -import { RADIUS } from "$styled"; +import { + createSteezy, + createDynamicStyleVars, + createMediaStyleVars, +} from '@bogoslavskiy/react-native-steezy'; +import { useSafeAreaInsets } from 'react-native-safe-area-context'; +import { ns } from '$utils'; +import { useTheme } from '@tonkeeper/uikit'; export const media = createMediaStyleVars({ isTablet: { @@ -16,17 +19,18 @@ const corners = { small: 12, medium: 16, large: 20, - full: (size: number) => size / 2, -} + full: (size: number) => size / 2, +}; export const variables = createDynamicStyleVars(() => { const safeArea = useSafeAreaInsets(); + const theme = useTheme(); return { safeArea, - colors: DarkTheme, + colors: theme, radius: corners, - corners + corners, }; }); diff --git a/packages/mobile/src/styles/index.ts b/packages/mobile/src/styles/index.ts index aa5d79513..91bdf6b15 100644 --- a/packages/mobile/src/styles/index.ts +++ b/packages/mobile/src/styles/index.ts @@ -1,8 +1,7 @@ import { StyleProp as SteezyStyleProp } from '@bogoslavskiy/react-native-steezy'; -import { ViewStyle, TextStyle, ImageStyle } from "react-native"; +import { ViewStyle, TextStyle, ImageStyle } from 'react-native'; export type StaticStyles = ViewStyle | TextStyle | ImageStyle; export type StyleProp = SteezyStyleProp; export { Steezy } from './Steezy'; -export { DarkTheme } from './DarkTheme'; diff --git a/packages/mobile/src/tabs/Activity/ActivityScreen.tsx b/packages/mobile/src/tabs/Activity/ActivityScreen.tsx index 4acd7c2fe..80756df35 100644 --- a/packages/mobile/src/tabs/Activity/ActivityScreen.tsx +++ b/packages/mobile/src/tabs/Activity/ActivityScreen.tsx @@ -72,7 +72,7 @@ export const ActivityScreen = memo(() => { activityList.error === null) ) { return ( - + {t('activity.empty_transaction_title')} diff --git a/packages/mobile/src/tabs/Wallet/WalletScreen.tsx b/packages/mobile/src/tabs/Wallet/WalletScreen.tsx index 8a589ccbc..38e128140 100644 --- a/packages/mobile/src/tabs/Wallet/WalletScreen.tsx +++ b/packages/mobile/src/tabs/Wallet/WalletScreen.tsx @@ -91,10 +91,11 @@ export const WalletScreen = memo(({ navigation }) => { ))} {shouldUpdate && } - - - {!isWatchOnly && } - + {wallet && isConnected !== false ? ( ({ width: TabletMaxWidth, }, }, - balanceWithBattery: { - flexDirection: 'row', - alignItems: 'center', - }, addressContainer: { marginBottom: 8, marginTop: 4, diff --git a/packages/mobile/src/tabs/Wallet/components/ExpiringDomainCell.tsx b/packages/mobile/src/tabs/Wallet/components/ExpiringDomainCell.tsx index bea49c233..ea5294543 100644 --- a/packages/mobile/src/tabs/Wallet/components/ExpiringDomainCell.tsx +++ b/packages/mobile/src/tabs/Wallet/components/ExpiringDomainCell.tsx @@ -53,7 +53,7 @@ export const ExpiringDomainCell = memo(({ withoutSpacer, styl onPress={() => openRenewAllDomainModal()} leftContent={ - + } title={ diff --git a/packages/mobile/src/tabs/Wallet/components/Tabs/TabsHeader.tsx b/packages/mobile/src/tabs/Wallet/components/Tabs/TabsHeader.tsx index 92048497e..e9de6d093 100644 --- a/packages/mobile/src/tabs/Wallet/components/Tabs/TabsHeader.tsx +++ b/packages/mobile/src/tabs/Wallet/components/Tabs/TabsHeader.tsx @@ -1,13 +1,14 @@ import { Steezy } from '$styles'; import { AnimatedView, Icon, TouchableOpacity, View } from '$uikit'; import { Style } from '@bogoslavskiy/react-native-steezy/dist/types'; -import { isAndroid } from '$utils'; +import { convertHexToRGBA, isAndroid } from '$utils'; import * as React from 'react'; import { useWindowDimensions } from 'react-native'; import { useAnimatedStyle } from 'react-native-reanimated'; import { useTabCtx } from './TabsContainer'; import { goBack } from '$navigation/imperative'; import LinearGradient from 'react-native-linear-gradient'; +import { useTheme } from '@tonkeeper/uikit'; interface TabsHeaderProps { style?: Style; @@ -19,6 +20,7 @@ interface TabsHeaderProps { export const TabsHeader: React.FC = (props) => { const dimensions = useWindowDimensions(); const { headerHeight, scrollY } = useTabCtx(); + const theme = useTheme(); const shouldRenderGoBackButton = props.withBackButton ?? false; @@ -47,7 +49,7 @@ export const TabsHeader: React.FC = (props) => { style={styles.leftContentGradient.static} start={{ x: 0, y: 1 }} end={{ x: 1, y: 1 }} - colors={['rgba(16, 22, 31, 1)', 'rgba(16, 22, 31, 0)']} + colors={[theme.backgroundPage, convertHexToRGBA(theme.backgroundPage, 0)]} pointerEvents="none" /> @@ -87,7 +89,7 @@ const styles = Steezy.create(({ colors, safeArea }) => ({ position: 'absolute', top: 0, zIndex: isAndroid ? 1 : 4, - backgroundColor: colors.backgroundPrimary, + backgroundColor: colors.backgroundPage, }, leftContent: { top: 16, @@ -106,7 +108,7 @@ const styles = Steezy.create(({ colors, safeArea }) => ({ zIndex: 2, }, backButton: { - backgroundColor: colors.backgroundSecondary, + backgroundColor: colors.buttonSecondaryBackground, height: 32, width: 32, borderRadius: 16, diff --git a/packages/mobile/src/tabs/Wallet/components/WalletContentList.tsx b/packages/mobile/src/tabs/Wallet/components/WalletContentList.tsx index 70862a1b8..bed257603 100644 --- a/packages/mobile/src/tabs/Wallet/components/WalletContentList.tsx +++ b/packages/mobile/src/tabs/Wallet/components/WalletContentList.tsx @@ -123,7 +123,7 @@ export const WalletContentList = memo((props) => { } diff --git a/packages/mobile/src/tabs/Wallet/components/WalletSelector.tsx b/packages/mobile/src/tabs/Wallet/components/WalletSelector.tsx index fad3394fd..a184b1496 100644 --- a/packages/mobile/src/tabs/Wallet/components/WalletSelector.tsx +++ b/packages/mobile/src/tabs/Wallet/components/WalletSelector.tsx @@ -17,11 +17,13 @@ import React, { FC, memo, useCallback } from 'react'; import { useNavigation } from '@tonkeeper/router'; import { FlashCountKeys, useFlashCount } from '$store'; import { tk } from '$wallet'; +import { useThemeName } from '$hooks/useThemeName'; const WalletSelectorComponent: FC = () => { const wallet = useWallet(); const nav = useNavigation(); const [flashShownCount, disableFlash] = useFlashCount(FlashCountKeys.MultiWallet); + const themeName = useThemeName(); const handlePress = useCallback(() => { disableFlash(); @@ -35,7 +37,7 @@ const WalletSelectorComponent: FC = () => { = 3} > @@ -43,15 +45,20 @@ const WalletSelectorComponent: FC = () => { emojiStyle={styles.emoji.static} size={20} value={wallet.config.emoji} + color="constantWhite" /> - + {wallet.config.name} - + diff --git a/packages/mobile/src/tabs/Wallet/content-providers/dependencies/inscriptions.ts b/packages/mobile/src/tabs/Wallet/content-providers/dependencies/inscriptions.ts index a537a0a00..51b50a9f8 100644 --- a/packages/mobile/src/tabs/Wallet/content-providers/dependencies/inscriptions.ts +++ b/packages/mobile/src/tabs/Wallet/content-providers/dependencies/inscriptions.ts @@ -1,18 +1,13 @@ import { DependencyPrototype } from './utils/prototype'; -import { tk } from '$wallet'; import { TonInscriptionsState } from '$wallet/managers/TonInscriptions'; +import { Wallet } from '$wallet/Wallet'; export class InscriptionsDependency extends DependencyPrototype< TonInscriptionsState, TonInscriptionsState['items'] > { - constructor() { - super(tk.wallet.tonInscriptions.state, (state) => state.items); - } - - setWallet(wallet) { - this.dataProvider = wallet.tonInscriptions.state; - super.setWallet(wallet); + constructor(wallet: Wallet) { + super(wallet.tonInscriptions.state, (state) => state.items); } } diff --git a/packages/mobile/src/tabs/Wallet/content-providers/dependencies/jettons.ts b/packages/mobile/src/tabs/Wallet/content-providers/dependencies/jettons.ts index 07177ddbb..a592c90dd 100644 --- a/packages/mobile/src/tabs/Wallet/content-providers/dependencies/jettons.ts +++ b/packages/mobile/src/tabs/Wallet/content-providers/dependencies/jettons.ts @@ -1,18 +1,18 @@ import { DependencyPrototype } from './utils/prototype'; -import { tk } from '$wallet'; import { JettonsState } from '$wallet/managers/JettonsManager'; import { FiatRate } from '../utils/types'; import { Address } from '@tonkeeper/core'; import { formatter } from '$utils/formatter'; import BigNumber from 'bignumber.js'; +import { Wallet } from '$wallet/Wallet'; export class JettonBalancesDependency extends DependencyPrototype< JettonsState, Pick > { - constructor() { - super(tk.wallet.jettons.state, (state) => ({ + constructor(wallet: Wallet) { + super(wallet.jettons.state, (state) => ({ jettonBalances: state.jettonBalances, jettonRates: state.jettonRates, })); @@ -40,6 +40,7 @@ export class JettonBalancesDependency extends DependencyPrototype< trend: rate.diff_24h.startsWith('+') || rate.diff_24h === '0' ? 'positive' : 'negative', total: { + in_ton: new BigNumber(jettonBalance).multipliedBy(rate.ton).toString(), formatted: formatter.format( new BigNumber(jettonBalance).multipliedBy(rate.fiat), { currency }, @@ -48,9 +49,4 @@ export class JettonBalancesDependency extends DependencyPrototype< }, }; } - - setWallet(wallet) { - this.dataProvider = wallet.jettons.state; - super.setWallet(wallet); - } } diff --git a/packages/mobile/src/tabs/Wallet/content-providers/dependencies/staking.ts b/packages/mobile/src/tabs/Wallet/content-providers/dependencies/staking.ts index 1a89e3605..8e111d76b 100644 --- a/packages/mobile/src/tabs/Wallet/content-providers/dependencies/staking.ts +++ b/packages/mobile/src/tabs/Wallet/content-providers/dependencies/staking.ts @@ -1,21 +1,16 @@ import { DependencyPrototype } from './utils/prototype'; -import { tk } from '$wallet'; import { StakingState } from '$wallet/managers/StakingManager'; import { AccountStakingInfo, PoolInfo } from '@tonkeeper/core/src/TonAPI'; +import { Wallet } from '$wallet/Wallet'; export class StakingDependency extends DependencyPrototype< StakingState, { info: AccountStakingInfo; pool: PoolInfo }[] > { - constructor() { - super(tk.wallet.staking.state, (s) => + constructor(wallet: Wallet) { + super(wallet.staking.state, (s) => s.pools.map((pool) => ({ info: s.stakingInfo[pool.address], pool })), ); } - - setWallet(wallet) { - this.dataProvider = wallet.staking.state; - super.setWallet(wallet); - } } diff --git a/packages/mobile/src/tabs/Wallet/content-providers/dependencies/stakingJettons.ts b/packages/mobile/src/tabs/Wallet/content-providers/dependencies/stakingJettons.ts index 66ada7c19..d5f736792 100644 --- a/packages/mobile/src/tabs/Wallet/content-providers/dependencies/stakingJettons.ts +++ b/packages/mobile/src/tabs/Wallet/content-providers/dependencies/stakingJettons.ts @@ -1,21 +1,16 @@ import { DependencyPrototype } from './utils/prototype'; -import { tk } from '$wallet'; import { Address } from '@tonkeeper/shared/Address'; import { StakingState } from '$wallet/managers/StakingManager'; import { JettonBalanceModel } from '$wallet/models/JettonBalanceModel'; +import { Wallet } from '$wallet/Wallet'; export class StakingJettonsDependency extends DependencyPrototype< StakingState, Pick > { - constructor() { - super(tk.wallet.staking.state, (state) => ({ stakingJettons: state.stakingJettons })); - } - - setWallet(wallet) { - this.dataProvider = wallet.staking.state; - super.setWallet(wallet); + constructor(wallet: Wallet) { + super(wallet.staking.state, (state) => ({ stakingJettons: state.stakingJettons })); } get filterTokensBalancesFn(): (balance: JettonBalanceModel) => boolean { diff --git a/packages/mobile/src/tabs/Wallet/content-providers/dependencies/tokenApproval.ts b/packages/mobile/src/tabs/Wallet/content-providers/dependencies/tokenApproval.ts index 059c9d47a..83388e478 100644 --- a/packages/mobile/src/tabs/Wallet/content-providers/dependencies/tokenApproval.ts +++ b/packages/mobile/src/tabs/Wallet/content-providers/dependencies/tokenApproval.ts @@ -1,5 +1,4 @@ import { DependencyPrototype } from './utils/prototype'; -import { tk } from '$wallet'; import { TokenApprovalState, @@ -11,18 +10,14 @@ import { JettonVerification, } from '$wallet/models/JettonBalanceModel'; import { InscriptionBalance } from '@tonkeeper/core/src/TonAPI'; +import { Wallet } from '$wallet/Wallet'; export class TokenApprovalDependency extends DependencyPrototype< TokenApprovalState, Pick > { - constructor() { - super(tk.wallet.tokenApproval.state, (state) => ({ tokens: state.tokens })); - } - - setWallet(wallet) { - this.dataProvider = wallet.tokenApproval.state; - super.setWallet(wallet); + constructor(wallet: Wallet) { + super(wallet.tokenApproval.state, (state) => ({ tokens: state.tokens })); } get filterInscriptionsFn(): (balance: InscriptionBalance) => boolean { diff --git a/packages/mobile/src/tabs/Wallet/content-providers/dependencies/tonBalances.ts b/packages/mobile/src/tabs/Wallet/content-providers/dependencies/tonBalances.ts index d78762f75..132f0a3cc 100644 --- a/packages/mobile/src/tabs/Wallet/content-providers/dependencies/tonBalances.ts +++ b/packages/mobile/src/tabs/Wallet/content-providers/dependencies/tonBalances.ts @@ -1,6 +1,6 @@ import { DependencyPrototype } from './utils/prototype'; -import { tk } from '$wallet'; import { BalancesState } from '$wallet/managers/BalancesManager'; +import { Wallet } from '$wallet/Wallet'; export enum TonBalanceType { Liquid = 'Liquid', @@ -17,19 +17,14 @@ export class TonBalancesDependency extends DependencyPrototype< BalancesState, Pick > { - constructor() { - super(tk.wallet.balances.state, (state) => ({ + constructor(wallet: Wallet) { + super(wallet.balances.state, (state) => ({ ton: state.ton, tonLocked: state.tonLocked, tonRestricted: state.tonRestricted, })); } - setWallet(wallet) { - this.dataProvider = wallet.balances.state; - super.setWallet(wallet); - } - get balances() { const balances: TonBalance[] = [ { type: TonBalanceType.Liquid, balance: this.state.ton }, diff --git a/packages/mobile/src/tabs/Wallet/content-providers/dependencies/tonPrice.ts b/packages/mobile/src/tabs/Wallet/content-providers/dependencies/tonPrice.ts index ecf6694d0..969554e19 100644 --- a/packages/mobile/src/tabs/Wallet/content-providers/dependencies/tonPrice.ts +++ b/packages/mobile/src/tabs/Wallet/content-providers/dependencies/tonPrice.ts @@ -1,16 +1,16 @@ import { DependencyPrototype } from './utils/prototype'; import { PricesState } from '$wallet/managers/TonPriceManager'; -import { tk } from '$wallet'; import { FiatRate } from '../utils/types'; import { formatter } from '@tonkeeper/shared/formatter'; import BigNumber from 'bignumber.js'; +import { State } from '@tonkeeper/core'; export class TonPriceDependency extends DependencyPrototype< PricesState, Pick > { - constructor() { - super(tk.tonPrice.state, (state) => ({ ton: state.ton, currency: state.currency })); + constructor(state: State) { + super(state, (state) => ({ ton: state.ton, currency: state.currency })); } protected shouldEmit( @@ -20,11 +20,6 @@ export class TonPriceDependency extends DependencyPrototype< return prev.ton !== cur.ton; } - setWallet(wallet) { - this.dataProvider = wallet.tonPrice.state; - super.setWallet(wallet); - } - public getRawTotal(balance: string): string { return new BigNumber(balance).multipliedBy(this.state.ton.fiat).toString(); } @@ -45,6 +40,7 @@ export class TonPriceDependency extends DependencyPrototype< ? 'positive' : 'negative', total: { + in_ton: balance, formatted: formatter.format(new BigNumber(balance).multipliedBy(rate.ton.fiat), { currency: rate.currency, }), diff --git a/packages/mobile/src/tabs/Wallet/content-providers/dependencies/utils/prototype.ts b/packages/mobile/src/tabs/Wallet/content-providers/dependencies/utils/prototype.ts index 08b8e6061..3e21054ea 100644 --- a/packages/mobile/src/tabs/Wallet/content-providers/dependencies/utils/prototype.ts +++ b/packages/mobile/src/tabs/Wallet/content-providers/dependencies/utils/prototype.ts @@ -69,10 +69,7 @@ export class DependencyPrototype< }; } - public setWallet(wallet: Wallet) { + public destroy() { this.unsubscribe?.(); - this.wallet = wallet; - this.subscribeToDataProvider(); - this.onStateChanged(); } } diff --git a/packages/mobile/src/tabs/Wallet/content-providers/inscriptions.ts b/packages/mobile/src/tabs/Wallet/content-providers/inscriptions.ts index 9f28412a9..1f644dd82 100644 --- a/packages/mobile/src/tabs/Wallet/content-providers/inscriptions.ts +++ b/packages/mobile/src/tabs/Wallet/content-providers/inscriptions.ts @@ -54,6 +54,7 @@ export class InscriptionsContentProvider extends ContentProviderPrototype<{ }, trend: 'negative', total: { + in_ton: '0', formatted: formatter.format(0, { currency: this.deps.tonPrice.state.currency, }), diff --git a/packages/mobile/src/tabs/Wallet/content-providers/utils/receiver.ts b/packages/mobile/src/tabs/Wallet/content-providers/utils/receiver.ts index 4179d0282..d4827df6b 100644 --- a/packages/mobile/src/tabs/Wallet/content-providers/utils/receiver.ts +++ b/packages/mobile/src/tabs/Wallet/content-providers/utils/receiver.ts @@ -8,13 +8,14 @@ import { TokensContentProvider } from '../tokens'; import { StakingJettonsDependency } from '../dependencies/stakingJettons'; import { TokenApprovalDependency } from '../dependencies/tokenApproval'; import { JettonBalancesDependency } from '../dependencies/jettons'; -import { Wallet } from '$wallet/Wallet'; import { StakingContentProvider } from '../staking'; import { StakingDependency } from '../dependencies/staking'; import { InscriptionsContentProvider } from '../inscriptions'; import { InscriptionsDependency } from '../dependencies/inscriptions'; import { NamespacedLogger, logger } from '$logger'; import BigNumber from 'bignumber.js'; +import { Wallet } from '$wallet/Wallet'; +import { tk } from '$wallet'; type Subscriber = (cells: CellItemToRender[]) => void; @@ -23,14 +24,15 @@ export class WalletContentReceiver { private subscribers = new Set(); private logger: NamespacedLogger; + private wallet: Wallet = tk.wallet; - private tonPrice = new TonPriceDependency(); - private tonBalances = new TonBalancesDependency(); - private stakingJettons = new StakingJettonsDependency(); - private tokenApproval = new TokenApprovalDependency(); - private jettonBalances = new JettonBalancesDependency(); - private staking = new StakingDependency(); - private inscriptions = new InscriptionsDependency(); + private tonPrice = new TonPriceDependency(tk.tonPrice.state); + private tonBalances = new TonBalancesDependency(this.wallet); + private stakingJettons = new StakingJettonsDependency(this.wallet); + private tokenApproval = new TokenApprovalDependency(this.wallet); + private jettonBalances = new JettonBalancesDependency(this.wallet); + private staking = new StakingDependency(this.wallet); + private inscriptions = new InscriptionsDependency(this.wallet); private memoizedCells = new Map(); @@ -44,6 +46,11 @@ export class WalletContentReceiver { this.inscriptions, ]; + constructor() { + this.subscribeToProvidersChanges(this.providersList); + this.logger = logger.extend('WalletListContent'); + } + private providersList: ContentProviderPrototype[] = [ new TONContentProvider(this.tonPrice, this.tonBalances), new TokensContentProvider( @@ -56,15 +63,10 @@ export class WalletContentReceiver { new InscriptionsContentProvider(this.tonPrice, this.inscriptions, this.tokenApproval), ]; - constructor() { - this.subscribeToProvidersChanges(this.providersList); - this.logger = logger.extend('WalletListContent'); - } - - public setWallet(wallet: Wallet) { - this.logger.debug('provide wallet to deps'); + public destroy() { + this.logger.warn('Destroy'); this.depsList.forEach((provider) => { - provider.setWallet(wallet); + provider.destroy(); }); } diff --git a/packages/mobile/src/tabs/Wallet/content-providers/utils/types.ts b/packages/mobile/src/tabs/Wallet/content-providers/utils/types.ts index 165ba9e80..b72c9d5d5 100644 --- a/packages/mobile/src/tabs/Wallet/content-providers/utils/types.ts +++ b/packages/mobile/src/tabs/Wallet/content-providers/utils/types.ts @@ -5,6 +5,7 @@ import { TonIconProps } from '@tonkeeper/uikit'; export type FiatRate = { total: { formatted: string; + in_ton: string; raw: string; }; percent?: string; diff --git a/packages/mobile/src/tabs/Wallet/content-providers/utils/usePreparedWalletContent.ts b/packages/mobile/src/tabs/Wallet/content-providers/utils/usePreparedWalletContent.ts index 8e1bed33d..e1819d182 100644 --- a/packages/mobile/src/tabs/Wallet/content-providers/utils/usePreparedWalletContent.ts +++ b/packages/mobile/src/tabs/Wallet/content-providers/utils/usePreparedWalletContent.ts @@ -1,17 +1,15 @@ -import { useEffect, useState } from 'react'; +import { useEffect, useMemo, useState } from 'react'; import { CellItemToRender } from './types'; -import { useInstance } from '$hooks/useInstance'; import { WalletContentReceiver } from './receiver'; -import { tk } from '$wallet'; +import { useWallet } from '@tonkeeper/shared/hooks'; export const usePreparedWalletContent = () => { - const providersReceiver = useInstance(() => new WalletContentReceiver()); + const wallet = useWallet(); + const providersReceiver = useMemo(() => new WalletContentReceiver(), [wallet]); const [preparedContent, setPreparedContent] = useState([]); useEffect(() => { - return tk.onChangeWallet(() => { - providersReceiver.setWallet(tk.wallet); - }); + return () => providersReceiver.destroy(); }, [providersReceiver]); useEffect(() => { diff --git a/packages/mobile/src/tabs/Wallet/hooks/useBalance.ts b/packages/mobile/src/tabs/Wallet/hooks/useBalance.ts index f9a30e3e1..e576e52d7 100644 --- a/packages/mobile/src/tabs/Wallet/hooks/useBalance.ts +++ b/packages/mobile/src/tabs/Wallet/hooks/useBalance.ts @@ -1,7 +1,7 @@ import { useMemo } from 'react'; import BigNumber from 'bignumber.js'; import { formatter } from '$utils/formatter'; -import { useWalletCurrency } from '@tonkeeper/shared/hooks'; +import { useDangerLevel, useWalletCurrency } from '@tonkeeper/shared/hooks'; import { CellItemToRender } from '../content-providers/utils/types'; export type Rate = { @@ -12,17 +12,33 @@ export type Rate = { export const useBalance = (cellItems: CellItemToRender[]) => { const currency = useWalletCurrency(); + const { inSelectedCurrency, inTonRaw } = useMemo( + () => + cellItems.reduce( + (total, item) => { + const balance = item.fiatRate?.total.raw ?? '0'; + const tonBalance = item.fiatRate?.total.in_ton ?? '0'; - return useMemo(() => { - const totalNumber = cellItems.reduce((total, item) => { - const balance = item.fiatRate?.total.raw ?? '0'; - return total.plus(balance); - }, new BigNumber(0)); + total.inSelectedCurrency = total.inSelectedCurrency.plus(balance); + total.inTonRaw = total.inTonRaw.plus(tonBalance); + + return total; + }, + { inSelectedCurrency: new BigNumber(0), inTonRaw: new BigNumber(0) }, + ), + [cellItems], + ); - return formatter.format(totalNumber.toString(), { - currency, - forceRespectDecimalPlaces: true, - decimals: totalNumber.gte(1000) ? 0 : 2, - }); - }, [cellItems, currency]); + const dangerLevel = useDangerLevel(inTonRaw.toString()); + + return useMemo(() => { + return { + inSelectedCurrency: formatter.format(inSelectedCurrency.toString(), { + currency, + forceRespectDecimalPlaces: true, + decimals: inSelectedCurrency.gte(1000) ? 0 : 2, + }), + dangerLevel, + }; + }, [currency, dangerLevel, inSelectedCurrency]); }; diff --git a/packages/mobile/src/uikit/Button/Button.style.ts b/packages/mobile/src/uikit/Button/Button.style.ts index 368c71bd0..edba3ed56 100644 --- a/packages/mobile/src/uikit/Button/Button.style.ts +++ b/packages/mobile/src/uikit/Button/Button.style.ts @@ -21,8 +21,16 @@ export const Button = styled(Animated.View)<{ return ` background: ${ inverted - ? theme.colors[isPressed ? 'backgroundSecondary' : 'backgroundTertiary'] - : theme.colors[isPressed ? 'backgroundTertiary' : 'backgroundSecondary'] + ? theme.colors[ + isPressed + ? 'buttonSecondaryBackground' + : 'buttonSecondaryBackgroundHighlighted' + ] + : theme.colors[ + isPressed + ? 'buttonSecondaryBackgroundHighlighted' + : 'buttonSecondaryBackground' + ] }; `; } else if (mode === 'tertiary') { @@ -39,10 +47,10 @@ export const Button = styled(Animated.View)<{ return ` background: ${ disabled - ? theme.colors.accentPrimaryDark + ? theme.colors.buttonPrimaryBackgroundDisabled : isPressed - ? theme.colors.accentPrimaryLight - : theme.colors.accentPrimary + ? theme.colors.buttonPrimaryBackgroundHighlighted + : theme.colors.buttonPrimaryBackground }; `; } @@ -110,9 +118,13 @@ export const Title = styled(Text).attrs({ allowFontScaling: false })<{ return ` color: ${theme.colors.backgroundPrimary}; `; + } else if (mode === 'secondary') { + return ` + color: ${theme.colors.buttonSecondaryForeground}; + `; } else { return ` - color: ${theme.colors.foregroundPrimary}; + color: ${theme.colors.buttonPrimaryForeground}; `; } }} diff --git a/packages/mobile/src/uikit/Button/Button.tsx b/packages/mobile/src/uikit/Button/Button.tsx index 2ead008b1..c771cffd7 100644 --- a/packages/mobile/src/uikit/Button/Button.tsx +++ b/packages/mobile/src/uikit/Button/Button.tsx @@ -2,8 +2,8 @@ import React, { FC, useCallback, useState } from 'react'; import * as S from './Button.style'; import { ButtonProps } from './Button.interface'; -import { Loader } from '../Loader/Loader'; import { TouchableWithoutFeedback } from 'react-native'; +import { Loader } from '@tonkeeper/uikit'; export const Button: FC = (props) => { const { @@ -37,7 +37,7 @@ export const Button: FC = (props) => { function renderContent() { if (isLoading) { - return ; + return ; } return ( diff --git a/packages/mobile/src/uikit/Checkbox/Checkbox.tsx b/packages/mobile/src/uikit/Checkbox/Checkbox.tsx index 172e6ef73..a6ed5c855 100644 --- a/packages/mobile/src/uikit/Checkbox/Checkbox.tsx +++ b/packages/mobile/src/uikit/Checkbox/Checkbox.tsx @@ -1,5 +1,4 @@ import React from 'react'; -import { Icon } from '../Icon/Icon'; import { Steezy } from '$styles'; import Animated, { interpolateColor, @@ -12,6 +11,7 @@ import { useTheme } from '$hooks/useTheme'; import { useEffect } from 'react'; import { TouchableOpacity } from 'react-native-gesture-handler'; import { Platform, TouchableOpacity as NTouchableOpacity } from 'react-native'; +import { Icon } from '@tonkeeper/uikit'; export interface CheckboxViewProps { checked: boolean; @@ -47,7 +47,7 @@ export const CheckboxView: React.FC = (props) => { borderColor: interpolateColor( colorProgress.value, [0, 1], - [colors.backgroundTertiary, 'transparent'], + [colors.iconTertiary, 'transparent'], ), }; }); @@ -69,7 +69,7 @@ export const CheckboxView: React.FC = (props) => { return ( - + ); @@ -98,7 +98,7 @@ const styles = Steezy.create(({ colors }) => ({ width: 22, height: 22, borderRadius: 6, - borderColor: colors.backgroundContentTint, + borderColor: colors.iconTertiary, borderWidth: 2, alignItems: 'center', justifyContent: 'center', diff --git a/packages/mobile/src/uikit/CheckmarkItem.tsx b/packages/mobile/src/uikit/CheckmarkItem.tsx index 3d9bf4465..03b3b9bbb 100644 --- a/packages/mobile/src/uikit/CheckmarkItem.tsx +++ b/packages/mobile/src/uikit/CheckmarkItem.tsx @@ -12,43 +12,39 @@ interface RadioItemProps { selected: boolean; } -export const CheckmarkItem = React.memo(({ label, selected, onChange }) => ( - onChange?.(!selected)} - activeOpacity={0.6} - > - - - - {label} - - - -)); +export const CheckmarkItem = React.memo( + ({ label, selected, onChange }) => ( + onChange?.(!selected)} activeOpacity={0.6}> + + + + {label} + + + + ), +); export const Checkmark = ({ checked }: { checked: boolean }) => { const theme = useTheme(); return ( - {checked && ( - + )} @@ -65,19 +61,19 @@ const styles = StyleSheet.create({ borderWidth: ns(2), }, checkmark: { - width: 22, - height: 22, - justifyContent: 'center', - alignItems: 'center' + width: 22, + height: 22, + justifyContent: 'center', + alignItems: 'center', }, checkmarkItem: { flexDirection: 'row', alignItems: 'center', paddingHorizontal: 16, paddingVertical: 12, - marginLeft: 3 + marginLeft: 3, }, checkmarkLabelText: { - marginLeft: 11 - } -}); \ No newline at end of file + marginLeft: 11, + }, +}); diff --git a/packages/mobile/src/uikit/Input/Input.style.ts b/packages/mobile/src/uikit/Input/Input.style.ts index b9591e5fd..0798203b0 100644 --- a/packages/mobile/src/uikit/Input/Input.style.ts +++ b/packages/mobile/src/uikit/Input/Input.style.ts @@ -13,9 +13,7 @@ export const InputWrapper = styled.View<{ overflow: hidden; border-radius: ${({ theme }) => ns(theme.radius.normal)}px; background-color: ${({ theme, isFailed }) => - isFailed - ? changeAlphaValue(convertHexToRGBA(theme.colors.accentNegative), 0.08) - : theme.colors.backgroundSecondary}; + isFailed ? theme.colors.fieldErrorBackground : theme.colors.fieldBackground}; padding-horizontal: ${ns(16)}px; padding-right: ${({ withClearButton }) => ns(withClearButton ? 52 : 16)}px; diff --git a/packages/mobile/src/uikit/Input/Input.tsx b/packages/mobile/src/uikit/Input/Input.tsx index c917e2155..471814439 100644 --- a/packages/mobile/src/uikit/Input/Input.tsx +++ b/packages/mobile/src/uikit/Input/Input.tsx @@ -137,7 +137,7 @@ export const Input: FC = (props) => { borderColor: interpolateColor( focusAnimation.value, [0, 1], - [colors.backgroundSecondary, colors.accentPrimary], + ['transparent', colors.accentPrimary], ), }; }); @@ -149,7 +149,7 @@ export const Input: FC = (props) => { borderColor: interpolateColor( failAnimation.value, [0, 1], - [colors.backgroundSecondary, colors.accentNegative], + ['transparent', colors.accentNegative], ), backgroundColor: `rgba(255,71,102, ${bgAlpha})`, }; @@ -165,7 +165,7 @@ export const Input: FC = (props) => { borderColor: interpolateColor( successAnimation.value, [0, 1], - [colors.backgroundSecondary, colors.accentPositive], + ['transparent', colors.accentPositive], ), }; }); diff --git a/packages/mobile/src/uikit/List/ListItem.tsx b/packages/mobile/src/uikit/List/ListItem.tsx index aefaba6ca..e90266395 100644 --- a/packages/mobile/src/uikit/List/ListItem.tsx +++ b/packages/mobile/src/uikit/List/ListItem.tsx @@ -1,11 +1,11 @@ import React, { memo, useCallback } from 'react'; import { TextStyle, ViewStyle } from 'react-native'; import { Steezy, StyleProp } from '$styles'; -import { View } from '@tonkeeper/uikit'; +import { View, useTheme } from '@tonkeeper/uikit'; import { SText } from '../StyledNativeComponents'; import { Icon } from '../Icon/Icon'; import { Pressable } from '../Pressable'; -import { DarkTheme, TonThemeColor } from '$styled'; +import { TonThemeColor } from '$styled'; import FastImage from 'react-native-fast-image'; import Animated, { SharedValue, @@ -128,9 +128,11 @@ export const ListItem = memo((props) => { ); }, [props.title, props.label, props.subtitle, compact, titleProps]); + const theme = useTheme(); + return ( theme.colors.backgroundSecondary}; border-radius: ${({ theme }) => ns(theme.radius.normal)}px; overflow: hidden; `; diff --git a/packages/mobile/src/uikit/List/old/List.tsx b/packages/mobile/src/uikit/List/old/List.tsx index 23c38df67..efdcb01dc 100644 --- a/packages/mobile/src/uikit/List/old/List.tsx +++ b/packages/mobile/src/uikit/List/old/List.tsx @@ -4,6 +4,7 @@ import * as S from './List.style'; import { ListCellProps, ListProps } from './List.interface'; import { Separator } from '../../Separator/Separator'; import { View } from '../../StyledNativeComponents'; +import { useTheme } from '@tonkeeper/uikit'; export const ListCell: FC = ({ label, @@ -57,5 +58,11 @@ export const List: FC = ({ }); }, [align, children, separator]); - return {content}; + const theme = useTheme(); + + return ( + + {content} + + ); }; diff --git a/packages/mobile/src/uikit/ListButton/ListButton.tsx b/packages/mobile/src/uikit/ListButton/ListButton.tsx index ba278c765..8bed17395 100644 --- a/packages/mobile/src/uikit/ListButton/ListButton.tsx +++ b/packages/mobile/src/uikit/ListButton/ListButton.tsx @@ -1,5 +1,5 @@ import React from 'react'; -import { View } from '@tonkeeper/uikit'; +import { View, useTheme } from '@tonkeeper/uikit'; import { Steezy } from '$styles'; import Svg, { Path, Rect } from 'react-native-svg'; import { TouchableOpacity } from 'react-native-gesture-handler'; @@ -11,24 +11,30 @@ export interface ListButtonProps { onPress: () => void; } -const PlusIcon = () => ( - - - -); +const PlusIcon = () => { + const theme = useTheme(); + return ( + + + + ); +}; const TouchableOpacityComponent = isAndroid ? NativeTouchableOpacity : TouchableOpacity; -const MinusIcon = () => ( - - - -); +const MinusIcon = () => { + const theme = useTheme(); + return ( + + + + ); +}; export const ListButton: React.FC = (props) => { return ( theme.colors.backgroundSecondary}; + background: ${({ theme }) => theme.colors.buttonSecondaryBackground}; height: ${hNs(32)}px; width: ${ns(32)}px; border-radius: ${ns(32 / 2)}px; diff --git a/packages/mobile/src/uikit/NavBar/NavBar.tsx b/packages/mobile/src/uikit/NavBar/NavBar.tsx index e2b62617b..45e09a64b 100644 --- a/packages/mobile/src/uikit/NavBar/NavBar.tsx +++ b/packages/mobile/src/uikit/NavBar/NavBar.tsx @@ -1,6 +1,6 @@ import React, { FC, useCallback, useMemo } from 'react'; import { useSafeAreaInsets } from 'react-native-safe-area-context'; -import { LayoutChangeEvent, View } from 'react-native'; +import { LayoutChangeEvent, StatusBar, View } from 'react-native'; import Animated, { useAnimatedStyle, useSharedValue, @@ -10,12 +10,11 @@ import Animated, { import * as S from './NavBar.style'; import { NavBarProps } from './NavBar.interface'; import { goBack } from '$navigation/imperative'; -import { Icon } from '../Icon/Icon'; import { useTheme } from '$hooks/useTheme'; import { NavBarHeight } from '$shared/constants'; -import { hNs } from '$utils'; +import { convertHexToRGBA, hNs } from '$utils'; import { Text } from '../Text/Text'; -import { Steezy } from '@tonkeeper/uikit'; +import { Icon, Steezy, isIOS } from '@tonkeeper/uikit'; export const NavBarHelper: FC = () => { const { top } = useSafeAreaInsets(); @@ -101,7 +100,7 @@ export const NavBar: FC = (props) => { - + @@ -152,9 +151,13 @@ export const NavBar: FC = (props) => { isTransparent={isTransparent} isBackground={fillBackground} > + {isModal && isIOS ? : null} {isTransparent && ( )} @@ -165,7 +168,7 @@ export const NavBar: FC = (props) => { disabled={hideBackButton} > - + diff --git a/packages/mobile/src/uikit/ShowMore/ShowMore.tsx b/packages/mobile/src/uikit/ShowMore/ShowMore.tsx index ea7e3b2c9..db85fc24e 100644 --- a/packages/mobile/src/uikit/ShowMore/ShowMore.tsx +++ b/packages/mobile/src/uikit/ShowMore/ShowMore.tsx @@ -4,22 +4,14 @@ import { TouchableOpacity, View } from 'react-native'; import LinearGradient from 'react-native-linear-gradient'; import { changeAlphaValue, convertHexToRGBA, ns } from '$utils'; import { t } from '@tonkeeper/shared/i18n'; +import { useTheme } from '$hooks/useTheme'; export interface ShowMoreProps { maxLines: number; text: string; - /** - * Background color for ellipsize button - */ - backgroundColor?: string; } -export const ShowMore: React.FC = ({ - maxLines, - text, - backgroundColor = '#1D2633', -}) => { - +export const ShowMore: React.FC = ({ maxLines, text }) => { const [showEllipsize, setShowEllipsize] = useState(false); const onTextLayout = useCallback( (e) => { @@ -34,6 +26,8 @@ export const ShowMore: React.FC = ({ const handleShowAll = useCallback(() => setShouldShowAll(true), [setShouldShowAll]); + const theme = useTheme(); + function Ellipsize() { return ( = ({ start={{ x: 0, y: 0 }} end={{ x: 1, y: 1 }} colors={[ - changeAlphaValue(convertHexToRGBA(backgroundColor), 0), - convertHexToRGBA(backgroundColor), + changeAlphaValue(convertHexToRGBA(theme.colors.backgroundSecondary), 0), + convertHexToRGBA(theme.colors.backgroundSecondary), ]} style={{ width: ns(24), height: ns(20) }} /> - + {t('nft_more')} diff --git a/packages/mobile/src/uikit/Toast/new/ToastComponent.tsx b/packages/mobile/src/uikit/Toast/new/ToastComponent.tsx index b63f60fca..543f1b3aa 100644 --- a/packages/mobile/src/uikit/Toast/new/ToastComponent.tsx +++ b/packages/mobile/src/uikit/Toast/new/ToastComponent.tsx @@ -104,7 +104,7 @@ export const ToastComponent = memo(() => { { backgroundColor: toast.warning ? theme.colors.accentOrange - : theme.colors.backgroundTertiary, + : theme.colors.backgroundContentTint, }, ]} > diff --git a/packages/mobile/src/utils/color.ts b/packages/mobile/src/utils/color.ts index 48c2081e2..63a6eb6a9 100644 --- a/packages/mobile/src/utils/color.ts +++ b/packages/mobile/src/utils/color.ts @@ -3,7 +3,7 @@ export function changeAlphaValue(rgbaColor: string, newAlphaValue: number): stri return rgbaColor.replace(regex, `$1 ${newAlphaValue})`); } -export function convertHexToRGBA(hex: string): string { +export function convertHexToRGBA(hex: string, alphaValue = 1): string { let c: any; if (/^#([A-Fa-f0-9]{3}){1,2}$/.test(hex)) { c = hex.substring(1).split(''); @@ -11,7 +11,9 @@ export function convertHexToRGBA(hex: string): string { c = [c[0], c[0], c[1], c[1], c[2], c[2]]; } c = '0x' + c.join(''); - return 'rgba(' + [(c >> 16) & 255, (c >> 8) & 255, c & 255].join(',') + ',1)'; + return ( + 'rgba(' + [(c >> 16) & 255, (c >> 8) & 255, c & 255].join(',') + `,${alphaValue})` + ); } throw new Error('Bad Hex'); } diff --git a/packages/mobile/src/wallet/Tonkeeper.ts b/packages/mobile/src/wallet/Tonkeeper.ts index 685bb5b1a..59d76d543 100644 --- a/packages/mobile/src/wallet/Tonkeeper.ts +++ b/packages/mobile/src/wallet/Tonkeeper.ts @@ -44,7 +44,7 @@ export interface WalletsStoreState { wallets: WalletConfig[]; selectedIdentifier: string; biometryEnabled: boolean; - lockEnabled: boolean; + lockScreenEnabled: boolean; } export interface MigrationState { @@ -79,7 +79,7 @@ export class Tonkeeper { wallets: [], selectedIdentifier: '', biometryEnabled: false, - lockEnabled: true, + lockScreenEnabled: false, }); public migrationStore = new State({ @@ -112,11 +112,21 @@ export class Tonkeeper { }); } - public get wallet() { - return this.wallets.get(this.walletsStore.data.selectedIdentifier)!; + public get wallet(): Wallet { + const wallet = this.wallets.get(this.walletsStore.data.selectedIdentifier)!; + + if (!wallet && this.walletsStore.data.wallets.length) { + return this.wallets.get(this.walletsStore.data.wallets[0].identifier)!; + } + + return wallet; } public get walletForUnlock() { + if (this.wallet && !this.wallet.isWatchOnly) { + return this.wallet; + } + return Array.from(this.wallets.values()).find((wallet) => !wallet.isWatchOnly)!; } @@ -124,8 +134,8 @@ export class Tonkeeper { return this.walletsStore.data.biometryEnabled; } - public get lockEnabled() { - return this.walletsStore.data.lockEnabled; + public get lockScreenEnabled() { + return this.walletsStore.data.lockScreenEnabled; } public async init() { @@ -553,10 +563,10 @@ export class Tonkeeper { } public async enableLock() { - await this.walletsStore.setAsync({ lockEnabled: true }); + await this.walletsStore.setAsync({ lockScreenEnabled: true }); } public async disableLock() { - await this.walletsStore.setAsync({ lockEnabled: false }); + await this.walletsStore.setAsync({ lockScreenEnabled: false }); } } diff --git a/packages/mobile/src/wallet/Wallet/WalletContent.ts b/packages/mobile/src/wallet/Wallet/WalletContent.ts index 3918274a8..e8bdad4f3 100644 --- a/packages/mobile/src/wallet/Wallet/WalletContent.ts +++ b/packages/mobile/src/wallet/Wallet/WalletContent.ts @@ -26,6 +26,7 @@ import { TonProofManager } from '../managers/TonProofManager'; import { JettonVerification } from '../models/JettonBalanceModel'; import { CardsManager } from '$wallet/managers/CardsManager'; import { JettonQuantity } from '@tonkeeper/core/src/TonAPI'; +import { WalletContentReceiver } from '../../tabs/Wallet/content-providers/utils/receiver'; export interface WalletStatusState { isReloading: boolean; @@ -193,6 +194,33 @@ export class WalletContent extends WalletBase { ]); } + public get totalTon() { + const ton = new BigNumber(this.balances.state.data.ton).multipliedBy( + this.tonPrice.state.data.ton.fiat, + ); + const jettons = this.jettons.state.data.jettonBalances.reduce((total, jetton) => { + const isBlacklisted = jetton.verification === JettonVerification.BLACKLIST; + const approvalStatus = + this.tokenApproval.state.data.tokens[Address.parse(jetton.jettonAddress).toRaw()]; + if ( + (isBlacklisted && !approvalStatus) || + approvalStatus?.current === TokenApprovalStatus.Declined + ) { + return total; + } + const rate = + this.jettons.state.data.jettonRates[Address.parse(jetton.jettonAddress).toRaw()]; + + return rate + ? total.plus(new BigNumber(jetton.balance).multipliedBy(rate.ton)) + : total; + }, new BigNumber(0)); + const staking = new BigNumber(this.staking.state.data.stakingBalance).multipliedBy( + this.tonPrice.state.data.ton.fiat, + ); + return ton.plus(jettons).plus(staking).toString(); + } + public get totalFiat() { const ton = new BigNumber(this.balances.state.data.ton).multipliedBy( this.tonPrice.state.data.ton.fiat, diff --git a/packages/router/package.json b/packages/router/package.json index 4d06a8ec5..b15da01c2 100644 --- a/packages/router/package.json +++ b/packages/router/package.json @@ -4,9 +4,9 @@ "main": "./src/index.ts", "scripts": {}, "dependencies": { - "@react-navigation/bottom-tabs": "^6.5.8", - "@react-navigation/native": "^6.1.7", - "@react-navigation/native-stack": "^6.9.13" + "@react-navigation/bottom-tabs": "^6.5.20", + "@react-navigation/native": "^6.1.17", + "@react-navigation/native-stack": "^6.9.26" }, "devDependencies": {} } diff --git a/packages/shared/components/BatteryIcon/BatteryIcon.tsx b/packages/shared/components/BatteryIcon/BatteryIcon.tsx index f13c0b7ea..8555140d8 100644 --- a/packages/shared/components/BatteryIcon/BatteryIcon.tsx +++ b/packages/shared/components/BatteryIcon/BatteryIcon.tsx @@ -29,25 +29,13 @@ export const BatteryIcon = memo(() => { return ( <> - - {getBatteryState(balance) === BatteryState.Empty ? ( - - ) : ( - - )} + ); diff --git a/packages/shared/components/RefillBattery/RefillBattery.tsx b/packages/shared/components/RefillBattery/RefillBattery.tsx index 3d6dfcc19..f4e998fdc 100644 --- a/packages/shared/components/RefillBattery/RefillBattery.tsx +++ b/packages/shared/components/RefillBattery/RefillBattery.tsx @@ -47,16 +47,14 @@ export const RefillBattery = memo((props) => { contentContainerStyle={{ paddingBottom: bottomInsets + 16 }} > - {batteryState === BatteryState.Empty ? ( - - ) : ( - - - - )} + + + {config.get('battery_beta') && ( <> diff --git a/packages/shared/components/WalletListItem/WalletListItem.tsx b/packages/shared/components/WalletListItem/WalletListItem.tsx index a22cc1443..02c0151d5 100644 --- a/packages/shared/components/WalletListItem/WalletListItem.tsx +++ b/packages/shared/components/WalletListItem/WalletListItem.tsx @@ -11,6 +11,7 @@ import { isAndroid, } from '@tonkeeper/uikit'; import { ListItemProps } from '@tonkeeper/uikit/src/components/List/ListItem'; +import { useThemeName } from '@tonkeeper/mobile/src/hooks/useThemeName'; import { FC, memo } from 'react'; import { t } from '../../i18n'; @@ -21,6 +22,8 @@ interface Props extends ListItemProps { const WalletListItemComponent: FC = (props) => { const { wallet, ...listItemProps } = props; + const themeName = useThemeName(); + const titleWithTag = wallet.isTestnet || wallet.isWatchOnly; return ( @@ -38,13 +41,14 @@ const WalletListItemComponent: FC = (props) => { } diff --git a/packages/shared/hooks/index.ts b/packages/shared/hooks/index.ts index faf7be6ea..6d386d70e 100644 --- a/packages/shared/hooks/index.ts +++ b/packages/shared/hooks/index.ts @@ -12,3 +12,4 @@ export * from './useBiometrySettings'; export * from './useLockSettings'; export * from './useWalletSetup'; export * from './useIsEnabledForBattery'; +export * from './useDangerLevel'; diff --git a/packages/shared/hooks/useDangerLevel.ts b/packages/shared/hooks/useDangerLevel.ts new file mode 100644 index 000000000..2d5d2eeaf --- /dev/null +++ b/packages/shared/hooks/useDangerLevel.ts @@ -0,0 +1,27 @@ +import BigNumber from 'bignumber.js'; +import { useWalletSetup } from './useWalletSetup'; +import { useWallet } from './useWallet'; + +export enum DangerLevel { + Normal, + Medium, + High, +} + +export const useDangerLevel = (inTonRaw: string): DangerLevel => { + const { lastBackupAt } = useWalletSetup(); + const wallet = useWallet(); + + if (lastBackupAt !== null || wallet.isWatchOnly) { + return DangerLevel.Normal; + } + + const inTonBn = new BigNumber(inTonRaw); + + if (inTonBn.gte(20)) { + return DangerLevel.High; + } else if (inTonBn.gte(2)) { + return DangerLevel.Medium; + } + return DangerLevel.Normal; +}; diff --git a/packages/shared/hooks/useLockSettings.ts b/packages/shared/hooks/useLockSettings.ts index c7ba40979..bb5e0e9e0 100644 --- a/packages/shared/hooks/useLockSettings.ts +++ b/packages/shared/hooks/useLockSettings.ts @@ -2,10 +2,13 @@ import { tk } from '@tonkeeper/mobile/src/wallet'; import { useExternalState } from './useExternalState'; export const useLockSettings = () => { - const lockEnabled = useExternalState(tk.walletsStore, (state) => state.lockEnabled); + const lockScreenEnabled = useExternalState( + tk.walletsStore, + (state) => state.lockScreenEnabled, + ); return { - lockEnabled, - toggleLock: () => (tk.lockEnabled ? tk.disableLock() : tk.enableLock()), + lockScreenEnabled, + toggleLock: () => (tk.lockScreenEnabled ? tk.disableLock() : tk.enableLock()), }; }; diff --git a/packages/shared/i18n/locales/tonkeeper/en.json b/packages/shared/i18n/locales/tonkeeper/en.json index 96196eb9d..7a95c6d44 100644 --- a/packages/shared/i18n/locales/tonkeeper/en.json +++ b/packages/shared/i18n/locales/tonkeeper/en.json @@ -650,7 +650,14 @@ "send_sending_wrong_time_description": "Turn on automatic time and date in your device settings. Then retry your transfer.", "send_sending_wrong_time_title": "Error occurred", "send_title": "Send %{currency}", - "settings_appearance": "Theme", + "settings_appearance": "Appearance", + "settings_theme": "Theme", + "settings_theme_names": { + "blue": "Deep Blue", + "dark": "Dark", + "light": "Light", + "system": "System" + }, "settings_backup_seed": "Backup", "settings_contact_support": "Contact us", "settings_delete_account": "Delete account", @@ -857,7 +864,7 @@ "swap_title": "Swap", "tab_browser": "Browser", "tab_nft": "NFTs", - "tab_collectibles": "Purchases", + "tab_collectibles": "Collectibles", "tab_swap": "Swap", "tab_wallet": "Wallet", "today": "Today", @@ -1168,6 +1175,7 @@ }, "backup_screen": { "title": "Backup", + "backup_warning": "Your balance is %{totalFiat}, and it's only protected by a recovery phrase you haven't written down yet. Backup the phrase to avoid losing funds in case of device issues.", "manual_title": "Manual", "manual_caption": "Back up your wallet manually by writing down the recovery phrase.", "manual_button": "Back Up Manually", diff --git a/packages/shared/i18n/locales/tonkeeper/ru-RU.json b/packages/shared/i18n/locales/tonkeeper/ru-RU.json index 7046f0ece..71b7dc202 100644 --- a/packages/shared/i18n/locales/tonkeeper/ru-RU.json +++ b/packages/shared/i18n/locales/tonkeeper/ru-RU.json @@ -609,7 +609,14 @@ "send_sending_wrong_time_description" : "Включите автоматическое время и дату в настройках своего устройства. После этого повторите перевод.", "send_sending_wrong_time_title" : "Произошла ошибка", "send_title" : "Отправить %{currency}", - "settings_appearance" : "Тема", + "settings_appearance" : "Оформление", + "settings_theme" : "Тема", + "settings_theme_names": { + "blue": "Темно-синяя", + "dark": "Тёмная", + "light": "Светлая", + "system": "Системная" + }, "settings_backup_seed" : "Резервная копия", "settings_contact_support" : "Написать команде", "settings_delete_account" : "Удалить аккаунт", @@ -845,7 +852,7 @@ "swap_title" : "Обмен", "tab_browser" : "Браузер", "tab_nft" : "NFT", - "tab_collectibles": "Покупки", + "tab_collectibles": "Коллекции", "tab_settings" : "Настройки", "tab_swap" : "Обмен", "tab_wallet" : "Кошелёк", @@ -1232,6 +1239,7 @@ }, "backup_screen": { "title": "Резервная копия", + "backup_warning": "На вашем балансе %{totalFiat} и он защищён только секретным ключом, который вы ещё не записали. Сделайте резервную копию ключа, чтобы не потерять средства в случае проблем с устройством.", "manual_title": "Вручную", "manual_caption": "Создайте резервную копию своего кошелька вручную, записав секретный ключ.", "manual_button": "Сделать копию вручную", diff --git a/packages/shared/modals/ActivityActionModal/ActionModalContent.tsx b/packages/shared/modals/ActivityActionModal/ActionModalContent.tsx index 2fc7b903c..a69a0783a 100644 --- a/packages/shared/modals/ActivityActionModal/ActionModalContent.tsx +++ b/packages/shared/modals/ActivityActionModal/ActionModalContent.tsx @@ -197,7 +197,7 @@ export const ActionModalContent = memo((props) => { )}