Merge branch 'develop' into wmwragg/direct-chat-sublist
This commit is contained in:
commit
769e7d3b2e
24 changed files with 707 additions and 130 deletions
|
@ -47,6 +47,9 @@ module.exports = React.createClass({
|
|||
},
|
||||
|
||||
_getState: function(props) {
|
||||
if (!props.member) {
|
||||
console.error("MemberAvatar called somehow with null member");
|
||||
}
|
||||
return {
|
||||
name: props.member.name,
|
||||
title: props.member.userId,
|
||||
|
|
|
@ -22,12 +22,6 @@ var sdk = require('../../../index');
|
|||
module.exports = React.createClass({
|
||||
displayName: 'MessageEvent',
|
||||
|
||||
statics: {
|
||||
needsSenderProfile: function() {
|
||||
return true;
|
||||
}
|
||||
},
|
||||
|
||||
propTypes: {
|
||||
/* the MatrixEvent to show */
|
||||
mxEvent: React.PropTypes.object.isRequired,
|
||||
|
|
|
@ -24,12 +24,6 @@ import sdk from '../../../index';
|
|||
module.exports = React.createClass({
|
||||
displayName: 'TextualEvent',
|
||||
|
||||
statics: {
|
||||
needsSenderProfile: function() {
|
||||
return false;
|
||||
}
|
||||
},
|
||||
|
||||
render: function() {
|
||||
const EmojiText = sdk.getComponent('elements.EmojiText');
|
||||
var text = TextForEvent.textForEvent(this.props.mxEvent);
|
||||
|
@ -39,4 +33,3 @@ module.exports = React.createClass({
|
|||
);
|
||||
},
|
||||
});
|
||||
|
||||
|
|
|
@ -1,7 +1,8 @@
|
|||
import React from 'react';
|
||||
import ReactCSSTransitionGroup from 'react-addons-css-transition-group';
|
||||
import ReactDOM from 'react-dom';
|
||||
import classNames from 'classnames';
|
||||
import flatMap from 'lodash/flatMap';
|
||||
import sdk from '../../../index';
|
||||
|
||||
import {getCompletions} from '../../../autocomplete/Autocompleter';
|
||||
|
||||
|
@ -100,11 +101,27 @@ export default class Autocomplete extends React.Component {
|
|||
this.setState({selectionOffset});
|
||||
}
|
||||
|
||||
componentDidUpdate() {
|
||||
// this is the selected completion, so scroll it into view if needed
|
||||
const selectedCompletion = this.refs[`completion${this.state.selectionOffset}`];
|
||||
if (selectedCompletion && this.container) {
|
||||
const domNode = ReactDOM.findDOMNode(selectedCompletion);
|
||||
const offsetTop = domNode && domNode.offsetTop;
|
||||
if (offsetTop > this.container.scrollTop + this.container.offsetHeight ||
|
||||
offsetTop < this.container.scrollTop) {
|
||||
this.container.scrollTop = offsetTop - this.container.offsetTop;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
render() {
|
||||
const EmojiText = sdk.getComponent('views.elements.EmojiText');
|
||||
|
||||
let position = 0;
|
||||
let renderedCompletions = this.state.completions.map((completionResult, i) => {
|
||||
let completions = completionResult.completions.map((completion, i) => {
|
||||
let className = classNames('mx_Autocomplete_Completion', {
|
||||
|
||||
const className = classNames('mx_Autocomplete_Completion', {
|
||||
'selected': position === this.state.selectionOffset,
|
||||
});
|
||||
let componentPosition = position;
|
||||
|
@ -116,40 +133,27 @@ export default class Autocomplete extends React.Component {
|
|||
this.onConfirm();
|
||||
};
|
||||
|
||||
return (
|
||||
<div key={i}
|
||||
className={className}
|
||||
onMouseOver={onMouseOver}
|
||||
onClick={onClick}>
|
||||
{completion.component}
|
||||
</div>
|
||||
);
|
||||
return React.cloneElement(completion.component, {
|
||||
key: i,
|
||||
ref: `completion${i}`,
|
||||
className,
|
||||
onMouseOver,
|
||||
onClick,
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
return completions.length > 0 ? (
|
||||
<div key={i} className="mx_Autocomplete_ProviderSection">
|
||||
<span className="mx_Autocomplete_provider_name">{completionResult.provider.getName()}</span>
|
||||
<ReactCSSTransitionGroup
|
||||
component="div"
|
||||
transitionName="autocomplete"
|
||||
transitionEnterTimeout={300}
|
||||
transitionLeaveTimeout={300}>
|
||||
{completions}
|
||||
</ReactCSSTransitionGroup>
|
||||
<EmojiText element="div" className="mx_Autocomplete_provider_name">{completionResult.provider.getName()}</EmojiText>
|
||||
{completionResult.provider.renderCompletions(completions)}
|
||||
</div>
|
||||
) : null;
|
||||
});
|
||||
|
||||
return (
|
||||
<div className="mx_Autocomplete">
|
||||
<ReactCSSTransitionGroup
|
||||
component="div"
|
||||
transitionName="autocomplete"
|
||||
transitionEnterTimeout={300}
|
||||
transitionLeaveTimeout={300}>
|
||||
{renderedCompletions}
|
||||
</ReactCSSTransitionGroup>
|
||||
<div className="mx_Autocomplete" ref={(e) => this.container = e}>
|
||||
{renderedCompletions}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
|
|
@ -62,7 +62,7 @@ var MAX_READ_AVATARS = 5;
|
|||
// '----------------------------------------------------------'
|
||||
|
||||
module.exports = React.createClass({
|
||||
displayName: 'Event',
|
||||
displayName: 'EventTile',
|
||||
|
||||
statics: {
|
||||
haveTileForEvent: function(e) {
|
||||
|
@ -368,7 +368,7 @@ module.exports = React.createClass({
|
|||
// room, or emote messages
|
||||
var isInfoMessage = (msgtype === 'm.emote' || eventType !== 'm.room.message');
|
||||
|
||||
var EventTileType = sdk.getComponent(eventTileTypes[this.props.mxEvent.getType()]);
|
||||
var EventTileType = sdk.getComponent(eventTileTypes[eventType]);
|
||||
// This shouldn't happen: the caller should check we support this type
|
||||
// before trying to instantiate us
|
||||
if (!EventTileType) {
|
||||
|
@ -395,25 +395,44 @@ module.exports = React.createClass({
|
|||
<MessageTimestamp ts={this.props.mxEvent.getTs()} />
|
||||
</a>
|
||||
|
||||
var aux = null;
|
||||
if (msgtype === 'm.image') aux = "sent an image";
|
||||
else if (msgtype === 'm.video') aux = "sent a video";
|
||||
else if (msgtype === 'm.file') aux = "uploaded a file";
|
||||
|
||||
var readAvatars = this.getReadAvatars();
|
||||
|
||||
var avatar, sender;
|
||||
if (!this.props.continuation && !isInfoMessage) {
|
||||
if (this.props.mxEvent.sender) {
|
||||
avatar = (
|
||||
let avatarSize;
|
||||
let needsSenderProfile;
|
||||
|
||||
if (isInfoMessage) {
|
||||
// a small avatar, with no sender profile, for emotes and
|
||||
// joins/parts/etc
|
||||
avatarSize = 14;
|
||||
needsSenderProfile = false;
|
||||
} else if (this.props.continuation) {
|
||||
// no avatar or sender profile for continuation messages
|
||||
avatarSize = 0;
|
||||
needsSenderProfile = false;
|
||||
} else {
|
||||
avatarSize = 30;
|
||||
needsSenderProfile = true;
|
||||
}
|
||||
|
||||
if (this.props.mxEvent.sender && avatarSize) {
|
||||
avatar = (
|
||||
<div className="mx_EventTile_avatar">
|
||||
<MemberAvatar member={this.props.mxEvent.sender} width={30} height={30} onClick={ this.onMemberAvatarClick } />
|
||||
<MemberAvatar member={this.props.mxEvent.sender}
|
||||
width={avatarSize} height={avatarSize}
|
||||
onClick={ this.onMemberAvatarClick }
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
if (EventTileType.needsSenderProfile()) {
|
||||
sender = <SenderProfile onClick={ this.onSenderProfileClick } mxEvent={this.props.mxEvent} aux={aux} />;
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
if (needsSenderProfile) {
|
||||
let aux = null;
|
||||
if (msgtype === 'm.image') aux = "sent an image";
|
||||
else if (msgtype === 'm.video') aux = "sent a video";
|
||||
else if (msgtype === 'm.file') aux = "uploaded a file";
|
||||
|
||||
sender = <SenderProfile onClick={ this.onSenderProfileClick } mxEvent={this.props.mxEvent} aux={aux} />;
|
||||
}
|
||||
|
||||
var editButton = (
|
||||
|
|
|
@ -531,7 +531,7 @@ module.exports = React.createClass({
|
|||
},
|
||||
|
||||
onMemberAvatarClick: function () {
|
||||
var avatarUrl = this.props.member.user.avatarUrl;
|
||||
var avatarUrl = this.props.member.user ? this.props.member.user.avatarUrl : this.props.member.events.member.getContent().avatar_url;
|
||||
if(!avatarUrl) return;
|
||||
|
||||
var httpUrl = MatrixClientPeg.get().mxcUrlToHttp(avatarUrl);
|
||||
|
|
|
@ -23,6 +23,7 @@ var Modal = require('../../../Modal');
|
|||
var ObjectUtils = require("../../../ObjectUtils");
|
||||
var dis = require("../../../dispatcher");
|
||||
var ScalarAuthClient = require("../../../ScalarAuthClient");
|
||||
var ScalarMessaging = require('../../../ScalarMessaging');
|
||||
var UserSettingsStore = require('../../../UserSettingsStore');
|
||||
|
||||
// parse a string as an integer; if the input is undefined, or cannot be parsed
|
||||
|
@ -70,6 +71,7 @@ module.exports = React.createClass({
|
|||
},
|
||||
|
||||
componentWillMount: function() {
|
||||
ScalarMessaging.startListening();
|
||||
MatrixClientPeg.get().getRoomDirectoryVisibility(
|
||||
this.props.room.roomId
|
||||
).done((result) => {
|
||||
|
@ -93,6 +95,8 @@ module.exports = React.createClass({
|
|||
},
|
||||
|
||||
componentWillUnmount: function() {
|
||||
ScalarMessaging.stopListening();
|
||||
|
||||
dis.dispatch({
|
||||
action: 'ui_opacity',
|
||||
sideOpacity: 1.0,
|
||||
|
@ -422,6 +426,27 @@ module.exports = React.createClass({
|
|||
}, "");
|
||||
},
|
||||
|
||||
onLeaveClick() {
|
||||
dis.dispatch({
|
||||
action: 'leave_room',
|
||||
room_id: this.props.room.roomId,
|
||||
});
|
||||
},
|
||||
|
||||
onForgetClick() {
|
||||
// FIXME: duplicated with RoomTagContextualMenu (and dead code in RoomView)
|
||||
MatrixClientPeg.get().forget(this.props.room.roomId).done(function() {
|
||||
dis.dispatch({ action: 'view_next_room' });
|
||||
}, function(err) {
|
||||
var errCode = err.errcode || "unknown error code";
|
||||
var ErrorDialog = sdk.getComponent("dialogs.ErrorDialog");
|
||||
Modal.createDialog(ErrorDialog, {
|
||||
title: "Error",
|
||||
description: `Failed to forget room (${errCode})`
|
||||
});
|
||||
});
|
||||
},
|
||||
|
||||
_renderEncryptionSection: function() {
|
||||
if (!UserSettingsStore.isFeatureEnabled("e2e_encryption")) {
|
||||
return null;
|
||||
|
@ -540,6 +565,25 @@ module.exports = React.createClass({
|
|||
);
|
||||
}
|
||||
|
||||
var leaveButton = null;
|
||||
var myMember = this.props.room.getMember(user_id);
|
||||
if (myMember) {
|
||||
if (myMember.membership === "join") {
|
||||
leaveButton = (
|
||||
<div className="mx_RoomSettings_leaveButton" onClick={ this.onLeaveClick }>
|
||||
Leave room
|
||||
</div>
|
||||
);
|
||||
}
|
||||
else if (myMember.membership === "leave") {
|
||||
leaveButton = (
|
||||
<div className="mx_RoomSettings_leaveButton" onClick={ this.onForgetClick }>
|
||||
Forget room
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: support editing custom events_levels
|
||||
// TODO: support editing custom user_levels
|
||||
|
||||
|
@ -627,6 +671,8 @@ module.exports = React.createClass({
|
|||
return (
|
||||
<div className="mx_RoomSettings">
|
||||
|
||||
{ leaveButton }
|
||||
|
||||
{ tagsSection }
|
||||
|
||||
<div className="mx_RoomSettings_toggles">
|
||||
|
|
|
@ -100,7 +100,7 @@ export default class DevicesPanelEntry extends React.Component {
|
|||
deleteButton = <div className="error">{this.state.deleteError}</div>
|
||||
} else {
|
||||
deleteButton = (
|
||||
<div className="textButton"
|
||||
<div className="mx_textButton"
|
||||
onClick={this._onDeleteClick}>
|
||||
Delete
|
||||
</div>
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue