mirror of
https://github.com/element-hq/element-web
synced 2024-11-25 02:35:48 +03:00
Merge branch 'develop' into departify
This commit is contained in:
commit
4ff369c884
8 changed files with 124 additions and 56 deletions
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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)}>
|
||||
|
|
|
@ -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>;
|
||||
},
|
||||
|
||||
|
|
|
@ -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>
|
||||
|
@ -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,
|
||||
})),
|
||||
},
|
||||
|
||||
|
|
|
@ -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,
|
||||
};
|
||||
}
|
||||
|
|
|
@ -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!",
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
|
|
Loading…
Reference in a new issue