From 22ff76e6b73e400f6966da0c59a1d1039164d60e Mon Sep 17 00:00:00 2001 From: "J. Ryan Stinnett" Date: Tue, 4 Dec 2018 16:42:50 -0600 Subject: [PATCH] Add error to UI when group member list fails to load Signed-off-by: J. Ryan Stinnett --- .../views/groups/GroupMemberList.js | 40 ++++- src/i18n/strings/en_EN.json | 1 + src/i18n/strings/en_US.json | 2 + .../views/groups/GroupMemberList-test.js | 149 ++++++++++++++++++ 4 files changed, 187 insertions(+), 5 deletions(-) create mode 100644 test/components/views/groups/GroupMemberList-test.js 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
{ _t("Failed to load group members") }
; + } + const GroupMemberTile = sdk.getComponent("groups.GroupMemberTile"); const TruncatedList = sdk.getComponent("elements.TruncatedList"); query = (query || "").toLowerCase(); @@ -153,15 +172,26 @@ export default React.createClass({ ); const joined = this.state.members ?
- { this.makeGroupMemberTiles(this.state.searchQuery, this.state.members) } + { + this.makeGroupMemberTiles( + this.state.searchQuery, + this.state.members, + this.state.membersError, + ) + }
:
; const invited = (this.state.invitedMembers && this.state.invitedMembers.length > 0) ?
-

{ _t("Invited") }

- { this.makeGroupMemberTiles(this.state.searchQuery, this.state.invitedMembers) } +

{_t("Invited")}

+ { + this.makeGroupMemberTiles( + this.state.searchQuery, + this.state.invitedMembers, + this.state.invitedMembersError, + ) + }
:
; - return (
{ 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; + }); +});