fix(app): message loading
This commit is contained in:
@@ -233,8 +233,15 @@ export const { use: useSync, provider: SyncProvider } = createSimpleContext({
|
|||||||
})
|
})
|
||||||
})
|
})
|
||||||
.finally(() => {
|
.finally(() => {
|
||||||
if (!tracked(input.directory, input.sessionID)) return
|
setMeta(
|
||||||
setMeta("loading", key, false)
|
produce((draft) => {
|
||||||
|
if (!tracked(input.directory, input.sessionID)) {
|
||||||
|
delete draft.loading[key]
|
||||||
|
return
|
||||||
|
}
|
||||||
|
draft.loading[key] = false
|
||||||
|
}),
|
||||||
|
)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -60,6 +60,7 @@ const emptyFollowups: (FollowupDraft & { id: string })[] = []
|
|||||||
type SessionHistoryWindowInput = {
|
type SessionHistoryWindowInput = {
|
||||||
sessionID: () => string | undefined
|
sessionID: () => string | undefined
|
||||||
messagesReady: () => boolean
|
messagesReady: () => boolean
|
||||||
|
loaded: () => number
|
||||||
visibleUserMessages: () => UserMessage[]
|
visibleUserMessages: () => UserMessage[]
|
||||||
historyMore: () => boolean
|
historyMore: () => boolean
|
||||||
historyLoading: () => boolean
|
historyLoading: () => boolean
|
||||||
@@ -157,23 +158,39 @@ function createSessionHistoryWindow(input: SessionHistoryWindowInput) {
|
|||||||
|
|
||||||
const start = turnStart()
|
const start = turnStart()
|
||||||
const beforeVisible = input.visibleUserMessages().length
|
const beforeVisible = input.visibleUserMessages().length
|
||||||
|
let loaded = input.loaded()
|
||||||
|
|
||||||
if (start > 0) setTurnStart(0)
|
if (start > 0) setTurnStart(0)
|
||||||
|
|
||||||
if (!input.historyMore() || input.historyLoading()) return
|
if (!input.historyMore() || input.historyLoading()) return
|
||||||
|
|
||||||
await input.loadMore(id)
|
let afterVisible = beforeVisible
|
||||||
if (input.sessionID() !== id) return
|
let added = 0
|
||||||
|
|
||||||
const afterVisible = input.visibleUserMessages().length
|
while (true) {
|
||||||
const growth = afterVisible - beforeVisible
|
await input.loadMore(id)
|
||||||
|
if (input.sessionID() !== id) return
|
||||||
|
|
||||||
|
afterVisible = input.visibleUserMessages().length
|
||||||
|
const nextLoaded = input.loaded()
|
||||||
|
const raw = nextLoaded - loaded
|
||||||
|
added += raw
|
||||||
|
loaded = nextLoaded
|
||||||
|
|
||||||
|
if (afterVisible > beforeVisible) break
|
||||||
|
if (raw <= 0) break
|
||||||
|
if (!input.historyMore()) break
|
||||||
|
}
|
||||||
|
|
||||||
|
if (added <= 0) return
|
||||||
if (state.prefetchNoGrowth) setState("prefetchNoGrowth", 0)
|
if (state.prefetchNoGrowth) setState("prefetchNoGrowth", 0)
|
||||||
|
|
||||||
|
const growth = afterVisible - beforeVisible
|
||||||
if (growth <= 0) return
|
if (growth <= 0) return
|
||||||
if (turnStart() !== 0) return
|
if (turnStart() !== 0) return
|
||||||
|
|
||||||
const target = Math.min(afterVisible, Math.max(beforeVisible, renderedUserMessages().length) + turnBatch)
|
const target = Math.min(afterVisible, beforeVisible + turnBatch)
|
||||||
const nextStart = Math.max(0, afterVisible - target)
|
setTurnStart(Math.max(0, afterVisible - target))
|
||||||
preserveScroll(() => setTurnStart(nextStart))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Scroll/prefetch path: fetch older history from server. */
|
/** Scroll/prefetch path: fetch older history from server. */
|
||||||
@@ -192,19 +209,35 @@ function createSessionHistoryWindow(input: SessionHistoryWindowInput) {
|
|||||||
const start = turnStart()
|
const start = turnStart()
|
||||||
const beforeVisible = input.visibleUserMessages().length
|
const beforeVisible = input.visibleUserMessages().length
|
||||||
const beforeRendered = start <= 0 ? beforeVisible : renderedUserMessages().length
|
const beforeRendered = start <= 0 ? beforeVisible : renderedUserMessages().length
|
||||||
|
let loaded = input.loaded()
|
||||||
|
let added = 0
|
||||||
|
let growth = 0
|
||||||
|
|
||||||
await input.loadMore(id)
|
while (true) {
|
||||||
if (input.sessionID() !== id) return
|
await input.loadMore(id)
|
||||||
|
if (input.sessionID() !== id) return
|
||||||
|
|
||||||
|
const nextLoaded = input.loaded()
|
||||||
|
const raw = nextLoaded - loaded
|
||||||
|
added += raw
|
||||||
|
loaded = nextLoaded
|
||||||
|
growth = input.visibleUserMessages().length - beforeVisible
|
||||||
|
|
||||||
|
if (growth > 0) break
|
||||||
|
if (raw <= 0) break
|
||||||
|
if (opts?.prefetch) break
|
||||||
|
if (!input.historyMore()) break
|
||||||
|
}
|
||||||
|
|
||||||
const afterVisible = input.visibleUserMessages().length
|
const afterVisible = input.visibleUserMessages().length
|
||||||
const growth = afterVisible - beforeVisible
|
|
||||||
|
|
||||||
if (opts?.prefetch) {
|
if (opts?.prefetch) {
|
||||||
setState("prefetchNoGrowth", growth > 0 ? 0 : state.prefetchNoGrowth + 1)
|
setState("prefetchNoGrowth", added > 0 ? 0 : state.prefetchNoGrowth + 1)
|
||||||
} else if (growth > 0 && state.prefetchNoGrowth) {
|
} else if (added > 0 && state.prefetchNoGrowth) {
|
||||||
setState("prefetchNoGrowth", 0)
|
setState("prefetchNoGrowth", 0)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (added <= 0) return
|
||||||
if (growth <= 0) return
|
if (growth <= 0) return
|
||||||
if (turnStart() !== start) return
|
if (turnStart() !== start) return
|
||||||
|
|
||||||
@@ -1161,6 +1194,7 @@ export default function Page() {
|
|||||||
|
|
||||||
let scrollStateFrame: number | undefined
|
let scrollStateFrame: number | undefined
|
||||||
let scrollStateTarget: HTMLDivElement | undefined
|
let scrollStateTarget: HTMLDivElement | undefined
|
||||||
|
let fillFrame: number | undefined
|
||||||
|
|
||||||
const updateScrollState = (el: HTMLDivElement) => {
|
const updateScrollState = (el: HTMLDivElement) => {
|
||||||
const max = el.scrollHeight - el.clientHeight
|
const max = el.scrollHeight - el.clientHeight
|
||||||
@@ -1208,10 +1242,14 @@ export default function Page() {
|
|||||||
),
|
),
|
||||||
)
|
)
|
||||||
|
|
||||||
|
let fill = () => {}
|
||||||
|
|
||||||
const setScrollRef = (el: HTMLDivElement | undefined) => {
|
const setScrollRef = (el: HTMLDivElement | undefined) => {
|
||||||
scroller = el
|
scroller = el
|
||||||
autoScroll.scrollRef(el)
|
autoScroll.scrollRef(el)
|
||||||
if (el) scheduleScrollState(el)
|
if (!el) return
|
||||||
|
scheduleScrollState(el)
|
||||||
|
fill()
|
||||||
}
|
}
|
||||||
|
|
||||||
const markUserScroll = () => {
|
const markUserScroll = () => {
|
||||||
@@ -1223,12 +1261,14 @@ export default function Page() {
|
|||||||
() => {
|
() => {
|
||||||
const el = scroller
|
const el = scroller
|
||||||
if (el) scheduleScrollState(el)
|
if (el) scheduleScrollState(el)
|
||||||
|
fill()
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
|
|
||||||
const historyWindow = createSessionHistoryWindow({
|
const historyWindow = createSessionHistoryWindow({
|
||||||
sessionID: () => params.id,
|
sessionID: () => params.id,
|
||||||
messagesReady,
|
messagesReady,
|
||||||
|
loaded: () => messages().length,
|
||||||
visibleUserMessages,
|
visibleUserMessages,
|
||||||
historyMore,
|
historyMore,
|
||||||
historyLoading,
|
historyLoading,
|
||||||
@@ -1237,6 +1277,45 @@ export default function Page() {
|
|||||||
scroller: () => scroller,
|
scroller: () => scroller,
|
||||||
})
|
})
|
||||||
|
|
||||||
|
fill = () => {
|
||||||
|
if (fillFrame !== undefined) return
|
||||||
|
|
||||||
|
fillFrame = requestAnimationFrame(() => {
|
||||||
|
fillFrame = undefined
|
||||||
|
|
||||||
|
if (!params.id || !messagesReady()) return
|
||||||
|
if (autoScroll.userScrolled() || historyLoading()) return
|
||||||
|
|
||||||
|
const el = scroller
|
||||||
|
if (!el) return
|
||||||
|
if (el.scrollHeight > el.clientHeight + 1) return
|
||||||
|
if (historyWindow.turnStart() <= 0 && !historyMore()) return
|
||||||
|
|
||||||
|
void historyWindow.loadAndReveal()
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
createEffect(
|
||||||
|
on(
|
||||||
|
() =>
|
||||||
|
[
|
||||||
|
params.id,
|
||||||
|
messagesReady(),
|
||||||
|
historyWindow.turnStart(),
|
||||||
|
historyMore(),
|
||||||
|
historyLoading(),
|
||||||
|
autoScroll.userScrolled(),
|
||||||
|
visibleUserMessages().length,
|
||||||
|
] as const,
|
||||||
|
([id, ready, start, more, loading, scrolled]) => {
|
||||||
|
if (!id || !ready || loading || scrolled) return
|
||||||
|
if (start <= 0 && !more) return
|
||||||
|
fill()
|
||||||
|
},
|
||||||
|
{ defer: true },
|
||||||
|
),
|
||||||
|
)
|
||||||
|
|
||||||
const draft = (id: string) =>
|
const draft = (id: string) =>
|
||||||
extractPromptFromParts(sync.data.part[id] ?? [], {
|
extractPromptFromParts(sync.data.part[id] ?? [], {
|
||||||
directory: sdk.directory,
|
directory: sdk.directory,
|
||||||
@@ -1532,6 +1611,7 @@ export default function Page() {
|
|||||||
if (stick) autoScroll.forceScrollToBottom()
|
if (stick) autoScroll.forceScrollToBottom()
|
||||||
|
|
||||||
if (el) scheduleScrollState(el)
|
if (el) scheduleScrollState(el)
|
||||||
|
fill()
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -1565,6 +1645,7 @@ export default function Page() {
|
|||||||
if (diffFrame !== undefined) cancelAnimationFrame(diffFrame)
|
if (diffFrame !== undefined) cancelAnimationFrame(diffFrame)
|
||||||
if (diffTimer !== undefined) window.clearTimeout(diffTimer)
|
if (diffTimer !== undefined) window.clearTimeout(diffTimer)
|
||||||
if (scrollStateFrame !== undefined) cancelAnimationFrame(scrollStateFrame)
|
if (scrollStateFrame !== undefined) cancelAnimationFrame(scrollStateFrame)
|
||||||
|
if (fillFrame !== undefined) cancelAnimationFrame(fillFrame)
|
||||||
})
|
})
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
|||||||
Reference in New Issue
Block a user