From 2340c1308eff852c6b948f37dc7457094db6137e Mon Sep 17 00:00:00 2001 From: David Baker Date: Mon, 23 Oct 2017 19:53:17 +0100 Subject: [PATCH 01/11] Fix bug with gen-i18n/js when adding new plurals --- scripts/gen-i18n.js | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/scripts/gen-i18n.js b/scripts/gen-i18n.js index 609c6b00c5..cbef54ba9c 100755 --- a/scripts/gen-i18n.js +++ b/scripts/gen-i18n.js @@ -177,7 +177,9 @@ const trObj = {}; for (const tr of translatables) { trObj[tr] = tr; if (tr.includes("|")) { - trObj[tr] = inputTranslationsRaw[tr]; + if (inputTranslationsRaw[tr]) { + trObj[tr] = inputTranslationsRaw[tr]; + } } } From 1cc427ba467000994de2fdcf144fc36ed09f341b Mon Sep 17 00:00:00 2001 From: Luke Barnard Date: Tue, 24 Oct 2017 09:58:45 +0100 Subject: [PATCH 02/11] Fix some react warnings in GroupMemberList - If the list contains two users twice, react would warn about duplicate keys. Use `index` instead. - Check if unmounted before setting state after fetching members. --- src/components/views/groups/GroupMemberList.js | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/components/views/groups/GroupMemberList.js b/src/components/views/groups/GroupMemberList.js index 511af37166..6cbbc968a7 100644 --- a/src/components/views/groups/GroupMemberList.js +++ b/src/components/views/groups/GroupMemberList.js @@ -59,6 +59,7 @@ export default withMatrixClient(React.createClass({ }, _fetchMembers: function() { + if (this._unmounted) return; this.setState({ members: this._groupStore.getGroupMembers(), invitedMembers: this._groupStore.getGroupInvitedMembers(), @@ -105,9 +106,9 @@ export default withMatrixClient(React.createClass({ }); } - memberList = memberList.map((m) => { + memberList = memberList.map((m, index) => { return ( - + ); }); From e4194460818ebca2b4c3b5350dc26137ee8856df Mon Sep 17 00:00:00 2001 From: Luke Barnard Date: Tue, 24 Oct 2017 10:13:38 +0100 Subject: [PATCH 03/11] Add fields needed by RoomDetailList to groupRoomFromApiObject and also alter props API for RDL to match the returned rooms. (This doesn't affect RoomDirectory - this does not use RDL (yet)) --- src/components/views/rooms/RoomDetailList.js | 38 +++++++++++++------- src/groups.js | 4 +++ 2 files changed, 30 insertions(+), 12 deletions(-) diff --git a/src/components/views/rooms/RoomDetailList.js b/src/components/views/rooms/RoomDetailList.js index 370bf1bb2d..be9de849e9 100644 --- a/src/components/views/rooms/RoomDetailList.js +++ b/src/components/views/rooms/RoomDetailList.js @@ -25,15 +25,28 @@ import MatrixClientPeg from '../../../MatrixClientPeg'; import PropTypes from 'prop-types'; function getDisplayAliasForRoom(room) { - return room.canonical_alias || (room.aliases ? room.aliases[0] : ""); + return room.canonicalAlias || (room.aliases ? room.aliases[0] : ""); } const RoomDetailRow = React.createClass({ + propTypes: PropTypes.shape({ + name: PropTypes.string, + topic: PropTypes.string, + roomId: PropTypes.string, + avatarUrl: PropTypes.string, + numJoinedMembers: PropTypes.number, + canonicalAlias: PropTypes.string, + aliases: PropTypes.arrayOf(PropTypes.string), + + worldReadable: PropTypes.bool, + guestCanJoin: PropTypes.bool, + }), + onClick: function(ev) { ev.preventDefault(); dis.dispatch({ action: 'view_room', - room_id: this.props.room.room_id, + room_id: this.props.room.roomId, }); }, @@ -50,10 +63,10 @@ const RoomDetailRow = React.createClass({ const name = room.name || getDisplayAliasForRoom(room) || _t('Unnamed room'); const topic = linkifyString(sanitizeHtml(room.topic || '')); - const guestRead = room.world_readable ? ( + const guestRead = room.worldReadable ? (
{ _t('World readable') }
) :
; - const guestJoin = room.guest_can_join ? ( + const guestJoin = room.guestCanJoin ? (
{ _t('Guests can join') }
) :
; @@ -62,13 +75,13 @@ const RoomDetailRow = React.createClass({ { guestJoin }
) :
; - return + return + room.avatarUrl, 24, 24, "crop")} />
{ name }
  @@ -79,7 +92,7 @@ const RoomDetailRow = React.createClass({
{ getDisplayAliasForRoom(room) }
- { room.num_joined_members } + { room.numJoinedMembers } ; }, @@ -92,13 +105,14 @@ export default React.createClass({ rooms: PropTypes.arrayOf(PropTypes.shape({ name: PropTypes.string, topic: PropTypes.string, - room_id: PropTypes.string, - num_joined_members: PropTypes.number, - canonical_alias: PropTypes.string, + roomId: PropTypes.string, + avatarUrl: PropTypes.string, + numJoinedMembers: PropTypes.number, + canonicalAlias: PropTypes.string, aliases: PropTypes.arrayOf(PropTypes.string), - world_readable: PropTypes.bool, - guest_can_join: PropTypes.bool, + worldReadable: PropTypes.bool, + guestCanJoin: PropTypes.bool, })), }, diff --git a/src/groups.js b/src/groups.js index 69871c45e9..0679fce59e 100644 --- a/src/groups.js +++ b/src/groups.js @@ -43,5 +43,9 @@ export function groupRoomFromApiObject(apiObject) { roomId: apiObject.room_id, canonicalAlias: apiObject.canonical_alias, avatarUrl: apiObject.avatar_url, + topic: apiObject.topic, + numJoinedMembers: apiObject.num_joined_members, + worldReadable: apiObject.world_readable, + canGuestsJoin: apiObject.can_guests_join, }; } From 46e9d4197a621b7a735040913544977167bf8672 Mon Sep 17 00:00:00 2001 From: David Baker Date: Tue, 24 Oct 2017 10:21:41 +0100 Subject: [PATCH 04/11] Use the 'and' feature! --- scripts/gen-i18n.js | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/scripts/gen-i18n.js b/scripts/gen-i18n.js index cbef54ba9c..670089ba63 100755 --- a/scripts/gen-i18n.js +++ b/scripts/gen-i18n.js @@ -176,10 +176,8 @@ for (const path of SEARCH_PATHS) { const trObj = {}; for (const tr of translatables) { trObj[tr] = tr; - if (tr.includes("|")) { - if (inputTranslationsRaw[tr]) { - trObj[tr] = inputTranslationsRaw[tr]; - } + if (tr.includes("|") && inputTranslationsRaw[tr]) { + trObj[tr] = inputTranslationsRaw[tr]; } } From 3ae31dd426b0879fcc594eb1bde43c7db3526332 Mon Sep 17 00:00:00 2001 From: Luke Barnard Date: Tue, 24 Oct 2017 10:39:47 +0100 Subject: [PATCH 05/11] Make GroupStoreCache global for cross-package access --- src/stores/GroupStoreCache.js | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/src/stores/GroupStoreCache.js b/src/stores/GroupStoreCache.js index 551b155615..df5ffcda5e 100644 --- a/src/stores/GroupStoreCache.js +++ b/src/stores/GroupStoreCache.js @@ -33,8 +33,7 @@ class GroupStoreCache { } } -let singletonGroupStoreCache = null; -if (!singletonGroupStoreCache) { - singletonGroupStoreCache = new GroupStoreCache(); +if (global.singletonGroupStoreCache === undefined) { + global.singletonGroupStoreCache = new GroupStoreCache(); } -module.exports = singletonGroupStoreCache; +export default global.singletonGroupStoreCache; From d1db0d6426ed04844d62a726df0c340e1ac5a6b2 Mon Sep 17 00:00:00 2001 From: Luke Barnard Date: Tue, 24 Oct 2017 10:50:31 +0100 Subject: [PATCH 06/11] Deduplicate members, key by userId --- src/components/views/groups/GroupMemberList.js | 17 +++++++++++------ 1 file changed, 11 insertions(+), 6 deletions(-) diff --git a/src/components/views/groups/GroupMemberList.js b/src/components/views/groups/GroupMemberList.js index 6cbbc968a7..a5ab22eb0e 100644 --- a/src/components/views/groups/GroupMemberList.js +++ b/src/components/views/groups/GroupMemberList.js @@ -106,12 +106,11 @@ export default withMatrixClient(React.createClass({ }); } - memberList = memberList.map((m, index) => { - return ( - - ); + const uniqueMembers = {}; + memberList.forEach((m) => { + if (!uniqueMembers[m.userId]) uniqueMembers[m.userId] = m; }); - + memberList = Object.keys(uniqueMembers).map((userId) => uniqueMembers[userId]); memberList.sort((a, b) => { // TODO: should put admins at the top: we don't yet have that info if (a < b) { @@ -123,10 +122,16 @@ export default withMatrixClient(React.createClass({ } }); + const memberTiles = memberList.map((m) => { + return ( + + ); + }); + return - { memberList } + { memberTiles } ; }, From f9c45d2c45b17b2f997dcd20df3b899789095648 Mon Sep 17 00:00:00 2001 From: David Baker Date: Tue, 24 Oct 2017 13:40:44 +0100 Subject: [PATCH 07/11] Re-run gen-i18n after fixing https://github.com/matrix-org/matrix-react-sdk/pull/1521 --- src/i18n/strings/en_EN.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/i18n/strings/en_EN.json b/src/i18n/strings/en_EN.json index 1e8a7cadbc..b1f457f375 100644 --- a/src/i18n/strings/en_EN.json +++ b/src/i18n/strings/en_EN.json @@ -54,8 +54,6 @@ "Room name or alias": "Room name or alias", "Add to community": "Add to community", "Failed to invite the following users to %(groupId)s:": "Failed to invite the following users to %(groupId)s:", - "Invites sent": "Invites sent", - "Your community invitations have been sent.": "Your community invitations have been sent.", "Failed to invite users to community": "Failed to invite users to community", "Failed to invite users to %(groupId)s": "Failed to invite users to %(groupId)s", "Failed to add the following rooms to %(groupId)s:": "Failed to add the following rooms to %(groupId)s:", @@ -156,6 +154,7 @@ "Message Pinning": "Message Pinning", "%(displayName)s is typing": "%(displayName)s is typing", "%(names)s and one other are typing": "%(names)s and one other are typing", + "%(names)s and %(count)s others are typing|other": "%(names)s and %(count)s others are typing|other", "%(names)s and %(lastPerson)s are typing": "%(names)s and %(lastPerson)s are typing", "Failure to create room": "Failure to create room", "Server may be unavailable, overloaded, or you hit a bug.": "Server may be unavailable, overloaded, or you hit a bug.", @@ -565,6 +564,7 @@ "Custom level": "Custom level", "Room directory": "Room directory", "Start chat": "Start chat", + "And %(count)s more...|other": "And %(count)s more...|other", "ex. @bob:example.com": "ex. @bob:example.com", "Add User": "Add User", "Something went wrong!": "Something went wrong!", From e955d27b5f0344e477ba7472cbbe03743464c517 Mon Sep 17 00:00:00 2001 From: Luke Barnard Date: Tue, 24 Oct 2017 13:47:10 +0100 Subject: [PATCH 08/11] Use the correct property of the API room objects --- src/groups.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/groups.js b/src/groups.js index 0679fce59e..06db5d067f 100644 --- a/src/groups.js +++ b/src/groups.js @@ -46,6 +46,6 @@ export function groupRoomFromApiObject(apiObject) { topic: apiObject.topic, numJoinedMembers: apiObject.num_joined_members, worldReadable: apiObject.world_readable, - canGuestsJoin: apiObject.can_guests_join, + guestCanJoin: apiObject.guest_can_join, }; } From 8447d1501306b9dfbd92b08f5e4abb26d323e68a Mon Sep 17 00:00:00 2001 From: David Baker Date: Tue, 24 Oct 2017 14:26:30 +0100 Subject: [PATCH 09/11] Don't include the |other in the translation value --- scripts/gen-i18n.js | 11 ++++++++--- src/i18n/strings/en_EN.json | 4 ++-- 2 files changed, 10 insertions(+), 5 deletions(-) diff --git a/scripts/gen-i18n.js b/scripts/gen-i18n.js index 52c16d25cd..dd990b5210 100755 --- a/scripts/gen-i18n.js +++ b/scripts/gen-i18n.js @@ -232,9 +232,14 @@ for (const path of SEARCH_PATHS) { const trObj = {}; for (const tr of translatables) { - trObj[tr] = tr; - if (tr.includes("|") && inputTranslationsRaw[tr]) { - trObj[tr] = inputTranslationsRaw[tr]; + if (tr.includes("|")) { + if (inputTranslationsRaw[tr]) { + trObj[tr] = inputTranslationsRaw[tr]; + } else { + trObj[tr] = tr.split("|")[0]; + } + } else { + trObj[tr] = tr; } } diff --git a/src/i18n/strings/en_EN.json b/src/i18n/strings/en_EN.json index b1f457f375..492113989b 100644 --- a/src/i18n/strings/en_EN.json +++ b/src/i18n/strings/en_EN.json @@ -154,7 +154,7 @@ "Message Pinning": "Message Pinning", "%(displayName)s is typing": "%(displayName)s is typing", "%(names)s and one other are typing": "%(names)s and one other are typing", - "%(names)s and %(count)s others are typing|other": "%(names)s and %(count)s others are typing|other", + "%(names)s and %(count)s others are typing|other": "%(names)s and %(count)s others are typing", "%(names)s and %(lastPerson)s are typing": "%(names)s and %(lastPerson)s are typing", "Failure to create room": "Failure to create room", "Server may be unavailable, overloaded, or you hit a bug.": "Server may be unavailable, overloaded, or you hit a bug.", @@ -564,7 +564,7 @@ "Custom level": "Custom level", "Room directory": "Room directory", "Start chat": "Start chat", - "And %(count)s more...|other": "And %(count)s more...|other", + "And %(count)s more...|other": "And %(count)s more...", "ex. @bob:example.com": "ex. @bob:example.com", "Add User": "Add User", "Something went wrong!": "Something went wrong!", From 459ffc47d6061f937b8c67362472dcaeafc84d07 Mon Sep 17 00:00:00 2001 From: Luke Barnard Date: Tue, 24 Oct 2017 15:25:35 +0100 Subject: [PATCH 10/11] Prevent editing of UI requiring user privilege if user unprivileged --- src/components/structures/GroupView.js | 93 +++++++++++++++++--------- 1 file changed, 63 insertions(+), 30 deletions(-) diff --git a/src/components/structures/GroupView.js b/src/components/structures/GroupView.js index b2e49fb67a..52680ea5fa 100644 --- a/src/components/structures/GroupView.js +++ b/src/components/structures/GroupView.js @@ -481,6 +481,10 @@ export default React.createClass({ editing: true, profileForm: Object.assign({}, this.state.summary.profile), }); + dis.dispatch({ + action: 'ui_opacity', + sideOpacity: 0.3, + }); }, _onCancelClick: function() { @@ -488,6 +492,7 @@ export default React.createClass({ editing: false, profileForm: null, }); + dis.dispatch({action: 'ui_opacity'}); }, _onNameChange: function(value) { @@ -535,12 +540,16 @@ export default React.createClass({ _onSaveClick: function() { this.setState({saving: true}); - MatrixClientPeg.get().setGroupProfile(this.props.groupId, this.state.profileForm).then((result) => { + const savePromise = this.state.isUserPrivileged ? + MatrixClientPeg.get().setGroupProfile(this.props.groupId, this.state.profileForm) : + Promise.resolve(); + savePromise.then((result) => { this.setState({ saving: false, editing: false, summary: null, }); + dis.dispatch({action: 'ui_opacity'}); this._initGroupStore(this.props.groupId); }).catch((e) => { this.setState({ @@ -624,23 +633,40 @@ export default React.createClass({ }); }, + _getGroupSection: function() { + const groupSettingsSectionClasses = classnames({ + "mx_GroupView_group": this.state.editing, + "mx_GroupView_group_disabled": this.state.editing && !this.state.isUserPrivileged, + }); + + const header = this.state.editing ?

{ _t('Community Settings') }

:
; + return
+ { header } + { this._getLongDescriptionNode() } + { this._getRoomsNode() } +
; + }, + _getRoomsNode: function() { const RoomDetailList = sdk.getComponent('rooms.RoomDetailList'); const AccessibleButton = sdk.getComponent('elements.AccessibleButton'); const TintableSvg = sdk.getComponent('elements.TintableSvg'); - const addButton = this.state.editing ? - ( -
+ + const addRoomRow = this.state.editing ? + ( +
-
+
{ _t('Add rooms to this community') }
) :
; return

{ _t('Rooms') }

- { addButton } + { addRoomRow }
; @@ -790,7 +816,7 @@ export default React.createClass({ _getMemberSettingsSection: function() { return
-

{ _t("Community Member Settings") }

+

{ _t("Community Member Settings") }

+ const groupDescEditingClasses = classnames({ + "mx_GroupView_groupDesc": true, + "mx_GroupView_groupDesc_disabled": !this.state.isUserPrivileged, + }); + + return this.state.editing ? +

{ _t("Long Description (HTML)") }