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

This commit is contained in:
David Baker 2017-08-22 10:08:02 +01:00
commit 5f816666e1
49 changed files with 5337 additions and 813 deletions

View file

@ -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',
@ -53,9 +55,6 @@ module.exports = React.createClass({
this.scalarClient = new ScalarAuthClient();
this.scalarClient.connect().done(() => {
this.forceUpdate();
if (this.state.apps && this.state.apps.length < 1) {
this.onClickAddWidget();
}
// TODO -- Handle Scalar errors
// },
// (err) => {
@ -64,6 +63,8 @@ module.exports = React.createClass({
// });
});
}
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}
</div>
{addWidget}
{this._canUserModify() && addWidget}
</div>
);
},

View file

@ -129,15 +129,17 @@ 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 (
<div className="mx_RoomView_auxPanel" style={{maxHeight: this.props.maxHeight}} >
<div className={ appsDrawer ? "mx_RoomView_auxPanel mx_RoomView_auxPanel_apps" : "mx_RoomView_auxPanel" } style={{maxHeight: this.props.maxHeight}} >
{ appsDrawer }
{ fileDropTarget }
{ callView }

View file

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

View file

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

View file

@ -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: function() {},
};
},
@ -320,10 +323,18 @@ module.exports = React.createClass({
}
let rightRow;
let manageIntegsButton;
if(this.props.room && this.props.room.roomId) {
manageIntegsButton = <ManageIntegsButton
roomId={this.props.room.roomId}
/>;
}
if (!this.props.editing) {
rightRow =
<div className="mx_RoomHeader_rightRow">
{ settingsButton }
{ manageIntegsButton }
{ forgetButton }
{ searchButton }
{ rightPanelButtons }

View file

@ -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);
@ -513,28 +492,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',
@ -796,46 +753,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 }
@ -871,7 +792,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">