improvemtns again, to the archetype builder, and playback
This commit is contained in:
@@ -28,6 +28,7 @@ class MelodyInfo(BaseModel):
|
|||||||
color: str = ""
|
color: str = ""
|
||||||
isTrueRing: bool = False
|
isTrueRing: bool = False
|
||||||
previewURL: str = ""
|
previewURL: str = ""
|
||||||
|
archetype_csv: Optional[str] = None
|
||||||
|
|
||||||
|
|
||||||
class MelodyAttributes(BaseModel):
|
class MelodyAttributes(BaseModel):
|
||||||
|
|||||||
@@ -5,6 +5,25 @@ import { useAuth } from "../auth/AuthContext";
|
|||||||
import ConfirmDialog from "../components/ConfirmDialog";
|
import ConfirmDialog from "../components/ConfirmDialog";
|
||||||
import SpeedCalculatorModal from "./SpeedCalculatorModal";
|
import SpeedCalculatorModal from "./SpeedCalculatorModal";
|
||||||
import PlaybackModal from "./PlaybackModal";
|
import PlaybackModal from "./PlaybackModal";
|
||||||
|
|
||||||
|
function fallbackCopy(text, onSuccess) {
|
||||||
|
const ta = document.createElement("textarea");
|
||||||
|
ta.value = text;
|
||||||
|
ta.style.cssText = "position:fixed;top:0;left:0;opacity:0";
|
||||||
|
document.body.appendChild(ta);
|
||||||
|
ta.focus();
|
||||||
|
ta.select();
|
||||||
|
try { document.execCommand("copy"); onSuccess?.(); } catch (_) {}
|
||||||
|
document.body.removeChild(ta);
|
||||||
|
}
|
||||||
|
|
||||||
|
function copyText(text, onSuccess) {
|
||||||
|
if (navigator.clipboard) {
|
||||||
|
navigator.clipboard.writeText(text).then(onSuccess).catch(() => fallbackCopy(text, onSuccess));
|
||||||
|
} else {
|
||||||
|
fallbackCopy(text, onSuccess);
|
||||||
|
}
|
||||||
|
}
|
||||||
import {
|
import {
|
||||||
getLocalizedValue,
|
getLocalizedValue,
|
||||||
getLanguageName,
|
getLanguageName,
|
||||||
@@ -415,12 +434,7 @@ export default function MelodyDetail() {
|
|||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
<button
|
<button
|
||||||
onClick={() => {
|
onClick={() => copyText(builtMelody.progmem_code, () => { setCodeCopied(true); setTimeout(() => setCodeCopied(false), 2000); })}
|
||||||
navigator.clipboard.writeText(builtMelody.progmem_code).then(() => {
|
|
||||||
setCodeCopied(true);
|
|
||||||
setTimeout(() => setCodeCopied(false), 2000);
|
|
||||||
});
|
|
||||||
}}
|
|
||||||
className="px-3 py-1.5 text-xs rounded transition-colors"
|
className="px-3 py-1.5 text-xs rounded transition-colors"
|
||||||
style={{
|
style={{
|
||||||
backgroundColor: codeCopied ? "var(--success-bg)" : "var(--bg-card-hover)",
|
backgroundColor: codeCopied ? "var(--success-bg)" : "var(--bg-card-hover)",
|
||||||
@@ -453,6 +467,7 @@ export default function MelodyDetail() {
|
|||||||
melody={melody}
|
melody={melody}
|
||||||
builtMelody={builtMelody}
|
builtMelody={builtMelody}
|
||||||
files={files}
|
files={files}
|
||||||
|
archetypeCsv={melody?.information?.archetype_csv || null}
|
||||||
onClose={() => setShowPlayback(false)}
|
onClose={() => setShowPlayback(false)}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
@@ -460,6 +475,7 @@ export default function MelodyDetail() {
|
|||||||
open={showSpeedCalc}
|
open={showSpeedCalc}
|
||||||
melody={melody}
|
melody={melody}
|
||||||
builtMelody={builtMelody}
|
builtMelody={builtMelody}
|
||||||
|
archetypeCsv={melody?.information?.archetype_csv || null}
|
||||||
onClose={() => setShowSpeedCalc(false)}
|
onClose={() => setShowSpeedCalc(false)}
|
||||||
onSaved={() => {
|
onSaved={() => {
|
||||||
setShowSpeedCalc(false);
|
setShowSpeedCalc(false);
|
||||||
|
|||||||
@@ -638,12 +638,14 @@ export default function MelodyForm() {
|
|||||||
open={showSpeedCalc}
|
open={showSpeedCalc}
|
||||||
melody={{ id, information, default_settings: settings, type, url, uid, pid }}
|
melody={{ id, information, default_settings: settings, type, url, uid, pid }}
|
||||||
builtMelody={builtMelody}
|
builtMelody={builtMelody}
|
||||||
|
archetypeCsv={information.archetype_csv || null}
|
||||||
onClose={() => setShowSpeedCalc(false)}
|
onClose={() => setShowSpeedCalc(false)}
|
||||||
onSaved={() => { setShowSpeedCalc(false); loadMelody(); }}
|
onSaved={() => { setShowSpeedCalc(false); loadMelody(); }}
|
||||||
/>
|
/>
|
||||||
<SelectBuiltMelodyModal
|
<SelectBuiltMelodyModal
|
||||||
open={showSelectBuilt}
|
open={showSelectBuilt}
|
||||||
melodyId={id}
|
melodyId={id}
|
||||||
|
currentMelody={{ information, default_settings: settings, type, url, uid, pid }}
|
||||||
onClose={() => setShowSelectBuilt(false)}
|
onClose={() => setShowSelectBuilt(false)}
|
||||||
onSuccess={(archetype) => {
|
onSuccess={(archetype) => {
|
||||||
setShowSelectBuilt(false);
|
setShowSelectBuilt(false);
|
||||||
@@ -655,6 +657,7 @@ export default function MelodyForm() {
|
|||||||
<BuildOnTheFlyModal
|
<BuildOnTheFlyModal
|
||||||
open={showBuildOnTheFly}
|
open={showBuildOnTheFly}
|
||||||
melodyId={id}
|
melodyId={id}
|
||||||
|
currentMelody={{ information, default_settings: settings, type, url, uid, pid }}
|
||||||
defaultName={getLocalizedValue(information.name, "en", "") || getLocalizedValue(information.name, editLang, "")}
|
defaultName={getLocalizedValue(information.name, "en", "") || getLocalizedValue(information.name, editLang, "")}
|
||||||
defaultPid={pid}
|
defaultPid={pid}
|
||||||
onClose={() => setShowBuildOnTheFly(false)}
|
onClose={() => setShowBuildOnTheFly(false)}
|
||||||
|
|||||||
@@ -42,6 +42,22 @@ function getActiveBells(stepValue) {
|
|||||||
return bells;
|
return bells;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function parseBellNotation(notation) {
|
||||||
|
notation = notation.trim();
|
||||||
|
if (notation === "0" || !notation) return 0;
|
||||||
|
let value = 0;
|
||||||
|
for (const part of notation.split("+")) {
|
||||||
|
const n = parseInt(part.trim(), 10);
|
||||||
|
if (!isNaN(n) && n >= 1 && n <= 16) value |= 1 << (n - 1);
|
||||||
|
}
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
|
||||||
|
function parseStepsString(stepsStr) {
|
||||||
|
if (!stepsStr || !stepsStr.trim()) return [];
|
||||||
|
return stepsStr.trim().split(",").map((s) => parseBellNotation(s));
|
||||||
|
}
|
||||||
|
|
||||||
async function decodeBsmBinary(url) {
|
async function decodeBsmBinary(url) {
|
||||||
const token = localStorage.getItem("access_token");
|
const token = localStorage.getItem("access_token");
|
||||||
const res = await fetch(url, {
|
const res = await fetch(url, {
|
||||||
@@ -81,7 +97,7 @@ const BEAT_DURATION_MS = 80; // fixed tone length for playback
|
|||||||
const mutedStyle = { color: "var(--text-muted)" };
|
const mutedStyle = { color: "var(--text-muted)" };
|
||||||
const labelStyle = { color: "var(--text-secondary)" };
|
const labelStyle = { color: "var(--text-secondary)" };
|
||||||
|
|
||||||
export default function PlaybackModal({ open, melody, builtMelody, files, onClose }) {
|
export default function PlaybackModal({ open, melody, builtMelody, files, archetypeCsv, onClose }) {
|
||||||
const info = melody?.information || {};
|
const info = melody?.information || {};
|
||||||
const minSpeed = info.minSpeed || null;
|
const minSpeed = info.minSpeed || null;
|
||||||
const maxSpeed = info.maxSpeed || null;
|
const maxSpeed = info.maxSpeed || null;
|
||||||
@@ -115,7 +131,7 @@ export default function PlaybackModal({ open, melody, builtMelody, files, onClos
|
|||||||
setCurrentStep(-1);
|
setCurrentStep(-1);
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
// Load binary on open
|
// Load steps on open — prefer archetype_csv, fall back to binary
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (!open) {
|
if (!open) {
|
||||||
stopPlayback();
|
stopPlayback();
|
||||||
@@ -126,14 +142,23 @@ export default function PlaybackModal({ open, melody, builtMelody, files, onClos
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// builtMelody.binary_url is a relative path needing /api prefix;
|
// Prefer CSV from archetype (no network request needed)
|
||||||
// files.binary_url from the /files endpoint is already a full URL path.
|
const csv = archetypeCsv || info.archetype_csv || null;
|
||||||
|
if (csv) {
|
||||||
|
const parsed = parseStepsString(csv);
|
||||||
|
setSteps(parsed);
|
||||||
|
stepsRef.current = parsed;
|
||||||
|
setLoadError("");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Fall back to binary fetch
|
||||||
const binaryUrl = builtMelody?.binary_url
|
const binaryUrl = builtMelody?.binary_url
|
||||||
? `/api${builtMelody.binary_url}`
|
? `/api${builtMelody.binary_url}`
|
||||||
: files?.binary_url || null;
|
: files?.binary_url || null;
|
||||||
|
|
||||||
if (!binaryUrl) {
|
if (!binaryUrl) {
|
||||||
setLoadError("No binary file available for this melody.");
|
setLoadError("No binary or archetype data available for this melody.");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -115,7 +115,7 @@ const DELAY_MAX = 3000;
|
|||||||
function delayToSlider(ms) { return DELAY_MIN + DELAY_MAX - ms; }
|
function delayToSlider(ms) { return DELAY_MIN + DELAY_MAX - ms; }
|
||||||
function sliderToDelay(val) { return DELAY_MIN + DELAY_MAX - val; }
|
function sliderToDelay(val) { return DELAY_MIN + DELAY_MAX - val; }
|
||||||
|
|
||||||
export default function SpeedCalculatorModal({ open, melody, builtMelody, onClose, onSaved }) {
|
export default function SpeedCalculatorModal({ open, melody, builtMelody, archetypeCsv, onClose, onSaved }) {
|
||||||
const info = melody?.information || {};
|
const info = melody?.information || {};
|
||||||
|
|
||||||
// Raw steps input
|
// Raw steps input
|
||||||
@@ -164,19 +164,28 @@ export default function SpeedCalculatorModal({ open, melody, builtMelody, onClos
|
|||||||
const maxWarning = capturedMax !== null && capturedMax < 100;
|
const maxWarning = capturedMax !== null && capturedMax < 100;
|
||||||
const orderWarning = capturedMax !== null && capturedNormal !== null && capturedNormal < capturedMax;
|
const orderWarning = capturedMax !== null && capturedNormal !== null && capturedNormal < capturedMax;
|
||||||
|
|
||||||
// Reset on open
|
// Reset on open — auto-load archetype CSV if available
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (open) {
|
if (open) {
|
||||||
setCapturedMax(info.maxSpeed > 0 ? info.maxSpeed : null);
|
setCapturedMax(info.maxSpeed > 0 ? info.maxSpeed : null);
|
||||||
setCapturedNormal(null);
|
setCapturedNormal(null);
|
||||||
setStepsInput("");
|
|
||||||
setSteps([]);
|
|
||||||
setBinaryLoadError("");
|
setBinaryLoadError("");
|
||||||
setCurrentStep(-1);
|
setCurrentStep(-1);
|
||||||
setPlaying(false);
|
setPlaying(false);
|
||||||
setPaused(false);
|
setPaused(false);
|
||||||
setSaveError("");
|
setSaveError("");
|
||||||
setSaveSuccess(false);
|
setSaveSuccess(false);
|
||||||
|
|
||||||
|
const csv = archetypeCsv || info.archetype_csv || null;
|
||||||
|
if (csv) {
|
||||||
|
const parsed = parseStepsString(csv);
|
||||||
|
setStepsInput(csv);
|
||||||
|
setSteps(parsed);
|
||||||
|
stepsRef.current = parsed;
|
||||||
|
} else {
|
||||||
|
setStepsInput("");
|
||||||
|
setSteps([]);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}, [open]); // eslint-disable-line react-hooks/exhaustive-deps
|
}, [open]); // eslint-disable-line react-hooks/exhaustive-deps
|
||||||
|
|
||||||
|
|||||||
@@ -10,7 +10,20 @@ function countSteps(stepsStr) {
|
|||||||
return stepsStr.trim().split(",").length;
|
return stepsStr.trim().split(",").length;
|
||||||
}
|
}
|
||||||
|
|
||||||
export default function BuildOnTheFlyModal({ open, melodyId, defaultName, defaultPid, onClose, onSuccess }) {
|
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 BuildOnTheFlyModal({ open, melodyId, currentMelody, defaultName, defaultPid, onClose, onSuccess }) {
|
||||||
const [name, setName] = useState(defaultName || "");
|
const [name, setName] = useState(defaultName || "");
|
||||||
const [pid, setPid] = useState(defaultPid || "");
|
const [pid, setPid] = useState(defaultPid || "");
|
||||||
const [steps, setSteps] = useState("");
|
const [steps, setSteps] = useState("");
|
||||||
@@ -57,8 +70,29 @@ export default function BuildOnTheFlyModal({ open, melodyId, defaultName, defaul
|
|||||||
setStatusMsg("Linking to melody...");
|
setStatusMsg("Linking to melody...");
|
||||||
await api.post(`/builder/melodies/${builtId}/assign?firestore_melody_id=${melodyId}`);
|
await api.post(`/builder/melodies/${builtId}/assign?firestore_melody_id=${melodyId}`);
|
||||||
|
|
||||||
|
// Step 5: Update the melody's information with archetype_csv, steps, and totalNotes
|
||||||
|
setStatusMsg("Saving archetype data...");
|
||||||
|
const csv = steps.trim();
|
||||||
|
const { steps: stepCount, totalNotes } = computeStepsAndNotes(csv);
|
||||||
|
if (currentMelody && csv) {
|
||||||
|
const existingInfo = currentMelody.information || {};
|
||||||
|
await api.put(`/melodies/${melodyId}`, {
|
||||||
|
information: {
|
||||||
|
...existingInfo,
|
||||||
|
archetype_csv: csv,
|
||||||
|
steps: stepCount,
|
||||||
|
totalNotes,
|
||||||
|
},
|
||||||
|
default_settings: currentMelody.default_settings,
|
||||||
|
type: currentMelody.type,
|
||||||
|
url: currentMelody.url,
|
||||||
|
uid: currentMelody.uid,
|
||||||
|
pid: currentMelody.pid,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
setStatusMsg("Done!");
|
setStatusMsg("Done!");
|
||||||
onSuccess({ name: name.trim(), pid: pid.trim() });
|
onSuccess({ name: name.trim(), pid: pid.trim(), steps: stepCount, totalNotes, archetype_csv: csv });
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
setError(err.message);
|
setError(err.message);
|
||||||
setStatusMsg("");
|
setStatusMsg("");
|
||||||
|
|||||||
@@ -7,6 +7,24 @@ const labelStyle = { color: "var(--text-secondary)" };
|
|||||||
const mutedStyle = { color: "var(--text-muted)" };
|
const mutedStyle = { color: "var(--text-muted)" };
|
||||||
const inputClass = "w-full px-3 py-2 rounded-md text-sm border";
|
const inputClass = "w-full px-3 py-2 rounded-md text-sm border";
|
||||||
|
|
||||||
|
function copyText(text, onSuccess) {
|
||||||
|
const fallback = () => {
|
||||||
|
const ta = document.createElement("textarea");
|
||||||
|
ta.value = text;
|
||||||
|
ta.style.cssText = "position:fixed;top:0;left:0;opacity:0";
|
||||||
|
document.body.appendChild(ta);
|
||||||
|
ta.focus();
|
||||||
|
ta.select();
|
||||||
|
try { document.execCommand("copy"); onSuccess?.(); } catch (_) {}
|
||||||
|
document.body.removeChild(ta);
|
||||||
|
};
|
||||||
|
if (navigator.clipboard) {
|
||||||
|
navigator.clipboard.writeText(text).then(onSuccess).catch(fallback);
|
||||||
|
} else {
|
||||||
|
fallback();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
function countSteps(stepsStr) {
|
function countSteps(stepsStr) {
|
||||||
if (!stepsStr || !stepsStr.trim()) return 0;
|
if (!stepsStr || !stepsStr.trim()) return 0;
|
||||||
return stepsStr.trim().split(",").length;
|
return stepsStr.trim().split(",").length;
|
||||||
@@ -128,10 +146,7 @@ export default function BuilderForm() {
|
|||||||
|
|
||||||
const handleCopy = () => {
|
const handleCopy = () => {
|
||||||
if (!progmemCode) return;
|
if (!progmemCode) return;
|
||||||
navigator.clipboard.writeText(progmemCode).then(() => {
|
copyText(progmemCode, () => { setCopied(true); setTimeout(() => setCopied(false), 2000); });
|
||||||
setCopied(true);
|
|
||||||
setTimeout(() => setCopied(false), 2000);
|
|
||||||
});
|
|
||||||
};
|
};
|
||||||
|
|
||||||
if (loading) {
|
if (loading) {
|
||||||
|
|||||||
@@ -3,6 +3,25 @@ import { useNavigate } from "react-router-dom";
|
|||||||
import api from "../../api/client";
|
import api from "../../api/client";
|
||||||
import ConfirmDialog from "../../components/ConfirmDialog";
|
import ConfirmDialog from "../../components/ConfirmDialog";
|
||||||
|
|
||||||
|
function fallbackCopy(text, onSuccess) {
|
||||||
|
const ta = document.createElement("textarea");
|
||||||
|
ta.value = text;
|
||||||
|
ta.style.cssText = "position:fixed;top:0;left:0;opacity:0";
|
||||||
|
document.body.appendChild(ta);
|
||||||
|
ta.focus();
|
||||||
|
ta.select();
|
||||||
|
try { document.execCommand("copy"); onSuccess?.(); } catch (_) {}
|
||||||
|
document.body.removeChild(ta);
|
||||||
|
}
|
||||||
|
|
||||||
|
function copyText(text, onSuccess) {
|
||||||
|
if (navigator.clipboard) {
|
||||||
|
navigator.clipboard.writeText(text).then(onSuccess).catch(() => fallbackCopy(text, onSuccess));
|
||||||
|
} else {
|
||||||
|
fallbackCopy(text, onSuccess);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
function CodeSnippetModal({ melody, onClose }) {
|
function CodeSnippetModal({ melody, onClose }) {
|
||||||
const [copied, setCopied] = useState(false);
|
const [copied, setCopied] = useState(false);
|
||||||
if (!melody) return null;
|
if (!melody) return null;
|
||||||
@@ -23,12 +42,7 @@ function CodeSnippetModal({ melody, onClose }) {
|
|||||||
</div>
|
</div>
|
||||||
<div className="flex items-center gap-3">
|
<div className="flex items-center gap-3">
|
||||||
<button
|
<button
|
||||||
onClick={() => {
|
onClick={() => copyText(melody.progmem_code, () => { setCopied(true); setTimeout(() => setCopied(false), 2000); })}
|
||||||
navigator.clipboard.writeText(melody.progmem_code).then(() => {
|
|
||||||
setCopied(true);
|
|
||||||
setTimeout(() => setCopied(false), 2000);
|
|
||||||
});
|
|
||||||
}}
|
|
||||||
className="px-3 py-1.5 text-xs rounded transition-colors"
|
className="px-3 py-1.5 text-xs rounded transition-colors"
|
||||||
style={{
|
style={{
|
||||||
backgroundColor: copied ? "var(--success-bg)" : "var(--bg-card-hover)",
|
backgroundColor: copied ? "var(--success-bg)" : "var(--bg-card-hover)",
|
||||||
|
|||||||
@@ -1,7 +1,20 @@
|
|||||||
import { useState, useEffect } from "react";
|
import { useState, useEffect } from "react";
|
||||||
import api from "../../api/client";
|
import api from "../../api/client";
|
||||||
|
|
||||||
export default function SelectBuiltMelodyModal({ open, melodyId, onClose, onSuccess }) {
|
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 [melodies, setMelodies] = useState([]);
|
||||||
const [loading, setLoading] = useState(false);
|
const [loading, setLoading] = useState(false);
|
||||||
const [assigning, setAssigning] = useState(null); // id of the one being assigned
|
const [assigning, setAssigning] = useState(null); // id of the one being assigned
|
||||||
@@ -44,7 +57,27 @@ export default function SelectBuiltMelodyModal({ open, melodyId, onClose, onSucc
|
|||||||
// 3. Mark this built melody as assigned to this Firestore melody
|
// 3. Mark this built melody as assigned to this Firestore melody
|
||||||
await api.post(`/builder/melodies/${builtMelody.id}/assign?firestore_melody_id=${melodyId}`);
|
await api.post(`/builder/melodies/${builtMelody.id}/assign?firestore_melody_id=${melodyId}`);
|
||||||
|
|
||||||
onSuccess({ name: builtMelody.name, pid: builtMelody.pid });
|
// 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) {
|
} catch (err) {
|
||||||
setError(err.message);
|
setError(err.message);
|
||||||
} finally {
|
} finally {
|
||||||
|
|||||||
Reference in New Issue
Block a user