831 lines
23 KiB
JavaScript
831 lines
23 KiB
JavaScript
import React, { useCallback, useEffect, useMemo, useRef, useState } from 'react'
|
||
import { Image as ExpoImage } from 'expo-image'
|
||
import { Pressable, ScrollView, StyleSheet, Text, View } from 'react-native'
|
||
import useMinuit from 'react-native-minuit/src/hooks/useMinuit.js'
|
||
import { background, cardsImg, icons } from '../../assets'
|
||
import Alert from '../../components/Alert'
|
||
import BorderGradient from '../../components/BorderGradient/BorderGradient'
|
||
import { LinearGradient } from '../../components/LinearGradient/LinearGradient'
|
||
import FullscreenIntroVideo from '../../components/FullscreenIntroVideo'
|
||
import GradientButton from '../../components/GradientButton'
|
||
import MoreMenu from '../../components/MoreMenu'
|
||
import ProjectDropDown from '../../components/ProjectDropDown/ProjectDropDown'
|
||
import { projectsRef, usersRef } from '../../config/firebase'
|
||
import { isWeb } from '../../hooks/useLayoutType.js'
|
||
import Page from '../../layouts/Page'
|
||
import LandingPage from '../LandingPage'
|
||
import { navigate } from '../../navigation/NavigationService'
|
||
import { Routes } from '../../navigation/Routes'
|
||
import { useUser } from '../../providers/UserDataProvider'
|
||
import CreditAmount from '../../components/CreditAmount'
|
||
import ShareBtn from '../../components/ShareBtn/ShareBtn'
|
||
import { Palette, gutters } from '../../styles'
|
||
import { FONT_FAMILY } from '../../styles/Fonts'
|
||
import { getCreationStageStates, getStageAction } from '../../utils/projectStages'
|
||
import useNavigateToMusicDetails from '../../hooks/useNavigateToMusicDetails'
|
||
import usePlayer from '../../hooks/usePlayer'
|
||
import StageCard from './components/StageCard'
|
||
import ClubCard from './components/ClubCard'
|
||
import { BlurView } from 'expo-blur'
|
||
|
||
const isProjectEmpty = (project) => {
|
||
if (!project) {
|
||
return false
|
||
}
|
||
|
||
return !(typeof project?.title === 'string' && project.title.trim().length > 0)
|
||
}
|
||
|
||
const HOME_BACKGROUND_WIDTH = isWeb ? 1280 : 520
|
||
const HOME_BACKGROUND_HEIGHT = isWeb ? 760 : 360
|
||
const HOME_BACKGROUND_STYLE_WIDTH = HOME_BACKGROUND_WIDTH + 120
|
||
const HOME_BACKGROUND_STYLE_HEIGHT = HOME_BACKGROUND_HEIGHT + 80
|
||
|
||
const STAGE_CARD_CONTENT = [
|
||
{
|
||
key: 'songwriter',
|
||
step: 'ÉTAPE 1',
|
||
description:
|
||
'Ensemble, nous allons écrire ta chanson.\nMéthodiquement je vais te guider pour construire une oeuvre unique et authentique.',
|
||
image: cardsImg.writing,
|
||
imagePosition: 'left',
|
||
textAlign: 'left',
|
||
lockSide: 'right',
|
||
},
|
||
{
|
||
key: 'beatmaker',
|
||
step: 'ÉTAPE 2',
|
||
description:
|
||
'Je suis Malik, responsable du studio de MusicLand.\nJe vais mettre en musique tes paroles en fonction de tes goûts musicaux, ça va être top !',
|
||
image: cardsImg.studio,
|
||
imagePosition: 'right',
|
||
textAlign: 'right',
|
||
lockSide: 'left',
|
||
},
|
||
{
|
||
key: 'director',
|
||
step: 'ÉTAPE 3',
|
||
description:
|
||
'Tu vas faire une expérience extraordinaire, tu vas te filmer en train d’interpreter ta chanson en Play Back et je vais te guider pour te faciliter la tâche!',
|
||
image: cardsImg.video,
|
||
imagePosition: 'left',
|
||
textAlign: 'left',
|
||
lockSide: 'right',
|
||
},
|
||
{
|
||
key: 'publisher',
|
||
step: 'ÉTAPE 4',
|
||
description:
|
||
'Je suis Mr Benhaï, Producteur de MusicLand, et je vais te faire une proposition qui pourrait t’intéresser. On se retrouve à la sortie du studio !',
|
||
image: cardsImg.production,
|
||
imagePosition: 'right',
|
||
textAlign: 'right',
|
||
lockSide: 'left',
|
||
},
|
||
]
|
||
|
||
const ACTIVE_SUBSCRIPTION_STATUSES = new Set(['trialing', 'active', 'past_due', 'unpaid'])
|
||
|
||
const Home = ({ navigation, route }) => {
|
||
const {
|
||
userProjects = [],
|
||
selectProject,
|
||
selectedProject,
|
||
selectedProjectId,
|
||
currentUserData,
|
||
currentUID,
|
||
createNewProject,
|
||
videos,
|
||
hasActiveSubscription,
|
||
} = useUser()
|
||
const { setTooltip } = useMinuit()
|
||
const [videoUrl, setVideoUrl] = useState(null)
|
||
const navigateToMusicDetails = useNavigateToMusicDetails()
|
||
|
||
if (!currentUID) {
|
||
return <LandingPage />
|
||
}
|
||
|
||
const projects = useMemo(() => (Array.isArray(userProjects) ? userProjects : []), [userProjects])
|
||
|
||
const currentProject = useMemo(() => {
|
||
if (!projects.length) return null
|
||
if (selectedProjectId === null) {
|
||
return null
|
||
}
|
||
const activeId = selectedProject?.id || selectedProjectId
|
||
if (!activeId) {
|
||
return projects[0]
|
||
}
|
||
return projects.find((project) => project?.id === activeId) || projects[0]
|
||
}, [projects, selectedProject?.id, selectedProjectId])
|
||
|
||
const formatDate = useCallback((timestamp) => {
|
||
try {
|
||
const value = timestamp?.toDate ? timestamp.toDate() : timestamp
|
||
const date = value ? new Date(value) : null
|
||
if (!date || Number.isNaN(date.getTime())) {
|
||
return ''
|
||
}
|
||
const day = date.getDate().toString().padStart(2, '0')
|
||
const month = (date.getMonth() + 1).toString().padStart(2, '0')
|
||
return `${day}/${month}`
|
||
} catch (error) {
|
||
console.warn('Home.web: formatDate error', error)
|
||
return ''
|
||
}
|
||
}, [])
|
||
|
||
const stageStates = useMemo(() => getCreationStageStates(currentProject), [currentProject])
|
||
|
||
const stageStatesByKey = useMemo(() => {
|
||
if (!Array.isArray(stageStates)) {
|
||
return {}
|
||
}
|
||
return stageStates.reduce((acc, stage) => {
|
||
if (stage?.key) {
|
||
acc[stage.key] = stage
|
||
}
|
||
return acc
|
||
}, {})
|
||
}, [stageStates])
|
||
|
||
const [menuVisible, setMenuVisible] = useState(false)
|
||
const [menuAnchor, setMenuAnchor] = useState(null)
|
||
const [menuProject, setMenuProject] = useState(null)
|
||
const menuAnchorRef = useRef(null)
|
||
const projectDropdownRef = useRef(null)
|
||
const [hasLocalAdventureFlag, setHasLocalAdventureFlag] = useState(false)
|
||
|
||
const { play } = usePlayer()
|
||
|
||
const handleSelectProject = useCallback(
|
||
(project) => {
|
||
if (!project?.id) return
|
||
selectProject(project.id)
|
||
},
|
||
[selectProject]
|
||
)
|
||
|
||
const handleCloseMenu = useCallback(() => {
|
||
setMenuVisible(false)
|
||
setMenuAnchor(null)
|
||
setMenuProject(null)
|
||
menuAnchorRef.current = null
|
||
}, [])
|
||
|
||
const handleDeleteProject = useCallback(async () => {
|
||
if (!menuProject?.id) return
|
||
try {
|
||
await projectsRef.doc(menuProject.id).delete()
|
||
setTooltip?.({ type: 'success', text: 'Projet supprimé' })
|
||
} catch (error) {
|
||
setTooltip?.({
|
||
type: 'error',
|
||
text: error?.message || 'Suppression impossible',
|
||
})
|
||
} finally {
|
||
handleCloseMenu()
|
||
}
|
||
}, [handleCloseMenu, menuProject?.id, setTooltip])
|
||
|
||
const handleModifyProject = useCallback((project, anchor) => {
|
||
if (!project?.id) return
|
||
|
||
const prevAnchor = menuAnchorRef.current
|
||
const normalizedAnchor =
|
||
typeof anchor === 'number'
|
||
? { top: anchor }
|
||
: anchor && typeof anchor === 'object'
|
||
? anchor
|
||
: {}
|
||
|
||
setMenuProject(project)
|
||
setMenuAnchor(normalizedAnchor)
|
||
setMenuVisible((prev) => {
|
||
if (!prev) return true
|
||
const prevTop = typeof prevAnchor?.top === 'number' ? prevAnchor.top : null
|
||
const nextTop = typeof normalizedAnchor?.top === 'number' ? normalizedAnchor.top : null
|
||
return prevTop !== nextTop
|
||
})
|
||
menuAnchorRef.current = normalizedAnchor
|
||
}, [])
|
||
|
||
const handlePlayProject = useCallback(
|
||
(project) => {
|
||
if (!project?.id || !project?.songUrl) return
|
||
const cover = project.coverUrl || project.coverUri || project.cover
|
||
play({
|
||
id: project.id,
|
||
url: project.songUrl,
|
||
title: project.title || 'Sans titre',
|
||
artist: 'MusicLand',
|
||
artwork: cover,
|
||
coverUrl: typeof cover === 'string' ? cover : cover?.uri,
|
||
metadata: {
|
||
projectId: project.id,
|
||
},
|
||
})
|
||
},
|
||
[play]
|
||
)
|
||
|
||
const moreMenuItems = useMemo(() => {
|
||
if (!menuProject?.id) return []
|
||
|
||
const items = []
|
||
const hasSongUrl = typeof menuProject?.songUrl === 'string' && menuProject.songUrl.length > 0
|
||
|
||
if (hasSongUrl) {
|
||
items.push({
|
||
label: 'Télécharger',
|
||
onPress: () => {
|
||
projectDropdownRef.current?.close?.()
|
||
navigate(Routes.SongDownload, {
|
||
project: menuProject,
|
||
skipAdventureGate: true,
|
||
promptPurchaseConfirm: true,
|
||
})
|
||
},
|
||
})
|
||
}
|
||
|
||
items.push({
|
||
label: 'Supprimer',
|
||
onPress: () =>
|
||
Alert(
|
||
'Confirmer la suppression',
|
||
'Cette action supprimera définitivement ce projet.',
|
||
[
|
||
{ text: 'Annuler', style: 'cancel' },
|
||
{
|
||
text: 'Supprimer',
|
||
style: 'destructive',
|
||
onPress: () => {
|
||
handleDeleteProject()
|
||
},
|
||
},
|
||
],
|
||
{ cancelable: true }
|
||
),
|
||
})
|
||
|
||
return items
|
||
}, [handleDeleteProject, menuProject, navigateToMusicDetails])
|
||
|
||
useEffect(() => {
|
||
if (!route?.params?.showLyricsCongrats) {
|
||
return
|
||
}
|
||
const beatmakerStage = currentProject ? getStageAction('beatmaker', currentProject) : null
|
||
Alert(
|
||
'Céline',
|
||
"Bravo, tu as créé les paroles de ta chanson! Prochaine étape, le studio! Malik t'attends pour mettre ça en musique! A bientôt! and...Feel The Good Vibe!",
|
||
[
|
||
{
|
||
text: "Retour à l'accueil",
|
||
style: 'cancel',
|
||
},
|
||
{
|
||
text: 'Continuer vers le Studio',
|
||
onPress: () => {
|
||
if (!currentProject?.id) {
|
||
return
|
||
}
|
||
if (selectedProject?.id !== currentProject.id) {
|
||
selectProject(currentProject.id)
|
||
}
|
||
const targetRoute = beatmakerStage?.route || Routes.Compose
|
||
navigate(targetRoute, beatmakerStage?.params)
|
||
},
|
||
},
|
||
]
|
||
)
|
||
navigation?.setParams?.({ showLyricsCongrats: false })
|
||
}, [
|
||
currentProject,
|
||
navigation,
|
||
selectProject,
|
||
selectedProject?.id,
|
||
route?.params?.showLyricsCongrats,
|
||
])
|
||
|
||
const hasActiveProject = !!currentProject
|
||
|
||
const ensureProjectSelected = useCallback(() => {
|
||
if (!currentProject?.id) {
|
||
return
|
||
}
|
||
if (selectedProject?.id !== currentProject.id) {
|
||
selectProject(currentProject.id)
|
||
}
|
||
}, [currentProject?.id, selectProject, selectedProject?.id])
|
||
|
||
const songwriterAction = useMemo(() => getStageAction('songwriter', null), [])
|
||
|
||
const handleStartNew = useCallback(async () => {
|
||
const targetRoute = songwriterAction?.route || Routes.WritingLyrics
|
||
const targetParams = songwriterAction?.params
|
||
|
||
try {
|
||
const emptyProject = projects.find((project) => isProjectEmpty(project)) || null
|
||
|
||
if (emptyProject?.id) {
|
||
selectProject(emptyProject.id)
|
||
navigate(targetRoute, targetParams)
|
||
return
|
||
}
|
||
|
||
const newProjectId = await createNewProject({ hasLyrics: false })
|
||
if (!newProjectId) {
|
||
return
|
||
}
|
||
navigate(targetRoute, targetParams)
|
||
} catch (error) {
|
||
console.warn('Home: unable to start new project', error)
|
||
setTooltip?.({
|
||
type: 'error',
|
||
text: 'Impossible de démarrer un nouveau projet',
|
||
})
|
||
}
|
||
}, [createNewProject, projects, selectProject, setTooltip, songwriterAction])
|
||
|
||
useEffect(() => {
|
||
if (currentUserData?.adventureStarted) {
|
||
setHasLocalAdventureFlag(true)
|
||
}
|
||
}, [currentUserData?.adventureStarted])
|
||
|
||
const persistAdventureStarted = useCallback(async () => {
|
||
if (!currentUID) {
|
||
return
|
||
}
|
||
await usersRef.doc(currentUID).set({ adventureStarted: true }, { merge: true })
|
||
}, [currentUID])
|
||
|
||
const markAdventureStarted = useCallback(() => {
|
||
if (hasLocalAdventureFlag) {
|
||
return
|
||
}
|
||
setHasLocalAdventureFlag(true)
|
||
persistAdventureStarted().catch((error) => {
|
||
console.warn('Home: adventureStarted update failed', error)
|
||
setHasLocalAdventureFlag(false)
|
||
setTooltip?.({
|
||
type: 'error',
|
||
text: 'Impossible de mettre à jour votre profil',
|
||
})
|
||
})
|
||
}, [hasLocalAdventureFlag, persistAdventureStarted, setTooltip])
|
||
|
||
const handleStartVisit = useCallback(() => {
|
||
setVideoUrl(isWeb ? videos?.landingWeb : videos?.landing)
|
||
}, [])
|
||
|
||
const handleReturnToLanding = useCallback(() => {
|
||
navigate(Routes.LandingPage)
|
||
}, [])
|
||
|
||
const handleIntroVideoClose = useCallback(() => {
|
||
setVideoUrl(null)
|
||
markAdventureStarted()
|
||
}, [markAdventureStarted])
|
||
|
||
const adventureStarted = hasLocalAdventureFlag || !!currentUserData?.adventureStarted
|
||
|
||
const homeBackgroundImage = background.bgTrans
|
||
const landingBackgroundImage = homeBackgroundImage
|
||
|
||
const stageCards = useMemo(
|
||
() =>
|
||
STAGE_CARD_CONTENT.map((card) => ({
|
||
...card,
|
||
isLocked: stageStatesByKey[card.key]?.isLocked ?? true,
|
||
})),
|
||
[stageStatesByKey]
|
||
)
|
||
|
||
const handleStagePress = useCallback(
|
||
(stageKey, isLocked) => {
|
||
if (isLocked) {
|
||
return
|
||
}
|
||
|
||
if (!hasActiveProject || !currentProject) {
|
||
if (stageKey === 'songwriter') {
|
||
handleStartNew()
|
||
}
|
||
return
|
||
}
|
||
|
||
ensureProjectSelected()
|
||
const action = getStageAction(stageKey, currentProject)
|
||
if (!action?.route) {
|
||
return
|
||
}
|
||
console.log("action route is ", action.route)
|
||
navigate(action.route, action.params)
|
||
},
|
||
[currentProject, ensureProjectSelected, hasActiveProject, handleStartNew]
|
||
)
|
||
|
||
const handleClubPress = useCallback(() => {
|
||
navigate(Routes.HitParade)
|
||
}, [])
|
||
|
||
const handleOpenCoinModal = useCallback(() => {
|
||
navigate(Routes.Payments)
|
||
}, [])
|
||
|
||
const stageCardContainerStyle = isWeb ? styles.cardsGrid : styles.cardsStack
|
||
const stageCardItemStyle = isWeb ? styles.webStageCard : styles.mobileStageCard
|
||
|
||
const stageCardsList = (
|
||
<View style={stageCardContainerStyle}>
|
||
{stageCards.map((card) => (
|
||
<StageCard
|
||
key={card.key}
|
||
step={card.step}
|
||
description={card.description}
|
||
image={card.image}
|
||
imagePosition={card.imagePosition}
|
||
textAlign={card.textAlign}
|
||
isLocked={card.isLocked}
|
||
lockSide={card.lockSide}
|
||
onPress={() => handleStagePress(card.key, card.isLocked)}
|
||
containerStyle={stageCardItemStyle}
|
||
variant={isWeb ? 'web' : 'mobile'}
|
||
/>
|
||
))}
|
||
</View>
|
||
)
|
||
|
||
const clubCard = (
|
||
<ClubCard onPress={handleClubPress} containerStyle={stageCardItemStyle} variant="stage" />
|
||
)
|
||
|
||
const coinBalance = useMemo(() => {
|
||
const value = currentUserData?.coins
|
||
if (typeof value === 'number' && Number.isFinite(value)) {
|
||
return value
|
||
}
|
||
if (typeof value === 'string') {
|
||
const parsed = Number(value)
|
||
if (Number.isFinite(parsed)) {
|
||
return parsed
|
||
}
|
||
}
|
||
return 0
|
||
}, [currentUserData?.coins])
|
||
|
||
const mobileInfoRow = !isWeb ? (
|
||
<View style={styles.mobileInfoRow}>
|
||
<Pressable
|
||
onPress={handleOpenCoinModal}
|
||
accessibilityRole="button"
|
||
style={styles.mobileCoinButton}
|
||
>
|
||
<CreditAmount
|
||
value={coinBalance}
|
||
style={styles.mobileCoinAmount}
|
||
textStyle={styles.mobileCoinText}
|
||
iconSize={20}
|
||
iconPosition="left"
|
||
/>
|
||
</Pressable>
|
||
<ShareBtn style={styles.mobileShareButton} label="Partager" />
|
||
</View>
|
||
) : null
|
||
|
||
const returnHomeButton = (
|
||
<Pressable onPress={handleReturnToLanding}>
|
||
<BlurView
|
||
tint="dark"
|
||
intensity={30}
|
||
style={{
|
||
paddingHorizontal: 10,
|
||
paddingVertical: 8,
|
||
borderRadius: 12,
|
||
overflow: 'hidden',
|
||
flexDirection: 'row',
|
||
alignItems: 'center',
|
||
justifyContent: 'center',
|
||
}}
|
||
>
|
||
<Text
|
||
style={{
|
||
fontSize: 14,
|
||
color: Palette.white,
|
||
fontFamily: FONT_FAMILY.InterRegular,
|
||
marginRight: 5,
|
||
}}
|
||
>
|
||
{"Retour à l'accueil"}
|
||
</Text>
|
||
</BlurView>
|
||
</Pressable>
|
||
)
|
||
|
||
const topBar = (
|
||
<View style={styles.topBar}>
|
||
{/* {returnHomeButton} */}
|
||
{mobileInfoRow}
|
||
{!isWeb ? returnHomeButton : null}
|
||
<ProjectDropDown
|
||
ref={projectDropdownRef}
|
||
style={styles.projectDropDown}
|
||
projects={projects}
|
||
selectedProject={currentProject}
|
||
allowEmptySelection
|
||
onSelectProject={handleSelectProject}
|
||
onModifyProject={handleModifyProject}
|
||
onPlayProject={handlePlayProject}
|
||
onCreateProject={handleStartNew}
|
||
formatDate={formatDate}
|
||
/>
|
||
</View>
|
||
)
|
||
|
||
const journeyContent = (
|
||
<>
|
||
<MoreMenu
|
||
visible={menuVisible}
|
||
top={menuAnchor?.top ?? 0}
|
||
position={menuAnchor}
|
||
onClose={handleCloseMenu}
|
||
inPlaylist={false}
|
||
projectId={menuProject?.id || null}
|
||
extraItems={moreMenuItems}
|
||
/>
|
||
|
||
<View style={styles.subtitleWrapper}>
|
||
{isWeb ? (
|
||
<LinearGradient
|
||
colors={['#F94697', '#7023F7']}
|
||
start={{ x: 0, y: 0.5 }}
|
||
end={{ x: 1, y: 0.5 }}
|
||
style={[styles.subtitleGradient, styles.subtitleGradientWeb]}
|
||
>
|
||
<View style={[styles.subtitleInner, styles.subtitleInnerWeb]}>
|
||
<Text style={styles.subtitle}>5 espaces à découvrir</Text>
|
||
</View>
|
||
</LinearGradient>
|
||
) : (
|
||
<BorderGradient
|
||
style={[styles.subtitleGradient, styles.subtitleBorder]}
|
||
gradientProps={{
|
||
colors: ['#F94697', '#7023F7'],
|
||
start: { x: 0, y: 0.5 },
|
||
end: { x: 1, y: 0.5 },
|
||
locations: [0, 1],
|
||
}}
|
||
>
|
||
<View style={styles.subtitleInner}>
|
||
<Text style={styles.subtitle}>5 espaces à découvrir</Text>
|
||
</View>
|
||
</BorderGradient>
|
||
)}
|
||
</View>
|
||
|
||
{stageCardsList}
|
||
|
||
{isWeb ? (
|
||
<View style={styles.webClubWrapper}>{clubCard}</View>
|
||
) : (
|
||
<View style={styles.mobileClubWrapper}>{clubCard}</View>
|
||
)}
|
||
</>
|
||
)
|
||
|
||
return (
|
||
<>
|
||
{/* {adventureStarted ? ( */}
|
||
<View style={styles.root}>
|
||
<Page
|
||
shareBtn
|
||
headerType="NONE"
|
||
scrollEnabled={false}
|
||
containerStyle={styles.page}
|
||
contentContainerStyle={styles.pageContent}
|
||
maxWidth={1600}
|
||
width="100%"
|
||
backgroundColor="#303438"
|
||
showReturnHome={isWeb}
|
||
onReturnHome={handleReturnToLanding}
|
||
>
|
||
<View style={styles.inner}>
|
||
<ExpoImage source={homeBackgroundImage} contentFit="cover" style={styles.centerImage} />
|
||
{isWeb ? (
|
||
<>
|
||
{/* <View style={styles.webReturnButtonWrapper}>
|
||
{returnHomeButton}
|
||
</View> */}
|
||
<View style={styles.contentOverlay}>
|
||
{topBar}
|
||
{journeyContent}
|
||
</View>
|
||
</>
|
||
) : (
|
||
<View style={[styles.contentOverlay, styles.mobileContent]}>
|
||
{topBar}
|
||
<ScrollView
|
||
style={styles.mobileScrollView}
|
||
contentContainerStyle={styles.mobileScrollContent}
|
||
showsVerticalScrollIndicator={false}
|
||
>
|
||
{journeyContent}
|
||
</ScrollView>
|
||
</View>
|
||
)}
|
||
</View>
|
||
</Page>
|
||
</View>
|
||
</>
|
||
)
|
||
}
|
||
|
||
export default Home
|
||
|
||
const styles = StyleSheet.create({
|
||
root: {
|
||
flex: 1,
|
||
backgroundColor: '#303438',
|
||
},
|
||
page: {
|
||
backgroundColor: 'transparent',
|
||
padding: 0,
|
||
paddingTop: 0,
|
||
paddingBottom: 0,
|
||
width: '100%',
|
||
},
|
||
pageContent: {
|
||
flexGrow: 1,
|
||
},
|
||
inner: {
|
||
flex: 1,
|
||
width: '100%',
|
||
alignSelf: 'stretch',
|
||
alignItems: 'stretch',
|
||
justifyContent: 'flex-start',
|
||
position: 'relative',
|
||
},
|
||
centerImage: {
|
||
width: HOME_BACKGROUND_STYLE_WIDTH,
|
||
height: HOME_BACKGROUND_STYLE_HEIGHT,
|
||
borderRadius: 22,
|
||
overflow: 'hidden',
|
||
position: 'absolute',
|
||
top: isWeb ? '45%' : '50%',
|
||
left: '50%',
|
||
transform: [
|
||
{ translateX: -HOME_BACKGROUND_STYLE_WIDTH / 2 },
|
||
{ translateY: -HOME_BACKGROUND_STYLE_HEIGHT / 2 },
|
||
],
|
||
pointerEvents: 'none',
|
||
zIndex: 0,
|
||
},
|
||
contentOverlay: {
|
||
position: 'relative',
|
||
width: '100%',
|
||
flexGrow: 1,
|
||
},
|
||
topBar: {
|
||
width: '100%',
|
||
flexDirection: isWeb ? 'row' : 'column',
|
||
flexWrap: isWeb ? 'wrap' : 'nowrap',
|
||
alignItems: isWeb ? 'center' : 'stretch',
|
||
justifyContent: isWeb ? 'center' : 'flex-start',
|
||
gap: 12,
|
||
marginTop: isWeb ? 12 : 0,
|
||
marginBottom: isWeb ? 4 : 16,
|
||
paddingHorizontal: isWeb ? 0 : gutters,
|
||
zIndex: 10,
|
||
},
|
||
returnButton: {
|
||
width: isWeb ? 220 : '100%',
|
||
alignSelf: isWeb ? 'flex-start' : 'stretch',
|
||
},
|
||
webReturnButtonWrapper: {
|
||
alignSelf: 'flex-start',
|
||
marginTop: gutters * 2.5,
|
||
marginBottom: 8,
|
||
},
|
||
projectDropDown: {
|
||
width: isWeb ? 420 : '100%',
|
||
maxWidth: 420,
|
||
flexGrow: 1,
|
||
},
|
||
mobileInfoRow: {
|
||
width: '100%',
|
||
flexDirection: 'row',
|
||
alignItems: 'center',
|
||
justifyContent: 'space-between',
|
||
gap: 12,
|
||
},
|
||
mobileCoinButton: {
|
||
flexDirection: 'row',
|
||
alignItems: 'center',
|
||
gap: 8,
|
||
paddingHorizontal: 14,
|
||
paddingVertical: 8,
|
||
borderRadius: 20,
|
||
backgroundColor: 'rgba(12, 14, 18, 0.72)',
|
||
borderWidth: 1,
|
||
borderColor: 'rgba(255, 255, 255, 0.1)',
|
||
flexShrink: 1,
|
||
},
|
||
mobileCoinAmount: {
|
||
flexDirection: 'row',
|
||
alignItems: 'center',
|
||
gap: 6,
|
||
},
|
||
mobileCoinText: {
|
||
fontSize: 18,
|
||
color: Palette.white,
|
||
fontFamily: FONT_FAMILY.InterSemiBold,
|
||
},
|
||
mobileShareButton: {
|
||
flexShrink: 0,
|
||
},
|
||
mobileContent: {
|
||
flex: 1,
|
||
width: '100%',
|
||
},
|
||
mobileScrollView: {
|
||
flex: 1,
|
||
width: '100%',
|
||
},
|
||
mobileScrollContent: {
|
||
paddingHorizontal: gutters,
|
||
paddingTop: 0,
|
||
paddingBottom: gutters * 6,
|
||
},
|
||
subtitleWrapper: {
|
||
width: '100%',
|
||
marginBottom: isWeb ? 12 : 16,
|
||
marginTop: isWeb ? 8 : 15,
|
||
alignItems: 'center',
|
||
},
|
||
subtitleGradient: {
|
||
width: '100%',
|
||
maxWidth: 420,
|
||
borderRadius: 999,
|
||
alignSelf: 'center',
|
||
},
|
||
subtitleBorder: {
|
||
borderWidth: 1,
|
||
},
|
||
subtitleGradientWeb: {
|
||
padding: 2,
|
||
},
|
||
subtitleInner: {
|
||
width: '100%',
|
||
paddingHorizontal: 24,
|
||
paddingVertical: 10,
|
||
alignItems: 'center',
|
||
justifyContent: 'center',
|
||
borderRadius: 999,
|
||
backgroundColor: Palette.glass,
|
||
},
|
||
subtitleInnerWeb: {
|
||
paddingVertical: 10,
|
||
backgroundColor: Palette.lightPurple,
|
||
},
|
||
subtitle: {
|
||
fontFamily: FONT_FAMILY.InterMedium,
|
||
fontSize: 18,
|
||
color: Palette.white,
|
||
textAlign: 'center',
|
||
textTransform: 'uppercase',
|
||
letterSpacing: 1,
|
||
},
|
||
cardsGrid: {
|
||
width: '100%',
|
||
flexDirection: 'row',
|
||
flexWrap: 'wrap',
|
||
justifyContent: 'space-between',
|
||
rowGap: isWeb ? 16 : 20,
|
||
},
|
||
cardsStack: {
|
||
width: '100%',
|
||
flexDirection: 'column',
|
||
alignItems: 'stretch',
|
||
},
|
||
webClubWrapper: {
|
||
width: '100%',
|
||
alignItems: 'center',
|
||
marginTop: 16,
|
||
},
|
||
mobileClubWrapper: {
|
||
width: '100%',
|
||
marginTop: 20,
|
||
},
|
||
webStageCard: {
|
||
width: '48%',
|
||
},
|
||
mobileStageCard: {
|
||
width: '100%',
|
||
marginBottom: 20,
|
||
},
|
||
})
|