⬆ Bump to RN 0.66

⬆ Bump apollo client
⬆ Bump dev dependencies
♻ Refactor ProofContainer into useProof (untested)
🚨 Format with prettier
This commit is contained in:
nikolark committed 2021-09-27 22:35:59 +02:00
1 parent bceaef16dc
commit b9268f6d46
14 files changed
+4665 -3244

No files matched your search

+4 -4
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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);
+3 -11
View File
@@ -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 () => {
-97
View File
@@ -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);
+78
View File
@@ -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;
+1 -1
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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) {
+4529 -3025
View File
File diff suppressed because it is too large. Load diff