From c820836bcc1c95d0fb292f47ecb377e602f5b9ee Mon Sep 17 00:00:00 2001
From: Michael Telatynski <7t3chguy@gmail.com>
Date: Tue, 12 Jun 2018 15:22:45 +0100
Subject: [PATCH] make RoomTooltip generic and add ContextMenu&Tooltip to
GroupInviteTile
Signed-off-by: Michael Telatynski <7t3chguy@gmail.com>
---
.../GroupInviteTileContextMenu.js | 87 ++++++++++++++++
.../views/groups/GroupInviteTile.js | 98 +++++++++++++++++--
src/components/views/rooms/RoomList.js | 3 +-
src/components/views/rooms/RoomTile.js | 4 +-
src/components/views/rooms/RoomTooltip.js | 25 ++---
5 files changed, 189 insertions(+), 28 deletions(-)
create mode 100644 src/components/views/context_menus/GroupInviteTileContextMenu.js
diff --git a/src/components/views/context_menus/GroupInviteTileContextMenu.js b/src/components/views/context_menus/GroupInviteTileContextMenu.js
new file mode 100644
index 0000000000..844845ea82
--- /dev/null
+++ b/src/components/views/context_menus/GroupInviteTileContextMenu.js
@@ -0,0 +1,87 @@
+/*
+Copyright 2018 Vector Creations Ltd
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+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.
+*/
+
+import React from 'react';
+import classNames from 'classnames';
+import PropTypes from 'prop-types';
+import sdk from '../../../index';
+import { _t, _td } from '../../../languageHandler';
+import Modal from '../../../Modal';
+import {Group} from 'matrix-js-sdk';
+import GroupStore from "../../../stores/GroupStore";
+
+export default class GroupInviteTileContextMenu extends React.Component {
+ static propTypes = {
+ group: PropTypes.instanceOf(Group).isRequired,
+ /* callback called when the menu is dismissed */
+ onFinished: PropTypes.func,
+ };
+
+ constructor(props, context) {
+ super(props, context);
+
+ this._onClickReject = this._onClickReject.bind(this);
+ }
+
+ componentWillMount() {
+ this._unmounted = false;
+ }
+
+ componentWillUnmount() {
+ this._unmounted = true;
+ }
+
+ _onClickReject() {
+ const QuestionDialog = sdk.getComponent('dialogs.QuestionDialog');
+ Modal.createTrackedDialog('Reject community invite', '', QuestionDialog, {
+ title: _t('Reject invitation'),
+ description: _t('Are you sure you want to reject the invitation?'),
+ onFinished: async (shouldLeave) => {
+ if (!shouldLeave) return;
+
+ // FIXME: controller shouldn't be loading a view :(
+ const Loader = sdk.getComponent("elements.Spinner");
+ const modal = Modal.createDialog(Loader, null, 'mx_Dialog_spinner');
+
+ try {
+ await GroupStore.leaveGroup(this.props.group.groupId);
+ } catch (e) {
+ console.error(e);
+ const ErrorDialog = sdk.getComponent("dialogs.ErrorDialog");
+ Modal.createTrackedDialog('Error rejecting invite', '', ErrorDialog, {
+ title: _t("Error"),
+ description: _t("Unable to reject invite"),
+ });
+ }
+ modal.close();
+ },
+ });
+
+ // Close the context menu
+ if (this.props.onFinished) {
+ this.props.onFinished();
+ }
+ }
+
+ render() {
+ return
+
+

+ { _t('Reject') }
+
+
;
+ }
+}
diff --git a/src/components/views/groups/GroupInviteTile.js b/src/components/views/groups/GroupInviteTile.js
index d97464e8ca..65e8a07d5a 100644
--- a/src/components/views/groups/GroupInviteTile.js
+++ b/src/components/views/groups/GroupInviteTile.js
@@ -1,5 +1,5 @@
/*
-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.
@@ -20,6 +20,8 @@ import { MatrixClient } from 'matrix-js-sdk';
import sdk from '../../../index';
import dis from '../../../dispatcher';
import AccessibleButton from '../elements/AccessibleButton';
+import * as ContextualMenu from "../../structures/ContextualMenu";
+import classNames from 'classnames';
export default React.createClass({
displayName: 'GroupInviteTile',
@@ -32,6 +34,15 @@ export default React.createClass({
matrixClient: PropTypes.instanceOf(MatrixClient),
},
+ getInitialState: function() {
+ return ({
+ hover: false,
+ badgeHover: false,
+ menuDisplayed: false,
+ selected: this.props.group.groupId === null, // XXX: this needs linking to LoggedInView/GroupView state
+ });
+ },
+
onClick: function(e) {
dis.dispatch({
action: 'view_group',
@@ -39,6 +50,56 @@ export default React.createClass({
});
},
+ onMouseEnter: function() {
+ const state = {hover: true};
+ // Only allow non-guests to access the context menu
+ if (!this.context.matrixClient.isGuest()) {
+ state.badgeHover = true;
+ }
+ this.setState(state);
+ },
+
+ onMouseLeave: function() {
+ this.setState({
+ badgeHover: false,
+ hover: false,
+ });
+ },
+
+ onBadgeClicked: function(e) {
+ // Prevent the RoomTile onClick event firing as well
+ e.stopPropagation();
+
+ // Only allow none guests to access the context menu
+ if (this.context.matrixClient.isGuest()) return;
+
+ // If the badge is clicked, then no longer show tooltip
+ if (this.props.collapsed) {
+ this.setState({ hover: false });
+ }
+
+ const RoomTileContextMenu = sdk.getComponent('context_menus.GroupInviteTileContextMenu');
+ 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
+
+ ContextualMenu.createMenu(RoomTileContextMenu, {
+ chevronOffset: chevronOffset,
+ left: x,
+ top: y,
+ group: this.props.group,
+ onFinished: () => {
+ this.setState({ menuDisplayed: false });
+ // this.props.refreshSubList();
+ },
+ });
+ this.setState({ menuDisplayed: true });
+ },
+
render: function() {
const BaseAvatar = sdk.getComponent('avatars.BaseAvatar');
const EmojiText = sdk.getComponent('elements.EmojiText');
@@ -49,19 +110,37 @@ export default React.createClass({
const av = ;
- const label =
+ const nameClasses = classNames({
+ 'mx_RoomTile_name': true,
+ 'mx_RoomTile_invite': this.props.isInvite,
+ 'mx_RoomTile_badgeShown': this.state.badgeHover || this.state.menuDisplayed,
+ });
+
+ const label =
{ groupName }
;
- const badge = !
;
+ const badgeEllipsis = this.state.badgeHover || this.state.menuDisplayed;
+ const badgeClasses = classNames('mx_RoomSubList_badge mx_RoomSubList_badgeHighlight', {
+ 'mx_RoomTile_badgeButton': badgeEllipsis,
+ });
+
+ const badgeContent = badgeEllipsis ? '\u00B7\u00B7\u00B7' : '!';
+ const badge = { badgeContent }
;
+
+ let tooltip;
+ if (this.props.collapsed && this.state.hover) {
+ const RoomTooltip = sdk.getComponent("rooms.RoomTooltip");
+ tooltip = ;
+ }
+
+ const classes = classNames('mx_RoomTile mx_RoomTile_highlight', {
+ 'mx_RoomTile_menuDisplayed': this.state.menuDisplayed,
+ 'mx_RoomTile_selected': this.state.selected,
+ });
return (
-
+
{ av }
@@ -69,6 +148,7 @@ export default React.createClass({
{ label }
{ badge }
+ { tooltip }
);
},
diff --git a/src/components/views/rooms/RoomList.js b/src/components/views/rooms/RoomList.js
index fc1872249f..2722bad88b 100644
--- a/src/components/views/rooms/RoomList.js
+++ b/src/components/views/rooms/RoomList.js
@@ -589,8 +589,7 @@ module.exports = React.createClass({
const GroupInviteTile = sdk.getComponent('groups.GroupInviteTile');
for (const group of MatrixClientPeg.get().getGroups()) {
if (group.myMembership !== 'invite') continue;
-
- ret.push();
+ ret.push();
}
return ret;
diff --git a/src/components/views/rooms/RoomTile.js b/src/components/views/rooms/RoomTile.js
index 05aaf79e0b..11eb2090f2 100644
--- a/src/components/views/rooms/RoomTile.js
+++ b/src/components/views/rooms/RoomTile.js
@@ -301,7 +301,7 @@ module.exports = React.createClass({
}
} else if (this.state.hover) {
const RoomTooltip = sdk.getComponent("rooms.RoomTooltip");
- tooltip = ;
+ tooltip = ;
}
//var incomingCallBox;
@@ -314,7 +314,7 @@ module.exports = React.createClass({
let directMessageIndicator;
if (this._isDirectMessageRoom(this.props.room.roomId)) {
- directMessageIndicator =
;
+ directMessageIndicator =
;
}
return
diff --git a/src/components/views/rooms/RoomTooltip.js b/src/components/views/rooms/RoomTooltip.js
index b17f54ef3c..bce0922637 100644
--- a/src/components/views/rooms/RoomTooltip.js
+++ b/src/components/views/rooms/RoomTooltip.js
@@ -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 = (
-
-
- { label }
+ const tooltip = (
+
);