Merge pull request #1475 from matrix-org/luke/groups-room-directory-esque
Modify GroupView UI
This commit is contained in:
commit
e4a3309752
4 changed files with 184 additions and 19 deletions
|
@ -29,6 +29,8 @@ import classnames from 'classnames';
|
||||||
|
|
||||||
import GroupStoreCache from '../../stores/GroupStoreCache';
|
import GroupStoreCache from '../../stores/GroupStoreCache';
|
||||||
import GroupStore from '../../stores/GroupStore';
|
import GroupStore from '../../stores/GroupStore';
|
||||||
|
import { showGroupAddRoomDialog } from '../../GroupAddressPicker';
|
||||||
|
import GeminiScrollbar from 'react-gemini-scrollbar';
|
||||||
|
|
||||||
const RoomSummaryType = PropTypes.shape({
|
const RoomSummaryType = PropTypes.shape({
|
||||||
room_id: PropTypes.string.isRequired,
|
room_id: PropTypes.string.isRequired,
|
||||||
|
@ -64,7 +66,7 @@ const CategoryRoomList = React.createClass({
|
||||||
editing: PropTypes.bool.isRequired,
|
editing: PropTypes.bool.isRequired,
|
||||||
},
|
},
|
||||||
|
|
||||||
onAddRoomsClicked: function(ev) {
|
onAddRoomsToSummaryClicked: function(ev) {
|
||||||
ev.preventDefault();
|
ev.preventDefault();
|
||||||
const AddressPickerDialog = sdk.getComponent("dialogs.AddressPickerDialog");
|
const AddressPickerDialog = sdk.getComponent("dialogs.AddressPickerDialog");
|
||||||
Modal.createTrackedDialog('Add Rooms to Group Summary', '', AddressPickerDialog, {
|
Modal.createTrackedDialog('Add Rooms to Group Summary', '', AddressPickerDialog, {
|
||||||
|
@ -106,7 +108,9 @@ const CategoryRoomList = React.createClass({
|
||||||
render: function() {
|
render: function() {
|
||||||
const TintableSvg = sdk.getComponent("elements.TintableSvg");
|
const TintableSvg = sdk.getComponent("elements.TintableSvg");
|
||||||
const addButton = this.props.editing ?
|
const addButton = this.props.editing ?
|
||||||
(<AccessibleButton className="mx_GroupView_featuredThings_addButton" onClick={this.onAddRoomsClicked}>
|
(<AccessibleButton className="mx_GroupView_featuredThings_addButton"
|
||||||
|
onClick={this.onAddRoomsToSummaryClicked}
|
||||||
|
>
|
||||||
<TintableSvg src="img/icons-create-room.svg" width="64" height="64" />
|
<TintableSvg src="img/icons-create-room.svg" width="64" height="64" />
|
||||||
<div className="mx_GroupView_featuredThings_addButton_label">
|
<div className="mx_GroupView_featuredThings_addButton_label">
|
||||||
{ _t('Add a Room') }
|
{ _t('Add a Room') }
|
||||||
|
@ -450,6 +454,7 @@ export default React.createClass({
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
this._groupStore.on('error', (err) => {
|
this._groupStore.on('error', (err) => {
|
||||||
|
console.error(err);
|
||||||
this.setState({
|
this.setState({
|
||||||
summary: null,
|
summary: null,
|
||||||
error: err,
|
error: err,
|
||||||
|
@ -601,6 +606,10 @@ export default React.createClass({
|
||||||
this._setPublicity(true);
|
this._setPublicity(true);
|
||||||
},
|
},
|
||||||
|
|
||||||
|
_onAddRoomsClick: function() {
|
||||||
|
showGroupAddRoomDialog(this.props.groupId);
|
||||||
|
},
|
||||||
|
|
||||||
_setPublicity: function(publicity) {
|
_setPublicity: function(publicity) {
|
||||||
this.setState({
|
this.setState({
|
||||||
publicityBusy: true,
|
publicityBusy: true,
|
||||||
|
@ -612,6 +621,28 @@ export default React.createClass({
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
|
||||||
|
_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 ?
|
||||||
|
(<AccessibleButton onClick={this._onAddRoomsClick} >
|
||||||
|
<div className="mx_GroupView_rooms_header_addButton" >
|
||||||
|
<TintableSvg src="img/icons-room-add.svg" width="24" height="24" />
|
||||||
|
</div>
|
||||||
|
<div className="mx_GroupView_rooms_header_addButton_label">
|
||||||
|
{ _t('Add rooms to this group') }
|
||||||
|
</div>
|
||||||
|
</AccessibleButton>) : <div />;
|
||||||
|
return <div className="mx_GroupView_rooms">
|
||||||
|
<div className="mx_GroupView_rooms_header">
|
||||||
|
<h3>Rooms</h3>
|
||||||
|
{ addButton }
|
||||||
|
</div>
|
||||||
|
<RoomDetailList rooms={this._groupStore.getGroupRooms()} />
|
||||||
|
</div>;
|
||||||
|
},
|
||||||
|
|
||||||
_getFeaturedRoomsNode: function() {
|
_getFeaturedRoomsNode: function() {
|
||||||
const summary = this.state.summary;
|
const summary = this.state.summary;
|
||||||
|
|
||||||
|
@ -799,7 +830,7 @@ export default React.createClass({
|
||||||
let avatarNode;
|
let avatarNode;
|
||||||
let nameNode;
|
let nameNode;
|
||||||
let shortDescNode;
|
let shortDescNode;
|
||||||
let roomBody;
|
let bodyNodes = [];
|
||||||
const rightButtons = [];
|
const rightButtons = [];
|
||||||
const headerClasses = {
|
const headerClasses = {
|
||||||
mx_GroupView_header: true,
|
mx_GroupView_header: true,
|
||||||
|
@ -856,14 +887,15 @@ export default React.createClass({
|
||||||
width="18" height="18" alt={_t("Cancel")} />
|
width="18" height="18" alt={_t("Cancel")} />
|
||||||
</AccessibleButton>,
|
</AccessibleButton>,
|
||||||
);
|
);
|
||||||
roomBody = <div>
|
bodyNodes = [
|
||||||
<textarea className="mx_GroupView_editLongDesc" value={this.state.profileForm.long_description}
|
<textarea className="mx_GroupView_editLongDesc"
|
||||||
|
value={this.state.profileForm.long_description}
|
||||||
onChange={this._onLongDescChange}
|
onChange={this._onLongDescChange}
|
||||||
tabIndex="3"
|
tabIndex="3"
|
||||||
/>
|
key="editLongDesc"
|
||||||
{ this._getFeaturedRoomsNode() }
|
/>,
|
||||||
{ this._getFeaturedUsersNode() }
|
this._getRoomsNode(),
|
||||||
</div>;
|
];
|
||||||
} else {
|
} else {
|
||||||
const groupAvatarUrl = summary.profile ? summary.profile.avatar_url : null;
|
const groupAvatarUrl = summary.profile ? summary.profile.avatar_url : null;
|
||||||
avatarNode = <GroupAvatar
|
avatarNode = <GroupAvatar
|
||||||
|
@ -881,18 +913,19 @@ export default React.createClass({
|
||||||
} else {
|
} else {
|
||||||
nameNode = <span>{ this.props.groupId }</span>;
|
nameNode = <span>{ this.props.groupId }</span>;
|
||||||
}
|
}
|
||||||
shortDescNode = <span>{ summary.profile.short_description }</span>;
|
if (summary.profile && summary.profile.short_description) {
|
||||||
|
shortDescNode = <span>{ summary.profile.short_description }</span>;
|
||||||
|
}
|
||||||
|
|
||||||
let description = null;
|
let description = null;
|
||||||
if (summary.profile && summary.profile.long_description) {
|
if (summary.profile && summary.profile.long_description) {
|
||||||
description = sanitizedHtmlNode(summary.profile.long_description);
|
description = sanitizedHtmlNode(summary.profile.long_description);
|
||||||
}
|
}
|
||||||
roomBody = <div>
|
bodyNodes = [
|
||||||
{ this._getMembershipSection() }
|
this._getMembershipSection(),
|
||||||
<div className="mx_GroupView_groupDesc">{ description }</div>
|
<div key="groupDesc" className="mx_GroupView_groupDesc">{ description }</div>,
|
||||||
{ this._getFeaturedRoomsNode() }
|
this._getRoomsNode(),
|
||||||
{ this._getFeaturedUsersNode() }
|
];
|
||||||
</div>;
|
|
||||||
if (summary.user && summary.user.is_privileged) {
|
if (summary.user && summary.user.is_privileged) {
|
||||||
rightButtons.push(
|
rightButtons.push(
|
||||||
<AccessibleButton className="mx_GroupHeader_button"
|
<AccessibleButton className="mx_GroupHeader_button"
|
||||||
|
@ -935,7 +968,9 @@ export default React.createClass({
|
||||||
{ rightButtons }
|
{ rightButtons }
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
{ roomBody }
|
<GeminiScrollbar className="mx_GroupView_body">
|
||||||
|
{ bodyNodes }
|
||||||
|
</GeminiScrollbar>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
} else if (this.state.error) {
|
} else if (this.state.error) {
|
||||||
|
|
|
@ -148,6 +148,7 @@ module.exports = React.createClass({
|
||||||
onFillRequest: function(backwards) { return Promise.resolve(false); },
|
onFillRequest: function(backwards) { return Promise.resolve(false); },
|
||||||
onUnfillRequest: function(backwards, scrollToken) {},
|
onUnfillRequest: function(backwards, scrollToken) {},
|
||||||
onScroll: function() {},
|
onScroll: function() {},
|
||||||
|
onResize: function() {},
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
|
|
||||||
|
|
128
src/components/views/rooms/RoomDetailList.js
Normal file
128
src/components/views/rooms/RoomDetailList.js
Normal file
|
@ -0,0 +1,128 @@
|
||||||
|
/*
|
||||||
|
Copyright 2017 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 sdk from '../../../index';
|
||||||
|
import dis from '../../../dispatcher';
|
||||||
|
import React from 'react';
|
||||||
|
import { _t } from '../../../languageHandler';
|
||||||
|
import linkifyString from 'linkifyjs/string';
|
||||||
|
import sanitizeHtml from 'sanitize-html';
|
||||||
|
import { ContentRepo } from 'matrix-js-sdk';
|
||||||
|
import MatrixClientPeg from '../../../MatrixClientPeg';
|
||||||
|
import PropTypes from 'prop-types';
|
||||||
|
|
||||||
|
function getDisplayAliasForRoom(room) {
|
||||||
|
return room.canonical_alias || (room.aliases ? room.aliases[0] : "");
|
||||||
|
}
|
||||||
|
|
||||||
|
const RoomDetailRow = React.createClass({
|
||||||
|
onClick: function(ev) {
|
||||||
|
ev.preventDefault();
|
||||||
|
dis.dispatch({
|
||||||
|
action: 'view_room',
|
||||||
|
room_id: this.props.room.room_id,
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
onTopicClick: function(ev) {
|
||||||
|
// When clicking a link in the topic, prevent the event being propagated
|
||||||
|
// to `onClick`.
|
||||||
|
ev.stopPropagation();
|
||||||
|
},
|
||||||
|
|
||||||
|
render: function() {
|
||||||
|
const BaseAvatar = sdk.getComponent('avatars.BaseAvatar');
|
||||||
|
|
||||||
|
const room = this.props.room;
|
||||||
|
const name = room.name || getDisplayAliasForRoom(room) || _t('Unnamed room');
|
||||||
|
const topic = linkifyString(sanitizeHtml(room.topic || ''));
|
||||||
|
|
||||||
|
const guestRead = room.world_readable ? (
|
||||||
|
<div className="mx_RoomDirectory_perm">{ _t('World readable') }</div>
|
||||||
|
) : <div />;
|
||||||
|
const guestJoin = room.guest_can_join ? (
|
||||||
|
<div className="mx_RoomDirectory_perm">{ _t('Guests can join') }</div>
|
||||||
|
) : <div />;
|
||||||
|
|
||||||
|
const perms = (guestRead || guestJoin) ? (<div className="mx_RoomDirectory_perms">
|
||||||
|
{ guestRead }
|
||||||
|
{ guestJoin }
|
||||||
|
</div>) : <div />;
|
||||||
|
|
||||||
|
return <tr key={room.room_id} onClick={this.onClick}>
|
||||||
|
<td className="mx_RoomDirectory_roomAvatar">
|
||||||
|
<BaseAvatar width={24} height={24} resizeMethod='crop'
|
||||||
|
name={name} idName={name}
|
||||||
|
url={ContentRepo.getHttpUriForMxc(
|
||||||
|
MatrixClientPeg.get().getHomeserverUrl(),
|
||||||
|
room.avatar_url, 24, 24, "crop")} />
|
||||||
|
</td>
|
||||||
|
<td className="mx_RoomDirectory_roomDescription">
|
||||||
|
<div className="mx_RoomDirectory_name">{ name }</div>
|
||||||
|
{ perms }
|
||||||
|
<div className="mx_RoomDirectory_topic"
|
||||||
|
onClick={this.onTopicClick}
|
||||||
|
dangerouslySetInnerHTML={{ __html: topic }} />
|
||||||
|
<div className="mx_RoomDirectory_alias">{ getDisplayAliasForRoom(room) }</div>
|
||||||
|
</td>
|
||||||
|
<td className="mx_RoomDirectory_roomMemberCount">
|
||||||
|
{ room.num_joined_members }
|
||||||
|
</td>
|
||||||
|
</tr>;
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
export default React.createClass({
|
||||||
|
displayName: 'RoomDetailList',
|
||||||
|
|
||||||
|
propTypes: {
|
||||||
|
rooms: PropTypes.arrayOf(PropTypes.shape({
|
||||||
|
name: PropTypes.string,
|
||||||
|
topic: PropTypes.string,
|
||||||
|
room_id: PropTypes.string,
|
||||||
|
num_joined_members: PropTypes.number,
|
||||||
|
canonical_alias: PropTypes.string,
|
||||||
|
aliases: PropTypes.arrayOf(PropTypes.string),
|
||||||
|
|
||||||
|
world_readable: PropTypes.bool,
|
||||||
|
guest_can_join: PropTypes.bool,
|
||||||
|
})),
|
||||||
|
},
|
||||||
|
|
||||||
|
getRows: function() {
|
||||||
|
if (!this.props.rooms) return [];
|
||||||
|
return this.props.rooms.map((room, index) => {
|
||||||
|
return <RoomDetailRow key={index} room={room} />;
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
render() {
|
||||||
|
const rows = this.getRows();
|
||||||
|
let rooms;
|
||||||
|
if (rows.length == 0) {
|
||||||
|
rooms = <i>{ _t('No rooms to show') }</i>;
|
||||||
|
} else {
|
||||||
|
rooms = <table ref="directory_table" className="mx_RoomDirectory_table">
|
||||||
|
<tbody>
|
||||||
|
{ this.getRows() }
|
||||||
|
</tbody>
|
||||||
|
</table>;
|
||||||
|
}
|
||||||
|
return <div className="mx_RoomDetailList">
|
||||||
|
{ rooms }
|
||||||
|
</div>;
|
||||||
|
},
|
||||||
|
});
|
|
@ -924,7 +924,8 @@
|
||||||
"Related groups for this room:": "Related groups for this room:",
|
"Related groups for this room:": "Related groups for this room:",
|
||||||
"This room has no related groups": "This room has no related groups",
|
"This room has no related groups": "This room has no related groups",
|
||||||
"New group ID (e.g. +foo:%(localDomain)s)": "New group ID (e.g. +foo:%(localDomain)s)",
|
"New group ID (e.g. +foo:%(localDomain)s)": "New group ID (e.g. +foo:%(localDomain)s)",
|
||||||
|
"%(serverName)s Matrix ID": "%(serverName)s Matrix ID",
|
||||||
|
"Add rooms to this group": "Add rooms to this group",
|
||||||
"Invites sent": "Invites sent",
|
"Invites sent": "Invites sent",
|
||||||
"Your group invitations have been sent.": "Your group invitations have been sent.",
|
"Your group invitations have been sent.": "Your group invitations have been sent."
|
||||||
"%(serverName)s Matrix ID": "%(serverName)s Matrix ID"
|
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue