Added storage view, and updated the UI in general
@@ -6,6 +6,7 @@ import {
|
|||||||
Button,
|
Button,
|
||||||
KeyboardAvoidingView,
|
KeyboardAvoidingView,
|
||||||
SafeAreaView,
|
SafeAreaView,
|
||||||
|
AsyncStorage,
|
||||||
} from 'react-native';
|
} from 'react-native';
|
||||||
import {
|
import {
|
||||||
DevTool,
|
DevTool,
|
||||||
@@ -26,12 +27,18 @@ xhr = new XMLHttpRequest();
|
|||||||
xhr.open('GET', 'https://google.com/sdfsfsdfsfdf');
|
xhr.open('GET', 'https://google.com/sdfsfsdfsfdf');
|
||||||
xhr.send();
|
xhr.send();
|
||||||
|
|
||||||
|
AsyncStorage.setItem('a', 'b');
|
||||||
|
AsyncStorage.setItem('b', 'c');
|
||||||
|
AsyncStorage.setItem('c', 'd');
|
||||||
|
|
||||||
const t = new Promise((resolve, reject) => {
|
const t = new Promise((resolve, reject) => {
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
throw new Error('everything is broken');
|
throw new Error('everything is broken');
|
||||||
}, 1000);
|
}, 1000);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
export default class App extends React.Component {
|
export default class App extends React.Component {
|
||||||
render() {
|
render() {
|
||||||
return (
|
return (
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "react-native-debug-console",
|
"name": "react-native-debug-console",
|
||||||
"version": "1.0.0-beta.8",
|
"version": "1.0.0-beta.9",
|
||||||
"main": "src/index.js",
|
"main": "src/index.js",
|
||||||
"repository": {
|
"repository": {
|
||||||
"url": "https://github.com/morten-olsen/react-native-debugger"
|
"url": "https://github.com/morten-olsen/react-native-debugger"
|
||||||
|
|||||||
@@ -3,21 +3,33 @@ import {
|
|||||||
StyleSheet,
|
StyleSheet,
|
||||||
View,
|
View,
|
||||||
Text,
|
Text,
|
||||||
Button,
|
|
||||||
TextInput,
|
TextInput,
|
||||||
} from 'react-native';
|
} from 'react-native';
|
||||||
|
import styled from 'styled-components/native';
|
||||||
import State from '../../data/State';
|
import State from '../../data/State';
|
||||||
import log from '../../../log';
|
import log from '../../../log';
|
||||||
|
import Icon from '../../base/Icon';
|
||||||
|
|
||||||
|
const Button = styled.TouchableOpacity`
|
||||||
|
padding: 12px 8px;
|
||||||
|
`;
|
||||||
|
|
||||||
const styles = StyleSheet.create({
|
const styles = StyleSheet.create({
|
||||||
container: {
|
container: {
|
||||||
borderColor: '#ccc',
|
borderColor: '#ccc',
|
||||||
borderTopWidth: 1,
|
borderTopWidth: 1,
|
||||||
flexDirection: 'row',
|
flexDirection: 'row',
|
||||||
|
alignItems: 'center',
|
||||||
|
paddingLeft: 10,
|
||||||
|
paddingRight: 10,
|
||||||
},
|
},
|
||||||
input: {
|
input: {
|
||||||
flex: 1,
|
flex: 1,
|
||||||
fontFamily: 'Menlo-Regular',
|
fontFamily: 'Menlo-Regular',
|
||||||
|
borderColor: '#ccc',
|
||||||
|
borderRadius: 5,
|
||||||
|
margin: 10,
|
||||||
|
padding: 5,
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -27,29 +39,70 @@ const Input = ({
|
|||||||
<State>
|
<State>
|
||||||
{({
|
{({
|
||||||
text = '',
|
text = '',
|
||||||
|
history = [],
|
||||||
|
historyOffset,
|
||||||
}, setState) => (
|
}, setState) => (
|
||||||
<View style={styles.container}>
|
<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
|
<TextInput
|
||||||
multiline
|
multiline
|
||||||
|
placeholder="{your code here}"
|
||||||
|
autoCapitalize="none"
|
||||||
|
autoCorrect={false}
|
||||||
style={styles.input}
|
style={styles.input}
|
||||||
value={text}
|
value={text}
|
||||||
onChangeText={text => setState({ text })}
|
onChangeText={text => setState({ text })}
|
||||||
/>
|
/>
|
||||||
<Button
|
<Button
|
||||||
title="eval"
|
|
||||||
onPress={() => {
|
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 {
|
try {
|
||||||
const response = fn();
|
fn(...contextValues);
|
||||||
log.info([response]);
|
|
||||||
setState({
|
setState({
|
||||||
text: '',
|
text: '',
|
||||||
|
history: newHistory,
|
||||||
|
historyOffset: undefined,
|
||||||
});
|
});
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
log.error([err]);
|
log.error([err]);
|
||||||
}
|
}
|
||||||
}}
|
}}
|
||||||
/>
|
>
|
||||||
|
<Icon name="play" />
|
||||||
|
</Button>
|
||||||
</View>
|
</View>
|
||||||
)}
|
)}
|
||||||
</State>
|
</State>
|
||||||
|
|||||||
@@ -73,13 +73,13 @@ const formatData = (data, options) => {
|
|||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
return <Fixed>{data.toString()}</Fixed>;
|
return <Fixed selectable={true}>{data.toString()}</Fixed>;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (typeof data === 'object') {
|
if (typeof data === 'object') {
|
||||||
return <JSONTree data={data} />
|
return <JSONTree data={data} />
|
||||||
}
|
}
|
||||||
return <Fixed>{data.toString()}</Fixed>;
|
return <Fixed selectable={true}>{data.toString()}</Fixed>;
|
||||||
}
|
}
|
||||||
|
|
||||||
const OutputList = ({
|
const OutputList = ({
|
||||||
|
|||||||
@@ -1,10 +1,13 @@
|
|||||||
import React from 'react';
|
import React from 'react';
|
||||||
import {
|
import {
|
||||||
StyleSheet,
|
StyleSheet,
|
||||||
|
Clipboard,
|
||||||
|
Alert,
|
||||||
View,
|
View,
|
||||||
Text,
|
|
||||||
} from 'react-native';
|
} from 'react-native';
|
||||||
|
import log from '../../../log';
|
||||||
import Log from '../../data/Log';
|
import Log from '../../data/Log';
|
||||||
|
import Toolbar from '../../base/Toolbar';
|
||||||
import Output from './Output';
|
import Output from './Output';
|
||||||
import Input from './Input';
|
import Input from './Input';
|
||||||
|
|
||||||
@@ -20,6 +23,22 @@ const Console = ({
|
|||||||
<Log>
|
<Log>
|
||||||
{({ logs }) => (
|
{({ logs }) => (
|
||||||
<View style={styles.container}>
|
<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} />
|
<Output logs={logs} includeStackTrace={includeStackTrace} />
|
||||||
<Input />
|
<Input />
|
||||||
</View>
|
</View>
|
||||||
|
|||||||
@@ -62,10 +62,9 @@ class Events extends Component {
|
|||||||
behavior="padding"
|
behavior="padding"
|
||||||
enabled
|
enabled
|
||||||
>
|
>
|
||||||
<DevTool includeStackTrace={includeStackTrace} />
|
<DevTool
|
||||||
<Button
|
includeStackTrace={includeStackTrace}
|
||||||
title="close"
|
onClose={() => {
|
||||||
onPress={() => {
|
|
||||||
events.publish('HIDE_DEVTOOLS');
|
events.publish('HIDE_DEVTOOLS');
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
|
|||||||
@@ -13,6 +13,10 @@ import Cell from '../../base/Cell';
|
|||||||
import CellHeader from '../../base/CellHeader';
|
import CellHeader from '../../base/CellHeader';
|
||||||
import Tab from '../Tab';
|
import Tab from '../Tab';
|
||||||
|
|
||||||
|
const WebView = styled.WebView`
|
||||||
|
background: red;
|
||||||
|
`;
|
||||||
|
|
||||||
const theme = {
|
const theme = {
|
||||||
scheme: 'bright',
|
scheme: 'bright',
|
||||||
author: 'chris kempson (http://chriskempson.com)',
|
author: 'chris kempson (http://chriskempson.com)',
|
||||||
@@ -50,7 +54,7 @@ const getResponse = (request) => {
|
|||||||
return <JSONTree theme={theme} data={data} />
|
return <JSONTree theme={theme} data={data} />
|
||||||
}
|
}
|
||||||
|
|
||||||
return <Fixed>{request.responseText}</Fixed>;
|
return <Fixed selectable={true}>{request.responseText}</Fixed>;
|
||||||
}
|
}
|
||||||
|
|
||||||
const Data = ({
|
const Data = ({
|
||||||
@@ -73,13 +77,13 @@ const Data = ({
|
|||||||
{headerInfo && (
|
{headerInfo && (
|
||||||
<Fragment>
|
<Fragment>
|
||||||
<CellHeader>Request Headers</CellHeader>
|
<CellHeader>Request Headers</CellHeader>
|
||||||
<Indented><Fixed>{headerInfo}</Fixed></Indented>
|
<Indented><Fixed selectable={true}>{headerInfo}</Fixed></Indented>
|
||||||
</Fragment>
|
</Fragment>
|
||||||
)}
|
)}
|
||||||
{args[0] && (
|
{args[0] && (
|
||||||
<Fragment>
|
<Fragment>
|
||||||
<CellHeader>Request Body</CellHeader>
|
<CellHeader>Request Body</CellHeader>
|
||||||
<Fixed>{args[0].toString()}</Fixed>
|
<Fixed selectable={true}>{args[0].toString()}</Fixed>
|
||||||
</Fragment>
|
</Fragment>
|
||||||
)}
|
)}
|
||||||
</View>
|
</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) => (
|
const RequestDetails = (props) => (
|
||||||
<Tab
|
<Tab
|
||||||
tabs={[{
|
tabs={[{
|
||||||
@@ -106,7 +134,7 @@ const RequestDetails = (props) => (
|
|||||||
}, {
|
}, {
|
||||||
name: 'Response',
|
name: 'Response',
|
||||||
view: <Response {...props} />
|
view: <Response {...props} />
|
||||||
}]}
|
}, ...getPreview(props.request, props.url)]}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|||||||
@@ -8,6 +8,8 @@ import {
|
|||||||
|
|
||||||
const ScrollView = styled.ScrollView`
|
const ScrollView = styled.ScrollView`
|
||||||
flex: 1;
|
flex: 1;
|
||||||
|
border-bottom-width: 1px;
|
||||||
|
border-color: #ccc;
|
||||||
`;
|
`;
|
||||||
|
|
||||||
const Wrapper = styled.View`
|
const Wrapper = styled.View`
|
||||||
|
|||||||
@@ -2,11 +2,15 @@ import React from 'react';
|
|||||||
import {
|
import {
|
||||||
StyleSheet,
|
StyleSheet,
|
||||||
View,
|
View,
|
||||||
|
Clipboard,
|
||||||
|
Alert,
|
||||||
Text,
|
Text,
|
||||||
TouchableOpacity,
|
TouchableOpacity,
|
||||||
} from 'react-native';
|
} from 'react-native';
|
||||||
|
import network from '../../../network';
|
||||||
import State from '../../data/State';
|
import State from '../../data/State';
|
||||||
import Network from '../../data/Network';
|
import Network from '../../data/Network';
|
||||||
|
import Toolbar from '../../base/Toolbar';
|
||||||
import Details from './Details';
|
import Details from './Details';
|
||||||
import List from './List';
|
import List from './List';
|
||||||
|
|
||||||
@@ -34,6 +38,22 @@ const Console = () => (
|
|||||||
const selected = active >= 0 ? requests[active] : undefined;
|
const selected = active >= 0 ? requests[active] : undefined;
|
||||||
return (
|
return (
|
||||||
<View style={styles.container}>
|
<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
|
<List
|
||||||
selected={selected ? selected.id : undefined}
|
selected={selected ? selected.id : undefined}
|
||||||
requests={requests}
|
requests={requests}
|
||||||
|
|||||||
41
lib/src/components/DevTool/Storage/Keys.js
Normal 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;
|
||||||
27
lib/src/components/DevTool/Storage/Value.js
Normal 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;
|
||||||
64
lib/src/components/DevTool/Storage/index.js
Normal 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;
|
||||||
@@ -1,4 +1,5 @@
|
|||||||
import React, { Fragment } from 'react';
|
import React, { Fragment } from 'react';
|
||||||
|
import styled from 'styled-components/native';
|
||||||
import PropTypes from 'prop-types';
|
import PropTypes from 'prop-types';
|
||||||
import {
|
import {
|
||||||
Text,
|
Text,
|
||||||
@@ -7,6 +8,7 @@ import {
|
|||||||
View,
|
View,
|
||||||
} from 'react-native';
|
} from 'react-native';
|
||||||
import State from '../data/State';
|
import State from '../data/State';
|
||||||
|
import Icon from '../base/Icon';
|
||||||
|
|
||||||
const styles = StyleSheet.create({
|
const styles = StyleSheet.create({
|
||||||
container: {
|
container: {
|
||||||
@@ -15,14 +17,15 @@ const styles = StyleSheet.create({
|
|||||||
},
|
},
|
||||||
tabs: {
|
tabs: {
|
||||||
flexDirection: 'row',
|
flexDirection: 'row',
|
||||||
|
alignItems: 'center',
|
||||||
|
borderBottomWidth: 1,
|
||||||
|
borderColor: '#ccc',
|
||||||
},
|
},
|
||||||
tabInactive: {
|
tabInactive: {
|
||||||
flex: 1,
|
flex: 1,
|
||||||
alignItems: 'center',
|
alignItems: 'center',
|
||||||
justifyContent: 'center',
|
justifyContent: 'center',
|
||||||
padding: 10,
|
padding: 10,
|
||||||
borderBottomWidth: 1,
|
|
||||||
borderColor: '#ccc',
|
|
||||||
},
|
},
|
||||||
tabActive: {
|
tabActive: {
|
||||||
flex: 1,
|
flex: 1,
|
||||||
@@ -34,8 +37,14 @@ const styles = StyleSheet.create({
|
|||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const Button = styled.TouchableOpacity`
|
||||||
|
padding: 10px 20px 10px 0;
|
||||||
|
`;
|
||||||
|
|
||||||
const Console = ({
|
const Console = ({
|
||||||
tabs,
|
tabs,
|
||||||
|
buttons = [],
|
||||||
|
onClose,
|
||||||
}) => (
|
}) => (
|
||||||
<View style={styles.container}>
|
<View style={styles.container}>
|
||||||
<State
|
<State
|
||||||
@@ -57,6 +66,14 @@ const Console = ({
|
|||||||
<Text>{name}</Text>
|
<Text>{name}</Text>
|
||||||
</TouchableOpacity>
|
</TouchableOpacity>
|
||||||
))}
|
))}
|
||||||
|
|
||||||
|
{onClose && (
|
||||||
|
<Button
|
||||||
|
onPress={onClose}
|
||||||
|
>
|
||||||
|
<Icon name="close" />
|
||||||
|
</Button>
|
||||||
|
)}
|
||||||
</View>
|
</View>
|
||||||
{tabs[active] && tabs[active].view}
|
{tabs[active] && tabs[active].view}
|
||||||
</Fragment>
|
</Fragment>
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ import {
|
|||||||
import Tab from './Tab';
|
import Tab from './Tab';
|
||||||
import Console from './Console';
|
import Console from './Console';
|
||||||
import Requests from './Requests';
|
import Requests from './Requests';
|
||||||
|
import Storage from './Storage';
|
||||||
|
|
||||||
const styles = StyleSheet.create({
|
const styles = StyleSheet.create({
|
||||||
container: {
|
container: {
|
||||||
@@ -15,7 +16,8 @@ const styles = StyleSheet.create({
|
|||||||
|
|
||||||
const DevTool = ({
|
const DevTool = ({
|
||||||
style,
|
style,
|
||||||
includeStackTrace
|
includeStackTrace,
|
||||||
|
onClose,
|
||||||
}) => (
|
}) => (
|
||||||
<View style={style || styles.container}>
|
<View style={style || styles.container}>
|
||||||
<Tab
|
<Tab
|
||||||
@@ -25,7 +27,11 @@ const DevTool = ({
|
|||||||
}, {
|
}, {
|
||||||
name: 'Network',
|
name: 'Network',
|
||||||
view: <Requests />,
|
view: <Requests />,
|
||||||
|
}, {
|
||||||
|
name: 'Storage',
|
||||||
|
view: <Storage />,
|
||||||
}]}
|
}]}
|
||||||
|
onClose={onClose}
|
||||||
/>
|
/>
|
||||||
</View>
|
</View>
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -22,7 +22,7 @@ const Row = ({
|
|||||||
<Left>
|
<Left>
|
||||||
<Emphasis>{left}:</Emphasis>
|
<Emphasis>{left}:</Emphasis>
|
||||||
</Left>
|
</Left>
|
||||||
<Body>{right}</Body>
|
<Body selectable={true}>{right}</Body>
|
||||||
</Wrapper>
|
</Wrapper>
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|||||||
BIN
lib/src/components/base/Icon/close.png
Normal file
|
After Width: | Height: | Size: 4.2 KiB |
BIN
lib/src/components/base/Icon/download.png
Normal file
|
After Width: | Height: | Size: 5.2 KiB |
41
lib/src/components/base/Icon/index.js
Normal file
@@ -0,0 +1,41 @@
|
|||||||
|
import React from 'react';
|
||||||
|
import styled from 'styled-components/native';
|
||||||
|
|
||||||
|
import reload from './reload.png';
|
||||||
|
import trash from './trash.png';
|
||||||
|
import remove from './return.png';
|
||||||
|
import right from './right.png';
|
||||||
|
import left from './left.png';
|
||||||
|
import play from './play.png';
|
||||||
|
import download from './download.png';
|
||||||
|
import close from './close.png';
|
||||||
|
|
||||||
|
const icons = {
|
||||||
|
reload,
|
||||||
|
trash,
|
||||||
|
remove,
|
||||||
|
right,
|
||||||
|
left,
|
||||||
|
play,
|
||||||
|
download,
|
||||||
|
close,
|
||||||
|
}
|
||||||
|
|
||||||
|
const Image = styled.Image`
|
||||||
|
height: ${({ height }) => height || '16'}px;
|
||||||
|
width: ${({ width }) => width || '16'}px;
|
||||||
|
`;
|
||||||
|
|
||||||
|
const Icon = ({
|
||||||
|
name,
|
||||||
|
width,
|
||||||
|
height
|
||||||
|
}) => (
|
||||||
|
<Image
|
||||||
|
width={width}
|
||||||
|
height={height}
|
||||||
|
source={icons[name]}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
|
||||||
|
export default Icon;
|
||||||
BIN
lib/src/components/base/Icon/left.png
Normal file
|
After Width: | Height: | Size: 3.0 KiB |
BIN
lib/src/components/base/Icon/play.png
Normal file
|
After Width: | Height: | Size: 8.3 KiB |
BIN
lib/src/components/base/Icon/reload.png
Normal file
|
After Width: | Height: | Size: 9.6 KiB |
BIN
lib/src/components/base/Icon/return.png
Normal file
|
After Width: | Height: | Size: 6.9 KiB |
BIN
lib/src/components/base/Icon/right.png
Normal file
|
After Width: | Height: | Size: 2.8 KiB |
BIN
lib/src/components/base/Icon/trash.png
Normal file
|
After Width: | Height: | Size: 6.2 KiB |
@@ -5,7 +5,9 @@ const Wrapper = styled.View`
|
|||||||
flex-direction: row;
|
flex-direction: row;
|
||||||
padding: 10px;
|
padding: 10px;
|
||||||
border-left-width: ${props => props.selected ? '10px' : '0'};
|
border-left-width: ${props => props.selected ? '10px' : '0'};
|
||||||
border-color: #2980b9;
|
border-left-color: #2980b9;
|
||||||
|
border-bottom-width: 1px;
|
||||||
|
border-bottom-color: #efefef;
|
||||||
`;
|
`;
|
||||||
|
|
||||||
const Left = styled.View`
|
const Left = styled.View`
|
||||||
|
|||||||
47
lib/src/components/base/Toolbar.js
Normal file
@@ -0,0 +1,47 @@
|
|||||||
|
import React from 'react';
|
||||||
|
import styled from 'styled-components/native';
|
||||||
|
import Icon from './Icon';
|
||||||
|
import {
|
||||||
|
Body,
|
||||||
|
} from './text';
|
||||||
|
|
||||||
|
const Wrapper = styled.View`
|
||||||
|
flex-direction: row;
|
||||||
|
justify-content: flex-end;
|
||||||
|
align-items: center;
|
||||||
|
border-bottom-width: 1px;
|
||||||
|
border-color: #ccc;
|
||||||
|
padding: 0 10px;
|
||||||
|
`;
|
||||||
|
|
||||||
|
const Item = styled.TouchableOpacity`
|
||||||
|
padding: 10px 10px;
|
||||||
|
opacity: ${({ disabled }) => disabled ? 0.3 : 1};
|
||||||
|
`
|
||||||
|
|
||||||
|
const Toolbar = ({
|
||||||
|
items = [],
|
||||||
|
}) => (
|
||||||
|
<Wrapper>
|
||||||
|
{items.map(({
|
||||||
|
name,
|
||||||
|
icon,
|
||||||
|
onPress,
|
||||||
|
disabled,
|
||||||
|
}) => (
|
||||||
|
<Item
|
||||||
|
key={name}
|
||||||
|
onPress={disabled ? undefined : onPress}
|
||||||
|
disabled={disabled}
|
||||||
|
>
|
||||||
|
{icon ? (
|
||||||
|
<Icon name={icon} />
|
||||||
|
) : (
|
||||||
|
<Body color={disabled ? '#ccc' : undefined}>{name}</Body>
|
||||||
|
)}
|
||||||
|
</Item>
|
||||||
|
))}
|
||||||
|
</Wrapper>
|
||||||
|
)
|
||||||
|
|
||||||
|
export default Toolbar;
|
||||||
58
lib/src/components/data/Storage.js
Normal file
@@ -0,0 +1,58 @@
|
|||||||
|
import { Component } from 'react';
|
||||||
|
import { AsyncStorage } from 'react-native';
|
||||||
|
|
||||||
|
class Storage extends Component {
|
||||||
|
constructor() {
|
||||||
|
super();
|
||||||
|
this.state = {
|
||||||
|
data: {},
|
||||||
|
};
|
||||||
|
this.update = this.update.bind(this);
|
||||||
|
this.removeItem = this.removeItem.bind(this);
|
||||||
|
this.clear = this.clear.bind(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
componentDidMount() {
|
||||||
|
this.update();
|
||||||
|
}
|
||||||
|
|
||||||
|
async removeItem(name) {
|
||||||
|
await AsyncStorage.removeItem(name);
|
||||||
|
await this.update();
|
||||||
|
}
|
||||||
|
|
||||||
|
async clear() {
|
||||||
|
await AsyncStorage.clear();
|
||||||
|
await this.update();
|
||||||
|
}
|
||||||
|
|
||||||
|
async update() {
|
||||||
|
try {
|
||||||
|
const keys = await AsyncStorage.getAllKeys();
|
||||||
|
const values = await Promise.all(keys.map(key => AsyncStorage.getItem(key)));
|
||||||
|
const data = {};
|
||||||
|
for (let i = 0; i < keys.length; i++) {
|
||||||
|
const key = keys[i];
|
||||||
|
const value = values[i];
|
||||||
|
data[key] = value;
|
||||||
|
}
|
||||||
|
this.setState({
|
||||||
|
data,
|
||||||
|
});
|
||||||
|
} catch (err) {
|
||||||
|
console.error(err);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
render() {
|
||||||
|
const {
|
||||||
|
children,
|
||||||
|
} = this.props;
|
||||||
|
const {
|
||||||
|
data,
|
||||||
|
} = this.state;
|
||||||
|
return children(data, this.update, this.removeItem, this.clear);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default Storage;
|
||||||
@@ -1,9 +1,16 @@
|
|||||||
const proxyConsole = window.console;
|
import { AsyncStorage } from 'react-native';
|
||||||
|
|
||||||
|
export const proxyConsole = window.console;
|
||||||
|
|
||||||
class Log {
|
class Log {
|
||||||
constructor() {
|
constructor() {
|
||||||
this.logs = [];
|
this.logs = [];
|
||||||
this.listeners = [];
|
this.listeners = [];
|
||||||
|
this.context = {
|
||||||
|
storage: AsyncStorage,
|
||||||
|
log: (...args) => console.log(...args),
|
||||||
|
clear: this.clear.bind(this),
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
listen(fn) {
|
listen(fn) {
|
||||||
@@ -15,6 +22,11 @@ class Log {
|
|||||||
this.listeners = this.listeners.filter(l => l !== fn);
|
this.listeners = this.listeners.filter(l => l !== fn);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
clear() {
|
||||||
|
this.logs = [];
|
||||||
|
this.listeners.forEach(l => l(this.logs));
|
||||||
|
}
|
||||||
|
|
||||||
log(type, data, keep) {
|
log(type, data, keep) {
|
||||||
const entry = {
|
const entry = {
|
||||||
type,
|
type,
|
||||||
|
|||||||
@@ -16,6 +16,11 @@ class Network {
|
|||||||
this.listeners = this.listeners.filter(l => l !== fn);
|
this.listeners = this.listeners.filter(l => l !== fn);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
clear() {
|
||||||
|
this.requests = [];
|
||||||
|
this.listeners.forEach(l => l(this.requests));
|
||||||
|
}
|
||||||
|
|
||||||
addRequest(request) {
|
addRequest(request) {
|
||||||
this.requests.push({
|
this.requests.push({
|
||||||
id: this.currentId++,
|
id: this.currentId++,
|
||||||
|
|||||||