Archived
⬆ Bump to RN 0.66
⬆ Bump apollo client
⬆ Bump dev dependencies
♻ Refactor ProofContainer into useProof (untested)
🚨 Format with prettier
This commit is contained in:
1 parent
bceaef16dc
commit
b9268f6d46
14 files changed
+4665
-3244
No files matched your search
@@ -12,19 +12,19 @@
|
||||
"no-underscore-dangle": 0,
|
||||
"no-param-reassign": 0,
|
||||
"react/prefer-stateless-function": 1,
|
||||
"react/prop-types": 1, // FIXME: refactor
|
||||
"class-methods-use-this": 1, // FIXME: refactor
|
||||
"react/prop-types": "off",
|
||||
"class-methods-use-this": "off",
|
||||
"react/jsx-filename-extension": [1, { "extensions": [".js", ".jsx"] }],
|
||||
"react/sort-comp": 0,
|
||||
"max-len": 0,
|
||||
"no-use-before-define": ["error", { "functions": true, "classes": true, "variables": false }] // disable the rule for variables, but enable it for functions and classes
|
||||
"no-use-before-define": ["error", { "functions": true, "classes": true, "variables": false }]
|
||||
|
||||
},
|
||||
"plugins": [
|
||||
"react",
|
||||
"prettier"
|
||||
],
|
||||
"parser": "babel-eslint",
|
||||
"parser": "@babel/eslint-parser",
|
||||
"settings": {
|
||||
"react": {
|
||||
"version": "16.8"
|
||||
|
||||
+29
-32
@@ -6,28 +6,25 @@
|
||||
"android": "react-native run-android",
|
||||
"build:android": "cd android && ./gradlew assembleRelease",
|
||||
"emulator": "emulator -avd $(emulator -list-avds | head)&",
|
||||
"format": "eslint --fix \"src/**/*.js\"",
|
||||
"format": "eslint --fix src",
|
||||
"ios": "react-native run-ios",
|
||||
"lint:format": "eslint --quiet \"src/**/*.js\"",
|
||||
"lint": "eslint --quiet src",
|
||||
"release:android": "cd android && bundle exec fastlane beta",
|
||||
"release:ios": "cd ios && bundle exec fastlane beta",
|
||||
"start": "node node_modules/react-native/local-cli/cli.js start",
|
||||
"test": "jest"
|
||||
},
|
||||
"dependencies": {
|
||||
"@react-native-community/async-storage": "^1.3.4",
|
||||
"apollo-cache-inmemory": "^1.5.1",
|
||||
"apollo-client": "^2.5.1",
|
||||
"apollo-link-http": "^1.5.14",
|
||||
"@apollo/client": "^3.4.14",
|
||||
"@react-native-async-storage/async-storage": "^1.15.8",
|
||||
"@react-navigation/native": "^6.0.4",
|
||||
"email-validator": "^2.0.4",
|
||||
"graphql": "^14.2.1",
|
||||
"graphql-tag": "^2.10.1",
|
||||
"graphql": "^15.6.0",
|
||||
"moment": "^2.24.0",
|
||||
"native-base": "^2.12.1",
|
||||
"prop-types": "^15.7.2",
|
||||
"react": "16.8.3",
|
||||
"react-apollo": "^2.5.5",
|
||||
"react-native": "0.59.5",
|
||||
"react": "^17.0.2",
|
||||
"react-native": "0.66.0-rc.4",
|
||||
"react-native-confetti": "^0.1.0",
|
||||
"react-native-config": "^0.11.7",
|
||||
"react-native-gesture-handler": "^1.2.1",
|
||||
@@ -35,31 +32,31 @@
|
||||
"react-native-screens": "^1.0.0-alpha.22",
|
||||
"react-native-sentry": "^0.42.0",
|
||||
"react-native-vector-icons": "^6.4.2",
|
||||
"react-navigation": "^3.9.1",
|
||||
"react-navigation-hooks": "^1.0.0-alpha.2",
|
||||
"react-redux": "^7.0.3",
|
||||
"redux": "^4.0.1",
|
||||
"react-redux": "^7.2.5",
|
||||
"redux": "^4.1.1",
|
||||
"redux-persist": "^5.10.0",
|
||||
"redux-thunk": "^2.3.0",
|
||||
"tipsi-stripe": "^7.5.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@babel/core": "^7.4.4",
|
||||
"@babel/runtime": "^7.4.4",
|
||||
"babel-eslint": "^10.0.1",
|
||||
"babel-jest": "^24.7.1",
|
||||
"eslint": "^5.16.0",
|
||||
"eslint-config-airbnb": "^17.1.0",
|
||||
"eslint-config-prettier": "^4.0.0",
|
||||
"eslint-plugin-import": "^2.16.0",
|
||||
"eslint-plugin-jsx-a11y": "^6.2.1",
|
||||
"eslint-plugin-prettier": "^3.0.1",
|
||||
"eslint-plugin-react": "^7.12.4",
|
||||
"jest": "^24.7.1",
|
||||
"jest-fetch-mock": "^1.6.5",
|
||||
"metro-react-native-babel-preset": "^0.54.0",
|
||||
"prettier": "^1.17.0",
|
||||
"react-dom": "16.8.3",
|
||||
"react-test-renderer": "16.8.3"
|
||||
"@babel/core": "^7.15.5",
|
||||
"@babel/eslint-parser": "^7.15.7",
|
||||
"@babel/preset-env": "^7.15.6",
|
||||
"@babel/runtime": "^7.15.4",
|
||||
"babel-jest": "^27.2.2",
|
||||
"eslint": "^7.32.0",
|
||||
"eslint-config-airbnb": "^18.2.1",
|
||||
"eslint-config-prettier": "^8.3.0",
|
||||
"eslint-plugin-import": "^2.24.2",
|
||||
"eslint-plugin-jsx-a11y": "^6.4.1",
|
||||
"eslint-plugin-prettier": "^4.0.0",
|
||||
"eslint-plugin-react": "^7.26.0",
|
||||
"eslint-plugin-react-hooks": "^4.2.0",
|
||||
"jest": "^27.2.2",
|
||||
"jest-fetch-mock": "^3.0.3",
|
||||
"metro-react-native-babel-preset": "^0.66.2",
|
||||
"prettier": "^2.4.1",
|
||||
"react-dom": "^17.0.2",
|
||||
"react-test-renderer": "^17.0.2"
|
||||
}
|
||||
}
|
||||
+1
-4
@@ -2,10 +2,7 @@ import React from 'react';
|
||||
import { Provider } from 'react-redux';
|
||||
import { PersistGate } from 'redux-persist/es/integration/react';
|
||||
import Config from 'react-native-config';
|
||||
import { ApolloClient } from 'apollo-client';
|
||||
import { HttpLink } from 'apollo-link-http';
|
||||
import { InMemoryCache } from 'apollo-cache-inmemory';
|
||||
import { ApolloProvider } from 'react-apollo';
|
||||
import { ApolloClient, HttpLink, InMemoryCache, ApolloProvider } from '@apollo/client';
|
||||
|
||||
import Dusken from './Dusken';
|
||||
import { store, persistor } from './reduxStore';
|
||||
|
||||
+1
-4
@@ -35,7 +35,4 @@ const mapDispatchToProps = {
|
||||
logout: logoutAction,
|
||||
};
|
||||
|
||||
export default connect(
|
||||
mapStateToProps,
|
||||
mapDispatchToProps
|
||||
)(DuskenContainer);
|
||||
export default connect(mapStateToProps, mapDispatchToProps)(DuskenContainer);
|
||||
@@ -2,7 +2,7 @@ import React, { Component } from 'react';
|
||||
import { connect } from 'react-redux';
|
||||
|
||||
import Membership from './Membership';
|
||||
import ProofContainer from './ProofContainer';
|
||||
import Proof from './Proof';
|
||||
import { logout } from '../../actions';
|
||||
import { requestUserData } from '../../api';
|
||||
|
||||
@@ -54,7 +54,7 @@ class MembershipContainer extends Component {
|
||||
render() {
|
||||
if (this.props.user) {
|
||||
return (
|
||||
<ProofContainer
|
||||
<Proof
|
||||
user={this.props.user}
|
||||
isFetchingUserData={this.props.isFetchingUserData}
|
||||
isAuthenticated={this.props.isAuthenticated}
|
||||
@@ -84,7 +84,4 @@ function storeToProps(store) {
|
||||
};
|
||||
}
|
||||
|
||||
export default connect(
|
||||
storeToProps,
|
||||
{ requestUserData, logout }
|
||||
)(MembershipContainer);
|
||||
export default connect(storeToProps, { requestUserData, logout })(MembershipContainer);
|
||||
@@ -4,6 +4,7 @@ import { Button, Text as NBText, Spinner } from 'native-base';
|
||||
import Confetti from 'react-native-confetti';
|
||||
|
||||
import theme from '../../theme';
|
||||
import useProof from './useProof';
|
||||
|
||||
const MembershipValidTo = ({ user }) => {
|
||||
if (!user.last_membership) {
|
||||
@@ -118,19 +119,10 @@ const MembershipName = ({ user }) => {
|
||||
);
|
||||
};
|
||||
|
||||
const Proof = ({
|
||||
chargeError,
|
||||
user,
|
||||
fetchUser,
|
||||
isFetchingUserData,
|
||||
isChargingMembership,
|
||||
isLoadingMembershipType,
|
||||
membershipPrice,
|
||||
onChargePress,
|
||||
onLogoutPress,
|
||||
}) => {
|
||||
const Proof = ({ onLogoutPress, user, fetchUser, isFetchingUserData }) => {
|
||||
const CONFETTI_TIMEOUT = 15000;
|
||||
const confettiRef = useRef(null);
|
||||
const { chargeError, isChargingMembership, isLoadingMembershipType, onChargePress, membershipPrice } = useProof();
|
||||
|
||||
useEffect(() => {
|
||||
return () => {
|
||||
|
||||
@@ -1,97 +0,0 @@
|
||||
import React, { Component } from 'react';
|
||||
import { connect } from 'react-redux';
|
||||
import stripe from 'tipsi-stripe';
|
||||
import Config from 'react-native-config';
|
||||
import { graphql } from 'react-apollo';
|
||||
import gql from 'graphql-tag';
|
||||
|
||||
import Proof from './Proof';
|
||||
import { requestMembershipCharge } from '../../api';
|
||||
|
||||
stripe.setOptions({
|
||||
publishableKey: Config.STRIPE_PUBLISHABLE_KEY || '',
|
||||
});
|
||||
|
||||
class ProofContainer extends Component {
|
||||
getPrice(membershipTypes) {
|
||||
if (!membershipTypes) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
return membershipTypes[0].price / 100;
|
||||
}
|
||||
|
||||
componentDidMount = () => {
|
||||
this.checkExpiry();
|
||||
};
|
||||
|
||||
render() {
|
||||
return (
|
||||
<Proof
|
||||
user={this.props.user}
|
||||
fetchUser={this.props.fetchUser}
|
||||
isFetchingUserData={this.props.isFetchingUserData}
|
||||
isChargingMembership={this.props.isChargingMembership}
|
||||
isLoadingMembershipType={this.props.data.loading}
|
||||
chargeError={this.props.chargeError ? this.props.chargeError : ''}
|
||||
onChargePress={this.openStripe}
|
||||
onLogoutPress={this.props.onLogoutPress}
|
||||
membershipPrice={this.getPrice(this.props.data.membershipTypes)}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
checkExpiry() {
|
||||
const { user, fetchUser } = this.props;
|
||||
const todaysDate = new Date().toISOString().substring(0, 10);
|
||||
const lastMembership = user.last_membership;
|
||||
if (lastMembership && lastMembership.membership_type !== 'lifelong' && lastMembership.end_date < todaysDate) {
|
||||
fetchUser();
|
||||
}
|
||||
}
|
||||
|
||||
openStripe = () => {
|
||||
const options = {
|
||||
prefilledInformation: {
|
||||
email: this.props.user.email,
|
||||
},
|
||||
smsAutofillDisabled: true,
|
||||
};
|
||||
|
||||
const membershipTypeSlug = this.props.data.membershipTypes ? this.props.data.membershipTypes[0].slug : 'standard';
|
||||
|
||||
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 },
|
||||
membershipTypeSlug
|
||||
);
|
||||
},
|
||||
() => {
|
||||
/* FIXME: Happens only if card dialog is canceled? */
|
||||
}
|
||||
);
|
||||
};
|
||||
}
|
||||
|
||||
const PROOF_QUERY = gql`
|
||||
{
|
||||
membershipTypes(isDefault: true) {
|
||||
slug
|
||||
price
|
||||
}
|
||||
}
|
||||
`;
|
||||
|
||||
const proofContainerWithState = connect(
|
||||
(store) => ({
|
||||
userToken: store.userToken,
|
||||
isChargingMembership: store.isChargingMembership,
|
||||
chargeError: store.chargeError,
|
||||
}),
|
||||
{ requestMembershipCharge }
|
||||
)(ProofContainer);
|
||||
|
||||
export default graphql(PROOF_QUERY)(proofContainerWithState);
|
||||
@@ -0,0 +1,78 @@
|
||||
import { useEffect } from 'react';
|
||||
import { useDispatch, useSelector } from 'react-redux';
|
||||
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 || '',
|
||||
});
|
||||
|
||||
const PROOF_QUERY = gql`
|
||||
{
|
||||
membershipTypes(isDefault: true) {
|
||||
slug
|
||||
price
|
||||
}
|
||||
}
|
||||
`;
|
||||
|
||||
function getPrice(membershipTypes) {
|
||||
if (!membershipTypes) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
return membershipTypes[0].price / 100;
|
||||
}
|
||||
|
||||
// FIXME: where do these come from?
|
||||
const useProof = ({ user, fetchUser }) => {
|
||||
const dispatch = useDispatch();
|
||||
const { userToken, isChargingMembership, chargeError } = useSelector((state) => ({
|
||||
userToken: state.userToken,
|
||||
isChargingMembership: state.isChargingMembership,
|
||||
chargeError: state.chargeError,
|
||||
}));
|
||||
const { data, loading } = useQuery(PROOF_QUERY);
|
||||
|
||||
const openStripe = () => {
|
||||
const options = {
|
||||
prefilledInformation: {
|
||||
email: user.email,
|
||||
},
|
||||
smsAutofillDisabled: true,
|
||||
};
|
||||
|
||||
const membershipTypeSlug = data.membershipTypes ? 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? */
|
||||
}
|
||||
);
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
const todaysDate = new Date().toISOString().substring(0, 10);
|
||||
const lastMembership = user.last_membership;
|
||||
if (lastMembership && lastMembership.membership_type !== 'lifelong' && lastMembership.end_date < todaysDate) {
|
||||
fetchUser();
|
||||
}
|
||||
}, []);
|
||||
|
||||
return {
|
||||
chargeError: chargeError || '',
|
||||
isChargingMembership,
|
||||
isLoadingMembershipType: loading,
|
||||
membershipPrice: getPrice(data.membershipTypes),
|
||||
onChargePress: openStripe,
|
||||
};
|
||||
};
|
||||
|
||||
export default useProof;
|
||||
@@ -23,7 +23,7 @@ export default class UserRegister extends Component {
|
||||
validateForm(triggeredBy) {
|
||||
const fieldNames = ['firstName', 'lastName', 'email', 'phoneNumber', 'password'];
|
||||
|
||||
const errors = Object.assign({}, this.state.errors);
|
||||
const errors = { ...this.state.errors };
|
||||
const touched = new Set(this.state.touched).add(triggeredBy);
|
||||
|
||||
/* No empty fields */
|
||||
|
||||
@@ -44,7 +44,4 @@ function storeToProps(store) {
|
||||
registerError: errors,
|
||||
};
|
||||
}
|
||||
export default connect(
|
||||
storeToProps,
|
||||
{ requestRegisterUser }
|
||||
)(UserRegisterContainer);
|
||||
export default connect(storeToProps, { requestRegisterUser })(UserRegisterContainer);
|
||||
+12
-51
@@ -43,58 +43,33 @@ export default function duskenApp(state = initialState, action) {
|
||||
switch (action.type) {
|
||||
/* Login */
|
||||
case LOGIN_REQUEST:
|
||||
return Object.assign({}, state, {
|
||||
isLoggingIn: true,
|
||||
loginError: null,
|
||||
});
|
||||
return { ...state, isLoggingIn: true, loginError: null };
|
||||
|
||||
case LOGIN_FAILURE:
|
||||
return Object.assign({}, state, {
|
||||
isLoggingIn: false,
|
||||
loginError: action.loginError,
|
||||
});
|
||||
return { ...state, isLoggingIn: false, loginError: action.loginError };
|
||||
|
||||
case LOGIN_SUCCESS:
|
||||
return Object.assign({}, state, {
|
||||
isLoggingIn: false,
|
||||
isAuthenticated: true,
|
||||
userToken: action.token,
|
||||
});
|
||||
return { ...state, isLoggingIn: false, isAuthenticated: true, userToken: action.token };
|
||||
|
||||
case LOGOUT:
|
||||
return initialState;
|
||||
|
||||
/* User data */
|
||||
case USER_DATA_REQUEST:
|
||||
return Object.assign({}, state, {
|
||||
isFetchingUserData: true,
|
||||
userError: null,
|
||||
});
|
||||
return { ...state, isFetchingUserData: true, userError: null };
|
||||
|
||||
case USER_DATA_FAILURE:
|
||||
return Object.assign({}, state, {
|
||||
isFetchingUserData: false,
|
||||
userError: action.userError,
|
||||
});
|
||||
return { ...state, isFetchingUserData: false, userError: action.userError };
|
||||
|
||||
case USER_DATA_SUCCESS:
|
||||
return Object.assign({}, state, {
|
||||
isFetchingUserData: false,
|
||||
user: action.data,
|
||||
});
|
||||
return { ...state, isFetchingUserData: false, user: action.data };
|
||||
|
||||
/* Register user */
|
||||
case REGISTER_USER_REQUEST:
|
||||
return Object.assign({}, state, {
|
||||
isRegisteringUser: true,
|
||||
registerError: null,
|
||||
});
|
||||
return { ...state, isRegisteringUser: true, registerError: null };
|
||||
|
||||
case REGISTER_USER_FAILURE:
|
||||
return Object.assign({}, state, {
|
||||
isRegisteringUser: false,
|
||||
registerError: formatErrors(action.registerError),
|
||||
});
|
||||
return { ...state, isRegisteringUser: false, registerError: formatErrors(action.registerError) };
|
||||
|
||||
case REGISTER_USER_SUCCESS:
|
||||
// eslint-disable-next-line no-case-declarations
|
||||
@@ -103,31 +78,17 @@ export default function duskenApp(state = initialState, action) {
|
||||
const token = action.data.auth_token;
|
||||
delete userData.auth_token;
|
||||
|
||||
return Object.assign({}, state, {
|
||||
isRegisteringUser: false,
|
||||
isAuthenticated: true,
|
||||
user: userData,
|
||||
userToken: token,
|
||||
});
|
||||
return { ...state, isRegisteringUser: false, isAuthenticated: true, user: userData, userToken: token };
|
||||
|
||||
/* User data */
|
||||
case MEMBERSHIP_CHARGE_REQUEST:
|
||||
return Object.assign({}, state, {
|
||||
isChargingMembership: true,
|
||||
chargeError: null,
|
||||
});
|
||||
return { ...state, isChargingMembership: true, chargeError: null };
|
||||
|
||||
case MEMBERSHIP_CHARGE_FAILURE:
|
||||
return Object.assign({}, state, {
|
||||
isChargingMembership: false,
|
||||
chargeError: action.chargeError,
|
||||
});
|
||||
return { ...state, isChargingMembership: false, chargeError: action.chargeError };
|
||||
|
||||
case MEMBERSHIP_CHARGE_SUCCESS:
|
||||
return Object.assign({}, state, {
|
||||
isChargingMembership: false,
|
||||
lastOrder: action.data,
|
||||
});
|
||||
return { ...state, isChargingMembership: false, lastOrder: action.data };
|
||||
|
||||
default:
|
||||
return state;
|
||||
|
||||
+1
-1
@@ -1,6 +1,6 @@
|
||||
import { applyMiddleware, createStore } from 'redux';
|
||||
import { persistStore, persistReducer } from 'redux-persist';
|
||||
import AsyncStorage from '@react-native-community/async-storage';
|
||||
import AsyncStorage from '@react-native-async-storage/async-storage';
|
||||
import thunk from 'redux-thunk';
|
||||
|
||||
import rootReducer from './reducers';
|
||||
|
||||
+2
-4
@@ -1,10 +1,8 @@
|
||||
/* global fetch */
|
||||
import AsyncStorage from '@react-native-community/async-storage';
|
||||
import AsyncStorage from '@react-native-async-storage/async-storage';
|
||||
|
||||
export function snakeToCamelCase(val) {
|
||||
return val.replace(/_([a-z])/g, function(g) {
|
||||
return g[1].toUpperCase();
|
||||
});
|
||||
return val.replace(/_([a-z])/g, (g) => g[1].toUpperCase());
|
||||
}
|
||||
|
||||
export function fetchWithTimeout(url, options, timeout = 10000) {
|
||||
|
||||
Reference in new issue
Block a user