From 8733f331bb53df1728c61666f60187230215e37d Mon Sep 17 00:00:00 2001 From: Nikolai R Kristiansen Date: Sun, 3 Oct 2021 20:52:22 +0200 Subject: [PATCH] =?UTF-8?q?=E2=99=BB=20Refactor=20MembershipScreen?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/Navigation.js | 7 +- src/hooks.js | 14 +++ src/modules/membership/Membership.js | 61 +++++-------- src/modules/membership/MembershipContainer.js | 87 ------------------- src/modules/membership/MembershipInfo.js | 71 +++++++++++++++ src/modules/membership/Proof.js | 9 ++ src/modules/membership/useMembership.js | 52 +++++++++++ src/modules/membership/useProof.js | 33 +++---- src/screens/LoginScreen.js | 1 - src/screens/MembershipScreen.js | 26 +----- 10 files changed, 192 insertions(+), 169 deletions(-) create mode 100644 src/hooks.js delete mode 100644 src/modules/membership/MembershipContainer.js create mode 100644 src/modules/membership/MembershipInfo.js create mode 100644 src/modules/membership/useMembership.js diff --git a/src/Navigation.js b/src/Navigation.js index 1794020..39f6c97 100644 --- a/src/Navigation.js +++ b/src/Navigation.js @@ -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 = () => ( {/* */} - + {/* */} + {/* */} diff --git a/src/hooks.js b/src/hooks.js new file mode 100644 index 0000000..71b4662 --- /dev/null +++ b/src/hooks.js @@ -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; +} diff --git a/src/modules/membership/Membership.js b/src/modules/membership/Membership.js index a95be92..27f7d98 100644 --- a/src/modules/membership/Membership.js +++ b/src/modules/membership/Membership.js @@ -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 ( - - - - ); - } +const Membership = () => { + const { isFetchingUserData, onRegisterPress, onLoginPress, onLogoutPress, user, isAuthenticated, fetchUser } = + useMembership(); + if (isFetchingUserData) { return ( - - - Hei 👋 - - Her kan du bli medlem i Det Norske Studentersamfund - Chateau Neuf og ha medlemsbeviset ditt på telefonen - - - - - Hva får jeg? - - - 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 - - Allerede medlem? - - - - - + + + ); } -} + + if (isAuthenticated) { + return ( + + ); + } + + return ; +}; +export default Membership; const styles = StyleSheet.create({ card: theme.card, diff --git a/src/modules/membership/MembershipContainer.js b/src/modules/membership/MembershipContainer.js deleted file mode 100644 index 9c7b59c..0000000 --- a/src/modules/membership/MembershipContainer.js +++ /dev/null @@ -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 ( - - ); - } - return ( - - ); - } -} - -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); diff --git a/src/modules/membership/MembershipInfo.js b/src/modules/membership/MembershipInfo.js new file mode 100644 index 0000000..1d1c6c9 --- /dev/null +++ b/src/modules/membership/MembershipInfo.js @@ -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 }) => ( + + + Hei 👋 + + Her kan du bli medlem i Det Norske Studentersamfund - Chateau Neuf og ha medlemsbeviset ditt på telefonen + + + + + Hva får jeg? + + - 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 + + Allerede medlem? + + + + + +); + +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, + }, +}); diff --git a/src/modules/membership/Proof.js b/src/modules/membership/Proof.js index 13400c8..7828371 100644 --- a/src/modules/membership/Proof.js +++ b/src/modules/membership/Proof.js @@ -144,6 +144,15 @@ const Proof = ({ onLogoutPress, user, fetchUser, isFetchingUserData }) => { }, CONFETTI_TIMEOUT); }; + if (!user) { + console.log('🔥', user); + return ( + + No user! + + ); + } + return ( diff --git a/src/modules/membership/useMembership.js b/src/modules/membership/useMembership.js new file mode 100644 index 0000000..246ac5a --- /dev/null +++ b/src/modules/membership/useMembership.js @@ -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; diff --git a/src/modules/membership/useProof.js b/src/modules/membership/useProof.js index 8fd60ab..806e71a 100644 --- a/src/modules/membership/useProof.js +++ b/src/modules/membership/useProof.js @@ -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, }; }; diff --git a/src/screens/LoginScreen.js b/src/screens/LoginScreen.js index f8812fa..1b97c12 100644 --- a/src/screens/LoginScreen.js +++ b/src/screens/LoginScreen.js @@ -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 ; }; diff --git a/src/screens/MembershipScreen.js b/src/screens/MembershipScreen.js index 72ce1b3..5d659cd 100644 --- a/src/screens/MembershipScreen.js +++ b/src/screens/MembershipScreen.js @@ -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 }) => , }; - loginNavigate = () => { - this.props.navigation.navigate('Login'); - }; - - logoutNavigate = () => { - this.props.navigation.navigate('Membership'); - }; - - registerNavigate = () => { - this.props.navigation.navigate('UserRegister'); - }; - render() { - return ( - - - - ); + return ; } }