From 3fcf3c08407d97a1a4478590d25b58c83b0950c0 Mon Sep 17 00:00:00 2001 From: Marcos Rodriguez Velez <marcospr@pm.me> Date: Tue, 25 Feb 2025 22:24:31 -0400 Subject: [PATCH] FIX: Disable screen protect until it supports nav 7. Fix speed issue exposed by disabling it --- components/TransactionListItem.tsx | 12 ++++- helpers/screenProtect.ts | 23 +++++++++ ios/BlueWallet.xcodeproj/project.pbxproj | 20 ++++---- ios/Podfile.lock | 35 ++++++++++--- package-lock.json | 31 ++++++----- package.json | 4 +- screen/send/create.js | 9 ++-- .../ExportMultisigCoordinationSetup.tsx | 16 ++++-- screen/wallets/ImportWalletDiscovery.tsx | 22 ++++---- screen/wallets/PleaseBackup.tsx | 17 ++++--- screen/wallets/ViewEditMultisigCosigners.tsx | 6 +-- screen/wallets/WalletAddresses.tsx | 25 ++++----- screen/wallets/WalletExport.tsx | 51 ++++++------------- screen/wallets/WalletTransactions.tsx | 19 ++++--- screen/wallets/WalletsList.tsx | 17 +++++-- screen/wallets/addMultisigStep2.js | 9 ++-- screen/wallets/pleaseBackupLNDHub.js | 9 ++-- screen/wallets/xpub.tsx | 7 ++- 18 files changed, 198 insertions(+), 134 deletions(-) create mode 100644 helpers/screenProtect.ts diff --git a/components/TransactionListItem.tsx b/components/TransactionListItem.tsx index 890dd7dc4..e926c8daa 100644 --- a/components/TransactionListItem.tsx +++ b/components/TransactionListItem.tsx @@ -1,4 +1,4 @@ -import React, { useCallback, useEffect, useMemo, useRef, useState } from 'react'; +import React, { useCallback, useEffect, useMemo, useRef, useState, memo } from 'react'; import AsyncStorage from '@react-native-async-storage/async-storage'; import Clipboard from '@react-native-clipboard/clipboard'; import { Linking, View, ViewStyle } from 'react-native'; @@ -36,7 +36,7 @@ interface TransactionListItemProps { type NavigationProps = NativeStackNavigationProp<DetailViewStackParamList>; -export const TransactionListItem: React.FC<TransactionListItemProps> = React.memo( +export const TransactionListItem: React.FC<TransactionListItemProps> = memo( ({ item, itemPriceUnit = BitcoinUnit.BTC, walletID, searchQuery, style, renderHighlightedText }) => { const [subtitleNumberOfLines, setSubtitleNumberOfLines] = useState(1); const { colors } = useTheme(); @@ -366,4 +366,12 @@ export const TransactionListItem: React.FC<TransactionListItemProps> = React.mem </ToolTipMenu> ); }, + (prevProps, nextProps) => { + return ( + prevProps.item.hash === nextProps.item.hash && + prevProps.item.received === nextProps.item.received && + prevProps.itemPriceUnit === nextProps.itemPriceUnit && + prevProps.walletID === nextProps.walletID + ); + }, ); diff --git a/helpers/screenProtect.ts b/helpers/screenProtect.ts new file mode 100644 index 000000000..f32bb49a1 --- /dev/null +++ b/helpers/screenProtect.ts @@ -0,0 +1,23 @@ +// import { enableSecureView, disableSecureView, forbidAndroidShare, allowAndroidShare } from 'react-native-prevent-screenshot-ios-android'; +// import { Platform } from 'react-native'; +// import { isDesktop } from '../blue_modules/environment'; + +export const enableScreenProtect = () => { + // if (isDesktop) return; + // if (Platform.OS === 'ios') { + // enableSecureView(); + // } else if (Platform.OS === 'android') { + // forbidAndroidShare(); + // } +}; + +export const disableScreenProtect = () => { + // if (isDesktop) return; + // if (Platform.OS === 'ios') { + // disableSecureView(); + // } else if (Platform.OS === 'android') { + // allowAndroidShare(); + // } +}; + +// CURRENTLY UNUSED AS WE WAIT FOR NAV 7 SUPPORT diff --git a/ios/BlueWallet.xcodeproj/project.pbxproj b/ios/BlueWallet.xcodeproj/project.pbxproj index b6c1790a9..6a1cfc089 100644 --- a/ios/BlueWallet.xcodeproj/project.pbxproj +++ b/ios/BlueWallet.xcodeproj/project.pbxproj @@ -1443,7 +1443,7 @@ "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Distribution"; "CODE_SIGN_IDENTITY[sdk=macosx*]" = "Apple Development"; CODE_SIGN_STYLE = Manual; - CURRENT_PROJECT_VERSION = 1703137999; + CURRENT_PROJECT_VERSION = 1703138999; DEAD_CODE_STRIPPING = YES; DEVELOPMENT_TEAM = ""; "DEVELOPMENT_TEAM[sdk=iphoneos*]" = A7W54YZ4WU; @@ -1506,7 +1506,7 @@ "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Distribution"; "CODE_SIGN_IDENTITY[sdk=macosx*]" = "Apple Distribution"; CODE_SIGN_STYLE = Manual; - CURRENT_PROJECT_VERSION = 1703137999; + CURRENT_PROJECT_VERSION = 1703138999; DEAD_CODE_STRIPPING = YES; DEVELOPMENT_TEAM = ""; "DEVELOPMENT_TEAM[sdk=iphoneos*]" = A7W54YZ4WU; @@ -1565,7 +1565,7 @@ CODE_SIGN_IDENTITY = "Apple Development"; "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; CODE_SIGN_STYLE = Manual; - CURRENT_PROJECT_VERSION = 1703137999; + CURRENT_PROJECT_VERSION = 1703138999; DEAD_CODE_STRIPPING = YES; DEBUG_INFORMATION_FORMAT = dwarf; DEVELOPMENT_TEAM = ""; @@ -1608,7 +1608,7 @@ "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Distribution"; CODE_SIGN_STYLE = Manual; COPY_PHASE_STRIP = NO; - CURRENT_PROJECT_VERSION = 1703137999; + CURRENT_PROJECT_VERSION = 1703138999; DEAD_CODE_STRIPPING = YES; DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; DEVELOPMENT_TEAM = ""; @@ -1652,7 +1652,7 @@ "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; "CODE_SIGN_IDENTITY[sdk=macosx*]" = "Apple Development"; CODE_SIGN_STYLE = Manual; - CURRENT_PROJECT_VERSION = 1703137999; + CURRENT_PROJECT_VERSION = 1703138999; DEAD_CODE_STRIPPING = YES; DEBUG_INFORMATION_FORMAT = dwarf; DEVELOPMENT_TEAM = ""; @@ -1708,7 +1708,7 @@ "CODE_SIGN_IDENTITY[sdk=macosx*]" = "Apple Distribution"; CODE_SIGN_STYLE = Manual; COPY_PHASE_STRIP = NO; - CURRENT_PROJECT_VERSION = 1703137999; + CURRENT_PROJECT_VERSION = 1703138999; DEAD_CODE_STRIPPING = YES; DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; DEVELOPMENT_TEAM = ""; @@ -1895,7 +1895,7 @@ CODE_SIGN_IDENTITY = "Apple Development"; "CODE_SIGN_IDENTITY[sdk=watchos*]" = "iPhone Developer"; CODE_SIGN_STYLE = Manual; - CURRENT_PROJECT_VERSION = 1703137999; + CURRENT_PROJECT_VERSION = 1703138999; DEAD_CODE_STRIPPING = YES; DEBUG_INFORMATION_FORMAT = dwarf; DEVELOPMENT_TEAM = ""; @@ -1948,7 +1948,7 @@ "CODE_SIGN_IDENTITY[sdk=watchos*]" = "iPhone Distribution"; CODE_SIGN_STYLE = Manual; COPY_PHASE_STRIP = NO; - CURRENT_PROJECT_VERSION = 1703137999; + CURRENT_PROJECT_VERSION = 1703138999; DEAD_CODE_STRIPPING = YES; DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; DEVELOPMENT_TEAM = ""; @@ -2000,7 +2000,7 @@ CODE_SIGN_IDENTITY = "Apple Development"; "CODE_SIGN_IDENTITY[sdk=watchos*]" = "iPhone Developer"; CODE_SIGN_STYLE = Manual; - CURRENT_PROJECT_VERSION = 1703137999; + CURRENT_PROJECT_VERSION = 1703138999; DEAD_CODE_STRIPPING = YES; DEBUG_INFORMATION_FORMAT = dwarf; DEVELOPMENT_TEAM = ""; @@ -2049,7 +2049,7 @@ "CODE_SIGN_IDENTITY[sdk=watchos*]" = "iPhone Distribution"; CODE_SIGN_STYLE = Manual; COPY_PHASE_STRIP = NO; - CURRENT_PROJECT_VERSION = 1703137999; + CURRENT_PROJECT_VERSION = 1703138999; DEAD_CODE_STRIPPING = YES; DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; DEVELOPMENT_TEAM = ""; diff --git a/ios/Podfile.lock b/ios/Podfile.lock index 2f171489f..3232dc5b7 100644 --- a/ios/Podfile.lock +++ b/ios/Podfile.lock @@ -1322,18 +1322,37 @@ PODS: - React-Core - react-native-menu (1.2.1): - React + - react-native-prevent-screenshot-ios-android (1.1.0): + - DoubleConversion + - glog + - hermes-engine + - RCT-Folly (= 2024.01.01.00) + - RCTRequired + - RCTTypeSafety + - React-Core + - React-debug + - React-Fabric + - React-featureflags + - React-graphics + - React-ImageManager + - React-NativeModulesApple + - React-RCTFabric + - React-rendererdebug + - React-utils + - ReactCodegen + - ReactCommon/turbomodule/bridging + - ReactCommon/turbomodule/core + - Yoga - react-native-randombytes (3.6.1): - React-Core - react-native-safe-area-context (5.2.0): - React-Core - - react-native-screen-capture (0.2.3): - - React - react-native-secure-key-store (2.0.10): - React-Core - react-native-tcp-socket (6.2.0): - CocoaAsyncSocket - React-Core - - react-native-true-sheet (1.1.1): + - react-native-true-sheet (2.0.0): - DoubleConversion - glog - hermes-engine @@ -1941,9 +1960,9 @@ DEPENDENCIES: - react-native-image-picker (from `../node_modules/react-native-image-picker`) - react-native-ios-context-menu (from `../node_modules/react-native-ios-context-menu`) - "react-native-menu (from `../node_modules/@react-native-menu/menu`)" + - react-native-prevent-screenshot-ios-android (from `../node_modules/react-native-prevent-screenshot-ios-android`) - react-native-randombytes (from `../node_modules/react-native-randombytes`) - react-native-safe-area-context (from `../node_modules/react-native-safe-area-context`) - - react-native-screen-capture (from `../node_modules/react-native-screen-capture`) - react-native-secure-key-store (from `../node_modules/react-native-secure-key-store`) - react-native-tcp-socket (from `../node_modules/react-native-tcp-socket`) - "react-native-true-sheet (from `../node_modules/@lodev09/react-native-true-sheet`)" @@ -2098,12 +2117,12 @@ EXTERNAL SOURCES: :path: "../node_modules/react-native-ios-context-menu" react-native-menu: :path: "../node_modules/@react-native-menu/menu" + react-native-prevent-screenshot-ios-android: + :path: "../node_modules/react-native-prevent-screenshot-ios-android" react-native-randombytes: :path: "../node_modules/react-native-randombytes" react-native-safe-area-context: :path: "../node_modules/react-native-safe-area-context" - react-native-screen-capture: - :path: "../node_modules/react-native-screen-capture" react-native-secure-key-store: :path: "../node_modules/react-native-secure-key-store" react-native-tcp-socket: @@ -2261,12 +2280,12 @@ SPEC CHECKSUMS: react-native-image-picker: 130fad649d07e4eec8faaed361d3bba570e1e5ff react-native-ios-context-menu: 986da6dcba70094bcc2a8049f68410fe7d25aff1 react-native-menu: 2cfe0a3b3c610ed331f00d9f0df300db14ba8692 + react-native-prevent-screenshot-ios-android: 490b2ae701658753e819ca215201f4aa8cab3d53 react-native-randombytes: 3c8f3e89d12487fd03a2f966c288d495415fc116 react-native-safe-area-context: 3e33e7c43c8b74dba436a5a32651cb8d7064c740 - react-native-screen-capture: 7b6121f529681ed2fde36cdedadd0bb39e9a3796 react-native-secure-key-store: eb45b44bdec3f48e9be5cdfca0f49ddf64892ea6 react-native-tcp-socket: 61379457d7e702e83e28c213b6e085ac079e480f - react-native-true-sheet: 58c0848a4326fd2eb7eedd266ec0f39e7c70e5bd + react-native-true-sheet: 15f8d1bfbf2aceca472b9ba585b4116041d20f34 React-nativeconfig: 67fa7a63ea288cb5b1d0dd2deaf240405fec164f React-NativeModulesApple: 34b7a4d7441a4ee78d18109ff107c1ccf7c074a9 React-perflogger: d1149037ac466ad2141d4ae541ca16cb73b2343b diff --git a/package-lock.json b/package-lock.json index de67b945e..ef0ee7ae1 100644 --- a/package-lock.json +++ b/package-lock.json @@ -14,7 +14,7 @@ "@bugsnag/react-native": "8.2.0", "@bugsnag/source-maps": "2.3.3", "@keystonehq/bc-ur-registry": "0.7.0", - "@lodev09/react-native-true-sheet": "github:BlueWallet/react-native-true-sheet#0fefdd1aca07fcc3c204f6f1511f9fc0a2c59111", + "@lodev09/react-native-true-sheet": "github:BlueWallet/react-native-true-sheet#5945184a2fea9fe5ba8f5cfcdb20e3fc5eed6e37", "@ngraveio/bc-ur": "1.1.13", "@noble/secp256k1": "1.6.3", "@react-native-async-storage/async-storage": "2.1.0", @@ -83,6 +83,7 @@ "react-native-linear-gradient": "2.8.3", "react-native-localize": "3.4.1", "react-native-permissions": "5.2.5", + "react-native-prevent-screenshot-ios-android": "github:BlueWallet/react-native-prevent-screenshot-ios-android#133004e", "react-native-prompt-android": "github:BlueWallet/react-native-prompt-android#ed168d66fed556bc2ed07cf498770f058b78a376", "react-native-push-notification": "8.1.1", "react-native-qrcode-svg": "6.3.12", @@ -91,7 +92,6 @@ "react-native-rate": "1.2.12", "react-native-reanimated": "3.16.7", "react-native-safe-area-context": "5.2.0", - "react-native-screen-capture": "github:BlueWallet/react-native-screen-capture#18cb79f", "react-native-screens": "4.9.0", "react-native-secure-key-store": "github:BlueWallet/react-native-secure-key-store#2076b4849e88aa0a78e08bfbb4ce3923e0925cbc", "react-native-share": "11.1.0", @@ -4699,9 +4699,9 @@ } }, "node_modules/@lodev09/react-native-true-sheet": { - "version": "1.1.1", - "resolved": "git+ssh://git@github.com/BlueWallet/react-native-true-sheet.git#0fefdd1aca07fcc3c204f6f1511f9fc0a2c59111", - "integrity": "sha512-44sy92Jofy5dmKYpu9CYAHEHnEUnZPvy5bCSCvlg/2W3HlIUY2IKp81VzrFLf7XvDO7ZREqNsTt4QsaOH+GUnQ==", + "version": "2.0.0", + "resolved": "git+ssh://git@github.com/BlueWallet/react-native-true-sheet.git#5945184a2fea9fe5ba8f5cfcdb20e3fc5eed6e37", + "integrity": "sha512-JoMgC3w8Xgzvb4zHRnBdycBDuhz1O8MuKh9LE3QJjCElcm8x0n3asHzrt+XaLs3XfVrvq/LNyIMQSSYtvvhFKA==", "license": "MIT", "workspaces": [ "example", @@ -21973,6 +21973,18 @@ } } }, + "node_modules/react-native-prevent-screenshot-ios-android": { + "version": "1.1.0", + "resolved": "git+ssh://git@github.com/BlueWallet/react-native-prevent-screenshot-ios-android.git#133004eff4b2e95176ad2a8cc1d6aa61ea43be98", + "license": "MIT", + "engines": { + "node": ">= 16.0.0" + }, + "peerDependencies": { + "react": "*", + "react-native": "*" + } + }, "node_modules/react-native-prompt-android": { "version": "1.0.0", "resolved": "git+ssh://git@github.com/BlueWallet/react-native-prompt-android.git#ed168d66fed556bc2ed07cf498770f058b78a376", @@ -22091,15 +22103,6 @@ "react-native": "*" } }, - "node_modules/react-native-screen-capture": { - "version": "0.2.3", - "resolved": "git+ssh://git@github.com/BlueWallet/react-native-screen-capture.git#18cb79f0cd3edf189c8fac926ee8cdb8a58b1174", - "license": "MIT", - "peerDependencies": { - "react": ">=17.0.2", - "react-native": ">=0.67.0-rc.0 <1.0.x" - } - }, "node_modules/react-native-screens": { "version": "4.9.0", "resolved": "https://registry.npmjs.org/react-native-screens/-/react-native-screens-4.9.0.tgz", diff --git a/package.json b/package.json index db3e7a6fe..80109154b 100644 --- a/package.json +++ b/package.json @@ -94,6 +94,7 @@ "@react-native-menu/menu": "https://github.com/BlueWallet/menu.git#038a9c9", "@react-native/gradle-plugin": "0.76.7", "@react-native/metro-config": "0.76.7", + "@react-navigation/devtools": "7.0.15", "@react-navigation/drawer": "7.1.1", "@react-navigation/native": "7.0.14", "@react-navigation/native-stack": "7.2.0", @@ -150,6 +151,7 @@ "react-native-linear-gradient": "2.8.3", "react-native-localize": "3.4.1", "react-native-permissions": "5.2.5", + "react-native-prevent-screenshot-ios-android": "github:BlueWallet/react-native-prevent-screenshot-ios-android#133004e", "react-native-prompt-android": "github:BlueWallet/react-native-prompt-android#ed168d66fed556bc2ed07cf498770f058b78a376", "react-native-push-notification": "8.1.1", "react-native-qrcode-svg": "6.3.12", @@ -157,9 +159,7 @@ "react-native-randombytes": "3.6.1", "react-native-rate": "1.2.12", "react-native-reanimated": "3.16.7", - "@react-navigation/devtools": "7.0.15", "react-native-safe-area-context": "5.2.0", - "react-native-screen-capture": "github:BlueWallet/react-native-screen-capture#18cb79f", "react-native-screens": "4.9.0", "react-native-secure-key-store": "github:BlueWallet/react-native-secure-key-store#2076b4849e88aa0a78e08bfbb4ce3923e0925cbc", "react-native-share": "11.1.0", diff --git a/screen/send/create.js b/screen/send/create.js index d936be714..f3e22eb66 100644 --- a/screen/send/create.js +++ b/screen/send/create.js @@ -9,17 +9,16 @@ import { Icon } from '@rneui/themed'; import RNFS from 'react-native-fs'; import { PERMISSIONS, request, RESULTS } from 'react-native-permissions'; import Share from 'react-native-share'; - import { satoshiToBTC } from '../../blue_modules/currency'; import { isDesktop } from '../../blue_modules/environment'; import { BlueSpacing20, BlueText } from '../../BlueComponents'; import presentAlert from '../../components/Alert'; import { DynamicQRCode } from '../../components/DynamicQRCode'; import { useTheme } from '../../components/themes'; -import { disallowScreenshot } from 'react-native-screen-capture'; import loc from '../../loc'; import { BitcoinUnit } from '../../models/bitcoinUnits'; import { useSettings } from '../../hooks/context/useSettings'; +import { enableScreenProtect, disableScreenProtect } from '../../helpers/screenProtect'; const SendCreate = () => { const { fee, recipients, memo = '', satoshiPerByte, psbt, showAnimatedQr, tx } = useRoute().params; @@ -49,9 +48,11 @@ const SendCreate = () => { useEffect(() => { console.log('send/create - useEffect'); - if (!isDesktop) disallowScreenshot(isPrivacyBlurEnabled); + if (isPrivacyBlurEnabled) { + enableScreenProtect(); + } return () => { - if (!isDesktop) disallowScreenshot(false); + disableScreenProtect(); }; }, [isPrivacyBlurEnabled]); diff --git a/screen/wallets/ExportMultisigCoordinationSetup.tsx b/screen/wallets/ExportMultisigCoordinationSetup.tsx index 8e3369821..a00f21c68 100644 --- a/screen/wallets/ExportMultisigCoordinationSetup.tsx +++ b/screen/wallets/ExportMultisigCoordinationSetup.tsx @@ -7,12 +7,11 @@ import { DynamicQRCode } from '../../components/DynamicQRCode'; import SaveFileButton from '../../components/SaveFileButton'; import { SquareButton } from '../../components/SquareButton'; import { useTheme } from '../../components/themes'; -import { disallowScreenshot } from 'react-native-screen-capture'; import loc from '../../loc'; import { useStorage } from '../../hooks/context/useStorage'; import { ExportMultisigCoordinationSetupStackRootParamList } from '../../navigation/ExportMultisigCoordinationSetupStack'; import { useSettings } from '../../hooks/context/useSettings'; -import { isDesktop } from '../../blue_modules/environment'; +import { enableScreenProtect, disableScreenProtect } from '../../helpers/screenProtect'; const enum ActionType { SET_LOADING = 'SET_LOADING', @@ -102,7 +101,6 @@ const ExportMultisigCoordinationSetup: React.FC = () => { dispatch({ type: ActionType.SET_LOADING, isLoading: true }); const task = InteractionManager.runAfterInteractions(() => { - if (!isDesktop) disallowScreenshot(isPrivacyBlurEnabled); if (wallet) { setTimeout(async () => { try { @@ -128,12 +126,22 @@ const ExportMultisigCoordinationSetup: React.FC = () => { return () => { task.cancel(); - if (!isDesktop) disallowScreenshot(false); }; // eslint-disable-next-line react-hooks/exhaustive-deps }, [walletID]), ); + useFocusEffect( + useCallback(() => { + if (isPrivacyBlurEnabled) { + enableScreenProtect(); + } + return () => { + disableScreenProtect(); + }; + }, [isPrivacyBlurEnabled]), + ); + useFocusEffect( useCallback(() => { if (closeButtonState) { diff --git a/screen/wallets/ImportWalletDiscovery.tsx b/screen/wallets/ImportWalletDiscovery.tsx index 07ddc72bb..dbedcb0c3 100644 --- a/screen/wallets/ImportWalletDiscovery.tsx +++ b/screen/wallets/ImportWalletDiscovery.tsx @@ -16,10 +16,9 @@ import { useStorage } from '../../hooks/context/useStorage'; import { AddWalletStackParamList } from '../../navigation/AddWalletStack'; import { NativeStackNavigationProp } from '@react-navigation/native-stack'; import { THDWalletForWatchOnly, TWallet } from '../../class/wallets/types'; -import { keepAwake, disallowScreenshot } from 'react-native-screen-capture'; import { useSettings } from '../../hooks/context/useSettings'; -import { isDesktop } from '../../blue_modules/environment'; import { useExtendedNavigation } from '../../hooks/useExtendedNavigation'; +import { enableScreenProtect, disableScreenProtect } from '../../helpers/screenProtect'; type RouteProps = RouteProp<AddWalletStackParamList, 'ImportWalletDiscovery'>; type NavigationProp = NativeStackNavigationProp<AddWalletStackParamList, 'ImportWalletDiscovery'>; @@ -39,7 +38,7 @@ const ImportWalletDiscovery: React.FC = () => { const { colors } = useTheme(); const route = useRoute<RouteProps>(); const { importText, askPassphrase, searchAccounts } = route.params; - const { isElectrumDisabled } = useSettings(); + const { isElectrumDisabled, isPrivacyBlurEnabled } = useSettings(); const task = useRef<TImport | null>(null); const { addAndSaveWallet } = useStorage(); const [loading, setLoading] = useState<boolean>(true); @@ -115,7 +114,6 @@ const ImportWalletDiscovery: React.FC = () => { } }; - if (!isDesktop) keepAwake(true); task.current = startImport(importText, askPassphrase, searchAccounts, isElectrumDisabled, onProgress, onWallet, onPassword); task.current.promise @@ -134,22 +132,24 @@ const ImportWalletDiscovery: React.FC = () => { .finally(() => { LayoutAnimation.configureNext(LayoutAnimation.Presets.easeInEaseOut); setLoading(false); - if (!isDesktop) keepAwake(false); }); return () => { - if (!isDesktop) keepAwake(false); task.current?.stop(); }; }, [askPassphrase, importText, isElectrumDisabled, navigation, saveWallet, searchAccounts]); + useEffect(() => { + if (isPrivacyBlurEnabled) { + enableScreenProtect(); + } + return () => { + disableScreenProtect(); + }; + }, [isPrivacyBlurEnabled]); + const handleCustomDerivation = () => { task.current?.stop(); - if (!isDesktop) { - keepAwake(false); - disallowScreenshot(false); - } - navigation.navigate('ImportCustomDerivationPath', { importText, password }); }; diff --git a/screen/wallets/PleaseBackup.tsx b/screen/wallets/PleaseBackup.tsx index 73a930a5d..0dbf74b55 100644 --- a/screen/wallets/PleaseBackup.tsx +++ b/screen/wallets/PleaseBackup.tsx @@ -1,17 +1,16 @@ -import { RouteProp, useNavigation, useRoute } from '@react-navigation/native'; +import { RouteProp, useFocusEffect, useNavigation, useRoute } from '@react-navigation/native'; import { NativeStackNavigationProp } from '@react-navigation/native-stack'; import React, { useCallback, useEffect } from 'react'; import { BackHandler, I18nManager, ScrollView, StyleSheet, Text, View } from 'react-native'; -import { disallowScreenshot } from 'react-native-screen-capture'; import Button from '../../components/Button'; import { useTheme } from '../../components/themes'; import { useSettings } from '../../hooks/context/useSettings'; import { useStorage } from '../../hooks/context/useStorage'; import loc from '../../loc'; import { AddWalletStackParamList } from '../../navigation/AddWalletStack'; -import { isDesktop } from '../../blue_modules/environment'; import SeedWords from '../../components/SeedWords'; +import { disableScreenProtect, enableScreenProtect } from '../../helpers/screenProtect'; type RouteProps = RouteProp<AddWalletStackParamList, 'PleaseBackup'>; type NavigationProp = NativeStackNavigationProp<AddWalletStackParamList, 'PleaseBackup'>; @@ -34,21 +33,27 @@ const PleaseBackup: React.FC = () => { }); const handleBackButton = useCallback(() => { - // @ts-ignore: Ignore navigation.getParent()?.goBack(); return true; }, [navigation]); useEffect(() => { BackHandler.addEventListener('hardwareBackPress', handleBackButton); - if (!isDesktop) disallowScreenshot(isPrivacyBlurEnabled); return () => { BackHandler.removeEventListener('hardwareBackPress', handleBackButton); - if (!isDesktop) disallowScreenshot(false); }; // eslint-disable-next-line react-hooks/exhaustive-deps }, []); + useFocusEffect( + useCallback(() => { + if (isPrivacyBlurEnabled) enableScreenProtect(); + return () => { + disableScreenProtect(); + }; + }, [isPrivacyBlurEnabled]), + ); + return ( <ScrollView style={styles.root} diff --git a/screen/wallets/ViewEditMultisigCosigners.tsx b/screen/wallets/ViewEditMultisigCosigners.tsx index 2119961a3..42138321f 100644 --- a/screen/wallets/ViewEditMultisigCosigners.tsx +++ b/screen/wallets/ViewEditMultisigCosigners.tsx @@ -33,7 +33,7 @@ import { useTheme } from '../../components/themes'; import prompt from '../../helpers/prompt'; import { unlockWithBiometrics, useBiometrics } from '../../hooks/useBiometrics'; import { useExtendedNavigation } from '../../hooks/useExtendedNavigation'; -import { disallowScreenshot } from 'react-native-screen-capture'; +import { enableScreenProtect, disableScreenProtect } from '../../helpers/screenProtect'; import loc from '../../loc'; import ActionSheet from '../ActionSheet'; import { useStorage } from '../../hooks/context/useStorage'; @@ -181,7 +181,7 @@ const ViewEditMultisigCosigners: React.FC = () => { // useFocusEffect is called on willAppear (example: when camera dismisses). we want to avoid this. if (hasLoaded.current) return; setIsLoading(true); - if (!isDesktop) disallowScreenshot(isPrivacyBlurEnabled); + if (isPrivacyBlurEnabled) enableScreenProtect(); const task = InteractionManager.runAfterInteractions(async () => { if (!w.current) { @@ -197,7 +197,7 @@ const ViewEditMultisigCosigners: React.FC = () => { setIsLoading(false); }); return () => { - if (!isDesktop) disallowScreenshot(false); + disableScreenProtect(); task.cancel(); }; // eslint-disable-next-line react-hooks/exhaustive-deps diff --git a/screen/wallets/WalletAddresses.tsx b/screen/wallets/WalletAddresses.tsx index 4a1850369..c43ec430e 100644 --- a/screen/wallets/WalletAddresses.tsx +++ b/screen/wallets/WalletAddresses.tsx @@ -1,10 +1,9 @@ import React, { useCallback, useEffect, useLayoutEffect, useRef, useReducer, useMemo } from 'react'; -import { useFocusEffect, useRoute, RouteProp } from '@react-navigation/native'; +import { useRoute, RouteProp, useFocusEffect } from '@react-navigation/native'; import { ActivityIndicator, FlatList, StyleSheet, View, Platform, UIManager } from 'react-native'; import { WatchOnlyWallet } from '../../class'; import { AddressItem } from '../../components/addresses/AddressItem'; import { useTheme } from '../../components/themes'; -import { disallowScreenshot } from 'react-native-screen-capture'; import { useStorage } from '../../hooks/context/useStorage'; import { NativeStackNavigationProp } from '@react-navigation/native-stack'; import { DetailViewStackParamList } from '../../navigation/DetailViewStackParamList'; @@ -12,7 +11,8 @@ import { useExtendedNavigation } from '../../hooks/useExtendedNavigation'; import SegmentedControl from '../../components/SegmentControl'; import loc from '../../loc'; import { BitcoinUnit } from '../../models/bitcoinUnits'; -import { isDesktop } from '../../blue_modules/environment'; +import { useSettings } from '../../hooks/context/useSettings'; +import { disableScreenProtect, enableScreenProtect } from '../../helpers/screenProtect'; export const TABS = { EXTERNAL: 'receive', @@ -131,6 +131,7 @@ const WalletAddresses: React.FC = () => { const allowSignVerifyMessage = (wallet && 'allowSignVerifyMessage' in wallet && wallet.allowSignVerifyMessage()) ?? false; const { colors } = useTheme(); + const { isPrivacyBlurEnabled } = useSettings(); const { setOptions } = useExtendedNavigation<NavigationProps>(); const stylesHook = StyleSheet.create({ @@ -139,6 +140,15 @@ const WalletAddresses: React.FC = () => { }, }); + useFocusEffect( + useCallback(() => { + if (isPrivacyBlurEnabled) enableScreenProtect(); + return () => { + disableScreenProtect(); + }; + }, [isPrivacyBlurEnabled]), + ); + const getAddresses = useMemo(() => { if (!walletInstance) return []; const newAddresses: Address[] = []; @@ -177,15 +187,6 @@ const WalletAddresses: React.FC = () => { }); }, [setOptions]); - useFocusEffect( - useCallback(() => { - if (!isDesktop) disallowScreenshot(true); - return () => { - if (!isDesktop) disallowScreenshot(false); - }; - }, []), - ); - const data = search.length > 0 ? filteredAddresses.filter(item => item.address.toLowerCase().includes(search.toLowerCase())) : filteredAddresses; diff --git a/screen/wallets/WalletExport.tsx b/screen/wallets/WalletExport.tsx index 374ecc909..b6ff96eeb 100644 --- a/screen/wallets/WalletExport.tsx +++ b/screen/wallets/WalletExport.tsx @@ -1,10 +1,9 @@ import React, { useCallback, useEffect, useMemo, useState } from 'react'; import Clipboard from '@react-native-clipboard/clipboard'; -import { RouteProp, useFocusEffect, useNavigation, useRoute } from '@react-navigation/native'; +import { RouteProp, useNavigation, useRoute } from '@react-navigation/native'; import { Icon } from '@rneui/themed'; -import { ActivityIndicator, InteractionManager, LayoutChangeEvent, ScrollView, StyleSheet, TouchableOpacity, View } from 'react-native'; -import { disallowScreenshot } from 'react-native-screen-capture'; - +import { LayoutChangeEvent, ScrollView, StyleSheet, TouchableOpacity, View } from 'react-native'; +import { enableScreenProtect, disableScreenProtect } from '../../helpers/screenProtect'; import { validateMnemonic } from '../../blue_modules/bip39'; import triggerHapticFeedback, { HapticFeedbackTypes } from '../../blue_modules/hapticFeedback'; import { BlueText } from '../../BlueComponents'; @@ -15,7 +14,6 @@ import SeedWords from '../../components/SeedWords'; import { useTheme } from '../../components/themes'; import { HandOffActivityType } from '../../components/types'; import { useSettings } from '../../hooks/context/useSettings'; -import { isDesktop } from '../../blue_modules/environment'; import { useStorage } from '../../hooks/context/useStorage'; import useAppState from '../../hooks/useAppState'; import loc from '../../loc'; @@ -57,9 +55,8 @@ const DoNotDisclose: React.FC = () => { }; const WalletExport: React.FC = () => { - const { wallets, saveToDisk } = useStorage(); + const { wallets } = useStorage(); const { walletID } = useRoute<RouteProps>().params; - const [isLoading, setIsLoading] = useState(true); const navigation = useNavigation(); const { isPrivacyBlurEnabled } = useSettings(); const { colors } = useTheme(); @@ -85,28 +82,20 @@ const WalletExport: React.FC = () => { }, [wallet]); useEffect(() => { - if (!isLoading && previousAppState === 'active' && currentAppState !== 'active') { + if (previousAppState === 'active' && currentAppState !== 'active') { const timer = setTimeout(() => navigation.goBack(), 500); return () => clearTimeout(timer); } - }, [currentAppState, previousAppState, navigation, isLoading]); + }, [currentAppState, previousAppState, navigation]); - useFocusEffect( - useCallback(() => { - if (!isDesktop) disallowScreenshot(isPrivacyBlurEnabled); - const task = InteractionManager.runAfterInteractions(async () => { - if (!wallet.getUserHasSavedExport()) { - wallet.setUserHasSavedExport(true); - saveToDisk(); - } - setIsLoading(false); - }); - return () => { - if (!isDesktop) disallowScreenshot(false); - task.cancel(); - }; - }, [isPrivacyBlurEnabled, wallet, saveToDisk]), - ); + useEffect(() => { + if (isPrivacyBlurEnabled) { + enableScreenProtect(); + } + return () => { + disableScreenProtect(); + }; + }, [isPrivacyBlurEnabled]); const onLayout = useCallback((e: LayoutChangeEvent) => { const { height, width } = e.nativeEvent.layout; @@ -128,22 +117,13 @@ const WalletExport: React.FC = () => { contentContainerStyle={styles.scrollViewContent} onLayout={onLayout} testID="WalletExportScroll" - centerContent={isLoading} > {children} </ScrollView> ), - [isLoading, onLayout, stylesHook.root], + [onLayout, stylesHook.root], ); - if (isLoading) { - return ( - <Scroll> - <ActivityIndicator /> - </Scroll> - ); - } - // for SLIP39 if (secrets.length !== 1) { return ( @@ -177,7 +157,6 @@ const WalletExport: React.FC = () => { contentContainerStyle={styles.scrollViewContent} onLayout={onLayout} testID="WalletExportScroll" - centerContent={isLoading} > {wallet.type !== WatchOnlyWallet.type && <DoNotDisclose />} diff --git a/screen/wallets/WalletTransactions.tsx b/screen/wallets/WalletTransactions.tsx index 758edaa1f..e2fc37eb8 100644 --- a/screen/wallets/WalletTransactions.tsx +++ b/screen/wallets/WalletTransactions.tsx @@ -297,10 +297,10 @@ const WalletTransactions: React.FC<WalletTransactionsProps> = ({ route }) => { const renderItem = useCallback( // eslint-disable-next-line react/no-unused-prop-types - ({ item }: { item: Transaction }) => { - return <TransactionListItem item={item} itemPriceUnit={wallet?.preferredBalanceUnit} walletID={walletID} />; - }, - [wallet, walletID], + ({ item }: { item: Transaction }) => ( + <TransactionListItem key={item.hash} item={item} itemPriceUnit={wallet?.preferredBalanceUnit} walletID={walletID} /> + ), + [wallet?.preferredBalanceUnit, walletID], ); const choosePhoto = () => { @@ -317,7 +317,7 @@ const WalletTransactions: React.FC<WalletTransactionsProps> = ({ route }) => { }); }; - const _keyExtractor = (_item: any, index: number) => index.toString(); + const _keyExtractor = useCallback((_item: any, index: number) => index.toString(), []); const pasteFromClipboard = async () => { onBarCodeRead({ data: await getClipboardContent() }); @@ -398,6 +398,7 @@ const WalletTransactions: React.FC<WalletTransactionsProps> = ({ route }) => { }); return () => { task.cancel(); + console.debug('Next screen is focused, clearing reloadTransactionsMenuActionFunction'); setReloadTransactionsMenuActionFunction(() => {}); }; }, [setReloadTransactionsMenuActionFunction, refreshTransactions]), @@ -525,7 +526,7 @@ const WalletTransactions: React.FC<WalletTransactionsProps> = ({ route }) => { <FlatList<Transaction> getItemLayout={getItemLayout} - updateCellsBatchingPeriod={30} + updateCellsBatchingPeriod={50} onEndReachedThreshold={0.3} onEndReached={loadMoreTransactions} ListFooterComponent={renderListFooterComponent} @@ -537,7 +538,7 @@ const WalletTransactions: React.FC<WalletTransactionsProps> = ({ route }) => { removeClippedSubviews contentContainerStyle={{ backgroundColor: colors.background }} contentInset={{ top: 0, left: 0, bottom: 90, right: 0 }} - maxToRenderPerBatch={15} + maxToRenderPerBatch={10} onScroll={handleScroll} scrollEventThrottle={16} stickyHeaderHiddenOnScroll @@ -555,6 +556,10 @@ const WalletTransactions: React.FC<WalletTransactionsProps> = ({ route }) => { <RefreshControl refreshing={isLoading} onRefresh={() => refreshTransactions(true)} tintColor={colors.msSuccessCheck} /> ) : undefined } + windowSize={15} + maintainVisibleContentPosition={{ + minIndexForVisible: 0, + }} /> <FContainer ref={walletActionButtonsRef}> {wallet?.allowReceive() && ( diff --git a/screen/wallets/WalletsList.tsx b/screen/wallets/WalletsList.tsx index 5e98b046b..ce69e2fcd 100644 --- a/screen/wallets/WalletsList.tsx +++ b/screen/wallets/WalletsList.tsx @@ -168,6 +168,7 @@ const WalletsList: React.FC = () => { }); return () => { task.cancel(); + console.debug('Next screen is focused, clearing reloadTransactionsMenuActionFunction'); setReloadTransactionsMenuActionFunction(() => {}); }; }, [onRefresh, setReloadTransactionsMenuActionFunction, verifyBalance, setSelectedWalletID]), @@ -259,7 +260,7 @@ const WalletsList: React.FC = () => { const renderTransactionListsRow = useCallback( (item: ExtendedTransaction) => ( <View style={styles.transaction}> - <TransactionListItem item={item} itemPriceUnit={item.walletPreferredBalanceUnit} walletID={item.walletID} /> + <TransactionListItem key={item.hash} item={item} itemPriceUnit={item.walletPreferredBalanceUnit} walletID={item.walletID} /> </View> ), [], @@ -357,9 +358,9 @@ const WalletsList: React.FC = () => { // eslint-disable-next-line react-hooks/exhaustive-deps }, [scanImage, wallets.length]); - const sectionListKeyExtractor = (item: any, index: any) => { + const sectionListKeyExtractor = useCallback((item: any, index: any) => { return `${item}${index}}`; - }; + }, []); const onScanButtonPressed = useCallback(() => { navigation.navigate('ScanQRCode', { @@ -420,6 +421,15 @@ const WalletsList: React.FC = () => { { key: WalletsListSections.TRANSACTIONS, data: dataSource }, ]; + const getItemLayout = useCallback( + (data: any, index: number) => ({ + length: 80, // Approximate height of each item + offset: 80 * index, + index, + }), + [], + ); + return ( <View style={styles.root}> <View style={[styles.walletsListWrapper, stylesHook.walletsListWrapper]}> @@ -438,6 +448,7 @@ const WalletsList: React.FC = () => { windowSize={21} maxToRenderPerBatch={10} updateCellsBatchingPeriod={50} + getItemLayout={getItemLayout} /> {renderScanButton()} </View> diff --git a/screen/wallets/addMultisigStep2.js b/screen/wallets/addMultisigStep2.js index 698192fa1..d5453f435 100644 --- a/screen/wallets/addMultisigStep2.js +++ b/screen/wallets/addMultisigStep2.js @@ -26,14 +26,12 @@ import QRCodeComponent from '../../components/QRCodeComponent'; import { useTheme } from '../../components/themes'; import confirm from '../../helpers/confirm'; import prompt from '../../helpers/prompt'; -import { disallowScreenshot } from 'react-native-screen-capture'; import loc from '../../loc'; import { useStorage } from '../../hooks/context/useStorage'; import { useExtendedNavigation } from '../../hooks/useExtendedNavigation'; import ToolTipMenu from '../../components/TooltipMenu'; import { CommonToolTipActions } from '../../typings/CommonToolTipActions'; import { useSettings } from '../../hooks/context/useSettings'; -import { isDesktop } from '../../blue_modules/environment'; import { useKeyboard } from '../../hooks/useKeyboard'; import { DoneAndDismissKeyboardInputAccessory, @@ -45,6 +43,7 @@ import MultipleStepsListItem, { MultipleStepsListItemDashType, } from '../../components/MultipleStepsListItem'; import { AddressInputScanButton } from '../../components/AddressInputScanButton'; +import { enableScreenProtect, disableScreenProtect } from '../../helpers/screenProtect'; const staticCache = {}; @@ -72,9 +71,11 @@ const WalletsAddMultisigStep2 = () => { useFocusEffect( useCallback(() => { - if (!isDesktop) disallowScreenshot(isPrivacyBlurEnabled); + if (isPrivacyBlurEnabled) { + enableScreenProtect(); + } return () => { - if (!isDesktop) disallowScreenshot(false); + disableScreenProtect(); }; }, [isPrivacyBlurEnabled]), ); diff --git a/screen/wallets/pleaseBackupLNDHub.js b/screen/wallets/pleaseBackupLNDHub.js index 54c6b8bec..8f23e398b 100644 --- a/screen/wallets/pleaseBackupLNDHub.js +++ b/screen/wallets/pleaseBackupLNDHub.js @@ -7,11 +7,10 @@ import CopyTextToClipboard from '../../components/CopyTextToClipboard'; import QRCodeComponent from '../../components/QRCodeComponent'; import SafeArea from '../../components/SafeArea'; import { useTheme } from '../../components/themes'; -import { disallowScreenshot } from 'react-native-screen-capture'; import loc from '../../loc'; import { useStorage } from '../../hooks/context/useStorage'; import { useSettings } from '../../hooks/context/useSettings'; -import { isDesktop } from '../../blue_modules/environment'; +import { enableScreenProtect, disableScreenProtect } from '../../helpers/screenProtect'; const PleaseBackupLNDHub = () => { const { wallets } = useStorage(); @@ -44,10 +43,12 @@ const PleaseBackupLNDHub = () => { }); useEffect(() => { - if (!isDesktop) disallowScreenshot(isPrivacyBlurEnabled); + if (isPrivacyBlurEnabled) { + enableScreenProtect(); + } BackHandler.addEventListener('hardwareBackPress', handleBackButton); return () => { - if (!isDesktop) disallowScreenshot(false); + disableScreenProtect(); BackHandler.removeEventListener('hardwareBackPress', handleBackButton); }; }, [handleBackButton, isPrivacyBlurEnabled]); diff --git a/screen/wallets/xpub.tsx b/screen/wallets/xpub.tsx index eac6a1f55..004211d0d 100644 --- a/screen/wallets/xpub.tsx +++ b/screen/wallets/xpub.tsx @@ -8,13 +8,12 @@ import CopyTextToClipboard from '../../components/CopyTextToClipboard'; import HandOffComponent from '../../components/HandOffComponent'; import QRCodeComponent from '../../components/QRCodeComponent'; import SafeArea from '../../components/SafeArea'; -import { disallowScreenshot } from 'react-native-screen-capture'; +import { enableScreenProtect, disableScreenProtect } from '../../helpers/screenProtect'; import loc from '../../loc'; import { styles, useDynamicStyles } from './xpub.styles'; import { useStorage } from '../../hooks/context/useStorage'; import { HandOffActivityType } from '../../components/types'; import { useSettings } from '../../hooks/context/useSettings'; -import { isDesktop } from '../../blue_modules/environment'; type WalletXpubRouteProp = RouteProp<{ params: { walletID: string; xpub: string } }, 'params'>; export type RootStackParamList = { @@ -39,7 +38,7 @@ const WalletXpub: React.FC = () => { useFocusEffect( useCallback(() => { - if (!isDesktop) disallowScreenshot(isPrivacyBlurEnabled); + if (isPrivacyBlurEnabled) enableScreenProtect(); // Skip execution if walletID hasn't changed if (lastWalletIdRef.current === walletID) { return; @@ -58,7 +57,7 @@ const WalletXpub: React.FC = () => { }); lastWalletIdRef.current = walletID; return () => { - if (!isDesktop) disallowScreenshot(false); + disableScreenProtect(); task.cancel(); }; }, [isPrivacyBlurEnabled, walletID, wallet, xpub, navigation]),