diff --git a/src/Dusken.js b/src/Dusken.js
index aa5c26a..df46362 100644
--- a/src/Dusken.js
+++ b/src/Dusken.js
@@ -9,7 +9,6 @@ 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 */
@@ -49,7 +48,6 @@ const DuskenContainer = StackNavigator({
Root: { screen: tabNav },
Login: { screen: LoginScreen },
UserRegister: { screen: UserRegisterScreen },
- Charge: { screen: ChargeScreen },
EventDetail: { screen: EventDetailScreen },
});
diff --git a/src/actions.js b/src/actions.js
index b2cf622..55f4c85 100644
--- a/src/actions.js
+++ b/src/actions.js
@@ -1,5 +1,3 @@
-import Config from 'react-native-config';
-
const LOGIN_REQUEST = 'LOGIN_REQUEST';
const LOGIN_SUCCESS = 'LOGIN_SUCCESS';
const LOGIN_FAILURE = 'LOGIN_FAILURE';
@@ -10,6 +8,9 @@ const USER_DATA_FAILURE = 'USER_DATA_FAILURE';
const REGISTER_USER_REQUEST = 'REGISTER_USER_REQUEST';
const REGISTER_USER_SUCCESS = 'REGISTER_USER_SUCCESS';
const REGISTER_USER_FAILURE = 'REGISTER_USER_FAILURE';
+const MEMBERSHIP_CHARGE_REQUEST = 'MEMBERSHIP_CHARGE_REQUEST';
+const MEMBERSHIP_CHARGE_SUCCESS = 'MEMBERSHIP_CHARGE_SUCCESS';
+const MEMBERSHIP_CHARGE_FAILURE = 'MEMBERSHIP_CHARGE_FAILURE';
function loginRequest() {
return {
@@ -77,6 +78,25 @@ function registerUserFailure(registerError) {
registerError
}
}
+function membershipChargeRequest() {
+ return {
+ type: MEMBERSHIP_CHARGE_REQUEST
+ }
+}
+
+function membershipChargeSuccess(data) {
+ return {
+ type: MEMBERSHIP_CHARGE_SUCCESS,
+ data
+ }
+}
+
+function membershipChargeFailure(chargeError) {
+ return {
+ type: MEMBERSHIP_CHARGE_FAILURE,
+ chargeError
+ }
+}
export {
LOGIN_REQUEST, loginRequest,
@@ -89,107 +109,7 @@ export {
REGISTER_USER_REQUEST, registerUserRequest,
REGISTER_USER_SUCCESS, registerUserSuccess,
REGISTER_USER_FAILURE, registerUserFailure,
-};
-
-export function requestLogin(username, password) {
- return (dispatch) => {
- // We are now logging in
- dispatch(loginRequest());
- return fetch(`${Config.DUSKEN_API_URL}/auth/obtain-token/`, {
- method: 'POST',
- headers: {
- 'Accept': 'application/json',
- 'Content-Type': 'application/json'
- },
- body: JSON.stringify({
- 'username': username,
- 'password': password
- })
- })
- .then(response => response.json().then(json => ({ json, response })))
- .then(({json, response}) => {
- if (response.ok === false) {
- return Promise.reject(json)
- }
- return json
- })
- .then(
- data => {
- dispatch(loginSuccess(username, data.token))
- },
- data => {
- console.log(data);
- dispatch(loginFailure(data || {'non_field_errors': ['Kunne ikke logge inn, prøv igjen']}))
- }
- )
- }
-}
-
-export function requestUserData(token) {
- return (dispatch) => {
- // We are now fetching user data
- dispatch(userDataRequest());
-
- const apiURL = Config.DUSKEN_API_URL;
- return fetch(`${apiURL}/api/me/`, {
- method: 'GET',
- headers: {
- 'Accept': 'application/json',
- 'Content-Type': 'application/json',
- 'Authorization': 'Token ' + token
- }
- })
- .then(response => response.json().then(json => ({ json, response })))
- .then(({json, response}) => {
- if (response.ok === false) {
- return Promise.reject(json)
- }
- return json
- })
- .then(
- data => {
- dispatch(userDataSuccess(data))
- },
- data => {
- dispatch(userDataFailure('Det funka ikke :-('))
- }
- )
- }
-}
-
-export function requestRegisterUser(firstName, lastName, email, phoneNumber, password) {
- return (dispatch) => {
- dispatch(registerUserRequest());
-
- return fetch(`${Config.DUSKEN_API_URL}/api/user/register/`, {
- method: 'POST',
- headers: {
- 'Accept': 'application/json',
- 'Content-Type': 'application/json',
- },
- body: JSON.stringify({
- 'first_name': firstName,
- 'last_name': lastName,
- 'email': email,
- 'phone_number': phoneNumber,
- 'password': password
- })
- })
- .then(response => response.json().then(json => ({ json, response })))
- .then(({json, response}) => {
- if (response.ok === false) {
- return Promise.reject(json)
- }
- return json
- })
- .then(
- data => {
- dispatch(registerUserSuccess(data))
- },
- data => {
- dispatch(registerUserFailure(data || {'non_field_errors': ['Kunne ikke registrere bruker, prøv igjen']}))
- }
- )
- }
-}
-
+ MEMBERSHIP_CHARGE_REQUEST, membershipChargeRequest,
+ MEMBERSHIP_CHARGE_SUCCESS, membershipChargeSuccess,
+ MEMBERSHIP_CHARGE_FAILURE, membershipChargeFailure,
+};
\ No newline at end of file
diff --git a/src/api.js b/src/api.js
new file mode 100644
index 0000000..133c5ca
--- /dev/null
+++ b/src/api.js
@@ -0,0 +1,146 @@
+import Config from 'react-native-config';
+
+import {
+ loginFailure, loginRequest, loginSuccess,
+ registerUserFailure, registerUserRequest, registerUserSuccess,
+ userDataFailure, userDataRequest, userDataSuccess,
+ membershipChargeFailure, membershipChargeRequest, membershipChargeSuccess,
+} from "./actions";
+
+export function requestLogin(username, password) {
+ return (dispatch) => {
+ // We are now logging in
+ dispatch(loginRequest());
+ return fetch(`${Config.DUSKEN_API_URL}/auth/obtain-token/`, {
+ method: 'POST',
+ headers: {
+ 'Accept': 'application/json',
+ 'Content-Type': 'application/json'
+ },
+ body: JSON.stringify({
+ 'username': username,
+ 'password': password
+ })
+ })
+ .then(response => response.json().then(json => ({ json, response })))
+ .then(({json, response}) => {
+ if (response.ok === false) {
+ return Promise.reject(json)
+ }
+ return json
+ })
+ .then(
+ data => {
+ dispatch(loginSuccess(username, data.token))
+ },
+ data => {
+ console.log(data);
+ dispatch(loginFailure(data || {'non_field_errors': ['Kunne ikke logge inn, prøv igjen']}))
+ }
+ )
+ }
+}
+
+export function requestUserData(auth_token) {
+ return (dispatch) => {
+ // We are now fetching user data
+ dispatch(userDataRequest());
+
+ return fetch(`${Config.DUSKEN_API_URL}/api/me/`, {
+ method: 'GET',
+ headers: {
+ 'Accept': 'application/json',
+ 'Content-Type': 'application/json',
+ 'Authorization': `Token ${auth_token}`
+ }
+ })
+ .then(response => response.json().then(json => ({ json, response })))
+ .then(({json, response}) => {
+ if (response.ok === false) {
+ return Promise.reject(json)
+ }
+ return json
+ })
+ .then(
+ data => {
+ dispatch(userDataSuccess(data))
+ },
+ data => {
+ dispatch(userDataFailure('Det funka ikke :-('))
+ }
+ )
+ }
+}
+
+export function requestRegisterUser(firstName, lastName, email, phoneNumber, password) {
+ return (dispatch) => {
+ dispatch(registerUserRequest());
+
+ return fetch(`${Config.DUSKEN_API_URL}/api/user/register/`, {
+ method: 'POST',
+ headers: {
+ 'Accept': 'application/json',
+ 'Content-Type': 'application/json',
+ },
+ body: JSON.stringify({
+ 'first_name': firstName,
+ 'last_name': lastName,
+ 'email': email,
+ 'phone_number': phoneNumber,
+ 'password': password
+ })
+ })
+ .then(response => response.json().then(json => ({ json, response })))
+ .then(({json, response}) => {
+ if (response.ok === false) {
+ return Promise.reject(json)
+ }
+ return json
+ })
+ .then(
+ data => {
+ dispatch(registerUserSuccess(data))
+ },
+ data => {
+ dispatch(registerUserFailure(data || {'non_field_errors': ['Kunne ikke registrere bruker, prøv igjen']}))
+ }
+ )
+ }
+}
+
+export function requestMembershipCharge(auth_token, stripe_token, membership_type) {
+ return (dispatch) => {
+ dispatch(membershipChargeRequest());
+
+ return fetch(`${Config.DUSKEN_API_URL}/api/membership/charge/`, {
+ method: 'POST',
+ headers: {
+ 'Accept': 'application/json',
+ 'Content-Type': 'application/json',
+ 'Authorization': `Token ${auth_token}`
+ },
+ body: JSON.stringify({
+ 'stripe_token': stripe_token,
+ 'membership_type': membership_type,
+ 'payment_method': 'app',
+ })
+ })
+ .then(response => response.json().then(json => ({ json, response })))
+ .then(({json, response}) => {
+ if (response.ok === false) {
+ return Promise.reject(json)
+ }
+ return json
+ })
+ .then(
+ data => {
+ dispatch(membershipChargeSuccess(data))
+ },
+ data => {
+ dispatch(membershipChargeFailure(data || {'non_field_errors': ['Kunne ikke belaste kort, prøv igjen']}))
+ }
+ )
+ }
+}
+
+
diff --git a/src/modules/login/LoginContainer.js b/src/modules/login/LoginContainer.js
index 55f61cb..0eb40fc 100644
--- a/src/modules/login/LoginContainer.js
+++ b/src/modules/login/LoginContainer.js
@@ -1,7 +1,7 @@
import React, {Component} from 'react';
import {connect} from "react-redux";
import Login from "./Login";
-import {requestLogin} from "../../actions";
+import {requestLogin} from "../../api";
class LoginContainer extends Component {
constructor(state) {
diff --git a/src/modules/membership/Charge.js b/src/modules/membership/Charge.js
deleted file mode 100644
index eda16e2..0000000
--- a/src/modules/membership/Charge.js
+++ /dev/null
@@ -1,27 +0,0 @@
-import stripe from 'tipsi-stripe'
-import React, {Component} from 'react';
-import Config from 'react-native-config';
-import {ActivityIndicator, Text, View} from "react-native";
-// import {ScrollView, Text, View, Linking, StyleSheet, Platform} from "react-native";
-// import {Button, Text as NBText} from "native-base";
-
-stripe.init({
- publishableKey: Config.STRIPE_PUBLISHABLE_KEY,
-});
-
-export default class Charge extends Component {
- // TODO: You are here
- render() {
- return
- In charge view
-
-
- }
-
- openStripe() {
- stripe.createTokenWithCard().then((data) => {
- // returns a token.tokenId which should be saved
-
- })
- }
-}
diff --git a/src/modules/membership/MembershipContainer.js b/src/modules/membership/MembershipContainer.js
index 729d403..9017461 100644
--- a/src/modules/membership/MembershipContainer.js
+++ b/src/modules/membership/MembershipContainer.js
@@ -2,8 +2,9 @@ import React, {Component} from 'react';
import {connect} from "react-redux";
import Membership from "./Membership";
-import Proof from "./Proof";
-import { requestUserData, logout } from "../../actions";
+import { logout } from "../../actions";
+import {requestUserData} from "../../api";
+import ProofContainer from "./ProofContainer";
class MembershipContainer extends Component {
constructor(props) {
@@ -11,9 +12,9 @@ class MembershipContainer extends Component {
this.state = {
isAuthenticated: props.isAuthenticated,
- userToken: props.userToken,
isFetchingUserData: props.isFetchingUserData,
- user: props.user,
+ userToken: props.userToken,
+ lastOrder: props.lastOrder,
}
}
@@ -29,8 +30,12 @@ class MembershipContainer extends Component {
});
}
- if(this.state.user !== nextProps.user) {
- this.setState({user: nextProps.user})
+ if(this.state.lastOrder !== nextProps.lastOrder) {
+ this.setState({lastOrder: nextProps.lastOrder}, () => {
+ if(nextProps.userToken) {
+ this.fetchUser();
+ }
+ });
}
}
@@ -46,22 +51,18 @@ class MembershipContainer extends Component {
this.props.onRegisterPress();
};
- onChargePress = () => {
- this.props.onChargePress();
- };
-
onLogoutPress = () => {
this.props.logout();
this.props.onLogoutPress();
};
render() {
- if (this.state.user) {
- return ;
}
return ;
@@ -74,6 +75,7 @@ function stateToProps(state) {
isFetchingUserData: state.isFetchingUserData,
userToken: state.userToken,
user: state.user,
+ lastOrder: state.lastOrder
};
}
diff --git a/src/modules/membership/Proof.js b/src/modules/membership/Proof.js
index 3894c66..f536e55 100644
--- a/src/modules/membership/Proof.js
+++ b/src/modules/membership/Proof.js
@@ -1,27 +1,18 @@
import React, {Component} from 'react';
-import {Image, Linking, StyleSheet, Platform, RefreshControl, TouchableOpacity, View} from "react-native";
+import {Image, StyleSheet, TouchableOpacity, View} from "react-native";
import {Button, Body, Card, CardItem, Text, Content} from 'native-base';
import Confetti from 'react-native-confetti';
export default class Proof extends Component {
- // FIXME: Move logic to container
CONFETTI_TIMEOUT = 15000;
constructor(props) {
super(props);
this.state = {
- user: props.user,
- refreshing: false,
stopConfetti: false,
}
};
- componentWillReceiveProps(nextProps) {
- if(this.state.user !== nextProps.user) {
- this.setState({user: nextProps.user})
- }
- }
-
componentDidUpdate () {
if (this._confettiView && this.state.stopConfetti) {
this._confettiView.stopConfetti();
@@ -35,26 +26,6 @@ export default class Proof extends Component {
}
}
- // fetchUser = () => {
- // this.props.fetchUser().then(() => {
- // this.setState({refreshing: false});
- // });
- // };
-
- // _onRefresh() {
- // console.log('onRefresh')
- // this.setState({refreshing: true});
- // this.fetchUser()
- // }
-
- // refreshControl() {
- // return (
- // )
- // }
-
onMembershipPress = () => {
this.setState({showConfetti: true}, () => {
if(this._confettiView) {
@@ -67,12 +38,12 @@ export default class Proof extends Component {
};
membershipValidTo() {
- if( !this.state.user.last_membership) {
+ if( !this.props.user.last_membership) {
return;
}
- let validTo = this.state.user.last_membership.end_date;
- if (this.state.user.last_membership.membership_type === 'lifelong') {
+ let validTo = this.props.user.last_membership.end_date;
+ if (this.props.user.last_membership.membership_type === 'lifelong') {
validTo = 'Livsvarig'
}
@@ -87,8 +58,8 @@ export default class Proof extends Component {
}
membershipStatus() {
- if (!this.state.user.is_member) {
- if ( !this.state.user.last_membership ) {
+ if (!this.props.user.is_member) {
+ if ( !this.props.user.last_membership ) {
return (
@@ -101,7 +72,7 @@ export default class Proof extends Component {
}
let statusText = 'Medlem';
- if (this.state.user.is_volunteer) {
+ if (this.props.user.is_volunteer) {
statusText = 'Aktiv';
}
@@ -114,7 +85,7 @@ export default class Proof extends Component {
}
purchaseButton() {
- if (this.state.user.is_member) {
+ if (this.props.user.is_member) {
return;
}
return
@@ -131,14 +102,14 @@ export default class Proof extends Component {
}
confetti() {
- if (this.state.user.is_member) {
+ if (this.props.user.is_member) {
// FIXME: allmost infinite confetti. Are the animations paused when not visible, if not power hog?
return this._confettiView = node} confettiCount={Number.MAX_SAFE_INTEGER} />
}
}
logo() {
- if (!this.state.user.is_member) {
+ if (!this.props.user.is_member) {
return;
}
// FIXME: URL to config
@@ -149,9 +120,6 @@ export default class Proof extends Component {
/>
}
- fetchUser = () => {
- this.props.fetchUser()
- };
render() {
return (
@@ -174,9 +142,9 @@ export default class Proof extends Component {
};
membershipName() {
- let name = `${this.state.user.first_name} ${this.state.user.last_name}`;
+ let name = `${this.props.user.first_name} ${this.props.user.last_name}`;
if(name === ' ') {
- name = this.state.user.email;
+ name = this.props.user.email;
}
return
{name}
diff --git a/src/modules/membership/ProofContainer.js b/src/modules/membership/ProofContainer.js
index 7ca641d..53afa9d 100644
--- a/src/modules/membership/ProofContainer.js
+++ b/src/modules/membership/ProofContainer.js
@@ -1,15 +1,82 @@
import React, {Component} from 'react';
import {connect} from "react-redux";
+import stripe from 'tipsi-stripe'
+import Config from 'react-native-config';
import Proof from "./Proof";
+import {requestMembershipCharge} from "../../api";
+
+
+stripe.init({
+ publishableKey: Config.STRIPE_PUBLISHABLE_KEY,
+});
class ProofContainer extends Component {
constructor(props) {
super(props);
+
+ this.state = {
+ refreshing: false,
+ }
}
+
+ componentWillReceiveProps(nextProps) {
+ if(this.state.user !== nextProps.user) {
+ this.setState({user: nextProps.user})
+ }
+ }
+
render() {
- return
+ return
}
+
+ openStripe = () => {
+ const options = {
+ prefilledInformation: {
+ email: this.props.user.email,
+ },
+ smsAutofillDisabled: true,
+ };
+
+ stripe.paymentRequestWithCardForm(options).then(
+ (token) => {
+ // returns a token.tokenId which should be saved
+ this.props.requestMembershipCharge(
+ this.props.userToken,
+ {id: token.tokenId, email: this.props.user.email},
+ 'standard');
+ },
+ (error) => {
+ // TODO: display error
+ console.log(error);
+ }
+ );
+ };
+
+ // fetchUser = () => {
+ // this.props.fetchUser().then(() => {
+ // this.setState({refreshing: false});
+ // });
+ // };
+
+ // _onRefresh() {
+ // console.log('onRefresh')
+ // this.setState({refreshing: true});
+ // this.fetchUser()
+ // }
+
+ // refreshControl() {
+ // return (
+ // )
+ // }
}
-export default connect()(ProofContainer);
+export default connect(
+ (state) => ({
+ userToken: state.userToken
+ }),
+ {requestMembershipCharge}
+)(ProofContainer);
diff --git a/src/modules/register/UserRegister.js b/src/modules/register/UserRegister.js
index e018f5f..0d89db8 100644
--- a/src/modules/register/UserRegister.js
+++ b/src/modules/register/UserRegister.js
@@ -163,7 +163,7 @@ export default class UserRegister extends Component {
onPress={this.onRegisterPress}
style={styles.registerButton}
>
- Registrer
+ Registrer meg
);
}
}
diff --git a/src/modules/register/UserRegisterContainer.js b/src/modules/register/UserRegisterContainer.js
index ff85fcf..aa6c47a 100644
--- a/src/modules/register/UserRegisterContainer.js
+++ b/src/modules/register/UserRegisterContainer.js
@@ -1,7 +1,7 @@
import React, {Component} from 'react';
import {connect} from "react-redux";
import UserRegister from "./UserRegister";
-import {requestRegisterUser} from "../../actions";
+import {requestRegisterUser} from "../../api";
class UserRegisterContainer extends Component {
diff --git a/src/reducers.js b/src/reducers.js
index 943d4bc..abf1dac 100644
--- a/src/reducers.js
+++ b/src/reducers.js
@@ -8,7 +8,7 @@ import {
USER_DATA_FAILURE,
REGISTER_USER_REQUEST,
REGISTER_USER_FAILURE,
- REGISTER_USER_SUCCESS,
+ REGISTER_USER_SUCCESS, MEMBERSHIP_CHARGE_REQUEST, MEMBERSHIP_CHARGE_FAILURE, MEMBERSHIP_CHARGE_SUCCESS,
} from './actions';
const initialState = {
@@ -16,11 +16,14 @@ const initialState = {
isLoggingIn: false,
isFetchingUserData: false,
isRegisteringUser: false,
+ isChargingMembership: false,
userToken: null,
user: null,
loginError: null,
userError: null,
registerError: null,
+ chargeError: null,
+ lastOrder: null,
};
export default function duskenApp(state = initialState, action) {
@@ -92,6 +95,25 @@ export default function duskenApp(state = initialState, action) {
userToken: token,
});
+ /* User data */
+ case MEMBERSHIP_CHARGE_REQUEST:
+ return Object.assign({}, state, {
+ isChargingMembership: true,
+ chargeError: null
+ });
+
+ case MEMBERSHIP_CHARGE_FAILURE:
+ return Object.assign({}, state, {
+ isChargingMembership: false,
+ chargeError: action.chargeError
+ });
+
+ case MEMBERSHIP_CHARGE_SUCCESS:
+ return Object.assign({}, state, {
+ isChargingMembership: false,
+ lastOrder: action.data,
+ });
+
default:
return state
}
diff --git a/src/screens/ChargeScreen.js b/src/screens/ChargeScreen.js
deleted file mode 100644
index ab9d586..0000000
--- a/src/screens/ChargeScreen.js
+++ /dev/null
@@ -1,30 +0,0 @@
-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 (
-
-
-
- )
- }
-}
\ No newline at end of file
diff --git a/src/screens/MembershipScreen.js b/src/screens/MembershipScreen.js
index e46b6ce..2de3deb 100644
--- a/src/screens/MembershipScreen.js
+++ b/src/screens/MembershipScreen.js
@@ -31,18 +31,13 @@ export default class MembershipScreen extends Component {
this.props.navigation.navigate('UserRegister');
};
- chargeNavigate = () => {
- this.props.navigation.navigate('Charge');
- };
-
render() {
return (
+ onRegisterPress={this.registerNavigate} />
)
}
diff --git a/src/screens/UserRegisterScreen.js b/src/screens/UserRegisterScreen.js
index 315a57d..0b16965 100644
--- a/src/screens/UserRegisterScreen.js
+++ b/src/screens/UserRegisterScreen.js
@@ -5,7 +5,7 @@ import getTheme from '../../native-base-theme/components';
export default class UserRegisterScreen extends Component {
static navigationOptions = {
- title: 'Registrer',
+ title: 'Bli medlem',
headerStyle: {
backgroundColor: '#f58220',
},