diff --git a/src/components/views/groups/GroupMemberList.js b/src/components/views/groups/GroupMemberList.js
index 38c679a5b5..9b96fa33a9 100644
--- a/src/components/views/groups/GroupMemberList.js
+++ b/src/components/views/groups/GroupMemberList.js
@@ -32,7 +32,9 @@ export default React.createClass({
getInitialState: function() {
return {
members: null,
+ membersError: null,
invitedMembers: null,
+ invitedMembersError: null,
truncateAt: INITIAL_LOAD_NUM_MEMBERS,
};
},
@@ -50,6 +52,19 @@ export default React.createClass({
GroupStore.registerListener(groupId, () => {
this._fetchMembers();
});
+ GroupStore.on('error', (err, errorGroupId, stateKey) => {
+ if (this._unmounted || groupId !== errorGroupId) return;
+ if (stateKey === GroupStore.STATE_KEY.GroupMembers) {
+ this.setState({
+ membersError: err,
+ });
+ }
+ if (stateKey === GroupStore.STATE_KEY.GroupInvitedMembers) {
+ this.setState({
+ invitedMembersError: err,
+ });
+ }
+ });
},
_fetchMembers: function() {
@@ -83,7 +98,11 @@ export default React.createClass({
this.setState({ searchQuery: ev.target.value });
},
- makeGroupMemberTiles: function(query, memberList) {
+ makeGroupMemberTiles: function(query, memberList, memberListError) {
+ if (memberListError) {
+ return
- { this.makeGroupMemberTiles(this.state.searchQuery, this.state.members) }
+ {
+ this.makeGroupMemberTiles(
+ this.state.searchQuery,
+ this.state.members,
+ this.state.membersError,
+ )
+ }
:
{ inputBox }
diff --git a/src/i18n/strings/en_EN.json b/src/i18n/strings/en_EN.json
index f9980f5645..8e25f96b64 100644
--- a/src/i18n/strings/en_EN.json
+++ b/src/i18n/strings/en_EN.json
@@ -1107,6 +1107,7 @@
"Community %(groupId)s not found": "Community %(groupId)s not found",
"This Home server does not support communities": "This Home server does not support communities",
"Failed to load %(groupId)s": "Failed to load %(groupId)s",
+ "Failed to load group members": "Failed to load group members",
"Couldn't load home page": "Couldn't load home page",
"You are currently using Riot anonymously as a guest.": "You are currently using Riot anonymously as a guest.",
"If you would like to create a Matrix account you can
register now.": "If you would like to create a Matrix account you can
register now.",
diff --git a/src/i18n/strings/en_US.json b/src/i18n/strings/en_US.json
index aa7140aaa8..b96a49eac7 100644
--- a/src/i18n/strings/en_US.json
+++ b/src/i18n/strings/en_US.json
@@ -134,6 +134,8 @@
"Failed to join room": "Failed to join room",
"Failed to kick": "Failed to kick",
"Failed to leave room": "Failed to leave room",
+ "Failed to load %(groupId)s": "Failed to load %(groupId)s",
+ "Failed to load group members": "Failed to load group members",
"Failed to load timeline position": "Failed to load timeline position",
"Failed to mute user": "Failed to mute user",
"Failed to reject invite": "Failed to reject invite",
diff --git a/test/components/views/groups/GroupMemberList-test.js b/test/components/views/groups/GroupMemberList-test.js
new file mode 100644
index 0000000000..d71d0377d7
--- /dev/null
+++ b/test/components/views/groups/GroupMemberList-test.js
@@ -0,0 +1,149 @@
+/*
+Copyright 2018 New Vector Ltd.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+import React from "react";
+import ReactDOM from "react-dom";
+import ReactTestUtils from "react-dom/test-utils";
+import expect from "expect";
+
+import MockHttpBackend from "matrix-mock-request";
+import MatrixClientPeg from "../../../../src/MatrixClientPeg";
+import sdk from "matrix-react-sdk";
+import Matrix from "matrix-js-sdk";
+
+import * as TestUtils from "test-utils";
+const { waitForUpdate } = TestUtils;
+
+const GroupMemberList = sdk.getComponent("views.groups.GroupMemberList");
+const WrappedGroupMemberList = TestUtils.wrapInMatrixClientContext(GroupMemberList);
+
+describe("GroupMemberList", function() {
+ let root;
+ let rootElement;
+ let httpBackend;
+ let summaryResponse;
+ let groupId;
+ let groupIdEncoded;
+
+ // Summary response fields
+ const user = {
+ is_privileged: true, // can edit the group
+ is_public: true, // appear as a member to non-members
+ is_publicised: true, // display flair
+ };
+ const usersSection = {
+ roles: {},
+ total_user_count_estimate: 0,
+ users: [],
+ };
+ const roomsSection = {
+ categories: {},
+ rooms: [],
+ total_room_count_estimate: 0,
+ };
+
+ // Users response fields
+ const usersResponse = {
+ chunk: [
+ {
+ user_id: "@test:matrix.org",
+ displayname: "Test",
+ avatar_url: "mxc://matrix.org/oUxxDyzQOHdVDMxgwFzyCWEe",
+ is_public: true,
+ is_privileged: true,
+ attestation: {},
+ },
+ ],
+ };
+
+ beforeEach(function() {
+ TestUtils.beforeEach(this);
+
+ httpBackend = new MockHttpBackend();
+
+ Matrix.request(httpBackend.requestFn);
+
+ MatrixClientPeg.get = () => Matrix.createClient({
+ baseUrl: "https://my.home.server",
+ userId: "@me:here",
+ accessToken: "123456789",
+ });
+
+ summaryResponse = {
+ profile: {
+ avatar_url: "mxc://someavatarurl",
+ is_openly_joinable: true,
+ is_public: true,
+ long_description: "This is a
LONG description.",
+ name: "The name of a community",
+ short_description: "This is a community",
+ },
+ user,
+ users_section: usersSection,
+ rooms_section: roomsSection,
+ };
+
+ groupId = "+" + Math.random().toString(16).slice(2) + ":domain";
+ groupIdEncoded = encodeURIComponent(groupId);
+
+ rootElement = document.createElement("div");
+ root = ReactDOM.render(
, rootElement);
+ });
+
+ afterEach(function() {
+ ReactDOM.unmountComponentAtNode(rootElement);
+ });
+
+ it("should show group member list after successful /users", function() {
+ const groupMemberList = ReactTestUtils.findRenderedComponentWithType(root, GroupMemberList);
+ const prom = waitForUpdate(groupMemberList, 4).then(() => {
+ ReactTestUtils.findRenderedDOMComponentWithClass(root, "mx_MemberList");
+
+ const memberList = ReactTestUtils.findRenderedDOMComponentWithClass(root, "mx_MemberList_joined");
+ const memberListElement = ReactDOM.findDOMNode(memberList);
+ expect(memberListElement).toExist();
+ expect(memberListElement.innerText).toBe("Test");
+ });
+
+ httpBackend.when("GET", "/groups/" + groupIdEncoded + "/summary").respond(200, summaryResponse);
+ httpBackend.when("GET", "/groups/" + groupIdEncoded + "/users").respond(200, usersResponse);
+ httpBackend.when("GET", "/groups/" + groupIdEncoded + "/invited_users").respond(200, { chunk: [] });
+ httpBackend.when("GET", "/groups/" + groupIdEncoded + "/rooms").respond(200, { chunk: [] });
+
+ httpBackend.flush(undefined, undefined, 0);
+ return prom;
+ });
+
+ it("should show error message after failed /users", function() {
+ const groupMemberList = ReactTestUtils.findRenderedComponentWithType(root, GroupMemberList);
+ const prom = waitForUpdate(groupMemberList, 4).then(() => {
+ ReactTestUtils.findRenderedDOMComponentWithClass(root, "mx_MemberList");
+
+ const memberList = ReactTestUtils.findRenderedDOMComponentWithClass(root, "mx_MemberList_joined");
+ const memberListElement = ReactDOM.findDOMNode(memberList);
+ expect(memberListElement).toExist();
+ expect(memberListElement.innerText).toBe("Failed to load group members");
+ });
+
+ httpBackend.when("GET", "/groups/" + groupIdEncoded + "/summary").respond(200, summaryResponse);
+ httpBackend.when("GET", "/groups/" + groupIdEncoded + "/users").respond(500, {});
+ httpBackend.when("GET", "/groups/" + groupIdEncoded + "/invited_users").respond(200, { chunk: [] });
+ httpBackend.when("GET", "/groups/" + groupIdEncoded + "/rooms").respond(200, { chunk: [] });
+
+ httpBackend.flush(undefined, undefined, 0);
+ return prom;
+ });
+});