[GSOC POC bottom tabs navigation]feat: migrate from drawer to bottom tabs navigation with iOS 26 Liqui…#7070
Conversation
…d Glass Replace DrawerNavigator with native bottom tab navigator using react-native-bottom-tabs for phone layout. Adds 5 tabs: Chats, Discussions, DMs, Search (with iOS 26 expanding search role), and More. - Add BottomTabNavigator with SF Symbol icons and sidebarAdaptable - Add SearchView with native headerSearchBarOptions - Add MoreView for Profile, Settings, Accessibility, Admin - Filter RoomsListView by tab via roomFilter route param - Patch react-native-bottom-tabs to preserve Liquid Glass on iOS 26 - Set UIDesignRequiresCompatibility to false for iOS 26 visual features
WalkthroughA new bottom tab navigation system is introduced, replacing drawer-based navigation with five tabs: Home, Discussions, DMs, Search, and More. Each tab manages its own native stack navigator. Chat-related tabs (Home, Discussions, DMs) filter room lists via Changes
Sequence DiagramsequenceDiagram
participant User
participant BottomTabNavigator
participant TabStack as Tab Stack<br/>(HomeStack/DiscussionsStack/DMsStack)
participant RoomsListView
participant useSubscriptions
participant useHeader
User->>BottomTabNavigator: Tap tab (e.g., Discussions)
BottomTabNavigator->>TabStack: Render tab with roomFilter param
TabStack->>RoomsListView: Navigate with roomFilter param
RoomsListView->>useSubscriptions: Pass roomFilter
useSubscriptions->>useSubscriptions: Filter subscriptions<br/>(home/discussions/dms)
useSubscriptions-->>RoomsListView: Return filtered rooms
RoomsListView->>useHeader: Pass roomFilter
useHeader->>useHeader: Derive isTabLayout<br/>Set tab-specific title
useHeader-->>RoomsListView: Return header options
RoomsListView-->>User: Display filtered room list<br/>with tab-specific header
Estimated code review effort🎯 4 (Complex) | ⏱️ ~50 minutes Suggested labels
🚥 Pre-merge checks | ✅ 3✅ Passed checks (3 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
Actionable comments posted: 8
🧹 Nitpick comments (1)
app/stacks/BottomTabNavigator.tsx (1)
68-68: Consider adding proper typing for theStackparameter.Using
anyloses type safety. You could create a generic type or useReturnType<typeof createNativeStackNavigator>to maintain type checking.♻️ Suggested improvement
-const renderChatScreens = (Stack: any) => ( +const renderChatScreens = <T extends ReturnType<typeof createNativeStackNavigator<ChatsStackParamList & TNavigation>>>(Stack: T) => (Alternatively, if the above is too complex, at minimum use a more specific type alias.
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@app/stacks/BottomTabNavigator.tsx` at line 68, The parameter Stack of renderChatScreens is typed as any which removes type safety; change its type to a navigator stack type (e.g., use ReturnType<typeof createNativeStackNavigator> or a generic navigator type) so renderChatScreens(Stack) and its children get correct props; update the function signature for renderChatScreens and add an appropriate import/type alias referencing createNativeStackNavigator (or a more specific alias) so TypeScript enforces navigator screen types.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@app/stacks/BottomTabNavigator.tsx`:
- Around line 235-236: BottomTabNavigator.tsx currently uses iOS-only sfSymbol
in each tabBarIcon (e.g., tabBarIcon: () => ({ sfSymbol: 'house.fill' })), so
update every tabBarIcon definition (the ones using sfSymbol at the listed sites)
to use Platform.select() and return platform-specific icon props: on iOS return
{ type: 'sfSymbol', sfSymbol: '...' } and on Android return { type: 'image',
image: <android resource name> } or { type: 'materialSymbol', name: '...' } as
appropriate; keep the same tabBarIcon property name and symbol intent but
provide an Android alternative for each sfSymbol occurrence.
In `@app/stacks/InsideStack.tsx`:
- Line 324: goRoom.ts unconditionally calls Navigation.popTo('DrawerNavigator')
which crashes when the DrawerNavigator route was removed from InsideStack;
update the goRoom function so the Navigation.popTo('DrawerNavigator') call is
executed only when isMasterDetail is true (i.e., wrap or guard that popTo call
with if (isMasterDetail)), matching the existing conditional logic used
elsewhere and ensuring all room navigation paths (RoomsListView, SearchView,
etc.) do not attempt to pop to a non-existent route.
In `@app/views/MoreView/index.tsx`:
- Around line 14-20: The admin visibility check misses the
'view-user-administration' permission: update the usePermissions call in
MoreView to request 'view-user-administration' (e.g., const
[viewRoomAdministrationPermission, viewStatisticsPermission,
viewPrivilegedSettingPermission, viewUserAdministrationPermission] =
usePermissions([...])) and include the new viewUserAdministrationPermission in
the showAdmin boolean (showAdmin = viewRoomAdministrationPermission ||
viewStatisticsPermission || viewPrivilegedSettingPermission ||
viewUserAdministrationPermission) so users with only user-admin rights see the
Admin panel; adjust variable names to match your naming convention.
In `@app/views/SearchView/index.tsx`:
- Around line 37-49: The debounced handleSearch callback can leave
searching=true when searchMethod rejects; wrap the await searchMethod({ text })
call in a try/catch/finally inside handleSearch (the function created by
useDebounce) so you catch errors, call setResults([]) or keep prior results in
the catch as appropriate, and always call setSearching(false) in finally; also
ensure setHasSearched(true) behavior remains correct and consider logging the
error or surfacing it to the UI inside the catch.
In `@ios/RocketChatRN.xcodeproj/project.pbxproj`:
- Line 1828: The change removed quoting around build path expansions which can
break when resolved paths contain spaces; restore quotes around the affected
build setting usages such as "$TARGET_BUILD_DIR/$INFOPLIST_PATH" and
"$PODS_CONFIGURATION_BUILD_DIR/Firebase" in the project.pbxproj entries to
ensure paths with spaces are handled correctly, updating each occurrence that
was unquoted (the entries referencing $TARGET_BUILD_DIR/$INFOPLIST_PATH and
$PODS_CONFIGURATION_BUILD_DIR/Firebase) so the build script lines wrap the
entire expanded path in double quotes.
In `@ios/RocketChatRN/Info.plist`:
- Around line 111-112: The Info.plist change sets UIDesignRequiresCompatibility
to false enabling iOS 26 visuals before we've updated the UI; revert this
temporary change by restoring UIDesignRequiresCompatibility to true (or removing
the false override) until the app's UI components and styles referenced by your
React Native screens and native iOS views are updated for the iOS 26 design
system (identify and update any custom UI components, storyboards, or
Swift/Objective-C view code), then run visual and functional testing across
supported OS versions (iOS 15.1 through 25.x and a device/simulator on iOS 26)
to confirm correct rendering and behavior; once all UI fixes and cross-version
tests pass, re-apply UIDesignRequiresCompatibility = false and document the
changes and test results.
In `@package.json`:
- Line 27: Tests fail because the package "@bottom-tabs/react-navigation" used
by app/stacks/BottomTabNavigator.tsx has no Jest mock or transform; add a Jest
manual mock or moduleNameMapper entry for "@bottom-tabs/react-navigation" and/or
include it in jest.transformIgnorePatterns so imports resolve during tests.
Specifically, create a manual mock under __mocks__ for the module name or add
mapping in jest.config.js (moduleNameMapper) to point
"@bottom-tabs/react-navigation" to a simple mock module, or update
transformIgnorePatterns to allow transforming this package so
BottomTabNavigator.tsx can import it in tests.
- Line 93: The dependency "react-native-bottom-tabs" is declared with a caret
("^1.1.0") which allows yarn to install 1.1.x and will break patch-package since
the patch file targets +1.1.0; update the package.json dependency entry for
"react-native-bottom-tabs" to the exact version "1.1.0" (remove the caret) so
installed version matches patches/react-native-bottom-tabs+1.1.0.patch and then
re-run install to regenerate the lockfile.
---
Nitpick comments:
In `@app/stacks/BottomTabNavigator.tsx`:
- Line 68: The parameter Stack of renderChatScreens is typed as any which
removes type safety; change its type to a navigator stack type (e.g., use
ReturnType<typeof createNativeStackNavigator> or a generic navigator type) so
renderChatScreens(Stack) and its children get correct props; update the function
signature for renderChatScreens and add an appropriate import/type alias
referencing createNativeStackNavigator (or a more specific alias) so TypeScript
enforces navigator screen types.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Organization UI
Review profile: CHILL
Plan: Pro
Run ID: 46e4164e-82e1-49b4-8a17-7db44dfca963
⛔ Files ignored due to path filters (2)
ios/Podfile.lockis excluded by!**/*.lockyarn.lockis excluded by!**/yarn.lock,!**/*.lock
📒 Files selected for processing (12)
app/stacks/BottomTabNavigator.tsxapp/stacks/InsideStack.tsxapp/stacks/types.tsapp/views/MoreView/index.tsxapp/views/RoomsListView/hooks/useHeader.tsxapp/views/RoomsListView/hooks/useSubscriptions.tsapp/views/RoomsListView/index.tsxapp/views/SearchView/index.tsxios/RocketChatRN.xcodeproj/project.pbxprojios/RocketChatRN/Info.plistpackage.jsonpatches/react-native-bottom-tabs+1.1.0.patch
📜 Review details
🧰 Additional context used
🧠 Learnings (2)
📚 Learning: 2026-03-10T15:21:45.098Z
Learnt from: Rohit3523
Repo: RocketChat/Rocket.Chat.ReactNative PR: 7046
File: app/containers/InAppNotification/NotifierComponent.stories.tsx:46-75
Timestamp: 2026-03-10T15:21:45.098Z
Learning: In `app/containers/InAppNotification/NotifierComponent.tsx` (React Native, Rocket.Chat), `NotifierComponent` is exported as a Redux-connected component via `connect(mapStateToProps)`. The `isMasterDetail` prop is automatically injected from `state.app.isMasterDetail` and does not need to be passed explicitly at call sites or in Storybook stories that use the default (connected) export.
Applied to files:
app/views/RoomsListView/index.tsxapp/stacks/InsideStack.tsxapp/views/MoreView/index.tsxapp/stacks/BottomTabNavigator.tsx
📚 Learning: 2026-02-05T13:55:00.974Z
Learnt from: Rohit3523
Repo: RocketChat/Rocket.Chat.ReactNative PR: 6930
File: package.json:101-101
Timestamp: 2026-02-05T13:55:00.974Z
Learning: In this repository, the dependency on react-native-image-crop-picker should reference the RocketChat fork (RocketChat/react-native-image-crop-picker) with explicit commit pins, not the upstream ivpusic/react-native-image-crop-picker. Update package.json dependencies (and any lockfile) to point to the fork URL and a specific commit, ensuring edge-to-edge Android fixes are included. This pattern should apply to all package.json files in the repo that declare this dependency.
Applied to files:
package.json
🔇 Additional comments (11)
app/stacks/BottomTabNavigator.tsx (8)
1-66: LGTM!Imports are well-organized by category (Chat screens, Search, More tab screens) and align with the components used in the navigator stacks below.
127-164: LGTM on the stack structure and roomFilter implementation.The three chat stacks (
HomeStack,DiscussionsStack,DMsStack) correctly pass distinctroomFiltervalues viainitialParams, which aligns with the updatedChatsStackParamList.RoomsListViewsignature intypes.ts.
166-180: LGTM!The
SearchStackis cleanly implemented with appropriate large title styling for iOS.
182-218: LGTM!The
MoreStackappropriately includes profile, settings, and admin screens. The duplicate registrations ofChangeAvatarView,PushTroubleshootView, andPickerView(also in chat stacks) are reasonable since they allow context-specific navigation from both the More tab and room-related flows.
220-275: LGTM on the overall BottomTabNavigator structure.The five-tab structure (Chats, Discussions, DMs, Search, More) is well-organized with appropriate i18n titles and SF Symbol icons for iOS.
226-229: Verify Android behavior for iOS-specific tab navigator options.
sidebarAdaptableandignoresTopSafeAreaare iOS-specific props added for the iOS 26 Liquid Glass design. Ensure@bottom-tabs/react-navigationgracefully ignores these on Android, and verify theheaderShownoption works as expected despite the type definition gap.
68-125: Verify stack-aware navigation for screens registered in multiple tab stacks.The
renderChatScreensfunction registers 30+ screens (includingRoomView,ThreadMessagesView,RoomInfoView,RoomActionsView) identically acrossHomeStack,DiscussionsStack, andDMsStack. Navigation calls likenavigation.navigate('ThreadMessagesView', { rid, t })from within these screens don't specify a parent stack, which could cause unexpected behavior when called from different tabs.Note: The codebase shows awareness of this issue in certain contexts—
RoomView/RightButtons.tsxuses stack-aware navigation (navigation.navigate('ModalStackNavigator', { screen: 'ThreadMessagesView', ... })) for the MasterDetail layout, but standard navigation calls remain unspecified. Consider standardizing navigation to explicitly target the intended stack or clarifying whether React Navigation's default resolution (nearest navigator) is the intended behavior.
259-259: Remove the suggestion to add version compatibility checks forrole: 'search'.The
role: 'search'property is a documented screen option in@bottom-tabs/react-navigation(as shown in their official documentation). The library itself handles platform compatibility internally—developers don't need to add conditional version or Platform checks. This property is supported on iOS 18+ and the native implementation gracefully handles older versions and Android through the library's abstraction layer.> Likely an incorrect or invalid review comment.app/stacks/types.ts (3)
22-23: LGTM!The
RoomFilterTypeunion type and optionalroomFilterparameter correctly support the tab-based filtering while maintaining backward compatibility with existing navigation calls.Also applies to: 31-31
244-261: LGTM!The new param list types are well-structured:
SearchStackParamListis minimal and correctMoreStackParamListappropriately combines multiple stack param lists via intersectionBottomTabParamListcorrectly wraps each stack's params withNavigatorScreenParams
299-301: The coexistence ofDrawerNavigatorandBottomTabNavigatorinInsideStackParamListis intentional and correct.Both navigators are used in a responsive design pattern:
InsideStack.tsximplements the phone layout withBottomTabNavigator, whileMasterDetailStackimplements the tablet layout withDrawerNavigator. Navigation code throughout the codebase correctly switches between them based on theisMasterDetailflag. The shared type definition supports both device configurations without issues.
| tabBarIcon: () => ({ sfSymbol: 'house.fill' }) | ||
| }} |
There was a problem hiding this comment.
🧩 Analysis chain
🌐 Web query:
@bottom-tabs/react-navigation Android icon support sfSymbol
💡 Result:
No, @react-navigation/bottom-tabs does not support SF Symbols (sfSymbol type) on Android. SF Symbols are iOS-only. Official React Navigation documentation explicitly states: - For Native Bottom Tabs Navigator (which uses native BottomNavigationView on Android): tabBarIcon with type: 'sfSymbol' is "Supported on iOS" only. On Android, use type: 'image' with source (images are always tinted). Example from docs: tabBarIcon: Platform.select({ ios: { type: 'sfSymbol', name: 'heart', }, android: { type: 'image', source: require('./path/to/icon.png'), }, }); - Icons docs: sfSymbol (iOS only), materialSymbol (Android only). - Customizing bottom tab bar (v8.x): "SF Symbols name - iOS", "For Android, you can use Material Symbols by setting type: 'materialSymbol'". A GitHub issue confirms users attempting sfSymbol on Android must use 'resource' or images instead. On Android (native impl), active/inactive icon switching with functions is not supported; inactive icon is used for both.
Citations:
- 1: sfsymbols not appearing on native tab bar react-navigation/react-navigation#12961
- 2: https://reactnavigation.org/docs/8.x/icons
- 3: https://reactnavigation.org/docs/native-bottom-tab-navigator/
- 4: https://reactnavigation.org/docs/8.x/customizing-tabbar/
- 5: https://reactnavigation.org/docs/native-bottom-tab-navigator
Add Android icon handling for all tab bar icons.
sfSymbol is iOS-only and will not display on Android. Use Platform.select() to provide iOS icons with type: 'sfSymbol' and Android icons with type: 'image' (image resource) or type: 'materialSymbol' (Material Symbols).
This applies to all tab bar icons at lines 235-236, 243-244, 251-252, 260-261, and 268-269.
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@app/stacks/BottomTabNavigator.tsx` around lines 235 - 236,
BottomTabNavigator.tsx currently uses iOS-only sfSymbol in each tabBarIcon
(e.g., tabBarIcon: () => ({ sfSymbol: 'house.fill' })), so update every
tabBarIcon definition (the ones using sfSymbol at the listed sites) to use
Platform.select() and return platform-specific icon props: on iOS return { type:
'sfSymbol', sfSymbol: '...' } and on Android return { type: 'image', image:
<android resource name> } or { type: 'materialSymbol', name: '...' } as
appropriate; keep the same tabBarIcon property name and symbol intent but
provide an Android alternative for each sfSymbol occurrence.
| return ( | ||
| <InsideStack.Navigator screenOptions={{ ...defaultHeader, ...themedHeader(theme), presentation: 'containedModal' }}> | ||
| <InsideStack.Screen name='DrawerNavigator' component={DrawerNavigator} options={{ headerShown: false }} /> | ||
| <InsideStack.Screen name='BottomTabNavigator' component={BottomTabNavigator} options={{ headerShown: false }} /> |
There was a problem hiding this comment.
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
set -euo pipefail
echo "All references to DrawerNavigator route name:"
rg -n "['\"]DrawerNavigator['\"]" -C2
echo
echo "InsideStack registered screen names:"
rg -n "InsideStack\\.Screen name=" app/stacks/InsideStack.tsx -C1Repository: RocketChat/Rocket.Chat.ReactNative
Length of output: 3870
🏁 Script executed:
# Check the goRoom.ts function and its callers
cat -n app/lib/methods/helpers/goRoom.ts | head -60
echo "---"
echo "Check if goRoom function has isMasterDetail usage:"
rg -B5 -A10 "popTo\('DrawerNavigator'\)" app/lib/methods/helpers/goRoom.tsRepository: RocketChat/Rocket.Chat.ReactNative
Length of output: 2730
🏁 Script executed:
# Check what's exported from goRoom and who calls it
echo "=== Exports from goRoom.ts ==="
grep -n "export" app/lib/methods/helpers/goRoom.ts
echo ""
echo "=== Callers of goRoom ==="
rg "from.*goRoom|import.*goRoom" -A2
echo ""
echo "=== Direct calls to navigate from goRoom ==="
rg "popTo\('DrawerNavigator'\)" -B20 | grep -A5 "goRoom"Repository: RocketChat/Rocket.Chat.ReactNative
Length of output: 7911
🏁 Script executed:
# Check the full goRoom function
sed -n '85,132p' app/lib/methods/helpers/goRoom.tsRepository: RocketChat/Rocket.Chat.ReactNative
Length of output: 1402
🏁 Script executed:
# Check how goRoom is called in a few key callers
echo "=== RoomsListView calling goRoom ==="
rg -B3 -A3 "goRoom\(" app/views/RoomsListView/index.tsx | head -30
echo ""
echo "=== SearchView calling goRoom ==="
rg -B3 -A3 "goRoom\(" app/views/SearchView/index.tsx | head -30
echo ""
echo "=== Check for isMasterDetail usage in these views ==="
rg "isMasterDetail" app/views/RoomsListView/index.tsx | head -10
</parameter>Repository: RocketChat/Rocket.Chat.ReactNative
Length of output: 773
🏁 Script executed:
# Find where InsideStack and MasterDetailStack are used
echo "=== InsideStack usage ==="
rg "InsideStack" --type tsx --type ts | grep -v "import" | head -20
echo ""
echo "=== MasterDetailStack usage ==="
rg "MasterDetailStack" --type tsx --type ts | grep -v "import" | head -20Repository: RocketChat/Rocket.Chat.ReactNative
Length of output: 209
🏁 Script executed:
# Find where InsideStack and MasterDetailStack are used
echo "=== InsideStack usage ==="
rg "InsideStack" -t typescript -t javascript | grep -v "^.*import" | head -20
echo ""
echo "=== MasterDetailStack usage ==="
rg "MasterDetailStack" -t typescript -t javascript | grep -v "^.*import" | head -20
echo ""
echo "=== Check RootNavigator to see which stack is used based on isMasterDetail ==="
find app -name "*RootNavigator*" -o -name "*root*" -o -name "*Root*" | grep -i navRepository: RocketChat/Rocket.Chat.ReactNative
Length of output: 303
🏁 Script executed:
# Find where InsideStack and MasterDetailStack are referenced
echo "=== InsideStack usage ==="
rg "InsideStack" | grep -v "^.*:.*import" | head -20
echo ""
echo "=== MasterDetailStack usage ==="
rg "MasterDetailStack" | grep -v "^.*:.*import" | head -20
echo ""
echo "=== List app/stacks directory ==="
ls -la app/stacks/Repository: RocketChat/Rocket.Chat.ReactNative
Length of output: 2950
🏁 Script executed:
# Check AppContainer to understand when each stack is used
echo "=== AppContainer navigation setup ==="
cat -n app/AppContainer.tsx | grep -B10 -A10 "MasterDetailStack\|InsideStack"
echo ""
echo "=== Check if there's conditional rendering based on isMasterDetail ==="
rg -B5 -A5 "isMasterDetail.*?" app/AppContainer.tsx | head -40Repository: RocketChat/Rocket.Chat.ReactNative
Length of output: 4271
Critical: goRoom.ts unconditionally targets removed DrawerNavigator route, causing runtime crashes in non-master-detail mode.
Line 324 removes DrawerNavigator from InsideStack, but goRoom.ts line 46 unconditionally calls Navigation.popTo('DrawerNavigator') regardless of isMasterDetail state. When isMasterDetail=false, the app uses InsideStack (not MasterDetailStack), and any call to goRoom() will crash attempting to pop to a non-existent route. This affects all room navigation (RoomsListView, SearchView, etc.) in non-master-detail mode.
The popTo call must be guarded by if (isMasterDetail) to match the conditional logic at line 47.
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@app/stacks/InsideStack.tsx` at line 324, goRoom.ts unconditionally calls
Navigation.popTo('DrawerNavigator') which crashes when the DrawerNavigator route
was removed from InsideStack; update the goRoom function so the
Navigation.popTo('DrawerNavigator') call is executed only when isMasterDetail is
true (i.e., wrap or guard that popTo call with if (isMasterDetail)), matching
the existing conditional logic used elsewhere and ensuring all room navigation
paths (RoomsListView, SearchView, etc.) do not attempt to pop to a non-existent
route.
| const [viewRoomAdministrationPermission, viewStatisticsPermission, viewPrivilegedSettingPermission] = usePermissions([ | ||
| 'view-room-administration', | ||
| 'view-statistics', | ||
| 'view-privileged-setting' | ||
| ]); | ||
| const showAdmin = viewRoomAdministrationPermission || viewStatisticsPermission || viewPrivilegedSettingPermission; | ||
|
|
There was a problem hiding this comment.
Admin gate misses view-user-administration permission.
This diverges from app/views/SidebarView/components/Admin.tsx (Lines 14-35), so users with only user-admin permission may not see Admin_Panel in More.
🔧 Proposed fix
- const [viewRoomAdministrationPermission, viewStatisticsPermission, viewPrivilegedSettingPermission] = usePermissions([
+ const [viewRoomAdministrationPermission, viewStatisticsPermission, viewUserAdministrationPermission, viewPrivilegedSettingPermission] =
+ usePermissions([
'view-room-administration',
'view-statistics',
+ 'view-user-administration',
'view-privileged-setting'
- ]);
- const showAdmin = viewRoomAdministrationPermission || viewStatisticsPermission || viewPrivilegedSettingPermission;
+ ]);
+ const showAdmin = [
+ viewRoomAdministrationPermission,
+ viewStatisticsPermission,
+ viewUserAdministrationPermission,
+ viewPrivilegedSettingPermission
+ ].some(Boolean);📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| const [viewRoomAdministrationPermission, viewStatisticsPermission, viewPrivilegedSettingPermission] = usePermissions([ | |
| 'view-room-administration', | |
| 'view-statistics', | |
| 'view-privileged-setting' | |
| ]); | |
| const showAdmin = viewRoomAdministrationPermission || viewStatisticsPermission || viewPrivilegedSettingPermission; | |
| const [viewRoomAdministrationPermission, viewStatisticsPermission, viewUserAdministrationPermission, viewPrivilegedSettingPermission] = | |
| usePermissions([ | |
| 'view-room-administration', | |
| 'view-statistics', | |
| 'view-user-administration', | |
| 'view-privileged-setting' | |
| ]); | |
| const showAdmin = [ | |
| viewRoomAdministrationPermission, | |
| viewStatisticsPermission, | |
| viewUserAdministrationPermission, | |
| viewPrivilegedSettingPermission | |
| ].some(Boolean); |
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@app/views/MoreView/index.tsx` around lines 14 - 20, The admin visibility
check misses the 'view-user-administration' permission: update the
usePermissions call in MoreView to request 'view-user-administration' (e.g.,
const [viewRoomAdministrationPermission, viewStatisticsPermission,
viewPrivilegedSettingPermission, viewUserAdministrationPermission] =
usePermissions([...])) and include the new viewUserAdministrationPermission in
the showAdmin boolean (showAdmin = viewRoomAdministrationPermission ||
viewStatisticsPermission || viewPrivilegedSettingPermission ||
viewUserAdministrationPermission) so users with only user-admin rights see the
Admin panel; adjust variable names to match your naming convention.
| const handleSearch = useDebounce(async (text: string) => { | ||
| if (!text.trim()) { | ||
| setResults([]); | ||
| setSearching(false); | ||
| setHasSearched(false); | ||
| return; | ||
| } | ||
| setSearching(true); | ||
| setHasSearched(true); | ||
| const result = await searchMethod({ text }); | ||
| setResults(result as IRoomItem[]); | ||
| setSearching(false); | ||
| }, 300); |
There was a problem hiding this comment.
Handle search failures to prevent a stuck loading state.
At Line 46, a rejected searchMethod call skips Line 48, leaving searching true and surfacing an unhandled promise rejection.
🔧 Proposed fix
const handleSearch = useDebounce(async (text: string) => {
if (!text.trim()) {
setResults([]);
setSearching(false);
setHasSearched(false);
return;
}
setSearching(true);
setHasSearched(true);
- const result = await searchMethod({ text });
- setResults(result as IRoomItem[]);
- setSearching(false);
+ try {
+ const result = await searchMethod({ text });
+ setResults(result as IRoomItem[]);
+ } catch {
+ setResults([]);
+ } finally {
+ setSearching(false);
+ }
}, 300);📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| const handleSearch = useDebounce(async (text: string) => { | |
| if (!text.trim()) { | |
| setResults([]); | |
| setSearching(false); | |
| setHasSearched(false); | |
| return; | |
| } | |
| setSearching(true); | |
| setHasSearched(true); | |
| const result = await searchMethod({ text }); | |
| setResults(result as IRoomItem[]); | |
| setSearching(false); | |
| }, 300); | |
| const handleSearch = useDebounce(async (text: string) => { | |
| if (!text.trim()) { | |
| setResults([]); | |
| setSearching(false); | |
| setHasSearched(false); | |
| return; | |
| } | |
| setSearching(true); | |
| setHasSearched(true); | |
| try { | |
| const result = await searchMethod({ text }); | |
| setResults(result as IRoomItem[]); | |
| } catch { | |
| setResults([]); | |
| } finally { | |
| setSearching(false); | |
| } | |
| }, 300); |
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@app/views/SearchView/index.tsx` around lines 37 - 49, The debounced
handleSearch callback can leave searching=true when searchMethod rejects; wrap
the await searchMethod({ text }) call in a try/catch/finally inside handleSearch
(the function created by useDebounce) so you catch errors, call setResults([])
or keep prior results in the catch as appropriate, and always call
setSearching(false) in finally; also ensure setHasSearched(true) behavior
remains correct and consider logging the error or surfacing it to the UI inside
the catch.
| ); | ||
| inputPaths = ( | ||
| "$TARGET_BUILD_DIR/$INFOPLIST_PATH", | ||
| $TARGET_BUILD_DIR/$INFOPLIST_PATH, |
There was a problem hiding this comment.
Removing quotes from paths may cause issues if paths contain spaces.
The quotes were removed from $TARGET_BUILD_DIR/$INFOPLIST_PATH (Lines 1828, 1848) and $PODS_CONFIGURATION_BUILD_DIR/Firebase (Lines 2596, 2672). While this typically works in standard configurations, unquoted paths can break if the resolved values contain spaces (e.g., custom derived data directories or user paths with spaces).
This is low-risk in most environments but worth verifying on your CI/CD setup.
Also applies to: 1848-1848, 2596-2596, 2672-2672
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@ios/RocketChatRN.xcodeproj/project.pbxproj` at line 1828, The change removed
quoting around build path expansions which can break when resolved paths contain
spaces; restore quotes around the affected build setting usages such as
"$TARGET_BUILD_DIR/$INFOPLIST_PATH" and "$PODS_CONFIGURATION_BUILD_DIR/Firebase"
in the project.pbxproj entries to ensure paths with spaces are handled
correctly, updating each occurrence that was unquoted (the entries referencing
$TARGET_BUILD_DIR/$INFOPLIST_PATH and $PODS_CONFIGURATION_BUILD_DIR/Firebase) so
the build script lines wrap the entire expanded path in double quotes.
| <key>UIDesignRequiresCompatibility</key> | ||
| <true/> | ||
| <false/> |
There was a problem hiding this comment.
🧩 Analysis chain
🌐 Web query:
What is UIDesignRequiresCompatibility in iOS Info.plist and what does setting it to false do?
💡 Result:
UIDesignRequiresCompatibility is a Boolean key in an iOS app's Info.plist file. It indicates whether the system should run the app in a compatibility mode for UI elements. When set to YES (true), the system uses compatibility mode, displaying the app's UI as it would appear when built against previous SDK versions, opting out of new UI designs like iOS 26's "Liquid Glass". Setting it to false (NO), or omitting it (default for apps linking against the latest SDKs), means the system uses the UI design of the running OS without compatibility mode, applying the latest design changes to standard UI components. Apple warns this key should be used temporarily while updating the app's UI for the latest SDKs.
Citations:
- 1: https://developer.apple.com/documentation/BundleResources/Information-Property-List/UIDesignRequiresCompatibility
- 2: https://stackoverflow.com/questions/79842759/can-uidesignrequirescompatibility-in-ios-26-be-enabled-only-for-part-of-the-ui-i
- 3: https://xorbix.com/insights/ios-26-liquid-glass-buttons-how-to-maintain-your-apps-custom-ui/
- 4: https://www.reddit.com/r/swift/comments/1lqzf3p/opting_your_app_out_of_liquid_glass_in_xcode_26/
🌐 Web query:
What is iOS Liquid Glass feature and which iOS version introduced it?
💡 Result:
iOS Liquid Glass is a new dynamic design material introduced by Apple that combines the optical properties of glass with fluidity. It is translucent, reflects and refracts surroundings, dynamically adapts to content and context, and is used in controls, navigation, app icons, and system interfaces to create expressive, harmonious experiences across Apple platforms. It was introduced in iOS 26, announced at WWDC 2025 on June 9, 2025, along with iPadOS 26, macOS Tahoe 26, watchOS 26, and tvOS 26.
Citations:
- 1: https://developer.apple.com/documentation/technologyoverviews/liquid-glass
- 2: https://www.apple.com/newsroom/2025/06/apple-introduces-a-delightful-and-elegant-new-software-design/
- 3: https://macrumors.com/guide/ios-26-liquid-glass
- 4: https://en.wikipedia.org/wiki/Liquid_Glass
🌐 Web query:
What is the latest iOS version as of March 2026?
💡 Result:
The latest iOS version as of March 28, 2026, is iOS 26.4.
Citations:
- 1: https://9to5mac.com/2026/03/06/ios-26-4-new-iphone-features/
- 2: https://9to5mac.com/2026/03/18/ios-26-4-release-date-heres-when-new-iphone-features-are-coming/
- 3: https://www.macworld.com/article/2575705/ios-26-features-latest-update-release-date-beta.html
- 4: https://support.apple.com/en-us/123075
- 5: https://support.apple.com/en-us/100100
- 6: https://www.macworld.com/article/1659017/ios-versions-list.html
- 7: https://developer.apple.com/documentation/ios-iPados-release-notes/ios-ipados-26_4-release-notes
- 8: https://me.mashable.com/tech/68900/apple-releases-ios-264-update-for-iphones-here-are-top-10-features-how-to-download
- 9: https://me.mashable.com/tech/68884/ios-264-available-now-all-updates-security-improvements-to-know
- 10: https://www.zdnet.com/article/apple-ios-26-4-new-features-siri/
🏁 Script executed:
# Check for minimum iOS version in Info.plist
grep -n "MinimumOSVersion\|NSMinimumOSVersion" ios/RocketChatRN/Info.plistRepository: RocketChat/Rocket.Chat.ReactNative
Length of output: 60
🏁 Script executed:
# Check for iOS version in project.pbxproj or other Xcode config
fd -e pbxproj -o -e xcconfig | xargs grep -l "IPHONEOS_DEPLOYMENT_TARGET\|MinimumOSVersion" 2>/dev/null | head -5Repository: RocketChat/Rocket.Chat.ReactNative
Length of output: 194
🏁 Script executed:
# Find Xcode project files
fd -e pbxproj -o -e xcconfigRepository: RocketChat/Rocket.Chat.ReactNative
Length of output: 194
🏁 Script executed:
# Search for deployment target in all project files
rg "IPHONEOS_DEPLOYMENT_TARGET|MinimumOSVersion|mmacosx-version-min" ios/Repository: RocketChat/Rocket.Chat.ReactNative
Length of output: 897
🏁 Script executed:
# Check the full Info.plist content around version-related keys
cat -n ios/RocketChatRN/Info.plist | grep -i "version\|deployment\|minimum\|os"Repository: RocketChat/Rocket.Chat.ReactNative
Length of output: 701
Verify backward compatibility and complete UI updates for iOS 26.
The change correctly enables iOS 26 Liquid Glass features. However, the app supports iOS 15.1+ (minimum deployment target), creating a 10+ major version gap. Apple's documentation explicitly states that UIDesignRequiresCompatibility = false should be used "temporarily while updating the app's UI for the latest SDKs."
Ensure:
- The app's UI has been fully updated to handle iOS 26 design system changes
- Behavior has been tested on older supported iOS versions (15.1-25.x) to confirm they render appropriately with their respective OS design systems
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@ios/RocketChatRN/Info.plist` around lines 111 - 112, The Info.plist change
sets UIDesignRequiresCompatibility to false enabling iOS 26 visuals before we've
updated the UI; revert this temporary change by restoring
UIDesignRequiresCompatibility to true (or removing the false override) until the
app's UI components and styles referenced by your React Native screens and
native iOS views are updated for the iOS 26 design system (identify and update
any custom UI components, storyboards, or Swift/Objective-C view code), then run
visual and functional testing across supported OS versions (iOS 15.1 through
25.x and a device/simulator on iOS 26) to confirm correct rendering and
behavior; once all UI fixes and cross-version tests pass, re-apply
UIDesignRequiresCompatibility = false and document the changes and test results.
| "bugsnag:upload-android": "bugsnag-cli upload react-native-android" | ||
| }, | ||
| "dependencies": { | ||
| "@bottom-tabs/react-navigation": "^1.1.0", |
There was a problem hiding this comment.
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
set -euo pipefail
echo "New bottom-tabs imports:"
rg -n '@bottom-tabs/react-navigation|react-native-bottom-tabs' app/stacks/BottomTabNavigator.tsx -C2
echo
echo "Jest coverage for these modules:"
rg -n '@bottom-tabs/react-navigation|react-native-bottom-tabs' jest.setup.js jest.config.js -C2Repository: RocketChat/Rocket.Chat.ReactNative
Length of output: 372
🏁 Script executed:
# Check BottomTabNavigator.tsx to see the imports and usage
cat -n app/stacks/BottomTabNavigator.tsxRepository: RocketChat/Rocket.Chat.ReactNative
Length of output: 14002
🏁 Script executed:
# Check current jest.setup.js
cat -n jest.setup.jsRepository: RocketChat/Rocket.Chat.ReactNative
Length of output: 7218
🏁 Script executed:
# Check current jest.config.js
cat -n jest.config.jsRepository: RocketChat/Rocket.Chat.ReactNative
Length of output: 924
🏁 Script executed:
# Search for imports of BottomTabNavigator in tests
rg -l 'BottomTabNavigator' --type ts --type tsx | head -20Repository: RocketChat/Rocket.Chat.ReactNative
Length of output: 105
🏁 Script executed:
# Check if react-native-bottom-tabs is actually imported anywhere
rg -r 'react-native-bottom-tabs' .Repository: RocketChat/Rocket.Chat.ReactNative
Length of output: 50394
🏁 Script executed:
# Search for test files that import BottomTabNavigator
fd -e test.ts -e test.tsx -e spec.ts -e spec.tsx | head -20 | xargs grep -l "BottomTabNavigator" 2>/dev/null || echo "No test files found importing BottomTabNavigator"Repository: RocketChat/Rocket.Chat.ReactNative
Length of output: 126
🏁 Script executed:
# Check package.json to see the actual dependencies
cat package.json | grep -A 50 '"dependencies"' | grep -E "(bottom-tabs|react-native-bottom)"Repository: RocketChat/Rocket.Chat.ReactNative
Length of output: 122
🏁 Script executed:
# Also check if react-native-bottom-tabs is listed in devDependencies or peerDependencies
cat package.json | grep -A 20 '"devDependencies"' | grep -E "(bottom-tabs|react-native-bottom)"Repository: RocketChat/Rocket.Chat.ReactNative
Length of output: 60
Add Jest mock for @bottom-tabs/react-navigation before merge.
@bottom-tabs/react-navigation is imported in app/stacks/BottomTabNavigator.tsx but lacks Jest mock/transform coverage. Tests importing from this file will fail.
🔧 Suggested updates (outside this file)
--- a/jest.setup.js
+++ b/jest.setup.js
@@
+jest.mock('@bottom-tabs/react-navigation', () => {
+ const React = require('react');
+ const MockNavigator = ({ children }) => React.createElement(React.Fragment, null, children);
+ const MockScreen = ({ children }) => React.createElement(React.Fragment, null, children);
+ return {
+ createNativeBottomTabNavigator: () => ({
+ Navigator: MockNavigator,
+ Screen: MockScreen
+ })
+ };
+});--- a/jest.config.js
+++ b/jest.config.js
@@
- 'node_modules/(?!((jest-)?react-native|@react-native(-community)?)|expo(nent)?|@expo(nent)?/.*|@expo-google-fonts/.*|react-navigation|@react-navigation/.*|@unimodules/.*|unimodules|sentry-expo|native-base|react-native-svg|@rocket.chat/ui-kit)'
+ 'node_modules/(?!((jest-)?react-native|@react-native(-community)?)|expo(nent)?|@expo(nent)?/.*|@expo-google-fonts/.*|react-navigation|@react-navigation/.*|@bottom-tabs/.*|@unimodules/.*|unimodules|sentry-expo|native-base|react-native-svg|@rocket.chat/ui-kit)'🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@package.json` at line 27, Tests fail because the package
"@bottom-tabs/react-navigation" used by app/stacks/BottomTabNavigator.tsx has no
Jest mock or transform; add a Jest manual mock or moduleNameMapper entry for
"@bottom-tabs/react-navigation" and/or include it in
jest.transformIgnorePatterns so imports resolve during tests. Specifically,
create a manual mock under __mocks__ for the module name or add mapping in
jest.config.js (moduleNameMapper) to point "@bottom-tabs/react-navigation" to a
simple mock module, or update transformIgnorePatterns to allow transforming this
package so BottomTabNavigator.tsx can import it in tests.
| "react-native-animatable": "1.3.3", | ||
| "react-native-background-timer": "2.4.1", | ||
| "react-native-bootsplash": "^6.3.8", | ||
| "react-native-bottom-tabs": "^1.1.0", |
There was a problem hiding this comment.
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
set -euo pipefail
echo "Declared dependency:"
node -e 'const p=require("./package.json"); console.log(p.dependencies["react-native-bottom-tabs"])'
echo
echo "Available patch files:"
fd -a 'react-native-bottom-tabs+*.patch' patches
echo
echo "Resolved versions in lockfile (if present):"
if [ -f yarn.lock ]; then
rg -n 'react-native-bottom-tabs@' yarn.lock -A2 -B1
fiRepository: RocketChat/Rocket.Chat.ReactNative
Length of output: 395
🏁 Script executed:
#!/bin/bash
set -euo pipefail
echo "=== Patches directory structure ==="
if [ -d patches ]; then
ls -la patches/ | head -20
echo
echo "Total patch files:"
fd -a '.patch$' patches | wc -l
echo
echo "All patch filenames:"
fd -a '.patch$' patches
else
echo "No patches directory found"
fi
echo
echo "=== Check for patch-package in package.json ==="
rg -A5 '"postinstall"' package.json
echo
echo "=== Check if `@bottom-tabs/react-navigation` exists ==="
node -e 'const p=require("./package.json"); console.log(p.dependencies["@bottom-tabs/react-navigation"] || "not found")'
echo
echo "=== Jest configuration check ==="
if [ -f jest.config.js ]; then
head -50 jest.config.js
fiRepository: RocketChat/Rocket.Chat.ReactNative
Length of output: 3513
🏁 Script executed:
#!/bin/bash
set -euo pipefail
echo "=== Checking `@bottom-tabs/react-navigation` in node_modules ==="
if [ -d node_modules/@bottom-tabs/react-navigation ]; then
cat node_modules/@bottom-tabs/react-navigation/package.json | jq '{name, main, module, types, exports}' 2>/dev/null || echo "Failed to parse package.json"
else
echo "@bottom-tabs/react-navigation not installed"
fi
echo
echo "=== Checking react-native-bottom-tabs in node_modules ==="
if [ -d node_modules/react-native-bottom-tabs ]; then
cat node_modules/react-native-bottom-tabs/package.json | jq '{name, main, module, types, exports}' 2>/dev/null || echo "Failed to parse package.json"
else
echo "react-native-bottom-tabs not installed"
fi
echo
echo "=== Check if these packages are used in code ==="
rg '@bottom-tabs/react-navigation|react-native-bottom-tabs' --type ts --type tsx -l src/ 2>/dev/null | head -5 || echo "No imports found in src/"
echo
echo "=== Verify lockfile has both dependencies ==="
rg -B1 -A1 '@bottom-tabs/react-navigation|react-native-bottom-tabs' yarn.lock | head -20Repository: RocketChat/Rocket.Chat.ReactNative
Length of output: 1021
🏁 Script executed:
#!/bin/bash
set -euo pipefail
echo "=== Git status of package.json changes ==="
git diff HEAD -- package.json | grep -A2 -B2 "react-native-bottom-tabs\|@bottom-tabs/react-navigation" || echo "No recent changes in git diff"
echo
echo "=== Check git log for these dependencies ==="
git log --oneline -20 -- package.json | head -10
echo
echo "=== Search for usage in entire codebase ==="
rg '@bottom-tabs/react-navigation|react-native-bottom-tabs' --type-list | head -5
rg '@bottom-tabs/react-navigation|react-native-bottom-tabs' --all-types 2>/dev/null | head -20 || echo "No matches found"
echo
echo "=== Check git status ==="
git status --short package.json patches/ 2>/dev/null | head -10Repository: RocketChat/Rocket.Chat.ReactNative
Length of output: 734
🏁 Script executed:
#!/bin/bash
set -euo pipefail
echo "=== Search for actual imports ==="
rg '@bottom-tabs/react-navigation|react-native-bottom-tabs' --no-type-add 2>/dev/null | head -20 || true
echo
echo "=== Check if patches are applied during install ==="
cat package.json | jq '.scripts.postinstall'
echo
echo "=== Look at the actual patch file content ==="
head -30 patches/react-native-bottom-tabs+1.1.0.patch
echo
echo "=== Check yarn.lock resolution behavior ==="
rg -A3 'react-native-bottom-tabs@' yarn.lock
</parameter>Repository: RocketChat/Rocket.Chat.ReactNative
Length of output: 1472
Pin react-native-bottom-tabs to an exact version while using patch-package.
Line 93 uses ^1.1.0, which allows yarn to resolve to 1.1.x versions, but the patch file is tied to the exact version +1.1.0 in patches/react-native-bottom-tabs+1.1.0.patch. If the lockfile is regenerated or a minor version bump occurs, the postinstall patch will fail to apply.
🔧 Proposed fix
- "react-native-bottom-tabs": "^1.1.0",
+ "react-native-bottom-tabs": "1.1.0",📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| "react-native-bottom-tabs": "^1.1.0", | |
| "react-native-bottom-tabs": "1.1.0", |
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@package.json` at line 93, The dependency "react-native-bottom-tabs" is
declared with a caret ("^1.1.0") which allows yarn to install 1.1.x and will
break patch-package since the patch file targets +1.1.0; update the package.json
dependency entry for "react-native-bottom-tabs" to the exact version "1.1.0"
(remove the caret) so installed version matches
patches/react-native-bottom-tabs+1.1.0.patch and then re-run install to
regenerate the lockfile.
Replace DrawerNavigator with native bottom tab navigator using react-native-bottom-tabs for phone layout. Adds 5 tabs: Chats, Discussions, DMs, Search (with iOS 26 expanding search role), and More.
Proposed changes
Issue(s)
How to test or reproduce
Screenshots
ezyZip.mp4
Types of changes
Checklist
Further comments