import React, {
useCallback,
useEffect,
useMemo,
useRef,
useState,
} from "react";
import { Platform, StyleSheet, View } from "react-native";
import { useIsFocused } from "@react-navigation/native";
import useMinuit from "react-native-minuit/src/hooks/useMinuit.js";
import { background, videos } from "../../assets";
import Alert from "../../components/Alert";
import BorderGradientButton from "../../components/BorderGradientButton";
import FeatureCarousel from "../../components/FeatureCarousel/FeatureCarousel";
import FullscreenIntroVideo from "../../components/FullscreenIntroVideo";
import GradientButton from "../../components/GradientButton";
import MoreMenu from "../../components/MoreMenu";
import ProjectDropDown from "../../components/ProjectDropDown/ProjectDropDown";
import ShareBtn from "../../components/ShareBtn/ShareBtn";
import { projectsRef, usersRef, videosRef } from "../../config/firebase";
import useDataFromRef from "../../hooks/useDataFromRef";
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 { Palette } from "../../styles";
import {
findFirstUnlockedStageIndex,
getCreationStageStates,
getStageAction,
} from "../../utils/projectStages";
const isProjectEmpty = (project) => {
if (!project) {
return false;
}
return !(
typeof project?.title === "string" && project.title.trim().length > 0
);
};
const Home = ({ navigation, route }) => {
const isFocused = useIsFocused();
const {
userProjects = [],
selectProject,
selectedProject,
selectedProjectId,
currentUserData,
currentUID,
createNewProject,
} = useUser();
const { setTooltip } = useMinuit();
if (!currentUID) {
return ;
}
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 preferredStageIndex = useMemo(() => {
if (!stageStates.length) {
return 0;
}
const actionableIndex = stageStates.findIndex(
(stage) => !stage.isCompleted && !stage.isLocked
);
if (actionableIndex !== -1) {
return actionableIndex;
}
const firstIncomplete = stageStates.findIndex(
(stage) => !stage.isCompleted
);
if (firstIncomplete !== -1) {
return firstIncomplete;
}
const firstUnlocked = stageStates.findIndex((stage) => !stage.isLocked);
if (firstUnlocked !== -1) {
return firstUnlocked;
}
return stageStates.length - 1;
}, [stageStates]);
const [activeStageIndex, setActiveStageIndex] = useState(preferredStageIndex);
const [menuVisible, setMenuVisible] = useState(false);
const [menuAnchor, setMenuAnchor] = useState(null);
const [menuProject, setMenuProject] = useState(null);
const menuAnchorRef = useRef(null);
const testLoaderTimeoutRef = useRef(null);
const [isIntroVideoVisible, setIsIntroVideoVisible] = useState(false);
const [hasLocalAdventureFlag, setHasLocalAdventureFlag] = useState(false);
useEffect(() => {
setActiveStageIndex((prev) => {
const fallbackIndex =
preferredStageIndex >= 0 && preferredStageIndex < stageStates.length
? preferredStageIndex
: 0;
if (prev == null || prev >= stageStates.length) {
return fallbackIndex;
}
const current = stageStates[prev];
if (!current) {
return fallbackIndex;
}
if (
(current.isLocked || current.isCompleted) &&
fallbackIndex !== prev &&
stageStates[fallbackIndex]
) {
return fallbackIndex;
}
return prev;
});
}, [stageStates, preferredStageIndex]);
const handleSelectProject = useCallback(
(project) => {
if (!project?.id) return;
selectProject(project.id);
setActiveStageIndex(findFirstUnlockedStageIndex(project));
},
[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 [];
return [
{
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 }
),
},
];
}, [handleDeleteProject, menuProject?.id]);
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 avec Malik. Dans cette étape vous allez pouvoir donner vie à votre chanson !",
[
{
text: "Retour à l'accueil",
style: "cancel",
},
{
text: "Continuer avec Malik",
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 activeStage =
stageStates[activeStageIndex] || stageStates[preferredStageIndex];
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 hasActiveProject = !!currentProject;
const ensureProjectSelected = useCallback(() => {
if (!currentProject?.id) {
return;
}
if (selectedProject?.id !== currentProject.id) {
selectProject(currentProject.id);
}
}, [currentProject?.id, selectProject, selectedProject?.id]);
const handleStageAction = useCallback(() => {
if (!hasActiveProject || stageLocked || !stageRoute) {
return;
}
ensureProjectSelected();
navigate(stageRoute, stageParams);
}, [
ensureProjectSelected,
hasActiveProject,
stageLocked,
stageParams,
stageRoute,
]);
useEffect(() => {
return () => {
if (testLoaderTimeoutRef.current) {
clearTimeout(testLoaderTimeoutRef.current);
testLoaderTimeoutRef.current = null;
}
};
}, []);
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);
setActiveStageIndex(findFirstUnlockedStageIndex(emptyProject));
navigate(targetRoute, targetParams);
return;
}
const newProjectId = await createNewProject({ hasLyrics: false });
if (!newProjectId) {
return;
}
setActiveStageIndex(0);
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,
setActiveStageIndex,
setTooltip,
songwriterAction,
]);
const continueDisabled = !hasActiveProject || stageLocked || !stageRoute;
const startDisabled = !songwriterAction?.route;
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(() => {
setIsIntroVideoVisible(true);
}, []);
const handleIntroVideoClose = useCallback(() => {
setIsIntroVideoVisible(false);
markAdventureStarted();
}, [markAdventureStarted]);
const adventureStarted =
hasLocalAdventureFlag || !!currentUserData?.adventureStarted;
const { data: video } = useDataFromRef({
ref: videosRef.doc("fr"),
simpleRef: true,
});
const videoUrl = isWeb ? video?.landingWeb : video?.landing;
const homeBackgroundImage = isWeb
? background.homeBGWeb
: background.homeBG;
const landingBackgroundImage = homeBackgroundImage;
return adventureStarted ? (
{!isWeb && }
{/*
Alert("Test", "Ceci est un test d'alert", [
{ text: "Annuler", style: "cancel" },
{ text: "OK", onPress: () => console.log("OK pressé") },
])
}
/> */}
{/* navigate(Routes.SongReady)}
disabled={continueDisabled}
/> */}
) : (
<>
>
);
};
export default Home;
const styles = StyleSheet.create({
root: {
flex: 1,
},
rootWeb: {
backgroundColor: "transparent",
},
rootNative: {
backgroundColor: "rgba(66, 91, 135, 0.3)",
},
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,
},
carouselSectionElevated: {
marginTop: -100,
paddingTop: 80,
},
});