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

This commit is contained in:
Adam
2026-02-19 10:23:15 -06:00
parent 08a2d002b8
commit 6b8902e8b9
3 changed files with 69 additions and 3 deletions

View File

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

View File

@@ -1,6 +1,13 @@
import { describe, expect, test } from "bun:test" import { describe, expect, test } from "bun:test"
import { collectOpenProjectDeepLinks, drainPendingDeepLinks, parseDeepLink } from "./deep-links" 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", () => { describe("layout deep links", () => {
test("parses open-project 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(new Error("broken"), "fallback")).toBe("broken")
expect(errorMessage("unknown", "fallback")).toBe("fallback") 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" })
})
}) })

View File

@@ -62,6 +62,24 @@ export const errorMessage = (err: unknown, fallback: string) => {
return fallback 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[]) => { export const syncWorkspaceOrder = (local: string, dirs: string[], existing?: string[]) => {
if (!existing) return dirs if (!existing) return dirs
const keep = existing.filter((d) => d !== local && dirs.includes(d)) const keep = existing.filter((d) => d !== local && dirs.includes(d))