import { useState, useEffect } from "react"; import api from "../../api/client"; function computeStepsAndNotes(stepsStr) { if (!stepsStr || !stepsStr.trim()) return { steps: 0, totalNotes: 0 }; const tokens = stepsStr.trim().split(","); const bellSet = new Set(); for (const token of tokens) { for (const part of token.split("+")) { const n = parseInt(part.trim(), 10); if (!isNaN(n) && n >= 1 && n <= 16) bellSet.add(n); } } return { steps: tokens.length, totalNotes: bellSet.size || 1 }; } export default function SelectBuiltMelodyModal({ open, melodyId, currentMelody, onClose, onSuccess }) { const [melodies, setMelodies] = useState([]); const [loading, setLoading] = useState(false); const [assigning, setAssigning] = useState(null); // id of the one being assigned const [error, setError] = useState(""); useEffect(() => { if (open) loadMelodies(); }, [open]); const loadMelodies = async () => { setLoading(true); setError(""); try { const data = await api.get("/builder/melodies"); // Only show those with a built binary setMelodies((data.melodies || []).filter((m) => m.binary_path)); } catch (err) { setError(err.message); } finally { setLoading(false); } }; const handleSelect = async (builtMelody) => { setAssigning(builtMelody.id); setError(""); try { // 1. Fetch the .bsm file from the builder endpoint const token = localStorage.getItem("access_token"); const res = await fetch(`/api${builtMelody.binary_url}`, { headers: token ? { Authorization: `Bearer ${token}` } : {}, }); if (!res.ok) throw new Error(`Failed to download binary: ${res.statusText}`); const blob = await res.blob(); const file = new File([blob], `${builtMelody.name}.bsm`, { type: "application/octet-stream" }); // 2. Upload to Firebase Storage via the existing melody upload endpoint await api.upload(`/melodies/${melodyId}/upload/binary`, file); // 3. Mark this built melody as assigned to this Firestore melody await api.post(`/builder/melodies/${builtMelody.id}/assign?firestore_melody_id=${melodyId}`); // 4. Update the melody's information with archetype_csv, steps, and totalNotes const csv = builtMelody.steps || ""; const { steps, totalNotes } = computeStepsAndNotes(csv); if (currentMelody && csv) { const existingInfo = currentMelody.information || {}; await api.put(`/melodies/${melodyId}`, { information: { ...existingInfo, archetype_csv: csv, steps, totalNotes, }, default_settings: currentMelody.default_settings, type: currentMelody.type, url: currentMelody.url, uid: currentMelody.uid, pid: currentMelody.pid, }); } onSuccess({ name: builtMelody.name, pid: builtMelody.pid, steps, totalNotes, archetype_csv: csv }); } catch (err) { setError(err.message); } finally { setAssigning(null); } }; if (!open) return null; return (
{m.name}
PID: {m.pid || "—"} · {m.steps?.split(",").length || 0} steps