mirror of
https://github.com/RoboSats/robosats.git
synced 2024-12-13 10:56:24 +00:00
193 lines
6.4 KiB
TypeScript
193 lines
6.4 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';
|
|
|
|
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('TorStatus', (payload) => {
|
|
if (payload.torStatus === 'OFF') TorModule.restart();
|
|
injectMessage({
|
|
category: 'system',
|
|
type: 'torStatus',
|
|
detail: payload.torStatus,
|
|
});
|
|
});
|
|
}, []);
|
|
|
|
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 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});})();`,
|
|
);
|
|
}
|
|
});
|
|
};
|
|
|
|
loadCookie('settings_fontsize_basic');
|
|
loadCookie('settings_language');
|
|
loadCookie('settings_mode');
|
|
loadCookie('settings_light_qr');
|
|
loadCookie('settings_network');
|
|
loadCookie('garage_slots').then(() => injectMessageResolve(responseId));
|
|
};
|
|
|
|
const onCatch = (dataId: string, event: any) => {
|
|
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 === '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);
|
|
} 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 (error) {}
|
|
};
|
|
|
|
return (
|
|
<SafeAreaView style={{ flex: 1, backgroundColor: backgroundColors[colorScheme] }}>
|
|
<WebView
|
|
source={{
|
|
uri,
|
|
}}
|
|
onMessage={onMessage}
|
|
// @ts-expect-error
|
|
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>}
|
|
/>
|
|
</SafeAreaView>
|
|
);
|
|
};
|
|
|
|
export default App;
|