merge develop
This commit is contained in:
commit
efdc5430d7
176 changed files with 7537 additions and 3401 deletions
|
@ -27,7 +27,7 @@ import SdkConfig from '../../../SdkConfig';
|
|||
import ScalarAuthClient from '../../../ScalarAuthClient';
|
||||
import ScalarMessaging from '../../../ScalarMessaging';
|
||||
import { _t } from '../../../languageHandler';
|
||||
import WidgetUtils from '../../../WidgetUtils';
|
||||
import WidgetUtils from '../../../utils/WidgetUtils';
|
||||
import SettingsStore from "../../../settings/SettingsStore";
|
||||
|
||||
// The maximum number of widgets that can be added in a room
|
||||
|
@ -94,15 +94,7 @@ module.exports = React.createClass({
|
|||
const hideWidgetKey = this.props.room.roomId + '_hide_widget_drawer';
|
||||
switch (action.action) {
|
||||
case 'appsDrawer':
|
||||
// When opening the app drawer 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();
|
||||
}
|
||||
|
||||
localStorage.removeItem(hideWidgetKey);
|
||||
} else {
|
||||
// Store hidden state of widget
|
||||
|
@ -171,14 +163,7 @@ module.exports = React.createClass({
|
|||
},
|
||||
|
||||
_getApps: function() {
|
||||
const appsStateEvents = this.props.room.currentState.getStateEvents('im.vector.modular.widgets');
|
||||
if (!appsStateEvents) {
|
||||
return [];
|
||||
}
|
||||
|
||||
return appsStateEvents.filter((ev) => {
|
||||
return ev.getContent().type && ev.getContent().url;
|
||||
}).map((ev) => {
|
||||
return WidgetUtils.getRoomWidgets(this.props.room).map((ev) => {
|
||||
return this._initAppConfig(ev.getStateKey(), ev.getContent(), ev.sender);
|
||||
});
|
||||
},
|
||||
|
|
|
@ -34,6 +34,7 @@ const ContextualMenu = require('../../structures/ContextualMenu');
|
|||
import dis from '../../../dispatcher';
|
||||
import {makeEventPermalink} from "../../../matrix-to";
|
||||
import SettingsStore from "../../../settings/SettingsStore";
|
||||
import {EventStatus} from 'matrix-js-sdk';
|
||||
|
||||
const ObjectUtils = require('../../../ObjectUtils');
|
||||
|
||||
|
@ -55,6 +56,7 @@ const stateEventTileTypes = {
|
|||
'm.room.topic': 'messages.TextualEvent',
|
||||
'm.room.power_levels': 'messages.TextualEvent',
|
||||
'm.room.pinned_events': 'messages.TextualEvent',
|
||||
'm.room.server_acl' : 'messages.TextualEvent',
|
||||
|
||||
'im.vector.modular.widgets': 'messages.TextualEvent',
|
||||
};
|
||||
|
@ -442,26 +444,27 @@ module.exports = withMatrixClient(React.createClass({
|
|||
const ev = this.props.mxEvent;
|
||||
const props = {onClick: this.onCryptoClicked};
|
||||
|
||||
|
||||
// event could not be decrypted
|
||||
if (ev.getContent().msgtype === 'm.bad.encrypted') {
|
||||
return <E2ePadlockUndecryptable {...props} />;
|
||||
} else if (ev.isEncrypted()) {
|
||||
if (this.state.verified) {
|
||||
return <E2ePadlockVerified {...props} />;
|
||||
} else {
|
||||
return <E2ePadlockUnverified {...props} />;
|
||||
}
|
||||
} else {
|
||||
// XXX: if the event is being encrypted (ie eventSendStatus ===
|
||||
// encrypting), it might be nice to show something other than the
|
||||
// open padlock?
|
||||
}
|
||||
|
||||
// if the event is not encrypted, but it's an e2e room, show the
|
||||
// open padlock
|
||||
const e2eEnabled = this.props.matrixClient.isRoomEncrypted(ev.getRoomId());
|
||||
if (e2eEnabled) {
|
||||
return <E2ePadlockUnencrypted {...props} />;
|
||||
// event is encrypted, display padlock corresponding to whether or not it is verified
|
||||
if (ev.isEncrypted()) {
|
||||
return this.state.verified ? <E2ePadlockVerified {...props} /> : <E2ePadlockUnverified {...props} />;
|
||||
}
|
||||
|
||||
if (this.props.matrixClient.isRoomEncrypted(ev.getRoomId())) {
|
||||
// else if room is encrypted
|
||||
// and event is being encrypted or is not_sent (Unknown Devices/Network Error)
|
||||
if (ev.status === EventStatus.ENCRYPTING) {
|
||||
return <E2ePadlockEncrypting {...props} />;
|
||||
}
|
||||
if (ev.status === EventStatus.NOT_SENT) {
|
||||
return <E2ePadlockNotSent {...props} />;
|
||||
}
|
||||
// if the event is not encrypted, but it's an e2e room, show the open padlock
|
||||
return <E2ePadlockUnencrypted {...props} />;
|
||||
}
|
||||
|
||||
// no padlock needed
|
||||
|
@ -490,7 +493,7 @@ module.exports = withMatrixClient(React.createClass({
|
|||
}
|
||||
|
||||
const isSending = (['sending', 'queued', 'encrypting'].indexOf(this.props.eventSendStatus) !== -1);
|
||||
const isRedacted = (eventType === 'm.room.message') && this.props.isRedacted;
|
||||
const isRedacted = isMessageEvent(this.props.mxEvent) && this.props.isRedacted;
|
||||
const isEncryptionFailure = this.props.mxEvent.isDecryptionFailure();
|
||||
|
||||
const classes = classNames({
|
||||
|
@ -608,13 +611,14 @@ module.exports = withMatrixClient(React.createClass({
|
|||
|
||||
switch (this.props.tileShape) {
|
||||
case 'notif': {
|
||||
const EmojiText = sdk.getComponent('elements.EmojiText');
|
||||
const room = this.props.matrixClient.getRoom(this.props.mxEvent.getRoomId());
|
||||
return (
|
||||
<div className={classes}>
|
||||
<div className="mx_EventTile_roomName">
|
||||
<a href={permalink} onClick={this.onPermalinkClicked}>
|
||||
<EmojiText element="a" href={permalink} onClick={this.onPermalinkClicked}>
|
||||
{ room ? room.name : '' }
|
||||
</a>
|
||||
</EmojiText>
|
||||
</div>
|
||||
<div className="mx_EventTile_senderDetails">
|
||||
{ avatar }
|
||||
|
@ -715,9 +719,15 @@ module.exports = withMatrixClient(React.createClass({
|
|||
},
|
||||
}));
|
||||
|
||||
// XXX this'll eventually be dynamic based on the fields once we have extensible event types
|
||||
const messageTypes = ['m.room.message', 'm.sticker'];
|
||||
function isMessageEvent(ev) {
|
||||
return (messageTypes.includes(ev.getType()));
|
||||
}
|
||||
|
||||
module.exports.haveTileForEvent = function(e) {
|
||||
// Only messages have a tile (black-rectangle) if redacted
|
||||
if (e.isRedacted() && e.getType() !== 'm.room.message') return false;
|
||||
if (e.isRedacted() && !isMessageEvent(e)) return false;
|
||||
|
||||
const handler = getHandlerTile(e);
|
||||
if (handler === undefined) return false;
|
||||
|
@ -736,6 +746,14 @@ function E2ePadlockUndecryptable(props) {
|
|||
);
|
||||
}
|
||||
|
||||
function E2ePadlockEncrypting(props) {
|
||||
return <E2ePadlock alt={_t("Encrypting")} src="img/e2e-encrypting.svg" width="10" height="12" {...props} />;
|
||||
}
|
||||
|
||||
function E2ePadlockNotSent(props) {
|
||||
return <E2ePadlock alt={_t("Encrypted, not sent")} src="img/e2e-not_sent.svg" width="10" height="12" {...props} />;
|
||||
}
|
||||
|
||||
function E2ePadlockVerified(props) {
|
||||
return (
|
||||
<E2ePadlock alt={_t("Encrypted by a verified device")}
|
||||
|
|
|
@ -332,13 +332,40 @@ module.exports = withMatrixClient(React.createClass({
|
|||
});
|
||||
},
|
||||
|
||||
onMuteToggle: function() {
|
||||
_warnSelfDemote: function() {
|
||||
const QuestionDialog = sdk.getComponent("dialogs.QuestionDialog");
|
||||
return new Promise((resolve) => {
|
||||
Modal.createTrackedDialog('Demoting Self', '', QuestionDialog, {
|
||||
title: _t("Demote yourself?"),
|
||||
description:
|
||||
<div>
|
||||
{ _t("You will not be able to undo this change as you are demoting yourself, " +
|
||||
"if you are the last privileged user in the room it will be impossible " +
|
||||
"to regain privileges.") }
|
||||
</div>,
|
||||
button: _t("Demote"),
|
||||
onFinished: resolve,
|
||||
});
|
||||
});
|
||||
},
|
||||
|
||||
onMuteToggle: async function() {
|
||||
const ErrorDialog = sdk.getComponent("dialogs.ErrorDialog");
|
||||
const roomId = this.props.member.roomId;
|
||||
const target = this.props.member.userId;
|
||||
const room = this.props.matrixClient.getRoom(roomId);
|
||||
if (!room) return;
|
||||
|
||||
// if muting self, warn as it may be irreversible
|
||||
if (target === this.props.matrixClient.getUserId()) {
|
||||
try {
|
||||
if (!(await this._warnSelfDemote())) return;
|
||||
} catch (e) {
|
||||
console.error("Failed to warn about self demotion: ", e);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
const powerLevelEvent = room.currentState.getStateEvents("m.room.power_levels", "");
|
||||
if (!powerLevelEvent) return;
|
||||
|
||||
|
@ -436,7 +463,7 @@ module.exports = withMatrixClient(React.createClass({
|
|||
}).done();
|
||||
},
|
||||
|
||||
onPowerChange: function(powerLevel) {
|
||||
onPowerChange: async function(powerLevel) {
|
||||
const roomId = this.props.member.roomId;
|
||||
const target = this.props.member.userId;
|
||||
const room = this.props.matrixClient.getRoom(roomId);
|
||||
|
@ -455,20 +482,12 @@ module.exports = withMatrixClient(React.createClass({
|
|||
|
||||
// If we are changing our own PL it can only ever be decreasing, which we cannot reverse.
|
||||
if (myUserId === target) {
|
||||
Modal.createTrackedDialog('Demoting Self', '', QuestionDialog, {
|
||||
title: _t("Warning!"),
|
||||
description:
|
||||
<div>
|
||||
{ _t("You will not be able to undo this change as you are demoting yourself, if you are the last privileged user in the room it will be impossible to regain privileges.") }<br />
|
||||
{ _t("Are you sure?") }
|
||||
</div>,
|
||||
button: _t("Continue"),
|
||||
onFinished: (confirmed) => {
|
||||
if (confirmed) {
|
||||
this._applyPowerChange(roomId, target, powerLevel, powerLevelEvent);
|
||||
}
|
||||
},
|
||||
});
|
||||
try {
|
||||
if (!(await this._warnSelfDemote())) return;
|
||||
this._applyPowerChange(roomId, target, powerLevel, powerLevelEvent);
|
||||
} catch (e) {
|
||||
console.error("Failed to warn about self demotion: ", e);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -478,7 +497,8 @@ module.exports = withMatrixClient(React.createClass({
|
|||
title: _t("Warning!"),
|
||||
description:
|
||||
<div>
|
||||
{ _t("You will not be able to undo this change as you are promoting the user to have the same power level as yourself.") }<br />
|
||||
{ _t("You will not be able to undo this change as you are promoting the user " +
|
||||
"to have the same power level as yourself.") }<br />
|
||||
{ _t("Are you sure?") }
|
||||
</div>,
|
||||
button: _t("Continue"),
|
||||
|
@ -632,6 +652,13 @@ module.exports = withMatrixClient(React.createClass({
|
|||
);
|
||||
},
|
||||
|
||||
onShareUserClick: function() {
|
||||
const ShareDialog = sdk.getComponent("dialogs.ShareDialog");
|
||||
Modal.createTrackedDialog('share room member dialog', '', ShareDialog, {
|
||||
target: this.props.member,
|
||||
});
|
||||
},
|
||||
|
||||
_renderUserOptions: function() {
|
||||
const cli = this.props.matrixClient;
|
||||
const member = this.props.member;
|
||||
|
@ -705,13 +732,18 @@ module.exports = withMatrixClient(React.createClass({
|
|||
}
|
||||
}
|
||||
|
||||
if (!ignoreButton && !readReceiptButton && !insertPillButton && !inviteUserButton) return null;
|
||||
const shareUserButton = (
|
||||
<AccessibleButton onClick={this.onShareUserClick} className="mx_MemberInfo_field">
|
||||
{ _t('Share Link to User') }
|
||||
</AccessibleButton>
|
||||
);
|
||||
|
||||
return (
|
||||
<div>
|
||||
<h3>{ _t("User Options") }</h3>
|
||||
<div className="mx_MemberInfo_buttons">
|
||||
{ readReceiptButton }
|
||||
{ shareUserButton }
|
||||
{ insertPillButton }
|
||||
{ ignoreButton }
|
||||
{ inviteUserButton }
|
||||
|
@ -902,7 +934,9 @@ module.exports = withMatrixClient(React.createClass({
|
|||
return (
|
||||
<div className="mx_MemberInfo">
|
||||
<GeminiScrollbarWrapper autoshow={true}>
|
||||
<AccessibleButton className="mx_MemberInfo_cancel" onClick={this.onCancel}> <img src="img/cancel.svg" width="18" height="18" /></AccessibleButton>
|
||||
<AccessibleButton className="mx_MemberInfo_cancel" onClick={this.onCancel}>
|
||||
<img src="img/cancel.svg" width="18" height="18" className="mx_filterFlipColor" />
|
||||
</AccessibleButton>
|
||||
<div className="mx_MemberInfo_avatar">
|
||||
<MemberAvatar onClick={this.onMemberAvatarClick} member={this.props.member} width={48} height={48} />
|
||||
</div>
|
||||
|
|
|
@ -270,7 +270,7 @@ module.exports = React.createClass({
|
|||
|
||||
// console.log("comparing " + this.memberString(memberA) + " and " + this.memberString(memberB));
|
||||
|
||||
if (userA.currentlyActive && userB.currentlyActive) {
|
||||
if ((userA.currentlyActive && userB.currentlyActive) || !this._showPresence) {
|
||||
// console.log(memberA.name + " and " + memberB.name + " are both active");
|
||||
if (memberA.powerLevel === memberB.powerLevel) {
|
||||
// console.log(memberA + " and " + memberB + " have same power level");
|
||||
|
|
|
@ -155,54 +155,20 @@ export default class MessageComposer extends React.Component {
|
|||
});
|
||||
}
|
||||
|
||||
// _startCallApp(isAudioConf) {
|
||||
// dis.dispatch({
|
||||
// action: 'appsDrawer',
|
||||
// show: true,
|
||||
// });
|
||||
|
||||
// const appsStateEvents = this.props.room.currentState.getStateEvents('im.vector.modular.widgets', '');
|
||||
// let appsStateEvent = {};
|
||||
// if (appsStateEvents) {
|
||||
// appsStateEvent = appsStateEvents.getContent();
|
||||
// }
|
||||
// if (!appsStateEvent.videoConf) {
|
||||
// appsStateEvent.videoConf = {
|
||||
// type: 'jitsi',
|
||||
// // FIXME -- This should not be localhost
|
||||
// url: 'http://localhost:8000/jitsi.html',
|
||||
// data: {
|
||||
// confId: this.props.room.roomId.replace(/[^A-Za-z0-9]/g, '_') + Date.now(),
|
||||
// isAudioConf: isAudioConf,
|
||||
// },
|
||||
// };
|
||||
// MatrixClientPeg.get().sendStateEvent(
|
||||
// this.props.room.roomId,
|
||||
// 'im.vector.modular.widgets',
|
||||
// appsStateEvent,
|
||||
// '',
|
||||
// ).then(() => console.log('Sent state'), (e) => console.error(e));
|
||||
// }
|
||||
// }
|
||||
|
||||
onCallClick(ev) {
|
||||
// NOTE -- Will be replaced by Jitsi code (currently commented)
|
||||
dis.dispatch({
|
||||
action: 'place_call',
|
||||
type: ev.shiftKey ? "screensharing" : "video",
|
||||
room_id: this.props.room.roomId,
|
||||
});
|
||||
// this._startCallApp(false);
|
||||
}
|
||||
|
||||
onVoiceCallClick(ev) {
|
||||
// NOTE -- Will be replaced by Jitsi code (currently commented)
|
||||
dis.dispatch({
|
||||
action: 'place_call',
|
||||
type: "voice",
|
||||
room_id: this.props.room.roomId,
|
||||
});
|
||||
// this._startCallApp(true);
|
||||
}
|
||||
|
||||
onInputStateChanged(inputState) {
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
/*
|
||||
Copyright 2015, 2016 OpenMarket Ltd
|
||||
Copyright 2017 New Vector Ltd
|
||||
Copyright 2017, 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.
|
||||
|
@ -37,7 +37,7 @@ import Promise from 'bluebird';
|
|||
|
||||
import MatrixClientPeg from '../../../MatrixClientPeg';
|
||||
import type {MatrixClient} from 'matrix-js-sdk/lib/matrix';
|
||||
import SlashCommands from '../../../SlashCommands';
|
||||
import {processCommandInput} from '../../../SlashCommands';
|
||||
import { KeyCode, isOnlyCtrlOrCmdKeyEvent } from '../../../Keyboard';
|
||||
import Modal from '../../../Modal';
|
||||
import sdk from '../../../index';
|
||||
|
@ -54,8 +54,7 @@ import Markdown from '../../../Markdown';
|
|||
import ComposerHistoryManager from '../../../ComposerHistoryManager';
|
||||
import MessageComposerStore from '../../../stores/MessageComposerStore';
|
||||
|
||||
import {MATRIXTO_URL_PATTERN, MATRIXTO_MD_LINK_PATTERN} from '../../../linkify-matrix';
|
||||
const REGEX_MATRIXTO = new RegExp(MATRIXTO_URL_PATTERN);
|
||||
import {MATRIXTO_MD_LINK_PATTERN} from '../../../linkify-matrix';
|
||||
const REGEX_MATRIXTO_MARKDOWN_GLOBAL = new RegExp(MATRIXTO_MD_LINK_PATTERN, 'g');
|
||||
|
||||
import {asciiRegexp, unicodeRegexp, shortnameToUnicode, emojioneList, asciiList, mapUnicodeToShort, toShort} from 'emojione';
|
||||
|
@ -314,7 +313,7 @@ export default class MessageComposerInput extends React.Component {
|
|||
switch (payload.action) {
|
||||
case 'reply_to_event':
|
||||
case 'focus_composer':
|
||||
editor.focus();
|
||||
this.focusComposer();
|
||||
break;
|
||||
case 'insert_mention':
|
||||
{
|
||||
|
@ -1511,6 +1510,10 @@ export default class MessageComposerInput extends React.Component {
|
|||
}
|
||||
};
|
||||
|
||||
focusComposer = () => {
|
||||
this.refs.editor.focus();
|
||||
};
|
||||
|
||||
render() {
|
||||
const activeEditorState = this.state.originalEditorState || this.state.editorState;
|
||||
|
||||
|
@ -1519,9 +1522,9 @@ export default class MessageComposerInput extends React.Component {
|
|||
});
|
||||
|
||||
return (
|
||||
<div className="mx_MessageComposer_input_wrapper">
|
||||
<div className="mx_MessageComposer_input_wrapper" onClick={this.focusComposer}>
|
||||
<div className="mx_MessageComposer_autocomplete_wrapper">
|
||||
{ SettingsStore.isFeatureEnabled("feature_rich_quoting") && <ReplyPreview /> }
|
||||
<ReplyPreview />
|
||||
<Autocomplete
|
||||
ref={(e) => this.autocomplete = e}
|
||||
room={this.props.room}
|
||||
|
|
|
@ -22,6 +22,7 @@ import AccessibleButton from "../elements/AccessibleButton";
|
|||
import MessageEvent from "../messages/MessageEvent";
|
||||
import MemberAvatar from "../avatars/MemberAvatar";
|
||||
import { _t } from '../../../languageHandler';
|
||||
import {formatFullDate} from '../../../DateUtils';
|
||||
|
||||
module.exports = React.createClass({
|
||||
displayName: 'PinnedEventTile',
|
||||
|
@ -80,11 +81,20 @@ module.exports = React.createClass({
|
|||
{ unpinButton }
|
||||
</div>
|
||||
|
||||
<MemberAvatar member={sender} width={avatarSize} height={avatarSize} />
|
||||
<span className="mx_PinnedEventTile_senderAvatar">
|
||||
<MemberAvatar member={sender} width={avatarSize} height={avatarSize} />
|
||||
</span>
|
||||
<span className="mx_PinnedEventTile_sender">
|
||||
{ sender.name }
|
||||
</span>
|
||||
<MessageEvent mxEvent={this.props.mxEvent} className="mx_PinnedEventTile_body" />
|
||||
<span className="mx_PinnedEventTile_timestamp">
|
||||
{ formatFullDate(new Date(this.props.mxEvent.getTs())) }
|
||||
</span>
|
||||
<div className="mx_PinnedEventTile_message">
|
||||
<MessageEvent mxEvent={this.props.mxEvent} className="mx_PinnedEventTile_body" maxImageHeight={150}
|
||||
onWidgetLoad={() => {}} // we need to give this, apparently
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
},
|
||||
|
|
|
@ -39,6 +39,19 @@ module.exports = React.createClass({
|
|||
|
||||
componentDidMount: function() {
|
||||
this._updatePinnedMessages();
|
||||
MatrixClientPeg.get().on("RoomState.events", this._onStateEvent);
|
||||
},
|
||||
|
||||
componentWillUnmount: function() {
|
||||
if (MatrixClientPeg.get()) {
|
||||
MatrixClientPeg.get().removeListener("RoomState.events", this._onStateEvent);
|
||||
}
|
||||
},
|
||||
|
||||
_onStateEvent: function(ev) {
|
||||
if (ev.getRoomId() === this.props.room.roomId && ev.getType() === "m.room.pinned_events") {
|
||||
this._updatePinnedMessages();
|
||||
}
|
||||
},
|
||||
|
||||
_updatePinnedMessages: function() {
|
||||
|
|
|
@ -149,6 +149,13 @@ module.exports = React.createClass({
|
|||
dis.dispatch({ action: 'show_right_panel' });
|
||||
},
|
||||
|
||||
onShareRoomClick: function(ev) {
|
||||
const ShareDialog = sdk.getComponent("dialogs.ShareDialog");
|
||||
Modal.createTrackedDialog('share room dialog', '', ShareDialog, {
|
||||
target: this.props.room,
|
||||
});
|
||||
},
|
||||
|
||||
_hasUnreadPins: function() {
|
||||
const currentPinEvent = this.props.room.currentState.getStateEvents("m.room.pinned_events", '');
|
||||
if (!currentPinEvent) return false;
|
||||
|
@ -379,6 +386,14 @@ module.exports = React.createClass({
|
|||
</AccessibleButton>;
|
||||
}
|
||||
|
||||
let shareRoomButton;
|
||||
if (this.props.inRoom) {
|
||||
shareRoomButton =
|
||||
<AccessibleButton className="mx_RoomHeader_button" onClick={this.onShareRoomClick} title={_t('Share room')}>
|
||||
<TintableSvg src="img/icons-share.svg" width="16" height="16" />
|
||||
</AccessibleButton>;
|
||||
}
|
||||
|
||||
let rightPanelButtons;
|
||||
if (this.props.collapsedRhs) {
|
||||
rightPanelButtons =
|
||||
|
@ -400,6 +415,7 @@ module.exports = React.createClass({
|
|||
<div className="mx_RoomHeader_rightRow">
|
||||
{ settingsButton }
|
||||
{ pinnedEventsButton }
|
||||
{ shareRoomButton }
|
||||
{ manageIntegsButton }
|
||||
{ forgetButton }
|
||||
{ searchButton }
|
||||
|
|
|
@ -16,6 +16,8 @@ limitations under the License.
|
|||
*/
|
||||
|
||||
'use strict';
|
||||
import SettingsStore from "../../../settings/SettingsStore";
|
||||
|
||||
const React = require("react");
|
||||
const ReactDOM = require("react-dom");
|
||||
import PropTypes from 'prop-types';
|
||||
|
@ -583,14 +585,18 @@ module.exports = React.createClass({
|
|||
}
|
||||
},
|
||||
|
||||
_makeGroupInviteTiles() {
|
||||
_makeGroupInviteTiles(filter) {
|
||||
const ret = [];
|
||||
const lcFilter = filter && filter.toLowerCase();
|
||||
|
||||
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} />);
|
||||
const {groupId, name, myMembership} = group;
|
||||
// filter to only groups in invite state and group_id starts with filter or group name includes it
|
||||
if (myMembership !== 'invite') continue;
|
||||
if (lcFilter && !groupId.toLowerCase().startsWith(lcFilter) &&
|
||||
!(name && name.toLowerCase().includes(lcFilter))) continue;
|
||||
ret.push(<GroupInviteTile key={groupId} group={group} collapsed={this.props.collapsed} />);
|
||||
}
|
||||
|
||||
return ret;
|
||||
|
@ -604,13 +610,17 @@ module.exports = React.createClass({
|
|||
const RoomSubList = sdk.getComponent('structures.RoomSubList');
|
||||
const GeminiScrollbarWrapper = sdk.getComponent("elements.GeminiScrollbarWrapper");
|
||||
|
||||
// XXX: we can't detect device-level (localStorage) settings onChange as the SettingsStore does not notify
|
||||
// so checking on every render is the sanest thing at this time.
|
||||
const showEmpty = SettingsStore.getValue('RoomSubList.showEmpty');
|
||||
|
||||
const self = this;
|
||||
return (
|
||||
<GeminiScrollbarWrapper className="mx_RoomList_scrollbar"
|
||||
autoshow={true} onScroll={self._whenScrolling} wrappedRef={this._collectGemini}>
|
||||
autoshow={true} onScroll={self._whenScrolling} onResize={self._whenScrolling} wrappedRef={this._collectGemini}>
|
||||
<div className="mx_RoomList">
|
||||
<RoomSubList list={[]}
|
||||
extraTiles={this._makeGroupInviteTiles()}
|
||||
extraTiles={this._makeGroupInviteTiles(self.props.searchFilter)}
|
||||
label={_t('Community Invites')}
|
||||
editable={false}
|
||||
order="recent"
|
||||
|
@ -619,6 +629,7 @@ module.exports = React.createClass({
|
|||
searchFilter={self.props.searchFilter}
|
||||
onHeaderClick={self.onSubListHeaderClick}
|
||||
onShowMoreRooms={self.onShowMoreRooms}
|
||||
showEmpty={showEmpty}
|
||||
/>
|
||||
|
||||
<RoomSubList list={self.state.lists['im.vector.fake.invite']}
|
||||
|
@ -631,6 +642,7 @@ module.exports = React.createClass({
|
|||
searchFilter={self.props.searchFilter}
|
||||
onHeaderClick={self.onSubListHeaderClick}
|
||||
onShowMoreRooms={self.onShowMoreRooms}
|
||||
showEmpty={showEmpty}
|
||||
/>
|
||||
|
||||
<RoomSubList list={self.state.lists['m.favourite']}
|
||||
|
@ -643,7 +655,8 @@ module.exports = React.createClass({
|
|||
collapsed={self.props.collapsed}
|
||||
searchFilter={self.props.searchFilter}
|
||||
onHeaderClick={self.onSubListHeaderClick}
|
||||
onShowMoreRooms={self.onShowMoreRooms} />
|
||||
onShowMoreRooms={self.onShowMoreRooms}
|
||||
showEmpty={showEmpty} />
|
||||
|
||||
<RoomSubList list={self.state.lists['im.vector.fake.direct']}
|
||||
label={_t('People')}
|
||||
|
@ -657,7 +670,8 @@ module.exports = React.createClass({
|
|||
alwaysShowHeader={true}
|
||||
searchFilter={self.props.searchFilter}
|
||||
onHeaderClick={self.onSubListHeaderClick}
|
||||
onShowMoreRooms={self.onShowMoreRooms} />
|
||||
onShowMoreRooms={self.onShowMoreRooms}
|
||||
showEmpty={showEmpty} />
|
||||
|
||||
<RoomSubList list={self.state.lists['im.vector.fake.recent']}
|
||||
label={_t('Rooms')}
|
||||
|
@ -669,7 +683,8 @@ module.exports = React.createClass({
|
|||
collapsed={self.props.collapsed}
|
||||
searchFilter={self.props.searchFilter}
|
||||
onHeaderClick={self.onSubListHeaderClick}
|
||||
onShowMoreRooms={self.onShowMoreRooms} />
|
||||
onShowMoreRooms={self.onShowMoreRooms}
|
||||
showEmpty={showEmpty} />
|
||||
|
||||
{ Object.keys(self.state.lists).map((tagName) => {
|
||||
if (!tagName.match(STANDARD_TAGS_REGEX)) {
|
||||
|
@ -684,7 +699,8 @@ module.exports = React.createClass({
|
|||
collapsed={self.props.collapsed}
|
||||
searchFilter={self.props.searchFilter}
|
||||
onHeaderClick={self.onSubListHeaderClick}
|
||||
onShowMoreRooms={self.onShowMoreRooms} />;
|
||||
onShowMoreRooms={self.onShowMoreRooms}
|
||||
showEmpty={showEmpty} />;
|
||||
}
|
||||
}) }
|
||||
|
||||
|
@ -698,9 +714,17 @@ module.exports = React.createClass({
|
|||
collapsed={self.props.collapsed}
|
||||
searchFilter={self.props.searchFilter}
|
||||
onHeaderClick={self.onSubListHeaderClick}
|
||||
onShowMoreRooms={self.onShowMoreRooms} />
|
||||
onShowMoreRooms={self.onShowMoreRooms}
|
||||
showEmpty={showEmpty} />
|
||||
|
||||
<RoomSubList list={self.state.lists['im.vector.fake.archived']}
|
||||
emptyContent={self.props.collapsed ? null :
|
||||
<div className="mx_RoomList_emptySubListTip_container">
|
||||
<div className="mx_RoomList_emptySubListTip">
|
||||
{ _t('You have no historical rooms') }
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
label={_t('Historical')}
|
||||
editable={false}
|
||||
order="recent"
|
||||
|
@ -708,10 +732,11 @@ module.exports = React.createClass({
|
|||
alwaysShowHeader={true}
|
||||
startAsHidden={true}
|
||||
showSpinner={self.state.isLoadingLeftRooms}
|
||||
onHeaderClick= {self.onArchivedHeaderClick}
|
||||
onHeaderClick={self.onArchivedHeaderClick}
|
||||
incomingCall={self.state.incomingCall}
|
||||
searchFilter={self.props.searchFilter}
|
||||
onShowMoreRooms={self.onShowMoreRooms} />
|
||||
onShowMoreRooms={self.onShowMoreRooms}
|
||||
showEmpty={showEmpty} />
|
||||
</div>
|
||||
</GeminiScrollbarWrapper>
|
||||
);
|
||||
|
|
|
@ -395,7 +395,17 @@ module.exports = React.createClass({
|
|||
powerLevels["events"] = Object.assign({}, this.state.powerLevels["events"] || {});
|
||||
powerLevels["events"][powerLevelKey.slice(eventsLevelPrefix.length)] = value;
|
||||
} else {
|
||||
powerLevels[powerLevelKey] = value;
|
||||
const keyPath = powerLevelKey.split('.');
|
||||
let parentObj;
|
||||
let currentObj = powerLevels;
|
||||
for (const key of keyPath) {
|
||||
if (!currentObj[key]) {
|
||||
currentObj[key] = {};
|
||||
}
|
||||
parentObj = currentObj;
|
||||
currentObj = currentObj[key];
|
||||
}
|
||||
parentObj[keyPath[keyPath.length - 1]] = value;
|
||||
}
|
||||
this.setState({
|
||||
powerLevels,
|
||||
|
@ -664,6 +674,10 @@ module.exports = React.createClass({
|
|||
desc: _t('To remove other users\' messages, you must be a'),
|
||||
defaultValue: 50,
|
||||
},
|
||||
"notifications.room": {
|
||||
desc: _t('To notify everyone in the room, you must be a'),
|
||||
defaultValue: 50,
|
||||
},
|
||||
};
|
||||
|
||||
const banLevel = parseIntWithDefault(powerLevels.ban, powerLevelDescriptors.ban.defaultValue);
|
||||
|
@ -695,26 +709,57 @@ module.exports = React.createClass({
|
|||
relatedGroupsEvent={this.props.room.currentState.getStateEvents('m.room.related_groups', '')}
|
||||
/>;
|
||||
|
||||
let userLevelsSection;
|
||||
let privilegedUsersSection = <div>{ _t('No users have specific privileges in this room') }.</div>; // default
|
||||
let mutedUsersSection;
|
||||
if (Object.keys(userLevels).length) {
|
||||
userLevelsSection =
|
||||
<div>
|
||||
<h3>{ _t('Privileged Users') }</h3>
|
||||
<ul className="mx_RoomSettings_userLevels">
|
||||
{ Object.keys(userLevels).map(function(user, i) {
|
||||
return (
|
||||
<li className="mx_RoomSettings_userLevel" key={user}>
|
||||
{ _t("%(user)s is a %(userRole)s", {
|
||||
user: user,
|
||||
userRole: <PowerSelector value={userLevels[user]} disabled={true} />,
|
||||
}) }
|
||||
</li>
|
||||
);
|
||||
const privilegedUsers = [];
|
||||
const mutedUsers = [];
|
||||
|
||||
Object.keys(userLevels).forEach(function(user) {
|
||||
if (userLevels[user] > defaultUserLevel) { // privileged
|
||||
privilegedUsers.push(<li className="mx_RoomSettings_userLevel" key={user}>
|
||||
{ _t("%(user)s is a %(userRole)s", {
|
||||
user: user,
|
||||
userRole: <PowerSelector value={userLevels[user]} disabled={true} />,
|
||||
}) }
|
||||
</ul>
|
||||
</div>;
|
||||
} else {
|
||||
userLevelsSection = <div>{ _t('No users have specific privileges in this room') }.</div>;
|
||||
</li>);
|
||||
} else if (userLevels[user] < defaultUserLevel) { // muted
|
||||
mutedUsers.push(<li className="mx_RoomSettings_userLevel" key={user}>
|
||||
{ _t("%(user)s is a %(userRole)s", {
|
||||
user: user,
|
||||
userRole: <PowerSelector value={userLevels[user]} disabled={true} />,
|
||||
}) }
|
||||
</li>);
|
||||
}
|
||||
});
|
||||
|
||||
// comparator for sorting PL users lexicographically on PL descending, MXID ascending. (case-insensitive)
|
||||
const comparator = (a, b) => {
|
||||
const plDiff = userLevels[b.key] - userLevels[a.key];
|
||||
return plDiff !== 0 ? plDiff : a.key.toLocaleLowerCase().localeCompare(b.key.toLocaleLowerCase());
|
||||
};
|
||||
|
||||
privilegedUsers.sort(comparator);
|
||||
mutedUsers.sort(comparator);
|
||||
|
||||
if (privilegedUsers.length) {
|
||||
privilegedUsersSection =
|
||||
<div>
|
||||
<h3>{ _t('Privileged Users') }</h3>
|
||||
<ul className="mx_RoomSettings_userLevels">
|
||||
{ privilegedUsers }
|
||||
</ul>
|
||||
</div>;
|
||||
}
|
||||
if (mutedUsers.length) {
|
||||
mutedUsersSection =
|
||||
<div>
|
||||
<h3>{ _t('Muted Users') }</h3>
|
||||
<ul className="mx_RoomSettings_userLevels">
|
||||
{ mutedUsers }
|
||||
</ul>
|
||||
</div>;
|
||||
}
|
||||
}
|
||||
|
||||
const banned = this.props.room.getMembersWithMembership("ban");
|
||||
|
@ -834,7 +879,16 @@ module.exports = React.createClass({
|
|||
const powerSelectors = Object.keys(powerLevelDescriptors).map((key, index) => {
|
||||
const descriptor = powerLevelDescriptors[key];
|
||||
|
||||
const value = parseIntWithDefault(powerLevels[key], descriptor.defaultValue);
|
||||
const keyPath = key.split('.');
|
||||
let currentObj = powerLevels;
|
||||
for (const prop of keyPath) {
|
||||
if (currentObj === undefined) {
|
||||
break;
|
||||
}
|
||||
currentObj = currentObj[prop];
|
||||
}
|
||||
|
||||
const value = parseIntWithDefault(currentObj, descriptor.defaultValue);
|
||||
return <div key={index} className="mx_RoomSettings_powerLevel">
|
||||
<span className="mx_RoomSettings_powerLevelKey">
|
||||
{ descriptor.desc }
|
||||
|
@ -979,8 +1033,8 @@ module.exports = React.createClass({
|
|||
{ unfederatableSection }
|
||||
</div>
|
||||
|
||||
{ userLevelsSection }
|
||||
|
||||
{ privilegedUsersSection }
|
||||
{ mutedUsersSection }
|
||||
{ bannedUsersSection }
|
||||
|
||||
<h3>{ _t('Advanced') }</h3>
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
/*
|
||||
Copyright 2015, 2016 OpenMarket Ltd
|
||||
Copyright 2017 New Vector Ltd
|
||||
Copyright 2018 Michael Telatynski <7t3chguy@gmail.com>
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
|
@ -15,19 +16,17 @@ See the License for the specific language governing permissions and
|
|||
limitations under the License.
|
||||
*/
|
||||
|
||||
'use strict';
|
||||
|
||||
const React = require('react');
|
||||
const ReactDOM = require("react-dom");
|
||||
import React from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
const classNames = require('classnames');
|
||||
import classNames from 'classnames';
|
||||
import dis from '../../../dispatcher';
|
||||
const MatrixClientPeg = require('../../../MatrixClientPeg');
|
||||
import MatrixClientPeg from '../../../MatrixClientPeg';
|
||||
import DMRoomMap from '../../../utils/DMRoomMap';
|
||||
const sdk = require('../../../index');
|
||||
const ContextualMenu = require('../../structures/ContextualMenu');
|
||||
const RoomNotifs = require('../../../RoomNotifs');
|
||||
const FormattingUtils = require('../../../utils/FormattingUtils');
|
||||
import sdk from '../../../index';
|
||||
import {createMenu} from '../../structures/ContextualMenu';
|
||||
import * as RoomNotifs from '../../../RoomNotifs';
|
||||
import * as FormattingUtils from '../../../utils/FormattingUtils';
|
||||
import AccessibleButton from '../elements/AccessibleButton';
|
||||
import ActiveRoomObserver from '../../../ActiveRoomObserver';
|
||||
import RoomViewStore from '../../../stores/RoomViewStore';
|
||||
|
@ -72,16 +71,12 @@ module.exports = React.createClass({
|
|||
},
|
||||
|
||||
_shouldShowMentionBadge: function() {
|
||||
return this.state.notifState != RoomNotifs.MUTE;
|
||||
return this.state.notifState !== RoomNotifs.MUTE;
|
||||
},
|
||||
|
||||
_isDirectMessageRoom: function(roomId) {
|
||||
const dmRooms = DMRoomMap.shared().getUserIdForRoomId(roomId);
|
||||
if (dmRooms) {
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
return Boolean(dmRooms);
|
||||
},
|
||||
|
||||
onRoomTimeline: function(ev, room) {
|
||||
|
@ -99,7 +94,7 @@ module.exports = React.createClass({
|
|||
},
|
||||
|
||||
onAccountData: function(accountDataEvent) {
|
||||
if (accountDataEvent.getType() == 'm.push_rules') {
|
||||
if (accountDataEvent.getType() === 'm.push_rules') {
|
||||
this.setState({
|
||||
notifState: RoomNotifs.getRoomNotifsState(this.props.room.roomId),
|
||||
});
|
||||
|
@ -187,6 +182,32 @@ module.exports = React.createClass({
|
|||
this.badgeOnMouseLeave();
|
||||
},
|
||||
|
||||
_showContextMenu: function(x, y, chevronOffset) {
|
||||
const RoomTileContextMenu = sdk.getComponent('context_menus.RoomTileContextMenu');
|
||||
|
||||
createMenu(RoomTileContextMenu, {
|
||||
chevronOffset,
|
||||
left: x,
|
||||
top: y,
|
||||
room: this.props.room,
|
||||
onFinished: () => {
|
||||
this.setState({ menuDisplayed: false });
|
||||
this.props.refreshSubList();
|
||||
},
|
||||
});
|
||||
this.setState({ menuDisplayed: true });
|
||||
},
|
||||
|
||||
onContextMenu: function(e) {
|
||||
// Prevent the RoomTile onClick event firing as well
|
||||
e.preventDefault();
|
||||
// Only allow non-guests to access the context menu
|
||||
if (MatrixClientPeg.get().isGuest()) return;
|
||||
|
||||
const chevronOffset = 12;
|
||||
this._showContextMenu(e.clientX, e.clientY - (chevronOffset + 8), chevronOffset);
|
||||
},
|
||||
|
||||
badgeOnMouseEnter: function() {
|
||||
// Only allow non-guests to access the context menu
|
||||
// and only change it if it needs to change
|
||||
|
@ -200,37 +221,25 @@ module.exports = React.createClass({
|
|||
},
|
||||
|
||||
onBadgeClicked: function(e) {
|
||||
// Only allow none guests to access the context menu
|
||||
if (!MatrixClientPeg.get().isGuest()) {
|
||||
// If the badge is clicked, then no longer show tooltip
|
||||
if (this.props.collapsed) {
|
||||
this.setState({ hover: false });
|
||||
}
|
||||
|
||||
const RoomTileContextMenu = sdk.getComponent('context_menus.RoomTileContextMenu');
|
||||
const elementRect = e.target.getBoundingClientRect();
|
||||
|
||||
// The window X and Y offsets are to adjust position when zoomed in to page
|
||||
const x = elementRect.right + window.pageXOffset + 3;
|
||||
const chevronOffset = 12;
|
||||
let y = (elementRect.top + (elementRect.height / 2) + window.pageYOffset);
|
||||
y = y - (chevronOffset + 8); // where 8 is half the height of the chevron
|
||||
|
||||
const self = this;
|
||||
ContextualMenu.createMenu(RoomTileContextMenu, {
|
||||
chevronOffset: chevronOffset,
|
||||
left: x,
|
||||
top: y,
|
||||
room: this.props.room,
|
||||
onFinished: function() {
|
||||
self.setState({ menuDisplayed: false });
|
||||
self.props.refreshSubList();
|
||||
},
|
||||
});
|
||||
this.setState({ menuDisplayed: true });
|
||||
}
|
||||
// Prevent the RoomTile onClick event firing as well
|
||||
e.stopPropagation();
|
||||
// Only allow non-guests to access the context menu
|
||||
if (MatrixClientPeg.get().isGuest()) return;
|
||||
|
||||
// If the badge is clicked, then no longer show tooltip
|
||||
if (this.props.collapsed) {
|
||||
this.setState({ hover: false });
|
||||
}
|
||||
|
||||
const elementRect = e.target.getBoundingClientRect();
|
||||
|
||||
// The window X and Y offsets are to adjust position when zoomed in to page
|
||||
const x = elementRect.right + window.pageXOffset + 3;
|
||||
const chevronOffset = 12;
|
||||
let y = (elementRect.top + (elementRect.height / 2) + window.pageYOffset);
|
||||
y = y - (chevronOffset + 8); // where 8 is half the height of the chevron
|
||||
|
||||
this._showContextMenu(x, y, chevronOffset);
|
||||
},
|
||||
|
||||
render: function() {
|
||||
|
@ -250,7 +259,7 @@ module.exports = React.createClass({
|
|||
'mx_RoomTile_unread': this.props.unread,
|
||||
'mx_RoomTile_unreadNotify': notifBadges,
|
||||
'mx_RoomTile_highlight': mentionBadges,
|
||||
'mx_RoomTile_invited': (me && me.membership == 'invite'),
|
||||
'mx_RoomTile_invited': (me && me.membership === 'invite'),
|
||||
'mx_RoomTile_menuDisplayed': this.state.menuDisplayed,
|
||||
'mx_RoomTile_noBadges': !badges,
|
||||
'mx_RoomTile_transparent': this.props.transparent,
|
||||
|
@ -268,7 +277,6 @@ module.exports = React.createClass({
|
|||
let name = this.state.roomName;
|
||||
name = name.replace(":", ":\u200b"); // add a zero-width space to allow linewrapping after the colon
|
||||
|
||||
let badge;
|
||||
let badgeContent;
|
||||
|
||||
if (this.state.badgeHover || this.state.menuDisplayed) {
|
||||
|
@ -280,7 +288,7 @@ module.exports = React.createClass({
|
|||
badgeContent = '\u200B';
|
||||
}
|
||||
|
||||
badge = <div className={badgeClasses} onClick={this.onBadgeClicked}>{ badgeContent }</div>;
|
||||
const badge = <div className={badgeClasses} onClick={this.onBadgeClicked}>{ badgeContent }</div>;
|
||||
|
||||
const EmojiText = sdk.getComponent('elements.EmojiText');
|
||||
let label;
|
||||
|
@ -301,7 +309,7 @@ module.exports = React.createClass({
|
|||
}
|
||||
} else if (this.state.hover) {
|
||||
const RoomTooltip = sdk.getComponent("rooms.RoomTooltip");
|
||||
tooltip = <RoomTooltip className="mx_RoomTile_tooltip" room={this.props.room} dir="auto" />;
|
||||
tooltip = <RoomTooltip className="mx_RoomTile_tooltip" label={this.props.room.name} dir="auto" />;
|
||||
}
|
||||
|
||||
//var incomingCallBox;
|
||||
|
@ -312,16 +320,22 @@ module.exports = React.createClass({
|
|||
|
||||
const RoomAvatar = sdk.getComponent('avatars.RoomAvatar');
|
||||
|
||||
let directMessageIndicator;
|
||||
let dmIndicator;
|
||||
if (this._isDirectMessageRoom(this.props.room.roomId)) {
|
||||
directMessageIndicator = <img src="img/icon_person.svg" className="mx_RoomTile_dm" width="11" height="13" alt="dm" />;
|
||||
dmIndicator = <img src="img/icon_person.svg" className="mx_RoomTile_dm" width="11" height="13" alt="dm" />;
|
||||
}
|
||||
|
||||
return <AccessibleButton className={classes} tabIndex="0" onClick={this.onClick} onMouseEnter={this.onMouseEnter} onMouseLeave={this.onMouseLeave}>
|
||||
return <AccessibleButton tabIndex="0"
|
||||
className={classes}
|
||||
onClick={this.onClick}
|
||||
onMouseEnter={this.onMouseEnter}
|
||||
onMouseLeave={this.onMouseLeave}
|
||||
onContextMenu={this.onContextMenu}
|
||||
>
|
||||
<div className={avatarClasses}>
|
||||
<div className="mx_RoomTile_avatar_container">
|
||||
<RoomAvatar room={this.props.room} width={24} height={24} />
|
||||
{ directMessageIndicator }
|
||||
{ dmIndicator }
|
||||
</div>
|
||||
</div>
|
||||
<div className="mx_RoomTile_nameContainer">
|
||||
|
|
|
@ -14,11 +14,10 @@ See the License for the specific language governing permissions and
|
|||
limitations under the License.
|
||||
*/
|
||||
|
||||
'use strict';
|
||||
|
||||
var React = require('react');
|
||||
var ReactDOM = require('react-dom');
|
||||
var dis = require('../../../dispatcher');
|
||||
import React from 'react';
|
||||
import ReactDOM from 'react-dom';
|
||||
import dis from '../../../dispatcher';
|
||||
import classNames from 'classnames';
|
||||
|
||||
const MIN_TOOLTIP_HEIGHT = 25;
|
||||
|
@ -77,25 +76,21 @@ module.exports = React.createClass({
|
|||
},
|
||||
|
||||
_renderTooltip: function() {
|
||||
var label = this.props.room ? this.props.room.name : this.props.label;
|
||||
|
||||
// Add the parent's position to the tooltips, so it's correctly
|
||||
// positioned, also taking into account any window zoom
|
||||
// NOTE: The additional 6 pixels for the left position, is to take account of the
|
||||
// tooltips chevron
|
||||
var parent = ReactDOM.findDOMNode(this).parentNode;
|
||||
var style = {};
|
||||
const parent = ReactDOM.findDOMNode(this).parentNode;
|
||||
let style = {};
|
||||
style = this._updatePosition(style);
|
||||
style.display = "block";
|
||||
|
||||
const tooltipClasses = classNames(
|
||||
"mx_RoomTooltip", this.props.tooltipClassName,
|
||||
);
|
||||
const tooltipClasses = classNames("mx_RoomTooltip", this.props.tooltipClassName);
|
||||
|
||||
var tooltip = (
|
||||
<div className={tooltipClasses} style={style} >
|
||||
<div className="mx_RoomTooltip_chevron"></div>
|
||||
{ label }
|
||||
const tooltip = (
|
||||
<div className={tooltipClasses} style={style}>
|
||||
<div className="mx_RoomTooltip_chevron" />
|
||||
{ this.props.label }
|
||||
</div>
|
||||
);
|
||||
|
||||
|
|
|
@ -15,7 +15,6 @@ limitations under the License.
|
|||
*/
|
||||
import React from 'react';
|
||||
import { _t } from '../../../languageHandler';
|
||||
import Widgets from '../../../utils/widgets';
|
||||
import AppTile from '../elements/AppTile';
|
||||
import MatrixClientPeg from '../../../MatrixClientPeg';
|
||||
import Modal from '../../../Modal';
|
||||
|
@ -24,9 +23,14 @@ import SdkConfig from '../../../SdkConfig';
|
|||
import ScalarAuthClient from '../../../ScalarAuthClient';
|
||||
import dis from '../../../dispatcher';
|
||||
import AccessibleButton from '../elements/AccessibleButton';
|
||||
import WidgetUtils from '../../../utils/WidgetUtils';
|
||||
|
||||
const widgetType = 'm.stickerpicker';
|
||||
|
||||
// We sit in a context menu, so the persisted element container needs to float
|
||||
// above it, so it needs a greater z-index than the ContextMenu
|
||||
const STICKERPICKER_Z_INDEX = 5000;
|
||||
|
||||
export default class Stickerpicker extends React.Component {
|
||||
constructor(props) {
|
||||
super(props);
|
||||
|
@ -67,7 +71,7 @@ export default class Stickerpicker extends React.Component {
|
|||
}
|
||||
|
||||
this.setState({showStickers: false});
|
||||
Widgets.removeStickerpickerWidgets().then(() => {
|
||||
WidgetUtils.removeStickerpickerWidgets().then(() => {
|
||||
this.forceUpdate();
|
||||
}).catch((e) => {
|
||||
console.error('Failed to remove sticker picker widget', e);
|
||||
|
@ -119,7 +123,7 @@ export default class Stickerpicker extends React.Component {
|
|||
}
|
||||
|
||||
_updateWidget() {
|
||||
const stickerpickerWidget = Widgets.getStickerpickerWidgets()[0];
|
||||
const stickerpickerWidget = WidgetUtils.getStickerpickerWidgets()[0];
|
||||
this.setState({
|
||||
stickerpickerWidget,
|
||||
widgetId: stickerpickerWidget ? stickerpickerWidget.id : null,
|
||||
|
@ -211,7 +215,7 @@ export default class Stickerpicker extends React.Component {
|
|||
width: this.popoverWidth,
|
||||
}}
|
||||
>
|
||||
<PersistedElement>
|
||||
<PersistedElement containerId="mx_persisted_stickerPicker" style={{zIndex: STICKERPICKER_Z_INDEX}}>
|
||||
<AppTile
|
||||
collectWidgetMessaging={this._collectWidgetMessaging}
|
||||
id={stickerpickerWidget.id}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue