Skip to content

refactor: migrate InsideStack to React Navigation static config#7415

Open
diegolmello wants to merge 2 commits into
native-1304-master-detail-static-configfrom
native-1303-inside-stack-static-config
Open

refactor: migrate InsideStack to React Navigation static config#7415
diegolmello wants to merge 2 commits into
native-1304-master-detail-static-configfrom
native-1303-inside-stack-static-config

Conversation

@diegolmello

@diegolmello diegolmello commented Jun 19, 2026

Copy link
Copy Markdown
Member

Summary

  • Converts all 8 nested navigators in InsideStack (Drawer, Chats, Profile, Settings, AdminPanel, Accessibility, NewMessage, E2ESaveYourPassword, E2EEnterYourPassword) from the dynamic JSX API to React Navigation 7's static config API.
  • Class components and function components that take navigation as a prop are wrapped with withNavigation() so useNavigation() injects it at the registration site.
  • Dynamic navigator props (themedHeader, drawerContent, overlayColor) go in .with() callbacks; static screenOptions stay in the config object.
  • TNavigation screens (PickerView, ForwardLivechatView, AttachmentView) keep precise param-type annotations so StaticParamList preserves the original types.
  • Record<string, any> (not bare any) annotations on param-bearing screens so StaticParamList infers a concrete type rather than unknown/undefined.
  • types.ts keeps hand-written param lists for ChatsStackParamList and SettingsStackParamList to preserve cross-stack navigation entries (e.g. ModalStackNavigator for the tablet/MasterDetail modal stack) and specific route param types that view callback parameters rely on for type inference.
  • Default export via InsideStack.getComponent()AppContainer.tsx is untouched.

Stacks on #7414 (MasterDetailStack static config). Base should retarget to develop once #7414 merges.

Issues

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

How to test

  • Open the app on phone — navigation within all stacks (Chats, Profile, Settings, Admin, Accessibility, NewMessage, E2E flows) should work as before.
  • On tablet — Drawer navigation and MasterDetail layout should be unaffected.
  • E2E password save and enter flows should navigate correctly.
  • Attachment, Status, ShareView, ModalBlockView, CallView screens should open from InsideStack.

https://claude.ai/code/session_012YMKHjug3ZGEibZ1BAGcTx

Summary by CodeRabbit

  • Refactor
    • Modernized internal navigation architecture for improved code structure and maintainability.

Converts all 8 nested navigators in InsideStack (ChatsStack, ProfileStack,
SettingsStack, AdminPanelStack, AccessibilityStack, DrawerStack, NewMessageStack,
E2ESaveYourPasswordStack, E2EEnterYourPasswordStack, InsideStack) from the
dynamic JSX API to the static config API.

- Class components and function components taking navigation as a prop are
  wrapped with withNavigation() so useNavigation() provides the prop at the
  registration site.
- Dynamic navigator props (themedHeader, drawerContent, overlayColor) go in
  .with() callbacks; static screenOptions stay in the config object.
- TNavigation screens (PickerView, ForwardLivechatView, AttachmentView) keep
  precise param-type annotations so StaticParamList preserves the original types.
- Record<string,any> annotations (not `any`) are used for param-bearing screens
  so StaticParamList infers a concrete type rather than unknown/undefined.
- types.ts keeps hand-written param list types for ChatsStackParamList and
  SettingsStackParamList to preserve cross-stack navigation entries and specific
  route param types relied on by view callback parameter inference.
- Default export via InsideStack.getComponent() — AppContainer.tsx is untouched.

Claude-Session: https://claude.ai/code/session_012YMKHjug3ZGEibZ1BAGcTx

@diegolmello diegolmello left a comment

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

High-effort code review (NATIVE-1303). Screen-set integrity verified across all 10 navigators: 78 registrations old vs 78 new, exact match — nothing dropped, renamed, duplicated, or moved between navigators. Per-navigator screenOptions split (static defaultHeader + .with() themedHeader(theme)), navigationOptionsoptions conversions, @ts-ignore removals, withNavigation wrapping, and useAppNavigation/useAppRoute all check out. tsc clean, no snapshot churn.

Two items worth fixing before this stacks the root slice (both inline):

  1. Three header titles are frozen at app-launch language (static I18n.t() literals) — a real but low-severity regression vs the old function-form navigationOptions.
  2. Ten dead derived param-list exports collide by name with the canonical hand-written types and could leak weaker typing into the root slice.

Nits not blocking: double-as any on the SearchMessagesView (line 202) / ModalBlockView (line 402) options callbacks where the .navigationOptions! non-null form (used in MasterDetailStack) is cleaner.

On-device authenticated-flow smoke test remains the verification gate.

Comment thread app/stacks/InsideStack.tsx Outdated
RoomView: RoomViewScreen,
RoomActionsView: createNativeStackScreen({
screen: RoomActionsViewScreen,
options: { title: I18n.t('Actions') }

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

bug (low): this title is a static object literal, so I18n.t('Actions') is evaluated once at module load and frozen. The pre-migration code passed RoomActionsView.navigationOptions as a function, which React Navigation re-evaluates per render. A non-RTL language change dispatches appStart({ root: ROOT_INSIDE }) without restarting the bundle, so this header (and Read_Receipt at line 224, Screen_lock at line 273) keeps the previous language until a full restart while every other screen updates. MasterDetailStack keeps these as functions for this reason. Suggest options: props => RoomActionsView.navigationOptions!(props) (and the .navigationOptions form for the other two).

Comment thread app/stacks/InsideStack.tsx Outdated
AddExistingChannelView: AddExistingChannelViewScreen,
ReadReceiptsView: createNativeStackScreen({
screen: ReadReceiptsViewScreen,
options: { title: I18n.t('Read_Receipt') }

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same title-staleness as line 191 — I18n.t('Read_Receipt') frozen at module load. Use the function form: options: ReadReceiptsView.navigationOptions.

Comment thread app/stacks/InsideStack.tsx Outdated
LegalView: LegalViewScreen,
ScreenLockConfigView: createNativeStackScreen({
screen: ScreenLockConfigViewScreen,
options: { title: I18n.t('Screen_lock') }

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same title-staleness as line 191 — I18n.t('Screen_lock') frozen at module load. Use the function form: options: ScreenLockConfigView.navigationOptions.

Comment thread app/stacks/InsideStack.tsx Outdated
</InsideStack.Navigator>
);
};
export type ChatsStackParamList = StaticParamList<typeof ChatsStack>;

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

slop (moderate): these derived param-list exports are unused — every consumer imports ChatsStackParamList/SettingsStackParamList/etc. from app/stacks/types.ts, and only this file's default export is imported elsewhere. They also duplicate the canonical names with structurally weaker (Record<string,any>-based) types, so a future import from this module (e.g. the root slice) would silently lose the rich param typing. Suggest deleting this block; the per-screen StaticScreenProps<Record<string,any>> annotations above exist only to feed it and can be simplified to plain casts.

FIX 1 (localization regression): convert three static I18n.t() object
literals to function-form options so React Navigation re-evaluates per
render on language change — RoomActionsView, ReadReceiptsView, and
ScreenLockConfigView now delegate to their static navigationOptions.

FIX 2 (dead type exports): remove 10 derived StaticParamList exports
that collided with the hand-written types in types.ts; simplify the
Record<string,any> screen annotations to plain `as any` casts now that
derived exports are gone. Remove unused StaticParamList and I18n imports.

Claude-Session: https://claude.ai/code/session_01Qh9P8Bbp6V7PFpCY2X5on3
@diegolmello diegolmello temporarily deployed to approve_e2e_testing June 19, 2026 03:03 — with GitHub Actions Inactive
@coderabbitai

coderabbitai Bot commented Jun 19, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 6fe80ed4-3292-4fba-9fb2-be60676db05d

📥 Commits

Reviewing files that changed from the base of the PR and between df36d5f and 7cd80b9.

📒 Files selected for processing (2)
  • app/stacks/InsideStack.tsx
  • app/stacks/types.ts
📜 Recent review details
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
  • GitHub Check: ESLint and Test / run-eslint-and-test
🧰 Additional context used
📓 Path-based instructions (5)
**/*.{js,ts,jsx,tsx}

📄 CodeRabbit inference engine (AGENTS.md)

**/*.{js,ts,jsx,tsx}: Use descriptive names for functions, variables, and classes that clearly convey their purpose
Write comments that explain the 'why' behind code decisions, not the 'what'
Keep functions small and focused on a single responsibility
Use const by default, let when reassignment is needed, and avoid var
Prefer async/await over .then() chains for handling asynchronous operations
Use explicit error handling with try/catch blocks for async operations
Avoid deeply nested code; refactor complex logic into helper functions

Files:

  • app/stacks/types.ts
  • app/stacks/InsideStack.tsx
**/*.{ts,tsx}

📄 CodeRabbit inference engine (AGENTS.md)

**/*.{ts,tsx}: Use TypeScript for type safety; add explicit type annotations to function parameters and return types
Prefer interfaces over type aliases for defining object shapes in TypeScript
Use enums for sets of related constants rather than magic strings or numbers

Use TypeScript with strict mode enabled

Files:

  • app/stacks/types.ts
  • app/stacks/InsideStack.tsx
**/*.{js,jsx,ts,tsx,json}

📄 CodeRabbit inference engine (CLAUDE.md)

Use Prettier formatting with tabs, single quotes, 130 character line width, no trailing commas, and avoid arrow function parentheses

Files:

  • app/stacks/types.ts
  • app/stacks/InsideStack.tsx
**/*.{js,jsx,ts,tsx}

📄 CodeRabbit inference engine (CLAUDE.md)

Enforce ESLint rules from @rocket.chat/eslint-config with React, React Native, TypeScript, and Jest plugins

Files:

  • app/stacks/types.ts
  • app/stacks/InsideStack.tsx
app/stacks/**/*.{ts,tsx}

📄 CodeRabbit inference engine (CLAUDE.md)

Place navigation stacks in 'app/stacks/' directory (InsideStack for authenticated, OutsideStack for login/register, MasterDetailStack for tablets, ShareExtensionStack)

Files:

  • app/stacks/types.ts
  • app/stacks/InsideStack.tsx
🧠 Learnings (1)
📚 Learning: 2026-04-30T17:07:51.020Z
Learnt from: diegolmello
Repo: RocketChat/Rocket.Chat.ReactNative PR: 7274
File: app/lib/services/voip/MediaCallEvents.ts:0-0
Timestamp: 2026-04-30T17:07:51.020Z
Learning: In this Rocket.Chat React Native codebase, the ESLint rule `no-void: error` is enforced. When you see a promise returned from an async call that is not awaited (a “floating promise”), do not silence it with the `void somePromise()` pattern. Instead, handle the promise explicitly by attaching `.catch(...)` (or otherwise awaiting/handling the error) so unhandled-rejection risks are addressed in a way that satisfies the existing ESLint configuration.

Applied to files:

  • app/stacks/types.ts
  • app/stacks/InsideStack.tsx
🔇 Additional comments (2)
app/stacks/InsideStack.tsx (1)

1-16: LGTM!

Also applies to: 43-160, 161-217, 218-259, 260-312, 313-354, 355-395

app/stacks/types.ts (1)

22-25: LGTM!

Also applies to: 50-50, 142-142, 153-153, 208-210, 258-260, 304-304


Walkthrough

InsideStack.tsx is refactored from *StackNavigator wrapper function components to composition-based navigator definitions using createNativeStackNavigator/createDrawerNavigator with with(...) for themed headers and withNavigation(...) for screen registration. The module default export changes from a function component to InsideStack.getComponent(). types.ts receives explanatory comments and has inline TODO comments removed.

Changes

InsideStack Composition Refactor

Layer / File(s) Summary
Imports, withNavigation screen wrappers, and type annotations
app/stacks/InsideStack.tsx, app/stacks/types.ts
Adds ComponentType, NativeStackNavigationOptions, and withNavigation imports; imports Jitsi, discussion, profile, security, and accessibility screen modules; introduces withNavigation(...)-wrapped screen constants with explicit ComponentType<StaticScreenProps<...>> annotations. types.ts gains explanatory comments on hand-written param types and cross-stack settings navigation; inline TODO comments are removed from several param fields.
ChatsStack refactor with createNativeStackScreen
app/stacks/InsideStack.tsx
Replaces ChatsStackNavigator with a ChatsStack navigator; registers routes via wrapped screen constants; conditionally applies createNativeStackScreen options including Jitsi header/animation behavior based on isIOS.
ProfileStack, SettingsStack, AdminPanelStack, AccessibilityStack, and DrawerStack refactors
app/stacks/InsideStack.tsx
Refactors four *StackNavigator wrapper components into createNativeStackNavigator instances using with(({ Navigator }) => ...) + useContext(ThemeContext) for themed headers; refactors drawer into a DrawerStack with RTL-aware position, swipe disabled, and Sidebar as drawerContent.
Modal stacks, InsideStack top-level, and module export
app/stacks/InsideStack.tsx
Defines NewMessageStack, E2ESaveYourPasswordStack, and E2EEnterYourPasswordStack with themed headers; replaces InsideStackNavigator with InsideStack using presentation: 'containedModal' and wires all top-level routes; changes default export to InsideStack.getComponent().

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Suggested labels

type: chore

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The PR title accurately summarizes the primary change: migrating InsideStack from JSX dynamic API to React Navigation's static config API, which aligns with the substantial refactoring described in the file summary and PR objectives.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

Warning

Review ran into problems

🔥 Problems

Errors were encountered while retrieving linked issues.

Errors (1)
  • NATIVE-1303: Request failed with status code 401

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.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

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