docs(dashboard): explain directory and identity lifecycle
This commit is contained in:
@@ -55,6 +55,7 @@ export default function DashboardSidebar() {
|
||||
// 路径workspace优先于查询和本地选择;slug由目录解析,活动board可补充该路由引用的真实ID。
|
||||
const workspaceRef = params.workspace || searchParams.get("workspace") || selectedWorkspace;
|
||||
const currentWorkspace = workspaces.find((item) => matchesRef(item, workspaceRef));
|
||||
// 目录未解析时可暂用路由 ref;这里的字符串回退不是“空间存在/有权限”的证明,后续服务请求仍需验证。
|
||||
const workspace = currentWorkspace?.id || (activeBoard?.workspace_ref === workspaceRef ? activeBoard.workspace_id : workspaceRef);
|
||||
|
||||
/** demo用本地资料,真实模式并行加载profile/空间;active+signal阻止卸载后的资料结果写入。 */
|
||||
@@ -68,6 +69,7 @@ export default function DashboardSidebar() {
|
||||
}
|
||||
let active = true;
|
||||
const controller = new AbortController();
|
||||
// 只切换加载状态,不清空之前 profile/workspaces/error;成功读取也不会在这里自动清除旧 error。
|
||||
setStatus("loading");
|
||||
loadWorkspaceHome(api, controller.signal)
|
||||
.then(({ profile, workspaces }) => {
|
||||
@@ -89,6 +91,7 @@ export default function DashboardSidebar() {
|
||||
/** 每次目录加载先留本空间项,再取完整分页;仅最新listVersion可采用列表,换空间时中断旧请求。 */
|
||||
useEffect(() => {
|
||||
if (!workspace || status !== "ready") return;
|
||||
// listVersion 只保护目录结果采用,不是网络取消或账号代次;创建成功时也递增它,防较旧列表覆盖新项。
|
||||
const version = ++listVersion.current;
|
||||
let active = true;
|
||||
setBoards((list) => list.filter((board) => board.workspace_id === workspace));
|
||||
@@ -120,6 +123,7 @@ export default function DashboardSidebar() {
|
||||
setCreating(true);
|
||||
setError("");
|
||||
try {
|
||||
// 捕获触发本次创建的 workspace/demo;await 后不另核对挂载/账户,宿主生命周期应避免旧操作采用到新界面。
|
||||
const board = demo
|
||||
? { id: crypto.randomUUID(), workspace_id: workspace, title: DEFAULT_BOARD_TITLE, kind: "board" as const, version: 1 }
|
||||
: await createBoard(api, DEFAULT_BOARD_TITLE, workspace, (created) => {
|
||||
@@ -128,6 +132,7 @@ export default function DashboardSidebar() {
|
||||
});
|
||||
listVersion.current += 1;
|
||||
setBoards((list) => [board, ...list]);
|
||||
// demo 已先更新 React 列表,随后 Storage 写失败也不回滚该列表;错误仅报告在侧栏。
|
||||
if (demo) localStorage.setItem("endoleap_demo_boards_" + workspace, JSON.stringify([board, ...readDemoBoards(workspace)]));
|
||||
setQuery("");
|
||||
router.push(boardHref(currentWorkspace?.slug || board.workspace_id, board.slug || board.id, demo));
|
||||
@@ -158,6 +163,7 @@ export default function DashboardSidebar() {
|
||||
}
|
||||
|
||||
/** 真实退出先retireIdentity并完成本地关闭/保存,再请求服务登出;失败留在当前界面报告,不跳过保存。 */
|
||||
// 此函数无独立 busy/ref 互斥;多次点击仍可调用。退休身份成功但上游登出失败时也不会复活旧 runtime。
|
||||
async function logout() {
|
||||
try {
|
||||
if (!demo) { await runtimeHost.retireIdentity(); await api("auth/logout", { method: "POST" }); }
|
||||
@@ -177,6 +183,7 @@ export default function DashboardSidebar() {
|
||||
|
||||
/** 成功后同时更新本地目录与匹配的活动board元数据;错误转换成文案交EditableTitle保留草稿显示。 */
|
||||
async function renameTitle(title: string) {
|
||||
// 优先使用 activeBoard.id;编辑按钮先约束路由/空间匹配,此函数本身不重新执行同一组匹配检查。
|
||||
const id = activeBoard?.id || params.board;
|
||||
if (!id || !workspace) return;
|
||||
try {
|
||||
@@ -184,6 +191,7 @@ export default function DashboardSidebar() {
|
||||
const list = readDemoBoards(workspace);
|
||||
localStorage.setItem("endoleap_demo_boards_" + workspace, JSON.stringify(list.map((board) => board.id === id ? { ...board, title } : board)));
|
||||
} else await renameBoard(api, workspace, id, title);
|
||||
// 目录更新按 ID 匹配,活动元数据更新额外比 workspace;此处不递增 listVersion,旧目录读取仍可能随后到达。
|
||||
setBoards((list) => list.map((board) => board.id === id ? { ...board, title } : board));
|
||||
setActiveBoard((board) => board?.id === id && board.workspace_id === workspace ? { ...board, title } : board);
|
||||
} catch (error) { throw new Error(errorText(error)); }
|
||||
@@ -196,6 +204,7 @@ export default function DashboardSidebar() {
|
||||
const transition = { duration: reduceMotion ? 0 : 0.24, ease: [0.22, 1, 0.36, 1] as const };
|
||||
const roleLabel = currentWorkspace?.role === "admin" ? "管理员" : currentWorkspace?.role === "editor" ? "编辑者" : currentWorkspace?.role === "viewer" ? "查看者" : "工作空间";
|
||||
|
||||
// 折叠只改变显示和键盘可达性,侧栏仍挂载,账户/目录状态和 effect 不因收起而销毁。
|
||||
// 展示分为浮动条、侧栏目录、共享账户popover和账户面板;路由/空间作为key让标题及目录局部状态重置。
|
||||
return (
|
||||
<>
|
||||
|
||||
Reference in New Issue
Block a user