diff --git a/res/css/views/beta/_BetaCard.scss b/res/css/views/beta/_BetaCard.scss
index 3463a653fc..1a8241b65f 100644
--- a/res/css/views/beta/_BetaCard.scss
+++ b/res/css/views/beta/_BetaCard.scss
@@ -19,49 +19,68 @@ limitations under the License.
padding: 24px;
background-color: $settings-profile-placeholder-bg-color;
border-radius: 8px;
- display: flex;
box-sizing: border-box;
- > div {
- .mx_BetaCard_title {
- font-weight: $font-semi-bold;
- font-size: $font-18px;
- line-height: $font-22px;
- color: $primary-fg-color;
- margin: 4px 0 14px;
+ .mx_BetaCard_columns {
+ display: flex;
- .mx_BetaCard_betaPill {
- margin-left: 12px;
+ > div {
+ .mx_BetaCard_title {
+ font-weight: $font-semi-bold;
+ font-size: $font-18px;
+ line-height: $font-22px;
+ color: $primary-fg-color;
+ margin: 4px 0 14px;
+
+ .mx_BetaCard_betaPill {
+ margin-left: 12px;
+ }
+ }
+
+ .mx_BetaCard_caption {
+ font-size: $font-15px;
+ line-height: $font-20px;
+ color: $secondary-fg-color;
+ margin-bottom: 20px;
+ }
+
+ .mx_BetaCard_buttons .mx_AccessibleButton {
+ display: block;
+ margin: 12px 0;
+ padding: 7px 40px;
+ width: auto;
+ }
+
+ .mx_BetaCard_disclaimer {
+ font-size: $font-12px;
+ line-height: $font-15px;
+ color: $secondary-fg-color;
+ margin-top: 20px;
}
}
- .mx_BetaCard_caption {
- font-size: $font-15px;
- line-height: $font-20px;
- color: $secondary-fg-color;
- margin-bottom: 20px;
- }
-
- .mx_AccessibleButton {
- display: block;
- margin: 12px 0;
- padding: 7px 40px;
- width: auto;
- }
-
- .mx_BetaCard_disclaimer {
- font-size: $font-12px;
- line-height: $font-15px;
- color: $secondary-fg-color;
- margin-top: 20px;
+ > img {
+ margin: auto 0 auto 20px;
+ width: 300px;
+ object-fit: contain;
+ height: 100%;
}
}
- > img {
- margin: auto 0 auto 20px;
- width: 300px;
- object-fit: contain;
- height: 100%;
+ .mx_BetaCard_relatedSettings {
+ .mx_SettingsFlag {
+ margin: 16px 0 0;
+ font-size: $font-15px;
+ line-height: $font-24px;
+ color: $primary-fg-color;
+
+ .mx_SettingsFlag_microcopy {
+ margin-top: 4px;
+ font-size: $font-12px;
+ line-height: $font-15px;
+ color: $secondary-fg-color;
+ }
+ }
}
}
diff --git a/src/components/views/beta/BetaCard.tsx b/src/components/views/beta/BetaCard.tsx
index 821c448f4f..aa4fe49f63 100644
--- a/src/components/views/beta/BetaCard.tsx
+++ b/src/components/views/beta/BetaCard.tsx
@@ -25,6 +25,7 @@ import TextWithTooltip from "../elements/TextWithTooltip";
import Modal from "../../../Modal";
import BetaFeedbackDialog from "../dialogs/BetaFeedbackDialog";
import SdkConfig from "../../../SdkConfig";
+import SettingsFlag from "../elements/SettingsFlag";
interface IProps {
title?: string;
@@ -66,7 +67,7 @@ const BetaCard = ({ title: titleOverride, featureId }: IProps) => {
const info = SettingsStore.getBetaInfo(featureId);
if (!info) return null; // Beta is invalid/disabled
- const { title, caption, disclaimer, image, feedbackLabel, feedbackSubheading } = info;
+ const { title, caption, disclaimer, image, feedbackLabel, feedbackSubheading, extraSettings } = info;
const value = SettingsStore.getValue(featureId);
let feedbackButton;
@@ -82,26 +83,33 @@ const BetaCard = ({ title: titleOverride, featureId }: IProps) => {
}
return
-
-
- { titleOverride || _t(title) }
-
-
-
{ _t(caption) }
+
- { feedbackButton }
-
SettingsStore.setValue(featureId, null, SettingLevel.DEVICE, !value)}
- kind={feedbackButton ? "primary_outline" : "primary"}
- >
- { value ? _t("Leave the beta") : _t("Join the beta") }
-
+
+ { titleOverride || _t(title) }
+
+
+
{ _t(caption) }
+
+ { feedbackButton }
+
SettingsStore.setValue(featureId, null, SettingLevel.DEVICE, !value)}
+ kind={feedbackButton ? "primary_outline" : "primary"}
+ >
+ { value ? _t("Leave the beta") : _t("Join the beta") }
+
+
+ { disclaimer &&
+ { disclaimer(value) }
+
}
- { disclaimer &&
- { disclaimer(value) }
-
}
+
-

+ { extraSettings &&
+ { extraSettings.map(key => (
+
+ )) }
+
}
;
};
diff --git a/src/components/views/dialogs/BetaFeedbackDialog.tsx b/src/components/views/dialogs/BetaFeedbackDialog.tsx
index 1c2dab4bfc..b8ff803627 100644
--- a/src/components/views/dialogs/BetaFeedbackDialog.tsx
+++ b/src/components/views/dialogs/BetaFeedbackDialog.tsx
@@ -44,7 +44,12 @@ const BetaFeedbackDialog: React.FC
= ({featureId, onFinished}) => {
const sendFeedback = async (ok: boolean) => {
if (!ok) return onFinished(false);
- submitFeedback(SdkConfig.get().bug_report_endpoint_url, info.feedbackLabel, comment, canContact);
+ const extraData = SettingsStore.getBetaInfo(featureId)?.extraSettings.reduce((o, k) => {
+ o[k] = SettingsStore.getValue(k);
+ return o;
+ }, {});
+
+ submitFeedback(SdkConfig.get().bug_report_endpoint_url, info.feedbackLabel, comment, canContact, extraData);
onFinished(true);
Modal.createTrackedDialog("Beta Dialog Sent", featureId, InfoDialog, {
diff --git a/src/components/views/elements/SettingsFlag.tsx b/src/components/views/elements/SettingsFlag.tsx
index 4f885ab47d..24a21e1a33 100644
--- a/src/components/views/elements/SettingsFlag.tsx
+++ b/src/components/views/elements/SettingsFlag.tsx
@@ -77,9 +77,10 @@ export default class SettingsFlag extends React.Component {
public render() {
const canChange = SettingsStore.canSetValue(this.props.name, this.props.roomId, this.props.level);
- let label = this.props.label;
- if (!label) label = SettingsStore.getDisplayName(this.props.name, this.props.level);
- else label = _t(label);
+ const label = this.props.label
+ ? _t(this.props.label)
+ : SettingsStore.getDisplayName(this.props.name, this.props.level);
+ const description = SettingsStore.getDescription(this.props.name);
if (this.props.useCheckbox) {
return {
disabled={this.props.disabled || !canChange}
aria-label={label}
/>
+ { description &&
+ { description }
+
}
);
}
diff --git a/src/components/views/spaces/SpacePanel.tsx b/src/components/views/spaces/SpacePanel.tsx
index eb63b21f0e..fbda34a03c 100644
--- a/src/components/views/spaces/SpacePanel.tsx
+++ b/src/components/views/spaces/SpacePanel.tsx
@@ -26,6 +26,7 @@ import {SpaceItem} from "./SpaceTreeLevel";
import AccessibleTooltipButton from "../elements/AccessibleTooltipButton";
import {useEventEmitter} from "../../../hooks/useEventEmitter";
import SpaceStore, {
+ HOME_SPACE,
UPDATE_INVITED_SPACES,
UPDATE_SELECTED_SPACE,
UPDATE_TOP_LEVEL_SPACES,
@@ -40,6 +41,7 @@ import {
import {Key} from "../../../Keyboard";
import {RoomNotificationStateStore} from "../../../stores/notifications/RoomNotificationStateStore";
import {NotificationState} from "../../../stores/notifications/NotificationState";
+import SettingsStore from "../../../settings/SettingsStore";
interface IButtonProps {
space?: Room;
@@ -205,6 +207,10 @@ const SpacePanel = () => {
const activeSpaces = activeSpace ? [activeSpace] : [];
const expandCollapseButtonTitle = isPanelCollapsed ? _t("Expand space panel") : _t("Collapse space panel");
+
+ const homeNotificationState = SettingsStore.getValue("feature_spaces.all_rooms")
+ ? RoomNotificationStateStore.instance.globalState : SpaceStore.instance.getNotificationState(HOME_SPACE);
+
// TODO drag and drop for re-arranging order
return
{({onKeyDownHandler}) => (
@@ -218,8 +224,8 @@ const SpacePanel = () => {
className="mx_SpaceButton_home"
onClick={() => SpaceStore.instance.setActiveSpace(null)}
selected={!activeSpace}
- tooltip={_t("All rooms")}
- notificationState={RoomNotificationStateStore.instance.globalState}
+ tooltip={SettingsStore.getValue("feature_spaces.all_rooms") ? _t("All rooms") : _t("Home")}
+ notificationState={homeNotificationState}
isNarrow={isPanelCollapsed}
/>
{ invites.map(s => = {},
+) {
let version = "UNKNOWN";
try {
version = await PlatformPeg.get().getAppVersion();
@@ -279,6 +285,10 @@ export async function submitFeedback(endpoint: string, label: string, comment: s
body.append("platform", PlatformPeg.get().getHumanReadableName());
body.append("user_id", MatrixClientPeg.get()?.getUserId());
+ for (const k in extraData) {
+ body.append(k, extraData[k]);
+ }
+
await _submitReport(SdkConfig.get().bug_report_endpoint_url, body, () => {});
}
diff --git a/src/settings/Settings.tsx b/src/settings/Settings.tsx
index 97f1beb979..6ed5e0c3d8 100644
--- a/src/settings/Settings.tsx
+++ b/src/settings/Settings.tsx
@@ -1,6 +1,6 @@
/*
Copyright 2017 Travis Ralston
-Copyright 2018, 2019, 2020 The Matrix.org Foundation C.I.C.
+Copyright 2018 - 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.
@@ -94,6 +94,9 @@ export interface ISetting {
[level: SettingLevel]: string;
};
+ // Optional description which will be shown as microCopy under SettingsFlags
+ description?: string;
+
// The supported levels are required. Preferably, use the preset arrays
// at the top of this file to define this rather than a custom array.
supportedLevels?: SettingLevel[];
@@ -127,6 +130,7 @@ export interface ISetting {
image: string; // require(...)
feedbackSubheading?: string;
feedbackLabel?: string;
+ extraSettings?: string[];
};
}
@@ -174,8 +178,33 @@ export const SETTINGS: {[setting: string]: ISetting} = {
feedbackSubheading: _td("Your feedback will help make spaces better. " +
"The more detail you can go into, the better."),
feedbackLabel: "spaces-feedback",
+ extraSettings: [
+ "feature_spaces.all_rooms",
+ "feature_spaces.space_member_dms",
+ "feature_spaces.space_dm_badges",
+ ],
},
},
+ "feature_spaces.all_rooms": {
+ displayName: _td("Show all rooms in Home"),
+ supportedLevels: LEVELS_FEATURE,
+ default: true,
+ controller: new ReloadOnChangeController(),
+ },
+ "feature_spaces.space_member_dms": {
+ displayName: _td("Show people in spaces"),
+ description: _td("If disabled, you can still add Direct Messages to Personal Spaces. " +
+ "If enabled, you'll automatically see everyone who is a member of the Space."),
+ supportedLevels: LEVELS_FEATURE,
+ default: true,
+ controller: new ReloadOnChangeController(),
+ },
+ "feature_spaces.space_dm_badges": {
+ displayName: _td("Show notification badges for People in Spaces"),
+ supportedLevels: LEVELS_FEATURE,
+ default: false,
+ controller: new ReloadOnChangeController(),
+ },
"feature_dnd": {
isFeature: true,
displayName: _td("Show options to enable 'Do not disturb' mode"),
diff --git a/src/settings/SettingsStore.ts b/src/settings/SettingsStore.ts
index e1e300e185..44f3d5d838 100644
--- a/src/settings/SettingsStore.ts
+++ b/src/settings/SettingsStore.ts
@@ -248,6 +248,16 @@ export default class SettingsStore {
return _t(displayName as string);
}
+ /**
+ * Gets the translated description for a given setting
+ * @param {string} settingName The setting to look up.
+ * @return {String} The description for the setting, or null if not found.
+ */
+ public static getDescription(settingName: string) {
+ if (!SETTINGS[settingName]?.description) return null;
+ return _t(SETTINGS[settingName].description);
+ }
+
/**
* Determines if a setting is also a feature.
* @param {string} settingName The setting to look up.
diff --git a/src/stores/SpaceStore.tsx b/src/stores/SpaceStore.tsx
index ed1a515ea6..d371086b45 100644
--- a/src/stores/SpaceStore.tsx
+++ b/src/stores/SpaceStore.tsx
@@ -14,37 +14,42 @@ See the License for the specific language governing permissions and
limitations under the License.
*/
-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 { 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 {AsyncStoreWithClient} from "./AsyncStoreWithClient";
+import { AsyncStoreWithClient } from "./AsyncStoreWithClient";
import defaultDispatcher from "../dispatcher/dispatcher";
-import {ActionPayload} from "../dispatcher/payloads";
+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 {ISpaceSummaryEvent, ISpaceSummaryRoom} from "../components/structures/SpaceRoomDirectory";
+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 { ISpaceSummaryEvent, ISpaceSummaryRoom } from "../components/structures/SpaceRoomDirectory";
import RoomViewStore from "./RoomViewStore";
-import {Action} from "../dispatcher/actions";
+import { Action } from "../dispatcher/actions";
+import { arrayHasDiff } from "../utils/arrays";
+import { objectDiff } from "../utils/objects";
+
+type SpaceKey = string | symbol;
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");
-// Space Room ID will be emitted when a Space's children change
+// Space Room ID/HOME_SPACE will be emitted when a Space's children change
export interface ISuggestedRoom extends ISpaceSummaryRoom {
viaServers: string[];
@@ -52,7 +57,8 @@ export interface ISuggestedRoom extends ISpaceSummaryRoom {
const MAX_SUGGESTED_ROOMS = 20;
-const getSpaceContextKey = (space?: Room) => `mx_space_context_${space?.roomId || "ALL_ROOMS"}`;
+const homeSpaceKey = SettingsStore.getValue("feature_spaces.all_rooms") ? "ALL_ROOMS" : "HOME_SPACE";
+const getSpaceContextKey = (space?: Room) => `mx_space_context_${space?.roomId || homeSpaceKey}`;
const partitionSpacesAndRooms = (arr: Room[]): [Room[], Room[]] => { // [spaces, rooms]
return arr.reduce((result, room: Room) => {
@@ -86,13 +92,15 @@ export class SpaceStoreClass extends AsyncStoreWithClient {
// The spaces representing the roots of the various tree-like hierarchies
private rootSpaces: Room[] = [];
+ // The list of rooms not present in any currently joined spaces
+ private orphanedRooms = new Set();
// Map from room ID to set of spaces which list it as a child
private parentMap = new EnhancedMap>();
- // Map from spaceId to SpaceNotificationState instance representing that space
- private notificationStateMap = new Map();
+ // Map from SpaceKey to SpaceNotificationState instance representing that space
+ private notificationStateMap = new Map();
// Map from space key to Set of room IDs that should be shown as part of that space's filter
- private spaceFilteredRooms = new Map>();
- // The space currently selected in the Space Panel - if null then All Rooms is selected
+ private spaceFilteredRooms = new Map>();
+ // The space currently selected in the Space Panel - if null then Home is selected
private _activeSpace?: Room = null;
private _suggestedRooms: ISuggestedRoom[] = [];
private _invitedSpaces = new Set();
@@ -252,10 +260,10 @@ export class SpaceStoreClass extends AsyncStoreWithClient {
}
public getSpaceFilteredRoomIds = (space: Room | null): Set => {
- if (!space) {
+ if (!space && SettingsStore.getValue("feature_spaces.all_rooms")) {
return new Set(this.matrixClient.getVisibleRooms().map(r => r.roomId));
}
- return this.spaceFilteredRooms.get(space.roomId) || new Set();
+ return this.spaceFilteredRooms.get(space?.roomId || HOME_SPACE) || new Set();
};
private rebuild = throttle(() => {
@@ -286,7 +294,7 @@ export class SpaceStoreClass extends AsyncStoreWithClient {
});
});
- const [rootSpaces] = partitionSpacesAndRooms(Array.from(unseenChildren));
+ const [rootSpaces, orphanedRooms] = partitionSpacesAndRooms(Array.from(unseenChildren));
// somewhat algorithm to handle full-cycles
const detachedNodes = new Set(spaces);
@@ -327,6 +335,7 @@ export class SpaceStoreClass extends AsyncStoreWithClient {
// rootSpaces.push(space);
// });
+ this.orphanedRooms = new Set(orphanedRooms);
this.rootSpaces = rootSpaces;
this.parentMap = backrefs;
@@ -343,10 +352,30 @@ export class SpaceStoreClass extends AsyncStoreWithClient {
this.emit(UPDATE_INVITED_SPACES, this.invitedSpaces);
}, 100, {trailing: true, leading: true});
- onSpaceUpdate = () => {
+ private onSpaceUpdate = () => {
this.rebuild();
}
+ private showInHomeSpace = (room: Room) => {
+ if (SettingsStore.getValue("feature_spaces.all_rooms")) 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
+ };
+
+ // 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);
+ }
+ };
+
private onSpaceMembersChange = (ev: MatrixEvent) => {
// skip this update if we do not have a DM with this user
if (DMRoomMap.shared().getDMRoomsForUserId(ev.getStateKey()).length < 1) return;
@@ -360,6 +389,18 @@ export class SpaceStoreClass extends AsyncStoreWithClient {
const oldFilteredRooms = this.spaceFilteredRooms;
this.spaceFilteredRooms = new Map();
+ if (!SettingsStore.getValue("feature_spaces.all_rooms")) {
+ // 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(invites.map(room => room.roomId)));
+
+ visibleRooms.forEach(room => {
+ if (this.showInHomeSpace(room)) {
+ this.spaceFilteredRooms.get(HOME_SPACE).add(room.roomId);
+ }
+ });
+ }
+
this.rootSpaces.forEach(s => {
// traverse each space tree in DFS to build up the supersets as you go up,
// reusing results from like subtrees.
@@ -375,13 +416,15 @@ export class SpaceStoreClass extends AsyncStoreWithClient {
const roomIds = new Set(childRooms.map(r => r.roomId));
const space = this.matrixClient?.getRoom(spaceId);
- // Add relevant DMs
- space?.getMembers().forEach(member => {
- if (member.membership !== "join" && member.membership !== "invite") return;
- DMRoomMap.shared().getDMRoomsForUserId(member.userId).forEach(roomId => {
- roomIds.add(roomId);
+ if (SettingsStore.getValue("feature_spaces.space_member_dms")) {
+ // Add relevant DMs
+ space?.getMembers().forEach(member => {
+ if (member.membership !== "join" && member.membership !== "invite") return;
+ DMRoomMap.shared().getDMRoomsForUserId(member.userId).forEach(roomId => {
+ roomIds.add(roomId);
+ });
});
- });
+ }
const newPath = new Set(parentPath).add(spaceId);
childSpaces.forEach(childSpace => {
@@ -407,6 +450,8 @@ export class SpaceStoreClass extends AsyncStoreWithClient {
// Update NotificationStates
this.getNotificationState(s)?.setRooms(visibleRooms.filter(room => {
if (roomIds.has(room.roomId)) {
+ if (s !== HOME_SPACE && SettingsStore.getValue("feature_spaces.space_dm_badges")) return true;
+
return !DMRoomMap.shared().getUserIdForRoomId(room.roomId)
|| RoomListStore.instance.getTagsForRoom(room).includes(DefaultTagID.Favourite);
}
@@ -496,6 +541,8 @@ export class SpaceStoreClass extends AsyncStoreWithClient {
// TODO confirm this after implementing parenting behaviour
if (room.isSpaceRoom()) {
this.onSpaceUpdate();
+ } else if (!SettingsStore.getValue("feature_spaces.all_rooms")) {
+ this.onRoomUpdate(room);
}
this.emit(room.roomId);
break;
@@ -508,8 +555,38 @@ export class SpaceStoreClass extends AsyncStoreWithClient {
}
};
+ private onRoomAccountData = (ev: MatrixEvent, room: Room, lastEvent?: MatrixEvent) => {
+ if (ev.getType() === EventType.Tag && !room.isSpaceRoom()) {
+ // If the room was in favourites and now isn't or the opposite then update its position in the trees
+ const oldTags = lastEvent?.getContent()?.tags || {};
+ const newTags = ev.getContent()?.tags || {};
+ if (!!oldTags[DefaultTagID.Favourite] !== !!newTags[DefaultTagID.Favourite]) {
+ this.onRoomUpdate(room);
+ }
+ }
+ }
+
+ private onAccountData = (ev: MatrixEvent, lastEvent: MatrixEvent) => {
+ if (ev.getType() === EventType.Direct) {
+ const lastContent = lastEvent.getContent();
+ const content = ev.getContent();
+
+ const diff = objectDiff>(lastContent, content);
+ // filter out keys which changed by reference only by checking whether the sets differ
+ const changed = diff.changed.filter(k => arrayHasDiff(lastContent[k], content[k]));
+ // DM tag changes, refresh relevant rooms
+ new Set([...diff.added, ...diff.removed, ...changed]).forEach(roomId => {
+ const room = this.matrixClient?.getRoom(roomId);
+ if (room) {
+ this.onRoomUpdate(room);
+ }
+ });
+ }
+ };
+
protected async reset() {
this.rootSpaces = [];
+ this.orphanedRooms = new Set();
this.parentMap = new EnhancedMap();
this.notificationStateMap = new Map();
this.spaceFilteredRooms = new Map();
@@ -524,6 +601,10 @@ export class SpaceStoreClass extends AsyncStoreWithClient {
this.matrixClient.removeListener("Room", this.onRoom);
this.matrixClient.removeListener("Room.myMembership", this.onRoom);
this.matrixClient.removeListener("RoomState.events", this.onRoomState);
+ if (!SettingsStore.getValue("feature_spaces.all_rooms")) {
+ this.matrixClient.removeListener("Room.accountData", this.onRoomAccountData);
+ this.matrixClient.removeListener("accountData", this.onAccountData);
+ }
}
await this.reset();
}
@@ -533,6 +614,10 @@ export class SpaceStoreClass extends AsyncStoreWithClient {
this.matrixClient.on("Room", this.onRoom);
this.matrixClient.on("Room.myMembership", this.onRoom);
this.matrixClient.on("RoomState.events", this.onRoomState);
+ if (!SettingsStore.getValue("feature_spaces.all_rooms")) {
+ this.matrixClient.on("Room.accountData", this.onRoomAccountData);
+ this.matrixClient.on("accountData", this.onAccountData);
+ }
await this.onSpaceUpdate(); // trigger an initial update
@@ -557,7 +642,10 @@ export class SpaceStoreClass extends AsyncStoreWithClient {
// 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);
- } else if (this.activeSpace && !this.getSpaceFilteredRoomIds(this.activeSpace).has(roomId)) {
+ } else if (
+ (!SettingsStore.getValue("feature_spaces.all_rooms") || this.activeSpace) &&
+ !this.getSpaceFilteredRoomIds(this.activeSpace).has(roomId)
+ ) {
this.switchToRelatedSpace(roomId);
}
@@ -581,7 +669,7 @@ export class SpaceStoreClass extends AsyncStoreWithClient {
}
}
- public getNotificationState(key: string): SpaceNotificationState {
+ public getNotificationState(key: SpaceKey): SpaceNotificationState {
if (this.notificationStateMap.has(key)) {
return this.notificationStateMap.get(key);
}
diff --git a/src/stores/room-list/SpaceWatcher.ts b/src/stores/room-list/SpaceWatcher.ts
index 0b1b78bc75..a1f7786578 100644
--- a/src/stores/room-list/SpaceWatcher.ts
+++ b/src/stores/room-list/SpaceWatcher.ts
@@ -19,6 +19,7 @@ import { Room } from "matrix-js-sdk/src/models/room";
import { RoomListStoreClass } from "./RoomListStore";
import { SpaceFilterCondition } from "./filters/SpaceFilterCondition";
import SpaceStore, { UPDATE_SELECTED_SPACE } from "../SpaceStore";
+import SettingsStore from "../../settings/SettingsStore";
/**
* Watches for changes in spaces to manage the filter on the provided RoomListStore
@@ -28,6 +29,11 @@ export class SpaceWatcher {
private activeSpace: Room = SpaceStore.instance.activeSpace;
constructor(private store: RoomListStoreClass) {
+ if (!SettingsStore.getValue("feature_spaces.all_rooms")) {
+ this.filter = new SpaceFilterCondition();
+ this.updateFilter();
+ store.addFilter(this.filter);
+ }
SpaceStore.instance.on(UPDATE_SELECTED_SPACE, this.onSelectedSpaceUpdated);
}
@@ -35,7 +41,7 @@ export class SpaceWatcher {
this.activeSpace = activeSpace;
if (this.filter) {
- if (activeSpace) {
+ if (activeSpace || !SettingsStore.getValue("feature_spaces.all_rooms")) {
this.updateFilter();
} else {
this.store.removeFilter(this.filter);
@@ -49,9 +55,11 @@ export class SpaceWatcher {
};
private updateFilter = () => {
- SpaceStore.instance.traverseSpace(this.activeSpace.roomId, roomId => {
- this.store.matrixClient?.getRoom(roomId)?.loadMembersIfNeeded();
- });
+ if (this.activeSpace) {
+ SpaceStore.instance.traverseSpace(this.activeSpace.roomId, roomId => {
+ this.store.matrixClient?.getRoom(roomId)?.loadMembersIfNeeded();
+ });
+ }
this.filter.updateSpace(this.activeSpace);
};
}
diff --git a/src/stores/room-list/filters/SpaceFilterCondition.ts b/src/stores/room-list/filters/SpaceFilterCondition.ts
index 79e258927d..0e6965d843 100644
--- a/src/stores/room-list/filters/SpaceFilterCondition.ts
+++ b/src/stores/room-list/filters/SpaceFilterCondition.ts
@@ -19,7 +19,7 @@ import { Room } from "matrix-js-sdk/src/models/room";
import { FILTER_CHANGED, FilterKind, IFilterCondition } from "./IFilterCondition";
import { IDestroyable } from "../../../utils/IDestroyable";
-import SpaceStore from "../../SpaceStore";
+import SpaceStore, { HOME_SPACE } from "../../SpaceStore";
import { setHasDiff } from "../../../utils/sets";
/**
@@ -55,12 +55,10 @@ export class SpaceFilterCondition extends EventEmitter implements IFilterConditi
}
};
- private getSpaceEventKey = (space: Room) => space.roomId;
+ private getSpaceEventKey = (space: Room | null) => space ? space.roomId : HOME_SPACE;
public updateSpace(space: Room) {
- if (this.space) {
- SpaceStore.instance.off(this.getSpaceEventKey(this.space), this.onStoreUpdate);
- }
+ SpaceStore.instance.off(this.getSpaceEventKey(this.space), this.onStoreUpdate);
SpaceStore.instance.on(this.getSpaceEventKey(this.space = space), this.onStoreUpdate);
this.onStoreUpdate(); // initial update from the change to the space
}
diff --git a/test/stores/SpaceStore-test.ts b/test/stores/SpaceStore-test.ts
index 01bd528b87..4cbd9f43c8 100644
--- a/test/stores/SpaceStore-test.ts
+++ b/test/stores/SpaceStore-test.ts
@@ -123,8 +123,15 @@ describe("SpaceStore", () => {
jest.runAllTimers();
client.getVisibleRooms.mockReturnValue(rooms = []);
getValue.mockImplementation(settingName => {
- if (settingName === "feature_spaces") {
- return true;
+ switch (settingName) {
+ case "feature_spaces":
+ return true;
+ case "feature_spaces.all_rooms":
+ return true;
+ case "feature_spaces.space_member_dms":
+ return true;
+ case "feature_spaces.space_dm_badges":
+ return false;
}
});
});