Skip to content

feat(home): infinite scroll + pull-to-refresh for the home sessions list (carried from #331) - #351

Open
0xSero wants to merge 16 commits into
mainfrom
feat/home-sessions-infinite-scroll
Open

0xSero wants to merge 16 commits into
mainfrom
feat/home-sessions-infinite-scroll

Conversation

@0xSero

@0xSero 0xSero commented Sep 13, 2026

Copy link
Copy Markdown
Owner

Carried from #331 so it can merge against current main: the original head branch lives in @DatScreamer's fork and could not be rebased here. All 15 home-sessions feature commits are cherry-picked with DatScreamer's authorship preserved (-x references the fork originals).

What's included (from #331): infinite scroll + pull-to-refresh on the home sessions list for both platforms, shared Rust cursor-paged session loading (AppStore::load_threads_page, retained per-runtime cursors, sessionListHasMore projection), first-page load on connect/reconnect, default page size 10→20, session-card spacing fixes, and the subagent-session exclusion (client-side filter).

Dropped from the original branch, with reasons:

Verification: Rust test suite + Android unit tests via CI on this PR; the original feature was device-verified by @DatScreamer on both platforms (see #331).

Closes #331.

DatScreamer and others added 16 commits September 14, 2026 11:46
Add cursor-paged session loading in the shared Rust store
(AppStore::load_threads_page) with retained per-runtime cursors and a
sessionListHasMore projection, so the home sessions list loads pages on
scroll instead of a fixed top-10.

iOS: HomeDashboardModel grows a recentLimit window (10 + 10 per load-more)
fed by AppModel.loadThreadsPage, a drag-down UIRefreshControl full reload,
and a near-bottom load-more trigger in the UIKit scroll view; topInset bumped
48 -> 56 for extra spacing above the list.

Android: HomeDashboardScreen grows the same recentLimit window via
AppModel.loadSessionsPage, adds Material3 PullToRefreshBox + snapshotFlow
near-bottom load-more, and +8dp top content padding. Connect-time loads use
the paged first page instead of a full drain.

Android Conversation parity with the iOS infinite-scroll PR: replace the
manual 'Load earlier messages' button with scroll-proximity auto-prefetch,
stable LazyColumn turn keys for prepend scroll preservation, a 200-item
auto-collapse policy, and turn page sizes bumped 5 -> 20.

(cherry picked from commit e056c6c)
Sessions were only ever loaded from the (unreachable) sessions screen or
search, so a freshly connected server never populated its session list.
Fetch the thread list in the post-connect warmup via a shared
refresh_thread_list fanout, and pin the alleycat bridges to a fork whose
opencode bridge enumerates sessions across all project worktrees (a bare
GET /session only returns the default project). Relax update-alleycat-main
to treat any rev-pinned git dep as pinned so the fork pin survives.

(cherry picked from commit aef6af4)
Restore auth state when needsLocalAuthRestore is true, without the
redundant loadSessionsPage call (the Rust post-connect warmup handles
thread population).

(cherry picked from commit c6c1b0e)
clear_thread_page_state after the warmup's full drain wiped session_pages
→ session_list_has_more = false → infinite scroll never triggered. After a
full drain the cursor is naturally None and has_more reflects the server's
actual state, so clearing is unnecessary and harmful.

(cherry picked from commit 1d921ae)
The warmup drained all pages (limit: None) which set session_list_has_more
= false immediately, preventing loadMoreSessions from ever firing. Now
loads just the first page (limit: 20) so the server's has_more flag is
accurate and the home list's infinite-scroll gate works.

Also persists cursor state in refresh_thread_list so session_pages is
non-empty after the warmup.

(cherry picked from commit b9bf872)
The warmup (spawn_post_connect_warmup) is a fire-and-forget Tokio task.
refreshSnapshot() in reconnectSavedServers() runs before the warmup
finishes, so the Kotlin snapshot always has stale session data and
session_list_has_more=false — making infinite scroll impossible.

Replace refreshSnapshot() with loadSessionsPage() for connected servers,
which synchronously loads the first page via Rust's load_threads_page,
sets accurate cursor/has_more state, and then snapshots.

(cherry picked from commit 259a153)
- iOS: local-only load-more path when server cursors are exhausted,
  firstPageLoaded/hadVisibleServers guards so the first page loads once
  runtime kinds are ready, and targetServerIds scoping for servers that
  still have remote pages.
- iOS: fix loadMoreFired reset so a new page can trigger after the
  in-flight flag flips.
- Android: add isLoadingMoreSessions in-flight guard, local-only
  recentLimit bump path, and LazyColumn listState wiring so the load-more
  footer reflects the right state.
- Bump home session page size 10 -> 20 and fix preview wiring for the
  new sessionListHasMore field.

(cherry picked from commit 4fb130d)
Keep subagent/agent threads off the home session list. The opencode
bridge now excludes them at the source (parentID filter + drop), so the
list is defense-in-depth on top of that. Re-pins alleycat to d503bfc
which contains the opencode-bridge subagent exclusion (thread/list drop
+ session.created guard).

(cherry picked from commit d393140)
The infinite-scroll change added the required session_list_has_more field
to AppServerSnapshot; five test files still built it with the old
signature and failed to compile under xcodebuild test. Add
sessionListHasMore: false to each.

(cherry picked from commit 09c3ffc)
@0xSero
0xSero force-pushed the feat/home-sessions-infinite-scroll branch from 6ed384f to 04d9ad6 Compare September 14, 2026 16:13
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants