From 9560eac07dc524f3a9919ba40b1c0fa89dd38ce0 Mon Sep 17 00:00:00 2001 From: swarnendusahu10-dotcom Date: Tue, 27 Oct 2026 14:56:17 +0530 Subject: [PATCH] fix(members): fetch up to 500 members and show correct total in sidebar MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit getChannelMembers() called /channels.members (and /groups.members) without a count param, so Rocket.Chat defaulted to returning 100 results. Rooms with more than 100 members were silently truncated. RoomMember.js displayed 'Showing N of N' using filteredMembers.length for both values, so the total was always the same as the visible count — never reflecting the actual room member count. Fixes: - Add count=500 to getChannelMembers() to fetch up to 500 members - Import useChannelStore in RoomMember.js - Use channelInfo.usersCount as the authoritative total in 'Showing X of Y' - Fallback to members.length when channelInfo not yet loaded Fixes #1286 --- packages/api/src/EmbeddedChatApi.ts | 76 +++++++++---------- .../react/src/views/RoomMembers/RoomMember.js | 9 ++- 2 files changed, 44 insertions(+), 41 deletions(-) diff --git a/packages/api/src/EmbeddedChatApi.ts b/packages/api/src/EmbeddedChatApi.ts index 6f2408414e..27391e15be 100644 --- a/packages/api/src/EmbeddedChatApi.ts +++ b/packages/api/src/EmbeddedChatApi.ts @@ -27,7 +27,7 @@ export default class EmbeddedChatApi { constructor( host: string, rid: string, - { getToken, saveToken, deleteToken }: IRocketChatAuthOptions + { getToken, saveToken, deleteToken }: IRocketChatAuthOptions, ) { this.host = host; this.rid = rid; @@ -73,7 +73,7 @@ export default class EmbeddedChatApi { private async _restRequest( endpoint: string, method: "GET" | "POST" | "PUT" | "DELETE" = "GET", - body?: any + body?: any, ) { const options: RequestInit = {}; if (body !== undefined) { @@ -151,7 +151,7 @@ export default class EmbeddedChatApi { if (!response.data.me.username) { await this.updateUserUsername( response.data.userId, - response.data.me.name + response.data.me.name, ); } return { status: response.status, me: response.data.me }; @@ -220,7 +220,7 @@ export default class EmbeddedChatApi { } catch (error) { console.error( "Auto-login failed:", - error instanceof Error ? error.message : error + error instanceof Error ? error.message : error, ); } } @@ -294,7 +294,7 @@ export default class EmbeddedChatApi { if (message) { this.onMessageCallbacks.forEach((callback) => callback(message)); } - } + }, ); this._activeSubscriptions.push(roomMsgSub); @@ -312,7 +312,7 @@ export default class EmbeddedChatApi { // Legacy "typing" event: args[1] is a boolean this.handleTypingEvent({ typingUser, isTyping: !!activities }); } - } + }, ); this._activeSubscriptions.push(notifyRoomSub); @@ -324,10 +324,10 @@ export default class EmbeddedChatApi { const messageId = args[0]?._id; if (messageId) { this.onMessageDeleteCallbacks.forEach((callback) => - callback(messageId) + callback(messageId), ); } - } + }, ); this._activeSubscriptions.push(deleteRoomSub); @@ -346,7 +346,7 @@ export default class EmbeddedChatApi { message.renderType = "blocks"; this.onMessageCallbacks.forEach((callback) => callback(message)); } - } + }, ); this._activeSubscriptions.push(notifyUserMsgSub); @@ -355,7 +355,7 @@ export default class EmbeddedChatApi { [`${userId}/uiInteraction`, false], (data: any) => { this.onUiInteractionCallbacks.forEach((callback) => callback(data)); - } + }, ); this._activeSubscriptions.push(notifyUserUiSub); } @@ -375,7 +375,7 @@ export default class EmbeddedChatApi { async removeMessageListener(callback: (message: any) => void) { this.onMessageCallbacks = this.onMessageCallbacks.filter( - (c) => c !== callback + (c) => c !== callback, ); } @@ -390,7 +390,7 @@ export default class EmbeddedChatApi { async removeMessageDeleteListener(callback: (messageId: string) => void) { this.onMessageDeleteCallbacks = this.onMessageDeleteCallbacks.filter( - (c) => c !== callback + (c) => c !== callback, ); } @@ -405,13 +405,13 @@ export default class EmbeddedChatApi { async removeTypingStatusListener(callback: (users: string[]) => void) { this.onTypingStatusCallbacks = this.onTypingStatusCallbacks.filter( - (c) => c !== callback + (c) => c !== callback, ); } async addActionTriggeredListener(callback: (data: any) => void) { const idx = this.onActionTriggeredCallbacks.findIndex( - (c) => c === callback + (c) => c === callback, ); if (idx !== -1) { this.onActionTriggeredCallbacks[idx] = callback; @@ -422,7 +422,7 @@ export default class EmbeddedChatApi { async removeActionTriggeredListener(callback: (data: any) => void) { this.onActionTriggeredCallbacks = this.onActionTriggeredCallbacks.filter( - (c) => c !== callback + (c) => c !== callback, ); } @@ -437,7 +437,7 @@ export default class EmbeddedChatApi { async removeUiInteractionListener(callback: (data: any) => void) { this.onUiInteractionCallbacks = this.onUiInteractionCallbacks.filter( - (c) => c !== callback + (c) => c !== callback, ); } @@ -466,14 +466,14 @@ export default class EmbeddedChatApi { typingHandlerLock = 0; const newTypingStatus = cloneArray(this.typingUsers); this.onTypingStatusCallbacks.forEach((callback) => - callback(newTypingStatus) + callback(newTypingStatus), ); } async getRCAppInfo() { try { const response = await fetch( - `${this.host}/api/apps/public/${ROCKETCHAT_APP_ID}/info` + `${this.host}/api/apps/public/${ROCKETCHAT_APP_ID}/info`, ); if (!response.ok) { @@ -489,7 +489,7 @@ export default class EmbeddedChatApi { async updateUserNameThroughSuggestion(userid: string) { try { const suggestedUsername = await this._restRequest( - "/v1/users.getUsernameSuggestion" + "/v1/users.getUsernameSuggestion", ); if (suggestedUsername.success) { return await this._restRequest("/v1/users.update", "POST", { @@ -564,7 +564,7 @@ export default class EmbeddedChatApi { latest?: string; count?: number; } = {}, - isChannelPrivate = false + isChannelPrivate = false, ) { const roomType = isChannelPrivate ? "groups" : "channels"; const endp = anonymousMode ? "anonymousread" : "messages"; @@ -573,7 +573,7 @@ export default class EmbeddedChatApi { const count = options?.count != null ? `&count=${options.count}` : ""; try { return await this._restRequest( - `/v1/${roomType}.${endp}?roomId=${this.rid}${oldest}${latest}${count}` + `/v1/${roomType}.${endp}?roomId=${this.rid}${oldest}${latest}${count}`, ); } catch (err: any) { console.error(err instanceof Error ? err.message : err); @@ -589,7 +589,7 @@ export default class EmbeddedChatApi { count?: number; offset?: number; } = {}, - isChannelPrivate = false + isChannelPrivate = false, ) { const roomType = isChannelPrivate ? "groups" : "channels"; const endp = anonymousMode ? "anonymousread" : "messages"; @@ -599,7 +599,7 @@ export default class EmbeddedChatApi { const offset = options?.offset ? `&offset=${options.offset}` : "&offset=0"; try { return await this._restRequest( - `/v1/${roomType}.${endp}?roomId=${this.rid}${oldest}${latest}${count}${offset}` + `/v1/${roomType}.${endp}?roomId=${this.rid}${oldest}${latest}${count}${offset}`, ); } catch (err: any) { console.error(err instanceof Error ? err.message : String(err)); @@ -609,7 +609,7 @@ export default class EmbeddedChatApi { async getThreadMessages( tmid: string, - isChannelPrivate = false + isChannelPrivate = false, ): Promise { try { return await this._restRequest(`/v1/chat.getThreadMessages?tmid=${tmid}`); @@ -623,7 +623,7 @@ export default class EmbeddedChatApi { const roomType = isChannelPrivate ? "groups" : "channels"; try { return await this._restRequest( - `/v1/${roomType}.roles?roomId=${this.rid}` + `/v1/${roomType}.roles?roomId=${this.rid}`, ); } catch (err: any) { console.error(err instanceof Error ? err.message : String(err)); @@ -659,7 +659,7 @@ export default class EmbeddedChatApi { "stream-notify-room", `${this.rid}/user-activity`, username, - typing ? ["user-typing"] : [] + typing ? ["user-typing"] : [], ); } catch (err) { // DDP typing indicator fails when connection is temporarily down — expected, safe to ignore @@ -761,7 +761,7 @@ export default class EmbeddedChatApi { async getStarredMessages(): Promise { try { return await this._restRequest( - `/v1/chat.getStarredMessages?roomId=${this.rid}` + `/v1/chat.getStarredMessages?roomId=${this.rid}`, ); } catch (err: any) { console.error(err instanceof Error ? err.message : err); @@ -772,7 +772,7 @@ export default class EmbeddedChatApi { async getPinnedMessages(): Promise { try { return await this._restRequest( - `/v1/chat.getPinnedMessages?roomId=${this.rid}` + `/v1/chat.getPinnedMessages?roomId=${this.rid}`, ); } catch (err: any) { console.error(err instanceof Error ? err.message : err); @@ -783,7 +783,7 @@ export default class EmbeddedChatApi { async getMentionedMessages(): Promise { try { return await this._restRequest( - `/v1/chat.getMentionedMessages?roomId=${this.rid}` + `/v1/chat.getMentionedMessages?roomId=${this.rid}`, ); } catch (err: any) { console.error(err instanceof Error ? err.message : err); @@ -815,7 +815,7 @@ export default class EmbeddedChatApi { async reactToMessage( emoji: string, messageId: string, - shouldReact: boolean | string + shouldReact: boolean | string, ): Promise { try { return await this._restRequest("/v1/chat.react", "POST", { @@ -861,7 +861,7 @@ export default class EmbeddedChatApi { file: File, fileName: string, fileDescription = "", - threadId = undefined + threadId = undefined, ) { try { const currentUser = await this.auth.getCurrentUser(); @@ -875,7 +875,7 @@ export default class EmbeddedChatApi { const uploadResult = await this._restUpload( `/v1/rooms.media/${this.rid}`, - form + form, ); if (!uploadResult.success || !uploadResult.file?._id) { @@ -888,7 +888,7 @@ export default class EmbeddedChatApi { "POST", threadId ? { msg: "", description: fileDescription || "", tmid: threadId } - : { msg: "", description: fileDescription || "" } + : { msg: "", description: fileDescription || "" }, ); } catch (err) { console.error("sendAttachment error:", err); @@ -908,7 +908,7 @@ export default class EmbeddedChatApi { const roomType = isChannelPrivate ? "groups" : "channels"; try { return await this._restRequest( - `/v1/${roomType}.members?roomId=${this.rid}` + `/v1/${roomType}.members?roomId=${this.rid}&count=500`, ); } catch (err: any) { console.error(err instanceof Error ? err.message : err); @@ -920,8 +920,8 @@ export default class EmbeddedChatApi { try { return await this._restRequest( `/v1/chat.search?roomId=${this.rid}&searchText=${encodeURIComponent( - text - )}` + text, + )}`, ); } catch (err: any) { console.error(err instanceof Error ? err.message : err); @@ -944,7 +944,7 @@ export default class EmbeddedChatApi { const interaction = await this._restRequest( `/apps/ui.interaction/${appId}`, "POST", - { triggerId, ...userInteraction } + { triggerId, ...userInteraction }, ); this.onActionTriggeredCallbacks.forEach((cb) => cb(interaction)); return interaction; @@ -985,7 +985,7 @@ export default class EmbeddedChatApi { async userData(username: string): Promise { return await this._restRequest( - `/v1/users.info?username=${encodeURIComponent(username)}` + `/v1/users.info?username=${encodeURIComponent(username)}`, ); } } diff --git a/packages/react/src/views/RoomMembers/RoomMember.js b/packages/react/src/views/RoomMembers/RoomMember.js index 27a97278fc..54df4df77c 100644 --- a/packages/react/src/views/RoomMembers/RoomMember.js +++ b/packages/react/src/views/RoomMembers/RoomMember.js @@ -16,6 +16,7 @@ import { css } from '@emotion/react'; import RoomMemberItem from './RoomMemberItem'; import RCContext, { useRCContext } from '../../context/RCInstance'; import useInviteStore from '../../store/inviteStore'; +import useChannelStore from '../../store/channelStore'; import InviteMembers from './InviteMembers'; import { getRoomMemberStyles } from './RoomMembers.styles'; import LoadingIndicator from '../MessageAggregators/common/LoadingIndicator'; @@ -30,6 +31,7 @@ const RoomMembers = ({ members }) => { const toggleInviteView = useInviteStore((state) => state.toggleInviteView); const showInvite = useInviteStore((state) => state.showInvite); + const channelInfo = useChannelStore((state) => state.channelInfo); const [isLoading, setIsLoading] = useState(true); const { variantOverrides } = useComponentOverrides('RoomMember'); const viewType = variantOverrides.viewType || 'Sidebar'; @@ -68,8 +70,8 @@ const RoomMembers = ({ members }) => { filtered.filter( (member) => member.name?.toLowerCase().includes(searchTerm.toLowerCase()) || - member.username?.toLowerCase().includes(searchTerm.toLowerCase()) - ) + member.username?.toLowerCase().includes(searchTerm.toLowerCase()), + ), ); }, [viewStatus, searchTerm, members]); @@ -163,7 +165,8 @@ const RoomMembers = ({ members }) => { - Showing {displayedMembers} of {displayedMembers} + Showing {displayedMembers} of{' '} + {channelInfo?.usersCount ?? members.length} {filteredMembers.length > 0 ? (