Skip to content

Instantly share code, notes, and snippets.

@kevincarpdev
Last active April 8, 2023 03:35
Show Gist options
  • Save kevincarpdev/691121880969df0edd34c84e2a63cce2 to your computer and use it in GitHub Desktop.
Save kevincarpdev/691121880969df0edd34c84e2a63cce2 to your computer and use it in GitHub Desktop.
import React, { useState, useEffect, useRef } from 'react';
import { useNavigation } from '@react-navigation/native';
import {
View,
TouchableOpacity,
ScrollView,
StyleSheet,
NativeSyntheticEvent,
NativeScrollEvent,
Animated,
Easing,
} from 'react-native';
import { Text } from 'react-native-paper';
import {
CONSTANT_COLOR as CC,
CONSTANT_SIZE,
GLOBAL_STYLE as GS,
icons,
} from '../../../../assets';
import {
BButton,
BIcon,
BInput,
BSwitch,
KYCAlert,
SizedBox,
} from '../../../../components';
import { useAuthContext } from '../../../../context/auth/auth.context';
import { useAppContext } from '../../../../context/auth/app.context';
import { DRAWER_ROUTES_NAMES } from '../../../../router/DrawerNavigator';
import {
AccountLayout,
AccountListItem,
AccountLoggedHeader,
AccountPromotions,
} from '../components';
import mobxStore from '../../../../store/RootStore';
import { observer } from 'mobx-react';
import { useKycUrl } from '../../../../hooks/useKycUrl';
import moment from 'moment';
import AsyncStorage from '@react-native-async-storage/async-storage';
import { useMyPromotions } from '../../../../hooks/useMyPromotions';
type TAB_PROPS =
| 'wallet'
| 'account_details'
| 'responsible_gaming'
| 'promotion_bonuses'
| 'preferences';
const TABS = [
{
title: 'Wallet',
key: 'wallet',
},
{
title: 'Account Details',
key: 'account_details',
},
{
title: 'Responsible Gaming',
key: 'responsible_gaming',
},
{
title: 'Promotion & Bonuses',
key: 'promotion_bonuses',
},
{ title: 'Preferences', key: 'preferences' },
];
const TabView = ({
routes,
}: {
routes: Array<{ title: string; route: string | null }>;
}) => {
const navigation = useNavigation();
return (
<View>
{routes.map((r, k) => {
const isLast = k === routes.length - 1;
return (
<AccountListItem
key={k}
title={r.title}
onPress={() => {
if (r.route != null) {
navigation.navigate(r.route as never);
}
}}
isLast={isLast}
/>
);
})}
</View>
);
};
const PersonalInfoView = () => {
const navigation = useNavigation();
const { userInfo } = useAuthContext();
const profile = React.useMemo(
() => userInfo?.profile || null,
[userInfo?.profile],
);
const dateOfBirth = React.useMemo(
() =>
profile?.dateOfBirth ? moment(profile?.dateOfBirth).format('L') : '',
[profile?.dateOfBirth],
);
return (
<View
style={{
padding: CONSTANT_SIZE.padding,
backgroundColor: 'rgba(0,0,0,0.25)',
marginTop: CONSTANT_SIZE.SPACE_LG,
borderRadius: 20,
}}>
<View style={[GS.row, GS.alignCenter]}>
<BIcon
icon={icons.lock_icon_symbol}
style={{ height: 18, width: 18, resizeMode: 'contain' }}
/>
<SizedBox width={10} />
<Text
style={{
...GS.FF_PoppinsSemiBold,
fontSize: 18,
lineHeight: 24,
textTransform: 'uppercase',
color: 'white',
}}>
Personal details
</Text>
</View>
<Text
style={[
GS.txtUpper,
GS.txtWhite,
GS.txtSm,
{ opacity: 0.8, marginTop: 15 },
]}>
TO EDIT THESE DETAILS, PLEASE CONTACT CUSTOMER SERVICE ON 406-528-3926.
YOU WIL HAVE TO RE-VERIFY ANY UPDATES YOU MAKE FOR SECURITY REASONS.
</Text>
<View
style={{
marginTop: 15,
marginBottom: 20,
backgroundColor: 'white',
opacity: 0.25,
height: 2,
width: '100%',
}}
/>
{/* GENDER */}
<BInput
label="Title"
value={profile?.titleName}
style={GS.mb3}
editable={false}
readonly
textUpper
/>
{/* */}
<View style={[GS.row, GS.mb3]}>
<View style={GS.flex1}>
<BInput
label="First name"
value={profile?.firstName}
editable={false}
readonly
textUpper
/>
</View>
<SizedBox width={10} />
<View style={GS.flex1}>
<BInput
label="Last name"
value={profile?.lastName}
editable={false}
readonly
textUpper
/>
</View>
</View>
{/* GENDER */}
<BInput
label="Date of birth"
value={dateOfBirth}
editable={false}
readonly
/>
{/* */}
<View style={{ marginTop: 25 }}>
<BButton
title="View more"
onPress={() =>
navigation.navigate(
DRAWER_ROUTES_NAMES.ACCOUNT__CHANGE_PERSONAL_INFORMATION as never,
)
}
mode="primary"
/>
</View>
</View>
);
};
const LoggedInScreeen = () => {
const navigation = useNavigation();
const [currentIndex, setCurrentIndex] = React.useState(0);
const [currentTab, setCurrentTab] = React.useState<TAB_PROPS>('wallet');
const { userInfo, supportedBiometry, isAuthDataLoading } = useAuthContext();
const { reAuthorize, walletData, refetchWallet } = useAuthContext();
const { isAppDataLoading, appData } = useAppContext();
const [isWalletLoading, setIsWalletLoading] = React.useState(false);
const [isWithdrawDisabled, setIsWithdrawDisabled] = React.useState(false);
const [isDepositDisabled, setIsDepositDisabled] = React.useState(false);
const [spinAnim, setSpinAnim] = useState(new Animated.Value(0));
const { data: kycUrl, fetch: getKycUrl } = useKycUrl({
key: `${userInfo?.profile.username}_kyc_url`,
config: { enabled: false },
});
// Set up our initial parameters for the promotions query
let promotionsParams: any = {
pagination: {
pageNumber: 1,
itemsPerPage: 50,
totalItems: 0,
totalPages: 0,
},
};
// push our status filter to the filters array
promotionsParams?.filters?.push({
name: 'status',
value: [
{
condition: 'INCLUDE',
logic: 'INCLUDE',
value: 'available',
},
],
});
// get total number of available promotions via useMyPromotions hook
const { data, isLoading } = useMyPromotions('available', promotionsParams);
// If it is not loading get the data
var totalAvailablePromotions = data?.response?.promotions?.length || 0;
if (!isLoading) {
totalAvailablePromotions = data?.response?.promotions?.length || 0;
}
// get
const WALLET_ROUTES = [
{
title: 'Gaming history',
route: DRAWER_ROUTES_NAMES.ACCOUNT__GAMING_HISTORY_STEP_1,
},
{
title: 'Transaction history',
route: DRAWER_ROUTES_NAMES.ACCOUNT__TRANSACTION_HISTORY,
},
{
title: 'Deposit',
route: DRAWER_ROUTES_NAMES.WALLET__DEPOSIT,
},
{
title: 'Withdraw',
route: DRAWER_ROUTES_NAMES.WALLET__WITHDRAW,
},
{
title: 'Manage Payment Method',
route: DRAWER_ROUTES_NAMES.WALLET__MANAGE_PAYMENT_METHODS,
}
];
const ACCOUNT_DETAILS_ROUTES = [
{
title: 'Change Username',
route: DRAWER_ROUTES_NAMES.ACCOUNT__CHANGE_USERNAME,
},
{
title: 'Change Password',
route: DRAWER_ROUTES_NAMES.ACCOUNT__CHANGE_PASSWORD,
},
{
title: 'Change Address',
route: DRAWER_ROUTES_NAMES.ACCOUNT__CHANGE_ADDRESS,
},
{
title: 'Change Email',
route: DRAWER_ROUTES_NAMES.ACCOUNT__CHANGE_EMAIL,
},
// {
// title: 'Change Security Question',
// route: DRAWER_ROUTES_NAMES.ACCOUNT__CHANGE_SECURITY_QUESTIONS,
// },
{
title: 'Change Mobile Number',
route: DRAWER_ROUTES_NAMES.ACCOUNT__CHANGE_MOBILE_NUMBER,
},
// {
// title: 'Edit Communication Preferences',
// route: DRAWER_ROUTES_NAMES.ACCOUNT__EDIT_COMMUNICATION_PREFERENCES,
// },
];
const RESPONSIBLE_GAMING_ROUTES = [
{
title: 'Deposit Limits',
route: DRAWER_ROUTES_NAMES.ACCOUNT__CHANGE_DEPOSIT_LIMIT_STEP_1,
},
{
title: 'Withdraw Limits',
route: DRAWER_ROUTES_NAMES.ACCOUNT__CHANGE_WITHDRAW_LIMIT_STEP_1,
},
{
title: 'Session Limits',
route: DRAWER_ROUTES_NAMES.ACCOUNT__SESSION_LIMIT_STEP_1,
},
{
title: 'Cool Off period',
route: DRAWER_ROUTES_NAMES.ACCOUNT__COOL_OFF,
},
{
title: 'Self Exclude',
route: DRAWER_ROUTES_NAMES.ACCOUNT__SELF_EXCLUSION,
},
{
title: 'Reality Check',
route: DRAWER_ROUTES_NAMES.ACCOUNT__REALITY_CHECK_STEP_1,
},
{
title: 'Loss Limits',
route: DRAWER_ROUTES_NAMES.ACCOUNT__CHANGE_LOSS_LIMIT_STEP_1,
},
{
title: 'Play Limits',
route: DRAWER_ROUTES_NAMES.ACCOUNT__CHANGE_PLAY_LIMIT_STEP_1,
},
];
const PROMOTION_BONUSES_ROUTES = [
{
title: 'List of Active/Open/Expired',
route: DRAWER_ROUTES_NAMES.ACCOUNT__MY_PROMOTIONS,
},
];
const PREFERENCES_ROUTES = [
{
title: 'My Favorites',
route: DRAWER_ROUTES_NAMES.ACCOUNT__MY_FAVORITES,
},
{
title: 'My Messages',
route: DRAWER_ROUTES_NAMES.ACCOUNT__MESSAGES,
},
{
title: 'Communication Preferences',
route: DRAWER_ROUTES_NAMES.ACCOUNT__EDIT_COMMUNICATION_PREFERENCES,
},
];
if (supportedBiometry) {
PREFERENCES_ROUTES.push({
title: 'QUICK LOGIN',
route: DRAWER_ROUTES_NAMES.ACCOUNT__ENABLE_QUICK_LOGIN,
});
}
const dynamicTabs = (tab: TAB_PROPS) => {
switch (tab) {
case 'wallet':
return <TabView routes={WALLET_ROUTES} />;
case 'account_details':
return (
<View>
<TabView routes={ACCOUNT_DETAILS_ROUTES} />
<SizedBox height={5} />
<PersonalInfoView />
</View>
);
case 'preferences':
return <TabView routes={PREFERENCES_ROUTES} />;
case 'promotion_bonuses':
return <TabView routes={PROMOTION_BONUSES_ROUTES} />;
case 'responsible_gaming':
return <TabView routes={RESPONSIBLE_GAMING_ROUTES} />;
}
};
React.useEffect(() => {
return navigation.addListener('focus', () => {
setCurrentTab('wallet');
setCurrentIndex(0);
});
}, [navigation]);
const BalanceObserver = observer((props) => {
const showBalance = props.mobxStore.showBalance;
return (
<View
style={{
backgroundColor: 'rgba(0,0,0,0.3)',
padding: 15,
borderRadius: 10,
}}>
<BSwitch
label={'Hide balance'}
value={!showBalance}
onValueChange={(_v: boolean) => mobxStore.handleShowBalance()}
/>
{showBalance && (
<View>
<View
style={{
height: 2,
width: '100%',
marginVertical: 10,
backgroundColor: 'rgba(255,255,255,0.3)',
}}
/>
<View style={[GS.row, GS.alignCenter]}>
<View style={GS.flex1}>
<Text style={[GS.txtUpper, GS.FF_PoppinsSemiBold, GS.txtWhite]}>
Cash balance
</Text>
</View>
<View style={[GS.row, GS.alignCenter]}>
<View
style={{
height: 28,
width: 28,
flexDirection: 'row',
alignItems: 'center',
justifyContent: 'center',
borderRadius: 50,
backgroundColor: CC.primary,
marginRight: 10,
}}>
<TouchableOpacity
onPress={() => {
spin(() => {
refetchWallet()
});
}}
>
<Animated.Image
source={icons.refresh_symbol}
style={{
height: 16,
width: 16,
resizeMode: 'contain'
}}
/>
</TouchableOpacity>
</View>
{userInfo &&
walletData?.response?.cashBalance !== undefined && (
<Text style={{ color: 'white' }}>
{`$${walletData?.response?.cashBalance
?.toFixed(2)
.replace(/(\d)(?=(\d{3})+(?!\d))/g, '$1,')}`}
</Text>
)}
</View>
</View>
<View style={[GS.row, GS.alignCenter]}>
<View style={GS.flex1}>
<Text style={[GS.txtUpper, GS.FF_PoppinsSemiBold, GS.txtWhite]}>
Bonus balance
</Text>
</View>
<View style={[GS.row, GS.alignCenter]}>
{userInfo && walletData?.response?.bonusMoney !== undefined && (
<Text style={{ color: 'white' }}>
{`$${walletData?.response?.bonusMoney
?.toFixed(2)
?.replace(/(\d)(?=(\d{3})+(?!\d))/g, '$1,')}`}
</Text>
)}
</View>
</View>
</View>
)}
</View>
);
});
React.useEffect(() => {
if (
!isAuthDataLoading &&
userInfo &&
userInfo?.kycCheckedData &&
!userInfo.kycCheckedData.kycChecked
) {
getKycUrl();
}
}, [getKycUrl, isAuthDataLoading, userInfo]);
const [scrollViewWidth, setScrollViewWidth] = React.useState(0);
const [currentXOffset, setCurrentXOffset] = React.useState(0);
const [layoutWidth, setLayoutWidth] = React.useState(0);
const scrollViewRef = useRef<ScrollView>(null);
const _handleScroll = (event: NativeSyntheticEvent<NativeScrollEvent>) => {
const newXOffset = event.nativeEvent.contentOffset.x;
setCurrentXOffset(newXOffset);
};
const handleLeftArrow = () => {
const eachItemOffset = scrollViewWidth / 6;
const _currentXOffset = currentXOffset - eachItemOffset;
scrollViewRef.current?.scrollTo({
x: _currentXOffset,
y: 0,
animated: true,
});
};
const handleRightArrow = () => {
const eachItemOffset = scrollViewWidth / 6;
const _currentXOffset = currentXOffset + eachItemOffset;
scrollViewRef.current?.scrollTo({
x: _currentXOffset,
y: 0,
animated: true,
});
};
const handleClick = async (item) => {
setCurrentTab(item.key as any)
await AsyncStorage.setItem('activeTab', item.key);
}
const getActiveTab = async () => {
try {
const activeTab = await AsyncStorage.getItem('activeTab');
if (activeTab !== null && activeTab !== undefined) {
setCurrentTab(activeTab as any)
return activeTab;
}
} catch (error) {
// Error retrieving data
}
};
React.useEffect(() => {
(async () => {
await getActiveTab();
})();
return () => {
// Component unmount code.
};
}, [currentTab]);
// Define the spinValue variable with an initial value of 0
const spinValue = new Animated.Value(0);
// Set up the animation to rotate from 0 to 1 over 3 seconds using the linear easing function
Animated.timing(spinValue, {
toValue: 1,
duration: 3000,
easing: Easing.linear,
useNativeDriver: true,
}).start();
const spin = (callback) => {
Animated.timing(spinAnim, {
toValue: 1,
duration: 1000,
easing: Easing.linear,
useNativeDriver: true,
}).start(() => {
setSpinAnim(new Animated.Value(0));
if (callback) {
callback();
}
});
};
// anytime walletData is updated, setIsWalletLoading to true
React.useEffect(() => {
if (isAppDataLoading) {
setIsWalletLoading(true);
Animated.timing(spinValue, {
toValue: 1,
duration: 1000,
easing: Easing.linear,
useNativeDriver: true,
}).start(() => {
spinValue.setValue(0);
setIsWalletLoading(false); // Set isWalletLoading to false after animation completes
});
}
}, [walletData]);
return (
<AccountLayout hasHeader={false}>
<View>
{/* Header */}
<AccountLoggedHeader />
{/* ALERT */}
{
(userInfo &&
userInfo?.kycCheckedData &&
!userInfo.kycCheckedData.kycChecked &&
userInfo.kycCheckedData.remainingDays
) ? (
<KYCAlert
url={kycUrl?.response?.scanUrl ?? ""}
remainingDays={userInfo.kycCheckedData.remainingDays}
/>
) :
(<></>)
}
<View style={GS.row}>
<BButton
title="Deposit"
onPress={() => {
navigation.navigate(DRAWER_ROUTES_NAMES.WALLET__DEPOSIT as never);
}}
mode="danger"
disabled={isDepositDisabled}
/>
<SizedBox width={10} />
<BButton
title="Withdraw"
onPress={() => {
navigation.navigate(
DRAWER_ROUTES_NAMES.WALLET__WITHDRAW as never,
);
}}
mode="primary"
disabled={!userInfo?.kycCheckedData.kycChecked || isWithdrawDisabled}
/>
</View>
{/* */}
<SizedBox height={20} />
{/* */}
<BalanceObserver mobxStore={mobxStore} />
{/* PROMOTIONS */}
<AccountPromotions availablePromotions={totalAvailablePromotions} />
{/* */}
<SizedBox height={20} />
{/* */}
<View
style={{
backgroundColor: 'rgba(0,0,0,0.3)',
borderRadius: 15,
padding: 10,
paddingRight: 30,
position: 'relative',
flexDirection: 'row',
alignItems: 'center',
height: 45,
}}>
<TouchableOpacity
style={{ position: 'relative', right: 1 }}
onPress={handleLeftArrow}>
<BIcon
icon={icons.left_arrow}
style={{
height: 15,
width: 11,
resizeMode: 'contain',
tintColor: CC.primary,
}}
/>
</TouchableOpacity>
<ScrollView
ref={scrollViewRef}
fadingEdgeLength={80}
onLayout={(event) => setLayoutWidth(event.nativeEvent.layout.width)}
onContentSizeChange={(width) =>
setScrollViewWidth(width - layoutWidth)
}
onScroll={_handleScroll}
horizontal
showsHorizontalScrollIndicator={false}
bounces={false}
pagingEnabled
snapToInterval={120}
centerContent
scrollEventThrottle={32}
contentContainerStyle={{
paddingHorizontal: 15,
}}
style={{
flex: 1,
}}>
<View
style={{
flex: 1,
flexDirection: 'row',
alignItems: 'center',
justifyContent: 'center',
}}>
{TABS.map((item, key) => {
const isLast = key === TABS.length - 1;
return (
<TouchableOpacity
onPress={() => handleClick(item)}
key={key}
style={
isLast ? styles.horizontalItemLast : styles.horizontalItem
}>
<Text
style={[
GS.txtUpper,
GS.txtSm,
GS.FF_PoppinsSemiBold,
{
color: currentTab === item.key ? CC.primary : 'white',
},
]}>
{item.title}
</Text>
</TouchableOpacity>
);
})}
</View>
</ScrollView>
<TouchableOpacity
style={{ position: 'absolute', right: 10 }}
onPress={handleRightArrow}>
<BIcon
icon={icons.right_arrow}
style={{
height: 15,
width: 11,
resizeMode: 'contain',
tintColor: CC.primary,
}}
/>
</TouchableOpacity>
</View>
{/* */}
<SizedBox height={20} />
{/* TABS */}
{dynamicTabs(currentTab)}
</View>
</AccountLayout>
);
};
export default LoggedInScreeen;
const styles = StyleSheet.create({
alert: {
borderColor: CC.secondary,
borderWidth: 1,
borderRadius: 20,
paddingHorizontal: 12.5,
paddingVertical: 17.5,
marginBottom: 25,
marginTop: 10,
flexDirection: 'row',
alignItems: 'center',
backgroundColor: 'rgba(0,0,0,0.3)',
},
alertText: {
...GS.txtSm,
opacity: 0.9,
textTransform: 'uppercase',
color: 'white',
},
horizontalItem: {
flexGrow: 1,
marginRight: 15,
borderRightColor: 'rgba(255,255,255,0.2)',
paddingRight: 15,
borderRightWidth: 2,
},
horizontalItemLast: {
flexGrow: 1,
marginRight: 0,
},
});
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment