Просмотр исходного кода

fix(app): navigate to last session on project nav

Adam 2 месяцев назад
Родитель
Сommit
6b8902e8b9

+ 13 - 2
packages/app/src/pages/layout.tsx

@@ -61,6 +61,7 @@ import {
   displayName,
   errorMessage,
   getDraggableId,
+  projectSessionTarget,
   sortedRootSessions,
   syncWorkspaceOrder,
   workspaceKey,
@@ -82,6 +83,7 @@ export default function Layout(props: ParentProps) {
     Persist.global("layout.page", ["layout.page.v1"]),
     createStore({
       lastSession: {} as { [directory: string]: string },
+      lastSessionAt: {} as { [directory: string]: number },
       activeProject: undefined as string | undefined,
       activeWorkspace: undefined as string | undefined,
       workspaceOrder: {} as Record<string, string[]>,
@@ -1077,8 +1079,16 @@ export default function Layout(props: ParentProps) {
   function navigateToProject(directory: string | undefined) {
     if (!directory) return
     server.projects.touch(directory)
-    const lastSession = store.lastSession[directory]
-    navigateWithSidebarReset(`/${base64Encode(directory)}${lastSession ? `/session/${lastSession}` : ""}`)
+    const project = layout.projects
+      .list()
+      .find((item) => item.worktree === directory || item.sandboxes?.includes(directory))
+    const target = projectSessionTarget({
+      directory,
+      project,
+      lastSession: store.lastSession,
+      lastSessionAt: store.lastSessionAt,
+    })
+    navigateWithSidebarReset(`/${base64Encode(target.directory)}${target.id ? `/session/${target.id}` : ""}`)
   }
 
   function navigateToSession(session: Session | undefined) {
@@ -1433,6 +1443,7 @@ export default function Layout(props: ParentProps) {
         const directory = decode64(dir)
         if (!directory) return
         setStore("lastSession", directory, id)
+        setStore("lastSessionAt", directory, Date.now())
         notification.session.markViewed(id)
         const expanded = untrack(() => store.workspaceExpanded[directory])
         if (expanded === false) {

+ 38 - 1
packages/app/src/pages/layout/helpers.test.ts

@@ -1,6 +1,13 @@
 import { describe, expect, test } from "bun:test"
 import { collectOpenProjectDeepLinks, drainPendingDeepLinks, parseDeepLink } from "./deep-links"
-import { displayName, errorMessage, getDraggableId, syncWorkspaceOrder, workspaceKey } from "./helpers"
+import {
+  displayName,
+  errorMessage,
+  getDraggableId,
+  projectSessionTarget,
+  syncWorkspaceOrder,
+  workspaceKey,
+} from "./helpers"
 
 describe("layout deep links", () => {
   test("parses open-project deep links", () => {
@@ -89,4 +96,34 @@ describe("layout workspace helpers", () => {
     expect(errorMessage(new Error("broken"), "fallback")).toBe("broken")
     expect(errorMessage("unknown", "fallback")).toBe("fallback")
   })
+
+  test("picks newest session across project workspaces", () => {
+    const result = projectSessionTarget({
+      directory: "/root",
+      project: { worktree: "/root", sandboxes: ["/root/a", "/root/b"] },
+      lastSession: {
+        "/root": "root-session",
+        "/root/a": "sandbox-a",
+        "/root/b": "sandbox-b",
+      },
+      lastSessionAt: {
+        "/root": 1,
+        "/root/a": 3,
+        "/root/b": 2,
+      },
+    })
+
+    expect(result).toEqual({ directory: "/root/a", id: "sandbox-a", at: 3 })
+  })
+
+  test("falls back to project route when no session exists", () => {
+    const result = projectSessionTarget({
+      directory: "/root",
+      project: { worktree: "/root", sandboxes: ["/root/a"] },
+      lastSession: {},
+      lastSessionAt: {},
+    })
+
+    expect(result).toEqual({ directory: "/root" })
+  })
 })

+ 18 - 0
packages/app/src/pages/layout/helpers.ts

@@ -62,6 +62,24 @@ export const errorMessage = (err: unknown, fallback: string) => {
   return fallback
 }
 
+export function projectSessionTarget(input: {
+  directory: string
+  project?: { worktree: string; sandboxes?: string[] }
+  lastSession: Record<string, string>
+  lastSessionAt: Record<string, number>
+}): { directory: string; id?: string; at?: number } {
+  const dirs = input.project ? [input.project.worktree, ...(input.project.sandboxes ?? [])] : [input.directory]
+  const best = dirs.reduce<{ directory: string; id: string; at: number } | undefined>((result, directory) => {
+    const id = input.lastSession[directory]
+    if (!id) return result
+    const at = input.lastSessionAt[directory] ?? 0
+    if (result && result.at >= at) return result
+    return { directory, id, at }
+  }, undefined)
+  if (best) return best
+  return { directory: input.directory }
+}
+
 export const syncWorkspaceOrder = (local: string, dirs: string[], existing?: string[]) => {
   if (!existing) return dirs
   const keep = existing.filter((d) => d !== local && dirs.includes(d))