From a651b66c6454dbddd1c9fdd67f63ae7771832aa9 Mon Sep 17 00:00:00 2001 From: Marcos Rodriguez Velez Date: Fri, 19 Apr 2024 18:36:12 -0400 Subject: [PATCH 1/5] DEL: Lapp Browser --- Navigation.tsx | 13 - class/deeplink-schema-match.ts | 61 +--- ios/Podfile.lock | 7 - loc/en.json | 1 - package-lock.json | 38 --- package.json | 1 - screen/lnd/browser.js | 527 --------------------------------- screen/wallets/transactions.js | 21 -- 8 files changed, 1 insertion(+), 668 deletions(-) delete mode 100644 screen/lnd/browser.js diff --git a/Navigation.tsx b/Navigation.tsx index 2cd8031c8..77077f5c2 100644 --- a/Navigation.tsx +++ b/Navigation.tsx @@ -71,7 +71,6 @@ import { isDesktop, isHandset, isTablet } from './blue_modules/environment'; import navigationStyle from './components/navigationStyle'; import { useTheme } from './components/themes'; import loc from './loc'; -import LappBrowser from './screen/lnd/browser'; import LdkInfo from './screen/lnd/ldkInfo'; import LdkOpenChannel from './screen/lnd/ldkOpenChannel'; import LNDCreateInvoice from './screen/lnd/lndCreateInvoice'; @@ -492,17 +491,6 @@ const WalletExportStackRoot = () => { ); }; -const LappBrowserStack = createNativeStackNavigator(); -const LappBrowserStackRoot = () => { - const theme = useTheme(); - - return ( - - - - ); -}; - const InitStack = createNativeStackNavigator(); const InitRoot = () => { const { walletsInitialized } = useContext(BlueStorageContext); @@ -638,7 +626,6 @@ const Navigation = () => { /> - { if (urlObject.protocol === 'bluewallet:' || urlObject.protocol === 'lapp:' || urlObject.protocol === 'blue:') { switch (urlObject.host) { - case 'openlappbrowser': { - console.log('opening LAPP', urlObject.query.url); - // searching for LN wallet: - let haveLnWallet = false; - for (const w of context.wallets) { - if (w.type === LightningCustodianWallet.type) { - haveLnWallet = true; - } - } - - if (!haveLnWallet) { - // need to create one - const w = new LightningCustodianWallet(); - w.setLabel(w.typeReadable); - - try { - const lndhub = await AsyncStorage.getItem(BlueApp.LNDHUB); - if (lndhub) { - w.setBaseURI(lndhub); - w.init(); - } - await w.createAccount(); - await w.authorize(); - } catch (Err) { - // giving up, not doing anything - return; - } - context.addWallet(w); - context.saveToDisk(); - } - - // now, opening lapp browser and navigating it to URL. - // looking for a LN wallet: - let lnWallet; - for (const w of context.wallets) { - if (w.type === LightningCustodianWallet.type) { - lnWallet = w; - break; - } - } - - if (!lnWallet) { - // something went wrong - return; - } - - completionHandler([ - 'LappBrowserRoot', - { - screen: 'LappBrowser', - params: { - walletID: lnWallet.getID(), - url: urlObject.query.url, - }, - }, - ]); - break; - } case 'setelectrumserver': completionHandler([ 'ElectrumSettings', diff --git a/ios/Podfile.lock b/ios/Podfile.lock index b1c98ec0d..42adc126f 100644 --- a/ios/Podfile.lock +++ b/ios/Podfile.lock @@ -352,9 +352,6 @@ PODS: - react-native-tcp-socket (6.0.6): - CocoaAsyncSocket - React-Core - - react-native-webview (13.8.6): - - RCT-Folly (= 2021.07.22.00) - - React-Core - react-native-widget-center (0.0.9): - React - React-NativeModulesApple (0.72.12): @@ -563,7 +560,6 @@ DEPENDENCIES: - react-native-safe-area-context (from `../node_modules/react-native-safe-area-context`) - react-native-secure-key-store (from `../node_modules/react-native-secure-key-store`) - react-native-tcp-socket (from `../node_modules/react-native-tcp-socket`) - - react-native-webview (from `../node_modules/react-native-webview`) - react-native-widget-center (from `../node_modules/react-native-widget-center`) - React-NativeModulesApple (from `../node_modules/react-native/ReactCommon/react/nativemodule/core/platform/ios`) - React-perflogger (from `../node_modules/react-native/ReactCommon/reactperflogger`) @@ -691,8 +687,6 @@ EXTERNAL SOURCES: :path: "../node_modules/react-native-secure-key-store" react-native-tcp-socket: :path: "../node_modules/react-native-tcp-socket" - react-native-webview: - :path: "../node_modules/react-native-webview" react-native-widget-center: :path: "../node_modules/react-native-widget-center" React-NativeModulesApple: @@ -822,7 +816,6 @@ SPEC CHECKSUMS: react-native-safe-area-context: b97eb6f9e3b7f437806c2ce5983f479f8eb5de4b react-native-secure-key-store: 910e6df6bc33cb790aba6ee24bc7818df1fe5898 react-native-tcp-socket: e724380c910c2e704816ec817ed28f1342246ff7 - react-native-webview: 2ce07db2190ee7b3af77e8fac75ff47abe47089f react-native-widget-center: 12dfba20a4fa995850b52cf0afecf734397f4b9c React-NativeModulesApple: 694679e4193a49c09f0a76ee27ec09b2c466d59c React-perflogger: 63606aeab27683112e1bd4ef25bd099ec1cb03f8 diff --git a/loc/en.json b/loc/en.json index 08a238b21..450bf8cde 100644 --- a/loc/en.json +++ b/loc/en.json @@ -449,7 +449,6 @@ "list_empty_txs2": "Start with your wallet.", "list_empty_txs2_lightning": "\nTo start using it, tap on Manage Funds and topup your balance.", "list_latest_transaction": "Latest Transaction", - "list_ln_browser": "LApp Browser", "list_long_choose": "Choose Photo", "list_long_clipboard": "Copy from Clipboard", "list_long_scan": "Scan QR Code", diff --git a/package-lock.json b/package-lock.json index 2e6f0f15b..6adae7b20 100644 --- a/package-lock.json +++ b/package-lock.json @@ -97,7 +97,6 @@ "react-native-tcp-socket": "6.0.6", "react-native-vector-icons": "10.0.3", "react-native-watch-connectivity": "1.1.0", - "react-native-webview": "13.8.6", "react-native-widget-center": "https://github.com/BlueWallet/react-native-widget-center#a128c38", "readable-stream": "3.6.2", "realm": "12.7.0", @@ -19827,27 +19826,6 @@ "react-native": ">=0.40" } }, - "node_modules/react-native-webview": { - "version": "13.8.6", - "resolved": "https://registry.npmjs.org/react-native-webview/-/react-native-webview-13.8.6.tgz", - "integrity": "sha512-jtZ9OgB2AN6rhDwto6dNL3PtOtl/SI4VN93pZEPbMLvRjqHfxiUrilGllL5fKAXq5Ry5FJyfUi82A4Ii8olZ7A==", - "dependencies": { - "escape-string-regexp": "2.0.0", - "invariant": "2.2.4" - }, - "peerDependencies": { - "react": "*", - "react-native": "*" - } - }, - "node_modules/react-native-webview/node_modules/escape-string-regexp": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-2.0.0.tgz", - "integrity": "sha512-UpzcLCXolUWcNu5HtVMHYdXJjArjsF9C0aNnquZYY4uW/Vu0miy5YoWvbV345HauVvcAUnpRuhMMcqTcGOY2+w==", - "engines": { - "node": ">=8" - } - }, "node_modules/react-native-widget-center": { "version": "0.0.9", "resolved": "git+ssh://git@github.com/BlueWallet/react-native-widget-center.git#a128c389526d55afdd67937494f2fec224dd0009", @@ -37368,22 +37346,6 @@ "lodash.sortby": "^4.7.0" } }, - "react-native-webview": { - "version": "13.8.6", - "resolved": "https://registry.npmjs.org/react-native-webview/-/react-native-webview-13.8.6.tgz", - "integrity": "sha512-jtZ9OgB2AN6rhDwto6dNL3PtOtl/SI4VN93pZEPbMLvRjqHfxiUrilGllL5fKAXq5Ry5FJyfUi82A4Ii8olZ7A==", - "requires": { - "escape-string-regexp": "2.0.0", - "invariant": "2.2.4" - }, - "dependencies": { - "escape-string-regexp": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-2.0.0.tgz", - "integrity": "sha512-UpzcLCXolUWcNu5HtVMHYdXJjArjsF9C0aNnquZYY4uW/Vu0miy5YoWvbV345HauVvcAUnpRuhMMcqTcGOY2+w==" - } - } - }, "react-native-widget-center": { "version": "git+ssh://git@github.com/BlueWallet/react-native-widget-center.git#a128c389526d55afdd67937494f2fec224dd0009", "from": "react-native-widget-center@https://github.com/BlueWallet/react-native-widget-center#a128c38" diff --git a/package.json b/package.json index c3a0ce0be..07d3d17cf 100644 --- a/package.json +++ b/package.json @@ -184,7 +184,6 @@ "react-native-tcp-socket": "6.0.6", "react-native-vector-icons": "10.0.3", "react-native-watch-connectivity": "1.1.0", - "react-native-webview": "13.8.6", "react-native-widget-center": "https://github.com/BlueWallet/react-native-widget-center#a128c38", "readable-stream": "3.6.2", "realm": "12.7.0", diff --git a/screen/lnd/browser.js b/screen/lnd/browser.js deleted file mode 100644 index dd7903d30..000000000 --- a/screen/lnd/browser.js +++ /dev/null @@ -1,527 +0,0 @@ -import React, { Component } from 'react'; -import PropTypes from 'prop-types'; -import Ionicons from 'react-native-vector-icons/Ionicons'; -import { ActivityIndicator, Alert, BackHandler, Keyboard, Platform, StyleSheet, TextInput, TouchableOpacity, View } from 'react-native'; -import { WebView } from 'react-native-webview'; - -import navigationStyle from '../../components/navigationStyle'; -import Notifications from '../../blue_modules/notifications'; -import loc from '../../loc'; -import { Button } from 'react-native-elements'; -import { BlueStorageContext } from '../../blue_modules/storage-context'; -import SafeArea from '../../components/SafeArea'; - -let processedInvoices = {}; -let lastTimeTriedToPay = 0; - -/// /////////////////////////////////////////////////////////////////////// -// this code has no use in RN, it gets copypasted in webview injected code -// -const bluewalletResponses = {}; -// eslint-disable-next-line @typescript-eslint/no-unused-vars, no-var -var webln = { - enable: function () { - window.ReactNativeWebView.postMessage(JSON.stringify({ enable: true })); - return new Promise(function (resolve, reject) { - resolve(true); - }); - }, - getInfo: function () { - window.ReactNativeWebView.postMessage('getInfo'); - return new Promise(function (resolve, reject) { - reject(new Error('not implemented')); - }); - }, - sendPayment: function (paymentRequest) { - window.ReactNativeWebView.postMessage(JSON.stringify({ sendPayment: paymentRequest })); - return new Promise(function (resolve, reject) { - /* nop. intentionally, forever hang promise. - lapp page usually asynchroniously checks payment itself, via ajax, - so atm there's no need to pass payment preimage from RN to webview and fullfill promise. - might change in future */ - }); - }, - makeInvoice: function (RequestInvoiceArgs) { - var id = Math.random(); // eslint-disable-line no-var - window.ReactNativeWebView.postMessage(JSON.stringify({ makeInvoice: RequestInvoiceArgs, id })); - return new Promise(function (resolve, reject) { - // eslint-disable-next-line no-var - var interval = setInterval(function () { - if (bluewalletResponses[id]) { - clearInterval(interval); - resolve(bluewalletResponses[id]); - } - }, 1000); - }); - }, - signMessage: function () { - window.ReactNativeWebView.postMessage('signMessage'); - return new Promise(function (resolve, reject) { - reject(new Error('not implemented')); - }); - }, - verifyMessage: function () { - window.ReactNativeWebView.postMessage('verifyMessage'); - return new Promise(function (resolve, reject) { - reject(new Error('not implemented')); - }); - }, -}; -// end injected code -/// ///////////////// -/// ///////////////// - -let alreadyInjected = false; -const injectedParadise = ` - -/* rules: - no 'let', only 'var' - no arrow functions - globals without 'var' - should work if compressed to single line -*/ - - - -/* this is a storage of responses from OUTER code (react native) - it gets written by message bus handler callback: - webview makes a call through bus to RN, each request with a unique ID. - RN processes the request from the bus, and posts response to the bus, with the same ID. - webview callback handler writes it in this hashmap. Then, some other code that patiently - waits for a response will see that the answer with such ID is present, and will fulfill a promise */ - -bluewalletResponses = {}; - - -/* this is injected WEBLN provider */ - - -webln = { - enable : function () { - window.ReactNativeWebView.postMessage(JSON.stringify({'enable': true})); - return new Promise(function(resolve, reject){ - resolve(true); - }) - }, - getInfo : function () { - window.ReactNativeWebView.postMessage('getInfo'); - return new Promise(function(resolve, reject){ - reject('not implemented'); - }) - }, - sendPayment: function(paymentRequest) { - window.ReactNativeWebView.postMessage(JSON.stringify({ sendPayment: paymentRequest })); - return new Promise(function(resolve, reject) { - /* nop. intentionally, forever hang promise. - lapp page usually asynchroniously checks payment itself, via ajax, - so atm there's no need to pass payment preimage from RN to webview and fullfill promise. - might change in future */ - }); - }, - makeInvoice: function (RequestInvoiceArgs) { - var id = Math.random(); - window.ReactNativeWebView.postMessage(JSON.stringify({makeInvoice: RequestInvoiceArgs, id: id})); - return new Promise(function(resolve, reject) { - var interval = setInterval(function () { - if (bluewalletResponses[id]) { - clearInterval(interval); - resolve(bluewalletResponses[id]); - } - }, 1000); - }); - }, - signMessage: function () { - window.ReactNativeWebView.postMessage('signMessage'); - return new Promise(function(resolve, reject){ - reject('not implemented'); - }) - }, - verifyMessage: function () { - window.ReactNativeWebView.postMessage('verifyMessage'); - return new Promise(function(resolve, reject){ - reject('not implemented'); - }) - }, -}; - - -/* end WEBLN */ - -/* listening to events that might come from RN: */ -document.addEventListener("message", function(event) { - window.ReactNativeWebView.postMessage("inside webview, received post message: " + event.detail); - var json; - try { - json = JSON.parse(event.detail); - } catch (_) {} - - if (json && json.bluewalletResponse) { - /* this is an answer to one of our inside-webview calls. - we store it in answers hashmap for someone who cares about it */ - bluewalletResponses[json.id] = json.bluewalletResponse - } - -}, false); - - - - -function tryToPay(invoice) { - window.ReactNativeWebView.postMessage(JSON.stringify({sendPayment:invoice})); -} - -/* for non-webln compatible pages we do it oldschool, - searching for all bolt11 manually */ - -setInterval(function() { -window.ReactNativeWebView.postMessage('interval'); - - var searchText = "lnbc"; - - var aTags = document.getElementsByTagName("span"); - var i; - for (i = 0; i < aTags.length; i++) { - if (aTags[i].textContent.indexOf(searchText) === 0) { - tryToPay(aTags[i].textContent); - } - } - - /* ------------------------- */ - - aTags = document.getElementsByTagName("input"); - for (i = 0; i < aTags.length; i++) { - if (aTags[i].value.indexOf(searchText) === 0) { - tryToPay(aTags[i].value); - } - } - - /* ------------------------- */ - - aTags = document.getElementsByTagName("a"); - searchText = "lightning:lnbc"; - - - for (i = 0; i < aTags.length; i++) { - var href = aTags[i].getAttribute('href') + ''; - if (href.indexOf(searchText) === 0) { - tryToPay(href.replace('lightning:', '')); - } - } - -}, 1000); - - `; - -const styles = StyleSheet.create({ - safeRoot: { - flexDirection: 'row', - alignItems: 'center', - justifyContent: 'space-between', - minHeight: 44, - }, - safeURL: { - flex: 1, - marginHorizontal: 8, - alignItems: 'center', - justifyContent: 'center', - }, - safeURLTextWrap: { - flexDirection: 'row', - borderColor: '#d2d2d2', - borderBottomColor: '#d2d2d2', - borderWidth: 1.0, - borderBottomWidth: 0.5, - backgroundColor: '#f5f5f5', - minHeight: 44, - height: 44, - alignItems: 'center', - marginVertical: 8, - borderRadius: 4, - }, - safeURLText: { - flex: 1, - marginLeft: 4, - minHeight: 33, - color: '#81868e', - }, - safeURLHome: { - alignContent: 'flex-end', - height: 44, - flexDirection: 'row', - marginHorizontal: 8, - }, - sync: { - color: 'red', - backgroundColor: 'transparent', - paddingLeft: 15, - }, - activity: { - flex: 1, - justifyContent: 'center', - paddingLeft: 20, - alignContent: 'center', - }, - goBack: { - backgroundColor: 'transparent', - paddingLeft: 10, - }, - colorRed: { - color: 'red', - }, - // eslint-disable-next-line react-native/no-unused-styles - colorGray: { - color: 'gray', - }, - transparent: { - backgroundColor: 'transparent', - }, - colorGreen: { - color: 'green', - }, -}); - -export default class Browser extends Component { - webView = React.createRef(); - constructor(props, context) { - super(props); - if (!props.route.params.walletID) throw new Error('Missing walletID param'); - let url; - if (props.route.params.url) url = props.route.params.url; - - this.state = { - url: url || 'https://www.duckduckgo.com/', - fromWallet: context.wallets.find(w => w.getID() === props.route.params.walletID), - canGoBack: false, - pageIsLoading: false, - stateURL: url || 'https://www.duckduckgo.com/', - }; - BackHandler.addEventListener('hardwareBackPress', this.handleBackButton.bind(this)); - } - - componentWillUnmount = () => { - BackHandler.removeEventListener('hardwareBackPress', this.handleBackButton.bind(this)); - }; - - handleBackButton() { - this.state.canGoBack ? this.webView.current?.goBack() : this.props.navigation.goBack(null); - return true; - } - - _onNavigationStateChange = webViewState => { - this.setState({ canGoBack: webViewState.canGoBack, stateURL: webViewState.url }); - }; - - renderWebView = () => { - return ( - { - // this is a handler which receives messages sent from within the browser - console.log('---- message from the bus:', e.nativeEvent.data); - let json = false; - try { - json = JSON.parse(e.nativeEvent.data); - } catch (_) {} - // message from browser has ln invoice - if (json && json.sendPayment) { - // checking that already asked about this invoice: - if (processedInvoices[json.sendPayment]) { - return; - } else { - // checking that we do not trigger alert too often: - if (+new Date() - lastTimeTriedToPay < 3000) { - return; - } - lastTimeTriedToPay = +new Date(); - // - processedInvoices[json.sendPayment] = 1; - } - - Alert.alert( - 'Page', - 'This page asks for permission to pay an invoice', - [ - { text: 'Cancel', onPress: () => console.log('Cancel Pressed'), style: 'cancel' }, - { - text: 'Pay', - onPress: () => { - console.log('OK Pressed'); - this.props.navigation.navigate('ScanLndInvoiceRoot', { - screen: 'ScanLndInvoice', - params: { - uri: json.sendPayment, - walletID: this.state.fromWallet.getID(), - }, - }); - }, - }, - ], - { cancelable: false }, - ); - } - - if (json && json.makeInvoice) { - const amount = Math.max( - json.makeInvoice.minimumAmount || 0, - json.makeInvoice.maximumAmount || 0, - json.makeInvoice.defaultAmount || 0, - json.makeInvoice.amount || 0, - ); - Alert.alert( - 'Page', - 'This page wants to pay you ' + amount + ' sats (' + json.makeInvoice.defaultMemo + ')', - [ - { text: 'No thanks', onPress: () => console.log('Cancel Pressed'), style: 'cancel' }, - { - text: 'Accept', - onPress: async () => { - /** @type {LightningCustodianWallet} */ - const fromWallet = this.state.fromWallet; - const payreq = await fromWallet.addInvoice(amount, json.makeInvoice.defaultMemo || ' '); - // this.webView.postMessage(JSON.stringify({ bluewalletResponse: { paymentRequest: payreq }, id: json.id })); - // Since webview.postMessage is removed from webview, we inject javascript that will manually triger document - // event; note how data is passed in 'detail', not 'data' - const jsonstr = JSON.stringify({ bluewalletResponse: { paymentRequest: payreq }, id: json.id }); - this.webView.current?.injectJavaScript( - "document.dispatchEvent( new CustomEvent('message', { detail: '" + jsonstr + "' }) );", - ); - - // lets decode payreq and subscribe groundcontrol so we can receive push notification when our invoice is paid - const decoded = await fromWallet.decodeInvoice(payreq); - await Notifications.tryToObtainPermissions(); - Notifications.majorTomToGroundControl([], [decoded.payment_hash], []); - }, - }, - ], - { cancelable: false }, - ); - } - - if (json && json.enable) { - console.log('webln enabled'); - this.setState({ weblnEnabled: true }); - } - }} - onLoadStart={e => { - alreadyInjected = false; - console.log('load start'); - this.setState({ pageIsLoading: true, weblnEnabled: false }); - }} - onLoadEnd={e => { - console.log('load end'); - this.setState({ url: e.nativeEvent.url, pageIsLoading: false }); - }} - onLoadProgress={e => { - console.log('progress:', e.nativeEvent.progress); - if (!alreadyInjected && e.nativeEvent.progress > 0.5) { - this.webView.current?.injectJavaScript(injectedParadise); - alreadyInjected = true; - console.log('injected'); - } - }} - /> - ); - }; - - render() { - return ( - - -