Merge branch 'develop' of github.com:matrix-org/matrix-react-sdk into t3chguy/fix/17227
This commit is contained in:
commit
5eed9f6cba
16 changed files with 379 additions and 182 deletions
112
CHANGELOG.md
112
CHANGELOG.md
|
@ -1,3 +1,115 @@
|
||||||
|
Changes in [3.20.0](https://github.com/matrix-org/matrix-react-sdk/releases/tag/v3.20.0) (2021-05-10)
|
||||||
|
=====================================================================================================
|
||||||
|
[Full Changelog](https://github.com/matrix-org/matrix-react-sdk/compare/v3.20.0-rc.1...v3.20.0)
|
||||||
|
|
||||||
|
* Upgrade to JS SDK 10.1.0
|
||||||
|
* [Release] Don't use the event's metadata to calc the scale of an image
|
||||||
|
[\#6004](https://github.com/matrix-org/matrix-react-sdk/pull/6004)
|
||||||
|
|
||||||
|
Changes in [3.20.0-rc.1](https://github.com/matrix-org/matrix-react-sdk/releases/tag/v3.20.0-rc.1) (2021-05-04)
|
||||||
|
===============================================================================================================
|
||||||
|
[Full Changelog](https://github.com/matrix-org/matrix-react-sdk/compare/v3.19.0...v3.20.0-rc.1)
|
||||||
|
|
||||||
|
* Upgrade to JS SDK 10.1.0-rc.1
|
||||||
|
* Translations update from Weblate
|
||||||
|
[\#5966](https://github.com/matrix-org/matrix-react-sdk/pull/5966)
|
||||||
|
* Fix more space panel layout and hover behaviour issues
|
||||||
|
[\#5965](https://github.com/matrix-org/matrix-react-sdk/pull/5965)
|
||||||
|
* Fix edge case with space panel alignment with subspaces on ff
|
||||||
|
[\#5964](https://github.com/matrix-org/matrix-react-sdk/pull/5964)
|
||||||
|
* Fix saving room pill part to history
|
||||||
|
[\#5951](https://github.com/matrix-org/matrix-react-sdk/pull/5951)
|
||||||
|
* Generate room preview even when minimized
|
||||||
|
[\#5948](https://github.com/matrix-org/matrix-react-sdk/pull/5948)
|
||||||
|
* Another change from recovery passphrase to Security Phrase
|
||||||
|
[\#5934](https://github.com/matrix-org/matrix-react-sdk/pull/5934)
|
||||||
|
* Sort rooms in the add existing to space dialog based on recency
|
||||||
|
[\#5943](https://github.com/matrix-org/matrix-react-sdk/pull/5943)
|
||||||
|
* Inhibit sending RR when context switching to a room
|
||||||
|
[\#5944](https://github.com/matrix-org/matrix-react-sdk/pull/5944)
|
||||||
|
* Prevent room list keyboard handling from landing focus on hidden nodes
|
||||||
|
[\#5950](https://github.com/matrix-org/matrix-react-sdk/pull/5950)
|
||||||
|
* Make the text filter search all spaces instead of just the selected one
|
||||||
|
[\#5942](https://github.com/matrix-org/matrix-react-sdk/pull/5942)
|
||||||
|
* Enable indent rule and fix indent
|
||||||
|
[\#5931](https://github.com/matrix-org/matrix-react-sdk/pull/5931)
|
||||||
|
* Prevent peeking members from reacting
|
||||||
|
[\#5946](https://github.com/matrix-org/matrix-react-sdk/pull/5946)
|
||||||
|
* Disallow inline display maths
|
||||||
|
[\#5939](https://github.com/matrix-org/matrix-react-sdk/pull/5939)
|
||||||
|
* Space creation prompt user to add existing rooms for "Just Me" spaces
|
||||||
|
[\#5923](https://github.com/matrix-org/matrix-react-sdk/pull/5923)
|
||||||
|
* Add test coverage collection script
|
||||||
|
[\#5937](https://github.com/matrix-org/matrix-react-sdk/pull/5937)
|
||||||
|
* Fix joining room using via servers regression
|
||||||
|
[\#5936](https://github.com/matrix-org/matrix-react-sdk/pull/5936)
|
||||||
|
* Revert "Fixes the two Todays problem in Redaction"
|
||||||
|
[\#5938](https://github.com/matrix-org/matrix-react-sdk/pull/5938)
|
||||||
|
* Handle encoded matrix URLs
|
||||||
|
[\#5903](https://github.com/matrix-org/matrix-react-sdk/pull/5903)
|
||||||
|
* Render ignored users setting regardless of if there are any
|
||||||
|
[\#5860](https://github.com/matrix-org/matrix-react-sdk/pull/5860)
|
||||||
|
* Fix inserting trailing colon after mention/pill
|
||||||
|
[\#5830](https://github.com/matrix-org/matrix-react-sdk/pull/5830)
|
||||||
|
* Fixes the two Todays problem in Redaction
|
||||||
|
[\#5917](https://github.com/matrix-org/matrix-react-sdk/pull/5917)
|
||||||
|
* Fix page up/down scrolling only half a page
|
||||||
|
[\#5920](https://github.com/matrix-org/matrix-react-sdk/pull/5920)
|
||||||
|
* Voice messages: Composer controls
|
||||||
|
[\#5935](https://github.com/matrix-org/matrix-react-sdk/pull/5935)
|
||||||
|
* Support MSC3086 asserted identity
|
||||||
|
[\#5886](https://github.com/matrix-org/matrix-react-sdk/pull/5886)
|
||||||
|
* Handle possible edge case with getting stuck in "unsent messages" bar
|
||||||
|
[\#5930](https://github.com/matrix-org/matrix-react-sdk/pull/5930)
|
||||||
|
* Fix suggested rooms not showing up regression from room list optimisation
|
||||||
|
[\#5932](https://github.com/matrix-org/matrix-react-sdk/pull/5932)
|
||||||
|
* Broadcast language change to ElectronPlatform
|
||||||
|
[\#5913](https://github.com/matrix-org/matrix-react-sdk/pull/5913)
|
||||||
|
* Fix VoIP PIP frame color
|
||||||
|
[\#5701](https://github.com/matrix-org/matrix-react-sdk/pull/5701)
|
||||||
|
* Convert some Flow-typed files to TypeScript
|
||||||
|
[\#5912](https://github.com/matrix-org/matrix-react-sdk/pull/5912)
|
||||||
|
* Initial SpaceStore tests work
|
||||||
|
[\#5906](https://github.com/matrix-org/matrix-react-sdk/pull/5906)
|
||||||
|
* Fix issues with space hierarchy in layout and with incompatible servers
|
||||||
|
[\#5926](https://github.com/matrix-org/matrix-react-sdk/pull/5926)
|
||||||
|
* Scale all mxc thumbs using device pixel ratio for hidpi
|
||||||
|
[\#5928](https://github.com/matrix-org/matrix-react-sdk/pull/5928)
|
||||||
|
* Fix add existing to space dialog no longer showing rooms for public spaces
|
||||||
|
[\#5918](https://github.com/matrix-org/matrix-react-sdk/pull/5918)
|
||||||
|
* Disable spaces context switching for when exploring a space
|
||||||
|
[\#5924](https://github.com/matrix-org/matrix-react-sdk/pull/5924)
|
||||||
|
* Autofocus search box in the add existing to space dialog
|
||||||
|
[\#5921](https://github.com/matrix-org/matrix-react-sdk/pull/5921)
|
||||||
|
* Use label element in add existing to space dialog for easier hit target
|
||||||
|
[\#5922](https://github.com/matrix-org/matrix-react-sdk/pull/5922)
|
||||||
|
* Dynamic max and min zoom in the new ImageView
|
||||||
|
[\#5916](https://github.com/matrix-org/matrix-react-sdk/pull/5916)
|
||||||
|
* Improve message error states
|
||||||
|
[\#5897](https://github.com/matrix-org/matrix-react-sdk/pull/5897)
|
||||||
|
* Check for null room in `VisibilityProvider`
|
||||||
|
[\#5914](https://github.com/matrix-org/matrix-react-sdk/pull/5914)
|
||||||
|
* Add unit tests for various collection-based utility functions
|
||||||
|
[\#5910](https://github.com/matrix-org/matrix-react-sdk/pull/5910)
|
||||||
|
* Spaces visual fixes
|
||||||
|
[\#5909](https://github.com/matrix-org/matrix-react-sdk/pull/5909)
|
||||||
|
* Remove reliance on DOM API to generated message preview
|
||||||
|
[\#5908](https://github.com/matrix-org/matrix-react-sdk/pull/5908)
|
||||||
|
* Expand upon voice message event & include overall waveform
|
||||||
|
[\#5888](https://github.com/matrix-org/matrix-react-sdk/pull/5888)
|
||||||
|
* Use floats for image background opacity
|
||||||
|
[\#5905](https://github.com/matrix-org/matrix-react-sdk/pull/5905)
|
||||||
|
* Show invites to spaces at the top of the space panel
|
||||||
|
[\#5902](https://github.com/matrix-org/matrix-react-sdk/pull/5902)
|
||||||
|
* Improve edge cases with spaces context switching
|
||||||
|
[\#5899](https://github.com/matrix-org/matrix-react-sdk/pull/5899)
|
||||||
|
* Fix spaces notification dots wrongly including upgraded (hidden) rooms
|
||||||
|
[\#5900](https://github.com/matrix-org/matrix-react-sdk/pull/5900)
|
||||||
|
* Iterate the spaces face pile design
|
||||||
|
[\#5898](https://github.com/matrix-org/matrix-react-sdk/pull/5898)
|
||||||
|
* Fix alignment issue with nested spaces being cut off wrong
|
||||||
|
[\#5890](https://github.com/matrix-org/matrix-react-sdk/pull/5890)
|
||||||
|
|
||||||
Changes in [3.19.0](https://github.com/matrix-org/matrix-react-sdk/releases/tag/v3.19.0) (2021-04-26)
|
Changes in [3.19.0](https://github.com/matrix-org/matrix-react-sdk/releases/tag/v3.19.0) (2021-04-26)
|
||||||
=====================================================================================================
|
=====================================================================================================
|
||||||
[Full Changelog](https://github.com/matrix-org/matrix-react-sdk/compare/v3.19.0-rc.1...v3.19.0)
|
[Full Changelog](https://github.com/matrix-org/matrix-react-sdk/compare/v3.19.0-rc.1...v3.19.0)
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
{
|
{
|
||||||
"name": "matrix-react-sdk",
|
"name": "matrix-react-sdk",
|
||||||
"version": "3.19.0",
|
"version": "3.20.0",
|
||||||
"description": "SDK for matrix.org using React",
|
"description": "SDK for matrix.org using React",
|
||||||
"author": "matrix.org",
|
"author": "matrix.org",
|
||||||
"repository": {
|
"repository": {
|
||||||
|
@ -97,7 +97,7 @@
|
||||||
"react-transition-group": "^4.4.1",
|
"react-transition-group": "^4.4.1",
|
||||||
"resize-observer-polyfill": "^1.5.1",
|
"resize-observer-polyfill": "^1.5.1",
|
||||||
"rfc4648": "^1.4.0",
|
"rfc4648": "^1.4.0",
|
||||||
"sanitize-html": "github:apostrophecms/sanitize-html#3c7f93f2058f696f5359e3e58d464161647226db",
|
"sanitize-html": "^2.3.2",
|
||||||
"tar-js": "^0.3.0",
|
"tar-js": "^0.3.0",
|
||||||
"text-encoding-utf-8": "^1.0.2",
|
"text-encoding-utf-8": "^1.0.2",
|
||||||
"url": "^0.11.0",
|
"url": "^0.11.0",
|
||||||
|
|
|
@ -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;
|
||||||
|
|
|
@ -108,8 +108,6 @@ export default class ImageView extends React.Component<IProps, IState> {
|
||||||
window.addEventListener("resize", this.calculateZoom);
|
window.addEventListener("resize", this.calculateZoom);
|
||||||
// After the image loads for the first time we want to calculate the zoom
|
// After the image loads for the first time we want to calculate the zoom
|
||||||
this.image.current.addEventListener("load", this.calculateZoom);
|
this.image.current.addEventListener("load", this.calculateZoom);
|
||||||
// Try to precalculate the zoom from width and height props
|
|
||||||
this.calculateZoom();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
componentWillUnmount() {
|
componentWillUnmount() {
|
||||||
|
@ -122,11 +120,8 @@ export default class ImageView extends React.Component<IProps, IState> {
|
||||||
const image = this.image.current;
|
const image = this.image.current;
|
||||||
const imageWrapper = this.imageWrapper.current;
|
const imageWrapper = this.imageWrapper.current;
|
||||||
|
|
||||||
const width = this.props.width || image.naturalWidth;
|
const zoomX = imageWrapper.clientWidth / image.naturalWidth;
|
||||||
const height = this.props.height || image.naturalHeight;
|
const zoomY = imageWrapper.clientHeight / image.naturalHeight;
|
||||||
|
|
||||||
const zoomX = imageWrapper.clientWidth / width;
|
|
||||||
const zoomY = imageWrapper.clientHeight / height;
|
|
||||||
|
|
||||||
// If the image is smaller in both dimensions set its the zoom to 1 to
|
// If the image is smaller in both dimensions set its the zoom to 1 to
|
||||||
// display it in its original size
|
// display it in its original size
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -1013,7 +1013,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",
|
||||||
|
@ -1442,6 +1442,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",
|
||||||
|
@ -1632,13 +1639,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",
|
||||||
|
@ -2017,10 +2017,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", () => {
|
||||||
|
|
31
yarn.lock
31
yarn.lock
|
@ -4226,9 +4226,9 @@ hoist-non-react-statics@^3.3.0:
|
||||||
react-is "^16.7.0"
|
react-is "^16.7.0"
|
||||||
|
|
||||||
hosted-git-info@^2.1.4:
|
hosted-git-info@^2.1.4:
|
||||||
version "2.8.8"
|
version "2.8.9"
|
||||||
resolved "https://registry.yarnpkg.com/hosted-git-info/-/hosted-git-info-2.8.8.tgz#7539bd4bc1e0e0a895815a2e0262420b12858488"
|
resolved "https://registry.yarnpkg.com/hosted-git-info/-/hosted-git-info-2.8.9.tgz#dffc0bf9a21c02209090f2aa69429e1414daf3f9"
|
||||||
integrity sha512-f/wzC2QaWBs7t9IYqB4T3sR1xviIViXJRJTWBlx2Gf3g0Xi5vI7Yy4koXQ1c9OYDGHN9sBy1DQ2AB8fqZBWhUg==
|
integrity sha512-mxIDAb9Lsm6DoOJ7xH+5+X4y1LU/4Hi50L9C5sIswK3JzULS4bwk1FvjdBgvYR4bzT4tuUQiC15FE2f5HbLvYw==
|
||||||
|
|
||||||
hosted-git-info@^3.0.6:
|
hosted-git-info@^3.0.6:
|
||||||
version "3.0.7"
|
version "3.0.7"
|
||||||
|
@ -5677,8 +5677,8 @@ mathml-tag-names@^2.1.3:
|
||||||
integrity sha512-APMBEanjybaPzUrfqU0IMU5I0AswKMH7k8OTLs0vvV4KZpExkTkY87nR/zpbuTPj+gARop7aGUbl11pnDfW6xg==
|
integrity sha512-APMBEanjybaPzUrfqU0IMU5I0AswKMH7k8OTLs0vvV4KZpExkTkY87nR/zpbuTPj+gARop7aGUbl11pnDfW6xg==
|
||||||
|
|
||||||
"matrix-js-sdk@github:matrix-org/matrix-js-sdk#develop":
|
"matrix-js-sdk@github:matrix-org/matrix-js-sdk#develop":
|
||||||
version "10.0.0"
|
version "10.1.0"
|
||||||
resolved "https://codeload.github.com/matrix-org/matrix-js-sdk/tar.gz/c8f69c0b7937b9064938c134d708c4d064b71315"
|
resolved "https://codeload.github.com/matrix-org/matrix-js-sdk/tar.gz/2d73805ca3d8c5a140fe05e574f826696de1656a"
|
||||||
dependencies:
|
dependencies:
|
||||||
"@babel/runtime" "^7.12.5"
|
"@babel/runtime" "^7.12.5"
|
||||||
another-json "^0.2.0"
|
another-json "^0.2.0"
|
||||||
|
@ -6307,6 +6307,11 @@ parse-json@^5.0.0:
|
||||||
json-parse-even-better-errors "^2.3.0"
|
json-parse-even-better-errors "^2.3.0"
|
||||||
lines-and-columns "^1.1.6"
|
lines-and-columns "^1.1.6"
|
||||||
|
|
||||||
|
parse-srcset@^1.0.2:
|
||||||
|
version "1.0.2"
|
||||||
|
resolved "https://registry.yarnpkg.com/parse-srcset/-/parse-srcset-1.0.2.tgz#f2bd221f6cc970a938d88556abc589caaaa2bde1"
|
||||||
|
integrity sha1-8r0iH2zJcKk42IVWq8WJyqqiveE=
|
||||||
|
|
||||||
parse5-htmlparser2-tree-adapter@^6.0.0:
|
parse5-htmlparser2-tree-adapter@^6.0.0:
|
||||||
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"
|
||||||
|
@ -7248,17 +7253,18 @@ sane@^4.0.3:
|
||||||
minimist "^1.1.1"
|
minimist "^1.1.1"
|
||||||
walker "~1.0.5"
|
walker "~1.0.5"
|
||||||
|
|
||||||
"sanitize-html@github:apostrophecms/sanitize-html#3c7f93f2058f696f5359e3e58d464161647226db":
|
sanitize-html@^2.3.2:
|
||||||
version "2.0.0-rc.3"
|
version "2.3.3"
|
||||||
resolved "https://codeload.github.com/apostrophecms/sanitize-html/tar.gz/3c7f93f2058f696f5359e3e58d464161647226db"
|
resolved "https://registry.yarnpkg.com/sanitize-html/-/sanitize-html-2.3.3.tgz#3db382c9a621cce4c46d90f10c64f1e9da9e8353"
|
||||||
|
integrity sha512-DCFXPt7Di0c6JUnlT90eIgrjs6TsJl/8HYU3KLdmrVclFN4O0heTcVbJiMa23OKVr6aR051XYtsgd8EWwEBwUA==
|
||||||
dependencies:
|
dependencies:
|
||||||
deepmerge "^4.2.2"
|
deepmerge "^4.2.2"
|
||||||
escape-string-regexp "^4.0.0"
|
escape-string-regexp "^4.0.0"
|
||||||
htmlparser2 "^4.1.0"
|
htmlparser2 "^6.0.0"
|
||||||
is-plain-object "^5.0.0"
|
is-plain-object "^5.0.0"
|
||||||
klona "^2.0.3"
|
klona "^2.0.3"
|
||||||
|
parse-srcset "^1.0.2"
|
||||||
postcss "^8.0.2"
|
postcss "^8.0.2"
|
||||||
srcset "^3.0.0"
|
|
||||||
|
|
||||||
saxes@^5.0.0:
|
saxes@^5.0.0:
|
||||||
version "5.0.1"
|
version "5.0.1"
|
||||||
|
@ -7515,11 +7521,6 @@ sprintf-js@~1.0.2:
|
||||||
resolved "https://registry.yarnpkg.com/sprintf-js/-/sprintf-js-1.0.3.tgz#04e6926f662895354f3dd015203633b857297e2c"
|
resolved "https://registry.yarnpkg.com/sprintf-js/-/sprintf-js-1.0.3.tgz#04e6926f662895354f3dd015203633b857297e2c"
|
||||||
integrity sha1-BOaSb2YolTVPPdAVIDYzuFcpfiw=
|
integrity sha1-BOaSb2YolTVPPdAVIDYzuFcpfiw=
|
||||||
|
|
||||||
srcset@^3.0.0:
|
|
||||||
version "3.0.0"
|
|
||||||
resolved "https://registry.yarnpkg.com/srcset/-/srcset-3.0.0.tgz#8afd8b971362dfc129ae9c1a99b3897301ce6441"
|
|
||||||
integrity sha512-D59vF08Qzu/C4GAOXVgMTLfgryt5fyWo93FZyhEWANo0PokFz/iWdDe13mX3O5TRf6l8vMTqckAfR4zPiaH0yQ==
|
|
||||||
|
|
||||||
sshpk@^1.7.0:
|
sshpk@^1.7.0:
|
||||||
version "1.16.1"
|
version "1.16.1"
|
||||||
resolved "https://registry.yarnpkg.com/sshpk/-/sshpk-1.16.1.tgz#fb661c0bef29b39db40769ee39fa70093d6f6877"
|
resolved "https://registry.yarnpkg.com/sshpk/-/sshpk-1.16.1.tgz#fb661c0bef29b39db40769ee39fa70093d6f6877"
|
||||||
|
|
Loading…
Reference in a new issue