Merge branch 'develop' of github.com:matrix-org/matrix-react-sdk into set_default_federate_by_settings
This commit is contained in:
commit
7492f2dffa
149 changed files with 7702 additions and 5253 deletions
|
@ -28,6 +28,8 @@ import ScalarMessaging from '../../../ScalarMessaging';
|
|||
import { _t } from '../../../languageHandler';
|
||||
import WidgetUtils from '../../../WidgetUtils';
|
||||
|
||||
// The maximum number of widgets that can be added in a room
|
||||
const MAX_WIDGETS = 2;
|
||||
|
||||
module.exports = React.createClass({
|
||||
displayName: 'AppsDrawer',
|
||||
|
@ -51,19 +53,18 @@ module.exports = React.createClass({
|
|||
this.scalarClient = null;
|
||||
if (SdkConfig.get().integrations_ui_url && SdkConfig.get().integrations_rest_url) {
|
||||
this.scalarClient = new ScalarAuthClient();
|
||||
this.scalarClient.connect().done(() => {
|
||||
this.scalarClient.connect().then(() => {
|
||||
this.forceUpdate();
|
||||
if (this.state.apps && this.state.apps.length < 1) {
|
||||
this.onClickAddWidget();
|
||||
}
|
||||
// TODO -- Handle Scalar errors
|
||||
// },
|
||||
// (err) => {
|
||||
// this.setState({
|
||||
// scalar_error: err,
|
||||
// });
|
||||
}).catch((e) => {
|
||||
console.log("Failed to connect to integrations server");
|
||||
// TODO -- Handle Scalar errors
|
||||
// this.setState({
|
||||
// scalar_error: err,
|
||||
// });
|
||||
});
|
||||
}
|
||||
|
||||
this.dispatcherRef = dis.register(this.onAction);
|
||||
},
|
||||
|
||||
componentWillUnmount: function() {
|
||||
|
@ -71,6 +72,27 @@ module.exports = React.createClass({
|
|||
if (MatrixClientPeg.get()) {
|
||||
MatrixClientPeg.get().removeListener("RoomState.events", this.onRoomStateEvents);
|
||||
}
|
||||
dis.unregister(this.dispatcherRef);
|
||||
},
|
||||
|
||||
componentWillReceiveProps(newProps) {
|
||||
// Room has changed probably, update apps
|
||||
this._updateApps();
|
||||
},
|
||||
|
||||
onAction: function(action) {
|
||||
switch (action.action) {
|
||||
case 'appsDrawer':
|
||||
// When opening the app draw when there aren't any apps, auto-launch the
|
||||
// integrations manager to skip the awkward click on "Add widget"
|
||||
if (action.show) {
|
||||
const apps = this._getApps();
|
||||
if (apps.length === 0) {
|
||||
this._launchManageIntegrations();
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
|
@ -93,7 +115,7 @@ module.exports = React.createClass({
|
|||
return pathTemplate;
|
||||
},
|
||||
|
||||
_initAppConfig: function(appId, app) {
|
||||
_initAppConfig: function(appId, app, sender) {
|
||||
const user = MatrixClientPeg.get().getUser(this.props.userId);
|
||||
const params = {
|
||||
'$matrix_user_id': this.props.userId,
|
||||
|
@ -111,6 +133,7 @@ module.exports = React.createClass({
|
|||
app.id = appId;
|
||||
app.name = app.name || app.type;
|
||||
app.url = this.encodeUri(app.url, params);
|
||||
app.creatorUserId = (sender && sender.userId) ? sender.userId : null;
|
||||
|
||||
return app;
|
||||
},
|
||||
|
@ -131,18 +154,12 @@ module.exports = React.createClass({
|
|||
return appsStateEvents.filter((ev) => {
|
||||
return ev.getContent().type && ev.getContent().url;
|
||||
}).map((ev) => {
|
||||
return this._initAppConfig(ev.getStateKey(), ev.getContent());
|
||||
return this._initAppConfig(ev.getStateKey(), ev.getContent(), ev.sender);
|
||||
});
|
||||
},
|
||||
|
||||
_updateApps: function() {
|
||||
const apps = this._getApps();
|
||||
if (apps.length < 1) {
|
||||
dis.dispatch({
|
||||
action: 'appsDrawer',
|
||||
show: false,
|
||||
});
|
||||
}
|
||||
this.setState({
|
||||
apps: apps,
|
||||
});
|
||||
|
@ -157,11 +174,7 @@ module.exports = React.createClass({
|
|||
}
|
||||
},
|
||||
|
||||
onClickAddWidget: function(e) {
|
||||
if (e) {
|
||||
e.preventDefault();
|
||||
}
|
||||
|
||||
_launchManageIntegrations: function() {
|
||||
const IntegrationsManager = sdk.getComponent("views.settings.IntegrationsManager");
|
||||
const src = (this.scalarClient !== null && this.scalarClient.hasCredentials()) ?
|
||||
this.scalarClient.getScalarInterfaceUrlForRoom(this.props.room.roomId, 'add_integ') :
|
||||
|
@ -171,6 +184,23 @@ module.exports = React.createClass({
|
|||
}, "mx_IntegrationsManager");
|
||||
},
|
||||
|
||||
onClickAddWidget: function(e) {
|
||||
e.preventDefault();
|
||||
// Display a warning dialog if the max number of widgets have already been added to the room
|
||||
const apps = this._getApps();
|
||||
if (apps && apps.length >= MAX_WIDGETS) {
|
||||
const ErrorDialog = sdk.getComponent("dialogs.ErrorDialog");
|
||||
const errorMsg = `The maximum number of ${MAX_WIDGETS} widgets have already been added to this room.`;
|
||||
console.error(errorMsg);
|
||||
Modal.createDialog(ErrorDialog, {
|
||||
title: _t("Cannot add any more widgets"),
|
||||
description: _t("The maximum permitted number of widgets have already been added to this room."),
|
||||
});
|
||||
return;
|
||||
}
|
||||
this._launchManageIntegrations();
|
||||
},
|
||||
|
||||
render: function() {
|
||||
const apps = this.state.apps.map(
|
||||
(app, index, arr) => {
|
||||
|
@ -183,24 +213,34 @@ module.exports = React.createClass({
|
|||
fullWidth={arr.length<2 ? true : false}
|
||||
room={this.props.room}
|
||||
userId={this.props.userId}
|
||||
show={this.props.showApps}
|
||||
creatorUserId={app.creatorUserId}
|
||||
/>);
|
||||
});
|
||||
|
||||
const addWidget = this.state.apps && this.state.apps.length < 2 && this._canUserModify() &&
|
||||
(<div onClick={this.onClickAddWidget}
|
||||
role="button"
|
||||
tabIndex="0"
|
||||
className="mx_AddWidget_button"
|
||||
title={_t('Add a widget')}>
|
||||
[+] {_t('Add a widget')}
|
||||
</div>);
|
||||
let addWidget;
|
||||
if (this.props.showApps &&
|
||||
this._canUserModify()
|
||||
) {
|
||||
addWidget = <div
|
||||
onClick={this.onClickAddWidget}
|
||||
role="button"
|
||||
tabIndex="0"
|
||||
className={this.state.apps.length<2 ?
|
||||
"mx_AddWidget_button mx_AddWidget_button_full_width" :
|
||||
"mx_AddWidget_button"
|
||||
}
|
||||
title={_t('Add a widget')}>
|
||||
[+] { _t('Add a widget') }
|
||||
</div>;
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="mx_AppsDrawer">
|
||||
<div id="apps" className="mx_AppsContainer">
|
||||
{apps}
|
||||
{ apps }
|
||||
</div>
|
||||
{addWidget}
|
||||
{ this._canUserModify() && addWidget }
|
||||
</div>
|
||||
);
|
||||
},
|
||||
|
|
|
@ -143,7 +143,6 @@ export default class Autocomplete extends React.Component {
|
|||
return null;
|
||||
}
|
||||
this.setSelection(selectionOffset);
|
||||
return selectionOffset === COMPOSER_SELECTED ? null : this.state.completionList[selectionOffset - 1];
|
||||
}
|
||||
|
||||
// called from MessageComposerInput
|
||||
|
@ -155,7 +154,6 @@ export default class Autocomplete extends React.Component {
|
|||
return null;
|
||||
}
|
||||
this.setSelection(selectionOffset);
|
||||
return selectionOffset === COMPOSER_SELECTED ? null : this.state.completionList[selectionOffset - 1];
|
||||
}
|
||||
|
||||
onEscape(e): boolean {
|
||||
|
@ -201,6 +199,9 @@ export default class Autocomplete extends React.Component {
|
|||
|
||||
setSelection(selectionOffset: number) {
|
||||
this.setState({selectionOffset, hide: false});
|
||||
if (this.props.onSelectionChange) {
|
||||
this.props.onSelectionChange(this.state.completionList[selectionOffset - 1]);
|
||||
}
|
||||
}
|
||||
|
||||
componentDidUpdate() {
|
||||
|
|
|
@ -129,11 +129,13 @@ module.exports = React.createClass({
|
|||
);
|
||||
|
||||
let appsDrawer = null;
|
||||
if(UserSettingsStore.isFeatureEnabled('matrix_apps') && this.props.showApps) {
|
||||
if(UserSettingsStore.isFeatureEnabled('matrix_apps')) {
|
||||
appsDrawer = <AppsDrawer ref="appsDrawer"
|
||||
room={this.props.room}
|
||||
userId={this.props.userId}
|
||||
maxHeight={this.props.maxHeight}/>;
|
||||
maxHeight={this.props.maxHeight}
|
||||
showApps={this.props.showApps}
|
||||
/>;
|
||||
}
|
||||
|
||||
return (
|
||||
|
|
|
@ -44,6 +44,8 @@ var eventTileTypes = {
|
|||
'm.room.history_visibility' : 'messages.TextualEvent',
|
||||
'm.room.encryption' : 'messages.TextualEvent',
|
||||
'm.room.power_levels' : 'messages.TextualEvent',
|
||||
|
||||
'im.vector.modular.widgets': 'messages.TextualEvent',
|
||||
};
|
||||
|
||||
var MAX_READ_AVATARS = 5;
|
||||
|
@ -506,10 +508,10 @@ module.exports = withMatrixClient(React.createClass({
|
|||
if (msgtype === 'm.image') aux = _t('sent an image');
|
||||
else if (msgtype === 'm.video') aux = _t('sent a video');
|
||||
else if (msgtype === 'm.file') aux = _t('uploaded a file');
|
||||
sender = <SenderProfile onClick={ this.onSenderProfileClick } mxEvent={this.props.mxEvent} aux={aux} />;
|
||||
sender = <SenderProfile onClick={ this.onSenderProfileClick } mxEvent={this.props.mxEvent} enableFlair={!aux} aux={aux} />;
|
||||
}
|
||||
else {
|
||||
sender = <SenderProfile mxEvent={this.props.mxEvent} />;
|
||||
sender = <SenderProfile mxEvent={this.props.mxEvent} enableFlair={true} />;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -61,7 +61,7 @@ module.exports = React.createClass({
|
|||
render: function() {
|
||||
return (
|
||||
<div className="mx_ForwardMessage">
|
||||
<h1>{_t('Please select the destination room for this message')}</h1>
|
||||
<h1>{ _t('Please select the destination room for this message') }</h1>
|
||||
</div>
|
||||
);
|
||||
},
|
||||
|
|
|
@ -62,6 +62,7 @@ module.exports = withMatrixClient(React.createClass({
|
|||
updating: 0,
|
||||
devicesLoading: true,
|
||||
devices: null,
|
||||
isIgnoring: false,
|
||||
};
|
||||
},
|
||||
|
||||
|
@ -81,6 +82,8 @@ module.exports = withMatrixClient(React.createClass({
|
|||
cli.on("RoomState.events", this.onRoomStateEvents);
|
||||
cli.on("RoomMember.name", this.onRoomMemberName);
|
||||
cli.on("accountData", this.onAccountData);
|
||||
|
||||
this._checkIgnoreState();
|
||||
},
|
||||
|
||||
componentDidMount: function() {
|
||||
|
@ -111,6 +114,11 @@ module.exports = withMatrixClient(React.createClass({
|
|||
}
|
||||
},
|
||||
|
||||
_checkIgnoreState: function() {
|
||||
const isIgnoring = this.props.matrixClient.isUserIgnored(this.props.member.userId);
|
||||
this.setState({isIgnoring: isIgnoring});
|
||||
},
|
||||
|
||||
_disambiguateDevices: function(devices) {
|
||||
var names = Object.create(null);
|
||||
for (var i = 0; i < devices.length; i++) {
|
||||
|
@ -225,6 +233,18 @@ module.exports = withMatrixClient(React.createClass({
|
|||
});
|
||||
},
|
||||
|
||||
onIgnoreToggle: function() {
|
||||
const ignoredUsers = this.props.matrixClient.getIgnoredUsers();
|
||||
if (this.state.isIgnoring) {
|
||||
const index = ignoredUsers.indexOf(this.props.member.userId);
|
||||
if (index !== -1) ignoredUsers.splice(index, 1);
|
||||
} else {
|
||||
ignoredUsers.push(this.props.member.userId);
|
||||
}
|
||||
|
||||
this.props.matrixClient.setIgnoredUsers(ignoredUsers).then(() => this.setState({isIgnoring: !this.state.isIgnoring}));
|
||||
},
|
||||
|
||||
onKick: function() {
|
||||
const membership = this.props.member.membership;
|
||||
const kickLabel = membership === "invite" ? _t("Disinvite") : _t("Kick");
|
||||
|
@ -607,6 +627,29 @@ module.exports = withMatrixClient(React.createClass({
|
|||
);
|
||||
},
|
||||
|
||||
_renderUserOptions: function() {
|
||||
// Only allow the user to ignore the user if its not ourselves
|
||||
let ignoreButton = null;
|
||||
if (this.props.member.userId !== this.props.matrixClient.getUserId()) {
|
||||
ignoreButton = (
|
||||
<AccessibleButton onClick={this.onIgnoreToggle} className="mx_MemberInfo_field">
|
||||
{this.state.isIgnoring ? _t("Unignore") : _t("Ignore")}
|
||||
</AccessibleButton>
|
||||
);
|
||||
}
|
||||
|
||||
if (!ignoreButton) return null;
|
||||
|
||||
return (
|
||||
<div>
|
||||
<h3>{ _t("User Options") }</h3>
|
||||
<div className="mx_MemberInfo_buttons">
|
||||
{ignoreButton}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
},
|
||||
|
||||
render: function() {
|
||||
var startChat, kickButton, banButton, muteButton, giveModButton, spinner;
|
||||
if (this.props.member.userId !== this.props.matrixClient.credentials.userId) {
|
||||
|
@ -708,7 +751,7 @@ module.exports = withMatrixClient(React.createClass({
|
|||
if (kickButton || banButton || muteButton || giveModButton) {
|
||||
adminTools =
|
||||
<div>
|
||||
<h3>{_t("Admin tools")}</h3>
|
||||
<h3>{_t("Admin Tools")}</h3>
|
||||
|
||||
<div className="mx_MemberInfo_buttons">
|
||||
{muteButton}
|
||||
|
@ -756,6 +799,8 @@ module.exports = withMatrixClient(React.createClass({
|
|||
</div>
|
||||
</div>
|
||||
|
||||
{ this._renderUserOptions() }
|
||||
|
||||
{ adminTools }
|
||||
|
||||
{ startChat }
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
/*
|
||||
Copyright 2015, 2016 OpenMarket Ltd
|
||||
Copyright 2017 Vector Creations Ltd
|
||||
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.
|
||||
|
@ -14,42 +15,37 @@ 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.
|
||||
*/
|
||||
var React = require('react');
|
||||
|
||||
import React from 'react';
|
||||
import { _t } from '../../../languageHandler';
|
||||
var classNames = require('classnames');
|
||||
var Matrix = require("matrix-js-sdk");
|
||||
import Promise from 'bluebird';
|
||||
var MatrixClientPeg = require("../../../MatrixClientPeg");
|
||||
var Modal = require("../../../Modal");
|
||||
var Entities = require("../../../Entities");
|
||||
var sdk = require('../../../index');
|
||||
var GeminiScrollbar = require('react-gemini-scrollbar');
|
||||
var rate_limited_func = require('../../../ratelimitedfunc');
|
||||
var CallHandler = require("../../../CallHandler");
|
||||
var Invite = require("../../../Invite");
|
||||
|
||||
var INITIAL_LOAD_NUM_MEMBERS = 30;
|
||||
const INITIAL_LOAD_NUM_MEMBERS = 30;
|
||||
const INITIAL_LOAD_NUM_INVITED = 5;
|
||||
const SHOW_MORE_INCREMENT = 100;
|
||||
|
||||
module.exports = React.createClass({
|
||||
displayName: 'MemberList',
|
||||
|
||||
getInitialState: function() {
|
||||
var state = {
|
||||
members: [],
|
||||
this.memberDict = this.getMemberDict();
|
||||
const members = this.roomMembers();
|
||||
|
||||
return {
|
||||
members: members,
|
||||
filteredJoinedMembers: this._filterMembers(members, 'join'),
|
||||
filteredInvitedMembers: this._filterMembers(members, 'invite'),
|
||||
|
||||
// ideally we'd size this to the page height, but
|
||||
// in practice I find that a little constraining
|
||||
truncateAt: INITIAL_LOAD_NUM_MEMBERS,
|
||||
truncateAtJoined: INITIAL_LOAD_NUM_MEMBERS,
|
||||
truncateAtInvited: INITIAL_LOAD_NUM_INVITED,
|
||||
searchQuery: "",
|
||||
};
|
||||
if (!this.props.roomId) return state;
|
||||
var cli = MatrixClientPeg.get();
|
||||
var room = cli.getRoom(this.props.roomId);
|
||||
if (!room) return state;
|
||||
|
||||
this.memberDict = this.getMemberDict();
|
||||
|
||||
state.members = this.roomMembers();
|
||||
return state;
|
||||
},
|
||||
|
||||
componentWillMount: function() {
|
||||
|
@ -147,10 +143,12 @@ module.exports = React.createClass({
|
|||
// console.log("Updating memberlist");
|
||||
this.memberDict = this.getMemberDict();
|
||||
|
||||
var self = this;
|
||||
this.setState({
|
||||
members: self.roomMembers()
|
||||
});
|
||||
const newState = {
|
||||
members: this.roomMembers(),
|
||||
};
|
||||
newState.filteredJoinedMembers = this._filterMembers(newState.members, 'join');
|
||||
newState.filteredInvitedMembers = this._filterMembers(newState.members, 'invite');
|
||||
this.setState(newState);
|
||||
}, 500),
|
||||
|
||||
getMemberDict: function() {
|
||||
|
@ -199,7 +197,15 @@ module.exports = React.createClass({
|
|||
return to_display;
|
||||
},
|
||||
|
||||
_createOverflowTile: function(overflowCount, totalCount) {
|
||||
_createOverflowTileJoined: function(overflowCount, totalCount) {
|
||||
return this._createOverflowTile(overflowCount, totalCount, this._showMoreJoinedMemberList);
|
||||
},
|
||||
|
||||
_createOverflowTileInvited: function(overflowCount, totalCount) {
|
||||
return this._createOverflowTile(overflowCount, totalCount, this._showMoreInvitedMemberList);
|
||||
},
|
||||
|
||||
_createOverflowTile: function(overflowCount, totalCount, onClick) {
|
||||
// For now we'll pretend this is any entity. It should probably be a separate tile.
|
||||
const EntityTile = sdk.getComponent("rooms.EntityTile");
|
||||
const BaseAvatar = sdk.getComponent("avatars.BaseAvatar");
|
||||
|
@ -208,13 +214,19 @@ module.exports = React.createClass({
|
|||
<EntityTile className="mx_EntityTile_ellipsis" avatarJsx={
|
||||
<BaseAvatar url="img/ellipsis.svg" name="..." width={36} height={36} />
|
||||
} name={text} presenceState="online" suppressOnHover={true}
|
||||
onClick={this._showFullMemberList} />
|
||||
onClick={onClick} />
|
||||
);
|
||||
},
|
||||
|
||||
_showFullMemberList: function() {
|
||||
_showMoreJoinedMemberList: function() {
|
||||
this.setState({
|
||||
truncateAt: -1
|
||||
truncateAtJoined: this.state.truncateAtJoined + SHOW_MORE_INCREMENT,
|
||||
});
|
||||
},
|
||||
|
||||
_showMoreInvitedMemberList: function() {
|
||||
this.setState({
|
||||
truncateAtInvited: this.state.truncateAtInvited + SHOW_MORE_INCREMENT,
|
||||
});
|
||||
},
|
||||
|
||||
|
@ -280,17 +292,17 @@ module.exports = React.createClass({
|
|||
},
|
||||
|
||||
onSearchQueryChanged: function(ev) {
|
||||
this.setState({ searchQuery: ev.target.value });
|
||||
const q = ev.target.value;
|
||||
this.setState({
|
||||
searchQuery: q,
|
||||
filteredJoinedMembers: this._filterMembers(this.state.members, 'join', q),
|
||||
filteredInvitedMembers: this._filterMembers(this.state.members, 'invite', q),
|
||||
});
|
||||
},
|
||||
|
||||
makeMemberTiles: function(membership, query) {
|
||||
var MemberTile = sdk.getComponent("rooms.MemberTile");
|
||||
query = (query || "").toLowerCase();
|
||||
|
||||
var self = this;
|
||||
|
||||
var memberList = self.state.members.filter(function(userId) {
|
||||
var m = self.memberDict[userId];
|
||||
_filterMembers: function(members, membership, query) {
|
||||
return members.filter((userId) => {
|
||||
const m = this.memberDict[userId];
|
||||
|
||||
if (query) {
|
||||
const matchesName = m.name.toLowerCase().indexOf(query) !== -1;
|
||||
|
@ -302,14 +314,23 @@ module.exports = React.createClass({
|
|||
}
|
||||
|
||||
return m.membership == membership;
|
||||
}).map(function(userId) {
|
||||
var m = self.memberDict[userId];
|
||||
});
|
||||
},
|
||||
|
||||
_makeMemberTiles: function(members, membership) {
|
||||
const MemberTile = sdk.getComponent("rooms.MemberTile");
|
||||
|
||||
const memberList = members.map((userId) => {
|
||||
const m = this.memberDict[userId];
|
||||
return (
|
||||
<MemberTile key={userId} member={m} ref={userId} />
|
||||
);
|
||||
});
|
||||
|
||||
// XXX: surely this is not the right home for this logic.
|
||||
// Double XXX: Now it's really, really not the right home for this logic:
|
||||
// we shouldn't even be passing in the 'membership' param to this function.
|
||||
// Ew, ew, and ew.
|
||||
if (membership === "invite") {
|
||||
// include 3pid invites (m.room.third_party_invite) state events.
|
||||
// The HS may have already converted these into m.room.member invites so
|
||||
|
@ -333,7 +354,7 @@ module.exports = React.createClass({
|
|||
return;
|
||||
}
|
||||
memberList.push(
|
||||
<EntityTile key={e.getStateKey()} name={e.getContent().display_name} />
|
||||
<EntityTile key={e.getStateKey()} name={e.getContent().display_name} suppressOnHover={true} />
|
||||
);
|
||||
});
|
||||
}
|
||||
|
@ -342,21 +363,42 @@ module.exports = React.createClass({
|
|||
return memberList;
|
||||
},
|
||||
|
||||
_getChildrenJoined: function(start, end) {
|
||||
return this._makeMemberTiles(this.state.filteredJoinedMembers.slice(start, end));
|
||||
},
|
||||
|
||||
_getChildCountJoined: function() {
|
||||
return this.state.filteredJoinedMembers.length;
|
||||
},
|
||||
|
||||
_getChildrenInvited: function(start, end) {
|
||||
return this._makeMemberTiles(this.state.filteredInvitedMembers.slice(start, end), 'invite');
|
||||
},
|
||||
|
||||
_getChildCountInvited: function() {
|
||||
return this.state.filteredInvitedMembers.length;
|
||||
},
|
||||
|
||||
render: function() {
|
||||
var invitedSection = null;
|
||||
var invitedMemberTiles = this.makeMemberTiles('invite', this.state.searchQuery);
|
||||
if (invitedMemberTiles.length > 0) {
|
||||
const TruncatedList = sdk.getComponent("elements.TruncatedList");
|
||||
|
||||
let invitedSection = null;
|
||||
if (this._getChildCountInvited() > 0) {
|
||||
invitedSection = (
|
||||
<div className="mx_MemberList_invited">
|
||||
<h2>{ _t("Invited") }</h2>
|
||||
<div className="mx_MemberList_wrapper">
|
||||
{invitedMemberTiles}
|
||||
<TruncatedList className="mx_MemberList_wrapper" truncateAt={this.state.truncateAtInvited}
|
||||
createOverflowElement={this._createOverflowTileInvited}
|
||||
getChildren={this._getChildrenInvited}
|
||||
getChildCount={this._getChildCountInvited}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
var inputBox = (
|
||||
const inputBox = (
|
||||
<form autoComplete="off">
|
||||
<input className="mx_MemberList_query" id="mx_MemberList_query" type="text"
|
||||
onChange={this.onSearchQueryChanged} value={this.state.searchQuery}
|
||||
|
@ -364,15 +406,15 @@ module.exports = React.createClass({
|
|||
</form>
|
||||
);
|
||||
|
||||
var TruncatedList = sdk.getComponent("elements.TruncatedList");
|
||||
return (
|
||||
<div className="mx_MemberList">
|
||||
{ inputBox }
|
||||
<GeminiScrollbar autoshow={true} className="mx_MemberList_joined mx_MemberList_outerWrapper">
|
||||
<TruncatedList className="mx_MemberList_wrapper" truncateAt={this.state.truncateAt}
|
||||
createOverflowElement={this._createOverflowTile}>
|
||||
{this.makeMemberTiles('join', this.state.searchQuery)}
|
||||
</TruncatedList>
|
||||
<TruncatedList className="mx_MemberList_wrapper" truncateAt={this.state.truncateAtJoined}
|
||||
createOverflowElement={this._createOverflowTileJoined}
|
||||
getChildren={this._getChildrenJoined}
|
||||
getChildCount={this._getChildCountJoined}
|
||||
/>
|
||||
{invitedSection}
|
||||
</GeminiScrollbar>
|
||||
</div>
|
||||
|
|
|
@ -289,12 +289,12 @@ export default class MessageComposer extends React.Component {
|
|||
if (this.props.showApps) {
|
||||
hideAppsButton =
|
||||
<div key="controls_hide_apps" className="mx_MessageComposer_apps" onClick={this.onHideAppsClick} title={_t("Hide Apps")}>
|
||||
<TintableSvg src="img/icons-apps-active.svg" width="35" height="35"/>
|
||||
<TintableSvg src="img/icons-hide-apps.svg" width="35" height="35"/>
|
||||
</div>;
|
||||
} else {
|
||||
showAppsButton =
|
||||
<div key="show_apps" className="mx_MessageComposer_apps" onClick={this.onShowAppsClick} title={_t("Show Apps")}>
|
||||
<TintableSvg src="img/icons-apps.svg" width="35" height="35"/>
|
||||
<TintableSvg src="img/icons-show-apps.svg" width="35" height="35"/>
|
||||
</div>;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -30,7 +30,7 @@ import SlashCommands from '../../../SlashCommands';
|
|||
import KeyCode from '../../../KeyCode';
|
||||
import Modal from '../../../Modal';
|
||||
import sdk from '../../../index';
|
||||
import { _t } from '../../../languageHandler';
|
||||
import { _t, _td } from '../../../languageHandler';
|
||||
import Analytics from '../../../Analytics';
|
||||
|
||||
import dis from '../../../dispatcher';
|
||||
|
@ -949,8 +949,7 @@ export default class MessageComposerInput extends React.Component {
|
|||
};
|
||||
|
||||
moveAutocompleteSelection = (up) => {
|
||||
const completion = up ? this.autocomplete.onUpArrow() : this.autocomplete.onDownArrow();
|
||||
return this.setDisplayedCompletion(completion);
|
||||
up ? this.autocomplete.onUpArrow() : this.autocomplete.onDownArrow();
|
||||
};
|
||||
|
||||
onEscape = async (e) => {
|
||||
|
@ -1033,10 +1032,10 @@ export default class MessageComposerInput extends React.Component {
|
|||
buttons. */
|
||||
getSelectionInfo(editorState: EditorState) {
|
||||
const styleName = {
|
||||
BOLD: 'bold',
|
||||
ITALIC: 'italic',
|
||||
STRIKETHROUGH: 'strike',
|
||||
UNDERLINE: 'underline',
|
||||
BOLD: _td('bold'),
|
||||
ITALIC: _td('italic'),
|
||||
STRIKETHROUGH: _td('strike'),
|
||||
UNDERLINE: _td('underline'),
|
||||
};
|
||||
|
||||
const originalStyle = editorState.getCurrentInlineStyle().toArray();
|
||||
|
@ -1045,10 +1044,10 @@ export default class MessageComposerInput extends React.Component {
|
|||
.filter((styleName) => !!styleName);
|
||||
|
||||
const blockName = {
|
||||
'code-block': 'code',
|
||||
'blockquote': 'quote',
|
||||
'unordered-list-item': 'bullet',
|
||||
'ordered-list-item': 'numbullet',
|
||||
'code-block': _td('code'),
|
||||
'blockquote': _td('quote'),
|
||||
'unordered-list-item': _td('bullet'),
|
||||
'ordered-list-item': _td('numbullet'),
|
||||
};
|
||||
const originalBlockType = editorState.getCurrentContent()
|
||||
.getBlockForKey(editorState.getSelection().getStartKey())
|
||||
|
@ -1133,6 +1132,7 @@ export default class MessageComposerInput extends React.Component {
|
|||
<Autocomplete
|
||||
ref={(e) => this.autocomplete = e}
|
||||
onConfirm={this.setDisplayedCompletion}
|
||||
onSelectionChange={this.setDisplayedCompletion}
|
||||
query={this.getAutocompleteQuery(content)}
|
||||
selection={selection}/>
|
||||
</div>
|
||||
|
|
|
@ -70,7 +70,7 @@ module.exports = React.createClass({
|
|||
if (presence === "online") return _t("Online");
|
||||
if (presence === "unavailable") return _t("Idle"); // XXX: is this actually right?
|
||||
if (presence === "offline") return _t("Offline");
|
||||
return "Unknown";
|
||||
return _t("Unknown");
|
||||
},
|
||||
|
||||
render: function() {
|
||||
|
|
|
@ -123,7 +123,19 @@ module.exports = React.createClass({
|
|||
}
|
||||
|
||||
var newElement = ReactDOM.findDOMNode(this);
|
||||
var startTopOffset = oldTop - newElement.offsetParent.getBoundingClientRect().top;
|
||||
let startTopOffset;
|
||||
if (!newElement.offsetParent) {
|
||||
// this seems to happen sometimes for reasons I don't understand
|
||||
// the docs for `offsetParent` say it may be null if `display` is
|
||||
// `none`, but I can't see why that would happen.
|
||||
console.warn(
|
||||
`ReadReceiptMarker for ${this.props.member.userId} in ` +
|
||||
`${this.props.member.roomId} has no offsetParent`,
|
||||
);
|
||||
startTopOffset = 0;
|
||||
} else {
|
||||
startTopOffset = oldTop - newElement.offsetParent.getBoundingClientRect().top;
|
||||
}
|
||||
|
||||
var startStyles = [];
|
||||
var enterTransitionOpts = [];
|
||||
|
@ -131,13 +143,12 @@ module.exports = React.createClass({
|
|||
if (oldInfo && oldInfo.left) {
|
||||
// start at the old height and in the old h pos
|
||||
|
||||
var leftOffset = oldInfo.left;
|
||||
startStyles.push({ top: startTopOffset+"px",
|
||||
left: oldInfo.left+"px" });
|
||||
|
||||
var reorderTransitionOpts = {
|
||||
duration: 100,
|
||||
easing: 'easeOut'
|
||||
easing: 'easeOut',
|
||||
};
|
||||
|
||||
enterTransitionOpts.push(reorderTransitionOpts);
|
||||
|
@ -175,7 +186,7 @@ module.exports = React.createClass({
|
|||
if (this.props.timestamp) {
|
||||
title = _t(
|
||||
"Seen by %(userName)s at %(dateTime)s",
|
||||
{userName: this.props.member.userId, dateTime: DateUtils.formatDate(new Date(this.props.timestamp), this.props.showTwelveHour)}
|
||||
{userName: this.props.member.userId, dateTime: DateUtils.formatDate(new Date(this.props.timestamp), this.props.showTwelveHour)},
|
||||
);
|
||||
}
|
||||
|
||||
|
|
|
@ -29,6 +29,7 @@ import * as linkify from 'linkifyjs';
|
|||
import linkifyElement from 'linkifyjs/element';
|
||||
import linkifyMatrix from '../../../linkify-matrix';
|
||||
import AccessibleButton from '../elements/AccessibleButton';
|
||||
import ManageIntegsButton from '../elements/ManageIntegsButton';
|
||||
import {CancelButton} from './SimpleRoomHeader';
|
||||
|
||||
linkifyMatrix(linkify);
|
||||
|
@ -47,6 +48,7 @@ module.exports = React.createClass({
|
|||
onSaveClick: React.PropTypes.func,
|
||||
onSearchClick: React.PropTypes.func,
|
||||
onLeaveClick: React.PropTypes.func,
|
||||
onCancelClick: React.PropTypes.func,
|
||||
},
|
||||
|
||||
getDefaultProps: function() {
|
||||
|
@ -54,6 +56,7 @@ module.exports = React.createClass({
|
|||
editing: false,
|
||||
inRoom: false,
|
||||
onSaveClick: function() {},
|
||||
onCancelClick: null,
|
||||
};
|
||||
},
|
||||
|
||||
|
@ -183,18 +186,18 @@ module.exports = React.createClass({
|
|||
|
||||
saveButton = (
|
||||
<AccessibleButton className="mx_RoomHeader_textButton" onClick={this.props.onSaveClick}>
|
||||
{_t("Save")}
|
||||
{ _t("Save") }
|
||||
</AccessibleButton>
|
||||
);
|
||||
}
|
||||
|
||||
if (this.props.onCancelClick) {
|
||||
cancelButton = <CancelButton onClick={this.props.onCancelClick}/>;
|
||||
cancelButton = <CancelButton onClick={this.props.onCancelClick} />;
|
||||
}
|
||||
|
||||
if (this.props.saving) {
|
||||
const Spinner = sdk.getComponent("elements.Spinner");
|
||||
spinner = <div className="mx_RoomHeader_spinner"><Spinner/></div>;
|
||||
spinner = <div className="mx_RoomHeader_spinner"><Spinner /></div>;
|
||||
}
|
||||
|
||||
if (canSetRoomName) {
|
||||
|
@ -251,7 +254,7 @@ module.exports = React.createClass({
|
|||
}
|
||||
if (topic) {
|
||||
topicElement =
|
||||
<div className="mx_RoomHeader_topic" ref="topic" title={ topic } dir="auto">{ topic }</div>;
|
||||
<div className="mx_RoomHeader_topic" ref="topic" title={topic} dir="auto">{ topic }</div>;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -259,16 +262,16 @@ module.exports = React.createClass({
|
|||
if (canSetRoomAvatar) {
|
||||
roomAvatar = (
|
||||
<div className="mx_RoomHeader_avatarPicker">
|
||||
<div onClick={ this.onAvatarPickerClick }>
|
||||
<div onClick={this.onAvatarPickerClick}>
|
||||
<ChangeAvatar ref="changeAvatar" room={this.props.room} showUploadSection={false} width={48} height={48} />
|
||||
</div>
|
||||
<div className="mx_RoomHeader_avatarPicker_edit">
|
||||
<label htmlFor="avatarInput" ref="file_label">
|
||||
<img src="img/camera.svg"
|
||||
alt={ _t("Upload avatar") } title={ _t("Upload avatar") }
|
||||
alt={_t("Upload avatar")} title={_t("Upload avatar")}
|
||||
width="17" height="15" />
|
||||
</label>
|
||||
<input id="avatarInput" type="file" onChange={ this.onAvatarSelected }/>
|
||||
<input id="avatarInput" type="file" onChange={this.onAvatarSelected} />
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
|
@ -283,7 +286,7 @@ module.exports = React.createClass({
|
|||
if (this.props.onSettingsClick) {
|
||||
settingsButton =
|
||||
<AccessibleButton className="mx_RoomHeader_button" onClick={this.props.onSettingsClick} title={_t("Settings")}>
|
||||
<TintableSvg src="img/icons-settings-room.svg" width="16" height="16"/>
|
||||
<TintableSvg src="img/icons-settings-room.svg" width="16" height="16" />
|
||||
</AccessibleButton>;
|
||||
}
|
||||
|
||||
|
@ -298,32 +301,40 @@ module.exports = React.createClass({
|
|||
let forgetButton;
|
||||
if (this.props.onForgetClick) {
|
||||
forgetButton =
|
||||
<AccessibleButton className="mx_RoomHeader_button" onClick={this.props.onForgetClick} title={ _t("Forget room") }>
|
||||
<TintableSvg src="img/leave.svg" width="26" height="20"/>
|
||||
<AccessibleButton className="mx_RoomHeader_button" onClick={this.props.onForgetClick} title={_t("Forget room")}>
|
||||
<TintableSvg src="img/leave.svg" width="26" height="20" />
|
||||
</AccessibleButton>;
|
||||
}
|
||||
|
||||
let searchButton;
|
||||
if (this.props.onSearchClick && this.props.inRoom) {
|
||||
searchButton =
|
||||
<AccessibleButton className="mx_RoomHeader_button" onClick={this.props.onSearchClick} title={ _t("Search") }>
|
||||
<TintableSvg src="img/icons-search.svg" width="35" height="35"/>
|
||||
<AccessibleButton className="mx_RoomHeader_button" onClick={this.props.onSearchClick} title={_t("Search")}>
|
||||
<TintableSvg src="img/icons-search.svg" width="35" height="35" />
|
||||
</AccessibleButton>;
|
||||
}
|
||||
|
||||
let rightPanelButtons;
|
||||
if (this.props.collapsedRhs) {
|
||||
rightPanelButtons =
|
||||
<AccessibleButton className="mx_RoomHeader_button" onClick={this.onShowRhsClick} title={ _t('Show panel') }>
|
||||
<TintableSvg src="img/maximise.svg" width="10" height="16"/>
|
||||
<AccessibleButton className="mx_RoomHeader_button" onClick={this.onShowRhsClick} title={_t('Show panel')}>
|
||||
<TintableSvg src="img/maximise.svg" width="10" height="16" />
|
||||
</AccessibleButton>;
|
||||
}
|
||||
|
||||
let rightRow;
|
||||
let manageIntegsButton;
|
||||
if(this.props.room && this.props.room.roomId && this.props.inRoom) {
|
||||
manageIntegsButton = <ManageIntegsButton
|
||||
roomId={this.props.room.roomId}
|
||||
/>;
|
||||
}
|
||||
|
||||
if (!this.props.editing) {
|
||||
rightRow =
|
||||
<div className="mx_RoomHeader_rightRow">
|
||||
{ settingsButton }
|
||||
{ manageIntegsButton }
|
||||
{ forgetButton }
|
||||
{ searchButton }
|
||||
{ rightPanelButtons }
|
||||
|
@ -331,7 +342,7 @@ module.exports = React.createClass({
|
|||
}
|
||||
|
||||
return (
|
||||
<div className={ "mx_RoomHeader " + (this.props.editing ? "mx_RoomHeader_editing" : "") }>
|
||||
<div className={"mx_RoomHeader " + (this.props.editing ? "mx_RoomHeader_editing" : "")}>
|
||||
<div className="mx_RoomHeader_wrapper">
|
||||
<div className="mx_RoomHeader_leftRow">
|
||||
<div className="mx_RoomHeader_avatar">
|
||||
|
@ -342,10 +353,10 @@ module.exports = React.createClass({
|
|||
{ topicElement }
|
||||
</div>
|
||||
</div>
|
||||
{spinner}
|
||||
{saveButton}
|
||||
{cancelButton}
|
||||
{rightRow}
|
||||
{ spinner }
|
||||
{ saveButton }
|
||||
{ cancelButton }
|
||||
{ rightRow }
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
|
|
|
@ -63,7 +63,6 @@ module.exports = React.createClass({
|
|||
propTypes: {
|
||||
ConferenceHandler: React.PropTypes.any,
|
||||
collapsed: React.PropTypes.bool.isRequired,
|
||||
currentRoom: React.PropTypes.string,
|
||||
searchFilter: React.PropTypes.string,
|
||||
},
|
||||
|
||||
|
@ -88,7 +87,9 @@ module.exports = React.createClass({
|
|||
cli.on("Room.receipt", this.onRoomReceipt);
|
||||
cli.on("RoomState.events", this.onRoomStateEvents);
|
||||
cli.on("RoomMember.name", this.onRoomMemberName);
|
||||
cli.on("Event.decrypted", this.onEventDecrypted);
|
||||
cli.on("accountData", this.onAccountData);
|
||||
cli.on("Group.myMembership", this._onGroupMyMembership);
|
||||
|
||||
this.refreshRoomList();
|
||||
|
||||
|
@ -155,7 +156,9 @@ module.exports = React.createClass({
|
|||
MatrixClientPeg.get().removeListener("Room.receipt", this.onRoomReceipt);
|
||||
MatrixClientPeg.get().removeListener("RoomState.events", this.onRoomStateEvents);
|
||||
MatrixClientPeg.get().removeListener("RoomMember.name", this.onRoomMemberName);
|
||||
MatrixClientPeg.get().removeListener("Event.decrypted", this.onEventDecrypted);
|
||||
MatrixClientPeg.get().removeListener("accountData", this.onAccountData);
|
||||
MatrixClientPeg.get().removeListener("Group.myMembership", this._onGroupMyMembership);
|
||||
}
|
||||
// cancel any pending calls to the rate_limited_funcs
|
||||
this._delayedRefreshRoomList.cancelPendingCall();
|
||||
|
@ -224,12 +227,21 @@ module.exports = React.createClass({
|
|||
this._delayedRefreshRoomList();
|
||||
},
|
||||
|
||||
onEventDecrypted: function(ev) {
|
||||
// An event being decrypted may mean we need to re-order the room list
|
||||
this._delayedRefreshRoomList();
|
||||
},
|
||||
|
||||
onAccountData: function(ev) {
|
||||
if (ev.getType() == 'm.direct') {
|
||||
this._delayedRefreshRoomList();
|
||||
}
|
||||
},
|
||||
|
||||
_onGroupMyMembership: function(group) {
|
||||
this.forceUpdate();
|
||||
},
|
||||
|
||||
_delayedRefreshRoomList: new rate_limited_func(function() {
|
||||
this.refreshRoomList();
|
||||
}, 500),
|
||||
|
@ -544,8 +556,24 @@ module.exports = React.createClass({
|
|||
}
|
||||
},
|
||||
|
||||
_makeGroupInviteTiles() {
|
||||
const ret = [];
|
||||
|
||||
const GroupInviteTile = sdk.getComponent('groups.GroupInviteTile');
|
||||
for (const group of MatrixClientPeg.get().getGroups()) {
|
||||
if (group.myMembership !== 'invite') continue;
|
||||
|
||||
ret.push(<GroupInviteTile key={group.groupId} group={group} />);
|
||||
}
|
||||
|
||||
return ret;
|
||||
},
|
||||
|
||||
render: function() {
|
||||
var RoomSubList = sdk.getComponent('structures.RoomSubList');
|
||||
const RoomSubList = sdk.getComponent('structures.RoomSubList');
|
||||
|
||||
const inviteSectionExtraTiles = this._makeGroupInviteTiles();
|
||||
|
||||
var self = this;
|
||||
return (
|
||||
<GeminiScrollbar className="mx_RoomList_scrollbar"
|
||||
|
@ -555,12 +583,15 @@ module.exports = React.createClass({
|
|||
label={ _t('Invites') }
|
||||
editable={ false }
|
||||
order="recent"
|
||||
isInvite={true}
|
||||
selectedRoom={ self.props.selectedRoom }
|
||||
incomingCall={ self.state.incomingCall }
|
||||
collapsed={ self.props.collapsed }
|
||||
searchFilter={ self.props.searchFilter }
|
||||
onHeaderClick={ self.onSubListHeaderClick }
|
||||
onShowMoreRooms={ self.onShowMoreRooms } />
|
||||
onShowMoreRooms={ self.onShowMoreRooms }
|
||||
extraTiles={ inviteSectionExtraTiles }
|
||||
/>
|
||||
|
||||
<RoomSubList list={ self.state.lists['m.favourite'] }
|
||||
label={ _t('Favourites') }
|
||||
|
|
|
@ -24,8 +24,6 @@ import sdk from '../../../index';
|
|||
import Modal from '../../../Modal';
|
||||
import ObjectUtils from '../../../ObjectUtils';
|
||||
import dis from '../../../dispatcher';
|
||||
import ScalarAuthClient from '../../../ScalarAuthClient';
|
||||
import ScalarMessaging from '../../../ScalarMessaging';
|
||||
import UserSettingsStore from '../../../UserSettingsStore';
|
||||
import AccessibleButton from '../elements/AccessibleButton';
|
||||
|
||||
|
@ -92,7 +90,6 @@ module.exports = React.createClass({
|
|||
propTypes: {
|
||||
room: React.PropTypes.object.isRequired,
|
||||
onSaveClick: React.PropTypes.func,
|
||||
onCancelClick: React.PropTypes.func,
|
||||
},
|
||||
|
||||
getInitialState: function() {
|
||||
|
@ -118,14 +115,10 @@ module.exports = React.createClass({
|
|||
// Default to false if it's undefined, otherwise react complains about changing
|
||||
// components from uncontrolled to controlled
|
||||
isRoomPublished: this._originalIsRoomPublished || false,
|
||||
scalar_error: null,
|
||||
showIntegrationsError: false,
|
||||
};
|
||||
},
|
||||
|
||||
componentWillMount: function() {
|
||||
ScalarMessaging.startListening();
|
||||
|
||||
MatrixClientPeg.get().on("RoomMember.membership", this._onRoomMemberMembership);
|
||||
|
||||
MatrixClientPeg.get().getRoomDirectoryVisibility(
|
||||
|
@ -137,18 +130,6 @@ module.exports = React.createClass({
|
|||
console.error("Failed to get room visibility: " + err);
|
||||
});
|
||||
|
||||
this.scalarClient = null;
|
||||
if (SdkConfig.get().integrations_ui_url && SdkConfig.get().integrations_rest_url) {
|
||||
this.scalarClient = new ScalarAuthClient();
|
||||
this.scalarClient.connect().done(() => {
|
||||
this.forceUpdate();
|
||||
}, (err) => {
|
||||
this.setState({
|
||||
scalar_error: err
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
dis.dispatch({
|
||||
action: 'ui_opacity',
|
||||
sideOpacity: 0.3,
|
||||
|
@ -157,8 +138,6 @@ module.exports = React.createClass({
|
|||
},
|
||||
|
||||
componentWillUnmount: function() {
|
||||
ScalarMessaging.stopListening();
|
||||
|
||||
const cli = MatrixClientPeg.get();
|
||||
if (cli) {
|
||||
cli.removeListener("RoomMember.membership", this._onRoomMemberMembership);
|
||||
|
@ -308,6 +287,9 @@ module.exports = React.createClass({
|
|||
promises.push(ps);
|
||||
}
|
||||
|
||||
// related groups
|
||||
promises.push(this.saveRelatedGroups());
|
||||
|
||||
// encryption
|
||||
p = this.saveEnableEncryption();
|
||||
if (!p.isFulfilled()) {
|
||||
|
@ -325,6 +307,11 @@ module.exports = React.createClass({
|
|||
return this.refs.alias_settings.saveSettings();
|
||||
},
|
||||
|
||||
saveRelatedGroups: function() {
|
||||
if (!this.refs.related_groups) { return Promise.resolve(); }
|
||||
return this.refs.related_groups.saveSettings();
|
||||
},
|
||||
|
||||
saveColor: function() {
|
||||
if (!this.refs.color_settings) { return Promise.resolve(); }
|
||||
return this.refs.color_settings.saveSettings();
|
||||
|
@ -514,28 +501,6 @@ module.exports = React.createClass({
|
|||
roomState.mayClientSendStateEvent("m.room.guest_access", cli));
|
||||
},
|
||||
|
||||
onManageIntegrations(ev) {
|
||||
ev.preventDefault();
|
||||
var IntegrationsManager = sdk.getComponent("views.settings.IntegrationsManager");
|
||||
Modal.createTrackedDialog('Integrations Manager', 'onManageIntegrations', IntegrationsManager, {
|
||||
src: (this.scalarClient !== null && this.scalarClient.hasCredentials()) ?
|
||||
this.scalarClient.getScalarInterfaceUrlForRoom(this.props.room.roomId) :
|
||||
null,
|
||||
onFinished: ()=>{
|
||||
if (this._calcSavePromises().length === 0) {
|
||||
this.props.onCancelClick(ev);
|
||||
}
|
||||
},
|
||||
}, "mx_IntegrationsManager");
|
||||
},
|
||||
|
||||
onShowIntegrationsError(ev) {
|
||||
ev.preventDefault();
|
||||
this.setState({
|
||||
showIntegrationsError: !this.state.showIntegrationsError,
|
||||
});
|
||||
},
|
||||
|
||||
onLeaveClick() {
|
||||
dis.dispatch({
|
||||
action: 'leave_room',
|
||||
|
@ -634,6 +599,7 @@ module.exports = React.createClass({
|
|||
var AliasSettings = sdk.getComponent("room_settings.AliasSettings");
|
||||
var ColorSettings = sdk.getComponent("room_settings.ColorSettings");
|
||||
var UrlPreviewSettings = sdk.getComponent("room_settings.UrlPreviewSettings");
|
||||
var RelatedGroupSettings = sdk.getComponent("room_settings.RelatedGroupSettings");
|
||||
var EditableText = sdk.getComponent('elements.EditableText');
|
||||
var PowerSelector = sdk.getComponent('elements.PowerSelector');
|
||||
var Loader = sdk.getComponent("elements.Spinner");
|
||||
|
@ -666,6 +632,14 @@ module.exports = React.createClass({
|
|||
|
||||
var self = this;
|
||||
|
||||
let relatedGroupsSection;
|
||||
if (UserSettingsStore.isFeatureEnabled('feature_groups')) {
|
||||
relatedGroupsSection = <RelatedGroupSettings ref="related_groups"
|
||||
roomId={this.props.room.roomId}
|
||||
canSetRelatedGroups={roomState.mayClientSendStateEvent("m.room.related_groups", cli)}
|
||||
relatedGroupsEvent={this.props.room.currentState.getStateEvents('m.room.related_groups', '')} />;
|
||||
}
|
||||
|
||||
var userLevelsSection;
|
||||
if (Object.keys(user_levels).length) {
|
||||
userLevelsSection =
|
||||
|
@ -797,46 +771,10 @@ module.exports = React.createClass({
|
|||
</div>;
|
||||
}
|
||||
|
||||
let integrationsButton;
|
||||
let integrationsError;
|
||||
|
||||
if (this.scalarClient !== null) {
|
||||
if (this.state.showIntegrationsError && this.state.scalar_error) {
|
||||
console.error(this.state.scalar_error);
|
||||
integrationsError = (
|
||||
<span className="mx_RoomSettings_integrationsButton_errorPopup">
|
||||
{ _t('Could not connect to the integration server') }
|
||||
</span>
|
||||
);
|
||||
}
|
||||
|
||||
if (this.scalarClient.hasCredentials()) {
|
||||
integrationsButton = (
|
||||
<div className="mx_RoomSettings_integrationsButton" onClick={ this.onManageIntegrations }>
|
||||
{ _t('Manage Integrations') }
|
||||
</div>
|
||||
);
|
||||
} else if (this.state.scalar_error) {
|
||||
integrationsButton = (
|
||||
<div className="mx_RoomSettings_integrationsButton_error" onClick={ this.onShowIntegrationsError }>
|
||||
Integrations Error <img src="img/warning.svg" width="17"/>
|
||||
{ integrationsError }
|
||||
</div>
|
||||
);
|
||||
} else {
|
||||
integrationsButton = (
|
||||
<div className="mx_RoomSettings_integrationsButton" style={{opacity: 0.5}}>
|
||||
{ _t('Manage Integrations') }
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="mx_RoomSettings">
|
||||
|
||||
{ leaveButton }
|
||||
{ integrationsButton }
|
||||
|
||||
{ tagsSection }
|
||||
|
||||
|
@ -872,7 +810,7 @@ module.exports = React.createClass({
|
|||
<input type="checkbox" disabled={ !roomState.mayClientSendStateEvent("m.room.aliases", cli) }
|
||||
onChange={ this._onToggle.bind(this, "isRoomPublished", true, false)}
|
||||
checked={this.state.isRoomPublished}/>
|
||||
{_t("List this room in %(domain)s's room directory?", { domain: MatrixClientPeg.get().getDomain() })}
|
||||
{_t("Publish this room to the public in %(domain)s's room directory?", { domain: MatrixClientPeg.get().getDomain() })}
|
||||
</label>
|
||||
</div>
|
||||
<div className="mx_RoomSettings_settings">
|
||||
|
@ -926,6 +864,8 @@ module.exports = React.createClass({
|
|||
canonicalAliasEvent={this.props.room.currentState.getStateEvents('m.room.canonical_alias', '')}
|
||||
aliasEvents={this.props.room.currentState.getStateEvents('m.room.aliases')} />
|
||||
|
||||
{ relatedGroupsSection }
|
||||
|
||||
<UrlPreviewSettings ref="url_preview_settings" room={this.props.room} />
|
||||
|
||||
<h3>{ _t('Permissions') }</h3>
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
/*
|
||||
Copyright 2015, 2016 OpenMarket Ltd
|
||||
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.
|
||||
|
@ -27,6 +28,8 @@ var RoomNotifs = require('../../../RoomNotifs');
|
|||
var FormattingUtils = require('../../../utils/FormattingUtils');
|
||||
import AccessibleButton from '../elements/AccessibleButton';
|
||||
var UserSettingsStore = require('../../../UserSettingsStore');
|
||||
import ActiveRoomObserver from '../../../ActiveRoomObserver';
|
||||
import RoomViewStore from '../../../stores/RoomViewStore';
|
||||
|
||||
module.exports = React.createClass({
|
||||
displayName: 'RoomTile',
|
||||
|
@ -39,7 +42,6 @@ module.exports = React.createClass({
|
|||
|
||||
room: React.PropTypes.object.isRequired,
|
||||
collapsed: React.PropTypes.bool.isRequired,
|
||||
selected: React.PropTypes.bool.isRequired,
|
||||
unread: React.PropTypes.bool.isRequired,
|
||||
highlight: React.PropTypes.bool.isRequired,
|
||||
isInvite: React.PropTypes.bool.isRequired,
|
||||
|
@ -58,6 +60,7 @@ module.exports = React.createClass({
|
|||
badgeHover : false,
|
||||
menuDisplayed: false,
|
||||
notifState: RoomNotifs.getRoomNotifsState(this.props.room.roomId),
|
||||
selected: this.props.room.roomId === RoomViewStore.getRoomId(),
|
||||
});
|
||||
},
|
||||
|
||||
|
@ -87,8 +90,15 @@ module.exports = React.createClass({
|
|||
}
|
||||
},
|
||||
|
||||
_onActiveRoomChange: function() {
|
||||
this.setState({
|
||||
selected: this.props.room.roomId === RoomViewStore.getRoomId(),
|
||||
});
|
||||
},
|
||||
|
||||
componentWillMount: function() {
|
||||
MatrixClientPeg.get().on("accountData", this.onAccountData);
|
||||
ActiveRoomObserver.addListener(this.props.room.roomId, this._onActiveRoomChange);
|
||||
},
|
||||
|
||||
componentWillUnmount: function() {
|
||||
|
@ -96,6 +106,7 @@ module.exports = React.createClass({
|
|||
if (cli) {
|
||||
MatrixClientPeg.get().removeListener("accountData", this.onAccountData);
|
||||
}
|
||||
ActiveRoomObserver.removeListener(this.props.room.roomId, this._onActiveRoomChange);
|
||||
},
|
||||
|
||||
onClick: function(ev) {
|
||||
|
@ -174,7 +185,7 @@ module.exports = React.createClass({
|
|||
|
||||
var classes = classNames({
|
||||
'mx_RoomTile': true,
|
||||
'mx_RoomTile_selected': this.props.selected,
|
||||
'mx_RoomTile_selected': this.state.selected,
|
||||
'mx_RoomTile_unread': this.props.unread,
|
||||
'mx_RoomTile_unreadNotify': notifBadges,
|
||||
'mx_RoomTile_highlight': mentionBadges,
|
||||
|
@ -221,7 +232,7 @@ module.exports = React.createClass({
|
|||
'mx_RoomTile_badgeShown': badges || this.state.badgeHover || this.state.menuDisplayed,
|
||||
});
|
||||
|
||||
if (this.props.selected) {
|
||||
if (this.state.selected) {
|
||||
let nameSelected = <EmojiText>{name}</EmojiText>;
|
||||
|
||||
label = <div title={ name } className={ nameClasses } dir="auto">{ nameSelected }</div>;
|
||||
|
|
|
@ -26,7 +26,7 @@ export function CancelButton(props) {
|
|||
return (
|
||||
<AccessibleButton className='mx_RoomHeader_cancelButton' onClick={onClick}>
|
||||
<img src="img/cancel.svg" className='mx_filterFlipColor'
|
||||
width="18" height="18" alt={_t("Cancel")}/>
|
||||
width="18" height="18" alt={_t("Cancel")} />
|
||||
</AccessibleButton>
|
||||
);
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue