Implement more meta-spaces (#7077)
This commit is contained in:
parent
dadac386fe
commit
5ad3261cb2
55 changed files with 970 additions and 353 deletions
|
@ -276,6 +276,7 @@
|
|||
@import "./views/settings/tabs/user/_NotificationUserSettingsTab.scss";
|
||||
@import "./views/settings/tabs/user/_PreferencesUserSettingsTab.scss";
|
||||
@import "./views/settings/tabs/user/_SecurityUserSettingsTab.scss";
|
||||
@import "./views/settings/tabs/user/_SidebarUserSettingsTab.scss";
|
||||
@import "./views/settings/tabs/user/_VoiceUserSettingsTab.scss";
|
||||
@import "./views/spaces/_SpaceBasicSettings.scss";
|
||||
@import "./views/spaces/_SpaceChildrenPicker.scss";
|
||||
|
|
|
@ -189,15 +189,35 @@ $activeBorderColor: $secondary-content;
|
|||
}
|
||||
}
|
||||
|
||||
&.mx_SpaceButton_home .mx_SpaceButton_icon {
|
||||
background-color: #ffffff;
|
||||
&.mx_SpaceButton_home,
|
||||
&.mx_SpaceButton_favourites,
|
||||
&.mx_SpaceButton_people,
|
||||
&.mx_SpaceButton_orphans {
|
||||
.mx_SpaceButton_icon {
|
||||
background-color: #ffffff;
|
||||
|
||||
&::before {
|
||||
background-color: #3f3d3d;
|
||||
mask-image: url('$(res)/img/element-icons/home.svg');
|
||||
&::before {
|
||||
background-color: #3f3d3d;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
&.mx_SpaceButton_home .mx_SpaceButton_icon::before {
|
||||
mask-image: url('$(res)/img/element-icons/home.svg');
|
||||
}
|
||||
|
||||
&.mx_SpaceButton_favourites .mx_SpaceButton_icon::before {
|
||||
mask-image: url('$(res)/img/element-icons/roomlist/favorite.svg');
|
||||
}
|
||||
|
||||
&.mx_SpaceButton_people .mx_SpaceButton_icon::before {
|
||||
mask-image: url('$(res)/img/element-icons/room/members.svg');
|
||||
}
|
||||
|
||||
&.mx_SpaceButton_orphans .mx_SpaceButton_icon::before {
|
||||
mask-image: url('$(res)/img/element-icons/roomlist/hash-circle.svg');
|
||||
}
|
||||
|
||||
&.mx_SpaceButton_new .mx_SpaceButton_icon {
|
||||
background-color: $roomlist-button-bg-color;
|
||||
|
||||
|
|
|
@ -37,6 +37,10 @@ limitations under the License.
|
|||
mask-image: url('$(res)/img/element-icons/settings/preference.svg');
|
||||
}
|
||||
|
||||
.mx_UserSettingsDialog_sidebarIcon::before {
|
||||
mask-image: url('$(res)/img/element-icons/settings/sidebar.svg');
|
||||
}
|
||||
|
||||
.mx_UserSettingsDialog_securityIcon::before {
|
||||
mask-image: url('$(res)/img/element-icons/security.svg');
|
||||
}
|
||||
|
|
|
@ -0,0 +1,87 @@
|
|||
/*
|
||||
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.
|
||||
*/
|
||||
|
||||
.mx_SidebarUserSettingsTab {
|
||||
.mx_SidebarUserSettingsTab_subheading {
|
||||
font-size: $font-15px;
|
||||
line-height: $font-24px;
|
||||
color: $primary-content;
|
||||
margin-bottom: 4px;
|
||||
}
|
||||
|
||||
.mx_Checkbox {
|
||||
margin-top: 12px;
|
||||
font-size: $font-15px;
|
||||
line-height: $font-24px;
|
||||
color: $secondary-content;
|
||||
}
|
||||
|
||||
.mx_SidebarUserSettingsTab_checkboxMicrocopy {
|
||||
margin-bottom: 12px;
|
||||
margin-left: 24px;
|
||||
font-size: $font-15px;
|
||||
line-height: $font-24px;
|
||||
color: $secondary-content;
|
||||
}
|
||||
|
||||
.mx_SidebarUserSettingsTab_homeAllRoomsCheckbox {
|
||||
margin-left: 24px;
|
||||
|
||||
& + div {
|
||||
margin-left: 48px;
|
||||
}
|
||||
}
|
||||
|
||||
.mx_SidebarUserSettingsTab_homeCheckbox,
|
||||
.mx_SidebarUserSettingsTab_favouritesCheckbox,
|
||||
.mx_SidebarUserSettingsTab_peopleCheckbox,
|
||||
.mx_SidebarUserSettingsTab_orphansCheckbox {
|
||||
.mx_Checkbox_background + div {
|
||||
padding-left: 20px;
|
||||
position: relative;
|
||||
|
||||
&::before {
|
||||
background-color: $secondary-content;
|
||||
content: "";
|
||||
mask-repeat: no-repeat;
|
||||
mask-position: center;
|
||||
mask-size: contain;
|
||||
width: 16px;
|
||||
height: 16px;
|
||||
position: absolute;
|
||||
left: 0;
|
||||
top: 50%;
|
||||
transform: translateY(-50%);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.mx_SidebarUserSettingsTab_homeCheckbox .mx_Checkbox_background + div::before {
|
||||
mask-image: url('$(res)/img/element-icons/home.svg');
|
||||
}
|
||||
|
||||
.mx_SidebarUserSettingsTab_favouritesCheckbox .mx_Checkbox_background + div::before {
|
||||
mask-image: url('$(res)/img/element-icons/roomlist/favorite.svg');
|
||||
}
|
||||
|
||||
.mx_SidebarUserSettingsTab_peopleCheckbox .mx_Checkbox_background + div::before {
|
||||
mask-image: url('$(res)/img/element-icons/room/members.svg');
|
||||
}
|
||||
|
||||
.mx_SidebarUserSettingsTab_orphansCheckbox .mx_Checkbox_background + div::before {
|
||||
mask-image: url('$(res)/img/element-icons/roomlist/hash-circle.svg');
|
||||
}
|
||||
}
|
7
res/img/element-icons/settings/sidebar.svg
Normal file
7
res/img/element-icons/settings/sidebar.svg
Normal file
|
@ -0,0 +1,7 @@
|
|||
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<mask id="path-1-inside-1_152:29307" fill="white">
|
||||
<rect x="1" y="1" width="14" height="14" rx="1"/>
|
||||
</mask>
|
||||
<rect x="1" y="1" width="14" height="14" rx="1" stroke="#737D8C" stroke-width="4" mask="url(#path-1-inside-1_152:29307)"/>
|
||||
<line x1="7" y1="1" x2="7" y2="14" stroke="#737D8C" stroke-width="2"/>
|
||||
</svg>
|
After Width: | Height: | Size: 406 B |
2
src/@types/global.d.ts
vendored
2
src/@types/global.d.ts
vendored
|
@ -41,7 +41,7 @@ import UserActivity from "../UserActivity";
|
|||
import { ModalWidgetStore } from "../stores/ModalWidgetStore";
|
||||
import { WidgetLayoutStore } from "../stores/widgets/WidgetLayoutStore";
|
||||
import VoipUserMapper from "../VoipUserMapper";
|
||||
import { SpaceStoreClass } from "../stores/SpaceStore";
|
||||
import { SpaceStoreClass } from "../stores/spaces/SpaceStore";
|
||||
import TypingStore from "../stores/TypingStore";
|
||||
import { EventIndexPeg } from "../indexing/EventIndexPeg";
|
||||
import { VoiceRecordingStore } from "../stores/VoiceRecordingStore";
|
||||
|
|
|
@ -22,7 +22,7 @@ import { split } from "lodash";
|
|||
|
||||
import DMRoomMap from './utils/DMRoomMap';
|
||||
import { mediaFromMxc } from "./customisations/Media";
|
||||
import SpaceStore from "./stores/SpaceStore";
|
||||
import SpaceStore from "./stores/spaces/SpaceStore";
|
||||
|
||||
// Not to be used for BaseAvatar urls as that has similar default avatar fallback already
|
||||
export function avatarUrlForMember(
|
||||
|
|
|
@ -27,7 +27,7 @@ import NotifProvider from './NotifProvider';
|
|||
import { timeout } from "../utils/promise";
|
||||
import AutocompleteProvider, { ICommand } from "./AutocompleteProvider";
|
||||
import SpaceProvider from "./SpaceProvider";
|
||||
import SpaceStore from "../stores/SpaceStore";
|
||||
import SpaceStore from "../stores/spaces/SpaceStore";
|
||||
|
||||
export interface ISelectionRange {
|
||||
beginning?: boolean; // whether the selection is in the first block of the editor or not
|
||||
|
|
|
@ -28,7 +28,7 @@ import { PillCompletion } from './Components';
|
|||
import { makeRoomPermalink } from "../utils/permalinks/Permalinks";
|
||||
import { ICompletion, ISelectionRange } from "./Autocompleter";
|
||||
import RoomAvatar from '../components/views/avatars/RoomAvatar';
|
||||
import SpaceStore from "../stores/SpaceStore";
|
||||
import SpaceStore from "../stores/spaces/SpaceStore";
|
||||
|
||||
const ROOM_REGEX = /\B#\S*/g;
|
||||
|
||||
|
|
|
@ -17,7 +17,6 @@ limitations under the License.
|
|||
import * as React from "react";
|
||||
import { createRef } from "react";
|
||||
import classNames from "classnames";
|
||||
import { Room } from "matrix-js-sdk/src/models/room";
|
||||
|
||||
import dis from "../../dispatcher/dispatcher";
|
||||
import { _t } from "../../languageHandler";
|
||||
|
@ -37,10 +36,12 @@ import AccessibleTooltipButton from "../views/elements/AccessibleTooltipButton";
|
|||
import RoomListNumResults from "../views/rooms/RoomListNumResults";
|
||||
import LeftPanelWidget from "./LeftPanelWidget";
|
||||
import { replaceableComponent } from "../../utils/replaceableComponent";
|
||||
import SpaceStore, { UPDATE_SELECTED_SPACE } from "../../stores/SpaceStore";
|
||||
import SpaceStore from "../../stores/spaces/SpaceStore";
|
||||
import { SpaceKey, UPDATE_SELECTED_SPACE } from "../../stores/spaces";
|
||||
import { getKeyBindingsManager, RoomListAction } from "../../KeyBindingsManager";
|
||||
import UIStore from "../../stores/UIStore";
|
||||
import { findSiblingElement, IState as IRovingTabIndexState } from "../../accessibility/RovingTabIndex";
|
||||
import MatrixClientContext from "../../contexts/MatrixClientContext";
|
||||
|
||||
interface IProps {
|
||||
isMinimized: boolean;
|
||||
|
@ -49,7 +50,7 @@ interface IProps {
|
|||
|
||||
interface IState {
|
||||
showBreadcrumbs: boolean;
|
||||
activeSpace?: Room;
|
||||
activeSpace: SpaceKey;
|
||||
}
|
||||
|
||||
@replaceableComponent("structures.LeftPanel")
|
||||
|
@ -61,6 +62,9 @@ export default class LeftPanel extends React.Component<IProps, IState> {
|
|||
private focusedElement = null;
|
||||
private isDoingStickyHeaders = false;
|
||||
|
||||
static contextType = MatrixClientContext;
|
||||
public context!: React.ContextType<typeof MatrixClientContext>;
|
||||
|
||||
constructor(props: IProps) {
|
||||
super(props);
|
||||
|
||||
|
@ -98,7 +102,7 @@ export default class LeftPanel extends React.Component<IProps, IState> {
|
|||
}
|
||||
}
|
||||
|
||||
private updateActiveSpace = (activeSpace: Room) => {
|
||||
private updateActiveSpace = (activeSpace: SpaceKey) => {
|
||||
this.setState({ activeSpace });
|
||||
};
|
||||
|
||||
|
@ -343,6 +347,7 @@ export default class LeftPanel extends React.Component<IProps, IState> {
|
|||
/>;
|
||||
}
|
||||
|
||||
const space = this.state.activeSpace[0] === "!" ? this.context.getRoom(this.state.activeSpace) : null;
|
||||
return (
|
||||
<div
|
||||
className="mx_LeftPanel_filterContainer"
|
||||
|
@ -363,9 +368,7 @@ export default class LeftPanel extends React.Component<IProps, IState> {
|
|||
mx_LeftPanel_exploreButton_space: !!this.state.activeSpace,
|
||||
})}
|
||||
onClick={this.onExplore}
|
||||
title={this.state.activeSpace
|
||||
? _t("Explore %(spaceName)s", { spaceName: this.state.activeSpace.name })
|
||||
: _t("Explore rooms")}
|
||||
title={space ? _t("Explore %(spaceName)s", { spaceName: space.name }) : _t("Explore rooms")}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
|
|
|
@ -64,7 +64,7 @@ import MyGroups from "./MyGroups";
|
|||
import UserView from "./UserView";
|
||||
import GroupView from "./GroupView";
|
||||
import BackdropPanel from "./BackdropPanel";
|
||||
import SpaceStore from "../../stores/SpaceStore";
|
||||
import SpaceStore from "../../stores/spaces/SpaceStore";
|
||||
import classNames from 'classnames';
|
||||
import GroupFilterPanel from './GroupFilterPanel';
|
||||
import CustomRoomTagPanel from './CustomRoomTagPanel';
|
||||
|
|
|
@ -78,7 +78,7 @@ import { CommunityPrototypeStore } from "../../stores/CommunityPrototypeStore";
|
|||
import DialPadModal from "../views/voip/DialPadModal";
|
||||
import { showToast as showMobileGuideToast } from '../../toasts/MobileGuideToast';
|
||||
import { shouldUseLoginForWelcome } from "../../utils/pages";
|
||||
import SpaceStore from "../../stores/SpaceStore";
|
||||
import SpaceStore from "../../stores/spaces/SpaceStore";
|
||||
import { replaceableComponent } from "../../utils/replaceableComponent";
|
||||
import RoomListStore from "../../stores/room-list/RoomListStore";
|
||||
import { RoomUpdateCause } from "../../stores/room-list/models";
|
||||
|
@ -712,10 +712,10 @@ export default class MatrixChat extends React.PureComponent<IProps, IState> {
|
|||
break;
|
||||
}
|
||||
case Action.ViewRoomDirectory: {
|
||||
if (SpaceStore.instance.activeSpace) {
|
||||
if (SpaceStore.instance.activeSpace[0] === "!") {
|
||||
defaultDispatcher.dispatch({
|
||||
action: "view_room",
|
||||
room_id: SpaceStore.instance.activeSpace.roomId,
|
||||
room_id: SpaceStore.instance.activeSpace,
|
||||
});
|
||||
} else {
|
||||
Modal.createTrackedDialog('Room directory', '', RoomDirectory, {
|
||||
|
|
|
@ -50,7 +50,7 @@ import NotificationPanel from "./NotificationPanel";
|
|||
import ResizeNotifier from "../../utils/ResizeNotifier";
|
||||
import PinnedMessagesCard from "../views/right_panel/PinnedMessagesCard";
|
||||
import { throttle } from 'lodash';
|
||||
import SpaceStore from "../../stores/SpaceStore";
|
||||
import SpaceStore from "../../stores/spaces/SpaceStore";
|
||||
import { RoomPermalinkCreator } from '../../utils/permalinks/Permalinks';
|
||||
import { E2EStatus } from '../../utils/ShieldUtils';
|
||||
import { dispatchShowThreadsPanelEvent } from '../../dispatcher/dispatch-actions/threads';
|
||||
|
|
|
@ -28,7 +28,8 @@ import RoomListStore from "../../stores/room-list/RoomListStore";
|
|||
import { NameFilterCondition } from "../../stores/room-list/filters/NameFilterCondition";
|
||||
import { getKeyBindingsManager, RoomListAction } from "../../KeyBindingsManager";
|
||||
import { replaceableComponent } from "../../utils/replaceableComponent";
|
||||
import SpaceStore, { UPDATE_SELECTED_SPACE, UPDATE_TOP_LEVEL_SPACES } from "../../stores/SpaceStore";
|
||||
import SpaceStore from "../../stores/spaces/SpaceStore";
|
||||
import { UPDATE_SELECTED_SPACE, UPDATE_TOP_LEVEL_SPACES } from "../../stores/spaces";
|
||||
|
||||
interface IProps {
|
||||
isMinimized: boolean;
|
||||
|
|
|
@ -88,7 +88,7 @@ import RoomStatusBar from "./RoomStatusBar";
|
|||
import MessageComposer from '../views/rooms/MessageComposer';
|
||||
import JumpToBottomButton from "../views/rooms/JumpToBottomButton";
|
||||
import TopUnreadMessagesBar from "../views/rooms/TopUnreadMessagesBar";
|
||||
import SpaceStore from "../../stores/SpaceStore";
|
||||
import SpaceStore from "../../stores/spaces/SpaceStore";
|
||||
|
||||
import { logger } from "matrix-js-sdk/src/logger";
|
||||
import { EventTimeline } from 'matrix-js-sdk/src/models/event-timeline';
|
||||
|
|
|
@ -49,7 +49,7 @@ import { mediaFromMxc } from "../../customisations/Media";
|
|||
import InfoTooltip from "../views/elements/InfoTooltip";
|
||||
import TextWithTooltip from "../views/elements/TextWithTooltip";
|
||||
import { useStateToggle } from "../../hooks/useStateToggle";
|
||||
import { getChildOrder } from "../../stores/SpaceStore";
|
||||
import { getChildOrder } from "../../stores/spaces/SpaceStore";
|
||||
import AccessibleTooltipButton from "../views/elements/AccessibleTooltipButton";
|
||||
import { linkifyElement } from "../../HtmlUtils";
|
||||
import { useDispatcher } from "../../hooks/useDispatcher";
|
||||
|
|
|
@ -57,7 +57,7 @@ import {
|
|||
} from "../../utils/space";
|
||||
import SpaceHierarchy, { joinRoom, showRoom } from "./SpaceHierarchy";
|
||||
import MemberAvatar from "../views/avatars/MemberAvatar";
|
||||
import SpaceStore from "../../stores/SpaceStore";
|
||||
import SpaceStore from "../../stores/spaces/SpaceStore";
|
||||
import FacePile from "../views/elements/FacePile";
|
||||
import {
|
||||
AddExistingToSpace,
|
||||
|
|
|
@ -54,7 +54,8 @@ import EditCommunityPrototypeDialog from "../views/dialogs/EditCommunityPrototyp
|
|||
import { UIFeature } from "../../settings/UIFeature";
|
||||
import HostSignupAction from "./HostSignupAction";
|
||||
import { IHostSignupConfig } from "../views/dialogs/HostSignupDialogTypes";
|
||||
import SpaceStore, { UPDATE_SELECTED_SPACE } from "../../stores/SpaceStore";
|
||||
import SpaceStore from "../../stores/spaces/SpaceStore";
|
||||
import { UPDATE_SELECTED_SPACE } from "../../stores/spaces";
|
||||
import RoomName from "../views/elements/RoomName";
|
||||
import { replaceableComponent } from "../../utils/replaceableComponent";
|
||||
import InlineSpinner from "../views/elements/InlineSpinner";
|
||||
|
@ -90,6 +91,7 @@ export default class UserMenu extends React.Component<IProps, IState> {
|
|||
isDarkTheme: this.isUserOnDarkTheme(),
|
||||
isHighContrast: this.isUserOnHighContrastTheme(),
|
||||
pendingRoomJoin: new Set<string>(),
|
||||
selectedSpace: SpaceStore.instance.activeSpaceRoom,
|
||||
};
|
||||
|
||||
OwnProfileStore.instance.on(UPDATE_EVENT, this.onProfileUpdate);
|
||||
|
@ -162,8 +164,10 @@ export default class UserMenu extends React.Component<IProps, IState> {
|
|||
this.forceUpdate();
|
||||
};
|
||||
|
||||
private onSelectedSpaceUpdate = async (selectedSpace?: Room) => {
|
||||
this.setState({ selectedSpace });
|
||||
private onSelectedSpaceUpdate = async () => {
|
||||
this.setState({
|
||||
selectedSpace: SpaceStore.instance.activeSpaceRoom,
|
||||
});
|
||||
};
|
||||
|
||||
private onThemeChanged = () => {
|
||||
|
|
|
@ -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>;
|
||||
}
|
||||
|
|
|
@ -40,7 +40,7 @@ import GroupStore from "./stores/GroupStore";
|
|||
import CountlyAnalytics from "./CountlyAnalytics";
|
||||
import { isJoinedOrNearlyJoined } from "./utils/membership";
|
||||
import { VIRTUAL_ROOM_EVENT_TYPE } from "./CallHandler";
|
||||
import SpaceStore from "./stores/SpaceStore";
|
||||
import SpaceStore from "./stores/spaces/SpaceStore";
|
||||
import { makeSpaceParentEvent } from "./utils/space";
|
||||
import { Action } from "./dispatcher/actions";
|
||||
import ErrorDialog from "./components/views/dialogs/ErrorDialog";
|
||||
|
|
|
@ -830,6 +830,7 @@
|
|||
"Polls (under active development)": "Polls (under active development)",
|
||||
"Show info about bridges in room settings": "Show info about bridges in room settings",
|
||||
"New layout switcher (with message bubbles)": "New layout switcher (with message bubbles)",
|
||||
"Meta Spaces": "Meta Spaces",
|
||||
"Don't send read receipts": "Don't send read receipts",
|
||||
"Font size": "Font size",
|
||||
"Use custom size": "Use custom size",
|
||||
|
@ -1064,6 +1065,9 @@
|
|||
"Show all rooms": "Show all rooms",
|
||||
"All rooms": "All rooms",
|
||||
"Options": "Options",
|
||||
"Favourites": "Favourites",
|
||||
"People": "People",
|
||||
"Other rooms": "Other rooms",
|
||||
"Spaces": "Spaces",
|
||||
"Expand space panel": "Expand space panel",
|
||||
"Collapse space panel": "Collapse space panel",
|
||||
|
@ -1426,6 +1430,16 @@
|
|||
"Learn more about how we use analytics.": "Learn more about how we use analytics.",
|
||||
"Where you're signed in": "Where you're signed in",
|
||||
"Manage your signed-in devices below. A device's name is visible to people you communicate with.": "Manage your signed-in devices below. A device's name is visible to people you communicate with.",
|
||||
"Sidebar": "Sidebar",
|
||||
"Spaces are ways to group rooms and people.": "Spaces are ways to group rooms and people.",
|
||||
"Spaces to show": "Spaces to show",
|
||||
"Along with the spaces you're in, you can use some pre-built ones too.": "Along with the spaces you're in, you can use some pre-built ones too.",
|
||||
"Home is useful for getting an overview of everything.": "Home is useful for getting an overview of everything.",
|
||||
"Show all your rooms in Home, even if they're in a space.": "Show all your rooms in Home, even if they're in a space.",
|
||||
"Automatically group all your favourite rooms and people together in one place.": "Automatically group all your favourite rooms and people together in one place.",
|
||||
"Automatically group all your people together in one place.": "Automatically group all your people together in one place.",
|
||||
"Rooms outside of a space": "Rooms outside of a space",
|
||||
"Automatically group all your rooms that aren't part of a space in one place.": "Automatically group all your rooms that aren't part of a space in one place.",
|
||||
"Default Device": "Default Device",
|
||||
"No media permissions": "No media permissions",
|
||||
"You may need to manually permit %(brand)s to access your microphone/webcam": "You may need to manually permit %(brand)s to access your microphone/webcam",
|
||||
|
@ -1670,8 +1684,6 @@
|
|||
"Show Widgets": "Show Widgets",
|
||||
"Search": "Search",
|
||||
"Invites": "Invites",
|
||||
"Favourites": "Favourites",
|
||||
"People": "People",
|
||||
"Start chat": "Start chat",
|
||||
"Rooms": "Rooms",
|
||||
"Add room": "Add room",
|
||||
|
|
|
@ -42,6 +42,7 @@ import ReducedMotionController from './controllers/ReducedMotionController';
|
|||
import IncompatibleController from "./controllers/IncompatibleController";
|
||||
import PseudonymousAnalyticsController from './controllers/PseudonymousAnalyticsController';
|
||||
import NewLayoutSwitcherController from './controllers/NewLayoutSwitcherController';
|
||||
import { MetaSpace } from "../stores/spaces";
|
||||
|
||||
// These are just a bunch of helper arrays to avoid copy/pasting a bunch of times
|
||||
const LEVELS_ROOM_SETTINGS = [
|
||||
|
@ -283,6 +284,16 @@ export const SETTINGS: {[setting: string]: ISetting} = {
|
|||
default: false,
|
||||
controller: new NewLayoutSwitcherController(),
|
||||
},
|
||||
"feature_spaces_metaspaces": {
|
||||
isFeature: true,
|
||||
supportedLevels: LEVELS_FEATURE,
|
||||
displayName: _td("Meta Spaces"),
|
||||
default: false,
|
||||
controller: new OrderedMultiController([
|
||||
new IncompatibleController("showCommunitiesInsteadOfSpaces"),
|
||||
new ReloadOnChangeController(),
|
||||
]),
|
||||
},
|
||||
"RoomList.backgroundImage": {
|
||||
supportedLevels: LEVELS_ACCOUNT_SETTINGS,
|
||||
default: null,
|
||||
|
@ -755,6 +766,15 @@ export const SETTINGS: {[setting: string]: ISetting} = {
|
|||
default: false,
|
||||
controller: new IncompatibleController("showCommunitiesInsteadOfSpaces", null),
|
||||
},
|
||||
"Spaces.enabledMetaSpaces": {
|
||||
supportedLevels: LEVELS_ACCOUNT_SETTINGS,
|
||||
default: {
|
||||
[MetaSpace.Home]: true,
|
||||
},
|
||||
controller: new IncompatibleController("feature_spaces_metaspaces", {
|
||||
[MetaSpace.Home]: true,
|
||||
}, false),
|
||||
},
|
||||
"showCommunitiesInsteadOfSpaces": {
|
||||
displayName: _td("Display Communities instead of Spaces"),
|
||||
description: _td("Temporarily show communities instead of Spaces for this session. " +
|
||||
|
|
|
@ -22,7 +22,7 @@ import defaultDispatcher from "../dispatcher/dispatcher";
|
|||
import { arrayHasDiff } from "../utils/arrays";
|
||||
import { isNullOrUndefined } from "matrix-js-sdk/src/utils";
|
||||
import { SettingLevel } from "../settings/SettingLevel";
|
||||
import SpaceStore from "./SpaceStore";
|
||||
import SpaceStore from "./spaces/SpaceStore";
|
||||
import { Action } from "../dispatcher/actions";
|
||||
import { SettingUpdatedPayload } from "../dispatcher/payloads/SettingUpdatedPayload";
|
||||
|
||||
|
|
|
@ -35,7 +35,7 @@ import { NameFilterCondition } from "./filters/NameFilterCondition";
|
|||
import { RoomNotificationStateStore } from "../notifications/RoomNotificationStateStore";
|
||||
import { VisibilityProvider } from "./filters/VisibilityProvider";
|
||||
import { SpaceWatcher } from "./SpaceWatcher";
|
||||
import SpaceStore from "../SpaceStore";
|
||||
import SpaceStore from "../spaces/SpaceStore";
|
||||
import { Action } from "../../dispatcher/actions";
|
||||
import { SettingUpdatedPayload } from "../../dispatcher/payloads/SettingUpdatedPayload";
|
||||
|
||||
|
|
|
@ -14,11 +14,10 @@ See the License for the specific language governing permissions and
|
|||
limitations under the License.
|
||||
*/
|
||||
|
||||
import { Room } from "matrix-js-sdk/src/models/room";
|
||||
|
||||
import { RoomListStoreClass } from "./RoomListStore";
|
||||
import { SpaceFilterCondition } from "./filters/SpaceFilterCondition";
|
||||
import SpaceStore, { UPDATE_HOME_BEHAVIOUR, UPDATE_SELECTED_SPACE } from "../SpaceStore";
|
||||
import SpaceStore from "../spaces/SpaceStore";
|
||||
import { MetaSpace, SpaceKey, UPDATE_HOME_BEHAVIOUR, UPDATE_SELECTED_SPACE } from "../spaces";
|
||||
|
||||
/**
|
||||
* Watches for changes in spaces to manage the filter on the provided RoomListStore
|
||||
|
@ -26,11 +25,11 @@ import SpaceStore, { UPDATE_HOME_BEHAVIOUR, UPDATE_SELECTED_SPACE } from "../Spa
|
|||
export class SpaceWatcher {
|
||||
private readonly filter = new SpaceFilterCondition();
|
||||
// we track these separately to the SpaceStore as we need to observe transitions
|
||||
private activeSpace: Room = SpaceStore.instance.activeSpace;
|
||||
private activeSpace: SpaceKey = SpaceStore.instance.activeSpace;
|
||||
private allRoomsInHome: boolean = SpaceStore.instance.allRoomsInHome;
|
||||
|
||||
constructor(private store: RoomListStoreClass) {
|
||||
if (!this.allRoomsInHome || this.activeSpace) {
|
||||
if (SpaceWatcher.needsFilter(this.activeSpace, this.allRoomsInHome)) {
|
||||
this.updateFilter();
|
||||
store.addFilter(this.filter);
|
||||
}
|
||||
|
@ -38,21 +37,26 @@ export class SpaceWatcher {
|
|||
SpaceStore.instance.on(UPDATE_HOME_BEHAVIOUR, this.onHomeBehaviourUpdated);
|
||||
}
|
||||
|
||||
private onSelectedSpaceUpdated = (activeSpace?: Room, allRoomsInHome = this.allRoomsInHome) => {
|
||||
private static needsFilter(spaceKey: SpaceKey, allRoomsInHome: boolean): boolean {
|
||||
return !(spaceKey === MetaSpace.Home && allRoomsInHome);
|
||||
}
|
||||
|
||||
private onSelectedSpaceUpdated = (activeSpace: SpaceKey, allRoomsInHome = this.allRoomsInHome) => {
|
||||
if (activeSpace === this.activeSpace && allRoomsInHome === this.allRoomsInHome) return; // nop
|
||||
|
||||
const oldActiveSpace = this.activeSpace;
|
||||
const oldAllRoomsInHome = this.allRoomsInHome;
|
||||
const neededFilter = SpaceWatcher.needsFilter(this.activeSpace, this.allRoomsInHome);
|
||||
const needsFilter = SpaceWatcher.needsFilter(activeSpace, allRoomsInHome);
|
||||
|
||||
this.activeSpace = activeSpace;
|
||||
this.allRoomsInHome = allRoomsInHome;
|
||||
|
||||
if (activeSpace || !allRoomsInHome) {
|
||||
if (needsFilter) {
|
||||
this.updateFilter();
|
||||
}
|
||||
|
||||
if (oldAllRoomsInHome && !oldActiveSpace) {
|
||||
if (!neededFilter && needsFilter) {
|
||||
this.store.addFilter(this.filter);
|
||||
} else if (allRoomsInHome && !activeSpace) {
|
||||
} else if (neededFilter && !needsFilter) {
|
||||
this.store.removeFilter(this.filter);
|
||||
}
|
||||
};
|
||||
|
@ -62,8 +66,8 @@ export class SpaceWatcher {
|
|||
};
|
||||
|
||||
private updateFilter = () => {
|
||||
if (this.activeSpace) {
|
||||
SpaceStore.instance.traverseSpace(this.activeSpace.roomId, roomId => {
|
||||
if (this.activeSpace[0] === "!") {
|
||||
SpaceStore.instance.traverseSpace(this.activeSpace, roomId => {
|
||||
this.store.matrixClient?.getRoom(roomId)?.loadMembersIfNeeded();
|
||||
});
|
||||
}
|
||||
|
|
|
@ -34,7 +34,7 @@ import { EffectiveMembership, getEffectiveMembership, splitRoomsByMembership } f
|
|||
import { OrderingAlgorithm } from "./list-ordering/OrderingAlgorithm";
|
||||
import { getListAlgorithmInstance } from "./list-ordering";
|
||||
import { VisibilityProvider } from "../filters/VisibilityProvider";
|
||||
import SpaceStore from "../../SpaceStore";
|
||||
import SpaceStore from "../../spaces/SpaceStore";
|
||||
|
||||
import { logger } from "matrix-js-sdk/src/logger";
|
||||
|
||||
|
|
|
@ -19,7 +19,8 @@ import { Room } from "matrix-js-sdk/src/models/room";
|
|||
|
||||
import { FILTER_CHANGED, FilterKind, IFilterCondition } from "./IFilterCondition";
|
||||
import { IDestroyable } from "../../../utils/IDestroyable";
|
||||
import SpaceStore, { HOME_SPACE } from "../../SpaceStore";
|
||||
import SpaceStore from "../../spaces/SpaceStore";
|
||||
import { MetaSpace, SpaceKey } from "../../spaces";
|
||||
import { setHasDiff } from "../../../utils/sets";
|
||||
|
||||
/**
|
||||
|
@ -30,7 +31,7 @@ import { setHasDiff } from "../../../utils/sets";
|
|||
*/
|
||||
export class SpaceFilterCondition extends EventEmitter implements IFilterCondition, IDestroyable {
|
||||
private roomIds = new Set<string>();
|
||||
private space: Room = null;
|
||||
private space: SpaceKey = MetaSpace.Home;
|
||||
|
||||
public get kind(): FilterKind {
|
||||
return FilterKind.Prefilter;
|
||||
|
@ -55,15 +56,13 @@ export class SpaceFilterCondition extends EventEmitter implements IFilterConditi
|
|||
}
|
||||
};
|
||||
|
||||
private getSpaceEventKey = (space: Room | null) => space ? space.roomId : HOME_SPACE;
|
||||
|
||||
public updateSpace(space: Room) {
|
||||
SpaceStore.instance.off(this.getSpaceEventKey(this.space), this.onStoreUpdate);
|
||||
SpaceStore.instance.on(this.getSpaceEventKey(this.space = space), this.onStoreUpdate);
|
||||
public updateSpace(space: SpaceKey) {
|
||||
SpaceStore.instance.off(this.space, this.onStoreUpdate);
|
||||
SpaceStore.instance.on(this.space = space, this.onStoreUpdate);
|
||||
this.onStoreUpdate(); // initial update from the change to the space
|
||||
}
|
||||
|
||||
public destroy(): void {
|
||||
SpaceStore.instance.off(this.getSpaceEventKey(this.space), this.onStoreUpdate);
|
||||
SpaceStore.instance.off(this.space, this.onStoreUpdate);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -18,7 +18,7 @@ import { Room } from "matrix-js-sdk/src/models/room";
|
|||
import CallHandler from "../../../CallHandler";
|
||||
import { RoomListCustomisations } from "../../../customisations/RoomList";
|
||||
import VoipUserMapper from "../../../VoipUserMapper";
|
||||
import SpaceStore from "../../SpaceStore";
|
||||
import SpaceStore from "../../spaces/SpaceStore";
|
||||
|
||||
export class VisibilityProvider {
|
||||
private static internalInstance: VisibilityProvider;
|
||||
|
|
|
@ -18,56 +18,51 @@ import { ListIteratee, Many, sortBy, throttle } from "lodash";
|
|||
import { EventType, RoomType } from "matrix-js-sdk/src/@types/event";
|
||||
import { Room } from "matrix-js-sdk/src/models/room";
|
||||
import { MatrixEvent } from "matrix-js-sdk/src/models/event";
|
||||
import { IHierarchyRoom } from "matrix-js-sdk/src/@types/spaces";
|
||||
import { IRoomCapability } from "matrix-js-sdk/src/client";
|
||||
|
||||
import { AsyncStoreWithClient } from "./AsyncStoreWithClient";
|
||||
import defaultDispatcher from "../dispatcher/dispatcher";
|
||||
import { ActionPayload } from "../dispatcher/payloads";
|
||||
import RoomListStore from "./room-list/RoomListStore";
|
||||
import SettingsStore from "../settings/SettingsStore";
|
||||
import DMRoomMap from "../utils/DMRoomMap";
|
||||
import { FetchRoomFn } from "./notifications/ListNotificationState";
|
||||
import { SpaceNotificationState } from "./notifications/SpaceNotificationState";
|
||||
import { RoomNotificationStateStore } from "./notifications/RoomNotificationStateStore";
|
||||
import { DefaultTagID } from "./room-list/models";
|
||||
import { EnhancedMap, mapDiff } from "../utils/maps";
|
||||
import { setHasDiff } from "../utils/sets";
|
||||
import RoomViewStore from "./RoomViewStore";
|
||||
import { Action } from "../dispatcher/actions";
|
||||
import { arrayHasDiff, arrayHasOrderChange } from "../utils/arrays";
|
||||
import { objectDiff } from "../utils/objects";
|
||||
import { reorderLexicographically } from "../utils/stringOrderField";
|
||||
import { TAG_ORDER } from "../components/views/rooms/RoomList";
|
||||
import { SettingUpdatedPayload } from "../dispatcher/payloads/SettingUpdatedPayload";
|
||||
|
||||
import { logger } from "matrix-js-sdk/src/logger";
|
||||
|
||||
type SpaceKey = string | symbol;
|
||||
import { AsyncStoreWithClient } from "../AsyncStoreWithClient";
|
||||
import defaultDispatcher from "../../dispatcher/dispatcher";
|
||||
import { ActionPayload } from "../../dispatcher/payloads";
|
||||
import RoomListStore from "../room-list/RoomListStore";
|
||||
import SettingsStore from "../../settings/SettingsStore";
|
||||
import DMRoomMap from "../../utils/DMRoomMap";
|
||||
import { FetchRoomFn } from "../notifications/ListNotificationState";
|
||||
import { SpaceNotificationState } from "../notifications/SpaceNotificationState";
|
||||
import { RoomNotificationStateStore } from "../notifications/RoomNotificationStateStore";
|
||||
import { DefaultTagID } from "../room-list/models";
|
||||
import { EnhancedMap, mapDiff } from "../../utils/maps";
|
||||
import { setHasDiff } from "../../utils/sets";
|
||||
import RoomViewStore from "../RoomViewStore";
|
||||
import { Action } from "../../dispatcher/actions";
|
||||
import { arrayHasDiff, arrayHasOrderChange } from "../../utils/arrays";
|
||||
import { objectDiff } from "../../utils/objects";
|
||||
import { reorderLexicographically } from "../../utils/stringOrderField";
|
||||
import { TAG_ORDER } from "../../components/views/rooms/RoomList";
|
||||
import { SettingUpdatedPayload } from "../../dispatcher/payloads/SettingUpdatedPayload";
|
||||
import {
|
||||
ISuggestedRoom,
|
||||
MetaSpace,
|
||||
SpaceKey,
|
||||
UPDATE_HOME_BEHAVIOUR,
|
||||
UPDATE_INVITED_SPACES,
|
||||
UPDATE_SELECTED_SPACE,
|
||||
UPDATE_SUGGESTED_ROOMS,
|
||||
UPDATE_TOP_LEVEL_SPACES,
|
||||
} from ".";
|
||||
|
||||
interface IState {}
|
||||
|
||||
const ACTIVE_SPACE_LS_KEY = "mx_active_space";
|
||||
|
||||
export const HOME_SPACE = Symbol("home-space");
|
||||
export const SUGGESTED_ROOMS = Symbol("suggested-rooms");
|
||||
|
||||
export const UPDATE_TOP_LEVEL_SPACES = Symbol("top-level-spaces");
|
||||
export const UPDATE_INVITED_SPACES = Symbol("invited-spaces");
|
||||
export const UPDATE_SELECTED_SPACE = Symbol("selected-space");
|
||||
export const UPDATE_HOME_BEHAVIOUR = Symbol("home-behaviour");
|
||||
// Space Room ID/HOME_SPACE will be emitted when a Space's children change
|
||||
|
||||
export interface ISuggestedRoom extends IHierarchyRoom {
|
||||
viaServers: string[];
|
||||
}
|
||||
const metaSpaceOrder: MetaSpace[] = [MetaSpace.Home, MetaSpace.Favourites, MetaSpace.People, MetaSpace.Orphans];
|
||||
|
||||
const MAX_SUGGESTED_ROOMS = 20;
|
||||
|
||||
// This setting causes the page to reload and can be costly if read frequently, so read it here only
|
||||
const spacesEnabled = !SettingsStore.getValue("showCommunitiesInsteadOfSpaces");
|
||||
|
||||
const getSpaceContextKey = (space?: Room) => `mx_space_context_${space?.roomId || "HOME_SPACE"}`;
|
||||
const getSpaceContextKey = (space: SpaceKey) => `mx_space_context_${space}`;
|
||||
|
||||
const partitionSpacesAndRooms = (arr: Room[]): [Room[], Room[]] => { // [spaces, rooms]
|
||||
return arr.reduce((result, room: Room) => {
|
||||
|
@ -105,30 +100,41 @@ export class SpaceStoreClass extends AsyncStoreWithClient<IState> {
|
|||
private notificationStateMap = new Map<SpaceKey, SpaceNotificationState>();
|
||||
// Map from space key to Set of room IDs that should be shown as part of that space's filter
|
||||
private spaceFilteredRooms = new Map<SpaceKey, Set<string>>();
|
||||
// The space currently selected in the Space Panel - if null then Home is selected
|
||||
private _activeSpace?: Room = null;
|
||||
// The space currently selected in the Space Panel
|
||||
private _activeSpace?: SpaceKey = MetaSpace.Home; // set properly by onReady
|
||||
private _suggestedRooms: ISuggestedRoom[] = [];
|
||||
private _invitedSpaces = new Set<Room>();
|
||||
private spaceOrderLocalEchoMap = new Map<string, string>();
|
||||
private _restrictedJoinRuleSupport?: IRoomCapability;
|
||||
private _allRoomsInHome: boolean = SettingsStore.getValue("Spaces.allRoomsInHome");
|
||||
private _enabledMetaSpaces: MetaSpace[] = []; // set by onReady
|
||||
|
||||
constructor() {
|
||||
super(defaultDispatcher, {});
|
||||
|
||||
SettingsStore.monitorSetting("Spaces.allRoomsInHome", null);
|
||||
SettingsStore.monitorSetting("Spaces.enabledMetaSpaces", null);
|
||||
}
|
||||
|
||||
public get invitedSpaces(): Room[] {
|
||||
return Array.from(this._invitedSpaces);
|
||||
}
|
||||
|
||||
public get enabledMetaSpaces(): MetaSpace[] {
|
||||
return this._enabledMetaSpaces;
|
||||
}
|
||||
|
||||
public get spacePanelSpaces(): Room[] {
|
||||
return this.rootSpaces;
|
||||
}
|
||||
|
||||
public get activeSpace(): Room | null {
|
||||
return this._activeSpace || null;
|
||||
public get activeSpace(): SpaceKey {
|
||||
return this._activeSpace;
|
||||
}
|
||||
|
||||
public get activeSpaceRoom(): Room | null {
|
||||
if (this._activeSpace[0] !== "!") return null;
|
||||
return this.matrixClient?.getRoom(this._activeSpace);
|
||||
}
|
||||
|
||||
public get suggestedRooms(): ISuggestedRoom[] {
|
||||
|
@ -139,12 +145,12 @@ export class SpaceStoreClass extends AsyncStoreWithClient<IState> {
|
|||
return this._allRoomsInHome;
|
||||
}
|
||||
|
||||
public setActiveRoomInSpace(space: Room | null): void {
|
||||
if (space && !space.isSpaceRoom()) return;
|
||||
public setActiveRoomInSpace(space: SpaceKey): void {
|
||||
if (space[0] === "!" && !this.matrixClient?.getRoom(space)?.isSpaceRoom()) return;
|
||||
if (space !== this.activeSpace) this.setActiveSpace(space);
|
||||
|
||||
if (space) {
|
||||
const roomId = this.getNotificationState(space.roomId).getFirstRoomWithNotifications();
|
||||
const roomId = this.getNotificationState(space).getFirstRoomWithNotifications();
|
||||
defaultDispatcher.dispatch({
|
||||
action: "view_room",
|
||||
room_id: roomId,
|
||||
|
@ -184,12 +190,20 @@ export class SpaceStoreClass extends AsyncStoreWithClient<IState> {
|
|||
* @param contextSwitch whether to switch the user's context,
|
||||
* should not be done when the space switch is done implicitly due to another event like switching room.
|
||||
*/
|
||||
public setActiveSpace(space: Room | null, contextSwitch = true) {
|
||||
if (!this.matrixClient || space === this.activeSpace || (space && !space.isSpaceRoom())) return;
|
||||
public setActiveSpace(space: SpaceKey, contextSwitch = true) {
|
||||
if (!space || !this.matrixClient || space === this.activeSpace) return;
|
||||
|
||||
let cliSpace: Room;
|
||||
if (space[0] === "!") {
|
||||
cliSpace = this.matrixClient.getRoom(space);
|
||||
if (!cliSpace?.isSpaceRoom()) return;
|
||||
} else if (!this.enabledMetaSpaces.includes(space as MetaSpace)) {
|
||||
return;
|
||||
}
|
||||
|
||||
this._activeSpace = space;
|
||||
this.emit(UPDATE_SELECTED_SPACE, this.activeSpace);
|
||||
this.emit(SUGGESTED_ROOMS, this._suggestedRooms = []);
|
||||
this.emit(UPDATE_SUGGESTED_ROOMS, this._suggestedRooms = []);
|
||||
|
||||
if (contextSwitch) {
|
||||
// view last selected room from space
|
||||
|
@ -198,7 +212,7 @@ export class SpaceStoreClass extends AsyncStoreWithClient<IState> {
|
|||
// if the space being selected is an invite then always view that invite
|
||||
// else if the last viewed room in this space is joined then view that
|
||||
// else view space home or home depending on what is being clicked on
|
||||
if (space?.getMyMembership() !== "invite" &&
|
||||
if (cliSpace?.getMyMembership() !== "invite" &&
|
||||
this.matrixClient.getRoom(roomId)?.getMyMembership() === "join" &&
|
||||
this.getSpaceFilteredRoomIds(space).has(roomId)
|
||||
) {
|
||||
|
@ -207,10 +221,10 @@ export class SpaceStoreClass extends AsyncStoreWithClient<IState> {
|
|||
room_id: roomId,
|
||||
context_switch: true,
|
||||
});
|
||||
} else if (space) {
|
||||
} else if (cliSpace) {
|
||||
defaultDispatcher.dispatch({
|
||||
action: "view_room",
|
||||
room_id: space.roomId,
|
||||
room_id: space,
|
||||
context_switch: true,
|
||||
});
|
||||
} else {
|
||||
|
@ -221,22 +235,18 @@ export class SpaceStoreClass extends AsyncStoreWithClient<IState> {
|
|||
}
|
||||
|
||||
// persist space selected
|
||||
if (space) {
|
||||
window.localStorage.setItem(ACTIVE_SPACE_LS_KEY, space.roomId);
|
||||
} else {
|
||||
window.localStorage.removeItem(ACTIVE_SPACE_LS_KEY);
|
||||
}
|
||||
window.localStorage.setItem(ACTIVE_SPACE_LS_KEY, space);
|
||||
|
||||
if (space) {
|
||||
this.loadSuggestedRooms(space);
|
||||
if (cliSpace) {
|
||||
this.loadSuggestedRooms(cliSpace);
|
||||
}
|
||||
}
|
||||
|
||||
private async loadSuggestedRooms(space: Room): Promise<void> {
|
||||
const suggestedRooms = await this.fetchSuggestedRooms(space);
|
||||
if (this._activeSpace === space) {
|
||||
if (this._activeSpace === space.roomId) {
|
||||
this._suggestedRooms = suggestedRooms;
|
||||
this.emit(SUGGESTED_ROOMS, this._suggestedRooms);
|
||||
this.emit(UPDATE_SUGGESTED_ROOMS, this._suggestedRooms);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -337,11 +347,11 @@ export class SpaceStoreClass extends AsyncStoreWithClient<IState> {
|
|||
return this.parentMap.get(roomId) || new Set();
|
||||
}
|
||||
|
||||
public getSpaceFilteredRoomIds = (space: Room | null): Set<string> => {
|
||||
if (!space && this.allRoomsInHome) {
|
||||
public getSpaceFilteredRoomIds = (space: SpaceKey): Set<string> => {
|
||||
if (space === MetaSpace.Home && this.allRoomsInHome) {
|
||||
return new Set(this.matrixClient.getVisibleRooms().map(r => r.roomId));
|
||||
}
|
||||
return this.spaceFilteredRooms.get(space?.roomId || HOME_SPACE) || new Set();
|
||||
return this.spaceFilteredRooms.get(space) || new Set();
|
||||
};
|
||||
|
||||
private rebuild = throttle(() => {
|
||||
|
@ -420,12 +430,12 @@ export class SpaceStoreClass extends AsyncStoreWithClient<IState> {
|
|||
this.parentMap = backrefs;
|
||||
|
||||
// if the currently selected space no longer exists, remove its selection
|
||||
if (this._activeSpace && detachedNodes.has(this._activeSpace)) {
|
||||
this.setActiveSpace(null, false);
|
||||
if (this._activeSpace[0] === "!" && detachedNodes.has(this.matrixClient.getRoom(this._activeSpace))) {
|
||||
this.goToFirstSpace();
|
||||
}
|
||||
|
||||
this.onRoomsUpdate(); // TODO only do this if a change has happened
|
||||
this.emit(UPDATE_TOP_LEVEL_SPACES, this.spacePanelSpaces);
|
||||
this.emit(UPDATE_TOP_LEVEL_SPACES, this.spacePanelSpaces, this.enabledMetaSpaces);
|
||||
|
||||
// build initial state of invited spaces as we would have missed the emitted events about the room at launch
|
||||
this._invitedSpaces = new Set(this.sortRootSpaces(invitedSpaces));
|
||||
|
@ -440,19 +450,22 @@ export class SpaceStoreClass extends AsyncStoreWithClient<IState> {
|
|||
if (this.allRoomsInHome) return true;
|
||||
if (room.isSpaceRoom()) return false;
|
||||
return !this.parentMap.get(room.roomId)?.size // put all orphaned rooms in the Home Space
|
||||
|| DMRoomMap.shared().getUserIdForRoomId(room.roomId) // put all DMs in the Home Space
|
||||
|| RoomListStore.instance.getTagsForRoom(room).includes(DefaultTagID.Favourite); // show all favourites
|
||||
|| DMRoomMap.shared().getUserIdForRoomId(room.roomId); // put all DMs in the Home Space
|
||||
};
|
||||
|
||||
// Update a given room due to its tag changing (e.g DM-ness or Fav-ness)
|
||||
// This can only change whether it shows up in the HOME_SPACE or not
|
||||
private onRoomUpdate = (room: Room) => {
|
||||
if (this.showInHomeSpace(room)) {
|
||||
this.spaceFilteredRooms.get(HOME_SPACE)?.add(room.roomId);
|
||||
this.emit(HOME_SPACE);
|
||||
} else if (!this.orphanedRooms.has(room.roomId)) {
|
||||
this.spaceFilteredRooms.get(HOME_SPACE)?.delete(room.roomId);
|
||||
this.emit(HOME_SPACE);
|
||||
const enabledMetaSpaces = new Set(this.enabledMetaSpaces);
|
||||
// TODO more metaspace stuffs
|
||||
if (enabledMetaSpaces.has(MetaSpace.Home)) {
|
||||
if (this.showInHomeSpace(room)) {
|
||||
this.spaceFilteredRooms.get(MetaSpace.Home)?.add(room.roomId);
|
||||
this.emit(MetaSpace.Home);
|
||||
} else if (!this.orphanedRooms.has(room.roomId)) {
|
||||
this.spaceFilteredRooms.get(MetaSpace.Home)?.delete(room.roomId);
|
||||
this.emit(MetaSpace.Home);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -469,18 +482,41 @@ export class SpaceStoreClass extends AsyncStoreWithClient<IState> {
|
|||
const oldFilteredRooms = this.spaceFilteredRooms;
|
||||
this.spaceFilteredRooms = new Map();
|
||||
|
||||
if (!this.allRoomsInHome) {
|
||||
const enabledMetaSpaces = new Set(this.enabledMetaSpaces);
|
||||
// populate the Home metaspace if it is enabled and is not set to all rooms
|
||||
if (enabledMetaSpaces.has(MetaSpace.Home) && !this.allRoomsInHome) {
|
||||
// put all room invites in the Home Space
|
||||
const invites = visibleRooms.filter(r => !r.isSpaceRoom() && r.getMyMembership() === "invite");
|
||||
this.spaceFilteredRooms.set(HOME_SPACE, new Set<string>(invites.map(room => room.roomId)));
|
||||
this.spaceFilteredRooms.set(MetaSpace.Home, new Set(invites.map(r => r.roomId)));
|
||||
|
||||
visibleRooms.forEach(room => {
|
||||
if (this.showInHomeSpace(room)) {
|
||||
this.spaceFilteredRooms.get(HOME_SPACE).add(room.roomId);
|
||||
this.spaceFilteredRooms.get(MetaSpace.Home).add(room.roomId);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// populate the Favourites metaspace if it is enabled
|
||||
if (enabledMetaSpaces.has(MetaSpace.Favourites)) {
|
||||
const favourites = visibleRooms.filter(r => r.tags[DefaultTagID.Favourite]);
|
||||
this.spaceFilteredRooms.set(MetaSpace.Favourites, new Set(favourites.map(r => r.roomId)));
|
||||
}
|
||||
|
||||
// populate the People metaspace if it is enabled
|
||||
if (enabledMetaSpaces.has(MetaSpace.People)) {
|
||||
const people = visibleRooms.filter(r => DMRoomMap.shared().getUserIdForRoomId(r.roomId));
|
||||
this.spaceFilteredRooms.set(MetaSpace.People, new Set(people.map(r => r.roomId)));
|
||||
}
|
||||
|
||||
// populate the Orphans metaspace if it is enabled
|
||||
if (enabledMetaSpaces.has(MetaSpace.Orphans)) {
|
||||
const orphans = visibleRooms.filter(r => {
|
||||
// filter out DMs and rooms with >0 parents
|
||||
return !this.parentMap.get(r.roomId)?.size && !DMRoomMap.shared().getUserIdForRoomId(r.roomId);
|
||||
});
|
||||
this.spaceFilteredRooms.set(MetaSpace.Orphans, new Set(orphans.map(r => r.roomId)));
|
||||
}
|
||||
|
||||
const hiddenChildren = new EnhancedMap<string, Set<string>>();
|
||||
visibleRooms.forEach(room => {
|
||||
if (room.getMyMembership() !== "join") return;
|
||||
|
@ -540,15 +576,23 @@ export class SpaceStoreClass extends AsyncStoreWithClient<IState> {
|
|||
this.emit(k);
|
||||
});
|
||||
|
||||
let dmBadgeSpace: MetaSpace;
|
||||
// only show badges on dms on the most relevant space if such exists
|
||||
if (enabledMetaSpaces.has(MetaSpace.People)) {
|
||||
dmBadgeSpace = MetaSpace.People;
|
||||
} else if (enabledMetaSpaces.has(MetaSpace.Home)) {
|
||||
dmBadgeSpace = MetaSpace.Home;
|
||||
}
|
||||
|
||||
this.spaceFilteredRooms.forEach((roomIds, s) => {
|
||||
if (this.allRoomsInHome && s === HOME_SPACE) return; // we'll be using the global notification state, skip
|
||||
if (this.allRoomsInHome && s === MetaSpace.Home) return; // we'll be using the global notification state, skip
|
||||
|
||||
// Update NotificationStates
|
||||
this.getNotificationState(s).setRooms(visibleRooms.filter(room => {
|
||||
if (!roomIds.has(room.roomId) || room.isSpaceRoom()) return false;
|
||||
|
||||
if (DMRoomMap.shared().getUserIdForRoomId(room.roomId)) {
|
||||
return s === HOME_SPACE;
|
||||
if (dmBadgeSpace && DMRoomMap.shared().getUserIdForRoomId(room.roomId)) {
|
||||
return s === dmBadgeSpace;
|
||||
}
|
||||
|
||||
return true;
|
||||
|
@ -575,7 +619,7 @@ export class SpaceStoreClass extends AsyncStoreWithClient<IState> {
|
|||
}
|
||||
|
||||
// don't trigger a context switch when we are switching a space to match the chosen room
|
||||
this.setActiveSpace(parent || null, false);
|
||||
this.setActiveSpace(parent?.roomId ?? MetaSpace.Home, false); // TODO
|
||||
};
|
||||
|
||||
private onRoom = (room: Room, newMembership?: string, oldMembership?: string) => {
|
||||
|
@ -597,7 +641,7 @@ export class SpaceStoreClass extends AsyncStoreWithClient<IState> {
|
|||
const numSuggestedRooms = this._suggestedRooms.length;
|
||||
this._suggestedRooms = this._suggestedRooms.filter(r => r.room_id !== room.roomId);
|
||||
if (numSuggestedRooms !== this._suggestedRooms.length) {
|
||||
this.emit(SUGGESTED_ROOMS, this._suggestedRooms);
|
||||
this.emit(UPDATE_SUGGESTED_ROOMS, this._suggestedRooms);
|
||||
}
|
||||
|
||||
// if the room currently being viewed was just joined then switch to its related space
|
||||
|
@ -622,10 +666,10 @@ export class SpaceStoreClass extends AsyncStoreWithClient<IState> {
|
|||
|
||||
if (membership === "join" && room.roomId === RoomViewStore.getRoomId()) {
|
||||
// if the user was looking at the space and then joined: select that space
|
||||
this.setActiveSpace(room, false);
|
||||
} else if (membership === "leave" && room.roomId === this.activeSpace?.roomId) {
|
||||
this.setActiveSpace(room.roomId, false);
|
||||
} else if (membership === "leave" && room.roomId === this.activeSpace) {
|
||||
// user's active space has gone away, go back to home
|
||||
this.setActiveSpace(null, true);
|
||||
this.goToFirstSpace(true);
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -633,7 +677,7 @@ export class SpaceStoreClass extends AsyncStoreWithClient<IState> {
|
|||
const rootSpaces = this.sortRootSpaces(this.rootSpaces);
|
||||
if (arrayHasOrderChange(this.rootSpaces, rootSpaces)) {
|
||||
this.rootSpaces = rootSpaces;
|
||||
this.emit(UPDATE_TOP_LEVEL_SPACES, this.spacePanelSpaces);
|
||||
this.emit(UPDATE_TOP_LEVEL_SPACES, this.spacePanelSpaces, this.enabledMetaSpaces);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -648,7 +692,7 @@ export class SpaceStoreClass extends AsyncStoreWithClient<IState> {
|
|||
this.emit(room.roomId);
|
||||
}
|
||||
|
||||
if (room === this.activeSpace && // current space
|
||||
if (room.roomId === this.activeSpace && // current space
|
||||
this.matrixClient.getRoom(ev.getStateKey())?.getMyMembership() !== "join" && // target not joined
|
||||
ev.getPrevContent().suggested !== ev.getContent().suggested // suggested flag changed
|
||||
) {
|
||||
|
@ -694,7 +738,7 @@ export class SpaceStoreClass extends AsyncStoreWithClient<IState> {
|
|||
if (order !== lastOrder) {
|
||||
this.notifyIfOrderChanged();
|
||||
}
|
||||
} else if (ev.getType() === EventType.Tag && !this.allRoomsInHome) {
|
||||
} else if (ev.getType() === EventType.Tag) {
|
||||
// If the room was in favourites and now isn't or the opposite then update its position in the trees
|
||||
const oldTags = lastEv?.getContent()?.tags || {};
|
||||
const newTags = ev.getContent()?.tags || {};
|
||||
|
@ -728,9 +772,10 @@ export class SpaceStoreClass extends AsyncStoreWithClient<IState> {
|
|||
this.parentMap = new EnhancedMap();
|
||||
this.notificationStateMap = new Map();
|
||||
this.spaceFilteredRooms = new Map();
|
||||
this._activeSpace = null;
|
||||
this._activeSpace = MetaSpace.Home; // set properly by onReady
|
||||
this._suggestedRooms = [];
|
||||
this._invitedSpaces = new Set();
|
||||
this._enabledMetaSpaces = [];
|
||||
}
|
||||
|
||||
protected async onNotReady() {
|
||||
|
@ -760,16 +805,27 @@ export class SpaceStoreClass extends AsyncStoreWithClient<IState> {
|
|||
?.["m.room_versions"]?.["org.matrix.msc3244.room_capabilities"]?.["restricted"];
|
||||
});
|
||||
|
||||
const enabledMetaSpaces = SettingsStore.getValue("Spaces.enabledMetaSpaces");
|
||||
this._enabledMetaSpaces = metaSpaceOrder.filter(k => enabledMetaSpaces[k]) as MetaSpace[];
|
||||
|
||||
await this.onSpaceUpdate(); // trigger an initial update
|
||||
|
||||
// restore selected state from last session if any and still valid
|
||||
const lastSpaceId = window.localStorage.getItem(ACTIVE_SPACE_LS_KEY);
|
||||
if (lastSpaceId) {
|
||||
if (lastSpaceId && (
|
||||
lastSpaceId[0] === "!" ? this.matrixClient.getRoom(lastSpaceId) : enabledMetaSpaces[lastSpaceId]
|
||||
)) {
|
||||
// don't context switch here as it may break permalinks
|
||||
this.setActiveSpace(this.matrixClient.getRoom(lastSpaceId), false);
|
||||
this.setActiveSpace(lastSpaceId, false);
|
||||
} else {
|
||||
this.goToFirstSpace();
|
||||
}
|
||||
}
|
||||
|
||||
private goToFirstSpace(contextSwitch = false) {
|
||||
this.setActiveSpace(this.enabledMetaSpaces[0] ?? this.spacePanelSpaces[0]?.roomId, contextSwitch);
|
||||
}
|
||||
|
||||
protected async onAction(payload: ActionPayload) {
|
||||
if (!spacesEnabled) return;
|
||||
switch (payload.action) {
|
||||
|
@ -783,9 +839,9 @@ export class SpaceStoreClass extends AsyncStoreWithClient<IState> {
|
|||
if (room?.isSpaceRoom()) {
|
||||
// Don't context switch when navigating to the space room
|
||||
// as it will cause you to end up in the wrong room
|
||||
this.setActiveSpace(room, false);
|
||||
this.setActiveSpace(room.roomId, false);
|
||||
} else if (
|
||||
(!this.allRoomsInHome || this.activeSpace) &&
|
||||
(!this.allRoomsInHome || this.activeSpace[0] === "!") &&
|
||||
!this.getSpaceFilteredRoomIds(this.activeSpace).has(roomId)
|
||||
) {
|
||||
this.switchToRelatedSpace(roomId);
|
||||
|
@ -799,31 +855,54 @@ export class SpaceStoreClass extends AsyncStoreWithClient<IState> {
|
|||
}
|
||||
|
||||
case "after_leave_room":
|
||||
if (this._activeSpace && payload.room_id === this._activeSpace.roomId) {
|
||||
this.setActiveSpace(null, false);
|
||||
if (this._activeSpace[0] === "!" && payload.room_id === this._activeSpace) {
|
||||
// User has left the current space, go to first space
|
||||
this.goToFirstSpace();
|
||||
}
|
||||
break;
|
||||
|
||||
case Action.SwitchSpace:
|
||||
// 1 is Home, 2-9 are the spaces after Home
|
||||
if (payload.num === 1) {
|
||||
this.setActiveSpace(null);
|
||||
} else if (payload.num > 0 && this.spacePanelSpaces.length > payload.num - 2) {
|
||||
this.setActiveSpace(this.spacePanelSpaces[payload.num - 2]);
|
||||
case Action.SwitchSpace: {
|
||||
// Metaspaces start at 1, Spaces follow
|
||||
if (payload.num < 1 || payload.num > 9) break;
|
||||
const numMetaSpaces = this.enabledMetaSpaces.length;
|
||||
if (payload.num <= numMetaSpaces) {
|
||||
this.setActiveSpace(this.enabledMetaSpaces[payload.num - 1]);
|
||||
} else if (this.spacePanelSpaces.length > payload.num - numMetaSpaces - 1) {
|
||||
this.setActiveSpace(this.spacePanelSpaces[payload.num - numMetaSpaces - 1].roomId);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
case Action.SettingUpdated: {
|
||||
const settingUpdatedPayload = payload as SettingUpdatedPayload;
|
||||
if (settingUpdatedPayload.settingName === "Spaces.allRoomsInHome") {
|
||||
const newValue = SettingsStore.getValue("Spaces.allRoomsInHome");
|
||||
if (this.allRoomsInHome !== newValue) {
|
||||
this._allRoomsInHome = newValue;
|
||||
this.emit(UPDATE_HOME_BEHAVIOUR, this.allRoomsInHome);
|
||||
this.rebuild(); // rebuild everything
|
||||
switch (settingUpdatedPayload.settingName) {
|
||||
case "Spaces.allRoomsInHome": {
|
||||
const newValue = SettingsStore.getValue("Spaces.allRoomsInHome");
|
||||
if (this.allRoomsInHome !== newValue) {
|
||||
this._allRoomsInHome = newValue;
|
||||
this.emit(UPDATE_HOME_BEHAVIOUR, this.allRoomsInHome);
|
||||
this.rebuild(); // rebuild everything
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
case "Spaces.enabledMetaSpaces": {
|
||||
const newValue = SettingsStore.getValue("Spaces.enabledMetaSpaces");
|
||||
const enabledMetaSpaces = metaSpaceOrder.filter(k => newValue[k]) as MetaSpace[];
|
||||
if (arrayHasDiff(this._enabledMetaSpaces, enabledMetaSpaces)) {
|
||||
this._enabledMetaSpaces = enabledMetaSpaces;
|
||||
// if a metaspace currently being viewed was remove, go to another one
|
||||
if (this.activeSpace[0] !== "!" &&
|
||||
!enabledMetaSpaces.includes(this.activeSpace as MetaSpace)
|
||||
) {
|
||||
this.goToFirstSpace();
|
||||
}
|
||||
this.emit(UPDATE_TOP_LEVEL_SPACES, this.spacePanelSpaces, this.enabledMetaSpaces);
|
||||
this.rebuild(); // rebuild everything
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
40
src/stores/spaces/index.ts
Normal file
40
src/stores/spaces/index.ts
Normal file
|
@ -0,0 +1,40 @@
|
|||
/*
|
||||
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 { Room } from "matrix-js-sdk/src/models/room";
|
||||
import { IHierarchyRoom } from "matrix-js-sdk/src/@types/spaces";
|
||||
|
||||
// The consts & types are moved out here to prevent cyclical imports
|
||||
|
||||
export const UPDATE_TOP_LEVEL_SPACES = Symbol("top-level-spaces");
|
||||
export const UPDATE_INVITED_SPACES = Symbol("invited-spaces");
|
||||
export const UPDATE_SELECTED_SPACE = Symbol("selected-space");
|
||||
export const UPDATE_HOME_BEHAVIOUR = Symbol("home-behaviour");
|
||||
export const UPDATE_SUGGESTED_ROOMS = Symbol("suggested-rooms");
|
||||
// Space Key will be emitted when a Space's children change
|
||||
|
||||
export enum MetaSpace {
|
||||
Home = "home-space",
|
||||
Favourites = "favourites-space",
|
||||
People = "people-space",
|
||||
Orphans = "orphans-space",
|
||||
}
|
||||
|
||||
export type SpaceKey = MetaSpace | Room["roomId"];
|
||||
|
||||
export interface ISuggestedRoom extends IHierarchyRoom {
|
||||
viaServers: string[];
|
||||
}
|
|
@ -21,7 +21,7 @@ import { inviteUsersToRoom } from "../RoomInvite";
|
|||
import Modal, { IHandle } from "../Modal";
|
||||
import { _t } from "../languageHandler";
|
||||
import ErrorDialog from "../components/views/dialogs/ErrorDialog";
|
||||
import SpaceStore from "../stores/SpaceStore";
|
||||
import SpaceStore from "../stores/spaces/SpaceStore";
|
||||
import Spinner from "../components/views/elements/Spinner";
|
||||
|
||||
import { logger } from "matrix-js-sdk/src/logger";
|
||||
|
|
|
@ -18,13 +18,16 @@ import { EventEmitter } from "events";
|
|||
import { EventType } from "matrix-js-sdk/src/@types/event";
|
||||
import { RoomMember } from "matrix-js-sdk/src/models/room-member";
|
||||
|
||||
import "./enable-metaspaces-labs";
|
||||
import "../skinned-sdk"; // Must be first for skinning to work
|
||||
import SpaceStore, {
|
||||
import SpaceStore from "../../src/stores/spaces/SpaceStore";
|
||||
import {
|
||||
MetaSpace,
|
||||
UPDATE_HOME_BEHAVIOUR,
|
||||
UPDATE_INVITED_SPACES,
|
||||
UPDATE_SELECTED_SPACE,
|
||||
UPDATE_TOP_LEVEL_SPACES,
|
||||
} from "../../src/stores/SpaceStore";
|
||||
} from "../../src/stores/spaces";
|
||||
import * as testUtils from "../utils/test-utils";
|
||||
import { mkEvent, stubClient } from "../test-utils";
|
||||
import DMRoomMap from "../../src/utils/DMRoomMap";
|
||||
|
@ -90,10 +93,18 @@ describe("SpaceStore", () => {
|
|||
await emitProm;
|
||||
};
|
||||
|
||||
beforeEach(() => {
|
||||
beforeEach(async () => {
|
||||
jest.runAllTimers(); // run async dispatch
|
||||
client.getVisibleRooms.mockReturnValue(rooms = []);
|
||||
|
||||
await SettingsStore.setValue("Spaces.enabledMetaSpaces", null, SettingLevel.DEVICE, {
|
||||
[MetaSpace.Home]: true,
|
||||
[MetaSpace.Favourites]: true,
|
||||
[MetaSpace.People]: true,
|
||||
[MetaSpace.Orphans]: true,
|
||||
});
|
||||
});
|
||||
|
||||
afterEach(async () => {
|
||||
await testUtils.resetAsyncStoreWithClient(store);
|
||||
});
|
||||
|
@ -377,69 +388,84 @@ describe("SpaceStore", () => {
|
|||
});
|
||||
|
||||
it("home space contains orphaned rooms", () => {
|
||||
expect(store.getSpaceFilteredRoomIds(null).has(orphan1)).toBeTruthy();
|
||||
expect(store.getSpaceFilteredRoomIds(null).has(orphan2)).toBeTruthy();
|
||||
expect(store.getSpaceFilteredRoomIds(MetaSpace.Home).has(orphan1)).toBeTruthy();
|
||||
expect(store.getSpaceFilteredRoomIds(MetaSpace.Home).has(orphan2)).toBeTruthy();
|
||||
});
|
||||
|
||||
it("home space contains favourites", () => {
|
||||
expect(store.getSpaceFilteredRoomIds(null).has(fav1)).toBeTruthy();
|
||||
expect(store.getSpaceFilteredRoomIds(null).has(fav2)).toBeTruthy();
|
||||
expect(store.getSpaceFilteredRoomIds(null).has(fav3)).toBeTruthy();
|
||||
it("home space does not contain all favourites", () => {
|
||||
expect(store.getSpaceFilteredRoomIds(MetaSpace.Home).has(fav1)).toBeFalsy();
|
||||
expect(store.getSpaceFilteredRoomIds(MetaSpace.Home).has(fav2)).toBeFalsy();
|
||||
expect(store.getSpaceFilteredRoomIds(MetaSpace.Home).has(fav3)).toBeFalsy();
|
||||
});
|
||||
|
||||
it("home space contains dm rooms", () => {
|
||||
expect(store.getSpaceFilteredRoomIds(null).has(dm1)).toBeTruthy();
|
||||
expect(store.getSpaceFilteredRoomIds(null).has(dm2)).toBeTruthy();
|
||||
expect(store.getSpaceFilteredRoomIds(null).has(dm3)).toBeTruthy();
|
||||
expect(store.getSpaceFilteredRoomIds(MetaSpace.Home).has(dm1)).toBeTruthy();
|
||||
expect(store.getSpaceFilteredRoomIds(MetaSpace.Home).has(dm2)).toBeTruthy();
|
||||
expect(store.getSpaceFilteredRoomIds(MetaSpace.Home).has(dm3)).toBeTruthy();
|
||||
});
|
||||
|
||||
it("home space contains invites", () => {
|
||||
expect(store.getSpaceFilteredRoomIds(null).has(invite1)).toBeTruthy();
|
||||
expect(store.getSpaceFilteredRoomIds(MetaSpace.Home).has(invite1)).toBeTruthy();
|
||||
});
|
||||
|
||||
it("home space contains invites even if they are also shown in a space", () => {
|
||||
expect(store.getSpaceFilteredRoomIds(null).has(invite2)).toBeTruthy();
|
||||
expect(store.getSpaceFilteredRoomIds(MetaSpace.Home).has(invite2)).toBeTruthy();
|
||||
});
|
||||
|
||||
it("all rooms space does contain rooms/low priority even if they are also shown in a space", async () => {
|
||||
await setShowAllRooms(true);
|
||||
expect(store.getSpaceFilteredRoomIds(null).has(room1)).toBeTruthy();
|
||||
expect(store.getSpaceFilteredRoomIds(MetaSpace.Home).has(room1)).toBeTruthy();
|
||||
});
|
||||
|
||||
it("favourites space does contain favourites even if they are also shown in a space", async () => {
|
||||
expect(store.getSpaceFilteredRoomIds(MetaSpace.Favourites).has(fav1)).toBeTruthy();
|
||||
expect(store.getSpaceFilteredRoomIds(MetaSpace.Favourites).has(fav2)).toBeTruthy();
|
||||
expect(store.getSpaceFilteredRoomIds(MetaSpace.Favourites).has(fav3)).toBeTruthy();
|
||||
});
|
||||
|
||||
it("people space does contain people even if they are also shown in a space", async () => {
|
||||
expect(store.getSpaceFilteredRoomIds(MetaSpace.People).has(dm1)).toBeTruthy();
|
||||
expect(store.getSpaceFilteredRoomIds(MetaSpace.People).has(dm2)).toBeTruthy();
|
||||
expect(store.getSpaceFilteredRoomIds(MetaSpace.People).has(dm3)).toBeTruthy();
|
||||
});
|
||||
|
||||
it("orphans space does contain orphans even if they are also shown in all rooms", async () => {
|
||||
await setShowAllRooms(true);
|
||||
expect(store.getSpaceFilteredRoomIds(MetaSpace.Orphans).has(orphan1)).toBeTruthy();
|
||||
expect(store.getSpaceFilteredRoomIds(MetaSpace.Orphans).has(orphan2)).toBeTruthy();
|
||||
});
|
||||
|
||||
it("home space doesn't contain rooms/low priority if they are also shown in a space", async () => {
|
||||
await setShowAllRooms(false);
|
||||
expect(store.getSpaceFilteredRoomIds(null).has(room1)).toBeFalsy();
|
||||
expect(store.getSpaceFilteredRoomIds(MetaSpace.Home).has(room1)).toBeFalsy();
|
||||
});
|
||||
|
||||
it("space contains child rooms", () => {
|
||||
const space = client.getRoom(space1);
|
||||
expect(store.getSpaceFilteredRoomIds(space).has(fav1)).toBeTruthy();
|
||||
expect(store.getSpaceFilteredRoomIds(space).has(room1)).toBeTruthy();
|
||||
expect(store.getSpaceFilteredRoomIds(space1).has(fav1)).toBeTruthy();
|
||||
expect(store.getSpaceFilteredRoomIds(space1).has(room1)).toBeTruthy();
|
||||
});
|
||||
|
||||
it("space contains child favourites", () => {
|
||||
const space = client.getRoom(space2);
|
||||
expect(store.getSpaceFilteredRoomIds(space).has(fav1)).toBeTruthy();
|
||||
expect(store.getSpaceFilteredRoomIds(space).has(fav2)).toBeTruthy();
|
||||
expect(store.getSpaceFilteredRoomIds(space).has(fav3)).toBeTruthy();
|
||||
expect(store.getSpaceFilteredRoomIds(space).has(room1)).toBeTruthy();
|
||||
expect(store.getSpaceFilteredRoomIds(space2).has(fav1)).toBeTruthy();
|
||||
expect(store.getSpaceFilteredRoomIds(space2).has(fav2)).toBeTruthy();
|
||||
expect(store.getSpaceFilteredRoomIds(space2).has(fav3)).toBeTruthy();
|
||||
expect(store.getSpaceFilteredRoomIds(space2).has(room1)).toBeTruthy();
|
||||
});
|
||||
|
||||
it("space contains child invites", () => {
|
||||
const space = client.getRoom(space3);
|
||||
expect(store.getSpaceFilteredRoomIds(space).has(invite2)).toBeTruthy();
|
||||
expect(store.getSpaceFilteredRoomIds(space3).has(invite2)).toBeTruthy();
|
||||
});
|
||||
|
||||
it("spaces contain dms which you have with members of that space", () => {
|
||||
expect(store.getSpaceFilteredRoomIds(client.getRoom(space1)).has(dm1)).toBeTruthy();
|
||||
expect(store.getSpaceFilteredRoomIds(client.getRoom(space2)).has(dm1)).toBeFalsy();
|
||||
expect(store.getSpaceFilteredRoomIds(client.getRoom(space3)).has(dm1)).toBeFalsy();
|
||||
expect(store.getSpaceFilteredRoomIds(client.getRoom(space1)).has(dm2)).toBeFalsy();
|
||||
expect(store.getSpaceFilteredRoomIds(client.getRoom(space2)).has(dm2)).toBeTruthy();
|
||||
expect(store.getSpaceFilteredRoomIds(client.getRoom(space3)).has(dm2)).toBeFalsy();
|
||||
expect(store.getSpaceFilteredRoomIds(client.getRoom(space1)).has(dm3)).toBeFalsy();
|
||||
expect(store.getSpaceFilteredRoomIds(client.getRoom(space2)).has(dm3)).toBeFalsy();
|
||||
expect(store.getSpaceFilteredRoomIds(client.getRoom(space3)).has(dm3)).toBeFalsy();
|
||||
expect(store.getSpaceFilteredRoomIds(space1).has(dm1)).toBeTruthy();
|
||||
expect(store.getSpaceFilteredRoomIds(space2).has(dm1)).toBeFalsy();
|
||||
expect(store.getSpaceFilteredRoomIds(space3).has(dm1)).toBeFalsy();
|
||||
expect(store.getSpaceFilteredRoomIds(space1).has(dm2)).toBeFalsy();
|
||||
expect(store.getSpaceFilteredRoomIds(space2).has(dm2)).toBeTruthy();
|
||||
expect(store.getSpaceFilteredRoomIds(space3).has(dm2)).toBeFalsy();
|
||||
expect(store.getSpaceFilteredRoomIds(space1).has(dm3)).toBeFalsy();
|
||||
expect(store.getSpaceFilteredRoomIds(space2).has(dm3)).toBeFalsy();
|
||||
expect(store.getSpaceFilteredRoomIds(space3).has(dm3)).toBeFalsy();
|
||||
});
|
||||
|
||||
it("dms are only added to Notification States for only the Home Space", () => {
|
||||
|
@ -491,11 +517,11 @@ describe("SpaceStore", () => {
|
|||
});
|
||||
|
||||
it("honours m.space.parent if sender has permission in parent space", () => {
|
||||
expect(store.getSpaceFilteredRoomIds(client.getRoom(space2)).has(room2)).toBeTruthy();
|
||||
expect(store.getSpaceFilteredRoomIds(space2).has(room2)).toBeTruthy();
|
||||
});
|
||||
|
||||
it("does not honour m.space.parent if sender does not have permission in parent space", () => {
|
||||
expect(store.getSpaceFilteredRoomIds(client.getRoom(space3)).has(room3)).toBeFalsy();
|
||||
expect(store.getSpaceFilteredRoomIds(space3).has(room3)).toBeFalsy();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
@ -586,8 +612,8 @@ describe("SpaceStore", () => {
|
|||
expect(store.invitedSpaces).toStrictEqual([]);
|
||||
expect(store.getChildSpaces(space1)).toStrictEqual([]);
|
||||
expect(store.getChildRooms(space1)).toStrictEqual([]);
|
||||
expect(store.getSpaceFilteredRoomIds(client.getRoom(space1)).has(invite1)).toBeFalsy();
|
||||
expect(store.getSpaceFilteredRoomIds(null).has(invite1)).toBeFalsy();
|
||||
expect(store.getSpaceFilteredRoomIds(space1).has(invite1)).toBeFalsy();
|
||||
expect(store.getSpaceFilteredRoomIds(MetaSpace.Home).has(invite1)).toBeFalsy();
|
||||
|
||||
const invite = mkRoom(invite1);
|
||||
invite.getMyMembership.mockReturnValue("invite");
|
||||
|
@ -599,8 +625,8 @@ describe("SpaceStore", () => {
|
|||
expect(store.invitedSpaces).toStrictEqual([]);
|
||||
expect(store.getChildSpaces(space1)).toStrictEqual([]);
|
||||
expect(store.getChildRooms(space1)).toStrictEqual([invite]);
|
||||
expect(store.getSpaceFilteredRoomIds(client.getRoom(space1)).has(invite1)).toBeTruthy();
|
||||
expect(store.getSpaceFilteredRoomIds(null).has(invite1)).toBeTruthy();
|
||||
expect(store.getSpaceFilteredRoomIds(space1).has(invite1)).toBeTruthy();
|
||||
expect(store.getSpaceFilteredRoomIds(MetaSpace.Home).has(invite1)).toBeTruthy();
|
||||
});
|
||||
});
|
||||
|
||||
|
@ -614,49 +640,46 @@ describe("SpaceStore", () => {
|
|||
]);
|
||||
mkSpace(space3).getMyMembership.mockReturnValue("invite");
|
||||
await run();
|
||||
store.setActiveSpace(null);
|
||||
expect(store.activeSpace).toBe(null);
|
||||
store.setActiveSpace(MetaSpace.Home);
|
||||
expect(store.activeSpace).toBe(MetaSpace.Home);
|
||||
});
|
||||
afterEach(() => {
|
||||
fn.mockClear();
|
||||
});
|
||||
|
||||
it("switch to home space", async () => {
|
||||
store.setActiveSpace(client.getRoom(space1));
|
||||
store.setActiveSpace(space1);
|
||||
fn.mockClear();
|
||||
|
||||
store.setActiveSpace(null);
|
||||
expect(fn).toHaveBeenCalledWith(UPDATE_SELECTED_SPACE, null);
|
||||
expect(store.activeSpace).toBe(null);
|
||||
store.setActiveSpace(MetaSpace.Home);
|
||||
expect(fn).toHaveBeenCalledWith(UPDATE_SELECTED_SPACE, MetaSpace.Home);
|
||||
expect(store.activeSpace).toBe(MetaSpace.Home);
|
||||
});
|
||||
|
||||
it("switch to invited space", async () => {
|
||||
const space = client.getRoom(space3);
|
||||
store.setActiveSpace(space);
|
||||
expect(fn).toHaveBeenCalledWith(UPDATE_SELECTED_SPACE, space);
|
||||
expect(store.activeSpace).toBe(space);
|
||||
store.setActiveSpace(space3);
|
||||
expect(fn).toHaveBeenCalledWith(UPDATE_SELECTED_SPACE, space3);
|
||||
expect(store.activeSpace).toBe(space3);
|
||||
});
|
||||
|
||||
it("switch to top level space", async () => {
|
||||
const space = client.getRoom(space1);
|
||||
store.setActiveSpace(space);
|
||||
expect(fn).toHaveBeenCalledWith(UPDATE_SELECTED_SPACE, space);
|
||||
expect(store.activeSpace).toBe(space);
|
||||
store.setActiveSpace(space1);
|
||||
expect(fn).toHaveBeenCalledWith(UPDATE_SELECTED_SPACE, space1);
|
||||
expect(store.activeSpace).toBe(space1);
|
||||
});
|
||||
|
||||
it("switch to subspace", async () => {
|
||||
const space = client.getRoom(space2);
|
||||
store.setActiveSpace(space);
|
||||
expect(fn).toHaveBeenCalledWith(UPDATE_SELECTED_SPACE, space);
|
||||
expect(store.activeSpace).toBe(space);
|
||||
store.setActiveSpace(space2);
|
||||
expect(fn).toHaveBeenCalledWith(UPDATE_SELECTED_SPACE, space2);
|
||||
expect(store.activeSpace).toBe(space2);
|
||||
});
|
||||
|
||||
it("switch to unknown space is a nop", async () => {
|
||||
expect(store.activeSpace).toBe(null);
|
||||
expect(store.activeSpace).toBe(MetaSpace.Home);
|
||||
const space = client.getRoom(room1); // not a space
|
||||
store.setActiveSpace(space);
|
||||
expect(fn).not.toHaveBeenCalledWith(UPDATE_SELECTED_SPACE, space);
|
||||
expect(store.activeSpace).toBe(null);
|
||||
store.setActiveSpace(space.roomId);
|
||||
expect(fn).not.toHaveBeenCalledWith(UPDATE_SELECTED_SPACE, space.roomId);
|
||||
expect(store.activeSpace).toBe(MetaSpace.Home);
|
||||
});
|
||||
});
|
||||
|
||||
|
@ -678,6 +701,7 @@ describe("SpaceStore", () => {
|
|||
});
|
||||
afterEach(() => {
|
||||
localStorage.clear();
|
||||
localStorage.setItem("mx_labs_feature_feature_spaces_metaspaces", "true");
|
||||
defaultDispatcher.unregister(dispatcherRef);
|
||||
});
|
||||
|
||||
|
@ -687,59 +711,59 @@ describe("SpaceStore", () => {
|
|||
};
|
||||
|
||||
it("last viewed room in target space is the current viewed and in both spaces", async () => {
|
||||
store.setActiveSpace(client.getRoom(space1));
|
||||
store.setActiveSpace(space1);
|
||||
viewRoom(room2);
|
||||
store.setActiveSpace(client.getRoom(space2));
|
||||
store.setActiveSpace(space2);
|
||||
viewRoom(room2);
|
||||
store.setActiveSpace(client.getRoom(space1));
|
||||
store.setActiveSpace(space1);
|
||||
expect(getCurrentRoom()).toBe(room2);
|
||||
});
|
||||
|
||||
it("last viewed room in target space is in the current space", async () => {
|
||||
store.setActiveSpace(client.getRoom(space1));
|
||||
store.setActiveSpace(space1);
|
||||
viewRoom(room2);
|
||||
store.setActiveSpace(client.getRoom(space2));
|
||||
store.setActiveSpace(space2);
|
||||
expect(getCurrentRoom()).toBe(space2);
|
||||
store.setActiveSpace(client.getRoom(space1));
|
||||
store.setActiveSpace(space1);
|
||||
expect(getCurrentRoom()).toBe(room2);
|
||||
});
|
||||
|
||||
it("last viewed room in target space is not in the current space", async () => {
|
||||
store.setActiveSpace(client.getRoom(space1));
|
||||
store.setActiveSpace(space1);
|
||||
viewRoom(room1);
|
||||
store.setActiveSpace(client.getRoom(space2));
|
||||
store.setActiveSpace(space2);
|
||||
viewRoom(room2);
|
||||
store.setActiveSpace(client.getRoom(space1));
|
||||
store.setActiveSpace(space1);
|
||||
expect(getCurrentRoom()).toBe(room1);
|
||||
});
|
||||
|
||||
it("last viewed room is target space is not known", async () => {
|
||||
store.setActiveSpace(client.getRoom(space1));
|
||||
store.setActiveSpace(space1);
|
||||
viewRoom(room1);
|
||||
localStorage.setItem(`mx_space_context_${space2}`, orphan2);
|
||||
store.setActiveSpace(client.getRoom(space2));
|
||||
store.setActiveSpace(space2);
|
||||
expect(getCurrentRoom()).toBe(space2);
|
||||
});
|
||||
|
||||
it("last viewed room is target space is no longer in that space", async () => {
|
||||
store.setActiveSpace(client.getRoom(space1));
|
||||
store.setActiveSpace(space1);
|
||||
viewRoom(room1);
|
||||
localStorage.setItem(`mx_space_context_${space2}`, room1);
|
||||
store.setActiveSpace(client.getRoom(space2));
|
||||
store.setActiveSpace(space2);
|
||||
expect(getCurrentRoom()).toBe(space2); // Space home instead of room1
|
||||
});
|
||||
|
||||
it("no last viewed room in target space", async () => {
|
||||
store.setActiveSpace(client.getRoom(space1));
|
||||
store.setActiveSpace(space1);
|
||||
viewRoom(room1);
|
||||
store.setActiveSpace(client.getRoom(space2));
|
||||
store.setActiveSpace(space2);
|
||||
expect(getCurrentRoom()).toBe(space2);
|
||||
});
|
||||
|
||||
it("no last viewed room in home space", async () => {
|
||||
store.setActiveSpace(client.getRoom(space1));
|
||||
store.setActiveSpace(space1);
|
||||
viewRoom(room1);
|
||||
store.setActiveSpace(null);
|
||||
store.setActiveSpace(MetaSpace.Home);
|
||||
expect(getCurrentRoom()).toBeNull(); // Home
|
||||
});
|
||||
});
|
||||
|
@ -767,38 +791,51 @@ describe("SpaceStore", () => {
|
|||
|
||||
it("no switch required, room is in current space", async () => {
|
||||
viewRoom(room1);
|
||||
store.setActiveSpace(client.getRoom(space1), false);
|
||||
store.setActiveSpace(space1, false);
|
||||
viewRoom(room2);
|
||||
expect(store.activeSpace).toBe(client.getRoom(space1));
|
||||
expect(store.activeSpace).toBe(space1);
|
||||
});
|
||||
|
||||
it("switch to canonical parent space for room", async () => {
|
||||
viewRoom(room1);
|
||||
store.setActiveSpace(client.getRoom(space2), false);
|
||||
store.setActiveSpace(space2, false);
|
||||
viewRoom(room2);
|
||||
expect(store.activeSpace).toBe(client.getRoom(space2));
|
||||
expect(store.activeSpace).toBe(space2);
|
||||
});
|
||||
|
||||
it("switch to first containing space for room", async () => {
|
||||
viewRoom(room2);
|
||||
store.setActiveSpace(client.getRoom(space2), false);
|
||||
store.setActiveSpace(space2, false);
|
||||
viewRoom(room3);
|
||||
expect(store.activeSpace).toBe(client.getRoom(space1));
|
||||
expect(store.activeSpace).toBe(space1);
|
||||
});
|
||||
|
||||
it("switch to home for orphaned room", async () => {
|
||||
viewRoom(room1);
|
||||
store.setActiveSpace(client.getRoom(space1), false);
|
||||
store.setActiveSpace(space1, false);
|
||||
viewRoom(orphan1);
|
||||
expect(store.activeSpace).toBeNull();
|
||||
expect(store.activeSpace).toBe(MetaSpace.Home);
|
||||
});
|
||||
|
||||
it("switch to first space when selected metaspace is disabled", async () => {
|
||||
store.setActiveSpace(MetaSpace.People, false);
|
||||
expect(store.activeSpace).toBe(MetaSpace.People);
|
||||
await SettingsStore.setValue("Spaces.enabledMetaSpaces", null, SettingLevel.DEVICE, {
|
||||
[MetaSpace.Home]: false,
|
||||
[MetaSpace.Favourites]: true,
|
||||
[MetaSpace.People]: false,
|
||||
[MetaSpace.Orphans]: true,
|
||||
});
|
||||
jest.runAllTimers();
|
||||
expect(store.activeSpace).toBe(MetaSpace.Favourites);
|
||||
});
|
||||
|
||||
it("when switching rooms in the all rooms home space don't switch to related space", async () => {
|
||||
await setShowAllRooms(true);
|
||||
viewRoom(room2);
|
||||
store.setActiveSpace(null, false);
|
||||
store.setActiveSpace(MetaSpace.Home, false);
|
||||
viewRoom(room1);
|
||||
expect(store.activeSpace).toBeNull();
|
||||
expect(store.activeSpace).toBe(MetaSpace.Home);
|
||||
});
|
||||
});
|
||||
|
||||
|
|
17
test/stores/enable-metaspaces-labs.ts
Normal file
17
test/stores/enable-metaspaces-labs.ts
Normal file
|
@ -0,0 +1,17 @@
|
|||
/*
|
||||
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.
|
||||
*/
|
||||
|
||||
localStorage.setItem("mx_labs_feature_feature_spaces_metaspaces", "true");
|
|
@ -14,17 +14,20 @@ See the License for the specific language governing permissions and
|
|||
limitations under the License.
|
||||
*/
|
||||
|
||||
import "../enable-metaspaces-labs";
|
||||
import "../../skinned-sdk"; // Must be first for skinning to work
|
||||
import { SpaceWatcher } from "../../../src/stores/room-list/SpaceWatcher";
|
||||
import type { RoomListStoreClass } from "../../../src/stores/room-list/RoomListStore";
|
||||
import SettingsStore from "../../../src/settings/SettingsStore";
|
||||
import SpaceStore, { UPDATE_HOME_BEHAVIOUR } from "../../../src/stores/SpaceStore";
|
||||
import SpaceStore from "../../../src/stores/spaces/SpaceStore";
|
||||
import { MetaSpace, UPDATE_HOME_BEHAVIOUR } from "../../../src/stores/spaces";
|
||||
import { stubClient } from "../../test-utils";
|
||||
import { SettingLevel } from "../../../src/settings/SettingLevel";
|
||||
import * as testUtils from "../../utils/test-utils";
|
||||
import { setupAsyncStoreWithClient } from "../../utils/test-utils";
|
||||
import { MatrixClientPeg } from "../../../src/MatrixClientPeg";
|
||||
import * as testUtils from "../../utils/test-utils";
|
||||
import { SpaceFilterCondition } from "../../../src/stores/room-list/filters/SpaceFilterCondition";
|
||||
import DMRoomMap from "../../../src/utils/DMRoomMap";
|
||||
|
||||
let filter: SpaceFilterCondition = null;
|
||||
|
||||
|
@ -33,8 +36,13 @@ const mockRoomListStore = {
|
|||
removeFilter: () => filter = null,
|
||||
} as unknown as RoomListStoreClass;
|
||||
|
||||
const space1Id = "!space1:server";
|
||||
const space2Id = "!space2:server";
|
||||
const getUserIdForRoomId = jest.fn();
|
||||
const getDMRoomsForUserId = jest.fn();
|
||||
// @ts-ignore
|
||||
DMRoomMap.sharedInstance = { getUserIdForRoomId, getDMRoomsForUserId };
|
||||
|
||||
const space1 = "!space1:server";
|
||||
const space2 = "!space2:server";
|
||||
|
||||
describe("SpaceWatcher", () => {
|
||||
stubClient();
|
||||
|
@ -50,17 +58,21 @@ describe("SpaceWatcher", () => {
|
|||
await testUtils.emitPromise(store, UPDATE_HOME_BEHAVIOUR);
|
||||
};
|
||||
|
||||
let space1;
|
||||
let space2;
|
||||
|
||||
beforeEach(async () => {
|
||||
filter = null;
|
||||
store.removeAllListeners();
|
||||
store.setActiveSpace(null);
|
||||
store.setActiveSpace(MetaSpace.Home);
|
||||
client.getVisibleRooms.mockReturnValue(rooms = []);
|
||||
|
||||
space1 = mkSpace(space1Id);
|
||||
space2 = mkSpace(space2Id);
|
||||
mkSpace(space1);
|
||||
mkSpace(space2);
|
||||
|
||||
await SettingsStore.setValue("Spaces.enabledMetaSpaces", null, SettingLevel.DEVICE, {
|
||||
[MetaSpace.Home]: true,
|
||||
[MetaSpace.Favourites]: true,
|
||||
[MetaSpace.People]: true,
|
||||
[MetaSpace.Orphans]: true,
|
||||
});
|
||||
|
||||
client.getRoom.mockImplementation(roomId => rooms.find(room => room.roomId === roomId));
|
||||
await setupAsyncStoreWithClient(store, client);
|
||||
|
@ -80,14 +92,14 @@ describe("SpaceWatcher", () => {
|
|||
expect(filter).toBeNull();
|
||||
});
|
||||
|
||||
it("sets space=null filter for all -> home transition", async () => {
|
||||
it("sets space=Home filter for all -> home transition", async () => {
|
||||
await setShowAllRooms(true);
|
||||
new SpaceWatcher(mockRoomListStore);
|
||||
|
||||
await setShowAllRooms(false);
|
||||
|
||||
expect(filter).toBeInstanceOf(SpaceFilterCondition);
|
||||
expect(filter["space"]).toBeNull();
|
||||
expect(filter["space"]).toBe(MetaSpace.Home);
|
||||
});
|
||||
|
||||
it("sets filter correctly for all -> space transition", async () => {
|
||||
|
@ -126,7 +138,43 @@ describe("SpaceWatcher", () => {
|
|||
SpaceStore.instance.setActiveSpace(space1);
|
||||
expect(filter).toBeInstanceOf(SpaceFilterCondition);
|
||||
expect(filter["space"]).toBe(space1);
|
||||
SpaceStore.instance.setActiveSpace(null);
|
||||
SpaceStore.instance.setActiveSpace(MetaSpace.Home);
|
||||
|
||||
expect(filter).toBeNull();
|
||||
});
|
||||
|
||||
it("removes filter for favourites -> all transition", async () => {
|
||||
await setShowAllRooms(true);
|
||||
new SpaceWatcher(mockRoomListStore);
|
||||
|
||||
SpaceStore.instance.setActiveSpace(MetaSpace.Favourites);
|
||||
expect(filter).toBeInstanceOf(SpaceFilterCondition);
|
||||
expect(filter["space"]).toBe(MetaSpace.Favourites);
|
||||
SpaceStore.instance.setActiveSpace(MetaSpace.Home);
|
||||
|
||||
expect(filter).toBeNull();
|
||||
});
|
||||
|
||||
it("removes filter for people -> all transition", async () => {
|
||||
await setShowAllRooms(true);
|
||||
new SpaceWatcher(mockRoomListStore);
|
||||
|
||||
SpaceStore.instance.setActiveSpace(MetaSpace.People);
|
||||
expect(filter).toBeInstanceOf(SpaceFilterCondition);
|
||||
expect(filter["space"]).toBe(MetaSpace.People);
|
||||
SpaceStore.instance.setActiveSpace(MetaSpace.Home);
|
||||
|
||||
expect(filter).toBeNull();
|
||||
});
|
||||
|
||||
it("removes filter for orphans -> all transition", async () => {
|
||||
await setShowAllRooms(true);
|
||||
new SpaceWatcher(mockRoomListStore);
|
||||
|
||||
SpaceStore.instance.setActiveSpace(MetaSpace.Orphans);
|
||||
expect(filter).toBeInstanceOf(SpaceFilterCondition);
|
||||
expect(filter["space"]).toBe(MetaSpace.Orphans);
|
||||
SpaceStore.instance.setActiveSpace(MetaSpace.Home);
|
||||
|
||||
expect(filter).toBeNull();
|
||||
});
|
||||
|
@ -138,10 +186,36 @@ describe("SpaceWatcher", () => {
|
|||
new SpaceWatcher(mockRoomListStore);
|
||||
expect(filter).toBeInstanceOf(SpaceFilterCondition);
|
||||
expect(filter["space"]).toBe(space1);
|
||||
SpaceStore.instance.setActiveSpace(null);
|
||||
SpaceStore.instance.setActiveSpace(MetaSpace.Home);
|
||||
|
||||
expect(filter).toBeInstanceOf(SpaceFilterCondition);
|
||||
expect(filter["space"]).toBe(null);
|
||||
expect(filter["space"]).toBe(MetaSpace.Home);
|
||||
});
|
||||
|
||||
it("updates filter correctly for space -> orphans transition", async () => {
|
||||
await setShowAllRooms(false);
|
||||
SpaceStore.instance.setActiveSpace(space1);
|
||||
|
||||
new SpaceWatcher(mockRoomListStore);
|
||||
expect(filter).toBeInstanceOf(SpaceFilterCondition);
|
||||
expect(filter["space"]).toBe(space1);
|
||||
SpaceStore.instance.setActiveSpace(MetaSpace.Orphans);
|
||||
|
||||
expect(filter).toBeInstanceOf(SpaceFilterCondition);
|
||||
expect(filter["space"]).toBe(MetaSpace.Orphans);
|
||||
});
|
||||
|
||||
it("updates filter correctly for orphans -> people transition", async () => {
|
||||
await setShowAllRooms(false);
|
||||
SpaceStore.instance.setActiveSpace(MetaSpace.Orphans);
|
||||
|
||||
new SpaceWatcher(mockRoomListStore);
|
||||
expect(filter).toBeInstanceOf(SpaceFilterCondition);
|
||||
expect(filter["space"]).toBe(MetaSpace.Orphans);
|
||||
SpaceStore.instance.setActiveSpace(MetaSpace.People);
|
||||
|
||||
expect(filter).toBeInstanceOf(SpaceFilterCondition);
|
||||
expect(filter["space"]).toBe(MetaSpace.People);
|
||||
});
|
||||
|
||||
it("updates filter correctly for space -> space transition", async () => {
|
||||
|
|
Loading…
Reference in a new issue