mirror of
https://github.com/RoboSats/robosats.git
synced 2025-01-18 12:11:35 +00:00
Persist Data on Android (#274)
* Android Cookies * Android Fix POST headers * Format * App & Cookies Working * Fix token on UserGen
This commit is contained in:
parent
f2dc7d0f90
commit
e78b5e9c8b
@ -134,6 +134,13 @@ export default class App extends Component {
|
||||
}
|
||||
}
|
||||
|
||||
const root = ReactDOM.createRoot(document.getElementById('app'));
|
||||
const loadApp = () => {
|
||||
if (systemClient.loading) {
|
||||
setTimeout(loadApp, 200);
|
||||
} else {
|
||||
const root = ReactDOM.createRoot(document.getElementById('app'));
|
||||
root.render(<App />);
|
||||
}
|
||||
};
|
||||
|
||||
root.render(<App />);
|
||||
loadApp();
|
||||
|
@ -19,6 +19,7 @@ import MediaQuery from 'react-responsive';
|
||||
import Flags from 'country-flag-icons/react/3x2';
|
||||
import { Link as LinkRouter } from 'react-router-dom';
|
||||
import { apiClient } from '../services/api';
|
||||
import { systemClient } from '../services/System';
|
||||
import RobotAvatar from './Robots/RobotAvatar';
|
||||
|
||||
// Icons
|
||||
@ -41,7 +42,6 @@ import {
|
||||
UpdateClientDialog,
|
||||
} from './Dialogs';
|
||||
|
||||
import { getCookie } from '../utils/cookies';
|
||||
import checkVer from '../utils/checkVer';
|
||||
|
||||
class BottomBar extends Component {
|
||||
@ -172,9 +172,8 @@ class BottomBar extends Component {
|
||||
showProfileButton = () => {
|
||||
return (
|
||||
this.props.avatarLoaded &&
|
||||
(window.NativeRobosats ||
|
||||
((this.props.token ? getCookie('robot_token') === this.props.token : true) &&
|
||||
getCookie('sessionid')))
|
||||
(this.props.token ? systemClient.getCookie('robot_token') === this.props.token : true) &&
|
||||
systemClient.getCookie('sessionid')
|
||||
);
|
||||
};
|
||||
|
||||
@ -467,7 +466,7 @@ class BottomBar extends Component {
|
||||
|
||||
handleClickOpenExchangeSummary = () => {
|
||||
// avoid calling getInfo while sessionid not yet set. Temporary fix.
|
||||
if (getCookie('sessionid')) {
|
||||
if (systemClient.getCookie('sessionid')) {
|
||||
this.getInfo();
|
||||
}
|
||||
this.setState({ openExchangeSummary: true });
|
||||
|
@ -36,7 +36,6 @@ import PersonAddAltIcon from '@mui/icons-material/PersonAddAlt';
|
||||
import EmojiEventsIcon from '@mui/icons-material/EmojiEvents';
|
||||
import { UserNinjaIcon, BitcoinIcon } from '../Icons';
|
||||
|
||||
import { getCookie } from '../../utils/cookies';
|
||||
import { systemClient } from '../../services/System';
|
||||
import { getWebln } from '../../utils/webln';
|
||||
import RobotAvatar from '../Robots/RobotAvatar';
|
||||
@ -98,7 +97,7 @@ const ProfileDialog = ({
|
||||
}, [showRewards]);
|
||||
|
||||
const copyTokenHandler = () => {
|
||||
const robotToken = getCookie('robot_token');
|
||||
const robotToken = systemClient.getCookie('robot_token');
|
||||
|
||||
if (robotToken) {
|
||||
systemClient.copyToClipboard(robotToken);
|
||||
@ -226,12 +225,12 @@ const ProfileDialog = ({
|
||||
</ListItemIcon>
|
||||
|
||||
<ListItemText secondary={t('Your token (will not remain here)')}>
|
||||
{getCookie('robot_token') ? (
|
||||
{systemClient.getCookie('robot_token') ? (
|
||||
<TextField
|
||||
disabled
|
||||
sx={{ width: '100%', maxWidth: '450px' }}
|
||||
label={t('Back it up!')}
|
||||
value={getCookie('robot_token')}
|
||||
value={systemClient.getCookie('robot_token')}
|
||||
variant='filled'
|
||||
size='small'
|
||||
InputProps={{
|
||||
|
@ -12,7 +12,7 @@ import {
|
||||
Button,
|
||||
Grid,
|
||||
} from '@mui/material';
|
||||
import { getCookie } from '../../utils/cookies';
|
||||
import { systemClient } from '../../services/System';
|
||||
import ContentCopy from '@mui/icons-material/ContentCopy';
|
||||
|
||||
interface Props {
|
||||
@ -50,7 +50,7 @@ const StoreTokenDialog = ({
|
||||
sx={{ width: '100%', maxWidth: '550px' }}
|
||||
disabled
|
||||
label={t('Back it up!')}
|
||||
value={getCookie('robot_token')}
|
||||
value={systemClient.getCookie('robot_token')}
|
||||
variant='filled'
|
||||
size='small'
|
||||
InputProps={{
|
||||
|
@ -16,7 +16,6 @@ import {
|
||||
} from '@mui/material';
|
||||
import ReconnectingWebSocket from 'reconnecting-websocket';
|
||||
import { encryptMessage, decryptMessage } from '../utils/pgp';
|
||||
import { getCookie } from '../utils/cookies';
|
||||
import { saveAsJson } from '../utils/saveFile';
|
||||
import { AuditPGPDialog } from './Dialogs';
|
||||
import RobotAvatar from './Robots/RobotAvatar';
|
||||
@ -37,10 +36,10 @@ class Chat extends Component {
|
||||
}
|
||||
|
||||
state = {
|
||||
own_pub_key: getCookie('pub_key').split('\\').join('\n'),
|
||||
own_enc_priv_key: getCookie('enc_priv_key').split('\\').join('\n'),
|
||||
own_pub_key: systemClient.getCookie('pub_key').split('\\').join('\n'),
|
||||
own_enc_priv_key: systemClient.getCookie('enc_priv_key').split('\\').join('\n'),
|
||||
peer_pub_key: null,
|
||||
token: getCookie('robot_token'),
|
||||
token: systemClient.getCookie('robot_token'),
|
||||
messages: [],
|
||||
value: '',
|
||||
connected: false,
|
||||
|
@ -43,7 +43,6 @@ import LockIcon from '@mui/icons-material/Lock';
|
||||
import HourglassTopIcon from '@mui/icons-material/HourglassTop';
|
||||
import ExpandMoreIcon from '@mui/icons-material/ExpandMore';
|
||||
|
||||
import { getCookie } from '../utils/cookies';
|
||||
import { pn } from '../utils/prettyNumbers';
|
||||
import { systemClient } from '../services/System';
|
||||
|
||||
@ -919,12 +918,12 @@ class MakerPage extends Component {
|
||||
const { t } = this.props;
|
||||
return (
|
||||
<Grid container align='center' spacing={1} sx={{ minWidth: '60%' }}>
|
||||
{getCookie('robot_token') ? (
|
||||
{systemClient.getCookie('robot_token') ? (
|
||||
<StoreTokenDialog
|
||||
open={this.state.openStoreToken}
|
||||
onClose={() => this.setState({ openStoreToken: false })}
|
||||
onClickCopy={() =>
|
||||
systemClient.copyToClipboard(getCookie('robot_token')) &
|
||||
systemClient.copyToClipboard(systemClient.getCookie('robot_token')) &
|
||||
this.props.setAppState({ copiedToken: true })
|
||||
}
|
||||
copyIconColor={this.props.copiedToken ? 'inherit' : 'primary'}
|
||||
|
@ -50,7 +50,6 @@ import HourglassTopIcon from '@mui/icons-material/HourglassTop';
|
||||
import CheckIcon from '@mui/icons-material/Check';
|
||||
import { SendReceiveIcon } from './Icons';
|
||||
|
||||
import { getCookie } from '../utils/cookies';
|
||||
import { pn } from '../utils/prettyNumbers';
|
||||
import { systemClient } from '../services/System';
|
||||
import { getWebln } from '../utils/webln';
|
||||
@ -522,12 +521,12 @@ class OrderPage extends Component {
|
||||
};
|
||||
|
||||
tokenDialog = () => {
|
||||
return getCookie('robot_token') ? (
|
||||
return systemClient.getCookie('robot_token') ? (
|
||||
<StoreTokenDialog
|
||||
open={this.state.openStoreToken}
|
||||
onClose={() => this.setState({ openStoreToken: false })}
|
||||
onClickCopy={() =>
|
||||
systemClient.copyToClipboard(getCookie('robot_token')) &
|
||||
systemClient.copyToClipboard(systemClient.getCookie('robot_token')) &
|
||||
this.props.setAppState({ copiedToken: true })
|
||||
}
|
||||
copyIconColor={this.props.copiedToken ? 'inherit' : 'primary'}
|
||||
|
@ -52,7 +52,6 @@ import RocketLaunchIcon from '@mui/icons-material/RocketLaunch';
|
||||
import RefreshIcon from '@mui/icons-material/Refresh';
|
||||
import { NewTabIcon } from './Icons';
|
||||
|
||||
import { getCookie } from '../utils/cookies';
|
||||
import { pn } from '../utils/prettyNumbers';
|
||||
|
||||
class TradeBox extends Component {
|
||||
|
@ -24,7 +24,6 @@ import { RoboSatsNoTextIcon } from './Icons';
|
||||
import { sha256 } from 'js-sha256';
|
||||
import { genBase62Token, tokenStrength } from '../utils/token';
|
||||
import { genKey } from '../utils/pgp';
|
||||
import { getCookie, writeCookie, deleteCookie } from '../utils/cookies';
|
||||
import { saveAsJson } from '../utils/saveFile';
|
||||
import { systemClient } from '../services/System';
|
||||
import { apiClient } from '../services/api/index';
|
||||
@ -47,10 +46,13 @@ class UserGenPage extends Component {
|
||||
// Displays the existing one
|
||||
if (this.props.nickname != null) {
|
||||
this.setState({
|
||||
nickname: this.props.nickname,
|
||||
token: this.props.token ? this.props.token : '',
|
||||
loadingRobot: false,
|
||||
});
|
||||
} else if (window.NativeRobosats && systemClient.getCookie('robot_token')) {
|
||||
const token = systemClient.getCookie('robot_token');
|
||||
this.props.setAppState({ token });
|
||||
this.setState({ token, loadingRobot: false });
|
||||
} else {
|
||||
const newToken = genBase62Token(36);
|
||||
this.setState({
|
||||
@ -78,7 +80,6 @@ class UserGenPage extends Component {
|
||||
requestBody.then((body) =>
|
||||
apiClient.post('/api/user/', body).then((data) => {
|
||||
this.setState({
|
||||
nickname: data.nickname,
|
||||
bit_entropy: data.token_bits_entropy,
|
||||
shannon_entropy: data.token_shannon_entropy,
|
||||
bad_request: data.bad_request,
|
||||
@ -110,9 +111,12 @@ class UserGenPage extends Component {
|
||||
tgBotName: data.tg_bot_name,
|
||||
tgToken: data.tg_token,
|
||||
}) &
|
||||
writeCookie('robot_token', token) &
|
||||
writeCookie('pub_key', data.public_key.split('\n').join('\\')) &
|
||||
writeCookie('enc_priv_key', data.encrypted_private_key.split('\n').join('\\'))) &
|
||||
systemClient.setCookie('robot_token', token) &
|
||||
systemClient.setCookie('pub_key', data.public_key.split('\n').join('\\')) &
|
||||
systemClient.setCookie(
|
||||
'enc_priv_key',
|
||||
data.encrypted_private_key.split('\n').join('\\'),
|
||||
)) &
|
||||
// If the robot has been found (recovered) we assume the token is backed up
|
||||
(data.found ? this.props.setAppState({ copiedToken: true }) : null);
|
||||
}),
|
||||
@ -122,10 +126,10 @@ class UserGenPage extends Component {
|
||||
delGeneratedUser() {
|
||||
apiClient.delete('/api/user');
|
||||
|
||||
deleteCookie('sessionid');
|
||||
deleteCookie('robot_token');
|
||||
deleteCookie('pub_key');
|
||||
deleteCookie('enc_priv_key');
|
||||
systemClient.deleteCookie('sessionid');
|
||||
systemClient.deleteCookie('robot_token');
|
||||
systemClient.deleteCookie('pub_key');
|
||||
systemClient.deleteCookie('enc_priv_key');
|
||||
}
|
||||
|
||||
handleClickNewRandomToken = () => {
|
||||
@ -168,11 +172,11 @@ class UserGenPage extends Component {
|
||||
|
||||
createJsonFile = () => {
|
||||
return {
|
||||
token: getCookie('robot_token'),
|
||||
token: systemClient.getCookie('robot_token'),
|
||||
token_shannon_entropy: this.state.shannon_entropy,
|
||||
token_bit_entropy: this.state.bit_entropy,
|
||||
public_key: getCookie('pub_key').split('\\').join('\n'),
|
||||
encrypted_private_key: getCookie('enc_priv_key').split('\\').join('\n'),
|
||||
public_key: systemClient.getCookie('pub_key').split('\\').join('\n'),
|
||||
encrypted_private_key: systemClient.getCookie('enc_priv_key').split('\\').join('\n'),
|
||||
};
|
||||
};
|
||||
|
||||
@ -191,12 +195,12 @@ class UserGenPage extends Component {
|
||||
align='center'
|
||||
sx={{ width: 370 * fontSizeFactor, height: 260 * fontSizeFactor }}
|
||||
>
|
||||
{this.props.avatarLoaded && this.state.nickname ? (
|
||||
{this.props.avatarLoaded && this.props.nickname ? (
|
||||
<div>
|
||||
<Grid item xs={12} align='center'>
|
||||
<Typography component='h5' variant='h5'>
|
||||
<b>
|
||||
{this.state.nickname && getCookie('sessionid') ? (
|
||||
{this.props.nickname && systemClient.getCookie('sessionid') ? (
|
||||
<div
|
||||
style={{
|
||||
display: 'flex',
|
||||
@ -213,7 +217,7 @@ class UserGenPage extends Component {
|
||||
width: 33 * fontSizeFactor,
|
||||
}}
|
||||
/>
|
||||
<a>{this.state.nickname}</a>
|
||||
<a>{this.props.nickname}</a>
|
||||
<BoltIcon
|
||||
sx={{
|
||||
color: '#fcba03',
|
||||
@ -230,7 +234,7 @@ class UserGenPage extends Component {
|
||||
</Grid>
|
||||
<Grid item xs={12} align='center'>
|
||||
<RobotAvatar
|
||||
nickname={this.state.nickname}
|
||||
nickname={this.props.nickname}
|
||||
smooth={true}
|
||||
style={{ maxWidth: 203 * fontSizeFactor, maxHeight: 203 * fontSizeFactor }}
|
||||
imageStyle={{
|
||||
@ -297,11 +301,10 @@ class UserGenPage extends Component {
|
||||
color='primary'
|
||||
disabled={
|
||||
!this.props.avatarLoaded ||
|
||||
(!window.NativeRobosats &&
|
||||
!(getCookie('robot_token') === this.state.token))
|
||||
!(systemClient.getCookie('robot_token') === this.state.token)
|
||||
}
|
||||
onClick={() =>
|
||||
saveAsJson(this.state.nickname + '.json', this.createJsonFile())
|
||||
saveAsJson(this.props.nickname + '.json', this.createJsonFile())
|
||||
}
|
||||
>
|
||||
<DownloadIcon
|
||||
@ -317,11 +320,10 @@ class UserGenPage extends Component {
|
||||
color={this.props.copiedToken ? 'inherit' : 'primary'}
|
||||
disabled={
|
||||
!this.props.avatarLoaded ||
|
||||
(!window.NativeRobosats &&
|
||||
!(getCookie('robot_token') === this.state.token))
|
||||
!(systemClient.getCookie('robot_token') === this.state.token)
|
||||
}
|
||||
onClick={() =>
|
||||
systemClient.copyToClipboard(getCookie('robot_token')) &
|
||||
systemClient.copyToClipboard(systemClient.getCookie('robot_token')) &
|
||||
this.props.setAppState({ copiedToken: true })
|
||||
}
|
||||
>
|
||||
@ -374,8 +376,9 @@ class UserGenPage extends Component {
|
||||
<Button
|
||||
disabled={
|
||||
this.state.loadingRobot !== false ||
|
||||
(!window.NativeRobosats &&
|
||||
!(this.props.token ? getCookie('robot_token') === this.props.token : true))
|
||||
!(this.props.token
|
||||
? systemClient.getCookie('robot_token') === this.props.token
|
||||
: true)
|
||||
}
|
||||
color='primary'
|
||||
to='/make/'
|
||||
@ -394,8 +397,9 @@ class UserGenPage extends Component {
|
||||
<Button
|
||||
disabled={
|
||||
this.state.loadingRobot !== false ||
|
||||
(!window.NativeRobosats &&
|
||||
!(this.props.token ? getCookie('robot_token') === this.props.token : true))
|
||||
!(this.props.token
|
||||
? systemClient.getCookie('robot_token') === this.props.token
|
||||
: true)
|
||||
}
|
||||
color='secondary'
|
||||
to='/book/'
|
||||
|
5
frontend/src/services/Native/index.d.ts
vendored
5
frontend/src/services/Native/index.d.ts
vendored
@ -23,8 +23,9 @@ export interface NativeWebViewMessageHttp {
|
||||
export interface NativeWebViewMessageSystem {
|
||||
id?: number;
|
||||
category: 'system';
|
||||
type: 'torStatus' | 'copyToClipboardString';
|
||||
detail: string;
|
||||
type: 'init' | 'torStatus' | 'copyToClipboardString' | 'setCookie' | 'deleteCookie';
|
||||
key?: string;
|
||||
detail?: string;
|
||||
}
|
||||
|
||||
export declare type NativeWebViewMessage = NativeWebViewMessageHttp | NativeWebViewMessageSystem;
|
||||
|
@ -1,16 +1,19 @@
|
||||
import { systemClient } from '../System';
|
||||
import { NativeRobosatsPromise, NativeWebViewMessage, NativeWebViewMessageSystem } from './index.d';
|
||||
|
||||
class NativeRobosats {
|
||||
constructor() {
|
||||
this.messageCounter = 0;
|
||||
}
|
||||
|
||||
public torDaemonStatus = 'NOTINIT';
|
||||
|
||||
private messageCounter: number;
|
||||
private messageCounter: number = 0;
|
||||
|
||||
private pendingMessages: { [id: number]: NativeRobosatsPromise } = {};
|
||||
|
||||
public cookies: { [key: string]: string } = {};
|
||||
|
||||
public loadCookie = (cookie: { key: string; value: string }) => {
|
||||
this.cookies[cookie.key] = cookie.value;
|
||||
};
|
||||
|
||||
public onMessageResolve: (messageId: number, response?: object) => void = (
|
||||
messageId,
|
||||
response = {},
|
||||
@ -33,8 +36,12 @@ class NativeRobosats {
|
||||
|
||||
public onMessage: (message: NativeWebViewMessageSystem) => void = (message) => {
|
||||
if (message.type === 'torStatus') {
|
||||
this.torDaemonStatus = message.detail;
|
||||
this.torDaemonStatus = message.detail || 'ERROR';
|
||||
window.dispatchEvent(new CustomEvent('torStatus', { detail: this.torDaemonStatus }));
|
||||
} else if (message.type === 'setCookie') {
|
||||
if (message.key !== undefined) {
|
||||
this.cookies[message.key] = message.detail;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -3,11 +3,17 @@ import NativeRobosats from '../../Native';
|
||||
|
||||
class SystemNativeClient implements SystemClient {
|
||||
constructor() {
|
||||
if (!window.NativeRobosats) {
|
||||
window.NativeRobosats = new NativeRobosats();
|
||||
}
|
||||
window.NativeRobosats = new NativeRobosats();
|
||||
window.NativeRobosats.postMessage({
|
||||
category: 'system',
|
||||
type: 'init',
|
||||
}).then(() => {
|
||||
this.loading = false;
|
||||
});
|
||||
}
|
||||
|
||||
public loading = true;
|
||||
|
||||
public copyToClipboard: (value: string) => void = (value) => {
|
||||
return window.NativeRobosats?.postMessage({
|
||||
category: 'system',
|
||||
@ -15,6 +21,30 @@ class SystemNativeClient implements SystemClient {
|
||||
detail: value,
|
||||
});
|
||||
};
|
||||
|
||||
public getCookie: (key: string) => string | undefined = (key) => {
|
||||
return window.NativeRobosats?.cookies[key];
|
||||
};
|
||||
|
||||
public setCookie: (key: string, value: string) => void = (key, value) => {
|
||||
delete window.NativeRobosats?.cookies[key];
|
||||
window.NativeRobosats?.postMessage({
|
||||
category: 'system',
|
||||
type: 'setCookie',
|
||||
key,
|
||||
detail: value,
|
||||
});
|
||||
};
|
||||
|
||||
public deleteCookie: (key: string) => void = (key) => {
|
||||
delete window.NativeRobosats?.cookies[key];
|
||||
|
||||
window.NativeRobosats?.postMessage({
|
||||
category: 'system',
|
||||
type: 'deleteCookie',
|
||||
key,
|
||||
});
|
||||
};
|
||||
}
|
||||
|
||||
export default SystemNativeClient;
|
||||
|
@ -1,6 +1,8 @@
|
||||
import { SystemClient } from '..';
|
||||
|
||||
class SystemWebClient implements SystemClient {
|
||||
public loading = false;
|
||||
|
||||
public copyToClipboard: (value: string) => void = (value) => {
|
||||
// navigator clipboard api needs a secure context (https)
|
||||
// this function attempts to copy also on http contexts
|
||||
@ -24,6 +26,31 @@ class SystemWebClient implements SystemClient {
|
||||
textArea.remove();
|
||||
}
|
||||
};
|
||||
|
||||
public getCookie: (key: string) => string | undefined = (key) => {
|
||||
let cookieValue = null;
|
||||
if (document.cookie && document.cookie !== '') {
|
||||
const cookies = document.cookie.split(';');
|
||||
for (let i = 0; i < cookies.length; i++) {
|
||||
const cookie = cookies[i].trim();
|
||||
// Does this cookie string begin with the key we want?
|
||||
if (cookie.substring(0, key.length + 1) === key + '=') {
|
||||
cookieValue = decodeURIComponent(cookie.substring(key.length + 1));
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return cookieValue || '';
|
||||
};
|
||||
|
||||
public setCookie: (key: string, value: string) => void = (key, value) => {
|
||||
document.cookie = `${key}=${value};path=/;SameSite=Strict`;
|
||||
};
|
||||
|
||||
public deleteCookie: (key: string) => void = (key) => {
|
||||
document.cookie = `${name}= ; expires = Thu, 01 Jan 1970 00:00:00 GMT`;
|
||||
};
|
||||
}
|
||||
|
||||
export default SystemWebClient;
|
||||
|
@ -2,7 +2,11 @@ import SystemNativeClient from './SystemNativeClient';
|
||||
import SystemWebClient from './SystemWebClient';
|
||||
|
||||
export interface SystemClient {
|
||||
loading: boolean;
|
||||
copyToClipboard: (value: string) => void;
|
||||
getCookie: (key: string) => string | undefined;
|
||||
setCookie: (key: string, value: string) => void;
|
||||
deleteCookie: (key: string) => void;
|
||||
}
|
||||
|
||||
export const systemClient: SystemClient =
|
||||
|
@ -1,19 +1,42 @@
|
||||
import { ApiClient } from '../api';
|
||||
import { getCookie } from '../../../utils/cookies';
|
||||
import { systemClient } from '../../System';
|
||||
import NativeRobosats from '../../Native';
|
||||
|
||||
class ApiNativeClient implements ApiClient {
|
||||
constructor() {
|
||||
if (!window.NativeRobosats) {
|
||||
window.NativeRobosats = new NativeRobosats();
|
||||
}
|
||||
}
|
||||
|
||||
private assetsCache: { [path: string]: string } = {};
|
||||
private assetsPromises: { [path: string]: Promise<string | undefined> } = {};
|
||||
|
||||
private readonly getHeaders: () => HeadersInit = () => {
|
||||
return { 'Content-Type': 'application/json', 'X-CSRFToken': getCookie('csrftoken') || '' };
|
||||
let headers = {
|
||||
'Content-Type': 'application/json',
|
||||
};
|
||||
|
||||
const sessionid = systemClient.getCookie('sessionid');
|
||||
if (sessionid) {
|
||||
const robotToken = systemClient.getCookie('robot_token');
|
||||
const csrftoken = systemClient.getCookie('csrftoken');
|
||||
const pubKey = systemClient.getCookie('pub_key');
|
||||
|
||||
headers = {
|
||||
...headers,
|
||||
...{
|
||||
'X-CSRFToken': csrftoken,
|
||||
Cookie: `sessionid=${sessionid};robot_token=${robotToken};csrftoken=${csrftoken};pub_key=${pubKey}`,
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
return headers;
|
||||
};
|
||||
|
||||
private readonly parseResponse = (response: { [key: string]: any }): object => {
|
||||
if (response.headers['set-cookie']) {
|
||||
response.headers['set-cookie'].forEach((cookie: string) => {
|
||||
const keySplit: string[] = cookie.split('=');
|
||||
systemClient.setCookie(keySplit[0], keySplit[1].split(';')[0]);
|
||||
});
|
||||
}
|
||||
return response.json;
|
||||
};
|
||||
|
||||
public put: (path: string, body: object) => Promise<object | undefined> = async (path, body) => {
|
||||
@ -26,7 +49,7 @@ class ApiNativeClient implements ApiClient {
|
||||
type: 'delete',
|
||||
path,
|
||||
headers: this.getHeaders(),
|
||||
});
|
||||
}).then(this.parseResponse);
|
||||
};
|
||||
|
||||
public post: (path: string, body: object) => Promise<object | undefined> = async (path, body) => {
|
||||
@ -36,7 +59,7 @@ class ApiNativeClient implements ApiClient {
|
||||
path,
|
||||
body,
|
||||
headers: this.getHeaders(),
|
||||
});
|
||||
}).then(this.parseResponse);
|
||||
};
|
||||
|
||||
public get: (path: string) => Promise<object | undefined> = async (path) => {
|
||||
@ -44,7 +67,8 @@ class ApiNativeClient implements ApiClient {
|
||||
category: 'http',
|
||||
type: 'get',
|
||||
path,
|
||||
});
|
||||
headers: this.getHeaders(),
|
||||
}).then(this.parseResponse);
|
||||
};
|
||||
|
||||
public fileImageUrl: (path: string) => Promise<string | undefined> = async (path) => {
|
||||
|
@ -1,9 +1,12 @@
|
||||
import { ApiClient } from '../api';
|
||||
import { getCookie } from '../../../utils/cookies';
|
||||
import { systemClient } from '../../System';
|
||||
|
||||
class ApiWebClient implements ApiClient {
|
||||
private readonly getHeaders: () => HeadersInit = () => {
|
||||
return { 'Content-Type': 'application/json', 'X-CSRFToken': getCookie('csrftoken') || '' };
|
||||
return {
|
||||
'Content-Type': 'application/json',
|
||||
'X-CSRFToken': systemClient.getCookie('csrftoken') || '',
|
||||
};
|
||||
};
|
||||
|
||||
public post: (path: string, body: object) => Promise<object | undefined> = async (path, body) => {
|
||||
|
@ -1,23 +0,0 @@
|
||||
export const getCookie = (name) => {
|
||||
let cookieValue = null;
|
||||
if (document.cookie && document.cookie !== '') {
|
||||
const cookies = document.cookie.split(';');
|
||||
for (let i = 0; i < cookies.length; i++) {
|
||||
const cookie = cookies[i].trim();
|
||||
// Does this cookie string begin with the name we want?
|
||||
if (cookie.substring(0, name.length + 1) === name + '=') {
|
||||
cookieValue = decodeURIComponent(cookie.substring(name.length + 1));
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
return cookieValue;
|
||||
};
|
||||
|
||||
export const writeCookie = (key, value) => {
|
||||
document.cookie = `${key}=${value};path=/;SameSite=Strict`;
|
||||
};
|
||||
|
||||
export const deleteCookie = (name) => {
|
||||
document.cookie = `${name}= ; expires = Thu, 01 Jan 1970 00:00:00 GMT`;
|
||||
};
|
@ -1,16 +1,18 @@
|
||||
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 TorClient from './services/Tor';
|
||||
import Clipboard from '@react-native-clipboard/clipboard';
|
||||
import NetInfo from '@react-native-community/netinfo';
|
||||
import EncryptedStorage from 'react-native-encrypted-storage';
|
||||
|
||||
const App = () => {
|
||||
const torClient = new TorClient();
|
||||
const webViewRef = useRef<WebView>();
|
||||
const uri = (Platform.OS === 'android' ? 'file:///android_asset/' : '') + 'Web.bundle/index.html';
|
||||
|
||||
const injectMessageResolve = (id: string, data: object) => {
|
||||
const json = JSON.stringify(data);
|
||||
const injectMessageResolve = (id: string, data?: object) => {
|
||||
const json = JSON.stringify(data || {});
|
||||
webViewRef.current?.injectJavaScript(
|
||||
`(function() {window.NativeRobosats.onMessageResolve(${id}, ${json});})();`,
|
||||
);
|
||||
@ -23,17 +25,36 @@ const App = () => {
|
||||
);
|
||||
};
|
||||
|
||||
const init = (reponseId: string) => {
|
||||
const loadCookie = (key: string) => {
|
||||
return EncryptedStorage.getItem(key).then((value) => {
|
||||
if (value) {
|
||||
const json = JSON.stringify({ key, value });
|
||||
webViewRef.current?.injectJavaScript(
|
||||
`(function() {window.NativeRobosats?.loadCookie(${json});})();`,
|
||||
);
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
loadCookie('sessionid');
|
||||
loadCookie('robot_token');
|
||||
loadCookie('csrftoken');
|
||||
loadCookie('pub_key');
|
||||
loadCookie('enc_priv_key').then(() => injectMessageResolve(reponseId));
|
||||
};
|
||||
|
||||
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)
|
||||
.get(data.path, data.headers)
|
||||
.then((response: object) => {
|
||||
injectMessageResolve(data.id, response);
|
||||
})
|
||||
.catch(sendTorStatus)
|
||||
.finally(sendTorStatus);
|
||||
} else if (data.type === 'post') {
|
||||
torClient
|
||||
@ -41,6 +62,7 @@ const App = () => {
|
||||
.then((response: object) => {
|
||||
injectMessageResolve(data.id, response);
|
||||
})
|
||||
.catch(sendTorStatus)
|
||||
.finally(sendTorStatus);
|
||||
} else if (data.type === 'delete') {
|
||||
torClient
|
||||
@ -48,6 +70,7 @@ const App = () => {
|
||||
.then((response: object) => {
|
||||
injectMessageResolve(data.id, response);
|
||||
})
|
||||
.catch(sendTorStatus)
|
||||
.finally(sendTorStatus);
|
||||
} else if (data.type === 'xhr') {
|
||||
torClient
|
||||
@ -55,15 +78,35 @@ const App = () => {
|
||||
.then((response: object) => {
|
||||
injectMessageResolve(data.id, response);
|
||||
})
|
||||
.catch(sendTorStatus)
|
||||
.finally(sendTorStatus);
|
||||
}
|
||||
} else if (data.category === 'system') {
|
||||
if (data.type === 'copyToClipboardString') {
|
||||
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);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
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) {}
|
||||
};
|
||||
|
||||
const sendTorStatus = async () => {
|
||||
NetInfo.fetch().then(async (state) => {
|
||||
let daemonStatus = 'ERROR';
|
||||
|
16
mobile/package-lock.json
generated
16
mobile/package-lock.json
generated
@ -12,6 +12,7 @@
|
||||
"@react-native-community/netinfo": "^9.3.4",
|
||||
"react": "^18.0.0",
|
||||
"react-native": "^0.69.6",
|
||||
"react-native-encrypted-storage": "^4.0.2",
|
||||
"react-native-tor": "^0.1.8",
|
||||
"react-native-v8": "^1.5.0",
|
||||
"react-native-webview": "^11.22.3",
|
||||
@ -13411,6 +13412,15 @@
|
||||
"nullthrows": "^1.1.1"
|
||||
}
|
||||
},
|
||||
"node_modules/react-native-encrypted-storage": {
|
||||
"version": "4.0.2",
|
||||
"resolved": "https://registry.npmjs.org/react-native-encrypted-storage/-/react-native-encrypted-storage-4.0.2.tgz",
|
||||
"integrity": "sha512-vneDkHGDuTvLQjUBztqb2YI8QoH1zxdJonPGTS+g57lfJZff9fAjoLSSb6NgMBebpXFcK3I3sEresGyL+3AArw==",
|
||||
"peerDependencies": {
|
||||
"react": "*",
|
||||
"react-native": "*"
|
||||
}
|
||||
},
|
||||
"node_modules/react-native-gradle-plugin": {
|
||||
"version": "0.0.7",
|
||||
"resolved": "https://registry.npmjs.org/react-native-gradle-plugin/-/react-native-gradle-plugin-0.0.7.tgz",
|
||||
@ -25923,6 +25933,12 @@
|
||||
"nullthrows": "^1.1.1"
|
||||
}
|
||||
},
|
||||
"react-native-encrypted-storage": {
|
||||
"version": "4.0.2",
|
||||
"resolved": "https://registry.npmjs.org/react-native-encrypted-storage/-/react-native-encrypted-storage-4.0.2.tgz",
|
||||
"integrity": "sha512-vneDkHGDuTvLQjUBztqb2YI8QoH1zxdJonPGTS+g57lfJZff9fAjoLSSb6NgMBebpXFcK3I3sEresGyL+3AArw==",
|
||||
"requires": {}
|
||||
},
|
||||
"react-native-gradle-plugin": {
|
||||
"version": "0.0.7",
|
||||
"resolved": "https://registry.npmjs.org/react-native-gradle-plugin/-/react-native-gradle-plugin-0.0.7.tgz",
|
||||
|
@ -16,6 +16,7 @@
|
||||
"@react-native-community/netinfo": "^9.3.4",
|
||||
"react": "^18.0.0",
|
||||
"react-native": "^0.69.6",
|
||||
"react-native-encrypted-storage": "^4.0.2",
|
||||
"react-native-tor": "^0.1.8",
|
||||
"react-native-v8": "^1.5.0",
|
||||
"react-native-webview": "^11.22.3",
|
||||
|
@ -20,14 +20,12 @@ class TorClient {
|
||||
}
|
||||
};
|
||||
|
||||
public get: (path: string) => Promise<object> = async (path) => {
|
||||
await this.connectDaemon();
|
||||
|
||||
public get: (path: string, headers: object) => Promise<object> = async (path, headers) => {
|
||||
return await new Promise<object>(async (resolve, reject) => {
|
||||
try {
|
||||
const response = await this.daemon.get(`${this.baseUrl}${path}`);
|
||||
const response = await this.daemon.get(`${this.baseUrl}${path}`, headers);
|
||||
|
||||
resolve(response.json);
|
||||
resolve(response);
|
||||
} catch (error) {
|
||||
reject(error);
|
||||
}
|
||||
@ -35,13 +33,11 @@ class TorClient {
|
||||
};
|
||||
|
||||
public delete: (path: string, headers: object) => Promise<object> = async (path, headers) => {
|
||||
await this.connectDaemon();
|
||||
|
||||
return await new Promise<object>(async (resolve, reject) => {
|
||||
try {
|
||||
const response = await this.daemon.delete(`${this.baseUrl}${path}`, '', headers);
|
||||
|
||||
resolve(response.json);
|
||||
resolve(response);
|
||||
} catch (error) {
|
||||
reject(error);
|
||||
}
|
||||
@ -49,8 +45,6 @@ class TorClient {
|
||||
};
|
||||
|
||||
public request: (path: string) => Promise<object> = async (path) => {
|
||||
await this.connectDaemon();
|
||||
|
||||
return await new Promise<object>(async (resolve, reject) => {
|
||||
try {
|
||||
const response = await this.daemon
|
||||
@ -71,14 +65,12 @@ class TorClient {
|
||||
body,
|
||||
headers,
|
||||
) => {
|
||||
await this.connectDaemon();
|
||||
|
||||
return await new Promise<object>(async (resolve, reject) => {
|
||||
try {
|
||||
const json = JSON.stringify(body);
|
||||
const response = await this.daemon.post(`${this.baseUrl}${path}`, json, headers);
|
||||
|
||||
resolve(response.json);
|
||||
resolve(response);
|
||||
} catch (error) {
|
||||
reject(error);
|
||||
}
|
||||
@ -86,6 +78,4 @@ class TorClient {
|
||||
};
|
||||
}
|
||||
|
||||
export const torClient = new TorClient();
|
||||
|
||||
export default TorClient;
|
||||
|
Loading…
Reference in New Issue
Block a user