Rewritten to hooks

This commit is contained in:
2019-07-05 13:38:02 +02:00
parent a17faa9482
commit 41b4d3b115
12 changed files with 278 additions and 393 deletions

View File

@@ -18,7 +18,7 @@ import {
} from 'react-native-debug-console'; } from 'react-native-debug-console';
network.attach(); network.attach();
log.attach(true); log.attach();
context.hello = () => 'earth'; context.hello = () => 'earth';
console.log('fooo'); console.log('fooo');

View File

@@ -1,12 +1,10 @@
import React from 'react'; import React, { useState } from 'react';
import { import {
StyleSheet, StyleSheet,
View, View,
Text,
TextInput, TextInput,
} from 'react-native'; } from 'react-native';
import styled from 'styled-components/native'; import styled from 'styled-components/native';
import State from '../../data/State';
import { context } from '../../../console'; import { context } from '../../../console';
import log from '../../../log'; import log from '../../../log';
import Icon from '../../base/Icon'; import Icon from '../../base/Icon';
@@ -36,77 +34,68 @@ const styles = StyleSheet.create({
const Input = ({ const Input = ({
logs, logs,
}) => ( }) => {
<State> const [text, setText] = useState('');
{({ const [history, setHistory] = useState([]);
text = '', const [historyOffset, setHistoryOffset] = useState();
history = [], return (
historyOffset, <View style={styles.container}>
}, setState) => ( <Button
<View style={styles.container}> onPress={() => {
<Button let currentOffset = typeof historyOffset === 'undefined' ? -1 : historyOffset;
onPress={() => { currentOffset += 1;
let currentOffset = typeof historyOffset === 'undefined' ? -1 : historyOffset; const index = history.length - 1 - currentOffset;
currentOffset += 1; if (history[index]) {
const index = history.length - 1 - currentOffset; setText(history[index]);
if (history[index]) { setHistoryOffset(currentOffset);
setState({ }
text: history[index], }}
historyOffset: currentOffset, >
}); <Icon name="left" />
} </Button>
}} <Button
> title=">"
<Icon name="left" /> onPress={() => {
</Button> let currentOffset = typeof historyOffset === 'undefined' ? -1 : historyOffset;
<Button currentOffset -= 1;
title=">" const index = history.length - 1 - currentOffset;
onPress={() => { if (history[index]) {
let currentOffset = typeof historyOffset === 'undefined' ? -1 : historyOffset; setText(history[index]);
currentOffset -= 1; setHistoryOffset(currentOffset);
const index = history.length - 1 - currentOffset; }
if (history[index]) { }}
setState({ >
text: history[index], <Icon name="right" />
historyOffset: currentOffset, </Button>
}); <TextInput
} multiline
}} placeholder="{your code here}"
> autoCapitalize="none"
<Icon name="right" /> autoCorrect={false}
</Button> style={styles.input}
<TextInput value={text}
multiline onChangeText={text => setText(text)}
placeholder="{your code here}" />
autoCapitalize="none" <Button
autoCorrect={false} onPress={() => {
style={styles.input} const newHistory = [...history, text];
value={text} const contextKeys = Object.keys(context);
onChangeText={text => setState({ text })} const contextValues = Object.values(context);
/> const fn = new Function(...contextKeys, text);
<Button try {
onPress={() => { fn(...contextValues);
const newHistory = [...history, text]; setText('');
const contextKeys = Object.keys(context); setHistoryOffset(undefined);
const contextValues = Object.values(context); setHistory(newHistory);
const fn = new Function(...contextKeys, text); } catch (err) {
try { log.error([err]);
fn(...contextValues); }
setState({ }}
text: '', >
history: newHistory, <Icon name="play" />
historyOffset: undefined, </Button>
}); </View>
} catch (err) { );
log.error([err]); };
}
}}
>
<Icon name="play" />
</Button>
</View>
)}
</State>
);
export default Input; export default Input;

View File

@@ -1,11 +1,10 @@
import React from 'react'; import React, { useState } from 'react';
import { import {
StyleSheet, StyleSheet,
View, View,
} from 'react-native'; } from 'react-native';
import log from '../../../log'; import log from '../../../log';
import Log from '../../data/Log'; import useLog from '../../data/log';
import State from '../../data/State';
import Toolbar, { import Toolbar, {
Button, Button,
Selector, Selector,
@@ -33,42 +32,32 @@ const initFilters = [
const Console = ({ const Console = ({
includeStackTrace, includeStackTrace,
}) => ( }) => {
<Log> const logs = useLog();
{({ logs }) => ( const [filters, setFilters] = useState(initFilters);
<State return (
initState={{ <View style={styles.container}>
filters: initFilters, <Toolbar>
}} <Selector
> name="Filter"
{({ filters }, setState) => ( icon="filter"
<View style={styles.container}> options={filters}
<Toolbar> multiSelect
<Selector onSelect={(selected) => {
name="Filter" setFilters([...selected]);
icon="filter" }}
options={filters} />
multiSelect <Seperator />
onSelect={(selected) => { <Button
setState({ name="Clear"
filters: selected, icon="trash"
}); onPress={() => log.clear()}
}} />
/> </Toolbar>
<Seperator /> <Output filter={filters.filter(f => f.selected).map(f => f.name)} logs={logs} includeStackTrace={includeStackTrace} />
<Button <Input />
name="Clear" </View>
icon="trash" );
onPress={() => log.clear()} };
/>
</Toolbar>
<Output filter={filters.filter(f => f.selected).map(f => f.name)} logs={logs} includeStackTrace={includeStackTrace} />
<Input />
</View>
)}
</State>
)}
</Log>
);
export default Console; export default Console;

View File

@@ -1,11 +1,10 @@
import React from 'react'; import React, { useState } from 'react';
import { import {
StyleSheet, StyleSheet,
View, View,
} from 'react-native'; } from 'react-native';
import network from '../../../network'; import network from '../../../network';
import State from '../../data/State'; import useRequests from '../../data/requests';
import Network from '../../data/Network';
import Toolbar, { import Toolbar, {
Button, Button,
Seperator, Seperator,
@@ -27,36 +26,28 @@ const styles = StyleSheet.create({
}, },
}); });
let i = 0; let i = 0;
const Console = () => ( const Console = () => {
<State> const requests = useRequests();
{({ const [active, setActive] = useState();
active, const selected = active >= 0 ? requests[active] : undefined;
}, setState) => ( return (
<Network> <View style={styles.container}>
{({ requests }) => { <Toolbar>
const selected = active >= 0 ? requests[active] : undefined; <Seperator />
return ( <Button
<View style={styles.container}> name="Clear"
<Toolbar> icon="trash"
<Seperator /> onPress={() => network.clear()}
<Button />
name="Clear" </Toolbar>
icon="trash" <List
onPress={() => network.clear()} selected={selected ? selected.id : undefined}
/> requests={requests}
</Toolbar> onSelect={(i) => setActive(i)}
<List />
selected={selected ? selected.id : undefined} {selected && <Details {...selected} />}
requests={requests} </View>
onSelect={(i) => setState({ active: i })} );
/> }
{selected && <Details {...selected} />}
</View>
);
}}
</Network>
)}
</State>
);
export default Console; export default Console;

View File

@@ -1,7 +1,6 @@
import React from 'react'; import React, { useState } from 'react';
import styled from 'styled-components/native'; import styled from 'styled-components/native';
import Storage from '../../data/Storage'; import useStorage from '../../data/storage';
import State from '../../data/State';
import Toolbar, { import Toolbar, {
Button, Button,
Seperator, Seperator,
@@ -15,44 +14,47 @@ const Wrapper = styled.View`
const StorageView = ({ const StorageView = ({
provider, provider,
}) => ( }) => {
<State> const [selected, setSelected] = useState();
{({ selected }, setState) => ( const {
<Storage provider={provider}> data,
{(data, update, removeItem, clear) => ( update,
<Wrapper> removeItem,
<Toolbar> clear,
<Seperator /> } = useStorage(provider);
<Button return (
name="Refresh" <Wrapper>
icon="reload" <Toolbar>
onPress={update} <Seperator />
/> <Button
<Button name="Refresh"
name="Clear" icon="reload"
icon="trash" onPress={update}
onPress={clear} />
/> <Button
<Button name="Clear"
name="Delete" icon="trash"
icon="remove" onPress={clear}
disabled={!selected} />
onPress={() => removeItem(selected)} <Button
/> name="Delete"
</Toolbar> icon="remove"
<Keys disabled={!selected}
selected={selected} onPress={() => removeItem(selected)}
onSelect={(key) => setState({ selected: key })} />
keys={Object.keys(data)} </Toolbar>
/> <Keys
{selected && data[selected] && ( selected={selected}
<Value value={data[selected]} /> onSelect={(key) => {
)} setSelected(key);
</Wrapper> }}
)} keys={Object.keys(data)}
</Storage> />
)} {selected && data[selected] && (
</State> <Value value={data[selected]} />
); )}
</Wrapper>
);
};
export default StorageView; export default StorageView;

View File

@@ -1,13 +1,11 @@
import React, { Fragment } from 'react'; import React, { Fragment, useState } from 'react';
import styled from 'styled-components/native'; import styled from 'styled-components/native';
import PropTypes from 'prop-types'; import PropTypes from 'prop-types';
import { import {
Text, Text,
StyleSheet, StyleSheet,
TouchableOpacity,
View, View,
} from 'react-native'; } from 'react-native';
import State from '../data/State';
import Icon from '../base/Icon'; import Icon from '../base/Icon';
const styles = StyleSheet.create({ const styles = StyleSheet.create({
@@ -67,52 +65,45 @@ const Console = ({
tabs, tabs,
onClose, onClose,
onDownload, onDownload,
}) => ( }) => {
<View style={styles.container}> const [active, setActive] = useState(0);
<State return (
initState={{ <View style={styles.container}>
active: 0, <Header>
}} <TabScroll horizontal>
> <TabWrapper>
{({ active }, setState) => ( {tabs.map(({ name }, i) => (
<Fragment> <Tab
<Header> key={name}
<TabScroll horizontal> style={active === i ? styles.tabActive : styles.tabInactive}
<TabWrapper> onPress={() => {
{tabs.map(({ name }, i) => ( setActive(i);
<Tab }}
key={name} >
style={active === i ? styles.tabActive : styles.tabInactive} <Text>{name}</Text>
onPress={() => { </Tab>
setState({ active: i }); ))}
}} </TabWrapper>
> </TabScroll>
<Text>{name}</Text> {onDownload && (
</Tab> <Button
))} onPress={onDownload}
</TabWrapper> >
</TabScroll> <Icon name="download" />
{onDownload && ( </Button>
<Button )}
onPress={onDownload} {onClose && (
> <Button
<Icon name="download" /> onPress={onClose}
</Button> >
)} <Icon name="close" />
{onClose && ( </Button>
<Button )}
onPress={onClose} </Header>
> {tabs[active] && tabs[active].view}
<Icon name="close" /> </View>
</Button> );
)} };
</Header>
{tabs[active] && tabs[active].view}
</Fragment>
)}
</State>
</View>
);
Console.propTypes = { Console.propTypes = {
tabs: PropTypes.arrayOf(PropTypes.shape({ tabs: PropTypes.arrayOf(PropTypes.shape({

View File

@@ -1,9 +1,8 @@
import React, { Fragment } from 'react'; import React, { Fragment, useState } from 'react';
import { import {
SafeAreaView, SafeAreaView,
TouchableOpacity, TouchableOpacity,
} from 'react-native'; } from 'react-native';
import State from '../../data/State';
import Button from './Button'; import Button from './Button';
import Row from '../Row'; import Row from '../Row';
import Icon from '../Icon'; import Icon from '../Icon';
@@ -17,14 +16,14 @@ const Selector = ({
onSelect, onSelect,
options = [], options = [],
...others ...others
}) => ( }) => {
<State> const [open, setOpen] = useState(false);
{({ open }, setState) => ( return (
<Fragment> <Fragment>
<Button <Button
{...others} {...others}
onPress={() => { onPress={() => {
setState({ open: true }) setOpen(true);
}} }}
/> />
<Modal <Modal
@@ -32,7 +31,7 @@ const Selector = ({
transparent={false} transparent={false}
visible={!!open} visible={!!open}
onRequestClose={() => { onRequestClose={() => {
setState({ open: false }) setOpen(false);
}} }}
> >
<SafeAreaView <SafeAreaView
@@ -43,7 +42,7 @@ const Selector = ({
name="Close" name="Close"
icon="close" icon="close"
onPress={() => { onPress={() => {
setState({ open: false }) setOpen(false);
}} }}
/> />
{options.map((option) => ( {options.map((option) => (
@@ -69,8 +68,7 @@ const Selector = ({
</SafeAreaView> </SafeAreaView>
</Modal> </Modal>
</Fragment> </Fragment>
)} );
</State> };
);
export default Selector; export default Selector;

View File

@@ -1,38 +1,19 @@
import { Component } from 'react'; import { useEffect, useState } from 'react';
import log from '../../log'; import log from '../../log';
class Log extends Component { const useLog = () => {
constructor() { const [logs, setLogs] = useState([]);
super(); useEffect(() => {
this.state = { const update = (newLogs) => {
logs: [], setLogs([...newLogs]);
}; }
this.setLogs = this.setLogs.bind(this); log.listen(update);
}
componentDidMount() { return () => {
log.listen(this.setLogs); log.unlisten(update);
} }
}, []);
return logs;
};
componentWillUnmount() { export default useLog;
log.unlisten(this.setLogs);
}
setLogs(logs) {
this.setState({
logs,
});
}
render() {
const {
children,
} = this.props;
const component = children(
this.state,
);
return component;
}
}
export default Log;

View File

@@ -1,38 +0,0 @@
import { Component } from 'react';
import network from '../../network';
class Network extends Component {
constructor() {
super();
this.state = {
requests: [],
};
this.setRequests = this.setRequests.bind(this);
}
componentDidMount() {
network.listen(this.setRequests);
}
componentWillUnmount() {
network.unlisten(this.setRequests);
}
setRequests(requests) {
this.setState({
requests,
});
}
render() {
const {
children,
} = this.props;
const component = children(
this.state,
);
return component;
}
}
export default Network;

View File

@@ -1,20 +0,0 @@
import React, { Component } from 'react';
class State extends Component {
constructor(props, ...others) {
super(props, ...others);
this.state = props.initState || {};
this.setState = this.setState.bind(this);
}
render() {
const { children } = this.props;
const component = children(
this.state,
this.setState,
);
return component;
}
}
export default State;

View File

@@ -1,57 +1,40 @@
import { Component } from 'react'; import { useState, useEffect } from 'react';
class Storage extends Component { const useStorage = (provider) => {
constructor() { const [data, setData] = useState({});
super();
this.state = {
data: {},
};
this.update = this.update.bind(this);
this.removeItem = this.removeItem.bind(this);
this.clear = this.clear.bind(this);
}
componentDidMount() { const update = async () => {
this.update(); const keys = await provider.getAllKeys();
} const values = await Promise.all(keys.map(key => provider.getItem(key)));
const data = {};
async removeItem(name) { for (let i = 0; i < keys.length; i++) {
await this.props.provider.removeItem(name); const key = keys[i];
await this.update(); const value = values[i];
} data[key] = value;
async clear() {
await this.props.provider.clear();
await this.update();
}
async update() {
try {
const keys = await this.props.provider.getAllKeys();
const values = await Promise.all(keys.map(key => this.props.provider.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);
} }
setData(data);
};
const removeItem = async (name) => {
await provider.removeItem(name);
await update();
} }
render() { const clear = async () => {
const { await provider.clear();
children, await update();
} = this.props; }
const {
data, useEffect(() => {
} = this.state; update().catch(err => console.error(err));
return children(data, this.update, this.removeItem, this.clear); }, []);
return {
data,
update,
removeItem,
clear,
} }
} }
export default Storage; export default useStorage;

View File

@@ -0,0 +1,19 @@
import { useState, useEffect } from 'react';
import network from '../../network';
const useRequests = () => {
const [requests, setRequests] = useState([]);
useEffect(() => {
const update = (newRequests) => {
setRequests(newRequests);
};
network.listen(update);
return () => {
network.unlisten(update);
};
}, []);
return requests;
};
export default useRequests;