diff --git a/src/screens/Writing/CustomizeSongStructure.js b/src/screens/Writing/CustomizeSongStructure.js index 8168f25..7b20779 100644 --- a/src/screens/Writing/CustomizeSongStructure.js +++ b/src/screens/Writing/CustomizeSongStructure.js @@ -2,6 +2,7 @@ import React, { useCallback, useEffect, useMemo, useState } from 'react' import { FlatList, Image, StyleSheet, Text, TouchableOpacity, View } from 'react-native' import Animated, { useAnimatedRef } from 'react-native-reanimated' import Sortable from 'react-native-sortables' +import { useGlobal } from 'reactn' import { icons } from '../../assets' import { Palette } from '../../styles' import { FONT_FAMILY } from '../../styles/Fonts' @@ -21,6 +22,11 @@ const createSortableItem = (type, index = 0) => ({ type, value: type, }) +const PRE_REFRAIN_TYPE = 'pre_refrain_instrumental' +const INTRO_TYPES = new Set(['short_intro', 'long_intro', 'intro', 'introduction']) +const PRE_REFRAIN_ERROR_MESSAGE = + 'Impossible de positionner un pré-refrain ici. Place-le juste avant un refrain.' +const INTRO_ERROR_MESSAGE = "L'introduction musicale doit rester en première position." const SANITIZE_OPTIONS = { autoInjectPreChorus: false } const buildSortableItems = (values = []) => @@ -28,8 +34,36 @@ const buildSortableItems = (values = []) => createSortableItem(type, index) ) +const validateStructureRules = (values = []) => { + for (let index = 0; index < values.length; index += 1) { + const type = values[index] + if (type === PRE_REFRAIN_TYPE) { + const nextType = values[index + 1] + if (nextType !== 'refrain') { + return { + valid: false, + reason: 'pre_refrain_invalid_position', + message: PRE_REFRAIN_ERROR_MESSAGE, + } + } + } + } + + const introIndex = values.findIndex((type) => INTRO_TYPES.has(type)) + if (introIndex > 0) { + return { + valid: false, + reason: 'intro_must_be_first', + message: INTRO_ERROR_MESSAGE, + } + } + + return { valid: true } +} + const CustomizeSongStructure = ({ baseStructure = [], onChange }) => { const scrollRef = useAnimatedRef() + const [, setTooltip] = useGlobal('_tooltip') const sanitizedBase = useMemo( () => sanitizeStructureList(baseStructure, SANITIZE_OPTIONS), [baseStructure] @@ -39,6 +73,13 @@ const CustomizeSongStructure = ({ baseStructure = [], onChange }) => { [] ) const [sortableItems, setSortableItems] = useState(() => buildSortableItems(sanitizedBase)) + const showTooltip = useCallback( + (text, type = 'error') => { + if (!text) return + setTooltip({ text, type }) + }, + [setTooltip] + ) useEffect(() => { setSortableItems((prev) => { @@ -119,12 +160,32 @@ const CustomizeSongStructure = ({ baseStructure = [], onChange }) => { return buildSortableItems(sanitized) } - nextValues.push(type) + if (type === PRE_REFRAIN_TYPE) { + const firstRefrainIndex = nextValues.findIndex((value) => value === 'refrain') + if (firstRefrainIndex === -1) { + showTooltip(PRE_REFRAIN_ERROR_MESSAGE) + return prev + } + nextValues.splice(firstRefrainIndex, 0, type) + } else if (meta?.exclusiveGroup === 'intro') { + nextValues.unshift(type) + } else if (meta?.exclusiveGroup === 'outro') { + nextValues.push(type) + } else { + nextValues.push(type) + } + + const validation = validateStructureRules(nextValues) + if (!validation.valid) { + showTooltip(validation.message) + return prev + } + const sanitized = sanitizeStructureList(nextValues, SANITIZE_OPTIONS) return buildSortableItems(sanitized) }) }, - [extractValues] + [extractValues, showTooltip] ) const Row = ({ item: rowData, onPress }) => { @@ -201,6 +262,12 @@ const CustomizeSongStructure = ({ baseStructure = [], onChange }) => { scrollableRef={scrollRef} onDragEnd={({ data: newData }) => { const values = extractValues(newData) + const validation = validateStructureRules(values) + if (!validation.valid) { + showTooltip(validation.message) + setSortableItems((prev) => buildSortableItems(extractValues(prev))) + return + } const sanitized = sanitizeStructureList(values, SANITIZE_OPTIONS) setSortableItems(buildSortableItems(sanitized)) }}