diff --git a/demo/App.js b/demo/App.js index 6f0189f..4482578 100644 --- a/demo/App.js +++ b/demo/App.js @@ -6,6 +6,7 @@ import { Button, KeyboardAvoidingView, SafeAreaView, + AsyncStorage, } from 'react-native'; import { DevTool, @@ -26,12 +27,18 @@ xhr = new XMLHttpRequest(); xhr.open('GET', 'https://google.com/sdfsfsdfsfdf'); xhr.send(); +AsyncStorage.setItem('a', 'b'); +AsyncStorage.setItem('b', 'c'); +AsyncStorage.setItem('c', 'd'); + const t = new Promise((resolve, reject) => { setTimeout(() => { throw new Error('everything is broken'); }, 1000); }); + + export default class App extends React.Component { render() { return ( diff --git a/lib/package.json b/lib/package.json index 5145711..d23e95d 100644 --- a/lib/package.json +++ b/lib/package.json @@ -1,6 +1,6 @@ { "name": "react-native-debug-console", - "version": "1.0.0-beta.8", + "version": "1.0.0-beta.9", "main": "src/index.js", "repository": { "url": "https://github.com/morten-olsen/react-native-debugger" diff --git a/lib/src/components/DevTool/Console/Input.js b/lib/src/components/DevTool/Console/Input.js index 7de5007..b13daf2 100644 --- a/lib/src/components/DevTool/Console/Input.js +++ b/lib/src/components/DevTool/Console/Input.js @@ -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 = ({ {({ text = '', + history = [], + historyOffset, }, setState) => ( + + setState({ text })} /> )} diff --git a/lib/src/components/DevTool/Console/Output.js b/lib/src/components/DevTool/Console/Output.js index e19a926..badd7c2 100644 --- a/lib/src/components/DevTool/Console/Output.js +++ b/lib/src/components/DevTool/Console/Output.js @@ -73,13 +73,13 @@ const formatData = (data, options) => { /> ); } else { - return {data.toString()}; + return {data.toString()}; } } if (typeof data === 'object') { return } - return {data.toString()}; + return {data.toString()}; } const OutputList = ({ diff --git a/lib/src/components/DevTool/Console/index.js b/lib/src/components/DevTool/Console/index.js index 3a734bc..8509c8a 100644 --- a/lib/src/components/DevTool/Console/index.js +++ b/lib/src/components/DevTool/Console/index.js @@ -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 = ({ {({ logs }) => ( + { + Clipboard.setString(JSON.stringify(logs, null, ' ')); + Alert.alert( + 'Copied to clipboard', + ); + }, + }, { + name: 'Clear', + icon: 'trash', + onPress: () => log.clear(), + }]} + /> diff --git a/lib/src/components/DevTool/Modal.js b/lib/src/components/DevTool/Modal.js index ec654dd..9285255 100644 --- a/lib/src/components/DevTool/Modal.js +++ b/lib/src/components/DevTool/Modal.js @@ -62,10 +62,9 @@ class Events extends Component { behavior="padding" enabled > - - + ))} + + +) + +export default Keys; \ No newline at end of file diff --git a/lib/src/components/DevTool/Storage/Value.js b/lib/src/components/DevTool/Storage/Value.js new file mode 100644 index 0000000..31bd6f9 --- /dev/null +++ b/lib/src/components/DevTool/Storage/Value.js @@ -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, +}) => ( + + + {value} + + +) + +export default Value; \ No newline at end of file diff --git a/lib/src/components/DevTool/Storage/index.js b/lib/src/components/DevTool/Storage/index.js new file mode 100644 index 0000000..eb942a3 --- /dev/null +++ b/lib/src/components/DevTool/Storage/index.js @@ -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 = ({ +}) => ( + + {({ selected }, setState) => ( + + {(data, update, removeItem, clear) => ( + + { + 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), + }]} + /> + setState({ selected: key })} + keys={Object.keys(data)} + /> + {selected && data[selected] && ( + + )} + + )} + + )} + +); + +export default StorageView; diff --git a/lib/src/components/DevTool/Tab.js b/lib/src/components/DevTool/Tab.js index 22a49e3..563574c 100644 --- a/lib/src/components/DevTool/Tab.js +++ b/lib/src/components/DevTool/Tab.js @@ -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, }) => ( {name} ))} + + {onClose && ( + + )} {tabs[active] && tabs[active].view} diff --git a/lib/src/components/DevTool/index.js b/lib/src/components/DevTool/index.js index 3d3b801..ab5b845 100644 --- a/lib/src/components/DevTool/index.js +++ b/lib/src/components/DevTool/index.js @@ -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, }) => ( , + }, { + name: 'Storage', + view: , }]} + onClose={onClose} /> ); diff --git a/lib/src/components/base/Cell.js b/lib/src/components/base/Cell.js index c9e270e..b324720 100644 --- a/lib/src/components/base/Cell.js +++ b/lib/src/components/base/Cell.js @@ -22,7 +22,7 @@ const Row = ({ {left}: - {right} + {right} ) diff --git a/lib/src/components/base/Icon/close.png b/lib/src/components/base/Icon/close.png new file mode 100644 index 0000000..fc32246 Binary files /dev/null and b/lib/src/components/base/Icon/close.png differ diff --git a/lib/src/components/base/Icon/download.png b/lib/src/components/base/Icon/download.png new file mode 100644 index 0000000..7d12d3a Binary files /dev/null and b/lib/src/components/base/Icon/download.png differ diff --git a/lib/src/components/base/Icon/index.js b/lib/src/components/base/Icon/index.js new file mode 100644 index 0000000..27f78f6 --- /dev/null +++ b/lib/src/components/base/Icon/index.js @@ -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 +}) => ( + +); + +export default Icon; diff --git a/lib/src/components/base/Icon/left.png b/lib/src/components/base/Icon/left.png new file mode 100644 index 0000000..c06a73a Binary files /dev/null and b/lib/src/components/base/Icon/left.png differ diff --git a/lib/src/components/base/Icon/play.png b/lib/src/components/base/Icon/play.png new file mode 100644 index 0000000..72e76e1 Binary files /dev/null and b/lib/src/components/base/Icon/play.png differ diff --git a/lib/src/components/base/Icon/reload.png b/lib/src/components/base/Icon/reload.png new file mode 100644 index 0000000..3aca73a Binary files /dev/null and b/lib/src/components/base/Icon/reload.png differ diff --git a/lib/src/components/base/Icon/return.png b/lib/src/components/base/Icon/return.png new file mode 100644 index 0000000..af4009f Binary files /dev/null and b/lib/src/components/base/Icon/return.png differ diff --git a/lib/src/components/base/Icon/right.png b/lib/src/components/base/Icon/right.png new file mode 100644 index 0000000..642bd5c Binary files /dev/null and b/lib/src/components/base/Icon/right.png differ diff --git a/lib/src/components/base/Icon/trash.png b/lib/src/components/base/Icon/trash.png new file mode 100644 index 0000000..ab13d6e Binary files /dev/null and b/lib/src/components/base/Icon/trash.png differ diff --git a/lib/src/components/base/Row.js b/lib/src/components/base/Row.js index 4425630..3aac458 100644 --- a/lib/src/components/base/Row.js +++ b/lib/src/components/base/Row.js @@ -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` diff --git a/lib/src/components/base/Toolbar.js b/lib/src/components/base/Toolbar.js new file mode 100644 index 0000000..845d515 --- /dev/null +++ b/lib/src/components/base/Toolbar.js @@ -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 = [], +}) => ( + + {items.map(({ + name, + icon, + onPress, + disabled, + }) => ( + + {icon ? ( + + ) : ( + {name} + )} + + ))} + +) + +export default Toolbar; \ No newline at end of file diff --git a/lib/src/components/data/Storage.js b/lib/src/components/data/Storage.js new file mode 100644 index 0000000..3de7d0d --- /dev/null +++ b/lib/src/components/data/Storage.js @@ -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; diff --git a/lib/src/log.js b/lib/src/log.js index d2e4f52..29b0521 100644 --- a/lib/src/log.js +++ b/lib/src/log.js @@ -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, diff --git a/lib/src/network.js b/lib/src/network.js index 270719d..b04703d 100644 --- a/lib/src/network.js +++ b/lib/src/network.js @@ -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++,