LỖI: màn hình đen khi load
This commit is contained in:
+69
-21
@@ -4899,7 +4899,6 @@ const AuthModal = ({
|
||||
onClose,
|
||||
onSuccess
|
||||
}) => {
|
||||
if (!isOpen) return null;
|
||||
const [activeTab, setActiveTab] = useState(mode || 'login');
|
||||
const [username, setUsername] = useState('admin');
|
||||
const [email, setEmail] = useState('');
|
||||
@@ -4965,6 +4964,10 @@ const AuthModal = ({
|
||||
};
|
||||
const isForceMode = activeTab === 'force_change';
|
||||
const canClose = !forceMandatory && !isForceMode;
|
||||
// return null SAU tất cả hooks (Rules of Hooks — bug 10:05: if !isOpen return
|
||||
// null TRƯỚC hooks → mở login lần đầu (false→true) → hook order đổi → React
|
||||
// crash → unmount → MÀN ĐEN)
|
||||
if (!isOpen) return null;
|
||||
return /*#__PURE__*/React.createElement("div", {
|
||||
className: "fixed inset-0 z-50 flex items-center justify-center bg-black/80 backdrop-blur-md"
|
||||
}, /*#__PURE__*/React.createElement("div", {
|
||||
@@ -5065,7 +5068,6 @@ const AIConfigModal = ({
|
||||
onClose,
|
||||
onConfigSaved
|
||||
}) => {
|
||||
if (!isOpen) return null;
|
||||
const defaultProvidersList = [{
|
||||
id: 'openai_default',
|
||||
name: 'OpenAI Official',
|
||||
@@ -5148,6 +5150,8 @@ const AIConfigModal = ({
|
||||
} : p));
|
||||
};
|
||||
const activeProvider = providers.find(p => p.id === selectedId) || providers[0];
|
||||
// return null SAU hooks (Rules of Hooks — bug 10:05)
|
||||
if (!isOpen) return null;
|
||||
return /*#__PURE__*/React.createElement("div", {
|
||||
className: "fixed inset-0 z-50 flex items-center justify-center bg-black/70 backdrop-blur-sm select-none"
|
||||
}, /*#__PURE__*/React.createElement("div", {
|
||||
@@ -5280,7 +5284,6 @@ const AIConfigModal = ({
|
||||
};
|
||||
|
||||
const PluginManagerModal = ({ isOpen, onClose, pluginsData }) => {
|
||||
if (!isOpen) return null;
|
||||
const [localData, setLocalData] = React.useState(pluginsData);
|
||||
const [sfUploadStatus, setSfUploadStatus] = React.useState('');
|
||||
const [sfToDelete, setSfToDelete] = React.useState(null);
|
||||
@@ -5311,6 +5314,8 @@ const PluginManagerModal = ({ isOpen, onClose, pluginsData }) => {
|
||||
}
|
||||
};
|
||||
const [pmTab, setPmTab] = React.useState('soundfont');
|
||||
// return null SAU hooks (Rules of Hooks — bug 10:05)
|
||||
if (!isOpen) return null;
|
||||
return React.createElement('div', {
|
||||
className: 'fixed inset-0 z-50 flex items-center justify-center bg-black/70 backdrop-blur-sm',
|
||||
onClick: onClose
|
||||
@@ -5548,7 +5553,6 @@ const ProfileModal = ({
|
||||
setSubTabs,
|
||||
trackMidiChannelsRef
|
||||
}) => {
|
||||
if (!isOpen) return null;
|
||||
const [activeTab, setActiveTab] = useState('account');
|
||||
const [profile, setProfile] = useState(null);
|
||||
const [oldPassword, setOldPassword] = useState('');
|
||||
@@ -5838,6 +5842,8 @@ const ProfileModal = ({
|
||||
top: `calc(50% + ${dragOfs.y}px)`,
|
||||
transform: 'translate(-50%, -50%)'
|
||||
};
|
||||
// return null SAU hooks (Rules of Hooks — bug 10:05)
|
||||
if (!isOpen) return null;
|
||||
return /*#__PURE__*/React.createElement(React.Fragment, null, /*#__PURE__*/React.createElement("div", {
|
||||
key: "backdrop",
|
||||
className: "fixed inset-0 z-40 bg-black/70 backdrop-blur-sm",
|
||||
@@ -6079,7 +6085,6 @@ const SaveProjectModal = ({
|
||||
onSaveLocal,
|
||||
projectName
|
||||
}) => {
|
||||
if (!isOpen) return null;
|
||||
const [name, setName] = useState(projectName || '');
|
||||
const [saveType, setSaveType] = useState(localStorage.getItem('sonic_token') ? 'cloud' : 'local');
|
||||
const [cloudProjects, setCloudProjects] = useState([]);
|
||||
@@ -6105,6 +6110,8 @@ const SaveProjectModal = ({
|
||||
else { onSaveLocal(name.trim()); onClose(); }
|
||||
}
|
||||
};
|
||||
// return null SAU hooks (Rules of Hooks — bug 10:05)
|
||||
if (!isOpen) return null;
|
||||
if (confirmOverwriteProject) {
|
||||
return React.createElement("div", {
|
||||
className: "fixed inset-0 z-50 flex items-center justify-center bg-black/70 backdrop-blur-sm"
|
||||
@@ -6182,7 +6189,6 @@ const SaveProjectModal = ({
|
||||
};
|
||||
|
||||
const OpenProjectModal = ({ isOpen, onClose, onOpenCloud, onOpenLocal }) => {
|
||||
if (!isOpen) return null;
|
||||
const [tab, setTab] = useState('cloud');
|
||||
const [projects, setProjects] = useState([]);
|
||||
const [loading, setLoading] = useState(false);
|
||||
@@ -6196,6 +6202,8 @@ const OpenProjectModal = ({ isOpen, onClose, onOpenCloud, onOpenLocal }) => {
|
||||
} else { setLoading(false); }
|
||||
}
|
||||
}, [isOpen, tab]);
|
||||
// return null SAU hooks (Rules of Hooks — bug 10:05)
|
||||
if (!isOpen) return null;
|
||||
return React.createElement("div", {
|
||||
className: "fixed inset-0 z-50 flex items-center justify-center bg-black/70 backdrop-blur-sm"
|
||||
}, React.createElement("div", {
|
||||
@@ -6256,7 +6264,6 @@ const SaveAsModal = ({
|
||||
onSaveCloud,
|
||||
onSaveLocal
|
||||
}) => {
|
||||
if (!isOpen) return null;
|
||||
const [name, setName] = useState(projectName || '');
|
||||
const [saveType, setSaveType] = useState('cloud');
|
||||
const handleSubmit = (e) => {
|
||||
@@ -6269,6 +6276,8 @@ const SaveAsModal = ({
|
||||
}
|
||||
onClose();
|
||||
};
|
||||
// return null SAU hooks (Rules of Hooks — bug 10:05)
|
||||
if (!isOpen) return null;
|
||||
return /*#__PURE__*/React.createElement("div", {
|
||||
className: "fixed inset-0 z-50 flex items-center justify-center bg-black/70 backdrop-blur-sm"
|
||||
}, /*#__PURE__*/React.createElement("div", {
|
||||
@@ -6336,7 +6345,6 @@ const SystemManagerModal = ({
|
||||
isOpen,
|
||||
onClose
|
||||
}) => {
|
||||
if (!isOpen) return null;
|
||||
const [users, setUsers] = useState([]);
|
||||
const [loading, setLoading] = useState(true);
|
||||
const [msg, setMsg] = useState('');
|
||||
@@ -6388,6 +6396,8 @@ const SystemManagerModal = ({
|
||||
setError(err.message || 'Lỗi khi xóa người dùng');
|
||||
}
|
||||
};
|
||||
// return null SAU hooks (Rules of Hooks — bug 10:05)
|
||||
if (!isOpen) return null;
|
||||
return /*#__PURE__*/React.createElement("div", {
|
||||
className: "fixed inset-0 z-50 flex items-center justify-center bg-black/70 backdrop-blur-sm"
|
||||
}, /*#__PURE__*/React.createElement("div", {
|
||||
@@ -6470,7 +6480,6 @@ const SystemManagerModal = ({
|
||||
};
|
||||
|
||||
const AIPresetModal = ({ isOpen, onClose }) => {
|
||||
if (!isOpen) return null;
|
||||
|
||||
const mgrRef = React.useRef(null);
|
||||
if (!mgrRef.current) mgrRef.current = new window.PromptTemplateManager();
|
||||
@@ -6688,6 +6697,8 @@ const AIPresetModal = ({ isOpen, onClose }) => {
|
||||
if (showFavoritesOnly) matchesCategory = matchesCategory && p.is_favorite;
|
||||
return matchesSearch && matchesCategory;
|
||||
});
|
||||
// return null SAU hooks (Rules of Hooks — bug 10:05)
|
||||
if (!isOpen) return null;
|
||||
|
||||
return /*#__PURE__*/React.createElement(React.Fragment, null, /*#__PURE__*/React.createElement("div", {
|
||||
className: "fixed inset-0 z-50 flex items-center justify-center bg-black/75 backdrop-blur-sm p-4 animate-fade-in"
|
||||
@@ -10419,12 +10430,10 @@ const FXRackModal = ({ track, onUpdateTrack, onClose }) => {
|
||||
const eqCurveRef = React.useRef(null);
|
||||
const scopeStateRef = React.useRef({ L: null, R: null, head: 0, len: 0, tmpL: null, tmpR: null, freq: null });
|
||||
|
||||
if (!track) return null;
|
||||
|
||||
const chain = track.fxChain || [];
|
||||
const chain = track ? (track.fxChain || []) : [];
|
||||
const setChain = (next) => {
|
||||
if (onUpdateTrack) onUpdateTrack(track.id, { fxChain: next });
|
||||
if (window.__rebuildTrackFxGraph) window.__rebuildTrackFxGraph(track.id);
|
||||
if (onUpdateTrack && track) onUpdateTrack(track.id, { fxChain: next });
|
||||
if (track && window.__rebuildTrackFxGraph) window.__rebuildTrackFxGraph(track.id);
|
||||
};
|
||||
const paramsOf = (m) => ({ ...(TRACK_FX_DEFAULTS[m.type] || {}), ...(m.params || {}) });
|
||||
const setParams = (idx, patch) => {
|
||||
@@ -10645,6 +10654,8 @@ const FXRackModal = ({ track, onUpdateTrack, onClose }) => {
|
||||
ctx.fillText('20kHz', w - 38, h - 2);
|
||||
}, [ap.g1, ap.g2, ap.g3, ap.g4, activeMod]);
|
||||
|
||||
if (!track) return null;
|
||||
|
||||
return (
|
||||
<div className="fixed inset-0 z-[115] bg-black/80 backdrop-blur-sm flex items-center justify-center p-4" onClick={onClose}>
|
||||
<div className="w-full max-w-4xl bg-slate-900 border border-slate-700 rounded-2xl p-5 space-y-4 shadow-2xl flex flex-col max-h-[90vh] text-slate-200" onClick={e => e.stopPropagation()}>
|
||||
@@ -15546,6 +15557,7 @@ const App = () => {
|
||||
// 401 bất kỳ → mở màn login (bug 09:45 — màn đen khi token hỏng/hết hạn)
|
||||
React.useEffect(() => {
|
||||
window.__onAuthFail = () => {
|
||||
console.log('[AUTH] __onAuthFail — mở login');
|
||||
setCurrentUser(null);
|
||||
setIsMandatoryLogin(true);
|
||||
setAuthMode('login');
|
||||
@@ -15553,6 +15565,14 @@ const App = () => {
|
||||
};
|
||||
return () => { delete window.__onAuthFail; };
|
||||
}, []);
|
||||
// Chẩn đoán: sau mount — root có nội dung không (bug màn đen 10:20)
|
||||
React.useEffect(() => {
|
||||
try {
|
||||
const rootEl = document.getElementById('root');
|
||||
console.log('[APP] mounted — root children:', rootEl ? rootEl.children.length : -1,
|
||||
'| authModalOpen:', authModalOpen, '| currentUser:', currentUser ? currentUser.username : null);
|
||||
} catch (e) { console.log('[APP] diag err', e.message); }
|
||||
});
|
||||
const [authMode, setAuthMode] = useState('login'); // 'login' | 'register' | 'force_change'
|
||||
const [isMandatoryLogin, setIsMandatoryLogin] = useState(false);
|
||||
const [profileModalOpen, setProfileModalOpen] = useState(false);
|
||||
@@ -15836,8 +15856,10 @@ const App = () => {
|
||||
|
||||
useEffect(() => {
|
||||
const checkAuthStatus = async () => {
|
||||
console.log('[AUTH] checkAuthStatus bắt đầu, token=', (localStorage.getItem('sonic_token') || '(rỗng)').slice(0, 12));
|
||||
const savedToken = localStorage.getItem('sonic_token');
|
||||
if (!savedToken) {
|
||||
console.log('[AUTH] không có token → mở login');
|
||||
setIsMandatoryLogin(true);
|
||||
setAuthMode('login');
|
||||
setAuthModalOpen(true);
|
||||
@@ -15845,6 +15867,7 @@ const App = () => {
|
||||
}
|
||||
try {
|
||||
const profile = await window.SonicAPI.getProfile();
|
||||
console.log('[AUTH] profile OK:', profile && profile.username);
|
||||
setCurrentUser(profile);
|
||||
localStorage.setItem('sonic_user', JSON.stringify(profile));
|
||||
if (profile.must_change_password) {
|
||||
@@ -15857,11 +15880,13 @@ const App = () => {
|
||||
restoreLastSessionProject();
|
||||
}
|
||||
} catch (err) {
|
||||
console.log('[AUTH] profile lỗi:', String((err && err.message) || err).slice(0, 80));
|
||||
// 401 (token hỏng/đổi SECRET_KEY — bug 09:45: dùng cached user → vào app
|
||||
// nhưng mọi API 401 → màn đen không login) → BỎ qua cached → mở LOGIN.
|
||||
// detail server: "Thiếu Token xác thực..." / "Token đã hết hạn..." (auth.py)
|
||||
const errMsg = String((err && err.message) || err || '');
|
||||
const isAuthErr = errMsg.includes('Token') || errMsg.includes('401') || errMsg.includes('Unauthorized') || errMsg.includes('unauthorized') || errMsg.includes('Không xác thực');
|
||||
console.log('[AUTH] isAuthErr=', isAuthErr, 'errMsg=', errMsg.slice(0, 60));
|
||||
if (!isAuthErr) {
|
||||
const cached = localStorage.getItem('sonic_user');
|
||||
if (cached) {
|
||||
@@ -25978,6 +26003,7 @@ STRICT CONSTRAINTS:
|
||||
});
|
||||
};
|
||||
|
||||
console.log('[APP] return UI — bắt đầu render');
|
||||
return /*#__PURE__*/React.createElement(React.Fragment, null, /*#__PURE__*/React.createElement("div", {
|
||||
className: "h-full w-full flex flex-col bg-[#1e1e1e]"
|
||||
}, /*#__PURE__*/React.createElement("header", {
|
||||
@@ -26854,6 +26880,7 @@ STRICT CONSTRAINTS:
|
||||
else if (id === 'midi_events') setShowMidiEvents(false);
|
||||
};
|
||||
const renderPanelContent = panelId => {
|
||||
console.log('[FN] renderPanelContent bắt đầu, panel=', panelId);
|
||||
const h = id => e => {
|
||||
startPanelDrag(id, e);
|
||||
};
|
||||
@@ -27424,6 +27451,7 @@ STRICT CONSTRAINTS:
|
||||
return null;
|
||||
};
|
||||
const renderDock = (pos, title) => {
|
||||
console.log('[FN] renderDock bắt đầu, pos=', pos);
|
||||
const panels = dockPanels[pos];
|
||||
if (panels.length === 0) return null;
|
||||
const isSide = pos === 'left' || pos === 'right';
|
||||
@@ -27486,7 +27514,7 @@ STRICT CONSTRAINTS:
|
||||
className: "w-3.5 h-3.5 text-cyan-400"
|
||||
})), dragGhostPanel === 'export' ? 'Export Panel' : dragGhostPanel === 'ai' ? 'AI Panel' : dragGhostPanel === 'python_tools' ? 'Audio Processing Panel' : 'Selection Panel'), /*#__PURE__*/React.createElement("div", {
|
||||
className: "text-xs text-zinc-500 mt-1"
|
||||
}, "Drop at edge to dock")), renderDock('top', 'Top'), /*#__PURE__*/React.createElement("div", {
|
||||
}, "Drop at edge to dock")), renderDock('top', 'Top'), console.log('[SEQ] dock-top xong'), /*#__PURE__*/React.createElement("div", {
|
||||
className: "flex-1 flex overflow-hidden"
|
||||
}, renderDock('left', 'Left'), /*#__PURE__*/React.createElement("div", {
|
||||
className: "flex-1 flex flex-col overflow-hidden min-w-0"
|
||||
@@ -28615,7 +28643,7 @@ STRICT CONSTRAINTS:
|
||||
})())), /*#__PURE__*/React.createElement("div", {
|
||||
className: "w-1 cursor-ew-resize hover:bg-cyan-500/50 active:bg-cyan-400 transition-colors shrink-0 z-10",
|
||||
onMouseDown: startColResize
|
||||
}), renderDock('right', 'Right')), renderDock('bottom', 'Bottom'), showMixer && /*#__PURE__*/React.createElement("div", {
|
||||
}), renderDock('right', 'Right')), renderDock('bottom', 'Bottom'), console.log('[SEQ] dock-bottom xong'), showMixer && /*#__PURE__*/React.createElement("div", {
|
||||
className: "flex flex-col border-t border-zinc-700 bg-slate-900 shrink-0 select-none",
|
||||
style: { height: mixerHeight + 'px' }
|
||||
}, /*#__PURE__*/React.createElement("div", {
|
||||
@@ -29123,7 +29151,7 @@ STRICT CONSTRAINTS:
|
||||
className: "flex-1"
|
||||
}, "Delete"), /*#__PURE__*/React.createElement("span", {
|
||||
className: "text-amber-400 text-xs font-semibold font-mono ml-auto pl-8"
|
||||
}, "Del")))), toastMessage && /*#__PURE__*/React.createElement("div", {
|
||||
}, "Del")))), console.log('[SEQ] trước toastMessage'), toastMessage && /*#__PURE__*/React.createElement("div", {
|
||||
className: "absolute top-14 right-4 z-50 px-4 py-2.5 rounded shadow-lg text-xs font-semibold flex items-center gap-2 border bg-zinc-900 text-zinc-100 border-zinc-800"
|
||||
}, /*#__PURE__*/React.createElement("span", {
|
||||
className: "inline-flex items-center shrink-0"
|
||||
@@ -29185,7 +29213,7 @@ STRICT CONSTRAINTS:
|
||||
handleSetTabColor(tabContextMenu.tabId, tabContextMenu.tabType, color);
|
||||
setTabContextMenu(null);
|
||||
}
|
||||
})))), /*#__PURE__*/React.createElement(AuthModal, {
|
||||
})))), console.log('[SEQ] trước AuthModal'), /*#__PURE__*/React.createElement(AuthModal, {
|
||||
isOpen: authModalOpen,
|
||||
mode: authMode,
|
||||
forceMandatory: isMandatoryLogin,
|
||||
@@ -29469,10 +29497,30 @@ STRICT CONSTRAINTS:
|
||||
}, vstScanning ? "Scanning..." : "Scan")
|
||||
),
|
||||
vstScanError && /*#__PURE__*/React.createElement("p", { className: "text-[10px] text-red-400" }, vstScanError),
|
||||
/*#__PURE__*/React.createElement("p", { className: "text-[10px] text-zinc-600" }, "Windows: C:\\Program Files\\Common Files\\VST3 \u00B7 macOS: /Library/Audio/Plug-Ins/Components \u00B7 Linux: /opt/daw_engine/vst3, ~/.vst3")
|
||||
/*#__PURE__*/React.createElement("p", { className: "text-[10px] text-zinc-600" }, "Windows: C:\\Program Files\\Common Files\\VST3 · macOS: /Library/Audio/Plug-Ins/Components · Linux: /opt/daw_engine/vst3, ~/.vst3"),
|
||||
console.log('[SEQ] cuối children — evaluate xong')
|
||||
)
|
||||
))
|
||||
};
|
||||
const root = ReactDOM.createRoot(document.getElementById('root'));
|
||||
root.render(/*#__PURE__*/React.createElement(App, null));
|
||||
// Legacy render — ĐỒNG BỘ COMMIT (createRoot/concurrent có vấn đề commit trên
|
||||
// môi trường này — root children 0 dù render chạy — bug màn đen 10:35)
|
||||
window.addEventListener('error', function(ev) {
|
||||
console.log('[GLOBAL-ERR]', ev.message, ev.filename, ev.lineno);
|
||||
});
|
||||
window.addEventListener('unhandledrejection', function(ev) {
|
||||
console.log('[GLOBAL-REJECT]', String(ev.reason && ev.reason.message || ev.reason).slice(0, 200));
|
||||
});
|
||||
class ErrorBoundary extends React.Component {
|
||||
constructor(props) { super(props); this.state = { err: null }; }
|
||||
static getDerivedStateFromError(err) { return { err: err }; }
|
||||
componentDidCatch(err, info) { console.log('[BOUNDARY-ERR]', err && err.message, info && info.componentStack ? info.componentStack.slice(0, 300) : ''); }
|
||||
render() {
|
||||
if (this.state.err) {
|
||||
return React.createElement('div', { style: { padding: 20, color: '#fbb', fontFamily: 'monospace', fontSize: 12, whiteSpace: 'pre-wrap' } },
|
||||
'⚠️ LỖI RENDER: ' + (this.state.err && this.state.err.message) + '\n' + (this.state.err && this.state.err.stack || '').slice(0, 600));
|
||||
}
|
||||
return this.props.children;
|
||||
}
|
||||
}
|
||||
ReactDOM.render(React.createElement(ErrorBoundary, null, React.createElement(App, null)), document.getElementById('root'));
|
||||
setTimeout(() => lucide.createIcons(), 300);
|
||||
|
||||
Reference in New Issue
Block a user