Added storage view, and updated the UI in general

This commit is contained in:
2018-08-21 07:10:22 +02:00
parent 41570fb15e
commit 5a10498da7
29 changed files with 472 additions and 24 deletions

View File

@@ -3,21 +3,33 @@ import {
StyleSheet,
View,
Text,
Button,
TextInput,
} from 'react-native';
import styled from 'styled-components/native';
import State from '../../data/State';
import log from '../../../log';
import Icon from '../../base/Icon';
const Button = styled.TouchableOpacity`
padding: 12px 8px;
`;
const styles = StyleSheet.create({
container: {
borderColor: '#ccc',
borderTopWidth: 1,
flexDirection: 'row',
alignItems: 'center',
paddingLeft: 10,
paddingRight: 10,
},
input: {
flex: 1,
fontFamily: 'Menlo-Regular',
borderColor: '#ccc',
borderRadius: 5,
margin: 10,
padding: 5,
},
});
@@ -27,29 +39,70 @@ const Input = ({
<State>
{({
text = '',
history = [],
historyOffset,
}, setState) => (
<View style={styles.container}>
<Button
onPress={() => {
let currentOffset = typeof historyOffset === 'undefined' ? -1 : historyOffset;
currentOffset += 1;
const index = history.length - 1 - currentOffset;
if (history[index]) {
setState({
text: history[index],
historyOffset: currentOffset,
});
}
}}
>
<Icon name="left" />
</Button>
<Button
title=">"
onPress={() => {
let currentOffset = typeof historyOffset === 'undefined' ? -1 : historyOffset;
currentOffset -= 1;
const index = history.length - 1 - currentOffset;
if (history[index]) {
setState({
text: history[index],
historyOffset: currentOffset,
});
}
}}
>
<Icon name="right" />
</Button>
<TextInput
multiline
placeholder="{your code here}"
autoCapitalize="none"
autoCorrect={false}
style={styles.input}
value={text}
onChangeText={text => setState({ text })}
/>
<Button
title="eval"
onPress={() => {
const fn = new Function(text);
const newHistory = [...history, text];
const contextKeys = Object.keys(log.context);
const contextValues = Object.values(log.context);
const fn = new Function(...contextKeys, text);
try {
const response = fn();
log.info([response]);
fn(...contextValues);
setState({
text: '',
history: newHistory,
historyOffset: undefined,
});
} catch (err) {
log.error([err]);
}
}}
/>
>
<Icon name="play" />
</Button>
</View>
)}
</State>

View File

@@ -73,13 +73,13 @@ const formatData = (data, options) => {
/>
);
} else {
return <Fixed>{data.toString()}</Fixed>;
return <Fixed selectable={true}>{data.toString()}</Fixed>;
}
}
if (typeof data === 'object') {
return <JSONTree data={data} />
}
return <Fixed>{data.toString()}</Fixed>;
return <Fixed selectable={true}>{data.toString()}</Fixed>;
}
const OutputList = ({

View File

@@ -1,10 +1,13 @@
import React from 'react';
import {
StyleSheet,
Clipboard,
Alert,
View,
Text,
} from 'react-native';
import log from '../../../log';
import Log from '../../data/Log';
import Toolbar from '../../base/Toolbar';
import Output from './Output';
import Input from './Input';
@@ -20,6 +23,22 @@ const Console = ({
<Log>
{({ logs }) => (
<View style={styles.container}>
<Toolbar
items={[{
name: 'Download',
icon: 'download',
onPress: () => {
Clipboard.setString(JSON.stringify(logs, null, ' '));
Alert.alert(
'Copied to clipboard',
);
},
}, {
name: 'Clear',
icon: 'trash',
onPress: () => log.clear(),
}]}
/>
<Output logs={logs} includeStackTrace={includeStackTrace} />
<Input />
</View>

View File

@@ -62,10 +62,9 @@ class Events extends Component {
behavior="padding"
enabled
>
<DevTool includeStackTrace={includeStackTrace} />
<Button
title="close"
onPress={() => {
<DevTool
includeStackTrace={includeStackTrace}
onClose={() => {
events.publish('HIDE_DEVTOOLS');
}}
/>

View File

@@ -13,6 +13,10 @@ import Cell from '../../base/Cell';
import CellHeader from '../../base/CellHeader';
import Tab from '../Tab';
const WebView = styled.WebView`
background: red;
`;
const theme = {
scheme: 'bright',
author: 'chris kempson (http://chriskempson.com)',
@@ -50,7 +54,7 @@ const getResponse = (request) => {
return <JSONTree theme={theme} data={data} />
}
return <Fixed>{request.responseText}</Fixed>;
return <Fixed selectable={true}>{request.responseText}</Fixed>;
}
const Data = ({
@@ -73,13 +77,13 @@ const Data = ({
{headerInfo && (
<Fragment>
<CellHeader>Request Headers</CellHeader>
<Indented><Fixed>{headerInfo}</Fixed></Indented>
<Indented><Fixed selectable={true}>{headerInfo}</Fixed></Indented>
</Fragment>
)}
{args[0] && (
<Fragment>
<CellHeader>Request Body</CellHeader>
<Fixed>{args[0].toString()}</Fixed>
<Fixed selectable={true}>{args[0].toString()}</Fixed>
</Fragment>
)}
</View>
@@ -98,6 +102,30 @@ const Response = ({
);
const getPreview = (request, url) => {
if (request.responseType == 'blob' || request.responseType == 'ArrayBuffer') {
return [];
}
const contentType = request.getResponseHeader('content-type');
const contentTypes = contentType.split(';').map(c => c.trim());
if (contentTypes.includes('text/html')) {
return [{
name: 'Preview',
view: (
<WebView
source={{
html: request.responseText,
baseUrl: url,
}}
style={{flex: 1}}
/>
),
}]
}
return [];
}
const RequestDetails = (props) => (
<Tab
tabs={[{
@@ -106,7 +134,7 @@ const RequestDetails = (props) => (
}, {
name: 'Response',
view: <Response {...props} />
}]}
}, ...getPreview(props.request, props.url)]}
/>
);

View File

@@ -8,6 +8,8 @@ import {
const ScrollView = styled.ScrollView`
flex: 1;
border-bottom-width: 1px;
border-color: #ccc;
`;
const Wrapper = styled.View`

View File

@@ -2,11 +2,15 @@ import React from 'react';
import {
StyleSheet,
View,
Clipboard,
Alert,
Text,
TouchableOpacity,
} from 'react-native';
import network from '../../../network';
import State from '../../data/State';
import Network from '../../data/Network';
import Toolbar from '../../base/Toolbar';
import Details from './Details';
import List from './List';
@@ -34,6 +38,22 @@ const Console = () => (
const selected = active >= 0 ? requests[active] : undefined;
return (
<View style={styles.container}>
<Toolbar
items={[{
name: 'Download',
icon: 'download',
onPress: () => {
Clipboard.setString(JSON.stringify(requests, null, ' '));
Alert.alert(
'Copied to clipboard',
);
},
}, {
name: 'Clear',
icon: 'trash',
onPress: () => network.clear(),
}]}
/>
<List
selected={selected ? selected.id : undefined}
requests={requests}

View File

@@ -0,0 +1,41 @@
import React from 'react';
import styled from 'styled-components/native';
import Row from '../../base/Row';
import {
Body,
} from '../../base/text';
const Scroll = styled.ScrollView`
flex: 1;
`;
const Wrapper = styled.View`
`;
const Button = styled.TouchableOpacity`
`;
const Keys = ({
keys,
selected,
onSelect,
}) => (
<Scroll>
<Wrapper>
{keys.map(key => (
<Button
key={key}
onPress={() => onSelect(key)}
>
<Row
selected={selected === key}
>
<Body>{key}</Body>
</Row>
</Button>
))}
</Wrapper>
</Scroll>
)
export default Keys;

View File

@@ -0,0 +1,27 @@
import React from 'react';
import styled from 'styled-components/native';
import {
Fixed,
} from '../../base/text';
const Scroll = styled.ScrollView`
flex: 1;
border-top-width: 1px;
border-color: #ccc;
`;
const Wrapper = styled.View`
padding: 8px 16px;
`;
const Value = ({
value,
}) => (
<Scroll>
<Wrapper>
<Fixed selectable={true}>{value}</Fixed>
</Wrapper>
</Scroll>
)
export default Value;

View File

@@ -0,0 +1,64 @@
import React from 'react';
import styled from 'styled-components/native';
import {
Clipboard,
Alert,
} from 'react-native';
import Storage from '../../data/Storage';
import State from '../../data/State';
import Toolbar from '../../base/Toolbar';
import Keys from './Keys';
import Value from './Value';
const Wrapper = styled.View`
flex: 1;
`;
const StorageView = ({
}) => (
<State>
{({ selected }, setState) => (
<Storage>
{(data, update, removeItem, clear) => (
<Wrapper>
<Toolbar
items={[{
name: 'Download',
icon: 'download',
onPress: () => {
Clipboard.setString(JSON.stringify(data, null, ' '));
Alert.alert(
'Copied to clipboard',
);
},
}, {
name: 'Refresh',
icon: 'reload',
onPress: update,
}, {
name: 'Clear',
icon: 'trash',
onPress: clear,
}, {
name: 'Delete',
icon: 'remove',
disabled: !selected,
onPress: () => removeItem(selected),
}]}
/>
<Keys
selected={selected}
onSelect={(key) => setState({ selected: key })}
keys={Object.keys(data)}
/>
{selected && data[selected] && (
<Value value={data[selected]} />
)}
</Wrapper>
)}
</Storage>
)}
</State>
);
export default StorageView;

View File

@@ -1,4 +1,5 @@
import React, { Fragment } from 'react';
import styled from 'styled-components/native';
import PropTypes from 'prop-types';
import {
Text,
@@ -7,6 +8,7 @@ import {
View,
} from 'react-native';
import State from '../data/State';
import Icon from '../base/Icon';
const styles = StyleSheet.create({
container: {
@@ -15,14 +17,15 @@ const styles = StyleSheet.create({
},
tabs: {
flexDirection: 'row',
alignItems: 'center',
borderBottomWidth: 1,
borderColor: '#ccc',
},
tabInactive: {
flex: 1,
alignItems: 'center',
justifyContent: 'center',
padding: 10,
borderBottomWidth: 1,
borderColor: '#ccc',
},
tabActive: {
flex: 1,
@@ -34,8 +37,14 @@ const styles = StyleSheet.create({
},
});
const Button = styled.TouchableOpacity`
padding: 10px 20px 10px 0;
`;
const Console = ({
tabs,
buttons = [],
onClose,
}) => (
<View style={styles.container}>
<State
@@ -57,6 +66,14 @@ const Console = ({
<Text>{name}</Text>
</TouchableOpacity>
))}
{onClose && (
<Button
onPress={onClose}
>
<Icon name="close" />
</Button>
)}
</View>
{tabs[active] && tabs[active].view}
</Fragment>

View File

@@ -6,6 +6,7 @@ import {
import Tab from './Tab';
import Console from './Console';
import Requests from './Requests';
import Storage from './Storage';
const styles = StyleSheet.create({
container: {
@@ -15,7 +16,8 @@ const styles = StyleSheet.create({
const DevTool = ({
style,
includeStackTrace
includeStackTrace,
onClose,
}) => (
<View style={style || styles.container}>
<Tab
@@ -25,7 +27,11 @@ const DevTool = ({
}, {
name: 'Network',
view: <Requests />,
}, {
name: 'Storage',
view: <Storage />,
}]}
onClose={onClose}
/>
</View>
);