mirror of
https://github.com/vector-im/element-web.git
synced 2024-11-16 05:04:57 +08:00
Add experimental options to the Spaces beta
This commit is contained in:
parent
bef20dde48
commit
b4fbc791bb
@ -42,7 +42,7 @@ limitations under the License.
|
|||||||
margin-bottom: 20px;
|
margin-bottom: 20px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.mx_AccessibleButton {
|
.mx_BetaCard_buttons .mx_AccessibleButton {
|
||||||
display: block;
|
display: block;
|
||||||
margin: 12px 0;
|
margin: 12px 0;
|
||||||
padding: 7px 40px;
|
padding: 7px 40px;
|
||||||
@ -55,6 +55,12 @@ limitations under the License.
|
|||||||
color: $secondary-fg-color;
|
color: $secondary-fg-color;
|
||||||
margin-top: 20px;
|
margin-top: 20px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.mx_BetaCard_relatedSettings {
|
||||||
|
summary + .mx_SettingsFlag {
|
||||||
|
margin-top: 4px;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
> img {
|
> img {
|
||||||
|
@ -25,6 +25,7 @@ import TextWithTooltip from "../elements/TextWithTooltip";
|
|||||||
import Modal from "../../../Modal";
|
import Modal from "../../../Modal";
|
||||||
import BetaFeedbackDialog from "../dialogs/BetaFeedbackDialog";
|
import BetaFeedbackDialog from "../dialogs/BetaFeedbackDialog";
|
||||||
import SdkConfig from "../../../SdkConfig";
|
import SdkConfig from "../../../SdkConfig";
|
||||||
|
import SettingsFlag from "../elements/SettingsFlag";
|
||||||
|
|
||||||
interface IProps {
|
interface IProps {
|
||||||
title?: string;
|
title?: string;
|
||||||
@ -66,7 +67,7 @@ const BetaCard = ({ title: titleOverride, featureId }: IProps) => {
|
|||||||
const info = SettingsStore.getBetaInfo(featureId);
|
const info = SettingsStore.getBetaInfo(featureId);
|
||||||
if (!info) return null; // Beta is invalid/disabled
|
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);
|
const value = SettingsStore.getValue(featureId);
|
||||||
|
|
||||||
let feedbackButton;
|
let feedbackButton;
|
||||||
@ -88,7 +89,7 @@ const BetaCard = ({ title: titleOverride, featureId }: IProps) => {
|
|||||||
<BetaPill />
|
<BetaPill />
|
||||||
</h3>
|
</h3>
|
||||||
<span className="mx_BetaCard_caption">{ _t(caption) }</span>
|
<span className="mx_BetaCard_caption">{ _t(caption) }</span>
|
||||||
<div>
|
<div className="mx_BetaCard_buttons">
|
||||||
{ feedbackButton }
|
{ feedbackButton }
|
||||||
<AccessibleButton
|
<AccessibleButton
|
||||||
onClick={() => SettingsStore.setValue(featureId, null, SettingLevel.DEVICE, !value)}
|
onClick={() => SettingsStore.setValue(featureId, null, SettingLevel.DEVICE, !value)}
|
||||||
@ -100,6 +101,12 @@ const BetaCard = ({ title: titleOverride, featureId }: IProps) => {
|
|||||||
{ disclaimer && <div className="mx_BetaCard_disclaimer">
|
{ disclaimer && <div className="mx_BetaCard_disclaimer">
|
||||||
{ disclaimer(value) }
|
{ disclaimer(value) }
|
||||||
</div> }
|
</div> }
|
||||||
|
{ extraSettings && <details className="mx_BetaCard_relatedSettings">
|
||||||
|
<summary>{ _t("Experimental options") }</summary>
|
||||||
|
{ extraSettings.map(key => (
|
||||||
|
<SettingsFlag key={key} name={key} level={SettingLevel.DEVICE} />
|
||||||
|
)) }
|
||||||
|
</details> }
|
||||||
</div>
|
</div>
|
||||||
<img src={image} alt="" />
|
<img src={image} alt="" />
|
||||||
</div>;
|
</div>;
|
||||||
|
@ -44,7 +44,12 @@ const BetaFeedbackDialog: React.FC<IProps> = ({featureId, onFinished}) => {
|
|||||||
const sendFeedback = async (ok: boolean) => {
|
const sendFeedback = async (ok: boolean) => {
|
||||||
if (!ok) return onFinished(false);
|
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);
|
onFinished(true);
|
||||||
|
|
||||||
Modal.createTrackedDialog("Beta Dialog Sent", featureId, InfoDialog, {
|
Modal.createTrackedDialog("Beta Dialog Sent", featureId, InfoDialog, {
|
||||||
|
@ -26,6 +26,7 @@ import {SpaceItem} from "./SpaceTreeLevel";
|
|||||||
import AccessibleTooltipButton from "../elements/AccessibleTooltipButton";
|
import AccessibleTooltipButton from "../elements/AccessibleTooltipButton";
|
||||||
import {useEventEmitter} from "../../../hooks/useEventEmitter";
|
import {useEventEmitter} from "../../../hooks/useEventEmitter";
|
||||||
import SpaceStore, {
|
import SpaceStore, {
|
||||||
|
HOME_SPACE,
|
||||||
UPDATE_INVITED_SPACES,
|
UPDATE_INVITED_SPACES,
|
||||||
UPDATE_SELECTED_SPACE,
|
UPDATE_SELECTED_SPACE,
|
||||||
UPDATE_TOP_LEVEL_SPACES,
|
UPDATE_TOP_LEVEL_SPACES,
|
||||||
@ -40,6 +41,7 @@ import {
|
|||||||
import {Key} from "../../../Keyboard";
|
import {Key} from "../../../Keyboard";
|
||||||
import {RoomNotificationStateStore} from "../../../stores/notifications/RoomNotificationStateStore";
|
import {RoomNotificationStateStore} from "../../../stores/notifications/RoomNotificationStateStore";
|
||||||
import {NotificationState} from "../../../stores/notifications/NotificationState";
|
import {NotificationState} from "../../../stores/notifications/NotificationState";
|
||||||
|
import SettingsStore from "../../../settings/SettingsStore";
|
||||||
|
|
||||||
interface IButtonProps {
|
interface IButtonProps {
|
||||||
space?: Room;
|
space?: Room;
|
||||||
@ -205,6 +207,10 @@ const SpacePanel = () => {
|
|||||||
|
|
||||||
const activeSpaces = activeSpace ? [activeSpace] : [];
|
const activeSpaces = activeSpace ? [activeSpace] : [];
|
||||||
const expandCollapseButtonTitle = isPanelCollapsed ? _t("Expand space panel") : _t("Collapse space panel");
|
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
|
// TODO drag and drop for re-arranging order
|
||||||
return <RovingTabIndexProvider handleHomeEnd={true} onKeyDown={onKeyDown}>
|
return <RovingTabIndexProvider handleHomeEnd={true} onKeyDown={onKeyDown}>
|
||||||
{({onKeyDownHandler}) => (
|
{({onKeyDownHandler}) => (
|
||||||
@ -218,8 +224,8 @@ const SpacePanel = () => {
|
|||||||
className="mx_SpaceButton_home"
|
className="mx_SpaceButton_home"
|
||||||
onClick={() => SpaceStore.instance.setActiveSpace(null)}
|
onClick={() => SpaceStore.instance.setActiveSpace(null)}
|
||||||
selected={!activeSpace}
|
selected={!activeSpace}
|
||||||
tooltip={_t("All rooms")}
|
tooltip={SettingsStore.getValue("feature_spaces.all_rooms") ? _t("All rooms") : _t("Home")}
|
||||||
notificationState={RoomNotificationStateStore.instance.globalState}
|
notificationState={homeNotificationState}
|
||||||
isNarrow={isPanelCollapsed}
|
isNarrow={isPanelCollapsed}
|
||||||
/>
|
/>
|
||||||
{ invites.map(s => <SpaceItem
|
{ invites.map(s => <SpaceItem
|
||||||
|
@ -793,6 +793,9 @@
|
|||||||
"You can leave the beta any time from settings or tapping on a beta badge, like the one above.": "You can leave the beta any time from settings or tapping on a beta badge, like the one above.",
|
"You can leave the beta any time from settings or tapping on a beta badge, like the one above.": "You can leave the beta any time from settings or tapping on a beta badge, like the one above.",
|
||||||
"Beta available for web, desktop and Android. Some features may be unavailable on your homeserver.": "Beta available for web, desktop and Android. Some features may be unavailable on your homeserver.",
|
"Beta available for web, desktop and Android. Some features may be unavailable on your homeserver.": "Beta available for web, desktop and Android. Some features may be unavailable on your homeserver.",
|
||||||
"Your feedback will help make spaces better. The more detail you can go into, the better.": "Your feedback will help make spaces better. The more detail you can go into, the better.",
|
"Your feedback will help make spaces better. The more detail you can go into, the better.": "Your feedback will help make spaces better. The more detail you can go into, the better.",
|
||||||
|
"Use an all rooms space instead of a home space.": "Use an all rooms space instead of a home space.",
|
||||||
|
"Show DMs for joined/invited members in the space.": "Show DMs for joined/invited members in the space.",
|
||||||
|
"Show notification badges for DMs in spaces.": "Show notification badges for DMs in spaces.",
|
||||||
"Show options to enable 'Do not disturb' mode": "Show options to enable 'Do not disturb' mode",
|
"Show options to enable 'Do not disturb' mode": "Show options to enable 'Do not disturb' mode",
|
||||||
"Send and receive voice messages": "Send and receive voice messages",
|
"Send and receive voice messages": "Send and receive voice messages",
|
||||||
"Render LaTeX maths in messages": "Render LaTeX maths in messages",
|
"Render LaTeX maths in messages": "Render LaTeX maths in messages",
|
||||||
@ -1021,6 +1024,7 @@
|
|||||||
"Expand space panel": "Expand space panel",
|
"Expand space panel": "Expand space panel",
|
||||||
"Collapse space panel": "Collapse space panel",
|
"Collapse space panel": "Collapse space panel",
|
||||||
"All rooms": "All rooms",
|
"All rooms": "All rooms",
|
||||||
|
"Home": "Home",
|
||||||
"Click to copy": "Click to copy",
|
"Click to copy": "Click to copy",
|
||||||
"Copied!": "Copied!",
|
"Copied!": "Copied!",
|
||||||
"Failed to copy": "Failed to copy",
|
"Failed to copy": "Failed to copy",
|
||||||
@ -2027,7 +2031,6 @@
|
|||||||
"Continue with %(provider)s": "Continue with %(provider)s",
|
"Continue with %(provider)s": "Continue with %(provider)s",
|
||||||
"Sign in with single sign-on": "Sign in with single sign-on",
|
"Sign in with single sign-on": "Sign in with single sign-on",
|
||||||
"And %(count)s more...|other": "And %(count)s more...",
|
"And %(count)s more...|other": "And %(count)s more...",
|
||||||
"Home": "Home",
|
|
||||||
"Enter a server name": "Enter a server name",
|
"Enter a server name": "Enter a server name",
|
||||||
"Looks good": "Looks good",
|
"Looks good": "Looks good",
|
||||||
"You are not allowed to view this server's rooms list": "You are not allowed to view this server's rooms list",
|
"You are not allowed to view this server's rooms list": "You are not allowed to view this server's rooms list",
|
||||||
@ -2507,6 +2510,7 @@
|
|||||||
"Beta": "Beta",
|
"Beta": "Beta",
|
||||||
"Leave the beta": "Leave the beta",
|
"Leave the beta": "Leave the beta",
|
||||||
"Join the beta": "Join the beta",
|
"Join the beta": "Join the beta",
|
||||||
|
"Experimental options": "Experimental options",
|
||||||
"Avatar": "Avatar",
|
"Avatar": "Avatar",
|
||||||
"This room is public": "This room is public",
|
"This room is public": "This room is public",
|
||||||
"Away": "Away",
|
"Away": "Away",
|
||||||
|
@ -263,7 +263,13 @@ function uint8ToString(buf: Buffer) {
|
|||||||
return out;
|
return out;
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function submitFeedback(endpoint: string, label: string, comment: string, canContact = false) {
|
export async function submitFeedback(
|
||||||
|
endpoint: string,
|
||||||
|
label: string,
|
||||||
|
comment: string,
|
||||||
|
canContact = false,
|
||||||
|
extraData: Record<string, string> = {},
|
||||||
|
) {
|
||||||
let version = "UNKNOWN";
|
let version = "UNKNOWN";
|
||||||
try {
|
try {
|
||||||
version = await PlatformPeg.get().getAppVersion();
|
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("platform", PlatformPeg.get().getHumanReadableName());
|
||||||
body.append("user_id", MatrixClientPeg.get()?.getUserId());
|
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, () => {});
|
await _submitReport(SdkConfig.get().bug_report_endpoint_url, body, () => {});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
/*
|
/*
|
||||||
Copyright 2017 Travis Ralston
|
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");
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
you may not use this file except in compliance with the License.
|
you may not use this file except in compliance with the License.
|
||||||
@ -127,6 +127,7 @@ export interface ISetting {
|
|||||||
image: string; // require(...)
|
image: string; // require(...)
|
||||||
feedbackSubheading?: string;
|
feedbackSubheading?: string;
|
||||||
feedbackLabel?: string;
|
feedbackLabel?: string;
|
||||||
|
extraSettings?: string[];
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -167,8 +168,31 @@ export const SETTINGS: {[setting: string]: ISetting} = {
|
|||||||
feedbackSubheading: _td("Your feedback will help make spaces better. " +
|
feedbackSubheading: _td("Your feedback will help make spaces better. " +
|
||||||
"The more detail you can go into, the better."),
|
"The more detail you can go into, the better."),
|
||||||
feedbackLabel: "spaces-feedback",
|
feedbackLabel: "spaces-feedback",
|
||||||
|
extraSettings: [
|
||||||
|
"feature_spaces.all_rooms",
|
||||||
|
"feature_spaces.space_member_dms",
|
||||||
|
"feature_spaces.space_dm_badges",
|
||||||
|
],
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
"feature_spaces.all_rooms": {
|
||||||
|
displayName: _td("Use an all rooms space instead of a home space."),
|
||||||
|
supportedLevels: LEVELS_FEATURE,
|
||||||
|
default: true,
|
||||||
|
controller: new ReloadOnChangeController(),
|
||||||
|
},
|
||||||
|
"feature_spaces.space_member_dms": {
|
||||||
|
displayName: _td("Show DMs for joined/invited members in the space."),
|
||||||
|
supportedLevels: LEVELS_FEATURE,
|
||||||
|
default: true,
|
||||||
|
controller: new ReloadOnChangeController(),
|
||||||
|
},
|
||||||
|
"feature_spaces.space_dm_badges": {
|
||||||
|
displayName: _td("Show notification badges for DMs in spaces."),
|
||||||
|
supportedLevels: LEVELS_FEATURE,
|
||||||
|
default: false,
|
||||||
|
controller: new ReloadOnChangeController(),
|
||||||
|
},
|
||||||
"feature_dnd": {
|
"feature_dnd": {
|
||||||
isFeature: true,
|
isFeature: true,
|
||||||
displayName: _td("Show options to enable 'Do not disturb' mode"),
|
displayName: _td("Show options to enable 'Do not disturb' mode"),
|
||||||
|
@ -14,36 +14,41 @@ See the License for the specific language governing permissions and
|
|||||||
limitations under the License.
|
limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import {ListIteratee, Many, sortBy, throttle} from "lodash";
|
import { ListIteratee, Many, sortBy, throttle } from "lodash";
|
||||||
import {EventType, RoomType} from "matrix-js-sdk/src/@types/event";
|
import { EventType, RoomType } from "matrix-js-sdk/src/@types/event";
|
||||||
import {Room} from "matrix-js-sdk/src/models/room";
|
import { Room } from "matrix-js-sdk/src/models/room";
|
||||||
import {MatrixEvent} from "matrix-js-sdk/src/models/event";
|
import { MatrixEvent } from "matrix-js-sdk/src/models/event";
|
||||||
|
|
||||||
import {AsyncStoreWithClient} from "./AsyncStoreWithClient";
|
import { AsyncStoreWithClient } from "./AsyncStoreWithClient";
|
||||||
import defaultDispatcher from "../dispatcher/dispatcher";
|
import defaultDispatcher from "../dispatcher/dispatcher";
|
||||||
import {ActionPayload} from "../dispatcher/payloads";
|
import { ActionPayload } from "../dispatcher/payloads";
|
||||||
import RoomListStore from "./room-list/RoomListStore";
|
import RoomListStore from "./room-list/RoomListStore";
|
||||||
import SettingsStore from "../settings/SettingsStore";
|
import SettingsStore from "../settings/SettingsStore";
|
||||||
import DMRoomMap from "../utils/DMRoomMap";
|
import DMRoomMap from "../utils/DMRoomMap";
|
||||||
import {FetchRoomFn} from "./notifications/ListNotificationState";
|
import { FetchRoomFn } from "./notifications/ListNotificationState";
|
||||||
import {SpaceNotificationState} from "./notifications/SpaceNotificationState";
|
import { SpaceNotificationState } from "./notifications/SpaceNotificationState";
|
||||||
import {RoomNotificationStateStore} from "./notifications/RoomNotificationStateStore";
|
import { RoomNotificationStateStore } from "./notifications/RoomNotificationStateStore";
|
||||||
import {DefaultTagID} from "./room-list/models";
|
import { DefaultTagID } from "./room-list/models";
|
||||||
import {EnhancedMap, mapDiff} from "../utils/maps";
|
import { EnhancedMap, mapDiff } from "../utils/maps";
|
||||||
import {setHasDiff} from "../utils/sets";
|
import { setHasDiff } from "../utils/sets";
|
||||||
import {ISpaceSummaryEvent, ISpaceSummaryRoom} from "../components/structures/SpaceRoomDirectory";
|
import { ISpaceSummaryEvent, ISpaceSummaryRoom } from "../components/structures/SpaceRoomDirectory";
|
||||||
import RoomViewStore from "./RoomViewStore";
|
import RoomViewStore from "./RoomViewStore";
|
||||||
|
import { arrayHasDiff } from "../utils/arrays";
|
||||||
|
import { objectDiff } from "../utils/objects";
|
||||||
|
|
||||||
|
type SpaceKey = string | symbol;
|
||||||
|
|
||||||
interface IState {}
|
interface IState {}
|
||||||
|
|
||||||
const ACTIVE_SPACE_LS_KEY = "mx_active_space";
|
const ACTIVE_SPACE_LS_KEY = "mx_active_space";
|
||||||
|
|
||||||
|
export const HOME_SPACE = Symbol("home-space");
|
||||||
export const SUGGESTED_ROOMS = Symbol("suggested-rooms");
|
export const SUGGESTED_ROOMS = Symbol("suggested-rooms");
|
||||||
|
|
||||||
export const UPDATE_TOP_LEVEL_SPACES = Symbol("top-level-spaces");
|
export const UPDATE_TOP_LEVEL_SPACES = Symbol("top-level-spaces");
|
||||||
export const UPDATE_INVITED_SPACES = Symbol("invited-spaces");
|
export const UPDATE_INVITED_SPACES = Symbol("invited-spaces");
|
||||||
export const UPDATE_SELECTED_SPACE = Symbol("selected-space");
|
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 {
|
export interface ISuggestedRoom extends ISpaceSummaryRoom {
|
||||||
viaServers: string[];
|
viaServers: string[];
|
||||||
@ -51,7 +56,8 @@ export interface ISuggestedRoom extends ISpaceSummaryRoom {
|
|||||||
|
|
||||||
const MAX_SUGGESTED_ROOMS = 20;
|
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]
|
const partitionSpacesAndRooms = (arr: Room[]): [Room[], Room[]] => { // [spaces, rooms]
|
||||||
return arr.reduce((result, room: Room) => {
|
return arr.reduce((result, room: Room) => {
|
||||||
@ -85,13 +91,15 @@ export class SpaceStoreClass extends AsyncStoreWithClient<IState> {
|
|||||||
|
|
||||||
// The spaces representing the roots of the various tree-like hierarchies
|
// The spaces representing the roots of the various tree-like hierarchies
|
||||||
private rootSpaces: Room[] = [];
|
private rootSpaces: Room[] = [];
|
||||||
|
// The list of rooms not present in any currently joined spaces
|
||||||
|
private orphanedRooms = new Set<string>();
|
||||||
// Map from room ID to set of spaces which list it as a child
|
// Map from room ID to set of spaces which list it as a child
|
||||||
private parentMap = new EnhancedMap<string, Set<string>>();
|
private parentMap = new EnhancedMap<string, Set<string>>();
|
||||||
// Map from spaceId to SpaceNotificationState instance representing that space
|
// Map from SpaceKey to SpaceNotificationState instance representing that space
|
||||||
private notificationStateMap = new Map<string, SpaceNotificationState>();
|
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
|
// Map from space key to Set of room IDs that should be shown as part of that space's filter
|
||||||
private spaceFilteredRooms = new Map<string, Set<string>>();
|
private spaceFilteredRooms = new Map<SpaceKey, Set<string>>();
|
||||||
// The space currently selected in the Space Panel - if null then All Rooms is selected
|
// The space currently selected in the Space Panel - if null then Home is selected
|
||||||
private _activeSpace?: Room = null;
|
private _activeSpace?: Room = null;
|
||||||
private _suggestedRooms: ISuggestedRoom[] = [];
|
private _suggestedRooms: ISuggestedRoom[] = [];
|
||||||
private _invitedSpaces = new Set<Room>();
|
private _invitedSpaces = new Set<Room>();
|
||||||
@ -251,7 +259,7 @@ export class SpaceStoreClass extends AsyncStoreWithClient<IState> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public getSpaceFilteredRoomIds = (space: Room | null): Set<string> => {
|
public getSpaceFilteredRoomIds = (space: Room | null): Set<string> => {
|
||||||
if (!space) {
|
if (!space && SettingsStore.getValue("feature_spaces.all_rooms")) {
|
||||||
return new Set(this.matrixClient.getVisibleRooms().map(r => r.roomId));
|
return new Set(this.matrixClient.getVisibleRooms().map(r => r.roomId));
|
||||||
}
|
}
|
||||||
return this.spaceFilteredRooms.get(space.roomId) || new Set();
|
return this.spaceFilteredRooms.get(space.roomId) || new Set();
|
||||||
@ -285,7 +293,7 @@ export class SpaceStoreClass extends AsyncStoreWithClient<IState> {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
const [rootSpaces] = partitionSpacesAndRooms(Array.from(unseenChildren));
|
const [rootSpaces, orphanedRooms] = partitionSpacesAndRooms(Array.from(unseenChildren));
|
||||||
|
|
||||||
// somewhat algorithm to handle full-cycles
|
// somewhat algorithm to handle full-cycles
|
||||||
const detachedNodes = new Set<Room>(spaces);
|
const detachedNodes = new Set<Room>(spaces);
|
||||||
@ -326,6 +334,7 @@ export class SpaceStoreClass extends AsyncStoreWithClient<IState> {
|
|||||||
// rootSpaces.push(space);
|
// rootSpaces.push(space);
|
||||||
// });
|
// });
|
||||||
|
|
||||||
|
this.orphanedRooms = new Set(orphanedRooms);
|
||||||
this.rootSpaces = rootSpaces;
|
this.rootSpaces = rootSpaces;
|
||||||
this.parentMap = backrefs;
|
this.parentMap = backrefs;
|
||||||
|
|
||||||
@ -342,10 +351,30 @@ export class SpaceStoreClass extends AsyncStoreWithClient<IState> {
|
|||||||
this.emit(UPDATE_INVITED_SPACES, this.invitedSpaces);
|
this.emit(UPDATE_INVITED_SPACES, this.invitedSpaces);
|
||||||
}, 100, {trailing: true, leading: true});
|
}, 100, {trailing: true, leading: true});
|
||||||
|
|
||||||
onSpaceUpdate = () => {
|
private onSpaceUpdate = () => {
|
||||||
this.rebuild();
|
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) => {
|
private onSpaceMembersChange = (ev: MatrixEvent) => {
|
||||||
// skip this update if we do not have a DM with this user
|
// skip this update if we do not have a DM with this user
|
||||||
if (DMRoomMap.shared().getDMRoomsForUserId(ev.getStateKey()).length < 1) return;
|
if (DMRoomMap.shared().getDMRoomsForUserId(ev.getStateKey()).length < 1) return;
|
||||||
@ -359,6 +388,18 @@ export class SpaceStoreClass extends AsyncStoreWithClient<IState> {
|
|||||||
const oldFilteredRooms = this.spaceFilteredRooms;
|
const oldFilteredRooms = this.spaceFilteredRooms;
|
||||||
this.spaceFilteredRooms = new Map();
|
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<string>(invites.map(room => room.roomId)));
|
||||||
|
|
||||||
|
visibleRooms.forEach(room => {
|
||||||
|
if (this.showInHomeSpace(room)) {
|
||||||
|
this.spaceFilteredRooms.get(HOME_SPACE).add(room.roomId);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
this.rootSpaces.forEach(s => {
|
this.rootSpaces.forEach(s => {
|
||||||
// traverse each space tree in DFS to build up the supersets as you go up,
|
// traverse each space tree in DFS to build up the supersets as you go up,
|
||||||
// reusing results from like subtrees.
|
// reusing results from like subtrees.
|
||||||
@ -374,6 +415,7 @@ export class SpaceStoreClass extends AsyncStoreWithClient<IState> {
|
|||||||
const roomIds = new Set(childRooms.map(r => r.roomId));
|
const roomIds = new Set(childRooms.map(r => r.roomId));
|
||||||
const space = this.matrixClient?.getRoom(spaceId);
|
const space = this.matrixClient?.getRoom(spaceId);
|
||||||
|
|
||||||
|
if (SettingsStore.getValue("feature_spaces.space_member_dms")) {
|
||||||
// Add relevant DMs
|
// Add relevant DMs
|
||||||
space?.getMembers().forEach(member => {
|
space?.getMembers().forEach(member => {
|
||||||
if (member.membership !== "join" && member.membership !== "invite") return;
|
if (member.membership !== "join" && member.membership !== "invite") return;
|
||||||
@ -381,6 +423,7 @@ export class SpaceStoreClass extends AsyncStoreWithClient<IState> {
|
|||||||
roomIds.add(roomId);
|
roomIds.add(roomId);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
}
|
||||||
|
|
||||||
const newPath = new Set(parentPath).add(spaceId);
|
const newPath = new Set(parentPath).add(spaceId);
|
||||||
childSpaces.forEach(childSpace => {
|
childSpaces.forEach(childSpace => {
|
||||||
@ -406,6 +449,8 @@ export class SpaceStoreClass extends AsyncStoreWithClient<IState> {
|
|||||||
// Update NotificationStates
|
// Update NotificationStates
|
||||||
this.getNotificationState(s)?.setRooms(visibleRooms.filter(room => {
|
this.getNotificationState(s)?.setRooms(visibleRooms.filter(room => {
|
||||||
if (roomIds.has(room.roomId)) {
|
if (roomIds.has(room.roomId)) {
|
||||||
|
if (s !== HOME_SPACE && SettingsStore.getValue("feature_spaces.space_dm_badges")) return true;
|
||||||
|
|
||||||
return !DMRoomMap.shared().getUserIdForRoomId(room.roomId)
|
return !DMRoomMap.shared().getUserIdForRoomId(room.roomId)
|
||||||
|| RoomListStore.instance.getTagsForRoom(room).includes(DefaultTagID.Favourite);
|
|| RoomListStore.instance.getTagsForRoom(room).includes(DefaultTagID.Favourite);
|
||||||
}
|
}
|
||||||
@ -489,6 +534,8 @@ export class SpaceStoreClass extends AsyncStoreWithClient<IState> {
|
|||||||
// TODO confirm this after implementing parenting behaviour
|
// TODO confirm this after implementing parenting behaviour
|
||||||
if (room.isSpaceRoom()) {
|
if (room.isSpaceRoom()) {
|
||||||
this.onSpaceUpdate();
|
this.onSpaceUpdate();
|
||||||
|
} else if (!SettingsStore.getValue("feature_spaces.all_rooms")) {
|
||||||
|
this.onRoomUpdate(room);
|
||||||
}
|
}
|
||||||
this.emit(room.roomId);
|
this.emit(room.roomId);
|
||||||
break;
|
break;
|
||||||
@ -501,8 +548,38 @@ export class SpaceStoreClass extends AsyncStoreWithClient<IState> {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
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<Record<string, string[]>>(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() {
|
protected async reset() {
|
||||||
this.rootSpaces = [];
|
this.rootSpaces = [];
|
||||||
|
this.orphanedRooms = new Set();
|
||||||
this.parentMap = new EnhancedMap();
|
this.parentMap = new EnhancedMap();
|
||||||
this.notificationStateMap = new Map();
|
this.notificationStateMap = new Map();
|
||||||
this.spaceFilteredRooms = new Map();
|
this.spaceFilteredRooms = new Map();
|
||||||
@ -517,6 +594,10 @@ export class SpaceStoreClass extends AsyncStoreWithClient<IState> {
|
|||||||
this.matrixClient.removeListener("Room", this.onRoom);
|
this.matrixClient.removeListener("Room", this.onRoom);
|
||||||
this.matrixClient.removeListener("Room.myMembership", this.onRoom);
|
this.matrixClient.removeListener("Room.myMembership", this.onRoom);
|
||||||
this.matrixClient.removeListener("RoomState.events", this.onRoomState);
|
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();
|
await this.reset();
|
||||||
}
|
}
|
||||||
@ -526,6 +607,10 @@ export class SpaceStoreClass extends AsyncStoreWithClient<IState> {
|
|||||||
this.matrixClient.on("Room", this.onRoom);
|
this.matrixClient.on("Room", this.onRoom);
|
||||||
this.matrixClient.on("Room.myMembership", this.onRoom);
|
this.matrixClient.on("Room.myMembership", this.onRoom);
|
||||||
this.matrixClient.on("RoomState.events", this.onRoomState);
|
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
|
await this.onSpaceUpdate(); // trigger an initial update
|
||||||
|
|
||||||
@ -550,7 +635,10 @@ export class SpaceStoreClass extends AsyncStoreWithClient<IState> {
|
|||||||
// Don't context switch when navigating to the space room
|
// Don't context switch when navigating to the space room
|
||||||
// as it will cause you to end up in the wrong room
|
// as it will cause you to end up in the wrong room
|
||||||
this.setActiveSpace(room, false);
|
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);
|
this.switchToRelatedSpace(roomId);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -568,7 +656,7 @@ export class SpaceStoreClass extends AsyncStoreWithClient<IState> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public getNotificationState(key: string): SpaceNotificationState {
|
public getNotificationState(key: SpaceKey): SpaceNotificationState {
|
||||||
if (this.notificationStateMap.has(key)) {
|
if (this.notificationStateMap.has(key)) {
|
||||||
return this.notificationStateMap.get(key);
|
return this.notificationStateMap.get(key);
|
||||||
}
|
}
|
||||||
|
@ -19,6 +19,7 @@ import { Room } from "matrix-js-sdk/src/models/room";
|
|||||||
import { RoomListStoreClass } from "./RoomListStore";
|
import { RoomListStoreClass } from "./RoomListStore";
|
||||||
import { SpaceFilterCondition } from "./filters/SpaceFilterCondition";
|
import { SpaceFilterCondition } from "./filters/SpaceFilterCondition";
|
||||||
import SpaceStore, { UPDATE_SELECTED_SPACE } from "../SpaceStore";
|
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
|
* Watches for changes in spaces to manage the filter on the provided RoomListStore
|
||||||
@ -28,6 +29,10 @@ export class SpaceWatcher {
|
|||||||
private activeSpace: Room = SpaceStore.instance.activeSpace;
|
private activeSpace: Room = SpaceStore.instance.activeSpace;
|
||||||
|
|
||||||
constructor(private store: RoomListStoreClass) {
|
constructor(private store: RoomListStoreClass) {
|
||||||
|
if (!SettingsStore.getValue("feature_spaces.all_rooms")) {
|
||||||
|
this.updateFilter();
|
||||||
|
store.addFilter(this.filter);
|
||||||
|
}
|
||||||
SpaceStore.instance.on(UPDATE_SELECTED_SPACE, this.onSelectedSpaceUpdated);
|
SpaceStore.instance.on(UPDATE_SELECTED_SPACE, this.onSelectedSpaceUpdated);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -35,7 +40,7 @@ export class SpaceWatcher {
|
|||||||
this.activeSpace = activeSpace;
|
this.activeSpace = activeSpace;
|
||||||
|
|
||||||
if (this.filter) {
|
if (this.filter) {
|
||||||
if (activeSpace) {
|
if (activeSpace || !SettingsStore.getValue("feature_spaces.all_rooms")) {
|
||||||
this.updateFilter();
|
this.updateFilter();
|
||||||
} else {
|
} else {
|
||||||
this.store.removeFilter(this.filter);
|
this.store.removeFilter(this.filter);
|
||||||
@ -49,9 +54,11 @@ export class SpaceWatcher {
|
|||||||
};
|
};
|
||||||
|
|
||||||
private updateFilter = () => {
|
private updateFilter = () => {
|
||||||
|
if (this.activeSpace) {
|
||||||
SpaceStore.instance.traverseSpace(this.activeSpace.roomId, roomId => {
|
SpaceStore.instance.traverseSpace(this.activeSpace.roomId, roomId => {
|
||||||
this.store.matrixClient?.getRoom(roomId)?.loadMembersIfNeeded();
|
this.store.matrixClient?.getRoom(roomId)?.loadMembersIfNeeded();
|
||||||
});
|
});
|
||||||
|
}
|
||||||
this.filter.updateSpace(this.activeSpace);
|
this.filter.updateSpace(this.activeSpace);
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
@ -19,7 +19,7 @@ import { Room } from "matrix-js-sdk/src/models/room";
|
|||||||
|
|
||||||
import { FILTER_CHANGED, FilterKind, IFilterCondition } from "./IFilterCondition";
|
import { FILTER_CHANGED, FilterKind, IFilterCondition } from "./IFilterCondition";
|
||||||
import { IDestroyable } from "../../../utils/IDestroyable";
|
import { IDestroyable } from "../../../utils/IDestroyable";
|
||||||
import SpaceStore from "../../SpaceStore";
|
import SpaceStore, { HOME_SPACE } from "../../SpaceStore";
|
||||||
import { setHasDiff } from "../../../utils/sets";
|
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) {
|
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);
|
SpaceStore.instance.on(this.getSpaceEventKey(this.space = space), this.onStoreUpdate);
|
||||||
this.onStoreUpdate(); // initial update from the change to the space
|
this.onStoreUpdate(); // initial update from the change to the space
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user