From cffa4c87aaf7816d1a39e9f816c68676c1af2e40 Mon Sep 17 00:00:00 2001 From: Diego Mello Date: Fri, 11 Sep 2026 17:39:57 -0300 Subject: [PATCH 1/2] refactor(MessageActions): read the room at action time instead of during render --- app/containers/MessageActions/index.tsx | 17 ++-- .../components/RoomMessageActions.tsx | 6 +- .../RoomView/reactCompilerContract.test.ts | 80 ------------------- 3 files changed, 13 insertions(+), 90 deletions(-) delete mode 100644 app/views/RoomView/reactCompilerContract.test.ts diff --git a/app/containers/MessageActions/index.tsx b/app/containers/MessageActions/index.tsx index 29325562d5..a24c50789a 100644 --- a/app/containers/MessageActions/index.tsx +++ b/app/containers/MessageActions/index.tsx @@ -45,7 +45,7 @@ const REFOCUS_BUFFER = 50; const isVideoConf = (message: TAnyMessageModel) => message.t === 'videoconf'; export interface IMessageActionsProps { - room: TSubscriptionModel; + getRoom: () => TSubscriptionModel; tmid?: string; user: Pick; editInit: (messageId: string) => void; @@ -81,7 +81,7 @@ const MessageActions = memo( forwardRef( ( { - room, + getRoom, tmid, user, editInit, @@ -133,7 +133,7 @@ const MessageActions = memo( createDirectMessagePermission, createDiscussionOtherUserPermission ]; - const result = await hasPermission(permission, room.rid); + const result = await hasPermission(permission, getRoom().rid); permissions = { hasEditPermission: result[0], hasDeletePermission: result[1], @@ -219,7 +219,7 @@ const MessageActions = memo( const handleCreateDiscussion = (message: TAnyMessageModel) => { logEvent(events.ROOM_MSG_ACTION_DISCUSSION); - const params = { message, channel: room, showCloseModal: true }; + const params = { message, channel: getRoom(), showCloseModal: true }; if (isMasterDetail) { Navigation.navigate('ModalStackNavigator', { screen: 'CreateDiscussionView', params }); } else { @@ -239,7 +239,7 @@ const MessageActions = memo( const handleUnread = async (message: TAnyMessageModel) => { logEvent(events.ROOM_MSG_ACTION_UNREAD); const { id: messageId, ts } = message; - const { rid } = room; + const { rid } = getRoom(); try { const db = database.active; const result = await markAsUnread({ messageId }); @@ -355,6 +355,7 @@ const MessageActions = memo( }; const handleToggleTranslation = async (message: TAnyMessageModel) => { + const room = getRoom(); try { if (!room.autoTranslateLanguage) { return; @@ -403,6 +404,7 @@ const MessageActions = memo( }; const getConversationOptions = (message: TAnyMessageModel) => { + const room = getRoom(); const options: TActionSheetOptionsItem[] = []; const videoConfBlock = isVideoConf(message); @@ -474,6 +476,7 @@ const MessageActions = memo( }; const getSharingOptions = (message: TAnyMessageModel) => { + const room = getRoom(); const options: TActionSheetOptionsItem[] = []; const videoConfBlock = isVideoConf(message); @@ -567,7 +570,7 @@ const MessageActions = memo( } // Toggle Auto-translate - if (room.autoTranslate && isFromAnotherUser) { + if (getRoom().autoTranslate && isFromAnotherUser) { options.push({ title: I18n.t(message.autoTranslate !== false ? 'View_Original' : 'Translate'), icon: 'language', @@ -624,7 +627,7 @@ const MessageActions = memo( headerHeight: HEADER_HEIGHT, customHeader: ( <> - {!isReadOnly || room.reactWhenReadOnly ? ( + {!isReadOnly || getRoom().reactWhenReadOnly ? (
) : null} diff --git a/app/views/RoomView/components/RoomMessageActions.tsx b/app/views/RoomView/components/RoomMessageActions.tsx index 9a4f3a178f..3448a51105 100644 --- a/app/views/RoomView/components/RoomMessageActions.tsx +++ b/app/views/RoomView/components/RoomMessageActions.tsx @@ -24,18 +24,18 @@ export const RoomMessageActions = ({ const user = useAppSelector(getUserSelector); const readOnly = useReadOnly(); + const getRoom = () => roomStore.getState().room as TSubscriptionModel; + if (!isSubscribed) { return null; } - const room = roomStore.getState().room as TSubscriptionModel; - return ( <> { - const entries = fs.readdirSync(dir, { withFileTypes: true }); - const files: string[] = []; - - for (const entry of entries) { - const fullPath = path.join(dir, entry.name); - - if (entry.isDirectory()) { - if (entry.name === '__snapshots__') continue; - files.push(...collectSourceFiles(fullPath)); - continue; - } - - if (!/\.tsx?$/.test(entry.name)) continue; - if (entry.name.includes('.test.')) continue; - - files.push(fullPath); - } - - return files; -}; - -const compile = (file: string) => - transformFileSync(file, { - babelrc: false, - configFile: false, - presets: [ - ['@babel/preset-typescript', { isTSX: true, allExtensions: true }], - ['@babel/preset-react', { runtime: 'automatic' }] - ], - plugins: [['babel-plugin-react-compiler', { compilationMode: 'infer', panicThreshold: 'all_errors' }]] - }); - -describe('React Compiler contract for RoomView', () => { - const absoluteFiles = [...collectSourceFiles(ROOM_VIEW_DIR), ...EXTRA_FILES]; - const relativeFiles = absoluteFiles.map(file => path.relative(REPO_ROOT, file)); - - it('finds RoomView source files to compile', () => { - expect(relativeFiles.length).toBeGreaterThan(0); - }); - - const cleanFiles = relativeFiles.filter(file => !KNOWN_SKIPPED.includes(file)); - - test.each(cleanFiles)('%s compiles without the compiler silently skipping it', relativeFile => { - const absoluteFile = path.join(REPO_ROOT, relativeFile); - expect(() => compile(absoluteFile)).not.toThrow(); - }); - - // test.each throws on an empty array; the guard keeps the ratchet dormant until a file regresses. - if (KNOWN_SKIPPED.length) { - test.each(KNOWN_SKIPPED)('%s is still silently skipped (remove from KNOWN_SKIPPED once fixed)', relativeFile => { - const absoluteFile = path.join(REPO_ROOT, relativeFile); - expect(() => compile(absoluteFile)).toThrow(); - }); - } -}); From 7bd665600a28a562ada54456b4ff94fc88289048 Mon Sep 17 00:00:00 2001 From: Diego Mello Date: Fri, 11 Sep 2026 17:39:59 -0300 Subject: [PATCH 2/2] test(RoomView): remove the React Compiler contract test --- app/views/RoomView/hooks/useRoomInit.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/views/RoomView/hooks/useRoomInit.ts b/app/views/RoomView/hooks/useRoomInit.ts index 8de7b2144e..cebf416e9b 100644 --- a/app/views/RoomView/hooks/useRoomInit.ts +++ b/app/views/RoomView/hooks/useRoomInit.ts @@ -23,7 +23,7 @@ interface IRunInitSetters { // Marks the screen unsettled for the duration of one init() run. init() resolves on the invite // early-return and on failure alike, so the finally is the only place that settles it; awaiting it is // what keeps the footer from flickering. Lives outside the hook because the React Compiler cannot -// lower a try/finally inside a hook body (see reactCompilerContract.test.ts). +// lower a try/finally inside a hook body. // // `controller` belongs to this run alone and is never reset by a later one: once a newer run aborts // it, this run stops writing for a screen that has already moved on.