feat: simplify login and register
This commit is contained in:
+76
-156
@@ -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,
|
|
||||||
},
|
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -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 },
|
||||||
|
},
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
})
|
})
|
||||||
|
|||||||
Reference in New Issue
Block a user