diff --git a/src/PageTypes.js b/src/PageTypes.js index d87b363a6f..b2346c62c3 100644 --- a/src/PageTypes.js +++ b/src/PageTypes.js @@ -22,4 +22,5 @@ export default { CreateRoom: "create_room", RoomDirectory: "room_directory", UserView: "user_view", + GroupView: "group_view", }; diff --git a/src/components/structures/GroupView.js b/src/components/structures/GroupView.js new file mode 100644 index 0000000000..2368c44319 --- /dev/null +++ b/src/components/structures/GroupView.js @@ -0,0 +1,130 @@ +/* +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 MatrixClientPeg from '../../MatrixClientPeg'; +import sdk from '../../index'; +import sanitizeHtml from "sanitize-html"; +import { sanitizeHtmlParams } from '../../HtmlUtils'; + + +module.exports = React.createClass({ + displayName: 'GroupView', + + propTypes: { + groupId: React.PropTypes.string.isRequired, + }, + + getInitialState: function() { + return { + phase: "GroupView.LOADING", // LOADING / DISPLAY / ERROR / NOT_FOUND + summary: null, + }; + }, + + componentWillMount: function() { + this.setState({ + phase: "GroupView.LOADING", + summary: null, + }) + this._loadGroupFromServer(this.props.groupId) + }, + + componentWillReceiveProps: function(new_props) { + if (this.props.groupId != new_props.groupId) { + this.setState({ + phase: "GroupView.LOADING", + summary: null, + }) + this._loadGroupFromServer(new_props.groupId); + } + }, + + _loadGroupFromServer: function(groupId) { + const self = this; + MatrixClientPeg.get().getGroupSummary(groupId).done(function(res) { + self.setState({ + phase: "GroupView.DISPLAY", + summary: res, + }); + }, function(err) { + self.setState({ + phase: err.errcode == 404 ? "GroupView.NOT_FOUND" :"GroupView.ERROR", + summary: null, + }); + }); + }, + + render: function() { + var BaseAvatar = sdk.getComponent("avatars.BaseAvatar"); + var Loader = sdk.getComponent("elements.Spinner"); + + if (this.state.phase == "GroupView.LOADING") { + return ( +
+ +
+ ); + } else if (this.state.phase == "GroupView.DISPLAY") { + const summary = this.state.summary; + let avatar_url = null; + if (summary.profile.avatar_url) { + avatar_url = MatrixClientPeg.get().mxcUrlToHttp(summary.profile.avatar_url); + } + let description = null; + if (summary.profile.long_description) { + description = sanitizeHtml(summary.profile.long_description); + } + return ( +
+
+
+
+ +
+
+
+ {summary.profile.name} + + ({this.props.groupId}) + +
+
+ {summary.profile.short_description} +
+
+
+
+
+
+ ); + } else if (this.state.phase == "GroupView.NOT_FOUND") { +
+ Group {this.props.groupId} not found +
+ } else { + return ( +
+ Failed to load {this.props.groupId} +
+ ); + } + }, +}); diff --git a/src/components/structures/LoggedInView.js b/src/components/structures/LoggedInView.js index e2fdeb4687..cf9a8310b1 100644 --- a/src/components/structures/LoggedInView.js +++ b/src/components/structures/LoggedInView.js @@ -179,6 +179,7 @@ export default React.createClass({ const CreateRoom = sdk.getComponent('structures.CreateRoom'); const RoomDirectory = sdk.getComponent('structures.RoomDirectory'); const HomePage = sdk.getComponent('structures.HomePage'); + const GroupView = sdk.getComponent('structures.GroupView'); const MatrixToolbar = sdk.getComponent('globals.MatrixToolbar'); const GuestWarningBar = sdk.getComponent('globals.GuestWarningBar'); const NewVersionBar = sdk.getComponent('globals.NewVersionBar'); @@ -247,6 +248,12 @@ export default React.createClass({ page_element = null; // deliberately null for now right_panel = ; break; + case PageTypes.GroupView: + // TODO + page_element = + break; } var topBar; diff --git a/src/components/structures/MatrixChat.js b/src/components/structures/MatrixChat.js index 0dedc02270..8771231e68 100644 --- a/src/components/structures/MatrixChat.js +++ b/src/components/structures/MatrixChat.js @@ -261,6 +261,9 @@ module.exports = React.createClass({ if (this.onUserClick) { linkifyMatrix.onUserClick = this.onUserClick; } + if (this.onGroupClick) { + linkifyMatrix.onGroupClick = this.onGroupClick; + } window.addEventListener('resize', this.handleResize); this.handleResize(); @@ -458,6 +461,12 @@ module.exports = React.createClass({ this._setPage(PageTypes.RoomDirectory); this.notifyNewScreen('directory'); break; + case 'view_group': + const groupId = payload.group_id; + this.setState({currentGroupId: groupId}); + this._setPage(PageTypes.GroupView); + this.notifyNewScreen('group/' + groupId); + break; case 'view_home_page': if (!this._teamToken) { dis.dispatch({action: 'view_room_directory'}); @@ -1001,6 +1010,15 @@ module.exports = React.createClass({ member: member, }); } + } else if (screen.indexOf('group/') == 0) { + const groupId = screen.substring(6); + + // TODO: Check valid group ID + + dis.dispatch({ + action: 'view_group', + group_id: groupId, + }); } else { console.info("Ignoring showScreen for '%s'", screen); } @@ -1029,6 +1047,11 @@ module.exports = React.createClass({ }); }, + onGroupClick: function(event, groupId) { + event.preventDefault(); + dis.dispatch({action: 'view_group', group_id: groupId}); + }, + onLogoutClick: function(event) { dis.dispatch({ action: 'logout', diff --git a/src/linkify-matrix.js b/src/linkify-matrix.js index d9b0b78982..b96730145a 100644 --- a/src/linkify-matrix.js +++ b/src/linkify-matrix.js @@ -108,11 +108,53 @@ function matrixLinkify(linkify) { S_AT_NAME_COLON_DOMAIN.on(TT.DOT, S_AT_NAME_COLON_DOMAIN_DOT); S_AT_NAME_COLON_DOMAIN_DOT.on(TT.DOMAIN, S_AT_NAME_COLON_DOMAIN); S_AT_NAME_COLON_DOMAIN_DOT.on(TT.TLD, S_USERID); + + + var GROUPID = function(value) { + MultiToken.call(this, value); + this.type = 'groupid'; + this.isLink = true; + }; + GROUPID.prototype = new MultiToken(); + + var S_PLUS = new linkify.parser.State(); + var S_PLUS_NAME = new linkify.parser.State(); + var S_PLUS_NAME_COLON = new linkify.parser.State(); + var S_PLUS_NAME_COLON_DOMAIN = new linkify.parser.State(); + var S_PLUS_NAME_COLON_DOMAIN_DOT = new linkify.parser.State(); + var S_GROUPID = new linkify.parser.State(GROUPID); + + var groupid_tokens = [ + TT.DOT, + TT.UNDERSCORE, + TT.PLUS, + TT.NUM, + TT.DOMAIN, + TT.TLD, + + // as in roomname_tokens + TT.LOCALHOST, + ]; + + S_START.on(TT.PLUS, S_PLUS); + + S_PLUS.on(groupid_tokens, S_PLUS_NAME); + S_PLUS_NAME.on(groupid_tokens, S_PLUS_NAME); + S_PLUS_NAME.on(TT.DOMAIN, S_PLUS_NAME); + + S_PLUS_NAME.on(TT.COLON, S_PLUS_NAME_COLON); + + S_PLUS_NAME_COLON.on(TT.DOMAIN, S_PLUS_NAME_COLON_DOMAIN); + S_PLUS_NAME_COLON.on(TT.LOCALHOST, S_GROUPID); // accept +foo:localhost + S_PLUS_NAME_COLON_DOMAIN.on(TT.DOT, S_PLUS_NAME_COLON_DOMAIN_DOT); + S_PLUS_NAME_COLON_DOMAIN_DOT.on(TT.DOMAIN, S_PLUS_NAME_COLON_DOMAIN); + S_PLUS_NAME_COLON_DOMAIN_DOT.on(TT.TLD, S_GROUPID); } // stubs, overwritten in MatrixChat's componentDidMount matrixLinkify.onUserClick = function(e, userId) { e.preventDefault(); }; matrixLinkify.onAliasClick = function(e, roomAlias) { e.preventDefault(); }; +matrixLinkify.onGroupClick = function(e, groupId) { e.preventDefault(); }; var escapeRegExp = function(string) { return string.replace(/[.*+?^${}()|[\]\\]/g, "\\$&"); @@ -143,6 +185,12 @@ matrixLinkify.options = { matrixLinkify.onAliasClick(e, href); } }; + case "groupid": + return { + click: function(e) { + matrixLinkify.onGroupClick(e, href); + } + }; } }, @@ -150,6 +198,7 @@ matrixLinkify.options = { switch (type) { case 'roomalias': case 'userid': + case "groupid": return matrixLinkify.MATRIXTO_BASE_URL + '/#/' + href; default: var m;