fix: Media Explorer preview cập nhật realtime khi chọn file

This commit is contained in:
2026-08-02 16:48:11 +07:00
parent b075a5eca7
commit c435d00897
4 changed files with 26 additions and 9 deletions
+16 -4
View File
@@ -9051,6 +9051,7 @@ const MediaExplorerPanel = ({ height }) => {
const canvasRef = React.useRef(null); const canvasRef = React.useRef(null);
const playStateRef = React.useRef(null); const playStateRef = React.useRef(null);
const rafRef = React.useRef(null); const rafRef = React.useRef(null);
const selectTokenRef = React.useRef(0);
React.useEffect(() => { React.useEffect(() => {
if (window.SonicAPI && window.SonicAPI.listMyFiles) { if (window.SonicAPI && window.SonicAPI.listMyFiles) {
@@ -9084,12 +9085,15 @@ const MediaExplorerPanel = ({ height }) => {
}, [folderFiles, filterText]); }, [folderFiles, filterText]);
const loadWaveform = async (f) => { const loadWaveform = async (f) => {
const token = selectTokenRef.current;
if (f && (f.handle || f.path)) { if (f && (f.handle || f.path)) {
try { try {
const buf = await readLocalFileBuffer(f); const buf = await readLocalFileBuffer(f);
if (selectTokenRef.current !== token) return;
if (!buf) { setPeaks(null); return; } if (!buf) { setPeaks(null); return; }
const ctx = getAudioContext(); const ctx = getAudioContext();
const decoded = await ctx.decodeAudioData(buf); const decoded = await ctx.decodeAudioData(buf);
if (selectTokenRef.current !== token) return;
setAudioBuffer(decoded); setAudioBuffer(decoded);
const data = decoded.getChannelData(0); const data = decoded.getChannelData(0);
const count = 600; const count = 600;
@@ -9104,7 +9108,7 @@ const MediaExplorerPanel = ({ height }) => {
pk.push(m); pk.push(m);
} }
setPeaks(pk); setPeaks(pk);
} catch (e) { setPeaks(null); } } catch (e) { if (selectTokenRef.current === token) setPeaks(null); }
return; return;
} }
const fid = f.file_id || f.fileId; const fid = f.file_id || f.fileId;
@@ -9112,6 +9116,7 @@ const MediaExplorerPanel = ({ height }) => {
try { try {
const resp = await fetch(`${API_BASE_URL}/api/v1/audio/waveform/${fid}?num_peaks=600`); const resp = await fetch(`${API_BASE_URL}/api/v1/audio/waveform/${fid}?num_peaks=600`);
const data = await resp.json(); const data = await resp.json();
if (selectTokenRef.current !== token) return;
setPeaks(data.peaks || []); setPeaks(data.peaks || []);
} catch (e) { setPeaks(null); } } catch (e) { setPeaks(null); }
}; };
@@ -9275,7 +9280,7 @@ const MediaExplorerPanel = ({ height }) => {
setIsPaused(false); setIsPaused(false);
}, []); }, []);
const playSelected = async (f) => { const playSelected = async (f, token) => {
if (!f) return; if (!f) return;
stopMediaPlayback(); stopMediaPlayback();
if (isMidiFile(f)) { if (isMidiFile(f)) {
@@ -9287,10 +9292,12 @@ const MediaExplorerPanel = ({ height }) => {
} }
const fid = f.file_id || f.fileId; const fid = f.file_id || f.fileId;
const buf = await readLocalFileBuffer(f); const buf = await readLocalFileBuffer(f);
if (selectTokenRef.current !== (token || selectTokenRef.current)) return;
if (!buf) return; if (!buf) return;
try { try {
const ctx = getAudioContext(); const ctx = getAudioContext();
const decoded = await ctx.decodeAudioData(buf); const decoded = await ctx.decodeAudioData(buf);
if (selectTokenRef.current !== (token || selectTokenRef.current)) return;
setAudioBuffer(decoded); setAudioBuffer(decoded);
const src = ctx.createBufferSource(); const src = ctx.createBufferSource();
src.buffer = decoded; src.buffer = decoded;
@@ -9406,10 +9413,15 @@ const MediaExplorerPanel = ({ height }) => {
const handleSelect = (f) => { const handleSelect = (f) => {
if (!f || f.is_dir) return; if (!f || f.is_dir) return;
selectTokenRef.current++;
const token = selectTokenRef.current;
setSelected(f); setSelected(f);
setCurrentTime(0); setCurrentTime(0);
if (f.kind === 'other') { stopMediaPlayback(); return; } setPeaks(null);
if (autoPlay) { playSelected(f); } else { stopMediaPlayback(); } setAudioBuffer(null);
stopMediaPlayback();
if (f.kind === 'other') return;
if (autoPlay) { playSelected(f, token); }
if (!isMidiFile(f) && (f.path || f.file_id || f.fileId)) loadWaveform(f); if (!isMidiFile(f) && (f.path || f.file_id || f.fileId)) loadWaveform(f);
}; };
File diff suppressed because one or more lines are too long
+1 -1
View File
@@ -23,7 +23,7 @@
<script src="/static/js/services/midiExtractor.js?v=202607281052"></script> <script src="/static/js/services/midiExtractor.js?v=202607281052"></script>
<script src="/static/js/services/promptTemplateManager.js?v=202607281039"></script> <script src="/static/js/services/promptTemplateManager.js?v=202607281039"></script>
<script src="/static/js/services/undoRedoEngine.js?v=202607290941"></script> <script src="/static/js/services/undoRedoEngine.js?v=202607290941"></script>
<script src="/static/js/app.precompiled.js?v=202608021640" defer></script> <script src="/static/js/app.precompiled.js?v=202608021650" defer></script>
<link rel="stylesheet" href="/static/css/styles.css?v=202607271016"> <link rel="stylesheet" href="/static/css/styles.css?v=202607271016">
<style> <style>
:root { :root {
+5
View File
@@ -1099,3 +1099,8 @@
- **Tóm tắt thay đổi:** `MediaExplorerPanel` directory tree bỏ class `w-44` cố định → width động qua state `treeWidth` (default 176px, min 110 max 420, ref `treeWidthRef`). Thêm drag handle `w-1.5 cursor-ew-resize` bên phải tree (mousedown/mousemove/mouseup trên document, `startTreeResize`). File list bên phải tự co giãn theo `flex-1`. - **Tóm tắt thay đổi:** `MediaExplorerPanel` directory tree bỏ class `w-44` cố định → width động qua state `treeWidth` (default 176px, min 110 max 420, ref `treeWidthRef`). Thêm drag handle `w-1.5 cursor-ew-resize` bên phải tree (mousedown/mousemove/mouseup trên document, `startTreeResize`). File list bên phải tự co giãn theo `flex-1`.
- **Các file ảnh hưởng:** `app/static/js/app.jsx`, `app/static/js/app.precompiled.js`, `app/templates/index.html` - **Các file ảnh hưởng:** `app/static/js/app.jsx`, `app/static/js/app.precompiled.js`, `app/templates/index.html`
- **Ghi chú/Test (nếu có):** Smoke jsdom: kéo handle +80px → tree width 176→256px OK, clamp 110420. Hard reload. - **Ghi chú/Test (nếu có):** Smoke jsdom: kéo handle +80px → tree width 176→256px OK, clamp 110420. Hard reload.
### [2026-08-02 16:50] Task: Media Explorer preview không cập nhật realtime khi chọn file
- **Tóm tắt thay đổi:** `MediaExplorerPanel`: (1) `handleSelect` giờ clear ngay `setPeaks(null)` + `setAudioBuffer(null)` + `stopMediaPlayback()` khi chọn file mới → canvas xóa dữ liệu cũ ngay; (2) thêm `selectTokenRef` guard async race: `loadWaveform``playSelected` check token sau mỗi await, kết quả cũ (file trước) không ghi đè file mới chọn; (3) gọi `drawCanvas` qua effect phụ thuộc `[peaks, audioBuffer, selected]` → vẽ lại realtime.
- **Các file ảnh hưởng:** `app/static/js/app.jsx`, `app/static/js/app.precompiled.js`, `app/templates/index.html`
- **Ghi chú/Test (nếu có):** Smoke jsdom: click file → row selected + metadata MIDI events hiển thị; switch nhanh liên tục → file cuối đúng được chọn, không bị race. Hard reload.