Inbox and history pagination

Inbox tabs and message history now use server cursors. Unread badges use response counts, and history loads send permissions in the same request.

Verified 6 October 2026 · sol-6-1 · TopFans Angular frontend

What changed

  • Server-owned inbox tabs. Priority requests featured, General requests general, and Message Lists requests broadcast. Fans request all and see one list without tab controls.
  • Rows keep the backend's membership and order. The frontend no longer splits, filters, or sorts each inbox response by featured flags, tips, broadcasts, or an empty last message. Existing badge normalization and row rendering stay in place.
  • Inbox scroll loads the next page. Rows append using the returned cursor. A tab change or a new search replaces the list and starts without a cursor. A null or missing cursor ends pagination.
  • Unread badges use server counts. Tab badges use their matching counts fields. The shared Messages total is general + featured + broadcast, independent of the number of loaded rows. Sidebar badge refreshes fetch fresh inbox counts, and the open inbox follows those updates.
  • History supplies send permissions. The first history request asks for validation and assigns it to dmValidations. The separate ChatUserValidation request is gone. Permission retry uses history with validation again.
  • Scroll-up loads older messages. Older pages prepend using the history cursor and keep the current validation. The scrollbar adjusts by the added content height to preserve the reading position. A null cursor stops further requests.
  • Broadcast history keeps its sender. Existing broadcast from stays on first and subsequent history requests.
  • Requests survive rapid navigation. Replacing a tab, search, or conversation cancels its previous request. Old responses cannot replace the new list, messages, or validation. Pending pagination requests cannot start duplicate pages.
  • Search handles input and paste. Searches remain debounced. Blank search text is omitted, and query values are URL-encoded.

Request contract

Inbox first page
GET api/get-chatGroups-and-users?tab=featured&counts=true. Replace the selected tab's rows; store counts and cursor.
Inbox next page
Keep the same tab and nonempty search_key. Add cursor from the last response and append data.
History first page
POST api/getpreviousMessagesFromHttp with { groupId, include_validation: true }. Use response for messages and validation for send permissions.
History next page
Send { groupId, cursor }. Preserve broadcast from. Omit include_validation and the old timestamp pagination field.

Verification results

26 checks passed on the running dev server, plus 12 isolated contract checks against the component methods. TypeScript checking and Angular development compilation passed. The table distinguishes real staging responses from controlled responses used to exercise populated lists.

CheckResultEvidence
Required staging setupPassedRan generate-white-label-staging, copied the Rudeus Greyrat staging environment into environment.ts, then ran cleanup-white-label before starting the dev server with the 10 GB Node memory setting.
Live creator tabsPassed liveSigned in to staging. Priority, General, and Message Lists returned successful responses with empty data, zero counts, and null cursors. The empty state rendered correctly.
Live search and clearingPassed liveA search sent search_key on the selected tab. Clearing it omitted both search_key and cursor.
Backend membership and orderingPassed controlledRendered 30 Priority rows whose users were not locally featured. Fan rows retained backend order, including a broadcast row with an empty last message.
Inbox pagination and stopPassed controlledScrolling requested an encoded cursor and appended two rows, taking the list from 30 to 32. A null cursor prevented more page requests.
Unread badgesPassed controlledTab counts were 7, 3, and 2. The sidebar showed 12 while the list had 30 rows with zero row unread counts.
Sidebar refresh and tab synchronizationPassed controlledSidebar refresh requested fresh creator counts. A later response with counts 1, 2, and 3 changed the shared total to 6 and updated the open inbox's tab badges.
First history and validationPassed controlledThe request included include_validation: true. The response supplied dmValidations and unlocked message actions.
Older history and reading positionPassed controlledScroll-up sent only the group and cursor. Ten older messages prepended to 25 messages. The viewport moved by exactly 867 pixels, matching the added height, while validation remained the same.
History terminationPassed controlledScrolling up after a null cursor did not send another history request.
Fan presentationPassed controlledRequested tab=all, retained the returned list, and verified that the tab component was hidden.
Broadcast senderPassed controlledBoth first and subsequent history requests retained from. Only the first request asked for validation.
Stale responses and permission retryPassed controlledOld tab, search, and conversation responses were ignored. Missing direct-message validation kept actions locked. Retry requested validation through history.
Failure and concurrency handlingPassed contract checksChecked duplicate-page guards, history errors, retained cursors after older-page failures, and permission retry after a failed first page.
Compilation and setup cleanupPassedTypeScript and Angular development compilation passed. The main project also compiled with its original environment and returned HTTP 200. Temporary environment and generated-file changes were restored after verification.
Populated live history and fan accountNeeds live dataThe supplied creator account had no inbox rows. Real multi-page history, backend validation payloads, nonzero unread transitions, and a signed-in fan account still need the populated-account checks below. Controlled tests cover the frontend contract.

Live verification checklist

Use a creator and a fan account with enough conversations and history to produce non-null cursors. Inspect the request payloads alongside the visible list.

  1. Open each creator tab. Confirm featured, general, and broadcast, always with counts=true. Compare each rendered row and its order with data.
  2. Scroll the inbox to load another page. Confirm the next request keeps the tab and search, sends the previous cursor, and appends rows. Continue until the cursor is null or missing; further scrolling must send no request.
  3. Switch tabs after loading several pages. The first request for the new tab must omit the cursor. Search, change the search quickly, paste text, and clear it. Confirm stale results never appear and blank search_key is omitted.
  4. Compare unread tab badges with counts. Confirm the sidebar and header Messages total equals their sum. Open an unread conversation, then check the refreshed server counts rather than the number of loaded rows.
  5. Navigate away from Messages and cause an unread badge refresh. Confirm the sidebar requests fresh inbox counts and updates the shared total. Return to Messages and compare the tab badges with the latest counts.
  6. Open a direct conversation. Confirm a single history request sends include_validation: true, validation controls the composer, and no api/chat-user-validation request appears.
  7. Scroll up through multiple history pages. Confirm cursor requests, older messages above the current content, stable reading position, and unchanged send permissions. After a null cursor, confirm scrolling sends no additional history request.
  8. Open broadcast history. Confirm from on the first and subsequent pages and no validation flag on subsequent pages.
  9. Switch conversations before history returns. Confirm the old response changes neither the messages nor the new conversation's send permissions.
  10. Simulate a history failure. First-page failure must keep actions locked until retry succeeds through history. An older-page failure must preserve the conversation and allow the same cursor to be retried.
  11. Sign in as a fan. Confirm one list, no creator tabs, tab=all, and the same search and scroll behavior.

Changed files

Messages component
src/app/modules/messages/components/message-container/message-container.component.ts and its HTML template contain the inbox requests, cursor state, scrolling, validation, cancellation, and search event changes.
Constants and response shape
src/app/modules/messages/constants/messages.constant.ts defines inbox tab values and labels. src/app/modules/messages/interfaces/messages.interface.ts defines InboxCounts.
Shared unread counts
src/app/services/core.service.ts stores server counts and their total. src/app/modules/shared/components/sidebar/sidebar.component.ts refreshes counts and clears them on sign-out.
Verification evidence
docs/messages-chat-pagination-verification-sol-6-1.json records the dev-server assertions. This report uses the palette from the existing notes pages and COLORS.md.