Remove DragDropContext from TagPanel and RoomList

So that we can have one context that can handle DND between
the TagPanel and RoomList.
This commit is contained in:
lukebarnard 2018-01-25 21:58:35 +01:00
parent 815f52587b
commit d0e3319bd9
2 changed files with 118 additions and 239 deletions

View file

@ -20,12 +20,11 @@ import { MatrixClient } from 'matrix-js-sdk';
import TagOrderStore from '../../stores/TagOrderStore'; import TagOrderStore from '../../stores/TagOrderStore';
import GroupActions from '../../actions/GroupActions'; import GroupActions from '../../actions/GroupActions';
import TagOrderActions from '../../actions/TagOrderActions';
import sdk from '../../index'; import sdk from '../../index';
import dis from '../../dispatcher'; import dis from '../../dispatcher';
import { DragDropContext, Droppable } from 'react-beautiful-dnd'; import { Droppable } from 'react-beautiful-dnd';
const TagPanel = React.createClass({ const TagPanel = React.createClass({
displayName: 'TagPanel', displayName: 'TagPanel',
@ -82,22 +81,6 @@ const TagPanel = React.createClass({
dis.dispatch({action: 'view_create_group'}); dis.dispatch({action: 'view_create_group'});
}, },
onTagTileEndDrag(result) {
// Dragged to an invalid destination, not onto a droppable
if (!result.destination) {
return;
}
// Dispatch synchronously so that the TagPanel receives an
// optimistic update from TagOrderStore before the previous
// state is shown.
dis.dispatch(TagOrderActions.moveTag(
this.context.matrixClient,
result.draggableId,
result.destination.index,
), true);
},
render() { render() {
const AccessibleButton = sdk.getComponent('elements.AccessibleButton'); const AccessibleButton = sdk.getComponent('elements.AccessibleButton');
const TintableSvg = sdk.getComponent('elements.TintableSvg'); const TintableSvg = sdk.getComponent('elements.TintableSvg');
@ -112,24 +95,22 @@ const TagPanel = React.createClass({
/>; />;
}); });
return <div className="mx_TagPanel"> return <div className="mx_TagPanel">
<DragDropContext onDragEnd={this.onTagTileEndDrag}> <Droppable droppableId="tag-panel-droppable">
<Droppable droppableId="tag-panel-droppable"> { (provided, snapshot) => (
{ (provided, snapshot) => ( <div
<div className="mx_TagPanel_tagTileContainer"
className="mx_TagPanel_tagTileContainer" ref={provided.innerRef}
ref={provided.innerRef} // react-beautiful-dnd has a bug that emits a click to the parent
// react-beautiful-dnd has a bug that emits a click to the parent // of draggables upon dropping
// of draggables upon dropping // https://github.com/atlassian/react-beautiful-dnd/issues/273
// https://github.com/atlassian/react-beautiful-dnd/issues/273 // so we use onMouseDown here as a workaround.
// so we use onMouseDown here as a workaround. onMouseDown={this.onClick}
onMouseDown={this.onClick} >
> { tags }
{ tags } { provided.placeholder }
{ provided.placeholder } </div>
</div> ) }
) } </Droppable>
</Droppable>
</DragDropContext>
<AccessibleButton className="mx_TagPanel_createGroupButton" onClick={this.onCreateGroupClick}> <AccessibleButton className="mx_TagPanel_createGroupButton" onClick={this.onCreateGroupClick}>
<TintableSvg src="img/icons-create-room.svg" width="25" height="25" /> <TintableSvg src="img/icons-create-room.svg" width="25" height="25" />
</AccessibleButton> </AccessibleButton>

View file

@ -18,7 +18,6 @@ limitations under the License.
'use strict'; 'use strict';
const React = require("react"); const React = require("react");
const ReactDOM = require("react-dom"); const ReactDOM = require("react-dom");
import { DragDropContext } from 'react-beautiful-dnd';
import PropTypes from 'prop-types'; import PropTypes from 'prop-types';
import { _t } from '../../../languageHandler'; import { _t } from '../../../languageHandler';
const GeminiScrollbar = require('react-gemini-scrollbar'); const GeminiScrollbar = require('react-gemini-scrollbar');
@ -33,8 +32,6 @@ const Receipt = require('../../../utils/Receipt');
import TagOrderStore from '../../../stores/TagOrderStore'; import TagOrderStore from '../../../stores/TagOrderStore';
import GroupStoreCache from '../../../stores/GroupStoreCache'; import GroupStoreCache from '../../../stores/GroupStoreCache';
import Modal from '../../../Modal';
const HIDE_CONFERENCE_CHANS = true; const HIDE_CONFERENCE_CHANS = true;
function phraseForSection(section) { function phraseForSection(section) {
@ -278,103 +275,6 @@ module.exports = React.createClass({
this.forceUpdate(); 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() { _delayedRefreshRoomList: new rate_limited_func(function() {
this.refreshRoomList(); this.refreshRoomList();
}, 500), }, 500),
@ -749,116 +649,114 @@ module.exports = React.createClass({
const self = this; const self = this;
return ( return (
<DragDropContext onDragEnd={this.onRoomTileEndDrag}> <GeminiScrollbar className="mx_RoomList_scrollbar"
<GeminiScrollbar className="mx_RoomList_scrollbar" autoshow={true} onScroll={self._whenScrolling} ref="gemscroll">
autoshow={true} onScroll={self._whenScrolling} ref="gemscroll"> <div className="mx_RoomList">
<div className="mx_RoomList"> <RoomSubList list={[]}
<RoomSubList list={[]} extraTiles={this._makeGroupInviteTiles()}
extraTiles={this._makeGroupInviteTiles()} label={_t('Community Invites')}
label={_t('Community Invites')} editable={false}
editable={false} order="recent"
order="recent" isInvite={true}
isInvite={true} collapsed={self.props.collapsed}
collapsed={self.props.collapsed} searchFilter={self.props.searchFilter}
searchFilter={self.props.searchFilter} onHeaderClick={self.onSubListHeaderClick}
onHeaderClick={self.onSubListHeaderClick} onShowMoreRooms={self.onShowMoreRooms}
onShowMoreRooms={self.onShowMoreRooms} />
/>
<RoomSubList list={self.state.lists['im.vector.fake.invite']} <RoomSubList list={self.state.lists['im.vector.fake.invite']}
label={_t('Invites')} label={_t('Invites')}
editable={false} editable={false}
order="recent" order="recent"
isInvite={true} isInvite={true}
incomingCall={self.state.incomingCall} incomingCall={self.state.incomingCall}
collapsed={self.props.collapsed} collapsed={self.props.collapsed}
searchFilter={self.props.searchFilter} searchFilter={self.props.searchFilter}
onHeaderClick={self.onSubListHeaderClick} onHeaderClick={self.onSubListHeaderClick}
onShowMoreRooms={self.onShowMoreRooms} onShowMoreRooms={self.onShowMoreRooms}
/> />
<RoomSubList list={self.state.lists['m.favourite']} <RoomSubList list={self.state.lists['m.favourite']}
label={_t('Favourites')} label={_t('Favourites')}
tagName="m.favourite" tagName="m.favourite"
emptyContent={this._getEmptyContent('m.favourite')} emptyContent={this._getEmptyContent('m.favourite')}
editable={true} editable={true}
order="manual" order="manual"
incomingCall={self.state.incomingCall} incomingCall={self.state.incomingCall}
collapsed={self.props.collapsed} collapsed={self.props.collapsed}
searchFilter={self.props.searchFilter} searchFilter={self.props.searchFilter}
onHeaderClick={self.onSubListHeaderClick} onHeaderClick={self.onSubListHeaderClick}
onShowMoreRooms={self.onShowMoreRooms} /> onShowMoreRooms={self.onShowMoreRooms} />
<RoomSubList list={self.state.lists['im.vector.fake.direct']} <RoomSubList list={self.state.lists['im.vector.fake.direct']}
label={_t('People')} label={_t('People')}
tagName="im.vector.fake.direct" tagName="im.vector.fake.direct"
emptyContent={this._getEmptyContent('im.vector.fake.direct')} emptyContent={this._getEmptyContent('im.vector.fake.direct')}
headerItems={this._getHeaderItems('im.vector.fake.direct')} headerItems={this._getHeaderItems('im.vector.fake.direct')}
editable={true} editable={true}
order="recent" order="recent"
incomingCall={self.state.incomingCall} incomingCall={self.state.incomingCall}
collapsed={self.props.collapsed} collapsed={self.props.collapsed}
alwaysShowHeader={true} alwaysShowHeader={true}
searchFilter={self.props.searchFilter} searchFilter={self.props.searchFilter}
onHeaderClick={self.onSubListHeaderClick} onHeaderClick={self.onSubListHeaderClick}
onShowMoreRooms={self.onShowMoreRooms} /> onShowMoreRooms={self.onShowMoreRooms} />
<RoomSubList list={self.state.lists['im.vector.fake.recent']} <RoomSubList list={self.state.lists['im.vector.fake.recent']}
label={_t('Rooms')} label={_t('Rooms')}
editable={true} editable={true}
emptyContent={this._getEmptyContent('im.vector.fake.recent')} emptyContent={this._getEmptyContent('im.vector.fake.recent')}
headerItems={this._getHeaderItems('im.vector.fake.recent')} headerItems={this._getHeaderItems('im.vector.fake.recent')}
order="recent" order="recent"
incomingCall={self.state.incomingCall} incomingCall={self.state.incomingCall}
collapsed={self.props.collapsed} collapsed={self.props.collapsed}
searchFilter={self.props.searchFilter} searchFilter={self.props.searchFilter}
onHeaderClick={self.onSubListHeaderClick} onHeaderClick={self.onSubListHeaderClick}
onShowMoreRooms={self.onShowMoreRooms} /> onShowMoreRooms={self.onShowMoreRooms} />
{ Object.keys(self.state.lists).map((tagName) => { { Object.keys(self.state.lists).map((tagName) => {
if (!tagName.match(/^(m\.(favourite|lowpriority)|im\.vector\.fake\.(invite|recent|direct|archived))$/)) { if (!tagName.match(/^(m\.(favourite|lowpriority)|im\.vector\.fake\.(invite|recent|direct|archived))$/)) {
return <RoomSubList list={self.state.lists[tagName]} return <RoomSubList list={self.state.lists[tagName]}
key={tagName} key={tagName}
label={tagName} label={tagName}
tagName={tagName} tagName={tagName}
emptyContent={this._getEmptyContent(tagName)} emptyContent={this._getEmptyContent(tagName)}
editable={true} editable={true}
order="manual" order="manual"
incomingCall={self.state.incomingCall} incomingCall={self.state.incomingCall}
collapsed={self.props.collapsed} collapsed={self.props.collapsed}
searchFilter={self.props.searchFilter} searchFilter={self.props.searchFilter}
onHeaderClick={self.onSubListHeaderClick} onHeaderClick={self.onSubListHeaderClick}
onShowMoreRooms={self.onShowMoreRooms} />; onShowMoreRooms={self.onShowMoreRooms} />;
} }
}) } }) }
<RoomSubList list={self.state.lists['m.lowpriority']} <RoomSubList list={self.state.lists['m.lowpriority']}
label={_t('Low priority')} label={_t('Low priority')}
tagName="m.lowpriority" tagName="m.lowpriority"
emptyContent={this._getEmptyContent('m.lowpriority')} emptyContent={this._getEmptyContent('m.lowpriority')}
editable={true} editable={true}
order="recent" order="recent"
incomingCall={self.state.incomingCall} incomingCall={self.state.incomingCall}
collapsed={self.props.collapsed} collapsed={self.props.collapsed}
searchFilter={self.props.searchFilter} searchFilter={self.props.searchFilter}
onHeaderClick={self.onSubListHeaderClick} onHeaderClick={self.onSubListHeaderClick}
onShowMoreRooms={self.onShowMoreRooms} /> onShowMoreRooms={self.onShowMoreRooms} />
<RoomSubList list={self.state.lists['im.vector.fake.archived']} <RoomSubList list={self.state.lists['im.vector.fake.archived']}
label={_t('Historical')} label={_t('Historical')}
editable={false} editable={false}
order="recent" order="recent"
collapsed={self.props.collapsed} collapsed={self.props.collapsed}
alwaysShowHeader={true} alwaysShowHeader={true}
startAsHidden={true} startAsHidden={true}
showSpinner={self.state.isLoadingLeftRooms} showSpinner={self.state.isLoadingLeftRooms}
onHeaderClick= {self.onArchivedHeaderClick} onHeaderClick= {self.onArchivedHeaderClick}
incomingCall={self.state.incomingCall} incomingCall={self.state.incomingCall}
searchFilter={self.props.searchFilter} searchFilter={self.props.searchFilter}
onShowMoreRooms={self.onShowMoreRooms} /> onShowMoreRooms={self.onShowMoreRooms} />
</div> </div>
</GeminiScrollbar> </GeminiScrollbar>
</DragDropContext>
); );
}, },
}); });