Skip to content
This repository has been archived by the owner on Sep 11, 2024. It is now read-only.

Allow reordering of the space panel via Drag and Drop #6137

Merged
merged 24 commits into from
Jun 22, 2021
Merged
Show file tree
Hide file tree
Changes from 18 commits
Commits
Show all changes
24 commits
Select commit Hold shift + click to select a range
da13ec1
Merge branch 't3chguy/fix/17529' of github.com:matrix-org/matrix-reac…
t3chguy Jun 2, 2021
079a5c1
Respect space ordering field in m.tag for top level spaces
t3chguy Jun 2, 2021
3f12b72
Make AutoHideScrollbar pass through all unknown props
t3chguy Jun 3, 2021
e334ce8
First cut of space panel drag-and-drop ordering
t3chguy Jun 3, 2021
dbaa394
i18n
t3chguy Jun 3, 2021
271f544
Stash
t3chguy Jun 7, 2021
21fc386
Move over to new lexicographic string sorting
t3chguy Jun 10, 2021
a4fa277
Iterate lexicographic ordering implementation
t3chguy Jun 11, 2021
3d44113
write a shedload more tests
t3chguy Jun 11, 2021
4af2675
stash bigint support
t3chguy Jun 14, 2021
8fd72fc
Iterate algorithm, base it on new js-sdk string lib
t3chguy Jun 14, 2021
2879b90
Use alphabet from js-sdk
t3chguy Jun 14, 2021
66fce64
Merge branch 'develop' of github.com:matrix-org/matrix-react-sdk into…
t3chguy Jun 14, 2021
b9f86d5
Update yarn.lock
t3chguy Jun 14, 2021
a63d922
Clear outstanding TODOs
t3chguy Jun 15, 2021
cee294f
iterate PR
t3chguy Jun 16, 2021
bceee79
improve naming of tests
t3chguy Jun 16, 2021
d4e3762
Break down the SpacePanel component
t3chguy Jun 16, 2021
e7fde26
remove unused imports
t3chguy Jun 16, 2021
7948aa6
Iterate PR, improve jsdoc and switch function style
t3chguy Jun 22, 2021
6e3c647
Merge branch 'develop' of github.com:matrix-org/matrix-react-sdk into…
t3chguy Jun 22, 2021
99e3aea
i18n and regen yarn lock
t3chguy Jun 22, 2021
49d20d2
consolidate the two onRoomAccountData listeners
t3chguy Jun 22, 2021
d212175
Merge branch 'develop' of github.com:matrix-org/matrix-react-sdk into…
t3chguy Jun 22, 2021
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -89,7 +89,7 @@
"qrcode": "^1.4.4",
"re-resizable": "^6.9.0",
"react": "^17.0.2",
"react-beautiful-dnd": "^4.0.1",
"react-beautiful-dnd": "^13.1.0",
"react-dom": "^17.0.2",
"react-focus-lock": "^2.5.0",
"react-transition-group": "^4.4.1",
Expand Down Expand Up @@ -133,6 +133,7 @@
"@types/parse5": "^6.0.0",
"@types/qrcode": "^1.3.5",
"@types/react": "^16.9",
"@types/react-beautiful-dnd": "^13.0.0",
"@types/react-dom": "^16.9.10",
"@types/react-transition-group": "^4.4.0",
"@types/sanitize-html": "^2.3.1",
Expand Down
7 changes: 6 additions & 1 deletion res/css/structures/_SpacePanel.scss
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,6 @@ $activeBorderColor: $secondary-fg-color;
// Create another flexbox so the Panel fills the container
display: flex;
flex-direction: column;
overflow-y: auto;

.mx_SpacePanel_spaceTreeWrapper {
flex: 1;
Expand Down Expand Up @@ -69,6 +68,12 @@ $activeBorderColor: $secondary-fg-color;
cursor: pointer;
}

.mx_SpaceItem_dragging {
.mx_SpaceButton_toggleCollapse {
visibility: hidden;
}
}

.mx_SpaceTreeLevel {
display: flex;
flex-direction: column;
Expand Down
9 changes: 9 additions & 0 deletions res/css/views/context_menus/_TagTileContextMenu.scss
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,15 @@ limitations under the License.
mask-image: url('$(res)/img/element-icons/view-community.svg');
}

.mx_TagTileContextMenu_moveUp::before {
transform: rotate(180deg);
mask-image: url('$(res)/img/feather-customised/chevron-down.svg');
}

.mx_TagTileContextMenu_moveDown::before {
mask-image: url('$(res)/img/feather-customised/chevron-down.svg');
}

.mx_TagTileContextMenu_hideCommunity::before {
mask-image: url('$(res)/img/element-icons/hide.svg');
}
Expand Down
18 changes: 11 additions & 7 deletions src/components/structures/AutoHideScrollbar.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -15,9 +15,9 @@ See the License for the specific language governing permissions and
limitations under the License.
*/

import React from "react";
import React, {HTMLAttributes} from "react";
t3chguy marked this conversation as resolved.
Show resolved Hide resolved

interface IProps {
interface IProps extends HTMLAttributes<HTMLDivElement> {
className?: string;
onScroll?: () => void;
onWheel?: () => void;
Expand Down Expand Up @@ -52,14 +52,18 @@ export default class AutoHideScrollbar extends React.Component<IProps> {
}

public render() {
// eslint-disable-next-line @typescript-eslint/no-unused-vars
const { className, onScroll, onWheel, style, tabIndex, wrappedRef, children, ...otherProps } = this.props;

return (<div
{...otherProps}
t3chguy marked this conversation as resolved.
Show resolved Hide resolved
ref={this.containerRef}
style={this.props.style}
className={["mx_AutoHideScrollbar", this.props.className].join(" ")}
onWheel={this.props.onWheel}
tabIndex={this.props.tabIndex}
style={style}
className={["mx_AutoHideScrollbar", className].join(" ")}
onWheel={onWheel}
tabIndex={tabIndex}
>
{ this.props.children }
{ children }
</div>);
}
}
32 changes: 9 additions & 23 deletions src/components/structures/GroupFilterPanel.js
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,6 @@ import * as sdk from '../../index';
import dis from '../../dispatcher/dispatcher';
import { _t } from '../../languageHandler';

import { Droppable } from 'react-beautiful-dnd';
import classNames from 'classnames';
import MatrixClientContext from "../../contexts/MatrixClientContext";
import AutoHideScrollbar from "./AutoHideScrollbar";
Expand Down Expand Up @@ -83,7 +82,7 @@ class GroupFilterPanel extends React.Component {
}
};

onMouseDown = e => {
onClick = e => {
// only dispatch if its not a no-op
if (this.state.selectedTags.length > 0) {
dis.dispatch({action: 'deselect_tags'});
Expand Down Expand Up @@ -151,28 +150,15 @@ class GroupFilterPanel extends React.Component {
return <div className={classes} onClick={this.onClearFilterClick}>
<AutoHideScrollbar
className="mx_GroupFilterPanel_scroller"
// XXX: Use onMouseDown as a workaround for https://github.com/atlassian/react-beautiful-dnd/issues/273
// instead of onClick. Otherwise we experience https://github.com/vector-im/element-web/issues/6253
onMouseDown={this.onMouseDown}
onClick={this.onClick}
>
<Droppable
droppableId="tag-panel-droppable"
type="draggable-TagTile"
>
{ (provided, snapshot) => (
<div
className="mx_GroupFilterPanel_tagTileContainer"
ref={provided.innerRef}
>
{ this.renderGlobalIcon() }
{ tags }
<div>
{createButton}
</div>
{ provided.placeholder }
</div>
) }
</Droppable>
<div className="mx_GroupFilterPanel_tagTileContainer">
{ this.renderGlobalIcon() }
{ tags }
<div>
{ createButton }
</div>
</div>
</AutoHideScrollbar>
</div>;
}
Expand Down
11 changes: 7 additions & 4 deletions src/components/structures/IndicatorScrollbar.js
Original file line number Diff line number Diff line change
Expand Up @@ -185,21 +185,24 @@ export default class IndicatorScrollbar extends React.Component {
};

render() {
// eslint-disable-next-line no-unused-vars
const { children, trackHorizontalOverflow, verticalScrollsHorizontally, ...otherProps } = this.props;

const leftIndicatorStyle = {left: this.state.leftIndicatorOffset};
const rightIndicatorStyle = {right: this.state.rightIndicatorOffset};
const leftOverflowIndicator = this.props.trackHorizontalOverflow
const leftOverflowIndicator = trackHorizontalOverflow
? <div className="mx_IndicatorScrollbar_leftOverflowIndicator" style={leftIndicatorStyle} /> : null;
const rightOverflowIndicator = this.props.trackHorizontalOverflow
const rightOverflowIndicator = trackHorizontalOverflow
? <div className="mx_IndicatorScrollbar_rightOverflowIndicator" style={rightIndicatorStyle} /> : null;

return (<AutoHideScrollbar
ref={this._collectScrollerComponent}
wrappedRef={this._collectScroller}
onWheel={this.onMouseWheel}
{...this.props}
{...otherProps}
t3chguy marked this conversation as resolved.
Show resolved Hide resolved
>
{ leftOverflowIndicator }
{ this.props.children }
{ children }
{ rightOverflowIndicator }
</AutoHideScrollbar>);
}
Expand Down
67 changes: 9 additions & 58 deletions src/components/structures/LoggedInView.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,6 @@ limitations under the License.
import * as React from 'react';
import * as PropTypes from 'prop-types';
import { MatrixClient } from 'matrix-js-sdk/src/client';
import { DragDropContext } from 'react-beautiful-dnd';

import {Key} from '../../Keyboard';
import PageTypes from '../../PageTypes';
Expand All @@ -30,8 +29,6 @@ import dis from '../../dispatcher/dispatcher';
import { IMatrixClientCreds } from '../../MatrixClientPeg';
import SettingsStore from "../../settings/SettingsStore";

import TagOrderActions from '../../actions/TagOrderActions';
import RoomListActions from '../../actions/RoomListActions';
import ResizeHandle from '../views/elements/ResizeHandle';
import {Resizer, CollapseDistributor} from '../../resizer';
import MatrixClientContext from "../../contexts/MatrixClientContext";
Expand Down Expand Up @@ -569,50 +566,6 @@ class LoggedInView extends React.Component<IProps, IState> {
}
};

_onDragEnd = (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 GroupFilterPanel receives an
// optimistic update from GroupFilterOrderStore 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 = (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() {
const RoomView = sdk.getComponent('structures.RoomView');
const UserView = sdk.getComponent('structures.UserView');
Expand Down Expand Up @@ -679,17 +632,15 @@ class LoggedInView extends React.Component<IProps, IState> {
aria-hidden={this.props.hideToSRUsers}
>
<ToastContainer />
<DragDropContext onDragEnd={this._onDragEnd}>
<div ref={this._resizeContainer} className={bodyClasses}>
{ SettingsStore.getValue("feature_spaces") ? <SpacePanel /> : null }
<LeftPanel
isMinimized={this.props.collapseLhs || false}
resizeNotifier={this.props.resizeNotifier}
/>
<ResizeHandle />
{ pageElement }
</div>
</DragDropContext>
<div ref={this._resizeContainer} className={bodyClasses}>
{ SettingsStore.getValue("feature_spaces") ? <SpacePanel /> : null }
<LeftPanel
isMinimized={this.props.collapseLhs || false}
resizeNotifier={this.props.resizeNotifier}
/>
<ResizeHandle />
{ pageElement }
</div>
</div>
<CallContainer />
<NonUrgentToastContainer />
Expand Down
3 changes: 1 addition & 2 deletions src/components/structures/MyGroups.js
Original file line number Diff line number Diff line change
Expand Up @@ -82,8 +82,7 @@ export default class MyGroups extends React.Component {
</p>
<p>
{ _t(
"To set up a filter, drag a community avatar over to the filter panel on " +
"the far left hand side of the screen. You can click on an avatar in the " +
"You can click on an avatar in the " +
"filter panel at any time to see only the rooms and people associated " +
"with that community.",
) }
Expand Down
4 changes: 2 additions & 2 deletions src/components/structures/SpaceRoomDirectory.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ import {mediaFromMxc} from "../../customisations/Media";
import InfoTooltip from "../views/elements/InfoTooltip";
import TextWithTooltip from "../views/elements/TextWithTooltip";
import {useStateToggle} from "../../hooks/useStateToggle";
import {getOrder} from "../../stores/SpaceStore";
import {getChildOrder} from "../../stores/SpaceStore";
t3chguy marked this conversation as resolved.
Show resolved Hide resolved
import AccessibleTooltipButton from "../views/elements/AccessibleTooltipButton";
import {linkifyElement} from "../../HtmlUtils";

Expand Down Expand Up @@ -286,7 +286,7 @@ export const HierarchyLevel = ({
const children = Array.from(relations.get(spaceId)?.values() || []);
const sortedChildren = sortBy(children, ev => {
// XXX: Space Summary API doesn't give the child origin_server_ts but once it does we should use it for sorting
return getOrder(ev.content.order, null, ev.state_key);
return getChildOrder(ev.content.order, null, ev.state_key);
});
const [subspaces, childRooms] = sortedChildren.reduce((result, ev: ISpaceSummaryEvent) => {
const roomId = ev.state_key;
Expand Down
49 changes: 37 additions & 12 deletions src/components/views/context_menus/TagTileContextMenu.js
Original file line number Diff line number Diff line change
Expand Up @@ -23,45 +23,70 @@ import TagOrderActions from '../../../actions/TagOrderActions';
import {MenuItem} from "../../structures/ContextMenu";
import MatrixClientContext from "../../../contexts/MatrixClientContext";
import {replaceableComponent} from "../../../utils/replaceableComponent";
import GroupFilterOrderStore from "../../../stores/GroupFilterOrderStore";

@replaceableComponent("views.context_menus.TagTileContextMenu")
export default class TagTileContextMenu extends React.Component {
static propTypes = {
tag: PropTypes.string.isRequired,
index: PropTypes.number.isRequired,
/* callback called when the menu is dismissed */
onFinished: PropTypes.func.isRequired,
};

static contextType = MatrixClientContext;

constructor() {
super();

this._onViewCommunityClick = this._onViewCommunityClick.bind(this);
this._onRemoveClick = this._onRemoveClick.bind(this);
}

_onViewCommunityClick() {
_onViewCommunityClick = () => {
dis.dispatch({
action: 'view_group',
group_id: this.props.tag,
});
this.props.onFinished();
}
};

_onRemoveClick() {
_onRemoveClick = () => {
dis.dispatch(TagOrderActions.removeTag(this.context, this.props.tag));
this.props.onFinished();
}
};

_onMoveUp = () => {
dis.dispatch(TagOrderActions.moveTag(this.context, this.props.tag, this.props.index - 1));
this.props.onFinished();
};

_onMoveDown = () => {
dis.dispatch(TagOrderActions.moveTag(this.context, this.props.tag, this.props.index + 1));
this.props.onFinished();
};

render() {
let moveUp;
let moveDown;
if (this.props.index > 0) {
moveUp = (
<MenuItem className="mx_TagTileContextMenu_item mx_TagTileContextMenu_moveUp" onClick={this._onMoveUp}>
{ _t("Move up") }
</MenuItem>
);
}
if (this.props.index < (GroupFilterOrderStore.getOrderedTags() || []).length - 1) {
moveDown = (
<MenuItem className="mx_TagTileContextMenu_item mx_TagTileContextMenu_moveDown" onClick={this._onMoveDown}>
{ _t("Move down") }
</MenuItem>
);
}

return <div>
<MenuItem className="mx_TagTileContextMenu_item mx_TagTileContextMenu_viewCommunity" onClick={this._onViewCommunityClick}>
{ _t('View Community') }
</MenuItem>
{ (moveUp || moveDown) ? <hr className="mx_TagTileContextMenu_separator" role="separator" /> : null }
{ moveUp }
{ moveDown }
<hr className="mx_TagTileContextMenu_separator" role="separator" />
<MenuItem className="mx_TagTileContextMenu_item mx_TagTileContextMenu_hideCommunity" onClick={this._onRemoveClick}>
{ _t('Hide') }
{ _t("Unpin") }
</MenuItem>
</div>;
}
Expand Down
Loading