Skip to content

fix: validate username on Profile screen#7425

Open
Shevilll wants to merge 3 commits into
RocketChat:developfrom
Shevilll:fix/username-validation-1682
Open

fix: validate username on Profile screen#7425
Shevilll wants to merge 3 commits into
RocketChat:developfrom
Shevilll:fix/username-validation-1682

Conversation

@Shevilll

@Shevilll Shevilll commented Jun 22, 2026

Copy link
Copy Markdown

Description

On the Profile screen, the username field had no character validation: an invalid username (e.g. containing spaces or disallowed characters) left the Save button enabled and only failed afterwards with a generic server error.

This adds client-side validation that mirrors the server's validateUsername logic and honors the UTF8_User_Names_Validation setting, so the Save button is disabled and an inline error is shown for invalid input.

Changes

  • Validate the username in the Profile form schema using the server-configured UTF8_User_Names_Validation pattern, with the standard default fallback ([0-9a-zA-Z-_.]+) and a guard for an invalid server-supplied regex.
  • Add the Username_invalid i18n string (English source).
  • Tests covering invalid input (blocks save + shows the message) and corrected valid input (saves successfully).

Closes #1682

Summary by CodeRabbit

  • New Features
    • Added client-side username validation to the profile form, using localized feedback (“Username invalid”) and the server’s username rules where available.
  • Tests
    • Updated/added profile view tests to ensure invalid usernames are blocked (no save) and valid usernames are saved after correction.

Validate the username field against the server's UTF8_User_Names_Validation
setting (with the default pattern as a fallback) before submitting the profile.
This prevents invalid characters such as spaces from reaching the server, which
previously surfaced only as a generic save error, and shows an inline message
explaining the allowed characters.

Closes RocketChat#1682
@coderabbitai

coderabbitai Bot commented Jun 22, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: b2d9b07a-4699-40ef-8c98-a79e274b6e96

📥 Commits

Reviewing files that changed from the base of the PR and between d3f11f8 and 193cf24.

📒 Files selected for processing (1)
  • app/views/ProfileView/index.tsx
🚧 Files skipped from review as they are similar to previous changes (1)
  • app/views/ProfileView/index.tsx

Walkthrough

Adds client-side username validation to ProfileView. A default regex and an isValidUsername helper are introduced, using a server-provided pattern from UTF8_User_Names_Validation in Redux settings with a fallback. The yup schema for username gains a .test() constraint, a new Username_invalid i18n string is added, and two tests verify the validation behavior.

Changes

Username Validation in ProfileView

Layer / File(s) Summary
Validation helper, i18n string, Redux wiring, and yup schema
app/views/ProfileView/index.tsx, app/i18n/locales/en.json
Defines a default username regex constant and isValidUsername helper that applies the server-provided pattern (from UTF8_User_Names_Validation in Redux settings) as an anchored regex with fallback to the default. Reads the setting via useAppSelector, extends the username yup field with a .test(...) constraint calling isValidUsername, and adds the Username_invalid translation key.
ProfileView username validation tests
app/views/ProfileView/index.test.tsx
Adds I18n import, injects UTF8_User_Names_Validation into mocked Redux settings, and adds two test cases: one asserting that a space-containing username blocks saveUserProfile and displays the i18n error message, and one asserting that a valid username triggers saveUserProfile with the correct payload.

Estimated code review effort

🎯 2 (Simple) | ⏱️ ~10 minutes

Suggested labels

type: bug

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly and concisely describes the main change: username validation on the Profile screen.
Linked Issues check ✅ Passed The PR adds client-side username validation, a clear invalid-username message, and tests, matching issue #1682's requirements.
Out of Scope Changes check ✅ Passed The changes stay focused on Profile username validation, the related i18n string, and tests with no obvious unrelated additions.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.

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

Warning

Review ran into problems

🔥 Problems

Errors were encountered while retrieving linked issues.

Errors (1)
  • VALIDATION-1682: 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.

@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.

🧹 Nitpick comments (1)
app/views/ProfileView/index.tsx (1)

54-61: 🧹 Nitpick | 🔵 Trivial | ⚡ Quick win

Add an explicit return type to isValidUsername.

Please annotate the helper with : boolean to satisfy the TS guideline for explicit function return types.

Suggested change
-const isValidUsername = (username: string, pattern: string) => {
+const isValidUsername = (username: string, pattern: string): boolean => {

As per coding guidelines, **/*.{ts,tsx} requires explicit type annotations for function parameters and return types.

🤖 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/ProfileView/index.tsx` around lines 54 - 61, The isValidUsername
function is missing an explicit return type annotation required by TypeScript
coding guidelines. Add `: boolean` to the function signature after the closing
parenthesis of the parameters list and before the opening curly brace to
explicitly declare that this function returns a boolean value. This satisfies
the TypeScript guideline that all functions in .ts and .tsx files must have
explicit return type annotations.

Source: Coding guidelines

🤖 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.

Nitpick comments:
In `@app/views/ProfileView/index.tsx`:
- Around line 54-61: The isValidUsername function is missing an explicit return
type annotation required by TypeScript coding guidelines. Add `: boolean` to the
function signature after the closing parenthesis of the parameters list and
before the opening curly brace to explicitly declare that this function returns
a boolean value. This satisfies the TypeScript guideline that all functions in
.ts and .tsx files must have explicit return type annotations.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 730fbeb5-6860-43c2-b623-a5ecd948b0aa

📥 Commits

Reviewing files that changed from the base of the PR and between 6bbf88a and 63a00c1.

📒 Files selected for processing (3)
  • app/i18n/locales/en.json
  • app/views/ProfileView/index.test.tsx
  • app/views/ProfileView/index.tsx
📜 Review details
🧰 Additional context used
📓 Path-based instructions (6)
**/*.{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/i18n/locales/en.json
  • app/views/ProfileView/index.test.tsx
  • app/views/ProfileView/index.tsx
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/en.json
**/*.{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/views/ProfileView/index.test.tsx
  • app/views/ProfileView/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/views/ProfileView/index.test.tsx
  • app/views/ProfileView/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/views/ProfileView/index.test.tsx
  • app/views/ProfileView/index.tsx
app/views/**/*.{ts,tsx}

📄 CodeRabbit inference engine (CLAUDE.md)

Place screen components in 'app/views/' directory

Files:

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

Applied to files:

  • app/views/ProfileView/index.test.tsx
  • app/views/ProfileView/index.tsx
🔇 Additional comments (3)
app/i18n/locales/en.json (1)

997-997: LGTM!

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

78-78: LGTM!

Also applies to: 91-103

app/views/ProfileView/index.test.tsx (1)

12-12: LGTM!

Also applies to: 74-75, 116-136

@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.

Caution

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

⚠️ Outside diff range comments (1)
app/views/ProfileView/index.tsx (1)

71-104: 🎯 Functional Correctness | 🔴 Critical | ⚡ Quick win

Remove the duplicate isMasterDetail binding (build/lint blocker).

isMasterDetail is destructured at Line 80 and then redeclared at Line 104, which triggers no-redeclare and breaks compilation.
Also, the selector object (Lines 83-94) does not provide an isMasterDetail key, so that destructured field is stale.

💡 Suggested fix
 	const {
 		Accounts_AllowDeleteOwnAccount,
 		Accounts_AllowEmailChange,
 		Accounts_AllowPasswordChange,
 		Accounts_AllowRealNameChange,
 		Accounts_AllowUserAvatarChange,
 		Accounts_AllowUsernameChange,
 		Accounts_CustomFields,
 		UTF8_User_Names_Validation,
-		isMasterDetail,
 		serverVersion,
 		user
 	} = useAppSelector(state => ({
 		user: getUserSelector(state),

As per coding guidelines, **/*.{js,jsx,ts,tsx} must enforce ESLint rules from @rocket.chat/eslint-config.

🤖 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/ProfileView/index.tsx` around lines 71 - 104, The ProfileView
component has a duplicate isMasterDetail binding that causes a no-redeclare
lint/build failure. In app/views/ProfileView/index.tsx, remove the stale
isMasterDetail from the useAppSelector destructuring since that selector does
not return it, and keep only the useMasterDetail() assignment (or otherwise use
a single source of truth). Verify the ProfileView function/component scope has
just one isMasterDetail declaration.

Sources: Coding guidelines, Linters/SAST tools

🤖 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.

Outside diff comments:
In `@app/views/ProfileView/index.tsx`:
- Around line 71-104: The ProfileView component has a duplicate isMasterDetail
binding that causes a no-redeclare lint/build failure. In
app/views/ProfileView/index.tsx, remove the stale isMasterDetail from the
useAppSelector destructuring since that selector does not return it, and keep
only the useMasterDetail() assignment (or otherwise use a single source of
truth). Verify the ProfileView function/component scope has just one
isMasterDetail declaration.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 2e733dac-fdb3-4777-a17b-49ba8d63d707

📥 Commits

Reviewing files that changed from the base of the PR and between 63a00c1 and d3f11f8.

📒 Files selected for processing (3)
  • app/i18n/locales/en.json
  • app/views/ProfileView/index.test.tsx
  • app/views/ProfileView/index.tsx
💤 Files with no reviewable changes (1)
  • app/views/ProfileView/index.test.tsx
✅ Files skipped from review due to trivial changes (1)
  • app/i18n/locales/en.json
📜 Review details
🧰 Additional context used
📓 Path-based instructions (5)
**/*.{js,ts,jsx,tsx}

📄 CodeRabbit inference engine (AGENTS.md)

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

Files:

  • app/views/ProfileView/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/views/ProfileView/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/views/ProfileView/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/views/ProfileView/index.tsx
app/views/**/*.{ts,tsx}

📄 CodeRabbit inference engine (CLAUDE.md)

Place screen components in 'app/views/' directory

Files:

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

Applied to files:

  • app/views/ProfileView/index.tsx
🪛 ESLint
app/views/ProfileView/index.tsx

[error] 104-104: 'isMasterDetail' is already defined.

(no-redeclare)

Drop the stale isMasterDetail destructured from useAppSelector (the
selector no longer returns it) so only the useMasterDetail() hook
declares it, fixing the no-redeclare lint/build failure introduced by
merging develop. Also annotate isValidUsername with an explicit boolean
return type per the TS guideline.
@Shevilll

Copy link
Copy Markdown
Author

Hi team! I've resolved the compilation/lint blocker on ProfileView by removing the redundant isMasterDetail destructuring, and added an explicit typescript return annotation. All CI checks are green. Please let me know if you have any feedback or if this is ready to merge! CC @diegolmello

@Shevilll

Copy link
Copy Markdown
Author

Hi @Shevilll,

This is a great feature! Correctly mirroring the server's username validation (UTF8_User_Names_Validation) on the Profile screen is an excellent way to prevent unnecessary API roundtrips and improve user experience with instant validation feedback.

I have one key suggestion regarding React performance and Yup schema reference stability:

Cache the Yup Validation Schema with useMemo

Currently, the validationSchema is declared directly inside the ProfileView component, which means the Yup schema object is reconstructed on every single render (e.g. whenever any form field changes or the user types):

const validationSchema = yup.object().shape({
	...
});

Since the schema depends dynamically on UTF8_User_Names_Validation (which comes from Redux), we can cache this schema using useMemo to ensure reference stability:

const validationSchema = useMemo(() => {
	return yup.object().shape({
		name: yup.string().required(I18n.t('Name_required')),
		email: yup.string().email(I18n.t('Email_must_be_a_valid_email')).required(I18n.t('Email_required')),
		username: yup
			.string()
			.required(I18n.t('Username_required'))
			.test('valid-username', I18n.t('Username_invalid'), value => isValidUsername(value ?? '', UTF8_User_Names_Validation))
	});
}, [UTF8_User_Names_Validation]);

This ensures that the validation schema object is only re-created when the server's validation setting actually changes, which optimizes form performance.

Other than this minor optimization, the implementation and tests are flawless! Nice work! 👍

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.

username change: no invalid chars should be allowed as input, improve error message

1 participant