refactor: migrate RoomView to a function component - #7482
Conversation
…ved to useIsArchived (NATIVE-22)
…ts-expect-error and try/catch (NATIVE-22)
…ontext value (NATIVE-22)
Resolve conflicts in MessagesView/SearchMessagesView: keep MessageRoomProvider wrapping while adopting develop's insets/paddingBottom on the FlatList.
The Timestamp story renders a fixed unix time with the relative (`R`) format, so its snapshot drifts with wall-clock time (`2 years ago` -> `a year ago`).
…tion verbs (NATIVE-22)
…e (NATIVE-22) Replace the writable module-level fallbackStore with a frozen inertStore whose actions throw on call, closing the latent cross-room write vector for no-provider consumers (search/pinned rows). Document useIsBeingEdited's graceful-degradation contract via JSDoc, and expand the test suite: no-provider isolation, useIsBeingEdited across all kinds, addQuote dedup guard, clear() from each kind. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Lands the message-action-store map into native-22-message-hooks. Pure refactor of the interaction store — no intended behavior change. - Rename InteractionStore -> MessageActionStore, normalize action verbs - Collapse useMessageAction + useSelectedMessages into one useMessageAction() - Harden the no-provider fallback with an inert MessageActionStore Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…IVE-22) Relocates MessageActionStore from views/RoomView into containers/message/stores so reusable layers no longer import upward from a view, and moves the TMessageActionState discriminated union into app/definitions (where the derived TMessageAction already lives), inverting the definitions -> views arrow. RoomView/ShareView keep creating and providing the store.
…s (NATIVE-22) JitsiBranch rendered a bare <Content /> after the refactor, and useIsInfoMessage() explicitly excludes jitsi_call_started (it isn't a compact/non-touchable info row), so InfoContent never rendered the localized "started a call" line — only the join button remained. Give Content a local isInfo override so JitsiBranch alone can force InfoContent, matching the pre-refactor output (User + Started_call text + join button) without touching useIsInfoMessage or any other branch. Add a jitsi_call_started story to cover it going forward.
Ticket 004: confirmed the stuck-false transition bug at the unit level, but the only caller (useMediaAutoDownload) keys its message list by the same id, so a real messageId change always remounts the hook with a fresh seed. Ruled the behavior fix out of scope; kept the test as a documented contract.
…ranches (NATIVE-22)
blocks now types to @rocket.chat/ui-kit's LayoutBlock union (the type already rendered by UiKitMessage), and e2eMentions matches getE2EEMentions's real return shape. Closes the any hole at the useBlocks() hook boundary.
|
Android Build Available Rocket.Chat 4.77.0.109595 Internal App Sharing: https://play.google.com/apps/test/RQQ8k09hlnQ/ahAO29uNR0xDv_FQJ8u0ftn71aIcDiKif_AmilxNYarhAb5j5R67hMCsAuqG2MUZbwHY05KaqJSuR6o_5m1jkbfBn1 |
|
iOS Build Available Rocket.Chat 4.77.0.109596 |
* test: lock the send contract in the useRoomMessaging tests * refactor: inline sendRoomMessage into useRoomMessaging
…7666) * refactor: pass composer roomTitle and t as scalars instead of room ComposerStore no longer mirrors room/roomUpdate. RoomScreen and ShareView compute roomTitle via getRoomTitle and pass it alongside t through RoomProviders/ComposerProvider. useComposerRoom is removed in favor of useComposerRoomTitle and useComposerType. * refactor(room-store): observe the subscription record directly, publish every emission Find the subscription row once by rid, observe it with room.observe() when present, or fall back to a query observable until a row appears. Every emission now reaches setState unconditionally. On record destruction, joined flips to false for non-DM rooms. subscribed is removed from RoomState. * refactor(room-view): derive livechat on-hold agent check in selector Remove lastMessageFromAgent from RoomStore; useCanPlaceLivechatOnHold now computes it from room.t, room.lastMessage and room.onHold. * refactor(room-view): select scalar fields in header and right buttons instead of the whole room * refactor(room-footer): select scalar room fields instead of the whole room TakeOrJoin, useFooterMessage, useRoomFooterState, RoomAnnouncementBanner, useReadOnly and useE2EEStatus now select the fields they use (t, onHold, ro, roles, encrypted, E2EKey, announcement, bannerClosed, and helper outputs like isBlocked/isRoomFederated) instead of subscribing to the whole room. useCloseBanner takes the RoomStore and reads room imperatively at call time instead of receiving it as a prop. * refactor: select room fields instead of whole room in message list/row/actions RoomMessageList, MessageRow, RoomMessageActions, the message handlers hook and the thread badge color hook now select only the room fields they need from RoomStore, since a whole-room selector never re-renders (WatermelonDB mutates the same model instance in place). callJitsi now reads getState().room imperatively at call time instead of closing over a rendered room. * refactor(room-view): delete whole-room snapshot machinery RoomStore now publishes the live room on every observe() emission with no diffing. roomUpdate, roomObservedFields, the observed column map, useRoomWithUpdateFromStore and useRoomWithUpdate are gone; every RoomView reader already selects the fields it needs. RoomScreen and RoomGate no longer hold a whole-room selector. * fix(room-view): address review findings on scalar-selector migration Kill the last whole-room render reads in useGoRoomActionsView and LeftButtons (the latter was genuinely stale on rename), narrow RoomGate's invite/encrypted-room props, drop the unused subscription field from useSubscriptionUnreads, guard RoomStore's synchronous-emission race in observeQueryUntilPresent, and make ComposerState.roomTitle required. * fix(room-view): drop the comment and var trick from the RoomStore race fix Use a subscribed flag set after subscribe() returns instead of var hoisting, so the callback never touches the subscription binding before it's safely assigned. * fix(room-view): select badge colors and simplify room observation * fix(room-view): narrow room instead of casting in the header selector The selector cast `room` to `ISubscription` and then read subscription-only fields off it, reachable with the preview-room literal that has none of them. Narrow once through the same `'id' in room` check the selector already uses. * test(database): pin Subscription @JSON reference stability RoomStore's field selectors compare @JSON references across observe() emits, which holds only while the columns keep WatermelonDB's memo option. Dropping it from the model fails 16 of these assertions. * refactor(room-view): room predicates, membership tri-state and observable test helper Add a shared BehaviorSubject-backed test helper for database.active so the RoomStore, room-and-thread screens, MessageRow and messages list suites stop hand-rolling observe fakes. Replace the structural `in` checks on a room with isSubscriptionModel and isPreviewRoom, drop `status` from TPreviewRoom, and narrow before reading presence in the header. Replace the boolean `joined` on RoomState with a RoomMembership tri-state of preview, invited and subscribed. RoomGate now selects a single nullable invitation. RoomFooter renders once the store is ready, so a subscribed room no longer flashes the join footer while its subscription row is still being looked up. Match only WatermelonDB's not-found error as an absent row and log anything else, still falling back to preview mode. * docs(context): add Room Membership to the glossary
Centralize the Subscription-model discrimination that every render-time Room field read repeated. fromSubscription(select, previewValue) applies select when the room is a Subscription model and returns the reader's own fallback in Preview Mode. Migrate the render selectors in the message list, announcement banner, message row, encryption status, read-only, omnichannel department, footer federation, thread badge and the grouped useShallow selectors in header, right buttons and subscription unreads. Persistence, initialization, membership derivation and action-time guards keep the explicit check.
* perf(room-view): memoize MessageRow * fix(message): derive separators inside MessageProvider MessageRow is memoized, but WatermelonDB mutates records in place. A message sent before midnight and confirmed after it keeps the same record identity, so the memo skipped the re-render and the date separator stayed stale. Move the derivation into MessageProvider, which already subscribes both the message and the previous message to the same tick. MessageRow keeps calling the shared getMessageSeparators for the LoadMore branch, which sits outside the provider.
…ntity (#7670) * refactor: render the empty room background when RoomView mounts without a route identity * refactor: name the RoomBackground export and type its return * test(RoomView): drop assertions on a testID the suite can never render * refactor: keep rid and t required on the room screen props * refactor: declare tmid on the room screen props
* test: prove RoomMessageActions sheet handle attachment * refactor: pass RoomMessageActions refs directly * test: reuse typed store factories in RoomMessageActions test
* test(RoomView): assert an exact-null render in RightButtons routing guards * test(RoomView): drop composed RightButtons cases duplicated by routing and leaf suites
|
Android Build Available Rocket.Chat 4.77.0.109608 Internal App Sharing: https://play.google.com/apps/test/RQQ8k09hlnQ/ahAO29uNTnr2G67t5Hzb1qq6U-nwuC98cduPQK2LkPP1GV3TzvREV6VjhaHI_5SGlKPuV976AvpwCGm3eJIZfVGFkA |
|
iOS Build Available Rocket.Chat 4.77.0.109609 |
| @@ -0,0 +1,14 @@ | |||
| import { type ReactElement, type ReactNode } from 'react'; | |||
| import { A11y } from 'react-native-a11y-order'; | |||
There was a problem hiding this comment.
We should check if the lib doesn't do the verification about accessibility enable or not and no-op
| @@ -57,27 +58,13 @@ const useMessageRoomStore = <T,>(selector: (state: MessageRoomState) => T): T => | |||
| // Handlers and room constants captured once at mount by MessageRoomStoreProvider below (everything | |||
| // outside the reactive-tail resync effect). Callers MUST pass referentially stable values for these. | |||
| const FROZEN_KEYS = [ | |||
There was a problem hiding this comment.
Can we get rid of this?
| const isIgnored = useIsIgnored(item?.u?._id); | ||
| const threadBadgeColor = useThreadBadgeColor(item.id); | ||
| const { lastSeen } = useRoomScreen(); | ||
| const { dateSeparator, showUnreadSeparator } = getMessageSeparators(item, previousItem, lastSeen); |
There was a problem hiding this comment.
What's the performance of this? Shouldn't we memo it?
| const room = useRoomStore(s => s.room); | ||
| const readOnly = useReadOnly(); |
There was a problem hiding this comment.
The components could use the slice hooks instead.
| const navigation = useNavigation<IRoomViewProps['navigation']>(); | ||
| const isMasterDetail = useMasterDetail(); | ||
| // `t` comes from the store (seeded at mount) rather than route.params, which navigation can wipe. | ||
| const room = useRoomStoreByRid(rid, s => s.room); |
There was a problem hiding this comment.
Do we need to get the whole room here?
| // Latest-closure ref so the trigger effect can key on messages alone (one run per re-observe) with | ||
| // honest deps: fetchMessages re-keys on highTs, so listing the handler would also fire this mid-jump. | ||
| const onReObserveRef = useRef(onReObserve); | ||
| // Refreshed in a layout effect declared first — layout effects run in declaration order, so the | ||
| // trigger below always reads this commit's closure. | ||
| useLayoutEffect(() => { | ||
| onReObserveRef.current = onReObserve; | ||
| }); | ||
| useLayoutEffect(() => { | ||
| onReObserveRef.current(); | ||
| }, [messages]); |
There was a problem hiding this comment.
Over-engineered or slop?
| aboveTs: Date | number | string, | ||
| direction: 'nearest' | 'closestToLiveTail' | ||
| ): Promise<TAnyMessageModel | null> => { | ||
| const rows = (await database.active |
There was a problem hiding this comment.
Do we need to await?
#7678) * chore: drop unused reassure devDependency * test(MessageComposer): rename media transfer ownership suite after its component * test(RoomView): move observableDatabase helper out of __tests__
… openThread/openRoom services (#7679) Rename the hook to useJumpToMessage to match the CONTEXT.md Positional State row. Move Thread and Room opening into plain async services with explicit deps. Isolate route-param consumption in a non-exported helper hook so the generation counter has a single owner. Behavior and public surface unchanged; the three existing suites pass with only import renames. NATIVE-1550
… compiler contract test (#7680) * refactor(MessageActions): read the room at action time instead of during render * test(RoomView): remove the React Compiler contract test
Proposed changes
Migrates
app/views/RoomViewfrom a 1726-line class component to a function component. Behavior-preserving — no user-visible change intended.shouldComponentUpdate;connect(mapStateToProps)and all HOCs preserved.RoomStoreregistry (stores/RoomStore.ts): self-hydrates from the DB, shared by a room and its threads, torn down on last release;goRoomwarms it at nav time.RoomContextreplaced by a per-instance composer Zustand store (stores/ComposerStore.tsx).useRoomInit,useRoomAudioLifecycle,useRoomRemoved,useHeader,useJumpToMessage,useMessageActions,useOmnichannelPermissions,useRoomNavigation) and presentational components (MessageRow,RoomFooter,RoomMessageActions, room-state screens).'use memo'throughout so the React Compiler owns memoization.SearchMessagesViewresults are wrapped inA11yGateProviderso long-press message actions work there too — a small a11y addition riding along with the shared message-handler extraction.Issue(s)
https://rocketchat.atlassian.net/browse/NATIVE-34
How to test or reproduce
Use a room end to end (send/edit/quote/react, drafts, autocomplete, threads, jump-to-message, join a not-subscribed room, header actions) on phone and tablet, plus an omnichannel/livechat room. Behavior should match the base branch.
Screenshots
Types of changes
Checklist
Further comments
Stacked on
native-22-message-hooks(PR #7455) and targets it until NATIVE-22 lands ondevelop, after which this will be rebased and retargeted todevelop.Summary by CodeRabbit