Login: Misc Improvements
Restyle with NativeBase components Use DUSKEN_API_URL Move redux handling stuff to LoginContainer Add redux-logger
This commit is contained in:
1 parent
aae1e982ed
commit
1c42c0c925
12 files changed
+163
-98
No files matched your search
+2
-1
@@ -1 +1,2 @@
|
||||
EVENT_API_BASE_URL=https://studentersamfundet.no
|
||||
EVENT_API_URL=https://studentersamfundet.no
|
||||
DUSKEN_API_URL=https://galtinn.neuf.no
|
||||
@@ -18,6 +18,7 @@
|
||||
"react-navigation": "^1.0.0-beta.11",
|
||||
"react-redux": "^5.0.5",
|
||||
"redux": "^3.7.2",
|
||||
"redux-logger": "^3.0.6",
|
||||
"redux-thunk": "^2.2.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
|
||||
+2
-1
@@ -2,10 +2,11 @@ import Dusken from "./Dusken";
|
||||
import React, {Component} from 'react';
|
||||
import { Provider } from 'react-redux';
|
||||
import { createStore, applyMiddleware } from 'redux';
|
||||
import logger from 'redux-logger';
|
||||
import thunk from 'redux-thunk';
|
||||
import duskenApp from "./reducers";
|
||||
|
||||
let store = createStore(duskenApp, applyMiddleware(thunk));
|
||||
let store = createStore(duskenApp, applyMiddleware(...[logger, thunk]));
|
||||
|
||||
export default class App extends Component {
|
||||
render() {
|
||||
|
||||
@@ -7,9 +7,11 @@ import EventsScreen from "./screens/EventScreen";
|
||||
import EventDetailsScreen from "./screens/EventDetailsScreen";
|
||||
import AboutScreen from "./screens/AboutScreen";
|
||||
import MembershipScreen from "./screens/MembershipScreen";
|
||||
import Login from "./modules/login/Login";
|
||||
|
||||
|
||||
const tabBarRoutes = {
|
||||
// Login: { screen: LoginScreen },
|
||||
Events: { screen: EventsScreen },
|
||||
Membership: { screen: MembershipScreen },
|
||||
About: { screen: AboutScreen },
|
||||
|
||||
+12
-8
@@ -1,3 +1,5 @@
|
||||
import Config from 'react-native-config';
|
||||
|
||||
const LOGIN_REQUEST = 'LOGIN_REQUEST';
|
||||
const LOGIN_SUCCESS = 'LOGIN_SUCCESS';
|
||||
const LOGIN_FAILURE = 'LOGIN_FAILURE';
|
||||
@@ -20,10 +22,10 @@ function loginSuccess(username, token) {
|
||||
}
|
||||
}
|
||||
|
||||
function loginFailure(errorMessage) {
|
||||
function loginFailure(loginError) {
|
||||
return {
|
||||
type: LOGIN_FAILURE,
|
||||
errorMessage
|
||||
loginError
|
||||
}
|
||||
}
|
||||
|
||||
@@ -66,9 +68,9 @@ export {
|
||||
export function requestLogin(username, password) {
|
||||
return (dispatch) => {
|
||||
// We are now logging in
|
||||
dispatch(loginRequest())
|
||||
|
||||
return fetch('http://10.0.0.10:3000/auth/obtain-token/', {
|
||||
dispatch(loginRequest());
|
||||
const apiURL = Config.DUSKEN_API_URL;
|
||||
return fetch(`${apiURL}/auth/obtain-token/`, {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Accept': 'application/json',
|
||||
@@ -91,7 +93,8 @@ export function requestLogin(username, password) {
|
||||
dispatch(loginSuccess(username, data.token))
|
||||
},
|
||||
data => {
|
||||
dispatch(loginFailure(data.error || 'Log in failed'))
|
||||
console.log(data);
|
||||
dispatch(loginFailure(data || {'non_field_errors': ['Kunne ikke logge inn, prøv igjen']}))
|
||||
}
|
||||
)
|
||||
}
|
||||
@@ -100,9 +103,10 @@ export function requestLogin(username, password) {
|
||||
export function requestUserData(token) {
|
||||
return (dispatch) => {
|
||||
// We are now fetching user data
|
||||
dispatch(userDataRequest())
|
||||
dispatch(userDataRequest());
|
||||
|
||||
return fetch('http://10.0.0.10:3000/api/me/', {
|
||||
const apiURL = Config.DUSKEN_API_URL;
|
||||
return fetch(`${apiURL}/api/me/`, {
|
||||
method: 'GET',
|
||||
headers: {
|
||||
'Accept': 'application/json',
|
||||
|
||||
@@ -2,9 +2,9 @@ import {connect} from "react-redux";
|
||||
import EventList from "./EventList";
|
||||
|
||||
import React from 'react';
|
||||
import Config from 'react-native-config';
|
||||
import moment from "moment";
|
||||
import 'moment/locale/nb';
|
||||
import Config from 'react-native-config'
|
||||
|
||||
moment.locale('nb');
|
||||
|
||||
@@ -74,7 +74,7 @@ class EventListContainer extends React.Component {
|
||||
|
||||
fetchEvents() {
|
||||
const { page } = this.state;
|
||||
const url = `${Config.EVENT_API_BASE_URL}/wp-json/wp/v2/events?page=${page}&future=1`;
|
||||
const url = `${Config.EVENT_API_URL}/wp-json/wp/v2/events?page=${page}&future=1`;
|
||||
this.setState({ loading: true });
|
||||
|
||||
fetch(url)
|
||||
|
||||
+84
-69
@@ -1,87 +1,104 @@
|
||||
import React, { Component } from 'react';
|
||||
import {Button, Image, StyleSheet, Text, TextInput, View} from 'react-native';
|
||||
import { Spinner } from 'native-base';
|
||||
|
||||
// import { login } from "../../actions";
|
||||
import {StyleSheet, View} from 'react-native';
|
||||
import { Container, Header, Content, Form, Item, Input, Label, Spinner, Button, Text as NBText, Icon} from 'native-base';
|
||||
|
||||
export default class Login extends Component {
|
||||
constructor(props) {
|
||||
super(props);
|
||||
this.state = {
|
||||
isAuthenticated: false,
|
||||
isLoggingIn: false,
|
||||
errorMessage: null,
|
||||
email: '',
|
||||
password: ''
|
||||
}
|
||||
}
|
||||
|
||||
componentWillReceiveProps(nextProps) {
|
||||
if(this.state.isAuthenticated !== nextProps.isAuthenticated) {
|
||||
this.setState({
|
||||
isAuthenticated: nextProps.isAuthenticated
|
||||
})
|
||||
if (nextProps.isAuthenticated) {
|
||||
this.onLogin();
|
||||
}
|
||||
}
|
||||
if(this.state.isLoggingIn !== nextProps.isLoggingIn) {
|
||||
this.setState({
|
||||
isLoggingIn: nextProps.isLoggingIn
|
||||
})
|
||||
}
|
||||
if(this.state.errorMessage !== nextProps.errorMessage) {
|
||||
this.setState({
|
||||
errorMessage: nextProps.errorMessage
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
render() {
|
||||
const pic = {
|
||||
uri: 'https://dusken.neuf.no/static/dist/images/logo.png'
|
||||
};
|
||||
return (
|
||||
<View style={styles.container}>
|
||||
<Text style={styles.text}>Det Norske Studentersamfund</Text>
|
||||
<Text style={styles.text}>Chateau Neuf</Text>
|
||||
<Image source={pic} style={{width: 200, height: 150, marginBottom: 40}} resizeMode='contain' />
|
||||
<TextInput
|
||||
placeholder="E-post"
|
||||
style={styles.input}
|
||||
emailInput() {
|
||||
if(this.props.loginError && this.props.loginError.username) {
|
||||
return <Item floatingLabel error>
|
||||
<Label>E-post / Brukernavn</Label>
|
||||
<Input
|
||||
keyboardType="email-address"
|
||||
autoCapitalize="none"
|
||||
returnKeyType="next"
|
||||
onChangeText={this.handleEmail}
|
||||
onSubmitEditing={(event) => {
|
||||
this.refs.passwordInput.focus();
|
||||
this.passwordInputEl._root.focus();
|
||||
}}
|
||||
/>
|
||||
<TextInput
|
||||
placeholder="Passord"
|
||||
style={styles.input}
|
||||
</Item>;
|
||||
}
|
||||
|
||||
return <Item floatingLabel>
|
||||
<Label>E-post / Brukernavn</Label>
|
||||
<Input
|
||||
keyboardType="email-address"
|
||||
autoCapitalize="none"
|
||||
returnKeyType="next"
|
||||
onChangeText={this.handleEmail}
|
||||
onSubmitEditing={(event) => {
|
||||
this.passwordInputEl._root.focus();
|
||||
}}
|
||||
/>
|
||||
</Item>;
|
||||
}
|
||||
|
||||
passwordInput() {
|
||||
if(this.props.loginError && this.props.loginError.password) {
|
||||
return <Item floatingLabel last error>
|
||||
<Label>Passord</Label>
|
||||
<Input
|
||||
secureTextEntry={true}
|
||||
autoCapitalize="none"
|
||||
ref='passwordInput'
|
||||
getRef={(input) => { this.passwordInputEl = input; }}
|
||||
onChangeText={this.handlePassword}
|
||||
onSubmitEditing={this.onLoginPress}
|
||||
/>
|
||||
<View style={styles.button}>
|
||||
<Button
|
||||
title="Logg inn"
|
||||
onPress={this.onLoginPress}
|
||||
/>
|
||||
</View>
|
||||
{this.spinIfLoggingIn}
|
||||
</View>
|
||||
</Item>
|
||||
}
|
||||
return <Item floatingLabel last>
|
||||
<Label>Passord</Label>
|
||||
<Input
|
||||
secureTextEntry={true}
|
||||
autoCapitalize="none"
|
||||
getRef={(input) => { this.passwordInputEl = input; }}
|
||||
onChangeText={this.handlePassword}
|
||||
onSubmitEditing={this.onLoginPress}
|
||||
/>
|
||||
</Item>
|
||||
}
|
||||
|
||||
render() {
|
||||
return (
|
||||
<Container style={styles.container}>
|
||||
<Content>
|
||||
<Form>
|
||||
{this.emailInput()}
|
||||
{this.passwordInput()}
|
||||
{this.showError()}
|
||||
<Button block onPress={this.onLoginPress} style={styles.loginButton}><NBText>Logg inn</NBText></Button>
|
||||
{this.showSpinner()}
|
||||
</Form>
|
||||
</Content>
|
||||
</Container>
|
||||
);
|
||||
}
|
||||
|
||||
spinIfLoggingIn = () => {
|
||||
if (this.state.isLoggingIn) {
|
||||
return <Spinner color="#f58220"></Spinner>
|
||||
showError= () => {
|
||||
if (this.props.loginError) {
|
||||
const err = this.props.loginError.non_field_errors;
|
||||
let errorFormatted = err ? err[0] : 'Feil brukernavn eller passord, prøv igjen...';
|
||||
// TODO: Format these and highlight error fields
|
||||
return <View style={styles.errorBox}><NBText style={styles.errorMessage}>{errorFormatted}</NBText></View>
|
||||
}
|
||||
}
|
||||
else {
|
||||
return <View style={styles.errorBox}/>
|
||||
}
|
||||
};
|
||||
|
||||
showSpinner = () => {
|
||||
if (this.props.isLoggingIn) {
|
||||
return <Spinner color="#f58220"/>
|
||||
}
|
||||
};
|
||||
|
||||
handleEmail = (text) => {
|
||||
this.setState({ email: text })
|
||||
@@ -95,30 +112,28 @@ export default class Login extends Component {
|
||||
this.props.requestLogin(this.state.email, this.state.password)
|
||||
};
|
||||
|
||||
onLogin = () => {
|
||||
this.props.onLogin();
|
||||
}
|
||||
}
|
||||
|
||||
const styles = StyleSheet.create({
|
||||
container: {
|
||||
flex: 1,
|
||||
justifyContent: 'center',
|
||||
alignItems: 'center',
|
||||
backgroundColor: '#F5FCFF',
|
||||
backgroundColor: 'white'
|
||||
},
|
||||
input: {
|
||||
width: 320,
|
||||
fontSize: 20,
|
||||
height: 52,
|
||||
},
|
||||
button: {
|
||||
marginTop: 20,
|
||||
padding: 10,
|
||||
width: 240,
|
||||
loginButton: {
|
||||
marginVertical: 16,
|
||||
marginHorizontal: 16
|
||||
},
|
||||
text: {
|
||||
fontSize: 18,
|
||||
fontWeight: 'bold'
|
||||
errorBox: {
|
||||
marginVertical: 16,
|
||||
},
|
||||
errorMessage: {
|
||||
textAlign: 'center',
|
||||
color: 'red',
|
||||
}
|
||||
});
|
||||
@@ -1,13 +1,42 @@
|
||||
import React, {Component} from 'react';
|
||||
import {connect} from "react-redux";
|
||||
import { bindActionCreators } from "redux";
|
||||
import Login from "./Login";
|
||||
import { requestLogin } from "../../actions";
|
||||
import {requestLogin} from "../../actions";
|
||||
|
||||
class LoginContainer extends Component {
|
||||
constructor(state) {
|
||||
super(state);
|
||||
this.state = {
|
||||
isAuthenticated: false,
|
||||
isLoggingIn: false,
|
||||
loginError: null,
|
||||
}
|
||||
}
|
||||
|
||||
componentWillReceiveProps(nextProps) {
|
||||
if (this.state.isAuthenticated !== nextProps.isAuthenticated) {
|
||||
if (nextProps.isAuthenticated) {
|
||||
this.props.onLogin();
|
||||
}
|
||||
}
|
||||
if (this.state.isLoggingIn !== nextProps.isLoggingIn) {
|
||||
this.setState({isLoggingIn: nextProps.isLoggingIn})
|
||||
}
|
||||
if (this.state.loginError !== nextProps.loginError) {
|
||||
this.setState({loginError: nextProps.loginError})
|
||||
}
|
||||
}
|
||||
|
||||
render() {
|
||||
return <Login {...this.state} requestLogin={this.props.requestLogin}/>
|
||||
}
|
||||
}
|
||||
|
||||
export default connect(
|
||||
(state) => ({
|
||||
isAuthenticated: state.isAuthenticated,
|
||||
isLoggingIn: state.isLoggingIn,
|
||||
errorMessage: state.errorMessage,
|
||||
}),
|
||||
{ requestLogin }
|
||||
)(Login);
|
||||
(state) => ({
|
||||
isAuthenticated: state.isAuthenticated,
|
||||
isLoggingIn: state.isLoggingIn,
|
||||
loginError: state.loginError,
|
||||
}),
|
||||
{requestLogin}
|
||||
)(LoginContainer);
|
||||
@@ -30,8 +30,8 @@ export default class Membership extends Component {
|
||||
};
|
||||
|
||||
onPurchasePress() {
|
||||
// FIXME: Move this to process.env.DUSKEN_URL and use babel thing to do search replace
|
||||
Linking.openURL('https://medlem.neuf.no');
|
||||
// FIXME: Move this to .env as DUSKEN_URL
|
||||
Linking.openURL('https://galtinn.neuf.no');
|
||||
};
|
||||
|
||||
onLoginPress = () => {
|
||||
|
||||
@@ -26,7 +26,7 @@ class MembershipContainer extends React.Component {
|
||||
this.setState({isAuthenticated: nextProps.isAuthenticated})
|
||||
}
|
||||
if(this.state.userToken !== nextProps.userToken) {
|
||||
this.setState({userToken: nextProps.userToken})
|
||||
this.setState({userToken: nextProps.userToken});
|
||||
this.fetchUser();
|
||||
}
|
||||
if(this.state.user !== nextProps.user) {
|
||||
@@ -37,11 +37,11 @@ class MembershipContainer extends React.Component {
|
||||
fetchUser = () => {
|
||||
if (!this.state.userToken) { return; }
|
||||
this.props.requestUserData(this.state.userToken)
|
||||
}
|
||||
};
|
||||
|
||||
onLoginPress = () => {
|
||||
this.props.onLoginPress();
|
||||
}
|
||||
};
|
||||
|
||||
render() {
|
||||
if (this.state.user) {
|
||||
|
||||
+5
-3
@@ -14,19 +14,21 @@ const initialState = {
|
||||
isFetchingUserData: false,
|
||||
userToken: null,
|
||||
user: null,
|
||||
loginError: null,
|
||||
};
|
||||
|
||||
export default function duskenApp(state = initialState, action) {
|
||||
switch (action.type) {
|
||||
case LOGIN_REQUEST:
|
||||
return Object.assign({}, state, {
|
||||
isLoggingIn: true
|
||||
isLoggingIn: true,
|
||||
loginError: null,
|
||||
});
|
||||
|
||||
case LOGIN_FAILURE:
|
||||
return Object.assign({}, state, {
|
||||
isLoggingIn: false,
|
||||
errorMessage: action.errorMessage
|
||||
loginError: action.loginError
|
||||
});
|
||||
|
||||
case LOGIN_SUCCESS:
|
||||
@@ -37,7 +39,7 @@ export default function duskenApp(state = initialState, action) {
|
||||
});
|
||||
|
||||
case LOGOUT:
|
||||
return initialState
|
||||
return initialState;
|
||||
|
||||
case USER_DATA_REQUEST:
|
||||
return Object.assign({}, state, {
|
||||
|
||||
@@ -1377,6 +1377,10 @@ decamelize@^1.0.0, decamelize@^1.1.1:
|
||||
version "1.2.0"
|
||||
resolved "https://registry.yarnpkg.com/decamelize/-/decamelize-1.2.0.tgz#f6534d15148269b20352e7bee26f501f9a191290"
|
||||
|
||||
deep-diff@^0.3.5:
|
||||
version "0.3.8"
|
||||
resolved "https://registry.yarnpkg.com/deep-diff/-/deep-diff-0.3.8.tgz#c01de63efb0eec9798801d40c7e0dae25b582c84"
|
||||
|
||||
deep-extend@~0.4.0:
|
||||
version "0.4.2"
|
||||
resolved "https://registry.yarnpkg.com/deep-extend/-/deep-extend-0.4.2.tgz#48b699c27e334bf89f10892be432f6e4c7d34a7f"
|
||||
@@ -3705,6 +3709,12 @@ rebound@^0.0.13:
|
||||
version "0.0.13"
|
||||
resolved "https://registry.yarnpkg.com/rebound/-/rebound-0.0.13.tgz#4a225254caf7da756797b19c5817bf7a7941fac1"
|
||||
|
||||
redux-logger@^3.0.6:
|
||||
version "3.0.6"
|
||||
resolved "https://registry.yarnpkg.com/redux-logger/-/redux-logger-3.0.6.tgz#f7555966f3098f3c88604c449cf0baf5778274bf"
|
||||
dependencies:
|
||||
deep-diff "^0.3.5"
|
||||
|
||||
redux-thunk@^2.2.0:
|
||||
version "2.2.0"
|
||||
resolved "https://registry.yarnpkg.com/redux-thunk/-/redux-thunk-2.2.0.tgz#e615a16e16b47a19a515766133d1e3e99b7852e5"
|
||||
|
||||
Reference in new issue
Block a user