fix: Media Explorer preview cập nhật realtime khi chọn file
This commit is contained in:
+16
-4
@@ -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
@@ -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 {
|
||||||
|
|||||||
@@ -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 110–420. Hard reload.
|
- **Ghi chú/Test (nếu có):** Smoke jsdom: kéo handle +80px → tree width 176→256px OK, clamp 110–420. 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` và `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.
|
||||||
|
|||||||
Reference in New Issue
Block a user