feat: new home

This commit is contained in:
2025-12-04 10:33:00 +01:00
parent 48fd1d8816
commit 6f81419fb8
2 changed files with 158 additions and 139 deletions
+104 -66
View File
@@ -8,6 +8,7 @@ import { subBadges } from "../assets";
import BaseHeader from "../components/BaseHeader"; import BaseHeader from "../components/BaseHeader";
import CreditAmount from "../components/CreditAmount"; import CreditAmount from "../components/CreditAmount";
import ConnectBtn from "../components/ConnectBtn.js"; import ConnectBtn from "../components/ConnectBtn.js";
import GradientButton from "../components/GradientButton";
import CoinPackModal from "../components/modal/CoinPackModal"; import CoinPackModal from "../components/modal/CoinPackModal";
import NavigateHeader from "../components/NavigateHeader"; import NavigateHeader from "../components/NavigateHeader";
import ShareBtn from "../components/ShareBtn/ShareBtn"; import ShareBtn from "../components/ShareBtn/ShareBtn";
@@ -48,6 +49,8 @@ export default ({
blurIntensity = 0, blurIntensity = 0,
backgroundColor = "#000", backgroundColor = "#000",
showCoin = true, showCoin = true,
showReturnHome = false,
onReturnHome = null,
}) => { }) => {
const { currentUID = null, currentUserData = null } = useUserData?.() || {}; const { currentUID = null, currentUserData = null } = useUserData?.() || {};
@@ -66,6 +69,8 @@ export default ({
}, [currentUserData?.coins]); }, [currentUserData?.coins]);
const showCoinBadge = isWeb && !!currentUID && showCoin; const showCoinBadge = isWeb && !!currentUID && showCoin;
const showReturnHomeButton = isWeb && showReturnHome;
const coinBadgeContainerVisible = showCoinBadge || showReturnHomeButton;
const [isCoinModalVisible, setCoinModalVisible] = React.useState(false); const [isCoinModalVisible, setCoinModalVisible] = React.useState(false);
const subscriptionBadgeSource = React.useMemo(() => { const subscriptionBadgeSource = React.useMemo(() => {
@@ -80,14 +85,14 @@ export default ({
const nestedSubscription = const nestedSubscription =
currentUserData?.subscription && currentUserData?.subscription &&
typeof currentUserData.subscription === "object" typeof currentUserData.subscription === "object"
? currentUserData.subscription ? currentUserData.subscription
: null; : null;
const stripeMetadata = const stripeMetadata =
currentUserData?.stripeSubscription && currentUserData?.stripeSubscription &&
typeof currentUserData.stripeSubscription === "object" && typeof currentUserData.stripeSubscription === "object" &&
typeof currentUserData.stripeSubscription.metadata === "object" typeof currentUserData.stripeSubscription.metadata === "object"
? currentUserData.stripeSubscription.metadata ? currentUserData.stripeSubscription.metadata
: null; : null;
@@ -100,18 +105,18 @@ export default ({
const nestedCandidates = nestedSubscription const nestedCandidates = nestedSubscription
? ["level", "plan", "pack", "type"].map((key) => ? ["level", "plan", "pack", "type"].map((key) =>
pickLevel(nestedSubscription?.[key]), pickLevel(nestedSubscription?.[key])
) )
: []; : [];
const metadataCandidates = stripeMetadata const metadataCandidates = stripeMetadata
? [ ? [
"level", "level",
"subscriptionLevel", "subscriptionLevel",
"subscription_level", "subscription_level",
"pack", "pack",
"Pack", "Pack",
].map((key) => pickLevel(stripeMetadata?.[key])) ].map((key) => pickLevel(stripeMetadata?.[key]))
: []; : [];
const resolvedLevel = [ const resolvedLevel = [
@@ -132,6 +137,14 @@ export default ({
setCoinModalVisible(false); setCoinModalVisible(false);
}, []); }, []);
const handleReturnHomePress = React.useCallback(() => {
if (typeof onReturnHome === "function") {
onReturnHome();
return;
}
navigate(Routes.LandingPage);
}, [onReturnHome]);
React.useEffect(() => { React.useEffect(() => {
if (!showCoinBadge && isCoinModalVisible) { if (!showCoinBadge && isCoinModalVisible) {
setCoinModalVisible(false); setCoinModalVisible(false);
@@ -170,15 +183,15 @@ export default ({
const resolvedContainerStyle = React.useMemo( const resolvedContainerStyle = React.useMemo(
() => StyleSheet.flatten(containerStyle) || {}, () => StyleSheet.flatten(containerStyle) || {},
[containerStyle], [containerStyle]
); );
const resolvedHeaderStyle = React.useMemo( const resolvedHeaderStyle = React.useMemo(
() => StyleSheet.flatten(headerStyle) || {}, () => StyleSheet.flatten(headerStyle) || {},
[headerStyle], [headerStyle]
); );
const resolvedContentContainerStyle = React.useMemo( const resolvedContentContainerStyle = React.useMemo(
() => StyleSheet.flatten(contentContainerStyle) || {}, () => StyleSheet.flatten(contentContainerStyle) || {},
[contentContainerStyle], [contentContainerStyle]
); );
return ( return (
@@ -207,54 +220,45 @@ export default ({
}} }}
/> />
) : null} ) : null}
{showCoinBadge ? ( {coinBadgeContainerVisible ? (
<View <View style={styles.coinBadgeContainer}>
style={{ {showCoinBadge ? (
position: "absolute", <View style={styles.coinRow}>
top: gutters, <Pressable
left: gutters, onPress={handleCoinPress}
flexDirection: "row", accessibilityRole="button"
alignItems: "center", style={styles.coinButton}
gap: 6, >
zIndex: 20, <CreditAmount
}} value={coinBalance}
> iconSize={22}
<Pressable textStyle={{
onPress={handleCoinPress} color: "#ffffff",
accessibilityRole="button" fontFamily: FONT_FAMILY.InterSemiBold,
style={{ fontSize: 14,
paddingHorizontal: 14, }}
paddingVertical: 8, />
borderRadius: 20, </Pressable>
backgroundColor: "rgba(12, 14, 18, 0.72)", {subscriptionBadgeSource ? (
borderWidth: 1, <Pressable
borderColor: "rgba(255, 255, 255, 0.1)", onPress={() => navigate(Routes.ManageSubscription)}
flexDirection: "row", accessibilityRole="button"
alignItems: "center", style={styles.subscriptionBadge}
gap: 8, >
}} <Image
> source={subscriptionBadgeSource}
<CreditAmount style={{ height: 50, width: 50, resizeMode: "contain" }}
value={coinBalance} />
iconSize={22} </Pressable>
textStyle={{ ) : null}
color: "#ffffff", </View>
fontFamily: FONT_FAMILY.InterSemiBold, ) : null}
fontSize: 14, {showReturnHomeButton ? (
}} <GradientButton
title="retour à l'accueil"
onPress={handleReturnHomePress}
containerStyle={styles.returnHomeButton}
/> />
</Pressable>
{subscriptionBadgeSource ? (
<Pressable
onPress={() => navigate(Routes.ManageSubscription)}
accessibilityRole="button"
style={{ paddingVertical: 4 }}
>
<Image
source={subscriptionBadgeSource}
style={{ height: 50, width: 50, resizeMode: "contain" }}
/>
</Pressable>
) : null} ) : null}
</View> </View>
) : null} ) : null}
@@ -293,11 +297,11 @@ export default ({
style={{ flex: 1, ...resolvedContentContainerStyle }} style={{ flex: 1, ...resolvedContentContainerStyle }}
{...(scrollEnabled {...(scrollEnabled
? { ? {
scrollEventThrottle: 80, scrollEventThrottle: 80,
showsVerticalScrollIndicator: false, showsVerticalScrollIndicator: false,
contentContainerStyle: { paddingBottom: responsiveHeight(55) }, contentContainerStyle: { paddingBottom: responsiveHeight(55) },
keyboardDismissMode: "on-drag", keyboardDismissMode: "on-drag",
} }
: {})} : {})}
> >
{children} {children}
@@ -328,3 +332,37 @@ export default ({
</View> </View>
); );
}; };
const styles = StyleSheet.create({
coinBadgeContainer: {
position: "absolute",
top: gutters,
left: gutters,
alignItems: "flex-start",
gap: 10,
zIndex: 20,
},
coinRow: {
flexDirection: "row",
alignItems: "center",
gap: 6,
},
coinButton: {
paddingHorizontal: 14,
paddingVertical: 8,
borderRadius: 20,
backgroundColor: "rgba(12, 14, 18, 0.72)",
borderWidth: 1,
borderColor: "rgba(255, 255, 255, 0.1)",
flexDirection: "row",
alignItems: "center",
gap: 8,
},
subscriptionBadge: {
paddingVertical: 4,
},
returnHomeButton: {
width: 220,
alignSelf: "flex-start",
},
});
+54 -73
View File
@@ -124,7 +124,7 @@ const Home = ({ navigation, route }) => {
const projects = useMemo( const projects = useMemo(
() => (Array.isArray(userProjects) ? userProjects : []), () => (Array.isArray(userProjects) ? userProjects : []),
[userProjects], [userProjects]
); );
const currentProject = useMemo(() => { const currentProject = useMemo(() => {
@@ -157,7 +157,7 @@ const Home = ({ navigation, route }) => {
const stageStates = useMemo( const stageStates = useMemo(
() => getCreationStageStates(currentProject), () => getCreationStageStates(currentProject),
[currentProject], [currentProject]
); );
const stageStatesByKey = useMemo(() => { const stageStatesByKey = useMemo(() => {
@@ -184,7 +184,7 @@ const Home = ({ navigation, route }) => {
if (!project?.id) return; if (!project?.id) return;
selectProject(project.id); selectProject(project.id);
}, },
[selectProject], [selectProject]
); );
const handleCloseMenu = useCallback(() => { const handleCloseMenu = useCallback(() => {
@@ -271,7 +271,7 @@ const Home = ({ navigation, route }) => {
}, },
}, },
], ],
{ cancelable: true }, { cancelable: true }
), ),
}); });
@@ -306,7 +306,7 @@ const Home = ({ navigation, route }) => {
navigate(targetRoute, beatmakerStage?.params); navigate(targetRoute, beatmakerStage?.params);
}, },
}, },
], ]
); );
navigation?.setParams?.({ showLyricsCongrats: false }); navigation?.setParams?.({ showLyricsCongrats: false });
}, [ }, [
@@ -330,7 +330,7 @@ const Home = ({ navigation, route }) => {
const songwriterAction = useMemo( const songwriterAction = useMemo(
() => getStageAction("songwriter", null), () => getStageAction("songwriter", null),
[], []
); );
const handleStartNew = useCallback(async () => { const handleStartNew = useCallback(async () => {
@@ -395,6 +395,10 @@ const Home = ({ navigation, route }) => {
setVideoUrl(isWeb ? videos?.landingWeb : videos?.landing); setVideoUrl(isWeb ? videos?.landingWeb : videos?.landing);
}, []); }, []);
const handleReturnToLanding = useCallback(() => {
navigate(Routes.LandingPage);
}, []);
const handleIntroVideoClose = useCallback(() => { const handleIntroVideoClose = useCallback(() => {
setVideoUrl(null); setVideoUrl(null);
markAdventureStarted(); markAdventureStarted();
@@ -412,7 +416,7 @@ const Home = ({ navigation, route }) => {
...card, ...card,
isLocked: stageStatesByKey[card.key]?.isLocked ?? true, isLocked: stageStatesByKey[card.key]?.isLocked ?? true,
})), })),
[stageStatesByKey], [stageStatesByKey]
); );
const [isCoinModalVisible, setCoinModalVisible] = useState(false); const [isCoinModalVisible, setCoinModalVisible] = useState(false);
@@ -437,7 +441,7 @@ const Home = ({ navigation, route }) => {
} }
navigate(action.route, action.params); navigate(action.route, action.params);
}, },
[currentProject, ensureProjectSelected, hasActiveProject, handleStartNew], [currentProject, ensureProjectSelected, hasActiveProject, handleStartNew]
); );
const handleClubPress = useCallback(() => { const handleClubPress = useCallback(() => {
@@ -456,10 +460,6 @@ const Home = ({ navigation, route }) => {
setCoinModalVisible(false); setCoinModalVisible(false);
}, []); }, []);
const handleNavigateToLanding = useCallback(() => {
navigate(Routes.LandingPage);
}, []);
const stageCardContainerStyle = isWeb ? styles.cardsGrid : styles.cardsStack; const stageCardContainerStyle = isWeb ? styles.cardsGrid : styles.cardsStack;
const stageCardItemStyle = isWeb const stageCardItemStyle = isWeb
? styles.webStageCard ? styles.webStageCard
@@ -518,34 +518,30 @@ const Home = ({ navigation, route }) => {
</View> </View>
) : null; ) : null;
const returnHomeButton = (
<GradientButton
title="retour à l'accueil"
onPress={handleReturnToLanding}
containerStyle={styles.returnButton}
/>
);
const topBar = ( const topBar = (
<View style={styles.topBar}> <View style={styles.topBar}>
<Pressable {/* {returnHomeButton} */}
onPress={handleNavigateToLanding} {mobileInfoRow}
accessibilityRole="button" {!isWeb ? returnHomeButton : null}
style={[ <ProjectDropDown
styles.returnLandingButton, ref={projectDropdownRef}
isWeb ? styles.returnLandingButtonWeb : null, style={styles.projectDropDown}
]} projects={projects}
> selectedProject={currentProject}
<Text style={styles.returnLandingButtonText}> allowEmptySelection
Retour à l'accueil onSelectProject={handleSelectProject}
</Text> onModifyProject={handleModifyProject}
</Pressable> onCreateProject={handleStartNew}
<View style={styles.topBarContent}> formatDate={formatDate}
{mobileInfoRow} />
<ProjectDropDown
ref={projectDropdownRef}
style={styles.projectDropDown}
projects={projects}
selectedProject={currentProject}
allowEmptySelection
onSelectProject={handleSelectProject}
onModifyProject={handleModifyProject}
onCreateProject={handleStartNew}
formatDate={formatDate}
/>
</View>
</View> </View>
); );
@@ -592,9 +588,7 @@ const Home = ({ navigation, route }) => {
{stageCardsList} {stageCardsList}
<ClubCard <ClubCard onPress={handleClubPress} />
onPress={handleClubPress}
/>
</> </>
); );
@@ -611,6 +605,8 @@ const Home = ({ navigation, route }) => {
maxWidth={1600} maxWidth={1600}
width="100%" width="100%"
backgroundColor="#303438" backgroundColor="#303438"
showReturnHome={isWeb}
onReturnHome={handleReturnToLanding}
> >
<View style={styles.inner}> <View style={styles.inner}>
<ExpoImage <ExpoImage
@@ -619,10 +615,15 @@ const Home = ({ navigation, route }) => {
style={styles.centerImage} style={styles.centerImage}
/> />
{isWeb ? ( {isWeb ? (
<View style={styles.contentOverlay}> <>
{topBar} {/* <View style={styles.webReturnButtonWrapper}>
{journeyContent} {returnHomeButton}
</View> </View> */}
<View style={styles.contentOverlay}>
{topBar}
{journeyContent}
</View>
</>
) : ( ) : (
<View style={[styles.contentOverlay, styles.mobileContent]}> <View style={[styles.contentOverlay, styles.mobileContent]}>
{topBar} {topBar}
@@ -728,40 +729,20 @@ const styles = StyleSheet.create({
flexWrap: isWeb ? "wrap" : "nowrap", flexWrap: isWeb ? "wrap" : "nowrap",
alignItems: isWeb ? "center" : "stretch", alignItems: isWeb ? "center" : "stretch",
justifyContent: isWeb ? "center" : "flex-start", justifyContent: isWeb ? "center" : "flex-start",
gap: isWeb ? 0 : 12, gap: 12,
marginTop: isWeb ? 12 : 0, marginTop: isWeb ? 12 : 0,
marginBottom: isWeb ? 4 : 16, marginBottom: isWeb ? 4 : 16,
paddingHorizontal: isWeb ? 0 : gutters, paddingHorizontal: isWeb ? 0 : gutters,
zIndex: 10, zIndex: 10,
position: "relative",
}, },
returnLandingButton: { returnButton: {
paddingHorizontal: 14, width: isWeb ? 220 : "100%",
paddingVertical: 10, alignSelf: isWeb ? "flex-start" : "stretch",
borderRadius: 18, },
backgroundColor: "rgba(12, 14, 18, 0.72)", webReturnButtonWrapper: {
borderWidth: 1,
borderColor: "rgba(255, 255, 255, 0.1)",
alignSelf: "flex-start", alignSelf: "flex-start",
}, marginTop: gutters * 2.5,
returnLandingButtonWeb: { marginBottom: 8,
position: "absolute",
left: 0,
top: 0,
transform: [{ translateY: -2 }],
},
returnLandingButtonText: {
fontSize: 14,
color: Palette.white,
fontFamily: FONT_FAMILY.InterSemiBold,
},
topBarContent: {
flex: 1,
width: "100%",
flexDirection: isWeb ? "row" : "column",
alignItems: isWeb ? "center" : "stretch",
justifyContent: isWeb ? "center" : "flex-start",
gap: 12,
}, },
projectDropDown: { projectDropDown: {
width: isWeb ? 420 : "100%", width: isWeb ? 420 : "100%",