diff --git a/App.js b/App.js index f896111..5d78051 100644 --- a/App.js +++ b/App.js @@ -89,6 +89,27 @@ const App = () => { return subscriber; }, []); + useEffect(() => { + if (Platform.OS === "web") { + const s = document.createElement("script"); + s.src = "https://minuit.app/embed/report-a-problem.js"; + s.async = true; + s.setAttribute("data-project-id", "playbackproduction"); + s.setAttribute("data-position", "right"); + s.setAttribute("data-offset", "16"); + s.setAttribute("data-primary-color", "#FB68A8"); + s.setAttribute("data-bg-primary-color", "#0E0E12"); + s.setAttribute("data-bg-secondary-color", "#1D1825"); + s.setAttribute("data-text-color", "#F3F0F5"); + + document.body.appendChild(s); + return () => { + try { + s.remove(); + } catch (_) {} + }; + } + }, []); const onAuthStateChanged = async (user) => { if (isInitializing) { setInitializing(false); @@ -112,7 +133,6 @@ const App = () => { if (!loaded || isInitializing) { return null; } - return ( <> diff --git a/src/screens/Writing/CustomizeSongStructure.web.js b/src/screens/Writing/CustomizeSongStructure.web.js new file mode 100644 index 0000000..79b0975 --- /dev/null +++ b/src/screens/Writing/CustomizeSongStructure.web.js @@ -0,0 +1,145 @@ +import React, { useCallback, useEffect, useMemo, useState } from "react"; +import { Image, StyleSheet, View, Text } from "react-native"; +import Animated, { useAnimatedRef } from "react-native-reanimated"; +import Sortable from "react-native-sortables"; +import CreateLyricsHeader from "./components/CreateLyricsHeader"; +import { Palette } from "../../styles"; +import { FONT_FAMILY } from "../../styles/Fonts"; +import { icons } from "../../assets"; + +const VALID_SEGMENTS = ["couplet", "refrain"]; + +const sanitizeStructure = (structure = []) => + structure + .map((segment) => `${segment}`.trim().toLowerCase()) + .filter((segment) => VALID_SEGMENTS.includes(segment)); + +const buildItems = (structure = []) => { + const counters = { couplet: 0, refrain: 0 }; + return structure.map((segment, index) => { + counters[segment] += 1; + const label = `${segment === "couplet" ? "Couplet" : "Refrain"} ${counters[segment]}`; + return { + id: `${segment}-${index}-${counters[segment]}`, + value: segment, + label, + }; + }); +}; + +const CustomizeSongStructure = ({ baseStructure = [], onChange }) => { + const sanitized = useMemo(() => sanitizeStructure(baseStructure), [baseStructure]); + const [items, setItems] = useState(() => buildItems(sanitized)); + const scrollRef = useAnimatedRef(); + + useEffect(() => { + setItems((prev) => { + if ( + prev.length === sanitized.length && + prev.every((item, index) => item.value === sanitized[index]) + ) { + return prev; + } + return buildItems(sanitized); + }); + }, [sanitized]); + + useEffect(() => { + onChange?.(items.map((item) => item.value)); + }, [items, onChange]); + + const renderItem = useCallback(({ item }) => { + return ( + + + {item.label} + + + + + + ); + }, []); + + if (!items.length) { + return ( + + + + ); + } + + return ( + + + + + item.id} + renderItem={renderItem} + rowGap={12} + customHandle + scrollableRef={scrollRef} + onDragEnd={({ data: nextItems }) => { + const nextOrder = nextItems.map((item) => item.value); + setItems(buildItems(nextOrder)); + }} + /> + + + + ); +}; + +export default CustomizeSongStructure; + +const styles = StyleSheet.create({ + listContainer: { + flex: 1, + }, + scrollContent: { + paddingVertical: 4, + paddingBottom: 24, + }, + itemContainer: { + height: 54, + borderRadius: 14, + backgroundColor: Palette.glass, + shadowColor: "#00000040", + shadowOffset: { width: 0, height: 2 }, + shadowOpacity: 0.25, + shadowRadius: 3.84, + elevation: 5, + overflow: "hidden", + }, + row: { + flex: 1, + paddingHorizontal: 16, + flexDirection: "row", + alignItems: "center", + justifyContent: "space-between", + }, + rowText: { + fontSize: 16, + color: Palette.white, + fontFamily: FONT_FAMILY.InterRegular, + }, + handleIcon: { + width: 22, + height: 22, + tintColor: Palette.white, + cursor: "grab", + }, +}); diff --git a/src/screens/Writing/WritingLyrics.js b/src/screens/Writing/WritingLyrics.js index 92ce1e1..371287e 100644 --- a/src/screens/Writing/WritingLyrics.js +++ b/src/screens/Writing/WritingLyrics.js @@ -1,16 +1,16 @@ -import { View, Image, StyleSheet } from "react-native"; import React, { useEffect, useState } from "react"; -import Page from "../../layouts/Page"; -import { ai } from "../../assets"; -import GradientButton from "../../components/GradientButton"; -import { gutters } from "../../styles"; -import BorderGradientButton from "../../components/BorderGradientButton"; -import MusicLandHeader from "../../components/MusicLandHeader"; -import { goBack, navigate } from "../../navigation/NavigationService"; -import { Routes } from "../../navigation"; -import { useUserData } from "../../providers/UserDataProvider"; +import { Image, StyleSheet, View } from "react-native"; import useMinuit from "react-native-minuit/src/hooks/useMinuit.js"; -import FullscreenIntroVideo from "../../components/FullscreenIntroVideo"; +import { ai } from "../../assets"; +import BorderGradientButton from "../../components/BorderGradientButton"; +import FullscreenIntroVideo from "../../components/FullscreenIntroVideo.web"; +import GradientButton from "../../components/GradientButton"; +import MusicLandHeader from "../../components/MusicLandHeader"; +import Page from "../../layouts/Page"; +import { Routes } from "../../navigation"; +import { goBack, navigate } from "../../navigation/NavigationService"; +import { useUserData } from "../../providers/UserDataProvider"; +import { gutters } from "../../styles"; const WritingLyrics = () => { const { createNewProject, selectedProject, updateProjectData } =