Skip to content
Open
Changes from 1 commit
Commits
Show all changes
28 commits
Select commit Hold shift + click to select a range
ea514b7
Search member in room using API
Rohit3523 Jan 21, 2026
fd7e0fe
Guard against out‑of‑order search responses
Rohit3523 Jan 21, 2026
ad736f7
Lint fix
Rohit3523 Jan 21, 2026
a3aeda5
chore: format code and fix lint issues [skip ci]
Rohit3523 Jan 21, 2026
3820bd7
merge search result
Rohit3523 Jan 22, 2026
f2edc75
use debounce hook
Rohit3523 Jan 23, 2026
ae0fbbc
Merge branch 'develop' into search-filter-fix
Rohit3523 Jan 23, 2026
d4839a8
Prevent stale member updates when the filter changes or clears
Rohit3523 Jan 23, 2026
cca7ef9
Merge branch 'search-filter-fix' of https://github.com/RocketChat/Roc…
Rohit3523 Jan 23, 2026
1492bec
Use members from state
Rohit3523 Jan 23, 2026
55cfd8b
Added E2E test for search member
Rohit3523 Jan 23, 2026
4f36a02
combine filter text with status filter
Rohit3523 Jan 23, 2026
cc8f8b4
test update
Rohit3523 Jan 23, 2026
cd9210a
Test update
Rohit3523 Jan 24, 2026
831d6d0
status filter is not using search text
Rohit3523 Jan 24, 2026
a0a2ec8
chore: format code and fix lint issues [skip ci]
Rohit3523 Jan 24, 2026
8bcb82c
Added state room in effect
Rohit3523 Jan 26, 2026
47da4ec
chore: code improvements
OtavioStasiak Jan 26, 2026
d852979
chore: format code and fix lint issues [skip ci]
OtavioStasiak Jan 26, 2026
8a0316b
chore: code improvements
OtavioStasiak Jan 26, 2026
98f0fad
Merge branch 'develop' into search-filter-fix
OtavioStasiak Jan 26, 2026
2cf2053
chore: format code and fix lint issues [skip ci]
OtavioStasiak Jan 26, 2026
806e79e
fix: remove duplicated fetch roles and remove members from state of u…
OtavioStasiak Jan 27, 2026
f207cb7
fix: remove duplicated useEffect and use primitive values in fetchRol…
OtavioStasiak Jan 27, 2026
33265f0
chore: format code and fix lint issues [skip ci]
OtavioStasiak Jan 27, 2026
503bc9f
fix: remove array from useEffect dependencies
OtavioStasiak Jan 27, 2026
2fab0a3
Merge branch 'develop' into search-filter-fix
Rohit3523 Feb 4, 2026
2d89185
Merge branch 'develop' into search-filter-fix
Rohit3523 Feb 5, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
71 changes: 62 additions & 9 deletions app/views/RoomMembersView/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@
import I18n from '../../i18n';
import { useAppSelector } from '../../lib/hooks/useAppSelector';
import { usePermissions } from '../../lib/hooks/usePermissions';
import { compareServerVersion, getRoomTitle, isGroupChat } from '../../lib/methods/helpers';
import { compareServerVersion, debounce, getRoomTitle, isGroupChat } from '../../lib/methods/helpers';
import { handleIgnore } from '../../lib/methods/helpers/handleIgnore';
import { showConfirmationAlert } from '../../lib/methods/helpers/info';
import log from '../../lib/methods/helpers/log';
Expand All @@ -41,7 +41,7 @@
type TRoomType
} from './helpers';
import styles from './styles';
import { sanitizeLikeString } from '../../lib/database/utils';

Check failure on line 44 in app/views/RoomMembersView/index.tsx

View workflow job for this annotation

GitHub Actions / format

'sanitizeLikeString' is defined but never used

Check failure on line 44 in app/views/RoomMembersView/index.tsx

View workflow job for this annotation

GitHub Actions / ESLint and Test / run-eslint-and-test

'sanitizeLikeString' is defined but never used

const PAGE_SIZE = 25;

Expand Down Expand Up @@ -367,10 +367,21 @@
limit: PAGE_SIZE,
allUsers: !status
});

const end = membersResult?.length < PAGE_SIZE;
const membersResultFiltered = membersResult?.filter((member: TUserModel) => !members.some(m => m._id === member._id));

let newMembers: TUserModel[];
if (filter) {
newMembers = membersResult || [];
} else {
const membersResultFiltered = membersResult?.filter((member: TUserModel) =>
!members.some(m => m._id === member._id)
);
newMembers = [...members, ...membersResultFiltered];
}

updateState({
members: [...members, ...membersResultFiltered],
members: newMembers,
isLoading: false,
end,
page: page + 1
Expand All @@ -381,11 +392,53 @@
}
};

const filter = sanitizeLikeString(state.filter.toLowerCase()) || '';
const filteredMembers =
state.members && state.members.length > 0 && state.filter
? state.members.filter(m => m.username.toLowerCase().match(filter) || m.name?.toLowerCase().match(filter))
: null;
const fetchMembersWithNewFilter = async (searchFilter: string) => {
Copy link
Contributor

Choose a reason for hiding this comment

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

We have two functions that do almost the same thing... can't we unify it?
Example:

	const loadMembers = async ({
		filterParam,
		allUsersParam,
		reset = false
	}: {
		filterParam?: string;
		allUsersParam?: boolean;
		reset?: boolean;
	} = {}) => {
		const { members, isLoading, end, room, filter, page, allUsers } = state;
		
		// Determine values to use (param override -> state -> default)
		const currentFilter = filterParam !== undefined ? filterParam : filter;
		const currentAllUsers = allUsersParam !== undefined ? allUsersParam : allUsers;

		// Prevent fetching if loading or reached end (unless it's a reset/new search)
		if (!reset && (isLoading || end)) {
			return;
		}

		const requestId = ++latestSearchRequest.current;

		// Optimistic State Updates
		if (reset) {
			updateState({
				isLoading: true,
				members: [],
				end: false,
				filter: currentFilter,
				allUsers: currentAllUsers,
				page: 0
			});
		} else {
			updateState({ isLoading: true });
		}

		try {
			const membersResult = await getRoomMembers({
				rid: room.rid,
				roomType: room.t,
				type: !currentAllUsers ? 'all' : 'online',
				filter: currentFilter,
				skip: reset ? 0 : PAGE_SIZE * page,
				limit: PAGE_SIZE,
				allUsers: !currentAllUsers
			});

			// Race Condition Check
			if (requestId !== latestSearchRequest.current) {
				return;
			}

			const isEnd = (membersResult?.length ?? 0) < PAGE_SIZE;
			let newMembers = membersResult || [];

			// If appending (not resetting), filter out duplicates
			if (!reset) {
				const filteredNewMembers = newMembers.filter(member => !members.some(m => m._id === member._id));
				newMembers = [...members, ...filteredNewMembers];
			}

			updateState({
				members: newMembers,
				isLoading: false,
				end: isEnd,
				page: reset ? 1 : page + 1
			});
		} catch (e) {
			log(e);
			if (requestId === latestSearchRequest.current) {
				updateState({ isLoading: false });
			}
		}
	};

const { room, allUsers } = state;
const { t } = room;

updateState({ isLoading: true });
try {
const membersResult = await getRoomMembers({
rid: room.rid,
roomType: t,
type: !allUsers ? 'all' : 'online',
filter: searchFilter,
skip: 0,
limit: PAGE_SIZE,
allUsers: !allUsers
});

const end = membersResult?.length < PAGE_SIZE;

updateState({
members: membersResult || [],
isLoading: false,
end,
page: 1
});
} catch (e) {
log(e);
updateState({ isLoading: false });
}
};

const handleFilterChange = (text: string) => {
const trimmedFilter = text.trim();

updateState({
filter: trimmedFilter,
page: 0,
members: [],
end: false
});

if (trimmedFilter.length > 0) {
fetchMembersWithNewFilter(trimmedFilter);
}
};

const debounceFilterChange = debounce(handleFilterChange, 500);
const filteredMembers = state.members.length > 0 ? state.members : null;

return (
<SafeAreaView testID='room-members-view'>
Expand All @@ -412,7 +465,7 @@
t={state.room.t}
abacAttributes={state.room.abacAttributes}
/>
<SearchBox onChangeText={text => updateState({ filter: text.trim() })} testID='room-members-view-search' />
<SearchBox onChangeText={text => debounceFilterChange(text)} testID='room-members-view-search' />
</>
}
ListFooterComponent={() => (state.isLoading ? <ActivityIndicator /> : null)}
Expand Down
Loading