fix: context menu icons only appear on first open

useEffect watching [activeTool, activeTab] runs createIcons()
but contextMenu is NOT in deps. On first right-click, if
activeTool/activeTab also change (e.g. timeline click), the
effect fires and icons appear. Subsequent right-clicks don't
change activeTool/activeTab, so createIcons() never runs for
the new context menu DOM.

Fix: add contextMenu to useEffect dependency array.
This commit is contained in:
2026-07-27 21:35:53 +07:00
parent 74992f7b55
commit 430e87445e
2 changed files with 2 additions and 2 deletions
+1 -1
View File
@@ -7483,7 +7483,7 @@ const App = () => {
window.lucide.createIcons();
}
}, 50);
}, [activeTool, activeTab]);
}, [activeTool, activeTab, contextMenu]);
const timelineWrapperRef = useRef(null);
const [timelineWrapperNode, setTimelineWrapperNode] = useState(null);
const handleTimelineWrapperRef = useCallback(node => {
+1 -1
View File
@@ -217,7 +217,7 @@ const[tempTabEffects,setTempTabEffects]=useState({reverse:false,gainDb:0,fadeInM
const[currentUser,setCurrentUser]=useState(null);const[authModalOpen,setAuthModalOpen]=useState(false);const[authMode,setAuthMode]=useState('login');// 'login' | 'register' | 'force_change'
const[isMandatoryLogin,setIsMandatoryLogin]=useState(false);const[profileModalOpen,setProfileModalOpen]=useState(false);const[systemManagerModalOpen,setSystemManagerModalOpen]=useState(false);const[aiConfigModalOpen,setAiConfigModalOpen]=useState(false);const[aiPresetModalOpen,setAiPresetModalOpen]=useState(false);const[pluginManagerModalOpen,setPluginManagerModalOpen]=useState(false);const[pluginsData,setPluginsData]=useState(null);useEffect(()=>{const checkAuthStatus=async()=>{const savedToken=localStorage.getItem('sonic_token');if(!savedToken){setIsMandatoryLogin(true);setAuthMode('login');setAuthModalOpen(true);return;}try{const profile=await window.SonicAPI.getProfile();setCurrentUser(profile);localStorage.setItem('sonic_user',JSON.stringify(profile));if(profile.must_change_password){setIsMandatoryLogin(true);setAuthMode('force_change');setAuthModalOpen(true);}else{setIsMandatoryLogin(false);setAuthModalOpen(false);}}catch(err){const cached=localStorage.getItem('sonic_user');if(cached){try{setCurrentUser(JSON.parse(cached));}catch(_){}setIsMandatoryLogin(false);setAuthModalOpen(false);}else{localStorage.removeItem('sonic_token');localStorage.removeItem('sonic_user');setCurrentUser(null);setIsMandatoryLogin(true);setAuthMode('login');setAuthModalOpen(true);}}};checkAuthStatus();},[]);const loadPendingSfsProject=()=>{const proj=window.__pendingSfsProject;if(!proj)return;try{let restoredTracks=[];let restoredBpm=bpm;let restoredSessionTabs=[];let restoredSubTabs=[];if(proj.main_session){const result=deserializeProjectFromSchema(proj);restoredTracks=result.tracks;restoredBpm=result.bpm;restoredSessionTabs=result.sessionTabs;restoredSubTabs=result.subTabs;}else{restoredTracks=(proj.tracks||[]).map(t=>{const{height:_h,...rest}=t;return{...rest,buffer:null,channelInfo:t.channelInfo||null,clips:t.clips||[],serverFileId:t.serverFileId||null};});}if(restoredTracks.length>0){setTracks(restoredTracks);setBpm(restoredBpm.toString());if(restoredSessionTabs.length>0){setSessionTabs(restoredSessionTabs);}if(restoredSubTabs.length>0){setSubTabs(restoredSubTabs);}showToast(`Đã tải dự án "${proj.metadata?.title||proj.name||'Dự án mới'}" từ liên kết .sfs thành công!`,"success");}}catch(e){showToast("Lỗi tải dự án từ .sfs","error");}finally{window.__pendingSfsProject=null;}};const handleAuthSuccess=user=>{setCurrentUser(user);if(user.must_change_password){setIsMandatoryLogin(true);setAuthMode('force_change');setAuthModalOpen(true);}else{setIsMandatoryLogin(false);setAuthModalOpen(false);loadPendingSfsProject();const loadPrefs=p=>{if(!p)return;if(p.showAIPanel!==undefined)setShowAIPanel(p.showAIPanel);if(p.showExportPanel!==undefined)setShowExportPanel(p.showExportPanel);if(p.showSelectionPanel!==undefined)setShowSelectionPanel(p.showSelectionPanel);if(p.showPythonToolsPanel!==undefined)setShowPythonToolsPanel(p.showPythonToolsPanel);if(p.showMediaExplorer!==undefined)setShowMediaExplorer(p.showMediaExplorer);if(p.showFxRack!==undefined)setShowFxRack(p.showFxRack);if(p.showMidiEvents!==undefined)setShowMidiEvents(p.showMidiEvents);if(p.panelPositions)setPanelPositions(p.panelPositions);if(p.rightSidebarWidth)setRightSidebarWidth(p.rightSidebarWidth);if(p.mediaExplorerHeight)setMediaExplorerHeight(p.mediaExplorerHeight);if(p.selectedProviderId)setSelectedProviderId(p.selectedProviderId);};(async()=>{try{const data=await window.SonicAPI.getPreferences();if(data&&data.preferences)loadPrefs(data.preferences);else{const cached=localStorage.getItem('sonic_preferences');if(cached)loadPrefs(JSON.parse(cached));}}catch(e){const cached=localStorage.getItem('sonic_preferences');if(cached)loadPrefs(JSON.parse(cached));}try{const data=await window.SonicAPI.getAIConfigs();if(data&&data.providers){setAiProviders(data.providers);const active=data.providers.find(p=>p.is_active)||data.providers[0];if(active)setSelectedProviderId(active.id);}}catch(e){}try{window.SonicAPI.getSoundfontCatalog().then(cat=>{window.__soundfontCatalog=cat;}).catch(()=>{});}catch(e){}})();}};const handleLogout=()=>{localStorage.removeItem('sonic_token');localStorage.removeItem('sonic_user');setCurrentUser(null);setIsMandatoryLogin(true);setAuthMode('login');setAuthModalOpen(true);};// ── Temp project auto-save (local + server) ──
useEffect(()=>{const serializeSafe=arr=>(arr||[]).map(t=>({id:t.id,name:t.name,startTime:t.startTime,height:t.height,volumeDb:t.volumeDb,pan:t.pan,muted:t.muted,solo:t.solo,color:t.color,markers:t.markers||[],serverFileId:t.serverFileId||null,channelInfo:t.channelInfo?{channels:t.channelInfo.channels,isStereo:t.channelInfo.isStereo,label:t.channelInfo.label}:null}));window.SonicStorage.scheduleTempAutoSave(()=>{return serializeProjectToSchema(currentProjectId||'temp_project',projectName||'Dự án tạm chưa lưu',bpm,tracks,subTabs,sessionTabs);});},[tracks,subTabs,sessionTabs]);// Lucide icons initialization
useEffect(()=>{setTimeout(()=>{if(window.lucide){window.lucide.createIcons();}},50);},[activeTool,activeTab]);const timelineWrapperRef=useRef(null);const[timelineWrapperNode,setTimelineWrapperNode]=useState(null);const handleTimelineWrapperRef=useCallback(node=>{timelineWrapperRef.current=node;setTimelineWrapperNode(node);},[]);const tcpContainerRef=useRef(null);const[scrollLeft,setScrollLeft]=useState(0);const handleTimelineScroll=e=>{if(tcpContainerRef.current){tcpContainerRef.current.scrollTop=e.currentTarget.scrollTop;}setScrollLeft(e.currentTarget.scrollLeft);};const handleTCPScroll=e=>{if(timelineWrapperRef.current){timelineWrapperRef.current.scrollTop=e.currentTarget.scrollTop;}};const panelDropZoneRef=useRef(null);const startPanelDrag=(panelId,e)=>{panelDragRef.current={panelId,startX:e.clientX,startY:e.clientY};setPanelDropZone(null);setDragGhostPanel(panelId);setDragGhostPos({x:e.clientX-120,y:e.clientY-20});const onMove=ev=>{if(!panelDragRef.current||!workspaceRef.current)return;const rect=workspaceRef.current.getBoundingClientRect();const x=ev.clientX-rect.left;const y=ev.clientY-rect.top;const w=rect.width;const h=rect.height;const margin=60;let zone=null;if(x<margin&&x>10)zone='left';else if(x>w-margin&&x<w-10)zone='right';else if(y<margin&&y>10)zone='top';else if(y>h-margin&&y<h-10)zone='bottom';panelDropZoneRef.current=zone;setPanelDropZone(zone);setDragGhostPos({x:ev.clientX-120,y:ev.clientY-20});};const onUp=()=>{if(panelDragRef.current){const pid=panelDragRef.current.panelId;const targetZone=panelDropZoneRef.current;if(targetZone&&targetZone!==panelPositions[pid]){setPanelPositions(prev=>({...prev,[pid]:targetZone}));}panelDragRef.current=null;panelDropZoneRef.current=null;setPanelDropZone(null);setDragGhostPos(null);setDragGhostPanel(null);}document.removeEventListener('mousemove',onMove);document.removeEventListener('mouseup',onUp);};document.addEventListener('mousemove',onMove);document.addEventListener('mouseup',onUp);};// ── Right Sidebar Column Resizer ──
useEffect(()=>{setTimeout(()=>{if(window.lucide){window.lucide.createIcons();}},50);},[activeTool,activeTab,contextMenu]);const timelineWrapperRef=useRef(null);const[timelineWrapperNode,setTimelineWrapperNode]=useState(null);const handleTimelineWrapperRef=useCallback(node=>{timelineWrapperRef.current=node;setTimelineWrapperNode(node);},[]);const tcpContainerRef=useRef(null);const[scrollLeft,setScrollLeft]=useState(0);const handleTimelineScroll=e=>{if(tcpContainerRef.current){tcpContainerRef.current.scrollTop=e.currentTarget.scrollTop;}setScrollLeft(e.currentTarget.scrollLeft);};const handleTCPScroll=e=>{if(timelineWrapperRef.current){timelineWrapperRef.current.scrollTop=e.currentTarget.scrollTop;}};const panelDropZoneRef=useRef(null);const startPanelDrag=(panelId,e)=>{panelDragRef.current={panelId,startX:e.clientX,startY:e.clientY};setPanelDropZone(null);setDragGhostPanel(panelId);setDragGhostPos({x:e.clientX-120,y:e.clientY-20});const onMove=ev=>{if(!panelDragRef.current||!workspaceRef.current)return;const rect=workspaceRef.current.getBoundingClientRect();const x=ev.clientX-rect.left;const y=ev.clientY-rect.top;const w=rect.width;const h=rect.height;const margin=60;let zone=null;if(x<margin&&x>10)zone='left';else if(x>w-margin&&x<w-10)zone='right';else if(y<margin&&y>10)zone='top';else if(y>h-margin&&y<h-10)zone='bottom';panelDropZoneRef.current=zone;setPanelDropZone(zone);setDragGhostPos({x:ev.clientX-120,y:ev.clientY-20});};const onUp=()=>{if(panelDragRef.current){const pid=panelDragRef.current.panelId;const targetZone=panelDropZoneRef.current;if(targetZone&&targetZone!==panelPositions[pid]){setPanelPositions(prev=>({...prev,[pid]:targetZone}));}panelDragRef.current=null;panelDropZoneRef.current=null;setPanelDropZone(null);setDragGhostPos(null);setDragGhostPanel(null);}document.removeEventListener('mousemove',onMove);document.removeEventListener('mouseup',onUp);};document.addEventListener('mousemove',onMove);document.addEventListener('mouseup',onUp);};// ── Right Sidebar Column Resizer ──
const startColResize=e=>{e.preventDefault();const startX=e.clientX;const startWidth=rightSidebarWidth;const onMove=ev=>{const deltaX=startX-ev.clientX;const newWidth=Math.max(200,Math.min(600,startWidth+deltaX));setRightSidebarWidth(newWidth);};const onUp=()=>{document.removeEventListener('mousemove',onMove);document.removeEventListener('mouseup',onUp);};document.addEventListener('mousemove',onMove);document.addEventListener('mouseup',onUp);};// ── TCP Resizer ──
const startTcpResize=e=>{e.preventDefault();const startX=e.clientX;const startWidth=tcpWidth;const onMove=ev=>{const deltaX=ev.clientX-startX;const newWidth=Math.max(280,Math.min(600,startWidth+deltaX));setTcpWidth(newWidth);};const onUp=()=>{document.removeEventListener('mousemove',onMove);document.removeEventListener('mouseup',onUp);};document.addEventListener('mousemove',onMove);document.addEventListener('mouseup',onUp);};// ── Right Sidebar Row Resizer (Media Explorer / AI Panel) ──
const startRowResize=e=>{e.preventDefault();const startY=e.clientY;const startHeight=mediaExplorerHeight;const sidebarEl=document.getElementById('right-sidebar');const sidebarHeight=sidebarEl?sidebarEl.getBoundingClientRect().height:400;const onMove=ev=>{const deltaY=startY-ev.clientY;const pct=(startHeight/100*sidebarHeight+deltaY)/sidebarHeight*100;const newPct=Math.max(20,Math.min(80,pct));setMediaExplorerHeight(newPct);};const onUp=()=>{document.removeEventListener('mousemove',onMove);document.removeEventListener('mouseup',onUp);};document.addEventListener('mousemove',onMove);document.addEventListener('mouseup',onUp);};const rulerRef=useRef(null);const activeSourcesRef=useRef([]);const activeTrackNodesRef=useRef({});// { [trackId]: { gainNode, pannerNode } }