From 90d380c8aeb686963dfdef616b2bbf8222e74687 Mon Sep 17 00:00:00 2001 From: Michael Telatynski <7t3chguy@gmail.com> Date: Thu, 15 Jul 2021 08:26:49 +0100 Subject: [PATCH 1/3] Cache value of feature_spaces* flags as they cause page refresh so are immutable --- src/Avatar.ts | 4 +- src/autocomplete/Autocompleter.ts | 5 +-- src/autocomplete/RoomProvider.tsx | 5 ++- src/components/structures/LoggedInView.tsx | 3 +- src/components/structures/MatrixChat.tsx | 8 ++-- src/components/structures/RightPanel.tsx | 3 +- src/components/structures/RoomView.tsx | 9 ++--- src/components/structures/SpaceRoomView.tsx | 5 +-- src/components/structures/UserMenu.tsx | 4 +- .../views/dialogs/ForwardDialog.tsx | 3 +- src/components/views/dialogs/InviteDialog.tsx | 3 +- src/components/views/right_panel/UserInfo.tsx | 17 ++++---- src/components/views/rooms/MemberList.tsx | 5 ++- src/components/views/rooms/RoomList.tsx | 2 +- .../views/rooms/ThirdPartyMemberInfo.tsx | 4 +- src/components/views/spaces/SpacePanel.tsx | 5 +-- src/stores/BreadcrumbsStore.ts | 3 +- src/stores/SpaceStore.tsx | 39 ++++++++++++------- src/stores/room-list/RoomListStore.ts | 11 +++--- src/stores/room-list/SpaceWatcher.ts | 5 +-- src/stores/room-list/algorithms/Algorithm.ts | 3 +- .../room-list/filters/VisibilityProvider.ts | 4 +- 22 files changed, 82 insertions(+), 68 deletions(-) diff --git a/src/Avatar.ts b/src/Avatar.ts index 4c4bd1c2655..198d4162a0f 100644 --- a/src/Avatar.ts +++ b/src/Avatar.ts @@ -21,7 +21,7 @@ import { ResizeMethod } from "matrix-js-sdk/src/@types/partials"; import DMRoomMap from './utils/DMRoomMap'; import { mediaFromMxc } from "./customisations/Media"; -import SettingsStore from "./settings/SettingsStore"; +import SpaceStore from "./stores/SpaceStore"; // Not to be used for BaseAvatar urls as that has similar default avatar fallback already export function avatarUrlForMember( @@ -153,7 +153,7 @@ export function avatarUrlForRoom(room: Room, width: number, height: number, resi } // space rooms cannot be DMs so skip the rest - if (SettingsStore.getValue("feature_spaces") && room.isSpaceRoom()) return null; + if (SpaceStore.spacesEnabled && room.isSpaceRoom()) return null; let otherMember = null; const otherUserId = DMRoomMap.shared().getUserIdForRoomId(room.roomId); diff --git a/src/autocomplete/Autocompleter.ts b/src/autocomplete/Autocompleter.ts index 7ab2ae70eab..acc78465108 100644 --- a/src/autocomplete/Autocompleter.ts +++ b/src/autocomplete/Autocompleter.ts @@ -27,8 +27,8 @@ import EmojiProvider from './EmojiProvider'; import NotifProvider from './NotifProvider'; import { timeout } from "../utils/promise"; import AutocompleteProvider, { ICommand } from "./AutocompleteProvider"; -import SettingsStore from "../settings/SettingsStore"; import SpaceProvider from "./SpaceProvider"; +import SpaceStore from "../stores/SpaceStore"; export interface ISelectionRange { beginning?: boolean; // whether the selection is in the first block of the editor or not @@ -58,8 +58,7 @@ const PROVIDERS = [ DuckDuckGoProvider, ]; -// as the spaces feature is device configurable only, and toggling it refreshes the page, we can do this here -if (SettingsStore.getValue("feature_spaces")) { +if (SpaceStore.spacesEnabled) { PROVIDERS.push(SpaceProvider); } else { PROVIDERS.push(CommunityProvider); diff --git a/src/autocomplete/RoomProvider.tsx b/src/autocomplete/RoomProvider.tsx index 7865a76daa9..37ddf2c387e 100644 --- a/src/autocomplete/RoomProvider.tsx +++ b/src/autocomplete/RoomProvider.tsx @@ -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 SettingsStore from "../settings/SettingsStore"; +import SpaceStore from "../stores/SpaceStore"; const ROOM_REGEX = /\B#\S*/g; @@ -59,7 +59,8 @@ export default class RoomProvider extends AutocompleteProvider { const cli = MatrixClientPeg.get(); let rooms = cli.getVisibleRooms(); - if (SettingsStore.getValue("feature_spaces")) { + // if spaces are enabled then filter them out here as they get their own autocomplete provider + if (SpaceStore.spacesEnabled) { rooms = rooms.filter(r => !r.isSpaceRoom()); } diff --git a/src/components/structures/LoggedInView.tsx b/src/components/structures/LoggedInView.tsx index 89fa8db3762..6c086ed17c2 100644 --- a/src/components/structures/LoggedInView.tsx +++ b/src/components/structures/LoggedInView.tsx @@ -63,6 +63,7 @@ import ToastContainer from './ToastContainer'; import MyGroups from "./MyGroups"; import UserView from "./UserView"; import GroupView from "./GroupView"; +import SpaceStore from "../../stores/SpaceStore"; // We need to fetch each pinned message individually (if we don't already have it) // so each pinned message may trigger a request. Limit the number per room for sanity. @@ -631,7 +632,7 @@ class LoggedInView extends React.Component { >
- { SettingsStore.getValue("feature_spaces") ? : null } + { SpaceStore.spacesEnabled ? : null } { private leaveRoomWarnings(roomId: string) { const roomToLeave = MatrixClientPeg.get().getRoom(roomId); - const isSpace = SettingsStore.getValue("feature_spaces") && roomToLeave?.isSpaceRoom(); + const isSpace = SpaceStore.spacesEnabled && roomToLeave?.isSpaceRoom(); // Show a warning if there are additional complications. const warnings = []; @@ -1137,7 +1137,7 @@ export default class MatrixChat extends React.PureComponent { const roomToLeave = MatrixClientPeg.get().getRoom(roomId); const warnings = this.leaveRoomWarnings(roomId); - const isSpace = SettingsStore.getValue("feature_spaces") && roomToLeave?.isSpaceRoom(); + const isSpace = SpaceStore.spacesEnabled && roomToLeave?.isSpaceRoom(); Modal.createTrackedDialog(isSpace ? "Leave space" : "Leave room", '', QuestionDialog, { title: isSpace ? _t("Leave space") : _t("Leave room"), description: ( @@ -1687,7 +1687,7 @@ export default class MatrixChat extends React.PureComponent { const type = screen === "start_sso" ? "sso" : "cas"; PlatformPeg.get().startSingleSignOn(cli, type, this.getFragmentAfterLogin()); } else if (screen === 'groups') { - if (SettingsStore.getValue("feature_spaces")) { + if (SpaceStore.spacesEnabled) { dis.dispatch({ action: "view_home_page" }); return; } @@ -1774,7 +1774,7 @@ export default class MatrixChat extends React.PureComponent { subAction: params.action, }); } else if (screen.indexOf('group/') === 0) { - if (SettingsStore.getValue("feature_spaces")) { + if (SpaceStore.spacesEnabled) { dis.dispatch({ action: "view_home_page" }); return; } diff --git a/src/components/structures/RightPanel.tsx b/src/components/structures/RightPanel.tsx index 63027ab627f..2a3448b0177 100644 --- a/src/components/structures/RightPanel.tsx +++ b/src/components/structures/RightPanel.tsx @@ -48,6 +48,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"; interface IProps { room?: Room; // if showing panels for a given room, this is set @@ -107,7 +108,7 @@ export default class RightPanel extends React.Component { return RightPanelPhases.GroupMemberList; } return rps.groupPanelPhase; - } else if (SettingsStore.getValue("feature_spaces") && this.props.room?.isSpaceRoom() + } else if (SpaceStore.spacesEnabled && this.props.room?.isSpaceRoom() && !RIGHT_PANEL_SPACE_PHASES.includes(rps.roomPanelPhase) ) { return RightPanelPhases.SpaceMemberList; diff --git a/src/components/structures/RoomView.tsx b/src/components/structures/RoomView.tsx index 2c118149a0e..a8f9e7ccb6e 100644 --- a/src/components/structures/RoomView.tsx +++ b/src/components/structures/RoomView.tsx @@ -89,6 +89,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"; const DEBUG = false; let debuglog = function(msg: string) {}; @@ -1748,10 +1749,8 @@ export default class RoomView extends React.Component { } const myMembership = this.state.room.getMyMembership(); - if (myMembership === "invite" - // SpaceRoomView handles invites itself - && (!SettingsStore.getValue("feature_spaces") || !this.state.room.isSpaceRoom()) - ) { + // SpaceRoomView handles invites itself + if (myMembership === "invite" && (!SpaceStore.spacesEnabled || !this.state.room.isSpaceRoom())) { if (this.state.joining || this.state.rejecting) { return ( @@ -1882,7 +1881,7 @@ export default class RoomView extends React.Component { room={this.state.room} /> ); - if (!this.state.canPeek && (!SettingsStore.getValue("feature_spaces") || !this.state.room?.isSpaceRoom())) { + if (!this.state.canPeek && (!SpaceStore.spacesEnabled || !this.state.room?.isSpaceRoom())) { return (
{ previewBar } diff --git a/src/components/structures/SpaceRoomView.tsx b/src/components/structures/SpaceRoomView.tsx index 24b460284ff..0ee68a95781 100644 --- a/src/components/structures/SpaceRoomView.tsx +++ b/src/components/structures/SpaceRoomView.tsx @@ -62,7 +62,6 @@ import IconizedContextMenu, { import AccessibleTooltipButton from "../views/elements/AccessibleTooltipButton"; import { BetaPill } from "../views/beta/BetaCard"; import { UserTab } from "../views/dialogs/UserSettingsDialog"; -import SettingsStore from "../../settings/SettingsStore"; import Modal from "../../Modal"; import BetaFeedbackDialog from "../views/dialogs/BetaFeedbackDialog"; import SdkConfig from "../../SdkConfig"; @@ -178,7 +177,7 @@ const SpacePreview = ({ space, onJoinButtonClicked, onRejectButtonClicked }) => const [busy, setBusy] = useState(false); - const spacesEnabled = SettingsStore.getValue("feature_spaces"); + const spacesEnabled = SpaceStore.spacesEnabled; const cannotJoin = getEffectiveMembership(myMembership) === EffectiveMembership.Leave && space.getJoinRule() !== JoinRule.Public; @@ -854,7 +853,7 @@ export default class SpaceRoomView extends React.PureComponent { private renderBody() { switch (this.state.phase) { case Phase.Landing: - if (this.state.myMembership === "join" && SettingsStore.getValue("feature_spaces")) { + if (this.state.myMembership === "join" && SpaceStore.spacesEnabled) { return ; } else { return { }; OwnProfileStore.instance.on(UPDATE_EVENT, this.onProfileUpdate); - if (SettingsStore.getValue("feature_spaces")) { + if (SpaceStore.spacesEnabled) { SpaceStore.instance.on(UPDATE_SELECTED_SPACE, this.onSelectedSpaceUpdate); } @@ -115,7 +115,7 @@ export default class UserMenu extends React.Component { if (this.dispatcherRef) defaultDispatcher.unregister(this.dispatcherRef); OwnProfileStore.instance.off(UPDATE_EVENT, this.onProfileUpdate); this.tagStoreRef.remove(); - if (SettingsStore.getValue("feature_spaces")) { + if (SpaceStore.spacesEnabled) { SpaceStore.instance.off(UPDATE_SELECTED_SPACE, this.onSelectedSpaceUpdate); } MatrixClientPeg.get().removeListener("Room", this.onRoom); diff --git a/src/components/views/dialogs/ForwardDialog.tsx b/src/components/views/dialogs/ForwardDialog.tsx index ba06436ae25..839ca6da2f9 100644 --- a/src/components/views/dialogs/ForwardDialog.tsx +++ b/src/components/views/dialogs/ForwardDialog.tsx @@ -43,6 +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"; const AVATAR_SIZE = 30; @@ -180,7 +181,7 @@ const ForwardDialog: React.FC = ({ matrixClient: cli, event, permalinkCr const [query, setQuery] = useState(""); const lcQuery = query.toLowerCase(); - const spacesEnabled = useFeatureEnabled("feature_spaces"); + const spacesEnabled = SpaceStore.spacesEnabled; const flairEnabled = useFeatureEnabled(UIFeature.Flair); const previewLayout = useSettingValue("layout"); diff --git a/src/components/views/dialogs/InviteDialog.tsx b/src/components/views/dialogs/InviteDialog.tsx index c9475d48490..2aa14449dfb 100644 --- a/src/components/views/dialogs/InviteDialog.tsx +++ b/src/components/views/dialogs/InviteDialog.tsx @@ -67,6 +67,7 @@ import GenericTextContextMenu from "../context_menus/GenericTextContextMenu"; import QuestionDialog from "./QuestionDialog"; import Spinner from "../elements/Spinner"; import BaseDialog from "./BaseDialog"; +import SpaceStore from "../../../stores/SpaceStore"; // we have a number of types defined from the Matrix spec which can't reasonably be altered here. /* eslint-disable camelcase */ @@ -1364,7 +1365,7 @@ export default class InviteDialog extends React.PureComponent; } else if (this.props.kind === KIND_INVITE) { const room = MatrixClientPeg.get()?.getRoom(this.props.roomId); - const isSpace = SettingsStore.getValue("feature_spaces") && room?.isSpaceRoom(); + const isSpace = SpaceStore.spacesEnabled && room?.isSpaceRoom(); title = isSpace ? _t("Invite to %(spaceName)s", { spaceName: room.name || _t("Unnamed Space"), diff --git a/src/components/views/right_panel/UserInfo.tsx b/src/components/views/right_panel/UserInfo.tsx index e9d80d49c52..fc3814136de 100644 --- a/src/components/views/right_panel/UserInfo.tsx +++ b/src/components/views/right_panel/UserInfo.tsx @@ -69,6 +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"; export interface IDevice { deviceId: string; @@ -728,7 +729,7 @@ const MuteToggleButton: React.FC = ({ member, room, powerLevels, // if muting self, warn as it may be irreversible if (target === cli.getUserId()) { try { - if (!(await warnSelfDemote(SettingsStore.getValue("feature_spaces") && room?.isSpaceRoom()))) return; + if (!(await warnSelfDemote(SpaceStore.spacesEnabled && room?.isSpaceRoom()))) return; } catch (e) { console.error("Failed to warn about self demotion: ", e); return; @@ -817,7 +818,7 @@ const RoomAdminToolsContainer: React.FC = ({ if (canAffectUser && me.powerLevel >= kickPowerLevel) { kickButton = ; } - if (me.powerLevel >= redactPowerLevel && (!SettingsStore.getValue("feature_spaces") || !room.isSpaceRoom())) { + if (me.powerLevel >= redactPowerLevel && (!SpaceStore.spacesEnabled || !room.isSpaceRoom())) { redactButton = ( ); @@ -1096,7 +1097,7 @@ const PowerLevelEditor: React.FC<{ } else if (myUserId === target) { // If we are changing our own PL it can only ever be decreasing, which we cannot reverse. try { - if (!(await warnSelfDemote(SettingsStore.getValue("feature_spaces") && room?.isSpaceRoom()))) return; + if (!(await warnSelfDemote(SpaceStore.spacesEnabled && room?.isSpaceRoom()))) return; } catch (e) { console.error("Failed to warn about self demotion: ", e); } @@ -1326,10 +1327,10 @@ const BasicUserInfo: React.FC<{ if (!isRoomEncrypted) { if (!cryptoEnabled) { text = _t("This client does not support end-to-end encryption."); - } else if (room && (!SettingsStore.getValue("feature_spaces") || !room.isSpaceRoom())) { + } else if (room && (!SpaceStore.spacesEnabled || !room.isSpaceRoom())) { text = _t("Messages in this room are not end-to-end encrypted."); } - } else if (!SettingsStore.getValue("feature_spaces") || !room.isSpaceRoom()) { + } else if (!SpaceStore.spacesEnabled || !room.isSpaceRoom()) { text = _t("Messages in this room are end-to-end encrypted."); } @@ -1405,7 +1406,7 @@ const BasicUserInfo: React.FC<{ canInvite={roomPermissions.canInvite} isIgnored={isIgnored} member={member as RoomMember} - isSpace={SettingsStore.getValue("feature_spaces") && room?.isSpaceRoom()} + isSpace={SpaceStore.spacesEnabled && room?.isSpaceRoom()} /> { adminToolsContainer } @@ -1568,7 +1569,7 @@ const UserInfo: React.FC = ({ previousPhase = RightPanelPhases.RoomMemberInfo; refireParams = { member: member }; } else if (room) { - previousPhase = previousPhase = SettingsStore.getValue("feature_spaces") && room.isSpaceRoom() + previousPhase = previousPhase = SpaceStore.spacesEnabled && room.isSpaceRoom() ? RightPanelPhases.SpaceMemberList : RightPanelPhases.RoomMemberList; } @@ -1617,7 +1618,7 @@ const UserInfo: React.FC = ({ } let scopeHeader; - if (SettingsStore.getValue("feature_spaces") && room?.isSpaceRoom()) { + if (SpaceStore.spacesEnabled && room?.isSpaceRoom()) { scopeHeader =
diff --git a/src/components/views/rooms/MemberList.tsx b/src/components/views/rooms/MemberList.tsx index f4df70c7ee3..71e54404c05 100644 --- a/src/components/views/rooms/MemberList.tsx +++ b/src/components/views/rooms/MemberList.tsx @@ -43,6 +43,7 @@ import EntityTile from "./EntityTile"; import MemberTile from "./MemberTile"; import BaseAvatar from '../avatars/BaseAvatar'; import { throttle } from 'lodash'; +import SpaceStore from "../../../stores/SpaceStore"; const INITIAL_LOAD_NUM_MEMBERS = 30; const INITIAL_LOAD_NUM_INVITED = 5; @@ -509,7 +510,7 @@ export default class MemberList extends React.Component { const chat = CommunityPrototypeStore.instance.getSelectedCommunityGeneralChat(); if (chat && chat.roomId === this.props.roomId) { inviteButtonText = _t("Invite to this community"); - } else if (SettingsStore.getValue("feature_spaces") && room.isSpaceRoom()) { + } else if (SpaceStore.spacesEnabled && room.isSpaceRoom()) { inviteButtonText = _t("Invite to this space"); } @@ -549,7 +550,7 @@ export default class MemberList extends React.Component { let previousPhase = RightPanelPhases.RoomSummary; // We have no previousPhase for when viewing a MemberList from a Space let scopeHeader; - if (SettingsStore.getValue("feature_spaces") && room?.isSpaceRoom()) { + if (SpaceStore.spacesEnabled && room?.isSpaceRoom()) { previousPhase = undefined; scopeHeader =
diff --git a/src/components/views/rooms/RoomList.tsx b/src/components/views/rooms/RoomList.tsx index c94256800db..7ece6add9c4 100644 --- a/src/components/views/rooms/RoomList.tsx +++ b/src/components/views/rooms/RoomList.tsx @@ -417,7 +417,7 @@ export default class RoomList extends React.PureComponent { } private renderCommunityInvites(): ReactComponentElement[] { - if (SettingsStore.getValue("feature_spaces")) return []; + if (SpaceStore.spacesEnabled) return []; // TODO: Put community invites in a more sensible place (not in the room list) // See https://github.com/vector-im/element-web/issues/14456 return MatrixClientPeg.get().getGroups().filter(g => { diff --git a/src/components/views/rooms/ThirdPartyMemberInfo.tsx b/src/components/views/rooms/ThirdPartyMemberInfo.tsx index 2bcc3ead57f..51bb891c629 100644 --- a/src/components/views/rooms/ThirdPartyMemberInfo.tsx +++ b/src/components/views/rooms/ThirdPartyMemberInfo.tsx @@ -25,9 +25,9 @@ import { isValid3pidInvite } from "../../../RoomInvite"; import RoomAvatar from "../avatars/RoomAvatar"; import RoomName from "../elements/RoomName"; import { replaceableComponent } from "../../../utils/replaceableComponent"; -import SettingsStore from "../../../settings/SettingsStore"; import ErrorDialog from '../dialogs/ErrorDialog'; import AccessibleButton from '../elements/AccessibleButton'; +import SpaceStore from "../../../stores/SpaceStore"; interface IProps { event: MatrixEvent; @@ -134,7 +134,7 @@ export default class ThirdPartyMemberInfo extends React.Component diff --git a/src/components/views/spaces/SpacePanel.tsx b/src/components/views/spaces/SpacePanel.tsx index 5b3cf31cad5..9cefbbd94c2 100644 --- a/src/components/views/spaces/SpacePanel.tsx +++ b/src/components/views/spaces/SpacePanel.tsx @@ -42,7 +42,6 @@ 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; @@ -134,7 +133,7 @@ const InnerSpacePanel = React.memo(({ children, isPanelCo const [invites, spaces, activeSpace] = useSpaces(); const activeSpaces = activeSpace ? [activeSpace] : []; - const homeNotificationState = SettingsStore.getValue("feature_spaces.all_rooms") + const homeNotificationState = SpaceStore.spacesTweakAllRoomsEnabled ? RoomNotificationStateStore.instance.globalState : SpaceStore.instance.getNotificationState(HOME_SPACE); return
@@ -142,7 +141,7 @@ const InnerSpacePanel = React.memo(({ children, isPanelCo className="mx_SpaceButton_home" onClick={() => SpaceStore.instance.setActiveSpace(null)} selected={!activeSpace} - tooltip={SettingsStore.getValue("feature_spaces.all_rooms") ? _t("All rooms") : _t("Home")} + tooltip={SpaceStore.spacesTweakAllRoomsEnabled ? _t("All rooms") : _t("Home")} notificationState={homeNotificationState} isNarrow={isPanelCollapsed} /> diff --git a/src/stores/BreadcrumbsStore.ts b/src/stores/BreadcrumbsStore.ts index a3b07435c64..aceaf8b8988 100644 --- a/src/stores/BreadcrumbsStore.ts +++ b/src/stores/BreadcrumbsStore.ts @@ -22,6 +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"; const MAX_ROOMS = 20; // arbitrary const AUTOJOIN_WAIT_THRESHOLD_MS = 90000; // 90s, the time we wait for an autojoined room to show up @@ -122,7 +123,7 @@ export class BreadcrumbsStore extends AsyncStoreWithClient { } private async appendRoom(room: Room) { - if (SettingsStore.getValue("feature_spaces") && room.isSpaceRoom()) return; // hide space rooms + if (SpaceStore.spacesEnabled && room.isSpaceRoom()) return; // hide space rooms let updated = false; const rooms = (this.state.rooms || []).slice(); // cheap clone diff --git a/src/stores/SpaceStore.tsx b/src/stores/SpaceStore.tsx index 99705a7abab..1a6b5109ec2 100644 --- a/src/stores/SpaceStore.tsx +++ b/src/stores/SpaceStore.tsx @@ -59,7 +59,13 @@ export interface ISuggestedRoom extends ISpaceSummaryRoom { const MAX_SUGGESTED_ROOMS = 20; -const homeSpaceKey = SettingsStore.getValue("feature_spaces.all_rooms") ? "ALL_ROOMS" : "HOME_SPACE"; +// All of these settings cause the page to reload and can be costly if read frequently, so read them here only +const spacesEnabled = SettingsStore.getValue("feature_spaces"); +const spacesTweakAllRoomsEnabled = SettingsStore.getValue("feature_spaces.all_rooms"); +const spacesTweakSpaceMemberDMsEnabled = SettingsStore.getValue("feature_spaces.space_member_dms"); +const spacesTweakSpaceDMBadgesEnabled = SettingsStore.getValue("feature_spaces.space_dm_badges"); + +const homeSpaceKey = spacesTweakAllRoomsEnabled ? "ALL_ROOMS" : "HOME_SPACE"; const getSpaceContextKey = (space?: Room) => `mx_space_context_${space?.roomId || homeSpaceKey}`; const partitionSpacesAndRooms = (arr: Room[]): [Room[], Room[]] => { // [spaces, rooms] @@ -260,7 +266,7 @@ export class SpaceStoreClass extends AsyncStoreWithClient { } public getSpaceFilteredRoomIds = (space: Room | null): Set => { - if (!space && SettingsStore.getValue("feature_spaces.all_rooms")) { + if (!space && spacesTweakAllRoomsEnabled) { return new Set(this.matrixClient.getVisibleRooms().map(r => r.roomId)); } return this.spaceFilteredRooms.get(space?.roomId || HOME_SPACE) || new Set(); @@ -357,7 +363,7 @@ export class SpaceStoreClass extends AsyncStoreWithClient { }; private showInHomeSpace = (room: Room) => { - if (SettingsStore.getValue("feature_spaces.all_rooms")) return true; + if (spacesTweakAllRoomsEnabled) 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 @@ -389,7 +395,7 @@ export class SpaceStoreClass extends AsyncStoreWithClient { const oldFilteredRooms = this.spaceFilteredRooms; this.spaceFilteredRooms = new Map(); - if (!SettingsStore.getValue("feature_spaces.all_rooms")) { + if (!spacesTweakAllRoomsEnabled) { // 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))); @@ -416,7 +422,7 @@ export class SpaceStoreClass extends AsyncStoreWithClient { const roomIds = new Set(childRooms.map(r => r.roomId)); const space = this.matrixClient?.getRoom(spaceId); - if (SettingsStore.getValue("feature_spaces.space_member_dms")) { + if (spacesTweakSpaceMemberDMsEnabled) { // Add relevant DMs space?.getMembers().forEach(member => { if (member.membership !== "join" && member.membership !== "invite") return; @@ -450,7 +456,7 @@ 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; + if (s !== HOME_SPACE && spacesTweakSpaceDMBadgesEnabled) return true; return !DMRoomMap.shared().getUserIdForRoomId(room.roomId) || RoomListStore.instance.getTagsForRoom(room).includes(DefaultTagID.Favourite); @@ -549,7 +555,7 @@ 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")) { + } else if (!spacesTweakAllRoomsEnabled) { this.onRoomUpdate(room); } this.emit(room.roomId); @@ -573,7 +579,7 @@ export class SpaceStoreClass extends AsyncStoreWithClient { if (order !== lastOrder) { this.notifyIfOrderChanged(); } - } else if (ev.getType() === EventType.Tag && !SettingsStore.getValue("feature_spaces.all_rooms")) { + } else if (ev.getType() === EventType.Tag && !spacesTweakAllRoomsEnabled) { // 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 || {}; @@ -613,13 +619,13 @@ export class SpaceStoreClass extends AsyncStoreWithClient { } protected async onNotReady() { - if (!SettingsStore.getValue("feature_spaces")) return; + if (!SpaceStore.spacesEnabled) return; if (this.matrixClient) { this.matrixClient.removeListener("Room", this.onRoom); this.matrixClient.removeListener("Room.myMembership", this.onRoom); this.matrixClient.removeListener("Room.accountData", this.onRoomAccountData); this.matrixClient.removeListener("RoomState.events", this.onRoomState); - if (!SettingsStore.getValue("feature_spaces.all_rooms")) { + if (!spacesTweakAllRoomsEnabled) { this.matrixClient.removeListener("accountData", this.onAccountData); } } @@ -627,12 +633,12 @@ export class SpaceStoreClass extends AsyncStoreWithClient { } protected async onReady() { - if (!SettingsStore.getValue("feature_spaces")) return; + if (!spacesEnabled) return; this.matrixClient.on("Room", this.onRoom); this.matrixClient.on("Room.myMembership", this.onRoom); this.matrixClient.on("Room.accountData", this.onRoomAccountData); this.matrixClient.on("RoomState.events", this.onRoomState); - if (!SettingsStore.getValue("feature_spaces.all_rooms")) { + if (!spacesTweakAllRoomsEnabled) { this.matrixClient.on("accountData", this.onAccountData); } @@ -646,7 +652,7 @@ export class SpaceStoreClass extends AsyncStoreWithClient { } protected async onAction(payload: ActionPayload) { - if (!SettingsStore.getValue("feature_spaces")) return; + if (!spacesEnabled) return; switch (payload.action) { case "view_room": { // Don't auto-switch rooms when reacting to a context-switch @@ -660,7 +666,7 @@ export class SpaceStoreClass extends AsyncStoreWithClient { // as it will cause you to end up in the wrong room this.setActiveSpace(room, false); } else if ( - (!SettingsStore.getValue("feature_spaces.all_rooms") || this.activeSpace) && + (!spacesTweakAllRoomsEnabled || this.activeSpace) && !this.getSpaceFilteredRoomIds(this.activeSpace).has(roomId) ) { this.switchToRelatedSpace(roomId); @@ -752,6 +758,11 @@ export class SpaceStoreClass extends AsyncStoreWithClient { } export default class SpaceStore { + public static spacesEnabled = spacesEnabled; + public static spacesTweakAllRoomsEnabled = spacesTweakAllRoomsEnabled; + public static spacesTweakSpaceMemberDMsEnabled = spacesTweakSpaceMemberDMsEnabled; + public static spacesTweakSpaceDMBadgesEnabled = spacesTweakSpaceDMBadgesEnabled; + private static internalInstance = new SpaceStoreClass(); public static get instance(): SpaceStoreClass { diff --git a/src/stores/room-list/RoomListStore.ts b/src/stores/room-list/RoomListStore.ts index e26c80bb2da..a87e45acb71 100644 --- a/src/stores/room-list/RoomListStore.ts +++ b/src/stores/room-list/RoomListStore.ts @@ -35,6 +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"; interface IState { tagsEnabled?: boolean; @@ -76,7 +77,7 @@ export class RoomListStoreClass extends AsyncStoreWithClient { } private setupWatchers() { - if (SettingsStore.getValue("feature_spaces")) { + if (SpaceStore.spacesEnabled) { this.spaceWatcher = new SpaceWatcher(this); } else { this.tagWatcher = new TagWatcher(this); @@ -608,9 +609,7 @@ export class RoomListStoreClass extends AsyncStoreWithClient { // if spaces are enabled only consider the prefilter conditions when there are no runtime conditions // for the search all spaces feature - if (this.prefilterConditions.length > 0 - && (!SettingsStore.getValue("feature_spaces") || !this.filterConditions.length) - ) { + if (this.prefilterConditions.length > 0 && (!SpaceStore.spacesEnabled || !this.filterConditions.length)) { rooms = rooms.filter(r => { for (const filter of this.prefilterConditions) { if (!filter.isVisible(r)) { @@ -682,7 +681,7 @@ export class RoomListStoreClass extends AsyncStoreWithClient { } else { this.filterConditions.push(filter); // Runtime filters with spaces disable prefiltering for the search all spaces feature - if (SettingsStore.getValue("feature_spaces")) { + if (SpaceStore.spacesEnabled) { // this has to be awaited so that `setKnownRooms` is called in time for the `addFilterCondition` below // this way the runtime filters are only evaluated on one dataset and not both. await this.recalculatePrefiltering(); @@ -715,7 +714,7 @@ export class RoomListStoreClass extends AsyncStoreWithClient { this.algorithm.removeFilterCondition(filter); } // Runtime filters with spaces disable prefiltering for the search all spaces feature - if (SettingsStore.getValue("feature_spaces")) { + if (SpaceStore.spacesEnabled) { promise = this.recalculatePrefiltering(); } } diff --git a/src/stores/room-list/SpaceWatcher.ts b/src/stores/room-list/SpaceWatcher.ts index a1f77865784..1cec612e6f2 100644 --- a/src/stores/room-list/SpaceWatcher.ts +++ b/src/stores/room-list/SpaceWatcher.ts @@ -19,7 +19,6 @@ 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 @@ -29,7 +28,7 @@ export class SpaceWatcher { private activeSpace: Room = SpaceStore.instance.activeSpace; constructor(private store: RoomListStoreClass) { - if (!SettingsStore.getValue("feature_spaces.all_rooms")) { + if (!SpaceStore.spacesTweakAllRoomsEnabled) { this.filter = new SpaceFilterCondition(); this.updateFilter(); store.addFilter(this.filter); @@ -41,7 +40,7 @@ export class SpaceWatcher { this.activeSpace = activeSpace; if (this.filter) { - if (activeSpace || !SettingsStore.getValue("feature_spaces.all_rooms")) { + if (activeSpace || !SpaceStore.spacesTweakAllRoomsEnabled) { this.updateFilter(); } else { this.store.removeFilter(this.filter); diff --git a/src/stores/room-list/algorithms/Algorithm.ts b/src/stores/room-list/algorithms/Algorithm.ts index 024c484c411..f50d112248b 100644 --- a/src/stores/room-list/algorithms/Algorithm.ts +++ b/src/stores/room-list/algorithms/Algorithm.ts @@ -34,6 +34,7 @@ import { OrderingAlgorithm } from "./list-ordering/OrderingAlgorithm"; import { getListAlgorithmInstance } from "./list-ordering"; import SettingsStore from "../../../settings/SettingsStore"; import { VisibilityProvider } from "../filters/VisibilityProvider"; +import SpaceStore from "../../SpaceStore"; /** * Fired when the Algorithm has determined a list has been updated. @@ -199,7 +200,7 @@ export class Algorithm extends EventEmitter { } private async doUpdateStickyRoom(val: Room) { - if (SettingsStore.getValue("feature_spaces") && val?.isSpaceRoom() && val.getMyMembership() !== "invite") { + if (SpaceStore.spacesEnabled && val?.isSpaceRoom() && val.getMyMembership() !== "invite") { // no-op sticky rooms for spaces - they're effectively virtual rooms val = null; } diff --git a/src/stores/room-list/filters/VisibilityProvider.ts b/src/stores/room-list/filters/VisibilityProvider.ts index a6c55226b0b..f63b622053e 100644 --- a/src/stores/room-list/filters/VisibilityProvider.ts +++ b/src/stores/room-list/filters/VisibilityProvider.ts @@ -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 SettingsStore from "../../../settings/SettingsStore"; +import SpaceStore from "../../SpaceStore"; export class VisibilityProvider { private static internalInstance: VisibilityProvider; @@ -50,7 +50,7 @@ export class VisibilityProvider { } // hide space rooms as they'll be shown in the SpacePanel - if (SettingsStore.getValue("feature_spaces") && room.isSpaceRoom()) { + if (SpaceStore.spacesEnabled && room.isSpaceRoom()) { return false; } From 80f9793c733866a4292103bb1a8f52febba32bed Mon Sep 17 00:00:00 2001 From: Michael Telatynski <7t3chguy@gmail.com> Date: Thu, 15 Jul 2021 08:29:50 +0100 Subject: [PATCH 2/3] only show space beta tweaks if you have the beta enabled as they do nothing otherwise --- src/components/views/beta/BetaCard.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/views/beta/BetaCard.tsx b/src/components/views/beta/BetaCard.tsx index 3127e1a9158..ec662d831b7 100644 --- a/src/components/views/beta/BetaCard.tsx +++ b/src/components/views/beta/BetaCard.tsx @@ -105,7 +105,7 @@ const BetaCard = ({ title: titleOverride, featureId }: IProps) => {
- { extraSettings &&
+ { extraSettings && value &&
{ extraSettings.map(key => ( )) } From 316b21408dc42c81b1933df05ac5d4cbe8839322 Mon Sep 17 00:00:00 2001 From: Michael Telatynski <7t3chguy@gmail.com> Date: Thu, 15 Jul 2021 10:59:52 +0100 Subject: [PATCH 3/3] Fix tests --- test/editor/caret-test.js | 1 + test/editor/model-test.js | 1 + test/editor/operations-test.js | 1 + test/editor/position-test.js | 1 + test/editor/range-test.js | 1 + test/editor/serialize-test.js | 1 + test/stores/SpaceStore-setup.ts | 23 +++++++++++++++++++++++ test/stores/SpaceStore-test.ts | 20 ++------------------ 8 files changed, 31 insertions(+), 18 deletions(-) create mode 100644 test/stores/SpaceStore-setup.ts diff --git a/test/editor/caret-test.js b/test/editor/caret-test.js index e1a66a44315..33b40e1c640 100644 --- a/test/editor/caret-test.js +++ b/test/editor/caret-test.js @@ -14,6 +14,7 @@ See the License for the specific language governing permissions and limitations under the License. */ +import "../skinned-sdk"; // Must be first for skinning to work import { getLineAndNodePosition } from "../../src/editor/caret"; import EditorModel from "../../src/editor/model"; import { createPartCreator } from "./mock"; diff --git a/test/editor/model-test.js b/test/editor/model-test.js index 35bd4143a7f..15c5af5806a 100644 --- a/test/editor/model-test.js +++ b/test/editor/model-test.js @@ -14,6 +14,7 @@ See the License for the specific language governing permissions and limitations under the License. */ +import "../skinned-sdk"; // Must be first for skinning to work import EditorModel from "../../src/editor/model"; import { createPartCreator, createRenderer } from "./mock"; diff --git a/test/editor/operations-test.js b/test/editor/operations-test.js index 32ccaa5440b..17a4c8ba118 100644 --- a/test/editor/operations-test.js +++ b/test/editor/operations-test.js @@ -14,6 +14,7 @@ See the License for the specific language governing permissions and limitations under the License. */ +import "../skinned-sdk"; // Must be first for skinning to work import EditorModel from "../../src/editor/model"; import { createPartCreator, createRenderer } from "./mock"; import { toggleInlineFormat } from "../../src/editor/operations"; diff --git a/test/editor/position-test.js b/test/editor/position-test.js index 813a8e9f7f9..ea8658b216f 100644 --- a/test/editor/position-test.js +++ b/test/editor/position-test.js @@ -14,6 +14,7 @@ See the License for the specific language governing permissions and limitations under the License. */ +import "../skinned-sdk"; // Must be first for skinning to work import EditorModel from "../../src/editor/model"; import { createPartCreator } from "./mock"; diff --git a/test/editor/range-test.js b/test/editor/range-test.js index d411a0d9114..87c5b06e44f 100644 --- a/test/editor/range-test.js +++ b/test/editor/range-test.js @@ -14,6 +14,7 @@ See the License for the specific language governing permissions and limitations under the License. */ +import "../skinned-sdk"; // Must be first for skinning to work import EditorModel from "../../src/editor/model"; import { createPartCreator, createRenderer } from "./mock"; diff --git a/test/editor/serialize-test.js b/test/editor/serialize-test.js index 691130bd34e..085a8afdbae 100644 --- a/test/editor/serialize-test.js +++ b/test/editor/serialize-test.js @@ -14,6 +14,7 @@ See the License for the specific language governing permissions and limitations under the License. */ +import "../skinned-sdk"; // Must be first for skinning to work import EditorModel from "../../src/editor/model"; import { htmlSerializeIfNeeded } from "../../src/editor/serialize"; import { createPartCreator } from "./mock"; diff --git a/test/stores/SpaceStore-setup.ts b/test/stores/SpaceStore-setup.ts new file mode 100644 index 00000000000..67d492255fc --- /dev/null +++ b/test/stores/SpaceStore-setup.ts @@ -0,0 +1,23 @@ +/* +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. +*/ + +// This needs to be executed before the SpaceStore gets imported but due to ES6 import hoisting we have to do this here. +// SpaceStore reads the SettingsStore which needs the localStorage values set at init time. + +localStorage.setItem("mx_labs_feature_feature_spaces", "true"); +localStorage.setItem("mx_labs_feature_feature_spaces.all_rooms", "true"); +localStorage.setItem("mx_labs_feature_feature_spaces.space_member_dms", "true"); +localStorage.setItem("mx_labs_feature_feature_spaces.space_dm_badges", "false"); diff --git a/test/stores/SpaceStore-test.ts b/test/stores/SpaceStore-test.ts index 4cbd9f43c8e..eb28a72d677 100644 --- a/test/stores/SpaceStore-test.ts +++ b/test/stores/SpaceStore-test.ts @@ -16,7 +16,9 @@ limitations under the License. import { EventEmitter } from "events"; import { EventType } from "matrix-js-sdk/src/@types/event"; +import { MatrixEvent } from "matrix-js-sdk/src/models/event"; +import "./SpaceStore-setup"; // enable space lab import "../skinned-sdk"; // Must be first for skinning to work import SpaceStore, { UPDATE_INVITED_SPACES, @@ -26,13 +28,10 @@ import SpaceStore, { import { resetAsyncStoreWithClient, setupAsyncStoreWithClient } from "../utils/test-utils"; import { mkEvent, mkStubRoom, stubClient } from "../test-utils"; import { EnhancedMap } from "../../src/utils/maps"; -import SettingsStore from "../../src/settings/SettingsStore"; import DMRoomMap from "../../src/utils/DMRoomMap"; import { MatrixClientPeg } from "../../src/MatrixClientPeg"; import defaultDispatcher from "../../src/dispatcher/dispatcher"; -type MatrixEvent = any; // importing from js-sdk upsets things - jest.useFakeTimers(); const mockStateEventImplementation = (events: MatrixEvent[]) => { @@ -79,9 +78,6 @@ const mkSpace = (spaceId: string, children: string[] = []) => { return space; }; -const getValue = jest.fn(); -SettingsStore.getValue = getValue; - const getUserIdForRoomId = jest.fn(); // @ts-ignore DMRoomMap.sharedInstance = { getUserIdForRoomId }; @@ -122,18 +118,6 @@ describe("SpaceStore", () => { beforeEach(() => { jest.runAllTimers(); client.getVisibleRooms.mockReturnValue(rooms = []); - getValue.mockImplementation(settingName => { - 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; - } - }); }); afterEach(async () => { await resetAsyncStoreWithClient(store);