Fix last typescript strict issues
This uses the `any` escape hatch a few more places, which should be tidied up later. Suggest upgrading redux usage to the latest version and start using slices to make a few things easier. Also the confetti component might be worth replacing Remove dead code from the old membership charge flow++
This commit is contained in:
19 files changed
+80
-178
No files matched your search
+7
-34
@@ -1,3 +1,5 @@
|
||||
import { UserType } from './types/data';
|
||||
|
||||
const LOGIN_REQUEST = 'LOGIN_REQUEST';
|
||||
const LOGIN_SUCCESS = 'LOGIN_SUCCESS';
|
||||
const LOGIN_FAILURE = 'LOGIN_FAILURE';
|
||||
@@ -8,9 +10,6 @@ const USER_DATA_FAILURE = 'USER_DATA_FAILURE';
|
||||
const REGISTER_USER_REQUEST = 'REGISTER_USER_REQUEST';
|
||||
const REGISTER_USER_SUCCESS = 'REGISTER_USER_SUCCESS';
|
||||
const REGISTER_USER_FAILURE = 'REGISTER_USER_FAILURE';
|
||||
const MEMBERSHIP_CHARGE_REQUEST = 'MEMBERSHIP_CHARGE_REQUEST';
|
||||
const MEMBERSHIP_CHARGE_SUCCESS = 'MEMBERSHIP_CHARGE_SUCCESS';
|
||||
const MEMBERSHIP_CHARGE_FAILURE = 'MEMBERSHIP_CHARGE_FAILURE';
|
||||
|
||||
function loginRequest() {
|
||||
return {
|
||||
@@ -26,7 +25,7 @@ function loginSuccess(username: string, token: string) {
|
||||
};
|
||||
}
|
||||
|
||||
function loginFailure(loginError) {
|
||||
function loginFailure(loginError: string) {
|
||||
return {
|
||||
type: LOGIN_FAILURE,
|
||||
loginError,
|
||||
@@ -45,14 +44,14 @@ function userDataRequest() {
|
||||
};
|
||||
}
|
||||
|
||||
function userDataSuccess(data) {
|
||||
function userDataSuccess(data: UserType) {
|
||||
return {
|
||||
type: USER_DATA_SUCCESS,
|
||||
data,
|
||||
};
|
||||
}
|
||||
|
||||
function userDataFailure(userError) {
|
||||
function userDataFailure(userError: string) {
|
||||
return {
|
||||
type: USER_DATA_FAILURE,
|
||||
userError,
|
||||
@@ -65,38 +64,19 @@ function registerUserRequest() {
|
||||
};
|
||||
}
|
||||
|
||||
function registerUserSuccess(data) {
|
||||
function registerUserSuccess(data: UserType) {
|
||||
return {
|
||||
type: REGISTER_USER_SUCCESS,
|
||||
data,
|
||||
};
|
||||
}
|
||||
|
||||
function registerUserFailure(registerError) {
|
||||
function registerUserFailure(registerError: Record<string, string>) {
|
||||
return {
|
||||
type: REGISTER_USER_FAILURE,
|
||||
registerError,
|
||||
};
|
||||
}
|
||||
function membershipChargeRequest() {
|
||||
return {
|
||||
type: MEMBERSHIP_CHARGE_REQUEST,
|
||||
};
|
||||
}
|
||||
|
||||
function membershipChargeSuccess(data) {
|
||||
return {
|
||||
type: MEMBERSHIP_CHARGE_SUCCESS,
|
||||
data,
|
||||
};
|
||||
}
|
||||
|
||||
function membershipChargeFailure(chargeError) {
|
||||
return {
|
||||
type: MEMBERSHIP_CHARGE_FAILURE,
|
||||
chargeError,
|
||||
};
|
||||
}
|
||||
|
||||
export {
|
||||
/* Login */
|
||||
@@ -123,11 +103,4 @@ export {
|
||||
registerUserSuccess,
|
||||
REGISTER_USER_FAILURE,
|
||||
registerUserFailure,
|
||||
/* Charge */
|
||||
MEMBERSHIP_CHARGE_REQUEST,
|
||||
membershipChargeRequest,
|
||||
MEMBERSHIP_CHARGE_SUCCESS,
|
||||
membershipChargeSuccess,
|
||||
MEMBERSHIP_CHARGE_FAILURE,
|
||||
membershipChargeFailure,
|
||||
};
|
||||
-38
@@ -8,9 +8,6 @@ import {
|
||||
userDataFailure,
|
||||
userDataRequest,
|
||||
userDataSuccess,
|
||||
membershipChargeFailure,
|
||||
membershipChargeRequest,
|
||||
membershipChargeSuccess,
|
||||
} from './actions';
|
||||
import type { UserRegisterFormValues } from './modules/register/useUserRegister';
|
||||
import { AppDispatch } from './reduxStore';
|
||||
@@ -118,41 +115,6 @@ export function requestRegisterUser({ firstName, lastName, email, phoneNumber, p
|
||||
};
|
||||
}
|
||||
|
||||
export function requestMembershipCharge(authToken: string, stripeToken: string, membershipType: string) {
|
||||
return (dispatch: AppDispatch) => {
|
||||
dispatch(membershipChargeRequest());
|
||||
|
||||
return fetchWithTimeout(`${process.env.EXPO_PUBLIC_DUSKEN_API_URL}/api/membership/charge/`, {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
Accept: 'application/json',
|
||||
'Content-Type': 'application/json',
|
||||
Authorization: `Token ${authToken}`,
|
||||
},
|
||||
body: JSON.stringify({
|
||||
stripe_token: stripeToken,
|
||||
membership_type: membershipType,
|
||||
payment_method: 'app',
|
||||
}),
|
||||
})
|
||||
.then((response) => response.json().then((json) => ({ json, response })))
|
||||
.then(({ json, response }) => {
|
||||
if (response.ok === false) {
|
||||
return Promise.reject(json);
|
||||
}
|
||||
return json;
|
||||
})
|
||||
.then(
|
||||
(data) => {
|
||||
dispatch(membershipChargeSuccess(data));
|
||||
},
|
||||
(data) => {
|
||||
dispatch(membershipChargeFailure(data.detail || 'Kunne ikke belaste bankkortet, prøv igjen'));
|
||||
},
|
||||
);
|
||||
};
|
||||
}
|
||||
|
||||
/** Create a stripe payment sheet. Note that this is not using redux */
|
||||
export async function requestStripePaymentSheet(authToken: string, membershipType: string) {
|
||||
const body = JSON.stringify({
|
||||
|
||||
@@ -2,7 +2,8 @@ import { Alert, HStack } from 'native-base';
|
||||
import React from 'react';
|
||||
import { Text } from 'react-native';
|
||||
|
||||
const FormErrors = ({ errors }) => {
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
const FormErrors = ({ errors }: { errors: Record<string, any> }) => {
|
||||
if (!Object.keys(errors).length || !['non_field_errors', 'detail'].some((errorField) => errorField in errors)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@@ -41,7 +41,7 @@ const FormInput = ({
|
||||
/>
|
||||
{fieldError && (
|
||||
<FormControl.ErrorMessage leftIcon={<WarningOutlineIcon size="xs" />}>
|
||||
<Text>{fieldError.message.toString()}</Text>
|
||||
<Text>{fieldError.message?.toString()}</Text>
|
||||
</FormControl.ErrorMessage>
|
||||
)}
|
||||
</FormControl>
|
||||
|
||||
+3
-3
@@ -6,8 +6,8 @@ import { useDispatch, useSelector, useStore } from 'react-redux';
|
||||
|
||||
import type { AppDispatch, AppStore, RootState } from './reduxStore';
|
||||
|
||||
export function usePrevious(value) {
|
||||
const ref = useRef();
|
||||
export function usePrevious<T>(value: T): T | undefined {
|
||||
const ref = useRef<T>();
|
||||
|
||||
// Store current value in ref
|
||||
useEffect(() => {
|
||||
@@ -23,7 +23,7 @@ export const useDeepLinking = () => {
|
||||
const { handleURLCallback } = useStripe();
|
||||
|
||||
const handleDeepLink = useCallback(
|
||||
async (url) => {
|
||||
async (url: string | null) => {
|
||||
if (!url) {
|
||||
return;
|
||||
}
|
||||
|
||||
+15
-12
@@ -21,10 +21,11 @@ const CHATEAU_NEUF_ADDRESS = 'Det Norske Studentersamfund, Slemdalsveien 15, 036
|
||||
|
||||
const openFacebook = () => {
|
||||
/* Ref: https://stackoverflow.com/questions/4810803/open-facebook-page-from-android-app */
|
||||
const url = Platform.select({
|
||||
ios: 'fb://page/?id=23664032291',
|
||||
android: 'fb://page/23664032291/',
|
||||
});
|
||||
const url =
|
||||
Platform.select({
|
||||
ios: 'fb://page/?id=23664032291',
|
||||
android: 'fb://page/23664032291/',
|
||||
}) || '';
|
||||
Linking.canOpenURL(url)
|
||||
.then((supported) => {
|
||||
if (!supported) {
|
||||
@@ -36,10 +37,11 @@ const openFacebook = () => {
|
||||
};
|
||||
|
||||
const openInstagram = () => {
|
||||
const url = Platform.select({
|
||||
ios: 'instagram://user?username=studentersamfundet',
|
||||
android: 'https://instagram.com/_u/studentersamfundet',
|
||||
});
|
||||
const url =
|
||||
Platform.select({
|
||||
ios: 'instagram://user?username=studentersamfundet',
|
||||
android: 'https://instagram.com/_u/studentersamfundet',
|
||||
}) || '';
|
||||
|
||||
Linking.canOpenURL(url)
|
||||
.then((supported) => {
|
||||
@@ -52,10 +54,11 @@ const openInstagram = () => {
|
||||
};
|
||||
|
||||
const openMap = () => {
|
||||
const url = Platform.select({
|
||||
ios: `comgooglemaps://?q=${CHATEAU_NEUF_ADDRESS}`,
|
||||
android: `https://www.google.com/maps/search/?api=1&query=${CHATEAU_NEUF_ADDRESS}`,
|
||||
});
|
||||
const url =
|
||||
Platform.select({
|
||||
ios: `comgooglemaps://?q=${CHATEAU_NEUF_ADDRESS}`,
|
||||
android: `https://www.google.com/maps/search/?api=1&query=${CHATEAU_NEUF_ADDRESS}`,
|
||||
}) || '';
|
||||
Linking.canOpenURL(url)
|
||||
.then((supported) => {
|
||||
if (!supported) {
|
||||
|
||||
@@ -69,10 +69,11 @@ const onFacebookUrlPress = (link: string) => {
|
||||
if (matches && matches.length) {
|
||||
iosURL = `fb://event/?id=${matches[1]}`;
|
||||
}
|
||||
const url = Platform.select({
|
||||
ios: iosURL,
|
||||
android: link,
|
||||
});
|
||||
const url =
|
||||
Platform.select({
|
||||
ios: iosURL,
|
||||
android: link,
|
||||
}) || '';
|
||||
Linking.canOpenURL(url)
|
||||
.then((supported) => {
|
||||
if (!supported) {
|
||||
|
||||
@@ -6,6 +6,7 @@ import { Button } from 'react-native-ui-lib';
|
||||
import 'moment/locale/nb';
|
||||
|
||||
import theme from 'src/theme';
|
||||
import { EventType } from 'src/types/data';
|
||||
|
||||
import useEventList from './useEventList';
|
||||
|
||||
@@ -15,7 +16,7 @@ const formatTime = (time: string) => {
|
||||
return moment(time).format('llll');
|
||||
};
|
||||
|
||||
const renderSectionHeader = ({ section }) => (
|
||||
const renderSectionHeader = ({ section }: { section: { title: string } }) => (
|
||||
<View style={styles.sectionHeader}>
|
||||
<Text style={styles.sectionTitle}>{section.title}</Text>
|
||||
</View>
|
||||
@@ -25,7 +26,7 @@ const Header = () => <View style={styles.listHeader} />;
|
||||
const EventList = () => {
|
||||
const { eventsSectioned, refreshing, handleRefresh, handleLoadMore, showEvent, error, loading } = useEventList();
|
||||
|
||||
const renderItem = ({ item }) => (
|
||||
const renderItem = ({ item }: { item: EventType }) => (
|
||||
<TouchableOpacity
|
||||
onPress={() => {
|
||||
showEvent(item);
|
||||
@@ -77,7 +78,7 @@ const EventList = () => {
|
||||
sections={eventsSectioned}
|
||||
renderItem={renderItem}
|
||||
renderSectionHeader={renderSectionHeader}
|
||||
keyExtractor={(item) => item.id}
|
||||
keyExtractor={(item: EventType) => item.id}
|
||||
refreshing={refreshing}
|
||||
onRefresh={handleRefresh}
|
||||
onEndReached={handleLoadMore}
|
||||
|
||||
@@ -8,25 +8,28 @@ import { fetchWithTimeout } from 'src/utils';
|
||||
|
||||
moment.locale('nb');
|
||||
|
||||
const formatDate = (time) => {
|
||||
const formatDate = (time: string) => {
|
||||
const m = moment(time);
|
||||
return moment().year() === m.year() ? m.format('dddd D. MMMM') : m.format('ll');
|
||||
};
|
||||
|
||||
type SectionedEvents = {
|
||||
data: EventType[];
|
||||
title: string;
|
||||
};
|
||||
/* Ref: https://medium.com/react-native-development/how-to-use-the-flatlist-component-react-native-basics-92c482816fe6 */
|
||||
const useEventList = () => {
|
||||
const { navigate } = useNavigation();
|
||||
|
||||
const [loading, setLoading] = useState(false);
|
||||
const [events, setEvents] = useState([]);
|
||||
const [eventsSectioned, setEventsSectioned] = useState([]);
|
||||
const [events, setEvents] = useState<EventType[]>([]);
|
||||
const [eventsSectioned, setEventsSectioned] = useState<SectionedEvents[]>([]);
|
||||
const [page, setPage] = useState(1);
|
||||
const [totalPages, setTotalPages] = useState(0);
|
||||
const [error, setError] = useState(null);
|
||||
const [error, setError] = useState<unknown>(null);
|
||||
const [refreshing, setRefreshing] = useState(false);
|
||||
|
||||
const toSectionFormat = (freshEvents) => {
|
||||
const sectioned = {};
|
||||
const toSectionFormat = (freshEvents: EventType[]) => {
|
||||
const sectioned: Record<string, EventType[]> = {};
|
||||
freshEvents.forEach((event) => {
|
||||
const title = formatDate(event.start_time);
|
||||
if (title in sectioned) {
|
||||
@@ -62,7 +65,7 @@ const useEventList = () => {
|
||||
let eventData;
|
||||
try {
|
||||
const res = await fetchWithTimeout(url);
|
||||
setTotalPages(parseInt(res.headers.get('x-wp-totalpages'), 10));
|
||||
setTotalPages(parseInt(res.headers.get('x-wp-totalpages') || '0', 10));
|
||||
eventData = await res.json();
|
||||
} catch (err) {
|
||||
setError(err);
|
||||
|
||||
@@ -75,14 +75,14 @@ interface PurchaseButtonProps {
|
||||
paymentLoading: boolean;
|
||||
isLoadingMembershipType: boolean;
|
||||
membershipPrice: number;
|
||||
onChargePress: () => void;
|
||||
onPurchasePress: () => void;
|
||||
}
|
||||
|
||||
const PurchaseButton = ({
|
||||
user,
|
||||
paymentLoading,
|
||||
isLoadingMembershipType,
|
||||
onChargePress,
|
||||
onPurchasePress,
|
||||
membershipPrice,
|
||||
}: PurchaseButtonProps) => {
|
||||
if (user.is_member) {
|
||||
@@ -93,7 +93,7 @@ const PurchaseButton = ({
|
||||
<View>
|
||||
{paymentLoading && <ActivityIndicator color={theme.colors.neufOrange} />}
|
||||
<Button
|
||||
onPress={onChargePress}
|
||||
onPress={onPurchasePress}
|
||||
fullWidth
|
||||
disabled={paymentLoading || isLoadingMembershipType}
|
||||
style={styles.purchaseButton}
|
||||
@@ -128,18 +128,6 @@ const Logo = ({ user }: { user: UserType }) => {
|
||||
);
|
||||
};
|
||||
|
||||
const ChargeError = ({ chargeError }) => {
|
||||
if (!chargeError) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return (
|
||||
<View>
|
||||
<Text style={styles.errorMessage}>{chargeError}</Text>
|
||||
</View>
|
||||
);
|
||||
};
|
||||
|
||||
const MembershipName = ({ user }: { user: UserType }) => {
|
||||
let name = `${user.first_name} ${user.last_name}`;
|
||||
if (name === ' ') {
|
||||
@@ -160,16 +148,7 @@ interface ProofProps {
|
||||
const Proof = ({ onLogoutPress, fetchUser, isFetchingUserData }: ProofProps) => {
|
||||
const CONFETTI_TIMEOUT = 15000;
|
||||
const confettiRef = useRef<Confetti>(null);
|
||||
const {
|
||||
chargeError,
|
||||
paymentLoading,
|
||||
isLoadingMembershipType,
|
||||
onChargePress,
|
||||
membershipPrice,
|
||||
toast,
|
||||
setToast,
|
||||
user,
|
||||
} = useProof({
|
||||
const { paymentLoading, isLoadingMembershipType, onPurchasePress, membershipPrice, toast, setToast, user } = useProof({
|
||||
fetchUser,
|
||||
});
|
||||
|
||||
@@ -183,7 +162,7 @@ const Proof = ({ onLogoutPress, fetchUser, isFetchingUserData }: ProofProps) =>
|
||||
});
|
||||
|
||||
const onMembershipPress = () => {
|
||||
confettiRef.current.startConfetti();
|
||||
confettiRef.current?.startConfetti();
|
||||
setTimeout(() => {
|
||||
if (confettiRef && confettiRef.current) {
|
||||
confettiRef.current.stopConfetti();
|
||||
@@ -208,13 +187,12 @@ const Proof = ({ onLogoutPress, fetchUser, isFetchingUserData }: ProofProps) =>
|
||||
<MembershipName user={user} />
|
||||
<Logo user={user} />
|
||||
<MembershipStatus user={user} onMembershipPress={onMembershipPress} />
|
||||
<ChargeError chargeError={chargeError} />
|
||||
<PurchaseButton
|
||||
user={user}
|
||||
paymentLoading={paymentLoading}
|
||||
isLoadingMembershipType={isLoadingMembershipType}
|
||||
membershipPrice={membershipPrice}
|
||||
onChargePress={onChargePress}
|
||||
onPurchasePress={onPurchasePress}
|
||||
/>
|
||||
<MembershipValidTo user={user} />
|
||||
</View>
|
||||
|
||||
@@ -10,10 +10,8 @@ const useMembership = () => {
|
||||
const dispatch = useAppDispatch();
|
||||
const isAuthenticated = useAppSelector((store) => store.isAuthenticated);
|
||||
const isFetchingUserData = useAppSelector((store) => store.isFetchingUserData);
|
||||
const lastOrder = useAppSelector((store) => store.lastOrder);
|
||||
const userToken = useAppSelector((store) => store.userToken);
|
||||
const prevUserToken = usePrevious(userToken);
|
||||
const prevLastOrder = usePrevious(lastOrder);
|
||||
|
||||
const onLogoutPress = () => {
|
||||
dispatch(logout());
|
||||
@@ -22,23 +20,19 @@ const useMembership = () => {
|
||||
const onRegisterPress = () => navigation.navigate('UserStack', { screen: 'UserRegister' });
|
||||
const onLoginPress = () => navigation.navigate('UserStack', { screen: 'Login' });
|
||||
const fetchUser = useCallback(() => {
|
||||
dispatch(requestUserData(userToken));
|
||||
dispatch(requestUserData(userToken || ''));
|
||||
}, [dispatch, userToken]);
|
||||
|
||||
useEffect(() => {
|
||||
if (userToken !== prevUserToken && userToken) {
|
||||
fetchUser();
|
||||
}
|
||||
if (prevLastOrder !== lastOrder && userToken) {
|
||||
fetchUser();
|
||||
}
|
||||
}, [userToken, prevUserToken, lastOrder, prevLastOrder, fetchUser]);
|
||||
}, [userToken, prevUserToken, fetchUser]);
|
||||
|
||||
return {
|
||||
isAuthenticated,
|
||||
isFetchingUserData,
|
||||
userToken,
|
||||
lastOrder,
|
||||
onLogoutPress,
|
||||
onRegisterPress,
|
||||
onLoginPress,
|
||||
|
||||
@@ -5,6 +5,7 @@ import { useEffect, useState } from 'react';
|
||||
|
||||
import { requestStripePaymentSheet } from 'src/api';
|
||||
import { useAppSelector } from 'src/hooks';
|
||||
import { ToastProps } from 'src/types/toast';
|
||||
|
||||
const PROOF_QUERY = gql`
|
||||
{
|
||||
@@ -15,16 +16,13 @@ const PROOF_QUERY = gql`
|
||||
}
|
||||
`;
|
||||
|
||||
interface ToastProps {
|
||||
message: string;
|
||||
visible: boolean;
|
||||
preset: 'failure' | 'success';
|
||||
interface UseProofProps {
|
||||
fetchUser: () => void;
|
||||
}
|
||||
|
||||
const useProof = ({ fetchUser }) => {
|
||||
const useProof = ({ fetchUser }: UseProofProps) => {
|
||||
const user = useAppSelector((state) => state.user);
|
||||
const userToken = useAppSelector((state) => state.userToken);
|
||||
const chargeError = useAppSelector((state) => state.chargeError);
|
||||
const { data, loading } = useQuery(PROOF_QUERY);
|
||||
const { initPaymentSheet, presentPaymentSheet } = useStripe();
|
||||
const [paymentLoading, setPaymentLoading] = useState(false);
|
||||
@@ -81,10 +79,9 @@ const useProof = ({ fetchUser }) => {
|
||||
|
||||
const membershipPrice = data?.membershipTypes?.[0]?.price;
|
||||
return {
|
||||
chargeError: chargeError || '',
|
||||
isLoadingMembershipType: loading,
|
||||
membershipPrice: membershipPrice ? membershipPrice / 100 : 0,
|
||||
onChargePress: openPaymentSheet,
|
||||
onPurchasePress: openPaymentSheet,
|
||||
paymentLoading,
|
||||
toast,
|
||||
setToast,
|
||||
|
||||
@@ -4,6 +4,7 @@ import { useForm } from 'react-hook-form';
|
||||
|
||||
import { requestRegisterUser } from 'src/api';
|
||||
import { useAppDispatch, useAppSelector, usePrevious } from 'src/hooks';
|
||||
import { ToastProps } from 'src/types/toast';
|
||||
|
||||
export interface UserRegisterFormValues {
|
||||
firstName: string;
|
||||
@@ -19,7 +20,7 @@ const useUserRegister = () => {
|
||||
const isAuthenticated = useAppSelector((store) => store.isAuthenticated);
|
||||
const isRegisteringUser = useAppSelector((store) => store.isRegisteringUser);
|
||||
const registerError = useAppSelector((store) => store.registerError) || {};
|
||||
const [toast, setToast] = useState(null);
|
||||
const [toast, setToast] = useState<ToastProps | null>(null);
|
||||
|
||||
const isAuthenticatedPrev = usePrevious(isAuthenticated);
|
||||
|
||||
|
||||
+8
-27
@@ -9,9 +9,6 @@ import {
|
||||
REGISTER_USER_REQUEST,
|
||||
REGISTER_USER_FAILURE,
|
||||
REGISTER_USER_SUCCESS,
|
||||
MEMBERSHIP_CHARGE_REQUEST,
|
||||
MEMBERSHIP_CHARGE_FAILURE,
|
||||
MEMBERSHIP_CHARGE_SUCCESS,
|
||||
} from './actions';
|
||||
import type { UserType } from './types/data';
|
||||
import { snakeToCamelCase } from './utils';
|
||||
@@ -21,15 +18,11 @@ interface RootState {
|
||||
isLoggingIn: boolean;
|
||||
isFetchingUserData: boolean;
|
||||
isRegisteringUser: boolean;
|
||||
isChargingMembership: boolean;
|
||||
userToken: string | null;
|
||||
user: UserType | null;
|
||||
// FIXME: Type rest
|
||||
loginError: unknown | null;
|
||||
userError: unknown | null;
|
||||
registerError: unknown | null;
|
||||
chargeError: unknown | null;
|
||||
lastOrder: unknown | null;
|
||||
loginError: Record<string, string> | null;
|
||||
userError: Record<string, string> | null;
|
||||
registerError: Record<string, string> | null;
|
||||
}
|
||||
|
||||
const initialState: RootState = {
|
||||
@@ -37,26 +30,24 @@ const initialState: RootState = {
|
||||
isLoggingIn: false,
|
||||
isFetchingUserData: false,
|
||||
isRegisteringUser: false,
|
||||
isChargingMembership: false,
|
||||
userToken: null,
|
||||
user: null,
|
||||
loginError: null,
|
||||
userError: null,
|
||||
registerError: null,
|
||||
chargeError: null,
|
||||
lastOrder: null,
|
||||
};
|
||||
|
||||
function formatErrors(errs) {
|
||||
const errors = {};
|
||||
function formatErrors(errs: Record<string, string | string[]>) {
|
||||
const errors: Record<string, string> = {};
|
||||
Object.entries(errs).forEach(([key, value]) => {
|
||||
errors[snakeToCamelCase(key)] = Array.isArray(value) ? value.join('\n') : value;
|
||||
});
|
||||
return errors;
|
||||
}
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/default-param-last
|
||||
export default function duskenApp(state = initialState, action) {
|
||||
// FIXME: refactor using redux slices
|
||||
// eslint-disable-next-line @typescript-eslint/default-param-last, @typescript-eslint/no-explicit-any
|
||||
export default function duskenApp(state = initialState, action: any) {
|
||||
switch (action.type) {
|
||||
/* Login */
|
||||
case LOGIN_REQUEST:
|
||||
@@ -97,16 +88,6 @@ export default function duskenApp(state = initialState, action) {
|
||||
|
||||
return { ...state, isRegisteringUser: false, isAuthenticated: true, user: userData, userToken: token };
|
||||
|
||||
/* Membership charge */
|
||||
case MEMBERSHIP_CHARGE_REQUEST:
|
||||
return { ...state, isChargingMembership: true, chargeError: null };
|
||||
|
||||
case MEMBERSHIP_CHARGE_FAILURE:
|
||||
return { ...state, isChargingMembership: false, chargeError: action.chargeError };
|
||||
|
||||
case MEMBERSHIP_CHARGE_SUCCESS:
|
||||
return { ...state, isChargingMembership: false, lastOrder: action.data };
|
||||
|
||||
default:
|
||||
return state;
|
||||
}
|
||||
|
||||
+1
-1
@@ -8,7 +8,7 @@ import rootReducer from './reducers';
|
||||
const persistConfig = {
|
||||
key: 'root',
|
||||
storage: AsyncStorage,
|
||||
blacklist: ['isLoggingIn', 'isFetchingUserData', 'isRegisteringUser', 'isChargingMembership', 'loginError'],
|
||||
blacklist: ['isLoggingIn', 'isFetchingUserData', 'isRegisteringUser', 'loginError'],
|
||||
};
|
||||
|
||||
const persistedReducer = persistReducer(persistConfig, rootReducer);
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
export interface EventType {
|
||||
id: string;
|
||||
start_time: string;
|
||||
end_time: string;
|
||||
content: {
|
||||
|
||||
@@ -0,0 +1,5 @@
|
||||
export interface ToastProps {
|
||||
message: string;
|
||||
visible: boolean;
|
||||
preset: 'failure' | 'success';
|
||||
}
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
export function snakeToCamelCase(val: string) {
|
||||
export function snakeToCamelCase(val: string): string {
|
||||
return val.replace(/_([a-z])/g, (g) => g[1].toUpperCase());
|
||||
}
|
||||
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
{
|
||||
"compilerOptions": {
|
||||
"baseUrl": "./",
|
||||
"strict": true
|
||||
},
|
||||
"extends": "expo/tsconfig.base"
|
||||
}
|
||||
Reference in new issue
Block a user