Merge branch 'develop' into departify

pull/21833/head
Stefan Parviainen 2017-10-24 18:27:24 +02:00
commit 4ff369c884
8 changed files with 124 additions and 56 deletions

View File

@ -232,9 +232,14 @@ for (const path of SEARCH_PATHS) {
const trObj = {};
for (const tr of translatables) {
trObj[tr] = tr;
if (tr.includes("|")) {
trObj[tr] = inputTranslationsRaw[tr];
if (inputTranslationsRaw[tr]) {
trObj[tr] = inputTranslationsRaw[tr];
} else {
trObj[tr] = tr.split("|")[0];
}
} else {
trObj[tr] = tr;
}
}

View File

@ -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 ? <h2> { _t('Community Settings') } </h2> : <div />;
return <div className={groupSettingsSectionClasses}>
{ header }
{ this._getLongDescriptionNode() }
{ this._getRoomsNode() }
</div>;
},
_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" >
const addRoomRow = this.state.editing ?
(<AccessibleButton className="mx_GroupView_rooms_header_addRow"
onClick={this._onAddRoomsClick}
>
<div className="mx_GroupView_rooms_header_addRow_button">
<TintableSvg src="img/icons-room-add.svg" width="24" height="24" />
</div>
<div className="mx_GroupView_rooms_header_addButton_label">
<div className="mx_GroupView_rooms_header_addRow_label">
{ _t('Add rooms to this community') }
</div>
</AccessibleButton>) : <div />;
return <div className="mx_GroupView_rooms">
<div className="mx_GroupView_rooms_header">
<h3>{ _t('Rooms') }</h3>
{ addButton }
{ addRoomRow }
</div>
<RoomDetailList rooms={this._groupStore.getGroupRooms()} />
</div>;
@ -790,7 +816,7 @@ export default React.createClass({
_getMemberSettingsSection: function() {
return <div className="mx_GroupView_memberSettings">
<h3> { _t("Community Member Settings") } </h3>
<h2> { _t("Community Member Settings") } </h2>
<div className="mx_GroupView_memberSettings_toggle">
<input type="checkbox"
onClick={this._onPublicityToggle}
@ -813,8 +839,13 @@ export default React.createClass({
if (summary.profile && summary.profile.long_description) {
description = sanitizedHtmlNode(summary.profile.long_description);
}
return this.state.editing && this.state.isUserPrivileged ?
<div className="mx_GroupView_groupDesc">
const groupDescEditingClasses = classnames({
"mx_GroupView_groupDesc": true,
"mx_GroupView_groupDesc_disabled": !this.state.isUserPrivileged,
});
return this.state.editing ?
<div className={groupDescEditingClasses}>
<h3> { _t("Long Description (HTML)") } </h3>
<textarea
value={this.state.profileForm.long_description}
@ -844,14 +875,10 @@ export default React.createClass({
const bodyNodes = [
this._getMembershipSection(),
this.state.editing ? this._getMemberSettingsSection() : null,
this._getLongDescriptionNode(),
this._getRoomsNode(),
this._getGroupSection(),
];
const rightButtons = [];
const headerClasses = {
mx_GroupView_header: true,
};
if (this.state.editing) {
if (this.state.editing && this.state.isUserPrivileged) {
let avatarImage;
if (this.state.uploadingAvatar) {
avatarImage = <Spinner />;
@ -900,19 +927,6 @@ export default React.createClass({
onValueChanged={this._onShortDescChange}
tabIndex="2"
dir="auto" />;
rightButtons.push(
<AccessibleButton className="mx_GroupView_textButton mx_RoomHeader_textButton"
onClick={this._onSaveClick} key="_saveButton"
>
{ _t('Save') }
</AccessibleButton>,
);
rightButtons.push(
<AccessibleButton className="mx_RoomHeader_cancelButton" onClick={this._onCancelClick} key="_cancelButton">
<img src="img/cancel.svg" className="mx_filterFlipColor"
width="18" height="18" alt={_t("Cancel")} />
</AccessibleButton>,
);
} else {
const groupAvatarUrl = summary.profile ? summary.profile.avatar_url : null;
avatarNode = <GroupAvatar
@ -934,6 +948,22 @@ export default React.createClass({
if (summary.profile && summary.profile.short_description) {
shortDescNode = <span onClick={this._onEditClick}>{ summary.profile.short_description }</span>;
}
}
if (this.state.editing) {
rightButtons.push(
<AccessibleButton className="mx_GroupView_textButton mx_RoomHeader_textButton"
onClick={this._onSaveClick} key="_saveButton"
>
{ _t('Save') }
</AccessibleButton>,
);
rightButtons.push(
<AccessibleButton className="mx_RoomHeader_cancelButton" onClick={this._onCancelClick} key="_cancelButton">
<img src="img/cancel.svg" className="mx_filterFlipColor"
width="18" height="18" alt={_t("Cancel")} />
</AccessibleButton>,
);
} else {
rightButtons.push(
<AccessibleButton className="mx_GroupHeader_button"
onClick={this._onEditClick} title={_t("Community Settings")} key="_editButton"
@ -950,10 +980,13 @@ export default React.createClass({
</AccessibleButton>,
);
}
headerClasses.mx_GroupView_header_view = true;
}
const headerClasses = {
mx_GroupView_header: true,
mx_GroupView_header_view: !this.state.editing,
};
return (
<div className="mx_GroupView">
<div className={classnames(headerClasses)}>

View File

@ -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,12 +106,11 @@ export default withMatrixClient(React.createClass({
});
}
memberList = memberList.map((m) => {
return (
<GroupMemberTile key={m.userId} groupId={this.props.groupId} member={m} />
);
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) {
@ -122,10 +122,16 @@ export default withMatrixClient(React.createClass({
}
});
const memberTiles = memberList.map((m) => {
return (
<GroupMemberTile key={m.userId} groupId={this.props.groupId} member={m} />
);
});
return <TruncatedList className="mx_MemberList_wrapper" truncateAt={this.state.truncateAt}
createOverflowElement={this._createOverflowTile}
>
{ memberList }
{ memberTiles }
</TruncatedList>;
},

View File

@ -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 ? (
<div className="mx_RoomDirectory_perm">{ _t('World readable') }</div>
) : <div />;
const guestJoin = room.guest_can_join ? (
const guestJoin = room.guestCanJoin ? (
<div className="mx_RoomDirectory_perm">{ _t('Guests can join') }</div>
) : <div />;
@ -62,13 +75,13 @@ const RoomDetailRow = React.createClass({
{ guestJoin }
</div>) : <div />;
return <tr key={room.room_id} onClick={this.onClick}>
return <tr key={room.roomId} 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")} />
room.avatarUrl, 24, 24, "crop")} />
</td>
<td className="mx_RoomDirectory_roomDescription">
<div className="mx_RoomDirectory_name">{ name }</div>&nbsp;
@ -79,7 +92,7 @@ const RoomDetailRow = React.createClass({
<div className="mx_RoomDirectory_alias">{ getDisplayAliasForRoom(room) }</div>
</td>
<td className="mx_RoomDirectory_roomMemberCount">
{ room.num_joined_members }
{ room.numJoinedMembers }
</td>
</tr>;
},
@ -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,
})),
},

View File

@ -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,
guestCanJoin: apiObject.guest_can_join,
};
}

View File

@ -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",
"%(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...",
"ex. @bob:example.com": "ex. @bob:example.com",
"Add User": "Add User",
"Something went wrong!": "Something went wrong!",

View File

@ -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;

View File

@ -72,6 +72,13 @@ class RoomViewStore extends Store {
case 'view_room':
this._viewRoom(payload);
break;
case 'view_my_groups':
case 'view_group':
this._setState({
roomId: null,
roomAlias: null,
});
break;
case 'view_room_error':
this._viewRoomError(payload);
break;