From 15c608a7afe5a41ca092ac7674d7f8af133bbe4e Mon Sep 17 00:00:00 2001 From: Marcos Rodriguez Velez Date: Tue, 17 Sep 2024 22:00:58 -0400 Subject: [PATCH] FIX: Allow user to define a custom block explorer URL #7077 --- blue_modules/notifications.js | 6 +- components/Context/SettingsProvider.tsx | 27 ++++ ...SettingsBlockExplorerCustomUrlListItem.tsx | 116 ++++++++++++++++++ components/TransactionListItem.tsx | 10 +- ios/Podfile.lock | 12 +- loc/en.json | 5 + models/blockExplorer.ts | 32 +++++ navigation/DetailViewScreensStack.tsx | 7 ++ navigation/DetailViewStackParamList.ts | 1 + navigation/LazyLoadSettingsStack.tsx | 7 ++ screen/send/Broadcast.tsx | 8 +- screen/send/success.js | 4 +- ...NetworkSettings.js => NetworkSettings.tsx} | 22 ++-- screen/settings/SettingsBlockExplorer.tsx | 90 ++++++++++++++ screen/transactions/TransactionDetails.tsx | 8 +- screen/transactions/TransactionStatus.tsx | 4 +- 16 files changed, 328 insertions(+), 31 deletions(-) create mode 100644 components/SettingsBlockExplorerCustomUrlListItem.tsx create mode 100644 models/blockExplorer.ts rename screen/settings/{NetworkSettings.js => NetworkSettings.tsx} (54%) create mode 100644 screen/settings/SettingsBlockExplorer.tsx diff --git a/blue_modules/notifications.js b/blue_modules/notifications.js index dc97ad4db..3fbb66c96 100644 --- a/blue_modules/notifications.js +++ b/blue_modules/notifications.js @@ -31,8 +31,6 @@ function Notifications(props) { return false; }; - Notifications.isNotificationsCapable = hasGmsSync() || hasHmsSync() || Platform.OS !== 'android'; - /** * Calls `configure`, which tries to obtain push token, save it, and registers all associated with * notifications callbacks @@ -131,7 +129,7 @@ function Notifications(props) { * @returns {Promise} TRUE if permissions were obtained, FALSE otherwise */ Notifications.tryToObtainPermissions = async function (anchor) { - if (!Notifications.isNotificationsCapable) return false; + if (!isNotificationsCapable) return false; if (await Notifications.getPushToken()) { // we already have a token, no sense asking again, just configure pushes to register callbacks and we are done if (!alreadyConfigured) configureNotifications(); // no await so it executes in background while we return TRUE and use token @@ -441,4 +439,6 @@ function Notifications(props) { return null; } +export const isNotificationsCapable = hasGmsSync() || hasHmsSync() || Platform.OS !== 'android'; + export default Notifications; diff --git a/components/Context/SettingsProvider.tsx b/components/Context/SettingsProvider.tsx index 444e98c10..dfde2b712 100644 --- a/components/Context/SettingsProvider.tsx +++ b/components/Context/SettingsProvider.tsx @@ -14,6 +14,7 @@ import { useStorage } from '../../hooks/context/useStorage'; import { BitcoinUnit } from '../../models/bitcoinUnits'; import { TotalWalletsBalanceKey, TotalWalletsBalancePreferredUnit } from '../TotalWalletsBalance'; import { LayoutAnimation } from 'react-native'; +import { BLOCK_EXPLORERS, getBlockExplorer, saveBlockExplorer } from '../../models/blockExplorer'; // DefaultPreference and AsyncStorage get/set @@ -85,6 +86,8 @@ interface SettingsContextType { setTotalBalancePreferredUnitStorage: (unit: BitcoinUnit) => Promise; isDrawerShouldHide: boolean; setIsDrawerShouldHide: (value: boolean) => void; + selectedBlockExplorer: string; + setBlockExplorerStorage: (url: string) => Promise; } const defaultSettingsContext: SettingsContextType = { @@ -112,6 +115,8 @@ const defaultSettingsContext: SettingsContextType = { setTotalBalancePreferredUnitStorage: async (unit: BitcoinUnit) => {}, isDrawerShouldHide: false, setIsDrawerShouldHide: () => {}, + selectedBlockExplorer: BLOCK_EXPLORERS.DEFAULT, + setBlockExplorerStorage: async () => false, }; export const SettingsContext = createContext(defaultSettingsContext); @@ -142,6 +147,8 @@ export const SettingsProvider: React.FC<{ children: React.ReactNode }> = ({ chil // Toggle Drawer (for screens like Manage Wallets or ScanQRCode) const [isDrawerShouldHide, setIsDrawerShouldHide] = useState(false); + const [selectedBlockExplorer, setSelectedBlockExplorer] = useState(BLOCK_EXPLORERS.DEFAULT); + const languageStorage = useAsyncStorage(STORAGE_KEY); const { walletsInitialized } = useStorage(); @@ -211,6 +218,14 @@ export const SettingsProvider: React.FC<{ children: React.ReactNode }> = ({ chil setTotalBalancePreferredUnitState(unit); }) .catch(error => console.error('Error fetching total balance preferred unit:', error)); + + getBlockExplorer() + .then(url => { + console.debug('SettingsContext blockExplorer:', url); + setSelectedBlockExplorer(url); + }) + .catch(error => console.error('Error fetching block explorer settings:', error)); + // eslint-disable-next-line react-hooks/exhaustive-deps }, []); @@ -295,6 +310,14 @@ export const SettingsProvider: React.FC<{ children: React.ReactNode }> = ({ chil setTotalBalancePreferredUnitState(unit); }, []); + const setBlockExplorerStorage = useCallback(async (url: string): Promise => { + const success = await saveBlockExplorer(url); + if (success) { + setSelectedBlockExplorer(url); + } + return success; + }, []); + const value = useMemo( () => ({ preferredFiatCurrency, @@ -321,6 +344,8 @@ export const SettingsProvider: React.FC<{ children: React.ReactNode }> = ({ chil setTotalBalancePreferredUnitStorage, isDrawerShouldHide, setIsDrawerShouldHide, + selectedBlockExplorer, + setBlockExplorerStorage, }), [ preferredFiatCurrency, @@ -347,6 +372,8 @@ export const SettingsProvider: React.FC<{ children: React.ReactNode }> = ({ chil setTotalBalancePreferredUnitStorage, isDrawerShouldHide, setIsDrawerShouldHide, + selectedBlockExplorer, + setBlockExplorerStorage, ], ); diff --git a/components/SettingsBlockExplorerCustomUrlListItem.tsx b/components/SettingsBlockExplorerCustomUrlListItem.tsx new file mode 100644 index 000000000..aea693c84 --- /dev/null +++ b/components/SettingsBlockExplorerCustomUrlListItem.tsx @@ -0,0 +1,116 @@ +import React from 'react'; +import { StyleSheet, TextInput, View, TouchableOpacity } from 'react-native'; +import { ListItem as RNElementsListItem } from '@rneui/themed'; +import { useTheme } from './themes'; +import loc from '../loc'; + +interface SettingsBlockExplorerCustomUrlListItemProps { + title: string; + customUrl?: string; + onCustomUrlChange?: (url: string) => void; + onSubmitCustomUrl?: () => void; + selected: boolean; + onPress: () => void; + checkmark?: boolean; + isLoading?: boolean; +} + +const SettingsBlockExplorerCustomUrlListItem: React.FC = ({ + title, + customUrl, + onCustomUrlChange, + onSubmitCustomUrl, + selected, + onPress, + checkmark = false, + isLoading = false, +}) => { + const { colors } = useTheme(); + const styleHook = StyleSheet.create({ + uri: { + borderColor: colors.formBorder, + borderBottomColor: colors.formBorder, + backgroundColor: colors.inputBackgroundColor, + }, + containerStyle: { + backgroundColor: colors.background, + minHeight: checkmark ? 140 : 60, + }, + checkmarkContainer: { + justifyContent: 'center', + alignItems: 'center', // + }, + checkmarkStyle: { + backgroundColor: 'transparent', + borderWidth: 0, + }, + }); + + return ( + + + + {title} + + {checkmark && ( + + + + )} + + + {selected && ( + + + + )} + + ); +}; + +const styles = StyleSheet.create({ + title: { + fontSize: 16, + fontWeight: '500', + }, + uri: { + flexDirection: 'row', + borderWidth: 1, + borderBottomWidth: 0.5, + minHeight: 44, + height: 44, + alignItems: 'center', + borderRadius: 4, + }, + uriText: { + flex: 1, + color: '#81868e', + marginHorizontal: 8, + minHeight: 36, + height: 36, + }, +}); + +export default SettingsBlockExplorerCustomUrlListItem; diff --git a/components/TransactionListItem.tsx b/components/TransactionListItem.tsx index 239980f4f..65231255f 100644 --- a/components/TransactionListItem.tsx +++ b/components/TransactionListItem.tsx @@ -43,7 +43,7 @@ export const TransactionListItem: React.FC = React.mem const { navigate } = useExtendedNavigation(); const menuRef = useRef(); const { txMetadata, counterpartyMetadata, wallets } = useStorage(); - const { language } = useSettings(); + const { language, selectedBlockExplorer } = useSettings(); const containerStyle = useMemo( () => ({ backgroundColor: 'transparent', @@ -253,16 +253,16 @@ export const TransactionListItem: React.FC = React.mem const handleOnCopyTransactionID = useCallback(() => Clipboard.setString(item.hash), [item.hash]); const handleOnCopyNote = useCallback(() => Clipboard.setString(subtitle ?? ''), [subtitle]); const handleOnViewOnBlockExplorer = useCallback(() => { - const url = `https://mempool.space/tx/${item.hash}`; + const url = `${selectedBlockExplorer}/${item.hash}`; Linking.canOpenURL(url).then(supported => { if (supported) { Linking.openURL(url); } }); - }, [item.hash]); + }, [item.hash, selectedBlockExplorer]); const handleCopyOpenInBlockExplorerPress = useCallback(() => { - Clipboard.setString(`https://mempool.space/tx/${item.hash}`); - }, [item.hash]); + Clipboard.setString(`${selectedBlockExplorer}/${item.hash}`); + }, [item.hash, selectedBlockExplorer]); const onToolTipPress = useCallback( (id: any) => { diff --git a/ios/Podfile.lock b/ios/Podfile.lock index 23cd31114..1b78d0995 100644 --- a/ios/Podfile.lock +++ b/ios/Podfile.lock @@ -1596,7 +1596,7 @@ PODS: - React - RNCAsyncStorage (1.24.0): - React-Core - - RNCClipboard (1.14.1): + - RNCClipboard (1.14.2): - React-Core - RNCPushNotificationIOS (1.11.0): - React-Core @@ -1641,7 +1641,7 @@ PODS: - React - RNRate (1.2.12): - React-Core - - RNReactNativeHapticFeedback (2.3.1): + - RNReactNativeHapticFeedback (2.3.2): - DoubleConversion - glog - hermes-engine @@ -1751,7 +1751,7 @@ PODS: - Yoga - RNShare (10.2.1): - React-Core - - RNSVG (15.6.0): + - RNSVG (15.7.1): - React-Core - RNVectorIcons (10.2.0): - DoubleConversion @@ -2202,7 +2202,7 @@ SPEC CHECKSUMS: ReactNativeCameraKit: 9d46a5d7dd544ca64aa9c03c150d2348faf437eb RealmJS: 325a7b621587dd9945306d4cbfd6b641bc20e2dd RNCAsyncStorage: ec53e44dc3e75b44aa2a9f37618a49c3bc080a7a - RNCClipboard: 0a720adef5ec193aa0e3de24c3977222c7e52a37 + RNCClipboard: 5e503962f0719ace8f7fdfe9c60282b526305c85 RNCPushNotificationIOS: 64218f3c776c03d7408284a819b2abfda1834bc8 RNDefaultPreference: 08bdb06cfa9188d5da97d4642dac745218d7fb31 RNDeviceInfo: b899ce37a403a4dea52b7cb85e16e49c04a5b88e @@ -2215,11 +2215,11 @@ SPEC CHECKSUMS: RNPrivacySnapshot: 71919dde3c6a29dd332115409c2aec564afee8f4 RNQuickAction: 6d404a869dc872cde841ad3147416a670d13fa93 RNRate: ef3bcff84f39bb1d1e41c5593d3eea4aab2bd73a - RNReactNativeHapticFeedback: 2bdbd63bcdbb52c4ae81a7b0c48ab1f00c06980a + RNReactNativeHapticFeedback: 9b35ff960958c399b03581140fa2d1499c09c8b6 RNReanimated: ece067b779e0d6c7887c6bb80d381d0a0efd43c9 RNScreens: 19719a9c326e925498ac3b2d35c4e50fe87afc06 RNShare: 0fad69ae2d71de9d1f7b9a43acf876886a6cb99c - RNSVG: 5da7a24f31968ec74f0b091e3440080f347e279b + RNSVG: 4590aa95758149fa27c5c83e54a6a466349a1688 RNVectorIcons: 6382277afab3c54658e9d555ee0faa7a37827136 RNWatch: fd30ca40a5b5ef58dcbc195638e68219bc455236 SocketRocket: abac6f5de4d4d62d24e11868d7a2f427e0ef940d diff --git a/loc/en.json b/loc/en.json index 9ecf5079e..a099d1c48 100644 --- a/loc/en.json +++ b/loc/en.json @@ -10,6 +10,8 @@ "never": "Never", "of": "{number} of {total}", "ok": "OK", + "default": "Default", + "enter_url": "Enter URL", "storage_is_encrypted": "Your storage is encrypted. Password is required to decrypt it.", "yes": "Yes", "no": "No", @@ -262,6 +264,9 @@ "encrypt_storage_explanation_description_line1": "Enabling Storage Encryption adds an extra layer of protection to your app by securing the way your data is stored on your device. This makes it harder for anyone to access your information without permission.", "encrypt_storage_explanation_description_line2": "However, it's important to know that this encryption only protects the access to your wallets stored on the device's keychain. It doesn't put a password or any extra protection on the wallets themselves.", "i_understand": "I understand", + "block_explorer": "Block Explorer", + "block_explorer_custom": "Custom Block Explorer", + "block_explorer_error_saving_custom": "Error saving custom block explorer", "encrypt_title": "Security", "encrypt_tstorage": "Storage", "encrypt_use": "Use {type}", diff --git a/models/blockExplorer.ts b/models/blockExplorer.ts new file mode 100644 index 000000000..628892cbc --- /dev/null +++ b/models/blockExplorer.ts @@ -0,0 +1,32 @@ +import DefaultPreference from 'react-native-default-preference'; +import { GROUP_IO_BLUEWALLET } from '../blue_modules/currency'; + +export const BLOCK_EXPLORER = 'blockExplorer'; +export const BLOCK_EXPLORERS = { + DEFAULT: 'https://mempool.space/tx', + BLOCKCHAIR: 'https://blockchair.com', + BLOCKSTREAM: 'https://blockstream.info', + CUSTOM: 'custom', +}; + +export const getBlockExplorer = async (): Promise => { + try { + await DefaultPreference.setName(GROUP_IO_BLUEWALLET); + const selectedExplorer = await DefaultPreference.get(BLOCK_EXPLORER); + return selectedExplorer || BLOCK_EXPLORERS.DEFAULT; // Return the selected explorer or default to mempool.space + } catch (error) { + console.error('Error getting block explorer:', error); + return BLOCK_EXPLORERS.DEFAULT; + } +}; + +export const saveBlockExplorer = async (url: string): Promise => { + try { + await DefaultPreference.setName(GROUP_IO_BLUEWALLET); + await DefaultPreference.set(BLOCK_EXPLORER, url); // Save whatever URL is provided (either predefined or custom) + return true; + } catch (error) { + console.error('Error saving block explorer:', error); + return false; + } +}; diff --git a/navigation/DetailViewScreensStack.tsx b/navigation/DetailViewScreensStack.tsx index 5d8e34b06..c9915756a 100644 --- a/navigation/DetailViewScreensStack.tsx +++ b/navigation/DetailViewScreensStack.tsx @@ -31,6 +31,7 @@ import AddWalletStack from './AddWalletStack'; import AztecoRedeemStackRoot from './AztecoRedeemStack'; import { AboutComponent, + BlockExplorerSettingsComponent, CurrencyComponent, DefaultViewComponent, ElectrumSettingsComponent, @@ -304,6 +305,12 @@ const DetailViewStackScreensStack = () => { component={NetworkSettingsComponent} options={navigationStyle({ title: loc.settings.network })(theme)} /> + + import('../screen/settings/Settings')); const GeneralSettings = lazy(() => import('../screen/settings/GeneralSettings')); @@ -46,6 +47,12 @@ export const NetworkSettingsComponent = () => ( ); +export const BlockExplorerSettingsComponent = () => ( + }> + + +); + export const AboutComponent = () => ( }> diff --git a/screen/send/Broadcast.tsx b/screen/send/Broadcast.tsx index cd39c947e..55b17088d 100644 --- a/screen/send/Broadcast.tsx +++ b/screen/send/Broadcast.tsx @@ -23,6 +23,7 @@ import { useTheme } from '../../components/themes'; import { scanQrHelper } from '../../helpers/scan-qr'; import loc from '../../loc'; import { DetailViewStackParamList } from '../../navigation/DetailViewStackParamList'; +import { useSettings } from '../../hooks/context/useSettings'; const BROADCAST_RESULT = Object.freeze({ none: 'Input transaction hex', @@ -39,6 +40,7 @@ const Broadcast: React.FC = () => { const [txHex, setTxHex] = useState(); const { colors } = useTheme(); const [broadcastResult, setBroadcastResult] = useState(BROADCAST_RESULT.none); + const { selectedBlockExplorer } = useSettings(); const stylesHooks = StyleSheet.create({ input: { @@ -158,13 +160,13 @@ const Broadcast: React.FC = () => { )} - {BROADCAST_RESULT.success === broadcastResult && tx && } + {BROADCAST_RESULT.success === broadcastResult && tx && } ); }; -const SuccessScreen: React.FC<{ tx: string }> = ({ tx }) => { +const SuccessScreen: React.FC<{ tx: string; url: string }> = ({ tx, url }) => { if (!tx) { return null; } @@ -177,7 +179,7 @@ const SuccessScreen: React.FC<{ tx: string }> = ({ tx }) => { {loc.settings.success_transaction_broadcasted} - Linking.openURL(`https://mempool.space/tx/${tx}`)} /> + Linking.openURL(url)} /> diff --git a/screen/send/success.js b/screen/send/success.js index d8351bb04..f83dc22c2 100644 --- a/screen/send/success.js +++ b/screen/send/success.js @@ -13,12 +13,14 @@ import loc from '../../loc'; import { BitcoinUnit } from '../../models/bitcoinUnits'; import HandOffComponent from '../../components/HandOffComponent'; import { HandOffActivityType } from '../../components/types'; +import { useSettings } from '../../hooks/context/useSettings'; const Success = () => { const pop = () => { getParent().pop(); }; const { colors } = useTheme(); + const { selectedBlockExplorer } = useSettings(); const { getParent } = useNavigation(); const { amount, fee, amountUnit = BitcoinUnit.BTC, invoiceDescription = '', onDonePressed = pop, txid } = useRoute().params; const stylesHook = StyleSheet.create({ @@ -52,7 +54,7 @@ const Success = () => { )} diff --git a/screen/settings/NetworkSettings.js b/screen/settings/NetworkSettings.tsx similarity index 54% rename from screen/settings/NetworkSettings.js rename to screen/settings/NetworkSettings.tsx index b8bf06bab..40123ae9e 100644 --- a/screen/settings/NetworkSettings.js +++ b/screen/settings/NetworkSettings.tsx @@ -1,30 +1,34 @@ -import { useNavigation } from '@react-navigation/native'; import React from 'react'; import { ScrollView } from 'react-native'; - -import Notifications from '../../blue_modules/notifications'; +import { isNotificationsCapable } from '../../blue_modules/notifications'; import ListItem from '../../components/ListItem'; import loc from '../../loc'; +import { useExtendedNavigation } from '../../hooks/useExtendedNavigation'; -const NetworkSettings = () => { - const { navigate } = useNavigation(); +const NetworkSettings: React.FC = () => { + const navigation = useExtendedNavigation(); const navigateToElectrumSettings = () => { - navigate('ElectrumSettings'); + navigation.navigate('ElectrumSettings'); }; const navigateToLightningSettings = () => { - navigate('LightningSettings'); + navigation.navigate('LightningSettings'); + }; + + const navigateToBlockExplorerSettings = () => { + navigation.navigate('SettingsBlockExplorer'); }; return ( + - {Notifications.isNotificationsCapable && ( + {isNotificationsCapable && ( navigate('NotificationSettings')} + onPress={() => navigation.navigate('NotificationSettings')} testID="NotificationSettings" chevron /> diff --git a/screen/settings/SettingsBlockExplorer.tsx b/screen/settings/SettingsBlockExplorer.tsx new file mode 100644 index 000000000..38315a97e --- /dev/null +++ b/screen/settings/SettingsBlockExplorer.tsx @@ -0,0 +1,90 @@ +import React, { useState } from 'react'; +import { FlatList, Alert, StyleSheet } from 'react-native'; +import ListItem from '../../components/ListItem'; +import SettingsBlockExplorerCustomUrlListItem from '../../components/SettingsBlockExplorerCustomUrlListItem'; +import loc from '../../loc'; +import { BLOCK_EXPLORERS } from '../../models/blockExplorer'; +import { useTheme } from '../../components/themes'; +import { useSettings } from '../../hooks/context/useSettings'; + +const SettingsBlockExplorer: React.FC = () => { + const { selectedBlockExplorer, setBlockExplorerStorage } = useSettings(); + const [customUrlInput, setCustomUrlInput] = useState(selectedBlockExplorer || ''); + const { colors } = useTheme(); + + const blockExplorers = [ + { name: `${loc._.default} - Mempool.space`, key: 'default', url: BLOCK_EXPLORERS.DEFAULT }, + { name: 'Blockchair', key: 'blockchair', url: BLOCK_EXPLORERS.BLOCKCHAIR }, + { name: 'Blockstream.info', key: 'blockstream', url: BLOCK_EXPLORERS.BLOCKSTREAM }, + { name: loc.settings.block_explorer_custom, key: 'custom', url: null }, + ]; + + const handleExplorerPress = async (key: string, url: string | null) => { + if (key === 'custom') { + setCustomUrlInput(selectedBlockExplorer); + return; + } + + const success = await setBlockExplorerStorage(url!); + if (!success) { + Alert.alert(loc.errors.error, loc.settings.block_explorer_error_saving_custom); + } + }; + + const handleCustomUrlChange = (url: string) => { + setCustomUrlInput(url); + }; + + const handleSubmitCustomUrl = async () => { + if (!customUrlInput) return; + const success = await setBlockExplorerStorage(customUrlInput); + if (!success) { + Alert.alert(loc.errors.error, loc.settings.block_explorer_error_saving_custom); + } + }; + + const renderItem = ({ item }: { item: { name: string; key: string; url: string | null } }) => { + if (item.key === 'custom') { + return ( + handleExplorerPress(item.key, item.url)} + checkmark={selectedBlockExplorer === customUrlInput} + /> + ); + } + + return ( + handleExplorerPress(item.key, item.url)} + checkmark={selectedBlockExplorer === item.url} + disabled={selectedBlockExplorer === item.url} + containerStyle={{ backgroundColor: colors.background }} + /> + ); + }; + + return ( + item.key} + renderItem={renderItem} + contentInsetAdjustmentBehavior="automatic" + automaticallyAdjustContentInsets + style={[styles.container, { backgroundColor: colors.background }]} + /> + ); +}; + +export default SettingsBlockExplorer; + +const styles = StyleSheet.create({ + container: { + flex: 1, + }, +}); diff --git a/screen/transactions/TransactionDetails.tsx b/screen/transactions/TransactionDetails.tsx index 9374b519e..4851cf033 100644 --- a/screen/transactions/TransactionDetails.tsx +++ b/screen/transactions/TransactionDetails.tsx @@ -19,6 +19,7 @@ import { useExtendedNavigation } from '../../hooks/useExtendedNavigation'; import { DetailViewStackParamList } from '../../navigation/DetailViewStackParamList'; import { useStorage } from '../../hooks/context/useStorage'; import { HandOffActivityType } from '../../components/types'; +import { useSettings } from '../../hooks/context/useSettings'; const actionKeys = { CopyToClipboard: 'copyToClipboard', @@ -63,6 +64,7 @@ const TransactionDetails = () => { const { setOptions, navigate } = useExtendedNavigation(); const { hash, walletID } = useRoute().params; const { saveToDisk, txMetadata, counterpartyMetadata, wallets, getTransactions } = useStorage(); + const { selectedBlockExplorer } = useSettings(); const [from, setFrom] = useState([]); const [to, setTo] = useState([]); const [isLoading, setIsLoading] = useState(true); @@ -159,7 +161,7 @@ const TransactionDetails = () => { ); const handleOnOpenTransactionOnBlockExplorerTapped = () => { - const url = `https://mempool.space/tx/${tx?.hash}`; + const url = `${selectedBlockExplorer}/${tx?.hash}`; Linking.canOpenURL(url) .then(supported => { if (supported) { @@ -184,7 +186,7 @@ const TransactionDetails = () => { }; const handleCopyPress = (stringToCopy: string) => { - Clipboard.setString(stringToCopy !== actionKeys.CopyToClipboard ? stringToCopy : `https://mempool.space/tx/${tx?.hash}`); + Clipboard.setString(stringToCopy !== actionKeys.CopyToClipboard ? stringToCopy : `${selectedBlockExplorer}/${tx?.hash}`); }; if (isLoading || !tx) { @@ -255,7 +257,7 @@ const TransactionDetails = () => { diff --git a/screen/transactions/TransactionStatus.tsx b/screen/transactions/TransactionStatus.tsx index 5094c606a..c43ee5db9 100644 --- a/screen/transactions/TransactionStatus.tsx +++ b/screen/transactions/TransactionStatus.tsx @@ -21,6 +21,7 @@ import { useStorage } from '../../hooks/context/useStorage'; import { HandOffActivityType } from '../../components/types'; import HeaderRightButton from '../../components/HeaderRightButton'; import { DetailViewStackParamList } from '../../navigation/DetailViewStackParamList'; +import { useSettings } from '../../hooks/context/useSettings'; enum ButtonStatus { Possible, @@ -97,6 +98,7 @@ const TransactionStatus = () => { const { navigate, setOptions, goBack } = useNavigation(); const { colors } = useTheme(); const wallet = useRef(wallets.find(w => w.getID() === walletID)); + const { selectedBlockExplorer } = useSettings(); const fetchTxInterval = useRef(); const stylesHook = StyleSheet.create({ value: { @@ -481,7 +483,7 @@ const TransactionStatus = () => {