Skip to content

refactor(RoomView): first-tier simplifications after the hooks migration - #7635

Merged
diegolmello merged 71 commits into
native-34-roomview-hooksfrom
native-34-roomview-hooks-3
Sep 3, 2026
Merged

refactor(RoomView): first-tier simplifications after the hooks migration#7635
diegolmello merged 71 commits into
native-34-roomview-hooksfrom
native-34-roomview-hooks-3

Conversation

@diegolmello

@diegolmello diegolmello commented Sep 2, 2026

Copy link
Copy Markdown
Member

Proposed changes

Follow-up to #7482 with the first tier of RoomView simplifications. Stacked on native-34-roomview-hooks so the migration and the cleanups can be reviewed separately.

Performance

  • The room record observer writes to the store only when an observed room attribute actually changed, so an incoming message no longer re-renders the root, providers, list, footer and composer. Livechat rooms derive lastMessageFromAgent from last_message inside the same observer to feed the on-hold permission.
  • MessageComposer reads send-time values from the store inside the press handler instead of subscribing to five slices.
  • useRoomStoreForScreen skips the extra render when the acquired store is unchanged.
  • The scroll FAB updates only on a threshold crossing.
  • The omnichannel routing config is fetched once per server and cached, instead of on every RightButtons and room-actions mount. The on-hold permission is derived from the room store's lastMessageFromAgent and the omnichannel permissions live in useOmnichannelPermissions.

Bug fixes

  • Taking an inquiry reads the workspace version from Redux, so a room whose store was warmed before init no longer hits the removed DDP path.
  • Jump to Message invalidates earlier jumps, and cancelling the thread loading overlay stops the thread from opening.
  • Debounced onEndReached and readThread are cancelled on unmount. useDebounce now returns the debounced state so .cancel is typed.
  • A room whose init fails renders a retryable screen instead of an empty list.
  • The route is parsed once at mount into a screen identity; a RoomView pushed without rid or t renders a Room_not_found screen with a Back button instead of an empty room.

One canonical wiring per behavior

  • Thread names resolve through fetchThreadName; a removed thread now titles as Message_removed (was Thread on one path).
  • One thread-press, one useReactionActions call and one sendRoomMessage wiring per room screen, passed to RoomMessageProvider as room actions.
  • MessageRoomStoreProvider / MessageRoomProviderWithSetting collapsed into one provider.
  • RoomPlaceholder shell shared by EncryptedRoom, MissingRoomE2EEKey, InvitedRoom and the new RoomLoadFailed.
  • Invited, missing-E2EE-key and encrypted rooms are decided in a RoomGate before the room tree mounts; the invalid-route screen sits above the gate and the init-failure screen below it, inside RoomScreen.

Mechanical

  • useMessages: DB clause is the only system-message filter, query clauses assembled in one literal, no Q.skip(0), thread record appended without an unconditional clone, shared newer-loader lookup with getLocalAnchor, no double casts.
  • Typed Animated.FlatList and roomUpdate instead of @ts-ignore / any.
  • Shared footer action button for TakeOrJoin / OnHold; blank-label sentinel removed from LeftButtons.
  • Inert message-action store reduced to the action it serves.
  • The 8,038-line LoadMore snapshot is replaced by state assertions.

Issue(s)

https://rocketchat.atlassian.net/browse/NATIVE-34

How to test or reproduce

  • Open a room and receive messages: the list and composer keep working, and the header title still updates on room rename.
  • Omnichannel room with manual on-hold enabled: the Place chat on hold entry in the header menu appears after an agent message and disappears after a visitor message.
  • Open a thread from the message tree: the loading overlay shows a Cancel button; open a thread whose parent was deleted: title reads Message_removed.
  • Invited room, room with missing E2EE key, encrypted room: screens look the same as before.
  • Force a room init failure (offline on a room not in the local DB): the retry screen appears and Try again re-runs init.
  • Navigate to RoomView without rid (for example a malformed deep link): the Room not found screen appears and Back returns to the previous screen.
  • Omnichannel room: Return to queue and the on-hold button still appear; open a second omnichannel room and confirm no extra routing config request is made.
  • Reply in thread from the message action sheet, send a message, react.

Screenshots

Types of changes

  • Bugfix (non-breaking change which fixes an issue)
  • Improvement (non-breaking change which improves a current function)
  • New feature (non-breaking change which adds functionality)
  • Documentation update (if none of the other choices apply)

Checklist

  • I have read the CONTRIBUTING doc
  • I have signed the CLA
  • Lint and unit tests pass locally with my changes
  • I have added tests that prove my fix is effective or that my feature works (if applicable)
  • I have added necessary documentation (if applicable)
  • Any dependent changes have been merged and published in downstream modules

Further comments

Not device-verified yet; unit suites, lint and the React Compiler contract test pass on every commit. useE2EEStatus and useRoomRemoved still peek the store registry because they run above the store provider; moving the provider is left for a later PR, as are the remaining R2/R3/R5/R6 items.

Summary by CodeRabbit

  • New Features

    • Added a retry screen when a room fails to load.
    • Added shared placeholders for encrypted, invited, and unavailable rooms.
    • Added dedicated livechat queue and hold availability checks.
    • Improved room and thread navigation, including cancellation of outdated jumps.
    • Added clearer loading and action states in room footers.
  • Bug Fixes

    • Improved system-message filtering, scrolling, and debounced action handling.
    • Improved livechat hold availability based on the latest agent message.
  • Tests

    • Expanded coverage for room loading, navigation, message loading, and room actions.

useThreadFollowing subscribed inside a promise callback, so a room opened
and closed before getMessageById resolved left the observer running: the
cleanup ran while unsubscribe was still undefined. A cancelled flag set in
cleanup and checked before subscribing closes that window, and also covers
a tmid/userId change, where cleanup runs before the pending resolve.

Dropped the paired suggestion to swap observe() for
observeWithColumns(['replies']): observeWithColumns is a Query method, not
a Model one, so it is not available on the record getMessageById returns.
The derived value is a boolean, so setState already bails out when it does
not change.
The room observer woke on every incoming message, rebuilding roomUpdate and
re-rendering the root, providers, list, footer and composer. Livechat is the
only consumer of last_message: it now gets its own observer, created only for
t === 'l', publishing lastMessageFromAgent into the room store.
joinRoom no longer reads the redux singleton at call time; the screen passes the version it already selects.
pushThreadRoom carried its own copy of the removed-thread branch and had
drifted to `Thread`; the shared helper's `Message_removed` now wins for
both call paths.
The handlers hook wired pushThreadRoom a second time without onCancel, so
the loading overlay opened there had no cancel button. It now takes the
screen's onThreadPress, and onReplyInit delegates to it instead of
repeating the push.
The handlers hook built a second set of reaction actions over the same
message action store; it now takes the ones the screen already created.
The answer-button handler duplicated the screen's send wiring; it now
reuses it, and the send behaviour is covered on the service itself.
The provider forked into two components only to default timeFormat; the
setting is now read unconditionally and used when the caller passes none.
pushThreadRoom is now the single owner of thread-name resolution and of
both reply paths, and neither it nor the helper had a test.
useRoomInit exposes failed and retry so a room whose init exhausted its
attempts no longer renders as an empty room.
useDebounce now returns DebouncedState so callers reach .cancel; the list
onEndReached and the readThread timer no longer fire after unmount.
… diegolmello/bug-room-not-rewritten-on-last-message-flip
… diegolmello/bug-livechat-flags-stale-on-type-change
Flatten the hook result so RoomScreen names every prop it passes instead of
spreading consumer-shaped bundles, drop the explanatory comments, and keep
room init ahead of the room subscription as before the split.
…icate unit tests

Also moves visibleSystemMessages out of the hooks folder, since it exports no hooks.
… diegolmello/bug-hidden-system-messages-untested
… diegolmello/roomview-split-roomscreen

# Conflicts:
#	app/views/RoomView/RoomScreen.tsx
… diegolmello/bug-routing-config-cache-no-retry
@diegolmello
diegolmello merged commit 915fd10 into native-34-roomview-hooks Sep 3, 2026
7 of 8 checks passed
@diegolmello
diegolmello deleted the native-34-roomview-hooks-3 branch September 3, 2026 17:33
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant