Commit everything done until beginning version control

Removed the boilerplate's counter, started a drawer menu, added a
toolbar to access the menu, created the calculator form and state.
This commit is contained in:
Tanner Collin 2017-02-01 22:52:00 -07:00
parent ff17b2a0d3
commit 7f6fc32d74
28 changed files with 607 additions and 94 deletions

View File

@ -66,6 +66,7 @@ import com.android.build.OutputFile
*/
apply from: "../../node_modules/react-native/react.gradle"
apply from: "../../node_modules/react-native-vector-icons/fonts.gradle"
/**
* Set this to true to create two separate APKs instead of one:
@ -129,6 +130,8 @@ dependencies {
compile fileTree(dir: "libs", include: ["*.jar"])
compile "com.android.support:appcompat-v7:23.0.1"
compile "com.facebook.react:react-native:+" // From node_modules
compile project(':react-native-vector-icons')
}
// Run this once to be able to run the application with BUCK

View File

@ -8,6 +8,7 @@ import com.facebook.react.ReactInstanceManager;
import com.facebook.react.ReactNativeHost;
import com.facebook.react.ReactPackage;
import com.facebook.react.shell.MainReactPackage;
import com.oblador.vectoricons.VectorIconsPackage;
import java.util.Arrays;
import java.util.List;
@ -24,6 +25,7 @@ public class MainApplication extends Application implements ReactApplication {
protected List<ReactPackage> getPackages() {
return Arrays.<ReactPackage>asList(
new MainReactPackage()
, new VectorIconsPackage()
);
}
};

View File

@ -3,4 +3,4 @@ distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-2.10-all.zip
distributionUrl=https\://services.gradle.org/distributions/gradle-2.2-all.zip

View File

@ -1,3 +1,6 @@
rootProject.name = 'Counter'
include ':app'
include ':react-native-vector-icons'
project(':react-native-vector-icons').projectDir = new File(rootProject.projectDir, '../node_modules/react-native-vector-icons/android')

View File

@ -1,2 +0,0 @@
export const INCREMENT = 'INCREMENT';
export const DECREMENT = 'DECREMENT';

View File

@ -1,13 +0,0 @@
import * as types from './actionTypes';
export function increment() {
return {
type: types.INCREMENT
};
}
export function decrement() {
return {
type: types.DECREMENT
};
}

View File

@ -1,36 +0,0 @@
import React, {Component} from 'react';
import {StyleSheet, View, Text, TouchableOpacity} from 'react-native';
const styles = StyleSheet.create({
button: {
width: 100,
height: 30,
padding: 10,
backgroundColor: 'lightgray',
alignItems: 'center',
justifyContent: 'center',
margin: 3
}
});
export default class Counter extends Component {
constructor(props) {
super(props);
}
render() {
const { counter, increment, decrement } = this.props;
return (
<View style={{flex: 1, alignItems: 'center', justifyContent: 'center' }}>
<Text>{counter}</Text>
<TouchableOpacity onPress={increment} style={styles.button}>
<Text>up</Text>
</TouchableOpacity>
<TouchableOpacity onPress={decrement} style={styles.button}>
<Text>down</Text>
</TouchableOpacity>
</View>
);
}
}

View File

@ -1,22 +0,0 @@
import * as types from '../actions/actionTypes';
const initialState = {
count: 0
};
export default function counter(state = initialState, action = {}) {
switch (action.type) {
case types.INCREMENT:
return {
...state,
count: state.count + 1
};
case types.DECREMENT:
return {
...state,
count: state.count - 1
};
default:
return state;
}
}

View File

@ -1,5 +0,0 @@
import counter from './counter';
export {
counter
};

View File

@ -1,6 +1,6 @@
'use strict';
import React, { AppRegistry } from 'react-native';
import App from './app/containers/app';
import App from './src/containers/app';
AppRegistry.registerComponent('Counter', () => App);

View File

@ -1,6 +1,6 @@
'use strict';
import React, { AppRegistry } from 'react-native';
import App from './app/containers/app';
import App from './src/containers/app';
AppRegistry.registerComponent('Counter', () => App);

View File

@ -11,6 +11,8 @@
"dependencies": {
"react": "15.3.2",
"react-native": "0.34.0",
"react-native-drawer": "^2.3.0",
"react-native-vector-icons": "^4.0.0",
"react-redux": "4.4.5",
"redux": "3.5.2",
"redux-actions": "0.11.0",

View File

@ -0,0 +1,7 @@
export const UPDATEVALUE = 'UPDATEVALUE';
export const UPDATEMULT = 'UPDATEMULT';
export const MENUOPEN = 'MENUOPEN';
export const MENUCLOSE = 'MENUCLOSE';
export const MAIN = 'MAIN';
export const HELP = 'HELP';
export const SETREF = 'SETREF';

View File

@ -0,0 +1,19 @@
import * as types from './actionTypes';
export function updatevalue(form, index, value) {
return {
type: types.UPDATEVALUE,
form,
index,
value,
};
}
export function updatemult(form, index, mult) {
return {
type: types.UPDATEMULT,
form,
index,
mult,
};
}

View File

@ -0,0 +1,25 @@
import * as types from './actionTypes';
export function menuopen() {
return {
type: types.MENUOPEN
};
}
export function menuclose() {
return {
type: types.MENUCLOSE
};
}
export function main() {
return {
type: types.MAIN
};
}
export function help() {
return {
type: types.HELP
};
}

67
src/components/calc.js Normal file
View File

@ -0,0 +1,67 @@
import React, {Component} from 'react';
import {ScrollView, StyleSheet, Text, TouchableOpacity, View} from 'react-native';
import Resistor from './resistor';
import Section from './section';
const styles = StyleSheet.create({
button: {
width: 100,
height: 30,
padding: 10,
backgroundColor: 'lightgray',
alignItems: 'center',
justifyContent: 'center',
margin: 3
}
});
export default class Calc extends Component {
constructor(props) {
super(props);
}
render() {
const {state} = this.props;
const allValid = state.have.every((x) => x.valid);
const nonEmpty = state.have.some((x) => x.value);
const reciprocalSum = state.have
.filter((x) => x.value)
.reduce((a,b) => a + 1.0/(b.value * b.mult), 0.0);
return (
<ScrollView>
<View style={{padding: 20}}>
<Section>
<Text>
Enter the exact resistance target:
</Text>
<Resistor form="target" />
</Section>
<Section>
<Text>
Overall parallel resistor value:
{allValid && nonEmpty ? ' ' + 1/reciprocalSum : ' ???'}
</Text>
<Text>
Error between this and the target: {0}%
</Text>
<Text>
Next recommended resistor value: {0}
</Text>
</Section>
<Section>
<Text>
Enter the resistors you have in parallel:
</Text>
{state.have.map((x,n) =>
<Resistor form="have" index={n} key={n} />
)}
</Section>
</View>
</ScrollView>
);
}
}

24
src/components/help.js Normal file
View File

@ -0,0 +1,24 @@
import React, {Component} from 'react';
import {StyleSheet, View, Text, TouchableOpacity} from 'react-native';
const styles = StyleSheet.create({
});
export default class Help extends Component {
constructor(props) {
super(props);
}
render() {
//const { counter } = this.props;
return (
<View style={{flex: 1, alignItems: 'center', justifyContent: 'center' }}>
<Text>This is a sample counter app.</Text>
<Text>Press Increase to add 1 to the value.</Text>
<Text>Press Decrease to subtract 1 from the value.</Text>
</View>
);
}
}

29
src/components/menu.js Normal file
View File

@ -0,0 +1,29 @@
import React, {Component} from 'react';
import {StyleSheet, View, Text, TouchableOpacity} from 'react-native';
const styles = StyleSheet.create({
main: {
backgroundColor: '#ffffff',
flex: 1,
alignItems: 'center',
justifyContent: 'center',
},
});
export default class Menu extends Component {
constructor(props) {
super(props);
}
render() {
const { main, help } = this.props;
return (
<View style={styles.main}>
<Text onPress={main}>Calculator</Text>
<Text onPress={help}>Help</Text>
<Text>Link 4</Text>
</View>
);
}
}

124
src/components/resistor.js Normal file
View File

@ -0,0 +1,124 @@
import React, {Component} from 'react';
import {bindActionCreators} from 'redux';
import {connect} from 'react-redux';
import {
Alert,
Picker,
StyleSheet,
View,
Text,
TextInput,
TouchableOpacity,
} from 'react-native';
import * as calcActions from '../actions/calcActions';
const styles = {
resistorinput: {
flex: 1,
height: 40,
maxWidth: 80,
borderWidth: 1,
textAlign: 'right',
},
clearbutton: {
height: 30,
padding: 10,
backgroundColor: 'lightgray',
alignItems: 'center',
justifyContent: 'center',
margin: 3,
},
};
class Resistor extends Component {
constructor(props) {
super(props);
}
render() {
const {data, actions} = this.props;
// Curry the update functions to make them simpler
const updatevalue = (x) => actions.updatevalue(
this.props.form, this.props.index, x
);
const updatemult = (x) => actions.updatemult(
this.props.form, this.props.index, x
);
const clear = () => {
updatevalue('');
updatemult('1');
}
console.log(this);
return (
<View style={{flex: 1, flexDirection: 'row', justifyContent: 'center', margin: 3}}>
<TextInput
value={data.value}
onChangeText={(x) => updatevalue(x)}
style={Object.assign({}, styles.resistorinput, {
borderColor: data.valid ? 'grey' : 'red',
})}
underlineColorAndroid={'transparent'}
autoCorrect={false}
keyboardType={'numeric'}
/>
<Picker
ref={(x) => this._picker = x}
selectedValue={data.mult}
onValueChange={(x) => updatemult(x)}
style={{height: 40, width: 100}}
mode={'dropdown'}
>
<Picker.Item label=" MΩ" value="1000000" />
<Picker.Item label=" kΩ" value="1000" />
<Picker.Item label=" Ω" value="1" />
<Picker.Item label=" mΩ" value="0.001" />
</Picker>
<TouchableOpacity onPress={() => {
console.log(this);
Alert.alert(
'Clear resistor value?',
data.value +
// I created this kludge for fun (so don't hate on me):
this._picker.props.children
.map((x) => x.props)
.filter((x) => x.value == data.mult)[0]
.label,
[
{text: 'CANCEL'},
{text: 'OK', onPress: clear},
]
)
}} style={styles.clearbutton} activeOpacity={1}
>
<Text>Clear</Text>
</TouchableOpacity>
</View>
);
}
}
//Redux stuff
function mapStateToProps(state, ownProps) {
if (ownProps.form == "target") {
return {
data: state.calc.target,
};
} else if (ownProps.form == "have") {
return {
data: state.calc.have[ownProps.index],
};
} else {
return {};
}
}
export default connect(mapStateToProps,
(dispatch) => ({
actions: bindActionCreators(calcActions, dispatch)
})
)(Resistor);

18
src/components/section.js Normal file
View File

@ -0,0 +1,18 @@
import React, {Component} from 'react';
import {View} from 'react-native';
export default class Section extends Component {
constructor(props) {
super(props);
}
render() {
return (
<View style={{marginBottom: 15}}>
{this.props.children}
</View>
);
}
}

View File

@ -1,10 +1,13 @@
import React, {Component} from 'react';
import { StyleSheet } from 'react-native';
import Ionicons from 'react-native-vector-icons/Ionicons';
import { createStore, applyMiddleware, combineReducers } from 'redux';
import { Provider } from 'react-redux';
import thunk from 'redux-thunk';
import Drawer from 'react-native-drawer';
import * as reducers from '../reducers';
import CounterApp from './counterApp';
import Nav from './nav';
const createStoreWithMiddleware = applyMiddleware(thunk)(createStore);
const reducer = combineReducers(reducers);
@ -14,7 +17,7 @@ export default class App extends Component {
render() {
return (
<Provider store={store}>
<CounterApp />
<Nav />
</Provider>
);
}

24
src/containers/calcApp.js Normal file
View File

@ -0,0 +1,24 @@
'use strict';
import React, {Component} from 'react';
import Calc from '../components/calc';
import {connect} from 'react-redux';
class CalcApp extends Component {
constructor(props) {
super(props);
}
render() {
const {state} = this.props;
console.log(this);
return (
<Calc state={state}/>
);
}
}
export default connect(state => ({
state: state.calc
}))(CalcApp);

View File

@ -2,14 +2,10 @@
import React, {Component} from 'react';
import {bindActionCreators} from 'redux';
import Counter from '../components/counter';
import * as counterActions from '../actions/counterActions';
import Help from '../components/help';
import { connect } from 'react-redux';
// @connect(state => ({
// state: state.counter
// }))
class CounterApp extends Component {
class HelpApp extends Component {
constructor(props) {
super(props);
}
@ -17,17 +13,15 @@ class CounterApp extends Component {
render() {
const { state, actions } = this.props;
return (
<Counter
counter={state.count}
<Help
{...actions} />
);
}
}
export default connect(state => ({
state: state.counter
}),
(dispatch) => ({
actions: bindActionCreators(counterActions, dispatch)
})
)(CounterApp);
)(HelpApp);

30
src/containers/menuApp.js Normal file
View File

@ -0,0 +1,30 @@
'use strict';
import React, {Component} from 'react';
import {bindActionCreators} from 'redux';
import Menu from '../components/menu';
import * as menuActions from '../actions/menuActions';
import { connect } from 'react-redux';
class MenuApp extends Component {
constructor(props) {
super(props);
}
render() {
const { state, actions } = this.props;
return (
<Menu {...actions} />
);
}
}
export default connect(state => ({
state: state
}),
(dispatch) => ({
actions: bindActionCreators(menuActions, dispatch)
})
)(MenuApp);

70
src/containers/nav.js Normal file
View File

@ -0,0 +1,70 @@
'use strict';
import React, {Component} from 'react';
import {View, StyleSheet} from 'react-native';
import {bindActionCreators} from 'redux';
import {connect} from 'react-redux';
import Ionicons from 'react-native-vector-icons/Ionicons';
import Drawer from 'react-native-drawer';
import * as menuActions from '../actions/menuActions';
import MenuApp from '../containers/menuApp';
class Nav extends Component {
constructor(props) {
super(props);
}
render() {
const { state, actions } = this.props;
const drawerStyles = {
drawer: { shadowColor: '#000000', shadowOpacity: 0.8, shadowRadius: 3},
main: {paddingLeft: 3},
}
const styles = StyleSheet.create({
toolbar: {
backgroundColor: '#e9eaed',
height: 56,
},
});
return (
<Drawer
open={state.isOpen}
type="overlay"
content={<MenuApp />}
tapToClose={true}
openDrawerOffset={0.3}
captureGestures={false}
panCloseMask={0.3}
closedDrawerOffset={-3}
styles={drawerStyles}
tweenHandler={(ratio) => ({
main: { opacity: 1, },
mainOverlay: { opacity: ratio/2, backgroundColor: 'black', },
})}
>
<Ionicons.ToolbarAndroid
navIconName={"md-menu"}
onIconClicked={actions.menuopen}
style={styles.toolbar}
subtitle={state.subtitle}
title="Exact Resistor Calculator"
/>
<View style={{flex: 1}}>
{state.page}
</View>
</Drawer>
);
}
}
export default connect(state => ({
state: state.menu
}),
(dispatch) => ({
actions: bindActionCreators(menuActions, dispatch)
})
)(Nav);

97
src/reducers/calc.js Normal file
View File

@ -0,0 +1,97 @@
import * as types from '../actions/actionTypes';
const initialState = {
target: {
value: '',
mult: '1',
valid: true,
},
have: [
{
value: '',
mult: '1',
valid: true,
},
{
value: '',
mult: '1',
valid: true,
},
{
value: '',
mult: '1',
valid: true,
},
],
};
function isPosReal(n) {
return n == '' || // Allowed to be blank
!isNaN(parseFloat(n)) && // Has to be real
isFinite(n) // Can't be infinite
&& parseFloat(n) > 0; // Must be positive, non-zero
}
export default function calc(state = initialState, action = {}) {
const {type, value, form, index, mult} = action;
switch (type) {
case types.UPDATEVALUE:
const valid = isPosReal(value);
if (form == "target") {
return {
...state,
target: {
...state.target,
value: value,
valid: valid,
}
};
} else if (form == "have") {
const newState = {
...state,
have: state.have.map((x,n) =>
n == index ? {
...x,
value: value,
valid: valid,
} : x
),
};
// If all values are full, add another resistor
if (newState.have.every((x) => x.value)) {
newState.have.push({
value: '',
mult: '1',
valid: true,
});
}
return newState;
}
case types.UPDATEMULT:
if (form == "target") {
return {
...state,
target: {
...state.target,
mult: mult,
}
};
} else if (form == "have") {
return {
...state,
have: state.have.map((x,n) =>
n == index ? {
...x,
mult: mult,
} : x
),
};
}
default:
return state;
}
}

7
src/reducers/index.js Normal file
View File

@ -0,0 +1,7 @@
import calc from './calc';
import menu from './menu';
export {
calc,
menu,
};

43
src/reducers/menu.js Normal file
View File

@ -0,0 +1,43 @@
import React, {Component} from 'react';
import * as types from '../actions/actionTypes';
import CalcApp from '../containers/calcApp';
import HelpApp from '../containers/helpApp';
const initialState = {
isOpen: false,
refdrawer: null,
page: <CalcApp />,
subtitle: 'Calculator Page',
};
export default function menu(state = initialState, action = {}) {
switch (action.type) {
case types.MENUOPEN:
return {
...state,
isOpen: true,
};
case types.MENUCLOSE:
return {
...state,
isOpen: false,
};
case types.MAIN:
return {
...state,
isOpen: false,
page: <CalcApp />,
subtitle: 'Calculator Page',
};
case types.HELP:
return {
...state,
isOpen: false,
page: <HelpApp />,
subtitle: 'Help',
};
default:
return state;
}
}