Merge remote-tracking branch 'upstream/develop' into feature/incoming-call-toast

Signed-off-by: Šimon Brandner <simon.bra.ag@gmail.com>
This commit is contained in:
Šimon Brandner
2021-08-04 09:21:45 +02:00
182 changed files with 5872 additions and 2200 deletions
+6 -3
View File
@@ -23,6 +23,8 @@ import { arrayHasDiff } from "../utils/arrays";
import { isNullOrUndefined } from "matrix-js-sdk/src/utils";
import { SettingLevel } from "../settings/SettingLevel";
import SpaceStore from "./SpaceStore";
import { Action } from "../dispatcher/actions";
import { SettingUpdatedPayload } from "../dispatcher/payloads/SettingUpdatedPayload";
const MAX_ROOMS = 20; // arbitrary
const AUTOJOIN_WAIT_THRESHOLD_MS = 90000; // 90s, the time we wait for an autojoined room to show up
@@ -63,10 +65,11 @@ export class BreadcrumbsStore extends AsyncStoreWithClient<IState> {
protected async onAction(payload: ActionPayload) {
if (!this.matrixClient) return;
if (payload.action === 'setting_updated') {
if (payload.settingName === 'breadcrumb_rooms') {
if (payload.action === Action.SettingUpdated) {
const settingUpdatedPayload = payload as SettingUpdatedPayload;
if (settingUpdatedPayload.settingName === 'breadcrumb_rooms') {
await this.updateRooms();
} else if (payload.settingName === 'breadcrumbs') {
} else if (settingUpdatedPayload.settingName === 'breadcrumbs') {
await this.updateState({ enabled: SettingsStore.getValue("breadcrumbs", null) });
}
} else if (payload.action === 'view_room') {
+1 -1
View File
@@ -56,7 +56,7 @@ class LifecycleStore extends Store<ActionPayload> {
deferredAction: null,
});
break;
case 'syncstate': {
case 'sync_state': {
if (payload.state !== 'PREPARED') {
break;
}
+59 -47
View File
@@ -37,9 +37,8 @@ import { EnhancedMap, mapDiff } from "../utils/maps";
import { setHasDiff } from "../utils/sets";
import RoomViewStore from "./RoomViewStore";
import { Action } from "../dispatcher/actions";
import { arrayHasDiff } from "../utils/arrays";
import { arrayHasDiff, arrayHasOrderChange } from "../utils/arrays";
import { objectDiff } from "../utils/objects";
import { arrayHasOrderChange } from "../utils/arrays";
import { reorderLexicographically } from "../utils/stringOrderField";
import { TAG_ORDER } from "../components/views/rooms/RoomList";
import { shouldShowSpaceSettings } from "../utils/space";
@@ -48,6 +47,7 @@ import { _t } from "../languageHandler";
import GenericToast from "../components/views/toasts/GenericToast";
import Modal from "../Modal";
import InfoDialog from "../components/views/dialogs/InfoDialog";
import { SettingUpdatedPayload } from "../dispatcher/payloads/SettingUpdatedPayload";
type SpaceKey = string | symbol;
@@ -61,6 +61,7 @@ export const SUGGESTED_ROOMS = Symbol("suggested-rooms");
export const UPDATE_TOP_LEVEL_SPACES = Symbol("top-level-spaces");
export const UPDATE_INVITED_SPACES = Symbol("invited-spaces");
export const UPDATE_SELECTED_SPACE = Symbol("selected-space");
export const UPDATE_HOME_BEHAVIOUR = Symbol("home-behaviour");
// Space Room ID/HOME_SPACE will be emitted when a Space's children change
export interface ISuggestedRoom extends ISpaceSummaryRoom {
@@ -69,14 +70,10 @@ export interface ISuggestedRoom extends ISpaceSummaryRoom {
const MAX_SUGGESTED_ROOMS = 20;
// All of these settings cause the page to reload and can be costly if read frequently, so read them here only
// This setting causes the page to reload and can be costly if read frequently, so read it 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 getSpaceContextKey = (space?: Room) => `mx_space_context_${space?.roomId || "HOME_SPACE"}`;
const partitionSpacesAndRooms = (arr: Room[]): [Room[], Room[]] => { // [spaces, rooms]
return arr.reduce((result, room: Room) => {
@@ -104,10 +101,6 @@ const getRoomFn: FetchRoomFn = (room: Room) => {
};
export class SpaceStoreClass extends AsyncStoreWithClient<IState> {
constructor() {
super(defaultDispatcher, {});
}
// 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
@@ -124,6 +117,13 @@ export class SpaceStoreClass extends AsyncStoreWithClient<IState> {
private _invitedSpaces = new Set<Room>();
private spaceOrderLocalEchoMap = new Map<string, string>();
private _restrictedJoinRuleSupport?: IRoomCapability;
private _allRoomsInHome: boolean = SettingsStore.getValue("Spaces.allRoomsInHome");
constructor() {
super(defaultDispatcher, {});
SettingsStore.monitorSetting("Spaces.allRoomsInHome", null);
}
public get invitedSpaces(): Room[] {
return Array.from(this._invitedSpaces);
@@ -141,13 +141,16 @@ export class SpaceStoreClass extends AsyncStoreWithClient<IState> {
return this._suggestedRooms;
}
public get allRoomsInHome(): boolean {
return this._allRoomsInHome;
}
public async setActiveRoomInSpace(space: Room | null): Promise<void> {
if (space && !space.isSpaceRoom()) return;
if (space !== this.activeSpace) await this.setActiveSpace(space);
if (space) {
const notificationState = this.getNotificationState(space.roomId);
const roomId = notificationState.getFirstRoomWithNotifications();
const roomId = this.getNotificationState(space.roomId).getFirstRoomWithNotifications();
defaultDispatcher.dispatch({
action: "view_room",
room_id: roomId,
@@ -202,7 +205,8 @@ export class SpaceStoreClass extends AsyncStoreWithClient<IState> {
// else if the last viewed room in this space is joined then view that
// else view space home or home depending on what is being clicked on
if (space?.getMyMembership() !== "invite" &&
this.matrixClient?.getRoom(roomId)?.getMyMembership() === "join"
this.matrixClient?.getRoom(roomId)?.getMyMembership() === "join" &&
this.getSpaceFilteredRoomIds(space).has(roomId)
) {
defaultDispatcher.dispatch({
action: "view_room",
@@ -331,7 +335,7 @@ export class SpaceStoreClass extends AsyncStoreWithClient<IState> {
}, roomId);
}
private getChildren(spaceId: string): Room[] {
public getChildren(spaceId: string): Room[] {
const room = this.matrixClient?.getRoom(spaceId);
const childEvents = room?.currentState.getStateEvents(EventType.SpaceChild).filter(ev => ev.getContent()?.via);
return sortBy(childEvents, ev => {
@@ -379,7 +383,7 @@ export class SpaceStoreClass extends AsyncStoreWithClient<IState> {
}
public getSpaceFilteredRoomIds = (space: Room | null): Set<string> => {
if (!space && spacesTweakAllRoomsEnabled) {
if (!space && this.allRoomsInHome) {
return new Set(this.matrixClient.getVisibleRooms().map(r => r.roomId));
}
return this.spaceFilteredRooms.get(space?.roomId || HOME_SPACE) || new Set();
@@ -476,7 +480,7 @@ export class SpaceStoreClass extends AsyncStoreWithClient<IState> {
};
private showInHomeSpace = (room: Room) => {
if (spacesTweakAllRoomsEnabled) return true;
if (this.allRoomsInHome) return true;
if (room.isSpaceRoom()) return false;
return !this.parentMap.get(room.roomId)?.size // put all orphaned rooms in the Home Space
|| DMRoomMap.shared().getUserIdForRoomId(room.roomId) // put all DMs in the Home Space
@@ -508,7 +512,7 @@ export class SpaceStoreClass extends AsyncStoreWithClient<IState> {
const oldFilteredRooms = this.spaceFilteredRooms;
this.spaceFilteredRooms = new Map();
if (!spacesTweakAllRoomsEnabled) {
if (!this.allRoomsInHome) {
// put all room invites in the Home Space
const invites = visibleRooms.filter(r => !r.isSpaceRoom() && r.getMyMembership() === "invite");
this.spaceFilteredRooms.set(HOME_SPACE, new Set<string>(invites.map(room => room.roomId)));
@@ -535,15 +539,13 @@ export class SpaceStoreClass extends AsyncStoreWithClient<IState> {
const roomIds = new Set(childRooms.map(r => r.roomId));
const space = this.matrixClient?.getRoom(spaceId);
if (spacesTweakSpaceMemberDMsEnabled) {
// 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);
});
// 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 => {
@@ -566,16 +568,17 @@ export class SpaceStoreClass extends AsyncStoreWithClient<IState> {
});
this.spaceFilteredRooms.forEach((roomIds, s) => {
// Update NotificationStates
this.getNotificationState(s)?.setRooms(visibleRooms.filter(room => {
if (roomIds.has(room.roomId)) {
if (s !== HOME_SPACE && spacesTweakSpaceDMBadgesEnabled) return true;
if (this.allRoomsInHome && s === HOME_SPACE) return; // we'll be using the global notification state, skip
return !DMRoomMap.shared().getUserIdForRoomId(room.roomId)
|| RoomListStore.instance.getTagsForRoom(room).includes(DefaultTagID.Favourite);
// Update NotificationStates
this.getNotificationState(s).setRooms(visibleRooms.filter(room => {
if (!roomIds.has(room.roomId)) return false;
if (DMRoomMap.shared().getUserIdForRoomId(room.roomId)) {
return s === HOME_SPACE;
}
return false;
return true;
}));
});
}, 100, { trailing: true, leading: true });
@@ -668,7 +671,7 @@ export class SpaceStoreClass extends AsyncStoreWithClient<IState> {
// TODO confirm this after implementing parenting behaviour
if (room.isSpaceRoom()) {
this.onSpaceUpdate();
} else if (!spacesTweakAllRoomsEnabled) {
} else if (!this.allRoomsInHome) {
this.onRoomUpdate(room);
}
this.emit(room.roomId);
@@ -692,7 +695,7 @@ export class SpaceStoreClass extends AsyncStoreWithClient<IState> {
if (order !== lastOrder) {
this.notifyIfOrderChanged();
}
} else if (ev.getType() === EventType.Tag && !spacesTweakAllRoomsEnabled) {
} else if (ev.getType() === EventType.Tag && !this.allRoomsInHome) {
// 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 || {};
@@ -703,7 +706,7 @@ export class SpaceStoreClass extends AsyncStoreWithClient<IState> {
};
private onAccountData = (ev: MatrixEvent, lastEvent: MatrixEvent) => {
if (ev.getType() === EventType.Direct) {
if (!this.allRoomsInHome && ev.getType() === EventType.Direct) {
const lastContent = lastEvent.getContent();
const content = ev.getContent();
@@ -738,9 +741,7 @@ export class SpaceStoreClass extends AsyncStoreWithClient<IState> {
this.matrixClient.removeListener("Room.myMembership", this.onRoom);
this.matrixClient.removeListener("Room.accountData", this.onRoomAccountData);
this.matrixClient.removeListener("RoomState.events", this.onRoomState);
if (!spacesTweakAllRoomsEnabled) {
this.matrixClient.removeListener("accountData", this.onAccountData);
}
this.matrixClient.removeListener("accountData", this.onAccountData);
}
await this.reset();
}
@@ -751,9 +752,7 @@ export class SpaceStoreClass extends AsyncStoreWithClient<IState> {
this.matrixClient.on("Room.myMembership", this.onRoom);
this.matrixClient.on("Room.accountData", this.onRoomAccountData);
this.matrixClient.on("RoomState.events", this.onRoomState);
if (!spacesTweakAllRoomsEnabled) {
this.matrixClient.on("accountData", this.onAccountData);
}
this.matrixClient.on("accountData", this.onAccountData);
this.matrixClient.getCapabilities().then(capabilities => {
this._restrictedJoinRuleSupport = capabilities
@@ -784,7 +783,7 @@ export class SpaceStoreClass extends AsyncStoreWithClient<IState> {
// as it will cause you to end up in the wrong room
this.setActiveSpace(room, false);
} else if (
(!spacesTweakAllRoomsEnabled || this.activeSpace) &&
(!this.allRoomsInHome || this.activeSpace) &&
!this.getSpaceFilteredRoomIds(this.activeSpace).has(roomId)
) {
this.switchToRelatedSpace(roomId);
@@ -796,17 +795,33 @@ export class SpaceStoreClass extends AsyncStoreWithClient<IState> {
window.localStorage.setItem(getSpaceContextKey(this.activeSpace), payload.room_id);
break;
}
case "after_leave_room":
if (this._activeSpace && payload.room_id === this._activeSpace.roomId) {
this.setActiveSpace(null, false);
}
break;
case Action.SwitchSpace:
if (payload.num === 0) {
this.setActiveSpace(null);
} else if (this.spacePanelSpaces.length >= payload.num) {
this.setActiveSpace(this.spacePanelSpaces[payload.num - 1]);
}
break;
case Action.SettingUpdated: {
const settingUpdatedPayload = payload as SettingUpdatedPayload;
if (settingUpdatedPayload.settingName === "Spaces.allRoomsInHome") {
const newValue = SettingsStore.getValue("Spaces.allRoomsInHome");
if (this.allRoomsInHome !== newValue) {
this._allRoomsInHome = newValue;
this.emit(UPDATE_HOME_BEHAVIOUR, this.allRoomsInHome);
this.rebuild(); // rebuild everything
}
}
break;
}
}
}
@@ -877,9 +892,6 @@ export class SpaceStoreClass extends AsyncStoreWithClient<IState> {
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();
@@ -23,7 +23,7 @@ import { NOTIFICATION_STATE_UPDATE, NotificationState } from "./NotificationStat
import { FetchRoomFn } from "./ListNotificationState";
export class SpaceNotificationState extends NotificationState {
private rooms: Room[] = [];
public rooms: Room[] = []; // exposed only for tests
private states: { [spaceId: string]: RoomNotificationState } = {};
constructor(private spaceId: string | symbol, private getRoomFn: FetchRoomFn) {
+14 -4
View File
@@ -36,6 +36,8 @@ import { RoomNotificationStateStore } from "../notifications/RoomNotificationSta
import { VisibilityProvider } from "./filters/VisibilityProvider";
import { SpaceWatcher } from "./SpaceWatcher";
import SpaceStore from "../SpaceStore";
import { Action } from "../../dispatcher/actions";
import { SettingUpdatedPayload } from "../../dispatcher/payloads/SettingUpdatedPayload";
interface IState {
tagsEnabled?: boolean;
@@ -213,10 +215,11 @@ export class RoomListStoreClass extends AsyncStoreWithClient<IState> {
const logicallyReady = this.matrixClient && this.initialListsGenerated;
if (!logicallyReady) return;
if (payload.action === 'setting_updated') {
if (this.watchedSettings.includes(payload.settingName)) {
if (payload.action === Action.SettingUpdated) {
const settingUpdatedPayload = payload as SettingUpdatedPayload;
if (this.watchedSettings.includes(settingUpdatedPayload.settingName)) {
// TODO: Remove with https://github.com/vector-im/element-web/issues/14602
if (payload.settingName === "advancedRoomListLogging") {
if (settingUpdatedPayload.settingName === "advancedRoomListLogging") {
// Log when the setting changes so we know when it was turned on in the rageshake
const enabled = SettingsStore.getValue("advancedRoomListLogging");
console.warn("Advanced room list logging is enabled? " + enabled);
@@ -708,6 +711,7 @@ export class RoomListStoreClass extends AsyncStoreWithClient<IState> {
}
let promise = Promise.resolve();
let idx = this.filterConditions.indexOf(filter);
let removed = false;
if (idx >= 0) {
this.filterConditions.splice(idx, 1);
@@ -718,14 +722,20 @@ export class RoomListStoreClass extends AsyncStoreWithClient<IState> {
if (SpaceStore.spacesEnabled) {
promise = this.recalculatePrefiltering();
}
removed = true;
}
idx = this.prefilterConditions.indexOf(filter);
if (idx >= 0) {
filter.off(FILTER_CHANGED, this.onPrefilterUpdated);
this.prefilterConditions.splice(idx, 1);
promise = this.recalculatePrefiltering();
removed = true;
}
if (removed) {
promise.then(() => this.updateFn.trigger());
}
promise.then(() => this.updateFn.trigger());
}
/**
+24 -16
View File
@@ -18,39 +18,47 @@ 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 SpaceStore, { UPDATE_HOME_BEHAVIOUR, UPDATE_SELECTED_SPACE } from "../SpaceStore";
/**
* Watches for changes in spaces to manage the filter on the provided RoomListStore
*/
export class SpaceWatcher {
private filter: SpaceFilterCondition;
private readonly filter = new SpaceFilterCondition();
// we track these separately to the SpaceStore as we need to observe transitions
private activeSpace: Room = SpaceStore.instance.activeSpace;
private allRoomsInHome: boolean = SpaceStore.instance.allRoomsInHome;
constructor(private store: RoomListStoreClass) {
if (!SpaceStore.spacesTweakAllRoomsEnabled) {
this.filter = new SpaceFilterCondition();
if (!this.allRoomsInHome || this.activeSpace) {
this.updateFilter();
store.addFilter(this.filter);
}
SpaceStore.instance.on(UPDATE_SELECTED_SPACE, this.onSelectedSpaceUpdated);
SpaceStore.instance.on(UPDATE_HOME_BEHAVIOUR, this.onHomeBehaviourUpdated);
}
private onSelectedSpaceUpdated = (activeSpace?: Room) => {
this.activeSpace = activeSpace;
private onSelectedSpaceUpdated = (activeSpace?: Room, allRoomsInHome = this.allRoomsInHome) => {
if (activeSpace === this.activeSpace && allRoomsInHome === this.allRoomsInHome) return; // nop
if (this.filter) {
if (activeSpace || !SpaceStore.spacesTweakAllRoomsEnabled) {
this.updateFilter();
} else {
this.store.removeFilter(this.filter);
this.filter = null;
}
} else if (activeSpace) {
this.filter = new SpaceFilterCondition();
const oldActiveSpace = this.activeSpace;
const oldAllRoomsInHome = this.allRoomsInHome;
this.activeSpace = activeSpace;
this.allRoomsInHome = allRoomsInHome;
if (activeSpace || !allRoomsInHome) {
this.updateFilter();
this.store.addFilter(this.filter);
}
if (oldAllRoomsInHome && !oldActiveSpace) {
this.store.addFilter(this.filter);
} else if (allRoomsInHome && !activeSpace) {
this.store.removeFilter(this.filter);
}
};
private onHomeBehaviourUpdated = (allRoomsInHome: boolean) => {
this.onSelectedSpaceUpdated(this.activeSpace, allRoomsInHome);
};
private updateFilter = () => {
@@ -17,7 +17,7 @@ limitations under the License.
import { IPreview } from "./IPreview";
import { TagID } from "../models";
import { MatrixEvent } from "matrix-js-sdk/src/models/event";
import { _t } from "../../../languageHandler";
import { _t, sanitizeForTranslation } from "../../../languageHandler";
import { getSenderName, isSelf, shouldPrefixMessagesIn } from "./utils";
import ReplyThread from "../../../components/views/elements/ReplyThread";
import { getHtmlText } from "../../../HtmlUtils";
@@ -58,6 +58,8 @@ export class MessageEventPreview implements IPreview {
body = getHtmlText(body);
}
body = sanitizeForTranslation(body);
if (msgtype === 'm.emote') {
return _t("* %(senderName)s %(emote)s", { senderName: getSenderName(event), emote: body });
}
+8 -2
View File
@@ -54,6 +54,7 @@ import { ElementWidgetCapabilities } from "./ElementWidgetCapabilities";
import { MatrixEvent } from "matrix-js-sdk/src/models/event";
import { ELEMENT_CLIENT_ID } from "../../identifiers";
import { getUserLanguage } from "../../languageHandler";
import { WidgetVariableCustomisations } from "../../customisations/WidgetVariables";
// TODO: Destroy all of this code
@@ -191,7 +192,8 @@ export class StopGapWidget extends EventEmitter {
}
private runUrlTemplate(opts = { asPopout: false }): string {
const templated = this.mockWidget.getCompleteUrl({
const fromCustomisation = WidgetVariableCustomisations?.provideVariables?.() ?? {};
const defaults: ITemplateParams = {
widgetRoomId: this.roomId,
currentUserId: MatrixClientPeg.get().getUserId(),
userDisplayName: OwnProfileStore.instance.displayName,
@@ -199,7 +201,8 @@ export class StopGapWidget extends EventEmitter {
clientId: ELEMENT_CLIENT_ID,
clientTheme: SettingsStore.getValue("theme"),
clientLanguage: getUserLanguage(),
}, opts?.asPopout);
};
const templated = this.mockWidget.getCompleteUrl(Object.assign(defaults, fromCustomisation), opts?.asPopout);
const parsed = new URL(templated);
@@ -363,6 +366,9 @@ export class StopGapWidget extends EventEmitter {
}
public async prepare(): Promise<void> {
// Ensure the variables are ready for us to be rendered before continuing
await (WidgetVariableCustomisations?.isReady?.() ?? Promise.resolve());
if (this.scalarToken) return;
const existingMessaging = WidgetMessagingStore.instance.getMessaging(this.mockWidget);
if (existingMessaging) this.messaging = existingMessaging;