animation, dots, dropdown, popover and design fixes

This commit is contained in:
Thomas Demirdjian
2025-10-06 10:13:17 +02:00
parent 3d14e47770
commit bd1ce3d26c
7 changed files with 427 additions and 116 deletions
+109 -20
View File
@@ -1,15 +1,22 @@
import React, { useCallback, useEffect, useMemo, useState } from "react";
import { Platform, StyleSheet, View } from "react-native";
import React, {
useCallback,
useEffect,
useMemo,
useRef,
useState,
} from "react";
import { Alert, Platform, StyleSheet, View } from "react-native";
import { background } from "../../assets";
import BorderGradientButton from "../../components/BorderGradientButton";
import FeatureCarousel from "../../components/FeatureCarousel/FeatureCarousel";
import GradientButton from "../../components/GradientButton";
import MoreMenu from "../../components/MoreMenu";
import ProjectDropDown from "../../components/ProjectDropDown/ProjectDropDown";
import ShareBtn from "../../components/ShareBtn/ShareBtn";
import { isWeb } from "../../hooks/useLayoutType.js";
import Page from "../../layouts/Page";
import { Routes } from "../../navigation";
import { navigate } from "../../navigation/NavigationService";
import { projectsRef } from "../../config/firebase";
import { useUser } from "../../providers/UserDataProvider";
import { gutters, Palette } from "../../styles";
import {
@@ -17,6 +24,7 @@ import {
getCreationStageStates,
getStageAction,
} from "../../utils/projectStages";
import useMinuit from "react-native-minuit/src/hooks/useMinuit.js";
const Home = () => {
const {
@@ -26,10 +34,11 @@ const Home = () => {
selectedProject,
selectedProjectId,
} = useUser();
const { setTooltip } = useMinuit();
const projects = useMemo(
() => (Array.isArray(userProjects) ? userProjects : []),
[userProjects]
[userProjects],
);
const currentProject = useMemo(() => {
@@ -60,22 +69,9 @@ const Home = () => {
}
}, []);
const handleSelectProject = (project) => {
if (!project?.id) return;
selectProject(project.id);
setActiveStageIndex(findFirstUnlockedStageIndex(project));
};
const handleModifyProject = (project) => {
if (!project?.id) return;
selectProject(project.id);
setActiveStageIndex(findFirstUnlockedStageIndex(project));
navigate(Routes.Home);
};
const stageStates = useMemo(
() => getCreationStageStates(currentProject),
[currentProject]
[currentProject],
);
const firstUnlockedIndex = useMemo(() => {
@@ -84,6 +80,10 @@ const Home = () => {
}, [stageStates]);
const [activeStageIndex, setActiveStageIndex] = useState(firstUnlockedIndex);
const [menuVisible, setMenuVisible] = useState(false);
const [menuAnchor, setMenuAnchor] = useState(null);
const [menuProject, setMenuProject] = useState(null);
const menuAnchorRef = useRef(null);
useEffect(() => {
setActiveStageIndex((prev) => {
@@ -99,6 +99,86 @@ const Home = () => {
});
}, [stageStates, firstUnlockedIndex]);
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.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]);
const activeStage =
stageStates[activeStageIndex] || stageStates[firstUnlockedIndex];
const stageAction = useMemo(() => {
@@ -139,7 +219,7 @@ const Home = () => {
const songwriterAction = useMemo(
() => getStageAction("songwriter", null),
[]
[],
);
const handleStartNew = useCallback(() => {
@@ -149,7 +229,7 @@ const Home = () => {
console.log(
"navigating to",
songwriterAction.route,
songwriterAction.params
songwriterAction.params,
);
navigate(songwriterAction.route, songwriterAction.params);
}
@@ -185,6 +265,15 @@ const Home = () => {
/>
{!isWeb && <ShareBtn />}
</View>
<MoreMenu
visible={menuVisible}
top={menuAnchor?.top ?? 0}
position={menuAnchor}
onClose={handleCloseMenu}
inPlaylist={false}
projectId={menuProject?.id || null}
extraItems={moreMenuItems}
/>
<View style={styles.carouselSection}>
<FeatureCarousel
selectedProject={currentProject}