robosats/mobile/App.tsx
KoalaSat 673037c3df
Websockets on Tor android (#1615)
* Websockets on Tor android

* Show turtle mode on android

* Display lnp2pbot orders (#1617)

* P2PBOT working

* Display LNP2POrders

* Filter by robosats orders

* Robosats hosts filter by default and better browse text

---------

Co-authored-by: Reckless_Satoshi <90936742+Reckless-Satoshi@users.noreply.github.com>
2024-11-24 08:16:50 +00:00

264 lines
8.9 KiB
TypeScript

import React, { useEffect, useRef } from 'react';
import { WebView, WebViewMessageEvent } from 'react-native-webview';
import { SafeAreaView, Text, Platform, Appearance, DeviceEventEmitter } from 'react-native';
import TorClient from './services/Tor';
import Clipboard from '@react-native-clipboard/clipboard';
import EncryptedStorage from 'react-native-encrypted-storage';
import { name as app_name, version as app_version } from './package.json';
import TorModule from './native/TorModule';
import RoboIdentitiesModule from './native/RoboIdentitiesModule';
import NotificationsModule from './native/NotificationsModule';
import SystemModule from './native/SystemModule';
const backgroundColors = {
light: 'white',
dark: 'black',
};
export type TorStatus = 'ON' | 'STARTING' | 'STOPPING' | 'OFF';
const App = () => {
const colorScheme = Appearance.getColorScheme() ?? 'light';
const torClient = new TorClient();
const webViewRef = useRef<WebView>();
const uri = (Platform.OS === 'android' ? 'file:///android_asset/' : '') + 'Web.bundle/index.html';
useEffect(() => {
TorModule.start();
DeviceEventEmitter.addListener('navigateToPage', (payload) => {
window.navigateToPage = payload;
injectMessage({
category: 'system',
type: 'navigateToPage',
detail: payload,
});
});
DeviceEventEmitter.addListener('TorStatus', (payload) => {
if (payload.torStatus === 'OFF') TorModule.restart();
injectMessage({
category: 'system',
type: 'torStatus',
detail: payload.torStatus,
});
});
DeviceEventEmitter.addListener('WsMessage', (payload) => {
injectMessage({
category: 'ws',
type: 'wsMessage',
detail: payload,
});
});
}, []);
useEffect(() => {
const interval = setInterval(() => {
TorModule.getTorStatus();
}, 2000);
return () => clearInterval(interval);
}, []);
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 onLoadEnd = () => {
if (window.navigateToPage) {
injectMessage({
category: 'system',
type: 'navigateToPage',
detail: window.navigateToPage,
});
window.navigateToPage = undefined;
}
};
const init = (responseId: string) => {
const loadCookie = async (key: string) => {
return await EncryptedStorage.getItem(key).then((value) => {
if (value) {
const json = JSON.stringify({ key, value });
webViewRef.current?.injectJavaScript(
`(function() {window.NativeRobosats?.loadCookie(${json});})();`,
);
return value;
}
});
};
loadCookie('settings_fontsize_basic');
loadCookie('settings_language');
loadCookie('settings_mode');
loadCookie('settings_light_qr');
loadCookie('settings_network');
loadCookie('settings_connection');
loadCookie('settings_use_proxy').then((useProxy: string | undefined) => {
SystemModule.useProxy(useProxy ?? 'true');
});
loadCookie('settings_stop_notifications').then((stopNotifications: string | undefined) => {
SystemModule.stopNotifications(stopNotifications ?? 'false');
});
loadCookie('garage_slots').then((slots) => {
NotificationsModule.monitorOrders(slots ?? '{}');
injectMessageResolve(responseId);
});
};
const onCatch = (dataId: string, event: unknown) => {
let json = '{}';
let code = 500;
if (event.message) {
const reponse = /Request Response Code \((?<code>\d*)\): (?<json>\{.*\})/.exec(event.message);
json = reponse?.groups?.json ?? '{}';
code = reponse?.groups?.code ? parseInt(reponse?.groups?.code) : 500;
}
injectMessageResolve(dataId, {
headers: {},
respCode: code,
json: JSON.parse(json),
});
};
const onMessage = async (event: WebViewMessageEvent) => {
const data = JSON.parse(event.nativeEvent.data);
if (data.category === 'ws') {
TorModule.getTorStatus();
if (data.type === 'open') {
torClient
.wsOpen(data.path)
.then((connection: boolean) => {
injectMessageResolve(data.id, { connection });
})
.catch((e) => onCatch(data.id, e))
.finally(TorModule.getTorStatus);
} else if (data.type === 'send') {
torClient
.wsSend(data.path, data.message)
.catch((e) => onCatch(data.id, e))
.finally(TorModule.getTorStatus);
} else if (data.type === 'close') {
torClient
.wsClose(data.path)
.then((connection: boolean) => {
injectMessageResolve(data.id, { connection });
})
.finally(TorModule.getTorStatus);
}
} else if (data.category === 'http') {
TorModule.getTorStatus();
if (data.type === 'get') {
torClient
.get(data.baseUrl, data.path, data.headers)
.then((response: object) => {
injectMessageResolve(data.id, response);
})
.catch((e) => onCatch(data.id, e))
.finally(TorModule.getTorStatus);
} else if (data.type === 'post') {
torClient
.post(data.baseUrl, data.path, data.body, data.headers)
.then((response: object) => {
injectMessageResolve(data.id, response);
})
.catch((e) => onCatch(data.id, e))
.finally(TorModule.getTorStatus);
} else if (data.type === 'delete') {
torClient
.delete(data.baseUrl, data.path, data.headers)
.then((response: object) => {
injectMessageResolve(data.id, response);
})
.catch((e) => onCatch(data.id, e))
.finally(TorModule.getTorStatus);
}
} else if (data.category === 'system') {
if (data.type === 'init') {
init(data.id);
} else if (data.type === 'copyToClipboardString') {
Clipboard.setString(data.detail);
} else if (data.type === 'setCookie') {
setCookie(data.key, data.detail);
if (data.key === 'federation') {
SystemModule.setFederation(data.detail ?? '{}');
} else if (data.key === 'garage_slots') {
NotificationsModule.monitorOrders(data.detail ?? '{}');
} else if (data.key === 'settings_use_proxy') {
SystemModule.useProxy(data.detail ?? 'true');
} else if (data.key === 'settings_stop_notifications') {
SystemModule.stopNotifications(data.detail ?? 'false');
}
} else if (data.type === 'deleteCookie') {
EncryptedStorage.removeItem(data.key);
}
} else if (data.category === 'roboidentities') {
if (data.type === 'roboname') {
const roboname = await RoboIdentitiesModule.generateRoboname(data.detail);
injectMessageResolve(data.id, { roboname });
} else if (data.type === 'robohash') {
const robohash = await RoboIdentitiesModule.generateRobohash(data.detail);
injectMessageResolve(data.id, { robohash });
}
}
};
const setCookie = async (key: string, value: string) => {
try {
await EncryptedStorage.setItem(key, value);
const storedValue = await EncryptedStorage.getItem(key);
injectMessage({
category: 'system',
type: 'setCookie',
key,
detail: storedValue,
});
} catch (e) {
console.log('setCookie', e);
}
};
return (
<SafeAreaView style={{ flex: 1, backgroundColor: backgroundColors[colorScheme] }}>
<WebView
source={{
uri,
}}
onMessage={onMessage}
userAgent={`${app_name} v${app_version} Android`}
style={{ backgroundColor: backgroundColors[colorScheme] }}
ref={(ref) => (webViewRef.current = ref)}
overScrollMode='never'
javaScriptEnabled={true}
domStorageEnabled={true}
sharedCookiesEnabled={true}
thirdPartyCookiesEnabled={true}
originWhitelist={[uri]}
scalesPageToFit={true}
startInLoadingState={true}
mixedContentMode={'always'}
allowsInlineMediaPlayback={true}
allowsFullscreenVideo={false}
setBuiltInZoomControls={false}
allowingReadAccessToURL={uri}
allowFileAccess={true}
allowsBackForwardNavigationGestures={true}
mediaPlaybackRequiresUserAction={false} // Allow autoplay
allowsLinkPreview={false}
renderLoading={() => <Text></Text>}
onError={(syntheticEvent) => <Text>{syntheticEvent.type}</Text>}
onLoadEnd={() => setTimeout(onLoadEnd, 3000)}
/>
</SafeAreaView>
);
};
export default App;