830 lines
22 KiB
JavaScript
830 lines
22 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 CoinPackModal from "../../components/modal/CoinPackModal";
|
||
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 { openCoinPackModal } from "../../utils/coinPackModal";
|
||
import useNavigateToMusicDetails from "../../hooks/useNavigateToMusicDetails";
|
||
import StageCard from "./components/StageCard";
|
||
import ClubCard from "./components/ClubCard";
|
||
|
||
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 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 moreMenuItems = useMemo(() => {
|
||
if (!menuProject?.id) return [];
|
||
|
||
const items = [];
|
||
const hasSongUrl =
|
||
typeof menuProject?.songUrl === "string" &&
|
||
menuProject.songUrl.length > 0;
|
||
|
||
if (hasSongUrl) {
|
||
items.push({
|
||
label: "Jouer la musique",
|
||
onPress: () => {
|
||
projectDropdownRef.current?.close?.();
|
||
navigateToMusicDetails({
|
||
projectId: menuProject.id,
|
||
songUrl: menuProject.songUrl,
|
||
project: menuProject,
|
||
});
|
||
},
|
||
});
|
||
}
|
||
|
||
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 ! Vous venez de terminer de créer les paroles de votre musique ! Maintenant vous pouvez passer à la prochaine étape : le Studio pour donner vie à votre chanson !",
|
||
[
|
||
{
|
||
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 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 [isCoinModalVisible, setCoinModalVisible] = useState(false);
|
||
|
||
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;
|
||
}
|
||
navigate(action.route, action.params);
|
||
},
|
||
[currentProject, ensureProjectSelected, hasActiveProject, handleStartNew],
|
||
);
|
||
|
||
const handleClubPress = useCallback(() => {
|
||
navigate(Routes.Payments);
|
||
}, []);
|
||
|
||
const handleOpenCoinModal = useCallback(() => {
|
||
if (isWeb) {
|
||
openCoinPackModal();
|
||
return;
|
||
}
|
||
setCoinModalVisible(true);
|
||
}, []);
|
||
|
||
const handleCloseCoinModal = useCallback(() => {
|
||
setCoinModalVisible(false);
|
||
}, []);
|
||
|
||
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 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 topBar = (
|
||
<View style={styles.topBar}>
|
||
{mobileInfoRow}
|
||
<ProjectDropDown
|
||
ref={projectDropdownRef}
|
||
style={styles.projectDropDown}
|
||
projects={projects}
|
||
selectedProject={currentProject}
|
||
allowEmptySelection
|
||
onSelectProject={handleSelectProject}
|
||
onModifyProject={handleModifyProject}
|
||
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}
|
||
|
||
<ClubCard
|
||
image={icons.clubIcon}
|
||
onPress={handleClubPress}
|
||
hasActiveSubscription={hasActiveSubscription}
|
||
/>
|
||
</>
|
||
);
|
||
|
||
return (
|
||
<>
|
||
{adventureStarted ? (
|
||
<View style={styles.root}>
|
||
<Page
|
||
shareBtn
|
||
headerType="NONE"
|
||
scrollEnabled={false}
|
||
containerStyle={styles.page}
|
||
contentContainerStyle={styles.pageContent}
|
||
maxWidth={1600}
|
||
width="100%"
|
||
backgroundColor="#303438"
|
||
>
|
||
<View style={styles.inner}>
|
||
<ExpoImage
|
||
source={homeBackgroundImage}
|
||
contentFit="cover"
|
||
style={styles.centerImage}
|
||
/>
|
||
{isWeb ? (
|
||
<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>
|
||
) : (
|
||
<>
|
||
<Page
|
||
shareBtn
|
||
title="Landing Page"
|
||
backgroundImg={landingBackgroundImage}
|
||
contentContainerStyle={styles.pageContent}
|
||
backgroundColor="#303438"
|
||
>
|
||
<View
|
||
style={{
|
||
flex: 1,
|
||
justifyContent: "center",
|
||
alignItems: "center",
|
||
}}
|
||
>
|
||
<GradientButton
|
||
title="Commencer l'aventure MusicLand"
|
||
onPress={handleStartVisit}
|
||
/>
|
||
</View>
|
||
</Page>
|
||
<FullscreenIntroVideo
|
||
url={videoUrl}
|
||
visible={!!videoUrl}
|
||
onClose={handleIntroVideoClose}
|
||
/>
|
||
</>
|
||
)}
|
||
{!isWeb ? (
|
||
<CoinPackModal
|
||
visible={isCoinModalVisible}
|
||
onClose={handleCloseCoinModal}
|
||
/>
|
||
) : null}
|
||
</>
|
||
);
|
||
};
|
||
|
||
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,
|
||
},
|
||
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",
|
||
},
|
||
webStageCard: {
|
||
width: "48%",
|
||
},
|
||
mobileStageCard: {
|
||
width: "100%",
|
||
marginBottom: 20,
|
||
},
|
||
});
|