From a2ff289ed81b0cdcf97e1ca885e6eb263675053e Mon Sep 17 00:00:00 2001 From: David Baker Date: Wed, 28 Jun 2017 13:56:18 +0100 Subject: [PATCH 01/14] Add 'groups' page --- src/PageTypes.js | 2 ++ src/components/structures/LoggedInView.js | 6 ++++++ src/components/structures/MatrixChat.js | 8 ++++++++ src/i18n/strings/en_EN.json | 4 +++- 4 files changed, 19 insertions(+), 1 deletion(-) diff --git a/src/PageTypes.js b/src/PageTypes.js index b2346c62c3..66d930c288 100644 --- a/src/PageTypes.js +++ b/src/PageTypes.js @@ -1,5 +1,6 @@ /* Copyright 2015, 2016 OpenMarket Ltd +Copyright 2017 Vector Creations Ltd Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. @@ -23,4 +24,5 @@ export default { RoomDirectory: "room_directory", UserView: "user_view", GroupView: "group_view", + MyGroups: "my_groups", }; diff --git a/src/components/structures/LoggedInView.js b/src/components/structures/LoggedInView.js index aef7fe9cce..a473df7c5b 100644 --- a/src/components/structures/LoggedInView.js +++ b/src/components/structures/LoggedInView.js @@ -211,6 +211,7 @@ export default React.createClass({ const RoomDirectory = sdk.getComponent('structures.RoomDirectory'); const HomePage = sdk.getComponent('structures.HomePage'); const GroupView = sdk.getComponent('structures.GroupView'); + const MyGroups = sdk.getComponent('structures.MyGroups'); const MatrixToolbar = sdk.getComponent('globals.MatrixToolbar'); const NewVersionBar = sdk.getComponent('globals.NewVersionBar'); const UpdateCheckBar = sdk.getComponent('globals.UpdateCheckBar'); @@ -248,6 +249,11 @@ export default React.createClass({ if (!this.props.collapse_rhs) right_panel = ; break; + case PageTypes.MyGroups: + page_element = ; + if (!this.props.collapse_rhs) right_panel = ; + break; + case PageTypes.CreateRoom: page_element = Date: Thu, 29 Jun 2017 17:03:05 +0100 Subject: [PATCH 02/14] Groups page / Create Group dialog --- src/components/structures/MyGroups.js | 112 +++++++++++ .../views/dialogs/CreateGroupDialog.js | 190 ++++++++++++++++++ 2 files changed, 302 insertions(+) create mode 100644 src/components/structures/MyGroups.js create mode 100644 src/components/views/dialogs/CreateGroupDialog.js diff --git a/src/components/structures/MyGroups.js b/src/components/structures/MyGroups.js new file mode 100644 index 0000000000..a3459a16c7 --- /dev/null +++ b/src/components/structures/MyGroups.js @@ -0,0 +1,112 @@ +/* +Copyright 2017 Vector Creations 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 sdk from '../../index'; +import { _t } from '../../languageHandler'; +import WithMatrixClient from '../../wrappers/WithMatrixClient'; +import AccessibleButton from '../views/elements/AccessibleButton'; +import dis from '../../dispatcher'; +import PropTypes from 'prop-types'; +import Modal from '../../Modal'; + +const GroupTile = React.createClass({ + displayName: 'GroupTile', + + propTypes: { + groupId: PropTypes.string.isRequired, + }, + + onClick: function(e) { + e.preventDefault(); + dis.dispatch({ + action: 'view_group', + group_id: this.props.groupId, + }); + }, + + render: function() { + return {this.props.groupId}; + } +}); + +module.exports = WithMatrixClient(React.createClass({ + displayName: 'GroupList', + + propTypes: { + matrixClient: React.PropTypes.object.isRequired, + }, + + getInitialState: function() { + return { + groups: null, + error: null, + }; + }, + + componentWillMount: function() { + this._fetch(); + }, + + componentWillUnmount: function() { + }, + + _onCreateGroupClick: function() { + const CreateGroupDialog = sdk.getComponent("dialogs.CreateGroupDialog"); + Modal.createDialog(CreateGroupDialog); + }, + + _fetch: function() { + this.props.matrixClient.getJoinedGroups().done((result) => { + this.setState({groups: result.groups, error: null}); + }, (err) => { + this.setState({result: null, error: err}); + }); + }, + + render: function() { + const Loader = sdk.getComponent("elements.Spinner"); + const SimpleRoomHeader = sdk.getComponent('rooms.SimpleRoomHeader'); + + let content; + if (this.state.groups) { + let groupNodes = []; + this.state.groups.forEach((g) => { + groupNodes.push( +
+ +
+ ); + }); + content =
{groupNodes}
; + } else if (this.state.error) { + content =
+ Error whilst fetching joined groups +
; + } + + return
+ +
+ + {_t('Create a new group')} + +
+ You are a member of these groups: + {content} +
; + }, +})); diff --git a/src/components/views/dialogs/CreateGroupDialog.js b/src/components/views/dialogs/CreateGroupDialog.js new file mode 100644 index 0000000000..64984ebf5c --- /dev/null +++ b/src/components/views/dialogs/CreateGroupDialog.js @@ -0,0 +1,190 @@ +/* +Copyright 2017 Vector Creations 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 sdk from '../../../index'; +import dis from '../../../dispatcher'; +import { _t } from '../../../languageHandler'; +import MatrixClientPeg from '../../../MatrixClientPeg'; +import AccessibleButton from '../elements/AccessibleButton'; + +// We match fairly liberally and leave it up to the server to reject if +// there are invalid characters etc. +const GROUP_REGEX = /^\+(.*?):(.*)$/; + +export default React.createClass({ + displayName: 'CreateGroupDialog', + propTypes: { + onFinished: React.PropTypes.func.isRequired, + }, + + getInitialState: function() { + return { + groupName: '', + groupId: '', + groupError: null, + creating: false, + createError: null, + }; + }, + + _onGroupNameChange: function(e) { + this.setState({ + groupName: e.target.value, + }); + }, + + _onGroupIdChange: function(e) { + this.setState({ + groupId: e.target.value, + }); + }, + + _onGroupIdBlur: function(e) { + this._checkGroupId(); + }, + + _checkGroupId: function(e) { + const parsedGroupId = this._parseGroupId(this.state.groupId); + let error = null; + if (parsedGroupId === null) { + error = _t("Group IDs must be of the form +localpart:%(domain)s", {domain: MatrixClientPeg.get().getDomain()}); + } else { + const localpart = parsedGroupId[0]; + const domain = parsedGroupId[1]; + if (domain !== MatrixClientPeg.get().getDomain()) { + error = _t( + "It is currently only possible to create groups on your own home server: use a group ID ending with %(domain)s", + {domain: MatrixClientPeg.get().getDomain()} + ); + } + } + this.setState({ + groupIdError: error, + }); + return error; + }, + + _onFormSubmit: function(e) { + e.preventDefault(); + + if (this._checkGroupId()) return; + + const parsedGroupId = this._parseGroupId(this.state.groupId); + const profile = {}; + if (this.state.groupName !== '') { + profile.name = this.state.groupName; + } + this.setState({creating: true}); + MatrixClientPeg.get().createGroup({ + localpart: parsedGroupId[0], + profile: profile, + }).then((result) => { + dis.dispatch({ + action: 'view_group', + group_id: result.group_id, + }); + this.props.onFinished(true); + }).catch((e) => { + this.setState({createError: e}); + }).finally(() => { + this.setState({creating: false}); + }).done(); + }, + + _onCancel: function() { + this.props.onFinished(false); + }, + + /** + * Parse a string that may be a group ID + * If the string is a valid group ID, return a list of [localpart, domain], + * otherwise return null. + */ + _parseGroupId: function(groupId) { + const matches = GROUP_REGEX.exec(this.state.groupId); + if (!matches || matches.length < 3) { + return null; + } + return [matches[1], matches[2]]; + }, + + render: function() { + const BaseDialog = sdk.getComponent('views.dialogs.BaseDialog'); + const Loader = sdk.getComponent("elements.Spinner"); + + if (this.state.creating) { + return ; + } + + let createErrorNode; + if (this.state.createError) { + // XXX: We should catch errcodes and give sensible i18ned messages for them, + // rather than displaying what the server gives us, but synapse doesn't give + // any yet. + createErrorNode =
+
{_t('Room creation failed')}
+
{this.state.createError.message}
+
; + } + + return ( + +
+
+
+ +
+
+ +
+
+
+ +
+
+ +
+
+ {this.state.groupIdError} +
+ {createErrorNode} +
+
+ + +
+
+
+ ); + }, +}); From d6ecec19871db15bafe2d6b6b290caeef62a8d2b Mon Sep 17 00:00:00 2001 From: David Baker Date: Thu, 29 Jun 2017 17:17:43 +0100 Subject: [PATCH 03/14] Behave better on rooms with no avatar / name Also add translation strings --- src/components/structures/GroupView.js | 33 ++++++++++++++++++-------- src/i18n/strings/en_EN.json | 11 ++++++++- 2 files changed, 33 insertions(+), 11 deletions(-) diff --git a/src/components/structures/GroupView.js b/src/components/structures/GroupView.js index 71deaf529b..f51e21a9ad 100644 --- a/src/components/structures/GroupView.js +++ b/src/components/structures/GroupView.js @@ -71,29 +71,42 @@ module.exports = React.createClass({ return ; } else if (this.state.summary) { const summary = this.state.summary; - let avatarUrl = null; + let avatarNode = null; if (summary.profile && summary.profile.avatar_url) { - avatarUrl = MatrixClientPeg.get().mxcUrlToHttp(summary.profile.avatar_url); + avatarNode = ; } let description = null; if (summary.profile && summary.profile.long_description) { description = sanitizedHtmlNode(summary.profile.long_description); } + + let nameNode; + if (summary.profile.name) { + nameNode =
+ {summary.profile.name} + + ({this.props.groupId}) + +
; + } else { + nameNode =
+ {this.props.groupId} +
; + } + return (
- + {avatarNode}
-
- {summary.profile.name} - - ({this.props.groupId}) - -
+ {nameNode}
{summary.profile.short_description}
diff --git a/src/i18n/strings/en_EN.json b/src/i18n/strings/en_EN.json index 6593c5aab8..e4a13e0689 100644 --- a/src/i18n/strings/en_EN.json +++ b/src/i18n/strings/en_EN.json @@ -927,5 +927,14 @@ "This Home server does not support groups": "This Home server does not support groups", "Loading device info...": "Loading device info...", "Groups": "Groups", - "Create a new group": "Create a new group" + "Create a new group": "Create a new group", + "Create Group": "Create Group", + "Group Name": "Group Name", + "Example": "Example", + "Create": "Create", + "Group ID": "Group ID", + "+example:%(domain)s": "+example:%(domain)s", + "Group IDs must be of the form +localpart:%(domain)s": "Group IDs must be of the form +localpart:%(domain)s", + "It is currently only possible to create groups on your own home server: use a group ID ending with %(domain)s": "It is currently only possible to create groups on your own home server: use a group ID ending with %(domain)s", + "Room creation failed": "Room creation failed" } From 84e13d54370a520f4f3215d337825585427d8658 Mon Sep 17 00:00:00 2001 From: David Baker Date: Thu, 29 Jun 2017 17:51:38 +0100 Subject: [PATCH 04/14] Add GroupAvatar to handle fallback images etc. And a few misc tidyups --- src/components/structures/GroupView.js | 21 +++---- src/components/structures/MyGroups.js | 2 +- src/components/views/avatars/GroupAvatar.js | 62 +++++++++++++++++++ .../views/dialogs/CreateGroupDialog.js | 3 +- 4 files changed, 74 insertions(+), 14 deletions(-) create mode 100644 src/components/views/avatars/GroupAvatar.js diff --git a/src/components/structures/GroupView.js b/src/components/structures/GroupView.js index f51e21a9ad..0f10ba60e2 100644 --- a/src/components/structures/GroupView.js +++ b/src/components/structures/GroupView.js @@ -15,17 +15,18 @@ limitations under the License. */ import React from 'react'; +import PropTypes from 'prop-types'; import MatrixClientPeg from '../../MatrixClientPeg'; import sdk from '../../index'; import { sanitizedHtmlNode } from '../../HtmlUtils'; import { _t } from '../../languageHandler'; -module.exports = React.createClass({ +export default React.createClass({ displayName: 'GroupView', propTypes: { - groupId: React.PropTypes.string.isRequired, + groupId: PropTypes.string.isRequired, }, getInitialState: function() { @@ -64,21 +65,13 @@ module.exports = React.createClass({ }, render: function() { - const BaseAvatar = sdk.getComponent("avatars.BaseAvatar"); + const GroupAvatar = sdk.getComponent("avatars.GroupAvatar"); const Loader = sdk.getComponent("elements.Spinner"); if (this.state.summary === null && this.state.error === null) { return ; } else if (this.state.summary) { const summary = this.state.summary; - let avatarNode = null; - if (summary.profile && summary.profile.avatar_url) { - avatarNode = ; - } let description = null; if (summary.profile && summary.profile.long_description) { description = sanitizedHtmlNode(summary.profile.long_description); @@ -103,7 +96,11 @@ module.exports = React.createClass({
- {avatarNode} +
{nameNode} diff --git a/src/components/structures/MyGroups.js b/src/components/structures/MyGroups.js index a3459a16c7..cd0dda33f9 100644 --- a/src/components/structures/MyGroups.js +++ b/src/components/structures/MyGroups.js @@ -43,7 +43,7 @@ const GroupTile = React.createClass({ } }); -module.exports = WithMatrixClient(React.createClass({ +export default WithMatrixClient(React.createClass({ displayName: 'GroupList', propTypes: { diff --git a/src/components/views/avatars/GroupAvatar.js b/src/components/views/avatars/GroupAvatar.js new file mode 100644 index 0000000000..36687af2ca --- /dev/null +++ b/src/components/views/avatars/GroupAvatar.js @@ -0,0 +1,62 @@ +/* +Copyright 2017 Vector Creations 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 PropTypes from 'prop-types'; +import sdk from '../../../index'; +import MatrixClientPeg from '../../../MatrixClientPeg'; + +export default React.createClass({ + displayName: 'GroupAvatar', + + propTypes: { + groupId: PropTypes.string, + groupAvatarUrl: PropTypes.string, + width: PropTypes.number, + height: PropTypes.number, + resizeMethod: PropTypes.string, + }, + + getDefaultProps: function() { + return { + width: 36, + height: 36, + resizeMethod: 'crop', + }; + }, + + getGroupAvatarUrl: function(props) { + return MatrixClientPeg.get().mxcUrlToHttp( + this.props.groupAvatarUrl, + this.props.width, + this.props.height, + this.props.resizeMethod, + ); + }, + + render: function() { + const BaseAvatar = sdk.getComponent("avatars.BaseAvatar"); + + return ( + + ); + } +}); diff --git a/src/components/views/dialogs/CreateGroupDialog.js b/src/components/views/dialogs/CreateGroupDialog.js index 64984ebf5c..5e050b53b2 100644 --- a/src/components/views/dialogs/CreateGroupDialog.js +++ b/src/components/views/dialogs/CreateGroupDialog.js @@ -15,6 +15,7 @@ limitations under the License. */ import React from 'react'; +import PropTypes from 'prop-types'; import sdk from '../../../index'; import dis from '../../../dispatcher'; import { _t } from '../../../languageHandler'; @@ -28,7 +29,7 @@ const GROUP_REGEX = /^\+(.*?):(.*)$/; export default React.createClass({ displayName: 'CreateGroupDialog', propTypes: { - onFinished: React.PropTypes.func.isRequired, + onFinished: PropTypes.func.isRequired, }, getInitialState: function() { From 38923623861ccf3b86753c3f5697e475658e35c6 Mon Sep 17 00:00:00 2001 From: David Baker Date: Thu, 29 Jun 2017 18:30:16 +0100 Subject: [PATCH 05/14] Make my groups page a bit saner --- src/components/structures/MyGroups.js | 8 ++++++-- src/i18n/strings/en_EN.json | 3 ++- 2 files changed, 8 insertions(+), 3 deletions(-) diff --git a/src/components/structures/MyGroups.js b/src/components/structures/MyGroups.js index cd0dda33f9..658a17c88f 100644 --- a/src/components/structures/MyGroups.js +++ b/src/components/structures/MyGroups.js @@ -91,11 +91,16 @@ export default WithMatrixClient(React.createClass({
); }); - content =
{groupNodes}
; + content =
+
{_t('You are a member of these groups')}:
+ {groupNodes} +
; } else if (this.state.error) { content =
Error whilst fetching joined groups
; + } else { + content = ; } return
@@ -105,7 +110,6 @@ export default WithMatrixClient(React.createClass({ {_t('Create a new group')}
- You are a member of these groups: {content}
; }, diff --git a/src/i18n/strings/en_EN.json b/src/i18n/strings/en_EN.json index e4a13e0689..a3b81136d6 100644 --- a/src/i18n/strings/en_EN.json +++ b/src/i18n/strings/en_EN.json @@ -936,5 +936,6 @@ "+example:%(domain)s": "+example:%(domain)s", "Group IDs must be of the form +localpart:%(domain)s": "Group IDs must be of the form +localpart:%(domain)s", "It is currently only possible to create groups on your own home server: use a group ID ending with %(domain)s": "It is currently only possible to create groups on your own home server: use a group ID ending with %(domain)s", - "Room creation failed": "Room creation failed" + "Room creation failed": "Room creation failed", + "You are a member of these groups": "You are a member of these groups" } From e5c1aeb14c5a4807ef06aa7fa686a3264a2e6d36 Mon Sep 17 00:00:00 2001 From: David Baker Date: Fri, 30 Jun 2017 13:59:49 +0100 Subject: [PATCH 06/14] Make the Groups page look more like the design --- src/components/structures/LoggedInView.js | 1 - src/components/structures/MyGroups.js | 42 +++++++++++++++++++---- src/i18n/strings/en_EN.json | 5 ++- 3 files changed, 39 insertions(+), 9 deletions(-) diff --git a/src/components/structures/LoggedInView.js b/src/components/structures/LoggedInView.js index a473df7c5b..edc918e2a4 100644 --- a/src/components/structures/LoggedInView.js +++ b/src/components/structures/LoggedInView.js @@ -251,7 +251,6 @@ export default React.createClass({ case PageTypes.MyGroups: page_element = ; - if (!this.props.collapse_rhs) right_panel = ; break; case PageTypes.CreateRoom: diff --git a/src/components/structures/MyGroups.js b/src/components/structures/MyGroups.js index 658a17c88f..222e424b2d 100644 --- a/src/components/structures/MyGroups.js +++ b/src/components/structures/MyGroups.js @@ -16,7 +16,7 @@ limitations under the License. import React from 'react'; import sdk from '../../index'; -import { _t } from '../../languageHandler'; +import { _t, _tJsx } from '../../languageHandler'; import WithMatrixClient from '../../wrappers/WithMatrixClient'; import AccessibleButton from '../views/elements/AccessibleButton'; import dis from '../../dispatcher'; @@ -44,7 +44,7 @@ const GroupTile = React.createClass({ }); export default WithMatrixClient(React.createClass({ - displayName: 'GroupList', + displayName: 'MyGroups', propTypes: { matrixClient: React.PropTypes.object.isRequired, @@ -80,6 +80,7 @@ export default WithMatrixClient(React.createClass({ render: function() { const Loader = sdk.getComponent("elements.Spinner"); const SimpleRoomHeader = sdk.getComponent('rooms.SimpleRoomHeader'); + const TintableSvg = sdk.getComponent("elements.TintableSvg"); let content; if (this.state.groups) { @@ -105,12 +106,39 @@ export default WithMatrixClient(React.createClass({ return
-
- - {_t('Create a new group')} - +
+
+
+ {_t('Create a new group')} +
+ + + + {_t( + 'Create a group to represent your community! '+ + 'Define a set of rooms and your own custom homepage '+ + 'to mark out your space in the Matrix universe.' + )} +
+
+
+ {_t('Join an existing group')} +
+ + + + {_tJsx( + 'To join an exisitng group you\'ll have to '+ + 'know its group identifier; this will look '+ + 'something like +example:matrix.org.', + /(.*)<\/i>/, + (sub) => {sub}, + )} +
+
+
+ {content}
- {content}
; }, })); diff --git a/src/i18n/strings/en_EN.json b/src/i18n/strings/en_EN.json index a3b81136d6..7de380e1d5 100644 --- a/src/i18n/strings/en_EN.json +++ b/src/i18n/strings/en_EN.json @@ -937,5 +937,8 @@ "Group IDs must be of the form +localpart:%(domain)s": "Group IDs must be of the form +localpart:%(domain)s", "It is currently only possible to create groups on your own home server: use a group ID ending with %(domain)s": "It is currently only possible to create groups on your own home server: use a group ID ending with %(domain)s", "Room creation failed": "Room creation failed", - "You are a member of these groups": "You are a member of these groups" + "You are a member of these groups": "You are a member of these groups", + "Create a group to represent your community! Define a set of rooms and your own custom homepage to mark out your space in the Matrix universe.": "Create a group to represent your community! Define a set of rooms and your own custom homepage to mark out your space in the Matrix universe.", + "Join an existing group": "Join an existing group", + "To join an exisitng group you'll have to know its group identifier; this will look something like +example:matrix.org.": "To join an exisitng group you'll have to know its group identifier; this will look something like +example:matrix.org." } From c07057d1d41d769fe48db5ca69c8c1d65f675d63 Mon Sep 17 00:00:00 2001 From: David Baker Date: Fri, 30 Jun 2017 16:05:19 +0100 Subject: [PATCH 07/14] Extract props we don't want to pass to BaseAvatar --- src/components/views/avatars/GroupAvatar.js | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/components/views/avatars/GroupAvatar.js b/src/components/views/avatars/GroupAvatar.js index 36687af2ca..4253b2f5b7 100644 --- a/src/components/views/avatars/GroupAvatar.js +++ b/src/components/views/avatars/GroupAvatar.js @@ -49,13 +49,15 @@ export default React.createClass({ render: function() { const BaseAvatar = sdk.getComponent("avatars.BaseAvatar"); + // extract the props we use from props so we can pass any others through + const {groupId, groupAvatarUrl, wifth, height, resizeMethod, ...otherProps} = this.props; return ( ); } From 3b06db07256c41e6afd7d636884c126dc11b228a Mon Sep 17 00:00:00 2001 From: David Baker Date: Wed, 5 Jul 2017 11:39:22 +0100 Subject: [PATCH 08/14] Pass with, height & resizeMethod through Was spelt wrong anyway --- src/components/views/avatars/GroupAvatar.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/views/avatars/GroupAvatar.js b/src/components/views/avatars/GroupAvatar.js index 4253b2f5b7..15c71e59d5 100644 --- a/src/components/views/avatars/GroupAvatar.js +++ b/src/components/views/avatars/GroupAvatar.js @@ -50,7 +50,7 @@ export default React.createClass({ render: function() { const BaseAvatar = sdk.getComponent("avatars.BaseAvatar"); // extract the props we use from props so we can pass any others through - const {groupId, groupAvatarUrl, wifth, height, resizeMethod, ...otherProps} = this.props; + const {groupId, groupAvatarUrl, ...otherProps} = this.props; return ( Date: Fri, 7 Jul 2017 10:41:59 +0100 Subject: [PATCH 09/14] Fix lint errors --- src/components/structures/LoggedInView.js | 22 +++++++++++---------- src/components/structures/MyGroups.js | 8 ++++---- src/components/views/avatars/GroupAvatar.js | 6 ++++-- 3 files changed, 20 insertions(+), 16 deletions(-) diff --git a/src/components/structures/LoggedInView.js b/src/components/structures/LoggedInView.js index edc918e2a4..f1053618dc 100644 --- a/src/components/structures/LoggedInView.js +++ b/src/components/structures/LoggedInView.js @@ -269,17 +269,19 @@ export default React.createClass({ break; case PageTypes.HomePage: - // If team server config is present, pass the teamServerURL. props.teamToken - // must also be set for the team page to be displayed, otherwise the - // welcomePageUrl is used (which might be undefined). - const teamServerUrl = this.props.config.teamServerConfig ? - this.props.config.teamServerConfig.teamServerURL : null; + { + // If team server config is present, pass the teamServerURL. props.teamToken + // must also be set for the team page to be displayed, otherwise the + // welcomePageUrl is used (which might be undefined). + const teamServerUrl = this.props.config.teamServerConfig ? + this.props.config.teamServerConfig.teamServerURL : null; - page_element = ; + page_element = ; + } break; case PageTypes.UserView: diff --git a/src/components/structures/MyGroups.js b/src/components/structures/MyGroups.js index 222e424b2d..fba6f69d99 100644 --- a/src/components/structures/MyGroups.js +++ b/src/components/structures/MyGroups.js @@ -40,7 +40,7 @@ const GroupTile = React.createClass({ render: function() { return {this.props.groupId}; - } + }, }); export default WithMatrixClient(React.createClass({ @@ -84,12 +84,12 @@ export default WithMatrixClient(React.createClass({ let content; if (this.state.groups) { - let groupNodes = []; + const groupNodes = []; this.state.groups.forEach((g) => { groupNodes.push(
-
+
, ); }); content =
@@ -117,7 +117,7 @@ export default WithMatrixClient(React.createClass({ {_t( 'Create a group to represent your community! '+ 'Define a set of rooms and your own custom homepage '+ - 'to mark out your space in the Matrix universe.' + 'to mark out your space in the Matrix universe.', )}
diff --git a/src/components/views/avatars/GroupAvatar.js b/src/components/views/avatars/GroupAvatar.js index 15c71e59d5..506714e857 100644 --- a/src/components/views/avatars/GroupAvatar.js +++ b/src/components/views/avatars/GroupAvatar.js @@ -38,7 +38,7 @@ export default React.createClass({ }; }, - getGroupAvatarUrl: function(props) { + getGroupAvatarUrl: function() { return MatrixClientPeg.get().mxcUrlToHttp( this.props.groupAvatarUrl, this.props.width, @@ -50,6 +50,8 @@ export default React.createClass({ render: function() { const BaseAvatar = sdk.getComponent("avatars.BaseAvatar"); // extract the props we use from props so we can pass any others through + // should consider adding this as a global rule in js-sdk? + /*eslint no-unused-vars: ["error", { "ignoreRestSiblings": true }]*/ const {groupId, groupAvatarUrl, ...otherProps} = this.props; return ( @@ -60,5 +62,5 @@ export default React.createClass({ {...otherProps} /> ); - } + }, }); From 3b439d4bb22aa2828b344d2cd1d6b3bd95934d8d Mon Sep 17 00:00:00 2001 From: David Baker Date: Fri, 7 Jul 2017 10:50:03 +0100 Subject: [PATCH 10/14] Empty commit Please re-test against update riot-web From fea0a941ce1a7d4f93e72e64b4c91bea9d936cdb Mon Sep 17 00:00:00 2001 From: David Baker Date: Fri, 7 Jul 2017 11:01:54 +0100 Subject: [PATCH 11/14] Fix lint --- src/components/views/dialogs/CreateGroupDialog.js | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/src/components/views/dialogs/CreateGroupDialog.js b/src/components/views/dialogs/CreateGroupDialog.js index 5e050b53b2..c436d938df 100644 --- a/src/components/views/dialogs/CreateGroupDialog.js +++ b/src/components/views/dialogs/CreateGroupDialog.js @@ -20,7 +20,6 @@ import sdk from '../../../index'; import dis from '../../../dispatcher'; import { _t } from '../../../languageHandler'; import MatrixClientPeg from '../../../MatrixClientPeg'; -import AccessibleButton from '../elements/AccessibleButton'; // We match fairly liberally and leave it up to the server to reject if // there are invalid characters etc. @@ -62,14 +61,17 @@ export default React.createClass({ const parsedGroupId = this._parseGroupId(this.state.groupId); let error = null; if (parsedGroupId === null) { - error = _t("Group IDs must be of the form +localpart:%(domain)s", {domain: MatrixClientPeg.get().getDomain()}); + error = _t( + "Group IDs must be of the form +localpart:%(domain)s", + {domain: MatrixClientPeg.get().getDomain()}, + ); } else { - const localpart = parsedGroupId[0]; const domain = parsedGroupId[1]; if (domain !== MatrixClientPeg.get().getDomain()) { error = _t( - "It is currently only possible to create groups on your own home server: use a group ID ending with %(domain)s", - {domain: MatrixClientPeg.get().getDomain()} + "It is currently only possible to create groups on your own home server: "+ + "use a group ID ending with %(domain)s", + {domain: MatrixClientPeg.get().getDomain()}, ); } } @@ -114,6 +116,9 @@ export default React.createClass({ * Parse a string that may be a group ID * If the string is a valid group ID, return a list of [localpart, domain], * otherwise return null. + * + * @param {string} groupId The ID of the group + * @return {string[]} array of localpart, domain */ _parseGroupId: function(groupId) { const matches = GROUP_REGEX.exec(this.state.groupId); From bc8c2d442b339788333b0d3c30a29656c1e71b27 Mon Sep 17 00:00:00 2001 From: David Baker Date: Fri, 7 Jul 2017 11:34:20 +0100 Subject: [PATCH 12/14] WithMatrixClient -> withMatrixClient because we're using it as a function rather than a React component --- src/components/structures/MyGroups.js | 4 ++-- src/components/views/rooms/EventTile.js | 4 ++-- src/components/views/rooms/MemberInfo.js | 4 ++-- src/components/views/settings/AddPhoneNumber.js | 4 ++-- src/wrappers/{WithMatrixClient.js => withMatrixClient.js} | 3 ++- 5 files changed, 10 insertions(+), 9 deletions(-) rename src/wrappers/{WithMatrixClient.js => withMatrixClient.js} (92%) diff --git a/src/components/structures/MyGroups.js b/src/components/structures/MyGroups.js index fba6f69d99..49a2367db8 100644 --- a/src/components/structures/MyGroups.js +++ b/src/components/structures/MyGroups.js @@ -17,7 +17,7 @@ limitations under the License. import React from 'react'; import sdk from '../../index'; import { _t, _tJsx } from '../../languageHandler'; -import WithMatrixClient from '../../wrappers/WithMatrixClient'; +import withMatrixClient from '../../wrappers/withMatrixClient'; import AccessibleButton from '../views/elements/AccessibleButton'; import dis from '../../dispatcher'; import PropTypes from 'prop-types'; @@ -43,7 +43,7 @@ const GroupTile = React.createClass({ }, }); -export default WithMatrixClient(React.createClass({ +export default withMatrixClient(React.createClass({ displayName: 'MyGroups', propTypes: { diff --git a/src/components/views/rooms/EventTile.js b/src/components/views/rooms/EventTile.js index 170925999d..bb085279e8 100644 --- a/src/components/views/rooms/EventTile.js +++ b/src/components/views/rooms/EventTile.js @@ -24,7 +24,7 @@ var Modal = require('../../../Modal'); var sdk = require('../../../index'); var TextForEvent = require('../../../TextForEvent'); -import WithMatrixClient from '../../../wrappers/WithMatrixClient'; +import withMatrixClient from '../../../wrappers/withMatrixClient'; var ContextualMenu = require('../../structures/ContextualMenu'); import dis from '../../../dispatcher'; @@ -59,7 +59,7 @@ var MAX_READ_AVATARS = 5; // | '--------------------------------------' | // '----------------------------------------------------------' -module.exports = WithMatrixClient(React.createClass({ +module.exports = withMatrixClient(React.createClass({ displayName: 'EventTile', propTypes: { diff --git a/src/components/views/rooms/MemberInfo.js b/src/components/views/rooms/MemberInfo.js index 6dc86f9a97..c034f0e704 100644 --- a/src/components/views/rooms/MemberInfo.js +++ b/src/components/views/rooms/MemberInfo.js @@ -36,12 +36,12 @@ import createRoom from '../../../createRoom'; import DMRoomMap from '../../../utils/DMRoomMap'; import Unread from '../../../Unread'; import { findReadReceiptFromUserId } from '../../../utils/Receipt'; -import WithMatrixClient from '../../../wrappers/WithMatrixClient'; +import withMatrixClient from '../../../wrappers/withMatrixClient'; import AccessibleButton from '../elements/AccessibleButton'; import GeminiScrollbar from 'react-gemini-scrollbar'; -module.exports = WithMatrixClient(React.createClass({ +module.exports = withMatrixClient(React.createClass({ displayName: 'MemberInfo', propTypes: { diff --git a/src/components/views/settings/AddPhoneNumber.js b/src/components/views/settings/AddPhoneNumber.js index dd79720e80..7bc551477e 100644 --- a/src/components/views/settings/AddPhoneNumber.js +++ b/src/components/views/settings/AddPhoneNumber.js @@ -19,10 +19,10 @@ import { _t } from '../../../languageHandler'; import sdk from '../../../index'; import AddThreepid from '../../../AddThreepid'; -import WithMatrixClient from '../../../wrappers/WithMatrixClient'; +import withMatrixClient from '../../../wrappers/withMatrixClient'; import Modal from '../../../Modal'; -export default WithMatrixClient(React.createClass({ +export default withMatrixClient(React.createClass({ displayName: 'AddPhoneNumber', propTypes: { diff --git a/src/wrappers/WithMatrixClient.js b/src/wrappers/withMatrixClient.js similarity index 92% rename from src/wrappers/WithMatrixClient.js rename to src/wrappers/withMatrixClient.js index 8e56d17dff..2333358817 100644 --- a/src/wrappers/WithMatrixClient.js +++ b/src/wrappers/withMatrixClient.js @@ -1,5 +1,6 @@ /* Copyright 2015, 2016 OpenMarket Ltd +Copyright 2017 Vector Creations Ltd Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. @@ -26,7 +27,7 @@ import React from 'react'; */ export default function(WrappedComponent) { return React.createClass({ - displayName: "WithMatrixClient<" + WrappedComponent.displayName + ">", + displayName: "withMatrixClient<" + WrappedComponent.displayName + ">", contextTypes: { matrixClient: React.PropTypes.instanceOf(Matrix.MatrixClient).isRequired, From 1e713557bb7e9183af20fb001f05d276353df85f Mon Sep 17 00:00:00 2001 From: David Baker Date: Fri, 7 Jul 2017 18:34:40 +0100 Subject: [PATCH 13/14] PR feedback --- src/components/structures/GroupView.js | 6 ++- src/components/structures/MyGroups.js | 9 ++-- .../views/dialogs/CreateGroupDialog.js | 51 ++++++++++--------- src/i18n/strings/en_EN.json | 5 +- 4 files changed, 37 insertions(+), 34 deletions(-) diff --git a/src/components/structures/GroupView.js b/src/components/structures/GroupView.js index 88c73b75a8..3f321d453d 100644 --- a/src/components/structures/GroupView.js +++ b/src/components/structures/GroupView.js @@ -79,7 +79,7 @@ export default React.createClass({ } let nameNode; - if (summary.profile.name) { + if (summary.profile && summary.profile.name) { nameNode =
{summary.profile.name} @@ -92,6 +92,8 @@ export default React.createClass({
; } + const groupAvatarUrl = summary.profile ? summary.profile.avatar_url : null; + return (
@@ -99,7 +101,7 @@ export default React.createClass({
diff --git a/src/components/structures/MyGroups.js b/src/components/structures/MyGroups.js index 49a2367db8..3eb694acce 100644 --- a/src/components/structures/MyGroups.js +++ b/src/components/structures/MyGroups.js @@ -61,9 +61,6 @@ export default withMatrixClient(React.createClass({ this._fetch(); }, - componentWillUnmount: function() { - }, - _onCreateGroupClick: function() { const CreateGroupDialog = sdk.getComponent("dialogs.CreateGroupDialog"); Modal.createDialog(CreateGroupDialog); @@ -73,7 +70,7 @@ export default withMatrixClient(React.createClass({ this.props.matrixClient.getJoinedGroups().done((result) => { this.setState({groups: result.groups, error: null}); }, (err) => { - this.setState({result: null, error: err}); + this.setState({groups: null, error: err}); }); }, @@ -93,12 +90,12 @@ export default withMatrixClient(React.createClass({ ); }); content =
-
{_t('You are a member of these groups')}:
+
{_t('You are a member of these groups:')}
{groupNodes}
; } else if (this.state.error) { content =
- Error whilst fetching joined groups + {_t('Error whilst fetching joined groups')}
; } else { content = ; diff --git a/src/components/views/dialogs/CreateGroupDialog.js b/src/components/views/dialogs/CreateGroupDialog.js index c436d938df..23194f20a5 100644 --- a/src/components/views/dialogs/CreateGroupDialog.js +++ b/src/components/views/dialogs/CreateGroupDialog.js @@ -130,10 +130,10 @@ export default React.createClass({ render: function() { const BaseDialog = sdk.getComponent('views.dialogs.BaseDialog'); - const Loader = sdk.getComponent("elements.Spinner"); + const Spinner = sdk.getComponent('elements.Spinner'); if (this.state.creating) { - return ; + return ; } let createErrorNode; @@ -154,29 +154,32 @@ export default React.createClass({ >
-
- +
+
+ +
+
+ +
-
- -
-
-
- -
-
- +
+
+ +
+
+ +
{this.state.groupIdError} diff --git a/src/i18n/strings/en_EN.json b/src/i18n/strings/en_EN.json index 6647126c28..dbef0fc7fe 100644 --- a/src/i18n/strings/en_EN.json +++ b/src/i18n/strings/en_EN.json @@ -948,9 +948,10 @@ "Group IDs must be of the form +localpart:%(domain)s": "Group IDs must be of the form +localpart:%(domain)s", "It is currently only possible to create groups on your own home server: use a group ID ending with %(domain)s": "It is currently only possible to create groups on your own home server: use a group ID ending with %(domain)s", "Room creation failed": "Room creation failed", - "You are a member of these groups": "You are a member of these groups", + "You are a member of these groups:": "You are a member of these groups:", "Create a group to represent your community! Define a set of rooms and your own custom homepage to mark out your space in the Matrix universe.": "Create a group to represent your community! Define a set of rooms and your own custom homepage to mark out your space in the Matrix universe.", "Join an existing group": "Join an existing group", "To join an exisitng group you'll have to know its group identifier; this will look something like +example:matrix.org.": "To join an exisitng group you'll have to know its group identifier; this will look something like +example:matrix.org.", - "Autocomplete Delay (ms):": "Autocomplete Delay (ms):" + "Autocomplete Delay (ms):": "Autocomplete Delay (ms):", + "Error whilst fetching joined groups": "Error whilst fetching joined groups" } From 86e717f30db4fe2f2f396656b29b0f4e74eb9792 Mon Sep 17 00:00:00 2001 From: David Baker Date: Sun, 9 Jul 2017 12:34:50 +0100 Subject: [PATCH 14/14] Fix indenting Also autocomplete delay was duplicated --- src/i18n/strings/en_EN.json | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/i18n/strings/en_EN.json b/src/i18n/strings/en_EN.json index dbef0fc7fe..fc4257cbc1 100644 --- a/src/i18n/strings/en_EN.json +++ b/src/i18n/strings/en_EN.json @@ -952,6 +952,5 @@ "Create a group to represent your community! Define a set of rooms and your own custom homepage to mark out your space in the Matrix universe.": "Create a group to represent your community! Define a set of rooms and your own custom homepage to mark out your space in the Matrix universe.", "Join an existing group": "Join an existing group", "To join an exisitng group you'll have to know its group identifier; this will look something like +example:matrix.org.": "To join an exisitng group you'll have to know its group identifier; this will look something like +example:matrix.org.", - "Autocomplete Delay (ms):": "Autocomplete Delay (ms):", - "Error whilst fetching joined groups": "Error whilst fetching joined groups" + "Error whilst fetching joined groups": "Error whilst fetching joined groups" }