384 lines
11 KiB
JavaScript
384 lines
11 KiB
JavaScript
/* eslint-disable react/display-name */
|
|
import { Image, Pressable, Text, View, StyleSheet } from 'react-native'
|
|
import { KeyboardAwareScrollView } from 'react-native-keyboard-aware-scroll-view'
|
|
import { SafeAreaView } from 'react-native-safe-area-context'
|
|
import React from 'reactn'
|
|
import { responsiveHeight } from '../actions/responsiveSizes.js'
|
|
import { subBadges } from '../assets'
|
|
import BaseHeader from '../components/BaseHeader'
|
|
import CreditAmount from '../components/CreditAmount'
|
|
import ConnectBtn from '../components/ConnectBtn.js'
|
|
import GradientButton from '../components/GradientButton'
|
|
import CoinPackModal from '../components/modal/CoinPackModal'
|
|
import NavigateHeader from '../components/NavigateHeader'
|
|
import ShareBtn from '../components/ShareBtn/ShareBtn'
|
|
import { isWeb } from '../hooks/useLayoutType.js'
|
|
import { useUserData } from '../providers/UserDataProvider'
|
|
import { Routes } from '../navigation'
|
|
import { navigate } from '../navigation/NavigationService'
|
|
import { gutters, Palette } from '../styles'
|
|
import { FONT_FAMILY } from '../styles/Fonts'
|
|
import { subscribeCoinPackModal } from '../utils/coinPackModal'
|
|
import { BlurView } from 'expo-blur'
|
|
|
|
export default ({
|
|
children,
|
|
|
|
topStickyContent = null,
|
|
bottomStickyContent = null,
|
|
|
|
title = '',
|
|
rightComponent = null,
|
|
|
|
containerType = 'SAFE_AREA_VIEW',
|
|
headerType = 'BASE',
|
|
|
|
containerStyle = {},
|
|
headerStyle = {},
|
|
contentContainerStyle = {},
|
|
|
|
scrollEnabled = false,
|
|
|
|
onBackPressed = null,
|
|
backgroundImg = null,
|
|
headerTitleStyle = {},
|
|
hideBackButton = false,
|
|
width = undefined,
|
|
maxWidth = 1200,
|
|
shareBtn = false,
|
|
connect = false,
|
|
blurIntensity = 0,
|
|
backgroundColor = '#000',
|
|
backgroundContent = null,
|
|
coinBadgeContainerStyle = null,
|
|
showCoin = true,
|
|
showReturnHome = false,
|
|
onReturnHome = null,
|
|
}) => {
|
|
const { currentUID = null, currentUserData = null } = useUserData?.() || {}
|
|
|
|
const coinBalance = React.useMemo(() => {
|
|
const value = currentUserData?.coins
|
|
if (typeof value === 'number' && Number.isFinite(value)) {
|
|
return value
|
|
}
|
|
if (typeof value === 'string') {
|
|
const parsed = Number(value)
|
|
if (Number.isFinite(parsed)) {
|
|
return parsed
|
|
}
|
|
}
|
|
return 0
|
|
}, [currentUserData?.coins])
|
|
|
|
const showCoinBadge = isWeb && !!currentUID && showCoin
|
|
const showReturnHomeButton = isWeb && showReturnHome
|
|
const coinBadgeContainerVisible = showCoinBadge || showReturnHomeButton
|
|
const [isCoinModalVisible, setCoinModalVisible] = React.useState(false)
|
|
|
|
const subscriptionBadgeSource = React.useMemo(() => {
|
|
const allowedLevels = new Set(['starter', 'pro', 'premium'])
|
|
const pickLevel = (value) => {
|
|
if (typeof value !== 'string') {
|
|
return null
|
|
}
|
|
const normalized = value.trim().toLowerCase()
|
|
return normalized && allowedLevels.has(normalized) ? normalized : null
|
|
}
|
|
|
|
const nestedSubscription =
|
|
currentUserData?.subscription && typeof currentUserData.subscription === 'object'
|
|
? currentUserData.subscription
|
|
: null
|
|
|
|
const stripeMetadata =
|
|
currentUserData?.stripeSubscription &&
|
|
typeof currentUserData.stripeSubscription === 'object' &&
|
|
typeof currentUserData.stripeSubscription.metadata === 'object'
|
|
? currentUserData.stripeSubscription.metadata
|
|
: null
|
|
|
|
const directCandidates = [
|
|
pickLevel(currentUserData?.premiumLevel),
|
|
pickLevel(currentUserData?.subscriptionLevel),
|
|
pickLevel(currentUserData?.subscriptionPlan),
|
|
pickLevel(currentUserData?.subscriptionPack),
|
|
]
|
|
|
|
const nestedCandidates = nestedSubscription
|
|
? ['level', 'plan', 'pack', 'type'].map((key) => pickLevel(nestedSubscription?.[key]))
|
|
: []
|
|
|
|
const metadataCandidates = stripeMetadata
|
|
? ['level', 'subscriptionLevel', 'subscription_level', 'pack', 'Pack'].map((key) =>
|
|
pickLevel(stripeMetadata?.[key])
|
|
)
|
|
: []
|
|
|
|
const resolvedLevel = [...directCandidates, ...nestedCandidates, ...metadataCandidates].find(
|
|
Boolean
|
|
)
|
|
|
|
return resolvedLevel ? subBadges[resolvedLevel] || null : null
|
|
}, [currentUserData])
|
|
|
|
const handleCoinPress = React.useCallback(() => {
|
|
if (!showCoinBadge) return
|
|
navigate(Routes.Payments, { pack: 'packs' })
|
|
}, [showCoinBadge])
|
|
|
|
const handleCloseCoinModal = React.useCallback(() => {
|
|
setCoinModalVisible(false)
|
|
}, [])
|
|
|
|
const handleReturnHomePress = React.useCallback(() => {
|
|
if (typeof onReturnHome === 'function') {
|
|
onReturnHome()
|
|
return
|
|
}
|
|
navigate(Routes.LandingPage)
|
|
}, [onReturnHome])
|
|
|
|
React.useEffect(() => {
|
|
if (!showCoinBadge && isCoinModalVisible) {
|
|
setCoinModalVisible(false)
|
|
}
|
|
}, [showCoinBadge, isCoinModalVisible])
|
|
|
|
React.useEffect(() => {
|
|
if (!showCoinBadge) {
|
|
return undefined
|
|
}
|
|
const unsubscribe = subscribeCoinPackModal(() => {
|
|
setCoinModalVisible(true)
|
|
})
|
|
return unsubscribe
|
|
}, [showCoinBadge])
|
|
|
|
const PageContainer = containerType === 'SAFE_AREA_VIEW' ? SafeAreaView : View
|
|
|
|
const ContentContainer = scrollEnabled ? KeyboardAwareScrollView : View
|
|
|
|
const computedWidth = width ?? (isWeb ? '60%' : '100%')
|
|
|
|
const shareButtonElement = (() => {
|
|
if (!shareBtn) return null
|
|
if (typeof React?.isValidElement === 'function') {
|
|
if (React.isValidElement(shareBtn)) {
|
|
return shareBtn
|
|
}
|
|
}
|
|
if (typeof shareBtn === 'object' && !Array.isArray(shareBtn)) {
|
|
return <ShareBtn {...shareBtn} />
|
|
}
|
|
return <ShareBtn />
|
|
})()
|
|
|
|
const resolvedContainerStyle = React.useMemo(
|
|
() => StyleSheet.flatten(containerStyle) || {},
|
|
[containerStyle]
|
|
)
|
|
const resolvedHeaderStyle = React.useMemo(
|
|
() => StyleSheet.flatten(headerStyle) || {},
|
|
[headerStyle]
|
|
)
|
|
const resolvedContentContainerStyle = React.useMemo(
|
|
() => StyleSheet.flatten(contentContainerStyle) || {},
|
|
[contentContainerStyle]
|
|
)
|
|
|
|
return (
|
|
<View
|
|
style={{
|
|
flex: 1,
|
|
minHeight: isWeb ? '100svh' : '100%',
|
|
position: 'relative',
|
|
backgroundColor: backgroundColor,
|
|
}}
|
|
>
|
|
{backgroundImg ? (
|
|
<Image
|
|
source={backgroundImg}
|
|
resizeMode="cover"
|
|
blurIntensity={blurIntensity}
|
|
style={{
|
|
position: 'absolute',
|
|
top: 0,
|
|
left: 0,
|
|
width: '100%',
|
|
height: '100%',
|
|
...(isWeb && blurIntensity ? { filter: `blur(${blurIntensity}px)` } : {}),
|
|
}}
|
|
/>
|
|
) : null}
|
|
{backgroundContent}
|
|
{coinBadgeContainerVisible ? (
|
|
<View style={[styles.coinBadgeContainer, coinBadgeContainerStyle]}>
|
|
{showCoinBadge ? (
|
|
<View style={styles.coinRow}>
|
|
<Pressable
|
|
onPress={handleCoinPress}
|
|
accessibilityRole="button"
|
|
style={styles.coinButton}
|
|
>
|
|
<CreditAmount
|
|
value={coinBalance}
|
|
iconSize={22}
|
|
textStyle={{
|
|
color: '#ffffff',
|
|
fontFamily: FONT_FAMILY.InterSemiBold,
|
|
fontSize: 14,
|
|
}}
|
|
/>
|
|
</Pressable>
|
|
{subscriptionBadgeSource ? (
|
|
<Pressable
|
|
onPress={() => navigate(Routes.ManageSubscription)}
|
|
accessibilityRole="button"
|
|
style={styles.subscriptionBadge}
|
|
>
|
|
<Image
|
|
source={subscriptionBadgeSource}
|
|
style={{ height: 50, width: 50, resizeMode: 'contain' }}
|
|
/>
|
|
</Pressable>
|
|
) : null}
|
|
</View>
|
|
) : null}
|
|
{showReturnHomeButton ? (
|
|
// <GradientButton
|
|
// title="retour à l'accueil"
|
|
// onPress={handleReturnHomePress}
|
|
// containerStyle={styles.returnHomeButton}
|
|
// />
|
|
<Pressable onPress={handleReturnHomePress} sty>
|
|
<BlurView
|
|
tint="light"
|
|
intensity={30}
|
|
style={{
|
|
paddingHorizontal: 10,
|
|
paddingVertical: 8,
|
|
borderRadius: 12,
|
|
overflow: 'hidden',
|
|
flexDirection: 'row',
|
|
alignItems: 'center',
|
|
justifyContent: 'center',
|
|
}}
|
|
>
|
|
<Text
|
|
style={{
|
|
fontSize: 14,
|
|
color: Palette.white,
|
|
fontFamily: FONT_FAMILY.InterRegular,
|
|
marginRight: 5,
|
|
}}
|
|
>
|
|
{"Retour à l'accueil"}
|
|
</Text>
|
|
</BlurView>
|
|
</Pressable>
|
|
) : null}
|
|
</View>
|
|
) : null}
|
|
<PageContainer
|
|
{...(containerType === 'SAFE_AREA_VIEW' ? { edges: ['top'] } : {})}
|
|
style={{
|
|
height: '100%',
|
|
paddingTop: isWeb ? gutters / 2 : 0,
|
|
padding: gutters,
|
|
paddingBottom: 0,
|
|
...(isWeb ? { maxHeight: '100svh' } : {}),
|
|
width: computedWidth,
|
|
maxWidth: maxWidth ? maxWidth : null,
|
|
alignSelf: isWeb ? 'center' : 'auto',
|
|
...resolvedContainerStyle,
|
|
}}
|
|
>
|
|
{headerType !== 'NONE' ? (
|
|
headerType === 'BASE' ? (
|
|
<BaseHeader containerStyle={{ ...resolvedHeaderStyle }} />
|
|
) : (
|
|
<NavigateHeader
|
|
containerStyle={{ ...resolvedHeaderStyle }}
|
|
title={title}
|
|
rightComponent={rightComponent}
|
|
onBackPressed={onBackPressed}
|
|
headerTitleStyle={headerTitleStyle}
|
|
hideBackButton={hideBackButton}
|
|
/>
|
|
)
|
|
) : null}
|
|
|
|
{topStickyContent?.()}
|
|
|
|
<ContentContainer
|
|
style={{ flex: 1, ...resolvedContentContainerStyle }}
|
|
{...(scrollEnabled
|
|
? {
|
|
scrollEventThrottle: 80,
|
|
showsVerticalScrollIndicator: false,
|
|
contentContainerStyle: { paddingBottom: responsiveHeight(55) },
|
|
keyboardDismissMode: 'on-drag',
|
|
}
|
|
: {})}
|
|
>
|
|
{children}
|
|
</ContentContainer>
|
|
</PageContainer>
|
|
|
|
{bottomStickyContent?.()}
|
|
{(shareButtonElement || connect) && isWeb && (
|
|
<View
|
|
style={{
|
|
position: 'absolute',
|
|
top: 20,
|
|
right: 20,
|
|
flexDirection: 'row',
|
|
alignItems: 'center',
|
|
gap: 12, // ou marginLeft sur chaque bouton si gap non supporté
|
|
}}
|
|
>
|
|
{shareButtonElement}
|
|
{connect && <ConnectBtn />}
|
|
</View>
|
|
)}
|
|
|
|
<CoinPackModal visible={isCoinModalVisible} onClose={handleCloseCoinModal} />
|
|
</View>
|
|
)
|
|
}
|
|
|
|
const styles = StyleSheet.create({
|
|
coinBadgeContainer: {
|
|
position: 'absolute',
|
|
top: gutters,
|
|
left: gutters,
|
|
alignItems: 'flex-start',
|
|
gap: 10,
|
|
zIndex: 20,
|
|
},
|
|
coinRow: {
|
|
flexDirection: 'row',
|
|
alignItems: 'center',
|
|
gap: 6,
|
|
},
|
|
coinButton: {
|
|
paddingHorizontal: 14,
|
|
paddingVertical: 8,
|
|
borderRadius: 20,
|
|
backgroundColor: 'rgba(12, 14, 18, 0.72)',
|
|
borderWidth: 1,
|
|
borderColor: 'rgba(255, 255, 255, 0.1)',
|
|
flexDirection: 'row',
|
|
alignItems: 'center',
|
|
gap: 8,
|
|
},
|
|
subscriptionBadge: {
|
|
paddingVertical: 4,
|
|
},
|
|
returnHomeButton: {
|
|
width: 220,
|
|
alignSelf: 'flex-start',
|
|
},
|
|
})
|