feat: update onboarding and application flows

This commit is contained in:
Jacques_tirot
2026-08-25 15:55:11 +02:00
parent 4fb8a3e1e0
commit bc7edcf3a7
38 changed files with 7234 additions and 289 deletions
+47 -22
View File
@@ -1,8 +1,9 @@
import { Portal } from '@gorhom/portal'
import { Asset } from 'expo-asset'
import React, { useCallback, useEffect, useRef, useState } from 'react'
import { Pressable, Text, View } from 'react-native'
import { resolveMediaUri } from '../utils/resolveMediaUri'
const CLOSE_THRESHOLD_SECONDS = 0.35
const CLOSE_POLL_INTERVAL_MS = 500
@@ -45,20 +46,30 @@ const closeTextStyle = {
fontSize: 14,
}
const resolveModuleUri = async (module) => {
const asset = Asset.fromModule(module)
if (!asset.localUri && !asset.uri) {
await asset.downloadAsync()
}
return asset.localUri ?? asset.uri ?? null
const playButtonStyle = {
backgroundColor: '#000000CC',
paddingVertical: 14,
paddingHorizontal: 20,
borderRadius: 24,
borderWidth: 1,
borderColor: '#FFFFFF88',
cursor: 'pointer',
zIndex: 1,
}
const FullscreenIntroVideo = ({ url, visible = true, onClose }) => {
const FullscreenIntroVideo = ({
url,
visible = true,
onClose,
contentFit = 'cover',
requireAudio = false,
skipLabel = 'Passer la vidéo',
playLabel = 'Lire la vidéo avec le son',
}) => {
const videoRef = useRef(null)
const [uri, setUri] = useState(null)
const [muted, setMuted] = useState(false)
const [awaitingInteraction, setAwaitingInteraction] = useState(false)
const hasClosedRef = useRef(false)
const handleClose = useCallback(() => {
@@ -92,20 +103,14 @@ const FullscreenIntroVideo = ({ url, visible = true, onClose }) => {
const assignUri = (nextUri) => {
if (isMounted) {
setMuted(false)
setAwaitingInteraction(false)
setUri(nextUri)
}
}
if (typeof url === 'string') {
assignUri(url)
return () => {
isMounted = false
}
}
const load = async () => {
try {
const nextUri = await resolveModuleUri(url)
const nextUri = await resolveMediaUri(url)
assignUri(nextUri)
} catch {
if (isMounted) {
@@ -142,7 +147,9 @@ const FullscreenIntroVideo = ({ url, visible = true, onClose }) => {
if (result?.catch) {
result.catch((error) => {
if (error?.name === 'NotAllowedError' && !muted) {
if (error?.name === 'NotAllowedError' && requireAudio) {
setAwaitingInteraction(true)
} else if (error?.name === 'NotAllowedError' && !muted) {
setMuted(true)
}
})
@@ -160,7 +167,20 @@ const FullscreenIntroVideo = ({ url, visible = true, onClose }) => {
const video = videoRef.current
video?.pause()
}
}, [evaluateShouldClose, muted, uri, visible])
}, [evaluateShouldClose, muted, requireAudio, uri, visible])
const handleManualPlay = useCallback(() => {
const video = videoRef.current
if (!video) return
setMuted(false)
video.muted = false
video.currentTime = 0
const result = video.play()
if (result?.then) {
result.then(() => setAwaitingInteraction(false)).catch(() => {})
}
}, [])
useEffect(() => {
const video = videoRef.current
@@ -187,7 +207,7 @@ const FullscreenIntroVideo = ({ url, visible = true, onClose }) => {
<video
ref={videoRef}
src={uri}
style={videoStyle}
style={{ ...videoStyle, objectFit: contentFit }}
playsInline
autoPlay
loop={false}
@@ -199,8 +219,13 @@ const FullscreenIntroVideo = ({ url, visible = true, onClose }) => {
onError={handleClose}
/>
) : null}
{awaitingInteraction ? (
<Pressable onPress={handleManualPlay} style={playButtonStyle}>
<Text style={closeTextStyle}>{playLabel}</Text>
</Pressable>
) : null}
<Pressable onPress={handleClose} style={closeButtonStyle}>
<Text style={closeTextStyle}>Passer la vidéo</Text>
<Text style={closeTextStyle}>{skipLabel}</Text>
</Pressable>
</View>
</Portal>