REF: Handoff in-app rather than browser

This commit is contained in:
Marcos Rodriguez Vélez 2021-09-24 23:36:08 -04:00
parent f1d52e8867
commit 359981ff9b
14 changed files with 108 additions and 49 deletions

31
App.js
View file

@ -34,6 +34,7 @@ import Biometric from './class/biometrics';
import WidgetCommunication from './blue_modules/WidgetCommunication'; import WidgetCommunication from './blue_modules/WidgetCommunication';
import changeNavigationBarColor from 'react-native-navigation-bar-color'; import changeNavigationBarColor from 'react-native-navigation-bar-color';
import ActionSheet from './screen/ActionSheet'; import ActionSheet from './screen/ActionSheet';
import HandoffComponent from './components/handoff';
const A = require('./blue_modules/analytics'); const A = require('./blue_modules/analytics');
const currency = require('./blue_modules/currency'); const currency = require('./blue_modules/currency');
@ -59,6 +60,7 @@ const App = () => {
const colorScheme = useColorScheme(); const colorScheme = useColorScheme();
const onNotificationReceived = async notification => { const onNotificationReceived = async notification => {
console.warn(notification);
const payload = Object.assign({}, notification, notification.data); const payload = Object.assign({}, notification, notification.data);
if (notification.data && notification.data.data) Object.assign(payload, notification.data.data); if (notification.data && notification.data.data) Object.assign(payload, notification.data.data);
payload.foreground = true; payload.foreground = true;
@ -77,6 +79,30 @@ const App = () => {
); );
}; };
const onUserActivityOpen = data => {
console.warn(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;
}
};
useEffect(() => { useEffect(() => {
if (walletsInitialized) { if (walletsInitialized) {
addListeners(); addListeners();
@ -90,6 +116,7 @@ const App = () => {
AppState.removeEventListener('change', handleAppStateChange); AppState.removeEventListener('change', handleAppStateChange);
eventEmitter.removeAllListeners('onNotificationReceived'); eventEmitter.removeAllListeners('onNotificationReceived');
eventEmitter.removeAllListeners('openSettings'); eventEmitter.removeAllListeners('openSettings');
eventEmitter.removeAllListeners('onUserActivityOpen');
}; };
// eslint-disable-next-line react-hooks/exhaustive-deps // eslint-disable-next-line react-hooks/exhaustive-deps
}, []); }, []);
@ -117,9 +144,11 @@ const App = () => {
*/ */
eventEmitter.addListener('onNotificationReceived', onNotificationReceived); eventEmitter.addListener('onNotificationReceived', onNotificationReceived);
eventEmitter.addListener('openSettings', openSettings); eventEmitter.addListener('openSettings', openSettings);
eventEmitter.addListener('onUserActivityOpen', onUserActivityOpen);
}; };
const popInitialAction = async data => { const popInitialAction = async data => {
console.warn(data);
if (data) { if (data) {
const wallet = wallets.find(wallet => wallet.getID() === data.userInfo.url.split('wallet/')[1]); const wallet = wallets.find(wallet => wallet.getID() === data.userInfo.url.split('wallet/')[1]);
NavigationService.dispatch( NavigationService.dispatch(
@ -134,6 +163,7 @@ const App = () => {
); );
} else { } else {
const url = await Linking.getInitialURL(); const url = await Linking.getInitialURL();
console.warn(url);
if (url) { if (url) {
if (DeeplinkSchemaMatch.hasSchema(url)) { if (DeeplinkSchemaMatch.hasSchema(url)) {
handleOpenURL({ url }); handleOpenURL({ url });
@ -295,6 +325,7 @@ const App = () => {
}; };
const handleOpenURL = event => { const handleOpenURL = event => {
console.warn(event);
DeeplinkSchemaMatch.navigationRouteFor(event, value => NavigationService.navigate(...value), { wallets, addWallet, saveToDisk }); DeeplinkSchemaMatch.navigationRouteFor(event, value => NavigationService.navigate(...value), { wallets, addWallet, saveToDisk });
}; };

View file

@ -1,15 +0,0 @@
import React, { useContext } from 'react';
import Handoff from 'react-native-handoff';
import { BlueStorageContext } from '../blue_modules/storage-context';
import PropTypes from 'prop-types';
const HandoffComponent = props => {
const { isHandOffUseEnabled } = useContext(BlueStorageContext);
return isHandOffUseEnabled && props && props.url ? <Handoff {...props} /> : null;
};
export default HandoffComponent;
HandoffComponent.propTypes = {
url: PropTypes.string,
};

View file

@ -1,5 +1,21 @@
const HandoffComponent = () => { import React, { useContext } from 'react';
return null; import Handoff from 'react-native-handoff';
import { BlueStorageContext } from '../blue_modules/storage-context';
import PropTypes from 'prop-types';
const HandoffComponent = props => {
const { isHandOffUseEnabled } = useContext(BlueStorageContext);
return isHandOffUseEnabled ? <Handoff {...props} /> : null;
};
export default HandoffComponent;
HandoffComponent.propTypes = {
url: PropTypes.string,
}; };
export default HandoffComponent; HandoffComponent.activityTypes = {
ReceiveOnchain: 'io.bluewallet.bluewallet.receiveonchain',
Xpub: 'io.bluewallet.bluewallet.xpub',
ViewInBlockExplorer: 'io.bluewallet.bluewallet.blockexplorer',
};

View file

@ -83,6 +83,21 @@ static void InitializeFlipper(UIApplication *application) {
} }
- (BOOL)application:(UIApplication *)application continueUserActivity:(nonnull NSUserActivity *)userActivity
restorationHandler:(nonnull void (^)(NSArray<id<UIUserActivityRestoring>> * _Nullable))restorationHandler
{
if (userActivity.activityType == NSUserActivityTypeBrowsingWeb) {
return [RCTLinkingManager application:application
continueUserActivity:userActivity
restorationHandler:restorationHandler];
}
else {
[EventEmitter.sharedInstance sendUserActivity:@{@"activityType": userActivity.activityType, @"userInfo": userActivity.userInfo}];
return YES;
}
}
- (BOOL)application:(UIApplication *)app openURL:(NSURL *)url options:(NSDictionary<UIApplicationOpenURLOptionsKey,id> *)options { - (BOOL)application:(UIApplication *)app openURL:(NSURL *)url options:(NSDictionary<UIApplicationOpenURLOptionsKey,id> *)options {
return [RCTLinkingManager application:app openURL:url options:options]; return [RCTLinkingManager application:app openURL:url options:options];
} }

View file

@ -91,6 +91,11 @@
<string>6.0</string> <string>6.0</string>
<key>CFBundleName</key> <key>CFBundleName</key>
<string>$(PRODUCT_NAME)</string> <string>$(PRODUCT_NAME)</string>
<key>NSUserActivityTypes</key>
<array>
<string>io.bluewallet.bluewallet.receiveonchain</string>
<string>io.bluewallet.bluewallet.xpub</string>
</array>
<key>CFBundlePackageType</key> <key>CFBundlePackageType</key>
<string>APPL</string> <string>APPL</string>
<key>CFBundleShortVersionString</key> <key>CFBundleShortVersionString</key>

View file

@ -14,5 +14,6 @@
+ (EventEmitter *)sharedInstance; + (EventEmitter *)sharedInstance;
- (void)sendNotification:(NSDictionary *)userInfo; - (void)sendNotification:(NSDictionary *)userInfo;
- (void)openSettings; - (void)openSettings;
- (void)sendUserActivity:(NSDictionary *)userInfo;
@end @end

View file

@ -29,7 +29,7 @@ RCT_EXPORT_MODULE();
} }
- (NSArray<NSString *> *)supportedEvents { - (NSArray<NSString *> *)supportedEvents {
return @[@"onNotificationReceived",@"openSettings"]; return @[@"onNotificationReceived",@"openSettings",@"onUserActivityOpen"];
} }
- (void)sendNotification:(NSDictionary *)userInfo - (void)sendNotification:(NSDictionary *)userInfo
@ -37,6 +37,11 @@ RCT_EXPORT_MODULE();
[sharedInstance sendEventWithName:@"onNotificationReceived" body:userInfo]; [sharedInstance sendEventWithName:@"onNotificationReceived" body:userInfo];
} }
- (void)sendUserActivity:(NSDictionary *)userInfo
{
[sharedInstance sendEventWithName:@"onUserActivityOpen" body:userInfo];
}
- (void)openSettings - (void)openSettings
{ {

4
package-lock.json generated
View file

@ -19803,8 +19803,8 @@
} }
}, },
"react-native-handoff": { "react-native-handoff": {
"version": "git+https://github.com/marcosrdz/react-native-handoff.git#f5becc63f3e36bf2da1ed1fc60fc690323e73602", "version": "git+https://github.com/BlueWallet/react-native-handoff.git#31d005f93d31099d0e564590a3bbd052b8a02b39",
"from": "git+https://github.com/marcosrdz/react-native-handoff.git#f5becc63f3e36bf2da1ed1fc60fc690323e73602" "from": "git+https://github.com/BlueWallet/react-native-handoff.git#31d005f93d31099d0e564590a3bbd052b8a02b39"
}, },
"react-native-haptic-feedback": { "react-native-haptic-feedback": {
"version": "git+https://github.com/junina-de/react-native-haptic-feedback.git#8e4d598ad9be886325316b0e2140df8df624a91f", "version": "git+https://github.com/junina-de/react-native-haptic-feedback.git#8e4d598ad9be886325316b0e2140df8df624a91f",

View file

@ -152,7 +152,7 @@
"react-native-fingerprint-scanner": "https://github.com/BlueWallet/react-native-fingerprint-scanner#ce644673681716335d786727bab998f7e632ab5e", "react-native-fingerprint-scanner": "https://github.com/BlueWallet/react-native-fingerprint-scanner#ce644673681716335d786727bab998f7e632ab5e",
"react-native-fs": "2.18.0", "react-native-fs": "2.18.0",
"react-native-gesture-handler": "1.10.3", "react-native-gesture-handler": "1.10.3",
"react-native-handoff": "https://github.com/marcosrdz/react-native-handoff#f5becc63f3e36bf2da1ed1fc60fc690323e73602", "react-native-handoff": "https://github.com/BlueWallet/react-native-handoff#31d005f93d31099d0e564590a3bbd052b8a02b39",
"react-native-haptic-feedback": "https://github.com/junina-de/react-native-haptic-feedback.git#8e4d598ad9be886325316b0e2140df8df624a91f", "react-native-haptic-feedback": "https://github.com/junina-de/react-native-haptic-feedback.git#8e4d598ad9be886325316b0e2140df8df624a91f",
"react-native-idle-timer": "https://github.com/BlueWallet/react-native-idle-timer#8587876d68ab5920e79619726aeca9e672beaf2b", "react-native-idle-timer": "https://github.com/BlueWallet/react-native-idle-timer#8587876d68ab5920e79619726aeca9e672beaf2b",
"react-native-image-picker": "4.1.1", "react-native-image-picker": "4.1.1",

View file

@ -407,6 +407,8 @@ const ReceiveDetails = () => {
} else { } else {
obtainWalletAddress(); obtainWalletAddress();
} }
} else if (!wallet && address) {
setAddressBIP21Encoded(address);
} }
}); });
return () => { return () => {
@ -507,9 +509,9 @@ const ReceiveDetails = () => {
<StatusBar barStyle="light-content" /> <StatusBar barStyle="light-content" />
{address !== undefined && showAddress && ( {address !== undefined && showAddress && (
<HandoffComponent <HandoffComponent
title={`Bitcoin Transaction ${address}`} title={loc.send.details_address}
type="io.bluewallet.bluewallet" type={HandoffComponent.activityTypes.ReceiveOnchain}
url={`https://blockstream.info/address/${address}`} userInfo={{ address: address }}
/> />
)} )}
{showConfirmedBalance ? renderConfirmedBalance() : null} {showConfirmedBalance ? renderConfirmedBalance() : null}

View file

@ -129,9 +129,9 @@ const TransactionsDetails = () => {
return ( return (
<SafeBlueArea> <SafeBlueArea>
<HandoffComponent <HandoffComponent
title={`Bitcoin Transaction ${tx.hash}`} title={loc.transactions.details_title}
type="io.bluewallet.bluewallet" type={HandoffComponent.activityTypes.ViewInBlockExplorer}
url={`https://blockstream.info/tx/${tx.hash}`} url={`https://mempool.space/tx/${tx.hash}`}
/> />
<StatusBar barStyle="default" /> <StatusBar barStyle="default" />
<ScrollView style={styles.scroll}> <ScrollView style={styles.scroll}>

View file

@ -369,9 +369,9 @@ const TransactionsStatus = () => {
return ( return (
<SafeBlueArea> <SafeBlueArea>
<HandoffComponent <HandoffComponent
title={`Bitcoin Transaction ${tx.hash}`} title={loc.transactions.details_title}
type="io.bluewallet.bluewallet" type={HandoffComponent.activityTypes.ViewInBlockExplorer}
url={`https://blockstream.info/tx/${tx.hash}`} url={`https://mempool.space/tx/${tx.hash}`}
/> />
<StatusBar barStyle="default" /> <StatusBar barStyle="default" />

View file

@ -25,7 +25,6 @@ import { BlueAlertWalletExportReminder } from '../../BlueComponents';
import WalletGradient from '../../class/wallet-gradient'; import WalletGradient from '../../class/wallet-gradient';
import navigationStyle from '../../components/navigationStyle'; import navigationStyle from '../../components/navigationStyle';
import { LightningCustodianWallet, LightningLdkWallet, MultisigHDWallet, WatchOnlyWallet } from '../../class'; import { LightningCustodianWallet, LightningLdkWallet, MultisigHDWallet, WatchOnlyWallet } from '../../class';
import HandoffComponent from '../../components/handoff';
import ActionSheet from '../ActionSheet'; import ActionSheet from '../ActionSheet';
import loc from '../../loc'; import loc from '../../loc';
import { FContainer, FButton } from '../../components/FloatButtons'; import { FContainer, FButton } from '../../components/FloatButtons';
@ -560,13 +559,6 @@ const WalletTransactions = () => {
return ( return (
<View style={styles.flex}> <View style={styles.flex}>
<StatusBar barStyle="light-content" backgroundColor={WalletGradient.headerColorFor(wallet.type)} animated /> <StatusBar barStyle="light-content" backgroundColor={WalletGradient.headerColorFor(wallet.type)} animated />
{wallet.chain === Chain.ONCHAIN && wallet.type !== MultisigHDWallet.type && (
<HandoffComponent
title={`Bitcoin Wallet ${wallet.getLabel()}`}
type="io.bluewallet.bluewallet"
url={`https://blockpath.com/search/addr?q=${wallet.getXpub()}`}
/>
)}
<TransactionsNavigationHeader <TransactionsNavigationHeader
wallet={wallet} wallet={wallet}
onWalletUnitChange={passedWallet => onWalletUnitChange={passedWallet =>

View file

@ -8,6 +8,7 @@ import Biometric from '../../class/biometrics';
import loc from '../../loc'; import loc from '../../loc';
import { BlueStorageContext } from '../../blue_modules/storage-context'; import { BlueStorageContext } from '../../blue_modules/storage-context';
import QRCodeComponent from '../../components/QRCodeComponent'; import QRCodeComponent from '../../components/QRCodeComponent';
import HandoffComponent from '../../components/handoff';
const styles = StyleSheet.create({ const styles = StyleSheet.create({
root: { root: {
@ -23,12 +24,11 @@ const styles = StyleSheet.create({
const WalletXpub = () => { const WalletXpub = () => {
const { wallets } = useContext(BlueStorageContext); const { wallets } = useContext(BlueStorageContext);
const { walletID } = useRoute().params; const { walletID, xPub } = useRoute().params;
const wallet = wallets.find(w => w.getID() === walletID); const wallet = wallets.find(w => w.getID() === walletID);
const [isLoading, setIsLoading] = useState(true); const [isLoading, setIsLoading] = useState(true);
const [xPub, setXPub] = useState();
const [xPubText, setXPubText] = useState(); const [xPubText, setXPubText] = useState();
const { goBack } = useNavigation(); const { goBack, setParams } = useNavigation();
const { colors } = useTheme(); const { colors } = useTheme();
const [qrCodeSize, setQRCodeSize] = useState(90); const [qrCodeSize, setQRCodeSize] = useState(90);
const stylesHook = StyleSheet.create({ root: { backgroundColor: colors.elevated } }); const stylesHook = StyleSheet.create({ root: { backgroundColor: colors.elevated } });
@ -45,9 +45,12 @@ const WalletXpub = () => {
return goBack(); return goBack();
} }
} }
setXPub(wallet.getXpub()); setParams({ xPub: wallet.getXpub() });
setXPubText(wallet.getXpub()); setXPubText(wallet.getXpub());
setIsLoading(false); setIsLoading(false);
} else if (xPub) {
setXPubText(xPub);
setIsLoading(false);
} }
}); });
return () => { return () => {
@ -55,7 +58,7 @@ const WalletXpub = () => {
Privacy.disableBlur(); Privacy.disableBlur();
}; };
// eslint-disable-next-line react-hooks/exhaustive-deps // eslint-disable-next-line react-hooks/exhaustive-deps
}, [goBack, walletID]), }, [goBack, walletID, xPub]),
); );
const onLayout = e => { const onLayout = e => {
@ -71,15 +74,19 @@ const WalletXpub = () => {
<SafeBlueArea style={[styles.root, stylesHook.root]} onLayout={onLayout}> <SafeBlueArea style={[styles.root, stylesHook.root]} onLayout={onLayout}>
<StatusBar barStyle="light-content" /> <StatusBar barStyle="light-content" />
<View style={styles.container}> <View style={styles.container}>
<View> {wallet && (
<BlueText>{wallet.typeReadable}</BlueText> <>
</View> <View>
<BlueSpacing20 /> <BlueText>{wallet.typeReadable}</BlueText>
</View>
<BlueSpacing20 />
</>
)}
<QRCodeComponent value={xPub} size={qrCodeSize} /> <QRCodeComponent value={xPub} size={qrCodeSize} />
<BlueSpacing20 /> <BlueSpacing20 />
<BlueCopyTextToClipboard text={xPubText} /> <BlueCopyTextToClipboard text={xPubText} />
<HandoffComponent title={loc.wallets.xpub_title} type={HandoffComponent.activityTypes.Xpub} userInfo={{ xpub: xPubText }} />
</View> </View>
</SafeBlueArea> </SafeBlueArea>
); );