Implement more meta-spaces (#7077)
This commit is contained in:
parent
dadac386fe
commit
5ad3261cb2
55 changed files with 970 additions and 353 deletions
|
@ -24,7 +24,7 @@ import { _t } from '../../../languageHandler';
|
|||
import BaseDialog from "./BaseDialog";
|
||||
import Dropdown from "../elements/Dropdown";
|
||||
import SearchBox from "../../structures/SearchBox";
|
||||
import SpaceStore from "../../../stores/SpaceStore";
|
||||
import SpaceStore from "../../../stores/spaces/SpaceStore";
|
||||
import RoomAvatar from "../avatars/RoomAvatar";
|
||||
import { getDisplayAliasForRoom } from "../../../Rooms";
|
||||
import AccessibleButton from "../elements/AccessibleButton";
|
||||
|
|
|
@ -17,7 +17,7 @@ limitations under the License.
|
|||
import React, { ComponentProps, useMemo, useState } from 'react';
|
||||
|
||||
import ConfirmUserActionDialog from "./ConfirmUserActionDialog";
|
||||
import SpaceStore from "../../../stores/SpaceStore";
|
||||
import SpaceStore from "../../../stores/spaces/SpaceStore";
|
||||
import { Room } from "matrix-js-sdk/src/models/room";
|
||||
import SpaceChildrenPicker from "../spaces/SpaceChildrenPicker";
|
||||
|
||||
|
|
|
@ -32,7 +32,7 @@ import RoomAliasField from "../elements/RoomAliasField";
|
|||
import LabelledToggleSwitch from "../elements/LabelledToggleSwitch";
|
||||
import DialogButtons from "../elements/DialogButtons";
|
||||
import BaseDialog from "../dialogs/BaseDialog";
|
||||
import SpaceStore from "../../../stores/SpaceStore";
|
||||
import SpaceStore from "../../../stores/spaces/SpaceStore";
|
||||
import JoinRuleDropdown from "../elements/JoinRuleDropdown";
|
||||
|
||||
interface IProps {
|
||||
|
|
|
@ -32,7 +32,7 @@ import { calculateRoomVia, makeRoomPermalink } from "../../../utils/permalinks/P
|
|||
import { useAsyncMemo } from "../../../hooks/useAsyncMemo";
|
||||
import Spinner from "../elements/Spinner";
|
||||
import { mediaFromMxc } from "../../../customisations/Media";
|
||||
import SpaceStore from "../../../stores/SpaceStore";
|
||||
import SpaceStore from "../../../stores/spaces/SpaceStore";
|
||||
import Modal from "../../../Modal";
|
||||
import InfoDialog from "./InfoDialog";
|
||||
import dis from "../../../dispatcher/dispatcher";
|
||||
|
|
|
@ -25,7 +25,7 @@ import MatrixClientContext from "../../../contexts/MatrixClientContext";
|
|||
import { BetaPill } from "../beta/BetaCard";
|
||||
import Field from "../elements/Field";
|
||||
import RoomAliasField from "../elements/RoomAliasField";
|
||||
import SpaceStore from "../../../stores/SpaceStore";
|
||||
import SpaceStore from "../../../stores/spaces/SpaceStore";
|
||||
import { createSpace, SpaceCreateForm } from "../spaces/SpaceCreateMenu";
|
||||
import { SubspaceSelector } from "./AddExistingToSpaceDialog";
|
||||
import JoinRuleDropdown from "../elements/JoinRuleDropdown";
|
||||
|
|
|
@ -43,7 +43,7 @@ import QueryMatcher from "../../../autocomplete/QueryMatcher";
|
|||
import TruncatedList from "../elements/TruncatedList";
|
||||
import EntityTile from "../rooms/EntityTile";
|
||||
import BaseAvatar from "../avatars/BaseAvatar";
|
||||
import SpaceStore from "../../../stores/SpaceStore";
|
||||
import SpaceStore from "../../../stores/spaces/SpaceStore";
|
||||
|
||||
const AVATAR_SIZE = 30;
|
||||
|
||||
|
|
|
@ -71,7 +71,7 @@ import QuestionDialog from "./QuestionDialog";
|
|||
import Spinner from "../elements/Spinner";
|
||||
import BaseDialog from "./BaseDialog";
|
||||
import DialPadBackspaceButton from "../elements/DialPadBackspaceButton";
|
||||
import SpaceStore from "../../../stores/SpaceStore";
|
||||
import SpaceStore from "../../../stores/spaces/SpaceStore";
|
||||
|
||||
import { logger } from "matrix-js-sdk/src/logger";
|
||||
|
||||
|
|
|
@ -21,7 +21,7 @@ import { JoinRule } from "matrix-js-sdk/src/@types/partials";
|
|||
import { _t } from '../../../languageHandler';
|
||||
import DialogButtons from "../elements/DialogButtons";
|
||||
import BaseDialog from "../dialogs/BaseDialog";
|
||||
import SpaceStore from "../../../stores/SpaceStore";
|
||||
import SpaceStore from "../../../stores/spaces/SpaceStore";
|
||||
import SpaceChildrenPicker from "../spaces/SpaceChildrenPicker";
|
||||
|
||||
interface IProps {
|
||||
|
|
|
@ -21,7 +21,7 @@ import { _t } from '../../../languageHandler';
|
|||
import { IDialogProps } from "./IDialogProps";
|
||||
import BaseDialog from "./BaseDialog";
|
||||
import SearchBox from "../../structures/SearchBox";
|
||||
import SpaceStore from "../../../stores/SpaceStore";
|
||||
import SpaceStore from "../../../stores/spaces/SpaceStore";
|
||||
import RoomAvatar from "../avatars/RoomAvatar";
|
||||
import AccessibleButton from "../elements/AccessibleButton";
|
||||
import AutoHideScrollbar from "../../structures/AutoHideScrollbar";
|
||||
|
@ -75,7 +75,7 @@ const ManageRestrictedJoinRuleDialog: React.FC<IProps> = ({ room, selected = [],
|
|||
const [spacesContainingRoom, otherEntries] = useMemo(() => {
|
||||
const spaces = cli.getVisibleRooms().filter(r => r.getMyMembership() === "join" && r.isSpaceRoom());
|
||||
return [
|
||||
spaces.filter(r => SpaceStore.instance.getSpaceFilteredRoomIds(r).has(room.roomId)),
|
||||
spaces.filter(r => SpaceStore.instance.getSpaceFilteredRoomIds(r.roomId).has(room.roomId)),
|
||||
selected.map(roomId => {
|
||||
const room = cli.getRoom(roomId);
|
||||
if (!room) {
|
||||
|
|
|
@ -34,6 +34,7 @@ import { UIFeature } from "../../../settings/UIFeature";
|
|||
import { replaceableComponent } from "../../../utils/replaceableComponent";
|
||||
import BaseDialog from "./BaseDialog";
|
||||
import { IDialogProps } from "./IDialogProps";
|
||||
import SidebarUserSettingsTab from "../settings/tabs/user/SidebarUserSettingsTab";
|
||||
|
||||
export enum UserTab {
|
||||
General = "USER_GENERAL_TAB",
|
||||
|
@ -41,6 +42,7 @@ export enum UserTab {
|
|||
Flair = "USER_FLAIR_TAB",
|
||||
Notifications = "USER_NOTIFICATIONS_TAB",
|
||||
Preferences = "USER_PREFERENCES_TAB",
|
||||
Sidebar = "USER_SIDEBAR_TAB",
|
||||
Voice = "USER_VOICE_TAB",
|
||||
Security = "USER_SECURITY_TAB",
|
||||
Labs = "USER_LABS_TAB",
|
||||
|
@ -117,6 +119,15 @@ export default class UserSettingsDialog extends React.Component<IProps, IState>
|
|||
<PreferencesUserSettingsTab closeSettingsFn={this.props.onFinished} />,
|
||||
));
|
||||
|
||||
if (SettingsStore.getValue("feature_spaces_metaspaces")) {
|
||||
tabs.push(new Tab(
|
||||
UserTab.Sidebar,
|
||||
_td("Sidebar"),
|
||||
"mx_UserSettingsDialog_sidebarIcon",
|
||||
<SidebarUserSettingsTab />,
|
||||
));
|
||||
}
|
||||
|
||||
if (SettingsStore.getValue(UIFeature.Voip)) {
|
||||
tabs.push(new Tab(
|
||||
UserTab.Voice,
|
||||
|
|
|
@ -69,7 +69,7 @@ import RoomName from "../elements/RoomName";
|
|||
import { mediaFromMxc } from "../../../customisations/Media";
|
||||
import UIStore from "../../../stores/UIStore";
|
||||
import { ComposerInsertPayload } from "../../../dispatcher/payloads/ComposerInsertPayload";
|
||||
import SpaceStore from "../../../stores/SpaceStore";
|
||||
import SpaceStore from "../../../stores/spaces/SpaceStore";
|
||||
import ConfirmSpaceUserActionDialog from "../dialogs/ConfirmSpaceUserActionDialog";
|
||||
import { bulkSpaceBehaviour } from "../../../utils/space";
|
||||
|
||||
|
|
|
@ -43,7 +43,7 @@ import EntityTile from "./EntityTile";
|
|||
import MemberTile from "./MemberTile";
|
||||
import BaseAvatar from '../avatars/BaseAvatar';
|
||||
import { throttle } from 'lodash';
|
||||
import SpaceStore from "../../../stores/SpaceStore";
|
||||
import SpaceStore from "../../../stores/spaces/SpaceStore";
|
||||
import { shouldShowComponent } from "../../../customisations/helpers/UIComponents";
|
||||
import { UIComponent } from "../../../settings/UIFeature";
|
||||
import { JoinRule } from "matrix-js-sdk/src/@types/partials";
|
||||
|
|
|
@ -31,7 +31,7 @@ import defaultDispatcher from "../../../dispatcher/dispatcher";
|
|||
import dis from "../../../dispatcher/dispatcher";
|
||||
import { ViewUserPayload } from "../../../dispatcher/payloads/ViewUserPayload";
|
||||
import { Action } from "../../../dispatcher/actions";
|
||||
import SpaceStore from "../../../stores/SpaceStore";
|
||||
import SpaceStore from "../../../stores/spaces/SpaceStore";
|
||||
import { showSpaceInvite } from "../../../utils/space";
|
||||
import { privateShouldBeEncrypted } from "../../../createRoom";
|
||||
import EventTileBubble from "../messages/EventTileBubble";
|
||||
|
@ -126,12 +126,12 @@ const NewRoomIntro = () => {
|
|||
});
|
||||
}
|
||||
|
||||
let parentSpace;
|
||||
let parentSpace: Room;
|
||||
if (
|
||||
SpaceStore.instance.activeSpace?.canInvite(cli.getUserId()) &&
|
||||
SpaceStore.instance.activeSpaceRoom?.canInvite(cli.getUserId()) &&
|
||||
SpaceStore.instance.getSpaceFilteredRoomIds(SpaceStore.instance.activeSpace).has(room.roomId)
|
||||
) {
|
||||
parentSpace = SpaceStore.instance.activeSpace;
|
||||
parentSpace = SpaceStore.instance.activeSpaceRoom;
|
||||
}
|
||||
|
||||
let buttons;
|
||||
|
|
|
@ -21,7 +21,7 @@ import * as fbEmitter from "fbemitter";
|
|||
import { EventType } from "matrix-js-sdk/src/@types/event";
|
||||
|
||||
import { _t, _td } from "../../../languageHandler";
|
||||
import { RovingTabIndexProvider, IState as IRovingTabIndexState } from "../../../accessibility/RovingTabIndex";
|
||||
import { IState as IRovingTabIndexState, RovingTabIndexProvider } from "../../../accessibility/RovingTabIndex";
|
||||
import ResizeNotifier from "../../../utils/ResizeNotifier";
|
||||
import RoomListStore, { LISTS_UPDATE_EVENT } from "../../../stores/room-list/RoomListStore";
|
||||
import RoomViewStore from "../../../stores/RoomViewStore";
|
||||
|
@ -44,7 +44,8 @@ import { objectShallowClone, objectWithOnly } from "../../../utils/objects";
|
|||
import { IconizedContextMenuOption, IconizedContextMenuOptionList } from "../context_menus/IconizedContextMenu";
|
||||
import AccessibleButton from "../elements/AccessibleButton";
|
||||
import { CommunityPrototypeStore } from "../../../stores/CommunityPrototypeStore";
|
||||
import SpaceStore, { ISuggestedRoom, SUGGESTED_ROOMS } from "../../../stores/SpaceStore";
|
||||
import SpaceStore from "../../../stores/spaces/SpaceStore";
|
||||
import { ISuggestedRoom, MetaSpace, SpaceKey, UPDATE_SUGGESTED_ROOMS } from "../../../stores/spaces";
|
||||
import { showAddExistingRooms, showCreateNewRoom, showSpaceInvite } from "../../../utils/space";
|
||||
import { replaceableComponent } from "../../../utils/replaceableComponent";
|
||||
import RoomAvatar from "../avatars/RoomAvatar";
|
||||
|
@ -52,6 +53,7 @@ import AccessibleTooltipButton from "../elements/AccessibleTooltipButton";
|
|||
import { shouldShowComponent } from "../../../customisations/helpers/UIComponents";
|
||||
import { UIComponent } from "../../../settings/UIFeature";
|
||||
import { JoinRule } from "matrix-js-sdk/src/@types/partials";
|
||||
import MatrixClientContext from "../../../contexts/MatrixClientContext";
|
||||
|
||||
interface IProps {
|
||||
onKeyDown: (ev: React.KeyboardEvent, state: IRovingTabIndexState) => void;
|
||||
|
@ -61,7 +63,7 @@ interface IProps {
|
|||
onListCollapse?: (isExpanded: boolean) => void;
|
||||
resizeNotifier: ResizeNotifier;
|
||||
isMinimized: boolean;
|
||||
activeSpace: Room;
|
||||
activeSpace: SpaceKey;
|
||||
}
|
||||
|
||||
interface IState {
|
||||
|
@ -131,9 +133,10 @@ const TAG_AESTHETICS: ITagAestheticsMap = {
|
|||
defaultHidden: false,
|
||||
addRoomLabel: _td("Add room"),
|
||||
addRoomContextMenu: (onFinished: () => void) => {
|
||||
if (SpaceStore.instance.activeSpace) {
|
||||
const canAddRooms = SpaceStore.instance.activeSpace.currentState.maySendStateEvent(EventType.SpaceChild,
|
||||
MatrixClientPeg.get().getUserId());
|
||||
if (SpaceStore.instance.activeSpaceRoom) {
|
||||
const userId = MatrixClientPeg.get().getUserId();
|
||||
const space = SpaceStore.instance.activeSpaceRoom;
|
||||
const canAddRooms = space.currentState.maySendStateEvent(EventType.SpaceChild, userId);
|
||||
|
||||
return <IconizedContextMenuOptionList first>
|
||||
{
|
||||
|
@ -146,7 +149,7 @@ const TAG_AESTHETICS: ITagAestheticsMap = {
|
|||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
onFinished();
|
||||
showCreateNewRoom(SpaceStore.instance.activeSpace);
|
||||
showCreateNewRoom(space);
|
||||
}}
|
||||
disabled={!canAddRooms}
|
||||
tooltip={canAddRooms ? undefined
|
||||
|
@ -159,7 +162,7 @@ const TAG_AESTHETICS: ITagAestheticsMap = {
|
|||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
onFinished();
|
||||
showAddExistingRooms(SpaceStore.instance.activeSpace);
|
||||
showAddExistingRooms(space);
|
||||
}}
|
||||
disabled={!canAddRooms}
|
||||
tooltip={canAddRooms ? undefined
|
||||
|
@ -251,6 +254,9 @@ export default class RoomList extends React.PureComponent<IProps, IState> {
|
|||
private roomStoreToken: fbEmitter.EventSubscription;
|
||||
private treeRef = createRef<HTMLDivElement>();
|
||||
|
||||
static contextType = MatrixClientContext;
|
||||
public context!: React.ContextType<typeof MatrixClientContext>;
|
||||
|
||||
constructor(props: IProps) {
|
||||
super(props);
|
||||
|
||||
|
@ -264,14 +270,14 @@ export default class RoomList extends React.PureComponent<IProps, IState> {
|
|||
public componentDidMount(): void {
|
||||
this.dispatcherRef = defaultDispatcher.register(this.onAction);
|
||||
this.roomStoreToken = RoomViewStore.addListener(this.onRoomViewStoreUpdate);
|
||||
SpaceStore.instance.on(SUGGESTED_ROOMS, this.updateSuggestedRooms);
|
||||
SpaceStore.instance.on(UPDATE_SUGGESTED_ROOMS, this.updateSuggestedRooms);
|
||||
RoomListStore.instance.on(LISTS_UPDATE_EVENT, this.updateLists);
|
||||
this.customTagStoreRef = CustomRoomTagStore.addListener(this.updateLists);
|
||||
this.updateLists(); // trigger the first update
|
||||
}
|
||||
|
||||
public componentWillUnmount() {
|
||||
SpaceStore.instance.off(SUGGESTED_ROOMS, this.updateSuggestedRooms);
|
||||
SpaceStore.instance.off(UPDATE_SUGGESTED_ROOMS, this.updateSuggestedRooms);
|
||||
RoomListStore.instance.off(LISTS_UPDATE_EVENT, this.updateLists);
|
||||
defaultDispatcher.unregister(this.dispatcherRef);
|
||||
if (this.customTagStoreRef) this.customTagStoreRef.remove();
|
||||
|
@ -379,7 +385,7 @@ export default class RoomList extends React.PureComponent<IProps, IState> {
|
|||
|
||||
private onSpaceInviteClick = () => {
|
||||
const initialText = RoomListStore.instance.getFirstNameFilterCondition()?.search;
|
||||
showSpaceInvite(this.props.activeSpace, initialText);
|
||||
showSpaceInvite(this.context.getRoom(this.props.activeSpace), initialText);
|
||||
};
|
||||
|
||||
private renderSuggestedRooms(): ReactComponentElement<typeof ExtraTile>[] {
|
||||
|
@ -485,6 +491,15 @@ export default class RoomList extends React.PureComponent<IProps, IState> {
|
|||
: TAG_AESTHETICS[orderedTagId];
|
||||
if (!aesthetics) throw new Error(`Tag ${orderedTagId} does not have aesthetics`);
|
||||
|
||||
let alwaysVisible = ALWAYS_VISIBLE_TAGS.includes(orderedTagId);
|
||||
if (
|
||||
(this.props.activeSpace === MetaSpace.Favourites && orderedTagId !== DefaultTagID.Favourite) ||
|
||||
(this.props.activeSpace === MetaSpace.People && orderedTagId !== DefaultTagID.DM) ||
|
||||
(this.props.activeSpace === MetaSpace.Orphans && orderedTagId === DefaultTagID.DM)
|
||||
) {
|
||||
alwaysVisible = false;
|
||||
}
|
||||
|
||||
// The cost of mounting/unmounting this component offsets the cost
|
||||
// of keeping it in the DOM and hiding it when it is not required
|
||||
return <RoomSublist
|
||||
|
@ -500,7 +515,7 @@ export default class RoomList extends React.PureComponent<IProps, IState> {
|
|||
showSkeleton={showSkeleton}
|
||||
extraTiles={extraTiles}
|
||||
resizeNotifier={this.props.resizeNotifier}
|
||||
alwaysVisible={ALWAYS_VISIBLE_TAGS.includes(orderedTagId)}
|
||||
alwaysVisible={alwaysVisible}
|
||||
onListCollapse={this.props.onListCollapse}
|
||||
/>;
|
||||
});
|
||||
|
@ -515,6 +530,7 @@ export default class RoomList extends React.PureComponent<IProps, IState> {
|
|||
public render() {
|
||||
const cli = MatrixClientPeg.get();
|
||||
const userId = cli.getUserId();
|
||||
const activeSpace = this.props.activeSpace[0] === "!" ? cli.getRoom(this.props.activeSpace) : null;
|
||||
|
||||
let explorePrompt: JSX.Element;
|
||||
if (!this.props.isMinimized) {
|
||||
|
@ -533,17 +549,16 @@ export default class RoomList extends React.PureComponent<IProps, IState> {
|
|||
kind="link"
|
||||
onClick={this.onExplore}
|
||||
>
|
||||
{ this.props.activeSpace ? _t("Explore rooms") : _t("Explore all public rooms") }
|
||||
{ activeSpace ? _t("Explore rooms") : _t("Explore all public rooms") }
|
||||
</AccessibleButton>
|
||||
</div>;
|
||||
} else if (
|
||||
this.props.activeSpace?.canInvite(userId) ||
|
||||
this.props.activeSpace?.getMyMembership() === "join" ||
|
||||
this.props.activeSpace?.getJoinRule() === JoinRule.Public
|
||||
activeSpace?.canInvite(userId) ||
|
||||
activeSpace?.getMyMembership() === "join" ||
|
||||
activeSpace?.getJoinRule() === JoinRule.Public
|
||||
) {
|
||||
const spaceName = this.props.activeSpace.name;
|
||||
const canInvite = this.props.activeSpace?.canInvite(userId) ||
|
||||
this.props.activeSpace?.getJoinRule() === JoinRule.Public;
|
||||
const spaceName = activeSpace.name;
|
||||
const canInvite = activeSpace?.canInvite(userId) || activeSpace?.getJoinRule() === JoinRule.Public;
|
||||
explorePrompt = <div className="mx_RoomList_explorePrompt">
|
||||
<div>{ _t("Quick actions") }</div>
|
||||
{ canInvite && <AccessibleTooltipButton
|
||||
|
@ -553,7 +568,7 @@ export default class RoomList extends React.PureComponent<IProps, IState> {
|
|||
>
|
||||
{ _t("Invite people") }
|
||||
</AccessibleTooltipButton> }
|
||||
{ this.props.activeSpace?.getMyMembership() === "join" && <AccessibleTooltipButton
|
||||
{ activeSpace?.getMyMembership() === "join" && <AccessibleTooltipButton
|
||||
className="mx_RoomList_explorePrompt_spaceExplore"
|
||||
onClick={this.onExplore}
|
||||
title={_t("Explore %(spaceName)s", { spaceName })}
|
||||
|
|
|
@ -19,7 +19,7 @@ import React, { useEffect, useState } from "react";
|
|||
import { _t } from "../../../languageHandler";
|
||||
import RoomListStore, { LISTS_UPDATE_EVENT } from "../../../stores/room-list/RoomListStore";
|
||||
import { useEventEmitter } from "../../../hooks/useEventEmitter";
|
||||
import SpaceStore from "../../../stores/SpaceStore";
|
||||
import SpaceStore from "../../../stores/spaces/SpaceStore";
|
||||
|
||||
interface IProps {
|
||||
onVisibilityChange?: () => void;
|
||||
|
|
|
@ -27,7 +27,7 @@ import RoomName from "../elements/RoomName";
|
|||
import { replaceableComponent } from "../../../utils/replaceableComponent";
|
||||
import ErrorDialog from '../dialogs/ErrorDialog';
|
||||
import AccessibleButton from '../elements/AccessibleButton';
|
||||
import SpaceStore from "../../../stores/SpaceStore";
|
||||
import SpaceStore from "../../../stores/spaces/SpaceStore";
|
||||
|
||||
import { logger } from "matrix-js-sdk/src/logger";
|
||||
|
||||
|
|
|
@ -23,7 +23,7 @@ import StyledRadioGroup, { IDefinition } from "../elements/StyledRadioGroup";
|
|||
import { _t } from "../../../languageHandler";
|
||||
import AccessibleButton from "../elements/AccessibleButton";
|
||||
import RoomAvatar from "../avatars/RoomAvatar";
|
||||
import SpaceStore from "../../../stores/SpaceStore";
|
||||
import SpaceStore from "../../../stores/spaces/SpaceStore";
|
||||
import { MatrixClientPeg } from "../../../MatrixClientPeg";
|
||||
import Modal from "../../../Modal";
|
||||
import ManageRestrictedJoinRuleDialog from "../dialogs/ManageRestrictedJoinRuleDialog";
|
||||
|
@ -67,8 +67,8 @@ const JoinRuleSettings = ({ room, promptUpgrade, onError, beforeChange, closeSet
|
|||
|
||||
const editRestrictedRoomIds = async (): Promise<string[] | undefined> => {
|
||||
let selected = restrictedAllowRoomIds;
|
||||
if (!selected?.length && SpaceStore.instance.activeSpace) {
|
||||
selected = [SpaceStore.instance.activeSpace.roomId];
|
||||
if (!selected?.length && SpaceStore.instance.activeSpaceRoom) {
|
||||
selected = [SpaceStore.instance.activeSpaceRoom.roomId];
|
||||
}
|
||||
|
||||
const matrixClient = MatrixClientPeg.get();
|
||||
|
@ -176,9 +176,9 @@ const JoinRuleSettings = ({ room, promptUpgrade, onError, beforeChange, closeSet
|
|||
{ moreText && <span>{ moreText }</span> }
|
||||
</div>
|
||||
</div>;
|
||||
} else if (SpaceStore.instance.activeSpace) {
|
||||
} else if (SpaceStore.instance.activeSpaceRoom) {
|
||||
description = _t("Anyone in <spaceName/> can find and join. You can select other spaces too.", {}, {
|
||||
spaceName: () => <b>{ SpaceStore.instance.activeSpace.name }</b>,
|
||||
spaceName: () => <b>{ SpaceStore.instance.activeSpaceRoom.name }</b>,
|
||||
});
|
||||
} else {
|
||||
description = _t("Anyone in a space can find and join. You can select multiple spaces.");
|
||||
|
|
|
@ -0,0 +1,123 @@
|
|||
/*
|
||||
Copyright 2021 The Matrix.org Foundation C.I.C.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
import React, { ChangeEvent } from 'react';
|
||||
|
||||
import { _t } from "../../../../../languageHandler";
|
||||
import SettingsStore from "../../../../../settings/SettingsStore";
|
||||
import { SettingLevel } from "../../../../../settings/SettingLevel";
|
||||
import StyledCheckbox from "../../../elements/StyledCheckbox";
|
||||
import { useSettingValue } from "../../../../../hooks/useSettings";
|
||||
import { MetaSpace } from "../../../../../stores/spaces";
|
||||
|
||||
const onMetaSpaceChangeFactory = (metaSpace: MetaSpace) => (e: ChangeEvent<HTMLInputElement>) => {
|
||||
const currentValue = SettingsStore.getValue("Spaces.enabledMetaSpaces");
|
||||
SettingsStore.setValue("Spaces.enabledMetaSpaces", null, SettingLevel.ACCOUNT, {
|
||||
...currentValue,
|
||||
[metaSpace]: e.target.checked,
|
||||
});
|
||||
};
|
||||
|
||||
const SidebarUserSettingsTab = () => {
|
||||
const {
|
||||
[MetaSpace.Home]: homeEnabled,
|
||||
[MetaSpace.Favourites]: favouritesEnabled,
|
||||
[MetaSpace.People]: peopleEnabled,
|
||||
[MetaSpace.Orphans]: orphansEnabled,
|
||||
} = useSettingValue<Record<MetaSpace, boolean>>("Spaces.enabledMetaSpaces");
|
||||
const allRoomsInHome = useSettingValue<boolean>("Spaces.allRoomsInHome");
|
||||
|
||||
return (
|
||||
<div className="mx_SettingsTab mx_SidebarUserSettingsTab">
|
||||
<div className="mx_SettingsTab_heading">{ _t("Sidebar") }</div>
|
||||
|
||||
<div className="mx_SettingsTab_section">
|
||||
<span className="mx_SettingsTab_subheading">{ _t("Spaces") }</span>
|
||||
<div className="mx_SettingsTab_subsectionText">{ _t("Spaces are ways to group rooms and people.") }</div>
|
||||
|
||||
<div className="mx_SidebarUserSettingsTab_subheading">{ _t("Spaces to show") }</div>
|
||||
<div className="mx_SettingsTab_subsectionText">
|
||||
{ _t("Along with the spaces you're in, you can use some pre-built ones too.") }
|
||||
</div>
|
||||
|
||||
<StyledCheckbox
|
||||
checked={!!homeEnabled}
|
||||
onChange={onMetaSpaceChangeFactory(MetaSpace.Home)}
|
||||
className="mx_SidebarUserSettingsTab_homeCheckbox"
|
||||
>
|
||||
{ _t("Home") }
|
||||
</StyledCheckbox>
|
||||
<div className="mx_SidebarUserSettingsTab_checkboxMicrocopy">
|
||||
{ _t("Home is useful for getting an overview of everything.") }
|
||||
</div>
|
||||
|
||||
<StyledCheckbox
|
||||
checked={allRoomsInHome}
|
||||
disabled={!homeEnabled}
|
||||
onChange={e => {
|
||||
SettingsStore.setValue(
|
||||
"Spaces.allRoomsInHome",
|
||||
null,
|
||||
SettingLevel.ACCOUNT,
|
||||
e.target.checked,
|
||||
);
|
||||
}}
|
||||
className="mx_SidebarUserSettingsTab_homeAllRoomsCheckbox"
|
||||
>
|
||||
{ _t("Show all rooms") }
|
||||
</StyledCheckbox>
|
||||
<div className="mx_SidebarUserSettingsTab_checkboxMicrocopy">
|
||||
{ _t("Show all your rooms in Home, even if they're in a space.") }
|
||||
</div>
|
||||
|
||||
<StyledCheckbox
|
||||
checked={!!favouritesEnabled}
|
||||
onChange={onMetaSpaceChangeFactory(MetaSpace.Favourites)}
|
||||
className="mx_SidebarUserSettingsTab_favouritesCheckbox"
|
||||
>
|
||||
{ _t("Favourites") }
|
||||
</StyledCheckbox>
|
||||
<div className="mx_SidebarUserSettingsTab_checkboxMicrocopy">
|
||||
{ _t("Automatically group all your favourite rooms and people together in one place.") }
|
||||
</div>
|
||||
|
||||
<StyledCheckbox
|
||||
checked={!!peopleEnabled}
|
||||
onChange={onMetaSpaceChangeFactory(MetaSpace.People)}
|
||||
className="mx_SidebarUserSettingsTab_peopleCheckbox"
|
||||
>
|
||||
{ _t("People") }
|
||||
</StyledCheckbox>
|
||||
<div className="mx_SidebarUserSettingsTab_checkboxMicrocopy">
|
||||
{ _t("Automatically group all your people together in one place.") }
|
||||
</div>
|
||||
|
||||
<StyledCheckbox
|
||||
checked={!!orphansEnabled}
|
||||
onChange={onMetaSpaceChangeFactory(MetaSpace.Orphans)}
|
||||
className="mx_SidebarUserSettingsTab_orphansCheckbox"
|
||||
>
|
||||
{ _t("Rooms outside of a space") }
|
||||
</StyledCheckbox>
|
||||
<div className="mx_SidebarUserSettingsTab_checkboxMicrocopy">
|
||||
{ _t("Automatically group all your rooms that aren't part of a space in one place.") }
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default SidebarUserSettingsTab;
|
|
@ -119,6 +119,7 @@ export const SpaceFeedbackPrompt = ({ onClick }: { onClick?: () => void }) => {
|
|||
rageshakeLabel: "spaces-feedback",
|
||||
rageshakeData: Object.fromEntries([
|
||||
"Spaces.allRoomsInHome",
|
||||
"Spaces.enabledMetaSpaces",
|
||||
].map(k => [k, SettingsStore.getValue(k)])),
|
||||
});
|
||||
}}
|
||||
|
|
|
@ -34,13 +34,15 @@ import SpaceCreateMenu from "./SpaceCreateMenu";
|
|||
import { SpaceButton, SpaceItem } from "./SpaceTreeLevel";
|
||||
import AccessibleTooltipButton from "../elements/AccessibleTooltipButton";
|
||||
import { useEventEmitterState } from "../../../hooks/useEventEmitter";
|
||||
import SpaceStore, {
|
||||
HOME_SPACE,
|
||||
import SpaceStore from "../../../stores/spaces/SpaceStore";
|
||||
import {
|
||||
MetaSpace,
|
||||
SpaceKey,
|
||||
UPDATE_HOME_BEHAVIOUR,
|
||||
UPDATE_INVITED_SPACES,
|
||||
UPDATE_SELECTED_SPACE,
|
||||
UPDATE_TOP_LEVEL_SPACES,
|
||||
} from "../../../stores/SpaceStore";
|
||||
} from "../../../stores/spaces";
|
||||
import AutoHideScrollbar from "../../structures/AutoHideScrollbar";
|
||||
import { RovingTabIndexProvider } from "../../../accessibility/RovingTabIndex";
|
||||
import { RoomNotificationStateStore } from "../../../stores/notifications/RoomNotificationStateStore";
|
||||
|
@ -53,17 +55,21 @@ import SettingsStore from "../../../settings/SettingsStore";
|
|||
import { SettingLevel } from "../../../settings/SettingLevel";
|
||||
import UIStore from "../../../stores/UIStore";
|
||||
|
||||
const useSpaces = (): [Room[], Room[], Room | null] => {
|
||||
const useSpaces = (): [Room[], MetaSpace[], Room[], SpaceKey] => {
|
||||
const invites = useEventEmitterState<Room[]>(SpaceStore.instance, UPDATE_INVITED_SPACES, () => {
|
||||
return SpaceStore.instance.invitedSpaces;
|
||||
});
|
||||
const spaces = useEventEmitterState<Room[]>(SpaceStore.instance, UPDATE_TOP_LEVEL_SPACES, () => {
|
||||
return SpaceStore.instance.spacePanelSpaces;
|
||||
});
|
||||
const activeSpace = useEventEmitterState<Room>(SpaceStore.instance, UPDATE_SELECTED_SPACE, () => {
|
||||
const [metaSpaces, actualSpaces] = useEventEmitterState<[MetaSpace[], Room[]]>(
|
||||
SpaceStore.instance, UPDATE_TOP_LEVEL_SPACES,
|
||||
() => [
|
||||
SpaceStore.instance.enabledMetaSpaces,
|
||||
SpaceStore.instance.spacePanelSpaces,
|
||||
],
|
||||
);
|
||||
const activeSpace = useEventEmitterState<SpaceKey>(SpaceStore.instance, UPDATE_SELECTED_SPACE, () => {
|
||||
return SpaceStore.instance.activeSpace;
|
||||
});
|
||||
return [invites, spaces, activeSpace];
|
||||
return [invites, metaSpaces, actualSpaces, activeSpace];
|
||||
};
|
||||
|
||||
interface IInnerSpacePanelProps {
|
||||
|
@ -99,37 +105,76 @@ const HomeButtonContextMenu = ({ onFinished, ...props }: ComponentProps<typeof S
|
|||
</IconizedContextMenu>;
|
||||
};
|
||||
|
||||
interface IHomeButtonProps {
|
||||
interface IMetaSpaceButtonProps extends ComponentProps<typeof SpaceButton> {
|
||||
selected: boolean;
|
||||
isPanelCollapsed: boolean;
|
||||
}
|
||||
|
||||
const HomeButton = ({ selected, isPanelCollapsed }: IHomeButtonProps) => {
|
||||
const allRoomsInHome = useEventEmitterState(SpaceStore.instance, UPDATE_HOME_BEHAVIOUR, () => {
|
||||
return SpaceStore.instance.allRoomsInHome;
|
||||
});
|
||||
type MetaSpaceButtonProps = Pick<IMetaSpaceButtonProps, "selected" | "isPanelCollapsed">;
|
||||
|
||||
const MetaSpaceButton = ({ selected, isPanelCollapsed, ...props }: IMetaSpaceButtonProps) => {
|
||||
return <li
|
||||
className={classNames("mx_SpaceItem", {
|
||||
"collapsed": isPanelCollapsed,
|
||||
})}
|
||||
role="treeitem"
|
||||
>
|
||||
<SpaceButton
|
||||
className="mx_SpaceButton_home"
|
||||
onClick={() => SpaceStore.instance.setActiveSpace(null)}
|
||||
selected={selected}
|
||||
label={allRoomsInHome ? _t("All rooms") : _t("Home")}
|
||||
notificationState={allRoomsInHome
|
||||
? RoomNotificationStateStore.instance.globalState
|
||||
: SpaceStore.instance.getNotificationState(HOME_SPACE)}
|
||||
isNarrow={isPanelCollapsed}
|
||||
ContextMenuComponent={HomeButtonContextMenu}
|
||||
contextMenuTooltip={_t("Options")}
|
||||
/>
|
||||
<SpaceButton {...props} selected={selected} isNarrow={isPanelCollapsed} />
|
||||
</li>;
|
||||
};
|
||||
|
||||
const HomeButton = ({ selected, isPanelCollapsed }: MetaSpaceButtonProps) => {
|
||||
const allRoomsInHome = useEventEmitterState(SpaceStore.instance, UPDATE_HOME_BEHAVIOUR, () => {
|
||||
return SpaceStore.instance.allRoomsInHome;
|
||||
});
|
||||
|
||||
return <MetaSpaceButton
|
||||
spaceKey={MetaSpace.Home}
|
||||
className="mx_SpaceButton_home"
|
||||
selected={selected}
|
||||
isPanelCollapsed={isPanelCollapsed}
|
||||
label={allRoomsInHome ? _t("All rooms") : _t("Home")}
|
||||
notificationState={allRoomsInHome
|
||||
? RoomNotificationStateStore.instance.globalState
|
||||
: SpaceStore.instance.getNotificationState(MetaSpace.Home)}
|
||||
ContextMenuComponent={HomeButtonContextMenu}
|
||||
contextMenuTooltip={_t("Options")}
|
||||
/>;
|
||||
};
|
||||
|
||||
const FavouritesButton = ({ selected, isPanelCollapsed }: MetaSpaceButtonProps) => {
|
||||
return <MetaSpaceButton
|
||||
spaceKey={MetaSpace.Favourites}
|
||||
className="mx_SpaceButton_favourites"
|
||||
selected={selected}
|
||||
isPanelCollapsed={isPanelCollapsed}
|
||||
label={_t("Favourites")}
|
||||
notificationState={SpaceStore.instance.getNotificationState(MetaSpace.Favourites)}
|
||||
/>;
|
||||
};
|
||||
|
||||
const PeopleButton = ({ selected, isPanelCollapsed }: MetaSpaceButtonProps) => {
|
||||
return <MetaSpaceButton
|
||||
spaceKey={MetaSpace.People}
|
||||
className="mx_SpaceButton_people"
|
||||
selected={selected}
|
||||
isPanelCollapsed={isPanelCollapsed}
|
||||
label={_t("People")}
|
||||
notificationState={SpaceStore.instance.getNotificationState(MetaSpace.People)}
|
||||
/>;
|
||||
};
|
||||
|
||||
const OrphansButton = ({ selected, isPanelCollapsed }: MetaSpaceButtonProps) => {
|
||||
return <MetaSpaceButton
|
||||
spaceKey={MetaSpace.Orphans}
|
||||
className="mx_SpaceButton_orphans"
|
||||
selected={selected}
|
||||
isPanelCollapsed={isPanelCollapsed}
|
||||
label={_t("Other rooms")}
|
||||
notificationState={SpaceStore.instance.getNotificationState(MetaSpace.Orphans)}
|
||||
/>;
|
||||
};
|
||||
|
||||
const CreateSpaceButton = ({
|
||||
isPanelCollapsed,
|
||||
setPanelCollapsed,
|
||||
|
@ -181,13 +226,25 @@ const CreateSpaceButton = ({
|
|||
</li>;
|
||||
};
|
||||
|
||||
const metaSpaceComponentMap: Record<MetaSpace, typeof HomeButton> = {
|
||||
[MetaSpace.Home]: HomeButton,
|
||||
[MetaSpace.Favourites]: FavouritesButton,
|
||||
[MetaSpace.People]: PeopleButton,
|
||||
[MetaSpace.Orphans]: OrphansButton,
|
||||
};
|
||||
|
||||
// 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 [invites, metaSpaces, actualSpaces, activeSpace] = useSpaces();
|
||||
const activeSpaces = activeSpace ? [activeSpace] : [];
|
||||
|
||||
const metaSpacesSection = metaSpaces.map(key => {
|
||||
const Component = metaSpaceComponentMap[key];
|
||||
return <Component key={key} selected={activeSpace === key} isPanelCollapsed={isPanelCollapsed} />;
|
||||
});
|
||||
|
||||
return <div className="mx_SpaceTreeLevel">
|
||||
<HomeButton selected={!activeSpace} isPanelCollapsed={isPanelCollapsed} />
|
||||
{ metaSpacesSection }
|
||||
{ invites.map(s => (
|
||||
<SpaceItem
|
||||
key={s.roomId}
|
||||
|
@ -197,7 +254,7 @@ const InnerSpacePanel = React.memo<IInnerSpacePanelProps>(({ children, isPanelCo
|
|||
onExpand={() => setPanelCollapsed(false)}
|
||||
/>
|
||||
)) }
|
||||
{ spaces.map((s, i) => (
|
||||
{ actualSpaces.map((s, i) => (
|
||||
<Draggable key={s.roomId} draggableId={s.roomId} index={i}>
|
||||
{ (provided, snapshot) => (
|
||||
<SpaceItem
|
||||
|
|
|
@ -16,7 +16,6 @@ limitations under the License.
|
|||
|
||||
import React, {
|
||||
createRef,
|
||||
MouseEvent,
|
||||
InputHTMLAttributes,
|
||||
LegacyRef,
|
||||
ComponentProps,
|
||||
|
@ -26,14 +25,15 @@ import classNames from "classnames";
|
|||
import { Room } from "matrix-js-sdk/src/models/room";
|
||||
|
||||
import RoomAvatar from "../avatars/RoomAvatar";
|
||||
import SpaceStore from "../../../stores/SpaceStore";
|
||||
import SpaceTreeLevelLayoutStore from "../../../stores/SpaceTreeLevelLayoutStore";
|
||||
import SpaceStore from "../../../stores/spaces/SpaceStore";
|
||||
import { SpaceKey } from "../../../stores/spaces";
|
||||
import SpaceTreeLevelLayoutStore from "../../../stores/spaces/SpaceTreeLevelLayoutStore";
|
||||
import NotificationBadge from "../rooms/NotificationBadge";
|
||||
import { _t } from "../../../languageHandler";
|
||||
import { ContextMenuTooltipButton } from "../../../accessibility/context_menu/ContextMenuTooltipButton";
|
||||
import { toRightOf, useContextMenu } from "../../structures/ContextMenu";
|
||||
import MatrixClientContext from "../../../contexts/MatrixClientContext";
|
||||
import AccessibleButton from "../elements/AccessibleButton";
|
||||
import AccessibleButton, { ButtonEvent } from "../elements/AccessibleButton";
|
||||
import { StaticNotificationState } from "../../../stores/notifications/StaticNotificationState";
|
||||
import { NotificationColor } from "../../../stores/notifications/NotificationColor";
|
||||
import { getKeyBindingsManager, RoomListAction } from "../../../KeyBindingsManager";
|
||||
|
@ -43,8 +43,9 @@ import AccessibleTooltipButton from "../elements/AccessibleTooltipButton";
|
|||
import { DraggableProvidedDragHandleProps } from "react-beautiful-dnd";
|
||||
import { useRovingTabIndex } from "../../../accessibility/RovingTabIndex";
|
||||
|
||||
interface IButtonProps extends Omit<ComponentProps<typeof AccessibleTooltipButton>, "title"> {
|
||||
interface IButtonProps extends Omit<ComponentProps<typeof AccessibleTooltipButton>, "title" | "onClick"> {
|
||||
space?: Room;
|
||||
spaceKey?: SpaceKey;
|
||||
className?: string;
|
||||
selected?: boolean;
|
||||
label: string;
|
||||
|
@ -53,14 +54,14 @@ interface IButtonProps extends Omit<ComponentProps<typeof AccessibleTooltipButto
|
|||
isNarrow?: boolean;
|
||||
avatarSize?: number;
|
||||
ContextMenuComponent?: ComponentType<ComponentProps<typeof SpaceContextMenu>>;
|
||||
onClick(ev: MouseEvent): void;
|
||||
onClick?(ev?: ButtonEvent): void;
|
||||
}
|
||||
|
||||
export const SpaceButton: React.FC<IButtonProps> = ({
|
||||
space,
|
||||
spaceKey,
|
||||
className,
|
||||
selected,
|
||||
onClick,
|
||||
label,
|
||||
contextMenuTooltip,
|
||||
notificationState,
|
||||
|
@ -88,7 +89,7 @@ export const SpaceButton: React.FC<IButtonProps> = ({
|
|||
|
||||
notifBadge = <div className="mx_SpacePanel_badgeContainer">
|
||||
<NotificationBadge
|
||||
onClick={() => SpaceStore.instance.setActiveRoomInSpace(space || null)}
|
||||
onClick={() => SpaceStore.instance.setActiveRoomInSpace(spaceKey ?? space.roomId)}
|
||||
forceCount={false}
|
||||
notification={notificationState}
|
||||
aria-label={ariaLabel}
|
||||
|
@ -116,7 +117,7 @@ export const SpaceButton: React.FC<IButtonProps> = ({
|
|||
mx_SpaceButton_narrow: isNarrow,
|
||||
})}
|
||||
title={label}
|
||||
onClick={onClick}
|
||||
onClick={spaceKey ? () => SpaceStore.instance.setActiveSpace(spaceKey) : props.onClick}
|
||||
onContextMenu={openMenu}
|
||||
forceHide={!isNarrow || menuDisplayed}
|
||||
inputRef={handle}
|
||||
|
@ -146,7 +147,7 @@ export const SpaceButton: React.FC<IButtonProps> = ({
|
|||
|
||||
interface IItemProps extends InputHTMLAttributes<HTMLLIElement> {
|
||||
space?: Room;
|
||||
activeSpaces: Room[];
|
||||
activeSpaces: SpaceKey[];
|
||||
isNested?: boolean;
|
||||
isPanelCollapsed?: boolean;
|
||||
onExpand?: Function;
|
||||
|
@ -258,7 +259,7 @@ export class SpaceItem extends React.PureComponent<IItemProps, IItemState> {
|
|||
private onClick = (ev: React.MouseEvent) => {
|
||||
ev.preventDefault();
|
||||
ev.stopPropagation();
|
||||
SpaceStore.instance.setActiveSpace(this.props.space);
|
||||
SpaceStore.instance.setActiveSpace(this.props.space.roomId);
|
||||
};
|
||||
|
||||
render() {
|
||||
|
@ -316,7 +317,7 @@ export class SpaceItem extends React.PureComponent<IItemProps, IItemState> {
|
|||
{...restDragHandleProps}
|
||||
space={space}
|
||||
className={isInvite ? "mx_SpaceButton_invite" : undefined}
|
||||
selected={activeSpaces.includes(space)}
|
||||
selected={activeSpaces.includes(space.roomId)}
|
||||
label={space.name}
|
||||
contextMenuTooltip={_t("Space options")}
|
||||
notificationState={notificationState}
|
||||
|
@ -337,7 +338,7 @@ export class SpaceItem extends React.PureComponent<IItemProps, IItemState> {
|
|||
|
||||
interface ITreeLevelProps {
|
||||
spaces: Room[];
|
||||
activeSpaces: Room[];
|
||||
activeSpaces: SpaceKey[];
|
||||
isNested?: boolean;
|
||||
parents: Set<string>;
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue