Skip to content

Conversation

@divyanshu-patil
Copy link

@divyanshu-patil divyanshu-patil commented Jan 8, 2026

Proposed changes

This pull request introduces Quick Actions support to the app, including full support when the app is launched from a killed state on both iOS and Android.

It adds the complete Quick Actions flow end-to-end, enabling users to quickly access common actions such as Search, Add Server, Recent Rooms, and Contact us directly from the app icon. Platform-specific handling is implemented to ensure reliable behavior during cold starts.

Issue(s)

closes: #1705
closes: #6227

Approach for Killed state

iOS

  • Quick Action Handling: When launched via a Quick Action from a killed state, the selected action is stored on the native side.
  • App Initialisation: During app initialisation, the JavaScript layer checks for any stored quick action and processes it once the app is ready.

Android

  • Android Quick Action Handling: Android uses Intents and deep linking to handle Quick Actions, unlike iOS.
  • Intent Retrieval: The app retrieves the intent containing the Quick Action information using deep linking.
  • Lifecycle Differences: iOS and Android have different lifecycle approaches, making the iOS method unsuitable for Android.

How to test or reproduce

Screenshots

Quick Actions

Android iOS
image image

Demo

Android

android.quickaction.mp4

iOS

ios.quickactions.mp4

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

the quick actions icons on android are temporary and should be changed later

Summary by CodeRabbit

  • New Features

    • Home-screen quick actions on iOS and Android with registration, updates and runtime handling; selections route into app navigation.
  • State & Persistence

    • Tracks, persists and restores last-visited room and recent rooms; recent list trimmed and restored on startup.
  • Localization

    • Added English and Brazilian Portuguese strings for quick-action UI and error messages.
  • UI

    • New quick-action icons added.
  • Tests

    • Unit tests for quick-actions reducer behavior.

✏️ Tip: You can customize this high-level summary in your review settings.

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Jan 8, 2026

Walkthrough

Adds cross-platform quick actions: native handlers and RN bridge (iOS/Android), app-side registration/updating of shortcuts, Redux actions/reducer and sagas to process quick actions, tracking/persisting recent/last-visited rooms, startup wiring, tests, icons, and localization.

Changes

Cohort / File(s) Summary
Android Native
android/app/src/main/java/chat/rocket/reactnative/MainActivity.kt, android/app/src/main/res/drawable/ic_quickaction_contact.xml, android/app/src/main/res/drawable/ic_quickaction_recent.xml
Embed quick-action intents into rocketchat://quick-action/{id} on create/new intent; add two vector icons. Review intent normalization, platform/version handling (PersistableBundle), and logging.
iOS Native
ios/AppDelegate.swift, ios/QuickActionsConnector.h, ios/QuickActionsConnector.mm, ios/QuickActionsStore.h, ios/QuickActionsStore.mm, ios/RocketChatRN-Bridging-Header.h, ios/Podfile, ios/RocketChatRN.xcodeproj/...
Capture/queue UIApplicationShortcutItem at launch/runtime, thread-safe pending store, RN bridge to consume initial quick action, add Expo Quick Actions pod and project references. Inspect thread-safety, bridging, and project config diffs.
Quick Actions Library
app/lib/quickActions/index.ts, app/lib/quickActions/getRecentQuickAction.ts
New module to register/unregister/update quick actions, build items from recent rooms + contact, listen for selections, and read initial iOS quick action. Check platform-specific icons, AppState handling, and dispatch timing.
App Integration / Startup
app/index.tsx, app/AppContainer.tsx, app/views/SettingsView/index.tsx
Detect and dispatch quick-action deep links during startup/runtime; register/unregister lifecycle listeners; extract sendEmail helper. Verify startup ordering and early-exit behavior for quick actions.
Redux: Types / Actions / Reducer
app/actions/actionsTypes.ts, app/actions/quickActions.ts, app/reducers/quickActions.ts, app/reducers/index.js
Add QUICK_ACTIONS types and action creator; new quickActions reducer (lastAction, handled); include reducer in root. Check reducer tests and action payload validation.
Rooms: State & Persistence
app/actions/rooms.ts, app/reducers/rooms.ts, app/sagas/init.js, app/lib/constants/keys.ts
Add lastVisited/recentRooms actions/state; persist/load LAST_VISITED_* and RECENT_VISITED_ROOMS keys on init. Review persistence format, JSON parsing, and server scoping.
Sagas / Orchestration
app/sagas/quickActions.ts, app/sagas/roomVisited.ts, app/sagas/index.js
New sagas to handle quick actions (contact, recent, add-server, search), wait for app/navigation readiness, navigate to rooms, and persist recent/last-visited rooms. Pay attention to timeouts, navigation readiness waits, and error handling.
Emitter & Navigation Hooks
app/lib/methods/helpers/emitter.ts, app/lib/methods/goRoom.ts, app/lib/methods/getServerList.ts, app/lib/methods/logout.ts
Add roomVisited emitter event; emit on DM creation and navigation; new getServersList; clear last-visited on logout. Verify emitter contract and avoid cycles.
Store / Types / Tests
app/lib/store/index.ts, app/reducers/quickactions.test.ts, app/reducers/rooms.test.ts
Export AppDispatch type; add reducer tests and last-visited persistence test. Check test coverage and mocks.
Localization
app/i18n/locales/en.json, app/i18n/locales/pt-BR.json
Add strings: Error_finding_room, Last_visited_room, Recent_Rooms, Something_Wrong?, We_are_here_to_help.
Misc / Build
package.json, app/sagas/rooms.js, app/views/RoomsListView/hooks/useSearch.ts
Add expo-quick-actions dependency; minor stylistic/whitespace tweaks; small project file changes in iOS. Inspect dependency bump and project-level pbxproj adjustments.

Sequence Diagram(s)

sequenceDiagram
    participant User
    participant Native as Native Layer (iOS / Android)
    participant App as JS App (index.tsx / AppContainer)
    participant Store as Redux Store
    participant Saga as QuickActions Saga
    participant Nav as Navigation

    User->>Native: Trigger quick action (shortcut / intent)
    Native->>Native: normalize / set pending quick action
    Native->>App: launch or deliver intent (intent.data or initial quick action)
    App->>Store: dispatch QUICK_ACTIONS.QUICK_ACTION_HANDLE { action }
    Store->>Saga: watcher receives QUICK_ACTIONS.QUICK_ACTION_HANDLE
    alt action == recent
        Saga->>Store: select server, ensure DB/navigation ready
        Saga->>Nav: navigate to room (rid)
        Nav->>App: room opened -> emitter emits roomVisited {rid,name,server}
    else action == contact
        Saga->>Native: open mailto URL
    else action == add-server or search
        Saga->>Nav: navigate to respective screen
    end
    Saga->>Store: dispatch QUICK_ACTIONS.QUICK_ACTION_HANDLED
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Possibly related PRs

Suggested reviewers

  • diegolmello
  • OtavioStasiak

Poem

🐰 I hopped through native doors,
Shortcuts sewn into app floors,
A dispatch, a saga, a gentle steer,
Recent rooms spring quickly near,
Rabbit cheers — quick actions here!

🚥 Pre-merge checks | ✅ 4 | ❌ 1
❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title 'feat: quick actions' directly corresponds to the primary objective of this PR—implementing quick actions support for iOS and Android.
Linked Issues check ✅ Passed The implementation addresses all coding requirements from issues #1705 and #6227: cross-platform quick actions on iOS and Android, recent rooms display, and contact/feedback action.
Out of Scope Changes check ✅ Passed All changes are scoped to quick actions feature: native iOS/Android handlers, Redux state management, quick action UIs, localization, and utility functions for recent rooms tracking.
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.

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

✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment

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.

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Jan 11, 2026

✅ Actions performed

Review triggered.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 18

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (3)
app/views/RoomsListView/hooks/useSearch.ts (1)

62-83: "use memo" directive is valid; however, wrap the debounced callback in useCallback to prevent unnecessary effect re-runs.

The 'use memo' directive is correct React Compiler syntax and correctly placed at line 62. However, the debounced search callback (lines 77–83) is recreated every render because it's not memoized. Since search is listed as a dependency in the effect at line 104, the effect runs every render, even when state.searchEnabled hasn't changed. Wrap the callback in useCallback with [state.searchEnabled] as dependencies:

Suggested fix
const search = useDebounce(
	useCallback(async (text: string) => {
		if (!state.searchEnabled) return;
		dispatch({ type: 'SET_SEARCHING' });
		const result = await searchLib({ text });
		dispatch({ type: 'SEARCH_SUCCESS', payload: result as IRoomItem[] });
		announceSearchResultsForAccessibility(result.length);
	}, [state.searchEnabled, announceSearchResultsForAccessibility]),
	500
);
app/AppContainer.tsx (1)

40-57: Guard against empty-string room name producing a blank quick-action title.
If lastVisitedRoomName can ever be '', recentRoomName ?? ... will keep it and show an empty title.

Proposed tweak
 useEffect(() => {
-	updateQuickActions({ recentRoomName: lastVisitedRoomName });
+	updateQuickActions({ recentRoomName: lastVisitedRoomName || undefined });
 }, [lastVisitedRoomName]);
ios/RocketChatRN.xcodeproj/project.pbxproj (1)

1284-1298: Remove miswired build phases from RocketChatRN target.

The RocketChatRN target contains two [Expo] Configure project phases (IDs: 589729E8381BA997CD19EF19 and 9C104B12BEE385F7555E641F). The second one (line 1295) is miswired—it executes the NotificationService-specific expo-configure script and should not be in this target. Additionally, the unnamed ShellScript at line 1289 (ID: 407D3EDE3DABEE15D27BD87D) outputs to a NotificationService-specific manifest file and appears misplaced. Move these phases to their appropriate targets.

🤖 Fix all issues with AI agents
In @android/app/src/main/java/chat/rocket/reactnative/MainActivity.kt:
- Around line 34-49: onCreate currently ends up invoking
NotificationIntentHandler.handleIntent twice on cold start because
handleVideoConfIntent already delegates to
NotificationIntentHandler.handleIntent and onCreate also calls
NotificationIntentHandler.handleIntent directly; remove the redundant direct
call in onCreate (the intent?.let { NotificationIntentHandler.handleIntent(this,
it) } line) so notification intents are only processed once, ensure
handleVideoConfIntent still calls NotificationIntentHandler.handleIntent for
video-conf flows, and add equivalent logic to onNewIntent to call
NotificationIntentHandler.handleIntent(this, intent) for warm starts so all
intent handling flows go through a single place.
- Around line 58-97: Remove or gate the sensitive debug logs in
MainActivity.handleVideoConfIntent so identifiers (rid, callerName, callId,
host) are not printed in production: wrap the Log.d(...) calls that output these
fields in a debug-only check (e.g., if (BuildConfig.DEBUG) { ... }) or remove
them entirely, and apply the same change for any other Log.d/Log.* lines in the
same method range (the other occurrence referenced in lines ~104-170) so
VideoConfModule.storePendingAction still receives full data but no sensitive
identifiers are emitted to production logs.

In @android/app/src/main/res/drawable/ic_quickaction_add.xml:
- Around line 1-5: Remove the redundant android:tint attribute from the root
<vector> element in ic_quickaction_add.xml so the icon matches the other
quick-action drawables; keep the existing path fillColor="@android:color/black"
unchanged and ensure only the android:tint attribute is deleted from the
<vector> tag.

In @app/index.tsx:
- Around line 64-77: In parseDeepLinking, when handling quick-action URLs (the
url.startsWith('rocketchat://quick-action/') branch), avoid using replace(...)
directly; instead extract the raw action by removing the
'rocketchat://quick-action/' prefix, strip any querystring or fragment by
splitting on /[?#]/ and taking the first segment, then URI-decode it with
decodeURIComponent (wrap in try/catch to fall back to the raw value on decode
errors) before returning { type: 'quick-action', action } so action contains the
clean, decoded value.

In @app/lib/methods/helpers/goRoom.ts:
- Around line 141-147: The new getRoom(item.rid) call in goRoom can throw and
will abort navigation; wrap the getRoom and emitter.emit('roomVisited', ...)
sequence in a try-catch so failures are swallowed (or logged) and do not reject
goRoom. Specifically, in goRoom around the getRoom/item.rid section, catch
errors from getRoom, optionally log via the existing logger/emitter, and only
emit roomVisited when getRoom succeeds so navigation continues even if room
lookup fails.

In @app/lib/quickActions/index.ts:
- Around line 1-3: The fix: make updateQuickActions async and await
QuickActions.setItems(...) so the returned Promise<void> is handled (change
function signature of updateQuickActions and await the setItems call inside it);
for addListener(), capture the returned Subscription object (const sub =
QuickActions.addListener(...)) and ensure you call sub.remove() during cleanup
(e.g., when replacing/tearing down listeners or component unmount) to avoid
leaks and duplicate registrations. Ensure references use the exact symbols
updateQuickActions, QuickActions.setItems, QuickActions.addListener, and
Subscription.remove.

In @app/sagas/init.js:
- Line 5: Rename the constant LAST_VISITED_ROOM_Name_KEY to
LAST_VISITED_ROOM_NAME_KEY in the constants definition, update its export, and
change all imports/usages that reference LAST_VISITED_ROOM_Name_KEY (e.g., in
the sagas that reference the last visited room key) to use
LAST_VISITED_ROOM_NAME_KEY instead; ensure import statements and any
destructuring that referenced the old name are updated so the symbol name is
consistent and all references compile.

In @app/sagas/quickActions.ts:
- Around line 82-104: In the 'recent' case of the quickActions saga, don't exit
the saga early with "if (!rid) return;" because that bypasses the final yield
put({ type: QUICK_ACTIONS.QUICK_ACTION_HANDLED });; instead change the
early-return to a switch-case-safe exit (e.g., "if (!rid) { break; }") or wrap
the rest of the case logic in "if (rid) { ... }" so the case completes normally
and the QUICK_ACTION_HANDLED dispatch always runs; update the code around the
rid check in the 'recent' case that uses waitForAppReady/select (variable rid)
to ensure control flows to the end of the saga.
- Around line 61-78: The switch case for 'search' in quickActions uses a lexical
declaration (const currentRoute) without block scoping; wrap the entire 'search'
case body in braces to scope declarations properly (i.e., change case 'search':
... to case 'search': { ... } ), keeping the existing calls to
waitForAppReady(), Navigation.getCurrentRoute(),
Navigation.navigate('RoomsListView') and the yield put({ type: UI.TRIGGER_SEARCH
}) intact so the behavior of quickAction handling remains unchanged.
- Around line 36-44: waitForRoomInDB can hang because take(APP.START) waits
indefinitely for the next APP.START; replace the current try/catch with a
bounded retry loop that calls getRoom up to a fixed maxAttempts and uses a short
delay/race to avoid deadlock: inside waitForRoomInDB attempt yield call(getRoom,
rid) and return on success; on failure yield a race between take(APP.START) and
call(delay, retryDelayMs) (or just call(delay, ms)) then loop until maxAttempts
exhausted; after maxAttempts either throw/return null to fail fast. Keep
references to waitForRoomInDB, getRoom and APP.START so the retry logic replaces
the current try/catch/return flow.

In @app/sagas/roomVisited.ts:
- Line 8: The constant name uses mixed case; rename LAST_VISITED_ROOM_Name_KEY
to LAST_VISITED_ROOM_NAME_KEY in the constant definition
(app/lib/constants/keys.ts) and update all imports/usages (e.g., the import in
app/sagas/roomVisited.ts and any other files) to the new identifier; ensure you
update export(s) and run type-check/grep to catch and fix all references so
builds pass.

In @app/views/SettingsView/index.tsx:
- Around line 36-50: Replace encodeURI with encodeURIComponent for the mailto
query parameters and use the helper values directly: change subject =
encodeURIComponent('Rocket.Chat Mobile App Support') and description =
encodeURIComponent(`version: ${getReadableVersion}\ndevice: ${getDeviceModel}`)
(do not call getReadableVersion/getDeviceModel as functions); keep the
Linking.openURL call using the template string
`mailto:${email}?subject=${subject}&body=${description}` and preserve the
try/catch and logging in sendEmail.

In @ios/QuickActionsConnector.mm:
- Around line 8-16: The getInitialQuickAction method reads and clears the global
RCPendingQuickActionType without synchronization, causing a race; fix by
serializing access to that variable (e.g., create a dedicated serial
dispatch_queue or guard with @synchronized on a shared object) and perform the
read-and-clear on that queue or within the lock so getInitialQuickAction
atomically captures the value and sets RCPendingQuickActionType = nil; update
any other writers/readers of RCPendingQuickActionType to use the same
synchronization primitive.

In @ios/QuickActionsStore.h:
- Around line 1-5: The header QuickActionsStore.h is missing include guards; add
a guard to prevent multiple inclusion (use either #pragma once at the top or
traditional #ifndef/#define/#endif) so the declarations for
RCPendingQuickActionType and RCSetPendingQuickActionType are only processed once
when included via RocketChatRN-Bridging-Header.h; place the guard around the
existing FOUNDATION_EXPORT declarations to enclose the whole header content.

In @ios/RocketChatRN.xcodeproj/project.pbxproj:
- Around line 1567-1583: Remove the entire PBXShellScriptBuildPhase entry with
identifier 1E1EA8082326CCE300E22452 from the project file and delete any
references to that ID from target buildPhases arrays so the shell script is
fully removed; ensure you also remove its block from PBXBuildPhase lists (and
any duplicate occurrences) to avoid dangling references that would break the
project.

In @package.json:
- Line 73: The dependency version for expo-quick-actions in package.json is
incompatible with Expo SDK 53; change the version string for
"expo-quick-actions" from "^6.0.0" to "5.0.0" (or another 5.x release) so it
targets SDK 53, then run your package manager install to update lockfile and
ensure tests/build succeed.
🧹 Nitpick comments (16)
android/app/src/main/res/drawable/ic_quickaction_contact.xml (1)

1-5: Consider standardizing viewport dimensions.

The vector drawable is correctly formatted. However, note that ic_quickaction_contact.xml uses a 24x24 viewport while ic_quickaction_recent.xml uses 512x512. While both will render correctly, standardizing viewports across related icon resources can improve maintainability.

♻️ Optional: standardize to 512x512 viewport

If you want consistency with the recent icon, you could rescale the path data to a 512x512 viewport. However, this is purely cosmetic and not required for functionality.

app/actions/actionsTypes.ts (1)

15-15: New action namespaces look consistent; consider tightening createRequestTypes typing.

Lines 15 and 63-65 align with the new quick-actions/UI flows. Small TS maintainability improvement: createRequestTypes(base = {}, ...) should likely take base: string (default '') to avoid accidental ${base} becoming [object Object].

Also applies to: 63-65

app/actions/quickActions.ts (1)

5-15: Make params optional (and avoid params.action ambiguity).
Right now IQuickAction.params is required (Line 9) and quickActionHandle requires an argument (Line 11), which makes “no-param” dispatch awkward, and params.action is easy to confuse with Redux “action”.

Proposed diff
 interface IQuickActionParams {
-	action: string;
+	action: string;
 }
 interface IQuickAction extends Action {
-	params: Partial<IQuickActionParams>;
+	params?: Partial<IQuickActionParams>;
 }
-export function quickActionHandle(params: Partial<IQuickActionParams>): IQuickAction {
+export function quickActionHandle(params: Partial<IQuickActionParams> = {}): IQuickAction {
 	return {
 		type: QUICK_ACTIONS.QUICK_ACTION_HANDLE,
 		params
 	};
 }
app/views/RoomsListView/hooks/useSearch.ts (1)

65-104: Type your Redux dispatch and avoid clearing triggers with untyped objects.
Since AppDispatch now exists, prefer useDispatch<AppDispatch>() so these dispatches are type-checked (Line 66, Line 96).

Proposed diff
-import { useDispatch } from 'react-redux';
+import { useDispatch } from 'react-redux';
+import type { AppDispatch } from '../../../lib/store';

 ...
-	const storeDispatch = useDispatch();
+	const storeDispatch = useDispatch<AppDispatch>();
app/lib/store/index.ts (1)

29-33: Good addition; watch out for store name collisions with auxStore.ts.
AppDispatch is a solid typing hook for useDispatch, but it’s easy to accidentally import the wrong store given app/lib/store/auxStore.ts also exports store.

app/reducers/rooms.ts (1)

9-10: Inconsistent naming: mix of abbreviation and full word.

The state fields use inconsistent naming styles:

  • lastVisitedRid — abbreviation ("Rid" for room ID)
  • lastVisitedName — full word ("Name")

The action payload uses full words (lastVisitedRoomId, lastVisitedRoomName), which is clearer. Consider renaming the state fields to match for consistency:

-	lastVisitedRid: string;
-	lastVisitedName: string;
+	lastVisitedRoomId: string;
+	lastVisitedRoomName: string;

You'll also need to update the initial state (lines 18-19) and the reducer case (lines 54-55) accordingly.

app/AppContainer.tsx (1)

4-25: Consider avoiding connect(...) + hooks in the same component.
Not a blocker, but it makes the data-flow harder to follow (props from HOC + hooks from store). Either approach alone would be simpler.

app/i18n/locales/en.json (1)

331-331: Same i18n key hygiene note (Something_Wrong?) + optional copy polish.
If you keep the \n in Error_finding_room, consider sentence punctuation for UI consistency (non-blocking).

Also applies to: 457-457, 698-699, 846-846, 984-984

app/index.tsx (1)

166-184: Replace non-null assertion and add quick-action ID validation.

The deepLinking! non-null assertion is unsafe since Linking.getInitialURL() can return null or undefined. Additionally, validation here prevents accidental dispatches of unknown quick-action IDs. The handler gracefully ignores them today, but explicit validation at the entry point is clearer and prevents unnecessary Redux actions.

Replace the non-null assertion:

const parsedDeepLinkingURL = deepLinking ? parseDeepLinking(deepLinking) : null;

And add ID validation before dispatch in the quick-action handler to match the known cases defined in app/lib/quickActions/index.ts ('search', 'add-server', 'recent', 'contact').

app/lib/quickActions/getRecentQuickAction.ts (1)

14-19: Consider simplifying the error handling return.

The try-catch appropriately returns null on any error. The ?? null on line 16 is defensive but potentially redundant, since getInitialQuickAction should return a string or null/undefined. If the native contract guarantees this, you can simplify to return action || null.

♻️ Optional simplification
 	try {
 		const action = await QuickActionsConnector.getInitialQuickAction();
-		return action ?? null;
+		return action || null;
 	} catch {
 		return null;
 	}
app/reducers/ui.ts (1)

13-28: Strengthen type safety with a discriminated union for UI actions.

The reducer uses a generic Action type, preventing TypeScript from verifying that UI.TRIGGER_SEARCH and UI.CLEAR_TRIGGERED_SEARCH are valid action types. Define a union of specific UI action types for better compile-time safety.

♻️ Proposed type-safe action union
+type UIAction = 
+	| { type: typeof UI.TRIGGER_SEARCH }
+	| { type: typeof UI.CLEAR_TRIGGERED_SEARCH };
+
-export default function ui(state = initialState, action: Action): IUIState {
+export default function ui(state = initialState, action: UIAction | Action): IUIState {
 	switch (action.type) {
 		case UI.TRIGGER_SEARCH:
 			return {
 				...state,
 				triggerSearch: true
 			};
 		case UI.CLEAR_TRIGGERED_SEARCH:
 			return {
 				...state,
 				triggerSearch: false
 			};
 		default:
 			return state;
 	}
 }

This approach allows UI actions to be type-checked while still accepting generic actions for Redux compatibility.

app/sagas/roomVisited.ts (1)

21-31: Add error handling for UserPreferences persistence.

The UserPreferences.setString calls (lines 28-29) lack error handling. If persistence fails, the saga will crash and stop processing future room visitation events. Consider wrapping these calls in a try-catch to ensure the saga remains resilient.

♻️ Proposed error handling
 function* watchRoomVisited(): SagaIterator {
 	const channel = yield call(createRoomVisitedChannel);
 
 	while (true) {
 		const { rid, name } = yield take(channel);
 		yield put(roomsStoreLastVisited(rid, name));
 
-		UserPreferences.setString(LAST_VISITED_ROOM_ID_KEY, rid);
-		UserPreferences.setString(LAST_VISITED_ROOM_Name_KEY, name);
+		try {
+			UserPreferences.setString(LAST_VISITED_ROOM_ID_KEY, rid);
+			UserPreferences.setString(LAST_VISITED_ROOM_Name_KEY, name);
+		} catch (error) {
+			// Log but don't crash the saga
+			console.warn('Failed to persist last visited room:', error);
+		}
 	}
 }
app/lib/methods/helpers/goRoom.ts (2)

115-116: Silent catch block may hide unexpected errors.

The empty catch block with "do nothing" comment intentionally swallows all errors from the room visitation tracking. While this prevents blocking navigation, it also hides potential bugs (e.g., emitter failures, getRoom issues). Consider logging the error for debugging.

📋 Optional logging addition
 				} catch {
-					// do nothing
+					// Ignore errors in room visit tracking to avoid blocking navigation
 				}

Or add minimal logging:

-				} catch {
-					// do nothing
+				} catch (error) {
+					// Log but don't block navigation
+					console.debug('Failed to track room visit:', error);
 				}

141-149: Sequential database queries may impact performance.

Lines 141 and 149 make two separate database calls (getRoom and getSubscriptionByRoomId) for the same item.rid. If these queries are frequently executed, consider optimizing by fetching both in parallel or combining into a single query.

Example parallel fetch:

const [room, sub] = await Promise.all([
  getRoom(item.rid).catch(() => null),
  getSubscriptionByRoomId(item.rid)
]);

if (room) {
  emitter.emit('roomVisited', {
    rid: room.rid,
    name: room.name
  });
}

if (sub) {
  _item = sub;
}

This reduces the total latency when both queries are independent.

app/reducers/quickActions.ts (1)

25-33: Consider refining the type narrowing.

The type guard on line 26 correctly checks for the payload, but line 31 still uses a type assertion. Since QuickActionsAction is a union including the bare Action type, TypeScript cannot automatically narrow the type after the guard. While the current implementation is functionally correct, you could improve type safety by using a more explicit type predicate or restructuring the action types to avoid the manual cast.

♻️ Alternative approach using type predicate
+function isQuickActionPayload(action: QuickActionsAction): action is IQuickActionPayloadAction {
+	return 'payload' in action && !!action.payload?.action;
+}
+
 export default function quickActions(state = initialState, action: QuickActionsAction): IQuickActionsState {
 	switch (action.type) {
 		case QUICK_ACTIONS.QUICK_ACTION_HANDLE:
-			if (!('payload' in action) || !action.payload?.action) {
+			if (!isQuickActionPayload(action)) {
 				return state;
 			}
 			return {
 				...state,
-				lastAction: (action as IQuickActionPayloadAction).payload.action,
+				lastAction: action.payload.action,
 				handled: false
 			};
ios/AppDelegate.swift (1)

7-7: Optional: Remove redundant Foundation import.

The Foundation import is typically unnecessary in iOS applications as it's already available through UIKit and other frameworks imported in this file. While not harmful, removing it can reduce clutter.

📜 Review details

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Disabled knowledge base sources:

  • Jira integration is disabled by default for public repositories

You can enable these sources in your CodeRabbit configuration.

📥 Commits

Reviewing files that changed from the base of the PR and between ede2569 and 79dbb31.

⛔ Files ignored due to path filters (2)
  • ios/Podfile.lock is excluded by !**/*.lock
  • yarn.lock is excluded by !**/yarn.lock, !**/*.lock
📒 Files selected for processing (41)
  • android/app/src/main/java/chat/rocket/reactnative/MainActivity.kt
  • android/app/src/main/res/drawable/ic_quickaction_add.xml
  • android/app/src/main/res/drawable/ic_quickaction_contact.xml
  • android/app/src/main/res/drawable/ic_quickaction_find.xml
  • android/app/src/main/res/drawable/ic_quickaction_recent.xml
  • app/AppContainer.tsx
  • app/actions/actionsTypes.ts
  • app/actions/quickActions.ts
  • app/actions/rooms.ts
  • app/definitions/redux/index.ts
  • app/i18n/locales/en.json
  • app/i18n/locales/pt-BR.json
  • app/index.tsx
  • app/lib/constants/keys.ts
  • app/lib/methods/helpers/emitter.ts
  • app/lib/methods/helpers/goRoom.ts
  • app/lib/navigation/addServer/index.ts
  • app/lib/quickActions/getRecentQuickAction.ts
  • app/lib/quickActions/index.ts
  • app/lib/store/index.ts
  • app/reducers/index.js
  • app/reducers/quickActions.ts
  • app/reducers/rooms.ts
  • app/reducers/ui.ts
  • app/sagas/index.js
  • app/sagas/init.js
  • app/sagas/quickActions.ts
  • app/sagas/roomVisited.ts
  • app/sagas/rooms.js
  • app/views/RoomsListView/hooks/useSearch.ts
  • app/views/SettingsView/index.tsx
  • ios/AppDelegate.swift
  • ios/Podfile
  • ios/QuickActionsConnector.h
  • ios/QuickActionsConnector.mm
  • ios/QuickActionsStore.h
  • ios/QuickActionsStore.mm
  • ios/RocketChatRN-Bridging-Header.h
  • ios/RocketChatRN.xcodeproj/project.pbxproj
  • ios/RocketChatRN/Info.plist
  • package.json
🧰 Additional context used
🧬 Code graph analysis (18)
app/lib/store/index.ts (1)
app/lib/store/auxStore.ts (1)
  • store (6-6)
app/definitions/redux/index.ts (1)
app/reducers/ui.ts (1)
  • IUIState (5-7)
app/lib/quickActions/index.ts (3)
app/lib/quickActions/getRecentQuickAction.ts (1)
  • getRecentQuickAction (9-20)
app/lib/store/auxStore.ts (1)
  • store (6-6)
app/actions/quickActions.ts (1)
  • quickActionHandle (11-16)
app/sagas/roomVisited.ts (4)
app/lib/methods/helpers/events.ts (1)
  • emit (49-59)
app/lib/methods/helpers/emitter.ts (1)
  • emitter (28-28)
app/actions/rooms.ts (1)
  • roomsStoreLastVisited (54-60)
app/lib/constants/keys.ts (2)
  • LAST_VISITED_ROOM_ID_KEY (28-28)
  • LAST_VISITED_ROOM_Name_KEY (29-29)
app/actions/rooms.ts (2)
app/sagas/deepLinking.js (1)
  • name (62-62)
app/actions/actionsTypes.ts (1)
  • ROOMS (15-15)
app/lib/methods/helpers/goRoom.ts (2)
app/lib/methods/getRoom.ts (1)
  • getRoom (4-12)
app/lib/methods/helpers/emitter.ts (1)
  • emitter (28-28)
app/actions/quickActions.ts (1)
app/actions/actionsTypes.ts (1)
  • QUICK_ACTIONS (63-63)
app/reducers/ui.ts (2)
app/reducers/quickActions.ts (1)
  • initialState (10-13)
app/actions/actionsTypes.ts (1)
  • UI (65-65)
app/lib/navigation/addServer/index.ts (4)
app/lib/store/index.ts (1)
  • AppDispatch (30-30)
app/lib/methods/helpers/log/index.ts (2)
  • logEvent (35-42)
  • events (7-7)
app/actions/app.ts (1)
  • appStart (31-37)
app/actions/server.ts (1)
  • serverInitAdd (104-109)
app/AppContainer.tsx (5)
app/sagas/init.js (1)
  • lastVisitedRoomName (56-56)
app/lib/hooks/useAppSelector.ts (1)
  • useAppSelector (6-6)
app/definitions/redux/index.ts (1)
  • IApplicationState (53-81)
app/lib/quickActions/index.ts (2)
  • registerQuickActions (55-69)
  • updateQuickActions (22-53)
app/actions/actionsTypes.ts (1)
  • NAVIGATION (64-64)
app/reducers/rooms.ts (1)
app/actions/actionsTypes.ts (1)
  • ROOMS (15-15)
app/reducers/quickActions.ts (2)
app/reducers/ui.ts (1)
  • initialState (9-11)
app/actions/actionsTypes.ts (1)
  • QUICK_ACTIONS (63-63)
app/views/SettingsView/index.tsx (2)
app/lib/methods/helpers/log/index.ts (1)
  • logEvent (35-42)
app/lib/methods/helpers/info.ts (1)
  • showErrorAlert (5-6)
app/index.tsx (3)
app/lib/quickActions/getRecentQuickAction.ts (1)
  • getRecentQuickAction (9-20)
app/lib/store/auxStore.ts (1)
  • store (6-6)
app/actions/quickActions.ts (1)
  • quickActionHandle (11-16)
app/sagas/init.js (2)
app/lib/constants/keys.ts (2)
  • LAST_VISITED_ROOM_ID_KEY (28-28)
  • LAST_VISITED_ROOM_Name_KEY (29-29)
app/actions/rooms.ts (1)
  • roomsStoreLastVisited (54-60)
app/sagas/quickActions.ts (9)
app/actions/app.ts (2)
  • appInit (45-49)
  • appStart (31-37)
app/actions/actionsTypes.ts (4)
  • APP (35-43)
  • NAVIGATION (64-64)
  • UI (65-65)
  • QUICK_ACTIONS (63-63)
app/lib/methods/getRoom.ts (1)
  • getRoom (4-12)
app/sagas/init.js (2)
  • server (27-27)
  • root (83-87)
app/lib/constants/keys.ts (1)
  • CURRENT_SERVER (26-26)
app/actions/server.ts (1)
  • serverInitAdd (104-109)
app/views/SettingsView/index.tsx (1)
  • sendEmail (36-50)
app/actions/rooms.ts (1)
  • roomsStoreLastVisited (54-60)
app/lib/methods/helpers/goRoom.ts (1)
  • goRoom (87-156)
ios/QuickActionsStore.h (1)
app/sagas/deepLinking.js (1)
  • type (61-61)
app/views/RoomsListView/hooks/useSearch.ts (3)
app/lib/hooks/useAppSelector.ts (1)
  • useAppSelector (6-6)
app/actions/actionsTypes.ts (1)
  • UI (65-65)
app/lib/methods/search.ts (1)
  • search (114-185)
🪛 Biome (2.1.2)
app/sagas/quickActions.ts

[error] 71-71: Other switch clauses can erroneously access this declaration.
Wrap the declaration in a block to restrict its access to the switch clause.

The declaration is defined in this switch clause:

Safe fix: Wrap the declaration in a block.

(lint/correctness/noSwitchDeclarations)

🪛 Clang (14.0.6)
ios/QuickActionsConnector.h

[error] 1-1: 'React/RCTBridgeModule.h' file not found

(clang-diagnostic-error)

ios/QuickActionsStore.h

[error] 1-1: 'Foundation/Foundation.h' file not found

(clang-diagnostic-error)

🔇 Additional comments (21)
ios/Podfile (1)

12-12: LGTM!

The ExpoQuickActions pod declaration is correctly configured with the standard local path to node_modules, aligning with the npm dependency addition in package.json.

android/app/src/main/res/drawable/ic_quickaction_recent.xml (1)

1-9: LGTM!

The vector drawable is correctly formatted with standard Android vector attributes and provides a clear recent/clock icon for the quick action.

app/sagas/rooms.js (2)

20-24: Stylistic-only callback param change looks safe.

Line 21 changes record => { ... } formatting only; behavior remains the same.


76-128: Bulk formatting changes in record preparation look safe.

Lines 78-127: only arrow-param formatting updates (sub =>, message =>, etc.). No logic changes spotted.

app/sagas/index.js (1)

18-40: No race condition exists here; quickActions and roomVisited have defensive guards or no dependencies on init() completion.

The root saga's yield all([...]) pattern is safe:

  • The init(), quickActions(), and roomVisited() generators are watcher sagas that register listeners and return immediately; they don't execute logic on startup.
  • quickActions includes defensive waitForAppReady() checks (lines 70, 80, 83) that block until APP.READY is dispatched before handling actions.
  • roomVisited is purely event-driven from emitter.on('roomVisited') with no app state dependency.
  • The init saga only responds to an APP.INIT action dispatched from the React component; actual initialization logic doesn't run until that action arrives.

The parallel wiring is intentional and correct.

Likely an incorrect or invalid review comment.

ios/RocketChatRN-Bridging-Header.h (1)

13-13: The import of QuickActionsStore.h is correctly configured. The header exists, is properly exported via FOUNDATION_EXPORT, and is already referenced multiple times in the project configuration. The import syntax matches existing working imports in the same bridging header.

ios/QuickActionsConnector.h (1)

1-4: LGTM! Standard React Native bridge module header.

The interface declaration follows React Native conventions for native module bridging. The static analysis error about the missing React header is a false positive—the React Native headers will be available during the Xcode build process.

app/sagas/init.js (1)

55-60: LGTM! Last visited room restoration logic is correct.

The logic appropriately:

  • Reads both room ID and name from UserPreferences
  • Guards against partial data by checking both values exist
  • Dispatches the action to restore state
app/lib/navigation/addServer/index.ts (1)

9-16: LGTM! Clean navigation helper with proper batching.

The implementation correctly:

  • Logs analytics events before navigation
  • Uses batch() to dispatch multiple actions atomically, preventing intermediate re-renders
  • Provides clear type annotations
app/reducers/rooms.ts (1)

51-56: LGTM! Reducer logic correctly stores last visited room.

The reducer case properly:

  • Spreads existing state
  • Maps action payload to state fields
  • Maintains immutability
app/definitions/redux/index.ts (1)

51-51: No review comment was provided for verification and rewriting. Please provide the review comment within <review_comment> tags so I can proceed with the verification and rewriting process.

app/AppContainer.tsx (1)

69-72: NAVIGATION_READY dispatch on container ready looks good.
Keeps Redux in sync with navigation readiness and complements the existing emitter.emit('navigationReady').

android/app/src/main/res/drawable/ic_quickaction_find.xml (1)

1-5: LGTM for a shortcut icon vector resource.

app/i18n/locales/pt-BR.json (1)

322-322: This review comment identifies the wrong lines. Only line 827 contains punctuation in an i18n key (Something_Wrong?), but lines 447, 686-687, and 966 do not. Additionally, keys with ? are already present throughout the codebase (22 keys in pt-BR.json alone, and similar patterns across all 25 locales), and the tooling (i18n-js 3.9.2) handles them without issue.

Likely an incorrect or invalid review comment.

app/lib/methods/helpers/emitter.ts (1)

20-23: LGTM! Well-typed event addition.

The new roomVisited event is properly typed with rid and name fields, consistent with the existing event patterns in the emitter.

ios/AppDelegate.swift (1)

46-53: LGTM! Correct iOS quick actions implementation.

The quick action handling follows iOS best practices:

  • Extracting the shortcut item from launch options and storing it via RCSetPendingQuickActionType
  • Returning false in didFinishLaunchingWithOptions to prevent iOS from invoking performActionFor again
  • Properly implementing the performActionFor callback with completion handler

Also applies to: 90-97

app/actions/rooms.ts (1)

17-20: LGTM! Well-structured action additions.

The new IRoomsLastVisited interface and roomsStoreLastVisited action creator are properly typed and consistent with existing action patterns in the file.

Also applies to: 54-60

app/reducers/index.js (1)

29-30: LGTM! Clean reducer integration.

The new quickActions and ui reducers are properly imported and integrated into the root reducer using standard Redux patterns.

Also applies to: 40-40, 43-43

app/lib/quickActions/index.ts (1)

22-53: Quick action item definitions look consistent (ids, i18n, platform icons).
The id values align with saga handling (search, add-server, recent, contact), and using recentRoomName for the “recent” title is a nice touch.

ios/RocketChatRN.xcodeproj/project.pbxproj (1)

13-16: QuickActions native files are correctly wired into sources (both app targets).
Looks consistent: .mm files are present in PBXSourcesBuildPhase and headers are referenced.

Also applies to: 477-481, 739-762, 2031-2085, 2308-2362

android/app/src/main/java/chat/rocket/reactnative/MainActivity.kt (1)

104-146: Verify Expo quick-action intent contract against documentation or custom Expo module configuration.

The code assumes a specific Intent action ("expo.modules.quickactions.SHORTCUT") and Intent extra ("shortcut_data" as PersistableBundle) that cannot be confirmed against publicly available Expo Quick Actions documentation. Standard Expo Quick Actions patterns on Android use Action.params mapped to Intent extras via ShortcutManager, with primitives and package-namespaced keys—not a custom "expo.modules.quickactions.SHORTCUT" action or unprefixed bundle keys like "id" or "shortcutId".

Confirm whether this Intent contract is defined in Rocket.Chat's Expo module configuration, custom Expo patches, or undocumented Expo behavior before relying on these assumptions.

Comment on lines +1567 to 1583
1E1EA8082326CCE300E22452 /* ShellScript */ = {
isa = PBXShellScriptBuildPhase;
alwaysOutOfDate = 1;
buildActionMask = 2147483647;
files = (
);
inputFileListPaths = (
);
inputPaths = (
);
name = "[Expo] Configure project";
outputFileListPaths = (
);
outputPaths = (
);
runOnlyForDeploymentPostprocessing = 0;
shellPath = /bin/sh;
shellScript = "# This script configures Expo modules and generates the modules provider file.\nbash -l -c \"./Pods/Target\\ Support\\ Files/Pods-defaults-RocketChatRN/expo-configure-project.sh\"\n";
shellScript = "echo \"Target architectures: $ARCHS\"\n\nAPP_PATH=\"${TARGET_BUILD_DIR}/${WRAPPER_NAME}\"\n\nfind \"$APP_PATH\" -name '*.framework' -type d | while read -r FRAMEWORK\ndo\nFRAMEWORK_EXECUTABLE_NAME=$(defaults read \"$FRAMEWORK/Info.plist\" CFBundleExecutable)\nFRAMEWORK_EXECUTABLE_PATH=\"$FRAMEWORK/$FRAMEWORK_EXECUTABLE_NAME\"\necho \"Executable is $FRAMEWORK_EXECUTABLE_PATH\"\necho $(lipo -info \"$FRAMEWORK_EXECUTABLE_PATH\")\n\nFRAMEWORK_TMP_PATH=\"$FRAMEWORK_EXECUTABLE_PATH-tmp\"\n\n# remove simulator's archs if location is not simulator's directory\ncase \"${TARGET_BUILD_DIR}\" in\n*\"iphonesimulator\")\necho \"No need to remove archs\"\n;;\n*)\nif $(lipo \"$FRAMEWORK_EXECUTABLE_PATH\" -verify_arch \"i386\") ; then\nlipo -output \"$FRAMEWORK_TMP_PATH\" -remove \"i386\" \"$FRAMEWORK_EXECUTABLE_PATH\"\necho \"i386 architecture removed\"\nrm \"$FRAMEWORK_EXECUTABLE_PATH\"\nmv \"$FRAMEWORK_TMP_PATH\" \"$FRAMEWORK_EXECUTABLE_PATH\"\nfi\nif $(lipo \"$FRAMEWORK_EXECUTABLE_PATH\" -verify_arch \"x86_64\") ; then\nlipo -output \"$FRAMEWORK_TMP_PATH\" -remove \"x86_64\" \"$FRAMEWORK_EXECUTABLE_PATH\"\necho \"x86_64 architecture removed\"\nrm \"$FRAMEWORK_EXECUTABLE_PATH\"\nmv \"$FRAMEWORK_TMP_PATH\" \"$FRAMEWORK_EXECUTABLE_PATH\"\nfi\n;;\nesac\n\necho \"Completed for executable $FRAMEWORK_EXECUTABLE_PATH\"\necho $\n\ndone\n";
};
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🏁 Script executed:

# First, check if the file exists and examine the script in context
find . -name "project.pbxproj" -type f | head -5

Repository: RocketChat/Rocket.Chat.ReactNative

Length of output: 122


🏁 Script executed:

# Search for the specific shell script in the pbxproj file
rg "1E1EA8082326CCE300E22452" --type-list | head -5

Repository: RocketChat/Rocket.Chat.ReactNative

Length of output: 467


🏁 Script executed:

# Look for the file structure
ls -la ios/ 2>/dev/null | head -20

Repository: RocketChat/Rocket.Chat.ReactNative

Length of output: 1367


🏁 Script executed:

# Check if the pbxproj file exists and examine build phases
if [ -f "ios/RocketChatRN.xcodeproj/project.pbxproj" ]; then
  wc -l "ios/RocketChatRN.xcodeproj/project.pbxproj"
  echo "---"
  # Try to extract lines around 1567-1583
  sed -n '1560,1590p' "ios/RocketChatRN.xcodeproj/project.pbxproj"
fi

Repository: RocketChat/Rocket.Chat.ReactNative

Length of output: 2364


🏁 Script executed:

# Search for similar shell scripts or lipo commands in the pbxproj
rg "lipo|ShellScript" ios/RocketChatRN.xcodeproj/project.pbxproj -A 2 -B 2 | head -50

Repository: RocketChat/Rocket.Chat.ReactNative

Length of output: 3171


🏁 Script executed:

# Check the Podfile for xcframeworks usage
cat ios/Podfile | head -50

Repository: RocketChat/Rocket.Chat.ReactNative

Length of output: 1613


🏁 Script executed:

# Search for any references or comments about this ShellScript or architecture stripping
rg "architecture|lipo|1E1EA8082326CCE300E22452" ios/ --type-list

Repository: RocketChat/Rocket.Chat.ReactNative

Length of output: 467


🏁 Script executed:

# Look for CI configuration
find . -name ".github" -o -name ".gitlab-ci.yml" -o -name "*.yml" -o -name "*.yaml" | grep -E "(github|gitlab|circle|travis|action)" | head -10

Repository: RocketChat/Rocket.Chat.ReactNative

Length of output: 485


🏁 Script executed:

# Search for any build configuration or documentation mentioning this
rg "ShellScript|strip.*arch" . -t md -t txt | head -20

Repository: RocketChat/Rocket.Chat.ReactNative

Length of output: 60


🏁 Script executed:

# Search for references to architecture stripping or this build phase
rg "lipo|verify_arch" ios/ -A 2 -B 2

Repository: RocketChat/Rocket.Chat.ReactNative

Length of output: 1789


🏁 Script executed:

# Check the CI build configuration to understand if this is actually run
cat .github/actions/build-ios/action.yml | head -100

Repository: RocketChat/Rocket.Chat.ReactNative

Length of output: 3506


🏁 Script executed:

# Look for any other shell scripts in the project that might indicate modern approach
rg "use_frameworks|xcframework" ios/

Repository: RocketChat/Rocket.Chat.ReactNative

Length of output: 127


🏁 Script executed:

# Search for any issues or TODOs related to architecture stripping
rg "TODO|FIXME|NOTE" ios/RocketChatRN.xcodeproj/ -i

Repository: RocketChat/Rocket.Chat.ReactNative

Length of output: 60


🏁 Script executed:

# Check if there are any mentions of this approach in recent commits or discussions
git log --oneline ios/RocketChatRN.xcodeproj/project.pbxproj 2>/dev/null | head -20

Repository: RocketChat/Rocket.Chat.ReactNative

Length of output: 129


🏁 Script executed:

# Let's verify if this script is actually being executed or if it's dead code
# Check if the build phase is referenced in the main target
rg "1E1EA8082326CCE300E22452" ios/RocketChatRN.xcodeproj/project.pbxproj -B 5 -A 5

Repository: RocketChat/Rocket.Chat.ReactNative

Length of output: 1151


🏁 Script executed:

# Check for any modern architecture handling in CocoaPods or Xcode config
rg "ARCHS|VALID_ARCHS|arm64" ios/RocketChatRN.xcodeproj/project.pbxproj | head -20

Repository: RocketChat/Rocket.Chat.ReactNative

Length of output: 1739


🏁 Script executed:

# Verify the exact content of that trailing echo line
python3 << 'EOF'
import re
content = open('ios/RocketChatRN.xcodeproj/project.pbxproj', 'r').read()
# Extract the shellScript value
match = re.search(r'shellScript = "([^"]*echo \$[^"]*)"', content)
if match:
    script = match.group(1)
    # Decode escape sequences
    script = script.encode().decode('unicode_escape')
    lines = script.split('\n')
    for i, line in enumerate(lines[-5:], len(lines)-4):
        print(f"Line {i}: {repr(line)}")
EOF

Repository: RocketChat/Rocket.Chat.ReactNative

Length of output: 60


🏁 Script executed:

# Let's decode the exact trailing line to confirm it's malformed
python3 << 'EOF'
# Read the pbxproj file
with open('ios/RocketChatRN.xcodeproj/project.pbxproj', 'r') as f:
    content = f.read()

# Find the specific script
import re
# Look for the pattern with the echo $ at the end
match = re.search(r'echo "Completed for executable.*?\necho \$\n', content)
if match:
    text = match.group(0)
    print("Found trailing lines:")
    print(repr(text))
    print("\nDecoded:")
    print(text)
else:
    print("Pattern not found, searching differently...")
    # Just look for the last few lines of that script
    match = re.search(r'Completed for executable.*?done', content, re.DOTALL)
    if match:
        lines = match.group(0).split('\n')
        for line in lines[-5:]:
            print(repr(line))
EOF

Repository: RocketChat/Rocket.Chat.ReactNative

Length of output: 197


🏁 Script executed:

# Verify if EXCLUDED_ARCHS is properly configured to handle architecture filtering
# This is the modern approach that should make the shell script unnecessary
rg "EXCLUDED_ARCHS.*iphonesimulator" ios/RocketChatRN.xcodeproj/project.pbxproj

Repository: RocketChat/Rocket.Chat.ReactNative

Length of output: 231


🏁 Script executed:

# Check if this build phase is actually necessary by looking for framework handling in CocoaPods
cat ios/Podfile | grep -A 10 -B 10 "frameworks\|xcframework" || echo "No xcframework config found"

Repository: RocketChat/Rocket.Chat.ReactNative

Length of output: 727


Remove this redundant and broken shell script build phase.

The script is malformed (trailing echo $ is incomplete), unnecessary, and potentially unsafe. The project already handles architecture filtering properly via EXCLUDED_ARCHS[sdk=iphonesimulator*] configuration, and modern CocoaPods with static framework linking handles framework embedding automatically. Direct lipo modifications on frameworks in the build directory can interfere with code signing and framework embedding, causing build failures especially with xcframeworks. Delete the entire 1E1EA8082326CCE300E22452 ShellScript build phase.

🤖 Prompt for AI Agents
In @ios/RocketChatRN.xcodeproj/project.pbxproj around lines 1567 - 1583, Remove
the entire PBXShellScriptBuildPhase entry with identifier
1E1EA8082326CCE300E22452 from the project file and delete any references to that
ID from target buildPhases arrays so the shell script is fully removed; ensure
you also remove its block from PBXBuildPhase lists (and any duplicate
occurrences) to avoid dangling references that would break the project.

@Rohit3523
Copy link
Contributor

The specified version ^6.0.0 targets Expo SDK 54, not SDK 53. For SDK 53 👀

@divyanshu-patil
Copy link
Author

divyanshu-patil commented Jan 11, 2026

The specified version ^6.0.0 targets Expo SDK 54, not SDK 53. For SDK 53 👀

yup,
applying suggestions of coderabbit

@divyanshu-patil
Copy link
Author

divyanshu-patil commented Jan 13, 2026

its better to show the last three visited rooms in the Quick Actions instead of showing Search and Add Workspace. This would help users quickly open a channel they recently visited.
A room should appear in Quick Actions only when a user clicks on a room from the list and enters it.

@Rohit3523 the original feature #1705 mentioned this 3 actions described
but showing last 3 visited rooms is also a good option

waiting for feedback before proceeding

@Rohit3523
Copy link
Contributor

As an improvement, we should also display the room image instead of an icon.

this is the expo-quick-actions limitation as image must be bundled with the app so we can't do that rn if in future lib added support for that i am happy to implement it

Let me discuss about this with the team

@Rohit3523
Copy link
Contributor

its better to show the last three visited rooms in the Quick Actions instead of showing Search and Add Workspace. This would help users quickly open a channel they recently visited.
A room should appear in Quick Actions only when a user clicks on a room from the list and enters it.

@Rohit3523 the original feature #1705 mentioned this 3 actions described but showing last 3 visited rooms is also a good option

waiting for feedback before proceeding

Yes, the original idea was to implement Add Workspace, Search, and Recent Chats. However, after reviewing several apps, we noticed that most of them already show recent chats in the options and we realized that Add Workspace and Search would be used very rarely by users.

So, we decided to simply show the last three visited rooms. Additionally, please make sure that if more than one workspace is added and rooms from multiple workspaces are shown in the list, clicking on an item opens the chat in the correct workspace.

@divyanshu-patil
Copy link
Author

divyanshu-patil commented Jan 14, 2026

Additionally, please make sure that if more than one workspace is added and rooms from multiple workspaces are shown in the list, clicking on an item opens the chat in the correct workspace.

okay thanks for feedback,
i will change options and ping you once done

@divyanshu-patil
Copy link
Author

converting to draft because of requested changes

@divyanshu-patil divyanshu-patil marked this pull request as draft January 14, 2026 20:56
@divyanshu-patil
Copy link
Author

Sharing a quick demo for feedback

updated.quick.actions.demo.mp4

@divyanshu-patil
Copy link
Author

Hello maintainers,

I am facing a weird bug while switching servers after launching the app from quick actions.

In this bug, when I launch the app using a quick action (e.g., recent room shortcut) to switch to a different server than the currently active one, the server names in the workspace list get mixed up. Both servers end up showing the same name instead of their actual distinct names.

For example:

  • Server 1: https://open.rocket.chat should show "Rocket.Chat Open"
  • Server 2: http://localhost:3000 should show "div"

But after launching via quick action to Server 2, both servers display "Rocket.Chat Open" (or sometimes both show "div"). The server URLs/IDs remain correct, only the display names are duplicated.

Steps to reproduce:

  1. Have two servers configured with different names
  2. Be on Server 1
  3. Long-press app icon and select a recent room from Server 2
  4. Open the workspace switcher - both servers now show the same name

What I found:

  • The saga dispatches selectServerSuccess with the correct name
  • The database stores the correct name
  • But the UI (ServersList component) receives incorrect names from WatermelonDB
  • This only happens when switching via quick actions, not manual server switching

The issue seems to be related to WatermelonDB caching or a race condition during the quick action flow.
i have attached a video of this behaivour

switch.server.3.mp4

@divyanshu-patil
Copy link
Author

divyanshu-patil commented Jan 20, 2026

When the app is launched using a quick action for a different server, both servers end up displaying the same name fetched from the database.

This behavior is unexpected, difficult to reproduce consistently, and hard to trace back to a clear root cause.

adding delays doesn't solves this

Note

happens mostly with local servers and works fine with deployed

Hello maintainers,

I am facing a weird bug while switching servers after launching the app from quick actions.

@divyanshu-patil divyanshu-patil marked this pull request as ready for review January 20, 2026 20:47
Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 0

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
app/reducers/index.js (1)

29-41: Add quickActions to IApplicationState type definition.

The quickActions reducer is wired into the root combineReducers in app/reducers/index.js (line 41), but the type definition at app/definitions/redux/index.ts is missing the corresponding property. Add quickActions: IQuickActionsState; to the IApplicationState interface (around line 79) to keep types in sync with the runtime state shape.

🧹 Nitpick comments (4)
app/reducers/rooms.ts (1)

61-89: Wrap STORE_LAST_VISITED case in a block for better scoping practice.
While the no-case-declarations rule is currently disabled in the project's ESLint config, wrapping lexical declarations in a block scope prevents potential variable hoisting issues and follows best practices for switch statements.

🛠️ Suggested fix
-		case ROOMS.STORE_LAST_VISITED:
+		case ROOMS.STORE_LAST_VISITED: {
 			const server = UserPreferences.getString(CURRENT_SERVER);
 			const newRoom = { rid: action.lastVisitedRoomId, name: action.lastVisitedRoomName, server };
@@
 			return {
 				...state,
 				lastVisitedRid: action.lastVisitedRoomId,
 				lastVisitedName: action.lastVisitedRoomName,
 				recentRooms: updatedRecentRooms
 			};
+		}
 		case ROOMS.STORE_RECENT_ROOMS:
app/lib/quickActions/index.ts (1)

80-85: appStateSubscription is not reset to null in unregisterQuickActions.

The cleanup function resets quickActionSubscription to null but not appStateSubscription, which could lead to inconsistent state if someone checks the subscription variables.

Proposed fix
 export function unregisterQuickActions() {
 	quickActionSubscription?.remove();
 	appStateSubscription?.remove();
 	quickActionSubscription = null;
+	appStateSubscription = null;
 	registered = false;
 }
app/AppContainer.tsx (2)

43-49: Async function called without error handling in effect.

registerQuickActions() is async but its promise is not handled. If it throws, the error will be silently swallowed. Consider adding error handling or logging.

Proposed fix
 	useEffect(() => {
-		registerQuickActions();
+		registerQuickActions().catch(e => {
+			// Log or handle registration failure
+			console.warn('Failed to register quick actions:', e);
+		});

 		return () => {
 			unregisterQuickActions();
 		};
 	}, []);

60-62: Same unhandled promise concern for updateQuickActions.

If updateQuickActions fails (e.g., getServersList() throws), the error is silently ignored.

Proposed fix
 	useEffect(() => {
-		updateQuickActions({ recentRooms });
+		updateQuickActions({ recentRooms }).catch(e => {
+			console.warn('Failed to update quick actions:', e);
+		});
 	}, [recentRooms]);

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🤖 Fix all issues with AI agents
In `@app/reducers/rooms.ts`:
- Around line 59-83: The switch case for ROOMS.STORE_LAST_VISITED declares
consts (newRoom, filteredRooms, updatedRecentRooms) in the case scope which
triggers noSwitchDeclarations; wrap the entire case body in a block (add { ... }
after case ROOMS.STORE_LAST_VISITED:) so the const declarations are scoped to
that block, keeping the logic that builds newRoom, filters state.recentRooms,
trims to 3 items, and returns the updated state intact.
🧹 Nitpick comments (1)
app/AppContainer.tsx (1)

61-68: Consider handling async updateQuickActions errors.
If the async call rejects, it can surface as an unhandled promise rejection. A small try/catch wrapper keeps this resilient.

🧯 Suggested pattern
-	useEffect(() => {
-		if (!currentServer) {
-			updateQuickActions({ recentRooms: [] });
-			return;
-		}
-
-		updateQuickActions({ recentRooms });
-	}, [recentRooms, currentServer]); // currentserver for updating after logging out
+	useEffect(() => {
+		const run = async () => {
+			if (!currentServer) {
+				await updateQuickActions({ recentRooms: [] });
+				return;
+			}
+			await updateQuickActions({ recentRooms });
+		};
+
+		run().catch(err => {
+			// TODO: log via existing logger if desired
+			console.warn('updateQuickActions failed', err);
+		});
+	}, [recentRooms, currentServer]); // currentserver for updating after logging out

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 3

🤖 Fix all issues with AI agents
In `@app/lib/methods/helpers/goRoom.ts`:
- Around line 106-118: The call to getRoom is using result?.room?.rid which is
undefined for IM creation; replace it to use result?.room?._id (e.g., in the try
block before emitter.emit) so getRoom(getId) receives the actual room id (use
result.room._id || '' as the fallback) and then keep emitting roomVisited with
result.room._id and the resolved room name/server; update any variable
references in that block (getRoom, result.room._id, emitter.emit) accordingly.

In `@app/sagas/init.js`:
- Around line 61-67: The JSON.parse call for recentRooms is unguarded and can
throw if the stored RECENT_VISITED_ROOMS_KEY is absent or corrupted; update the
logic in the init saga to safely parse by reading
UserPreferences.getString(RECENT_VISITED_ROOMS_KEY) into a variable, then wrap
JSON.parse in a try/catch (or use a safeParse helper) and fallback to an empty
array/null on error, and only yield put(roomsStoreRecentRooms(recentRooms)) when
the parsed value is valid; keep the existing LAST_VISITED_ROOM_ID_KEY and
LAST_VISITED_ROOM_NAME_KEY reads unchanged.

In `@app/sagas/roomVisited.ts`:
- Around line 28-38: When the saga receives a roomVisited event with an empty
rid (logout) it still persists the previous recentRooms and writes blank
last-visited keys; change the while loop handling so that if rid is falsy you
(1) clear the in-memory state by dispatching a clear action (or dispatch
roomsStoreLastVisited with an explicit clear payload) instead of the normal
update, and (2) clear persisted keys via UserPreferences (remove or write an
empty array for RECENT_VISITED_ROOMS_KEY and remove/clear
LAST_VISITED_ROOM_ID_KEY and LAST_VISITED_ROOM_NAME_KEY) and skip
selecting/writing the previous recentRooms; update the logic around the yield
take(channel) in roomVisited saga to branch on if (!rid) and perform these clear
steps, otherwise continue the existing flow that selects getRecentRooms and
persists them.
🧹 Nitpick comments (1)
app/actions/rooms.ts (1)

4-5: Use a union for IRoomsAction, not an intersection.

An intersection forces every action to carry all fields, which defeats discriminated unions in reducers. A union is the idiomatic and safer shape that allows TypeScript to narrow types in the reducer's switch statement based on the type field.

♻️ Suggested type change
-export type IRoomsAction = IRoomsRequest & ISetSearch & IRoomsFailure & IRoomsLastVisited & IRecentRooms;
+export type IRoomsAction =
+	| IRoomsRequest
+	| ISetSearch
+	| IRoomsFailure
+	| IRoomsLastVisited
+	| IRecentRooms;

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

feature request: iOS Quick Action to request feedback [NEW] Quick actions

3 participants