Skip to content

Instantly share code, notes, and snippets.

@raphaelrk
Last active July 11, 2018 06:18
Show Gist options
  • Save raphaelrk/c2d774ec679f377f4131f624ae6ebc7e to your computer and use it in GitHub Desktop.
Save raphaelrk/c2d774ec679f377f4131f624ae6ebc7e to your computer and use it in GitHub Desktop.
React Native Hacker Mode. Execute JS using your fingertips.
/**
* React Native Hacker Mode
* Raphael Rouvinov-Kats
*
* Screen with a REPL
* Helpful for debugging your app when it's in production
* - See the redux state by typing `return this.props.state`
* - Import your redux actions/dispatch to dispatch redux actions
* - Fetch your push token and figure out why it's not
* registering with your backend ( reason I wrote this! )
* - Etc.
*
* Works by dynamically adding a function to the class and running it
* Only has access to 'this' and not global state, so make sure to
* Add any external functions/classes/modules to `this` in the constructor
*
* Examples:
// alert 'done'
const m = this.modules;
const rn = m.react_native;
rn.Alert.alert('test');
return 'done';
// get and log expo push token
// only works on device
(async () => {
const m = this.modules;
const e = m.expo;
const rn = m.react_native;
const n = e.Notifications;
const g = n.getExpoPushTokenAsync;
this.addRes('token','fetching...');
const token = await g();
this.addRes('token',token);
})();
return '__no_log__';
* */
import React, { Component } from 'react';
import {connect} from "react-redux";
import JSONTree from 'react-native-json-tree'
import * as expo from 'expo';
import * as react from 'react';
import {
FlatList,
Image,
ScrollView,
Text,
TextInput,
View,
Alert,
Animated,
AppState,
AsyncStorage,
CameraRoll,
Clipboard,
DeviceInfo,
Dimensions,
ImagePickerIOS,
InteractionManager,
Keyboard,
Linking,
NetInfo,
PanResponder,
PixelRatio,
Settings,
Share,
StyleSheet,
Systrace,
Vibration,
YellowBox,
Platform,
} from 'react-native';
class HackerMode extends Component {
constructor(props) {
super(props);
this.modules = {
expo,
react,
react_native: {
Alert,
Animated,
AppState,
AsyncStorage,
CameraRoll,
Clipboard,
DeviceInfo,
Dimensions,
ImagePickerIOS,
InteractionManager,
Keyboard,
Linking,
NetInfo,
PanResponder,
PixelRatio,
Settings,
Share,
StyleSheet,
Systrace,
Vibration,
YellowBox,
Platform,
},
};
this.state = {
query: '',
results: [
{ query: 'return this.props.state', res: props.state },
],
};
}
shouldComponentUpdate(nextProps, nextState) {
return nextState !== this.state;
}
onChangeText = (text) => {
// remove fancy apostrophes and quotes
let query = text.replace(/[“”]/g,'"').replace(/[‘’]/g,"'");
this.setState({ query });
};
addRes = (query, res) => {
this.setState({
results: [
{ query, res },
...this.state.results,
],
})
};
blah = async () => {};
run = async () => {
const { query } = this.state;
let res;
// Wrap object literals, otherwise they are expressed as a block
// via http://www.mattzabriskie.com/blog/adventures-with-react-native
let wrap = query.indexOf('{') === 0 && query.indexOf('}') === code.length - 1;
try {
this.blah = Function(query);
res = await this.blah();
} catch(e) {
res = e;
}
if (res === '__no_log__') return;
this.addRes(query, res);
};
renderItem = ({ item }) => (
<View style={{ width: '100%' }}>
<Text style={styles.queryText}>{item.query}</Text>
<JSONTree data={item.res} theme={theme} invertTheme={false}/>
</View>
);
extractKey = (item, idx) => idx;
renderSeparator = () => <View style={styles.separator}/>;
render() {
return (
<View style={styles.container}>
<View style={styles.topBar}>
<TouchableOpacity onPress={this.props.close}>
<Text style={styles.closeIcon}>✕</Text>
</TouchableOpacity>
<TextInput
style={styles.textInput}
underlineColorAndroid="transparent"
placeholder="let a = 5; return a;"
placeholderTextColor="#aaa"
value={this.state.query}
onChangeText={this.onChangeText}
autoCorrect={false}
autoCapitalize={'none'}
multiline
lines={5}
/>
<TouchableOpacity style={styles.runContainer} onPress={this.run}>
<Text style={styles.runText}>Run</Text>
</TouchableOpacity>
</View>
<ScrollView
maximumZoomScale={2}
minimumZoomScale={0}
horizontal
>
<FlatList
onEndReachedThreshold={0.5}
renderItem={this.renderItem}
data={this.state.results}
keyboardShouldPersistTaps="always"
keyExtractor={this.extractKey}
showsVerticalScrollIndicator={false}
ListHeaderComponent={this.renderSeparator}
ListFooterComponent={<View style={{ height: getBottomBarHeight() + 40 }} />}
style={styles.listStyle}
removeClippedSubviews
ItemSeparatorComponent={this.renderSeparator}
/>
</ScrollView>
</View>
);
}
}
const mapStateToProps = (state) => ({ state });
const mapDispatchToProps = dispatch => ({});
export default connect(mapStateToProps, mapDispatchToProps)(HackerMode);
const styles = StyleSheet.create({
container: {
flex: 1,
backgroundColor: '#7d7d7d'
},
topBar: {
paddingVertical: 10,
alignItems: 'center',
flexDirection: 'row',
backgroundColor: '#f7f7f7',
borderBottomColor: '#e0e0e0',
borderBottomWidth: 1,
},
closeIcon: {
marginHorizontal: 6,
color: '#333',
fontWeight: 'bold',
fontSize: 20,
},
textInput: {
backgroundColor: '#fff',
borderRadius: 4,
color: '#000',
flex: 1,
paddingHorizontal: 10,
paddingVertical: 10,
borderColor: '#e0e0e0',
borderWidth: 1,
fontFamily: Platform.OS === 'ios' ? 'Menlo' : 'monospace',
fontSize: 10,
},
runContainer: {
paddingHorizontal: 20,
paddingVertical: 7,
marginHorizontal: 6,
backgroundColor: '#fff',
borderColor: '#e0e0e0',
borderWidth: 1,
},
run: {
color: 'black',
},
listStyle: {
paddingLeft: 10,
paddingTop: 10,
},
separator: {
height: 1,
backgroundColor: 'black',
},
queryText: {
width: '100%',
backgroundColor: '#333',
color: '#fff',
paddingLeft: 10,
paddingVertical: 3,
fontSize: 14,
fontFamily: Platform.OS === 'ios' ? 'Menlo' : 'monospace',
},
});
const getBottomBarHeight = () => ifIphoneX(34, 0);
const theme = {
scheme: 'monokai',
author: 'wimer hazenberg (http://www.monokai.nl)',
base00: '#272822',
base01: '#383830',
base02: '#49483e',
base03: '#75715e',
base04: '#a59f85',
base05: '#f8f8f2',
base06: '#f5f4f1',
base07: '#f9f8f5',
base08: '#f92672',
base09: '#fd971f',
base0A: '#f4bf75',
base0B: '#a6e22e',
base0C: '#a1efe4',
base0D: '#66d9ef',
base0E: '#ae81ff',
base0F: '#cc6633',
nestedNodeLabel: ({ style }, nodeType, expanded) => ({
style: {
...style,
fontFamily: Platform.OS === 'ios' ? 'Menlo' : 'monospace',
}
})
};
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment