147 lines
4.4 KiB
JavaScript
147 lines
4.4 KiB
JavaScript
import { View, StyleSheet, Image, Platform, Text } from "react-native";
|
|
import React, { useMemo, useState, useCallback, useEffect } from "react";
|
|
import Animated, { useAnimatedRef } from "react-native-reanimated";
|
|
import { BlurView } from "expo-blur";
|
|
import CreateLyricsHeader from "./components/CreateLyricsHeader";
|
|
import { Palette, Style } from "../../styles";
|
|
import { FONT_FAMILY } from "../../styles/Fonts";
|
|
import Sortable from "react-native-sortables";
|
|
import { icons } from "../../assets";
|
|
|
|
// baseStructure: array like ['couplet','refrain',...]
|
|
// onChange: callback that receives array like ['couplet','refrain',...]
|
|
const CustomizeSongStructure = ({ baseStructure = [], onChange }) => {
|
|
const [containerLayout, setContainerLayout] = useState(null);
|
|
const scrollRef = useAnimatedRef();
|
|
|
|
// Build stable keyed data like { 'couplet-1': { label, value }, ... }
|
|
const data = useMemo(() => {
|
|
const counters = { couplet: 0, refrain: 0 };
|
|
const out = {};
|
|
(baseStructure || []).forEach((type) => {
|
|
const key = (type || "").toLowerCase();
|
|
counters[key] = (counters[key] || 0) + 1;
|
|
const idx = counters[key];
|
|
const id = `${key}-${idx}`;
|
|
out[id] = {
|
|
id,
|
|
label: `${key === "couplet" ? "Couplet" : "Refrain"} ${idx}`,
|
|
value: key,
|
|
};
|
|
});
|
|
return out;
|
|
}, [baseStructure]);
|
|
|
|
const items = useMemo(() => Object.values(data), [data]);
|
|
|
|
// Initialize parent with current order (so it's saved even without drag)
|
|
useEffect(() => {
|
|
const initialValues = (items || []).map((it) => it?.value).filter(Boolean);
|
|
onChange?.(initialValues);
|
|
// We only want to run when baseStructure-derived items change
|
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
}, [items.length]);
|
|
|
|
const Row = ({ item: rowData }) => (
|
|
<View style={styles.itemContainer}>
|
|
<BlurView
|
|
intensity={Platform.OS !== "ios" ? 10 : 30}
|
|
style={styles.rowBlur}
|
|
// experimentalBlurMethod={Platform.OS !== "ios" ? "dimezisBlurView" : "none"}
|
|
>
|
|
<View style={{ ...Style.containerSpaceBetween, alignItems: "center" }}>
|
|
<View>
|
|
<View>
|
|
<View>
|
|
<View />
|
|
</View>
|
|
</View>
|
|
<View>
|
|
<View />
|
|
</View>
|
|
</View>
|
|
<View style={{ flex: 1, justifyContent: "center" }}>
|
|
<View>
|
|
<Text style={styles.rowText}>{rowData?.label || ""}</Text>
|
|
</View>
|
|
</View>
|
|
<Sortable.Handle>
|
|
<Image source={icons.dragDots} style={styles.handleIcon} />
|
|
</Sortable.Handle>
|
|
</View>
|
|
</BlurView>
|
|
</View>
|
|
);
|
|
|
|
return (
|
|
<View style={{ flex: 1, gap: 10, marginTop: 16 }}>
|
|
<CreateLyricsHeader
|
|
title="Personnalise la structure de ta chanson"
|
|
subTitle="Réorganise par glisser-déposer"
|
|
/>
|
|
<View
|
|
style={{ flex: 1 }}
|
|
onLayout={(e) => setContainerLayout(e.nativeEvent.layout)}
|
|
>
|
|
<Animated.ScrollView
|
|
ref={scrollRef}
|
|
contentContainerStyle={{ paddingVertical: 4, paddingBottom: 24 }}
|
|
showsVerticalScrollIndicator={false}
|
|
>
|
|
<Sortable.Grid
|
|
columns={1}
|
|
rowGap={12}
|
|
data={items}
|
|
renderItem={({ item }) => <Row item={item} />}
|
|
customHandle
|
|
// Enable auto-scroll when dragging near edges
|
|
scrollableRef={scrollRef}
|
|
onDragEnd={({ data: newData }) => {
|
|
const values = (newData || [])
|
|
.map((it) => it?.value)
|
|
.filter(Boolean);
|
|
onChange?.(values);
|
|
}}
|
|
/>
|
|
</Animated.ScrollView>
|
|
</View>
|
|
</View>
|
|
);
|
|
};
|
|
|
|
export default CustomizeSongStructure;
|
|
|
|
const styles = StyleSheet.create({
|
|
itemContainer: {
|
|
height: 54,
|
|
backgroundColor: Palette.glass,
|
|
borderRadius: 14,
|
|
overflow: "hidden",
|
|
shadowColor: "#00000040",
|
|
shadowOffset: {
|
|
width: 0,
|
|
height: 2,
|
|
},
|
|
shadowOpacity: 0.25,
|
|
shadowRadius: 3.84,
|
|
elevation: 5,
|
|
},
|
|
rowBlur: {
|
|
flex: 1,
|
|
backgroundColor: Palette.glass,
|
|
justifyContent: "center",
|
|
paddingHorizontal: 12,
|
|
height: "100%",
|
|
},
|
|
rowText: {
|
|
fontSize: 16,
|
|
color: Palette.white,
|
|
fontFamily: FONT_FAMILY.InterRegular,
|
|
},
|
|
handleIcon: {
|
|
width: 22,
|
|
height: 22,
|
|
tintColor: Palette.white,
|
|
},
|
|
});
|