Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 6 additions & 2 deletions cli/src/components/freebuff-model-selector.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -49,10 +49,14 @@ export const FreebuffModelSelector: React.FC = () => {
// subtract. In-queue ('queued'): for the user's queue, "ahead" is
// `position - 1` (themselves don't count); for every other queue, switching
// would land them at the back, so it's that queue's full depth. Null before
// any snapshot so the UI doesn't flash misleading zeros.
// any snapshot so the UI doesn't flash misleading zeros — in particular,
// landing mode after a session ends initially sets status='none' with no
// queueDepthByModel; returning null here keeps the hint blank until the
// fetch lands, instead of showing "No wait" on every row.
const aheadByModel = useMemo<Record<string, number> | null>(() => {
if (session?.status === 'none') {
const depths = session.queueDepthByModel ?? {}
if (!session.queueDepthByModel) return null
const depths = session.queueDepthByModel
const out: Record<string, number> = {}
for (const { id } of FREEBUFF_MODELS) out[id] = depths[id] ?? 0
return out
Expand Down
28 changes: 22 additions & 6 deletions cli/src/hooks/use-freebuff-session.ts
Original file line number Diff line number Diff line change
Expand Up @@ -438,13 +438,29 @@ export function useFreebuffSession(): UseFreebuffSessionResult {
// doesn't bounce a 'landing' restart straight back to 'ended'.
previousStatus = null
if (mode === 'landing') {
// Land on the picker without a probe GET. If the preceding
// DELETE hasn't propagated, a GET here could still see
// queued/active and trip the startup-takeover branch below into
// an auto-POST — the exact silent-rejoin this mode exists to
// avoid. Polling resumes when the user commits to a model via
// joinFreebuffQueue.
// Land on the picker immediately. We can't go through the normal
// tick/apply path because a server-side row that hasn't been
// swept yet would trip the startup-takeover branch into an
// auto-POST — the exact silent-rejoin this mode exists to
// prevent. But the picker still needs live queue depths for its
// "N ahead" hints, so kick off a fire-and-forget GET and extract
// just queueDepthByModel from the response, ignoring whatever
// status it claims. Polling resumes when the user commits to a
// model via joinFreebuffQueue.
apply({ status: 'none' })
const fetchController = abortController
callSession('GET', token, { signal: fetchController.signal })
.then((response) => {
if (cancelled || fetchController.signal.aborted) return
const depths =
response.status === 'none' || response.status === 'queued'
? response.queueDepthByModel
: undefined
if (depths) apply({ status: 'none', queueDepthByModel: depths })
})
.catch(() => {
// Silent — blank hints are acceptable if the fetch fails.
})
return
}
nextMethod = 'POST'
Expand Down
Loading