@@ -2,7 +2,7 @@ import { useDialog } from "../ui/dialog"
22import { DialogSelect } from "../ui/dialog-select"
33import { useRoute } from "../context/route"
44import { useSync } from "../context/sync"
5- import { createMemo , createResource , createSignal , onMount } from "solid-js"
5+ import { createMemo , createResource , createSignal , onCleanup , onMount } from "solid-js"
66import path from "path"
77import { Locale } from "../util/locale"
88import { useProject } from "../context/project"
@@ -17,6 +17,30 @@ import { Spinner } from "./spinner"
1717import { errorMessage } from "../util/error"
1818import { DialogSessionDeleteFailed } from "./dialog-session-delete-failed"
1919import { useCommandShortcut } from "../keymap"
20+ import { useEvent } from "../context/event"
21+
22+ type SessionListFilter = { scope ?: "project" ; path ?: string }
23+
24+ export function createDialogSessionListQuery ( input : { search ?: string ; filter : SessionListFilter } ) {
25+ const search = input . search ?. trim ( )
26+ return {
27+ roots : true ,
28+ limit : search ? 30 : 100 ,
29+ ...( search ? { search } : { } ) ,
30+ ...input . filter ,
31+ }
32+ }
33+
34+ export function loadDialogSessionList < T > ( input : {
35+ search ?: string
36+ filter : SessionListFilter
37+ list : ( query : ReturnType < typeof createDialogSessionListQuery > ) => Promise < { data ?: T [ ] } >
38+ } ) {
39+ return input . list ( createDialogSessionListQuery ( input ) ) . then (
40+ ( result ) => result . data ,
41+ ( ) => undefined ,
42+ )
43+ }
2044
2145export function DialogSessionList ( ) {
2246 const dialog = useDialog ( )
@@ -25,25 +49,54 @@ export function DialogSessionList() {
2549 const project = useProject ( )
2650 const { theme } = useTheme ( )
2751 const sdk = useSDK ( )
52+ const event = useEvent ( )
2853 const local = useLocal ( )
2954 const toast = useToast ( )
3055 const [ toDelete , setToDelete ] = createSignal < string > ( )
56+ const [ deleted , setDeleted ] = createSignal ( new Set < string > ( ) )
3157 const [ search , setSearch ] = createDebouncedSignal ( "" , 150 )
3258 const deleteHint = useCommandShortcut ( "session.delete" )
3359 const quickSwitch1 = useCommandShortcut ( "session.quick_switch.1" )
3460 const quickSwitch9 = useCommandShortcut ( "session.quick_switch.9" )
3561
62+ const [ browseResults , { refetch : refetchBrowse } ] = createResource (
63+ ( ) => sync . session . query ( ) ,
64+ ( filter ) => loadDialogSessionList ( { filter, list : ( query ) => sdk . client . session . list ( query ) } ) ,
65+ )
3666 const [ searchResults , { refetch } ] = createResource (
3767 ( ) => ( { query : search ( ) , filter : sync . session . query ( ) } ) ,
38- async ( input ) => {
68+ ( input ) => {
3969 if ( ! input . query ) return undefined
40- const result = await sdk . client . session . list ( { search : input . query , limit : 30 , ...input . filter } )
41- return result . data ?? [ ]
70+ return loadDialogSessionList ( {
71+ search : input . query ,
72+ filter : input . filter ,
73+ list : ( query ) => sdk . client . session . list ( query ) ,
74+ } )
4275 } ,
4376 )
4477
4578 const currentSessionID = createMemo ( ( ) => ( route . data . type === "session" ? route . data . sessionID : undefined ) )
46- const sessions = createMemo ( ( ) => searchResults ( ) ?? sync . data . session )
79+ const sessions = createMemo ( ( ) => {
80+ const result = searchResults ( ) ?? browseResults ( ) ?? sync . data . session
81+ const synced = new Map ( sync . data . session . map ( ( session ) => [ session . id , session ] ) )
82+ const ids = new Set ( result . map ( ( session ) => session . id ) )
83+ const extra = [ currentSessionID ( ) , ...local . session . pinned ( ) ] . flatMap ( ( id ) => {
84+ if ( ! id || ids . has ( id ) ) return [ ]
85+ const session = synced . get ( id )
86+ if ( session ) ids . add ( id )
87+ return session ? [ session ] : [ ]
88+ } )
89+ const query = search ( ) . trim ( ) . toLowerCase ( )
90+ return [ ...result . map ( ( session ) => synced . get ( session . id ) ?? session ) , ...extra ]
91+ . filter ( ( session ) => ! deleted ( ) . has ( session . id ) )
92+ . filter ( ( session ) => ! query || session . title . toLowerCase ( ) . includes ( query ) )
93+ } )
94+
95+ onCleanup (
96+ event . on ( "session.deleted" , ( event ) => {
97+ setDeleted ( ( current ) => new Set ( current ) . add ( event . properties . info . id ) )
98+ } ) ,
99+ )
47100
48101 function recover ( session : NonNullable < ReturnType < typeof sessions > [ number ] > ) {
49102 const workspace = project . workspace . get ( session . workspaceID ! )
@@ -108,6 +161,7 @@ export function DialogSessionList() {
108161 }
109162 await project . workspace . sync ( )
110163 await sync . session . refresh ( )
164+ await refetchBrowse ( )
111165 if ( search ( ) ) await refetch ( )
112166 if ( info ?. workspaceID === session . workspaceID ) {
113167 route . navigate ( { type : "home" } )
@@ -138,7 +192,7 @@ export function DialogSessionList() {
138192 . map ( ( x ) => x . id )
139193 }
140194
141- const [ browseOrder ] = createSignal < string [ ] > ( orderByRecency ( sync . data . session ) )
195+ const browseOrder = createMemo ( ( ) => orderByRecency ( browseResults ( ) ?? sync . data . session ) )
142196
143197 const quickSwitchHint = createMemo ( ( ) => {
144198 const first = quickSwitch1 ( )
@@ -160,7 +214,9 @@ export function DialogSessionList() {
160214 )
161215
162216 const searchResult = searchResults ( )
163- const displayOrder = searchResult ? orderByRecency ( searchResult ) : browseOrder ( )
217+ const order = searchResult ? orderByRecency ( sessions ( ) ) : browseOrder ( )
218+ const current = currentSessionID ( )
219+ const displayOrder = current && sessionMap . has ( current ) && ! order . includes ( current ) ? [ ...order , current ] : order
164220
165221 const pinned = local . session . pinned ( ) . filter ( ( id ) => sessionMap . has ( id ) )
166222 const pinnedSet = new Set ( pinned )
@@ -218,6 +274,7 @@ export function DialogSessionList() {
218274 title = "Sessions"
219275 options = { options ( ) }
220276 skipFilter = { true }
277+ preserveSelection = { true }
221278 current = { currentSessionID ( ) }
222279 onFilter = { setSearch }
223280 onMove = { ( ) => {
@@ -279,6 +336,7 @@ export function DialogSessionList() {
279336 if ( status && status !== "connected" ) {
280337 await sync . session . refresh ( )
281338 }
339+ await refetchBrowse ( )
282340 if ( search ( ) ) await refetch ( )
283341 setToDelete ( undefined )
284342 return
0 commit comments