Skip to content

feat: presence sync#7392

Open
Rohit3523 wants to merge 108 commits into
developfrom
feat/presence-sync
Open

feat: presence sync#7392
Rohit3523 wants to merge 108 commits into
developfrom
feat/presence-sync

Conversation

@Rohit3523

@Rohit3523 Rohit3523 commented Jun 10, 2026

Copy link
Copy Markdown
Member

Proposed changes

Adds support for temporary/expiring custom status messages. Users can now set a status that automatically clears after a chosen duration (30 minutes, 1 hour, or a custom date/time). When a status has an expiry, a clock icon and "Until" timestamp are shown next to the status in the room header, room actions, and room info views for direct messages.

This feature requires server version 8.6.0+.

On workspaces running 8.6.0+, users can no longer manually set their status to "Away" — it is handled automatically by the server. The Away option is hidden from the Status screen unless the user is already in that state.

Key changes:

  • New ClearAfterPicker component in StatusView for selecting expiry duration
  • formatStatusExpiry helper to display formatted expiry timestamps
  • normalizeStatusExpiresAt helper to handle EJSON and ISO date formats
  • Presence stream handlers (stream-user-presence, user-status) updated to process statusExpiresAt and statusSource fields
  • users.setStatus REST API now sends expiresAt when set
  • Updated types: ILoggedUser, IActiveUser, TStatusSource
  • Added stories and tests for the new functionality

Issue(s)

https://rocketchat.atlassian.net/browse/PRES-39

How to test or reproduce

  1. Connect to a Rocket.Chat server 8.6.0+
  2. Go to Edit Status screen
  3. Set a custom status text (e.g. "In a meeting")
  4. Choose a clear-after duration (30 min, 1 hour, or custom date/time)
  5. Save — verify the status shows with a clock icon and "Until" timestamp in:
    • Room header subtitle (DM)
    • Room actions sheet (DM)
    • Room info view (DM)
  6. Verify the status auto-clears after the selected duration
  7. Test on servers <8.6.0 — the clear-after picker should be hidden and "Away" should still be selectable
  8. On 8.6.0+, verify "Away" is not shown as a selectable option (unless already away)
  9. Verify presence sync works across multiple clients

Screenshots

Android iOS
screenshot_android screenshot_ios
Condition Room Header Room Action User Info
Offline Screenshot 2026-06-16 at 9 56 43 PM Screenshot 2026-06-16 at 9 57 44 PM Screenshot 2026-06-16 at 9 57 56 PM
Busy Screenshot 2026-06-16 at 9 59 09 PM Screenshot 2026-06-16 at 9 59 19 PM Screenshot 2026-06-16 at 9 59 34 PM
Online Screenshot 2026-06-16 at 10 02 00 PM Screenshot 2026-06-16 at 10 02 17 PM Screenshot 2026-06-16 at 10 02 34 PM
Expiring Status Screenshot 2026-06-16 at 10 03 58 PM Screenshot 2026-06-16 at 10 04 08 PM Screenshot 2026-06-16 at 10 04 25 PM

Setting expiring status

Android iOS
Screen.Recording.2026-06-16.at.10.05.18.PM.mov
Screen.Recording.2026-06-16.at.10.28.48.PM.mov

Realtime status update

  1. Room header

    Screen.Recording.2026-06-16.at.10.17.00.PM.mov

  2. Room Action

    Screen.Recording.2026-06-16.at.10.18.55.PM.mov

  3. User info

    Screen.Recording.2026-06-16.at.10.26.22.PM.mov

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

Summary by CodeRabbit

Summary

  • New Features
    • Added temporary status expiration with preset options (30 minutes, 1 hour) or custom date/time, including a “don’t clear” option.
    • Display “Until …” with a clock for status expiry in direct-room headers and room info views.
    • Expiry handling now supports presence updates coming from supported sources.
  • Localization
    • Added translations for timed-status options and the “Until” label.
  • Tests
    • Added automated coverage for status-expiry formatting and expiry normalization (and stabilized status-related snapshots).

@coderabbitai

coderabbitai Bot commented Jun 10, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review

Walkthrough

Adds timed status expiry to user presence across type definitions, REST API, presence propagation, status-editing UI, and room information surfaces. Introduces status source tracking and expiry formatting; threads expiry through presence streams and socket handlers; implements a clear-after picker with preset and custom date selection; renders expiry with clock icons in room headers and info views. Localization support added across 24 locales.

Changes

Timed Status Expiry Feature

Layer / File(s) Summary
Status metadata types and contracts
app/definitions/TStatusSource.ts, app/definitions/index.ts, app/definitions/ILoggedUser.ts, app/definitions/TUserStatus.ts, app/reducers/activeUsers.ts, app/lib/methods/setUser.ts, app/definitions/rest/v1/users.ts
Introduces TStatusSource union type with values 'internal', 'external', 'manual'; adds STATUS_I18N_KEYS constant mapping presence statuses to i18n keys. Extends ILoggedUser, IActiveUser, and IActiveUsers with optional statusExpiresAt, statusSource, and statusDefault fields. Updates users.setStatus REST endpoint to accept optional expiresAt parameter and return { success: boolean }.
Status expiry normalization and formatting utilities
app/lib/methods/helpers/normalizeStatusExpiresAt.ts, app/lib/methods/helpers/normalizeStatusExpiresAt.test.ts, app/lib/methods/helpers/index.ts, app/lib/methods/helpers/formatStatusExpiry.ts, app/lib/methods/helpers/formatStatusExpiry.test.ts
Adds normalizeStatusExpiresAt helper converting ISO strings, Date objects, numeric timestamps, and EJSON {$date} objects to ISO strings; returns undefined for invalid/falsy inputs. Adds formatStatusExpiry returning localized "Until …" labels for future timestamps (time-only for same-day, month+day+time otherwise) or undefined for invalid/past dates. Both helpers include comprehensive Jest test suites.
Presence stream propagation and REST submission
app/lib/methods/getUsersPresence.ts, app/lib/services/connect.ts, app/lib/services/restApi.ts
Propagates statusExpiresAt and statusSource through getUsersPresence presence map building, stream-user-presence RC 4.1+ socket handler, and legacy user-status event handler. Normalizes raw expiry values via normalizeStatusExpiresAt. Migrates setUserStatus from DDP method call to users.setStatus REST POST with optional expiresAt parameter.
Clear-after picker UI
app/views/StatusView/ClearAfterPicker.tsx
Introduces ClearAfterValue type for preset (`''
StatusView expiry integration
app/views/StatusView/index.tsx
Integrates ClearAfterPicker with server-version gating (≥ 8.6.0 for expiry support); adds state for clearAfter, clearAfterDate, and clearAfterTouched ref. Computes expiresAt via computeExpiresAt when picker touched and passes it through setCustomStatussetUserStatus. Updates disabled logic in isStatusChanged to account for picker interaction; adds footerComponentWithPicker style variant; renames text input label from Message to Status; improves error handling with showErrorAlertWithEMessage.
RoomHeader expiry subtitle
app/containers/RoomHeader/index.tsx, app/containers/RoomHeader/RoomHeader.tsx, app/containers/RoomHeader/RoomHeader.stories.tsx, app/containers/RoomHeader/RoomHeader.test.tsx
Threads statusExpiresAt from Redux-connected container through Header component; formats via formatStatusExpiry and conditionally renders clock CustomIcon + MarkdownPreview in subtitle when expiry exists. Extends accessibility label subtitle portion with formatted expiry. Adds DM_Status Storybook story rendering multiple DM status variants with and without expiry timestamps. Configures Jest fake timers in test file for deterministic snapshot generation.
RoomInfoViewTitle multi-row status and expiry
app/views/RoomInfoView/components/RoomInfoViewTitle.tsx, app/views/RoomInfoView/components/RoomInfoViewTitle.stories.tsx, app/views/RoomInfoView/styles.ts
Extends IRoomInfoViewTitle interface with userId, status, and statusExpiresAt optional props; computes presenceLabel from status when statusText absent. Replaces single custom-status collapsible row with three conditional rows: custom-status with optional Status icon, localized presence label from STATUS_I18N_KEYS, and formatted expiry with clock CustomIcon. Adds statusRow style with flex row layout and gap spacing. Includes DM_Status Storybook story with status and expiry variants.
RoomInfoView presence wiring and avatar refactor
app/views/RoomInfoView/index.tsx, app/views/RoomInfoView/components/RoomInfoViewAvatar.tsx
Selects activeUsers from Redux state; derives roomUserId for direct rooms and looks up activeUserStatus. Passes userId, status, and statusExpiresAt to RoomInfoViewTitle with statusText preferring activeUserStatus over roomUser fallback. Removes userId prop from RoomInfoViewAvatar and wires edit handler directly via handleEdit prop, eliminating inline Status UI rendering.
RoomActionsView status rows
app/views/RoomActionsView/index.tsx, app/views/RoomActionsView/styles.ts
Adds activeUsers: IActiveUsers Redux prop with syncMemberFromActiveUsers helper and componentDidUpdate merge logic to keep direct-room member state in sync. Restructures room-info status area: removes inline Status from Avatar children and adds three dedicated rows for custom statusText, localized presence label via STATUS_I18N_KEYS, and formatted statusExpiresAt with clock CustomIcon. Updates mapStateToProps to pass activeUsers state. Adds statusRow style. Improves type safety in updateRoomMember by casting getUserInfo result as unknown as Partial<IUser>.
Localization across 24 locales
app/i18n/locales/ar.json, app/i18n/locales/bn-IN.json, app/i18n/locales/cs.json, app/i18n/locales/de.json, app/i18n/locales/en.json, app/i18n/locales/es.json, app/i18n/locales/fi.json, app/i18n/locales/fr.json, app/i18n/locales/hi-IN.json, app/i18n/locales/hu.json, app/i18n/locales/it.json, app/i18n/locales/ja.json, app/i18n/locales/nl.json, app/i18n/locales/nn.json, app/i18n/locales/no.json, app/i18n/locales/pt-BR.json, app/i18n/locales/pt-PT.json, app/i18n/locales/ru.json, app/i18n/locales/sl-SI.json, app/i18n/locales/sv.json, app/i18n/locales/ta-IN.json, app/i18n/locales/te-IN.json, app/i18n/locales/tr.json, app/i18n/locales/zh-CN.json
Adds eight i18n keys across all 24 supported locales: Status, Status_1_hour, Status_30_minutes, Status_choose_date_and_time, Status_clear_after, Status_clear_after_hint (describing calendar integration and voice/video call changes), Status_dont_clear, and Until. All locales consistently support the clear-after UI feature with locale-specific translations.

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~75 minutes

Suggested reviewers

  • diegolmello
  • OtavioStasiak
🚥 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
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The PR title 'feat: presence sync' clearly summarizes the main feature addition, which aligns with the core objective of implementing presence synchronization with temporary/expiring status support.
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 (3)
  • PRES-39: Request failed with status code 401
  • AD43-4608: Request failed with status code 401
  • C7C5-4990: 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.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Actionable comments posted: 4

🧹 Nitpick comments (5)
app/containers/RoomHeader/RoomHeader.tsx (1)

119-126: ⚡ Quick win

Extract inline style to StyleSheet for performance.

The inline style object { marginRight: 4 } creates a new object on every render. Per React Native best practices, extract it to StyleSheet.create to avoid unnecessary re-renders and improve performance.

♻️ Proposed refactor

Add to the existing styles definition around line 30:

 const styles = StyleSheet.create({
 	container: {
 		flex: 1,
 		justifyContent: 'center'
 	},
 	titleContainer: {
 		alignItems: 'center',
 		flexDirection: 'row'
 	},
 	title: {
 		flexShrink: 1,
 		...sharedStyles.textSemibold
 	},
 	subtitle: {
 		flexShrink: 1,
 		...sharedStyles.textRegular
 	},
 	typingUsers: {
 		...sharedStyles.textSemibold
+	},
+	clockIcon: {
+		marginRight: 4
 	}
 });

Then update line 122:

-					<CustomIcon name='clock' size={fontSize} color={colors.fontSecondaryInfo} style={{ marginRight: 4 }} />
+					<CustomIcon name='clock' size={fontSize} color={colors.fontSecondaryInfo} style={styles.clockIcon} />
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@app/containers/RoomHeader/RoomHeader.tsx` around lines 119 - 126, The inline
style { marginRight: 4 } on the CustomIcon in RoomHeader.tsx should be moved
into the component's StyleSheet to avoid creating a new object each render; add
a new style (e.g., iconSpacing or clockIcon) to the existing styles created with
StyleSheet.create and include marginRight: 4 there, then replace style={{
marginRight: 4 }} on the CustomIcon with style={styles.iconSpacing} (keeping the
existing styles.titleContainer reference intact).
app/lib/methods/helpers/formatStatusExpiry.ts (2)

6-12: 💤 Low value

Consider computing "now" once for consistency.

The function calls dayjs() on line 6 and again on line 10. While the performance impact is negligible, storing "now" in a single variable improves code clarity and eliminates the (extremely rare) edge case where midnight strikes between the two calls.

♻️ Suggested refactor
 export const formatStatusExpiry = (statusExpiresAt: string): string | undefined => {
+	const now = dayjs();
-	const expiresAt = dayjs(statusExpiresAt);
-	if (!expiresAt.isValid() || !expiresAt.isAfter(dayjs())) {
+	const expiresAt = dayjs(statusExpiresAt);
+	if (!expiresAt.isValid() || !expiresAt.isAfter(now)) {
 		return undefined;
 	}

-	const now = dayjs();
-
 	if (expiresAt.isSame(now, 'day')) {
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@app/lib/methods/helpers/formatStatusExpiry.ts` around lines 6 - 12, The
function currently calls dayjs() twice (once in the expiresAt.isAfter(dayjs())
check and again when assigning now), causing inconsistency; compute now once at
the start (e.g., const now = dayjs()) and use that same now in the isAfter check
and in the isSame(now, 'day') comparison so both validations reference the
identical timestamp (update references to expiresAt.isAfter(now) and
expiresAt.isSame(now, 'day')).

16-16: 💤 Low value

Consider combining format calls for brevity.

The date and time formatting can be combined into a single .format() call.

♻️ Suggested refactor
-	return `${I18n.t('Until')} ${expiresAt.format('MMM D')}, ${expiresAt.format('h:mm A')}`;
+	return `${I18n.t('Until')} ${expiresAt.format('MMM D, h:mm A')}`;
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@app/lib/methods/helpers/formatStatusExpiry.ts` at line 16, The return
currently calls expiresAt.format twice; update formatStatusExpiry to call
expiresAt.format once by combining the date and time pattern (e.g. 'MMM D, h:mm
A') and use that single formatted string in the template; refer to the expiresAt
variable and the formatStatusExpiry function to locate and change the expression
accordingly.
app/views/StatusView/ClearAfterPicker.tsx (1)

166-172: 💤 Low value

Consider defensive handling for edge case.

If value === 'custom' but customDate is null (line 167 condition fails), the function falls through to line 170-171. While this shouldn't occur in normal flow, a defensive check would improve robustness:

🛡️ Optional defensive refactor
 const getDisplayLabel = (): string => {
 	if (value === 'custom' && customDate) {
 		return customDate.toLocaleString(undefined, { month: 'short', day: 'numeric', hour: 'numeric', minute: '2-digit' });
 	}
+	if (value === 'custom' && !customDate) {
+		return I18n.t('Status_dont_clear');
+	}
 	const option = CLEAR_AFTER_OPTIONS.find(o => o.value === value);
 	return option ? I18n.t(option.labelKey) : I18n.t('Status_dont_clear');
 };
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@app/views/StatusView/ClearAfterPicker.tsx` around lines 166 - 172,
getDisplayLabel currently assumes customDate exists when value === 'custom' and
falls back to CLEAR_AFTER_OPTIONS lookup if it doesn't; add a defensive branch
at the top of getDisplayLabel to handle the edge case where value === 'custom'
but customDate is null/undefined by returning a safe fallback (e.g.,
I18n.t('Status_dont_clear') or another appropriate label) so the function never
attempts to call toLocaleString on a null customDate; reference getDisplayLabel,
value, customDate, CLEAR_AFTER_OPTIONS and I18n.t when making the change.
app/views/StatusView/index.tsx (1)

117-118: 💤 Low value

Consider extracting version constant.

The version string '8.6.0' is a magic value that determines feature availability. Extracting it to a named constant would improve maintainability:

♻️ Optional refactor
+const MIN_VERSION_STATUS_EXPIRY = '8.6.0';
+
 const StatusView = (): React.ReactElement => {
 	// ...
 	const serverVersion = useSelector((state: IApplicationState) => state.server.version);
-	const supportsStatusExpiry = compareServerVersion(serverVersion as string, 'greaterThanOrEqualTo', '8.6.0');
+	const supportsStatusExpiry = compareServerVersion(serverVersion as string, 'greaterThanOrEqualTo', MIN_VERSION_STATUS_EXPIRY);
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@app/views/StatusView/index.tsx` around lines 117 - 118, Extract the magic
version string '8.6.0' into a named constant (e.g., STATUS_EXPIRY_MIN_VERSION)
and use that constant in the compareServerVersion call that sets
supportsStatusExpiry; update the declaration around serverVersion,
supportsStatusExpiry and compareServerVersion usages so they reference
STATUS_EXPIRY_MIN_VERSION instead of the literal string to improve readability
and maintainability.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@app/i18n/locales/en.json`:
- Line 975: The en.json introduces the localized key "Until" (and potentially
"Status") but those same keys are missing from the other locale files (es, fi,
fr, hi-IN), causing fallbacks; add matching keys "Until" and "Status" to each
locale JSON present in app/i18n (es, fi, fr, hi-IN) with appropriate
translations or safe placeholder strings, keep the key names identical to the
en.json entries, maintain existing JSON ordering/formatting conventions, and run
the i18n validation/lint script to ensure no missing keys remain.

In `@app/i18n/locales/nn.json`:
- Line 401: The value for the key Status_clear_after_hint contains a
mixed-script typo ("Kalenderintegrасjonar"); open the JSON entry for
Status_clear_after_hint and replace the non-Latin characters with the correct
Latin spelling ("Kalenderintegrasjonar") so the string reads correctly: "Når
denne nye statusen vert nytta, kan han verta endra av: Kalenderintegrasjonar,
taleanrop eller videokonferanse."; keep the rest of the sentence unchanged and
ensure the JSON remains valid (proper quotes and commas) after editing.

In `@app/lib/services/connect.ts`:
- Around line 238-239: The deprecated "user-status" handler currently assumes
the server payload order by destructuring userStatus into [id, , status,
statusText, statusSource, statusExpiresAt]; instead, validate that userStatus is
an array and defensively read indexes (fallback to undefined/defaults) before
assigning into _activeUsers.activeUsers, using STATUSES lookup only when status
exists; mirror the safe parsing used in the stream-user-presence handler (check
length, coerce types, and treat missing statusText/statusSource/statusExpiresAt
as optional) so older RC 4.1+ servers that omit new fields won't corrupt the
active user map.

In `@app/views/RoomInfoView/components/RoomInfoViewTitle.tsx`:
- Around line 77-84: The code calls formatStatusExpiry(statusExpiresAt) twice;
compute it once into a local variable (e.g., const formattedExpiry =
formatStatusExpiry(statusExpiresAt)) near the top of RoomInfoViewTitle (or
inside the render/component function), then use formattedExpiry in the
conditional (!!statusExpiresAt && !!formattedExpiry) and inside the JSX Text
node instead of calling formatStatusExpiry again; this removes redundant
computation while keeping the same behavior.

---

Nitpick comments:
In `@app/containers/RoomHeader/RoomHeader.tsx`:
- Around line 119-126: The inline style { marginRight: 4 } on the CustomIcon in
RoomHeader.tsx should be moved into the component's StyleSheet to avoid creating
a new object each render; add a new style (e.g., iconSpacing or clockIcon) to
the existing styles created with StyleSheet.create and include marginRight: 4
there, then replace style={{ marginRight: 4 }} on the CustomIcon with
style={styles.iconSpacing} (keeping the existing styles.titleContainer reference
intact).

In `@app/lib/methods/helpers/formatStatusExpiry.ts`:
- Around line 6-12: The function currently calls dayjs() twice (once in the
expiresAt.isAfter(dayjs()) check and again when assigning now), causing
inconsistency; compute now once at the start (e.g., const now = dayjs()) and use
that same now in the isAfter check and in the isSame(now, 'day') comparison so
both validations reference the identical timestamp (update references to
expiresAt.isAfter(now) and expiresAt.isSame(now, 'day')).
- Line 16: The return currently calls expiresAt.format twice; update
formatStatusExpiry to call expiresAt.format once by combining the date and time
pattern (e.g. 'MMM D, h:mm A') and use that single formatted string in the
template; refer to the expiresAt variable and the formatStatusExpiry function to
locate and change the expression accordingly.

In `@app/views/StatusView/ClearAfterPicker.tsx`:
- Around line 166-172: getDisplayLabel currently assumes customDate exists when
value === 'custom' and falls back to CLEAR_AFTER_OPTIONS lookup if it doesn't;
add a defensive branch at the top of getDisplayLabel to handle the edge case
where value === 'custom' but customDate is null/undefined by returning a safe
fallback (e.g., I18n.t('Status_dont_clear') or another appropriate label) so the
function never attempts to call toLocaleString on a null customDate; reference
getDisplayLabel, value, customDate, CLEAR_AFTER_OPTIONS and I18n.t when making
the change.

In `@app/views/StatusView/index.tsx`:
- Around line 117-118: Extract the magic version string '8.6.0' into a named
constant (e.g., STATUS_EXPIRY_MIN_VERSION) and use that constant in the
compareServerVersion call that sets supportsStatusExpiry; update the declaration
around serverVersion, supportsStatusExpiry and compareServerVersion usages so
they reference STATUS_EXPIRY_MIN_VERSION instead of the literal string to
improve readability and maintainability.
🪄 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: 08ab0ff8-0004-4a51-809b-2a50101339eb

📥 Commits

Reviewing files that changed from the base of the PR and between 583986e and f2a4f52.

📒 Files selected for processing (44)
  • app/containers/RoomHeader/RoomHeader.tsx
  • app/containers/RoomHeader/index.tsx
  • app/definitions/ILoggedUser.ts
  • app/definitions/TStatusSource.ts
  • app/definitions/index.ts
  • app/definitions/rest/v1/users.ts
  • app/i18n/locales/ar.json
  • app/i18n/locales/bn-IN.json
  • app/i18n/locales/cs.json
  • app/i18n/locales/de.json
  • app/i18n/locales/en.json
  • app/i18n/locales/es.json
  • app/i18n/locales/fi.json
  • app/i18n/locales/fr.json
  • app/i18n/locales/hi-IN.json
  • app/i18n/locales/hu.json
  • app/i18n/locales/it.json
  • app/i18n/locales/ja.json
  • app/i18n/locales/nl.json
  • app/i18n/locales/nn.json
  • app/i18n/locales/no.json
  • app/i18n/locales/pt-BR.json
  • app/i18n/locales/pt-PT.json
  • app/i18n/locales/ru.json
  • app/i18n/locales/sl-SI.json
  • app/i18n/locales/sv.json
  • app/i18n/locales/ta-IN.json
  • app/i18n/locales/te-IN.json
  • app/i18n/locales/tr.json
  • app/i18n/locales/zh-CN.json
  • app/lib/methods/getUsersPresence.ts
  • app/lib/methods/helpers/formatStatusExpiry.test.ts
  • app/lib/methods/helpers/formatStatusExpiry.ts
  • app/lib/methods/setUser.ts
  • app/lib/services/connect.ts
  • app/lib/services/restApi.ts
  • app/reducers/activeUsers.ts
  • app/views/RoomActionsView/index.tsx
  • app/views/RoomActionsView/styles.ts
  • app/views/RoomInfoView/components/RoomInfoViewTitle.tsx
  • app/views/RoomInfoView/index.tsx
  • app/views/RoomInfoView/styles.ts
  • app/views/StatusView/ClearAfterPicker.tsx
  • app/views/StatusView/index.tsx
📜 Review details
🧰 Additional context used
📓 Path-based instructions (11)
**/*.{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/definitions/index.ts
  • app/definitions/rest/v1/users.ts
  • app/reducers/activeUsers.ts
  • app/definitions/TStatusSource.ts
  • app/containers/RoomHeader/RoomHeader.tsx
  • app/lib/methods/helpers/formatStatusExpiry.test.ts
  • app/lib/services/restApi.ts
  • app/views/RoomActionsView/styles.ts
  • app/lib/methods/setUser.ts
  • app/lib/methods/helpers/formatStatusExpiry.ts
  • app/views/RoomInfoView/index.tsx
  • app/lib/services/connect.ts
  • app/definitions/ILoggedUser.ts
  • app/views/RoomInfoView/styles.ts
  • app/containers/RoomHeader/index.tsx
  • app/views/RoomInfoView/components/RoomInfoViewTitle.tsx
  • app/lib/methods/getUsersPresence.ts
  • app/views/StatusView/ClearAfterPicker.tsx
  • app/views/StatusView/index.tsx
  • app/views/RoomActionsView/index.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/definitions/index.ts
  • app/definitions/rest/v1/users.ts
  • app/reducers/activeUsers.ts
  • app/definitions/TStatusSource.ts
  • app/containers/RoomHeader/RoomHeader.tsx
  • app/lib/methods/helpers/formatStatusExpiry.test.ts
  • app/lib/services/restApi.ts
  • app/views/RoomActionsView/styles.ts
  • app/lib/methods/setUser.ts
  • app/lib/methods/helpers/formatStatusExpiry.ts
  • app/views/RoomInfoView/index.tsx
  • app/lib/services/connect.ts
  • app/definitions/ILoggedUser.ts
  • app/views/RoomInfoView/styles.ts
  • app/containers/RoomHeader/index.tsx
  • app/views/RoomInfoView/components/RoomInfoViewTitle.tsx
  • app/lib/methods/getUsersPresence.ts
  • app/views/StatusView/ClearAfterPicker.tsx
  • app/views/StatusView/index.tsx
  • app/views/RoomActionsView/index.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/definitions/index.ts
  • app/definitions/rest/v1/users.ts
  • app/i18n/locales/sl-SI.json
  • app/reducers/activeUsers.ts
  • app/i18n/locales/cs.json
  • app/i18n/locales/nn.json
  • app/i18n/locales/no.json
  • app/i18n/locales/nl.json
  • app/definitions/TStatusSource.ts
  • app/i18n/locales/te-IN.json
  • app/i18n/locales/en.json
  • app/i18n/locales/bn-IN.json
  • app/i18n/locales/ru.json
  • app/containers/RoomHeader/RoomHeader.tsx
  • app/lib/methods/helpers/formatStatusExpiry.test.ts
  • app/i18n/locales/zh-CN.json
  • app/lib/services/restApi.ts
  • app/views/RoomActionsView/styles.ts
  • app/i18n/locales/hi-IN.json
  • app/i18n/locales/sv.json
  • app/lib/methods/setUser.ts
  • app/i18n/locales/tr.json
  • app/i18n/locales/es.json
  • app/lib/methods/helpers/formatStatusExpiry.ts
  • app/i18n/locales/pt-PT.json
  • app/views/RoomInfoView/index.tsx
  • app/lib/services/connect.ts
  • app/i18n/locales/ar.json
  • app/i18n/locales/fr.json
  • app/i18n/locales/hu.json
  • app/definitions/ILoggedUser.ts
  • app/views/RoomInfoView/styles.ts
  • app/i18n/locales/ja.json
  • app/i18n/locales/fi.json
  • app/i18n/locales/it.json
  • app/i18n/locales/de.json
  • app/i18n/locales/ta-IN.json
  • app/containers/RoomHeader/index.tsx
  • app/i18n/locales/pt-BR.json
  • app/views/RoomInfoView/components/RoomInfoViewTitle.tsx
  • app/lib/methods/getUsersPresence.ts
  • app/views/StatusView/ClearAfterPicker.tsx
  • app/views/StatusView/index.tsx
  • app/views/RoomActionsView/index.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/definitions/index.ts
  • app/definitions/rest/v1/users.ts
  • app/reducers/activeUsers.ts
  • app/definitions/TStatusSource.ts
  • app/containers/RoomHeader/RoomHeader.tsx
  • app/lib/methods/helpers/formatStatusExpiry.test.ts
  • app/lib/services/restApi.ts
  • app/views/RoomActionsView/styles.ts
  • app/lib/methods/setUser.ts
  • app/lib/methods/helpers/formatStatusExpiry.ts
  • app/views/RoomInfoView/index.tsx
  • app/lib/services/connect.ts
  • app/definitions/ILoggedUser.ts
  • app/views/RoomInfoView/styles.ts
  • app/containers/RoomHeader/index.tsx
  • app/views/RoomInfoView/components/RoomInfoViewTitle.tsx
  • app/lib/methods/getUsersPresence.ts
  • app/views/StatusView/ClearAfterPicker.tsx
  • app/views/StatusView/index.tsx
  • app/views/RoomActionsView/index.tsx
app/definitions/**/*.{ts,tsx}

📄 CodeRabbit inference engine (CLAUDE.md)

Place shared TypeScript type definitions in 'app/definitions/' directory

Files:

  • app/definitions/index.ts
  • app/definitions/rest/v1/users.ts
  • app/definitions/TStatusSource.ts
  • app/definitions/ILoggedUser.ts
app/i18n/**/*.{ts,tsx,json}

📄 CodeRabbit inference engine (CLAUDE.md)

Place internationalization (i18n) configuration in 'app/i18n/' directory with support for 40+ locales and RTL

Files:

  • app/i18n/locales/sl-SI.json
  • app/i18n/locales/cs.json
  • app/i18n/locales/nn.json
  • app/i18n/locales/no.json
  • app/i18n/locales/nl.json
  • app/i18n/locales/te-IN.json
  • app/i18n/locales/en.json
  • app/i18n/locales/bn-IN.json
  • app/i18n/locales/ru.json
  • app/i18n/locales/zh-CN.json
  • app/i18n/locales/hi-IN.json
  • app/i18n/locales/sv.json
  • app/i18n/locales/tr.json
  • app/i18n/locales/es.json
  • app/i18n/locales/pt-PT.json
  • app/i18n/locales/ar.json
  • app/i18n/locales/fr.json
  • app/i18n/locales/hu.json
  • app/i18n/locales/ja.json
  • app/i18n/locales/fi.json
  • app/i18n/locales/it.json
  • app/i18n/locales/de.json
  • app/i18n/locales/ta-IN.json
  • app/i18n/locales/pt-BR.json
app/reducers/**/*.{ts,tsx}

📄 CodeRabbit inference engine (CLAUDE.md)

Place reducers in 'app/reducers/' directory

Files:

  • app/reducers/activeUsers.ts
app/containers/**/*.{ts,tsx}

📄 CodeRabbit inference engine (CLAUDE.md)

Place reusable UI components in 'app/containers/' directory

Files:

  • app/containers/RoomHeader/RoomHeader.tsx
  • app/containers/RoomHeader/index.tsx
app/lib/services/restApi.ts

📄 CodeRabbit inference engine (CLAUDE.md)

Use 'app/lib/services/restApi.ts' for HTTP requests via fetch

Files:

  • app/lib/services/restApi.ts
app/views/**/*.{ts,tsx}

📄 CodeRabbit inference engine (CLAUDE.md)

Place screen components in 'app/views/' directory

Files:

  • app/views/RoomActionsView/styles.ts
  • app/views/RoomInfoView/index.tsx
  • app/views/RoomInfoView/styles.ts
  • app/views/RoomInfoView/components/RoomInfoViewTitle.tsx
  • app/views/StatusView/ClearAfterPicker.tsx
  • app/views/StatusView/index.tsx
  • app/views/RoomActionsView/index.tsx
app/lib/services/connect.ts

📄 CodeRabbit inference engine (CLAUDE.md)

Use 'app/lib/services/connect.ts' for server connection management

Files:

  • app/lib/services/connect.ts
🧠 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/definitions/index.ts
  • app/definitions/rest/v1/users.ts
  • app/reducers/activeUsers.ts
  • app/definitions/TStatusSource.ts
  • app/containers/RoomHeader/RoomHeader.tsx
  • app/lib/methods/helpers/formatStatusExpiry.test.ts
  • app/lib/services/restApi.ts
  • app/views/RoomActionsView/styles.ts
  • app/lib/methods/setUser.ts
  • app/lib/methods/helpers/formatStatusExpiry.ts
  • app/views/RoomInfoView/index.tsx
  • app/lib/services/connect.ts
  • app/definitions/ILoggedUser.ts
  • app/views/RoomInfoView/styles.ts
  • app/containers/RoomHeader/index.tsx
  • app/views/RoomInfoView/components/RoomInfoViewTitle.tsx
  • app/lib/methods/getUsersPresence.ts
  • app/views/StatusView/ClearAfterPicker.tsx
  • app/views/StatusView/index.tsx
  • app/views/RoomActionsView/index.tsx
🔇 Additional comments (40)
app/lib/methods/getUsersPresence.ts (1)

83-89: LGTM!

app/lib/services/connect.ts (2)

243-243: LGTM!


205-206: Harden stream-user-presence payload parsing in app/lib/services/connect.ts (tuple order/version compatibility).

app/lib/services/connect.ts (lines 205-206) destructures userStatus as [, status, statusText, statusSource, statusExpiresAt], which is position-dependent and assumes the server always sends those fields in that exact order and always includes statusSource/statusExpiresAt. If the server payload differs or fields are missing on older versions, the client may map wrong values.

Add runtime guards/defaults (and/or a server-version gate) and confirm from the server-side stream-user-presence contract/tests that this tuple ordering is stable across all supported server versions.

app/containers/RoomHeader/RoomHeader.tsx (3)

12-12: LGTM!


55-55: LGTM!

Also applies to: 71-71, 93-93


152-152: LGTM!

Also applies to: 246-246

app/containers/RoomHeader/index.tsx (1)

48-48: LGTM!

Also applies to: 69-71, 84-84

app/lib/methods/helpers/formatStatusExpiry.test.ts (1)

1-41: LGTM!

app/views/RoomInfoView/index.tsx (1)

291-308: LGTM!

app/views/RoomInfoView/styles.ts (1)

102-106: LGTM!

app/views/RoomActionsView/index.tsx (1)

779-826: LGTM!

app/views/RoomActionsView/styles.ts (1)

34-39: LGTM!

app/definitions/TStatusSource.ts (1)

1-1: LGTM!

app/definitions/index.ts (1)

40-40: LGTM!

app/definitions/ILoggedUser.ts (1)

4-4: LGTM!

Also applies to: 14-17

app/definitions/rest/v1/users.ts (1)

38-38: LGTM!

app/reducers/activeUsers.ts (1)

2-2: LGTM!

Also applies to: 4-10

app/views/StatusView/ClearAfterPicker.tsx (4)

23-29: LGTM!


56-85: LGTM!


87-154: LGTM!


162-194: LGTM!

app/views/StatusView/index.tsx (4)

134-150: LGTM!


168-185: LGTM!


189-200: LGTM!


202-219: LGTM!

app/lib/methods/setUser.ts (1)

56-59: Potentially drops additional status metadata from _setUser DDP updates

  • IActiveUsers includes statusText, statusExpiresAt, and statusSource, but this handler only writes { status: ... } from ddpMessage.fields.status (lines 56-59), so those optional fields are left unset.
  • The DDP client treats ddpMessage.fields as a generic payload map, so if the server sends statusText/statusExpiresAt/statusSource in this message, they would be present in ddpMessage.fields and currently discarded.
  • Confirm whether _setUser receives those keys in ddpMessage.fields; if it does, map/spread them into _activeUsers.activeUsers[ddpMessage.id] instead of status-only assignment.
app/lib/services/restApi.ts (1)

332-339: LGTM!

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

592-597: LGTM!

app/i18n/locales/bn-IN.json (1)

798-803: LGTM!

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

857-862: LGTM!

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

794-799: LGTM!

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

867-872: LGTM!

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

516-521: LGTM!

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

746-751: LGTM!

app/i18n/locales/sl-SI.json (1)

722-727: LGTM!

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

768-773: LGTM!

app/i18n/locales/ta-IN.json (1)

798-803: LGTM!

app/i18n/locales/te-IN.json (1)

797-802: LGTM!

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

608-613: LGTM!

app/i18n/locales/zh-CN.json (1)

581-586: LGTM!

Comment thread app/i18n/locales/en.json Outdated
Comment thread app/i18n/locales/nn.json Outdated
Comment thread app/lib/services/connect.ts Outdated
Comment thread app/views/RoomInfoView/components/RoomInfoViewTitle.tsx Outdated
@github-actions

Copy link
Copy Markdown

iOS Build Available

Rocket.Chat 4.74.0.109139

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.

Image

Comment thread app/lib/methods/helpers/helpers.ts
@github-actions

Copy link
Copy Markdown

@github-actions

Copy link
Copy Markdown

iOS Build Available

Rocket.Chat 4.74.0.109143

@github-actions

Copy link
Copy Markdown

iOS Build Available

Rocket.Chat 4.74.0.109157

@github-actions

Copy link
Copy Markdown

Comment thread app/containers/RoomHeader/RoomHeader.tsx Outdated
Comment thread app/containers/RoomHeader/RoomHeader.tsx
Comment thread app/containers/Status/StatusRows.tsx Outdated
Comment thread app/views/RoomActionsView/index.tsx Outdated
@github-actions

Copy link
Copy Markdown

@github-actions

Copy link
Copy Markdown

iOS Build Available

Rocket.Chat 4.74.0.109162

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

The more I look at this file, more I find new functions and components 🙈
Organize the 🍝 and the code is going to look better

(state: IApplicationState) => state.settings.Accounts_AllowInvisibleStatusOption
);
const serverVersion = useSelector((state: IApplicationState) => state.server.version);
const supportsStatusExpiry = compareServerVersion(serverVersion, 'greaterThanOrEqualTo', '8.6.0');

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

You're running this on every re-render

Comment thread app/views/StatusView/index.tsx Outdated
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.

2 participants