feat: fix payment flow
This commit is contained in:
@@ -19,6 +19,7 @@ const Overlay = ({
|
|||||||
contentContainerStyle = {},
|
contentContainerStyle = {},
|
||||||
|
|
||||||
hasPortal = true,
|
hasPortal = true,
|
||||||
|
keepMounted = false,
|
||||||
}) => {
|
}) => {
|
||||||
const { isNative } = useLayoutType()
|
const { isNative } = useLayoutType()
|
||||||
|
|
||||||
@@ -27,15 +28,17 @@ const Overlay = ({
|
|||||||
const shouldUseBlur = resolvedBlurIntensity > 0
|
const shouldUseBlur = resolvedBlurIntensity > 0
|
||||||
const BackgroundView = shouldUseBlur ? BlurView : View
|
const BackgroundView = shouldUseBlur ? BlurView : View
|
||||||
const backgroundProps = shouldUseBlur ? { intensity: resolvedBlurIntensity, tint: 'dark' } : {}
|
const backgroundProps = shouldUseBlur ? { intensity: resolvedBlurIntensity, tint: 'dark' } : {}
|
||||||
|
const shouldRender = isVisible || keepMounted
|
||||||
|
const isKeptHidden = keepMounted && !isVisible
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<ContentContainerView>
|
<ContentContainerView>
|
||||||
<AnimatePresence>
|
<AnimatePresence>
|
||||||
{isVisible ? (
|
{shouldRender ? (
|
||||||
<Motion.View
|
<Motion.View
|
||||||
key="overlay"
|
key="overlay"
|
||||||
initial={{ opacity: 0 }}
|
initial={{ opacity: 0 }}
|
||||||
animate={{ opacity: 1 }}
|
animate={{ opacity: isVisible ? 1 : 0 }}
|
||||||
exit={{ opacity: 0 }}
|
exit={{ opacity: 0 }}
|
||||||
transition={{
|
transition={{
|
||||||
type: 'tween',
|
type: 'tween',
|
||||||
@@ -47,6 +50,7 @@ const Overlay = ({
|
|||||||
backgroundColor: Palette.ultraLightBlack,
|
backgroundColor: Palette.ultraLightBlack,
|
||||||
flex: 1,
|
flex: 1,
|
||||||
...Style.containerCenter,
|
...Style.containerCenter,
|
||||||
|
pointerEvents: isKeptHidden ? 'none' : 'auto',
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<BackgroundView
|
<BackgroundView
|
||||||
|
|||||||
+221
-15
@@ -1,10 +1,11 @@
|
|||||||
import React from 'react'
|
import React from 'react'
|
||||||
import { Linking, Platform, StyleSheet, View } from 'react-native'
|
import { Linking, Platform, StyleSheet, Text, View } from 'react-native'
|
||||||
import { EmbeddedCheckout, EmbeddedCheckoutProvider } from '@stripe/react-stripe-js'
|
import { EmbeddedCheckout, EmbeddedCheckoutProvider } from '@stripe/react-stripe-js'
|
||||||
import { loadStripe } from '@stripe/stripe-js'
|
import { loadStripe } from '@stripe/stripe-js'
|
||||||
import * as WebBrowser from 'expo-web-browser'
|
import * as WebBrowser from 'expo-web-browser'
|
||||||
import Overlay from '../components/Overlay'
|
import Overlay from '../components/Overlay'
|
||||||
import Button from '../components/Button'
|
import Button from '../components/Button'
|
||||||
|
import ActivityLoader from '../components/ActivityLoader'
|
||||||
import { Palette } from '../styles'
|
import { Palette } from '../styles'
|
||||||
import { mainBorderRadius } from '../styles/Style'
|
import { mainBorderRadius } from '../styles/Style'
|
||||||
import { STRIPE_PUBLISHABLE_KEY_LIVE, STRIPE_PUBLISHABLE_KEY_TEST } from '../data/keys'
|
import { STRIPE_PUBLISHABLE_KEY_LIVE, STRIPE_PUBLISHABLE_KEY_TEST } from '../data/keys'
|
||||||
@@ -14,6 +15,8 @@ import { useUserData } from './UserDataProvider'
|
|||||||
import { formatDate, toDate } from '../utils/dateFormatting'
|
import { formatDate, toDate } from '../utils/dateFormatting'
|
||||||
|
|
||||||
const FUNCTIONS_REGION = 'europe-west1'
|
const FUNCTIONS_REGION = 'europe-west1'
|
||||||
|
const EMBEDDED_CHECKOUT_BOOTSTRAP_HEIGHT = 620
|
||||||
|
const EMBEDDED_CHECKOUT_TIMEOUT_MS = 6000
|
||||||
const STRIPE_SUCCESS_URL =
|
const STRIPE_SUCCESS_URL =
|
||||||
'https://dashboard.stripe.com/test/billing/starter-guide/checkout-success'
|
'https://dashboard.stripe.com/test/billing/starter-guide/checkout-success'
|
||||||
const STRIPE_CANCEL_URL = STRIPE_SUCCESS_URL
|
const STRIPE_CANCEL_URL = STRIPE_SUCCESS_URL
|
||||||
@@ -85,6 +88,11 @@ const StripeProvider = ({ children }) => {
|
|||||||
const canUseEmbeddedCheckout = isWeb && Boolean(stripePromise)
|
const canUseEmbeddedCheckout = isWeb && Boolean(stripePromise)
|
||||||
|
|
||||||
const [clientSecret, setClientSecret] = React.useState(null)
|
const [clientSecret, setClientSecret] = React.useState(null)
|
||||||
|
const [isEmbeddedCheckoutVisible, setIsEmbeddedCheckoutVisible] = React.useState(false)
|
||||||
|
const [embeddedCheckoutAttempt, setEmbeddedCheckoutAttempt] = React.useState(0)
|
||||||
|
const [embeddedCheckoutInstanceKey, setEmbeddedCheckoutInstanceKey] = React.useState(0)
|
||||||
|
const [embeddedCheckoutPhase, setEmbeddedCheckoutPhase] = React.useState('idle')
|
||||||
|
const embeddedCheckoutSessionKeyRef = React.useRef(null)
|
||||||
const [subscriptions, setSubscriptions] = React.useState({
|
const [subscriptions, setSubscriptions] = React.useState({
|
||||||
monthly: [],
|
monthly: [],
|
||||||
annual: [],
|
annual: [],
|
||||||
@@ -103,9 +111,32 @@ const StripeProvider = ({ children }) => {
|
|||||||
null
|
null
|
||||||
|
|
||||||
const closeEmbeddedCheckout = React.useCallback(() => {
|
const closeEmbeddedCheckout = React.useCallback(() => {
|
||||||
|
setIsEmbeddedCheckoutVisible(false)
|
||||||
|
}, [])
|
||||||
|
|
||||||
|
const resetEmbeddedCheckout = React.useCallback(() => {
|
||||||
|
embeddedCheckoutSessionKeyRef.current = null
|
||||||
|
setIsEmbeddedCheckoutVisible(false)
|
||||||
|
setEmbeddedCheckoutAttempt(0)
|
||||||
|
setEmbeddedCheckoutPhase('idle')
|
||||||
setClientSecret(null)
|
setClientSecret(null)
|
||||||
}, [])
|
}, [])
|
||||||
|
|
||||||
|
const retryEmbeddedCheckout = React.useCallback(() => {
|
||||||
|
setEmbeddedCheckoutPhase('loading')
|
||||||
|
setEmbeddedCheckoutAttempt((currentAttempt) => currentAttempt + 1)
|
||||||
|
setEmbeddedCheckoutInstanceKey((currentKey) => currentKey + 1)
|
||||||
|
}, [])
|
||||||
|
|
||||||
|
const openEmbeddedCheckout = React.useCallback((nextClientSecret) => {
|
||||||
|
embeddedCheckoutSessionKeyRef.current = null
|
||||||
|
setEmbeddedCheckoutAttempt(0)
|
||||||
|
setEmbeddedCheckoutPhase('loading')
|
||||||
|
setEmbeddedCheckoutInstanceKey((currentKey) => currentKey + 1)
|
||||||
|
setClientSecret(nextClientSecret)
|
||||||
|
setIsEmbeddedCheckoutVisible(true)
|
||||||
|
}, [])
|
||||||
|
|
||||||
const redirectToCheckout = React.useCallback(async (checkoutUrl = {}) => {
|
const redirectToCheckout = React.useCallback(async (checkoutUrl = {}) => {
|
||||||
if (!checkoutUrl) {
|
if (!checkoutUrl) {
|
||||||
throw new Error('Session Stripe introuvable.')
|
throw new Error('Session Stripe introuvable.')
|
||||||
@@ -144,6 +175,20 @@ const StripeProvider = ({ children }) => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const wantsEmbeddedCheckout = useEmbeddedFlow && canUseEmbeddedCheckout
|
const wantsEmbeddedCheckout = useEmbeddedFlow && canUseEmbeddedCheckout
|
||||||
|
const embeddedCheckoutSessionKey = wantsEmbeddedCheckout
|
||||||
|
? JSON.stringify({ callableName, payload })
|
||||||
|
: null
|
||||||
|
|
||||||
|
if (
|
||||||
|
wantsEmbeddedCheckout &&
|
||||||
|
clientSecret &&
|
||||||
|
embeddedCheckoutPhase !== 'failed' &&
|
||||||
|
embeddedCheckoutSessionKeyRef.current === embeddedCheckoutSessionKey
|
||||||
|
) {
|
||||||
|
setIsEmbeddedCheckoutVisible(true)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
const safariWindow = wantsEmbeddedCheckout ? null : openSafariCheckoutWindow()
|
const safariWindow = wantsEmbeddedCheckout ? null : openSafariCheckoutWindow()
|
||||||
try {
|
try {
|
||||||
const callable = getFunctionsClient(FUNCTIONS_REGION).httpsCallable(callableName)
|
const callable = getFunctionsClient(FUNCTIONS_REGION).httpsCallable(callableName)
|
||||||
@@ -155,7 +200,12 @@ const StripeProvider = ({ children }) => {
|
|||||||
if (!clientSecret) {
|
if (!clientSecret) {
|
||||||
throw new Error('Session Stripe introuvable.')
|
throw new Error('Session Stripe introuvable.')
|
||||||
}
|
}
|
||||||
|
embeddedCheckoutSessionKeyRef.current = embeddedCheckoutSessionKey
|
||||||
|
setEmbeddedCheckoutAttempt(0)
|
||||||
|
setEmbeddedCheckoutPhase('loading')
|
||||||
|
setEmbeddedCheckoutInstanceKey((currentKey) => currentKey + 1)
|
||||||
setClientSecret(clientSecret)
|
setClientSecret(clientSecret)
|
||||||
|
setIsEmbeddedCheckoutVisible(true)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -175,7 +225,12 @@ const StripeProvider = ({ children }) => {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
[canUseEmbeddedCheckout, redirectToCheckout, setClientSecret]
|
[
|
||||||
|
canUseEmbeddedCheckout,
|
||||||
|
clientSecret,
|
||||||
|
embeddedCheckoutPhase,
|
||||||
|
redirectToCheckout,
|
||||||
|
]
|
||||||
)
|
)
|
||||||
|
|
||||||
const createSubscriptionCheckout = React.useCallback(
|
const createSubscriptionCheckout = React.useCallback(
|
||||||
@@ -334,6 +389,96 @@ const StripeProvider = ({ children }) => {
|
|||||||
}
|
}
|
||||||
}, [stripeCustomerId, localSubscriptionId])
|
}, [stripeCustomerId, localSubscriptionId])
|
||||||
|
|
||||||
|
const handleEmbeddedCheckoutComplete = React.useCallback(() => {
|
||||||
|
resetEmbeddedCheckout()
|
||||||
|
fetchActiveSubscription()
|
||||||
|
}, [fetchActiveSubscription, resetEmbeddedCheckout])
|
||||||
|
|
||||||
|
const embeddedCheckoutOptions = React.useMemo(
|
||||||
|
() => ({
|
||||||
|
clientSecret,
|
||||||
|
onComplete: handleEmbeddedCheckoutComplete,
|
||||||
|
}),
|
||||||
|
[clientSecret, handleEmbeddedCheckoutComplete]
|
||||||
|
)
|
||||||
|
|
||||||
|
React.useEffect(() => {
|
||||||
|
if (
|
||||||
|
!isWeb ||
|
||||||
|
!clientSecret ||
|
||||||
|
embeddedCheckoutPhase !== 'loading' ||
|
||||||
|
typeof document === 'undefined' ||
|
||||||
|
typeof MutationObserver !== 'function'
|
||||||
|
) {
|
||||||
|
return undefined
|
||||||
|
}
|
||||||
|
|
||||||
|
let checkoutFrameObserver = null
|
||||||
|
let documentObserver = null
|
||||||
|
let observedFrame = null
|
||||||
|
let isReady = false
|
||||||
|
|
||||||
|
const markCheckoutReady = () => {
|
||||||
|
if (isReady) return
|
||||||
|
isReady = true
|
||||||
|
setEmbeddedCheckoutPhase('ready')
|
||||||
|
}
|
||||||
|
|
||||||
|
const inspectCheckoutFrame = () => {
|
||||||
|
const checkoutFrame = document.querySelector(
|
||||||
|
'#musicland-embedded-checkout iframe[title="Embedded checkout"]'
|
||||||
|
)
|
||||||
|
if (!checkoutFrame) return
|
||||||
|
|
||||||
|
const frameHeight = Number.parseFloat(checkoutFrame.style.height)
|
||||||
|
if (
|
||||||
|
Number.isFinite(frameHeight) &&
|
||||||
|
Math.abs(frameHeight - EMBEDDED_CHECKOUT_BOOTSTRAP_HEIGHT) > 1
|
||||||
|
) {
|
||||||
|
markCheckoutReady()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if (checkoutFrame === observedFrame) return
|
||||||
|
checkoutFrameObserver?.disconnect()
|
||||||
|
observedFrame = checkoutFrame
|
||||||
|
checkoutFrameObserver = new MutationObserver(inspectCheckoutFrame)
|
||||||
|
checkoutFrameObserver.observe(checkoutFrame, {
|
||||||
|
attributes: true,
|
||||||
|
attributeFilter: ['style'],
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
documentObserver = new MutationObserver(inspectCheckoutFrame)
|
||||||
|
documentObserver.observe(document.body, {
|
||||||
|
childList: true,
|
||||||
|
subtree: true,
|
||||||
|
})
|
||||||
|
inspectCheckoutFrame()
|
||||||
|
|
||||||
|
const loadingTimeout = setTimeout(() => {
|
||||||
|
if (isReady) return
|
||||||
|
if (embeddedCheckoutAttempt === 0) {
|
||||||
|
console.warn('[StripeProvider] Embedded Checkout stalled, retrying initialization')
|
||||||
|
retryEmbeddedCheckout()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
console.error('[StripeProvider] Embedded Checkout failed to become ready')
|
||||||
|
setEmbeddedCheckoutPhase('failed')
|
||||||
|
}, EMBEDDED_CHECKOUT_TIMEOUT_MS)
|
||||||
|
|
||||||
|
return () => {
|
||||||
|
clearTimeout(loadingTimeout)
|
||||||
|
checkoutFrameObserver?.disconnect()
|
||||||
|
documentObserver?.disconnect()
|
||||||
|
}
|
||||||
|
}, [
|
||||||
|
clientSecret,
|
||||||
|
embeddedCheckoutAttempt,
|
||||||
|
embeddedCheckoutPhase,
|
||||||
|
retryEmbeddedCheckout,
|
||||||
|
])
|
||||||
|
|
||||||
const fetchStripeCatalog = React.useCallback(async () => {
|
const fetchStripeCatalog = React.useCallback(async () => {
|
||||||
setIsCatalogLoading(true)
|
setIsCatalogLoading(true)
|
||||||
setCatalogError(null)
|
setCatalogError(null)
|
||||||
@@ -410,7 +555,7 @@ const StripeProvider = ({ children }) => {
|
|||||||
createSongDownloadCheckout,
|
createSongDownloadCheckout,
|
||||||
createPlaybackDownloadCheckout,
|
createPlaybackDownloadCheckout,
|
||||||
createCoinPackCheckout,
|
createCoinPackCheckout,
|
||||||
openEmbeddedCheckout: setClientSecret,
|
openEmbeddedCheckout,
|
||||||
closeEmbeddedCheckout,
|
closeEmbeddedCheckout,
|
||||||
activeSubscription: activeSubscriptionInfo.subscription,
|
activeSubscription: activeSubscriptionInfo.subscription,
|
||||||
activeSubscriptionInfo,
|
activeSubscriptionInfo,
|
||||||
@@ -428,7 +573,7 @@ const StripeProvider = ({ children }) => {
|
|||||||
createSongDownloadCheckout,
|
createSongDownloadCheckout,
|
||||||
createPlaybackDownloadCheckout,
|
createPlaybackDownloadCheckout,
|
||||||
createCoinPackCheckout,
|
createCoinPackCheckout,
|
||||||
setClientSecret,
|
openEmbeddedCheckout,
|
||||||
closeEmbeddedCheckout,
|
closeEmbeddedCheckout,
|
||||||
activeSubscriptionInfo,
|
activeSubscriptionInfo,
|
||||||
isActiveSubscriptionLoading,
|
isActiveSubscriptionLoading,
|
||||||
@@ -441,27 +586,46 @@ const StripeProvider = ({ children }) => {
|
|||||||
<StripeContext.Provider value={providerValue}>
|
<StripeContext.Provider value={providerValue}>
|
||||||
{children}
|
{children}
|
||||||
<Overlay
|
<Overlay
|
||||||
isVisible={clientSecret !== null}
|
isVisible={isEmbeddedCheckoutVisible}
|
||||||
setIsVisible={closeEmbeddedCheckout}
|
setIsVisible={closeEmbeddedCheckout}
|
||||||
contentContainerStyle={styles.overlayContentFull}
|
contentContainerStyle={styles.overlayContentFull}
|
||||||
|
keepMounted={clientSecret !== null}
|
||||||
>
|
>
|
||||||
<View style={[StyleSheet.absoluteFillObject, styles.overlayContent]}>
|
<View style={[StyleSheet.absoluteFillObject, styles.overlayContent]}>
|
||||||
<View style={[styles.embeddedWrapper, styles.embeddedWrapperFull]}>
|
<View style={[styles.embeddedWrapper, styles.embeddedWrapperFull]}>
|
||||||
{clientSecret && stripePromise ? (
|
{clientSecret && stripePromise ? (
|
||||||
<EmbeddedCheckoutProvider stripe={stripePromise} options={{ clientSecret }}>
|
<EmbeddedCheckoutProvider
|
||||||
<EmbeddedCheckout
|
key={embeddedCheckoutInstanceKey}
|
||||||
onComplete={() => {
|
stripe={stripePromise}
|
||||||
closeEmbeddedCheckout()
|
options={embeddedCheckoutOptions}
|
||||||
fetchActiveSubscription()
|
>
|
||||||
}}
|
<EmbeddedCheckout id="musicland-embedded-checkout" />
|
||||||
/>
|
|
||||||
</EmbeddedCheckoutProvider>
|
</EmbeddedCheckoutProvider>
|
||||||
) : null}
|
) : null}
|
||||||
</View>
|
</View>
|
||||||
|
{embeddedCheckoutPhase === 'loading' ? (
|
||||||
|
<View style={styles.checkoutLoading}>
|
||||||
|
<ActivityLoader defaultMessage="Chargement du paiement sécurisé..." />
|
||||||
|
</View>
|
||||||
|
) : null}
|
||||||
|
{embeddedCheckoutPhase === 'failed' ? (
|
||||||
|
<View style={styles.checkoutFailure}>
|
||||||
|
<Text style={styles.checkoutFailureTitle}>Le paiement ne s’est pas chargé.</Text>
|
||||||
|
<Text style={styles.checkoutFailureMessage}>
|
||||||
|
Vérifie ta connexion puis réessaie.
|
||||||
|
</Text>
|
||||||
|
<Button
|
||||||
|
type="primary"
|
||||||
|
text="Réessayer"
|
||||||
|
onPress={retryEmbeddedCheckout}
|
||||||
|
containerStyle={styles.retryButton}
|
||||||
|
/>
|
||||||
|
</View>
|
||||||
|
) : null}
|
||||||
<Button
|
<Button
|
||||||
type="primary"
|
type="primary"
|
||||||
isAbsoluteBottom
|
isAbsoluteBottom
|
||||||
text="Fermer"
|
text="Fermer le paiement"
|
||||||
onPress={closeEmbeddedCheckout}
|
onPress={closeEmbeddedCheckout}
|
||||||
containerStyle={styles.closeButton}
|
containerStyle={styles.closeButton}
|
||||||
textStyle={styles.closeButtonText}
|
textStyle={styles.closeButtonText}
|
||||||
@@ -488,11 +652,52 @@ const styles = StyleSheet.create({
|
|||||||
overflow: 'hidden',
|
overflow: 'hidden',
|
||||||
},
|
},
|
||||||
embeddedWrapperFull: {
|
embeddedWrapperFull: {
|
||||||
width: '100%',
|
position: 'absolute',
|
||||||
height: '100%',
|
top: 0,
|
||||||
|
right: 0,
|
||||||
|
bottom: 72,
|
||||||
|
left: 0,
|
||||||
|
width: 'auto',
|
||||||
|
height: 'auto',
|
||||||
maxWidth: '100%',
|
maxWidth: '100%',
|
||||||
borderRadius: 0,
|
borderRadius: 0,
|
||||||
alignSelf: 'stretch',
|
alignSelf: 'stretch',
|
||||||
|
overflow: 'auto',
|
||||||
|
zIndex: 1,
|
||||||
|
},
|
||||||
|
checkoutLoading: {
|
||||||
|
...StyleSheet.absoluteFillObject,
|
||||||
|
alignItems: 'center',
|
||||||
|
justifyContent: 'center',
|
||||||
|
paddingBottom: 72,
|
||||||
|
pointerEvents: 'none',
|
||||||
|
backgroundColor: 'rgba(7, 17, 27, 0.88)',
|
||||||
|
zIndex: 2,
|
||||||
|
},
|
||||||
|
checkoutFailure: {
|
||||||
|
...StyleSheet.absoluteFillObject,
|
||||||
|
alignItems: 'center',
|
||||||
|
justifyContent: 'center',
|
||||||
|
gap: 12,
|
||||||
|
paddingHorizontal: 24,
|
||||||
|
paddingBottom: 72,
|
||||||
|
backgroundColor: 'rgba(7, 17, 27, 0.94)',
|
||||||
|
zIndex: 2,
|
||||||
|
},
|
||||||
|
checkoutFailureTitle: {
|
||||||
|
color: Palette.white,
|
||||||
|
fontSize: 20,
|
||||||
|
textAlign: 'center',
|
||||||
|
},
|
||||||
|
checkoutFailureMessage: {
|
||||||
|
color: Palette.white,
|
||||||
|
fontSize: 15,
|
||||||
|
opacity: 0.8,
|
||||||
|
textAlign: 'center',
|
||||||
|
},
|
||||||
|
retryButton: {
|
||||||
|
width: '100%',
|
||||||
|
maxWidth: 320,
|
||||||
},
|
},
|
||||||
closeButton: {
|
closeButton: {
|
||||||
height: 56,
|
height: 56,
|
||||||
@@ -503,6 +708,7 @@ const styles = StyleSheet.create({
|
|||||||
shadowRadius: 10,
|
shadowRadius: 10,
|
||||||
shadowOffset: { width: 0, height: 6 },
|
shadowOffset: { width: 0, height: 6 },
|
||||||
elevation: 6,
|
elevation: 6,
|
||||||
|
zIndex: 3,
|
||||||
},
|
},
|
||||||
closeButtonText: {
|
closeButtonText: {
|
||||||
color: Palette.white,
|
color: Palette.white,
|
||||||
|
|||||||
@@ -173,10 +173,13 @@ const PlaybackDownload = ({ route }) => {
|
|||||||
const hasShownAfterPlaybackRef = useRef(false)
|
const hasShownAfterPlaybackRef = useRef(false)
|
||||||
|
|
||||||
const projectForDownload = useMemo(() => {
|
const projectForDownload = useMemo(() => {
|
||||||
if (routeProject?.id && selectedProject?.id && routeProject.id === selectedProject.id) {
|
if (routeProject && typeof routeProject === 'object' && routeProject.id) {
|
||||||
|
if (selectedProject?.id === routeProject.id) {
|
||||||
return selectedProject
|
return selectedProject
|
||||||
}
|
}
|
||||||
return routeProject || selectedProject || null
|
return routeProject
|
||||||
|
}
|
||||||
|
return selectedProject?.id ? selectedProject : null
|
||||||
}, [routeProject, selectedProject])
|
}, [routeProject, selectedProject])
|
||||||
|
|
||||||
const playbackDownloadStatus = projectForDownload?.playbackDownloadPurchase?.status || null
|
const playbackDownloadStatus = projectForDownload?.playbackDownloadPurchase?.status || null
|
||||||
@@ -365,7 +368,7 @@ const PlaybackDownload = ({ route }) => {
|
|||||||
)
|
)
|
||||||
|
|
||||||
const startPlaybackDownloadCheckout = useCallback(
|
const startPlaybackDownloadCheckout = useCallback(
|
||||||
async (intent, { force = false } = {}) => {
|
async (intent) => {
|
||||||
if (!projectForDownload?.id) {
|
if (!projectForDownload?.id) {
|
||||||
setTooltip({
|
setTooltip({
|
||||||
type: 'error',
|
type: 'error',
|
||||||
@@ -374,7 +377,7 @@ const PlaybackDownload = ({ route }) => {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!force && (isCheckoutLaunching || downloadPaymentPending)) {
|
if (isCheckoutLaunching) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -396,7 +399,6 @@ const PlaybackDownload = ({ route }) => {
|
|||||||
},
|
},
|
||||||
[
|
[
|
||||||
createPlaybackDownloadCheckout,
|
createPlaybackDownloadCheckout,
|
||||||
downloadPaymentPending,
|
|
||||||
isCheckoutLaunching,
|
isCheckoutLaunching,
|
||||||
projectForDownload,
|
projectForDownload,
|
||||||
setTooltip,
|
setTooltip,
|
||||||
@@ -480,7 +482,7 @@ const PlaybackDownload = ({ route }) => {
|
|||||||
<Text style={styles.sectionTitle}>Téléchargement</Text>
|
<Text style={styles.sectionTitle}>Téléchargement</Text>
|
||||||
<Text style={styles.sectionDescription}>{downloadAccessLabel}</Text>
|
<Text style={styles.sectionDescription}>{downloadAccessLabel}</Text>
|
||||||
<BorderGradientButton
|
<BorderGradientButton
|
||||||
title="Option 1 · Téléchargement à des fins personnelles"
|
title="Télécharger à des fins personnelles"
|
||||||
onPress={() => handlePlaybackChoice('personal')}
|
onPress={() => handlePlaybackChoice('personal')}
|
||||||
disabled={isPublishing || isDownloading || isCheckoutLaunching}
|
disabled={isPublishing || isDownloading || isCheckoutLaunching}
|
||||||
loading={isDownloading && !isPublishing}
|
loading={isDownloading && !isPublishing}
|
||||||
@@ -492,7 +494,7 @@ const PlaybackDownload = ({ route }) => {
|
|||||||
title={
|
title={
|
||||||
isPublishing
|
isPublishing
|
||||||
? 'Téléchargement et publication en cours...'
|
? 'Téléchargement et publication en cours...'
|
||||||
: 'Option 2 · Télécharger et publier sur MusicLand'
|
: 'Télécharger et publier sur MusicLand'
|
||||||
}
|
}
|
||||||
onPress={() => handlePlaybackChoice('publish')}
|
onPress={() => handlePlaybackChoice('publish')}
|
||||||
disabled={isPublishing || isDownloading || isCheckoutLaunching}
|
disabled={isPublishing || isDownloading || isCheckoutLaunching}
|
||||||
|
|||||||
@@ -74,9 +74,6 @@ const getStageLockState = (key, metadata) => {
|
|||||||
}
|
}
|
||||||
return metadata.lyricsCount <= 0
|
return metadata.lyricsCount <= 0
|
||||||
case 'director':
|
case 'director':
|
||||||
if (metadata.hasPlaybackAsset) {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
return !metadata.hasCover
|
return !metadata.hasCover
|
||||||
default:
|
default:
|
||||||
return true
|
return true
|
||||||
@@ -135,7 +132,7 @@ const getStageDescription = (key, metadata) => {
|
|||||||
return 'Votre playback IA est prêt à être validé'
|
return 'Votre playback IA est prêt à être validé'
|
||||||
}
|
}
|
||||||
if (hasPlaybackAsset) {
|
if (hasPlaybackAsset) {
|
||||||
return 'Modifier le playback généré'
|
return 'Télécharger ou publier votre playback'
|
||||||
}
|
}
|
||||||
return 'Commencer la création de votre playback'
|
return 'Commencer la création de votre playback'
|
||||||
default:
|
default:
|
||||||
@@ -153,7 +150,7 @@ const getStageLockedDescription = (key, metadata) => {
|
|||||||
}
|
}
|
||||||
return metadata.lyricsCount > 0 ? undefined : 'Créez vos paroles pour débloquer le studio'
|
return metadata.lyricsCount > 0 ? undefined : 'Créez vos paroles pour débloquer le studio'
|
||||||
case 'director':
|
case 'director':
|
||||||
return metadata.hasPlaybackAsset ? 'Impossible de modifier le playback' : undefined
|
return undefined
|
||||||
default:
|
default:
|
||||||
return undefined
|
return undefined
|
||||||
}
|
}
|
||||||
@@ -230,6 +227,15 @@ export const getStageAction = (key, project) => {
|
|||||||
return { route: hasFinalCover ? Routes.ChooseCoverType : Routes.PouchReady }
|
return { route: hasFinalCover ? Routes.ChooseCoverType : Routes.PouchReady }
|
||||||
}
|
}
|
||||||
case 'director':
|
case 'director':
|
||||||
|
if (project?.playbackUrl) {
|
||||||
|
return {
|
||||||
|
route: Routes.PlaybackDownload,
|
||||||
|
params: {
|
||||||
|
action: 'playback',
|
||||||
|
project,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
return {
|
return {
|
||||||
route: Routes.Playback,
|
route: Routes.Playback,
|
||||||
params: project ? { project } : undefined,
|
params: project ? { project } : undefined,
|
||||||
|
|||||||
Reference in New Issue
Block a user