@@ -32,6 +32,12 @@ const keyFor = (directory: string, id: string) => `${directory}\n${id}`
3232
3333const cmp = ( a : string , b : string ) => ( a < b ? - 1 : a > b ? 1 : 0 )
3434
35+ function merge < T extends { id : string } > ( a : readonly T [ ] , b : readonly T [ ] ) {
36+ const map = new Map ( a . map ( ( item ) => [ item . id , item ] as const ) )
37+ for ( const item of b ) map . set ( item . id , item )
38+ return [ ...map . values ( ) ] . sort ( ( x , y ) => cmp ( x . id , y . id ) )
39+ }
40+
3541type OptimisticStore = {
3642 message : Record < string , Message [ ] | undefined >
3743 part : Record < string , Part [ ] | undefined >
@@ -119,6 +125,7 @@ export const { use: useSync, provider: SyncProvider } = createSimpleContext({
119125 const seen = new Map < string , Set < string > > ( )
120126 const [ meta , setMeta ] = createStore ( {
121127 limit : { } as Record < string , number > ,
128+ cursor : { } as Record < string , string | undefined > ,
122129 complete : { } as Record < string , boolean > ,
123130 loading : { } as Record < string , boolean > ,
124131 } )
@@ -157,6 +164,7 @@ export const { use: useSync, provider: SyncProvider } = createSimpleContext({
157164 for ( const sessionID of sessionIDs ) {
158165 const key = keyFor ( directory , sessionID )
159166 delete draft . limit [ key ]
167+ delete draft . cursor [ key ]
160168 delete draft . complete [ key ]
161169 delete draft . loading [ key ]
162170 }
@@ -187,17 +195,24 @@ export const { use: useSync, provider: SyncProvider } = createSimpleContext({
187195 evict ( directory , setStore , stale )
188196 }
189197
190- const fetchMessages = async ( input : { client : typeof sdk . client ; sessionID : string ; limit : number } ) => {
198+ const fetchMessages = async ( input : {
199+ client : typeof sdk . client
200+ sessionID : string
201+ limit : number
202+ before ?: string
203+ } ) => {
191204 const messages = await retry ( ( ) =>
192- input . client . session . messages ( { sessionID : input . sessionID , limit : input . limit } ) ,
205+ input . client . session . messages ( { sessionID : input . sessionID , limit : input . limit , before : input . before } ) ,
193206 )
194207 const items = ( messages . data ?? [ ] ) . filter ( ( x ) => ! ! x ?. info ?. id )
195208 const session = items . map ( ( x ) => x . info ) . sort ( ( a , b ) => cmp ( a . id , b . id ) )
196209 const part = items . map ( ( message ) => ( { id : message . info . id , part : sortParts ( message . parts ) } ) )
210+ const cursor = messages . response . headers . get ( "x-next-cursor" ) ?? undefined
197211 return {
198212 session,
199213 part,
200- complete : session . length < input . limit ,
214+ cursor,
215+ complete : ! cursor ,
201216 }
202217 }
203218
@@ -209,6 +224,8 @@ export const { use: useSync, provider: SyncProvider } = createSimpleContext({
209224 setStore : Setter
210225 sessionID : string
211226 limit : number
227+ before ?: string
228+ mode ?: "replace" | "prepend"
212229 } ) => {
213230 const key = keyFor ( input . directory , input . sessionID )
214231 if ( meta . loading [ key ] ) return
@@ -217,17 +234,22 @@ export const { use: useSync, provider: SyncProvider } = createSimpleContext({
217234 await fetchMessages ( input )
218235 . then ( ( next ) => {
219236 if ( ! tracked ( input . directory , input . sessionID ) ) return
237+ const [ store ] = globalSync . child ( input . directory , { bootstrap : false } )
238+ const cached = input . mode === "prepend" ? ( store . message [ input . sessionID ] ?? [ ] ) : [ ]
239+ const message = input . mode === "prepend" ? merge ( cached , next . session ) : next . session
220240 batch ( ( ) => {
221- input . setStore ( "message" , input . sessionID , reconcile ( next . session , { key : "id" } ) )
241+ input . setStore ( "message" , input . sessionID , reconcile ( message , { key : "id" } ) )
222242 for ( const p of next . part ) {
223243 input . setStore ( "part" , p . id , p . part )
224244 }
225- setMeta ( "limit" , key , input . limit )
245+ setMeta ( "limit" , key , message . length )
246+ setMeta ( "cursor" , key , next . cursor )
226247 setMeta ( "complete" , key , next . complete )
227248 setSessionPrefetch ( {
228249 directory : input . directory ,
229250 sessionID : input . sessionID ,
230- limit : input . limit ,
251+ limit : message . length ,
252+ cursor : next . cursor ,
231253 complete : next . complete ,
232254 } )
233255 } )
@@ -312,6 +334,7 @@ export const { use: useSync, provider: SyncProvider } = createSimpleContext({
312334 if ( seeded && store . message [ sessionID ] !== undefined && meta . limit [ key ] === undefined ) {
313335 batch ( ( ) => {
314336 setMeta ( "limit" , key , seeded . limit )
337+ setMeta ( "cursor" , key , seeded . cursor )
315338 setMeta ( "complete" , key , seeded . complete )
316339 setMeta ( "loading" , key , false )
317340 } )
@@ -325,6 +348,7 @@ export const { use: useSync, provider: SyncProvider } = createSimpleContext({
325348 if ( seeded && store . message [ sessionID ] !== undefined && meta . limit [ key ] === undefined ) {
326349 batch ( ( ) => {
327350 setMeta ( "limit" , key , seeded . limit )
351+ setMeta ( "cursor" , key , seeded . cursor )
328352 setMeta ( "complete" , key , seeded . complete )
329353 setMeta ( "loading" , key , false )
330354 } )
@@ -420,7 +444,7 @@ export const { use: useSync, provider: SyncProvider } = createSimpleContext({
420444 if ( store . message [ sessionID ] === undefined ) return false
421445 if ( meta . limit [ key ] === undefined ) return false
422446 if ( meta . complete [ key ] ) return false
423- return true
447+ return ! ! meta . cursor [ key ]
424448 } ,
425449 loading ( sessionID : string ) {
426450 const key = keyFor ( sdk . directory , sessionID )
@@ -435,14 +459,17 @@ export const { use: useSync, provider: SyncProvider } = createSimpleContext({
435459 const step = count ?? messagePageSize
436460 if ( meta . loading [ key ] ) return
437461 if ( meta . complete [ key ] ) return
462+ const before = meta . cursor [ key ]
463+ if ( ! before ) return
438464
439- const currentLimit = meta . limit [ key ] ?? messagePageSize
440465 await loadMessages ( {
441466 directory,
442467 client,
443468 setStore,
444469 sessionID,
445- limit : currentLimit + step ,
470+ limit : step ,
471+ before,
472+ mode : "prepend" ,
446473 } )
447474 } ,
448475 } ,
0 commit comments