new options
This commit is contained in:
@@ -40,7 +40,11 @@ const MusicLandHeader = ({
|
|||||||
<View style={{ alignItems: "center", gap: 16 }}>
|
<View style={{ alignItems: "center", gap: 16 }}>
|
||||||
<Image
|
<Image
|
||||||
source={logo || icons.musicLandLogo}
|
source={logo || icons.musicLandLogo}
|
||||||
style={{ alignSelf: "center", height: 150, resizeMode: "contain" }}
|
style={{
|
||||||
|
alignSelf: "center",
|
||||||
|
height: isWeb ? 150 : 100,
|
||||||
|
resizeMode: "contain",
|
||||||
|
}}
|
||||||
/>
|
/>
|
||||||
<View style={{ width: "100%", ...Style.containerRow, gap: 16 }}>
|
<View style={{ width: "100%", ...Style.containerRow, gap: 16 }}>
|
||||||
<Pressable style={backButtonStyle} onPress={onPressBack}>
|
<Pressable style={backButtonStyle} onPress={onPressBack}>
|
||||||
|
|||||||
@@ -250,13 +250,7 @@ const MusicDetails = ({ route }) => {
|
|||||||
console.log("MusicDetails seek error", e?.message);
|
console.log("MusicDetails seek error", e?.message);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
[
|
[trackDescriptor, durationMs, isCurrentTrack, ensureLoaded, seekTrackTo]
|
||||||
trackDescriptor,
|
|
||||||
durationMs,
|
|
||||||
isCurrentTrack,
|
|
||||||
ensureLoaded,
|
|
||||||
seekTrackTo,
|
|
||||||
]
|
|
||||||
);
|
);
|
||||||
|
|
||||||
const handleSliderSeekEnd = useCallback(async () => {
|
const handleSliderSeekEnd = useCallback(async () => {
|
||||||
@@ -293,13 +287,7 @@ const MusicDetails = ({ route }) => {
|
|||||||
console.log("MusicDetails seekBy error", e?.message);
|
console.log("MusicDetails seekBy error", e?.message);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
[
|
[trackDescriptor, positionMs, isCurrentTrack, ensureLoaded, seekTrackBy]
|
||||||
trackDescriptor,
|
|
||||||
positionMs,
|
|
||||||
isCurrentTrack,
|
|
||||||
ensureLoaded,
|
|
||||||
seekTrackBy,
|
|
||||||
]
|
|
||||||
);
|
);
|
||||||
|
|
||||||
const handleLyricsSeek = useCallback(
|
const handleLyricsSeek = useCallback(
|
||||||
|
|||||||
@@ -1,6 +1,13 @@
|
|||||||
import { BlurView } from "expo-blur";
|
import { BlurView } from "expo-blur";
|
||||||
import React, { useEffect, useMemo, useState } from "react";
|
import React, { useEffect, useMemo } from "react";
|
||||||
import { Image, Platform, StyleSheet, Text, View } from "react-native";
|
import {
|
||||||
|
FlatList,
|
||||||
|
Image,
|
||||||
|
Platform,
|
||||||
|
StyleSheet,
|
||||||
|
Text,
|
||||||
|
View,
|
||||||
|
} from "react-native";
|
||||||
import Animated, { useAnimatedRef } from "react-native-reanimated";
|
import Animated, { useAnimatedRef } from "react-native-reanimated";
|
||||||
import Sortable from "react-native-sortables";
|
import Sortable from "react-native-sortables";
|
||||||
import { icons } from "../../assets";
|
import { icons } from "../../assets";
|
||||||
@@ -11,7 +18,6 @@ import CreateLyricsHeader from "./components/CreateLyricsHeader";
|
|||||||
// baseStructure: array like ['couplet','refrain',...]
|
// baseStructure: array like ['couplet','refrain',...]
|
||||||
// onChange: callback that receives array like ['couplet','refrain',...]
|
// onChange: callback that receives array like ['couplet','refrain',...]
|
||||||
const CustomizeSongStructure = ({ baseStructure = [], onChange }) => {
|
const CustomizeSongStructure = ({ baseStructure = [], onChange }) => {
|
||||||
const [containerLayout, setContainerLayout] = useState(null);
|
|
||||||
const scrollRef = useAnimatedRef();
|
const scrollRef = useAnimatedRef();
|
||||||
|
|
||||||
// Build stable keyed data like { 'couplet-1': { label, value }, ... }
|
// Build stable keyed data like { 'couplet-1': { label, value }, ... }
|
||||||
@@ -34,6 +40,23 @@ const CustomizeSongStructure = ({ baseStructure = [], onChange }) => {
|
|||||||
|
|
||||||
const items = useMemo(() => Object.values(data), [data]);
|
const items = useMemo(() => Object.values(data), [data]);
|
||||||
|
|
||||||
|
const newItems = [
|
||||||
|
{
|
||||||
|
id: "short_intro",
|
||||||
|
label: "Introduction instrumentale courte",
|
||||||
|
value: "short_intro",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: "long_intro",
|
||||||
|
label: "Introduction instrumentale longue",
|
||||||
|
value: "long_intro",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: "pre_chorus",
|
||||||
|
label: "Pré-refrain",
|
||||||
|
value: "pre_chorus",
|
||||||
|
},
|
||||||
|
];
|
||||||
// Initialize parent with current order (so it's saved even without drag)
|
// Initialize parent with current order (so it's saved even without drag)
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const initialValues = (items || []).map((it) => it?.value).filter(Boolean);
|
const initialValues = (items || []).map((it) => it?.value).filter(Boolean);
|
||||||
@@ -65,9 +88,12 @@ const CustomizeSongStructure = ({ baseStructure = [], onChange }) => {
|
|||||||
<Text style={styles.rowText}>{rowData?.label || ""}</Text>
|
<Text style={styles.rowText}>{rowData?.label || ""}</Text>
|
||||||
</View>
|
</View>
|
||||||
</View>
|
</View>
|
||||||
|
{/* Render handle only for items that come from the sortable data (have an id like 'couplet-1') */}
|
||||||
|
{String(rowData?.id || "").includes("-") ? (
|
||||||
<Sortable.Handle>
|
<Sortable.Handle>
|
||||||
<Image source={icons.dragDots} style={styles.handleIcon} />
|
<Image source={icons.dragDots} style={styles.handleIcon} />
|
||||||
</Sortable.Handle>
|
</Sortable.Handle>
|
||||||
|
) : null}
|
||||||
</View>
|
</View>
|
||||||
</BlurView>
|
</BlurView>
|
||||||
</View>
|
</View>
|
||||||
@@ -79,15 +105,13 @@ const CustomizeSongStructure = ({ baseStructure = [], onChange }) => {
|
|||||||
title="Personnalise la structure de ta chanson"
|
title="Personnalise la structure de ta chanson"
|
||||||
subTitle="Réorganise par glisser-déposer"
|
subTitle="Réorganise par glisser-déposer"
|
||||||
/>
|
/>
|
||||||
<View
|
<View style={{ flex: 1 }}>
|
||||||
style={{ flex: 1 }}
|
|
||||||
onLayout={(e) => setContainerLayout(e.nativeEvent.layout)}
|
|
||||||
>
|
|
||||||
<Animated.ScrollView
|
<Animated.ScrollView
|
||||||
ref={scrollRef}
|
ref={scrollRef}
|
||||||
contentContainerStyle={{ paddingVertical: 4, paddingBottom: 24 }}
|
contentContainerStyle={{ paddingVertical: 4, paddingBottom: 24 }}
|
||||||
showsVerticalScrollIndicator={false}
|
showsVerticalScrollIndicator={false}
|
||||||
>
|
>
|
||||||
|
<>
|
||||||
<Sortable.Grid
|
<Sortable.Grid
|
||||||
columns={1}
|
columns={1}
|
||||||
rowGap={12}
|
rowGap={12}
|
||||||
@@ -103,6 +127,21 @@ const CustomizeSongStructure = ({ baseStructure = [], onChange }) => {
|
|||||||
onChange?.(values);
|
onChange?.(values);
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
|
<FlatList
|
||||||
|
data={newItems}
|
||||||
|
gap={12}
|
||||||
|
renderItem={({ item }) => (
|
||||||
|
<View style={{ marginTop: 12 }}>
|
||||||
|
<Row item={item} />
|
||||||
|
</View>
|
||||||
|
)}
|
||||||
|
keyExtractor={(item) => item.id}
|
||||||
|
contentContainerStyle={{ paddingBottom: 24 }}
|
||||||
|
// Prevent FlatList from capturing scroll gestures so Sortable can handle drags
|
||||||
|
scrollEnabled={false}
|
||||||
|
nestedScrollEnabled={false}
|
||||||
|
/>
|
||||||
|
</>
|
||||||
</Animated.ScrollView>
|
</Animated.ScrollView>
|
||||||
</View>
|
</View>
|
||||||
</View>
|
</View>
|
||||||
|
|||||||
Reference in New Issue
Block a user