Mostly working user registration

Refactor a bit

Up next: card purchase
This commit is contained in:
Nikolai R Kristiansen committed 2017-10-02 00:28:20 +02:00
1 parent be5c49fa04
commit 2d4d0b2cf4
19 files changed
+223 -71

No files matched your search

+1 -1
View File
@@ -52,7 +52,7 @@ fastlane/report.xml
fastlane/Preview.html
fastlane/screenshots
.env
.env*
*.swp
# Sentry
-1
View File
@@ -1,6 +1,5 @@
EVENT_API_URL=https://studentersamfundet.no
DUSKEN_API_URL=https://galtinn.neuf.no
DUSKEN_PURCHASE_URL=https://galtinn.neuf.no/?utm_source=purchase-button&utm_medium=app
DUSKEN_FORGOT_PASSWORD_URL=https://galtinn.neuf.no/auth/password_reset/
SENTRY_DSN=
STRIPE_PUBLISHABLE_KEY=
+1 -1
View File
@@ -17,7 +17,7 @@
"react-native-confetti": "^0.0.6",
"react-native-config": "^0.6.1",
"react-native-htmlview": "^0.12.0",
"react-native-sentry": "^0.23.2",
"react-native-sentry": "^0.25.0",
"react-navigation": "^1.0.0-beta.11",
"react-redux": "^5.0.6",
"redux": "^3.7.2",
+4
View File
@@ -8,6 +8,8 @@ import EventDetailScreen from "./screens/EventDetailScreen";
import EventListScreen from "./screens/EventListScreen";
import LoginScreen from "./screens/LoginScreen";
import MembershipScreen from "./screens/MembershipScreen";
import UserRegisterScreen from "./screens/UserRegisterScreen";
import ChargeScreen from "./screens/ChargeScreen";
/* Navigation */
@@ -46,6 +48,8 @@ const tabNav = TabNavigator(tabBarRoutes, tabBarOptions);
const DuskenContainer = StackNavigator({
Root: { screen: tabNav },
Login: { screen: LoginScreen },
UserRegister: { screen: UserRegisterScreen },
Charge: { screen: ChargeScreen },
EventDetail: { screen: EventDetailScreen },
});
+1 -1
View File
@@ -161,7 +161,7 @@ export function requestRegisterUser(firstName, lastName, email, phoneNumber, pas
return (dispatch) => {
dispatch(registerUserRequest());
return fetch(`${Config.DUSKEN_API_URL}/api/user/register`, {
return fetch(`${Config.DUSKEN_API_URL}/api/user/register/`, {
method: 'POST',
headers: {
'Accept': 'application/json',
+1
View File
@@ -10,6 +10,7 @@ stripe.init({
});
export default class Charge extends Component {
// TODO: You are here
render() {
return <View>
<Text>In charge view</Text>
+2 -7
View File
@@ -1,8 +1,6 @@
import React, {Component} from 'react';
import {ScrollView, Text, View, Linking, StyleSheet, Platform} from "react-native";
import {Button, Text as NBText} from "native-base";
import Config from 'react-native-config';
export default class Membership extends Component {
render() {
return (
@@ -11,7 +9,7 @@ export default class Membership extends Component {
<Text style={styles.headerTitle}>Hei 👋</Text>
<Text style={styles.paragraph}>Her kan du bli medlem i Det Norske Studentersamfund - Chateau Neuf og ha medlemsbeviset ditt på telefonen</Text>
<View style={styles.button}>
<Button onPress={() => { Linking.openURL(Config.DUSKEN_PURCHASE_URL); } } full><NBText>Bli medlem</NBText></Button>
<Button onPress={this.props.onRegisterPress} full><NBText>Bli medlem</NBText></Button>
</View>
<Text style={styles.header}>Hva får jeg?</Text>
<Text style={[styles.paragraph, {textAlign: 'left', paddingLeft: 4}]}>
@@ -22,16 +20,13 @@ export default class Membership extends Component {
- Mulighet til å delta i landets eldste studentdemokrati</Text>
<Text style={styles.em}>Allerede medlem?</Text>
<View style={styles.button}>
<Button onPress={this.onLoginPress} full><NBText>Logg inn</NBText></Button>
<Button onPress={this.props.onLoginPress} full><NBText>Logg inn</NBText></Button>
</View>
</View>
</ScrollView>
)
};
onLoginPress = () => {
this.props.onLoginPress();
};
}
+13 -4
View File
@@ -2,7 +2,7 @@ import React, {Component} from 'react';
import {connect} from "react-redux";
import Membership from "./Membership";
import MembershipProof from "./MembershipProof";
import Proof from "./Proof";
import { requestUserData, logout } from "../../actions";
class MembershipContainer extends Component {
@@ -42,20 +42,29 @@ class MembershipContainer extends Component {
this.props.onLoginPress();
};
onRegisterPress = () => {
this.props.onRegisterPress();
};
onChargePress = () => {
this.props.onChargePress();
};
onLogoutPress = () => {
this.props.logout();
this.props.logoutNavigate();
this.props.onLogoutPress();
};
render() {
if (this.state.user) {
return <MembershipProof
return <Proof
user={this.state.user}
fetchUser={this.fetchUser}
onLogoutPress={this.onLogoutPress}
onChargePress={this.onChargePress}
/>;
}
return <Membership onLoginPress={this.onLoginPress}/>;
return <Membership onLoginPress={this.onLoginPress} onRegisterPress={this.onRegisterPress} />;
}
}
@@ -1,10 +1,10 @@
import React, {Component} from 'react';
import {Image, Linking, StyleSheet, Platform, RefreshControl, TouchableOpacity, View} from "react-native";
import {Button, Body, Card, CardItem, Text, Content} from 'native-base';
import Config from 'react-native-config';
import Confetti from 'react-native-confetti';
export default class MembershipProof extends Component {
export default class Proof extends Component {
// FIXME: Move logic to container
CONFETTI_TIMEOUT = 15000;
constructor(props) {
@@ -120,9 +120,7 @@ export default class MembershipProof extends Component {
return <CardItem>
<Body>
<Button
onPress={() => {
Linking.openURL(Config.DUSKEN_PURCHASE_URL);
}}
onPress={this.props.onChargePress}
full
style={styles.purchaseButton}
>
+15
View File
@@ -0,0 +1,15 @@
import React, {Component} from 'react';
import {connect} from "react-redux";
import Proof from "./Proof";
class ProofContainer extends Component {
constructor(props) {
super(props);
}
render() {
return <Proof />
}
}
export default connect()(ProofContainer);
@@ -1,25 +0,0 @@
import React, {Component} from 'react';
import {connect} from "react-redux";
import UserRegister from "./UserRegister";
import {requestRegisterUser} from "../../actions";
class UserRegisterContainer extends Component {
onRegisterPress = (firstName, lastName, email, phoneNumber, password) => {
this.props.requestRegisterUser(firstName, lastName, email, phoneNumber, password);
};
render() {
return <UserRegister {...this.props} onRegisterPress={this.onRegisterPress} />;
}
}
function stateToProps(state) {
return {
isRegisteringUser: state.isRegisteringUser,
registerError: state.registerError
};
}
export default connect(
stateToProps,
{requestRegisterUser}
)(UserRegisterContainer);
@@ -14,6 +14,18 @@ export default class UserRegister extends Component {
password: ''
}
}
canSubmitForm() {
/* No empty fields */
for([key, value] of Object.entries(this.state)) {
if(value === '') {
return false;
}
}
return true;
};
emailInput() {
return <Item stackedLabel last>
<Label>E-post</Label>
@@ -51,10 +63,10 @@ export default class UserRegister extends Component {
}
showError = () => {
if (this.props.registerError) {
// TODO: Format these and highlight error fields
if (this.props.registerError && Object.keys(this.props.registerError).length !== 0) {
const err = this.props.registerError.non_field_errors;
let errorFormatted = err ? err[0] : 'Generell feil, prøv igjen...';
// TODO: Format these and highlight error fields
let errorFormatted = err ? err[0] : 'Kunne ikke registrere bruker, prøv igjen...';
return <View style={styles.errorBox}><Text style={styles.errorMessage}>{errorFormatted}</Text></View>
}
else {
@@ -116,13 +128,7 @@ export default class UserRegister extends Component {
</Item>
{this.passwordInput()}
{this.showError()}
<Button
full
onPress={this.onRegisterPress}
style={styles.registerButton}
>
<Text>Registrer</Text>
</Button>
{this.registerbutton()}
{this.showSpinner()}
</Form>
</Content>
@@ -138,6 +144,27 @@ export default class UserRegister extends Component {
this.state.phoneNumber,
this.state.password,
);
};
registerbutton() {
if( this.props.isRegisteringUser || !this.canSubmitForm() ) {
return (
<Button
full
disabled
style={styles.registerButton}
>
<Text>Registrer</Text>
</Button>);
}
return (<Button
full
onPress={this.onRegisterPress}
style={styles.registerButton}
>
<Text>Registrer</Text>
</Button>);
}
}
const styles = StyleSheet.create({
@@ -0,0 +1,50 @@
import React, {Component} from 'react';
import {connect} from "react-redux";
import UserRegister from "./UserRegister";
import {requestRegisterUser} from "../../actions";
class UserRegisterContainer extends Component {
constructor(state) {
super(state);
this.state = {
isAuthenticated: false,
isRegisteringUser: false,
registerError: null
}
}
componentWillReceiveProps(nextProps) {
if (this.state.isAuthenticated !== nextProps.isAuthenticated) {
if (nextProps.isAuthenticated) {
this.props.onRegister();
}
}
if (this.state.isRegisteringUser !== nextProps.isRegisteringUser) {
this.setState({isRegisteringUser: nextProps.isRegisteringUser});
}
if (this.state.registerError !== nextProps.registerError) {
this.setState({registerError: nextProps.registerError});
}
}
onRegisterPress = (firstName, lastName, email, phoneNumber, password) => {
this.props.requestRegisterUser(firstName, lastName, email, phoneNumber, password);
};
render() {
return <UserRegister {...this.state} onRegisterPress={this.onRegisterPress} />;
}
}
function stateToProps(state) {
return {
isAuthenticated: state.isAuthenticated,
isRegisteringUser: state.isRegisteringUser,
registerError: state.registerError
};
}
export default connect(
stateToProps,
{requestRegisterUser}
)(UserRegisterContainer);
+12 -4
View File
@@ -51,7 +51,8 @@ export default function duskenApp(state = initialState, action) {
/* User data */
case USER_DATA_REQUEST:
return Object.assign({}, state, {
isFetchingUserData: true
isFetchingUserData: true,
userError: null
});
case USER_DATA_FAILURE:
@@ -63,13 +64,14 @@ export default function duskenApp(state = initialState, action) {
case USER_DATA_SUCCESS:
return Object.assign({}, state, {
isFetchingUserData: false,
user: action.data
user: action.data,
});
/* Register user */
case REGISTER_USER_REQUEST:
return Object.assign({}, state, {
isRegisteringUser: true
isRegisteringUser: true,
registerError: null,
});
case REGISTER_USER_FAILURE:
@@ -79,9 +81,15 @@ export default function duskenApp(state = initialState, action) {
});
case REGISTER_USER_SUCCESS:
let userData = action.data;
const token = action.data.auth_token;
delete userData['auth_token'];
return Object.assign({}, state, {
isRegisteringUser: false,
user: action.data
isAuthenticated: true,
user: userData,
userToken: token,
});
default:
+30
View File
@@ -0,0 +1,30 @@
import React, { Component } from 'react';
import Charge from "../modules/membership/Charge";
import {StyleProvider} from "native-base";
import getTheme from '../../native-base-theme/components';
export default class ChargeScreen extends Component {
static navigationOptions = {
title: 'Kjøp medlemskap',
headerStyle: {
backgroundColor: '#f58220',
},
headerTitleStyle: {
color: 'white',
},
headerTintColor: 'white',
};
onCharged = () => {
// This works since ChargeScreen is allways navigated to from MembershipScreen
this.props.navigation.goBack();
};
render() {
return (
<StyleProvider style={getTheme()}>
<Charge onCharged={this.onCharged} />
</StyleProvider>
)
}
}
+2 -1
View File
@@ -22,7 +22,8 @@ export default class LoginScreen extends Component {
render() {
return (
<StyleProvider style={getTheme()}><LoginContainer onLogin={this.onLogin} />
<StyleProvider style={getTheme()}>
<LoginContainer onLogin={this.onLogin} />
</StyleProvider>
)
}
+14 -4
View File
@@ -3,7 +3,6 @@ import MembershipContainer from "../modules/membership/MembershipContainer";
import Icon from 'react-native-vector-icons/SimpleLineIcons';
import {StyleProvider} from "native-base";
import getTheme from '../../native-base-theme/components';
import UserRegisterContainer from "../modules/membership/UserRegisterContainer";
export default class MembershipScreen extends Component {
static navigationOptions = {
@@ -20,7 +19,7 @@ export default class MembershipScreen extends Component {
),
};
onLoginPress = () => {
loginNavigate = () => {
this.props.navigation.navigate('Login');
};
@@ -28,11 +27,22 @@ export default class MembershipScreen extends Component {
this.props.navigation.navigate('Membership');
};
registerNavigate = () => {
this.props.navigation.navigate('UserRegister');
};
chargeNavigate = () => {
this.props.navigation.navigate('Charge');
};
render() {
return (
<StyleProvider style={getTheme()}>
<UserRegisterContainer/>
{/*<MembershipContainer onLoginPress={this.onLoginPress} logoutNavigate={this.logoutNavigate} />*/}
<MembershipContainer
onLoginPress={this.loginNavigate}
onLogoutPress={this.logoutNavigate}
onRegisterPress={this.registerNavigate}
onChargePress={this.chargeNavigate} />
</StyleProvider>
)
}
+30
View File
@@ -0,0 +1,30 @@
import React, { Component } from 'react';
import UserRegisterContainer from "../modules/register/UserRegisterContainer";
import {StyleProvider} from "native-base";
import getTheme from '../../native-base-theme/components';
export default class UserRegisterScreen extends Component {
static navigationOptions = {
title: 'Registrer',
headerStyle: {
backgroundColor: '#f58220',
},
headerTitleStyle: {
color: 'white',
},
headerTintColor: 'white',
};
onRegister = () => {
// This works since UserRegisterScreen is allways navigated to from MembershipScreen
this.props.navigation.goBack();
};
render() {
return (
<StyleProvider style={getTheme()}>
<UserRegisterContainer onRegister={this.onRegister} />
</StyleProvider>
)
}
}
+7 -7
View File
@@ -3582,15 +3582,15 @@ react-native-htmlview@^0.12.0:
prop-types "^15.5.10"
react-timer-mixin "^0.13.3"
react-native-sentry@^0.23.2:
version "0.23.2"
resolved "https://registry.yarnpkg.com/react-native-sentry/-/react-native-sentry-0.23.2.tgz#f9859263d5d274faa6d17604deae2adc94568234"
react-native-sentry@^0.25.0:
version "0.25.0"
resolved "https://registry.yarnpkg.com/react-native-sentry/-/react-native-sentry-0.25.0.tgz#f7530aa2fc48d71e0088f808a83d72c356e2abd5"
dependencies:
chalk "^1.1.1"
glob "7.1.1"
inquirer "3.0.6"
raven-js "^3.17.0"
sentry-cli-binary "^1.19.1"
sentry-cli-binary "^1.20.0"
xcode "0.9.3"
react-native-tab-view@^0.0.65:
@@ -4110,9 +4110,9 @@ send@0.13.2:
range-parser "~1.0.3"
statuses "~1.2.1"
sentry-cli-binary@^1.19.1:
version "1.19.1"
resolved "https://registry.yarnpkg.com/sentry-cli-binary/-/sentry-cli-binary-1.19.1.tgz#0abc210ed7f35d7ec0680963e612b15bd2bc14c0"
sentry-cli-binary@^1.20.0:
version "1.20.0"
resolved "https://registry.yarnpkg.com/sentry-cli-binary/-/sentry-cli-binary-1.20.0.tgz#a620145863fde98c398adcc2166cf17c0738d513"
dependencies:
progress "2.0.0"