diff --git a/src/actions/TagOrderActions.js b/src/actions/TagOrderActions.js index 3504adb09b..38bada2228 100644 --- a/src/actions/TagOrderActions.js +++ b/src/actions/TagOrderActions.js @@ -35,6 +35,7 @@ const TagOrderActions = {}; TagOrderActions.moveTag = function(matrixClient, tag, destinationIx) { // Only commit tags if the state is ready, i.e. not null let tags = TagOrderStore.getOrderedTags(); + let removedTags = TagOrderStore.getRemovedTagsAccountData(); if (!tags) { return; } @@ -42,17 +43,19 @@ TagOrderActions.moveTag = function(matrixClient, tag, destinationIx) { tags = tags.filter((t) => t !== tag); tags = [...tags.slice(0, destinationIx), tag, ...tags.slice(destinationIx)]; + removedTags = removedTags.filter((t) => t !== tag); + const storeId = TagOrderStore.getStoreId(); return asyncAction('TagOrderActions.moveTag', () => { Analytics.trackEvent('TagOrderActions', 'commitTagOrdering'); return matrixClient.setAccountData( 'im.vector.web.tag_ordering', - {tags, _storeId: storeId}, + {tags, removedTags, _storeId: storeId}, ); }, () => { // For an optimistic update - return {tags}; + return {tags, removedTags}; }); }; diff --git a/src/components/structures/LoggedInView.js b/src/components/structures/LoggedInView.js index d7fe699156..f6bbfd247b 100644 --- a/src/components/structures/LoggedInView.js +++ b/src/components/structures/LoggedInView.js @@ -19,6 +19,7 @@ limitations under the License. import * as Matrix from 'matrix-js-sdk'; import React from 'react'; import PropTypes from 'prop-types'; +import { DragDropContext } from 'react-beautiful-dnd'; import { KeyCode, isOnlyCtrlOrCmdKeyEvent } from '../../Keyboard'; import Notifier from '../../Notifier'; @@ -30,6 +31,9 @@ import sessionStore from '../../stores/SessionStore'; import MatrixClientPeg from '../../MatrixClientPeg'; import SettingsStore from "../../settings/SettingsStore"; +import TagOrderActions from '../../actions/TagOrderActions'; +import RoomListActions from '../../actions/RoomListActions'; + /** * This is what our MatrixChat shows when we are logged in. The precise view is * determined by the page_type property. @@ -207,6 +211,50 @@ const LoggedInView = React.createClass({ } }, + _onDragEnd: function(result) { + // Dragged to an invalid destination, not onto a droppable + if (!result.destination) { + return; + } + + const dest = result.destination.droppableId; + + if (dest === 'tag-panel-droppable') { + // Could be "GroupTile +groupId:domain" + const draggableId = result.draggableId.split(' ').pop(); + + // Dispatch synchronously so that the TagPanel receives an + // optimistic update from TagOrderStore before the previous + // state is shown. + dis.dispatch(TagOrderActions.moveTag( + this._matrixClient, + draggableId, + result.destination.index, + ), true); + } else if (dest.startsWith('room-sub-list-droppable_')) { + this._onRoomTileEndDrag(result); + } + }, + + _onRoomTileEndDrag: function(result) { + 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 oldIndex = result.source.index; + const newIndex = result.destination.index; + + dis.dispatch(RoomListActions.tagRoom( + this._matrixClient, + this._matrixClient.getRoom(roomId), + prevTag, newTag, + oldIndex, newIndex, + ), true); + }, + render: function() { const LeftPanel = sdk.getComponent('structures.LeftPanel'); const RightPanel = sdk.getComponent('structures.RightPanel'); @@ -328,16 +376,18 @@ const LoggedInView = React.createClass({ return (
{ topBar } -
- -
- { page_element } -
- { right_panel } -
+ +
+ +
+ { page_element } +
+ { right_panel } +
+
); }, diff --git a/src/components/structures/MyGroups.js b/src/components/structures/MyGroups.js index 22157beaca..116607fb08 100644 --- a/src/components/structures/MyGroups.js +++ b/src/components/structures/MyGroups.js @@ -73,8 +73,10 @@ export default withMatrixClient(React.createClass({ }); contentHeader = groupNodes.length > 0 ?

{ _t('Your Communities') }

:
; content = groupNodes.length > 0 ? - - { groupNodes } + +
+ { groupNodes } +
:
{ _t( diff --git a/src/components/views/groups/GroupTile.js b/src/components/views/groups/GroupTile.js index ce426a9b78..f1dbb75988 100644 --- a/src/components/views/groups/GroupTile.js +++ b/src/components/views/groups/GroupTile.js @@ -17,10 +17,12 @@ limitations under the License. import React from 'react'; import PropTypes from 'prop-types'; import {MatrixClient} from 'matrix-js-sdk'; +import { Draggable, Droppable } from 'react-beautiful-dnd'; import sdk from '../../../index'; import dis from '../../../dispatcher'; import FlairStore from '../../../stores/FlairStore'; + const GroupTile = React.createClass({ displayName: 'GroupTile', @@ -78,9 +80,39 @@ const GroupTile = React.createClass({ profile.avatarUrl, avatarHeight, avatarHeight, "crop", ) : null; return -
- -
+ + { (droppableProvided, droppableSnapshot) => ( +
+ + { (provided, snapshot) => ( +
+
+
+ +
+
+ { /* Instead of a blank placeholder, use a copy of the avatar itself. */ } + { provided.placeholder ? +
+ +
: +
+ } +
+ ) } + +
+ ) } +
{ name }
{ descElement } diff --git a/src/stores/TagOrderStore.js b/src/stores/TagOrderStore.js index 78e4a6e95d..eef078d8da 100644 --- a/src/stores/TagOrderStore.js +++ b/src/stores/TagOrderStore.js @@ -89,6 +89,7 @@ class TagOrderStore extends Store { // Optimistic update of a moved tag this._setState({ orderedTags: payload.request.tags, + removedTagsAccountData: payload.request.removedTags, }); break; }