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 } =