All notes

PR 10900 review-sol-6-1

The reviewed pagination work now includes stable message tracking and a fix for the fan tab recreation loop. I found no new merge blocker.

Reviewed 6 October 202611 commits, including merges8 changed files1036 additions, 384 deletions

Messages revamp: cursor inbox, live updates, and fan NG0956 fix

Head 61843a4e3b4ca9cf087c1d2fcc9bac57002a8691
Target hotfix/release-0.0.23/wls-21-priority at c7ea08dfbd18ffc31ec8261a249e27d46bbd0df1
Merge base 91dd0ed2c5487d0740707e676f79f90e3d432eb7

Should I merge it now? Yes.

I would merge 61843a4e3b4 within the agreed scope.

The search and live-history repairs still pass. The new tab changes preserve creator badge updates and tab selection, while removing the tabset entirely for fans. Stable keys also keep the shared delegation tabs intact when their options are cloned.

Socket tab movement remains deferred as agreed in the PR 10896 review. The fan count example depends on an unverified backend response shape and is not a confirmed merge blocker. Neither concern becomes a new blocker just because this PR carries the same code to another branch.

GitHub reports the PR open, ready for review, and mergeable with a clean merge state. It lists no status checks or submitted reviews. The yes recommendation comes from the source review and focused checks below.

What this PR carries

The PR changes eight files relative to its merge base. The target branch has separate changes, so a direct comparison between the two branch tips also lists unrelated work. I used the merge-base comparison and GitHub's changed-file list for the review scope.

The inbox requests the server's tab, counts, and cursor. History requests validation with the first direct-message page and prepends older pages. Live entries survive the first history response. Presence polling and paged New Message recipients are also included.

Compared with the approved 425977eec067 head from PR 10896, the new implementation changes only three files, with 23 additions and 16 deletions. Those files are the shared tab template and the message container's template and TypeScript file. The five other files match the previously reviewed implementation.

The new fixes pass focused checks

Fans no longer create a hidden tabset

message-container.component.html, creator condition

The template now creates app-tab-view only when isCreator is true. Hiding the element previously left its tabset alive, with selection callbacks still attached. Fans now have no tabset to recreate.

The selection handler also ignores fan callbacks and repeated selections. The fan profile setup retains General by key, rather than leaving the first Priority option behind.

The Angular fixture rendered zero fan tabs through 25 repeated field updates. It emitted no extra selection events and no NG0955 or NG0956 warnings. The component-method fixture also confirmed that a fan callback leaves the active tab at General without starting an inbox refresh.

Stable tab keys keep existing tab instances

tab-view.component.html, track tab.key

The shared loop tracks each option's key instead of its object identity. A deep clone of the field therefore keeps the existing ngx-bootstrap tabs. I checked both callers, Messages and Delegation. Both provide distinct keys.

Messages also stops cloning its field for badge and active-tab updates. The component uses normal Angular change detection, and the real-template fixture confirmed that changing counts in place paints Priority 7 and General 2. Selecting General emitted once and retained the same tab instances.

The delegation fixture used its two existing keys. Cloning the options retained both tab instances. Removing Delegated Access left the original My Delegates instance in place.

History rows can use message_id for tracking

trackThreadMessage

The message loop calls trackThreadMessage. It keeps the existing sk and optimistic uinqueId choices, then uses message_id when those identifiers are absent. Timestamp and object identity are the remaining fallbacks.

The extracted-method check returned distinct keys for two rows with only message_id. It also confirmed the legacy identifier precedence and object fallback. This fixes the case where the old expression returned an absent key for both rows.

Checks and limits

Check on 61843a4e3b4Result
Creator tabs, badge updates, and General selectionPassed. Three tabs, updated counts, one selection event.
Cloned keyed options and delegation option removalPassed. Existing tab instances retained.
Fan tab rendering through 25 updatesPassed. No tabset and no NG0955 or NG0956 warnings in the fixture.
Actual badge, tab-selection, and message-tracking methodsPassed. Stable field references, guarded fan callbacks, correct key fallbacks.
Return to the previous search within the debounce windowPassed. Loader stays false and the next-page cursor remains.
Live entry arrives before the first history responsePassed. The entry remains visible after the response.
History duplicate ID, request error, and conversation switchPassed under the fixture inputs. Live entries survive and unrelated entries are excluded.
Ordinary same-tab live preview and unread incrementPassed.
Cursor containing plus, slash, and equalsEncoded without changing the value.
Whitespace check for the full PR diffgit diff --check passed.
Local merge calculation against the recorded targetgit merge-tree completed without conflicts.
Full application builds and main or generated-tenant SSRNot run for this review.
Signed-in staging creator and fan flowsNot run for this review. The PR checklist remains unchecked.

The tab checks use Angular TestBed, the real shared template, and real ngx-bootstrap tab directives with a domino DOM. A small decorated component shell supplies the inputs and output, and a minimal host reproduces the source template's creator condition. The fixture uses zone-based change detection, matching the application configuration. It is not a browser session running the complete Messages page.

The method checks extract the current methods verbatim with the TypeScript parser. They use the repository's TypeScript and RxJS packages, with network and unrelated Angular effects stubbed. Search uses real RxJS operators with virtual time.

The history duplicate check uses matching stable IDs in the socket entry and response. These tests do not establish every backend identifier format, presence response, or recipient pagination path. The successful merge calculation establishes conflict resolution only. It does not replace a build.

Follow-up work stays outside this merge decision

  • Socket tab movement still leaves a loaded row in its old tab under the recorded test event. The user deferred that investigation during the PR 10896 review.
  • The synthetic split-count fan example still produces the earlier discrepancy. A live fan response has not established that count shape. Verify the backend contract before treating it as a product defect.

I have kept both points visible without turning them into merge blockers. This review found no new blocker in the changes since the approved PR 10896 head.