fix: loadAudioBuffersForTracks scoped in ProfileModal crashes project load
Move fn to App component scope, pass as prop to ProfileModal. restoreLastSessionProject now has access on startup.
This commit is contained in:
+68
-66
@@ -4487,7 +4487,8 @@ const ProfileModal = ({
|
|||||||
setProjectName,
|
setProjectName,
|
||||||
currentProjectId,
|
currentProjectId,
|
||||||
setCurrentProjectId,
|
setCurrentProjectId,
|
||||||
showToast
|
showToast,
|
||||||
|
loadAudioBuffersForTracks
|
||||||
}) => {
|
}) => {
|
||||||
if (!isOpen) return null;
|
if (!isOpen) return null;
|
||||||
const [activeTab, setActiveTab] = useState('account');
|
const [activeTab, setActiveTab] = useState('account');
|
||||||
@@ -4573,70 +4574,6 @@ const ProfileModal = ({
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const loadAudioBuffersForTracks = async (tracksList) => {
|
|
||||||
let hasLoadedAny = false;
|
|
||||||
const updatedTracks = await Promise.all(tracksList.map(async t => {
|
|
||||||
let trackBuffer = t.buffer;
|
|
||||||
let trackChannelInfo = t.channelInfo;
|
|
||||||
if (t.serverFileId && !trackBuffer) {
|
|
||||||
try {
|
|
||||||
const res = await fetch('/static/audio/uploads/' + t.serverFileId);
|
|
||||||
if (res.ok) {
|
|
||||||
const blob = await res.blob();
|
|
||||||
const { audioBuffer, channelInfo } = await window.SonicAudio.decodeAudioFile(blob);
|
|
||||||
trackBuffer = audioBuffer;
|
|
||||||
trackChannelInfo = channelInfo;
|
|
||||||
hasLoadedAny = true;
|
|
||||||
}
|
|
||||||
} catch (e) {
|
|
||||||
console.warn("Failed to autoload track buffer for " + t.serverFileId, e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
let clipsUpdated = false;
|
|
||||||
const updatedClips = await Promise.all((t.clips || []).map(async c => {
|
|
||||||
let clipBuffer = c.buffer;
|
|
||||||
const targetFileId = c.serverFileId || t.serverFileId;
|
|
||||||
if (targetFileId && !clipBuffer) {
|
|
||||||
try {
|
|
||||||
const res = await fetch('/static/audio/uploads/' + targetFileId);
|
|
||||||
if (res.ok) {
|
|
||||||
const blob = await res.blob();
|
|
||||||
const { audioBuffer } = await window.SonicAudio.decodeAudioFile(blob);
|
|
||||||
clipBuffer = audioBuffer;
|
|
||||||
clipsUpdated = true;
|
|
||||||
hasLoadedAny = true;
|
|
||||||
}
|
|
||||||
} catch (e) {
|
|
||||||
console.warn("Failed to autoload clip buffer for " + targetFileId, e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return { ...c, buffer: clipBuffer };
|
|
||||||
}));
|
|
||||||
|
|
||||||
if (trackBuffer && updatedClips.length === 0) {
|
|
||||||
const clipId = `default_${t.id}`;
|
|
||||||
return {
|
|
||||||
...t,
|
|
||||||
buffer: trackBuffer,
|
|
||||||
channelInfo: trackChannelInfo,
|
|
||||||
clips: [{ id: clipId, buffer: trackBuffer, startTime: t.startTime || 0, name: t.name, speed: 1.0 }]
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
return {
|
|
||||||
...t,
|
|
||||||
buffer: trackBuffer,
|
|
||||||
channelInfo: trackChannelInfo,
|
|
||||||
clips: updatedClips
|
|
||||||
};
|
|
||||||
}));
|
|
||||||
|
|
||||||
if (hasLoadedAny) {
|
|
||||||
setTracks(updatedTracks);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
const handleOpenProject = async (projectId, projectName) => {
|
const handleOpenProject = async (projectId, projectName) => {
|
||||||
setAppWarningModal({
|
setAppWarningModal({
|
||||||
title: "Mở dự án",
|
title: "Mở dự án",
|
||||||
@@ -9799,6 +9736,70 @@ const App = () => {
|
|||||||
const [pluginManagerModalOpen, setPluginManagerModalOpen] = useState(false);
|
const [pluginManagerModalOpen, setPluginManagerModalOpen] = useState(false);
|
||||||
const [pluginsData, setPluginsData] = useState(null);
|
const [pluginsData, setPluginsData] = useState(null);
|
||||||
|
|
||||||
|
const loadAudioBuffersForTracks = async (tracksList) => {
|
||||||
|
let hasLoadedAny = false;
|
||||||
|
const updatedTracks = await Promise.all(tracksList.map(async t => {
|
||||||
|
let trackBuffer = t.buffer;
|
||||||
|
let trackChannelInfo = t.channelInfo;
|
||||||
|
if (t.serverFileId && !trackBuffer) {
|
||||||
|
try {
|
||||||
|
const res = await fetch('/static/audio/uploads/' + t.serverFileId);
|
||||||
|
if (res.ok) {
|
||||||
|
const blob = await res.blob();
|
||||||
|
const { audioBuffer, channelInfo } = await window.SonicAudio.decodeAudioFile(blob);
|
||||||
|
trackBuffer = audioBuffer;
|
||||||
|
trackChannelInfo = channelInfo;
|
||||||
|
hasLoadedAny = true;
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
console.warn("Failed to autoload track buffer for " + t.serverFileId, e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let clipsUpdated = false;
|
||||||
|
const updatedClips = await Promise.all((t.clips || []).map(async c => {
|
||||||
|
let clipBuffer = c.buffer;
|
||||||
|
const targetFileId = c.serverFileId || t.serverFileId;
|
||||||
|
if (targetFileId && !clipBuffer) {
|
||||||
|
try {
|
||||||
|
const res = await fetch('/static/audio/uploads/' + targetFileId);
|
||||||
|
if (res.ok) {
|
||||||
|
const blob = await res.blob();
|
||||||
|
const { audioBuffer } = await window.SonicAudio.decodeAudioFile(blob);
|
||||||
|
clipBuffer = audioBuffer;
|
||||||
|
clipsUpdated = true;
|
||||||
|
hasLoadedAny = true;
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
console.warn("Failed to autoload clip buffer for " + targetFileId, e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return { ...c, buffer: clipBuffer };
|
||||||
|
}));
|
||||||
|
|
||||||
|
if (trackBuffer && updatedClips.length === 0) {
|
||||||
|
const clipId = `default_${t.id}`;
|
||||||
|
return {
|
||||||
|
...t,
|
||||||
|
buffer: trackBuffer,
|
||||||
|
channelInfo: trackChannelInfo,
|
||||||
|
clips: [{ id: clipId, buffer: trackBuffer, startTime: t.startTime || 0, name: t.name, speed: 1.0 }]
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
...t,
|
||||||
|
buffer: trackBuffer,
|
||||||
|
channelInfo: trackChannelInfo,
|
||||||
|
clips: updatedClips
|
||||||
|
};
|
||||||
|
}));
|
||||||
|
|
||||||
|
if (hasLoadedAny) {
|
||||||
|
setTracks(updatedTracks);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
const restoreLastSessionProject = async () => {
|
const restoreLastSessionProject = async () => {
|
||||||
var pendingWasNull = !window.__pendingSfsProject;
|
var pendingWasNull = !window.__pendingSfsProject;
|
||||||
loadPendingSfsProject();
|
loadPendingSfsProject();
|
||||||
@@ -21139,7 +21140,8 @@ const App = () => {
|
|||||||
setProjectName: setProjectName,
|
setProjectName: setProjectName,
|
||||||
currentProjectId: currentProjectId,
|
currentProjectId: currentProjectId,
|
||||||
setCurrentProjectId: setCurrentProjectId,
|
setCurrentProjectId: setCurrentProjectId,
|
||||||
showToast: showToast
|
showToast: showToast,
|
||||||
|
loadAudioBuffersForTracks: loadAudioBuffersForTracks
|
||||||
}), /*#__PURE__*/React.createElement(SaveProjectModal, {
|
}), /*#__PURE__*/React.createElement(SaveProjectModal, {
|
||||||
isOpen: saveProjectModalOpen,
|
isOpen: saveProjectModalOpen,
|
||||||
onClose: () => setSaveProjectModalOpen(false),
|
onClose: () => setSaveProjectModalOpen(false),
|
||||||
|
|||||||
File diff suppressed because one or more lines are too long
@@ -849,6 +849,12 @@
|
|||||||
- **Các file ảnh hưởng:** `app/templates/index.html`, `app/static/js/app.jsx`
|
- **Các file ảnh hưởng:** `app/templates/index.html`, `app/static/js/app.jsx`
|
||||||
- **Ghi chú/Test (nếu có):** `npm run build` — build passes.
|
- **Ghi chú/Test (nếu có):** `npm run build` — build passes.
|
||||||
|
|
||||||
|
### [2026-07-30 20:36] Task: Fix project load crash — loadAudioBuffersForTracks scoped inside ProfileModal
|
||||||
|
- **Tóm tắt thay đổi:** `loadAudioBuffersForTracks` defined inside `ProfileModal`, but `restoreLastSessionProject` in App component called it → ReferenceError on startup + silent catch → project not restored. Fix: move function to App component, pass as prop to ProfileModal.
|
||||||
|
- **Các file ảnh hưởng:** `app/static/js/app.jsx`, `app/static/js/app.precompiled.js`
|
||||||
|
- **Ghi chú/Test (nếu có):** `npm run build` pass. Mở lại trang → dự án cũ được khôi phục.
|
||||||
|
---
|
||||||
|
|
||||||
### [2026-07-30 20:32] Task: Add "Tạo preset với cấu trúc" button in AI Preset Manager
|
### [2026-07-30 20:32] Task: Add "Tạo preset với cấu trúc" button in AI Preset Manager
|
||||||
- **Tóm tắt thay đổi:** Thêm `handleNewStructured` — tạo preset mới với template có cấu trúc (mục: thể loại, bố cục, nhạc cụ, hòa âm, hiệu ứng, MIDI). Button xanh lục "Tạo preset với cấu trúc" bên trái "Quay lại" trong form edit.
|
- **Tóm tắt thay đổi:** Thêm `handleNewStructured` — tạo preset mới với template có cấu trúc (mục: thể loại, bố cục, nhạc cụ, hòa âm, hiệu ứng, MIDI). Button xanh lục "Tạo preset với cấu trúc" bên trái "Quay lại" trong form edit.
|
||||||
- **Các file ảnh hưởng:** `app/static/js/app.jsx`
|
- **Các file ảnh hưởng:** `app/static/js/app.jsx`
|
||||||
|
|||||||
Reference in New Issue
Block a user