Merge pull request #6363 from BlueWallet/hadnoff

REF: Handoff component
This commit is contained in:
GLaDOS 2024-04-03 17:35:35 +00:00 committed by GitHub
commit 4bdf987e26
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
11 changed files with 171 additions and 50 deletions

36
App.js
View File

@ -29,15 +29,15 @@ import Notifications from './blue_modules/notifications';
import Biometric from './class/biometrics';
import WidgetCommunication from './blue_modules/WidgetCommunication';
import ActionSheet from './screen/ActionSheet';
import HandoffComponent from './components/handoff';
import triggerHapticFeedback, { HapticFeedbackTypes } from './blue_modules/hapticFeedback';
import MenuElements from './components/MenuElements';
import { updateExchangeRate } from './blue_modules/currency';
import { NavigationProvider } from './components/NavigationProvider';
import A from './blue_modules/analytics';
import HandOffComponentListener from './components/HandOffComponentListener';
const eventEmitter = Platform.OS === 'ios' ? new NativeEventEmitter(NativeModules.EventEmitter) : undefined;
const { EventEmitter, SplashScreen } = NativeModules;
const { SplashScreen } = NativeModules;
LogBox.ignoreLogs(['Require cycle:', 'Battery state `unknown` and monitoring disabled, this is normal for simulators and tvOS.']);
@ -77,47 +77,17 @@ const App = () => {
if (payload.foreground) await processPushNotifications();
};
const onUserActivityOpen = data => {
switch (data.activityType) {
case HandoffComponent.activityTypes.ReceiveOnchain:
NavigationService.navigate('ReceiveDetailsRoot', {
screen: 'ReceiveDetails',
params: {
address: data.userInfo.address,
},
});
break;
case HandoffComponent.activityTypes.Xpub:
NavigationService.navigate('WalletXpubRoot', {
screen: 'WalletXpub',
params: {
xpub: data.userInfo.xpub,
},
});
break;
default:
break;
}
};
const addListeners = () => {
const urlSubscription = Linking.addEventListener('url', handleOpenURL);
const appStateSubscription = AppState.addEventListener('change', handleAppStateChange);
// Note: `getMostRecentUserActivity` doesn't create a persistent listener, so no need to unsubscribe
EventEmitter?.getMostRecentUserActivity()
.then(onUserActivityOpen)
.catch(() => console.log('No userActivity object sent'));
const notificationSubscription = eventEmitter?.addListener('onNotificationReceived', onNotificationReceived);
const activitySubscription = eventEmitter?.addListener('onUserActivityOpen', onUserActivityOpen);
// Store subscriptions in a ref or state to remove them later
return {
urlSubscription,
appStateSubscription,
notificationSubscription,
activitySubscription,
};
};
@ -130,7 +100,6 @@ const App = () => {
subscriptions.urlSubscription?.remove();
subscriptions.appStateSubscription?.remove();
subscriptions.notificationSubscription?.remove();
subscriptions.activitySubscription?.remove();
};
}
// eslint-disable-next-line react-hooks/exhaustive-deps
@ -306,6 +275,7 @@ const App = () => {
<MenuElements />
<DeviceQuickActions />
<Biometric />
<HandOffComponentListener />
</NavigationProvider>
</NavigationContainer>
</View>

View File

@ -0,0 +1,41 @@
import React, { useContext } from 'react';
// @ts-ignore: react-native-handoff is not in the type definition
import Handoff from 'react-native-handoff';
import { BlueStorageContext } from '../blue_modules/storage-context';
interface HandOffComponentProps {
url?: string;
title?: string;
type: (typeof HandOffComponent.activityTypes)[keyof typeof HandOffComponent.activityTypes];
userInfo?: object;
}
interface HandOffComponentWithActivityTypes extends React.FC<HandOffComponentProps> {
activityTypes: {
ReceiveOnchain: string;
Xpub: string;
ViewInBlockExplorer: string;
};
}
const HandOffComponent: HandOffComponentWithActivityTypes = props => {
const { isHandOffUseEnabled } = useContext(BlueStorageContext);
if (process.env.NODE_ENV === 'development') {
console.log('HandOffComponent: props', props);
}
if (isHandOffUseEnabled) {
return <Handoff {...props} />;
}
return null;
};
const activityTypes = {
ReceiveOnchain: 'io.bluewallet.bluewallet.receiveonchain',
Xpub: 'io.bluewallet.bluewallet.xpub',
ViewInBlockExplorer: 'io.bluewallet.bluewallet.blockexplorer',
};
HandOffComponent.activityTypes = activityTypes;
export default HandOffComponent;

View File

@ -0,0 +1,30 @@
import React from 'react';
interface HandOffComponentProps {
url?: string;
title?: string;
type: (typeof HandOffComponent.activityTypes)[keyof typeof HandOffComponent.activityTypes];
userInfo?: object;
}
interface HandOffComponentWithActivityTypes extends React.FC<HandOffComponentProps> {
activityTypes: {
ReceiveOnchain: string;
Xpub: string;
ViewInBlockExplorer: string;
};
}
const HandOffComponent: HandOffComponentWithActivityTypes = props => {
return null;
};
const activityTypes = {
ReceiveOnchain: 'io.bluewallet.bluewallet.receiveonchain',
Xpub: 'io.bluewallet.bluewallet.xpub',
ViewInBlockExplorer: 'io.bluewallet.bluewallet.blockexplorer',
};
HandOffComponent.activityTypes = activityTypes;
export default HandOffComponent;

View File

@ -0,0 +1,73 @@
import React, { useEffect, useContext } from 'react';
import * as NavigationService from '../NavigationService';
import { BlueStorageContext } from '../blue_modules/storage-context';
import { NativeEventEmitter, NativeModules } from 'react-native';
import HandOffComponent from './HandOffComponent.ios';
interface UserActivityData {
activityType: keyof typeof HandOffComponent.activityTypes;
userInfo: {
address?: string;
xpub?: string;
};
}
const { EventEmitter } = NativeModules;
const eventEmitter = new NativeEventEmitter(EventEmitter);
const HandOffComponentListener: React.FC = () => {
const { walletsInitialized } = useContext(BlueStorageContext); // Assuming 'walletsInitialized' is stored in context
useEffect(() => {
if (!walletsInitialized) {
return;
}
const onUserActivityOpen = (data: UserActivityData) => {
switch (data.activityType) {
case HandOffComponent.activityTypes.ReceiveOnchain:
NavigationService.navigate('ReceiveDetailsRoot', {
// @ts-ignore: fix later
screen: 'ReceiveDetails',
params: {
address: data.userInfo.address,
},
});
break;
case HandOffComponent.activityTypes.Xpub:
NavigationService.navigate('WalletXpubRoot', {
// @ts-ignore: fix later
screen: 'WalletXpub',
params: {
xpub: data.userInfo.xpub,
},
});
break;
default:
console.log(`Unhandled activity type: ${data.activityType}`);
break;
}
};
const addListeners = () => {
const activitySubscription = eventEmitter.addListener('onUserActivityOpen', onUserActivityOpen);
// Attempt to fetch the most recent user activity
EventEmitter.getMostRecentUserActivity?.()
.then(onUserActivityOpen)
.catch(() => console.log('No userActivity object sent'));
return { activitySubscription };
};
const subscriptions = addListeners();
return () => {
subscriptions.activitySubscription?.remove();
};
}, [walletsInitialized]);
return null;
};
export default HandOffComponentListener;

View File

@ -0,0 +1,7 @@
import React from 'react';
const HandOffComponentListener: React.FC = () => {
return null;
};
export default HandOffComponentListener;

View File

@ -6,7 +6,7 @@ import { BlueStorageContext } from '../blue_modules/storage-context';
interface HandoffComponentProps {
url?: string;
title?: string;
type: (typeof HandoffComponent.activityTypes)[keyof typeof HandoffComponent.activityTypes];
type: (typeof HandOffComponent.activityTypes)[keyof typeof HandOffComponent.activityTypes];
userInfo?: object;
}
@ -18,7 +18,7 @@ interface HandoffComponentWithActivityTypes extends React.FC<HandoffComponentPro
};
}
const HandoffComponent: HandoffComponentWithActivityTypes = props => {
const HandOffComponent: HandoffComponentWithActivityTypes = props => {
const { isHandOffUseEnabled } = useContext(BlueStorageContext);
if (isHandOffUseEnabled) {
@ -33,6 +33,6 @@ const activityTypes = {
ViewInBlockExplorer: 'io.bluewallet.bluewallet.blockexplorer',
};
HandoffComponent.activityTypes = activityTypes;
HandOffComponent.activityTypes = activityTypes;
export default HandoffComponent;
export default HandOffComponent;

View File

@ -17,7 +17,7 @@ import { BlueLoading, BlueButtonLink, BlueText, BlueSpacing20, BlueCard, BlueSpa
import navigationStyle from '../../components/navigationStyle';
import BottomModal from '../../components/BottomModal';
import { Chain, BitcoinUnit } from '../../models/bitcoinUnits';
import HandoffComponent from '../../components/handoff';
import HandOffComponent from '../../components/HandOffComponent';
import AmountInput from '../../components/AmountInput';
import DeeplinkSchemaMatch from '../../class/deeplink-schema-match';
import loc, { formatBalance } from '../../loc';
@ -426,7 +426,7 @@ const ReceiveDetails = () => {
return (
<View style={[styles.root, stylesHook.root]}>
{address !== undefined && showAddress && (
<HandoffComponent title={loc.send.details_address} type={HandoffComponent.activityTypes.ReceiveOnchain} userInfo={{ address }} />
<HandOffComponent title={loc.send.details_address} type={HandOffComponent.activityTypes.ReceiveOnchain} userInfo={{ address }} />
)}
{showConfirmedBalance ? renderConfirmedBalance() : null}
{showPendingBalance ? renderPendingBalance() : null}

View File

@ -4,7 +4,7 @@ import { useNavigation, useRoute } from '@react-navigation/native';
import Clipboard from '@react-native-clipboard/clipboard';
import { BlueCard, BlueLoading, BlueSpacing20, BlueText } from '../../BlueComponents';
import navigationStyle from '../../components/navigationStyle';
import HandoffComponent from '../../components/handoff';
import HandOffComponent from '../../components/HandOffComponent';
import loc from '../../loc';
import { BlueStorageContext } from '../../blue_modules/storage-context';
import ToolTipMenu from '../../components/TooltipMenu';
@ -221,9 +221,9 @@ const TransactionsDetails = () => {
return (
<ScrollView style={styles.scroll} automaticallyAdjustContentInsets contentInsetAdjustmentBehavior="automatic">
<HandoffComponent
<HandOffComponent
title={loc.transactions.details_title}
type={HandoffComponent.activityTypes.ViewInBlockExplorer}
type={HandOffComponent.activityTypes.ViewInBlockExplorer}
url={`https://mempool.space/tx/${tx.hash}`}
/>
<BlueCard>

View File

@ -8,7 +8,7 @@ import TransactionIncomingIcon from '../../components/icons/TransactionIncomingI
import TransactionOutgoingIcon from '../../components/icons/TransactionOutgoingIcon';
import TransactionPendingIcon from '../../components/icons/TransactionPendingIcon';
import navigationStyle from '../../components/navigationStyle';
import HandoffComponent from '../../components/handoff';
import HandOffComponent from '../../components/HandOffComponent';
import { HDSegwitBech32Transaction } from '../../class';
import { BitcoinUnit } from '../../models/bitcoinUnits';
import loc, { formatBalanceWithoutSuffix } from '../../loc';
@ -360,9 +360,9 @@ const TransactionsStatus = () => {
}
return (
<SafeArea>
<HandoffComponent
<HandOffComponent
title={loc.transactions.details_title}
type={HandoffComponent.activityTypes.ViewInBlockExplorer}
type={HandOffComponent.activityTypes.ViewInBlockExplorer}
url={`https://mempool.space/tx/${tx.hash}`}
/>

View File

@ -7,7 +7,7 @@ import { LegacyWallet, LightningCustodianWallet, SegwitBech32Wallet, SegwitP2SHW
import loc from '../../loc';
import { BlueStorageContext } from '../../blue_modules/storage-context';
import QRCodeComponent from '../../components/QRCodeComponent';
import HandoffComponent from '../../components/handoff';
import HandOffComponent from '../../components/HandOffComponent';
import { useTheme } from '../../components/themes';
import SafeArea from '../../components/SafeArea';
import usePrivacy from '../../hooks/usePrivacy';
@ -116,9 +116,9 @@ const WalletExport = () => {
</BlueText>
)}
{wallet.type === WatchOnlyWallet.type && (
<HandoffComponent
<HandOffComponent
title={loc.wallets.xpub_title}
type={HandoffComponent.activityTypes.Xpub}
type={HandOffComponent.activityTypes.Xpub}
userInfo={{ xpub: wallet.getSecret() }}
/>
)}

View File

@ -7,7 +7,7 @@ import { BlueStorageContext } from '../../blue_modules/storage-context';
import Button from '../../components/Button';
import QRCodeComponent from '../../components/QRCodeComponent';
import SafeArea from '../../components/SafeArea';
import HandoffComponent from '../../components/handoff';
import HandOffComponent from '../../components/HandOffComponent';
import usePrivacy from '../../hooks/usePrivacy';
import loc from '../../loc';
import { styles, useDynamicStyles } from './xpub.styles';
@ -94,7 +94,7 @@ const WalletXpub: React.FC = () => {
<BlueSpacing20 />
{xPubText && <CopyTextToClipboard text={xPubText} />}
</View>
<HandoffComponent title={loc.wallets.xpub_title} type={HandoffComponent.activityTypes.Xpub} userInfo={{ xpub: xPubText }} />
<HandOffComponent title={loc.wallets.xpub_title} type={HandOffComponent.activityTypes.Xpub} userInfo={{ xpub: xPubText }} />
<View style={styles.share}>
<Button onPress={handleShareButtonPressed} title={loc.receive.details_share} />
</View>