Merge branch 'master' into realmtmp

This commit is contained in:
Marcos Rodriguez Velez 2024-04-09 14:39:24 -04:00
commit 80a3318780
No known key found for this signature in database
GPG key ID: 6030B2F48CCE86D7
24 changed files with 239 additions and 146 deletions

2
App.js
View file

@ -27,7 +27,7 @@ import WatchConnectivity from './WatchConnectivity';
import DeviceQuickActions from './class/quick-actions'; import DeviceQuickActions from './class/quick-actions';
import Notifications from './blue_modules/notifications'; import Notifications from './blue_modules/notifications';
import Biometric from './class/biometrics'; import Biometric from './class/biometrics';
import WidgetCommunication from './blue_modules/WidgetCommunication'; import WidgetCommunication from './components/WidgetCommunication';
import ActionSheet from './screen/ActionSheet'; import ActionSheet from './screen/ActionSheet';
import triggerHapticFeedback, { HapticFeedbackTypes } from './blue_modules/hapticFeedback'; import triggerHapticFeedback, { HapticFeedbackTypes } from './blue_modules/hapticFeedback';
import MenuElements from './components/MenuElements'; import MenuElements from './components/MenuElements';

View file

@ -4,11 +4,10 @@ import * as bitcoin from 'bitcoinjs-lib';
import { Alert } from 'react-native'; import { Alert } from 'react-native';
import DefaultPreference from 'react-native-default-preference'; import DefaultPreference from 'react-native-default-preference';
import Realm from 'realm'; import Realm from 'realm';
import RNFS from 'react-native-fs';
import { LegacyWallet, SegwitBech32Wallet, SegwitP2SHWallet, TaprootWallet } from '../class'; import { LegacyWallet, SegwitBech32Wallet, SegwitP2SHWallet, TaprootWallet } from '../class';
import presentAlert from '../components/Alert'; import presentAlert from '../components/Alert';
import loc from '../loc'; import loc from '../loc';
import WidgetCommunication from './WidgetCommunication'; import { reloadAllTimelines } from '../components/WidgetCommunication';
const ElectrumClient = require('electrum-client'); const ElectrumClient = require('electrum-client');
const net = require('net'); const net = require('net');
@ -213,7 +212,7 @@ export async function connectMain(): Promise<void> {
await DefaultPreference.set(ELECTRUM_SSL_PORT, usingPeer.ssl ?? ''); await DefaultPreference.set(ELECTRUM_SSL_PORT, usingPeer.ssl ?? '');
} }
WidgetCommunication.reloadAllTimelines(); reloadAllTimelines();
} catch (e) { } catch (e) {
// Must be running on Android // Must be running on Android
console.log(e); console.log(e);
@ -340,7 +339,7 @@ const presentNetworkErrorAlert = async (usingPeer?: Peer) => {
await DefaultPreference.clear(ELECTRUM_HOST); await DefaultPreference.clear(ELECTRUM_HOST);
await DefaultPreference.clear(ELECTRUM_SSL_PORT); await DefaultPreference.clear(ELECTRUM_SSL_PORT);
await DefaultPreference.clear(ELECTRUM_TCP_PORT); await DefaultPreference.clear(ELECTRUM_TCP_PORT);
WidgetCommunication.reloadAllTimelines(); reloadAllTimelines();
} catch (e) { } catch (e) {
// Must be running on Android // Must be running on Android
console.log(e); console.log(e);

View file

@ -1,79 +0,0 @@
import { useContext, useEffect } from 'react';
import { BlueStorageContext } from './storage-context';
import DefaultPreference from 'react-native-default-preference';
import RNWidgetCenter from 'react-native-widget-center';
import AsyncStorage from '@react-native-async-storage/async-storage';
function WidgetCommunication() {
WidgetCommunication.WidgetCommunicationAllWalletsSatoshiBalance = 'WidgetCommunicationAllWalletsSatoshiBalance';
WidgetCommunication.WidgetCommunicationAllWalletsLatestTransactionTime = 'WidgetCommunicationAllWalletsLatestTransactionTime';
WidgetCommunication.WidgetCommunicationDisplayBalanceAllowed = 'WidgetCommunicationDisplayBalanceAllowed';
WidgetCommunication.LatestTransactionIsUnconfirmed = 'WidgetCommunicationLatestTransactionIsUnconfirmed';
const { wallets, walletsInitialized, isStorageEncrypted } = useContext(BlueStorageContext);
WidgetCommunication.isBalanceDisplayAllowed = async () => {
try {
const displayBalance = JSON.parse(await AsyncStorage.getItem(WidgetCommunication.WidgetCommunicationDisplayBalanceAllowed));
if (displayBalance !== null) {
return displayBalance;
} else {
return true;
}
} catch (e) {
return true;
}
};
WidgetCommunication.setBalanceDisplayAllowed = async value => {
await AsyncStorage.setItem(WidgetCommunication.WidgetCommunicationDisplayBalanceAllowed, JSON.stringify(value));
setValues();
};
WidgetCommunication.reloadAllTimelines = () => {
RNWidgetCenter.reloadAllTimelines();
};
const allWalletsBalanceAndTransactionTime = async () => {
if ((await isStorageEncrypted()) || !(await WidgetCommunication.isBalanceDisplayAllowed())) {
return { allWalletsBalance: 0, latestTransactionTime: 0 };
} else {
let balance = 0;
let latestTransactionTime = 0;
for (const wallet of wallets) {
if (wallet.hideBalance) {
continue;
}
balance += wallet.getBalance();
if (wallet.getLatestTransactionTimeEpoch() > latestTransactionTime) {
if (wallet.getTransactions()[0].confirmations === 0) {
latestTransactionTime = WidgetCommunication.LatestTransactionIsUnconfirmed;
} else {
latestTransactionTime = wallet.getLatestTransactionTimeEpoch();
}
}
}
return { allWalletsBalance: balance, latestTransactionTime };
}
};
const setValues = async () => {
await DefaultPreference.setName('group.io.bluewallet.bluewallet');
const { allWalletsBalance, latestTransactionTime } = await allWalletsBalanceAndTransactionTime();
await DefaultPreference.set(WidgetCommunication.WidgetCommunicationAllWalletsSatoshiBalance, JSON.stringify(allWalletsBalance));
await DefaultPreference.set(
WidgetCommunication.WidgetCommunicationAllWalletsLatestTransactionTime,
JSON.stringify(latestTransactionTime),
);
RNWidgetCenter.reloadAllTimelines();
};
useEffect(() => {
if (walletsInitialized) {
setValues();
}
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [wallets, walletsInitialized]);
return null;
}
export default WidgetCommunication;

View file

@ -3,7 +3,7 @@ import DefaultPreference from 'react-native-default-preference';
import * as RNLocalize from 'react-native-localize'; import * as RNLocalize from 'react-native-localize';
import BigNumber from 'bignumber.js'; import BigNumber from 'bignumber.js';
import { FiatUnit, FiatUnitType, getFiatRate } from '../models/fiatUnit'; import { FiatUnit, FiatUnitType, getFiatRate } from '../models/fiatUnit';
import WidgetCommunication from './WidgetCommunication'; import { reloadAllTimelines } from '../components/WidgetCommunication';
const PREFERRED_CURRENCY_STORAGE_KEY = 'preferredCurrency'; const PREFERRED_CURRENCY_STORAGE_KEY = 'preferredCurrency';
const PREFERRED_CURRENCY_LOCALE_STORAGE_KEY = 'preferredCurrencyLocale'; const PREFERRED_CURRENCY_LOCALE_STORAGE_KEY = 'preferredCurrencyLocale';
@ -33,7 +33,7 @@ async function setPreferredCurrency(item: FiatUnitType): Promise<void> {
await DefaultPreference.set(PREFERRED_CURRENCY_STORAGE_KEY, item.endPointKey); await DefaultPreference.set(PREFERRED_CURRENCY_STORAGE_KEY, item.endPointKey);
await DefaultPreference.set(PREFERRED_CURRENCY_LOCALE_STORAGE_KEY, item.locale.replace('-', '_')); await DefaultPreference.set(PREFERRED_CURRENCY_LOCALE_STORAGE_KEY, item.locale.replace('-', '_'));
// @ts-ignore: Convert to TSX later // @ts-ignore: Convert to TSX later
WidgetCommunication.reloadAllTimelines(); reloadAllTimelines();
} }
async function getPreferredCurrency(): Promise<FiatUnitType> { async function getPreferredCurrency(): Promise<FiatUnitType> {

View file

@ -41,7 +41,7 @@ export class DynamicQRCode extends Component<DynamicQRCodeProps, DynamicQRCodeSt
fragments: string[] = []; fragments: string[] = [];
componentDidMount() { componentDidMount() {
const { value, capacity = 200, hideControls = true } = this.props; const { value, capacity = 175, hideControls = true } = this.props;
try { try {
this.fragments = encodeUR(value, capacity); this.fragments = encodeUR(value, capacity);
this.setState( this.setState(

View file

@ -12,9 +12,20 @@ interface SaveFileButtonProps {
style?: StyleProp<ViewStyle>; style?: StyleProp<ViewStyle>;
afterOnPress?: () => void; afterOnPress?: () => void;
beforeOnPress?: () => Promise<void>; // Changed this line beforeOnPress?: () => Promise<void>; // Changed this line
onMenuWillHide?: () => void;
onMenuWillShow?: () => void;
} }
const SaveFileButton: React.FC<SaveFileButtonProps> = ({ fileName, fileContent, children, style, beforeOnPress, afterOnPress }) => { const SaveFileButton: React.FC<SaveFileButtonProps> = ({
fileName,
fileContent,
children,
style,
beforeOnPress,
afterOnPress,
onMenuWillHide,
onMenuWillShow,
}) => {
const actions = [ const actions = [
{ id: 'save', text: loc._.save, icon: actionIcons.Save }, { id: 'save', text: loc._.save, icon: actionIcons.Save },
{ id: 'share', text: loc.receive.details_share, icon: actionIcons.Share }, { id: 'share', text: loc.receive.details_share, icon: actionIcons.Share },
@ -39,7 +50,15 @@ const SaveFileButton: React.FC<SaveFileButtonProps> = ({ fileName, fileContent,
return ( return (
// @ts-ignore: Tooltip must be refactored to use TSX} // @ts-ignore: Tooltip must be refactored to use TSX}
<ToolTipMenu isButton isMenuPrimaryAction actions={actions} onPressMenuItem={handlePressMenuItem} buttonStyle={style}> <ToolTipMenu
onMenuWillHide={onMenuWillHide}
onMenuWillShow={onMenuWillShow}
isButton
isMenuPrimaryAction
actions={actions}
onPressMenuItem={handlePressMenuItem}
buttonStyle={style}
>
{children} {children}
</ToolTipMenu> </ToolTipMenu>
); );

View file

@ -34,7 +34,13 @@ export const SecondButton = forwardRef<TouchableOpacity, SecondButtonProps>((pro
); );
return props.onPress ? ( return props.onPress ? (
<TouchableOpacity accessibilityRole="button" style={[styles.button, { backgroundColor }]} {...props} ref={ref}> <TouchableOpacity
disabled={props.disabled}
accessibilityRole="button"
style={[styles.button, { backgroundColor }]}
{...props}
ref={ref}
>
{buttonView} {buttonView}
</TouchableOpacity> </TouchableOpacity>
) : ( ) : (

View file

@ -42,12 +42,16 @@ const BaseToolTipMenu = (props, ref) => {
const renderPreview = props.renderPreview ?? undefined; const renderPreview = props.renderPreview ?? undefined;
const disabled = props.disabled ?? false; const disabled = props.disabled ?? false;
const onPress = props.onPress ?? undefined; const onPress = props.onPress ?? undefined;
const onMenuWillShow = props.onMenuWillShow ?? undefined;
const onMenuWillHide = props.onMenuWillHide ?? undefined;
const buttonStyle = props.buttonStyle; const buttonStyle = props.buttonStyle;
return isButton ? ( return isButton ? (
<TouchableOpacity onPress={onPress} disabled={disabled} accessibilityRole="button" style={buttonStyle}> <TouchableOpacity onPress={onPress} disabled={disabled} accessibilityRole="button" style={buttonStyle}>
<ContextMenuButton <ContextMenuButton
ref={ref} ref={ref}
onMenuWillShow={onMenuWillShow}
onMenuWillHide={onMenuWillHide}
useActionSheetFallback={false} useActionSheetFallback={false}
onPressMenuItem={({ nativeEvent }) => { onPressMenuItem={({ nativeEvent }) => {
props.onPressMenuItem(nativeEvent.actionKey); props.onPressMenuItem(nativeEvent.actionKey);

View file

@ -1,6 +1,6 @@
import { forwardRef } from 'react'; import { forwardRef } from 'react';
const BaseToolTipMenu = props => { const BaseToolTipMenu = (props, _ref) => {
return props.children; return props.children;
}; };

View file

@ -227,10 +227,10 @@ export const WalletCarouselItem = ({ item, _, onPress, handleLongPress, isSelect
<Text <Text
numberOfLines={1} numberOfLines={1}
key={balance} // force component recreation on balance change. To fix right-to-left languages, like Farsi key={balance} // force component recreation on balance change. To fix right-to-left languages, like Farsi
adjustsFontSizeToFit ellipsizeMode='middle'
style={[iStyles.balance, { color: colors.inverseForegroundColor }]} style={[iStyles.balance, { color: colors.inverseForegroundColor }]}
> >
{balance} {`${balance} `}
</Text> </Text>
)} )}
<Text style={iStyles.br} /> <Text style={iStyles.br} />

View file

@ -0,0 +1,80 @@
import React, { useContext, useEffect } from 'react';
import DefaultPreference from 'react-native-default-preference';
// @ts-ignore: fix later
import RNWidgetCenter from 'react-native-widget-center';
import { BlueStorageContext } from '../blue_modules/storage-context';
import { TWallet } from '../class/wallets/types';
enum WidgetCommunicationKeys {
AllWalletsSatoshiBalance = 'WidgetCommunicationAllWalletsSatoshiBalance',
AllWalletsLatestTransactionTime = 'WidgetCommunicationAllWalletsLatestTransactionTime',
DisplayBalanceAllowed = 'WidgetCommunicationDisplayBalanceAllowed',
LatestTransactionIsUnconfirmed = 'WidgetCommunicationLatestTransactionIsUnconfirmed',
}
export const reloadAllTimelines = (): void => {
RNWidgetCenter.reloadAllTimelines();
};
export const isBalanceDisplayAllowed = async (): Promise<boolean> => {
try {
const displayBalance = await DefaultPreference.get(WidgetCommunicationKeys.DisplayBalanceAllowed);
return displayBalance === '1';
} catch {
return false;
}
};
export const setBalanceDisplayAllowed = async (value: boolean): Promise<void> => {
if (value) {
await DefaultPreference.set(WidgetCommunicationKeys.DisplayBalanceAllowed, '1');
} else {
await DefaultPreference.clear(WidgetCommunicationKeys.DisplayBalanceAllowed);
}
reloadAllTimelines();
};
const WidgetCommunication: React.FC = () => {
const { wallets, walletsInitialized } = useContext(BlueStorageContext);
useEffect(() => {
const allWalletsBalanceAndTransactionTime = async (): Promise<{
allWalletsBalance: number;
latestTransactionTime: number | string;
}> => {
if (!walletsInitialized || !(await isBalanceDisplayAllowed())) {
return { allWalletsBalance: 0, latestTransactionTime: 0 };
} else {
let balance = 0;
let latestTransactionTime: number | string = 0;
wallets.forEach((wallet: TWallet) => {
if (wallet.hideBalance) return;
balance += wallet.getBalance();
const walletLatestTime = wallet.getLatestTransactionTimeEpoch();
if (typeof latestTransactionTime === 'number' && walletLatestTime > latestTransactionTime) {
latestTransactionTime =
wallet.getTransactions()[0]?.confirmations === 0 ? WidgetCommunicationKeys.LatestTransactionIsUnconfirmed : walletLatestTime;
}
});
return { allWalletsBalance: balance, latestTransactionTime };
}
};
const setValues = async (): Promise<void> => {
await DefaultPreference.setName('group.io.bluewallet.bluewallet');
const { allWalletsBalance, latestTransactionTime } = await allWalletsBalanceAndTransactionTime();
await DefaultPreference.set(WidgetCommunicationKeys.AllWalletsSatoshiBalance, String(allWalletsBalance));
await DefaultPreference.set(WidgetCommunicationKeys.AllWalletsLatestTransactionTime, String(latestTransactionTime));
reloadAllTimelines();
};
if (walletsInitialized) {
setValues();
}
}, [wallets, walletsInitialized]);
return null;
};
export default WidgetCommunication;

View file

@ -0,0 +1,15 @@
import React from 'react';
export const isBalanceDisplayAllowed = async (): Promise<boolean> => {
return true;
};
export const setBalanceDisplayAllowed = async (value: boolean): Promise<void> => {};
export const reloadAllTimelines = (): void => {};
const WidgetCommunication: React.FC = () => {
return null; // This component does not render anything.
};
export default WidgetCommunication;

View file

@ -8,9 +8,10 @@ export const presentWalletExportReminder = (): Promise<void> => {
loc.pleasebackup.ask, loc.pleasebackup.ask,
[ [
{ text: loc.pleasebackup.ask_yes, onPress: () => resolve(), style: 'default' }, { text: loc.pleasebackup.ask_yes, onPress: () => resolve(), style: 'default' },
{ text: loc.pleasebackup.ask_no, onPress: () => reject(new Error('User has denied saving the wallet backup.')), style: 'cancel' }, { text: loc.pleasebackup.ask_no, onPress: () => reject(new Error('User has denied saving the wallet backup.')) },
{ text: loc._.cancel, style: 'cancel' },
], ],
{ cancelable: false }, { cancelable: true },
); );
}); });
}; };

View file

@ -74,11 +74,13 @@ export const useExtendedNavigation = (): NavigationProp<ParamListBase> => {
wallet.setUserHasSavedExport(true); wallet.setUserHasSavedExport(true);
await saveToDisk(); // Assuming saveToDisk() returns a Promise. await saveToDisk(); // Assuming saveToDisk() returns a Promise.
proceedWithNavigation(); proceedWithNavigation();
} catch { } catch (error) {
originalNavigation.navigate('WalletExportRoot', { if (error) {
screen: 'WalletExport', originalNavigation.navigate('WalletExportRoot', {
params: { walletID }, screen: 'WalletExport',
}); params: { walletID },
});
}
} }
return; // Prevent proceeding with the original navigation if the reminder is shown return; // Prevent proceeding with the original navigation if the reminder is shown

View file

@ -30,22 +30,15 @@ NSUserDefaults *group = [[NSUserDefaults alloc] initWithSuiteName:@"group.io.blu
config.appType = @"macOS"; config.appType = @"macOS";
// Start Bugsnag with the configuration // Start Bugsnag with the configuration
[Bugsnag startWithConfiguration:config]; [Bugsnag startWithConfiguration:config];
[self copyDeviceUID];
#else #else
[Bugsnag start]; [Bugsnag start];
[self copyDeviceUID];
#endif #endif
} }
[self copyDeviceUID];
[[NSUserDefaults standardUserDefaults] addObserver:self
forKeyPath:@"deviceUID"
options:NSKeyValueObservingOptionNew
context:NULL];
[[NSUserDefaults standardUserDefaults] addObserver:self
forKeyPath:@"deviceUIDCopy"
options:NSKeyValueObservingOptionNew
context:NULL];
[self addSplashScreenView]; [self addSplashScreenView];
self.moduleName = @"BlueWallet"; self.moduleName = @"BlueWallet";
@ -95,6 +88,14 @@ NSUserDefaults *group = [[NSUserDefaults alloc] initWithSuiteName:@"group.io.blu
} }
- (void)copyDeviceUID { - (void)copyDeviceUID {
[[NSUserDefaults standardUserDefaults] addObserver:self
forKeyPath:@"deviceUID"
options:NSKeyValueObservingOptionNew
context:NULL];
[[NSUserDefaults standardUserDefaults] addObserver:self
forKeyPath:@"deviceUIDCopy"
options:NSKeyValueObservingOptionNew
context:NULL];
NSString *deviceUID = [[NSUserDefaults standardUserDefaults] stringForKey:@"deviceUID"]; NSString *deviceUID = [[NSUserDefaults standardUserDefaults] stringForKey:@"deviceUID"];
if (deviceUID && deviceUID.length > 0) { if (deviceUID && deviceUID.length > 0) {
[NSUserDefaults.standardUserDefaults setValue:deviceUID forKey:@"deviceUIDCopy"]; [NSUserDefaults.standardUserDefaults setValue:deviceUID forKey:@"deviceUIDCopy"];

View file

@ -20,9 +20,9 @@ PODS:
- hermes-engine/Pre-built (= 0.72.12) - hermes-engine/Pre-built (= 0.72.12)
- hermes-engine/Pre-built (0.72.12) - hermes-engine/Pre-built (0.72.12)
- libevent (2.1.12) - libevent (2.1.12)
- lottie-ios (4.4.2) - lottie-ios (4.4.1)
- lottie-react-native (6.7.0): - lottie-react-native (6.7.2):
- lottie-ios (~> 4.4.1) - lottie-ios (= 4.4.1)
- React-Core - React-Core
- PasscodeAuth (1.0.0): - PasscodeAuth (1.0.0):
- React - React
@ -792,8 +792,8 @@ SPEC CHECKSUMS:
glog: 04b94705f318337d7ead9e6d17c019bd9b1f6b1b glog: 04b94705f318337d7ead9e6d17c019bd9b1f6b1b
hermes-engine: e89344b9e9e54351c3c5cac075e0275148fb37ba hermes-engine: e89344b9e9e54351c3c5cac075e0275148fb37ba
libevent: 4049cae6c81cdb3654a443be001fb9bdceff7913 libevent: 4049cae6c81cdb3654a443be001fb9bdceff7913
lottie-ios: 4445b0bdb583c7a5325529f62246d311ee85fcd0 lottie-ios: e047b1d2e6239b787cc5e9755b988869cf190494
lottie-react-native: e3205322282d72e23efb3bff3287d0bd16fb1b01 lottie-react-native: 17547b2f3c7034e2ae8672833fdb63262164d18a
PasscodeAuth: 3e88093ff46c31a952d8b36c488240de980517be PasscodeAuth: 3e88093ff46c31a952d8b36c488240de980517be
RCT-Folly: 424b8c9a7a0b9ab2886ffe9c3b041ef628fd4fb1 RCT-Folly: 424b8c9a7a0b9ab2886ffe9c3b041ef628fd4fb1
RCTRequired: b6cea797b684c6d8d82ba0107cef58cbb679afdb RCTRequired: b6cea797b684c6d8d82ba0107cef58cbb679afdb

View file

@ -104,7 +104,7 @@ struct WalletInformationWidget: Widget {
struct WalletInformationWidget_Previews: PreviewProvider { struct WalletInformationWidget_Previews: PreviewProvider {
static var previews: some View { static var previews: some View {
WalletInformationWidgetEntryView(entry: WalletInformationWidgetEntry(date: Date(), marketData: MarketData(nextBlock: "26", sats: "9 134", price: "$10,000", rate: Double(0)), allWalletsBalance: WalletData(balance: 10000, latestTransactionTime: LatestTransaction(isUnconfirmed: false, epochValue: 1568804029000)))) WalletInformationWidgetEntryView(entry: WalletInformationWidgetEntry(date: Date(), marketData: MarketData(nextBlock: "26", sats: "9 134", price: "$10,000", rate: Double(0)), allWalletsBalance: WalletData(balance: 0, latestTransactionTime: LatestTransaction(isUnconfirmed: nil, epochValue: nil))))
.previewContext(WidgetPreviewContext(family: .systemSmall)) .previewContext(WidgetPreviewContext(family: .systemSmall))
} }
} }

14
package-lock.json generated
View file

@ -49,7 +49,7 @@
"events": "3.3.0", "events": "3.3.0",
"frisbee": "3.1.0", "frisbee": "3.1.0",
"junderw-crc32c": "1.2.0", "junderw-crc32c": "1.2.0",
"lottie-react-native": "6.7.0", "lottie-react-native": "6.7.2",
"metro-react-native-babel-preset": "0.77.0", "metro-react-native-babel-preset": "0.77.0",
"path-browserify": "1.0.1", "path-browserify": "1.0.1",
"payjoin-client": "1.0.1", "payjoin-client": "1.0.1",
@ -16909,9 +16909,9 @@
} }
}, },
"node_modules/lottie-react-native": { "node_modules/lottie-react-native": {
"version": "6.7.0", "version": "6.7.2",
"resolved": "https://registry.npmjs.org/lottie-react-native/-/lottie-react-native-6.7.0.tgz", "resolved": "https://registry.npmjs.org/lottie-react-native/-/lottie-react-native-6.7.2.tgz",
"integrity": "sha512-doiF/36LaKkzo0XkgUIK8egxALNY6jGjCI4szpRuwop15LTW3DFtIA2L3pusNdaH7oM797aSH5UylIJw2k+Hgw==", "integrity": "sha512-MZVx6N1EeO/EaSx8T44mJ0aHc5Mqee+xIfWwszni0oz8U2wlHdaWGjES44dHxaxgAp/0dRaFt3PkpZ6egTzcBg==",
"peerDependencies": { "peerDependencies": {
"@dotlottie/react-player": "^1.6.1", "@dotlottie/react-player": "^1.6.1",
"@lottiefiles/react-lottie-player": "^3.5.3", "@lottiefiles/react-lottie-player": "^3.5.3",
@ -35090,9 +35090,9 @@
} }
}, },
"lottie-react-native": { "lottie-react-native": {
"version": "6.7.0", "version": "6.7.2",
"resolved": "https://registry.npmjs.org/lottie-react-native/-/lottie-react-native-6.7.0.tgz", "resolved": "https://registry.npmjs.org/lottie-react-native/-/lottie-react-native-6.7.2.tgz",
"integrity": "sha512-doiF/36LaKkzo0XkgUIK8egxALNY6jGjCI4szpRuwop15LTW3DFtIA2L3pusNdaH7oM797aSH5UylIJw2k+Hgw==" "integrity": "sha512-MZVx6N1EeO/EaSx8T44mJ0aHc5Mqee+xIfWwszni0oz8U2wlHdaWGjES44dHxaxgAp/0dRaFt3PkpZ6egTzcBg=="
}, },
"lru-cache": { "lru-cache": {
"version": "5.1.1", "version": "5.1.1",

View file

@ -135,7 +135,7 @@
"events": "3.3.0", "events": "3.3.0",
"frisbee": "3.1.0", "frisbee": "3.1.0",
"junderw-crc32c": "1.2.0", "junderw-crc32c": "1.2.0",
"lottie-react-native": "6.7.0", "lottie-react-native": "6.7.2",
"metro-react-native-babel-preset": "0.77.0", "metro-react-native-babel-preset": "0.77.0",
"path-browserify": "1.0.1", "path-browserify": "1.0.1",
"payjoin-client": "1.0.1", "payjoin-client": "1.0.1",

View file

@ -8,7 +8,7 @@ import loc from '../../loc';
import DeviceQuickActions from '../../class/quick-actions'; import DeviceQuickActions from '../../class/quick-actions';
import BlueClipboard from '../../blue_modules/clipboard'; import BlueClipboard from '../../blue_modules/clipboard';
import { BlueStorageContext } from '../../blue_modules/storage-context'; import { BlueStorageContext } from '../../blue_modules/storage-context';
import WidgetCommunication from '../../blue_modules/WidgetCommunication'; import { isBalanceDisplayAllowed, setBalanceDisplayAllowed } from '../../components/WidgetCommunication';
import { useTheme } from '../../components/themes'; import { useTheme } from '../../components/themes';
import ListItem from '../../components/ListItem'; import ListItem from '../../components/ListItem';
import A from '../../blue_modules/analytics'; import A from '../../blue_modules/analytics';
@ -38,7 +38,7 @@ const SettingsPrivacy = () => {
setIsReadClipboardAllowed(await BlueClipboard().isReadClipboardAllowed()); setIsReadClipboardAllowed(await BlueClipboard().isReadClipboardAllowed());
setStorageIsEncrypted(await isStorageEncrypted()); setStorageIsEncrypted(await isStorageEncrypted());
setIsQuickActionsEnabled(await DeviceQuickActions.getEnabled()); setIsQuickActionsEnabled(await DeviceQuickActions.getEnabled());
setIsDisplayWidgetBalanceAllowed(await WidgetCommunication.isBalanceDisplayAllowed()); setIsDisplayWidgetBalanceAllowed(await isBalanceDisplayAllowed());
} catch (e) { } catch (e) {
console.log(e); console.log(e);
} }
@ -84,7 +84,7 @@ const SettingsPrivacy = () => {
const onWidgetsTotalBalanceValueChange = async value => { const onWidgetsTotalBalanceValueChange = async value => {
setIsLoading(sections.WIDGETS); setIsLoading(sections.WIDGETS);
try { try {
await WidgetCommunication.setBalanceDisplayAllowed(value); await setBalanceDisplayAllowed(value);
setIsDisplayWidgetBalanceAllowed(value); setIsDisplayWidgetBalanceAllowed(value);
} catch (e) { } catch (e) {
console.log(e); console.log(e);

View file

@ -29,7 +29,7 @@ import {
BlueDismissKeyboardInputAccessory, BlueDismissKeyboardInputAccessory,
} from '../../BlueComponents'; } from '../../BlueComponents';
import { BlueCurrentTheme } from '../../components/themes'; import { BlueCurrentTheme } from '../../components/themes';
import WidgetCommunication from '../../blue_modules/WidgetCommunication'; import { reloadAllTimelines } from '../../components/WidgetCommunication';
import { BlueStorageContext } from '../../blue_modules/storage-context'; import { BlueStorageContext } from '../../blue_modules/storage-context';
import presentAlert from '../../components/Alert'; import presentAlert from '../../components/Alert';
import { requestCameraAuthorization } from '../../helpers/scan-qr'; import { requestCameraAuthorization } from '../../helpers/scan-qr';
@ -170,7 +170,7 @@ export default class ElectrumSettings extends Component {
await DefaultPreference.clear(BlueElectrum.ELECTRUM_HOST); await DefaultPreference.clear(BlueElectrum.ELECTRUM_HOST);
await DefaultPreference.clear(BlueElectrum.ELECTRUM_SSL_PORT); await DefaultPreference.clear(BlueElectrum.ELECTRUM_SSL_PORT);
await DefaultPreference.clear(BlueElectrum.ELECTRUM_TCP_PORT); await DefaultPreference.clear(BlueElectrum.ELECTRUM_TCP_PORT);
WidgetCommunication.reloadAllTimelines(); reloadAllTimelines();
} catch (e) { } catch (e) {
// Must be running on Android // Must be running on Android
console.log(e); console.log(e);
@ -199,7 +199,7 @@ export default class ElectrumSettings extends Component {
await DefaultPreference.set(BlueElectrum.ELECTRUM_HOST, host); await DefaultPreference.set(BlueElectrum.ELECTRUM_HOST, host);
await DefaultPreference.set(BlueElectrum.ELECTRUM_TCP_PORT, port); await DefaultPreference.set(BlueElectrum.ELECTRUM_TCP_PORT, port);
await DefaultPreference.set(BlueElectrum.ELECTRUM_SSL_PORT, sslPort); await DefaultPreference.set(BlueElectrum.ELECTRUM_SSL_PORT, sslPort);
WidgetCommunication.reloadAllTimelines(); reloadAllTimelines();
} catch (e) { } catch (e) {
// Must be running on Android // Must be running on Android
console.log(e); console.log(e);

View file

@ -97,7 +97,6 @@ const styles = StyleSheet.create({
justifyContent: 'space-between', justifyContent: 'space-between',
}, },
delete: { delete: {
color: '#d0021b',
fontSize: 15, fontSize: 15,
fontWeight: '500', fontWeight: '500',
textAlign: 'center', textAlign: 'center',
@ -147,6 +146,10 @@ const WalletDetails = () => {
} }
}, [wallet]); }, [wallet]);
const [lightningWalletInfo, setLightningWalletInfo] = useState({}); const [lightningWalletInfo, setLightningWalletInfo] = useState({});
const [isToolTipMenuVisible, setIsToolTipMenuVisible] = useState(false);
const onMenuWillShow = () => setIsToolTipMenuVisible(true);
const onMenuWillHide = () => setIsToolTipMenuVisible(false);
useEffect(() => { useEffect(() => {
if (isAdvancedModeEnabledRender && wallet.allowMasterFingerprint()) { if (isAdvancedModeEnabledRender && wallet.allowMasterFingerprint()) {
@ -177,6 +180,9 @@ const WalletDetails = () => {
saveText: { saveText: {
color: colors.buttonTextColor, color: colors.buttonTextColor,
}, },
delete: {
color: isToolTipMenuVisible ? colors.buttonDisabledTextColor : '#d0021b',
},
}); });
useEffect(() => { useEffect(() => {
if (wallet.type === LightningLdkWallet.type) { if (wallet.type === LightningLdkWallet.type) {
@ -216,7 +222,7 @@ const WalletDetails = () => {
<TouchableOpacity <TouchableOpacity
accessibilityRole="button" accessibilityRole="button"
testID="Save" testID="Save"
disabled={isLoading} disabled={isLoading || isToolTipMenuVisible}
style={[styles.save, stylesHook.save]} style={[styles.save, stylesHook.save]}
onPress={save} onPress={save}
> >
@ -225,7 +231,7 @@ const WalletDetails = () => {
), ),
}); });
// eslint-disable-next-line react-hooks/exhaustive-deps // eslint-disable-next-line react-hooks/exhaustive-deps
}, [isLoading, colors, walletName, useWithHardwareWallet, hideTransactionsInWalletsList, isBIP47Enabled]); }, [isLoading, colors, walletName, useWithHardwareWallet, hideTransactionsInWalletsList, isBIP47Enabled, isToolTipMenuVisible]);
useEffect(() => { useEffect(() => {
if (wallets.some(w => w.getID() === walletID)) { if (wallets.some(w => w.getID() === walletID)) {
@ -594,7 +600,11 @@ const WalletDetails = () => {
</Text> </Text>
<View style={styles.hardware}> <View style={styles.hardware}>
<BlueText onPress={() => setBackdoorBip47Pressed(prevState => prevState + 1)}>{loc.wallets.details_display}</BlueText> <BlueText onPress={() => setBackdoorBip47Pressed(prevState => prevState + 1)}>{loc.wallets.details_display}</BlueText>
<Switch value={hideTransactionsInWalletsList} onValueChange={setHideTransactionsInWalletsList} /> <Switch
disabled={isToolTipMenuVisible}
value={hideTransactionsInWalletsList}
onValueChange={setHideTransactionsInWalletsList}
/>
</View> </View>
</> </>
<> <>
@ -647,17 +657,27 @@ const WalletDetails = () => {
</View> </View>
</BlueCard> </BlueCard>
{(wallet instanceof AbstractHDElectrumWallet || (wallet.type === WatchOnlyWallet.type && wallet.isHd())) && ( {(wallet instanceof AbstractHDElectrumWallet || (wallet.type === WatchOnlyWallet.type && wallet.isHd())) && (
<ListItem onPress={navigateToAddresses} title={loc.wallets.details_show_addresses} chevron /> <ListItem disabled={isToolTipMenuVisible} onPress={navigateToAddresses} title={loc.wallets.details_show_addresses} chevron />
)} )}
{wallet.allowBIP47() && isBIP47Enabled && <ListItem onPress={navigateToPaymentCodes} title="Show payment codes" chevron />} {wallet.allowBIP47() && isBIP47Enabled && <ListItem onPress={navigateToPaymentCodes} title="Show payment codes" chevron />}
<BlueCard style={styles.address}> <BlueCard style={styles.address}>
<View> <View>
<BlueSpacing20 /> <BlueSpacing20 />
<Button onPress={navigateToWalletExport} testID="WalletExport" title={loc.wallets.details_export_backup} /> <Button
disabled={isToolTipMenuVisible}
onPress={navigateToWalletExport}
testID="WalletExport"
title={loc.wallets.details_export_backup}
/>
{walletTransactionsLength > 0 && ( {walletTransactionsLength > 0 && (
<> <>
<BlueSpacing20 /> <BlueSpacing20 />
<SaveFileButton fileName={fileName} fileContent={exportHistoryContent()}> <SaveFileButton
onMenuWillHide={onMenuWillHide}
onMenuWillShow={onMenuWillShow}
fileName={fileName}
fileContent={exportHistoryContent()}
>
<SecondButton title={loc.wallets.details_export_history} /> <SecondButton title={loc.wallets.details_export_history} />
</SaveFileButton> </SaveFileButton>
</> </>
@ -666,6 +686,7 @@ const WalletDetails = () => {
<> <>
<BlueSpacing20 /> <BlueSpacing20 />
<SecondButton <SecondButton
disabled={isToolTipMenuVisible}
onPress={navigateToMultisigCoordinationSetup} onPress={navigateToMultisigCoordinationSetup}
testID="MultisigCoordinationSetup" testID="MultisigCoordinationSetup"
title={loc.multisig.export_coordination_setup.replace(/^\w/, c => c.toUpperCase())} title={loc.multisig.export_coordination_setup.replace(/^\w/, c => c.toUpperCase())}
@ -677,6 +698,7 @@ const WalletDetails = () => {
<> <>
<BlueSpacing20 /> <BlueSpacing20 />
<SecondButton <SecondButton
disabled={isToolTipMenuVisible}
onPress={navigateToViewEditCosigners} onPress={navigateToViewEditCosigners}
testID="ViewEditCosigners" testID="ViewEditCosigners"
title={loc.multisig.view_edit_cosigners} title={loc.multisig.view_edit_cosigners}
@ -687,25 +709,48 @@ const WalletDetails = () => {
{wallet.allowXpub() && ( {wallet.allowXpub() && (
<> <>
<BlueSpacing20 /> <BlueSpacing20 />
<SecondButton onPress={navigateToXPub} testID="XPub" title={loc.wallets.details_show_xpub} /> <SecondButton
disabled={isToolTipMenuVisible}
onPress={navigateToXPub}
testID="XPub"
title={loc.wallets.details_show_xpub}
/>
</> </>
)} )}
{wallet.allowSignVerifyMessage() && ( {wallet.allowSignVerifyMessage() && (
<> <>
<BlueSpacing20 /> <BlueSpacing20 />
<SecondButton onPress={navigateToSignVerify} testID="SignVerify" title={loc.addresses.sign_title} /> <SecondButton
disabled={isToolTipMenuVisible}
onPress={navigateToSignVerify}
testID="SignVerify"
title={loc.addresses.sign_title}
/>
</> </>
)} )}
{wallet.type === LightningLdkWallet.type && ( {wallet.type === LightningLdkWallet.type && (
<> <>
<BlueSpacing20 /> <BlueSpacing20 />
<SecondButton onPress={navigateToLdkViewLogs} testID="LdkLogs" title={loc.lnd.view_logs} /> <SecondButton
disabled={isToolTipMenuVisible}
onPress={navigateToLdkViewLogs}
testID="LdkLogs"
title={loc.lnd.view_logs}
/>
</> </>
)} )}
<BlueSpacing20 /> <BlueSpacing20 />
<BlueSpacing20 /> <BlueSpacing20 />
<TouchableOpacity accessibilityRole="button" onPress={handleDeleteButtonTapped} testID="DeleteButton"> <TouchableOpacity
<Text textBreakStrategy="simple" style={styles.delete}>{`${loc.wallets.details_delete}${' '}`}</Text> disabled={isToolTipMenuVisible}
accessibilityRole="button"
onPress={handleDeleteButtonTapped}
testID="DeleteButton"
>
<Text
textBreakStrategy="simple"
style={[styles.delete, stylesHook.delete]}
>{`${loc.wallets.details_delete}${' '}`}</Text>
</TouchableOpacity> </TouchableOpacity>
<BlueSpacing20 /> <BlueSpacing20 />
<BlueSpacing20 /> <BlueSpacing20 />

View file

@ -1,4 +1,4 @@
import React, { useEffect, useRef, useContext, useState } from 'react'; import React, { useEffect, useRef, useContext, useState, useLayoutEffect } from 'react';
import { StyleSheet, useColorScheme, Platform } from 'react-native'; import { StyleSheet, useColorScheme, Platform } from 'react-native';
import DraggableFlatList, { ScaleDecorator } from 'react-native-draggable-flatlist'; import DraggableFlatList, { ScaleDecorator } from 'react-native-draggable-flatlist';
import navigationStyle from '../../components/navigationStyle'; import navigationStyle from '../../components/navigationStyle';
@ -54,7 +54,7 @@ const ReorderWallets = () => {
setWalletData(filteredWallets); setWalletData(filteredWallets);
}, [wallets, searchQuery]); }, [wallets, searchQuery]);
useEffect(() => { useLayoutEffect(() => {
// Set navigation options dynamically // Set navigation options dynamically
setOptions({ setOptions({
headerSearchBarOptions: { headerSearchBarOptions: {

View file

@ -187,7 +187,7 @@ jest.mock('react-native-share', () => {
}; };
}); });
jest.mock('../blue_modules/WidgetCommunication', () => { jest.mock('../components/WidgetCommunication', () => {
return { return {
reloadAllTimelines: jest.fn(), reloadAllTimelines: jest.fn(),
}; };