From 9bda934ee5f67a1b631298340473a4e7c10223f2 Mon Sep 17 00:00:00 2001 From: KoalaSat Date: Fri, 7 Oct 2022 14:10:21 +0000 Subject: [PATCH] Android Tor icon and copy to clipboard (#269) * Android Clipboard and Tor Status Icon * working clipboard and lintern * Fix * Add style for Tor connection component * Fix Freeze and Internet out * Fix Typo Co-authored-by: Reckless_Satoshi --- frontend/src/components/App.js | 3 + frontend/src/components/Dialogs/AuditPGP.tsx | 4 +- frontend/src/components/Dialogs/Profile.tsx | 6 +- frontend/src/components/EncryptedChat.js | 4 +- frontend/src/components/Icons/Tor.tsx | 10 +++ frontend/src/components/Icons/index.ts | 1 + frontend/src/components/MakerPage.js | 4 +- frontend/src/components/OrderPage.js | 5 +- frontend/src/components/TorConnection.tsx | 80 +++++++++++++++++++ frontend/src/components/TradeBox.js | 8 +- frontend/src/components/UserGenPage.js | 4 +- frontend/src/services/Native/index.d.ts | 9 ++- frontend/src/services/Native/index.ts | 11 ++- .../System/SystemNativeClient/index.ts | 20 +++++ .../services/System/SystemWebClient/index.ts | 29 +++++++ frontend/src/services/System/index.ts | 9 +++ .../src/services/api/ApiNativeClient/index.ts | 4 +- frontend/src/utils/clipboard.js | 25 ------ frontend/static/locales/en.json | 4 + mobile/App.tsx | 72 +++++++++++++---- mobile/package-lock.json | 31 +++++++ mobile/package.json | 4 +- mobile/services/Tor/index.ts | 16 ++-- 23 files changed, 296 insertions(+), 67 deletions(-) create mode 100644 frontend/src/components/Icons/Tor.tsx create mode 100644 frontend/src/components/TorConnection.tsx create mode 100644 frontend/src/services/System/SystemNativeClient/index.ts create mode 100644 frontend/src/services/System/SystemWebClient/index.ts create mode 100644 frontend/src/services/System/index.ts delete mode 100644 frontend/src/utils/clipboard.js diff --git a/frontend/src/components/App.js b/frontend/src/components/App.js index 7da43b3e..853cbbc9 100644 --- a/frontend/src/components/App.js +++ b/frontend/src/components/App.js @@ -5,6 +5,7 @@ import { CssBaseline, IconButton, Link } from '@mui/material'; import { ThemeProvider, createTheme } from '@mui/material/styles'; import UnsafeAlert from './UnsafeAlert'; import { LearnDialog } from './Dialogs'; +import TorConnection from './TorConnection'; import { I18nextProvider } from 'react-i18next'; import i18n from './i18n'; @@ -16,6 +17,7 @@ import SchoolIcon from '@mui/icons-material/School'; import ZoomOutIcon from '@mui/icons-material/ZoomOut'; import ZoomInIcon from '@mui/icons-material/ZoomIn'; import SettingsIcon from '@mui/icons-material/Settings'; +import { systemClient } from '../services/System'; export default class App extends Component { constructor(props) { @@ -102,6 +104,7 @@ export default class App extends Component { open={this.state.openLearn} onClose={() => this.setState({ openLearn: false })} /> + - await copyToClipboard(props.value)}> + systemClient.copyToClipboard(props.value)}> diff --git a/frontend/src/components/Dialogs/Profile.tsx b/frontend/src/components/Dialogs/Profile.tsx index 0d75dd07..95a57aa3 100644 --- a/frontend/src/components/Dialogs/Profile.tsx +++ b/frontend/src/components/Dialogs/Profile.tsx @@ -37,7 +37,7 @@ import EmojiEventsIcon from '@mui/icons-material/EmojiEvents'; import { UserNinjaIcon, BitcoinIcon } from '../Icons'; import { getCookie } from '../../utils/cookies'; -import { copyToClipboard } from '../../utils/clipboard'; +import { systemClient } from '../../services/System'; import { getWebln } from '../../utils/webln'; import RobotAvatar from '../Robots/RobotAvatar'; @@ -101,13 +101,13 @@ const ProfileDialog = ({ const robotToken = getCookie('robot_token'); if (robotToken) { - copyToClipboard(robotToken); + systemClient.copyToClipboard(robotToken); setAppState({ copiedToken: true }); } }; const copyReferralCodeHandler = () => { - copyToClipboard(`http://${host}/ref/${referralCode}`); + systemClient.copyToClipboard(`http://${host}/ref/${referralCode}`); }; const handleWeblnInvoiceClicked = async (e: any) => { diff --git a/frontend/src/components/EncryptedChat.js b/frontend/src/components/EncryptedChat.js index 675f49d0..c1e53d18 100644 --- a/frontend/src/components/EncryptedChat.js +++ b/frontend/src/components/EncryptedChat.js @@ -18,9 +18,9 @@ import ReconnectingWebSocket from 'reconnecting-websocket'; import { encryptMessage, decryptMessage } from '../utils/pgp'; import { getCookie } from '../utils/cookies'; import { saveAsJson } from '../utils/saveFile'; -import { copyToClipboard } from '../utils/clipboard'; import { AuditPGPDialog } from './Dialogs'; import RobotAvatar from './Robots/RobotAvatar'; +import { systemClient } from '../services/System'; // Icons import CheckIcon from '@mui/icons-material/Check'; @@ -336,7 +336,7 @@ class Chat extends Component { - copyToClipboard( + systemClient.copyToClipboard( this.state.showPGP[props.index] ? props.message.encryptedMessage : props.message.plainTextMessage, diff --git a/frontend/src/components/Icons/Tor.tsx b/frontend/src/components/Icons/Tor.tsx new file mode 100644 index 00000000..42d6a567 --- /dev/null +++ b/frontend/src/components/Icons/Tor.tsx @@ -0,0 +1,10 @@ +import React, { Component } from 'react'; +import { SvgIcon } from '@mui/material'; + +export default function TorIcon(props) { + return ( + + + + ); +} diff --git a/frontend/src/components/Icons/index.ts b/frontend/src/components/Icons/index.ts index 984804d7..df1cfa2c 100644 --- a/frontend/src/components/Icons/index.ts +++ b/frontend/src/components/Icons/index.ts @@ -14,6 +14,7 @@ export { default as SellSatsIcon } from './SellSats'; export { default as SendReceiveIcon } from './SendReceive'; export { default as ExportIcon } from './Export'; export { default as UserNinjaIcon } from './UserNinja'; +export { default as TorIcon } from './Tor'; // Some Flags missing on react-flags export { default as BasqueCountryFlag } from './BasqueCountryFlag'; diff --git a/frontend/src/components/MakerPage.js b/frontend/src/components/MakerPage.js index 4c68d317..ab193882 100644 --- a/frontend/src/components/MakerPage.js +++ b/frontend/src/components/MakerPage.js @@ -45,7 +45,7 @@ import ExpandMoreIcon from '@mui/icons-material/ExpandMore'; import { getCookie } from '../utils/cookies'; import { pn } from '../utils/prettyNumbers'; -import { copyToClipboard } from '../utils/clipboard'; +import { systemClient } from '../services/System'; class MakerPage extends Component { defaultCurrency = 1; @@ -924,7 +924,7 @@ class MakerPage extends Component { open={this.state.openStoreToken} onClose={() => this.setState({ openStoreToken: false })} onClickCopy={() => - copyToClipboard(getCookie('robot_token')) & + systemClient.copyToClipboard(getCookie('robot_token')) & this.props.setAppState({ copiedToken: true }) } copyIconColor={this.props.copiedToken ? 'inherit' : 'primary'} diff --git a/frontend/src/components/OrderPage.js b/frontend/src/components/OrderPage.js index 75fd0f35..f7a73147 100644 --- a/frontend/src/components/OrderPage.js +++ b/frontend/src/components/OrderPage.js @@ -52,7 +52,7 @@ import { SendReceiveIcon } from './Icons'; import { getCookie } from '../utils/cookies'; import { pn } from '../utils/prettyNumbers'; -import { copyToClipboard } from '../utils/clipboard'; +import { systemClient } from '../services/System'; import { getWebln } from '../utils/webln'; import { apiClient } from '../services/api'; import RobotAvatar from './Robots/RobotAvatar'; @@ -527,7 +527,8 @@ class OrderPage extends Component { open={this.state.openStoreToken} onClose={() => this.setState({ openStoreToken: false })} onClickCopy={() => - copyToClipboard(getCookie('robot_token')) & this.props.setAppState({ copiedToken: true }) + systemClient.copyToClipboard(getCookie('robot_token')) & + this.props.setAppState({ copiedToken: true }) } copyIconColor={this.props.copiedToken ? 'inherit' : 'primary'} onClickBack={() => this.setState({ openStoreToken: false })} diff --git a/frontend/src/components/TorConnection.tsx b/frontend/src/components/TorConnection.tsx new file mode 100644 index 00000000..6b0ed2d5 --- /dev/null +++ b/frontend/src/components/TorConnection.tsx @@ -0,0 +1,80 @@ +import React, { useEffect, useState } from 'react'; +import { Box, CircularProgress, Tooltip } from '@mui/material'; +import { TorIcon } from './Icons'; +import { useTranslation } from 'react-i18next'; + +const TorConnection = (): JSX.Element => { + const [torStatus, setTorStatus] = useState('NOTINIT'); + const { t } = useTranslation(); + + useEffect(() => { + window.addEventListener('torStatus', (event) => { + setTorStatus(event?.detail); + }); + }, []); + + if (window?.NativeRobosats && (torStatus === 'NOTINIT' || torStatus === 'STARTING')) { + return ( + + + + + + + + + ); + } else if (window?.NativeRobosats && (torStatus === '"Done"' || torStatus === 'DONE')) { + return ( + + + + + + ); + } else if (true) { + return ( + + + + + + ); + } +}; + +export default TorConnection; diff --git a/frontend/src/components/TradeBox.js b/frontend/src/components/TradeBox.js index 7c91be18..de378a43 100644 --- a/frontend/src/components/TradeBox.js +++ b/frontend/src/components/TradeBox.js @@ -32,7 +32,7 @@ import Countdown, { zeroPad } from 'react-countdown'; import Chat from './EncryptedChat'; import TradeSummary from './TradeSummary'; import MediaQuery from 'react-responsive'; -import { copyToClipboard } from '../utils/clipboard'; +import { systemClient } from '../services/System'; import { apiClient } from '../services/api'; // Icons @@ -286,7 +286,7 @@ class TradeBox extends Component { size='small' color='inherit' onClick={() => { - copyToClipboard(this.props.data.bond_invoice); + systemClient.copyToClipboard(this.props.data.bond_invoice); }} align='center' > @@ -420,7 +420,7 @@ class TradeBox extends Component { size='small' color='inherit' onClick={() => { - copyToClipboard(this.props.data.escrow_invoice); + systemClient.copyToClipboard(this.props.data.escrow_invoice); }} align='center' > @@ -1549,7 +1549,7 @@ class TradeBox extends Component { { - copyToClipboard(this.props.data.txid); + systemClient.copyToClipboard(this.props.data.txid); }} > diff --git a/frontend/src/components/UserGenPage.js b/frontend/src/components/UserGenPage.js index 7cf45728..9cc6fcdf 100644 --- a/frontend/src/components/UserGenPage.js +++ b/frontend/src/components/UserGenPage.js @@ -26,7 +26,7 @@ import { genBase62Token, tokenStrength } from '../utils/token'; import { genKey } from '../utils/pgp'; import { getCookie, writeCookie, deleteCookie } from '../utils/cookies'; import { saveAsJson } from '../utils/saveFile'; -import { copyToClipboard } from '../utils/clipboard'; +import { systemClient } from '../services/System'; import { apiClient } from '../services/api/index'; import RobotAvatar from './Robots/RobotAvatar'; @@ -321,7 +321,7 @@ class UserGenPage extends Component { !(getCookie('robot_token') === this.state.token)) } onClick={() => - copyToClipboard(getCookie('robot_token')) & + systemClient.copyToClipboard(getCookie('robot_token')) & this.props.setAppState({ copiedToken: true }) } > diff --git a/frontend/src/services/Native/index.d.ts b/frontend/src/services/Native/index.d.ts index 98a96e5b..48f57049 100644 --- a/frontend/src/services/Native/index.d.ts +++ b/frontend/src/services/Native/index.d.ts @@ -20,7 +20,14 @@ export interface NativeWebViewMessageHttp { body?: object; } -export declare type NativeWebViewMessage = NativeWebViewMessageHttp; +export interface NativeWebViewMessageSystem { + id?: number; + category: 'system'; + type: 'torStatus' | 'copyToClipboardString'; + detail: string; +} + +export declare type NativeWebViewMessage = NativeWebViewMessageHttp | NativeWebViewMessageSystem; export interface NativeRobosatsPromise { resolve: (value: object | PromiseLike) => void; diff --git a/frontend/src/services/Native/index.ts b/frontend/src/services/Native/index.ts index 8c94de9b..503725c2 100644 --- a/frontend/src/services/Native/index.ts +++ b/frontend/src/services/Native/index.ts @@ -1,10 +1,12 @@ -import { NativeRobosatsPromise, NativeWebViewMessage } from './index.d'; +import { NativeRobosatsPromise, NativeWebViewMessage, NativeWebViewMessageSystem } from './index.d'; class NativeRobosats { constructor() { this.messageCounter = 0; } + public torDaemonStatus = 'NOTINIT'; + private messageCounter: number; private pendingMessages: { [id: number]: NativeRobosatsPromise } = {}; @@ -29,6 +31,13 @@ class NativeRobosats { } }; + public onMessage: (message: NativeWebViewMessageSystem) => void = (message) => { + if (message.type === 'torStatus') { + this.torDaemonStatus = message.detail; + window.dispatchEvent(new CustomEvent('torStatus', { detail: this.torDaemonStatus })); + } + }; + public postMessage: (message: NativeWebViewMessage) => Promise<{ [key: string]: any }> = async ( message, ) => { diff --git a/frontend/src/services/System/SystemNativeClient/index.ts b/frontend/src/services/System/SystemNativeClient/index.ts new file mode 100644 index 00000000..21c986bf --- /dev/null +++ b/frontend/src/services/System/SystemNativeClient/index.ts @@ -0,0 +1,20 @@ +import { SystemClient } from '..'; +import NativeRobosats from '../../Native'; + +class SystemNativeClient implements SystemClient { + constructor() { + if (!window.NativeRobosats) { + window.NativeRobosats = new NativeRobosats(); + } + } + + public copyToClipboard: (value: string) => void = (value) => { + return window.NativeRobosats?.postMessage({ + category: 'system', + type: 'copyToClipboardString', + detail: value, + }); + }; +} + +export default SystemNativeClient; diff --git a/frontend/src/services/System/SystemWebClient/index.ts b/frontend/src/services/System/SystemWebClient/index.ts new file mode 100644 index 00000000..25077258 --- /dev/null +++ b/frontend/src/services/System/SystemWebClient/index.ts @@ -0,0 +1,29 @@ +import { SystemClient } from '..'; + +class SystemWebClient implements SystemClient { + public copyToClipboard: (value: string) => void = (value) => { + // navigator clipboard api needs a secure context (https) + // this function attempts to copy also on http contexts + // useful on the http i2p site and on torified browsers + if (navigator.clipboard && window.isSecureContext) { + // navigator clipboard api method' + navigator.clipboard.writeText(value); + } else { + // text area method + const textArea = document.createElement('textarea'); + textArea.value = value; + // make the textarea out of viewport + textArea.style.position = 'fixed'; + textArea.style.left = '-999999px'; + textArea.style.top = '-999999px'; + document.body.appendChild(textArea); + textArea.focus(); + textArea.select(); + // here the magic happens + document.execCommand('copy'); + textArea.remove(); + } + }; +} + +export default SystemWebClient; diff --git a/frontend/src/services/System/index.ts b/frontend/src/services/System/index.ts new file mode 100644 index 00000000..85918126 --- /dev/null +++ b/frontend/src/services/System/index.ts @@ -0,0 +1,9 @@ +import SystemNativeClient from './SystemNativeClient'; +import SystemWebClient from './SystemWebClient'; + +export interface SystemClient { + copyToClipboard: (value: string) => void; +} + +export const systemClient: SystemClient = + window.ReactNativeWebView != null ? new SystemNativeClient() : new SystemWebClient(); diff --git a/frontend/src/services/api/ApiNativeClient/index.ts b/frontend/src/services/api/ApiNativeClient/index.ts index 781e18a5..78761d6f 100644 --- a/frontend/src/services/api/ApiNativeClient/index.ts +++ b/frontend/src/services/api/ApiNativeClient/index.ts @@ -4,7 +4,9 @@ import NativeRobosats from '../../Native'; class ApiNativeClient implements ApiClient { constructor() { - window.NativeRobosats = new NativeRobosats(); + if (!window.NativeRobosats) { + window.NativeRobosats = new NativeRobosats(); + } } private assetsCache: { [path: string]: string } = {}; diff --git a/frontend/src/utils/clipboard.js b/frontend/src/utils/clipboard.js deleted file mode 100644 index b9832c0c..00000000 --- a/frontend/src/utils/clipboard.js +++ /dev/null @@ -1,25 +0,0 @@ -export function copyToClipboard(textToCopy) { - // navigator clipboard api needs a secure context (https) - // this function attempts to copy also on http contexts - // useful on the http i2p site and on torified browsers - if (navigator.clipboard && window.isSecureContext) { - // navigator clipboard api method' - return navigator.clipboard.writeText(textToCopy); - } else { - // text area method - const textArea = document.createElement('textarea'); - textArea.value = textToCopy; - // make the textarea out of viewport - textArea.style.position = 'fixed'; - textArea.style.left = '-999999px'; - textArea.style.top = '-999999px'; - document.body.appendChild(textArea); - textArea.focus(); - textArea.select(); - return new Promise((res, rej) => { - // here the magic happens - document.execCommand('copy') ? res() : rej(); - textArea.remove(); - }); - } -} diff --git a/frontend/static/locales/en.json b/frontend/static/locales/en.json index be153820..1b1c7e74 100644 --- a/frontend/static/locales/en.json +++ b/frontend/static/locales/en.json @@ -7,6 +7,10 @@ "You are self-hosting RoboSats": "You are self-hosting RoboSats", "RoboSats client is served from your own node granting you the strongest security and privacy.": "RoboSats client is served from your own node granting you the strongest security and privacy.", + "Connected to TOR network": "Connected to TOR network", + "TOR connection error": "TOR connection error", + "Connecting to TOR network": "Connecting to TOR network", + "USER GENERATION PAGE - UserGenPage.js": "Landing Page and User Generation", "Simple and Private LN P2P Exchange": "Simple and Private LN P2P Exchange", "This is your trading avatar": "This is your trading avatar", diff --git a/mobile/App.tsx b/mobile/App.tsx index eaed8e66..f5268654 100644 --- a/mobile/App.tsx +++ b/mobile/App.tsx @@ -1,43 +1,85 @@ -import React, { useRef } from 'react'; +import React, { useEffect, useRef } from 'react'; import { WebView, WebViewMessageEvent } from 'react-native-webview'; import { SafeAreaView, Text, Platform } from 'react-native'; import { torClient } from './services/Tor'; +import Clipboard from '@react-native-clipboard/clipboard'; +import NetInfo from '@react-native-community/netinfo'; const App = () => { const webViewRef = useRef(); const uri = (Platform.OS === 'android' ? 'file:///android_asset/' : '') + 'Web.bundle/index.html'; - const injectMessage = (id: string, data: object) => { + const injectMessageResolve = (id: string, data: object) => { const json = JSON.stringify(data); webViewRef.current?.injectJavaScript( `(function() {window.NativeRobosats.onMessageResolve(${id}, ${json});})();`, ); }; + const injectMessage = (message: object) => { + const json = JSON.stringify(message); + webViewRef.current?.injectJavaScript( + `(function() {window.NativeRobosats?.onMessage(${json});})();`, + ); + }; + const onMessage = async (event: WebViewMessageEvent) => { const data = JSON.parse(event.nativeEvent.data); if (data.category === 'http') { + sendTorStatus(); + if (data.type === 'get') { - torClient.get(data.path).then((response: object) => { - injectMessage(data.id, response); - }); + torClient + .get(data.path) + .then((response: object) => { + injectMessageResolve(data.id, response); + }) + .finally(sendTorStatus); } else if (data.type === 'post') { - torClient.post(data.path, data.body, data.headers).then((response: object) => { - injectMessage(data.id, response); - }); + torClient + .post(data.path, data.body, data.headers) + .then((response: object) => { + injectMessageResolve(data.id, response); + }) + .finally(sendTorStatus); } else if (data.type === 'delete') { - torClient.delete(data.path, data.headers).then((response: object) => { - injectMessage(data.id, response); - }); + torClient + .delete(data.path, data.headers) + .then((response: object) => { + injectMessageResolve(data.id, response); + }) + .finally(sendTorStatus); } else if (data.type === 'xhr') { - torClient.request(data.path).then((response: object) => { - injectMessage(data.id, response); - }); + torClient + .request(data.path) + .then((response: object) => { + injectMessageResolve(data.id, response); + }) + .finally(sendTorStatus); + } + } else if (data.category === 'system') { + if (data.type === 'copyToClipboardString') { + Clipboard.setString(data.detail); } } }; - torClient.startDaemon(); + const sendTorStatus = async () => { + NetInfo.fetch().then(async (state) => { + let daemonStatus = 'ERROR'; + if (state.isInternetReachable) { + try { + daemonStatus = await torClient.daemon.getDaemonStatus(); + } catch {} + } + + injectMessage({ + category: 'system', + type: 'torStatus', + detail: daemonStatus, + }); + }); + }; return ( diff --git a/mobile/package-lock.json b/mobile/package-lock.json index f91d37ac..93b63945 100644 --- a/mobile/package-lock.json +++ b/mobile/package-lock.json @@ -8,6 +8,8 @@ "name": "robosats", "version": "0.2.0", "dependencies": { + "@react-native-clipboard/clipboard": "^1.11.1", + "@react-native-community/netinfo": "^9.3.4", "react": "^18.0.0", "react-native": "^0.69.6", "react-native-tor": "^0.1.8", @@ -2808,6 +2810,15 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/@react-native-clipboard/clipboard": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@react-native-clipboard/clipboard/-/clipboard-1.11.1.tgz", + "integrity": "sha512-nvSIIHzybVWqYxcJE5hpT17ekxAAg383Ggzw5WrYHtkKX61N1AwaKSNmXs5xHV7pmKSOe/yWjtSwxIzfW51I5Q==", + "peerDependencies": { + "react": ">=16.0", + "react-native": ">=0.57.0" + } + }, "node_modules/@react-native-community/cli": { "version": "8.0.6", "resolved": "https://registry.npmjs.org/@react-native-community/cli/-/cli-8.0.6.tgz", @@ -4348,6 +4359,14 @@ "integrity": "sha512-o6aam+0Ug1xGK3ABYmBm0B1YuEKfM/5kaoZO0eHbZwSpw9UzDX4G5y4Nx/K20FHqUmJHkZmLvOUFYwN4N+HqKA==", "dev": true }, + "node_modules/@react-native-community/netinfo": { + "version": "9.3.4", + "resolved": "https://registry.npmjs.org/@react-native-community/netinfo/-/netinfo-9.3.4.tgz", + "integrity": "sha512-IXbJ+L8p4oE2ssDPfXCyxx9xVo5WuTMv6HA5YJw2McuRLLtVKR/vambycrB47AWTkHCTj3e0VOz28iUOvTSVPw==", + "peerDependencies": { + "react-native": ">=0.59" + } + }, "node_modules/@react-native/assets": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/@react-native/assets/-/assets-1.0.0.tgz", @@ -17949,6 +17968,12 @@ } } }, + "@react-native-clipboard/clipboard": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@react-native-clipboard/clipboard/-/clipboard-1.11.1.tgz", + "integrity": "sha512-nvSIIHzybVWqYxcJE5hpT17ekxAAg383Ggzw5WrYHtkKX61N1AwaKSNmXs5xHV7pmKSOe/yWjtSwxIzfW51I5Q==", + "requires": {} + }, "@react-native-community/cli": { "version": "8.0.6", "resolved": "https://registry.npmjs.org/@react-native-community/cli/-/cli-8.0.6.tgz", @@ -19068,6 +19093,12 @@ "integrity": "sha512-o6aam+0Ug1xGK3ABYmBm0B1YuEKfM/5kaoZO0eHbZwSpw9UzDX4G5y4Nx/K20FHqUmJHkZmLvOUFYwN4N+HqKA==", "dev": true }, + "@react-native-community/netinfo": { + "version": "9.3.4", + "resolved": "https://registry.npmjs.org/@react-native-community/netinfo/-/netinfo-9.3.4.tgz", + "integrity": "sha512-IXbJ+L8p4oE2ssDPfXCyxx9xVo5WuTMv6HA5YJw2McuRLLtVKR/vambycrB47AWTkHCTj3e0VOz28iUOvTSVPw==", + "requires": {} + }, "@react-native/assets": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/@react-native/assets/-/assets-1.0.0.tgz", diff --git a/mobile/package.json b/mobile/package.json index d138e7b2..617eefa4 100644 --- a/mobile/package.json +++ b/mobile/package.json @@ -12,6 +12,8 @@ "format": "prettier --write '**/**/*.{js,jsx,ts,tsx,css,md,json}' --config ./.prettierrc" }, "dependencies": { + "@react-native-clipboard/clipboard": "^1.11.1", + "@react-native-community/netinfo": "^9.3.4", "react": "^18.0.0", "react-native": "^0.69.6", "react-native-tor": "^0.1.8", @@ -39,9 +41,9 @@ "eslint-plugin-promise": "^6.0.1", "eslint-plugin-react": "^7.31.1", "eslint-plugin-react-hooks": "^4.6.0", - "prettier": "^2.7.1", "jest": "^26.6.3", "metro-react-native-babel-preset": "^0.70.3", + "prettier": "^2.7.1", "react-test-renderer": "18.0.0", "typescript": "^4.4.4" }, diff --git a/mobile/services/Tor/index.ts b/mobile/services/Tor/index.ts index 46b87aec..07dbf72b 100644 --- a/mobile/services/Tor/index.ts +++ b/mobile/services/Tor/index.ts @@ -12,12 +12,16 @@ class TorClient { }); } - public startDaemon = async () => { - await this.daemon.startIfNotStarted(); + private connectDaemon: () => void = async () => { + try { + this.daemon.startIfNotStarted(); + } catch { + console.log('TOR already started'); + } }; public get: (path: string) => Promise = async (path) => { - await this.startDaemon(); + await this.connectDaemon(); return await new Promise(async (resolve, reject) => { try { @@ -31,7 +35,7 @@ class TorClient { }; public delete: (path: string, headers: object) => Promise = async (path, headers) => { - await this.startDaemon(); + await this.connectDaemon(); return await new Promise(async (resolve, reject) => { try { @@ -45,7 +49,7 @@ class TorClient { }; public request: (path: string) => Promise = async (path) => { - await this.startDaemon(); + await this.connectDaemon(); return await new Promise(async (resolve, reject) => { try { @@ -67,7 +71,7 @@ class TorClient { body, headers, ) => { - await this.startDaemon(); + await this.connectDaemon(); return await new Promise(async (resolve, reject) => { try {