Skip to content

Commit ac848ba

Browse files
authored
sound-editor.jsx -- fix weird React timing issue
1 parent 5d3a415 commit ac848ba

File tree

1 file changed

+118
-100
lines changed

1 file changed

+118
-100
lines changed

src/containers/sound-editor.jsx

Lines changed: 118 additions & 100 deletions
Original file line numberDiff line numberDiff line change
@@ -522,69 +522,78 @@ class SoundEditor extends React.Component {
522522
{ name: "Cancel", callback: () => audio.close() },
523523
);
524524

525-
menu.setAttribute("style", "margin: 0 10px 15px 10px;position: relative;display: flex;justify-content: flex-end;flex-direction: row;height: calc(100% - (3.125em + 2.125em + 16px));align-items: center;");
526-
menu.append(pitchDiv, volumeDiv);
527-
528-
const previewButton = document.createElement("button");
529-
previewButton.style = "border-radius: 1000px;padding: 5px;width: 45px;height: 45px;margin-right: 10px;border-style: none;background: #00c3ff;";
530-
previewButton.innerHTML = `<img draggable="false" style="max-width: 100%;max-height: 100%" src="${playURI}">`;
531-
menu.append(previewButton);
532-
533-
// preview functionality
534-
// create an audio buffer using the selection
535-
const properBuffer = audio.createBuffer(1, bufferSelection.samples.length, bufferSelection.sampleRate);
536-
properBuffer.getChannelData(0).set(bufferSelection.samples);
537-
538-
let bufferSource, audioPlaying = false;
539-
function play() {
540-
bufferSource = audio.createBufferSource();
541-
bufferSource.connect(gainNode);
542-
bufferSource.buffer = properBuffer;
543-
bufferSource.start(0);
544-
bufferSource.detune.value = pitchParts[1].value * 10;
545-
previewButton.innerHTML = `<img draggable="false" style="max-width: 100%;max-height: 100%" src="${stopURI}">`;
546-
audioPlaying = true;
547-
bufferSource.onended = () => {
548-
previewButton.firstChild.src = playURI;
525+
const modalHandler = () => {
526+
menu.setAttribute("style", "margin: 0 10px 15px 10px;position: relative;display: flex;justify-content: flex-end;flex-direction: row;height: calc(100% - (3.125em + 2.125em + 16px));align-items: center;");
527+
menu.append(pitchDiv, volumeDiv);
528+
529+
const previewButton = document.createElement("button");
530+
previewButton.style = "border-radius: 1000px;padding: 5px;width: 45px;height: 45px;margin-right: 10px;border-style: none;background: #00c3ff;";
531+
previewButton.innerHTML = `<img draggable="false" style="max-width: 100%;max-height: 100%" src="${playURI}">`;
532+
menu.append(previewButton);
533+
534+
// preview functionality
535+
// create an audio buffer using the selection
536+
const properBuffer = audio.createBuffer(1, bufferSelection.samples.length, bufferSelection.sampleRate);
537+
properBuffer.getChannelData(0).set(bufferSelection.samples);
538+
539+
let bufferSource, audioPlaying = false;
540+
function play() {
541+
bufferSource = audio.createBufferSource();
542+
bufferSource.connect(gainNode);
543+
bufferSource.buffer = properBuffer;
544+
bufferSource.start(0);
545+
bufferSource.detune.value = pitchParts[1].value * 10;
546+
previewButton.innerHTML = `<img draggable="false" style="max-width: 100%;max-height: 100%" src="${stopURI}">`;
547+
audioPlaying = true;
548+
bufferSource.onended = () => {
549+
previewButton.firstChild.src = playURI;
550+
audioPlaying = false;
551+
}
552+
}
553+
function stop() {
554+
bufferSource.stop();
555+
previewButton.firstChild.src = stopURI;
549556
audioPlaying = false;
550557
}
551-
}
552-
function stop() {
553-
bufferSource.stop();
554-
previewButton.firstChild.src = stopURI;
555-
audioPlaying = false;
556-
}
557-
previewButton.onclick = () => {
558-
if (audioPlaying) stop();
559-
else play();
560-
}
558+
previewButton.onclick = () => {
559+
if (audioPlaying) stop();
560+
else play();
561+
}
561562

562-
// slider/number updates
563-
const pSlider = pitchParts[1];
564-
const pNumber = pitchParts[2];
565-
pSlider.onchange = (updateValue) => {
566-
if (updateValue !== false) pNumber.value = Number(pSlider.value);
567-
if (bufferSource) bufferSource.detune.value = pSlider.value * 10;
568-
}
569-
pSlider.oninput = pSlider.onchange;
570-
pNumber.onchange = () => {
571-
pSlider.value = pNumber.value;
572-
pSlider.onchange(false);
573-
};
574-
pNumber.oninput = pNumber.onchange;
563+
// slider/number updates
564+
const pSlider = pitchParts[1];
565+
const pNumber = pitchParts[2];
566+
pSlider.onchange = (updateValue) => {
567+
if (updateValue !== false) pNumber.value = Number(pSlider.value);
568+
if (bufferSource) bufferSource.detune.value = pSlider.value * 10;
569+
}
570+
pSlider.oninput = pSlider.onchange;
571+
pNumber.onchange = () => {
572+
pSlider.value = pNumber.value;
573+
pSlider.onchange(false);
574+
};
575+
pNumber.oninput = pNumber.onchange;
575576

576-
const vSlider = volumeParts[1];
577-
const vNumber = volumeParts[2];
578-
vSlider.onchange = (updateValue) => {
579-
gainNode.gain.value = vSlider.value;
580-
if (updateValue !== false) vNumber.value = Number(vSlider.value) * 100;
581-
}
582-
vSlider.oninput = vSlider.onchange;
583-
vNumber.onchange = () => {
584-
vSlider.value = vNumber.value / 100;
585-
vSlider.onchange(false);
577+
const vSlider = volumeParts[1];
578+
const vNumber = volumeParts[2];
579+
vSlider.onchange = (updateValue) => {
580+
gainNode.gain.value = vSlider.value;
581+
if (updateValue !== false) vNumber.value = Number(vSlider.value) * 100;
582+
}
583+
vSlider.oninput = vSlider.onchange;
584+
vNumber.onchange = () => {
585+
vSlider.value = vNumber.value / 100;
586+
vSlider.onchange(false);
587+
};
588+
vNumber.oninput = vNumber.onchange;
586589
};
587-
vNumber.oninput = vNumber.onchange;
590+
591+
// account for weird react timing issue
592+
if (menu) modalHandler();
593+
else queueMicrotask(() => {
594+
menu = document.querySelector(`div[class="ReactModalPortal"] div[class*="prompt_body_"] div`);
595+
modalHandler();
596+
});
588597
}
589598

590599
handleFormatMenu() {
@@ -617,7 +626,7 @@ class SoundEditor extends React.Component {
617626
];
618627
let selectedSampleRate = this.props.sampleRate;
619628
let selectedForceRate = false;
620-
const menu = window.ScratchBlocks.customPrompt(
629+
let menu = window.ScratchBlocks.customPrompt(
621630
"Format Sound", { width: 350, height: "auto" },
622631
{
623632
name: "Apply", callback: () => {
@@ -629,50 +638,59 @@ class SoundEditor extends React.Component {
629638
{ name: "Cancel", callback: () => {} },
630639
);
631640

632-
menu.style.marginBottom = "15px";
633-
const rateTitle = genTitle("New Sample Rate:");
634-
635-
const rateSelector = document.createElement("select");
636-
rateSelector.style = "border-radius: 5px;text-align: center;margin-left: 10px;width: 50%;";
637-
for (const rate of sampleRates) {
638-
const option = document.createElement("option");
639-
option.value = rate;
640-
option.textContent = rate;
641-
rateSelector.append(option);
642-
}
643-
rateSelector.selectedIndex = sampleRates.indexOf(this.props.sampleRate);
644-
rateSelector.onchange = () => {
645-
selectedSampleRate = rateSelector.value;
641+
const modalHandler = () => {
642+
menu.style.marginBottom = "15px";
643+
const rateTitle = genTitle("New Sample Rate:");
644+
645+
const rateSelector = document.createElement("select");
646+
rateSelector.style = "border-radius: 5px;text-align: center;margin-left: 10px;width: 50%;";
647+
for (const rate of sampleRates) {
648+
const option = document.createElement("option");
649+
option.value = rate;
650+
option.textContent = rate;
651+
rateSelector.append(option);
652+
}
653+
rateSelector.selectedIndex = sampleRates.indexOf(this.props.sampleRate);
654+
rateSelector.onchange = () => {
655+
selectedSampleRate = rateSelector.value;
656+
};
657+
rateTitle.appendChild(rateSelector);
658+
659+
const warningDiv = document.createElement("div");
660+
warningDiv.style.marginBottom = "15px";
661+
const warning = document.createElement("i");
662+
warning.textContent = "Choosing a higher sample rate than the current rate will not make the existing audio higher quality";
663+
warning.style = "font-size:13px;opacity:0.5;";
664+
warningDiv.appendChild(warning);
665+
666+
const warningDiv2 = warning.cloneNode(true);
667+
warningDiv2.textContent = "If 'whole sound' is selected, all added audio will use the new sample rate";
668+
669+
const applicatorDiv = document.createElement("div");
670+
applicatorDiv.append(
671+
genCheckableLabel("this selection", "0", true),
672+
genCheckableLabel("whole sound", "1", false)
673+
);
674+
applicatorDiv.addEventListener("click", (e) => {
675+
const div = e.target.closest(`div[class="check-outer"]`);
676+
if (!div) return;
677+
678+
for (const checkable of Array.from(div.parentNode.children)) {
679+
checkable.firstChild.checked = false;
680+
}
681+
div.firstChild.checked = true;
682+
selectedForceRate = div.id == "1";
683+
e.stopPropagation();
684+
});
685+
menu.append(rateTitle, warningDiv, genTitle("Apply to:"), applicatorDiv, warningDiv2);
646686
};
647-
rateTitle.appendChild(rateSelector);
648-
649-
const warningDiv = document.createElement("div");
650-
warningDiv.style.marginBottom = "15px";
651-
const warning = document.createElement("i");
652-
warning.textContent = "Choosing a higher sample rate than the current rate will not make the existing audio higher quality";
653-
warning.style = "font-size:13px;opacity:0.5;";
654-
warningDiv.appendChild(warning);
655-
656-
const warningDiv2 = warning.cloneNode(true);
657-
warningDiv2.textContent = "If 'whole sound' is selected, all added audio will use the new sample rate";
658-
659-
const applicatorDiv = document.createElement("div");
660-
applicatorDiv.append(
661-
genCheckableLabel("this selection", "0", true),
662-
genCheckableLabel("whole sound", "1", false)
663-
);
664-
applicatorDiv.addEventListener("click", (e) => {
665-
const div = e.target.closest(`div[class="check-outer"]`);
666-
if (!div) return;
667687

668-
for (const checkable of Array.from(div.parentNode.children)) {
669-
checkable.firstChild.checked = false;
670-
}
671-
div.firstChild.checked = true;
672-
selectedForceRate = div.id == "1";
673-
e.stopPropagation();
688+
// account for weird react timing issue
689+
if (menu) modalHandler();
690+
else queueMicrotask(() => {
691+
menu = document.querySelector(`div[class="ReactModalPortal"] div[class*="prompt_body_"] div`);
692+
modalHandler();
674693
});
675-
menu.append(rateTitle, warningDiv, genTitle("Apply to:"), applicatorDiv, warningDiv2);
676694
}
677695
render() {
678696
const { effectTypes } = AudioEffects;

0 commit comments

Comments
 (0)