You've already forked matrix-react-sdk
							
							
				mirror of
				https://github.com/matrix-org/matrix-react-sdk.git
				synced 2025-11-04 11:51:45 +03:00 
			
		
		
		
	Merge remote-tracking branch 'origin/develop' into jryans/upgrade-sanitize-types
This commit is contained in:
		@@ -58,7 +58,7 @@
 | 
				
			|||||||
    "blueimp-canvas-to-blob": "^3.28.0",
 | 
					    "blueimp-canvas-to-blob": "^3.28.0",
 | 
				
			||||||
    "browser-encrypt-attachment": "^0.3.0",
 | 
					    "browser-encrypt-attachment": "^0.3.0",
 | 
				
			||||||
    "browser-request": "^0.3.3",
 | 
					    "browser-request": "^0.3.3",
 | 
				
			||||||
    "cheerio": "^1.0.0-rc.5",
 | 
					    "cheerio": "^1.0.0-rc.9",
 | 
				
			||||||
    "classnames": "^2.2.6",
 | 
					    "classnames": "^2.2.6",
 | 
				
			||||||
    "commonmark": "^0.29.3",
 | 
					    "commonmark": "^0.29.3",
 | 
				
			||||||
    "counterpart": "^0.18.6",
 | 
					    "counterpart": "^0.18.6",
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -422,8 +422,12 @@ export function bodyToHtml(content: IContent, highlights: string[], opts: IOpts
 | 
				
			|||||||
            safeBody = sanitizeHtml(formattedBody, sanitizeParams);
 | 
					            safeBody = sanitizeHtml(formattedBody, sanitizeParams);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            if (SettingsStore.getValue("feature_latex_maths")) {
 | 
					            if (SettingsStore.getValue("feature_latex_maths")) {
 | 
				
			||||||
                const phtml = cheerio.load(safeBody,
 | 
					                const phtml = cheerio.load(safeBody, {
 | 
				
			||||||
                    { _useHtmlParser2: true, decodeEntities: false })
 | 
					                    // @ts-ignore: The `_useHtmlParser2` internal option is the
 | 
				
			||||||
 | 
					                    // simplest way to both parse and render using `htmlparser2`.
 | 
				
			||||||
 | 
					                    _useHtmlParser2: true,
 | 
				
			||||||
 | 
					                    decodeEntities: false,
 | 
				
			||||||
 | 
					                });
 | 
				
			||||||
                // @ts-ignore - The types for `replaceWith` wrongly expect
 | 
					                // @ts-ignore - The types for `replaceWith` wrongly expect
 | 
				
			||||||
                // Cheerio instance to be returned.
 | 
					                // Cheerio instance to be returned.
 | 
				
			||||||
                phtml('div, span[data-mx-maths!=""]').replaceWith(function(i, e) {
 | 
					                phtml('div, span[data-mx-maths!=""]').replaceWith(function(i, e) {
 | 
				
			||||||
@@ -431,6 +435,7 @@ export function bodyToHtml(content: IContent, highlights: string[], opts: IOpts
 | 
				
			|||||||
                        AllHtmlEntities.decode(phtml(e).attr('data-mx-maths')),
 | 
					                        AllHtmlEntities.decode(phtml(e).attr('data-mx-maths')),
 | 
				
			||||||
                        {
 | 
					                        {
 | 
				
			||||||
                            throwOnError: false,
 | 
					                            throwOnError: false,
 | 
				
			||||||
 | 
					                            // @ts-ignore - `e` can be an Element, not just a Node
 | 
				
			||||||
                            displayMode: e.name == 'div',
 | 
					                            displayMode: e.name == 'div',
 | 
				
			||||||
                            output: "htmlAndMathml",
 | 
					                            output: "htmlAndMathml",
 | 
				
			||||||
                        });
 | 
					                        });
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -544,11 +544,13 @@ export default class MessagePanel extends React.Component {
 | 
				
			|||||||
            }
 | 
					            }
 | 
				
			||||||
            if (!grouper) {
 | 
					            if (!grouper) {
 | 
				
			||||||
                const wantTile = this._shouldShowEvent(mxEv);
 | 
					                const wantTile = this._shouldShowEvent(mxEv);
 | 
				
			||||||
 | 
					                const isGrouped = false;
 | 
				
			||||||
                if (wantTile) {
 | 
					                if (wantTile) {
 | 
				
			||||||
                    // make sure we unpack the array returned by _getTilesForEvent,
 | 
					                    // make sure we unpack the array returned by _getTilesForEvent,
 | 
				
			||||||
                    // otherwise react will auto-generate keys and we will end up
 | 
					                    // otherwise react will auto-generate keys and we will end up
 | 
				
			||||||
                    // replacing all of the DOM elements every time we paginate.
 | 
					                    // replacing all of the DOM elements every time we paginate.
 | 
				
			||||||
                    ret.push(...this._getTilesForEvent(prevEvent, mxEv, last, nextEvent, nextTile));
 | 
					                    ret.push(...this._getTilesForEvent(prevEvent, mxEv, last, isGrouped,
 | 
				
			||||||
 | 
					                        nextEvent, nextTile));
 | 
				
			||||||
                    prevEvent = mxEv;
 | 
					                    prevEvent = mxEv;
 | 
				
			||||||
                }
 | 
					                }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -564,7 +566,7 @@ export default class MessagePanel extends React.Component {
 | 
				
			|||||||
        return ret;
 | 
					        return ret;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    _getTilesForEvent(prevEvent, mxEv, last, nextEvent, nextEventWithTile) {
 | 
					    _getTilesForEvent(prevEvent, mxEv, last, isGrouped=false, nextEvent, nextEventWithTile) {
 | 
				
			||||||
        const TileErrorBoundary = sdk.getComponent('messages.TileErrorBoundary');
 | 
					        const TileErrorBoundary = sdk.getComponent('messages.TileErrorBoundary');
 | 
				
			||||||
        const EventTile = sdk.getComponent('rooms.EventTile');
 | 
					        const EventTile = sdk.getComponent('rooms.EventTile');
 | 
				
			||||||
        const DateSeparator = sdk.getComponent('messages.DateSeparator');
 | 
					        const DateSeparator = sdk.getComponent('messages.DateSeparator');
 | 
				
			||||||
@@ -584,7 +586,7 @@ export default class MessagePanel extends React.Component {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
        // do we need a date separator since the last event?
 | 
					        // do we need a date separator since the last event?
 | 
				
			||||||
        const wantsDateSeparator = this._wantsDateSeparator(prevEvent, eventDate);
 | 
					        const wantsDateSeparator = this._wantsDateSeparator(prevEvent, eventDate);
 | 
				
			||||||
        if (wantsDateSeparator) {
 | 
					        if (wantsDateSeparator && !isGrouped) {
 | 
				
			||||||
            const dateSeparator = <li key={ts1}><DateSeparator key={ts1} ts={ts1} /></li>;
 | 
					            const dateSeparator = <li key={ts1}><DateSeparator key={ts1} ts={ts1} /></li>;
 | 
				
			||||||
            ret.push(dateSeparator);
 | 
					            ret.push(dateSeparator);
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
@@ -968,9 +970,9 @@ class CreationGrouper {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
        const DateSeparator = sdk.getComponent('messages.DateSeparator');
 | 
					        const DateSeparator = sdk.getComponent('messages.DateSeparator');
 | 
				
			||||||
        const EventListSummary = sdk.getComponent('views.elements.EventListSummary');
 | 
					        const EventListSummary = sdk.getComponent('views.elements.EventListSummary');
 | 
				
			||||||
 | 
					 | 
				
			||||||
        const panel = this.panel;
 | 
					        const panel = this.panel;
 | 
				
			||||||
        const ret = [];
 | 
					        const ret = [];
 | 
				
			||||||
 | 
					        const isGrouped = true;
 | 
				
			||||||
        const createEvent = this.createEvent;
 | 
					        const createEvent = this.createEvent;
 | 
				
			||||||
        const lastShownEvent = this.lastShownEvent;
 | 
					        const lastShownEvent = this.lastShownEvent;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -984,12 +986,12 @@ class CreationGrouper {
 | 
				
			|||||||
        // If this m.room.create event should be shown (room upgrade) then show it before the summary
 | 
					        // If this m.room.create event should be shown (room upgrade) then show it before the summary
 | 
				
			||||||
        if (panel._shouldShowEvent(createEvent)) {
 | 
					        if (panel._shouldShowEvent(createEvent)) {
 | 
				
			||||||
            // pass in the createEvent as prevEvent as well so no extra DateSeparator is rendered
 | 
					            // pass in the createEvent as prevEvent as well so no extra DateSeparator is rendered
 | 
				
			||||||
            ret.push(...panel._getTilesForEvent(createEvent, createEvent, false));
 | 
					            ret.push(...panel._getTilesForEvent(createEvent, createEvent));
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        for (const ejected of this.ejectedEvents) {
 | 
					        for (const ejected of this.ejectedEvents) {
 | 
				
			||||||
            ret.push(...panel._getTilesForEvent(
 | 
					            ret.push(...panel._getTilesForEvent(
 | 
				
			||||||
                createEvent, ejected, createEvent === lastShownEvent,
 | 
					                createEvent, ejected, createEvent === lastShownEvent, isGrouped,
 | 
				
			||||||
            ));
 | 
					            ));
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -998,7 +1000,7 @@ class CreationGrouper {
 | 
				
			|||||||
            // of EventListSummary, render each member event as if the previous
 | 
					            // of EventListSummary, render each member event as if the previous
 | 
				
			||||||
            // one was itself. This way, the timestamp of the previous event === the
 | 
					            // one was itself. This way, the timestamp of the previous event === the
 | 
				
			||||||
            // timestamp of the current event, and no DateSeparator is inserted.
 | 
					            // timestamp of the current event, and no DateSeparator is inserted.
 | 
				
			||||||
            return panel._getTilesForEvent(e, e, e === lastShownEvent);
 | 
					            return panel._getTilesForEvent(e, e, e === lastShownEvent, isGrouped);
 | 
				
			||||||
        }).reduce((a, b) => a.concat(b), []);
 | 
					        }).reduce((a, b) => a.concat(b), []);
 | 
				
			||||||
        // Get sender profile from the latest event in the summary as the m.room.create doesn't contain one
 | 
					        // Get sender profile from the latest event in the summary as the m.room.create doesn't contain one
 | 
				
			||||||
        const ev = this.events[this.events.length - 1];
 | 
					        const ev = this.events[this.events.length - 1];
 | 
				
			||||||
@@ -1083,7 +1085,7 @@ class RedactionGrouper {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
        const DateSeparator = sdk.getComponent('messages.DateSeparator');
 | 
					        const DateSeparator = sdk.getComponent('messages.DateSeparator');
 | 
				
			||||||
        const EventListSummary = sdk.getComponent('views.elements.EventListSummary');
 | 
					        const EventListSummary = sdk.getComponent('views.elements.EventListSummary');
 | 
				
			||||||
 | 
					        const isGrouped = true;
 | 
				
			||||||
        const panel = this.panel;
 | 
					        const panel = this.panel;
 | 
				
			||||||
        const ret = [];
 | 
					        const ret = [];
 | 
				
			||||||
        const lastShownEvent = this.lastShownEvent;
 | 
					        const lastShownEvent = this.lastShownEvent;
 | 
				
			||||||
@@ -1103,7 +1105,8 @@ class RedactionGrouper {
 | 
				
			|||||||
        let eventTiles = this.events.map((e, i) => {
 | 
					        let eventTiles = this.events.map((e, i) => {
 | 
				
			||||||
            senders.add(e.sender);
 | 
					            senders.add(e.sender);
 | 
				
			||||||
            const prevEvent = i === 0 ? this.prevEvent : this.events[i - 1];
 | 
					            const prevEvent = i === 0 ? this.prevEvent : this.events[i - 1];
 | 
				
			||||||
            return panel._getTilesForEvent(prevEvent, e, e === lastShownEvent, this.nextEvent, this.nextEventTile);
 | 
					            return panel._getTilesForEvent(
 | 
				
			||||||
 | 
					                prevEvent, e, e === lastShownEvent, isGrouped, this.nextEvent, this.nextEventTile);
 | 
				
			||||||
        }).reduce((a, b) => a.concat(b), []);
 | 
					        }).reduce((a, b) => a.concat(b), []);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        if (eventTiles.length === 0) {
 | 
					        if (eventTiles.length === 0) {
 | 
				
			||||||
@@ -1182,7 +1185,7 @@ class MemberGrouper {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
        const DateSeparator = sdk.getComponent('messages.DateSeparator');
 | 
					        const DateSeparator = sdk.getComponent('messages.DateSeparator');
 | 
				
			||||||
        const MemberEventListSummary = sdk.getComponent('views.elements.MemberEventListSummary');
 | 
					        const MemberEventListSummary = sdk.getComponent('views.elements.MemberEventListSummary');
 | 
				
			||||||
 | 
					        const isGrouped = true;
 | 
				
			||||||
        const panel = this.panel;
 | 
					        const panel = this.panel;
 | 
				
			||||||
        const lastShownEvent = this.lastShownEvent;
 | 
					        const lastShownEvent = this.lastShownEvent;
 | 
				
			||||||
        const ret = [];
 | 
					        const ret = [];
 | 
				
			||||||
@@ -1215,7 +1218,7 @@ class MemberGrouper {
 | 
				
			|||||||
            // of MemberEventListSummary, render each member event as if the previous
 | 
					            // of MemberEventListSummary, render each member event as if the previous
 | 
				
			||||||
            // one was itself. This way, the timestamp of the previous event === the
 | 
					            // one was itself. This way, the timestamp of the previous event === the
 | 
				
			||||||
            // timestamp of the current event, and no DateSeparator is inserted.
 | 
					            // timestamp of the current event, and no DateSeparator is inserted.
 | 
				
			||||||
            return panel._getTilesForEvent(e, e, e === lastShownEvent);
 | 
					            return panel._getTilesForEvent(e, e, e === lastShownEvent, isGrouped);
 | 
				
			||||||
        }).reduce((a, b) => a.concat(b), []);
 | 
					        }).reduce((a, b) => a.concat(b), []);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        if (eventTiles.length === 0) {
 | 
					        if (eventTiles.length === 0) {
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -27,8 +27,8 @@ import { Action } from "../../dispatcher/actions";
 | 
				
			|||||||
import RoomListStore from "../../stores/room-list/RoomListStore";
 | 
					import RoomListStore from "../../stores/room-list/RoomListStore";
 | 
				
			||||||
import { NameFilterCondition } from "../../stores/room-list/filters/NameFilterCondition";
 | 
					import { NameFilterCondition } from "../../stores/room-list/filters/NameFilterCondition";
 | 
				
			||||||
import { getKeyBindingsManager, RoomListAction } from "../../KeyBindingsManager";
 | 
					import { getKeyBindingsManager, RoomListAction } from "../../KeyBindingsManager";
 | 
				
			||||||
import {replaceableComponent} from "../../utils/replaceableComponent";
 | 
					import { replaceableComponent } from "../../utils/replaceableComponent";
 | 
				
			||||||
import SpaceStore, {UPDATE_SELECTED_SPACE, UPDATE_TOP_LEVEL_SPACES} from "../../stores/SpaceStore";
 | 
					import SpaceStore, { UPDATE_SELECTED_SPACE, UPDATE_TOP_LEVEL_SPACES } from "../../stores/SpaceStore";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
interface IProps {
 | 
					interface IProps {
 | 
				
			||||||
    isMinimized: boolean;
 | 
					    isMinimized: boolean;
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -187,9 +187,15 @@ function DeviceItem({userId, device}: {userId: string, device: IDevice}) {
 | 
				
			|||||||
        verifyDevice(cli.getUser(userId), device);
 | 
					        verifyDevice(cli.getUser(userId), device);
 | 
				
			||||||
    };
 | 
					    };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    const deviceName = device.ambiguous ?
 | 
					    let deviceName;
 | 
				
			||||||
        (device.getDisplayName() ? device.getDisplayName() : "") + " (" + device.deviceId + ")" :
 | 
					    if (!device.getDisplayName()?.trim()) {
 | 
				
			||||||
        device.getDisplayName();
 | 
					        deviceName = device.deviceId;
 | 
				
			||||||
 | 
					    } else {
 | 
				
			||||||
 | 
					        deviceName = device.ambiguous ?
 | 
				
			||||||
 | 
					            device.getDisplayName() + " (" + device.deviceId + ")" :
 | 
				
			||||||
 | 
					            device.getDisplayName();
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    let trustedLabel = null;
 | 
					    let trustedLabel = null;
 | 
				
			||||||
    if (userTrust.isVerified()) trustedLabel = isVerified ? _t("Trusted") : _t("Not trusted");
 | 
					    if (userTrust.isVerified()) trustedLabel = isVerified ? _t("Trusted") : _t("Not trusted");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -16,7 +16,7 @@ limitations under the License.
 | 
				
			|||||||
*/
 | 
					*/
 | 
				
			||||||
import React from 'react';
 | 
					import React from 'react';
 | 
				
			||||||
import * as sdk from '../../../index';
 | 
					import * as sdk from '../../../index';
 | 
				
			||||||
import {_t} from '../../../languageHandler';
 | 
					import {_t, _td} from '../../../languageHandler';
 | 
				
			||||||
import PropTypes from 'prop-types';
 | 
					import PropTypes from 'prop-types';
 | 
				
			||||||
import dis from '../../../dispatcher/dispatcher';
 | 
					import dis from '../../../dispatcher/dispatcher';
 | 
				
			||||||
import EditorModel from '../../../editor/model';
 | 
					import EditorModel from '../../../editor/model';
 | 
				
			||||||
@@ -24,16 +24,18 @@ import {getCaretOffsetAndText} from '../../../editor/dom';
 | 
				
			|||||||
import {htmlSerializeIfNeeded, textSerialize, containsEmote, stripEmoteCommand} from '../../../editor/serialize';
 | 
					import {htmlSerializeIfNeeded, textSerialize, containsEmote, stripEmoteCommand} from '../../../editor/serialize';
 | 
				
			||||||
import {findEditableEvent} from '../../../utils/EventUtils';
 | 
					import {findEditableEvent} from '../../../utils/EventUtils';
 | 
				
			||||||
import {parseEvent} from '../../../editor/deserialize';
 | 
					import {parseEvent} from '../../../editor/deserialize';
 | 
				
			||||||
import {PartCreator} from '../../../editor/parts';
 | 
					import {CommandPartCreator} from '../../../editor/parts';
 | 
				
			||||||
import EditorStateTransfer from '../../../utils/EditorStateTransfer';
 | 
					import EditorStateTransfer from '../../../utils/EditorStateTransfer';
 | 
				
			||||||
import classNames from 'classnames';
 | 
					import classNames from 'classnames';
 | 
				
			||||||
import {EventStatus} from 'matrix-js-sdk/src/models/event';
 | 
					import {EventStatus} from 'matrix-js-sdk/src/models/event';
 | 
				
			||||||
import BasicMessageComposer from "./BasicMessageComposer";
 | 
					import BasicMessageComposer from "./BasicMessageComposer";
 | 
				
			||||||
import MatrixClientContext from "../../../contexts/MatrixClientContext";
 | 
					import MatrixClientContext from "../../../contexts/MatrixClientContext";
 | 
				
			||||||
 | 
					import {CommandCategories, getCommand} from '../../../SlashCommands';
 | 
				
			||||||
import {Action} from "../../../dispatcher/actions";
 | 
					import {Action} from "../../../dispatcher/actions";
 | 
				
			||||||
import CountlyAnalytics from "../../../CountlyAnalytics";
 | 
					import CountlyAnalytics from "../../../CountlyAnalytics";
 | 
				
			||||||
import {getKeyBindingsManager, MessageComposerAction} from '../../../KeyBindingsManager';
 | 
					import {getKeyBindingsManager, MessageComposerAction} from '../../../KeyBindingsManager';
 | 
				
			||||||
import {replaceableComponent} from "../../../utils/replaceableComponent";
 | 
					import {replaceableComponent} from "../../../utils/replaceableComponent";
 | 
				
			||||||
 | 
					import Modal from '../../../Modal';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
function _isReply(mxEvent) {
 | 
					function _isReply(mxEvent) {
 | 
				
			||||||
    const relatesTo = mxEvent.getContent()["m.relates_to"];
 | 
					    const relatesTo = mxEvent.getContent()["m.relates_to"];
 | 
				
			||||||
@@ -178,6 +180,22 @@ export default class EditMessageComposer extends React.Component {
 | 
				
			|||||||
        dis.fire(Action.FocusComposer);
 | 
					        dis.fire(Action.FocusComposer);
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    _isSlashCommand() {
 | 
				
			||||||
 | 
					        const parts = this.model.parts;
 | 
				
			||||||
 | 
					        const firstPart = parts[0];
 | 
				
			||||||
 | 
					        if (firstPart) {
 | 
				
			||||||
 | 
					            if (firstPart.type === "command" && firstPart.text.startsWith("/") && !firstPart.text.startsWith("//")) {
 | 
				
			||||||
 | 
					                return true;
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            if (firstPart.text.startsWith("/") && !firstPart.text.startsWith("//")
 | 
				
			||||||
 | 
					                && (firstPart.type === "plain" || firstPart.type === "pill-candidate")) {
 | 
				
			||||||
 | 
					                return true;
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        return false;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    _isContentModified(newContent) {
 | 
					    _isContentModified(newContent) {
 | 
				
			||||||
        // if nothing has changed then bail
 | 
					        // if nothing has changed then bail
 | 
				
			||||||
        const oldContent = this.props.editState.getEvent().getContent();
 | 
					        const oldContent = this.props.editState.getEvent().getContent();
 | 
				
			||||||
@@ -190,19 +208,112 @@ export default class EditMessageComposer extends React.Component {
 | 
				
			|||||||
        return true;
 | 
					        return true;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    _sendEdit = () => {
 | 
					    _getSlashCommand() {
 | 
				
			||||||
 | 
					        const commandText = this.model.parts.reduce((text, part) => {
 | 
				
			||||||
 | 
					            // use mxid to textify user pills in a command
 | 
				
			||||||
 | 
					            if (part.type === "user-pill") {
 | 
				
			||||||
 | 
					                return text + part.resourceId;
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            return text + part.text;
 | 
				
			||||||
 | 
					        }, "");
 | 
				
			||||||
 | 
					        const {cmd, args} = getCommand(commandText);
 | 
				
			||||||
 | 
					        return [cmd, args, commandText];
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    async _runSlashCommand(cmd, args, roomId) {
 | 
				
			||||||
 | 
					        const result = cmd.run(roomId, args);
 | 
				
			||||||
 | 
					        let messageContent;
 | 
				
			||||||
 | 
					        let error = result.error;
 | 
				
			||||||
 | 
					        if (result.promise) {
 | 
				
			||||||
 | 
					            try {
 | 
				
			||||||
 | 
					                if (cmd.category === CommandCategories.messages) {
 | 
				
			||||||
 | 
					                    messageContent = await result.promise;
 | 
				
			||||||
 | 
					                } else {
 | 
				
			||||||
 | 
					                    await result.promise;
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					            } catch (err) {
 | 
				
			||||||
 | 
					                error = err;
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        if (error) {
 | 
				
			||||||
 | 
					            console.error("Command failure: %s", error);
 | 
				
			||||||
 | 
					            const ErrorDialog = sdk.getComponent("dialogs.ErrorDialog");
 | 
				
			||||||
 | 
					            // assume the error is a server error when the command is async
 | 
				
			||||||
 | 
					            const isServerError = !!result.promise;
 | 
				
			||||||
 | 
					            const title = isServerError ? _td("Server error") : _td("Command error");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            let errText;
 | 
				
			||||||
 | 
					            if (typeof error === 'string') {
 | 
				
			||||||
 | 
					                errText = error;
 | 
				
			||||||
 | 
					            } else if (error.message) {
 | 
				
			||||||
 | 
					                errText = error.message;
 | 
				
			||||||
 | 
					            } else {
 | 
				
			||||||
 | 
					                errText = _t("Server unavailable, overloaded, or something else went wrong.");
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            Modal.createTrackedDialog(title, '', ErrorDialog, {
 | 
				
			||||||
 | 
					                title: _t(title),
 | 
				
			||||||
 | 
					                description: errText,
 | 
				
			||||||
 | 
					            });
 | 
				
			||||||
 | 
					        } else {
 | 
				
			||||||
 | 
					            console.log("Command success.");
 | 
				
			||||||
 | 
					            if (messageContent) return messageContent;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    _sendEdit = async () => {
 | 
				
			||||||
        const startTime = CountlyAnalytics.getTimestamp();
 | 
					        const startTime = CountlyAnalytics.getTimestamp();
 | 
				
			||||||
        const editedEvent = this.props.editState.getEvent();
 | 
					        const editedEvent = this.props.editState.getEvent();
 | 
				
			||||||
        const editContent = createEditContent(this.model, editedEvent);
 | 
					        const editContent = createEditContent(this.model, editedEvent);
 | 
				
			||||||
        const newContent = editContent["m.new_content"];
 | 
					        const newContent = editContent["m.new_content"];
 | 
				
			||||||
 | 
					        let shouldSend = true;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        // If content is modified then send an updated event into the room
 | 
					        // If content is modified then send an updated event into the room
 | 
				
			||||||
        if (this._isContentModified(newContent)) {
 | 
					        if (this._isContentModified(newContent)) {
 | 
				
			||||||
            const roomId = editedEvent.getRoomId();
 | 
					            const roomId = editedEvent.getRoomId();
 | 
				
			||||||
            this._cancelPreviousPendingEdit();
 | 
					            if (!containsEmote(this.model) && this._isSlashCommand()) {
 | 
				
			||||||
            const prom = this.context.sendMessage(roomId, editContent);
 | 
					                const [cmd, args, commandText] = this._getSlashCommand();
 | 
				
			||||||
            dis.dispatch({action: "message_sent"});
 | 
					                if (cmd) {
 | 
				
			||||||
            CountlyAnalytics.instance.trackSendMessage(startTime, prom, roomId, true, false, editContent);
 | 
					                    if (cmd.category === CommandCategories.messages) {
 | 
				
			||||||
 | 
					                        editContent["m.new_content"] = await this._runSlashCommand(cmd, args, roomId);
 | 
				
			||||||
 | 
					                    } else {
 | 
				
			||||||
 | 
					                        this._runSlashCommand(cmd, args, roomId);
 | 
				
			||||||
 | 
					                        shouldSend = false;
 | 
				
			||||||
 | 
					                    }
 | 
				
			||||||
 | 
					                } else {
 | 
				
			||||||
 | 
					                    // ask the user if their unknown command should be sent as a message
 | 
				
			||||||
 | 
					                    const QuestionDialog = sdk.getComponent("dialogs.QuestionDialog");
 | 
				
			||||||
 | 
					                    const {finished} = Modal.createTrackedDialog("Unknown command", "", QuestionDialog, {
 | 
				
			||||||
 | 
					                        title: _t("Unknown Command"),
 | 
				
			||||||
 | 
					                        description: <div>
 | 
				
			||||||
 | 
					                            <p>
 | 
				
			||||||
 | 
					                                { _t("Unrecognised command: %(commandText)s", {commandText}) }
 | 
				
			||||||
 | 
					                            </p>
 | 
				
			||||||
 | 
					                            <p>
 | 
				
			||||||
 | 
					                                { _t("You can use <code>/help</code> to list available commands. " +
 | 
				
			||||||
 | 
					                                    "Did you mean to send this as a message?", {}, {
 | 
				
			||||||
 | 
					                                    code: t => <code>{ t }</code>,
 | 
				
			||||||
 | 
					                                }) }
 | 
				
			||||||
 | 
					                            </p>
 | 
				
			||||||
 | 
					                            <p>
 | 
				
			||||||
 | 
					                                { _t("Hint: Begin your message with <code>//</code> to start it with a slash.", {}, {
 | 
				
			||||||
 | 
					                                    code: t => <code>{ t }</code>,
 | 
				
			||||||
 | 
					                                }) }
 | 
				
			||||||
 | 
					                            </p>
 | 
				
			||||||
 | 
					                        </div>,
 | 
				
			||||||
 | 
					                        button: _t('Send as message'),
 | 
				
			||||||
 | 
					                    });
 | 
				
			||||||
 | 
					                    const [sendAnyway] = await finished;
 | 
				
			||||||
 | 
					                    // if !sendAnyway bail to let the user edit the composer and try again
 | 
				
			||||||
 | 
					                    if (!sendAnyway) return;
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            if (shouldSend) {
 | 
				
			||||||
 | 
					                this._cancelPreviousPendingEdit();
 | 
				
			||||||
 | 
					                const prom = this.context.sendMessage(roomId, editContent);
 | 
				
			||||||
 | 
					                dis.dispatch({action: "message_sent"});
 | 
				
			||||||
 | 
					                CountlyAnalytics.instance.trackSendMessage(startTime, prom, roomId, true, false, editContent);
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        // close the event editing and focus composer
 | 
					        // close the event editing and focus composer
 | 
				
			||||||
@@ -240,7 +351,7 @@ export default class EditMessageComposer extends React.Component {
 | 
				
			|||||||
    _createEditorModel() {
 | 
					    _createEditorModel() {
 | 
				
			||||||
        const {editState} = this.props;
 | 
					        const {editState} = this.props;
 | 
				
			||||||
        const room = this._getRoom();
 | 
					        const room = this._getRoom();
 | 
				
			||||||
        const partCreator = new PartCreator(room, this.context);
 | 
					        const partCreator = new CommandPartCreator(room, this.context);
 | 
				
			||||||
        let parts;
 | 
					        let parts;
 | 
				
			||||||
        if (editState.hasEditorState()) {
 | 
					        if (editState.hasEditorState()) {
 | 
				
			||||||
            // if restoring state from a previous editor,
 | 
					            // if restoring state from a previous editor,
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -26,13 +26,11 @@ 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,
 | 
				
			||||||
} from "../../../stores/SpaceStore";
 | 
					} from "../../../stores/SpaceStore";
 | 
				
			||||||
import AutoHideScrollbar from "../../structures/AutoHideScrollbar";
 | 
					import AutoHideScrollbar from "../../structures/AutoHideScrollbar";
 | 
				
			||||||
import {SpaceNotificationState} from "../../../stores/notifications/SpaceNotificationState";
 | 
					 | 
				
			||||||
import NotificationBadge from "../rooms/NotificationBadge";
 | 
					import NotificationBadge from "../rooms/NotificationBadge";
 | 
				
			||||||
import {
 | 
					import {
 | 
				
			||||||
    RovingAccessibleButton,
 | 
					    RovingAccessibleButton,
 | 
				
			||||||
@@ -40,13 +38,15 @@ import {
 | 
				
			|||||||
    RovingTabIndexProvider,
 | 
					    RovingTabIndexProvider,
 | 
				
			||||||
} from "../../../accessibility/RovingTabIndex";
 | 
					} from "../../../accessibility/RovingTabIndex";
 | 
				
			||||||
import {Key} from "../../../Keyboard";
 | 
					import {Key} from "../../../Keyboard";
 | 
				
			||||||
 | 
					import {RoomNotificationStateStore} from "../../../stores/notifications/RoomNotificationStateStore";
 | 
				
			||||||
 | 
					import {NotificationState} from "../../../stores/notifications/NotificationState";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
interface IButtonProps {
 | 
					interface IButtonProps {
 | 
				
			||||||
    space?: Room;
 | 
					    space?: Room;
 | 
				
			||||||
    className?: string;
 | 
					    className?: string;
 | 
				
			||||||
    selected?: boolean;
 | 
					    selected?: boolean;
 | 
				
			||||||
    tooltip?: string;
 | 
					    tooltip?: string;
 | 
				
			||||||
    notificationState?: SpaceNotificationState;
 | 
					    notificationState?: NotificationState;
 | 
				
			||||||
    isNarrow?: boolean;
 | 
					    isNarrow?: boolean;
 | 
				
			||||||
    onClick(): void;
 | 
					    onClick(): void;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
@@ -212,8 +212,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("Home")}
 | 
					                            tooltip={_t("All rooms")}
 | 
				
			||||||
                            notificationState={SpaceStore.instance.getNotificationState(HOME_SPACE)}
 | 
					                            notificationState={RoomNotificationStateStore.instance.globalState}
 | 
				
			||||||
                            isNarrow={isPanelCollapsed}
 | 
					                            isNarrow={isPanelCollapsed}
 | 
				
			||||||
                        />
 | 
					                        />
 | 
				
			||||||
                        { invites.map(s => <SpaceItem
 | 
					                        { invites.map(s => <SpaceItem
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -116,14 +116,22 @@ export function htmlSerializeIfNeeded(model: EditorModel, {forceHTML = false} =
 | 
				
			|||||||
    const parser = new Markdown(md);
 | 
					    const parser = new Markdown(md);
 | 
				
			||||||
    if (!parser.isPlainText() || forceHTML) {
 | 
					    if (!parser.isPlainText() || forceHTML) {
 | 
				
			||||||
        // feed Markdown output to HTML parser
 | 
					        // feed Markdown output to HTML parser
 | 
				
			||||||
        const phtml = cheerio.load(parser.toHTML(),
 | 
					        const phtml = cheerio.load(parser.toHTML(), {
 | 
				
			||||||
            { _useHtmlParser2: true, decodeEntities: false });
 | 
					            // @ts-ignore: The `_useHtmlParser2` internal option is the
 | 
				
			||||||
 | 
					            // simplest way to both parse and render using `htmlparser2`.
 | 
				
			||||||
 | 
					            _useHtmlParser2: true,
 | 
				
			||||||
 | 
					            decodeEntities: false,
 | 
				
			||||||
 | 
					        });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        if (SettingsStore.getValue("feature_latex_maths")) {
 | 
					        if (SettingsStore.getValue("feature_latex_maths")) {
 | 
				
			||||||
            // original Markdown without LaTeX replacements
 | 
					            // original Markdown without LaTeX replacements
 | 
				
			||||||
            const parserOrig = new Markdown(orig);
 | 
					            const parserOrig = new Markdown(orig);
 | 
				
			||||||
            const phtmlOrig = cheerio.load(parserOrig.toHTML(),
 | 
					            const phtmlOrig = cheerio.load(parserOrig.toHTML(), {
 | 
				
			||||||
                { _useHtmlParser2: true, decodeEntities: false });
 | 
					                // @ts-ignore: The `_useHtmlParser2` internal option is the
 | 
				
			||||||
 | 
					                // simplest way to both parse and render using `htmlparser2`.
 | 
				
			||||||
 | 
					                _useHtmlParser2: true,
 | 
				
			||||||
 | 
					                decodeEntities: false,
 | 
				
			||||||
 | 
					            });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            // since maths delimiters are handled before Markdown,
 | 
					            // since maths delimiters are handled before Markdown,
 | 
				
			||||||
            // code blocks could contain mangled content.
 | 
					            // code blocks could contain mangled content.
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1012,7 +1012,7 @@
 | 
				
			|||||||
    "Create": "Create",
 | 
					    "Create": "Create",
 | 
				
			||||||
    "Expand space panel": "Expand space panel",
 | 
					    "Expand space panel": "Expand space panel",
 | 
				
			||||||
    "Collapse space panel": "Collapse space panel",
 | 
					    "Collapse space panel": "Collapse space panel",
 | 
				
			||||||
    "Home": "Home",
 | 
					    "All rooms": "All rooms",
 | 
				
			||||||
    "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",
 | 
				
			||||||
@@ -1441,6 +1441,13 @@
 | 
				
			|||||||
    "Someone is using an unknown session": "Someone is using an unknown session",
 | 
					    "Someone is using an unknown session": "Someone is using an unknown session",
 | 
				
			||||||
    "This room is end-to-end encrypted": "This room is end-to-end encrypted",
 | 
					    "This room is end-to-end encrypted": "This room is end-to-end encrypted",
 | 
				
			||||||
    "Everyone in this room is verified": "Everyone in this room is verified",
 | 
					    "Everyone in this room is verified": "Everyone in this room is verified",
 | 
				
			||||||
 | 
					    "Server error": "Server error",
 | 
				
			||||||
 | 
					    "Server unavailable, overloaded, or something else went wrong.": "Server unavailable, overloaded, or something else went wrong.",
 | 
				
			||||||
 | 
					    "Unknown Command": "Unknown Command",
 | 
				
			||||||
 | 
					    "Unrecognised command: %(commandText)s": "Unrecognised command: %(commandText)s",
 | 
				
			||||||
 | 
					    "You can use <code>/help</code> to list available commands. Did you mean to send this as a message?": "You can use <code>/help</code> to list available commands. Did you mean to send this as a message?",
 | 
				
			||||||
 | 
					    "Hint: Begin your message with <code>//</code> to start it with a slash.": "Hint: Begin your message with <code>//</code> to start it with a slash.",
 | 
				
			||||||
 | 
					    "Send as message": "Send as message",
 | 
				
			||||||
    "Edit message": "Edit message",
 | 
					    "Edit message": "Edit message",
 | 
				
			||||||
    "Mod": "Mod",
 | 
					    "Mod": "Mod",
 | 
				
			||||||
    "This event could not be displayed": "This event could not be displayed",
 | 
					    "This event could not be displayed": "This event could not be displayed",
 | 
				
			||||||
@@ -1631,13 +1638,6 @@
 | 
				
			|||||||
    "This Room": "This Room",
 | 
					    "This Room": "This Room",
 | 
				
			||||||
    "All Rooms": "All Rooms",
 | 
					    "All Rooms": "All Rooms",
 | 
				
			||||||
    "Search…": "Search…",
 | 
					    "Search…": "Search…",
 | 
				
			||||||
    "Server error": "Server error",
 | 
					 | 
				
			||||||
    "Server unavailable, overloaded, or something else went wrong.": "Server unavailable, overloaded, or something else went wrong.",
 | 
					 | 
				
			||||||
    "Unknown Command": "Unknown Command",
 | 
					 | 
				
			||||||
    "Unrecognised command: %(commandText)s": "Unrecognised command: %(commandText)s",
 | 
					 | 
				
			||||||
    "You can use <code>/help</code> to list available commands. Did you mean to send this as a message?": "You can use <code>/help</code> to list available commands. Did you mean to send this as a message?",
 | 
					 | 
				
			||||||
    "Hint: Begin your message with <code>//</code> to start it with a slash.": "Hint: Begin your message with <code>//</code> to start it with a slash.",
 | 
					 | 
				
			||||||
    "Send as message": "Send as message",
 | 
					 | 
				
			||||||
    "Failed to connect to integration manager": "Failed to connect to integration manager",
 | 
					    "Failed to connect to integration manager": "Failed to connect to integration manager",
 | 
				
			||||||
    "You don't currently have any stickerpacks enabled": "You don't currently have any stickerpacks enabled",
 | 
					    "You don't currently have any stickerpacks enabled": "You don't currently have any stickerpacks enabled",
 | 
				
			||||||
    "Add some now": "Add some now",
 | 
					    "Add some now": "Add some now",
 | 
				
			||||||
@@ -2016,10 +2016,10 @@
 | 
				
			|||||||
    "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",
 | 
				
			||||||
    "Can't find this server or its room list": "Can't find this server or its room list",
 | 
					    "Can't find this server or its room list": "Can't find this server or its room list",
 | 
				
			||||||
    "All rooms": "All rooms",
 | 
					 | 
				
			||||||
    "Your server": "Your server",
 | 
					    "Your server": "Your server",
 | 
				
			||||||
    "Are you sure you want to remove <b>%(serverName)s</b>": "Are you sure you want to remove <b>%(serverName)s</b>",
 | 
					    "Are you sure you want to remove <b>%(serverName)s</b>": "Are you sure you want to remove <b>%(serverName)s</b>",
 | 
				
			||||||
    "Remove server": "Remove server",
 | 
					    "Remove server": "Remove server",
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -31,28 +31,23 @@ import {RoomNotificationStateStore} from "./notifications/RoomNotificationStateS
 | 
				
			|||||||
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 {objectDiff} from "../utils/objects";
 | 
					 | 
				
			||||||
import {arrayHasDiff} from "../utils/arrays";
 | 
					 | 
				
			||||||
import {ISpaceSummaryEvent, ISpaceSummaryRoom} from "../components/structures/SpaceRoomDirectory";
 | 
					import {ISpaceSummaryEvent, ISpaceSummaryRoom} from "../components/structures/SpaceRoomDirectory";
 | 
				
			||||||
import RoomViewStore from "./RoomViewStore";
 | 
					import RoomViewStore from "./RoomViewStore";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
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/HOME_SPACE will be emitted when a Space's children change
 | 
					// Space Room ID will be emitted when a Space's children change
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const MAX_SUGGESTED_ROOMS = 20;
 | 
					const MAX_SUGGESTED_ROOMS = 20;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const getSpaceContextKey = (space?: Room) => `mx_space_context_${space?.roomId || "home_space"}`;
 | 
					const getSpaceContextKey = (space?: Room) => `mx_space_context_${space?.roomId || "ALL_ROOMS"}`;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
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) => {
 | 
				
			||||||
@@ -86,15 +81,13 @@ 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 space key to SpaceNotificationState instance representing that space
 | 
					    // Map from spaceId to SpaceNotificationState instance representing that space
 | 
				
			||||||
    private notificationStateMap = new Map<SpaceKey, SpaceNotificationState>();
 | 
					    private notificationStateMap = new Map<string, 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 | symbol, Set<string>>();
 | 
					    private spaceFilteredRooms = new Map<string, Set<string>>();
 | 
				
			||||||
    // The space currently selected in the Space Panel - if null then `Home` is selected
 | 
					    // The space currently selected in the Space Panel - if null then All Rooms is selected
 | 
				
			||||||
    private _activeSpace?: Room = null;
 | 
					    private _activeSpace?: Room = null;
 | 
				
			||||||
    private _suggestedRooms: ISpaceSummaryRoom[] = [];
 | 
					    private _suggestedRooms: ISpaceSummaryRoom[] = [];
 | 
				
			||||||
    private _invitedSpaces = new Set<Room>();
 | 
					    private _invitedSpaces = new Set<Room>();
 | 
				
			||||||
@@ -244,7 +237,10 @@ export class SpaceStoreClass extends AsyncStoreWithClient<IState> {
 | 
				
			|||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    public getSpaceFilteredRoomIds = (space: Room | null): Set<string> => {
 | 
					    public getSpaceFilteredRoomIds = (space: Room | null): Set<string> => {
 | 
				
			||||||
        return this.spaceFilteredRooms.get(space?.roomId || HOME_SPACE) || new Set();
 | 
					        if (!space) {
 | 
				
			||||||
 | 
					            return new Set(this.matrixClient.getVisibleRooms().map(r => r.roomId));
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        return this.spaceFilteredRooms.get(space.roomId) || new Set();
 | 
				
			||||||
    };
 | 
					    };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    private rebuild = throttle(() => {
 | 
					    private rebuild = throttle(() => {
 | 
				
			||||||
@@ -275,7 +271,7 @@ export class SpaceStoreClass extends AsyncStoreWithClient<IState> {
 | 
				
			|||||||
            });
 | 
					            });
 | 
				
			||||||
        });
 | 
					        });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        const [rootSpaces, orphanedRooms] = partitionSpacesAndRooms(Array.from(unseenChildren));
 | 
					        const [rootSpaces] = 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);
 | 
				
			||||||
@@ -316,7 +312,6 @@ 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;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -337,25 +332,6 @@ export class SpaceStoreClass extends AsyncStoreWithClient<IState> {
 | 
				
			|||||||
        this.rebuild();
 | 
					        this.rebuild();
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    private showInHomeSpace = (room: Room) => {
 | 
					 | 
				
			||||||
        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;
 | 
				
			||||||
@@ -369,16 +345,6 @@ export class SpaceStoreClass extends AsyncStoreWithClient<IState> {
 | 
				
			|||||||
        const oldFilteredRooms = this.spaceFilteredRooms;
 | 
					        const oldFilteredRooms = this.spaceFilteredRooms;
 | 
				
			||||||
        this.spaceFilteredRooms = new Map();
 | 
					        this.spaceFilteredRooms = new Map();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        // 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.
 | 
				
			||||||
@@ -425,13 +391,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)) {
 | 
				
			||||||
                    // Don't aggregate notifications for DMs except in the Home Space
 | 
					                    return !DMRoomMap.shared().getUserIdForRoomId(room.roomId)
 | 
				
			||||||
                    if (s !== HOME_SPACE) {
 | 
					                        || RoomListStore.instance.getTagsForRoom(room).includes(DefaultTagID.Favourite);
 | 
				
			||||||
                        return !DMRoomMap.shared().getUserIdForRoomId(room.roomId)
 | 
					 | 
				
			||||||
                            || RoomListStore.instance.getTagsForRoom(room).includes(DefaultTagID.Favourite);
 | 
					 | 
				
			||||||
                    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
                    return true;
 | 
					 | 
				
			||||||
                }
 | 
					                }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                return false;
 | 
					                return false;
 | 
				
			||||||
@@ -513,8 +474,6 @@ 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 {
 | 
					 | 
				
			||||||
                    this.onRoomUpdate(room);
 | 
					 | 
				
			||||||
                }
 | 
					                }
 | 
				
			||||||
                this.emit(room.roomId);
 | 
					                this.emit(room.roomId);
 | 
				
			||||||
                break;
 | 
					                break;
 | 
				
			||||||
@@ -527,38 +486,8 @@ 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();
 | 
				
			||||||
@@ -573,8 +502,6 @@ 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);
 | 
				
			||||||
            this.matrixClient.removeListener("Room.accountData", this.onRoomAccountData);
 | 
					 | 
				
			||||||
            this.matrixClient.removeListener("accountData", this.onAccountData);
 | 
					 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
        await this.reset();
 | 
					        await this.reset();
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
@@ -584,8 +511,6 @@ 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);
 | 
				
			||||||
        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
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -610,7 +535,7 @@ 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.getSpaceFilteredRoomIds(this.activeSpace).has(roomId)) {
 | 
					                } else if (this.activeSpace && !this.getSpaceFilteredRoomIds(this.activeSpace).has(roomId)) {
 | 
				
			||||||
                    this.switchToRelatedSpace(roomId);
 | 
					                    this.switchToRelatedSpace(roomId);
 | 
				
			||||||
                }
 | 
					                }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -628,7 +553,7 @@ export class SpaceStoreClass extends AsyncStoreWithClient<IState> {
 | 
				
			|||||||
        }
 | 
					        }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    public getNotificationState(key: SpaceKey): SpaceNotificationState {
 | 
					    public getNotificationState(key: string): SpaceNotificationState {
 | 
				
			||||||
        if (this.notificationStateMap.has(key)) {
 | 
					        if (this.notificationStateMap.has(key)) {
 | 
				
			||||||
            return this.notificationStateMap.get(key);
 | 
					            return this.notificationStateMap.get(key);
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -680,7 +680,7 @@ export class RoomListStoreClass extends AsyncStoreWithClient<IState> {
 | 
				
			|||||||
            promise = this.recalculatePrefiltering();
 | 
					            promise = this.recalculatePrefiltering();
 | 
				
			||||||
        } else {
 | 
					        } else {
 | 
				
			||||||
            this.filterConditions.push(filter);
 | 
					            this.filterConditions.push(filter);
 | 
				
			||||||
            // Runtime filters with spaces disable prefiltering for the search all spaces effect
 | 
					            // Runtime filters with spaces disable prefiltering for the search all spaces feature
 | 
				
			||||||
            if (SettingsStore.getValue("feature_spaces")) {
 | 
					            if (SettingsStore.getValue("feature_spaces")) {
 | 
				
			||||||
                // this has to be awaited so that `setKnownRooms` is called in time for the `addFilterCondition` below
 | 
					                // 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.
 | 
					                // this way the runtime filters are only evaluated on one dataset and not both.
 | 
				
			||||||
@@ -712,10 +712,10 @@ export class RoomListStoreClass extends AsyncStoreWithClient<IState> {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
            if (this.algorithm) {
 | 
					            if (this.algorithm) {
 | 
				
			||||||
                this.algorithm.removeFilterCondition(filter);
 | 
					                this.algorithm.removeFilterCondition(filter);
 | 
				
			||||||
                // Runtime filters with spaces disable prefiltering for the search all spaces effect
 | 
					            }
 | 
				
			||||||
                if (SettingsStore.getValue("feature_spaces")) {
 | 
					            // Runtime filters with spaces disable prefiltering for the search all spaces feature
 | 
				
			||||||
                    promise = this.recalculatePrefiltering();
 | 
					            if (SettingsStore.getValue("feature_spaces")) {
 | 
				
			||||||
                }
 | 
					                promise = this.recalculatePrefiltering();
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
        idx = this.prefilterConditions.indexOf(filter);
 | 
					        idx = this.prefilterConditions.indexOf(filter);
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -24,26 +24,34 @@ import SpaceStore, { UPDATE_SELECTED_SPACE } from "../SpaceStore";
 | 
				
			|||||||
 * 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
 | 
				
			||||||
 */
 | 
					 */
 | 
				
			||||||
export class SpaceWatcher {
 | 
					export class SpaceWatcher {
 | 
				
			||||||
    private filter = new SpaceFilterCondition();
 | 
					    private filter: SpaceFilterCondition;
 | 
				
			||||||
    private activeSpace: Room = SpaceStore.instance.activeSpace;
 | 
					    private activeSpace: Room = SpaceStore.instance.activeSpace;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    constructor(private store: RoomListStoreClass) {
 | 
					    constructor(private store: RoomListStoreClass) {
 | 
				
			||||||
        this.updateFilter(); // get the filter into a consistent state
 | 
					 | 
				
			||||||
        store.addFilter(this.filter);
 | 
					 | 
				
			||||||
        SpaceStore.instance.on(UPDATE_SELECTED_SPACE, this.onSelectedSpaceUpdated);
 | 
					        SpaceStore.instance.on(UPDATE_SELECTED_SPACE, this.onSelectedSpaceUpdated);
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    private onSelectedSpaceUpdated = (activeSpace: Room) => {
 | 
					    private onSelectedSpaceUpdated = (activeSpace?: Room) => {
 | 
				
			||||||
        this.activeSpace = activeSpace;
 | 
					        this.activeSpace = activeSpace;
 | 
				
			||||||
        this.updateFilter();
 | 
					
 | 
				
			||||||
 | 
					        if (this.filter) {
 | 
				
			||||||
 | 
					            if (activeSpace) {
 | 
				
			||||||
 | 
					                this.updateFilter();
 | 
				
			||||||
 | 
					            } else {
 | 
				
			||||||
 | 
					                this.store.removeFilter(this.filter);
 | 
				
			||||||
 | 
					                this.filter = null;
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        } else if (activeSpace) {
 | 
				
			||||||
 | 
					            this.filter = new SpaceFilterCondition();
 | 
				
			||||||
 | 
					            this.updateFilter();
 | 
				
			||||||
 | 
					            this.store.addFilter(this.filter);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
    };
 | 
					    };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    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, {HOME_SPACE} from "../../SpaceStore";
 | 
					import SpaceStore from "../../SpaceStore";
 | 
				
			||||||
import { setHasDiff } from "../../../utils/sets";
 | 
					import { setHasDiff } from "../../../utils/sets";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/**
 | 
					/**
 | 
				
			||||||
@@ -55,10 +55,12 @@ export class SpaceFilterCondition extends EventEmitter implements IFilterConditi
 | 
				
			|||||||
        }
 | 
					        }
 | 
				
			||||||
    };
 | 
					    };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    private getSpaceEventKey = (space: Room | null) => space ? space.roomId : HOME_SPACE;
 | 
					    private getSpaceEventKey = (space: Room) => space.roomId;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    public updateSpace(space: Room) {
 | 
					    public updateSpace(space: Room) {
 | 
				
			||||||
        SpaceStore.instance.off(this.getSpaceEventKey(this.space), this.onStoreUpdate);
 | 
					        if (this.space) {
 | 
				
			||||||
 | 
					            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
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -77,7 +77,7 @@ describe('MessagePanel', function() {
 | 
				
			|||||||
        DMRoomMap.makeShared();
 | 
					        DMRoomMap.makeShared();
 | 
				
			||||||
    });
 | 
					    });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    afterEach(function() {
 | 
					    afterEach(function () {
 | 
				
			||||||
        clock.uninstall();
 | 
					        clock.uninstall();
 | 
				
			||||||
    });
 | 
					    });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -88,7 +88,21 @@ describe('MessagePanel', function() {
 | 
				
			|||||||
            events.push(test_utils.mkMessage(
 | 
					            events.push(test_utils.mkMessage(
 | 
				
			||||||
                {
 | 
					                {
 | 
				
			||||||
                    event: true, room: "!room:id", user: "@user:id",
 | 
					                    event: true, room: "!room:id", user: "@user:id",
 | 
				
			||||||
                    ts: ts0 + i*1000,
 | 
					                    ts: ts0 + i * 1000,
 | 
				
			||||||
 | 
					                }));
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        return events;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    // Just to avoid breaking Dateseparator tests that might run at 00hrs
 | 
				
			||||||
 | 
					    function mkOneDayEvents() {
 | 
				
			||||||
 | 
					        const events = [];
 | 
				
			||||||
 | 
					        const ts0 = Date.parse('09 May 2004 00:12:00 GMT');
 | 
				
			||||||
 | 
					        for (let i = 0; i < 10; i++) {
 | 
				
			||||||
 | 
					            events.push(test_utils.mkMessage(
 | 
				
			||||||
 | 
					                {
 | 
				
			||||||
 | 
					                    event: true, room: "!room:id", user: "@user:id",
 | 
				
			||||||
 | 
					                    ts: ts0 + i * 1000,
 | 
				
			||||||
                }));
 | 
					                }));
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
        return events;
 | 
					        return events;
 | 
				
			||||||
@@ -104,7 +118,7 @@ describe('MessagePanel', function() {
 | 
				
			|||||||
        let i = 0;
 | 
					        let i = 0;
 | 
				
			||||||
        events.push(test_utils.mkMessage({
 | 
					        events.push(test_utils.mkMessage({
 | 
				
			||||||
            event: true, room: "!room:id", user: "@user:id",
 | 
					            event: true, room: "!room:id", user: "@user:id",
 | 
				
			||||||
            ts: ts0 + ++i*1000,
 | 
					            ts: ts0 + ++i * 1000,
 | 
				
			||||||
        }));
 | 
					        }));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        for (i = 0; i < 10; i++) {
 | 
					        for (i = 0; i < 10; i++) {
 | 
				
			||||||
@@ -151,7 +165,7 @@ describe('MessagePanel', function() {
 | 
				
			|||||||
                    },
 | 
					                    },
 | 
				
			||||||
                    getMxcAvatarUrl: () => 'mxc://avatar.url/image.png',
 | 
					                    getMxcAvatarUrl: () => 'mxc://avatar.url/image.png',
 | 
				
			||||||
                },
 | 
					                },
 | 
				
			||||||
                ts: ts0 + i*1000,
 | 
					                ts: ts0 + i * 1000,
 | 
				
			||||||
                mship: 'join',
 | 
					                mship: 'join',
 | 
				
			||||||
                prevMship: 'join',
 | 
					                prevMship: 'join',
 | 
				
			||||||
                name: 'A user',
 | 
					                name: 'A user',
 | 
				
			||||||
@@ -250,7 +264,6 @@ describe('MessagePanel', function() {
 | 
				
			|||||||
            }),
 | 
					            }),
 | 
				
			||||||
        ];
 | 
					        ];
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					 | 
				
			||||||
    function isReadMarkerVisible(rmContainer) {
 | 
					    function isReadMarkerVisible(rmContainer) {
 | 
				
			||||||
        return rmContainer && rmContainer.children.length > 0;
 | 
					        return rmContainer && rmContainer.children.length > 0;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
@@ -437,4 +450,17 @@ describe('MessagePanel', function() {
 | 
				
			|||||||
        // read marker should be hidden given props and at the last event
 | 
					        // read marker should be hidden given props and at the last event
 | 
				
			||||||
        expect(isReadMarkerVisible(rm)).toBeFalsy();
 | 
					        expect(isReadMarkerVisible(rm)).toBeFalsy();
 | 
				
			||||||
    });
 | 
					    });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    it('should render Date separators for the events', function () {
 | 
				
			||||||
 | 
					        const events = mkOneDayEvents();
 | 
				
			||||||
 | 
					        const res = mount(
 | 
				
			||||||
 | 
					            <WrappedMessagePanel
 | 
				
			||||||
 | 
					                className="cls"
 | 
				
			||||||
 | 
					                events={events}
 | 
				
			||||||
 | 
					            />,
 | 
				
			||||||
 | 
					        );
 | 
				
			||||||
 | 
					        const Dates = res.find(sdk.getComponent('messages.DateSeparator'));
 | 
				
			||||||
 | 
					       
 | 
				
			||||||
 | 
					        expect(Dates.length).toEqual(1);
 | 
				
			||||||
 | 
					    });
 | 
				
			||||||
});
 | 
					});
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -101,6 +101,7 @@ const invite1 = "!invite1:server";
 | 
				
			|||||||
const invite2 = "!invite2:server";
 | 
					const invite2 = "!invite2:server";
 | 
				
			||||||
const room1 = "!room1:server";
 | 
					const room1 = "!room1:server";
 | 
				
			||||||
const room2 = "!room2:server";
 | 
					const room2 = "!room2:server";
 | 
				
			||||||
 | 
					const room3 = "!room3:server";
 | 
				
			||||||
const space1 = "!space1:server";
 | 
					const space1 = "!space1:server";
 | 
				
			||||||
const space2 = "!space2:server";
 | 
					const space2 = "!space2:server";
 | 
				
			||||||
const space3 = "!space3:server";
 | 
					const space3 = "!space3:server";
 | 
				
			||||||
@@ -361,8 +362,8 @@ describe("SpaceStore", () => {
 | 
				
			|||||||
                expect(store.getSpaceFilteredRoomIds(null).has(invite2)).toBeTruthy();
 | 
					                expect(store.getSpaceFilteredRoomIds(null).has(invite2)).toBeTruthy();
 | 
				
			||||||
            });
 | 
					            });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            it("home space does not contain rooms/low priority from rooms within spaces", () => {
 | 
					            it("home space does contain rooms/low priority even if they are also shown in a space", () => {
 | 
				
			||||||
                expect(store.getSpaceFilteredRoomIds(null).has(room1)).toBeFalsy();
 | 
					                expect(store.getSpaceFilteredRoomIds(null).has(room1)).toBeTruthy();
 | 
				
			||||||
            });
 | 
					            });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            it("space contains child rooms", () => {
 | 
					            it("space contains child rooms", () => {
 | 
				
			||||||
@@ -614,8 +615,8 @@ describe("SpaceStore", () => {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
    describe("space auto switching tests", () => {
 | 
					    describe("space auto switching tests", () => {
 | 
				
			||||||
        beforeEach(async () => {
 | 
					        beforeEach(async () => {
 | 
				
			||||||
            [room1, room2, orphan1].forEach(mkRoom);
 | 
					            [room1, room2, room3, orphan1].forEach(mkRoom);
 | 
				
			||||||
            mkSpace(space1, [room1, room2]);
 | 
					            mkSpace(space1, [room1, room2, room3]);
 | 
				
			||||||
            mkSpace(space2, [room1, room2]);
 | 
					            mkSpace(space2, [room1, room2]);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            client.getRoom(room2).currentState.getStateEvents.mockImplementation(mockStateEventImplementation([
 | 
					            client.getRoom(room2).currentState.getStateEvents.mockImplementation(mockStateEventImplementation([
 | 
				
			||||||
@@ -641,15 +642,15 @@ describe("SpaceStore", () => {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
        it("switch to canonical parent space for room", async () => {
 | 
					        it("switch to canonical parent space for room", async () => {
 | 
				
			||||||
            viewRoom(room1);
 | 
					            viewRoom(room1);
 | 
				
			||||||
            await store.setActiveSpace(null, false);
 | 
					            await store.setActiveSpace(client.getRoom(space2), false);
 | 
				
			||||||
            viewRoom(room2);
 | 
					            viewRoom(room2);
 | 
				
			||||||
            expect(store.activeSpace).toBe(client.getRoom(space2));
 | 
					            expect(store.activeSpace).toBe(client.getRoom(space2));
 | 
				
			||||||
        });
 | 
					        });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        it("switch to first containing space for room", async () => {
 | 
					        it("switch to first containing space for room", async () => {
 | 
				
			||||||
            viewRoom(room2);
 | 
					            viewRoom(room2);
 | 
				
			||||||
            await store.setActiveSpace(null, false);
 | 
					            await store.setActiveSpace(client.getRoom(space2), false);
 | 
				
			||||||
            viewRoom(room1);
 | 
					            viewRoom(room3);
 | 
				
			||||||
            expect(store.activeSpace).toBe(client.getRoom(space1));
 | 
					            expect(store.activeSpace).toBe(client.getRoom(space1));
 | 
				
			||||||
        });
 | 
					        });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -659,6 +660,13 @@ describe("SpaceStore", () => {
 | 
				
			|||||||
            viewRoom(orphan1);
 | 
					            viewRoom(orphan1);
 | 
				
			||||||
            expect(store.activeSpace).toBeNull();
 | 
					            expect(store.activeSpace).toBeNull();
 | 
				
			||||||
        });
 | 
					        });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        it("when switching rooms in the all rooms home space don't switch to related space", async () => {
 | 
				
			||||||
 | 
					            viewRoom(room2);
 | 
				
			||||||
 | 
					            await store.setActiveSpace(null, false);
 | 
				
			||||||
 | 
					            viewRoom(room1);
 | 
				
			||||||
 | 
					            expect(store.activeSpace).toBeNull();
 | 
				
			||||||
 | 
					        });
 | 
				
			||||||
    });
 | 
					    });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    describe("traverseSpace", () => {
 | 
					    describe("traverseSpace", () => {
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										127
									
								
								yarn.lock
									
									
									
									
									
								
							
							
						
						
									
										127
									
								
								yarn.lock
									
									
									
									
									
								
							@@ -2401,29 +2401,29 @@ chardet@^0.7.0:
 | 
				
			|||||||
  resolved "https://registry.yarnpkg.com/chardet/-/chardet-0.7.0.tgz#90094849f0937f2eedc2425d0d28a9e5f0cbad9e"
 | 
					  resolved "https://registry.yarnpkg.com/chardet/-/chardet-0.7.0.tgz#90094849f0937f2eedc2425d0d28a9e5f0cbad9e"
 | 
				
			||||||
  integrity sha512-mT8iDcrh03qDGRRmoA2hmBJnxpllMR+0/0qlzjqZES6NdiWDcZkCNAk4rPFZ9Q85r27unkiNNg8ZOiwZXBHwcA==
 | 
					  integrity sha512-mT8iDcrh03qDGRRmoA2hmBJnxpllMR+0/0qlzjqZES6NdiWDcZkCNAk4rPFZ9Q85r27unkiNNg8ZOiwZXBHwcA==
 | 
				
			||||||
 | 
					
 | 
				
			||||||
cheerio-select-tmp@^0.1.0:
 | 
					cheerio-select@^1.4.0:
 | 
				
			||||||
  version "0.1.1"
 | 
					  version "1.4.0"
 | 
				
			||||||
  resolved "https://registry.yarnpkg.com/cheerio-select-tmp/-/cheerio-select-tmp-0.1.1.tgz#55bbef02a4771710195ad736d5e346763ca4e646"
 | 
					  resolved "https://registry.yarnpkg.com/cheerio-select/-/cheerio-select-1.4.0.tgz#3a16f21e37a2ef0f211d6d1aa4eff054bb22cdc9"
 | 
				
			||||||
  integrity sha512-YYs5JvbpU19VYJyj+F7oYrIE2BOll1/hRU7rEy/5+v9BzkSo3bK81iAeeQEMI92vRIxz677m72UmJUiVwwgjfQ==
 | 
					  integrity sha512-sobR3Yqz27L553Qa7cK6rtJlMDbiKPdNywtR95Sj/YgfpLfy0u6CGJuaBKe5YE/vTc23SCRKxWSdlon/w6I/Ew==
 | 
				
			||||||
  dependencies:
 | 
					  dependencies:
 | 
				
			||||||
    css-select "^3.1.2"
 | 
					    css-select "^4.1.2"
 | 
				
			||||||
    css-what "^4.0.0"
 | 
					    css-what "^5.0.0"
 | 
				
			||||||
    domelementtype "^2.1.0"
 | 
					    domelementtype "^2.2.0"
 | 
				
			||||||
    domhandler "^4.0.0"
 | 
					    domhandler "^4.2.0"
 | 
				
			||||||
    domutils "^2.4.4"
 | 
					    domutils "^2.6.0"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
cheerio@^1.0.0-rc.3, cheerio@^1.0.0-rc.5:
 | 
					cheerio@^1.0.0-rc.3, cheerio@^1.0.0-rc.9:
 | 
				
			||||||
  version "1.0.0-rc.5"
 | 
					  version "1.0.0-rc.9"
 | 
				
			||||||
  resolved "https://registry.yarnpkg.com/cheerio/-/cheerio-1.0.0-rc.5.tgz#88907e1828674e8f9fee375188b27dadd4f0fa2f"
 | 
					  resolved "https://registry.yarnpkg.com/cheerio/-/cheerio-1.0.0-rc.9.tgz#a3ae6b7ce7af80675302ff836f628e7cb786a67f"
 | 
				
			||||||
  integrity sha512-yoqps/VCaZgN4pfXtenwHROTp8NG6/Hlt4Jpz2FEP0ZJQ+ZUkVDd0hAPDNKhj3nakpfPt/CNs57yEtxD1bXQiw==
 | 
					  integrity sha512-QF6XVdrLONO6DXRF5iaolY+odmhj2CLj+xzNod7INPWMi/x9X4SOylH0S/vaPpX+AUU6t04s34SQNh7DbkuCng==
 | 
				
			||||||
  dependencies:
 | 
					  dependencies:
 | 
				
			||||||
    cheerio-select-tmp "^0.1.0"
 | 
					    cheerio-select "^1.4.0"
 | 
				
			||||||
    dom-serializer "~1.2.0"
 | 
					    dom-serializer "^1.3.1"
 | 
				
			||||||
    domhandler "^4.0.0"
 | 
					    domhandler "^4.2.0"
 | 
				
			||||||
    entities "~2.1.0"
 | 
					    htmlparser2 "^6.1.0"
 | 
				
			||||||
    htmlparser2 "^6.0.0"
 | 
					    parse5 "^6.0.1"
 | 
				
			||||||
    parse5 "^6.0.0"
 | 
					    parse5-htmlparser2-tree-adapter "^6.0.1"
 | 
				
			||||||
    parse5-htmlparser2-tree-adapter "^6.0.0"
 | 
					    tslib "^2.2.0"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
chokidar@^3.4.0, chokidar@^3.5.1:
 | 
					chokidar@^3.4.0, chokidar@^3.5.1:
 | 
				
			||||||
  version "3.5.1"
 | 
					  version "3.5.1"
 | 
				
			||||||
@@ -2705,21 +2705,21 @@ cross-spawn@^7.0.0, cross-spawn@^7.0.2:
 | 
				
			|||||||
    shebang-command "^2.0.0"
 | 
					    shebang-command "^2.0.0"
 | 
				
			||||||
    which "^2.0.1"
 | 
					    which "^2.0.1"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
css-select@^3.1.2:
 | 
					css-select@^4.1.2:
 | 
				
			||||||
  version "3.1.2"
 | 
					  version "4.1.2"
 | 
				
			||||||
  resolved "https://registry.yarnpkg.com/css-select/-/css-select-3.1.2.tgz#d52cbdc6fee379fba97fb0d3925abbd18af2d9d8"
 | 
					  resolved "https://registry.yarnpkg.com/css-select/-/css-select-4.1.2.tgz#8b52b6714ed3a80d8221ec971c543f3b12653286"
 | 
				
			||||||
  integrity sha512-qmss1EihSuBNWNNhHjxzxSfJoFBM/lERB/Q4EnsJQQC62R2evJDW481091oAdOr9uh46/0n4nrg0It5cAnj1RA==
 | 
					  integrity sha512-nu5ye2Hg/4ISq4XqdLY2bEatAcLIdt3OYGFc9Tm9n7VSlFBcfRv0gBNksHRgSdUDQGtN3XrZ94ztW+NfzkFSUw==
 | 
				
			||||||
  dependencies:
 | 
					  dependencies:
 | 
				
			||||||
    boolbase "^1.0.0"
 | 
					    boolbase "^1.0.0"
 | 
				
			||||||
    css-what "^4.0.0"
 | 
					    css-what "^5.0.0"
 | 
				
			||||||
    domhandler "^4.0.0"
 | 
					    domhandler "^4.2.0"
 | 
				
			||||||
    domutils "^2.4.3"
 | 
					    domutils "^2.6.0"
 | 
				
			||||||
    nth-check "^2.0.0"
 | 
					    nth-check "^2.0.0"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
css-what@^4.0.0:
 | 
					css-what@^5.0.0:
 | 
				
			||||||
  version "4.0.0"
 | 
					  version "5.0.0"
 | 
				
			||||||
  resolved "https://registry.yarnpkg.com/css-what/-/css-what-4.0.0.tgz#35e73761cab2eeb3d3661126b23d7aa0e8432233"
 | 
					  resolved "https://registry.yarnpkg.com/css-what/-/css-what-5.0.0.tgz#f0bf4f8bac07582722346ab243f6a35b512cfc47"
 | 
				
			||||||
  integrity sha512-teijzG7kwYfNVsUh2H/YN62xW3KK9YhXEgSlbxMlcyjPNvdKJqFx5lrwlJgoFP1ZHlB89iGDlo/JyshKeRhv5A==
 | 
					  integrity sha512-qxyKHQvgKwzwDWC/rGbT821eJalfupxYW2qbSJSAtdSTimsr/MlaGONoNLllaUPZWf8QnbcKM/kPVYUQuEKAFA==
 | 
				
			||||||
 | 
					
 | 
				
			||||||
cssesc@^3.0.0:
 | 
					cssesc@^3.0.0:
 | 
				
			||||||
  version "3.0.0"
 | 
					  version "3.0.0"
 | 
				
			||||||
@@ -2925,9 +2925,9 @@ doctrine@^3.0.0:
 | 
				
			|||||||
    esutils "^2.0.2"
 | 
					    esutils "^2.0.2"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
dom-helpers@^5.0.1:
 | 
					dom-helpers@^5.0.1:
 | 
				
			||||||
  version "5.2.0"
 | 
					  version "5.2.1"
 | 
				
			||||||
  resolved "https://registry.yarnpkg.com/dom-helpers/-/dom-helpers-5.2.0.tgz#57fd054c5f8f34c52a3eeffdb7e7e93cd357d95b"
 | 
					  resolved "https://registry.yarnpkg.com/dom-helpers/-/dom-helpers-5.2.1.tgz#d9400536b2bf8225ad98fe052e029451ac40e902"
 | 
				
			||||||
  integrity sha512-Ru5o9+V8CpunKnz5LGgWXkmrH/20cGKwcHwS4m73zIvs54CN9epEmT/HLqFJW3kXpakAFkEdzgy1hzlJe3E4OQ==
 | 
					  integrity sha512-nRCa7CK3VTrM2NmGkIy4cbK7IZlgBE/PYMn55rrXefr5xXDP0LdtfPnblFDoVdcAfslJ7or6iqAUnx0CCGIWQA==
 | 
				
			||||||
  dependencies:
 | 
					  dependencies:
 | 
				
			||||||
    "@babel/runtime" "^7.8.7"
 | 
					    "@babel/runtime" "^7.8.7"
 | 
				
			||||||
    csstype "^3.0.2"
 | 
					    csstype "^3.0.2"
 | 
				
			||||||
@@ -2940,10 +2940,10 @@ dom-serializer@0:
 | 
				
			|||||||
    domelementtype "^2.0.1"
 | 
					    domelementtype "^2.0.1"
 | 
				
			||||||
    entities "^2.0.0"
 | 
					    entities "^2.0.0"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
dom-serializer@^1.0.1, dom-serializer@~1.2.0:
 | 
					dom-serializer@^1.0.1, dom-serializer@^1.3.1:
 | 
				
			||||||
  version "1.2.0"
 | 
					  version "1.3.1"
 | 
				
			||||||
  resolved "https://registry.yarnpkg.com/dom-serializer/-/dom-serializer-1.2.0.tgz#3433d9136aeb3c627981daa385fc7f32d27c48f1"
 | 
					  resolved "https://registry.yarnpkg.com/dom-serializer/-/dom-serializer-1.3.1.tgz#d845a1565d7c041a95e5dab62184ab41e3a519be"
 | 
				
			||||||
  integrity sha512-n6kZFH/KlCrqs/1GHMOd5i2fd/beQHuehKdWvNNffbGHTr/almdhuVvTVFb3V7fglz+nC50fFusu3lY33h12pA==
 | 
					  integrity sha512-Pv2ZluG5ife96udGgEDovOOOA5UELkltfJpnIExPrAk1LTvecolUGn6lIaoLh86d83GiB86CjzciMd9BuRB71Q==
 | 
				
			||||||
  dependencies:
 | 
					  dependencies:
 | 
				
			||||||
    domelementtype "^2.0.1"
 | 
					    domelementtype "^2.0.1"
 | 
				
			||||||
    domhandler "^4.0.0"
 | 
					    domhandler "^4.0.0"
 | 
				
			||||||
@@ -2954,10 +2954,10 @@ domelementtype@1, domelementtype@^1.3.1:
 | 
				
			|||||||
  resolved "https://registry.yarnpkg.com/domelementtype/-/domelementtype-1.3.1.tgz#d048c44b37b0d10a7f2a3d5fee3f4333d790481f"
 | 
					  resolved "https://registry.yarnpkg.com/domelementtype/-/domelementtype-1.3.1.tgz#d048c44b37b0d10a7f2a3d5fee3f4333d790481f"
 | 
				
			||||||
  integrity sha512-BSKB+TSpMpFI/HOxCNr1O8aMOTZ8hT3pM3GQ0w/mWRmkhEDSFJkkyzz4XQsBV44BChwGkrDfMyjVD0eA2aFV3w==
 | 
					  integrity sha512-BSKB+TSpMpFI/HOxCNr1O8aMOTZ8hT3pM3GQ0w/mWRmkhEDSFJkkyzz4XQsBV44BChwGkrDfMyjVD0eA2aFV3w==
 | 
				
			||||||
 | 
					
 | 
				
			||||||
domelementtype@^2.0.1, domelementtype@^2.1.0:
 | 
					domelementtype@^2.0.1, domelementtype@^2.2.0:
 | 
				
			||||||
  version "2.1.0"
 | 
					  version "2.2.0"
 | 
				
			||||||
  resolved "https://registry.yarnpkg.com/domelementtype/-/domelementtype-2.1.0.tgz#a851c080a6d1c3d94344aed151d99f669edf585e"
 | 
					  resolved "https://registry.yarnpkg.com/domelementtype/-/domelementtype-2.2.0.tgz#9a0b6c2782ed6a1c7323d42267183df9bd8b1d57"
 | 
				
			||||||
  integrity sha512-LsTgx/L5VpD+Q8lmsXSHW2WpA+eBlZ9HPf3erD1IoPF00/3JKHZ3BknUVA2QGDNu69ZNmyFmCWBSO45XjYKC5w==
 | 
					  integrity sha512-DtBMo82pv1dFtUmHyr48beiuq792Sxohr+8Hm9zoxklYPfa6n0Z3Byjj2IV7bmr2IyqClnqEQhfgHJJ5QF0R5A==
 | 
				
			||||||
 | 
					
 | 
				
			||||||
domexception@^2.0.1:
 | 
					domexception@^2.0.1:
 | 
				
			||||||
  version "2.0.1"
 | 
					  version "2.0.1"
 | 
				
			||||||
@@ -2973,12 +2973,12 @@ domhandler@^2.3.0:
 | 
				
			|||||||
  dependencies:
 | 
					  dependencies:
 | 
				
			||||||
    domelementtype "1"
 | 
					    domelementtype "1"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
domhandler@^4.0.0:
 | 
					domhandler@^4.0.0, domhandler@^4.2.0:
 | 
				
			||||||
  version "4.0.0"
 | 
					  version "4.2.0"
 | 
				
			||||||
  resolved "https://registry.yarnpkg.com/domhandler/-/domhandler-4.0.0.tgz#01ea7821de996d85f69029e81fa873c21833098e"
 | 
					  resolved "https://registry.yarnpkg.com/domhandler/-/domhandler-4.2.0.tgz#f9768a5f034be60a89a27c2e4d0f74eba0d8b059"
 | 
				
			||||||
  integrity sha512-KPTbnGQ1JeEMQyO1iYXoagsI6so/C96HZiFyByU3T6iAzpXn8EGEvct6unm1ZGoed8ByO2oirxgwxBmqKF9haA==
 | 
					  integrity sha512-zk7sgt970kzPks2Bf+dwT/PLzghLnsivb9CcxkvR8Mzr66Olr0Ofd8neSbglHJHaHa2MadfoSdNlKYAaafmWfA==
 | 
				
			||||||
  dependencies:
 | 
					  dependencies:
 | 
				
			||||||
    domelementtype "^2.1.0"
 | 
					    domelementtype "^2.2.0"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
domutils@^1.5.1:
 | 
					domutils@^1.5.1:
 | 
				
			||||||
  version "1.7.0"
 | 
					  version "1.7.0"
 | 
				
			||||||
@@ -2988,14 +2988,14 @@ domutils@^1.5.1:
 | 
				
			|||||||
    dom-serializer "0"
 | 
					    dom-serializer "0"
 | 
				
			||||||
    domelementtype "1"
 | 
					    domelementtype "1"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
domutils@^2.4.3, domutils@^2.4.4:
 | 
					domutils@^2.4.4, domutils@^2.5.2, domutils@^2.6.0:
 | 
				
			||||||
  version "2.4.4"
 | 
					  version "2.6.0"
 | 
				
			||||||
  resolved "https://registry.yarnpkg.com/domutils/-/domutils-2.4.4.tgz#282739c4b150d022d34699797369aad8d19bbbd3"
 | 
					  resolved "https://registry.yarnpkg.com/domutils/-/domutils-2.6.0.tgz#2e15c04185d43fb16ae7057cb76433c6edb938b7"
 | 
				
			||||||
  integrity sha512-jBC0vOsECI4OMdD0GC9mGn7NXPLb+Qt6KW1YDQzeQYRUFKmNG8lh7mO5HiELfr+lLQE7loDVI4QcAxV80HS+RA==
 | 
					  integrity sha512-y0BezHuy4MDYxh6OvolXYsH+1EMGmFbwv5FKW7ovwMG6zTPWqNPq3WF9ayZssFq+UlKdffGLbOEaghNdaOm1WA==
 | 
				
			||||||
  dependencies:
 | 
					  dependencies:
 | 
				
			||||||
    dom-serializer "^1.0.1"
 | 
					    dom-serializer "^1.0.1"
 | 
				
			||||||
    domelementtype "^2.0.1"
 | 
					    domelementtype "^2.2.0"
 | 
				
			||||||
    domhandler "^4.0.0"
 | 
					    domhandler "^4.2.0"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
ecc-jsbn@~0.1.1:
 | 
					ecc-jsbn@~0.1.1:
 | 
				
			||||||
  version "0.1.2"
 | 
					  version "0.1.2"
 | 
				
			||||||
@@ -3061,7 +3061,7 @@ entities@^1.1.1:
 | 
				
			|||||||
  resolved "https://registry.yarnpkg.com/entities/-/entities-1.1.2.tgz#bdfa735299664dfafd34529ed4f8522a275fea56"
 | 
					  resolved "https://registry.yarnpkg.com/entities/-/entities-1.1.2.tgz#bdfa735299664dfafd34529ed4f8522a275fea56"
 | 
				
			||||||
  integrity sha512-f2LZMYl1Fzu7YSBKg+RoROelpOaNrcGmE9AZubeDfrCEia483oW4MI4VyFd5VNHIgQ/7qm1I0wUHK1eJnn2y2w==
 | 
					  integrity sha512-f2LZMYl1Fzu7YSBKg+RoROelpOaNrcGmE9AZubeDfrCEia483oW4MI4VyFd5VNHIgQ/7qm1I0wUHK1eJnn2y2w==
 | 
				
			||||||
 | 
					
 | 
				
			||||||
entities@^2.0.0, entities@~2.1.0:
 | 
					entities@^2.0.0:
 | 
				
			||||||
  version "2.1.0"
 | 
					  version "2.1.0"
 | 
				
			||||||
  resolved "https://registry.yarnpkg.com/entities/-/entities-2.1.0.tgz#992d3129cf7df6870b96c57858c249a120f8b8b5"
 | 
					  resolved "https://registry.yarnpkg.com/entities/-/entities-2.1.0.tgz#992d3129cf7df6870b96c57858c249a120f8b8b5"
 | 
				
			||||||
  integrity sha512-hCx1oky9PFrJ611mf0ifBLBRW8lUUVRlFolb5gWRfIELabBlbp9xZvrqZLZAs+NxFnbfQoeGd8wDkygjg7U85w==
 | 
					  integrity sha512-hCx1oky9PFrJ611mf0ifBLBRW8lUUVRlFolb5gWRfIELabBlbp9xZvrqZLZAs+NxFnbfQoeGd8wDkygjg7U85w==
 | 
				
			||||||
@@ -4281,6 +4281,16 @@ htmlparser2@^6.0.0:
 | 
				
			|||||||
    domutils "^2.4.4"
 | 
					    domutils "^2.4.4"
 | 
				
			||||||
    entities "^2.0.0"
 | 
					    entities "^2.0.0"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					htmlparser2@^6.1.0:
 | 
				
			||||||
 | 
					  version "6.1.0"
 | 
				
			||||||
 | 
					  resolved "https://registry.yarnpkg.com/htmlparser2/-/htmlparser2-6.1.0.tgz#c4d762b6c3371a05dbe65e94ae43a9f845fb8fb7"
 | 
				
			||||||
 | 
					  integrity sha512-gyyPk6rgonLFEDGoeRgQNaEUvdJ4ktTmmUh/h2t7s+M8oPpIPxgNACWa+6ESR57kXstwqPiCut0V8NRpcwgU7A==
 | 
				
			||||||
 | 
					  dependencies:
 | 
				
			||||||
 | 
					    domelementtype "^2.0.1"
 | 
				
			||||||
 | 
					    domhandler "^4.0.0"
 | 
				
			||||||
 | 
					    domutils "^2.5.2"
 | 
				
			||||||
 | 
					    entities "^2.0.0"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
http-signature@~1.2.0:
 | 
					http-signature@~1.2.0:
 | 
				
			||||||
  version "1.2.0"
 | 
					  version "1.2.0"
 | 
				
			||||||
  resolved "https://registry.yarnpkg.com/http-signature/-/http-signature-1.2.0.tgz#9aecd925114772f3d95b65a60abb8f7c18fbace1"
 | 
					  resolved "https://registry.yarnpkg.com/http-signature/-/http-signature-1.2.0.tgz#9aecd925114772f3d95b65a60abb8f7c18fbace1"
 | 
				
			||||||
@@ -6295,7 +6305,7 @@ parse-srcset@^1.0.2:
 | 
				
			|||||||
  resolved "https://registry.yarnpkg.com/parse-srcset/-/parse-srcset-1.0.2.tgz#f2bd221f6cc970a938d88556abc589caaaa2bde1"
 | 
					  resolved "https://registry.yarnpkg.com/parse-srcset/-/parse-srcset-1.0.2.tgz#f2bd221f6cc970a938d88556abc589caaaa2bde1"
 | 
				
			||||||
  integrity sha1-8r0iH2zJcKk42IVWq8WJyqqiveE=
 | 
					  integrity sha1-8r0iH2zJcKk42IVWq8WJyqqiveE=
 | 
				
			||||||
 | 
					
 | 
				
			||||||
parse5-htmlparser2-tree-adapter@^6.0.0:
 | 
					parse5-htmlparser2-tree-adapter@^6.0.1:
 | 
				
			||||||
  version "6.0.1"
 | 
					  version "6.0.1"
 | 
				
			||||||
  resolved "https://registry.yarnpkg.com/parse5-htmlparser2-tree-adapter/-/parse5-htmlparser2-tree-adapter-6.0.1.tgz#2cdf9ad823321140370d4dbf5d3e92c7c8ddc6e6"
 | 
					  resolved "https://registry.yarnpkg.com/parse5-htmlparser2-tree-adapter/-/parse5-htmlparser2-tree-adapter-6.0.1.tgz#2cdf9ad823321140370d4dbf5d3e92c7c8ddc6e6"
 | 
				
			||||||
  integrity sha512-qPuWvbLgvDGilKc5BoicRovlT4MtYT6JfJyBOMDsKoiT+GiuP5qyrPCnR9HcPECIJJmZh5jRndyNThnhhb/vlA==
 | 
					  integrity sha512-qPuWvbLgvDGilKc5BoicRovlT4MtYT6JfJyBOMDsKoiT+GiuP5qyrPCnR9HcPECIJJmZh5jRndyNThnhhb/vlA==
 | 
				
			||||||
@@ -6307,7 +6317,7 @@ parse5@5.1.1:
 | 
				
			|||||||
  resolved "https://registry.yarnpkg.com/parse5/-/parse5-5.1.1.tgz#f68e4e5ba1852ac2cadc00f4555fff6c2abb6178"
 | 
					  resolved "https://registry.yarnpkg.com/parse5/-/parse5-5.1.1.tgz#f68e4e5ba1852ac2cadc00f4555fff6c2abb6178"
 | 
				
			||||||
  integrity sha512-ugq4DFI0Ptb+WWjAdOK16+u/nHfiIrcE+sh8kZMaM0WllQKLI9rOUq6c2b7cwPkXdzfQESqvoqK6ug7U/Yyzug==
 | 
					  integrity sha512-ugq4DFI0Ptb+WWjAdOK16+u/nHfiIrcE+sh8kZMaM0WllQKLI9rOUq6c2b7cwPkXdzfQESqvoqK6ug7U/Yyzug==
 | 
				
			||||||
 | 
					
 | 
				
			||||||
parse5@^6.0.0, parse5@^6.0.1:
 | 
					parse5@^6.0.1:
 | 
				
			||||||
  version "6.0.1"
 | 
					  version "6.0.1"
 | 
				
			||||||
  resolved "https://registry.yarnpkg.com/parse5/-/parse5-6.0.1.tgz#e1a1c085c569b3dc08321184f19a39cc27f7c30b"
 | 
					  resolved "https://registry.yarnpkg.com/parse5/-/parse5-6.0.1.tgz#e1a1c085c569b3dc08321184f19a39cc27f7c30b"
 | 
				
			||||||
  integrity sha512-Ofn/CTFzRGTTxwpNEs9PP93gXShHcTq255nzRYSKe8AkVpZY7e1fpmTfOyoIvjP5HG7Z2ZM7VS9PPhQGW2pOpw==
 | 
					  integrity sha512-Ofn/CTFzRGTTxwpNEs9PP93gXShHcTq255nzRYSKe8AkVpZY7e1fpmTfOyoIvjP5HG7Z2ZM7VS9PPhQGW2pOpw==
 | 
				
			||||||
@@ -7988,6 +7998,11 @@ tslib@^2.0.0, tslib@^2.0.1, tslib@^2.0.3:
 | 
				
			|||||||
  resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.1.0.tgz#da60860f1c2ecaa5703ab7d39bc05b6bf988b97a"
 | 
					  resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.1.0.tgz#da60860f1c2ecaa5703ab7d39bc05b6bf988b97a"
 | 
				
			||||||
  integrity sha512-hcVC3wYEziELGGmEEXue7D75zbwIIVUMWAVbHItGPx0ziyXxrOMQx4rQEVEV45Ut/1IotuEvwqPopzIOkDMf0A==
 | 
					  integrity sha512-hcVC3wYEziELGGmEEXue7D75zbwIIVUMWAVbHItGPx0ziyXxrOMQx4rQEVEV45Ut/1IotuEvwqPopzIOkDMf0A==
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					tslib@^2.2.0:
 | 
				
			||||||
 | 
					  version "2.2.0"
 | 
				
			||||||
 | 
					  resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.2.0.tgz#fb2c475977e35e241311ede2693cee1ec6698f5c"
 | 
				
			||||||
 | 
					  integrity sha512-gS9GVHRU+RGn5KQM2rllAlR3dU6m7AcpJKdtH8gFvQiC4Otgk98XnmMU+nZenHt/+VhnBPWwgrJsyrdcw6i23w==
 | 
				
			||||||
 | 
					
 | 
				
			||||||
tsutils@^3.17.1:
 | 
					tsutils@^3.17.1:
 | 
				
			||||||
  version "3.19.1"
 | 
					  version "3.19.1"
 | 
				
			||||||
  resolved "https://registry.yarnpkg.com/tsutils/-/tsutils-3.19.1.tgz#d8566e0c51c82f32f9c25a4d367cd62409a547a9"
 | 
					  resolved "https://registry.yarnpkg.com/tsutils/-/tsutils-3.19.1.tgz#d8566e0c51c82f32f9c25a4d367cd62409a547a9"
 | 
				
			||||||
 
 | 
				
			|||||||
		Reference in New Issue
	
	Block a user