fix favicon, playlist subscription only
This commit is contained in:
@@ -82,7 +82,8 @@
|
||||
]
|
||||
},
|
||||
"web": {
|
||||
"bundler": "metro"
|
||||
"bundler": "metro",
|
||||
"favicon": "./assets/favicon.png"
|
||||
},
|
||||
"plugins": [
|
||||
"react-native-compressor",
|
||||
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 4.5 KiB |
@@ -25,7 +25,7 @@ const MobileCoinBadge = ({ style, textStyle, iconSize = 20, iconPosition = 'left
|
||||
}, [currentUserData?.coins])
|
||||
|
||||
const handlePress = useCallback(() => {
|
||||
navigate(Routes.Payments)
|
||||
navigate(Routes.Payments, { pack: 'packs' })
|
||||
}, [])
|
||||
|
||||
if (!currentUID || Platform.OS === 'web') {
|
||||
|
||||
@@ -1,9 +1,10 @@
|
||||
import React, { useMemo, useRef, useState } from 'react'
|
||||
import React, { useCallback, useEffect, useMemo, useRef, useState } from 'react'
|
||||
import { Dimensions, FlatList, Image, Pressable, Text, TextInput, View } from 'react-native'
|
||||
import { SheetManager } from 'react-native-actions-sheet'
|
||||
import useMinuit from 'react-native-minuit/src/hooks/useMinuit'
|
||||
import SwiperFlatList from 'react-native-swiper-flatlist'
|
||||
import { icons } from '../../assets'
|
||||
import usePlaylistCreationGuard from '../../hooks/usePlaylistCreationGuard'
|
||||
import { useUserData } from '../../providers/UserDataProvider'
|
||||
import { addMusicToPlaylist, createPlaylist } from '../../screens/Library/Playlists/playlist'
|
||||
import { Palette } from '../../styles'
|
||||
@@ -20,8 +21,9 @@ const PlaylistModal = (props) => {
|
||||
const [selectedId, setSelectedId] = useState('')
|
||||
const [playlist, setPlaylist] = useState('')
|
||||
const [isCreating, setIsCreating] = useState(false)
|
||||
const { currentUID, userPlaylists = [] } = useUserData()
|
||||
const { currentUID, userPlaylists = [], hasActiveSubscription } = useUserData()
|
||||
const startAtCreate = props?.payload?.startAtCreate || false
|
||||
const ensureCanCreatePlaylist = usePlaylistCreationGuard()
|
||||
const { setTooltip } = useMinuit()
|
||||
|
||||
const sanitizedPlaylists = useMemo(
|
||||
@@ -35,17 +37,27 @@ const PlaylistModal = (props) => {
|
||||
return sanitizedPlaylists.some((p) => (p?.name || '').trim().toLowerCase() === lower)
|
||||
}, [playlist, sanitizedPlaylists])
|
||||
|
||||
const hideSheet = () => {
|
||||
const hideSheet = useCallback(() => {
|
||||
SheetManager.hide('Playlist')
|
||||
}, [])
|
||||
|
||||
useEffect(() => {
|
||||
if (!startAtCreate || hasActiveSubscription) {
|
||||
return
|
||||
}
|
||||
|
||||
if (!ensureCanCreatePlaylist()) {
|
||||
hideSheet()
|
||||
}
|
||||
}, [ensureCanCreatePlaylist, hasActiveSubscription, hideSheet, startAtCreate])
|
||||
|
||||
return (
|
||||
<AppActionSheet id="Playlist">
|
||||
<SwiperFlatList
|
||||
style={{ width, left: -14 }}
|
||||
ref={scrollRef}
|
||||
disableGesture
|
||||
index={startAtCreate ? 1 : 0}
|
||||
index={startAtCreate && hasActiveSubscription ? 1 : 0}
|
||||
>
|
||||
<View style={{ width, paddingHorizontal: 14 }}>
|
||||
<View style={{ gap: 20 }}>
|
||||
@@ -70,12 +82,17 @@ const PlaylistModal = (props) => {
|
||||
...Style.containerRow,
|
||||
gap: 10,
|
||||
}}
|
||||
onPress={() =>
|
||||
onPress={() => {
|
||||
if (!ensureCanCreatePlaylist()) {
|
||||
hideSheet()
|
||||
return
|
||||
}
|
||||
|
||||
scrollRef.current?.scrollToIndex({
|
||||
index: 1,
|
||||
animated: true,
|
||||
})
|
||||
}
|
||||
}}
|
||||
>
|
||||
<Image source={icons.add} style={size({ size: 15 })} />
|
||||
<Text
|
||||
@@ -212,6 +229,11 @@ const PlaylistModal = (props) => {
|
||||
onPress={async () => {
|
||||
if (!playlist?.trim()?.length || !currentUID) return
|
||||
try {
|
||||
if (!ensureCanCreatePlaylist()) {
|
||||
hideSheet()
|
||||
return
|
||||
}
|
||||
|
||||
setIsCreating(true)
|
||||
const name = playlist.trim()
|
||||
const lower = name.toLowerCase()
|
||||
@@ -232,7 +254,7 @@ const PlaylistModal = (props) => {
|
||||
musics: props?.payload?.projectId ? [props.payload.projectId] : [],
|
||||
createdAt: new Date(),
|
||||
updatedAt: new Date(),
|
||||
})
|
||||
}, { hasActiveSubscription })
|
||||
|
||||
setTooltip({
|
||||
type: 'success',
|
||||
|
||||
@@ -1,11 +1,12 @@
|
||||
import { Portal } from '@gorhom/portal'
|
||||
import { BlurView } from 'expo-blur'
|
||||
import React, { useEffect, useMemo, useState } from 'react'
|
||||
import React, { useCallback, useEffect, useMemo, useState } from 'react'
|
||||
import { FlatList, Image, Pressable, StyleSheet, Text, TextInput, View } from 'react-native'
|
||||
import { SheetManager, useProviderContext } from 'react-native-actions-sheet'
|
||||
import { actionSheetEventManager } from 'react-native-actions-sheet/dist/src/eventmanager'
|
||||
import useMinuit from 'react-native-minuit/src/hooks/useMinuit'
|
||||
import { icons } from '../../assets'
|
||||
import usePlaylistCreationGuard from '../../hooks/usePlaylistCreationGuard'
|
||||
import { useUserData } from '../../providers/UserDataProvider'
|
||||
import { addMusicToPlaylist, createPlaylist } from '../../screens/Library/Playlists/playlist'
|
||||
import { Palette } from '../../styles'
|
||||
@@ -18,16 +19,17 @@ const PlaylistModal = (props) => {
|
||||
const [selectedId, setSelectedId] = useState('')
|
||||
const [playlist, setPlaylist] = useState('')
|
||||
const [isCreating, setIsCreating] = useState(false)
|
||||
const { currentUID, userPlaylists = [] } = useUserData()
|
||||
const { currentUID, userPlaylists = [], hasActiveSubscription } = useUserData()
|
||||
const ensureCanCreatePlaylist = usePlaylistCreationGuard()
|
||||
const { setTooltip } = useMinuit()
|
||||
const startAtCreate = props?.payload?.startAtCreate || false
|
||||
const [mode, setMode] = useState(startAtCreate ? 'create' : 'select')
|
||||
const [mode, setMode] = useState(startAtCreate && hasActiveSubscription ? 'create' : 'select')
|
||||
const [visible, setVisible] = useState(true)
|
||||
const providerContext = useProviderContext()
|
||||
|
||||
useEffect(() => {
|
||||
setMode(startAtCreate ? 'create' : 'select')
|
||||
}, [startAtCreate])
|
||||
setMode(startAtCreate && hasActiveSubscription ? 'create' : 'select')
|
||||
}, [hasActiveSubscription, startAtCreate])
|
||||
|
||||
const sanitizedPlaylists = useMemo(
|
||||
() => (Array.isArray(userPlaylists) ? userPlaylists : []),
|
||||
@@ -44,7 +46,7 @@ const PlaylistModal = (props) => {
|
||||
setVisible(true)
|
||||
}, [props?.sheetId, props?.payload])
|
||||
|
||||
const hideModal = () => {
|
||||
const hideModal = useCallback(() => {
|
||||
setVisible(false)
|
||||
props?.payload?.onClose?.()
|
||||
|
||||
@@ -56,8 +58,18 @@ const PlaylistModal = (props) => {
|
||||
// close event manually to let the SheetProvider unmount it.
|
||||
actionSheetEventManager.publish(`onclose_${sheetId}`, undefined, providerContext)
|
||||
}
|
||||
}, [props?.payload, props?.sheetId, providerContext])
|
||||
|
||||
useEffect(() => {
|
||||
if (!startAtCreate || hasActiveSubscription) {
|
||||
return
|
||||
}
|
||||
|
||||
if (!ensureCanCreatePlaylist()) {
|
||||
hideModal()
|
||||
}
|
||||
}, [ensureCanCreatePlaylist, hasActiveSubscription, hideModal, startAtCreate])
|
||||
|
||||
const handleValidateSelection = async () => {
|
||||
if (!selectedId) return
|
||||
try {
|
||||
@@ -84,6 +96,10 @@ const PlaylistModal = (props) => {
|
||||
|
||||
const handleCreatePlaylist = async () => {
|
||||
if (!playlist?.trim()?.length || !currentUID) return
|
||||
if (!ensureCanCreatePlaylist()) {
|
||||
hideModal()
|
||||
return
|
||||
}
|
||||
try {
|
||||
setIsCreating(true)
|
||||
const name = playlist.trim()
|
||||
@@ -103,7 +119,7 @@ const PlaylistModal = (props) => {
|
||||
musics: props?.payload?.projectId ? [props.payload.projectId] : [],
|
||||
createdAt: new Date(),
|
||||
updatedAt: new Date(),
|
||||
})
|
||||
}, { hasActiveSubscription })
|
||||
|
||||
setTooltip({
|
||||
type: 'success',
|
||||
@@ -152,6 +168,10 @@ const PlaylistModal = (props) => {
|
||||
alignSelf: 'center',
|
||||
}}
|
||||
onPress={() => {
|
||||
if (!ensureCanCreatePlaylist()) {
|
||||
hideModal()
|
||||
return
|
||||
}
|
||||
setPlaylist('')
|
||||
setMode('create')
|
||||
}}
|
||||
|
||||
@@ -0,0 +1,29 @@
|
||||
import { useCallback } from 'react'
|
||||
import useMinuit from 'react-native-minuit/src/hooks/useMinuit'
|
||||
import { Routes } from '../navigation'
|
||||
import { navigate } from '../navigation/NavigationService'
|
||||
import { useUserData } from '../providers/UserDataProvider'
|
||||
import {
|
||||
canCreatePlaylist,
|
||||
PLAYLIST_CREATION_SUBSCRIPTION_MESSAGE,
|
||||
} from '../utils/playlistCreationAccess'
|
||||
|
||||
const usePlaylistCreationGuard = () => {
|
||||
const { hasActiveSubscription } = useUserData() || {}
|
||||
const { setTooltip } = useMinuit()
|
||||
|
||||
return useCallback(() => {
|
||||
if (canCreatePlaylist(hasActiveSubscription)) {
|
||||
return true
|
||||
}
|
||||
|
||||
setTooltip?.({
|
||||
type: 'error',
|
||||
text: PLAYLIST_CREATION_SUBSCRIPTION_MESSAGE,
|
||||
})
|
||||
navigate(Routes.Payments)
|
||||
return false
|
||||
}, [hasActiveSubscription, setTooltip])
|
||||
}
|
||||
|
||||
export default usePlaylistCreationGuard
|
||||
+1
-1
@@ -122,7 +122,7 @@ export default ({
|
||||
|
||||
const handleCoinPress = React.useCallback(() => {
|
||||
if (!showCoinBadge) return
|
||||
navigate(Routes.Payments)
|
||||
navigate(Routes.Payments, { pack: 'packs' })
|
||||
}, [showCoinBadge])
|
||||
|
||||
const handleCloseCoinModal = React.useCallback(() => {
|
||||
|
||||
@@ -434,7 +434,7 @@ const Home = ({ navigation, route }) => {
|
||||
}, [])
|
||||
|
||||
const handleOpenCoinModal = useCallback(() => {
|
||||
navigate(Routes.Payments)
|
||||
navigate(Routes.Payments, { pack: 'packs' })
|
||||
}, [])
|
||||
|
||||
const stageCardContainerStyle = isWeb ? styles.cardsGrid : styles.cardsStack
|
||||
|
||||
@@ -3,6 +3,7 @@ import React from 'react'
|
||||
import { Image, Platform, Pressable, Text, View } from 'react-native'
|
||||
import { SheetManager } from 'react-native-actions-sheet'
|
||||
import { background, icons } from '../../assets'
|
||||
import usePlaylistCreationGuard from '../../hooks/usePlaylistCreationGuard'
|
||||
import Page from '../../layouts/Page'
|
||||
import { Routes } from '../../navigation'
|
||||
import { navigate } from '../../navigation/NavigationService'
|
||||
@@ -12,15 +13,23 @@ import { FONT_FAMILY } from '../../styles/Fonts'
|
||||
import { size } from '../../styles/Style'
|
||||
const AllMyPlaylist = () => {
|
||||
const { userPlaylists = [] } = useUser()
|
||||
const ensureCanCreatePlaylist = usePlaylistCreationGuard()
|
||||
|
||||
const handleCreatePlaylistPress = () => {
|
||||
if (!ensureCanCreatePlaylist()) {
|
||||
return
|
||||
}
|
||||
|
||||
SheetManager.show('Playlist', { payload: { startAtCreate: true } })
|
||||
}
|
||||
|
||||
return (
|
||||
<Page
|
||||
headerType="NAVIGATION"
|
||||
backgroundImg={background.libraryBG}
|
||||
title="Mes Playlists"
|
||||
rightComponent={() => (
|
||||
<Pressable
|
||||
onPress={() => SheetManager.show('Playlist', { payload: { startAtCreate: true } })}
|
||||
>
|
||||
<Pressable onPress={handleCreatePlaylistPress}>
|
||||
<Image source={icons.add} style={size({ size: 20 })} resizeMode="contain" />
|
||||
</Pressable>
|
||||
)}
|
||||
|
||||
@@ -7,6 +7,7 @@ import { background, icons } from '../../assets'
|
||||
import MoreMenu from '../../components/MoreMenu'
|
||||
import { playlistsRef, projectsRef } from '../../config/firebase'
|
||||
import useDataFromArrayDocId from '../../hooks/useDataFromArrayId'
|
||||
import usePlaylistCreationGuard from '../../hooks/usePlaylistCreationGuard'
|
||||
import useDataFromRef from '../../hooks/useDataFromRef'
|
||||
import useNavigateToMusicDetails from '../../hooks/useNavigateToMusicDetails'
|
||||
import usePlayer from '../../hooks/usePlayer'
|
||||
@@ -22,6 +23,7 @@ const AllMyPlaylist = ({ route }) => {
|
||||
const { playListId } = route?.params || {}
|
||||
const [, setTooltip] = useGlobal('_tooltip')
|
||||
const { userPlaylists = [] } = useUser()
|
||||
const ensureCanCreatePlaylist = usePlaylistCreationGuard()
|
||||
const [selectedPlaylistId, setSelectedPlaylistId] = useState(playListId || null)
|
||||
const { data: playlist } = useDataFromRef({
|
||||
ref: selectedPlaylistId ? playlistsRef.doc(selectedPlaylistId) : null,
|
||||
@@ -140,6 +142,16 @@ const AllMyPlaylist = ({ route }) => {
|
||||
})
|
||||
}, [playlist?.musics, selectedPlaylistId])
|
||||
|
||||
const handleCreatePlaylistPress = useCallback(() => {
|
||||
if (!ensureCanCreatePlaylist()) {
|
||||
return
|
||||
}
|
||||
|
||||
SheetManager.show('Playlist', {
|
||||
payload: { startAtCreate: true },
|
||||
})
|
||||
}, [ensureCanCreatePlaylist])
|
||||
|
||||
return (
|
||||
<Page
|
||||
headerType="NAVIGATION"
|
||||
@@ -233,13 +245,7 @@ const AllMyPlaylist = ({ route }) => {
|
||||
)
|
||||
})}
|
||||
</View>
|
||||
<Pressable
|
||||
onPress={() =>
|
||||
SheetManager.show('Playlist', {
|
||||
payload: { startAtCreate: true },
|
||||
})
|
||||
}
|
||||
>
|
||||
<Pressable onPress={handleCreatePlaylistPress}>
|
||||
<View
|
||||
style={{
|
||||
borderRadius: 12,
|
||||
|
||||
@@ -1,7 +1,10 @@
|
||||
import { arrayUnion, playlistsRef } from '../../../config/firebase'
|
||||
import { assertCanCreatePlaylist } from '../../../utils/playlistCreationAccess'
|
||||
|
||||
export const createPlaylist = async (payload = {}) => {
|
||||
export const createPlaylist = async (payload = {}, options = {}) => {
|
||||
try {
|
||||
assertCanCreatePlaylist(options?.hasActiveSubscription)
|
||||
|
||||
const createdBy = payload?.createdBy
|
||||
const name = payload?.name?.trim?.()
|
||||
const musics = Array.isArray(payload?.musics) ? payload.musics : []
|
||||
|
||||
@@ -5,6 +5,7 @@ import { SheetManager } from 'react-native-actions-sheet'
|
||||
import { icons } from '../../../assets'
|
||||
import GradientButton from '../../../components/GradientButton'
|
||||
import useLayoutType from '../../../hooks/useLayoutType'
|
||||
import usePlaylistCreationGuard from '../../../hooks/usePlaylistCreationGuard'
|
||||
import { Routes } from '../../../navigation'
|
||||
import { navigate } from '../../../navigation/NavigationService'
|
||||
import { useUser } from '../../../providers/UserDataProvider'
|
||||
@@ -16,16 +17,24 @@ const MyPlaylist = () => {
|
||||
const { userPlaylists = [] } = useUser()
|
||||
const playlists = Array.isArray(userPlaylists) ? userPlaylists.slice(0, 2) : []
|
||||
const { isWeb } = useLayoutType()
|
||||
const ensureCanCreatePlaylist = usePlaylistCreationGuard()
|
||||
const hasPlaylists = playlists.length > 0
|
||||
return (
|
||||
<CardContainer
|
||||
label="Mes Playlists"
|
||||
onPress={() => navigate(Routes.AllMyPlaylist)}
|
||||
onPressPlus={() =>
|
||||
|
||||
const handleCreatePlaylistPress = () => {
|
||||
if (!ensureCanCreatePlaylist()) {
|
||||
return
|
||||
}
|
||||
|
||||
SheetManager.show('Playlist', {
|
||||
payload: { startAtCreate: true },
|
||||
})
|
||||
}
|
||||
|
||||
return (
|
||||
<CardContainer
|
||||
label="Mes Playlists"
|
||||
onPress={() => navigate(Routes.AllMyPlaylist)}
|
||||
onPressPlus={handleCreatePlaylistPress}
|
||||
>
|
||||
<View style={{ gap: 8 }}>
|
||||
{hasPlaylists ? (
|
||||
@@ -91,11 +100,7 @@ const MyPlaylist = () => {
|
||||
</Text>
|
||||
<GradientButton
|
||||
containerStyle={{ padding: 2 }}
|
||||
onPress={() =>
|
||||
SheetManager.show('Playlist', {
|
||||
payload: { startAtCreate: true },
|
||||
})
|
||||
}
|
||||
onPress={handleCreatePlaylistPress}
|
||||
title=" Créer une playlist"
|
||||
/>
|
||||
</View>
|
||||
|
||||
+104
-16
@@ -183,6 +183,55 @@ function SubscriptionCard({ plan, selected, onSelect, isAnnual }) {
|
||||
)
|
||||
}
|
||||
|
||||
function CoinPackCard({ pack, onBuy, isProcessing }) {
|
||||
const rawAmount = pack?.unitAmount ?? pack?.amount ?? pack?.price ?? 0
|
||||
const formattedPrice = formatCurrency(rawAmount, pack?.currency)
|
||||
const coins = Number(pack?.coinAmount ?? pack?.metadata?.coins ?? 0)
|
||||
const packKey = getCoinPackKey(pack)
|
||||
const details = packKey ? COIN_PACK_DETAILS[packKey] : null
|
||||
const displayName = details?.label || pack?.name || 'Pack'
|
||||
const creditsText = details?.creditsDisplay
|
||||
const displayPrice = details?.priceDisplay || formattedPrice || `${rawAmount / 100}€`
|
||||
const isPopular = details?.popular === true
|
||||
|
||||
return (
|
||||
<View style={styles.cardWrapper}>
|
||||
<BlurView intensity={18} tint="dark" style={styles.cardBlur}>
|
||||
<View style={styles.cardContent}>
|
||||
<View style={styles.cardHeader}>
|
||||
<Text style={styles.planName}>{displayName}</Text>
|
||||
{creditsText ? (
|
||||
<Text style={styles.coinPackAmount}>{creditsText}</Text>
|
||||
) : (
|
||||
<CreditAmount value={coins} iconSize={24} textStyle={styles.coinPackAmountValue} />
|
||||
)}
|
||||
{details?.disclaimer ? (
|
||||
<Text style={styles.packDisclaimer}>{details.disclaimer}</Text>
|
||||
) : null}
|
||||
</View>
|
||||
|
||||
<View style={styles.priceBlock}>
|
||||
{displayPrice ? <Text style={styles.priceValue}>{displayPrice}</Text> : null}
|
||||
</View>
|
||||
</View>
|
||||
|
||||
{isPopular ? (
|
||||
<View style={styles.popularBadge}>
|
||||
<Text style={styles.popularBadgeText}>Le plus populaire !</Text>
|
||||
</View>
|
||||
) : null}
|
||||
|
||||
<GradientButton
|
||||
title={isProcessing ? 'Redirection...' : 'Acheter ce pack'}
|
||||
onPress={() => onBuy(pack)}
|
||||
disabled={isProcessing}
|
||||
gradientStyle={styles.coinPackButtonGradient}
|
||||
/>
|
||||
</BlurView>
|
||||
</View>
|
||||
)
|
||||
}
|
||||
|
||||
const PRICE_PRIORITY_BY_PERIOD = {
|
||||
monthly: [
|
||||
'price_1SPgitCzf2o5bDRdbnhLFx6f', // Starter
|
||||
@@ -248,6 +297,7 @@ export default function Subscriptions() {
|
||||
const rawPack = route?.params?.pack
|
||||
return typeof rawPack === 'string' ? rawPack.toLowerCase() : null
|
||||
}, [route?.params?.pack])
|
||||
const isCoinPackView = isMobile && initialPack === 'packs'
|
||||
|
||||
const initialSubscriptionPack = initialPack && initialPack !== 'packs' ? initialPack : null
|
||||
|
||||
@@ -285,6 +335,7 @@ export default function Subscriptions() {
|
||||
const hasAnyPlan = availablePeriods.length > 0
|
||||
const isLoadingPlans = isCatalogLoading && !hasAnyPlan
|
||||
const combinedErrorMessage = errorMessage || catalogError
|
||||
const screenTitle = isCoinPackView ? COIN_PACK_SECTION_TITLE : 'Rejoignez le club MusicLand'
|
||||
|
||||
React.useEffect(() => {
|
||||
initialPackHandledRef.current = false
|
||||
@@ -410,9 +461,9 @@ export default function Subscriptions() {
|
||||
|
||||
const mobileListContentInset = React.useMemo(
|
||||
() => ({
|
||||
paddingBottom: mobileActionSafePadding + gutters * 3,
|
||||
paddingBottom: isCoinPackView ? gutters * 2 : mobileActionSafePadding + gutters * 3,
|
||||
}),
|
||||
[mobileActionSafePadding]
|
||||
[isCoinPackView, mobileActionSafePadding]
|
||||
)
|
||||
const handleBackPress = React.useCallback(() => {
|
||||
goBack()
|
||||
@@ -455,7 +506,7 @@ export default function Subscriptions() {
|
||||
return (
|
||||
<>
|
||||
<View style={styles.header}>
|
||||
<Text style={styles.title}>Rejoignez le club MusicLand</Text>
|
||||
<Text style={styles.title}>{screenTitle}</Text>
|
||||
<View style={styles.balanceContainer}>
|
||||
<Text style={styles.balanceLabel}>Votre solde actuel :</Text>
|
||||
<CreditAmount value={currentUserData?.coins || 0} iconSize={20} showPlus={false} />
|
||||
@@ -464,9 +515,13 @@ export default function Subscriptions() {
|
||||
{combinedErrorMessage ? <Text style={styles.errorText}>{combinedErrorMessage}</Text> : null}
|
||||
</>
|
||||
)
|
||||
}, [combinedErrorMessage])
|
||||
}, [combinedErrorMessage, currentUserData?.coins, screenTitle])
|
||||
|
||||
const renderSegmentedControl = React.useCallback(() => {
|
||||
if (isCoinPackView) {
|
||||
return null
|
||||
}
|
||||
|
||||
const segments = [
|
||||
{ key: 'monthly', label: 'Mensuel', plans: normalizedPlansByPeriod?.monthly },
|
||||
{ key: 'annual', label: 'Annuel', plans: normalizedPlansByPeriod?.annual },
|
||||
@@ -504,7 +559,7 @@ export default function Subscriptions() {
|
||||
})}
|
||||
</View>
|
||||
)
|
||||
}, [handlePeriodChange, isMobile, normalizedPlansByPeriod, selectedPeriodKey])
|
||||
}, [handlePeriodChange, isCoinPackView, isMobile, normalizedPlansByPeriod, selectedPeriodKey])
|
||||
|
||||
const renderMobilePlanItem = React.useCallback(
|
||||
({ item }) => (
|
||||
@@ -520,30 +575,51 @@ export default function Subscriptions() {
|
||||
[handleSelect, selectedPeriodKey, selectedPriceId]
|
||||
)
|
||||
|
||||
const renderMobileCoinPackItem = React.useCallback(
|
||||
({ item }) => (
|
||||
<View style={styles.mobileCard}>
|
||||
<CoinPackCard
|
||||
pack={item}
|
||||
onBuy={handleBuyCredits}
|
||||
isProcessing={processingPriceId === item.productId}
|
||||
/>
|
||||
</View>
|
||||
),
|
||||
[handleBuyCredits, processingPriceId]
|
||||
)
|
||||
|
||||
const renderMobileEmptyComponent = React.useCallback(() => {
|
||||
return (
|
||||
<View style={styles.mobileEmptyWrapper}>
|
||||
{isLoadingPlans ? (
|
||||
{(isCoinPackView ? isCatalogLoading && !coinPacks.length : isLoadingPlans) ? (
|
||||
<ActivityIndicator color={Palette.white} />
|
||||
) : (
|
||||
<Text style={styles.emptyState}>Aucun abonnement Stripe disponible pour le moment.</Text>
|
||||
<Text style={styles.emptyState}>
|
||||
{isCoinPackView
|
||||
? "Aucun pack de crédits n'est disponible pour le moment."
|
||||
: 'Aucun abonnement Stripe disponible pour le moment.'}
|
||||
</Text>
|
||||
)}
|
||||
</View>
|
||||
)
|
||||
}, [isLoadingPlans])
|
||||
}, [coinPacks.length, isCatalogLoading, isCoinPackView, isLoadingPlans])
|
||||
|
||||
const renderMobileFooterComponent = React.useCallback(() => {
|
||||
return (
|
||||
<View style={styles.mobileFooter}>
|
||||
{isLoadingPlans && currentPlanCount > 0 ? (
|
||||
{(isCoinPackView ? isCatalogLoading && coinPacks.length > 0 : isLoadingPlans && currentPlanCount > 0) ? (
|
||||
<View style={styles.inlineLoader}>
|
||||
<ActivityIndicator color={Palette.white} size="small" />
|
||||
</View>
|
||||
) : null}
|
||||
<Text style={styles.disclaimer}>{SUBSCRIPTION_DISCLAIMER}</Text>
|
||||
<Text style={styles.disclaimer}>
|
||||
{isCoinPackView
|
||||
? 'Les crédits sont ajoutés dès que le paiement Stripe est validé.'
|
||||
: SUBSCRIPTION_DISCLAIMER}
|
||||
</Text>
|
||||
</View>
|
||||
)
|
||||
}, [currentPlanCount, isLoadingPlans])
|
||||
}, [coinPacks.length, currentPlanCount, isCatalogLoading, isCoinPackView, isLoadingPlans])
|
||||
|
||||
const renderMobileTopSticky = React.useCallback(() => {
|
||||
return (
|
||||
@@ -566,6 +642,7 @@ export default function Subscriptions() {
|
||||
</View>
|
||||
)
|
||||
}, [actionButtonTitle, handleCheckout, isActionDisabled, mobileActionSafePadding])
|
||||
|
||||
const renderWebBackButton = React.useCallback(() => {
|
||||
if (isMobile) return null
|
||||
return (
|
||||
@@ -586,7 +663,7 @@ export default function Subscriptions() {
|
||||
<View style={styles.root}>
|
||||
<Page
|
||||
headerType="NAVIGATE"
|
||||
title="Abonnements"
|
||||
title={isCoinPackView ? COIN_PACK_SECTION_TITLE : 'Abonnements'}
|
||||
scrollEnabled={isWeb}
|
||||
width={isWeb ? 960 : undefined}
|
||||
containerStyle={styles.page}
|
||||
@@ -601,9 +678,9 @@ export default function Subscriptions() {
|
||||
<View style={[styles.content, isMobile && styles.mobileContent]}>
|
||||
{isMobile ? (
|
||||
<FlatList
|
||||
data={currentPlans}
|
||||
keyExtractor={(plan) => plan.priceId}
|
||||
renderItem={renderMobilePlanItem}
|
||||
data={isCoinPackView ? coinPacks : currentPlans}
|
||||
keyExtractor={(item) => item.productId || item.priceId}
|
||||
renderItem={isCoinPackView ? renderMobileCoinPackItem : renderMobilePlanItem}
|
||||
style={styles.mobileList}
|
||||
contentContainerStyle={[styles.mobileListContent, mobileListContentInset]}
|
||||
ListEmptyComponent={renderMobileEmptyComponent}
|
||||
@@ -716,7 +793,7 @@ export default function Subscriptions() {
|
||||
</View>
|
||||
</View>
|
||||
</Page>
|
||||
{isMobile ? renderMobileBottomActions() : null}
|
||||
{isMobile && !isCoinPackView ? renderMobileBottomActions() : null}
|
||||
</View>
|
||||
)
|
||||
}
|
||||
@@ -1162,6 +1239,17 @@ const styles = StyleSheet.create({
|
||||
fontSize: isWeb ? 22 : 20,
|
||||
color: Palette.white,
|
||||
},
|
||||
coinPackAmount: {
|
||||
fontFamily: FONT_FAMILY.InterBold,
|
||||
fontSize: 16,
|
||||
color: Palette.white,
|
||||
},
|
||||
coinPackAmountValue: {
|
||||
fontSize: 20,
|
||||
},
|
||||
coinPackButtonGradient: {
|
||||
width: '100%',
|
||||
},
|
||||
period: {
|
||||
fontFamily: FONT_FAMILY.InterRegular,
|
||||
fontSize: 14,
|
||||
|
||||
@@ -0,0 +1,10 @@
|
||||
export const PLAYLIST_CREATION_SUBSCRIPTION_MESSAGE =
|
||||
'La création de playlist est réservée aux abonnés.'
|
||||
|
||||
export const canCreatePlaylist = (hasActiveSubscription = false) => hasActiveSubscription === true
|
||||
|
||||
export const assertCanCreatePlaylist = (hasActiveSubscription = false) => {
|
||||
if (!canCreatePlaylist(hasActiveSubscription)) {
|
||||
throw new Error(PLAYLIST_CREATION_SUBSCRIPTION_MESSAGE)
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user