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

View file

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

View file

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

View file

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

4
package-lock.json generated
View file

@ -19803,8 +19803,8 @@
}
},
"react-native-handoff": {
"version": "git+https://github.com/marcosrdz/react-native-handoff.git#f5becc63f3e36bf2da1ed1fc60fc690323e73602",
"from": "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/BlueWallet/react-native-handoff.git#31d005f93d31099d0e564590a3bbd052b8a02b39"
},
"react-native-haptic-feedback": {
"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-fs": "2.18.0",
"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-idle-timer": "https://github.com/BlueWallet/react-native-idle-timer#8587876d68ab5920e79619726aeca9e672beaf2b",
"react-native-image-picker": "4.1.1",

View file

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

View file

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

View file

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

View file

@ -25,7 +25,6 @@ import { BlueAlertWalletExportReminder } from '../../BlueComponents';
import WalletGradient from '../../class/wallet-gradient';
import navigationStyle from '../../components/navigationStyle';
import { LightningCustodianWallet, LightningLdkWallet, MultisigHDWallet, WatchOnlyWallet } from '../../class';
import HandoffComponent from '../../components/handoff';
import ActionSheet from '../ActionSheet';
import loc from '../../loc';
import { FContainer, FButton } from '../../components/FloatButtons';
@ -560,13 +559,6 @@ const WalletTransactions = () => {
return (
<View style={styles.flex}>
<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
wallet={wallet}
onWalletUnitChange={passedWallet =>

View file

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