Files

6.2 KiB
Raw Permalink Blame History

Fix: MAIN SESSION & SECTION-TAB Mouse Behavior (MOUSE.md)

Mô tả

Kiểm tra và sửa các lỗi trong MAIN SESSION và SECTION-TAB theo đặc tả MOUSE.md:

Hành động Đặc tả MOUSE.md
Click item select item, drag to move
Ctrl+Click item toggle selection (add/remove from group)
Ctrl+Click empty deselect all
Ctrl+Click+Drag item/group copy item/group to new track
Ctrl+Click+Drag empty marquee selection/deselection

Bugs đã tìm thấy

Bug 1 handleSetPendingDrag: luôn thêm itemId vào ids, bất kể đã toggle hay deselect

Vị trí: app.jsx L11834-11837

const handleSetPendingDrag = (trackId, itemType, itemId, clickOffset, e, preToggleSnapshot) => {
  var ids = preToggleSnapshot || new Set(selectedItemIds);
  ids.add(itemId);  // ← luôn ADD itemId, bất kể đây là lần toggle-off
  pendingDragRef.current = { ... };
};
  • Khi user Ctrl+Click vào một item đang được selected → item đó bị deselect (line 1107).
  • Nhưng preToggleSnapshot (được build trước toggle tại line 1111/1178) luôn được ids.add(itemId), nên pendingDrag vẫn include item đó trong nhóm copy.
  • Kết quả: Ctrl+Drag sau deselect vẫn copy item bị deselect → số lượng copies bị lỗi.

Fix: Khi pendingDrag được set sau toggle, nếu item bị deselect (tức preToggleSnapshot.has(itemId)) thì xóa itemId khỏi ids thay vì add.

Caller (line 1111) truyền preToggleSnapshot (snapshot TRƯỚC toggle), nên logic cần:

// Nếu item đang có trong snapshot (tức là toggle-off / deselect), xóa khỏi ids
// Nếu item không có trong snapshot (tức là toggle-on / add), thêm vào ids
if (preToggleSnapshot && preToggleSnapshot.has(itemId)) {
  ids.delete(itemId);
} else {
  ids.add(itemId);
}

Bug 2 handleSectionItemDragStart với isDuplicate=true: newOriginals bị build bên trong callback async của updateActiveTracks

Vị trí: app.jsx L11888-11924

var newOriginals = {};
updateActiveTracks(function(prev) {
  return prev.map(function(t) {
    // ... build newOriginals ở đây (bên trong callback)
    newOriginals[newId] = { ... };
    return { ...t, ... };
  });
});
// Sử dụng newOriginals NGAY SAU updateActiveTracks (có thể chưa được fill)
var newItemId = Object.keys(newOriginals)[0] || itemId;
setDraggedSectionItem({ ..., itemId: newItemId, multiIds: newOriginals });

updateActiveTracks gọi setState (React batch), callback của nó được gọi đồng bộ (React's setState updater function chạy đồng bộ). Nhưng vì newOriginals được closure-captured bên trong callback, điều này thực ra hoạt động đúng trong React 18 với batching.

Tuy nhiên: nếu nhiều items có cùng loại (section + midiItem + clip), vòng lặp qua Object.keys(multiIds) gọi Date.now() nhiều lần trong cùng một tick → có thể cho cùng một newId cho các items khác nhau, dẫn đến items bị merge.

Fix: Thêm index counter vào ID tạo để đảm bảo unique:

var dupCounter = 0;
var newId = 'sec_dup_' + Date.now() + '_' + (dupCounter++) + '_' + Math.random()...

Bug 3 Section-only path trong isDuplicate (non-multi): không xử lý clip type

Vị trí: app.jsx L11925-11938

Khi không có multiIds (chỉ 1 item đơn lẻ), code chỉ handle sectionmidiItem:

var items = itemType === 'section' ? (track.sections || []) : (track.midiItems || []);

Nếu itemType === 'clip', items sẽ dùng midiItems (sai). Cần thêm case cho clip.


Bug 4 Ctrl+Click trên CLIP ở line 1172: điều kiện kiểm tra bỏ qua hitItem

Vị trí: app.jsx L1172

if (clickedClip && (e.altKey || e.ctrlKey)) { ... }

Đoạn này xử lý Ctrl+Click trên audio clip. Tuy nhiên ở trên (line 1101), block if (hitItem && !e.altKey && !e.shiftKey) đã return nếu hitItem (section/midiItem) được click với Ctrl. Nhưng nếu không có hitItem và không có clickedClip, flow đúng (sweep select). Không có lỗi ở đây.


Bug 5 handleSetPendingDrag cho Clip (line 1184): cùng vấn đề như Bug 1

Tương tự Bug 1, khi Ctrl+Click trên clip đang selected (sẽ bị deselect ở line 1180), nhưng clipPreToggleSnapshot.has(clipCanonicalId) là true và ids.add(clipCanonicalId) vẫn được thực thi trong handleSetPendingDrag.


Proposed Changes

[MODIFY] app.jsx

Fix Bug 1 & 5 handleSetPendingDrag (L11834-11838)

Sửa logic toggle để đúng: nếu item trong preToggleSnapshot → nó vừa bị deselect → xóa khỏi ids. Nếu không có → vừa được add → thêm vào ids.

Fix Bug 2 ID uniqueness trong multi-copy loop

Thêm counter index vào ID generation trong vòng lặp Object.keys(multiIds).forEach.

Fix Bug 3 Single-item copy: xử lý clip type

Trong nhánh else (non-multiIds) của isDuplicate, thêm xử lý clip type.


Verification Plan

Manual Verification

  1. Mở MAIN SESSION
  2. Thêm vài sections/MIDI items trên nhiều tracks
  3. Test Ctrl+Click toggle: Click vào item A → selected. Ctrl+Click A → deselected. Ctrl+Click B → B selected (không phải A+B).
  4. Test Ctrl+Drag single: Ctrl+Click item → drag → chỉ copy 1 item, không thêm item nào bị deselect
  5. Test Ctrl+Drag group: Ctrl+Click A, Ctrl+Click B (2 selected) → drag từ A → copy cả A và B, không thêm C hay D
  6. Test Ctrl+Click empty: Click trống → deselect all
  7. Test Ctrl+Drag empty: Drag trên empty → marquee select
  8. Lặp lại trên SECTION-TAB (session_xxx)