fix(app): increase project session limit and add scrolling (#31035)
This commit is contained in:
parent
b36b85936d
commit
f750deaa3e
5 changed files with 48 additions and 12 deletions
|
|
@ -133,6 +133,7 @@ describe("createChildStoreManager", () => {
|
||||||
const [store] = manager.child("/project")
|
const [store] = manager.child("/project")
|
||||||
|
|
||||||
expect(store.status).toBe("loading")
|
expect(store.status).toBe("loading")
|
||||||
|
expect(store.limit).toBe(5)
|
||||||
expect(bootstraps).toEqual(["/project"])
|
expect(bootstraps).toEqual(["/project"])
|
||||||
} finally {
|
} finally {
|
||||||
dispose()
|
dispose()
|
||||||
|
|
|
||||||
|
|
@ -134,6 +134,27 @@ describe("applyGlobalEvent", () => {
|
||||||
})
|
})
|
||||||
|
|
||||||
describe("applyDirectoryEvent", () => {
|
describe("applyDirectoryEvent", () => {
|
||||||
|
test("preserves a Home-specific retained session limit", () => {
|
||||||
|
const [store, setStore] = createStore(
|
||||||
|
baseState({
|
||||||
|
limit: 1,
|
||||||
|
session: [rootSession({ id: "a" }), rootSession({ id: "b" }), rootSession({ id: "c" })],
|
||||||
|
}),
|
||||||
|
)
|
||||||
|
|
||||||
|
applyDirectoryEvent({
|
||||||
|
event: { type: "session.created", properties: { info: rootSession({ id: "d" }) } },
|
||||||
|
store,
|
||||||
|
setStore,
|
||||||
|
push() {},
|
||||||
|
directory: "/tmp",
|
||||||
|
loadLsp() {},
|
||||||
|
retainedLimit: 3,
|
||||||
|
})
|
||||||
|
|
||||||
|
expect(store.session).toHaveLength(3)
|
||||||
|
})
|
||||||
|
|
||||||
test("inserts root sessions in sorted order and updates sessionTotal", () => {
|
test("inserts root sessions in sorted order and updates sessionTotal", () => {
|
||||||
const [store, setStore] = createStore(
|
const [store, setStore] = createStore(
|
||||||
baseState({
|
baseState({
|
||||||
|
|
|
||||||
|
|
@ -99,8 +99,10 @@ export function applyDirectoryEvent(input: {
|
||||||
loadLsp: () => void
|
loadLsp: () => void
|
||||||
vcsCache?: VcsCache
|
vcsCache?: VcsCache
|
||||||
setSessionTodo?: (sessionID: string, todos: Todo[] | undefined) => void
|
setSessionTodo?: (sessionID: string, todos: Todo[] | undefined) => void
|
||||||
|
retainedLimit?: number
|
||||||
}) {
|
}) {
|
||||||
const event = input.event
|
const event = input.event
|
||||||
|
const limit = Math.max(input.store.limit, input.retainedLimit ?? 0)
|
||||||
switch (event.type) {
|
switch (event.type) {
|
||||||
case "server.instance.disposed": {
|
case "server.instance.disposed": {
|
||||||
input.push(input.directory)
|
input.push(input.directory)
|
||||||
|
|
@ -115,7 +117,7 @@ export function applyDirectoryEvent(input: {
|
||||||
}
|
}
|
||||||
const next = input.store.session.slice()
|
const next = input.store.session.slice()
|
||||||
next.splice(result.index, 0, info)
|
next.splice(result.index, 0, info)
|
||||||
const trimmed = trimSessions(next, { limit: input.store.limit, permission: input.store.permission })
|
const trimmed = trimSessions(next, { limit, permission: input.store.permission })
|
||||||
input.setStore("session", reconcile(trimmed, { key: "id" }))
|
input.setStore("session", reconcile(trimmed, { key: "id" }))
|
||||||
cleanupDroppedSessionCaches(input.store, input.setStore, trimmed, input.setSessionTodo)
|
cleanupDroppedSessionCaches(input.store, input.setStore, trimmed, input.setSessionTodo)
|
||||||
if (!info.parentID) input.setStore("sessionTotal", (value) => value + 1)
|
if (!info.parentID) input.setStore("sessionTotal", (value) => value + 1)
|
||||||
|
|
@ -145,7 +147,7 @@ export function applyDirectoryEvent(input: {
|
||||||
}
|
}
|
||||||
const next = input.store.session.slice()
|
const next = input.store.session.slice()
|
||||||
next.splice(result.index, 0, info)
|
next.splice(result.index, 0, info)
|
||||||
const trimmed = trimSessions(next, { limit: input.store.limit, permission: input.store.permission })
|
const trimmed = trimSessions(next, { limit, permission: input.store.permission })
|
||||||
input.setStore("session", reconcile(trimmed, { key: "id" }))
|
input.setStore("session", reconcile(trimmed, { key: "id" }))
|
||||||
cleanupDroppedSessionCaches(input.store, input.setStore, trimmed, input.setSessionTodo)
|
cleanupDroppedSessionCaches(input.store, input.setStore, trimmed, input.setSessionTodo)
|
||||||
break
|
break
|
||||||
|
|
|
||||||
|
|
@ -247,17 +247,21 @@ export function createServerSyncContextInner(_serverSDK?: ServerSDK) {
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
|
|
||||||
async function loadSessions(directory: string) {
|
async function loadSessions(directory: string, options?: { limit?: number }) {
|
||||||
const key = directoryKey(directory)
|
const key = directoryKey(directory)
|
||||||
const pending = sessionLoads.get(key)
|
const pending = sessionLoads.get(key)
|
||||||
if (pending) return pending
|
if (pending) {
|
||||||
|
await pending
|
||||||
|
return loadSessions(directory, options)
|
||||||
|
}
|
||||||
|
|
||||||
children.pin(key)
|
children.pin(key)
|
||||||
const [store, setStore] = children.child(directory, { bootstrap: false })
|
const [store, setStore] = children.child(directory, { bootstrap: false })
|
||||||
const meta = sessionMeta.get(key)
|
const meta = sessionMeta.get(key)
|
||||||
if (meta && meta.limit >= store.limit) {
|
const retainedLimit = Math.max(store.limit, options?.limit ?? 0, meta?.limit ?? 0)
|
||||||
|
if (meta && meta.limit >= retainedLimit) {
|
||||||
const next = trimSessions(store.session, {
|
const next = trimSessions(store.session, {
|
||||||
limit: store.limit,
|
limit: retainedLimit,
|
||||||
permission: store.permission,
|
permission: store.permission,
|
||||||
})
|
})
|
||||||
if (next.length !== store.session.length) {
|
if (next.length !== store.session.length) {
|
||||||
|
|
@ -268,7 +272,7 @@ export function createServerSyncContextInner(_serverSDK?: ServerSDK) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
const limit = Math.max(store.limit + SESSION_RECENT_LIMIT, SESSION_RECENT_LIMIT)
|
const limit = Math.max(retainedLimit + SESSION_RECENT_LIMIT, SESSION_RECENT_LIMIT)
|
||||||
const promise = queryClient
|
const promise = queryClient
|
||||||
.fetchQuery({
|
.fetchQuery({
|
||||||
...queryOptionsApi.sessions(key),
|
...queryOptionsApi.sessions(key),
|
||||||
|
|
@ -283,7 +287,7 @@ export function createServerSyncContextInner(_serverSDK?: ServerSDK) {
|
||||||
.filter((s) => !!s?.id)
|
.filter((s) => !!s?.id)
|
||||||
.filter((s) => !s.time?.archived)
|
.filter((s) => !s.time?.archived)
|
||||||
.sort((a, b) => (a.id < b.id ? -1 : a.id > b.id ? 1 : 0))
|
.sort((a, b) => (a.id < b.id ? -1 : a.id > b.id ? 1 : 0))
|
||||||
const limit = store.limit
|
const limit = Math.max(store.limit, options?.limit ?? 0, sessionMeta.get(key)?.limit ?? 0)
|
||||||
const childSessions = store.session.filter((s) => !!s.parentID)
|
const childSessions = store.session.filter((s) => !!s.parentID)
|
||||||
const sessions = trimSessions([...nonArchived, ...childSessions], {
|
const sessions = trimSessions([...nonArchived, ...childSessions], {
|
||||||
limit,
|
limit,
|
||||||
|
|
@ -400,6 +404,7 @@ export function createServerSyncContextInner(_serverSDK?: ServerSDK) {
|
||||||
setStore,
|
setStore,
|
||||||
push: queue.push,
|
push: queue.push,
|
||||||
setSessionTodo,
|
setSessionTodo,
|
||||||
|
retainedLimit: sessionMeta.get(key)?.limit,
|
||||||
vcsCache: children.vcsCache.get(key),
|
vcsCache: children.vcsCache.get(key),
|
||||||
loadLsp: () => {
|
loadLsp: () => {
|
||||||
void queryClient.fetchQuery(queryOptionsApi.lsp(key))
|
void queryClient.fetchQuery(queryOptionsApi.lsp(key))
|
||||||
|
|
|
||||||
|
|
@ -47,7 +47,7 @@ import { ServerRowMenu } from "@/components/server/server-row-menu"
|
||||||
import { ServerHealthIndicator } from "@/components/server/server-row"
|
import { ServerHealthIndicator } from "@/components/server/server-row"
|
||||||
import { type ServerHealth } from "@/utils/server-health"
|
import { type ServerHealth } from "@/utils/server-health"
|
||||||
|
|
||||||
const HOME_SESSION_LIMIT = 15
|
const HOME_SESSION_LIMIT = 64
|
||||||
const HOME_ROW_LAYOUT =
|
const HOME_ROW_LAYOUT =
|
||||||
"flex min-w-0 w-full shrink-0 cursor-default items-center rounded-[6px] bg-transparent text-left transition-[background-color,color,box-shadow] duration-[120ms] ease-in-out focus-visible:outline-none"
|
"flex min-w-0 w-full shrink-0 cursor-default items-center rounded-[6px] bg-transparent text-left transition-[background-color,color,box-shadow] duration-[120ms] ease-in-out focus-visible:outline-none"
|
||||||
const HOME_ROW_BASE = `${HOME_ROW_LAYOUT} border-0`
|
const HOME_ROW_BASE = `${HOME_ROW_LAYOUT} border-0`
|
||||||
|
|
@ -166,7 +166,11 @@ function HomeDesign() {
|
||||||
const sessionLoad = useQuery(() => ({
|
const sessionLoad = useQuery(() => ({
|
||||||
queryKey: ["home", "sessions", state.selection.server, ...projectDirectories()] as const,
|
queryKey: ["home", "sessions", state.selection.server, ...projectDirectories()] as const,
|
||||||
queryFn: async () => {
|
queryFn: async () => {
|
||||||
await Promise.all(projectDirectories().map((directory) => focusedSync().project.loadSessions(directory)))
|
await Promise.all(
|
||||||
|
projectDirectories().map((directory) =>
|
||||||
|
focusedSync().project.loadSessions(directory, { limit: HOME_SESSION_LIMIT }),
|
||||||
|
),
|
||||||
|
)
|
||||||
return null
|
return null
|
||||||
},
|
},
|
||||||
}))
|
}))
|
||||||
|
|
@ -339,7 +343,7 @@ function HomeDesign() {
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div class="rounded-[10px] shadow-[var(--v2-elevation-raised)] m-2 bg-v2-background-bg-base self-stretch flex-1">
|
<div class="rounded-[10px] shadow-[var(--v2-elevation-raised)] m-2 min-h-0 lg:overflow-hidden bg-v2-background-bg-base self-stretch flex-1">
|
||||||
<div class="mx-auto grid w-full h-full max-w-[1080px] gap-8 px-6 pb-16 lg:grid-cols-[280px_minmax(0,720px)]">
|
<div class="mx-auto grid w-full h-full max-w-[1080px] gap-8 px-6 pb-16 lg:grid-cols-[280px_minmax(0,720px)]">
|
||||||
<HomeProjectColumn
|
<HomeProjectColumn
|
||||||
projects={projects()}
|
projects={projects()}
|
||||||
|
|
@ -365,7 +369,10 @@ function HomeDesign() {
|
||||||
language={language}
|
language={language}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<section class="min-w-0 flex-1 flex flex-col pt-12" aria-label={language.t("sidebar.project.recentSessions")}>
|
<section
|
||||||
|
class="min-h-0 min-w-0 flex-1 flex flex-col pt-12"
|
||||||
|
aria-label={language.t("sidebar.project.recentSessions")}
|
||||||
|
>
|
||||||
<HomeSessionSearch
|
<HomeSessionSearch
|
||||||
value={state.search}
|
value={state.search}
|
||||||
placeholder={language.t("home.sessions.search.placeholder")}
|
placeholder={language.t("home.sessions.search.placeholder")}
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue