Added storage view, and updated the UI in general
@@ -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
@@ -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 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>
|
||||
);
|
||||
|
||||
@@ -22,7 +22,7 @@ const Row = ({
|
||||
<Left>
|
||||
<Emphasis>{left}:</Emphasis>
|
||||
</Left>
|
||||
<Body>{right}</Body>
|
||||
<Body selectable={true}>{right}</Body>
|
||||
</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;
|
||||
padding: 10px;
|
||||
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`
|
||||
|
||||
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 {
|
||||
constructor() {
|
||||
this.logs = [];
|
||||
this.listeners = [];
|
||||
this.context = {
|
||||
storage: AsyncStorage,
|
||||
log: (...args) => console.log(...args),
|
||||
clear: this.clear.bind(this),
|
||||
};
|
||||
}
|
||||
|
||||
listen(fn) {
|
||||
@@ -15,6 +22,11 @@ class Log {
|
||||
this.listeners = this.listeners.filter(l => l !== fn);
|
||||
}
|
||||
|
||||
clear() {
|
||||
this.logs = [];
|
||||
this.listeners.forEach(l => l(this.logs));
|
||||
}
|
||||
|
||||
log(type, data, keep) {
|
||||
const entry = {
|
||||
type,
|
||||
|
||||
@@ -16,6 +16,11 @@ class Network {
|
||||
this.listeners = this.listeners.filter(l => l !== fn);
|
||||
}
|
||||
|
||||
clear() {
|
||||
this.requests = [];
|
||||
this.listeners.forEach(l => l(this.requests));
|
||||
}
|
||||
|
||||
addRequest(request) {
|
||||
this.requests.push({
|
||||
id: this.currentId++,
|
||||
|
||||