feat: play from dropdown
This commit is contained in:
Binary file not shown.
|
Before Width: | Height: | Size: 2.3 MiB After Width: | Height: | Size: 2.2 MiB |
@@ -1,8 +1,10 @@
|
|||||||
import { BlurView } from "expo-blur";
|
import { BlurView } from "expo-blur";
|
||||||
import { Portal } from "@gorhom/portal";
|
import { Portal } from "@gorhom/portal";
|
||||||
import React, {
|
import React, {
|
||||||
|
forwardRef,
|
||||||
useCallback,
|
useCallback,
|
||||||
useEffect,
|
useEffect,
|
||||||
|
useImperativeHandle,
|
||||||
useMemo,
|
useMemo,
|
||||||
useRef,
|
useRef,
|
||||||
useState,
|
useState,
|
||||||
@@ -78,21 +80,25 @@ const defaultFormatDate = (timestamp) => {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const ProjectDropDown = ({
|
const ProjectDropDown = forwardRef(
|
||||||
style,
|
(
|
||||||
projects = [],
|
{
|
||||||
selectedProject,
|
style,
|
||||||
onSelectProject,
|
projects = [],
|
||||||
onModifyProject,
|
selectedProject,
|
||||||
onCreateProject,
|
onSelectProject,
|
||||||
formatDate = defaultFormatDate,
|
onModifyProject,
|
||||||
allowEmptySelection = false,
|
onCreateProject,
|
||||||
}) => {
|
formatDate = defaultFormatDate,
|
||||||
const [isOpen, setIsOpen] = useState(false);
|
allowEmptySelection = false,
|
||||||
const [triggerLayout, setTriggerLayout] = useState(null);
|
},
|
||||||
const [triggerWindowLayout, setTriggerWindowLayout] = useState(null);
|
ref
|
||||||
const triggerRef = useRef(null);
|
) => {
|
||||||
const dropdownAnimation = useRef(new Animated.Value(0)).current;
|
const [isOpen, setIsOpen] = useState(false);
|
||||||
|
const [triggerLayout, setTriggerLayout] = useState(null);
|
||||||
|
const [triggerWindowLayout, setTriggerWindowLayout] = useState(null);
|
||||||
|
const triggerRef = useRef(null);
|
||||||
|
const dropdownAnimation = useRef(new Animated.Value(0)).current;
|
||||||
|
|
||||||
const measureTriggerPosition = useCallback(() => {
|
const measureTriggerPosition = useCallback(() => {
|
||||||
if (triggerRef.current?.measureInWindow) {
|
if (triggerRef.current?.measureInWindow) {
|
||||||
@@ -133,39 +139,47 @@ const ProjectDropDown = ({
|
|||||||
})
|
})
|
||||||
: normalizedProjects;
|
: normalizedProjects;
|
||||||
|
|
||||||
const toggleDropdown = useCallback(() => {
|
const toggleDropdown = useCallback(() => {
|
||||||
if (isDisabled) {
|
if (isDisabled) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (!isOpen) {
|
if (!isOpen) {
|
||||||
measureTriggerPosition();
|
measureTriggerPosition();
|
||||||
}
|
}
|
||||||
setIsOpen((prev) => !prev);
|
setIsOpen((prev) => !prev);
|
||||||
}, [isDisabled, isOpen, measureTriggerPosition]);
|
}, [isDisabled, isOpen, measureTriggerPosition]);
|
||||||
|
|
||||||
const closeDropdown = useCallback(() => setIsOpen(false), []);
|
const closeDropdown = useCallback(() => setIsOpen(false), []);
|
||||||
|
|
||||||
const handleSelect = useCallback(
|
useImperativeHandle(
|
||||||
(project) => {
|
ref,
|
||||||
if (!project?.id) return;
|
() => ({
|
||||||
onSelectProject?.(project);
|
close: closeDropdown,
|
||||||
|
}),
|
||||||
|
[closeDropdown]
|
||||||
|
);
|
||||||
|
|
||||||
|
const handleSelect = useCallback(
|
||||||
|
(project) => {
|
||||||
|
if (!project?.id) return;
|
||||||
|
onSelectProject?.(project);
|
||||||
|
closeDropdown();
|
||||||
|
},
|
||||||
|
[closeDropdown, onSelectProject]
|
||||||
|
);
|
||||||
|
|
||||||
|
const handleModify = useCallback(
|
||||||
|
(project, anchor) => {
|
||||||
|
if (!project?.id) return;
|
||||||
|
onModifyProject?.(project, anchor);
|
||||||
|
},
|
||||||
|
[onModifyProject]
|
||||||
|
);
|
||||||
|
|
||||||
|
const handleCreate = useCallback(() => {
|
||||||
|
onCreateProject?.();
|
||||||
closeDropdown();
|
closeDropdown();
|
||||||
},
|
}, [closeDropdown, onCreateProject]);
|
||||||
[closeDropdown, onSelectProject]
|
|
||||||
);
|
|
||||||
|
|
||||||
const handleModify = useCallback(
|
|
||||||
(project, anchor) => {
|
|
||||||
if (!project?.id) return;
|
|
||||||
onModifyProject?.(project, anchor);
|
|
||||||
},
|
|
||||||
[onModifyProject]
|
|
||||||
);
|
|
||||||
|
|
||||||
const handleCreate = useCallback(() => {
|
|
||||||
onCreateProject?.();
|
|
||||||
closeDropdown();
|
|
||||||
}, [closeDropdown, onCreateProject]);
|
|
||||||
|
|
||||||
const dropdownWidth = useMemo(
|
const dropdownWidth = useMemo(
|
||||||
() => triggerLayout?.width || 0,
|
() => triggerLayout?.width || 0,
|
||||||
@@ -286,74 +300,75 @@ const ProjectDropDown = ({
|
|||||||
</ThemedBlurView>
|
</ThemedBlurView>
|
||||||
);
|
);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<View style={[styles.container, style]}>
|
<View style={[styles.container, style]}>
|
||||||
<View
|
<View
|
||||||
ref={triggerRef}
|
ref={triggerRef}
|
||||||
style={styles.triggerWrapper}
|
style={styles.triggerWrapper}
|
||||||
onLayout={(event) => {
|
onLayout={(event) => {
|
||||||
setTriggerLayout(event.nativeEvent.layout);
|
setTriggerLayout(event.nativeEvent.layout);
|
||||||
measureTriggerPosition();
|
measureTriggerPosition();
|
||||||
}}
|
}}
|
||||||
>
|
|
||||||
<Animated.View
|
|
||||||
pointerEvents={isOpen ? "none" : "auto"}
|
|
||||||
style={[
|
|
||||||
styles.dropdownTriggerContainer,
|
|
||||||
{
|
|
||||||
opacity: triggerOpacity,
|
|
||||||
transform: [{ translateY: triggerTranslateY }],
|
|
||||||
},
|
|
||||||
]}
|
|
||||||
>
|
>
|
||||||
<ThemedBlurView
|
<Animated.View
|
||||||
|
pointerEvents={isOpen ? "none" : "auto"}
|
||||||
style={[
|
style={[
|
||||||
styles.dropdownBlur,
|
styles.dropdownTriggerContainer,
|
||||||
isDisabled && styles.dropdownBlurDisabled,
|
{
|
||||||
|
opacity: triggerOpacity,
|
||||||
|
transform: [{ translateY: triggerTranslateY }],
|
||||||
|
},
|
||||||
]}
|
]}
|
||||||
>
|
>
|
||||||
<ProjectRow
|
<ThemedBlurView
|
||||||
isTitle={true}
|
style={[
|
||||||
isOpen={isOpen}
|
styles.dropdownBlur,
|
||||||
project={currentProject}
|
isDisabled && styles.dropdownBlurDisabled,
|
||||||
formatDate={formatDate}
|
]}
|
||||||
onSelect={toggleDropdown}
|
>
|
||||||
/>
|
<ProjectRow
|
||||||
</ThemedBlurView>
|
isTitle={true}
|
||||||
</Animated.View>
|
isOpen={isOpen}
|
||||||
|
project={currentProject}
|
||||||
|
formatDate={formatDate}
|
||||||
|
onSelect={toggleDropdown}
|
||||||
|
/>
|
||||||
|
</ThemedBlurView>
|
||||||
|
</Animated.View>
|
||||||
|
|
||||||
{isOpen && triggerWindowLayout ? (
|
{isOpen && triggerWindowLayout ? (
|
||||||
<Portal>
|
<Portal>
|
||||||
<View style={styles.portalContainer}>
|
<View style={styles.portalContainer}>
|
||||||
<Pressable style={styles.backdrop} onPress={closeDropdown}>
|
<Pressable style={styles.backdrop} onPress={closeDropdown}>
|
||||||
<ThemedBlurView
|
<ThemedBlurView
|
||||||
pointerEvents="none"
|
pointerEvents="none"
|
||||||
style={styles.backdropBlur}
|
style={styles.backdropBlur}
|
||||||
/>
|
/>
|
||||||
</Pressable>
|
</Pressable>
|
||||||
<Animated.View
|
<Animated.View
|
||||||
pointerEvents="auto"
|
pointerEvents="auto"
|
||||||
style={[
|
style={[
|
||||||
styles.dropdownOverlayContainer,
|
styles.dropdownOverlayContainer,
|
||||||
overlayPositionStyle,
|
overlayPositionStyle,
|
||||||
{
|
{
|
||||||
opacity: dropdownAnimation,
|
opacity: dropdownAnimation,
|
||||||
transform: [
|
transform: [
|
||||||
{ translateY: overlayTranslateY },
|
{ translateY: overlayTranslateY },
|
||||||
{ scale: overlayScale },
|
{ scale: overlayScale },
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
]}
|
]}
|
||||||
>
|
>
|
||||||
{dropdownContent}
|
{dropdownContent}
|
||||||
</Animated.View>
|
</Animated.View>
|
||||||
</View>
|
</View>
|
||||||
</Portal>
|
</Portal>
|
||||||
) : null}
|
) : null}
|
||||||
|
</View>
|
||||||
</View>
|
</View>
|
||||||
</View>
|
);
|
||||||
);
|
}
|
||||||
};
|
);
|
||||||
|
|
||||||
export default ProjectDropDown;
|
export default ProjectDropDown;
|
||||||
|
|
||||||
|
|||||||
+47
-23
@@ -33,6 +33,7 @@ import {
|
|||||||
getStageAction,
|
getStageAction,
|
||||||
} from "../../utils/projectStages";
|
} from "../../utils/projectStages";
|
||||||
import { openCoinPackModal } from "../../utils/coinPackModal";
|
import { openCoinPackModal } from "../../utils/coinPackModal";
|
||||||
|
import useNavigateToMusicDetails from "../../hooks/useNavigateToMusicDetails";
|
||||||
import StageCard from "./components/StageCard";
|
import StageCard from "./components/StageCard";
|
||||||
import ClubCard from "./components/ClubCard";
|
import ClubCard from "./components/ClubCard";
|
||||||
|
|
||||||
@@ -66,7 +67,7 @@ const STAGE_CARD_CONTENT = [
|
|||||||
key: "beatmaker",
|
key: "beatmaker",
|
||||||
step: "ÉTAPE 2",
|
step: "ÉTAPE 2",
|
||||||
description:
|
description:
|
||||||
"Bienvenue au studio MusicLand.\nNous mettons en musique tes paroles en fonction de tes inspirations, ça va être top !",
|
"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,
|
image: cardsImg.studio,
|
||||||
imagePosition: "right",
|
imagePosition: "right",
|
||||||
textAlign: "right",
|
textAlign: "right",
|
||||||
@@ -86,7 +87,7 @@ const STAGE_CARD_CONTENT = [
|
|||||||
key: "publisher",
|
key: "publisher",
|
||||||
step: "ÉTAPE 4",
|
step: "ÉTAPE 4",
|
||||||
description:
|
description:
|
||||||
"À la sortie du studio, l'équipe production te propose les prochaines étapes pour partager ta musique avec le monde.",
|
"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,
|
image: cardsImg.production,
|
||||||
imagePosition: "right",
|
imagePosition: "right",
|
||||||
textAlign: "right",
|
textAlign: "right",
|
||||||
@@ -115,6 +116,7 @@ const Home = ({ navigation, route }) => {
|
|||||||
} = useUser();
|
} = useUser();
|
||||||
const { setTooltip } = useMinuit();
|
const { setTooltip } = useMinuit();
|
||||||
const [videoUrl, setVideoUrl] = useState(null);
|
const [videoUrl, setVideoUrl] = useState(null);
|
||||||
|
const navigateToMusicDetails = useNavigateToMusicDetails();
|
||||||
|
|
||||||
if (!currentUID) {
|
if (!currentUID) {
|
||||||
return <LandingPage />;
|
return <LandingPage />;
|
||||||
@@ -174,6 +176,7 @@ const Home = ({ navigation, route }) => {
|
|||||||
const [menuAnchor, setMenuAnchor] = useState(null);
|
const [menuAnchor, setMenuAnchor] = useState(null);
|
||||||
const [menuProject, setMenuProject] = useState(null);
|
const [menuProject, setMenuProject] = useState(null);
|
||||||
const menuAnchorRef = useRef(null);
|
const menuAnchorRef = useRef(null);
|
||||||
|
const projectDropdownRef = useRef(null);
|
||||||
const [hasLocalAdventureFlag, setHasLocalAdventureFlag] = useState(false);
|
const [hasLocalAdventureFlag, setHasLocalAdventureFlag] = useState(false);
|
||||||
|
|
||||||
const handleSelectProject = useCallback(
|
const handleSelectProject = useCallback(
|
||||||
@@ -232,28 +235,48 @@ const Home = ({ navigation, route }) => {
|
|||||||
|
|
||||||
const moreMenuItems = useMemo(() => {
|
const moreMenuItems = useMemo(() => {
|
||||||
if (!menuProject?.id) return [];
|
if (!menuProject?.id) return [];
|
||||||
return [
|
|
||||||
{
|
const items = [];
|
||||||
label: "Supprimer",
|
const hasSongUrl =
|
||||||
onPress: () =>
|
typeof menuProject?.songUrl === "string" &&
|
||||||
Alert(
|
menuProject.songUrl.length > 0;
|
||||||
"Confirmer la suppression",
|
|
||||||
"Cette action supprimera définitivement ce projet.",
|
if (hasSongUrl) {
|
||||||
[
|
items.push({
|
||||||
{ text: "Annuler", style: "cancel" },
|
label: "Jouer la musique",
|
||||||
{
|
onPress: () => {
|
||||||
text: "Supprimer",
|
projectDropdownRef.current?.close?.();
|
||||||
style: "destructive",
|
navigateToMusicDetails({
|
||||||
onPress: () => {
|
projectId: menuProject.id,
|
||||||
handleDeleteProject();
|
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 },
|
],
|
||||||
),
|
{ cancelable: true },
|
||||||
},
|
),
|
||||||
];
|
});
|
||||||
}, [handleDeleteProject, menuProject?.id]);
|
|
||||||
|
return items;
|
||||||
|
}, [handleDeleteProject, menuProject, navigateToMusicDetails]);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (!route?.params?.showLyricsCongrats) {
|
if (!route?.params?.showLyricsCongrats) {
|
||||||
@@ -495,6 +518,7 @@ const Home = ({ navigation, route }) => {
|
|||||||
<View style={styles.topBar}>
|
<View style={styles.topBar}>
|
||||||
{mobileInfoRow}
|
{mobileInfoRow}
|
||||||
<ProjectDropDown
|
<ProjectDropDown
|
||||||
|
ref={projectDropdownRef}
|
||||||
style={styles.projectDropDown}
|
style={styles.projectDropDown}
|
||||||
projects={projects}
|
projects={projects}
|
||||||
selectedProject={currentProject}
|
selectedProject={currentProject}
|
||||||
|
|||||||
Reference in New Issue
Block a user