Break down the SpacePanel component

This commit is contained in:
Michael Telatynski 2021-06-16 09:44:37 +01:00
parent bceee7978e
commit d4e376201f

View file

@ -14,18 +14,18 @@ See the License for the specific language governing permissions and
limitations under the License. limitations under the License.
*/ */
import React, { useEffect, useState } from "react"; import React, { Dispatch, ReactNode, SetStateAction, useEffect, useState } from "react";
import { DragDropContext, Droppable, Draggable } from "react-beautiful-dnd"; import { DragDropContext, Droppable, Draggable } from "react-beautiful-dnd";
import classNames from "classnames"; import classNames from "classnames";
import { Room } from "matrix-js-sdk/src/models/room"; import { Room } from "matrix-js-sdk/src/models/room";
import {_t} from "../../../languageHandler"; import { _t } from "../../../languageHandler";
import RoomAvatar from "../avatars/RoomAvatar"; import RoomAvatar from "../avatars/RoomAvatar";
import {useContextMenu} from "../../structures/ContextMenu"; import { useContextMenu } from "../../structures/ContextMenu";
import SpaceCreateMenu from "./SpaceCreateMenu"; import SpaceCreateMenu from "./SpaceCreateMenu";
import {SpaceItem} from "./SpaceTreeLevel"; import { SpaceItem } from "./SpaceTreeLevel";
import AccessibleTooltipButton from "../elements/AccessibleTooltipButton"; import AccessibleTooltipButton from "../elements/AccessibleTooltipButton";
import {useEventEmitter} from "../../../hooks/useEventEmitter"; import { useEventEmitter } from "../../../hooks/useEventEmitter";
import SpaceStore, { import SpaceStore, {
UPDATE_INVITED_SPACES, UPDATE_INVITED_SPACES,
UPDATE_SELECTED_SPACE, UPDATE_SELECTED_SPACE,
@ -38,9 +38,9 @@ import {
RovingAccessibleTooltipButton, RovingAccessibleTooltipButton,
RovingTabIndexProvider, RovingTabIndexProvider,
} from "../../../accessibility/RovingTabIndex"; } from "../../../accessibility/RovingTabIndex";
import {Key} from "../../../Keyboard"; import { Key } from "../../../Keyboard";
import {RoomNotificationStateStore} from "../../../stores/notifications/RoomNotificationStateStore"; import { RoomNotificationStateStore } from "../../../stores/notifications/RoomNotificationStateStore";
import {NotificationState} from "../../../stores/notifications/NotificationState"; import { NotificationState } from "../../../stores/notifications/NotificationState";
interface IButtonProps { interface IButtonProps {
space?: Room; space?: Room;
@ -121,11 +121,62 @@ const useSpaces = (): [Room[], Room[], Room | null] => {
return [invites, spaces, activeSpace]; return [invites, spaces, activeSpace];
}; };
interface IInnerSpacePanelProps {
children?: ReactNode;
isPanelCollapsed: boolean;
setPanelCollapsed: Dispatch<SetStateAction<boolean>>;
}
// Optimisation based on https://github.com/atlassian/react-beautiful-dnd/blob/master/docs/api/droppable.md#recommended-droppable--performance-optimisation
const InnerSpacePanel = React.memo<IInnerSpacePanelProps>(({ children, isPanelCollapsed, setPanelCollapsed }) => {
const [invites, spaces, activeSpace] = useSpaces();
const activeSpaces = activeSpace ? [activeSpace] : [];
return <div className="mx_SpaceTreeLevel">
<SpaceButton
className="mx_SpaceButton_home"
onClick={() => SpaceStore.instance.setActiveSpace(null)}
selected={!activeSpace}
tooltip={_t("All rooms")}
notificationState={RoomNotificationStateStore.instance.globalState}
isNarrow={isPanelCollapsed}
/>
{ invites.map(s => (
<SpaceItem
key={s.roomId}
space={s}
activeSpaces={activeSpaces}
isPanelCollapsed={isPanelCollapsed}
onExpand={() => setPanelCollapsed(false)}
/>
)) }
{ spaces.map((s, i) => (
<Draggable key={s.roomId} draggableId={s.roomId} index={i}>
{(provided, snapshot) => (
<SpaceItem
{...provided.draggableProps}
{...provided.dragHandleProps}
key={s.roomId}
innerRef={provided.innerRef}
className={snapshot.isDragging
? "mx_SpaceItem_dragging"
: undefined}
space={s}
activeSpaces={activeSpaces}
isPanelCollapsed={isPanelCollapsed}
onExpand={() => setPanelCollapsed(false)}
/>
)}
</Draggable>
)) }
{ children }
</div>;
});
const SpacePanel = () => { const SpacePanel = () => {
// We don't need the handle as we position the menu in a constant location // We don't need the handle as we position the menu in a constant location
// eslint-disable-next-line @typescript-eslint/no-unused-vars // eslint-disable-next-line @typescript-eslint/no-unused-vars
const [menuDisplayed, handle, openMenu, closeMenu] = useContextMenu<void>(); const [menuDisplayed, handle, openMenu, closeMenu] = useContextMenu<void>();
const [invites, spaces, activeSpace] = useSpaces();
const [isPanelCollapsed, setPanelCollapsed] = useState(true); const [isPanelCollapsed, setPanelCollapsed] = useState(true);
useEffect(() => { useEffect(() => {
@ -134,10 +185,6 @@ const SpacePanel = () => {
} }
}, [isPanelCollapsed]); // eslint-disable-line react-hooks/exhaustive-deps }, [isPanelCollapsed]); // eslint-disable-line react-hooks/exhaustive-deps
const newClasses = classNames("mx_SpaceButton_new", {
mx_SpaceButton_newCancel: menuDisplayed,
});
let contextMenu = null; let contextMenu = null;
if (menuDisplayed) { if (menuDisplayed) {
contextMenu = <SpaceCreateMenu onFinished={closeMenu} />; contextMenu = <SpaceCreateMenu onFinished={closeMenu} />;
@ -204,7 +251,11 @@ const SpacePanel = () => {
} }
}; };
const activeSpaces = activeSpace ? [activeSpace] : []; const onNewClick = menuDisplayed ? closeMenu : () => {
if (!isPanelCollapsed) setPanelCollapsed(true);
openMenu();
};
return ( return (
<DragDropContext onDragEnd={result => { <DragDropContext onDragEnd={result => {
if (!result.destination) return; // dropped outside the list if (!result.destination) return; // dropped outside the list
@ -226,59 +277,26 @@ const SpacePanel = () => {
pointerEvents: "none", pointerEvents: "none",
} : undefined} } : undefined}
> >
<div className="mx_SpaceTreeLevel"> <InnerSpacePanel
<SpaceButton
className="mx_SpaceButton_home"
onClick={() => SpaceStore.instance.setActiveSpace(null)}
selected={!activeSpace}
tooltip={_t("All rooms")}
notificationState={RoomNotificationStateStore.instance.globalState}
isNarrow={isPanelCollapsed}
/>
{ invites.map(s => (
<SpaceItem
key={s.roomId}
space={s}
activeSpaces={activeSpaces}
isPanelCollapsed={isPanelCollapsed} isPanelCollapsed={isPanelCollapsed}
onExpand={() => setPanelCollapsed(false)} setPanelCollapsed={setPanelCollapsed}
/> >
)) }
{ spaces.map((s, i) => (
<Draggable key={s.roomId} draggableId={s.roomId} index={i}>
{(provided, snapshot) => (
<SpaceItem
{...provided.draggableProps}
{...provided.dragHandleProps}
key={s.roomId}
innerRef={provided.innerRef}
className={snapshot.isDragging
? "mx_SpaceItem_dragging"
: undefined}
space={s}
activeSpaces={activeSpaces}
isPanelCollapsed={isPanelCollapsed}
onExpand={() => setPanelCollapsed(false)}
/>
)}
</Draggable>
)) }
{ provided.placeholder } { provided.placeholder }
</div> </InnerSpacePanel>
<SpaceButton <SpaceButton
className={newClasses} className={classNames("mx_SpaceButton_new", {
mx_SpaceButton_newCancel: menuDisplayed,
})}
tooltip={menuDisplayed ? _t("Cancel") : _t("Create a space")} tooltip={menuDisplayed ? _t("Cancel") : _t("Create a space")}
onClick={menuDisplayed ? closeMenu : () => { onClick={onNewClick}
if (!isPanelCollapsed) setPanelCollapsed(true);
openMenu();
}}
isNarrow={isPanelCollapsed} isNarrow={isPanelCollapsed}
/> />
</AutoHideScrollbar> </AutoHideScrollbar>
)} )}
</Droppable> </Droppable>
<AccessibleTooltipButton <AccessibleTooltipButton
className={classNames("mx_SpacePanel_toggleCollapse", {expanded: !isPanelCollapsed})} className={classNames("mx_SpacePanel_toggleCollapse", { expanded: !isPanelCollapsed })}
onClick={() => setPanelCollapsed(!isPanelCollapsed)} onClick={() => setPanelCollapsed(!isPanelCollapsed)}
title={isPanelCollapsed ? _t("Expand space panel") : _t("Collapse space panel")} title={isPanelCollapsed ? _t("Expand space panel") : _t("Collapse space panel")}
/> />