v1.0.13 - botao de reset funcional para limpar as salas

This commit is contained in:
JotaChina committed 2025-11-15 15:36:39 -03:00
1 parent 611aec9ba1
commit c8323d56cf
9 files changed
+1593 -532

No files matched your search

+192 -29
View File
@@ -3,7 +3,7 @@
// -------------------relómm------------------------------------------------------
// IMPORTS & STATE
// -----------------------------------------------------------------------------
import { appState, resetProjectState } from "./state.js";
import { appState, saveStateToSession } from "./state.js";
import {
addTrackToState,
removeLastTrackFromState,
@@ -33,7 +33,12 @@ import {
restartAudioEditorIfPlaying, // 👈 Adicionado
} from "./audio/audio_audio.js";
import { parseMmpContent } from "./file.js";
import {
parseMmpContent,
handleLocalProjectReset,
syncPatternStateToServer,
BLANK_PROJECT_XML,
} from "./file.js";
import { renderAll, showToast } from "./ui.js"; // showToast()
import { updateStepUI, renderPatternEditor } from "./pattern/pattern_ui.js";
import { PORT_SOCK } from "./config.js";
@@ -167,12 +172,14 @@ socket.on("connect_error", (err) => {
socket.on("system_update", (data) => {
if (data.type === "RELOAD_SAMPLES") {
console.log(`[System Update] Recebida ordem para recarregar samples: ${data.message}`);
console.log(
`[System Update] Recebida ordem para recarregar samples: ${data.message}`
);
// Certifique-se de que esta função existe e faz o que é esperado:
// 1. Fetch dos novos manifestos (metadata/samples-manifest.json etc.)
// 2. Renderiza a interface do navegador de samples
loadAndRenderSampleBrowser();
loadAndRenderSampleBrowser();
}
// Se houver outras notificações, adicione-as aqui (ex: RELOAD_PROJECTS)
});
@@ -186,14 +193,40 @@ socket.on("load_project_state", async (projectXml) => {
if (isLoadingProject) return;
isLoadingProject = true;
try {
await parseMmpContent(projectXml);
await parseMmpContent(projectXml); //
renderAll();
showToast("🎵 Projeto carregado com sucesso", "success");
// --- INÍCIO DA CORREÇÃO ---
// Mova a lógica de snapshot para AQUI.
// Só peça um snapshot DEPOIS que o XML foi carregado
// e SE o XML (appState) não tiver áudio.
const hasAudio =
(appState.audio?.clips?.length || 0) > 0 ||
(appState.audio?.tracks?.length || 0) > 0;
if (!hasAudio && currentRoom) {
console.log(
"Projeto XML carregado, sem áudio. Pedindo snapshot de áudio..."
);
// O 'sendAction' agora é chamado daqui, não do joinRoom.
sendAction({ type: "AUDIO_SNAPSHOT_REQUEST" });
}
// --- FIM DA CORREÇÃO ---
} catch (e) {
console.error("Erro ao carregar projeto:", e);
showToast("❌ Erro ao carregar projeto", "error");
}
isLoadingProject = false;
// Desativa a flag de reset após um delay.
// Isso dá tempo para a "condição de corrida" (o snapshot sujo)
// ser recebida e ignorada.
setTimeout(() => {
if (appState.global.justReset) {
console.log("Socket: Limpando flag 'justReset'.");
appState.global.justReset = false;
}
}, 250); // 2.5 segundos de "janela de proteção"
// --- FIM DA CORREÇÃO ---
});
// -----------------------------------------------------------------------------
@@ -206,16 +239,6 @@ export function joinRoom() {
console.log(`Entrando na sala: ${currentRoom} como ${USER_NAME}`);
showToast(`🚪 Entrando na sala ${currentRoom}`, "info");
socket.emit("join_room", { roomName: currentRoom, userName: USER_NAME });
setTimeout(() => {
try {
const hasAudio =
(appState.audio?.clips?.length || 0) > 0 ||
(appState.audio?.tracks?.length || 0) > 0;
if (!hasAudio && currentRoom) {
sendAction({ type: "AUDIO_SNAPSHOT_REQUEST" });
}
} catch {}
}, 800);
} else {
console.warn("joinRoom() chamado, mas nenhuma sala encontrada na URL.");
showToast("⚠️ Nenhuma sala encontrada na URL", "warning");
@@ -498,6 +521,7 @@ async function handleActionBroadcast(action) {
const seekTime = action.seekTime ?? appState.audio.audioEditorSeekTime;
setTimeout(() => startAudioEditorPlayback(seekTime), delayMs);
break;
case "STOP_AUDIO_PLAYBACK":
setTimeout(
() => stopAudioEditorPlayback(action.rewind || false),
@@ -564,6 +588,8 @@ async function handleActionBroadcast(action) {
const modeText = newMode === "global" ? "Global 🌐" : "Local 🏠";
showToast(`${who} mudou modo para ${modeText}`, "info");
}
// Salva o estado localmente também!
saveStateToSession();
break;
}
// =================================================================
@@ -571,28 +597,80 @@ async function handleActionBroadcast(action) {
// =================================================================
// Estado Global
case "LOAD_PROJECT":
case "LOAD_PROJECT": //
isLoadingProject = true;
showToast("📂 Carregando...", "info");
// Esta parte está CORRETA. Sempre limpa o sessionStorage.
if (window.ROOM_NAME) {
try {
sessionStorage.removeItem(`temp_state_${window.ROOM_NAME}`);
console.log(
"Socket: Estado da sessão local limpo para LOAD_PROJECT."
);
} catch (e) {
console.error("Socket: Falha ao limpar estado da sessão:", e);
}
}
try {
await parseMmpContent(action.xml);
await parseMmpContent(action.xml); //
renderAll();
showToast("🎶 Projeto sync", "success");
// --- INÍCIO DA CORREÇÃO ---
// O problema é esta chamada. Ela cria um "eco" (SYNC_PATTERN_STATE)
// que força um segundo 'parseMmpContent' e quebra o estado.
// REMOVA ou COMENTE este bloco:
/*
if (window.ROOM_NAME) {
console.log(
"LOAD_PROJECT: Sincronizando novo estado com o servidor..."
);
syncPatternStateToServer(); //
}
*/
// EM VEZ DISSO, apenas salve o estado na sessão local.
// O `syncPatternStateToServer` agora é responsabilidade do servidor
// (que já recebeu o LOAD_PROJECT) ou de outras ações.
saveStateToSession();
// --- FIM DA CORREÇÃO ---
} catch (e) {
console.error("Erro LOAD_PROJECT:", e);
showToast("❌ Erro projeto", "error");
}
isLoadingProject = false;
break;
case "RESET_PROJECT":
resetProjectState();
document.getElementById("bpm-input").value = 140;
document.getElementById("bars-input").value = 1;
document.getElementById("compasso-a-input").value = 4;
document.getElementById("compasso-b-input").value = 4;
renderAll();
case "SYNC_PATTERN_STATE": //
// Esta ação agora só será recebida de *outros* usuários,
// ou quando o servidor enviar, não de você mesmo.
try {
await parseMmpContent(action.xml); //
renderAll();
saveStateToSession(); //
console.log("Socket: Pattern state sincronizado.");
} catch (e) {
console.error("Erro SYNC_PATTERN_STATE:", e);
showToast("❌ Erro sync pattern", "error");
}
break;
case "RESET_ROOM":
console.log("Socket: Recebendo comando de RESET_ROOM do servidor."); //
// --- INÍCIO DA CORREÇÃO ---
// Faltava esta linha para definir quem executou a ação
const who = actorOf(action);
showToast(`🧹 Reset por ${who}`, "warning");
// --- FIM DA CORREÇÃO ---
handleLocalProjectReset(); //
showToast(`🧹 Reset por ${who}`, "warning"); //
// Salva o estado localmente também!
saveStateToSession(); //
break;
// Configs
@@ -601,24 +679,35 @@ async function handleActionBroadcast(action) {
renderAll();
const who = actorOf(action);
showToast(`🕰 ${who} BPM ${action.value}`, "info");
// Salva o estado localmente também!
saveStateToSession();
break;
}
case "SET_BARS": {
document.getElementById("bars-input").value = action.value;
renderAll();
const who = actorOf(action);
showToast(`🕰 ${who} Compasso add`, "info");
// Salva o estado localmente também!
saveStateToSession();
break;
}
case "SET_TIMESIG_A":
document.getElementById("compasso-a-input").value = action.value;
renderAll();
showToast("Compasso alt", "info");
// Salva o estado localmente também!
saveStateToSession();
break;
case "SET_TIMESIG_B":
document.getElementById("compasso-b-input").value = action.value;
renderAll();
showToast("Compasso alt", "info");
// Salva o estado localmente também!
saveStateToSession();
break;
// Tracks
@@ -627,15 +716,21 @@ async function handleActionBroadcast(action) {
renderPatternEditor();
const who = actorOf(action);
showToast(`🥁 Faixa add por ${who}`, "info");
// Salva o estado localmente também!
saveStateToSession();
break;
}
case "REMOVE_LAST_TRACK": {
removeLastTrackFromState();
renderPatternEditor();
const who = actorOf(action);
showToast(`❌ Faixa remov. por ${who}`, "warning");
// Salva o estado localmente também!
saveStateToSession();
break;
}
case "ADD_AUDIO_LANE": {
const id = action.trackId;
if (!id) {
@@ -654,14 +749,19 @@ async function handleActionBroadcast(action) {
renderAll();
const who = actorOf(action);
showToast(`🎧 Pista add por ${who}`, "info");
// Salva o estado localmente também!
saveStateToSession();
break;
}
case "REMOVE_AUDIO_CLIP":
if (removeAudioClip(action.clipId)) {
appState.global.selectedClipId = null;
renderAll();
const who = actorOf(action);
showToast(`🎚️ Clip remov. por ${who}`, "info");
// Salva o estado localmente também!
saveStateToSession();
}
break;
@@ -687,6 +787,8 @@ async function handleActionBroadcast(action) {
const who = actorOf(action);
const v = isActive ? "+" : "-";
showToast(`🎯 ${who} ${v} nota ${ti + 1}.${pi + 1}.${si + 1}`, "info");
// Salva o estado localmente também!
saveStateToSession();
break;
}
@@ -707,6 +809,35 @@ async function handleActionBroadcast(action) {
showToast("❌ Erro sample", "error");
}
}
// Salva o estado localmente também!
saveStateToSession();
break;
}
case "SET_ACTIVE_PATTERN": {
// índice que veio do seletor global
const { patternIndex } = action;
// O correto é iterar em TODAS as tracks e definir o
// novo índice de pattern para CADA UMA delas.
appState.pattern.tracks.forEach((track) => {
track.activePatternIndex = patternIndex;
});
// Mostra o toast (só uma vez)
if (!isFromSelf) {
const who = actorOf(action);
showToast(`🔄 ${who} mudou para Pattern ${patternIndex + 1}`, "info");
}
// O renderAll() vai redesenhar tudo.
// O redrawSequencer() (que é chamado pelo renderAll)
// vai ler o 'track.activePatternIndex' (agora atualizado)
// para TODAS as tracks e desenhar o pattern correto.
renderAll();
// Salva o estado localmente também!
saveStateToSession();
break;
}
@@ -730,10 +861,17 @@ async function handleActionBroadcast(action) {
} catch (e) {
console.warn("Erro AUDIO_SNAPSHOT_REQUEST:", e);
}
// Salva o estado localmente também!
saveStateToSession();
break;
}
case "AUDIO_SNAPSHOT": {
if (action.__target && action.__target !== socket.id) break;
if (appState.global.justReset) {
console.warn("Socket: Snapshot de áudio ignorado (justReset=true).");
break; // Ignora o snapshot
}
const hasClips = (appState.audio?.clips?.length || 0) > 0;
if (hasClips) break;
try {
@@ -745,13 +883,26 @@ async function handleActionBroadcast(action) {
console.error("Erro AUDIO_SNAPSHOT:", e);
showToast("❌ Erro sync áudio", "error");
}
// Salva o estado localmente também!
saveStateToSession();
break;
}
// Clip Sync
case "ADD_AUDIO_CLIP": {
try {
const { filePath, trackId, startTimeInSeconds, clipId, name } = action;
// --- INÍCIO DA MODIFICAÇÃO (Passo 2) ---
// Agora extraímos o 'patternData' que o main.js enviou.
const {
filePath,
trackId,
startTimeInSeconds,
clipId,
name,
patternData,
} = action;
// --- FIM DA MODIFICAÇÃO ---
if (
appState.audio?.clips?.some((c) => String(c.id) === String(clipId))
) {
@@ -769,13 +920,20 @@ async function handleActionBroadcast(action) {
name: `Pista ${appState.audio.tracks.length + 1}`,
});
}
// --- INÍCIO DA MODIFICAÇÃO (Passo 2) ---
// Passamos o 'patternData' como o novo (sexto) argumento
// para a função que armazena o clipe no estado.
addAudioClipToTimeline(
filePath,
trackId,
startTimeInSeconds,
clipId,
name
name,
patternData // <-- AQUI ESTÁ A "PARTITURA"
);
// --- FIM DA MODIFICAÇÃO ---
renderAll();
const who = actorOf(action);
const track = appState.audio?.tracks?.find((t) => t.id === trackId);
@@ -790,8 +948,11 @@ async function handleActionBroadcast(action) {
console.error("Erro ADD_AUDIO_CLIP:", e);
showToast("❌ Erro add clip", "error");
}
// Salva o estado localmente também!
saveStateToSession();
break;
}
case "UPDATE_AUDIO_CLIP": {
try {
if (action.props?.__operation === "slice") {
@@ -806,6 +967,8 @@ async function handleActionBroadcast(action) {
console.error("Erro UPDATE_AUDIO_CLIP:", e);
showToast("❌ Erro att clip", "error");
}
// Salva o estado localmente também!
saveStateToSession();
break;
}
@@ -815,4 +978,4 @@ async function handleActionBroadcast(action) {
}
// EXPORTS
export { socket };
export { socket };