feat: simplify login and register

This commit is contained in:
2026-08-11 14:35:08 +02:00
parent afc48375ae
commit 1de35c4e43
2 changed files with 81 additions and 158 deletions
+76 -156
View File
@@ -1,96 +1,108 @@
import AsyncStorage from '@react-native-async-storage/async-storage' import AsyncStorage from '@react-native-async-storage/async-storage'
import { useNavigation } from '@react-navigation/native' import { useCallback, useEffect, useRef, useState } from 'react'
import { useCallback, useEffect, useState } from 'react' import {
import { Image, Platform, StyleSheet, Text, TouchableOpacity, View } from 'react-native' ActivityIndicator,
Image,
Platform,
StyleSheet,
Text,
TouchableOpacity,
View,
} from 'react-native'
import { background, icons } from '../assets' import { background, icons } from '../assets'
import EntryJingle from '../components/EntryJingle' import EntryJingle from '../components/EntryJingle'
import FullscreenIntroVideo from '../components/FullscreenIntroVideo' import FullscreenIntroVideo from '../components/FullscreenIntroVideo'
import GradientButton from '../components/GradientButton'
import { videosRef } from '../config/firebase' import { videosRef } from '../config/firebase'
import useDataFromRef from '../hooks/useDataFromRef' import useDataFromRef from '../hooks/useDataFromRef'
import Page from '../layouts/Page' import Page from '../layouts/Page'
import { Routes } from '../navigation' import { Routes } from '../navigation'
import { navigate, reset } from '../navigation/NavigationService'
import { useUser } from '../providers/UserDataProvider' import { useUser } from '../providers/UserDataProvider'
import { Palette } from '../styles' import { Palette } from '../styles'
import { FONT_FAMILY } from '../styles/Fonts' import { FONT_FAMILY } from '../styles/Fonts'
import { BlurView } from 'expo-blur'
const LANGUAGE_STORAGE_KEY = 'preferredLanguage' const LANGUAGE_STORAGE_KEY = 'preferredLanguage'
export default function LandingPage() { export default function LandingPage() {
const navigation = useNavigation()
const { currentUID } = useUser() const { currentUID } = useUser()
const [videoUrl, setVideoUrl] = useState(null) const [videoUrl, setVideoUrl] = useState(null)
const [selectedLanguage, setSelectedLanguage] = useState(null) const [selectedLanguage, setSelectedLanguage] = useState(null)
const [hasLoadedPreferredLanguage, setHasLoadedPreferredLanguage] = useState(false) const continuationHandledRef = useRef(false)
const [showFreeCreditsPopup, setShowFreeCreditsPopup] = useState(false)
const isAuthenticated = !!currentUID const isAuthenticated = !!currentUID
const handleSelectLanguage = useCallback((language) => { const { data: welcomeVideo, loading: welcomeVideoLoading } = useDataFromRef({
setSelectedLanguage(language) ref: selectedLanguage ? videosRef.doc(selectedLanguage) : null,
AsyncStorage.setItem(LANGUAGE_STORAGE_KEY, language).catch((error) => { initialState: null,
console.warn('LandingPage: failed to persist language', error)
})
}, [])
const handleLaunchAdventure = useCallback(() => {
if (isAuthenticated) {
navigation.navigate(Routes.BottomTab, {
screen: Routes.HomeStack,
params: { screen: Routes.Home },
})
return
}
navigation.navigate(Routes.Register)
}, [isAuthenticated, navigation])
const handleOpenSubscriptions = useCallback(() => {
navigation.navigate(Routes.Payments)
}, [navigation])
const { data: video } = useDataFromRef({
ref: videosRef.doc('fr'),
simpleRef: true, simpleRef: true,
condition: !!selectedLanguage,
refreshArray: [selectedLanguage],
}) })
useEffect(() => { const openRegistration = useCallback(() => {
let isMounted = true navigate(Routes.Register)
AsyncStorage.getItem(LANGUAGE_STORAGE_KEY)
.then((storedLanguage) => {
if (storedLanguage && isMounted) {
setSelectedLanguage(storedLanguage)
}
})
.catch((error) => {
console.warn('LandingPage: failed to load language', error)
})
.finally(() => {
if (isMounted) {
setHasLoadedPreferredLanguage(true)
}
})
return () => {
isMounted = false
}
}, []) }, [])
const handleSelectLanguage = useCallback(async (language) => {
try {
await AsyncStorage.setItem(LANGUAGE_STORAGE_KEY, language)
} catch (error) {
console.warn('LandingPage: failed to persist language', error)
}
continuationHandledRef.current = false
setSelectedLanguage(language)
}, [])
const handleVideoClose = useCallback(() => {
setVideoUrl(null)
openRegistration()
}, [openRegistration])
useEffect(() => { useEffect(() => {
if (currentUID) { if (!isAuthenticated) {
setShowFreeCreditsPopup(false)
return return
} }
if (selectedLanguage) { reset({
setShowFreeCreditsPopup(true) index: 0,
routes: [
{
name: Routes.BottomTab,
params: {
screen: Routes.HomeStack,
params: { screen: Routes.Home },
},
},
],
})
}, [isAuthenticated])
useEffect(() => {
if (
!selectedLanguage ||
welcomeVideoLoading ||
continuationHandledRef.current ||
isAuthenticated
) {
return
} }
}, [currentUID, selectedLanguage])
continuationHandledRef.current = true
const nextVideoUrl =
Platform.OS === 'web'
? welcomeVideo?.registrationIntroWeb
: welcomeVideo?.registrationIntro
if (typeof nextVideoUrl === 'string' && nextVideoUrl.trim().length > 0) {
setVideoUrl(nextVideoUrl)
return
}
openRegistration()
}, [isAuthenticated, openRegistration, selectedLanguage, welcomeVideo, welcomeVideoLoading])
return ( return (
<> <>
<EntryJingle active={hasLoadedPreferredLanguage && !selectedLanguage} /> <EntryJingle active={!isAuthenticated && !selectedLanguage} />
<Page <Page
shareBtn
// connect
title="Landing Page" title="Landing Page"
backgroundImg={background.homeBGWeb} backgroundImg={background.homeBGWeb}
> >
@@ -120,62 +132,13 @@ export default function LandingPage() {
</TouchableOpacity> </TouchableOpacity>
</View> </View>
)} )}
{selectedLanguage && ( {selectedLanguage && welcomeVideoLoading ? (
<View style={styles.actions}> <ActivityIndicator color={Palette.white} size="large" />
<GradientButton ) : null}
title="Découvrir Musicland"
onPress={() => setVideoUrl(video?.introWeb)}
containerStyle={styles.actionButton}
/>
<GradientButton
title="Présentation de Musicland"
onPress={() =>
setVideoUrl(Platform.OS === 'web' ? video?.landingWeb : video?.landing)
}
containerStyle={styles.actionButton}
/>
<GradientButton
title={
isAuthenticated
? "Continuer l'aventure"
: Platform.OS === 'web'
? "Je me lance dans l'aventure"
: 'Commencer'
}
onPress={handleLaunchAdventure}
containerStyle={styles.actionButton}
/>
{!isAuthenticated ? (
<GradientButton
title="Nos tarifs"
onPress={handleOpenSubscriptions}
containerStyle={styles.actionButton}
/>
) : null}
{showFreeCreditsPopup && (
<BlurView style={styles.freeCreditsCard}>
<View style={styles.freeCreditsContent}>
<View style={styles.freeCreditsTextContainer}>
<Text style={styles.freeCreditsTitle}>10 crédits offert</Text>
<Text style={styles.freeCreditsSubtitle}>pour 1 parcours gratuit !</Text>
</View>
<TouchableOpacity
onPress={() => setShowFreeCreditsPopup(false)}
accessibilityRole="button"
accessibilityLabel="Fermer le message des crédits offerts"
style={styles.freeCreditsClose}
>
<Image source={icons.close} style={styles.freeCreditsCloseIcon} />
</TouchableOpacity>
</View>
</BlurView>
)}
</View>
)}
</View> </View>
</View> </View>
</Page> </Page>
<FullscreenIntroVideo url={videoUrl} visible={!!videoUrl} onClose={() => setVideoUrl(null)} /> <FullscreenIntroVideo url={videoUrl} visible={!!videoUrl} onClose={handleVideoClose} />
</> </>
) )
} }
@@ -213,47 +176,4 @@ const styles = StyleSheet.create({
color: Palette.white, color: Palette.white,
fontFamily: FONT_FAMILY.InterSemiBold, fontFamily: FONT_FAMILY.InterSemiBold,
}, },
actions: {
width: '100%',
maxWidth: 320,
gap: 16,
},
actionButton: {
width: '100%',
},
freeCreditsCard: {
width: '100%',
borderRadius: 18,
paddingVertical: 14,
paddingHorizontal: 20,
overflow: 'hidden',
},
freeCreditsContent: {
flexDirection: 'row',
alignItems: 'center',
gap: 12,
},
freeCreditsTextContainer: {
flex: 1,
gap: 2,
},
freeCreditsTitle: {
fontSize: 16,
color: Palette.white,
fontFamily: FONT_FAMILY.InterSemiBold,
},
freeCreditsSubtitle: {
fontSize: 13,
color: Palette.white,
fontFamily: FONT_FAMILY.InterRegular,
},
freeCreditsClose: {
padding: 6,
marginLeft: 6,
},
freeCreditsCloseIcon: {
width: 14,
height: 14,
tintColor: Palette.grayMid,
},
}) })
+5 -2
View File
@@ -6,7 +6,6 @@ import { Routes } from '../navigation'
import { SplashAnimationContext } from '../providers/SplashAnimationProvider' import { SplashAnimationContext } from '../providers/SplashAnimationProvider'
import { UserDataContext } from '../providers/UserDataProvider' import { UserDataContext } from '../providers/UserDataProvider'
import { getRouteAfterAuthentication } from '../utils/registrationFlow'
export default ({ navigation }) => { export default ({ navigation }) => {
const { setCurrentUserData } = useContext(UserDataContext) const { setCurrentUserData } = useContext(UserDataContext)
@@ -29,7 +28,11 @@ export default ({ navigation }) => {
index: 0, index: 0,
routes: [ routes: [
{ {
name: getRouteAfterAuthentication(user), name: Routes.BottomTab,
params: {
screen: Routes.HomeStack,
params: { screen: Routes.Home },
},
}, },
], ],
}) })