diff --git a/package.json b/package.json
index dbc27b5a08..f81e72e556 100644
--- a/package.json
+++ b/package.json
@@ -78,8 +78,6 @@
"react": "^15.4.0",
"react-addons-css-transition-group": "15.3.2",
"react-beautiful-dnd": "^4.0.0",
- "react-dnd": "^2.1.4",
- "react-dnd-html5-backend": "^2.1.2",
"react-dom": "^15.4.0",
"react-gemini-scrollbar": "matrix-org/react-gemini-scrollbar#5e97aef",
"sanitize-html": "^1.14.1",
diff --git a/src/components/structures/LoggedInView.js b/src/components/structures/LoggedInView.js
index bebc109806..e97d9dd0a1 100644
--- a/src/components/structures/LoggedInView.js
+++ b/src/components/structures/LoggedInView.js
@@ -19,8 +19,6 @@ limitations under the License.
import * as Matrix from 'matrix-js-sdk';
import React from 'react';
import PropTypes from 'prop-types';
-import { DragDropContext } from 'react-dnd';
-import HTML5Backend from 'react-dnd-html5-backend';
import { KeyCode, isOnlyCtrlOrCmdKeyEvent } from '../../Keyboard';
import Notifier from '../../Notifier';
@@ -347,4 +345,4 @@ const LoggedInView = React.createClass({
},
});
-export default DragDropContext(HTML5Backend)(LoggedInView);
+export default LoggedInView;
diff --git a/src/components/structures/MatrixChat.js b/src/components/structures/MatrixChat.js
index 733007677b..d6d0b00c84 100644
--- a/src/components/structures/MatrixChat.js
+++ b/src/components/structures/MatrixChat.js
@@ -847,16 +847,36 @@ export default React.createClass({
}).close;
},
+ _leaveRoomWarnings: function(roomId) {
+ const roomToLeave = MatrixClientPeg.get().getRoom(roomId);
+ // Show a warning if there are additional complications.
+ const joinRules = roomToLeave.currentState.getStateEvents('m.room.join_rules', '');
+ const warnings = [];
+ if (joinRules) {
+ const rule = joinRules.getContent().join_rule;
+ if (rule !== "public") {
+ warnings.push((
+
+ { _t("This room is not public. You will not be able to rejoin without an invite.") }
+
+ ));
+ }
+ }
+ return warnings;
+ },
+
_leaveRoom: function(roomId) {
const QuestionDialog = sdk.getComponent("dialogs.QuestionDialog");
const ErrorDialog = sdk.getComponent("dialogs.ErrorDialog");
-
const roomToLeave = MatrixClientPeg.get().getRoom(roomId);
+ const warnings = this._leaveRoomWarnings(roomId);
+
Modal.createTrackedDialog('Leave room', '', QuestionDialog, {
title: _t("Leave room"),
description: (
{ _t("Are you sure you want to leave the room '%(roomName)s'?", {roomName: roomToLeave.name}) }
+ { warnings }
),
onFinished: (shouldLeave) => {
@@ -1066,10 +1086,10 @@ export default React.createClass({
// this if we are not scrolled up in the view. To find out, delegate to
// the timeline panel. If the timeline panel doesn't exist, then we assume
// it is safe to reset the timeline.
- if (!self._loggedInView) {
+ if (!self._loggedInView || !self._loggedInView.child) {
return true;
}
- return self._loggedInView.getDecoratedComponentInstance().canResetTimelineInRoom(roomId);
+ return self._loggedInView.child.canResetTimelineInRoom(roomId);
});
cli.on('sync', function(state, prevState) {
diff --git a/src/components/views/elements/Flair.js b/src/components/views/elements/Flair.js
index a487395a87..76566e8c4d 100644
--- a/src/components/views/elements/Flair.js
+++ b/src/components/views/elements/Flair.js
@@ -107,7 +107,11 @@ export default class Flair extends React.Component {
}
const profiles = await this._getGroupProfiles(groups);
if (!this.unmounted) {
- this.setState({profiles: profiles.filter((profile) => {return profile.avatarUrl;})});
+ this.setState({
+ profiles: profiles.filter((profile) => {
+ return profile ? profile.avatarUrl : false;
+ }),
+ });
}
}
diff --git a/src/components/views/rooms/RoomList.js b/src/components/views/rooms/RoomList.js
index d1ef6c2f2c..ca1fccd1f5 100644
--- a/src/components/views/rooms/RoomList.js
+++ b/src/components/views/rooms/RoomList.js
@@ -18,6 +18,7 @@ limitations under the License.
'use strict';
const React = require("react");
const ReactDOM = require("react-dom");
+import { DragDropContext } from 'react-beautiful-dnd';
import PropTypes from 'prop-types';
import { _t } from '../../../languageHandler';
const GeminiScrollbar = require('react-gemini-scrollbar');
@@ -32,6 +33,8 @@ const Receipt = require('../../../utils/Receipt');
import TagOrderStore from '../../../stores/TagOrderStore';
import GroupStoreCache from '../../../stores/GroupStoreCache';
+import Modal from '../../../Modal';
+
const HIDE_CONFERENCE_CHANS = true;
function phraseForSection(section) {
@@ -275,6 +278,103 @@ module.exports = React.createClass({
this.forceUpdate();
},
+ onRoomTileEndDrag: function(result) {
+ if (!result.destination) return;
+
+ let newTag = result.destination.droppableId.split('_')[1];
+ let prevTag = result.source.droppableId.split('_')[1];
+ if (newTag === 'undefined') newTag = undefined;
+ if (prevTag === 'undefined') prevTag = undefined;
+
+ const roomId = result.draggableId.split('_')[1];
+ const room = MatrixClientPeg.get().getRoom(roomId);
+
+ const newIndex = result.destination.index;
+
+ // Evil hack to get DMs behaving
+ if ((prevTag === undefined && newTag === 'im.vector.fake.direct') ||
+ (prevTag === 'im.vector.fake.direct' && newTag === undefined)
+ ) {
+ Rooms.guessAndSetDMRoom(
+ room, newTag === 'im.vector.fake.direct',
+ ).catch((err) => {
+ const ErrorDialog = sdk.getComponent("dialogs.ErrorDialog");
+ console.error("Failed to set direct chat tag " + err);
+ Modal.createTrackedDialog('Failed to set direct chat tag', '', ErrorDialog, {
+ title: _t('Failed to set direct chat tag'),
+ description: ((err && err.message) ? err.message : _t('Operation failed')),
+ });
+ });
+ return;
+ }
+
+ const hasChangedSubLists = result.source.droppableId !== result.destination.droppableId;
+
+ let newOrder = null;
+
+ // Is the tag ordered manually?
+ if (newTag && !newTag.match(/^(m\.lowpriority|im\.vector\.fake\.(invite|recent|direct|archived))$/)) {
+ const newList = Object.assign({}, this.state.lists[newTag]);
+
+ // If the room was moved "down" (increasing index) in the same list we
+ // need to use the orders of the tiles with indices shifted by +1
+ const offset = (
+ newTag === prevTag && result.source.index < result.destination.index
+ ) ? 1 : 0;
+
+ const prevOrder = newIndex === 0 ?
+ 0 : newList[offset + newIndex - 1].tags[newTag].order;
+ const nextOrder = newIndex === newList.length ?
+ 1 : newList[offset + newIndex].tags[newTag].order;
+
+ newOrder = {
+ order: (prevOrder + nextOrder) / 2.0,
+ };
+ }
+
+ // More evilness: We will still be dealing with moving to favourites/low prio,
+ // but we avoid ever doing a request with 'im.vector.fake.direct`.
+ //
+ // if we moved lists, remove the old tag
+ if (prevTag && prevTag !== 'im.vector.fake.direct' &&
+ hasChangedSubLists
+ ) {
+ // Optimistic update of what will happen to the room tags
+ delete room.tags[prevTag];
+
+ MatrixClientPeg.get().deleteRoomTag(roomId, prevTag).catch(function(err) {
+ const ErrorDialog = sdk.getComponent("dialogs.ErrorDialog");
+ console.error("Failed to remove tag " + prevTag + " from room: " + err);
+ Modal.createTrackedDialog('Failed to remove tag from room', '', ErrorDialog, {
+ title: _t('Failed to remove tag %(tagName)s from room', {tagName: prevTag}),
+ description: ((err && err.message) ? err.message : _t('Operation failed')),
+ });
+ });
+ }
+
+ // if we moved lists or the ordering changed, add the new tag
+ if (newTag && newTag !== 'im.vector.fake.direct' &&
+ (hasChangedSubLists || newOrder)
+ ) {
+ // Optimistic update of what will happen to the room tags
+ room.tags[newTag] = newOrder;
+
+ MatrixClientPeg.get().setRoomTag(roomId, newTag, newOrder).catch(function(err) {
+ const ErrorDialog = sdk.getComponent("dialogs.ErrorDialog");
+ console.error("Failed to add tag " + newTag + " to room: " + err);
+ Modal.createTrackedDialog('Failed to add tag to room', '', ErrorDialog, {
+ title: _t('Failed to add tag %(tagName)s to room', {tagName: newTag}),
+ description: ((err && err.message) ? err.message : _t('Operation failed')),
+ });
+ });
+ }
+
+ // Refresh to display the optimistic updates - this needs to be done in the
+ // same tick as the drag finishing otherwise the room will pop back to its
+ // previous position - hence no delayed refresh
+ this.refreshRoomList();
+ },
+
_delayedRefreshRoomList: new rate_limited_func(function() {
this.refreshRoomList();
}, 500),
@@ -649,114 +749,116 @@ module.exports = React.createClass({
const self = this;
return (
-