Merge branch 'develop' into matthew/slate

This commit is contained in:
Matthew Hodgson 2018-05-13 19:50:55 +01:00
commit 721410b710
30 changed files with 908 additions and 278 deletions

View file

@ -202,17 +202,12 @@ module.exports = withMatrixClient(React.createClass({
return true;
}
if (!this._propsEqual(this.props, nextProps)) {
return true;
}
return false;
return !this._propsEqual(this.props, nextProps);
},
componentWillUnmount: function() {
const client = this.props.matrixClient;
client.removeListener("deviceVerificationChanged",
this.onDeviceVerificationChanged);
client.removeListener("deviceVerificationChanged", this.onDeviceVerificationChanged);
this.props.mxEvent.removeListener("Event.decrypted", this._onDecrypted);
},
@ -227,7 +222,7 @@ module.exports = withMatrixClient(React.createClass({
},
onDeviceVerificationChanged: function(userId, device) {
if (userId == this.props.mxEvent.getSender()) {
if (userId === this.props.mxEvent.getSender()) {
this._verifyEvent(this.props.mxEvent);
}
},
@ -262,7 +257,7 @@ module.exports = withMatrixClient(React.createClass({
}
// need to deep-compare readReceipts
if (key == 'readReceipts') {
if (key === 'readReceipts') {
const rA = objA[key];
const rB = objB[key];
if (rA === rB) {
@ -336,7 +331,7 @@ module.exports = withMatrixClient(React.createClass({
getReadAvatars: function() {
// return early if there are no read receipts
if (!this.props.readReceipts || this.props.readReceipts.length === 0) {
return (<span className="mx_EventTile_readAvatars"></span>);
return (<span className="mx_EventTile_readAvatars" />);
}
const ReadReceiptMarker = sdk.getComponent('rooms.ReadReceiptMarker');
@ -360,7 +355,7 @@ module.exports = withMatrixClient(React.createClass({
left = (hidden ? MAX_READ_AVATARS - 1 : i) * -receiptOffset;
const userId = receipt.roomMember.userId;
var readReceiptInfo;
let readReceiptInfo;
if (this.props.readReceiptMap) {
readReceiptInfo = this.props.readReceiptMap[userId];
@ -502,17 +497,17 @@ module.exports = withMatrixClient(React.createClass({
mx_EventTile: true,
mx_EventTile_info: isInfoMessage,
mx_EventTile_12hr: this.props.isTwelveHour,
mx_EventTile_encrypting: this.props.eventSendStatus == 'encrypting',
mx_EventTile_encrypting: this.props.eventSendStatus === 'encrypting',
mx_EventTile_sending: isSending,
mx_EventTile_notSent: this.props.eventSendStatus == 'not_sent',
mx_EventTile_highlight: this.props.tileShape == 'notif' ? false : this.shouldHighlight(),
mx_EventTile_notSent: this.props.eventSendStatus === 'not_sent',
mx_EventTile_highlight: this.props.tileShape === 'notif' ? false : this.shouldHighlight(),
mx_EventTile_selected: this.props.isSelectedEvent,
mx_EventTile_continuation: this.props.tileShape ? '' : this.props.continuation,
mx_EventTile_last: this.props.last,
mx_EventTile_contextual: this.props.contextual,
menu: this.state.menu,
mx_EventTile_verified: this.state.verified == true,
mx_EventTile_unverified: this.state.verified == false,
mx_EventTile_verified: this.state.verified === true,
mx_EventTile_unverified: this.state.verified === false,
mx_EventTile_bad: isEncryptionFailure,
mx_EventTile_emote: msgtype === 'm.emote',
mx_EventTile_redacted: isRedacted,
@ -522,7 +517,8 @@ module.exports = withMatrixClient(React.createClass({
const readAvatars = this.getReadAvatars();
let avatar, sender;
let avatar;
let sender;
let avatarSize;
let needsSenderProfile;
@ -556,11 +552,14 @@ module.exports = withMatrixClient(React.createClass({
if (needsSenderProfile) {
let text = null;
if (!this.props.tileShape) {
if (!this.props.tileShape || this.props.tileShape === 'reply' || this.props.tileShape === 'reply_preview') {
if (msgtype === 'm.image') text = _td('%(senderName)s sent an image');
else if (msgtype === 'm.video') text = _td('%(senderName)s sent a video');
else if (msgtype === 'm.file') text = _td('%(senderName)s uploaded a file');
sender = <SenderProfile onClick={this.onSenderProfileClick} mxEvent={this.props.mxEvent} enableFlair={!text} text={text} />;
sender = <SenderProfile onClick={this.onSenderProfileClick}
mxEvent={this.props.mxEvent}
enableFlair={!text}
text={text} />;
} else {
sender = <SenderProfile mxEvent={this.props.mxEvent} enableFlair={true} />;
}

View file

@ -338,10 +338,7 @@ export default class MessageComposer extends React.Component {
}
}
let stickerpickerButton;
if (SettingsStore.isFeatureEnabled('feature_sticker_messages')) {
stickerpickerButton = <Stickerpicker key='stickerpicker_controls_button' room={this.props.room} />;
}
const stickerpickerButton = <Stickerpicker key='stickerpicker_controls_button' room={this.props.room} />;
controls.push(
<MessageComposerInput

View file

@ -17,7 +17,6 @@ import React from 'react';
import { _t } from '../../../languageHandler';
import Widgets from '../../../utils/widgets';
import AppTile from '../elements/AppTile';
import ContextualMenu from '../../structures/ContextualMenu';
import MatrixClientPeg from '../../../MatrixClientPeg';
import Modal from '../../../Modal';
import sdk from '../../../index';
@ -36,21 +35,28 @@ export default class Stickerpicker extends React.Component {
this._launchManageIntegrations = this._launchManageIntegrations.bind(this);
this._removeStickerpickerWidgets = this._removeStickerpickerWidgets.bind(this);
this._onWidgetAction = this._onWidgetAction.bind(this);
this._onResize = this._onResize.bind(this);
this._onFinished = this._onFinished.bind(this);
this._collectWidgetMessaging = this._collectWidgetMessaging.bind(this);
this.popoverWidth = 300;
this.popoverHeight = 300;
this.state = {
showStickers: false,
imError: null,
stickerpickerX: null,
stickerpickerY: null,
stickerpickerWidget: null,
widgetId: null,
};
}
_removeStickerpickerWidgets() {
console.warn('Removing Stickerpicker widgets');
if (this.widgetId) {
this.scalarClient.disableWidgetAssets(widgetType, this.widgetId).then(() => {
if (this.state.widgetId) {
this.scalarClient.disableWidgetAssets(widgetType, this.state.widgetId).then(() => {
console.warn('Assets disabled');
}).catch((err) => {
console.error('Failed to disable assets');
@ -59,8 +65,7 @@ export default class Stickerpicker extends React.Component {
console.warn('No widget ID specified, not disabling assets');
}
// Wrap this in a timeout in order to avoid the DOM node from being pulled from under its feet
setTimeout(() => this.stickersMenu.close());
this.setState({showStickers: false});
Widgets.removeStickerpickerWidgets().then(() => {
this.forceUpdate();
}).catch((e) => {
@ -69,6 +74,9 @@ export default class Stickerpicker extends React.Component {
}
componentDidMount() {
// Close the sticker picker when the window resizes
window.addEventListener('resize', this._onResize);
this.scalarClient = null;
if (SdkConfig.get().integrations_ui_url && SdkConfig.get().integrations_rest_url) {
this.scalarClient = new ScalarAuthClient();
@ -82,14 +90,24 @@ export default class Stickerpicker extends React.Component {
if (!this.state.imError) {
this.dispatcherRef = dis.register(this._onWidgetAction);
}
const stickerpickerWidget = Widgets.getStickerpickerWidgets()[0];
this.setState({
stickerpickerWidget,
widgetId: stickerpickerWidget ? stickerpickerWidget.id : null,
});
}
componentWillUnmount() {
window.removeEventListener('resize', this._onResize);
if (this.dispatcherRef) {
dis.unregister(this.dispatcherRef);
}
}
componentDidUpdate(prevProps, prevState) {
this._sendVisibilityToWidget(this.state.showStickers);
}
_imError(errorMsg, e) {
console.error(errorMsg, e);
this.setState({
@ -102,9 +120,7 @@ export default class Stickerpicker extends React.Component {
if (payload.action === "user_widget_updated") {
this.forceUpdate();
} else if (payload.action === "stickerpicker_close") {
// Wrap this in a timeout in order to avoid the DOM node from being
// pulled from under its feet
setTimeout(() => this.stickersMenu.close());
this.setState({showStickers: false});
}
}
@ -127,6 +143,21 @@ export default class Stickerpicker extends React.Component {
);
}
_collectWidgetMessaging(widgetMessaging) {
this._appWidgetMessaging = widgetMessaging;
// Do this now instead of in componentDidMount because we might not have had the
// reference to widgetMessaging when mounting
this._sendVisibilityToWidget(true);
}
_sendVisibilityToWidget(visible) {
if (this._appWidgetMessaging && visible !== this._prevSentVisibility) {
this._appWidgetMessaging.sendVisibility(visible);
this._prevSentVisibility = visible;
}
}
_getStickerpickerContent() {
// Handle Integration Manager errors
if (this.state._imError) {
@ -137,14 +168,18 @@ export default class Stickerpicker extends React.Component {
// TODO - Add support for Stickerpickers from multiple app stores.
// Render content from multiple stickerpack sources, each within their
// own iframe, within the stickerpicker UI element.
const stickerpickerWidget = Widgets.getStickerpickerWidgets()[0];
const stickerpickerWidget = this.state.stickerpickerWidget;
let stickersContent;
// Use a separate ReactDOM tree to render the AppTile separately so that it persists and does
// not unmount when we (a) close the sticker picker (b) switch rooms. It's properties are still
// updated.
const PersistedElement = sdk.getComponent("elements.PersistedElement");
// Load stickerpack content
if (stickerpickerWidget && stickerpickerWidget.content && stickerpickerWidget.content.url) {
// Set default name
stickerpickerWidget.content.name = stickerpickerWidget.name || _t("Stickerpack");
this.widgetId = stickerpickerWidget.id;
stickersContent = (
<div className='mx_Stickers_content_container'>
@ -157,15 +192,17 @@ export default class Stickerpicker extends React.Component {
width: this.popoverWidth,
}}
>
<PersistedElement>
<AppTile
collectWidgetMessaging={this._collectWidgetMessaging}
id={stickerpickerWidget.id}
url={stickerpickerWidget.content.url}
name={stickerpickerWidget.content.name}
room={this.props.room}
type={stickerpickerWidget.content.type}
fullWidth={true}
userId={stickerpickerWidget.sender || MatrixClientPeg.get().credentials.userId}
creatorUserId={MatrixClientPeg.get().credentials.userId}
userId={MatrixClientPeg.get().credentials.userId}
creatorUserId={stickerpickerWidget.sender || MatrixClientPeg.get().credentials.userId}
waitForIframeLoad={true}
show={true}
showMenubar={true}
@ -177,8 +214,10 @@ export default class Stickerpicker extends React.Component {
showPopout={false}
onMinimiseClick={this._onHideStickersClick}
handleMinimisePointerEvents={true}
whitelistCapabilities={['m.sticker']}
whitelistCapabilities={['m.sticker', 'visibility']}
userWidget={true}
/>
</PersistedElement>
</div>
</div>
);
@ -186,12 +225,7 @@ export default class Stickerpicker extends React.Component {
// Default content to show if stickerpicker widget not added
console.warn("No available sticker picker widgets");
stickersContent = this._defaultStickerpickerContent();
this.widgetId = null;
this.forceUpdate();
}
this.setState({
showStickers: false,
});
return stickersContent;
}
@ -201,29 +235,17 @@ export default class Stickerpicker extends React.Component {
* @param {Event} e Event that triggered the function
*/
_onShowStickersClick(e) {
const GenericElementContextMenu = sdk.getComponent('context_menus.GenericElementContextMenu');
const buttonRect = e.target.getBoundingClientRect();
// The window X and Y offsets are to adjust position when zoomed in to page
const x = buttonRect.right + window.pageXOffset - 42;
const y = (buttonRect.top + (buttonRect.height / 2) + window.pageYOffset) - 19;
// const self = this;
this.stickersMenu = ContextualMenu.createMenu(GenericElementContextMenu, {
chevronOffset: 10,
chevronFace: 'bottom',
left: x,
top: y,
menuWidth: this.popoverWidth,
menuHeight: this.popoverHeight,
element: this._getStickerpickerContent(),
onFinished: this._onFinished,
menuPaddingTop: 0,
menuPaddingLeft: 0,
menuPaddingRight: 0,
this.setState({
showStickers: true,
stickerPickerX: x,
stickerPickerY: y,
});
this.setState({showStickers: true});
}
/**
@ -231,7 +253,14 @@ export default class Stickerpicker extends React.Component {
* @param {Event} ev Event that triggered the function call
*/
_onHideStickersClick(ev) {
setTimeout(() => this.stickersMenu.close());
this.setState({showStickers: false});
}
/**
* Called when the window is resized
*/
_onResize() {
this.setState({showStickers: false});
}
/**
@ -250,20 +279,37 @@ export default class Stickerpicker extends React.Component {
this.scalarClient.getScalarInterfaceUrlForRoom(
this.props.room,
'type_' + widgetType,
this.widgetId,
this.state.widgetId,
) :
null;
Modal.createTrackedDialog('Integrations Manager', '', IntegrationsManager, {
src: src,
}, "mx_IntegrationsManager");
// Wrap this in a timeout in order to avoid the DOM node from being pulled from under its feet
setTimeout(() => this.stickersMenu.close());
this.setState({showStickers: false});
}
render() {
const TintableSvg = sdk.getComponent("elements.TintableSvg");
const ContextualMenu = sdk.getComponent('structures.ContextualMenu');
const GenericElementContextMenu = sdk.getComponent('context_menus.GenericElementContextMenu');
let stickersButton;
const stickerPicker = <ContextualMenu
elementClass={GenericElementContextMenu}
chevronOffset={10}
chevronFace={'bottom'}
left={this.state.stickerPickerX}
top={this.state.stickerPickerY}
menuWidth={this.popoverWidth}
menuHeight={this.popoverHeight}
element={this._getStickerpickerContent()}
onFinished={this._onFinished}
menuPaddingTop={0}
menuPaddingLeft={0}
menuPaddingRight={0}
/>;
if (this.state.showStickers) {
// Show hide-stickers button
stickersButton =
@ -288,6 +334,9 @@ export default class Stickerpicker extends React.Component {
<TintableSvg src="img/icons-show-stickers.svg" width="35" height="35" />
</div>;
}
return stickersButton;
return <div>
{stickersButton}
{this.state.showStickers && stickerPicker}
</div>;
}
}