end of home
This commit is contained in:
+195
-128
@@ -1,145 +1,188 @@
|
||||
import { View, Text, Image, Platform } from "react-native";
|
||||
import React, { useMemo, useState } from "react";
|
||||
import React, { useCallback, useEffect, useMemo, useState } from "react";
|
||||
import { Platform, StyleSheet, View } from "react-native";
|
||||
import Page from "../../layouts/Page";
|
||||
import { background, img } from "../../assets";
|
||||
import { responsiveHeight } from "react-native-responsive-dimensions";
|
||||
import { Palette } from "../../styles";
|
||||
import { FONT_FAMILY } from "../../styles/Fonts";
|
||||
import { background } from "../../assets";
|
||||
import { gutters, Palette } from "../../styles";
|
||||
import { navigate } from "../../navigation/NavigationService";
|
||||
import { Routes } from "../../navigation";
|
||||
import { FlatList, Alert } from "react-native";
|
||||
import { BlurView } from "expo-blur";
|
||||
import { useUser } from "../../providers/UserDataProvider";
|
||||
import MusicCard from "../Library/components/MusicCard";
|
||||
import ProjectDropDown from "../../components/ProjectDropDown/ProjectDropDown";
|
||||
import FeatureCarousel from "../../components/FeatureCarousel/FeatureCarousel";
|
||||
import GradientButton from "../../components/GradientButton";
|
||||
import MoreMenu from "../../components/MoreMenu";
|
||||
import { projectsRef } from "../../config/firebase";
|
||||
import { useGlobal } from "reactn";
|
||||
import BorderGradientButton from "../../components/BorderGradientButton";
|
||||
import { isWeb } from "../../hooks/useLayoutType.js";
|
||||
import ShareBtn from "../../components/ShareBtn/ShareBtn";
|
||||
import {
|
||||
getCreationStageStates,
|
||||
getStageAction,
|
||||
} from "../../utils/projectStages";
|
||||
|
||||
const Home = () => {
|
||||
const { userProjects = [], resetSelectedProject, selectProject } = useUser();
|
||||
const {
|
||||
userProjects = [],
|
||||
resetSelectedProject,
|
||||
selectProject,
|
||||
selectedProject,
|
||||
selectedProjectId,
|
||||
} = useUser();
|
||||
|
||||
const projects = useMemo(
|
||||
() => (Array.isArray(userProjects) ? userProjects : []),
|
||||
[userProjects],
|
||||
);
|
||||
const [, setTooltip] = useGlobal("_tooltip");
|
||||
const [menuTop, setMenuTop] = useState(0);
|
||||
const [showMenu, setShowMenu] = useState(false);
|
||||
const [menuProjectId, setMenuProjectId] = useState(null);
|
||||
|
||||
const currentProject = useMemo(() => {
|
||||
if (!projects.length) 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 handleSelectProject = (project) => {
|
||||
if (!project?.id) return;
|
||||
selectProject(project.id);
|
||||
};
|
||||
|
||||
const handleModifyProject = (project) => {
|
||||
if (!project?.id) return;
|
||||
selectProject(project.id);
|
||||
navigate(Routes.FlowSelection);
|
||||
};
|
||||
|
||||
const handleCreateNew = () => {
|
||||
resetSelectedProject();
|
||||
navigate(Routes.FlowSelection);
|
||||
};
|
||||
|
||||
const stageStates = useMemo(
|
||||
() => getCreationStageStates(currentProject),
|
||||
[currentProject],
|
||||
);
|
||||
|
||||
const firstUnlockedIndex = useMemo(() => {
|
||||
const index = stageStates.findIndex((stage) => !stage.isLocked);
|
||||
return index === -1 ? 0 : index;
|
||||
}, [stageStates]);
|
||||
|
||||
const [activeStageIndex, setActiveStageIndex] = useState(firstUnlockedIndex);
|
||||
|
||||
useEffect(() => {
|
||||
setActiveStageIndex((prev) => {
|
||||
if (prev == null || prev >= stageStates.length) {
|
||||
return firstUnlockedIndex;
|
||||
}
|
||||
const current = stageStates[prev];
|
||||
const fallback = stageStates[firstUnlockedIndex];
|
||||
if (current?.isLocked && fallback && !fallback.isLocked) {
|
||||
return firstUnlockedIndex;
|
||||
}
|
||||
return prev;
|
||||
});
|
||||
}, [stageStates, firstUnlockedIndex]);
|
||||
|
||||
const activeStage =
|
||||
stageStates[activeStageIndex] || stageStates[firstUnlockedIndex];
|
||||
const stageAction = useMemo(() => {
|
||||
if (!activeStage) {
|
||||
return null;
|
||||
}
|
||||
return getStageAction(activeStage.key, currentProject);
|
||||
}, [activeStage, currentProject]);
|
||||
|
||||
const stageRoute = stageAction?.route || null;
|
||||
const stageParams = stageAction?.params;
|
||||
const stageLocked = activeStage?.isLocked ?? true;
|
||||
|
||||
const ensureProjectSelected = useCallback(() => {
|
||||
if (!currentProject?.id) {
|
||||
return;
|
||||
}
|
||||
if (selectedProject?.id !== currentProject.id) {
|
||||
selectProject(currentProject.id);
|
||||
}
|
||||
}, [currentProject?.id, selectProject, selectedProject?.id]);
|
||||
|
||||
const handleStageAction = useCallback(() => {
|
||||
if (stageLocked || !stageRoute) {
|
||||
return;
|
||||
}
|
||||
ensureProjectSelected();
|
||||
navigate(stageRoute, stageParams);
|
||||
}, [ensureProjectSelected, stageLocked, stageParams, stageRoute]);
|
||||
|
||||
const primaryDisabled = stageLocked || !stageRoute;
|
||||
return (
|
||||
<Page backgroundImg={background.homeBG} headerType="NONE">
|
||||
<View style={{ flex: 1 }}>
|
||||
<Image
|
||||
source={img.goodVibe}
|
||||
style={{ alignSelf: "center", position: "absolute" }}
|
||||
/>
|
||||
{projects.length > 0 && (
|
||||
<View
|
||||
style={{
|
||||
height: responsiveHeight(70),
|
||||
paddingTop: responsiveHeight(6),
|
||||
}}
|
||||
>
|
||||
<BlurView
|
||||
intensity={Platform.OS !== "ios" ? 10 : 20}
|
||||
style={{
|
||||
flex: 1,
|
||||
borderRadius: 20,
|
||||
overflow: "hidden",
|
||||
backgroundColor: Palette.glass,
|
||||
padding: 12,
|
||||
gap: 8,
|
||||
}}
|
||||
// experimentalBlurMethod={
|
||||
// Platform.OS !== "ios" ? "dimezisBlurView" : "none"
|
||||
// }
|
||||
>
|
||||
<Text
|
||||
style={{
|
||||
fontSize: 22,
|
||||
color: Palette.white,
|
||||
fontFamily: FONT_FAMILY.InterSemiBold,
|
||||
marginBottom: 8,
|
||||
textAlign: "center",
|
||||
}}
|
||||
>
|
||||
Musiques en cours
|
||||
</Text>
|
||||
<FlatList
|
||||
data={projects}
|
||||
keyExtractor={(item) => item.id}
|
||||
contentContainerStyle={{ gap: 10, paddingBottom: 10 }}
|
||||
renderItem={({ item }) => (
|
||||
<MusicCard
|
||||
title={item?.title || "Sans titre"}
|
||||
subtitle={"MusicLand"}
|
||||
imageUri={item?.coverUrl || null}
|
||||
projectId={item?.id}
|
||||
likedBy={item?.likedBy || []}
|
||||
onPress={() => {
|
||||
selectProject(item.id);
|
||||
navigate(Routes.FlowSelection);
|
||||
}}
|
||||
onPressMore={(posTop) => {
|
||||
setMenuProjectId(item.id);
|
||||
setMenuTop(posTop);
|
||||
setShowMenu((prev) => !prev || posTop !== menuTop);
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
/>
|
||||
<MoreMenu
|
||||
visible={showMenu}
|
||||
top={menuTop}
|
||||
onClose={() => setShowMenu(false)}
|
||||
inPlaylist={false}
|
||||
projectId={menuProjectId}
|
||||
extraItems={[
|
||||
{
|
||||
label: "Supprimer",
|
||||
onPress: () =>
|
||||
Alert.alert(
|
||||
"Confirmer la suppression",
|
||||
"Cette action supprimera définitivement ce projet.",
|
||||
[
|
||||
{ text: "Annuler", style: "cancel" },
|
||||
{
|
||||
text: "Supprimer",
|
||||
style: "destructive",
|
||||
onPress: async () => {
|
||||
try {
|
||||
if (!menuProjectId) return;
|
||||
await projectsRef.doc(menuProjectId).delete();
|
||||
setTooltip({
|
||||
type: "success",
|
||||
text: "Projet supprimé",
|
||||
});
|
||||
} catch (e) {
|
||||
setTooltip({
|
||||
type: "error",
|
||||
text: e?.message || "Suppression impossible",
|
||||
});
|
||||
}
|
||||
},
|
||||
},
|
||||
],
|
||||
{ cancelable: true },
|
||||
),
|
||||
},
|
||||
]}
|
||||
/>
|
||||
</BlurView>
|
||||
</View>
|
||||
)}
|
||||
<View style={{ paddingTop: 12, marginBottom: responsiveHeight(10) }}>
|
||||
<Page shareBtn backgroundImg={background.homeBGWeb} headerType="NONE">
|
||||
<View style={styles.root}>
|
||||
<View
|
||||
style={{
|
||||
zIndex: 10,
|
||||
position: "absolute",
|
||||
top: 10,
|
||||
width: 400,
|
||||
alignSelf: "center",
|
||||
flexDirection: isWeb ? "column" : "row",
|
||||
alignItems: "center",
|
||||
paddingHorizontal: isWeb ? 0 : gutters,
|
||||
gap: 10,
|
||||
}}
|
||||
>
|
||||
<ProjectDropDown
|
||||
style={[isWeb ? { width: "100%" } : { flex: 1 }]}
|
||||
projects={projects}
|
||||
selectedProject={currentProject}
|
||||
onSelectProject={handleSelectProject}
|
||||
onModifyProject={handleModifyProject}
|
||||
onCreateProject={handleCreateNew}
|
||||
formatDate={formatDate}
|
||||
/>
|
||||
{!isWeb && <ShareBtn />}
|
||||
</View>
|
||||
<View style={styles.carouselSection}>
|
||||
<FeatureCarousel
|
||||
selectedProject={currentProject}
|
||||
activeIndex={activeStageIndex}
|
||||
onActiveIndexChange={setActiveStageIndex}
|
||||
/>
|
||||
</View>
|
||||
<View
|
||||
style={{
|
||||
position: "absolute",
|
||||
bottom: isWeb ? 180 : 120,
|
||||
flexDirection: isWeb ? "row" : "column",
|
||||
gap: 5,
|
||||
alignSelf: "center",
|
||||
}}
|
||||
>
|
||||
<GradientButton
|
||||
title="Créer une nouvelle musique"
|
||||
containerStyle={{ width: "80%", alignSelf: "center" }}
|
||||
onPress={() => {
|
||||
resetSelectedProject();
|
||||
navigate(Routes.FlowSelection);
|
||||
}}
|
||||
containerStyle={{ width: Platform.OS === "web" ? 250 : "100%" }}
|
||||
title={"Commencer à créer"}
|
||||
onPress={handleStageAction}
|
||||
disabled={primaryDisabled}
|
||||
/>
|
||||
<BorderGradientButton
|
||||
containerStyle={{ width: Platform.OS === "web" ? 250 : "100%" }}
|
||||
title={"Continuer la création"}
|
||||
onPress={handleStageAction}
|
||||
disabled={primaryDisabled}
|
||||
/>
|
||||
</View>
|
||||
</View>
|
||||
@@ -148,3 +191,27 @@ const Home = () => {
|
||||
};
|
||||
|
||||
export default Home;
|
||||
|
||||
const styles = StyleSheet.create({
|
||||
root: {
|
||||
flex: 1,
|
||||
},
|
||||
heroImage: {
|
||||
position: "absolute",
|
||||
top: -60,
|
||||
alignSelf: "center",
|
||||
width: "80%",
|
||||
maxWidth: 920,
|
||||
height: 420,
|
||||
opacity: 0.9,
|
||||
},
|
||||
shareIcon: {
|
||||
width: 18,
|
||||
height: 18,
|
||||
tintColor: Palette.white,
|
||||
},
|
||||
carouselSection: {
|
||||
width: "100%",
|
||||
flex: 1,
|
||||
},
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user