Merge remote-tracking branch 'origin/develop' into dbkr/roomcreate

This commit is contained in:
David Baker 2018-08-29 18:18:08 +01:00
commit c40ac49b2a
25 changed files with 410 additions and 92 deletions

View file

@ -19,6 +19,7 @@ import {ContentRepo} from "matrix-js-sdk";
import MatrixClientPeg from "../../../MatrixClientPeg";
import Modal from '../../../Modal';
import sdk from "../../../index";
import DMRoomMap from '../../../utils/DMRoomMap';
module.exports = React.createClass({
displayName: 'RoomAvatar',
@ -107,58 +108,37 @@ module.exports = React.createClass({
},
getOneToOneAvatar: function(props) {
if (!props.room) return null;
const mlist = props.room.currentState.members;
const userIds = [];
const leftUserIds = [];
// for .. in optimisation to return early if there are >2 keys
for (const uid in mlist) {
if (mlist.hasOwnProperty(uid)) {
if (["join", "invite"].includes(mlist[uid].membership)) {
userIds.push(uid);
} else {
leftUserIds.push(uid);
}
}
if (userIds.length > 2) {
return null;
}
const room = props.room;
if (!room) {
return null;
}
if (userIds.length == 2) {
let theOtherGuy = null;
if (mlist[userIds[0]].userId == MatrixClientPeg.get().credentials.userId) {
theOtherGuy = mlist[userIds[1]];
} else {
theOtherGuy = mlist[userIds[0]];
}
return theOtherGuy.getAvatarUrl(
MatrixClientPeg.get().getHomeserverUrl(),
Math.floor(props.width * window.devicePixelRatio),
Math.floor(props.height * window.devicePixelRatio),
props.resizeMethod,
false,
);
} else if (userIds.length == 1) {
// The other 1-1 user left, leaving just the current user, so show the left user's avatar
if (leftUserIds.length === 1) {
return mlist[leftUserIds[0]].getAvatarUrl(
MatrixClientPeg.get().getHomeserverUrl(),
props.width, props.height, props.resizeMethod,
false,
);
}
return mlist[userIds[0]].getAvatarUrl(
MatrixClientPeg.get().getHomeserverUrl(),
Math.floor(props.width * window.devicePixelRatio),
Math.floor(props.height * window.devicePixelRatio),
props.resizeMethod,
false,
);
let otherMember = null;
const otherUserId = DMRoomMap.shared().getUserIdForRoomId(room.roomId);
if (otherUserId) {
otherMember = room.getMember(otherUserId);
} else {
return null;
// if the room is not marked as a 1:1, but only has max 2 members
// then still try to show any avatar (pref. other member)
const totalMemberCount = room.getJoinedMemberCount() +
room.getInvitedMemberCount();
const members = room.currentState.getMembers();
if (totalMemberCount == 2) {
const myUserId = MatrixClientPeg.get().getUserId();
otherMember = members.find(m => m.userId !== myUserId);
} else if (totalMemberCount == 1) {
otherMember = members[0];
}
}
if (otherMember) {
return otherMember.getAvatarUrl(
MatrixClientPeg.get().getHomeserverUrl(),
Math.floor(props.width * window.devicePixelRatio),
Math.floor(props.height * window.devicePixelRatio),
props.resizeMethod,
false,
);
}
return null;
},
onRoomAvatarClick: function() {

View file

@ -346,20 +346,18 @@ module.exports = React.createClass({
},
render: function() {
const myMember = this.props.room.getMember(
MatrixClientPeg.get().credentials.userId,
);
const myMembership = this.props.room.getMyMembership();
// Can't set notif level or tags on non-join rooms
if (myMember.membership !== 'join') {
return this._renderLeaveMenu(myMember.membership);
if (myMembership !== 'join') {
return this._renderLeaveMenu(myMembership);
}
return (
<div>
{ this._renderNotifMenu() }
<hr className="mx_RoomTileContextMenu_separator" />
{ this._renderLeaveMenu(myMember.membership) }
{ this._renderLeaveMenu(myMembership) }
<hr className="mx_RoomTileContextMenu_separator" />
{ this._renderRoomTagMenu() }
</div>

View file

@ -0,0 +1,106 @@
/*
Copyright 2018 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 React from 'react';
import PropTypes from 'prop-types';
import sdk from '../../../index';
import MatrixClientPeg from '../../../MatrixClientPeg';
import Modal from '../../../Modal';
import { _t } from '../../../languageHandler';
export default React.createClass({
displayName: 'RoomUpgradeDialog',
propTypes: {
room: PropTypes.object.isRequired,
onFinished: PropTypes.func.isRequired,
},
componentWillMount: function() {
this._targetVersion = this.props.room.shouldUpgradeToVersion();
},
getInitialState: function() {
return {
busy: false,
};
},
_onCancelClick: function() {
this.props.onFinished(false);
},
_onUpgradeClick: function() {
this.setState({busy: true});
MatrixClientPeg.get().upgradeRoom(this.props.room.roomId, this._targetVersion).catch((err) => {
const ErrorDialog = sdk.getComponent("dialogs.ErrorDialog");
Modal.createTrackedDialog('Failed to upgrade room', '', ErrorDialog, {
title: _t("Failed to upgrade room"),
description: ((err && err.message) ? err.message : _t("The room upgrade could not be completed")),
});
}).finally(() => {
this.setState({busy: false});
});
},
render: function() {
const BaseDialog = sdk.getComponent('views.dialogs.BaseDialog');
const DialogButtons = sdk.getComponent('views.elements.DialogButtons');
const Spinner = sdk.getComponent('views.elements.Spinner');
let buttons;
if (this.state.busy) {
buttons = <Spinner />;
} else {
buttons = <DialogButtons
primaryButton={_t(
'Upgrade this room to version %(version)s',
{version: this._targetVersion},
)}
primaryButtonClass="danger"
hasCancel={true}
onPrimaryButtonClick={this._onUpgradeClick}
focus={this.props.focus}
onCancel={this._onCancelClick}
/>;
}
return (
<BaseDialog className="mx_RoomUpgradeDialog"
onFinished={this.onCancelled}
title={_t("Upgrade Room Version")}
contentId='mx_Dialog_content'
onFinished={this.props.onFinished}
hasCancel={true}
>
<p>
{_t(
"Upgrading this room requires closing down the current " +
"instance of the room and creating a new room it its place. " +
"To give room members the best possible experience, we will:",
)}
</p>
<ol>
<li>{_t("Create a new room with the same name, description and avatar")}</li>
<li>{_t("Update any local room aliases to point to the new room")}</li>
<li>{_t("Stop users from speaking in the old version of the room, and post a message advising users to move to the new room")}</li>
<li>{_t("Put a link back to the old room at the start of the new room so people can see old messages")}</li>
</ol>
{buttons}
</BaseDialog>
);
},
});

View file

@ -71,6 +71,23 @@ export default class MessageComposer extends React.Component {
// XXX: fragile as all hell - fixme somehow, perhaps with a dedicated Room.encryption event or something.
MatrixClientPeg.get().on("event", this.onEvent);
this._roomStoreToken = RoomViewStore.addListener(this._onRoomViewStoreUpdate);
this._waitForOwnMember();
}
_waitForOwnMember() {
// if we have the member already, do that
const me = this.props.room.getMember(MatrixClientPeg.get().getUserId());
if (me) {
this.setState({me});
return;
}
// Otherwise, wait for member loading to finish and then update the member for the avatar.
// The members should already be loading, and loadMembersIfNeeded
// will return the promise for the existing operation
this.props.room.loadMembersIfNeeded().then(() => {
const me = this.props.room.getMember(MatrixClientPeg.get().getUserId());
this.setState({me});
});
}
componentWillUnmount() {
@ -208,7 +225,6 @@ export default class MessageComposer extends React.Component {
}
render() {
const me = this.props.room.getMember(MatrixClientPeg.get().credentials.userId);
const uploadInputStyle = {display: 'none'};
const MemberAvatar = sdk.getComponent('avatars.MemberAvatar');
const TintableSvg = sdk.getComponent("elements.TintableSvg");
@ -216,11 +232,13 @@ export default class MessageComposer extends React.Component {
const controls = [];
controls.push(
<div key="controls_avatar" className="mx_MessageComposer_avatar">
<MemberAvatar member={me} width={24} height={24} />
</div>,
);
if (this.state.me) {
controls.push(
<div key="controls_avatar" className="mx_MessageComposer_avatar">
<MemberAvatar member={this.state.me} width={24} height={24} />
</div>,
);
}
let e2eImg, e2eTitle, e2eClass;
const roomIsEncrypted = MatrixClientPeg.get().isRoomEncrypted(this.props.room.roomId);

View file

@ -97,7 +97,7 @@ module.exports = React.createClass({
};
// All rooms that should be kept in the room list when filtering.
// By default, show all rooms.
this._visibleRooms = MatrixClientPeg.get().getRooms();
this._visibleRooms = MatrixClientPeg.get().getVisibleRooms();
// Listen to updates to group data. RoomList cares about members and rooms in order
// to filter the room list when group tags are selected.
@ -302,7 +302,7 @@ module.exports = React.createClass({
this._visibleRooms = Array.from(roomSet);
} else {
// Show all rooms
this._visibleRooms = MatrixClientPeg.get().getRooms();
this._visibleRooms = MatrixClientPeg.get().getVisibleRooms();
}
this._delayedRefreshRoomList();
},

View file

@ -572,6 +572,11 @@ module.exports = React.createClass({
});
},
_onRoomUpgradeClick: function() {
const RoomUpgradeDialog = sdk.getComponent('dialogs.RoomUpgradeDialog');
Modal.createTrackedDialog('Upgrade Room Version', '', RoomUpgradeDialog, {room: this.props.room});
},
_onRoomMemberMembership: function() {
// Update, since our banned user list may have changed
this.forceUpdate();
@ -794,15 +799,15 @@ module.exports = React.createClass({
}
let leaveButton = null;
const myMember = this.props.room.getMember(myUserId);
if (myMember) {
if (myMember.membership === "join") {
const myMemberShip = this.props.room.getMyMembership();
if (myMemberShip) {
if (myMemberShip === "join") {
leaveButton = (
<AccessibleButton className="mx_RoomSettings_leaveButton" onClick={this.onLeaveClick}>
{ _t('Leave room') }
</AccessibleButton>
);
} else if (myMember.membership === "leave") {
} else if (myMemberShip === "leave") {
leaveButton = (
<AccessibleButton className="mx_RoomSettings_leaveButton" onClick={this.onForgetClick}>
{ _t('Forget room') }
@ -930,6 +935,13 @@ module.exports = React.createClass({
);
});
let roomUpgradeButton = null;
if (this.props.room.shouldUpgradeToVersion() && this.props.room.userMayUpgradeRoom(myUserId)) {
roomUpgradeButton = <AccessibleButton className="mx_RoomSettings_upgradeButton danger" onClick={this._onRoomUpgradeClick}>
{ _t("Upgrade room to version %(ver)s", {ver: this.props.room.shouldUpgradeToVersion()}) }
</AccessibleButton>;
}
return (
<div className="mx_RoomSettings">
@ -1041,7 +1053,8 @@ module.exports = React.createClass({
<h3>{ _t('Advanced') }</h3>
<div className="mx_RoomSettings_settings">
{ _t('Internal room ID: ') } <code>{ this.props.room.roomId }</code><br />
{ _t('Room version number: ') } <code>{ this.props.room.getVersion() }</code>
{ _t('Room version number: ') } <code>{ this.props.room.getVersion() }</code><br />
{ roomUpgradeButton }
</div>
</div>
);

View file

@ -0,0 +1,57 @@
/*
Copyright 2018 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 React from 'react';
import PropTypes from 'prop-types';
import sdk from '../../../index';
import Modal from '../../../Modal';
import { _t } from '../../../languageHandler';
module.exports = React.createClass({
displayName: 'RoomUpgradeWarningBar',
propTypes: {
room: PropTypes.object.isRequired,
},
onUpgradeClick: function() {
const RoomUpgradeDialog = sdk.getComponent('dialogs.RoomUpgradeDialog');
Modal.createTrackedDialog('Upgrade Room Version', '', RoomUpgradeDialog, {room: this.props.room});
},
render: function() {
const AccessibleButton = sdk.getComponent('elements.AccessibleButton');
return (
<div className="mx_RoomUpgradeWarningBar">
<div className="mx_RoomUpgradeWarningBar_header">
{_t("There is a known vulnerability affecting this room.")}
</div>
<div className="mx_RoomUpgradeWarningBar_body">
{_t("This room version is vulnerable to malicious modification of room state.")}
</div>
<p className="mx_RoomUpgradeWarningBar_upgradelink">
<AccessibleButton onClick={this.onUpgradeClick}>
{_t("Click here to upgrade to the latest room version and ensure room integrity is protected.")}
</AccessibleButton>
</p>
<div className="mx_RoomUpgradeWarningBar_small">
{_t("Only room administrators will see this warning")}
</div>
</div>
);
},
});