mirror of
https://github.com/kingomarnajjar/flex-template-web.git
synced 2026-07-28 12:43:11 +10:00
527 lines
16 KiB
JavaScript
527 lines
16 KiB
JavaScript
import omitBy from 'lodash/omitBy';
|
|
import isUndefined from 'lodash/isUndefined';
|
|
import config from '../config';
|
|
import { denormalisedResponseEntities, ensureOwnListing } from '../util/data';
|
|
import { storableError } from '../util/errors';
|
|
import { transitionsToRequested } from '../util/transaction';
|
|
import { LISTING_STATE_DRAFT } from '../util/types';
|
|
import * as log from '../util/log';
|
|
import { authInfo } from './Auth.duck';
|
|
|
|
// ================ Action types ================ //
|
|
|
|
export const CURRENT_USER_SHOW_REQUEST = 'app/user/CURRENT_USER_SHOW_REQUEST';
|
|
export const CURRENT_USER_SHOW_SUCCESS = 'app/user/CURRENT_USER_SHOW_SUCCESS';
|
|
export const CURRENT_USER_SHOW_ERROR = 'app/user/CURRENT_USER_SHOW_ERROR';
|
|
|
|
export const CLEAR_CURRENT_USER = 'app/user/CLEAR_CURRENT_USER';
|
|
|
|
export const STRIPE_ACCOUNT_CREATE_REQUEST = 'app/user/STRIPE_ACCOUNT_CREATE_REQUEST';
|
|
export const STRIPE_ACCOUNT_CREATE_SUCCESS = 'app/user/STRIPE_ACCOUNT_CREATE_SUCCESS';
|
|
export const STRIPE_ACCOUNT_CREATE_ERROR = 'app/user/STRIPE_ACCOUNT_CREATE_ERROR';
|
|
|
|
export const STRIPE_ACCOUNT_CLEAR_ERROR = 'app/user/STRIPE_ACCOUNT_CLEAR_ERROR';
|
|
|
|
export const FETCH_CURRENT_USER_HAS_LISTINGS_REQUEST =
|
|
'app/user/FETCH_CURRENT_USER_HAS_LISTINGS_REQUEST';
|
|
export const FETCH_CURRENT_USER_HAS_LISTINGS_SUCCESS =
|
|
'app/user/FETCH_CURRENT_USER_HAS_LISTINGS_SUCCESS';
|
|
export const FETCH_CURRENT_USER_HAS_LISTINGS_ERROR =
|
|
'app/user/FETCH_CURRENT_USER_HAS_LISTINGS_ERROR';
|
|
|
|
export const FETCH_CURRENT_USER_NOTIFICATIONS_REQUEST =
|
|
'app/user/FETCH_CURRENT_USER_NOTIFICATIONS_REQUEST';
|
|
export const FETCH_CURRENT_USER_NOTIFICATIONS_SUCCESS =
|
|
'app/user/FETCH_CURRENT_USER_NOTIFICATIONS_SUCCESS';
|
|
export const FETCH_CURRENT_USER_NOTIFICATIONS_ERROR =
|
|
'app/user/FETCH_CURRENT_USER_NOTIFICATIONS_ERROR';
|
|
|
|
export const FETCH_CURRENT_USER_HAS_ORDERS_REQUEST =
|
|
'app/user/FETCH_CURRENT_USER_HAS_ORDERS_REQUEST';
|
|
export const FETCH_CURRENT_USER_HAS_ORDERS_SUCCESS =
|
|
'app/user/FETCH_CURRENT_USER_HAS_ORDERS_SUCCESS';
|
|
export const FETCH_CURRENT_USER_HAS_ORDERS_ERROR = 'app/user/FETCH_CURRENT_USER_HAS_ORDERS_ERROR';
|
|
|
|
export const SEND_VERIFICATION_EMAIL_REQUEST = 'app/user/SEND_VERIFICATION_EMAIL_REQUEST';
|
|
export const SEND_VERIFICATION_EMAIL_SUCCESS = 'app/user/SEND_VERIFICATION_EMAIL_SUCCESS';
|
|
export const SEND_VERIFICATION_EMAIL_ERROR = 'app/user/SEND_VERIFICATION_EMAIL_ERROR';
|
|
|
|
// ================ Reducer ================ //
|
|
|
|
const initialState = {
|
|
currentUser: null,
|
|
currentUserShowError: null,
|
|
createStripeAccountInProgress: false,
|
|
createStripeAccountError: null,
|
|
currentUserHasListings: false,
|
|
currentUserHasListingsError: null,
|
|
currentUserNotificationCount: 0,
|
|
currentUserNotificationCountError: null,
|
|
currentUserHasOrders: null, // This is not fetched unless unverified emails exist
|
|
currentUserHasOrdersError: null,
|
|
sendVerificationEmailInProgress: false,
|
|
sendVerificationEmailError: null,
|
|
};
|
|
|
|
export default function reducer(state = initialState, action = {}) {
|
|
const { type, payload } = action;
|
|
switch (type) {
|
|
case CURRENT_USER_SHOW_REQUEST:
|
|
return { ...state, currentUserShowError: null };
|
|
case CURRENT_USER_SHOW_SUCCESS:
|
|
return { ...state, currentUser: payload };
|
|
case CURRENT_USER_SHOW_ERROR:
|
|
// eslint-disable-next-line no-console
|
|
console.error(payload);
|
|
return { ...state, currentUserShowError: payload };
|
|
|
|
case CLEAR_CURRENT_USER:
|
|
return {
|
|
...state,
|
|
currentUser: null,
|
|
currentUserShowError: null,
|
|
currentUserHasListings: false,
|
|
currentUserHasListingsError: null,
|
|
currentUserNotificationCount: 0,
|
|
currentUserNotificationCountError: null,
|
|
};
|
|
|
|
case FETCH_CURRENT_USER_HAS_LISTINGS_REQUEST:
|
|
return { ...state, currentUserHasListingsError: null };
|
|
case FETCH_CURRENT_USER_HAS_LISTINGS_SUCCESS:
|
|
return { ...state, currentUserHasListings: payload.hasListings };
|
|
case FETCH_CURRENT_USER_HAS_LISTINGS_ERROR:
|
|
console.error(payload); // eslint-disable-line
|
|
return { ...state, currentUserHasListingsError: payload };
|
|
|
|
case FETCH_CURRENT_USER_NOTIFICATIONS_REQUEST:
|
|
return { ...state, currentUserNotificationCountError: null };
|
|
case FETCH_CURRENT_USER_NOTIFICATIONS_SUCCESS:
|
|
return { ...state, currentUserNotificationCount: payload.transactions.length };
|
|
case FETCH_CURRENT_USER_NOTIFICATIONS_ERROR:
|
|
console.error(payload); // eslint-disable-line
|
|
return { ...state, currentUserNotificationCountError: payload };
|
|
|
|
case FETCH_CURRENT_USER_HAS_ORDERS_REQUEST:
|
|
return { ...state, currentUserHasOrdersError: null };
|
|
case FETCH_CURRENT_USER_HAS_ORDERS_SUCCESS:
|
|
return { ...state, currentUserHasOrders: payload.hasOrders };
|
|
case FETCH_CURRENT_USER_HAS_ORDERS_ERROR:
|
|
console.error(payload); // eslint-disable-line
|
|
return { ...state, currentUserHasOrdersError: payload };
|
|
|
|
case STRIPE_ACCOUNT_CREATE_REQUEST:
|
|
return { ...state, createStripeAccountError: null, createStripeAccountInProgress: true };
|
|
case STRIPE_ACCOUNT_CREATE_SUCCESS:
|
|
return { ...state, createStripeAccountInProgress: false };
|
|
case STRIPE_ACCOUNT_CREATE_ERROR:
|
|
// eslint-disable-next-line no-console
|
|
console.error(payload);
|
|
return { ...state, createStripeAccountError: payload, createStripeAccountInProgress: false };
|
|
|
|
case STRIPE_ACCOUNT_CLEAR_ERROR:
|
|
return { ...state, createStripeAccountError: null, createStripeAccountInProgress: false };
|
|
|
|
case SEND_VERIFICATION_EMAIL_REQUEST:
|
|
return {
|
|
...state,
|
|
sendVerificationEmailInProgress: true,
|
|
sendVerificationEmailError: null,
|
|
};
|
|
case SEND_VERIFICATION_EMAIL_SUCCESS:
|
|
return {
|
|
...state,
|
|
sendVerificationEmailInProgress: false,
|
|
};
|
|
case SEND_VERIFICATION_EMAIL_ERROR:
|
|
return {
|
|
...state,
|
|
sendVerificationEmailInProgress: false,
|
|
sendVerificationEmailError: payload,
|
|
};
|
|
|
|
default:
|
|
return state;
|
|
}
|
|
}
|
|
|
|
// ================ Selectors ================ //
|
|
|
|
export const hasCurrentUserErrors = state => {
|
|
const { user } = state;
|
|
return (
|
|
user.currentUserShowError ||
|
|
user.currentUserHasListingsError ||
|
|
user.currentUserNotificationCountError ||
|
|
user.currentUserHasOrdersError
|
|
);
|
|
};
|
|
|
|
export const verificationSendingInProgress = state => {
|
|
return state.user.sendVerificationEmailInProgress;
|
|
};
|
|
|
|
// ================ Action creators ================ //
|
|
|
|
export const currentUserShowRequest = () => ({ type: CURRENT_USER_SHOW_REQUEST });
|
|
|
|
export const currentUserShowSuccess = user => ({
|
|
type: CURRENT_USER_SHOW_SUCCESS,
|
|
payload: user,
|
|
});
|
|
|
|
export const currentUserShowError = e => ({
|
|
type: CURRENT_USER_SHOW_ERROR,
|
|
payload: e,
|
|
error: true,
|
|
});
|
|
|
|
export const clearCurrentUser = () => ({ type: CLEAR_CURRENT_USER });
|
|
|
|
export const stripeAccountCreateRequest = () => ({ type: STRIPE_ACCOUNT_CREATE_REQUEST });
|
|
|
|
export const stripeAccountCreateSuccess = response => ({
|
|
type: STRIPE_ACCOUNT_CREATE_SUCCESS,
|
|
payload: response,
|
|
});
|
|
|
|
export const stripeAccountCreateError = e => ({
|
|
type: STRIPE_ACCOUNT_CREATE_ERROR,
|
|
payload: e,
|
|
error: true,
|
|
});
|
|
|
|
export const stripeAccountClearError = () => ({
|
|
type: STRIPE_ACCOUNT_CLEAR_ERROR,
|
|
});
|
|
|
|
const fetchCurrentUserHasListingsRequest = () => ({
|
|
type: FETCH_CURRENT_USER_HAS_LISTINGS_REQUEST,
|
|
});
|
|
|
|
export const fetchCurrentUserHasListingsSuccess = hasListings => ({
|
|
type: FETCH_CURRENT_USER_HAS_LISTINGS_SUCCESS,
|
|
payload: { hasListings },
|
|
});
|
|
|
|
const fetchCurrentUserHasListingsError = e => ({
|
|
type: FETCH_CURRENT_USER_HAS_LISTINGS_ERROR,
|
|
error: true,
|
|
payload: e,
|
|
});
|
|
|
|
const fetchCurrentUserNotificationsRequest = () => ({
|
|
type: FETCH_CURRENT_USER_NOTIFICATIONS_REQUEST,
|
|
});
|
|
|
|
export const fetchCurrentUserNotificationsSuccess = transactions => ({
|
|
type: FETCH_CURRENT_USER_NOTIFICATIONS_SUCCESS,
|
|
payload: { transactions },
|
|
});
|
|
|
|
const fetchCurrentUserNotificationsError = e => ({
|
|
type: FETCH_CURRENT_USER_NOTIFICATIONS_ERROR,
|
|
error: true,
|
|
payload: e,
|
|
});
|
|
|
|
const fetchCurrentUserHasOrdersRequest = () => ({
|
|
type: FETCH_CURRENT_USER_HAS_ORDERS_REQUEST,
|
|
});
|
|
|
|
export const fetchCurrentUserHasOrdersSuccess = hasOrders => ({
|
|
type: FETCH_CURRENT_USER_HAS_ORDERS_SUCCESS,
|
|
payload: { hasOrders },
|
|
});
|
|
|
|
const fetchCurrentUserHasOrdersError = e => ({
|
|
type: FETCH_CURRENT_USER_HAS_ORDERS_ERROR,
|
|
error: true,
|
|
payload: e,
|
|
});
|
|
|
|
export const sendVerificationEmailRequest = () => ({
|
|
type: SEND_VERIFICATION_EMAIL_REQUEST,
|
|
});
|
|
|
|
export const sendVerificationEmailSuccess = () => ({
|
|
type: SEND_VERIFICATION_EMAIL_SUCCESS,
|
|
});
|
|
|
|
export const sendVerificationEmailError = e => ({
|
|
type: SEND_VERIFICATION_EMAIL_ERROR,
|
|
error: true,
|
|
payload: e,
|
|
});
|
|
|
|
// ================ Thunks ================ //
|
|
|
|
export const fetchCurrentUserHasListings = () => (dispatch, getState, sdk) => {
|
|
dispatch(fetchCurrentUserHasListingsRequest());
|
|
const { currentUser } = getState().user;
|
|
|
|
if (!currentUser) {
|
|
dispatch(fetchCurrentUserHasListingsSuccess(false));
|
|
return Promise.resolve(null);
|
|
}
|
|
|
|
const params = {
|
|
// Since we are only interested in if the user has
|
|
// listings, we only need at most one result.
|
|
page: 1,
|
|
per_page: 1,
|
|
};
|
|
|
|
return sdk.ownListings
|
|
.query(params)
|
|
.then(response => {
|
|
const hasListings = response.data.data && response.data.data.length > 0;
|
|
|
|
const hasPublishedListings =
|
|
hasListings &&
|
|
ensureOwnListing(response.data.data[0]).attributes.state !== LISTING_STATE_DRAFT;
|
|
dispatch(fetchCurrentUserHasListingsSuccess(!!hasPublishedListings));
|
|
})
|
|
.catch(e => dispatch(fetchCurrentUserHasListingsError(storableError(e))));
|
|
};
|
|
|
|
export const fetchCurrentUserHasOrders = () => (dispatch, getState, sdk) => {
|
|
dispatch(fetchCurrentUserHasOrdersRequest());
|
|
|
|
if (!getState().user.currentUser) {
|
|
dispatch(fetchCurrentUserHasOrdersSuccess(false));
|
|
return Promise.resolve(null);
|
|
}
|
|
|
|
const params = {
|
|
only: 'order',
|
|
page: 1,
|
|
per_page: 1,
|
|
};
|
|
|
|
return sdk.transactions
|
|
.query(params)
|
|
.then(response => {
|
|
const hasOrders = response.data.data && response.data.data.length > 0;
|
|
dispatch(fetchCurrentUserHasOrdersSuccess(!!hasOrders));
|
|
})
|
|
.catch(e => dispatch(fetchCurrentUserHasOrdersError(storableError(e))));
|
|
};
|
|
|
|
// Notificaiton page size is max (100 items on page)
|
|
const NOTIFICATION_PAGE_SIZE = 100;
|
|
|
|
export const fetchCurrentUserNotifications = () => (dispatch, getState, sdk) => {
|
|
dispatch(fetchCurrentUserNotificationsRequest());
|
|
|
|
const apiQueryParams = {
|
|
only: 'sale',
|
|
last_transitions: transitionsToRequested,
|
|
page: 1,
|
|
per_page: NOTIFICATION_PAGE_SIZE,
|
|
};
|
|
|
|
sdk.transactions
|
|
.query(apiQueryParams)
|
|
.then(response => {
|
|
const transactions = response.data.data;
|
|
dispatch(fetchCurrentUserNotificationsSuccess(transactions));
|
|
})
|
|
.catch(e => dispatch(fetchCurrentUserNotificationsError(storableError(e))));
|
|
};
|
|
|
|
export const fetchCurrentUser = () => (dispatch, getState, sdk) => {
|
|
dispatch(currentUserShowRequest());
|
|
const { isAuthenticated } = getState().Auth;
|
|
|
|
if (!isAuthenticated) {
|
|
// Make sure current user is null
|
|
dispatch(currentUserShowSuccess(null));
|
|
return Promise.resolve({});
|
|
}
|
|
|
|
const params = {
|
|
include: ['profileImage'],
|
|
'fields.image': ['variants.square-small', 'variants.square-small2x'],
|
|
};
|
|
|
|
return sdk.currentUser
|
|
.show(params)
|
|
.then(response => {
|
|
const entities = denormalisedResponseEntities(response);
|
|
if (entities.length !== 1) {
|
|
throw new Error('Expected a resource in the sdk.currentUser.show response');
|
|
}
|
|
const currentUser = entities[0];
|
|
|
|
// set current user id to the logger
|
|
log.setUserId(currentUser.id.uuid);
|
|
dispatch(currentUserShowSuccess(currentUser));
|
|
return currentUser;
|
|
})
|
|
.then(currentUser => {
|
|
dispatch(fetchCurrentUserHasListings());
|
|
dispatch(fetchCurrentUserNotifications());
|
|
if (!currentUser.attributes.emailVerified) {
|
|
dispatch(fetchCurrentUserHasOrders());
|
|
}
|
|
|
|
// Make sure auth info is up to date
|
|
dispatch(authInfo());
|
|
})
|
|
.catch(e => {
|
|
// Make sure auth info is up to date
|
|
dispatch(authInfo());
|
|
log.error(e, 'fetch-current-user-failed');
|
|
dispatch(currentUserShowError(storableError(e)));
|
|
});
|
|
};
|
|
|
|
export const createStripeAccount = payoutDetails => (dispatch, getState, sdk) => {
|
|
if (typeof window === 'undefined' || !window.Stripe) {
|
|
throw new Error('Stripe must be loaded for submitting PayoutPreferences');
|
|
}
|
|
|
|
const stripe = window.Stripe(config.stripe.publishableKey);
|
|
|
|
dispatch(stripeAccountCreateRequest());
|
|
|
|
const { accountType, country } = payoutDetails;
|
|
|
|
let payoutDetailValues;
|
|
if (accountType === 'company') {
|
|
payoutDetailValues = payoutDetails['company'];
|
|
} else {
|
|
payoutDetailValues = payoutDetails['individual'];
|
|
}
|
|
|
|
const {
|
|
firstName,
|
|
lastName,
|
|
birthDate,
|
|
address,
|
|
bankAccountToken,
|
|
personalIdNumber,
|
|
companyName,
|
|
companyTaxId,
|
|
personalAddress,
|
|
additionalOwners,
|
|
} = payoutDetailValues;
|
|
|
|
const hasProvince = address.province && !address.state;
|
|
|
|
const addressValue = {
|
|
city: address.city,
|
|
line1: address.streetAddress,
|
|
postal_code: address.postalCode,
|
|
state: hasProvince ? address.province : address.state ? address.state : '',
|
|
};
|
|
|
|
let personalAddressValue;
|
|
if (personalAddress) {
|
|
personalAddressValue = {
|
|
city: personalAddress.city,
|
|
line1: personalAddress.streetAddress,
|
|
postal_code: personalAddress.postalCode,
|
|
state: hasProvince
|
|
? personalAddress.province
|
|
: personalAddress.state
|
|
? personalAddress.state
|
|
: '',
|
|
};
|
|
}
|
|
|
|
const additionalOwnersValue = additionalOwners
|
|
? additionalOwners.map(owner => {
|
|
return {
|
|
first_name: owner.firstName,
|
|
last_name: owner.lastName,
|
|
dob: owner.birthDate,
|
|
address: {
|
|
city: owner.city,
|
|
line1: owner.streetAddress,
|
|
postal_code: owner.postalCode,
|
|
state: hasProvince ? owner.province : owner.state ? owner.state : '',
|
|
},
|
|
};
|
|
})
|
|
: [];
|
|
|
|
const idNumber =
|
|
country === 'US' ? { ssn_last_4: personalIdNumber } : { personal_id_number: personalIdNumber };
|
|
|
|
let params;
|
|
|
|
// You can check which API version you are using from Stripe Dasboard -> Developers.
|
|
// If you are using older version than '2019-02-19'
|
|
// edit 'useDeprecatedLegalEntityWithStripe' config in the stripe-config.js
|
|
|
|
const isNewAPI = !config.stripe.useDeprecatedLegalEntityWithStripe;
|
|
|
|
if (isNewAPI) {
|
|
params = {
|
|
business_type: 'individual',
|
|
individual: {
|
|
first_name: firstName,
|
|
last_name: lastName,
|
|
address: omitBy(addressValue, isUndefined),
|
|
dob: birthDate,
|
|
...idNumber,
|
|
},
|
|
tos_shown_and_accepted: true,
|
|
};
|
|
} else {
|
|
params = {
|
|
legal_entity: {
|
|
first_name: firstName,
|
|
last_name: lastName,
|
|
address: omitBy(addressValue, isUndefined),
|
|
dob: birthDate,
|
|
type: accountType,
|
|
business_name: companyName,
|
|
business_tax_id: companyTaxId,
|
|
personal_address: personalAddressValue,
|
|
additional_owners: additionalOwnersValue,
|
|
...idNumber,
|
|
},
|
|
tos_shown_and_accepted: true,
|
|
};
|
|
}
|
|
|
|
let accountResponse;
|
|
|
|
return stripe
|
|
.createToken('account', params)
|
|
.then(response => {
|
|
const accountToken = response.token.id;
|
|
return sdk.currentUser.createStripeAccount({ accountToken, bankAccountToken, country });
|
|
})
|
|
.then(response => {
|
|
accountResponse = response;
|
|
return dispatch(fetchCurrentUser());
|
|
})
|
|
.then(() => {
|
|
dispatch(stripeAccountCreateSuccess(accountResponse));
|
|
})
|
|
.catch(err => {
|
|
const e = storableError(err);
|
|
dispatch(stripeAccountCreateError(e));
|
|
const stripeMessage =
|
|
e.apiErrors && e.apiErrors.length > 0 && e.apiErrors[0].meta
|
|
? e.apiErrors[0].meta.stripeMessage
|
|
: null;
|
|
log.error(err, 'create-stripe-account-failed', { stripeMessage });
|
|
throw e;
|
|
});
|
|
};
|
|
|
|
export const sendVerificationEmail = () => (dispatch, getState, sdk) => {
|
|
if (verificationSendingInProgress(getState())) {
|
|
return Promise.reject(new Error('Verification email sending already in progress'));
|
|
}
|
|
dispatch(sendVerificationEmailRequest());
|
|
return sdk.currentUser
|
|
.sendVerificationEmail()
|
|
.then(() => dispatch(sendVerificationEmailSuccess()))
|
|
.catch(e => dispatch(sendVerificationEmailError(storableError(e))));
|
|
};
|