This commit is contained in:
Morten
2018-08-14 10:24:19 +02:00
commit 38b440e2f6
33 changed files with 12188 additions and 0 deletions

View File

@@ -0,0 +1,57 @@
import React from 'react';
import {
StyleSheet,
View,
Text,
Button,
TextInput,
} from 'react-native';
import State from '../../data/State';
import log from '../../../log';
const styles = StyleSheet.create({
container: {
borderColor: '#ccc',
borderTopWidth: 1,
flexDirection: 'row',
},
input: {
flex: 1,
},
});
const Input = ({
logs,
}) => (
<State>
{({
text = '',
}, setState) => (
<View style={styles.container}>
<TextInput
multiline
style={styles.input}
value={text}
onChangeText={text => setState({ text })}
/>
<Button
title="eval"
onPress={() => {
const fn = new Function(text);
try {
const response = eval(text);
log.info(response);
setState({
text: '',
});
} catch (err) {
log.error(err);
}
}}
/>
</View>
)}
</State>
);
export default Input;

View File

@@ -0,0 +1,60 @@
import React from 'react';
import {
StyleSheet,
ScrollView,
View,
Text,
} from 'react-native';
import JSONTree from 'react-native-json-tree'
import prune from './tools';
const styles = StyleSheet.create({
container: {
flex: 1,
},
});
const getCircularReplacer = () => {
const seen = [];
return (key, val) => {
if (val != null && typeof val == "object") {
if (seen.indexOf(val) >= 0) {
return;
}
seen.push(val);
}
return val;
}
};
const formatData = (data) => {
if (typeof data === 'undefined') {
return <Text>undefined</Text>;
}
if (data instanceof Error) {
return <Text>Error {data.toString()} {data.stack.toString()}</Text>;
}
if (typeof data === 'object') {
return <JSONTree data={prune(data, null, ' ')} />
}
return <Text>{data.toString()}</Text>;
}
const Console = ({
logs,
}) => (
<ScrollView style={styles.container}>
<View>
{logs.map((log, i) => (
<View key={i}>
<Text>
{log.type}
</Text>
{formatData(log.data)}
</View>
))}
</View>
</ScrollView>
);
export default Console;

View File

@@ -0,0 +1,28 @@
import React from 'react';
import {
StyleSheet,
View,
Text,
} from 'react-native';
import Log from '../../data/Log';
import Output from './Output';
import Input from './Input';
const styles = StyleSheet.create({
container: {
flex: 1,
},
});
const Console = () => (
<Log>
{({ logs }) => (
<View style={styles.container}>
<Output logs={logs} />
<Input />
</View>
)}
</Log>
);
export default Console;

View File

@@ -0,0 +1,94 @@
var DEFAULT_MAX_DEPTH = 3;
var DEFAULT_ARRAY_MAX_LENGTH = 50;
var seen; // Same variable used for all stringifications
Date.prototype.toPrunedJSON = Date.prototype.toJSON;
String.prototype.toPrunedJSON = String.prototype.toJSON;
var cx = /[\u0000\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/g,
escapable = /[\\\"\x00-\x1f\x7f-\x9f\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/g,
meta = { // table of character substitutions
'\b': '\\b',
'\t': '\\t',
'\n': '\\n',
'\f': '\\f',
'\r': '\\r',
'"' : '\\"',
'\\': '\\\\'
};
function quote(string) {
escapable.lastIndex = 0;
return escapable.test(string) ? '"' + string.replace(escapable, function (a) {
var c = meta[a];
return typeof c === 'string'
? c
: '\\u' + ('0000' + a.charCodeAt(0).toString(16)).slice(-4);
}) + '"' : '"' + string + '"';
}
function str(key, holder, depthDecr, arrayMaxLength) {
var i, // The loop counter.
k, // The member key.
v, // The member value.
length,
partial,
value = holder[key];
if (value && typeof value === 'object' && typeof value.toPrunedJSON === 'function') {
value = value.toPrunedJSON(key);
}
switch (typeof value) {
case 'string':
return quote(value);
case 'number':
return isFinite(value) ? String(value) : 'null';
case 'boolean':
case 'null':
return String(value);
case 'object':
if (!value) {
return 'null';
}
if (depthDecr<=0 || seen.indexOf(value)!==-1) {
return '"-pruned-"';
}
seen.push(value);
partial = [];
if (Object.prototype.toString.apply(value) === '[object Array]') {
length = Math.min(value.length, arrayMaxLength);
for (i = 0; i < length; i += 1) {
partial[i] = str(i, value, depthDecr-1, arrayMaxLength) || 'null';
}
v = partial.length === 0
? '[]'
: '[' + partial.join(',') + ']';
return v;
}
for (k in value) {
if (Object.prototype.hasOwnProperty.call(value, k)) {
try {
v = str(k, value, depthDecr-1, arrayMaxLength);
if (v) partial.push(quote(k) + ':' + v);
} catch (e) {
// this try/catch due to some "Accessing selectionEnd on an input element that cannot have a selection." on Chrome
}
}
}
v = partial.length === 0
? '{}'
: '{' + partial.join(',') + '}';
return v;
}
}
export default function (value, depthDecr, arrayMaxLength) {
seen = [];
depthDecr = depthDecr || DEFAULT_MAX_DEPTH;
arrayMaxLength = arrayMaxLength || DEFAULT_ARRAY_MAX_LENGTH;
const raw = str('', {'': value}, depthDecr, arrayMaxLength);
// return JSON.stringify(JSON.parse(raw.root), null, ' ');
return raw;
};

View File

@@ -0,0 +1,64 @@
import React, { Component, Fragment } from 'react';
import {
Button,
Modal,
} from 'react-native';
import events from '../../events';
import DevTool from './index';
class Events extends Component {
constructor() {
super();
this.state = {
visible: false,
};
this.listen = this.listen.bind(this);
}
componentDidMount() {
events.listen(this.listen);
}
componentWillUnmount() {
events.unlisten(this.listen);
}
listen(type, data) {
if (type === 'SHOW_DEVTOOLS') {
return this.setState({
visible: true,
});
}
if (type === 'HIDE_DEVTOOLS') {
return this.setState({
visible: false,
});
}
}
render() {
return (
<Modal
animationType="slide"
transparent={false}
visible={this.state.visible}
onRequestClose={() => {
}}
>
<Fragment>
<DevTool />
<Button
title="close"
onPress={() => {
events.publish('HIDE_DEVTOOLS');
}}
/>
</Fragment>
</Modal>
)
}
}
export default Events;

View File

@@ -0,0 +1,58 @@
import React from 'react';
import {
StyleSheet,
ScrollView,
View,
Text,
} from 'react-native';
import Tab from '../Tab';
const styles = StyleSheet.create({
container: {
flex: 1,
},
});
const Data = ({
url,
method,
status,
args = [],
}) => (
<ScrollView style={styles.container}>
<View>
<Text>Status: {status}</Text>
<Text>Method: {method}</Text>
<Text>Url: {url}</Text>
<Text>Request Body:</Text>
{args[0] && (
<Text>{args[0].toString()}</Text>
)}
</View>
</ScrollView>
);
const Response = ({
request,
}) => (
<ScrollView style={styles.container}>
<View>
<Text>Response: {request.responseText}</Text>
</View>
</ScrollView>
);
const RequestDetails = (props) => (
<Tab
tabs={[{
name: 'overview',
view: <Data {...props} />
}, {
name: 'response',
view: <Response {...props} />
}]}
/>
);
export default RequestDetails;

View File

@@ -0,0 +1,58 @@
import React from 'react';
import {
StyleSheet,
View,
Text,
ScrollView,
TouchableOpacity,
} from 'react-native';
const styles = StyleSheet.create({
container: {
flex: 1,
borderColor: '#ccc',
borderBottomWidth: 1,
},
row: {
flexDirection: 'row',
padding: 10,
},
method: {
padding: 5,
},
url: {
flex: 1,
padding: 5,
},
status: {
padding: 5,
},
});
const RequestDetails = ({
requests,
onSelect,
}) => (
<ScrollView style={styles.container}>
<View>
{requests.map(({
status,
method,
url,
}, i) => (
<TouchableOpacity
key={i}
onPress={() => onSelect(i)}
>
<View style={styles.row}>
<Text style={styles.method}>{method}</Text>
<Text style={styles.url}>{url}</Text>
<Text style={styles.status}>{status}</Text>
</View>
</TouchableOpacity>
))}
</View>
</ScrollView>
);
export default RequestDetails;

View File

@@ -0,0 +1,50 @@
import React from 'react';
import {
StyleSheet,
View,
Text,
TouchableOpacity,
} from 'react-native';
import State from '../../data/State';
import Network from '../../data/Network';
import Details from './Details';
import List from './List';
const styles = StyleSheet.create({
container: {
flex: 1,
},
list: {
flex: 1,
borderColor: '#ccc',
borderBottomWidth: 1,
},
details: {
flex: 1,
},
});
const Console = () => (
<State>
{({
active,
}, setState) => (
<Network>
{({ requests }) => {
const selected = active >= 0 ? requests[active] : undefined;
return (
<View style={styles.container}>
<List
requests={requests}
onSelect={(i) => setState({ active: i })}
/>
{selected && <Details {...selected} />}
</View>
);
}}
</Network>
)}
</State>
);
export default Console;

View File

@@ -0,0 +1,76 @@
import React from 'react';
import PropTypes from 'prop-types';
import {
Text,
StyleSheet,
TouchableOpacity,
View,
} from 'react-native';
import State from '../data/State';
const styles = StyleSheet.create({
container: {
flex: 1,
backgroundColor: '#fff',
},
tabs: {
flexDirection: 'row',
},
tabInactive: {
flex: 1,
alignItems: 'center',
justifyContent: 'center',
padding: 10,
borderBottomWidth: 1,
borderColor: '#ccc',
},
tabActive: {
flex: 1,
alignItems: 'center',
justifyContent: 'center',
padding: 10,
borderBottomWidth: 4,
borderColor: 'red',
},
});
const Console = ({
tabs,
}) => (
<State
initState={{
active: 0,
}}
>
{({ active }, setState) => (
<View style={styles.container}>
<View style={styles.tabs}>
{tabs.map(({ name }, i) => (
<TouchableOpacity
key={name}
style={active === i ? styles.tabActive : styles.tabInactive}
onPress={() => {
setState({ active: i });
}}
>
<Text>{name}</Text>
</TouchableOpacity>
))}
</View>
{tabs[active] && tabs[active].view}
</View>
)}
</State>
);
Console.propTypes = {
tabs: PropTypes.arrayOf(PropTypes.shape({
name: PropTypes.string,
})),
};
Console.defaultProps = {
tabs: [],
};
export default Console;

View File

@@ -0,0 +1,32 @@
import React from 'react';
import {
StyleSheet,
View,
} from 'react-native';
import Tab from './Tab';
import Console from './Console';
import Requests from './Requests';
const styles = StyleSheet.create({
container: {
flex: 1,
},
});
const DevTool = ({
style,
}) => (
<View style={style || styles.container}>
<Tab
tabs={[{
name: 'console',
view: <Console />,
}, {
name: 'network',
view: <Requests />,
}]}
/>
</View>
);
export default DevTool;