Merge pull request #1196 from matrix-org/dbkr/groups_better_groupview

Add more features to Group View
This commit is contained in:
Luke Barnard 2017-07-11 17:03:47 +01:00 committed by GitHub
commit af3b6484cd
4 changed files with 259 additions and 9 deletions

View file

@ -18,9 +18,154 @@ import React from 'react';
import PropTypes from 'prop-types'; import PropTypes from 'prop-types';
import MatrixClientPeg from '../../MatrixClientPeg'; import MatrixClientPeg from '../../MatrixClientPeg';
import sdk from '../../index'; import sdk from '../../index';
import dis from '../../dispatcher';
import { sanitizedHtmlNode } from '../../HtmlUtils'; import { sanitizedHtmlNode } from '../../HtmlUtils';
import { _t } from '../../languageHandler'; import { _t } from '../../languageHandler';
import AccessibleButton from '../views/elements/AccessibleButton';
const RoomSummaryType = PropTypes.shape({
room_id: PropTypes.string.isRequired,
profile: PropTypes.shape({
name: PropTypes.string,
avatar_url: PropTypes.string,
canonical_alias: PropTypes.string,
}).isRequired,
});
const UserSummaryType = PropTypes.shape({
summaryInfo: PropTypes.shape({
user_id: PropTypes.string.isRequired,
}).isRequired,
});
const CategoryRoomList = React.createClass({
displayName: 'CategoryRoomList',
props: {
rooms: PropTypes.arrayOf(RoomSummaryType).isRequired,
category: PropTypes.shape({
profile: PropTypes.shape({
name: PropTypes.string,
}).isRequired,
}),
},
render: function() {
const roomNodes = this.props.rooms.map((r) => {
return <FeaturedRoom key={r.room_id} summaryInfo={r} />;
});
let catHeader = null;
if (this.props.category && this.props.category.profile) {
catHeader = <div className="mx_GroupView_featuredThings_category">{this.props.category.profile.name}</div>;
}
return <div>
{catHeader}
{roomNodes}
</div>;
},
});
const FeaturedRoom = React.createClass({
displayName: 'FeaturedRoom',
props: {
summaryInfo: RoomSummaryType.isRequired,
},
onClick: function(e) {
e.preventDefault();
e.stopPropagation();
dis.dispatch({
action: 'view_room',
room_alias: this.props.summaryInfo.profile.canonical_alias,
room_id: this.props.summaryInfo.room_id,
});
},
render: function() {
const RoomAvatar = sdk.getComponent("avatars.RoomAvatar");
const oobData = {
roomId: this.props.summaryInfo.room_id,
avatarUrl: this.props.summaryInfo.profile.avatar_url,
name: this.props.summaryInfo.profile.name,
};
let permalink = null;
if (this.props.summaryInfo.profile && this.props.summaryInfo.profile.canonical_alias) {
permalink = 'https://matrix.to/#/' + this.props.summaryInfo.profile.canonical_alias;
}
let roomNameNode = null;
if (permalink) {
roomNameNode = <a href={permalink} onClick={this.onClick} >{this.props.summaryInfo.profile.name}</a>;
} else {
roomNameNode = <span>{this.props.summaryInfo.profile.name}</span>;
}
return <AccessibleButton className="mx_GroupView_featuredThing" onClick={this.onClick}>
<RoomAvatar oobData={oobData} width={64} height={64} />
<div className="mx_GroupView_featuredThing_name">{roomNameNode}</div>
</AccessibleButton>;
},
});
const RoleUserList = React.createClass({
displayName: 'RoleUserList',
props: {
users: PropTypes.arrayOf(UserSummaryType).isRequired,
role: PropTypes.shape({
profile: PropTypes.shape({
name: PropTypes.string,
}).isRequired,
}),
},
render: function() {
const userNodes = this.props.users.map((u) => {
return <FeaturedUser key={u.user_id} summaryInfo={u} />;
});
let roleHeader = null;
if (this.props.role && this.props.role.profile) {
roleHeader = <div className="mx_GroupView_featuredThings_category">{this.props.role.profile.name}</div>;
}
return <div>
{roleHeader}
{userNodes}
</div>;
},
});
const FeaturedUser = React.createClass({
displayName: 'FeaturedUser',
props: {
summaryInfo: UserSummaryType.isRequired,
},
onClick: function(e) {
e.preventDefault();
e.stopPropagation();
dis.dispatch({
action: 'view_start_chat_or_reuse',
user_id: this.props.summaryInfo.user_id,
go_home_on_cancel: false,
});
},
render: function() {
// Add avatar once we get profile info inline in the summary response
//const BaseAvatar = sdk.getComponent("avatars.BaseAvatar");
const permalink = 'https://matrix.to/#/' + this.props.summaryInfo.user_id;
const userNameNode = <a href={permalink} onClick={this.onClick} >{this.props.summaryInfo.user_id}</a>;
return <AccessibleButton className="mx_GroupView_featuredThing" onClick={this.onClick}>
<div className="mx_GroupView_featuredThing_name">{userNameNode}</div>
</AccessibleButton>;
},
});
export default React.createClass({ export default React.createClass({
displayName: 'GroupView', displayName: 'GroupView',
@ -33,6 +178,7 @@ export default React.createClass({
return { return {
summary: null, summary: null,
error: null, error: null,
editing: false,
}; };
}, },
@ -65,12 +211,95 @@ export default React.createClass({
}); });
}, },
_onSettingsClick: function() {
this.setState({editing: true});
},
_getFeaturedRoomsNode() {
const summary = this.state.summary;
if (summary.rooms_section.rooms.length == 0) return null;
const defaultCategoryRooms = [];
const categoryRooms = {};
summary.rooms_section.rooms.forEach((r) => {
if (r.category_id === null) {
defaultCategoryRooms.push(r);
} else {
let list = categoryRooms[r.category_id];
if (list === undefined) {
list = [];
categoryRooms[r.category_id] = list;
}
list.push(r);
}
});
let defaultCategoryNode = null;
if (defaultCategoryRooms.length > 0) {
defaultCategoryNode = <CategoryRoomList rooms={defaultCategoryRooms} />;
}
const categoryRoomNodes = Object.keys(categoryRooms).map((catId) => {
const cat = summary.rooms_section.categories[catId];
return <CategoryRoomList key={catId} rooms={categoryRooms[catId]} category={cat} />;
});
return <div className="mx_GroupView_featuredThings">
<div className="mx_GroupView_featuredThings_header">
{_t('Featured Rooms:')}
</div>
{defaultCategoryNode}
{categoryRoomNodes}
</div>;
},
_getFeaturedUsersNode() {
const summary = this.state.summary;
if (summary.users_section.users.length == 0) return null;
const noRoleUsers = [];
const roleUsers = {};
summary.users_section.users.forEach((u) => {
if (u.role_id === null) {
noRoleUsers.push(u);
} else {
let list = roleUsers[u.role_id];
if (list === undefined) {
list = [];
roleUsers[u.role_id] = list;
}
list.push(u);
}
});
let noRoleNode = null;
if (noRoleUsers.length > 0) {
noRoleNode = <RoleUserList users={noRoleUsers} />;
}
const roleUserNodes = Object.keys(roleUsers).map((roleId) => {
const role = summary.users_section.roles[roleId];
return <RoleUserList key={roleId} users={roleUsers[roleId]} role={role} />;
});
return <div className="mx_GroupView_featuredThings">
<div className="mx_GroupView_featuredThings_header">
{_t('Featured Users:')}
</div>
{noRoleNode}
{roleUserNodes}
</div>;
},
render: function() { render: function() {
const GroupAvatar = sdk.getComponent("avatars.GroupAvatar"); const GroupAvatar = sdk.getComponent("avatars.GroupAvatar");
const Loader = sdk.getComponent("elements.Spinner"); const Loader = sdk.getComponent("elements.Spinner");
const TintableSvg = sdk.getComponent("elements.TintableSvg");
if (this.state.summary === null && this.state.error === null) { if (this.state.summary === null && this.state.error === null) {
return <Loader />; return <Loader />;
} else if (this.state.editing) {
return <div />;
} else if (this.state.summary) { } else if (this.state.summary) {
const summary = this.state.summary; const summary = this.state.summary;
let description = null; let description = null;
@ -78,6 +307,12 @@ export default React.createClass({
description = sanitizedHtmlNode(summary.profile.long_description); description = sanitizedHtmlNode(summary.profile.long_description);
} }
const roomBody = <div>
<div className="mx_GroupView_groupDesc">{description}</div>
{this._getFeaturedRoomsNode()}
{this._getFeaturedUsersNode()}
</div>;
let nameNode; let nameNode;
if (summary.profile && summary.profile.name) { if (summary.profile && summary.profile.name) {
nameNode = <div className="mx_RoomHeader_name"> nameNode = <div className="mx_RoomHeader_name">
@ -94,6 +329,7 @@ export default React.createClass({
const groupAvatarUrl = summary.profile ? summary.profile.avatar_url : null; const groupAvatarUrl = summary.profile ? summary.profile.avatar_url : null;
// settings button is display: none until settings is wired up
return ( return (
<div className="mx_GroupView"> <div className="mx_GroupView">
<div className="mx_RoomHeader"> <div className="mx_RoomHeader">
@ -111,9 +347,12 @@ export default React.createClass({
{summary.profile.short_description} {summary.profile.short_description}
</div> </div>
</div> </div>
<AccessibleButton className="mx_RoomHeader_button" onClick={this._onSettingsClick} title={_t("Settings")} style={{display: 'none'}}>
<TintableSvg src="img/icons-settings-room.svg" width="16" height="16"/>
</AccessibleButton>
</div> </div>
</div> </div>
{description} {roomBody}
</div> </div>
); );
} else if (this.state.error) { } else if (this.state.error) {

View file

@ -506,7 +506,7 @@ module.exports = React.createClass({
this._setMxId(payload); this._setMxId(payload);
break; break;
case 'view_start_chat_or_reuse': case 'view_start_chat_or_reuse':
this._chatCreateOrReuse(payload.user_id); this._chatCreateOrReuse(payload.user_id, payload.go_home_on_cancel);
break; break;
case 'view_create_chat': case 'view_create_chat':
this._createChat(); this._createChat();
@ -801,7 +801,9 @@ module.exports = React.createClass({
}); });
}, },
_chatCreateOrReuse: function(userId) { _chatCreateOrReuse: function(userId, goHomeOnCancel) {
if (goHomeOnCancel === undefined) goHomeOnCancel = true;
const ChatCreateOrReuseDialog = sdk.getComponent( const ChatCreateOrReuseDialog = sdk.getComponent(
'views.dialogs.ChatCreateOrReuseDialog', 'views.dialogs.ChatCreateOrReuseDialog',
); );
@ -832,7 +834,7 @@ module.exports = React.createClass({
const close = Modal.createDialog(ChatCreateOrReuseDialog, { const close = Modal.createDialog(ChatCreateOrReuseDialog, {
userId: userId, userId: userId,
onFinished: (success) => { onFinished: (success) => {
if (!success) { if (!success && goHomeOnCancel) {
// Dialog cancelled, default to home // Dialog cancelled, default to home
dis.dispatch({ action: 'view_home_page' }); dis.dispatch({ action: 'view_home_page' });
} }

View file

@ -72,7 +72,7 @@ module.exports = React.createClass({
}, },
getRoomAvatarUrl: function(props) { getRoomAvatarUrl: function(props) {
if (!this.props.room) return null; if (!props.room) return null;
return props.room.getAvatarUrl( return props.room.getAvatarUrl(
MatrixClientPeg.get().getHomeserverUrl(), MatrixClientPeg.get().getHomeserverUrl(),
@ -84,7 +84,7 @@ module.exports = React.createClass({
}, },
getOneToOneAvatar: function(props) { getOneToOneAvatar: function(props) {
if (!this.props.room) return null; if (!props.room) return null;
var mlist = props.room.currentState.members; var mlist = props.room.currentState.members;
var userIds = []; var userIds = [];
@ -126,9 +126,16 @@ module.exports = React.createClass({
}, },
getFallbackAvatar: function(props) { getFallbackAvatar: function(props) {
if (!this.props.room) return null; let roomId = null;
if (props.oobData && props.oobData.roomId) {
roomId = this.props.oobData.roomId;
} else if (props.room) {
roomId = props.room.roomId;
} else {
return null;
}
return Avatar.defaultAvatarUrlForString(props.room.roomId); return Avatar.defaultAvatarUrlForString(roomId);
}, },
render: function() { render: function() {

View file

@ -954,5 +954,7 @@
"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.", "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", "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 <i>+example:matrix.org</i>.": "To join an exisitng group you'll have to know its group identifier; this will look something like <i>+example:matrix.org</i>.", "To join an exisitng group you'll have to know its group identifier; this will look something like <i>+example:matrix.org</i>.": "To join an exisitng group you'll have to know its group identifier; this will look something like <i>+example:matrix.org</i>.",
"Error whilst fetching joined groups": "Error whilst fetching joined groups" "Featured Rooms:": "Featured Rooms:",
"Error whilst fetching joined groups": "Error whilst fetching joined groups",
"Featured Users:": "Featured Users:"
} }