♻ Refactor MembershipScreen

This commit is contained in:
nikolark committed 2021-10-03 20:52:22 +02:00
1 parent 311117669d
commit 8733f331bb
10 files changed
+192 -169

No files matched your search

+4 -3
View File
@@ -8,8 +8,8 @@ import { createNativeStackNavigator } from '@react-navigation/native-stack';
// import EventListScreen from './screens/EventListScreen';
// import AboutScreen from './screens/AboutScreen';
// import EventDetailScreen from './screens/EventDetailScreen';
import LoginScreen from './screens/LoginScreen';
// import MembershipScreen from './screens/MembershipScreen';
// import LoginScreen from './screens/LoginScreen';
import MembershipScreen from './screens/MembershipScreen';
// import UserRegisterScreen from './screens/UserRegisterScreen';
// const tabBarRoutes = {
@@ -86,7 +86,8 @@ const DuskenNavigation = () => (
<NavigationContainer>
<Stack.Navigator>
{/* <Stack.Screen name="About" component={AboutScreen} /> */}
<Stack.Screen name="Login" component={LoginScreen} />
{/* <Stack.Screen name="Login" component={LoginScreen} /> */}
<Stack.Screen name="Membership" component={MembershipScreen} />
{/* <Stack.Screen name="EventList" component={EventListScreen} /> */}
</Stack.Navigator>
</NavigationContainer>
+14
View File
@@ -0,0 +1,14 @@
/* eslint-disable import/prefer-default-export */
import { useRef, useEffect } from 'react';
export function usePrevious(value) {
const ref = useRef();
// Store current value in ref
useEffect(() => {
ref.current = value;
}, [value]); // Only re-run if value changes
// Return previous value (happens before update in useEffect above)
return ref.current;
}
+23 -38
View File
@@ -1,48 +1,33 @@
import React, { Component } from 'react';
import { ScrollView, Text, View, StyleSheet } from 'react-native';
import { Button, Text as NBText, Spinner } from 'native-base';
import React from 'react';
import { View, StyleSheet } from 'react-native';
import { Spinner } from 'native-base';
import theme from '../../theme';
import useMembership from './useMembership';
import MembershipInfo from './MembershipInfo';
import Proof from './Proof';
export default class Membership extends Component {
render() {
if (this.props.isFetchingUserData) {
return (
<View style={styles.card}>
<Spinner color="#f58220" />
</View>
);
}
const Membership = () => {
const { isFetchingUserData, onRegisterPress, onLoginPress, onLogoutPress, user, isAuthenticated, fetchUser } =
useMembership();
if (isFetchingUserData) {
return (
<ScrollView>
<View style={styles.card}>
<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={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 }]}>
- Minst 25% rabatt på de fleste arrangement{'\n'}- Gratis inngang på enkelte arrangement{'\n'}- Store
rabatter i barene{'\n'}- Egne medlemsarrangement og -tilbud{'\n'}- Mulighet til å delta i landets eldste
studentdemokrati
</Text>
<Text style={styles.em}>Allerede medlem?</Text>
<View style={styles.button}>
<Button onPress={this.props.onLoginPress} full>
<NBText>Logg inn</NBText>
</Button>
</View>
</View>
</ScrollView>
<View style={styles.card}>
<Spinner color="#f58220" />
</View>
);
}
}
if (isAuthenticated) {
return (
<Proof onLogoutPress={onLogoutPress} user={user} fetchUser={fetchUser} isFetchingUserData={isFetchingUserData} />
);
}
return <MembershipInfo onRegister={onRegisterPress} onLogin={onLoginPress} />;
};
export default Membership;
const styles = StyleSheet.create({
card: theme.card,
@@ -1,87 +0,0 @@
import React, { Component } from 'react';
import { connect } from 'react-redux';
import Membership from './Membership';
import Proof from './Proof';
import { logout } from '../../actions';
import { requestUserData } from '../../api';
class MembershipContainer extends Component {
constructor(props) {
super(props);
this.state = {
isFetchingUserData: props.isFetchingUserData,
userToken: props.userToken,
lastOrder: props.lastOrder,
};
}
componentWillReceiveProps(nextProps) {
if (this.state.userToken !== nextProps.userToken) {
this.setState({ userToken: nextProps.userToken }, () => {
if (nextProps.userToken) {
this.fetchUser();
}
});
}
if (this.state.lastOrder !== nextProps.lastOrder) {
this.setState({ lastOrder: nextProps.lastOrder }, () => {
if (nextProps.userToken) {
this.fetchUser();
}
});
}
}
fetchUser = () => {
this.props.requestUserData(this.state.userToken);
};
onLoginPress = () => {
this.props.onLoginPress();
};
onRegisterPress = () => {
this.props.onRegisterPress();
};
onLogoutPress = () => {
this.props.logout();
this.props.onLogoutPress();
};
render() {
if (this.props.user) {
return (
<Proof
user={this.props.user}
isFetchingUserData={this.props.isFetchingUserData}
isAuthenticated={this.props.isAuthenticated}
userToken={this.state.userToken}
fetchUser={this.fetchUser}
onLogoutPress={this.onLogoutPress}
/>
);
}
return (
<Membership
isFetchingUserData={this.props.isFetchingUserData}
onLoginPress={this.onLoginPress}
onRegisterPress={this.onRegisterPress}
/>
);
}
}
function storeToProps(store) {
return {
isAuthenticated: store.isAuthenticated,
isFetchingUserData: store.isFetchingUserData,
userToken: store.userToken,
user: store.user,
lastOrder: store.lastOrder,
};
}
export default connect(storeToProps, { requestUserData, logout })(MembershipContainer);
+71
View File
@@ -0,0 +1,71 @@
import React from 'react';
import { ScrollView, Text, View, StyleSheet } from 'react-native';
import { Button, Text as NBText } from 'native-base';
import theme from '../../theme';
const MembershipInfo = ({ onRegister, onLogin }) => (
<ScrollView>
<View style={styles.card}>
<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={onRegister} full>
<NBText>Bli medlem</NBText>
</Button>
</View>
<Text style={styles.header}>Hva får jeg?</Text>
<Text style={[styles.paragraph, { textAlign: 'left', paddingLeft: 4 }]}>
- Minst 25% rabatt på de fleste arrangement{'\n'}- Gratis inngang på enkelte arrangement{'\n'}- Store rabatter i
barene{'\n'}- Egne medlemsarrangement og -tilbud{'\n'}- Mulighet til å delta i landets eldste studentdemokrati
</Text>
<Text style={styles.em}>Allerede medlem?</Text>
<View style={styles.button}>
<Button onPress={onLogin} full>
<NBText>Logg inn</NBText>
</Button>
</View>
</View>
</ScrollView>
);
export default MembershipInfo;
const styles = StyleSheet.create({
card: theme.card,
paragraph: {
textAlign: 'center',
fontSize: 16,
color: '#000',
paddingBottom: 10,
lineHeight: 25,
},
button: {
marginTop: 6,
marginBottom: 16,
},
em: {
fontSize: 16,
color: '#000',
fontStyle: 'italic',
textAlign: 'center',
marginVertical: 10,
},
headerTitle: {
textAlign: 'center',
fontWeight: 'bold',
fontSize: 20,
color: '#000',
paddingVertical: 10,
lineHeight: 25,
},
header: {
fontSize: 16,
fontWeight: 'bold',
color: '#000',
lineHeight: 25,
paddingLeft: 2,
},
});
+9
View File
@@ -144,6 +144,15 @@ const Proof = ({ onLogoutPress, user, fetchUser, isFetchingUserData }) => {
}, CONFETTI_TIMEOUT);
};
if (!user) {
console.log('🔥', user);
return (
<View>
<Text>No user!</Text>
</View>
);
}
return (
<View>
<UserConfetti user={user} confettiRef={confettiRef} />
+52
View File
@@ -0,0 +1,52 @@
import { useNavigation } from '@react-navigation/native';
import { useEffect } from 'react';
import { useDispatch, useSelector } from 'react-redux';
import { logout } from '../../actions';
import { requestUserData } from '../../api';
import { usePrevious } from '../../hooks';
const useMembership = () => {
const navigation = useNavigation();
const dispatch = useDispatch();
const { isAuthenticated, isFetchingUserData, userToken, user, lastOrder } = useSelector((store) => ({
isAuthenticated: store.isAuthenticated,
isFetchingUserData: store.isFetchingUserData,
userToken: store.userToken,
user: store.user,
lastOrder: store.lastOrder,
}));
const prevUserToken = usePrevious(userToken);
const prevLastOrder = usePrevious(lastOrder);
const onLogoutPress = () => {
dispatch(logout());
navigation.navigate('Membership');
};
const onRegisterPress = () => navigation.navigate('UserRegister');
const onLoginPress = () => navigation.navigate('Login');
const fetchUser = () => {
dispatch(requestUserData(userToken));
};
useEffect(() => {
if (userToken !== prevUserToken && userToken) {
fetchUser();
}
if (prevLastOrder !== lastOrder && userToken) {
fetchUser();
}
}, [userToken, prevUserToken, lastOrder, prevLastOrder, fetchUser]);
return {
isAuthenticated,
isFetchingUserData,
userToken,
user,
lastOrder,
onLogoutPress,
onRegisterPress,
onLoginPress,
};
};
export default useMembership;
+17 -16
View File
@@ -1,14 +1,15 @@
import { useEffect } from 'react';
import { useDispatch, useSelector } from 'react-redux';
import stripe from 'tipsi-stripe';
// import stripe from 'tipsi-stripe';
import Config from 'react-native-config';
import { gql, useQuery } from '@apollo/client';
import { requestMembershipCharge } from '../../api';
stripe.setOptions({
publishableKey: Config.STRIPE_PUBLISHABLE_KEY || '',
});
// TODO: switch to official stripe client
// stripe.setOptions({
// publishableKey: Config.STRIPE_PUBLISHABLE_KEY || '',
// });
const PROOF_QUERY = gql`
{
@@ -44,22 +45,22 @@ const useProof = ({ user, fetchUser }) => {
smsAutofillDisabled: true,
};
const membershipTypeSlug = data.membershipTypes ? data.membershipTypes[0].slug : 'standard';
const membershipTypeSlug = data?.membershipTypes?.[0]?.slug ?? 'standard';
stripe.paymentRequestWithCardForm(options).then(
(token) => {
// returns a token.tokenId which should be saved
dispatch(requestMembershipCharge(userToken, { id: token.tokenId, email: user.email }, membershipTypeSlug));
},
() => {
/* FIXME: Happens only if card dialog is canceled? */
}
);
// stripe.paymentRequestWithCardForm(options).then(
// (token) => {
// // returns a token.tokenId which should be saved
// dispatch(requestMembershipCharge(userToken, { id: token.tokenId, email: user.email }, membershipTypeSlug));
// },
// () => {
// /* FIXME: Happens only if card dialog is canceled? */
// }
// );
};
useEffect(() => {
const todaysDate = new Date().toISOString().substring(0, 10);
const lastMembership = user.last_membership;
const lastMembership = user?.last_membership;
if (lastMembership && lastMembership.membership_type !== 'lifelong' && lastMembership.end_date < todaysDate) {
fetchUser();
}
@@ -69,7 +70,7 @@ const useProof = ({ user, fetchUser }) => {
chargeError: chargeError || '',
isChargingMembership,
isLoadingMembershipType: loading,
membershipPrice: getPrice(data.membershipTypes),
membershipPrice: getPrice(data?.membershipTypes),
onChargePress: openStripe,
};
};
-1
View File
@@ -2,7 +2,6 @@ import React from 'react';
import Login from '../modules/login/Login';
// navigation.goBack onLogin works since LoginScreen is allways navigated to from MembershipScreen
const LoginScreen = () => {
return <Login />;
};
+2 -24
View File
@@ -1,8 +1,6 @@
import React, { Component } from 'react';
import Icon from 'react-native-vector-icons/SimpleLineIcons';
import { StyleProvider } from 'native-base';
import MembershipContainer from '../modules/membership/MembershipContainer';
import getTheme from '../../native-base-theme/components';
import Membership from '../modules/membership/Membership';
export default class MembershipScreen extends Component {
static navigationOptions = {
@@ -10,27 +8,7 @@ export default class MembershipScreen extends Component {
tabBarIcon: ({ tintColor }) => <Icon name="user" size={16} color={tintColor} style={{ marginTop: 5 }} />,
};
loginNavigate = () => {
this.props.navigation.navigate('Login');
};
logoutNavigate = () => {
this.props.navigation.navigate('Membership');
};
registerNavigate = () => {
this.props.navigation.navigate('UserRegister');
};
render() {
return (
<StyleProvider style={getTheme()}>
<MembershipContainer
onLoginPress={this.loginNavigate}
onLogoutPress={this.logoutNavigate}
onRegisterPress={this.registerNavigate}
/>
</StyleProvider>
);
return <Membership />;
}
}