Skip to content

Instantly share code, notes, and snippets.

@joshbedo
Created June 1, 2017 22:08
Show Gist options
  • Save joshbedo/8ab11d32553918b2840a8b4c23b94fe8 to your computer and use it in GitHub Desktop.
Save joshbedo/8ab11d32553918b2840a8b4c23b94fe8 to your computer and use it in GitHub Desktop.
React/Redux
/**
* Actions
**/
import {
VEHICLE_MAKE_CHANGED,
VEHICLE_MODEL_CHANGED,
VEHICLE_ADD_REQUEST,
VEHICLE_ADD_SUCCESS,
VEHICLE_ADD_ERROR
} from './types';
import axios from 'axios';
import ApiManager from '../api/manager';
export const addVehicleRequest = () => {
return { type: VEHICLE_ADD_REQUEST };
};
export const addVehicleSuccess = (vehicle) => {
return { type: VEHICLE_ADD_SUCCESS, payload: vehicle };
};
export const addVehicleError = (errors) => {
return { type: VEHICLE_ADD_ERROR, payload: errors };
}
export const addVehicle = (vehicle, router) => {
return (dispatch, getState) => {
const path = ApiManager.buildRoute('addVehicle', getState().user.accessToken);
dispatch(addVehicleRequest());
axios
.post(path, vehicle)
.then(response => {
dispatch(addVehicleSuccess(response.data));
router.pop();
})
.catch(err => {
dispatch(addVehicleError(err.response.data.message));
});
}
};
/**
* Map Component
**/
import React from 'react';
import {
StyleSheet,
Text,
View,
ScrollView,
Image as ImageNative
} from 'react-native';
import { connect } from 'react-redux';
import { Button } from 'react-native-elements';
import { Components } from 'exponent';
import { Container, Content, List, ListItem, Grid, Col, Row, Radio } from 'native-base';
import _ from 'lodash';
import { GooglePlacesAutocomplete } from 'react-native-google-places-autocomplete';
import numeral from 'numeral';
import AddressManager from '../../../api/AddressManager';
import { validateZipCode, setSelectedSavedLocation } from '../../../actions/BookingActions';
import cs from '../../../constants/SharedStyles';
import AddButton from '../../../components/AddButton';
class LocationAdd extends React.Component {
static route = {
navigationBar: {
title: 'Add Location',
backgroundColor: '#fff',
renderRight: (route, props) => <AddButton name='locationAdd' />
},
}
constructor(props) {
super(props);
this.state = { addressFull: 'Address' };
this.state = {
currentPosition: 0
}
}
componentDidUpdate(prevProps, prevState) {
if (this.props.valid && prevProps.lat !== this.props.lat && prevProps.long !== this.props.long) {
this.map.fitToSuppliedMarkers(['location'], true);
}
}
goToSchedule = () => {
if (this.props.valid) {
this.props.navigator.push('schedule');
}
}
parseAddress = (details, skip = false) => {
if (skip === false) {
const result = AddressManager.parse(details);
if (result && result.postal_code) {
this.props.validate(result);
}
} else {
this.props.setAddress(details);
}
}
renderMarker = () => {
if (this.props.valid) {
return (
<Components.MapView.Marker
identifier="location"
coordinate={{
latitude: this.props.lat,
longitude: this.props.long
}}
/>
);
}
}
render = () => {
return (
<View style={cs.container}>
<Components.MapView
style={{ flex: 1 }}
showsCompass={false}
ref={ref => { this.map = ref; }}
initialRegion={{
latitude: 27.9050697,
longitude: -82.675962,
latitudeDelta: 0.0622,
longitudeDelta: 0.0421,
}}
>
<GooglePlacesAutocomplete
placeholder='Enter your address here'
minLength={2} // minimum length of text to search
autoFocus={true}
listViewDisplayed='auto' // true/false/undefined
fetchDetails
enablePoweredByContainer
renderDescription={(row) => row.description} // custom description render
onPress={(data, details = null) => { // 'details' is provided when fetchDetails = true
let skip = false;
if (typeof data.user_id !== 'undefined') {
skip = true;
}
this.parseAddress(details, skip);
}}
getDefaultValue={() => {
return ''; // text input default value
}}
query={{
// available options: https://developers.google.com/places/web-service/autocomplete
key: 'AIzaSyD3QX7tsD1X5UEOgBXQJ4_uekvBQO5Bc3s',
language: 'en', // language of the results
types: 'address', // default: 'geocode'
}}
styles={{
textInput: {
marginLeft: 10,
},
description: {
fontWeight: '500',
},
predefinedPlacesDescription: {
color: '#3498db',
},
textInputContainer: {
backgroundColor: '#fff',
borderTopWidth: 0,
borderBottomWidth:0
},
listView: {
backgroundColor: '#fff',
paddingLeft: 7,
},
container: {
flex: 0,
}
}}
currentLocation={false} // Will add a 'Current location' button at the top of the predefined places list
currentLocationLabel="Current location"
nearbyPlacesAPI='None' // Which API to use: GoogleReverseGeocoding or GooglePlacesSearch
GoogleReverseGeocodingQuery={{}}
// available options for GoogleReverseGeocoding API : https://developers.google.com/maps/documentation/geocoding/intro}
GooglePlacesSearchQuery={{
// available options for GooglePlacesSearch API : https://developers.google.com/places/web-service/search
rankby: 'distance',
types: '(cities)',
}}
filterReverseGeocodingByTypes={['locality', 'administrative_area_level_3']} // filter the reverse geocoding results by types - ['locality', 'administrative_area_level_3'] if you want to display only cities
predefinedPlaces={this.props.locations}
debounce={200} // debounce the requests in ms. Set to 0 to remove debounce. By default 200ms.
/>
{/*}{this.renderMarker()}
<View style={[cs.bottom1, cs.bookingNavigationContainer]}>
<BookingProcessButtonLocation valid={this.props.valid} />
<View style={[cs.ml1, cs.mr1, cs.mb105, cs.mt105]}>
<StepIndicator
customStyles={customStyles}
currentPosition={this.state.currentPosition}
/>
</View>
</View>*/}
</Components.MapView>
</View>
);
}
}
const customStyles = {
stepIndicatorSize: 15,
currentStepIndicatorSize:15,
separatorStrokeWidth: 0.5,
currentStepStrokeWidth: 4,
stepStrokeCurrentColor: '#3498db',
stepStrokeWidth: 4,
stepStrokeFinishedColor: '#3498db',
stepStrokeUnFinishedColor: '#bdc3c7',
separatorFinishedColor: '#3498db',
separatorUnFinishedColor: '#aaaaaa',
stepIndicatorFinishedColor: '#3498db',
stepIndicatorUnFinishedColor: '#fff',
stepIndicatorCurrentColor: '#fff',
stepIndicatorLabelFontSize: 1,
currentStepIndicatorLabelFontSize: 1,
};
const mapStateToProps = state => {
let lat = state.booking.new.address.lat;
let long = state.booking.new.address.long;
if (lat !== '') {
lat = numeral(lat).value();
}
if (long !== '') {
long = numeral(long).value();
}
return {
valid: state.booking.new.address.valid,
lat,
long,
locations: _.toArray(state.location.list)
};
};
const mapDispatchToProps = dispatch => {
return {
validate: (zipcode) => dispatch(validateZipCode(zipcode)),
setAddress: (address) => dispatch(setSelectedSavedLocation(address))
};
};
export default connect(mapStateToProps, mapDispatchToProps)(LocationAdd);
/**
* Another Component using Redux Form
**/
import React from 'react';
import {
ScrollView,
StyleSheet,
Text,
View,
Dimensions,
Picker
} from 'react-native';
import {Field, reduxForm as form} from 'redux-form';
import { connect } from 'react-redux';
import Spinner from 'react-native-loading-spinner-overlay';
import { Grid, Col, Row, List, ListItem, ListGroup, Input } from 'native-base';
import cs from '../../../constants/SharedStyles';
import { update } from '../../../actions/PaymentActions';
import { KeyboardAwareScrollView } from 'react-native-keyboard-aware-scroll-view';
@form({
form: 'paymentEdit',
validate: (values) => {
const errors = {};
if (!values.name) {
errors.name = 'Cardholder Name is required';
}
if (!values.card_number) {
errors.card_number = 'Card Number is required';
}
if (!values.exp_month) {
errors.exp_month = 'Expiration Month is required';
}
if (!values.exp_year) {
errors.exp_year = 'Expiration Year is required';
}
if (!values.cvc) {
errors.cvc = 'CVC is required';
}
return errors;
}
})
@connect((state) => {
return {
isLoading: state.payment.isLoading
}
}, { update })
class PaymentEdit extends React.Component {
updatePayment = (formProps) => {
const router = this.props.navigation.getNavigator('root');
this.props.update(formProps, router);
this.props.reset();
}
render = () => {
const {handleSubmit} = this.props;
return (
<View style={[cs.container]}>
<KeyboardAwareScrollView>
<View>
<Grid style={[cs.mt2]}>
<Row>
<Text style={[cs.ml2, cs.mb1, cs.fw5, cs.color500]}>
Edit Card Information
</Text>
</Row>
</Grid>
<View>
<Field
name="name"
type="text"
component={renderField}
label="Cardholder Name" />
<Field
name="card_number"
type="text"
component={renderField}
label="Card Number #" />
<Field
name="exp_month"
type="text"
component={renderField}
label="Exp Month" />
<Field
name="exp_year"
type="text"
component={renderField}
label="Exp Year" />
<Field
name="cvc"
type="text"
component={renderField}
label="CVC" />
</View>
<Grid style={[cs.mt2, cs.mb2, cs.ml4, cs.mr4, cs.bgP6, cs.radius5, cs.shadow1]} onPress={handleSubmit(this.props.update.bind(this))}>
<Row style={[cs.pl2, cs.pr2]}>
<Col style={[cs.pt105, cs.pb105]}>
<Row style={[cs.selfCenter]}>
<Text style={[cs.color000, cs.f4, cs.fw5]}>
Update Card
</Text>
</Row>
</Col>
</Row>
<Spinner visible={this.props.isLoading} />
</Grid>
</View>
</KeyboardAwareScrollView>
</View>
);
}
}
const renderField = ({input, label, type, meta: {touched, error, warning}}) => {
const renderMessage = () => {
return (touched && error) ? <Text style={[cs.colorP3]}>{error}</Text> : null;
}
return (
<Row style={[cs.pl1, cs.pr1, cs.pt05, cs.pb05, cs.bg000, cs.borderBWidth1, cs.border200]}>
<Input
{...input}
placeholder={label}
style={[cs.flx1, cs.fi5, cs.color800]} />
{renderMessage()}
</Row>
)
};
export default PaymentEdit
/**
* Reducers
**/
import {
PAYMENT_LIST_FETCH_SUCCESS,
PAYMENT_REMOVE_REQUEST,
PAYMENT_REMOVE_SUCCESS,
PAYMENT_REMOVE_ERROR,
PAYMENT_ADD_REQUEST,
PAYMENT_ADD_SUCCESS,
PAYMENT_ADD_ERROR,
PAYMENT_UPDATE_REQUEST,
PAYMENT_UPDATE_SUCCESS,
PAYMENT_UPDATE_ERROR,
} from '../actions/types';
const INITIAL_STATE = {
list: [],
new: {},
isLoading: false,
error: null,
};
export default (state = INITIAL_STATE, action) => {
switch (action.type) {
case PAYMENT_LIST_FETCH_SUCCESS:
return {
...state,
list: action.payload
};
case PAYMENT_UPDATE_REQUEST:
case PAYMENT_ADD_REQUEST:
case PAYMENT_REMOVE_REQUEST:
return { ...state, isLoading: true };
case PAYMENT_REMOVE_SUCCESS:
return { ...state, list: action.payload, isLoading: false };
case PAYMENT_ADD_SUCCESS:
var list = state.list.concat([action.payload]);
return { ...state, list: list, isLoading: false };
case PAYMENT_UPDATE_SUCCESS:
return { ...state, list: action.payload, isLoading: false };
case PAYMENT_UPDATE_ERROR:
case PAYMENT_ADD_ERROR:
case PAYMENT_REMOVE_ERROR:
return { ...state, error: action.payload, isLoading: false };
default:
return state;
}
};
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment