Skip to content

feat: show loaded remotes instantly while searching - #1093

Merged
xHeaven merged 2 commits into
dev-null-sh:productionfrom
xHeaven:feat/instant-inbox-search
Oct 23, 2026
Merged

xHeaven merged 2 commits into
dev-null-sh:productionfrom
xHeaven:feat/instant-inbox-search

Conversation

@xHeaven

@xHeaven xHeaven commented Oct 23, 2026

Copy link
Copy Markdown
Member

Summary

Searching the remotes inbox showed nothing until the server answered, even when matching conversations were already on screen. Typing a name now shows the loaded conversations that match straight away. Skeleton rows appear underneath while the server looks for conversations that aren't loaded yet and for people to start a conversation with, and those results join the list when they arrive. Refining a term (dan → dani) keeps the conversations the server already found visible instead of blanking the list.

Changes

  • resources/js/lib/conversation-search.ts: conversationsMatching(term, onHand, found) mirrors the server's inbox search rule (partner name or handle, org name, or any org member's name or handle, case-insensitive). The server's results for the current term come first and are never re-filtered, so a stale local copy can't hide a server hit. Conversations on hand are filtered before duplicates are removed.
  • InboxPanel (Remotes page and dock) shows the loaded tab list plus earlier same-tab server results, filtered by the term, while the current request is pending. The existing ConversationListSkeleton (300 ms delayed fade-in) renders under them until results arrive. "fatal: nothing matches" still waits for the server.
  • People already in the merged list are left out of "Start a conversation" when it renders.
  • The search term strips every leading @, matching ListInboxRequest::term().

Validation

All local; hosted CI not run yet.

  • node --experimental-strip-types --test tests/Unit/ConversationSearch.test.ts: 3/3. The new tests failed before the implementation.
  • bun run test:unit: 153/153. bun run test:types: pass. bun run test:lint: pass (warnings were already there).
  • composer test:lint: pass (Pint, Rector, bun lint). composer test:type-coverage: 100.0%.
  • php -d memory_limit=-1 vendor/bin/pest --parallel --ci --coverage --exactly=100.0 --passthru-php="'-d' 'memory_limit=-1'" on an isolated test database: 4089 passed, 22990 assertions, coverage exactly 100%.
  • Layout shift (PerformanceObserver layout-shift, excluding recent input) for page load, typing dan with the server delayed 1.2 s, typing i, then Escape: 0 at 1440×900, 1366×650 and 390×844, before and after.
  • Browser check: @@dan and dan return the same 5 conversations and people.

Visual evidence

Local demo data: @mark has 40 direct remotes and 3 orgs, so only the newest 30 are loaded. For the term dan, the loaded matches are Platform Team (member Danielle), Danielle Park, Danish Devs and an unnamed org with Daniel. Daniel Kim matches but isn't loaded. Recordings and "pending" stills simulate a slow server by delaying /remotes/inbox?q= and /remotes/candidates (1.8 s in videos, held open in stills).

Typing dan then i (desktop 1280×800, simulated 1.8 s latency)

Before After
Before: list stays empty or skeleton-only until the server responds After: loaded matches appear immediately, skeletons below, server results join
MP4 MP4

Waiting for the server (desktop 1440×900, light)

Before After
Before pending After pending

After, pending vs. settled

Pending, light Settled, light
Pending desktop light Settled desktop light
Pending, dark Settled, dark
Pending desktop dark Settled desktop dark
Mobile pending, light Mobile settled, light Mobile pending, dark Mobile settled, dark
Mobile pending light Mobile settled light Mobile pending dark Mobile settled dark

1366×650 pending: Pending 1366x650

Mobile dark, typing (390×844 viewport emulation, simulated 1.8 s latency)

Mobile dark search recording MP4

Device verification

iOS Simulator, iPhone 18 Pro, iOS 27.0, Safari, local server without simulated latency, recorded on 75856677. The reviewer accepted it for cf8aa22b because round 2 changed no layout. Tapping the search field and typing dan/dani showed the loaded matches immediately with the keyboard open, and the page didn't zoom (16px input). Client-side transition: signed in through the local sign-in modal, then opened Remotes. Safari's keyboard covers the lower rows of the list, as it already does on production; this PR doesn't change that.

Keyboard open, dan Recording
iPhone 18 Pro simulator with keyboard open MP4

Independent review

Separate read-only reviewer, GPT-6.1 Sol (high reasoning, priority tier), base 8d0c6b29, head cf8aa22b, media as linked above.

  • Round 1 (75856677): CODE FAIL, VISUAL PASS.
    • Stale loaded copies could hide current server hits.
    • @@term normalization didn't match the server.
    • The PHP checks hadn't been run.
    • All three were fixed in cf8aa22b.
  • Round 2 (cf8aa22b): CODE: PASS, VISUAL: PASS, no actionable findings. The reviewer didn't rerun the PHP suites, sampled the recordings through contact sheets, and didn't test physical devices, Android or the Fetched tab (requests are disabled in the demo).

Limitations

  • While a new term is pending, earlier results for the same tab may briefly include a conversation that has since moved tabs, until the refetch replaces them.
  • The server search returns at most 30 conversations, so the merged list can show more when more of the loaded conversations match.
  • The Fetched tab got code review only, because requests are disabled in the demo environment. No physical device or Android testing.

@xHeaven
xHeaven merged commit fc47be8 into dev-null-sh:production Oct 23, 2026
5 checks passed
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.

1 participant