mirror of
https://github.com/morten-olsen/react-native-debug-console.git
synced 2026-02-08 00:36:26 +01:00
Added storage view, and updated the UI in general
This commit is contained in:
@@ -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>
|
||||
|
||||
@@ -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 = ({
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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');
|
||||
}}
|
||||
/>
|
||||
|
||||
@@ -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)]}
|
||||
/>
|
||||
);
|
||||
|
||||
|
||||
@@ -8,6 +8,8 @@ import {
|
||||
|
||||
const ScrollView = styled.ScrollView`
|
||||
flex: 1;
|
||||
border-bottom-width: 1px;
|
||||
border-color: #ccc;
|
||||
`;
|
||||
|
||||
const Wrapper = styled.View`
|
||||
|
||||
@@ -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}
|
||||
|
||||
41
lib/src/components/DevTool/Storage/Keys.js
Normal file
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
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
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 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>
|
||||
|
||||
@@ -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>
|
||||
);
|
||||
|
||||
Reference in New Issue
Block a user