Merge remote-tracking branch 'robintown/widget-feed-state' into element-call-nov-preview
|
@ -127,7 +127,7 @@
|
||||||
"matrix-encrypt-attachment": "^1.0.3",
|
"matrix-encrypt-attachment": "^1.0.3",
|
||||||
"matrix-events-sdk": "0.0.1",
|
"matrix-events-sdk": "0.0.1",
|
||||||
"matrix-js-sdk": "github:matrix-org/matrix-js-sdk#develop",
|
"matrix-js-sdk": "github:matrix-org/matrix-js-sdk#develop",
|
||||||
"matrix-widget-api": "^1.9.0",
|
"matrix-widget-api": "^1.10.0",
|
||||||
"memoize-one": "^6.0.0",
|
"memoize-one": "^6.0.0",
|
||||||
"oidc-client-ts": "^3.0.1",
|
"oidc-client-ts": "^3.0.1",
|
||||||
"opus-recorder": "^8.0.3",
|
"opus-recorder": "^8.0.3",
|
||||||
|
|
|
@ -20,7 +20,7 @@ import { randB64Bytes } from "../../utils/rand";
|
||||||
// Docker tag to use for synapse docker image.
|
// Docker tag to use for synapse docker image.
|
||||||
// We target a specific digest as every now and then a Synapse update will break our CI.
|
// We target a specific digest as every now and then a Synapse update will break our CI.
|
||||||
// This digest is updated by the playwright-image-updates.yaml workflow periodically.
|
// This digest is updated by the playwright-image-updates.yaml workflow periodically.
|
||||||
const DOCKER_TAG = "develop@sha256:d1a89bd0fcdc2bf2900dac30696d53bb9e44da1231faacd5c2d3b9f539ce9586";
|
const DOCKER_TAG = "develop@sha256:b90c4e10abfc6bb4fb9301d5b148ab7e1ab752298624a705e84e7e1ad6037d08";
|
||||||
|
|
||||||
async function cfgDirFromTemplate(opts: StartHomeserverOpts): Promise<Omit<HomeserverConfig, "dockerUrl">> {
|
async function cfgDirFromTemplate(opts: StartHomeserverOpts): Promise<Omit<HomeserverConfig, "dockerUrl">> {
|
||||||
const templateDir = path.join(__dirname, "templates", opts.template);
|
const templateDir = path.join(__dirname, "templates", opts.template);
|
||||||
|
|
Before Width: | Height: | Size: 1.3 MiB After Width: | Height: | Size: 1.1 MiB |
Before Width: | Height: | Size: 1.2 MiB After Width: | Height: | Size: 1.0 MiB |
Before Width: | Height: | Size: 1.2 MiB After Width: | Height: | Size: 1.1 MiB |
Before Width: | Height: | Size: 50 KiB After Width: | Height: | Size: 50 KiB |
Before Width: | Height: | Size: 62 KiB After Width: | Height: | Size: 62 KiB |
Before Width: | Height: | Size: 49 KiB After Width: | Height: | Size: 49 KiB |
Before Width: | Height: | Size: 198 KiB After Width: | Height: | Size: 198 KiB |
Before Width: | Height: | Size: 36 KiB After Width: | Height: | Size: 36 KiB |
Before Width: | Height: | Size: 5.8 KiB After Width: | Height: | Size: 4.8 KiB |
|
@ -186,7 +186,7 @@ input[type="search"].mx_textinput_icon {
|
||||||
/* FIXME THEME - Tint by CSS rather than referencing a duplicate asset */
|
/* FIXME THEME - Tint by CSS rather than referencing a duplicate asset */
|
||||||
input[type="text"].mx_textinput_icon.mx_textinput_search,
|
input[type="text"].mx_textinput_icon.mx_textinput_search,
|
||||||
input[type="search"].mx_textinput_icon.mx_textinput_search {
|
input[type="search"].mx_textinput_icon.mx_textinput_search {
|
||||||
background-image: url("$(res)/img/feather-customised/search-input.svg");
|
background-image: url("@vector-im/compound-design-tokens/icons/search.svg");
|
||||||
}
|
}
|
||||||
|
|
||||||
/* dont search UI as not all browsers support it, */
|
/* dont search UI as not all browsers support it, */
|
||||||
|
|
|
@ -32,8 +32,8 @@ Please see LICENSE files in the repository root for full details.
|
||||||
}
|
}
|
||||||
|
|
||||||
.mx_DeviceExpandDetailsButton_icon {
|
.mx_DeviceExpandDetailsButton_icon {
|
||||||
height: 16px;
|
height: 24px;
|
||||||
width: 16px;
|
width: 24px;
|
||||||
|
|
||||||
transition: all 0.3s;
|
transition: all 0.3s;
|
||||||
transform: var(--icon-transform);
|
transform: var(--icon-transform);
|
||||||
|
|
|
@ -25,7 +25,7 @@ Please see LICENSE files in the repository root for full details.
|
||||||
width: 18px;
|
width: 18px;
|
||||||
height: 18px;
|
height: 18px;
|
||||||
background: currentColor;
|
background: currentColor;
|
||||||
mask-image: url("$(res)/img/feather-customised/chevron-down.svg");
|
mask-image: url("@vector-im/compound-design-tokens/icons/chevron-down.svg");
|
||||||
mask-size: 100%;
|
mask-size: 100%;
|
||||||
mask-repeat: no-repeat;
|
mask-repeat: no-repeat;
|
||||||
float: right;
|
float: right;
|
||||||
|
|
|
@ -62,7 +62,7 @@ Please see LICENSE files in the repository root for full details.
|
||||||
|
|
||||||
&::before {
|
&::before {
|
||||||
background-color: $info-plinth-fg-color;
|
background-color: $info-plinth-fg-color;
|
||||||
mask: url("$(res)/img/feather-customised/search-input.svg");
|
mask: url("@vector-im/compound-design-tokens/icons/search.svg");
|
||||||
mask-repeat: no-repeat;
|
mask-repeat: no-repeat;
|
||||||
mask-position: center;
|
mask-position: center;
|
||||||
mask-size: 50px;
|
mask-size: 50px;
|
||||||
|
|
|
@ -121,7 +121,7 @@ Please see LICENSE files in the repository root for full details.
|
||||||
background-color: $tertiary-content;
|
background-color: $tertiary-content;
|
||||||
mask-size: 16px;
|
mask-size: 16px;
|
||||||
transform: rotate(270deg);
|
transform: rotate(270deg);
|
||||||
mask-image: url("$(res)/img/feather-customised/chevron-down.svg");
|
mask-image: url("@vector-im/compound-design-tokens/icons/chevron-down.svg");
|
||||||
}
|
}
|
||||||
|
|
||||||
&.mx_SpaceHierarchy_subspace_toggle_shown::before {
|
&.mx_SpaceHierarchy_subspace_toggle_shown::before {
|
||||||
|
|
|
@ -48,7 +48,7 @@ Please see LICENSE files in the repository root for full details.
|
||||||
mask-size: contain;
|
mask-size: contain;
|
||||||
mask-repeat: no-repeat;
|
mask-repeat: no-repeat;
|
||||||
background-color: $background;
|
background-color: $background;
|
||||||
mask-image: url("$(res)/img/feather-customised/chevron-down.svg");
|
mask-image: url("@vector-im/compound-design-tokens/icons/chevron-down.svg");
|
||||||
transform: rotate(270deg);
|
transform: rotate(270deg);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -169,7 +169,7 @@ Please see LICENSE files in the repository root for full details.
|
||||||
mask-size: 20px;
|
mask-size: 20px;
|
||||||
mask-repeat: no-repeat;
|
mask-repeat: no-repeat;
|
||||||
background-color: $tertiary-content;
|
background-color: $tertiary-content;
|
||||||
mask-image: url("$(res)/img/feather-customised/chevron-down.svg");
|
mask-image: url("@vector-im/compound-design-tokens/icons/chevron-down.svg");
|
||||||
}
|
}
|
||||||
|
|
||||||
.mx_SpaceButton_icon {
|
.mx_SpaceButton_icon {
|
||||||
|
|
|
@ -36,9 +36,24 @@ Please see LICENSE files in the repository root for full details.
|
||||||
}
|
}
|
||||||
|
|
||||||
.mx_AnalyticsLearnMore_bullets li {
|
.mx_AnalyticsLearnMore_bullets li {
|
||||||
background: url("$(res)/img/tick-circle.svg") no-repeat;
|
|
||||||
list-style-type: none;
|
list-style-type: none;
|
||||||
padding: 2px 0px 20px 32px;
|
padding: 2px 0 0 32px;
|
||||||
|
margin-bottom: 20px;
|
||||||
vertical-align: middle;
|
vertical-align: middle;
|
||||||
|
position: relative;
|
||||||
|
|
||||||
|
&::before {
|
||||||
|
content: "";
|
||||||
|
position: absolute;
|
||||||
|
width: 26px;
|
||||||
|
height: 26px;
|
||||||
|
left: 0;
|
||||||
|
top: 0;
|
||||||
|
background-color: #0dbd8b;
|
||||||
|
mask-image: url("@vector-im/compound-design-tokens/icons/check-circle.svg");
|
||||||
|
mask-repeat: no-repeat;
|
||||||
|
mask-position: center;
|
||||||
|
mask-size: contain;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -39,11 +39,13 @@ Please see LICENSE files in the repository root for full details.
|
||||||
}
|
}
|
||||||
|
|
||||||
.mx_Dropdown_arrow {
|
.mx_Dropdown_arrow {
|
||||||
width: 10px;
|
width: 16px;
|
||||||
height: 6px;
|
height: 16px;
|
||||||
padding-right: 9px;
|
margin-right: 4px;
|
||||||
mask: url("$(res)/img/feather-customised/dropdown-arrow.svg");
|
mask: url("@vector-im/compound-design-tokens/icons/chevron-down.svg");
|
||||||
mask-repeat: no-repeat;
|
mask-repeat: no-repeat;
|
||||||
|
mask-position: center;
|
||||||
|
mask-size: 18px;
|
||||||
background: $primary-content;
|
background: $primary-content;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -51,12 +51,15 @@ Please see LICENSE files in the repository root for full details.
|
||||||
.mx_Field_select::before {
|
.mx_Field_select::before {
|
||||||
content: "";
|
content: "";
|
||||||
position: absolute;
|
position: absolute;
|
||||||
top: 15px;
|
top: 50%;
|
||||||
right: 10px;
|
transform: translateY(-50%);
|
||||||
width: 10px;
|
right: 4px;
|
||||||
height: 6px;
|
width: 18px;
|
||||||
mask: url("$(res)/img/feather-customised/dropdown-arrow.svg");
|
height: 18px;
|
||||||
|
mask: url("@vector-im/compound-design-tokens/icons/chevron-down.svg");
|
||||||
mask-repeat: no-repeat;
|
mask-repeat: no-repeat;
|
||||||
|
mask-position: center;
|
||||||
|
mask-size: contain;
|
||||||
background-color: $primary-content;
|
background-color: $primary-content;
|
||||||
z-index: 1;
|
z-index: 1;
|
||||||
pointer-events: none;
|
pointer-events: none;
|
||||||
|
|
|
@ -30,6 +30,6 @@ Please see LICENSE files in the repository root for full details.
|
||||||
mask-position: center;
|
mask-position: center;
|
||||||
mask-size: contain;
|
mask-size: contain;
|
||||||
mask-repeat: no-repeat;
|
mask-repeat: no-repeat;
|
||||||
mask-image: url("$(res)/img/feather-customised/chevron-down.svg");
|
mask-image: url("@vector-im/compound-design-tokens/icons/chevron-down.svg");
|
||||||
background-color: var(--cpd-color-icon-secondary);
|
background-color: var(--cpd-color-icon-secondary);
|
||||||
}
|
}
|
||||||
|
|
|
@ -45,7 +45,7 @@ Please see LICENSE files in the repository root for full details.
|
||||||
width: 18px;
|
width: 18px;
|
||||||
height: 18px;
|
height: 18px;
|
||||||
background: currentColor;
|
background: currentColor;
|
||||||
mask-image: url("$(res)/img/feather-customised/chevron-down.svg");
|
mask-image: url("@vector-im/compound-design-tokens/icons/chevron-down.svg");
|
||||||
mask-size: 100%;
|
mask-size: 100%;
|
||||||
mask-repeat: no-repeat;
|
mask-repeat: no-repeat;
|
||||||
float: right;
|
float: right;
|
||||||
|
|
|
@ -26,9 +26,9 @@ Please see LICENSE files in the repository root for full details.
|
||||||
height: 16px;
|
height: 16px;
|
||||||
width: 16px;
|
width: 16px;
|
||||||
padding: 4px;
|
padding: 4px;
|
||||||
mask-image: url("$(res)/img/minimise.svg");
|
mask-image: url("@vector-im/compound-design-tokens/icons/chevron-left.svg");
|
||||||
mask-repeat: no-repeat;
|
mask-repeat: no-repeat;
|
||||||
mask-position: 7px center;
|
mask-position: center;
|
||||||
background-color: $header-panel-text-primary-color;
|
background-color: $header-panel-text-primary-color;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -18,7 +18,7 @@ Please see LICENSE files in the repository root for full details.
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
&:hover .mx_LinkPreviewGroup_hide img,
|
&:hover .mx_LinkPreviewGroup_hide svg,
|
||||||
.mx_LinkPreviewGroup_hide:focus-visible:focus svg {
|
.mx_LinkPreviewGroup_hide:focus-visible:focus svg {
|
||||||
visibility: visible;
|
visibility: visible;
|
||||||
}
|
}
|
||||||
|
|
|
@ -42,7 +42,7 @@ Please see LICENSE files in the repository root for full details.
|
||||||
mask-size: contain;
|
mask-size: contain;
|
||||||
mask-repeat: no-repeat;
|
mask-repeat: no-repeat;
|
||||||
background-color: $tertiary-content;
|
background-color: $tertiary-content;
|
||||||
mask-image: url("$(res)/img/feather-customised/chevron-down.svg");
|
mask-image: url("@vector-im/compound-design-tokens/icons/chevron-down.svg");
|
||||||
}
|
}
|
||||||
|
|
||||||
&[aria-expanded="true"] {
|
&[aria-expanded="true"] {
|
||||||
|
|
|
@ -160,7 +160,7 @@ Please see LICENSE files in the repository root for full details.
|
||||||
mask-size: contain;
|
mask-size: contain;
|
||||||
mask-repeat: no-repeat;
|
mask-repeat: no-repeat;
|
||||||
background-color: var(--cpd-color-icon-secondary);
|
background-color: var(--cpd-color-icon-secondary);
|
||||||
mask-image: url("$(res)/img/feather-customised/chevron-down.svg");
|
mask-image: url("@vector-im/compound-design-tokens/icons/chevron-down.svg");
|
||||||
}
|
}
|
||||||
|
|
||||||
&.mx_RoomSublist_collapseBtn_collapsed::before {
|
&.mx_RoomSublist_collapseBtn_collapsed::before {
|
||||||
|
@ -276,7 +276,7 @@ Please see LICENSE files in the repository root for full details.
|
||||||
|
|
||||||
.mx_RoomSublist_showMoreButtonChevron,
|
.mx_RoomSublist_showMoreButtonChevron,
|
||||||
.mx_RoomSublist_showLessButtonChevron {
|
.mx_RoomSublist_showLessButtonChevron {
|
||||||
mask-image: url("$(res)/img/feather-customised/chevron-down.svg");
|
mask-image: url("@vector-im/compound-design-tokens/icons/chevron-down.svg");
|
||||||
}
|
}
|
||||||
|
|
||||||
.mx_RoomSublist_showLessButtonChevron {
|
.mx_RoomSublist_showLessButtonChevron {
|
||||||
|
|
|
@ -67,7 +67,7 @@ Please see LICENSE files in the repository root for full details.
|
||||||
mask-repeat: no-repeat;
|
mask-repeat: no-repeat;
|
||||||
mask-position: 2px 3px;
|
mask-position: 2px 3px;
|
||||||
mask-size: 24px;
|
mask-size: 24px;
|
||||||
mask-image: url("$(res)/img/feather-customised/chevron-down.svg");
|
mask-image: url("@vector-im/compound-design-tokens/icons/chevron-down.svg");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -147,7 +147,7 @@ Please see LICENSE files in the repository root for full details.
|
||||||
&::before {
|
&::before {
|
||||||
content: "";
|
content: "";
|
||||||
display: inline-block;
|
display: inline-block;
|
||||||
mask-image: url("$(res)/img/feather-customised/chevron-down.svg");
|
mask-image: url("@vector-im/compound-design-tokens/icons/chevron-down.svg");
|
||||||
mask-size: 20px;
|
mask-size: 20px;
|
||||||
mask-position: center;
|
mask-position: center;
|
||||||
background-color: $call-primary-content;
|
background-color: $call-primary-content;
|
||||||
|
|
|
@ -1,3 +0,0 @@
|
||||||
<svg width="18" height="18" viewBox="0 0 18 18" fill="none" xmlns="http://www.w3.org/2000/svg">
|
|
||||||
<path d="M6 7.5L9 10.5L12 7.5" stroke="black" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
|
|
||||||
</svg>
|
|
Before Width: | Height: | Size: 217 B |
|
@ -1,11 +0,0 @@
|
||||||
|
|
||||||
<svg width="16px" height="16px" viewBox="0 0 16 16" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
|
|
||||||
<g id="Page-1" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd" stroke-linecap="round" stroke-linejoin="round">
|
|
||||||
<g id="LifeBuoy" transform="translate(-1378.000000, -91.000000)" stroke="#61708b" stroke-width="1">
|
|
||||||
<g id="search-copy" transform="translate(1379.000000, 92.000000)">
|
|
||||||
<circle id="Oval" cx="6.22222222" cy="6.22222222" r="6.22222222"></circle>
|
|
||||||
<path d="M14,14 L10.6166667,10.6166667" id="Path"></path>
|
|
||||||
</g>
|
|
||||||
</g>
|
|
||||||
</g>
|
|
||||||
</svg>
|
|
Before Width: | Height: | Size: 674 B |
|
@ -1,18 +0,0 @@
|
||||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
|
||||||
<svg width="10px" height="16px" viewBox="-1 -1 10 16" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns:sketch="http://www.bohemiancoding.com/sketch/ns">
|
|
||||||
<!-- Generator: sketchtool 3.5.1 (25234) - http://www.bohemiancoding.com/sketch -->
|
|
||||||
<title>minimise</title>
|
|
||||||
<desc>Created with sketchtool.</desc>
|
|
||||||
<defs></defs>
|
|
||||||
<g id="02-Chat" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd" sketch:type="MSPage">
|
|
||||||
<g id="02_1-Chat-collapsed-w-topic" sketch:type="MSArtboardGroup" transform="translate(-176.000000, -27.000000)" stroke-width="2" stroke="#9FA9BA">
|
|
||||||
<g id="Room-list" sketch:type="MSLayerGroup">
|
|
||||||
<g id="Room-list/Header" sketch:type="MSShapeGroup">
|
|
||||||
<g id="minimise" transform="translate(172.000000, 25.000000)">
|
|
||||||
<path d="M7,5 L15,5 L15,13" id="Path-53-Copy" transform="translate(11.000000, 9.000000) scale(-1, -1) rotate(-315.000000) translate(-11.000000, -9.000000) "></path>
|
|
||||||
</g>
|
|
||||||
</g>
|
|
||||||
</g>
|
|
||||||
</g>
|
|
||||||
</g>
|
|
||||||
</svg>
|
|
Before Width: | Height: | Size: 1.2 KiB |
|
@ -1,4 +0,0 @@
|
||||||
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
|
|
||||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M12 2C6.5 2 2 6.5 2 12C2 17.5 6.5 22 12 22C17.5 22 22 17.5 22 12C22 6.5 17.5 2 12 2V2Z" stroke="#0DBD8B" stroke-width="2" stroke-linecap="square"/>
|
|
||||||
<path d="M6.54549 12.8882L9.80306 16.2426L17.4546 8.36377" stroke="#0DBD8B" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
|
|
||||||
</svg>
|
|
Before Width: | Height: | Size: 442 B |
|
@ -8,8 +8,8 @@ Please see LICENSE files in the repository root for full details.
|
||||||
|
|
||||||
import classNames from "classnames";
|
import classNames from "classnames";
|
||||||
import React, { ComponentProps } from "react";
|
import React, { ComponentProps } from "react";
|
||||||
|
import { ChevronDownIcon } from "@vector-im/compound-design-tokens/assets/web/icons";
|
||||||
|
|
||||||
import { Icon as CaretIcon } from "../../../../../res/img/feather-customised/dropdown-arrow.svg";
|
|
||||||
import { _t } from "../../../../languageHandler";
|
import { _t } from "../../../../languageHandler";
|
||||||
import AccessibleButton from "../../elements/AccessibleButton";
|
import AccessibleButton from "../../elements/AccessibleButton";
|
||||||
|
|
||||||
|
@ -38,7 +38,7 @@ export const DeviceExpandDetailsButton = <T extends keyof JSX.IntrinsicElements>
|
||||||
})}
|
})}
|
||||||
onClick={onClick}
|
onClick={onClick}
|
||||||
>
|
>
|
||||||
<CaretIcon className="mx_DeviceExpandDetailsButton_icon" />
|
<ChevronDownIcon className="mx_DeviceExpandDetailsButton_icon" />
|
||||||
</AccessibleButton>
|
</AccessibleButton>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
|
@ -6,7 +6,14 @@
|
||||||
* Please see LICENSE files in the repository root for full details.
|
* Please see LICENSE files in the repository root for full details.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import { Room, MatrixEvent, MatrixEventEvent, MatrixClient, ClientEvent } from "matrix-js-sdk/src/matrix";
|
import {
|
||||||
|
Room,
|
||||||
|
MatrixEvent,
|
||||||
|
MatrixEventEvent,
|
||||||
|
MatrixClient,
|
||||||
|
ClientEvent,
|
||||||
|
RoomStateEvent,
|
||||||
|
} from "matrix-js-sdk/src/matrix";
|
||||||
import { KnownMembership } from "matrix-js-sdk/src/types";
|
import { KnownMembership } from "matrix-js-sdk/src/types";
|
||||||
import {
|
import {
|
||||||
ClientWidgetApi,
|
ClientWidgetApi,
|
||||||
|
@ -154,7 +161,10 @@ export class StopGapWidget extends EventEmitter {
|
||||||
private kind: WidgetKind;
|
private kind: WidgetKind;
|
||||||
private readonly virtual: boolean;
|
private readonly virtual: boolean;
|
||||||
private readUpToMap: { [roomId: string]: string } = {}; // room ID to event ID
|
private readUpToMap: { [roomId: string]: string } = {}; // room ID to event ID
|
||||||
private stickyPromise?: () => Promise<void>; // This promise will be called and needs to resolve before the widget will actually become sticky.
|
// This promise will be called and needs to resolve before the widget will actually become sticky.
|
||||||
|
private stickyPromise?: () => Promise<void>;
|
||||||
|
// Holds events that should be fed to the widget once they finish decrypting
|
||||||
|
private readonly eventsToFeed = new WeakSet<MatrixEvent>();
|
||||||
|
|
||||||
public constructor(private appTileProps: IAppTileProps) {
|
public constructor(private appTileProps: IAppTileProps) {
|
||||||
super();
|
super();
|
||||||
|
@ -330,6 +340,7 @@ export class StopGapWidget extends EventEmitter {
|
||||||
// Attach listeners for feeding events - the underlying widget classes handle permissions for us
|
// Attach listeners for feeding events - the underlying widget classes handle permissions for us
|
||||||
this.client.on(ClientEvent.Event, this.onEvent);
|
this.client.on(ClientEvent.Event, this.onEvent);
|
||||||
this.client.on(MatrixEventEvent.Decrypted, this.onEventDecrypted);
|
this.client.on(MatrixEventEvent.Decrypted, this.onEventDecrypted);
|
||||||
|
this.client.on(RoomStateEvent.Events, this.onStateEvent);
|
||||||
this.client.on(ClientEvent.ToDeviceEvent, this.onToDeviceEvent);
|
this.client.on(ClientEvent.ToDeviceEvent, this.onToDeviceEvent);
|
||||||
|
|
||||||
this.messaging.on(
|
this.messaging.on(
|
||||||
|
@ -460,17 +471,33 @@ export class StopGapWidget extends EventEmitter {
|
||||||
|
|
||||||
this.client.off(ClientEvent.Event, this.onEvent);
|
this.client.off(ClientEvent.Event, this.onEvent);
|
||||||
this.client.off(MatrixEventEvent.Decrypted, this.onEventDecrypted);
|
this.client.off(MatrixEventEvent.Decrypted, this.onEventDecrypted);
|
||||||
|
this.client.off(RoomStateEvent.Events, this.onStateEvent);
|
||||||
this.client.off(ClientEvent.ToDeviceEvent, this.onToDeviceEvent);
|
this.client.off(ClientEvent.ToDeviceEvent, this.onToDeviceEvent);
|
||||||
}
|
}
|
||||||
|
|
||||||
private onEvent = (ev: MatrixEvent): void => {
|
private onEvent = (ev: MatrixEvent): void => {
|
||||||
this.client.decryptEventIfNeeded(ev);
|
this.client.decryptEventIfNeeded(ev);
|
||||||
if (ev.isBeingDecrypted() || ev.isDecryptionFailure()) return;
|
// Only process non-state events here; we don't want to confuse the
|
||||||
this.feedEvent(ev);
|
// widget with a state event from the timeline that might not have
|
||||||
|
// actually updated the room's state
|
||||||
|
if (!ev.isState()) this.handleEvent(ev);
|
||||||
};
|
};
|
||||||
|
|
||||||
private onEventDecrypted = (ev: MatrixEvent): void => {
|
private onEventDecrypted = (ev: MatrixEvent): void => {
|
||||||
if (ev.isDecryptionFailure()) return;
|
this.handleEvent(ev);
|
||||||
|
};
|
||||||
|
|
||||||
|
private onStateEvent = (ev: MatrixEvent): void => {
|
||||||
|
// State events get to skip all the checks of handleEvent and be fed
|
||||||
|
// directly to the widget. When it comes to state events, we don't care
|
||||||
|
// so much about getting the order and contents of the timeline right as
|
||||||
|
// we care about state updates reliably getting through to the widget so
|
||||||
|
// that it sees the same state as what the server calculated.
|
||||||
|
// TODO: We can provide widgets with a more complete timeline stream
|
||||||
|
// while also getting state updates right if we create a separate widget
|
||||||
|
// action for communicating state deltas, similar to how the 'state'
|
||||||
|
// sections of sync responses in Simplified Sliding Sync and MSC4222
|
||||||
|
// work.
|
||||||
this.feedEvent(ev);
|
this.feedEvent(ev);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -480,69 +507,104 @@ export class StopGapWidget extends EventEmitter {
|
||||||
await this.messaging?.feedToDevice(ev.getEffectiveEvent() as IRoomEvent, ev.isEncrypted());
|
await this.messaging?.feedToDevice(ev.getEffectiveEvent() as IRoomEvent, ev.isEncrypted());
|
||||||
};
|
};
|
||||||
|
|
||||||
private feedEvent(ev: MatrixEvent): void {
|
/**
|
||||||
if (!this.messaging) return;
|
* Determines whether the event has a relation to an unknown parent.
|
||||||
|
*/
|
||||||
|
private relatesToUnknown(ev: MatrixEvent): boolean {
|
||||||
|
// Replies to unknown events don't count
|
||||||
|
if (!ev.relationEventId || ev.replyEventId) return false;
|
||||||
|
const room = this.client.getRoom(ev.getRoomId());
|
||||||
|
return room === null || !room.findEventById(ev.relationEventId);
|
||||||
|
}
|
||||||
|
|
||||||
// Check to see if this event would be before or after our "read up to" marker. If it's
|
/**
|
||||||
// before, or we can't decide, then we assume the widget will have already seen the event.
|
* Determines whether the event comes from a room that we've been invited to
|
||||||
// If the event is after, or we don't have a marker for the room, then we'll send it through.
|
* (in which case we likely don't have the full timeline).
|
||||||
//
|
*/
|
||||||
// This approach of "read up to" prevents widgets receiving decryption spam from startup or
|
private isFromInvite(ev: MatrixEvent): boolean {
|
||||||
// receiving out-of-order events from backfill and such.
|
const room = this.client.getRoom(ev.getRoomId());
|
||||||
//
|
return room?.getMyMembership() === KnownMembership.Invite;
|
||||||
// Skip marker timeline check for events with relations to unknown parent because these
|
}
|
||||||
// events are not added to the timeline here and will be ignored otherwise:
|
|
||||||
// https://github.com/matrix-org/matrix-js-sdk/blob/d3dfcd924201d71b434af3d77343b5229b6ed75e/src/models/room.ts#L2207-L2213
|
|
||||||
let isRelationToUnknown: boolean | undefined = undefined;
|
|
||||||
const upToEventId = this.readUpToMap[ev.getRoomId()!];
|
|
||||||
if (upToEventId) {
|
|
||||||
// Small optimization for exact match (prevent search)
|
|
||||||
if (upToEventId === ev.getId()) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// should be true to forward the event to the widget
|
/**
|
||||||
let shouldForward = false;
|
* Advances the "read up to" marker for a room to a certain event. No-ops if
|
||||||
|
* the event is before the marker.
|
||||||
const room = this.client.getRoom(ev.getRoomId()!);
|
* @returns Whether the "read up to" marker was advanced.
|
||||||
if (!room) return;
|
*/
|
||||||
// Timelines are most recent last, so reverse the order and limit ourselves to 100 events
|
private advanceReadUpToMarker(ev: MatrixEvent): boolean {
|
||||||
// to avoid overusing the CPU.
|
|
||||||
const timeline = room.getLiveTimeline();
|
|
||||||
const events = arrayFastClone(timeline.getEvents()).reverse().slice(0, 100);
|
|
||||||
|
|
||||||
for (const timelineEvent of events) {
|
|
||||||
if (timelineEvent.getId() === upToEventId) {
|
|
||||||
break;
|
|
||||||
} else if (timelineEvent.getId() === ev.getId()) {
|
|
||||||
shouldForward = true;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!shouldForward) {
|
|
||||||
// checks that the event has a relation to unknown event
|
|
||||||
isRelationToUnknown =
|
|
||||||
!ev.replyEventId && !!ev.relationEventId && !room.findEventById(ev.relationEventId);
|
|
||||||
if (!isRelationToUnknown) {
|
|
||||||
// Ignore the event: it is before our interest.
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Skip marker assignment if membership is 'invite', otherwise 'm.room.member' from
|
|
||||||
// invitation room will assign it and new state events will be not forwarded to the widget
|
|
||||||
// because of empty timeline for invitation room and assigned marker.
|
|
||||||
const evRoomId = ev.getRoomId();
|
|
||||||
const evId = ev.getId();
|
const evId = ev.getId();
|
||||||
if (evRoomId && evId) {
|
if (evId === undefined) return false;
|
||||||
const room = this.client.getRoom(evRoomId);
|
const roomId = ev.getRoomId();
|
||||||
if (room && room.getMyMembership() === KnownMembership.Join && !isRelationToUnknown) {
|
if (roomId === undefined) return false;
|
||||||
this.readUpToMap[evRoomId] = evId;
|
const room = this.client.getRoom(roomId);
|
||||||
|
if (room === null) return false;
|
||||||
|
|
||||||
|
const upToEventId = this.readUpToMap[ev.getRoomId()!];
|
||||||
|
if (!upToEventId) {
|
||||||
|
// There's no marker yet; start it at this event
|
||||||
|
this.readUpToMap[roomId] = evId;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Small optimization for exact match (skip the search)
|
||||||
|
if (upToEventId === evId) return false;
|
||||||
|
|
||||||
|
// Timelines are most recent last, so reverse the order and limit ourselves to 100 events
|
||||||
|
// to avoid overusing the CPU.
|
||||||
|
const timeline = room.getLiveTimeline();
|
||||||
|
const events = arrayFastClone(timeline.getEvents()).reverse().slice(0, 100);
|
||||||
|
|
||||||
|
for (const timelineEvent of events) {
|
||||||
|
if (timelineEvent.getId() === upToEventId) {
|
||||||
|
// The event must be somewhere before the "read up to" marker
|
||||||
|
return false;
|
||||||
|
} else if (timelineEvent.getId() === ev.getId()) {
|
||||||
|
// The event is after the marker; advance it
|
||||||
|
this.readUpToMap[roomId] = evId;
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// We can't say for sure whether the widget has seen the event; let's
|
||||||
|
// just assume that it has
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
private handleEvent(ev: MatrixEvent): void {
|
||||||
|
if (
|
||||||
|
// If we had decided earlier to feed this event to the widget, but
|
||||||
|
// it just wasn't ready, give it another try
|
||||||
|
this.eventsToFeed.delete(ev) ||
|
||||||
|
// Skip marker timeline check for events with relations to unknown parent because these
|
||||||
|
// events are not added to the timeline here and will be ignored otherwise:
|
||||||
|
// https://github.com/matrix-org/matrix-js-sdk/blob/d3dfcd924201d71b434af3d77343b5229b6ed75e/src/models/room.ts#L2207-L2213
|
||||||
|
this.relatesToUnknown(ev) ||
|
||||||
|
// Skip marker timeline check for rooms where membership is
|
||||||
|
// 'invite', otherwise the membership event from the invitation room
|
||||||
|
// will advance the marker and new state events will not be
|
||||||
|
// forwarded to the widget.
|
||||||
|
this.isFromInvite(ev) ||
|
||||||
|
// Check whether this event would be before or after our "read up to" marker. If it's
|
||||||
|
// before, or we can't decide, then we assume the widget will have already seen the event.
|
||||||
|
// If the event is after, or we don't have a marker for the room, then the marker will advance and we'll
|
||||||
|
// send it through.
|
||||||
|
// This approach of "read up to" prevents widgets receiving decryption spam from startup or
|
||||||
|
// receiving ancient events from backfill and such.
|
||||||
|
this.advanceReadUpToMarker(ev)
|
||||||
|
) {
|
||||||
|
// If the event is still being decrypted, remember that we want to
|
||||||
|
// feed it to the widget (even if not strictly in the order given by
|
||||||
|
// the timeline) and get back to it later
|
||||||
|
if (ev.isBeingDecrypted() || ev.isDecryptionFailure()) {
|
||||||
|
this.eventsToFeed.add(ev);
|
||||||
|
} else {
|
||||||
|
this.feedEvent(ev);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private feedEvent(ev: MatrixEvent): void {
|
||||||
|
if (this.messaging === null) return;
|
||||||
const raw = ev.getEffectiveEvent();
|
const raw = ev.getEffectiveEvent();
|
||||||
this.messaging.feedEvent(raw as IRoomEvent, this.eventListenerRoomId!).catch((e) => {
|
this.messaging.feedEvent(raw as IRoomEvent, this.eventListenerRoomId!).catch((e) => {
|
||||||
logger.error("Error sending event to widget: ", e);
|
logger.error("Error sending event to widget: ", e);
|
||||||
|
|
|
@ -24,6 +24,7 @@ import {
|
||||||
WidgetDriver,
|
WidgetDriver,
|
||||||
WidgetEventCapability,
|
WidgetEventCapability,
|
||||||
WidgetKind,
|
WidgetKind,
|
||||||
|
IWidgetApiErrorResponseDataDetails,
|
||||||
ISearchUserDirectoryResult,
|
ISearchUserDirectoryResult,
|
||||||
IGetMediaConfigResult,
|
IGetMediaConfigResult,
|
||||||
UpdateDelayedEventAction,
|
UpdateDelayedEventAction,
|
||||||
|
@ -33,6 +34,7 @@ import {
|
||||||
ITurnServer as IClientTurnServer,
|
ITurnServer as IClientTurnServer,
|
||||||
EventType,
|
EventType,
|
||||||
IContent,
|
IContent,
|
||||||
|
MatrixError,
|
||||||
MatrixEvent,
|
MatrixEvent,
|
||||||
Room,
|
Room,
|
||||||
Direction,
|
Direction,
|
||||||
|
@ -689,4 +691,15 @@ export class StopGapWidgetDriver extends WidgetDriver {
|
||||||
const blob = await response.blob();
|
const blob = await response.blob();
|
||||||
return { file: blob };
|
return { file: blob };
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Expresses a {@link MatrixError} as a JSON payload
|
||||||
|
* for use by Widget API error responses.
|
||||||
|
* @param error The error to handle.
|
||||||
|
* @returns The error expressed as a JSON payload,
|
||||||
|
* or undefined if it is not a {@link MatrixError}.
|
||||||
|
*/
|
||||||
|
public processError(error: unknown): IWidgetApiErrorResponseDataDetails | undefined {
|
||||||
|
return error instanceof MatrixError ? { matrix_api_error: error.asWidgetApiErrorData() } : undefined;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -263,9 +263,18 @@ exports[`<CurrentDeviceSection /> renders device and correct security card when
|
||||||
role="button"
|
role="button"
|
||||||
tabindex="0"
|
tabindex="0"
|
||||||
>
|
>
|
||||||
<div
|
<svg
|
||||||
class="mx_DeviceExpandDetailsButton_icon"
|
class="mx_DeviceExpandDetailsButton_icon"
|
||||||
/>
|
fill="currentColor"
|
||||||
|
height="1em"
|
||||||
|
viewBox="0 0 24 24"
|
||||||
|
width="1em"
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
>
|
||||||
|
<path
|
||||||
|
d="M12 14.95c-.133 0-.258-.02-.375-.063a.876.876 0 0 1-.325-.212l-4.6-4.6a.948.948 0 0 1-.275-.7.95.95 0 0 1 .275-.7.948.948 0 0 1 .7-.275.95.95 0 0 1 .7.275l3.9 3.9 3.9-3.9a.948.948 0 0 1 .7-.275.95.95 0 0 1 .7.275.948.948 0 0 1 .275.7.948.948 0 0 1-.275.7l-4.6 4.6c-.1.1-.208.17-.325.212a1.106 1.106 0 0 1-.375.063Z"
|
||||||
|
/>
|
||||||
|
</svg>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -416,9 +425,18 @@ exports[`<CurrentDeviceSection /> renders device and correct security card when
|
||||||
role="button"
|
role="button"
|
||||||
tabindex="0"
|
tabindex="0"
|
||||||
>
|
>
|
||||||
<div
|
<svg
|
||||||
class="mx_DeviceExpandDetailsButton_icon"
|
class="mx_DeviceExpandDetailsButton_icon"
|
||||||
/>
|
fill="currentColor"
|
||||||
|
height="1em"
|
||||||
|
viewBox="0 0 24 24"
|
||||||
|
width="1em"
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
>
|
||||||
|
<path
|
||||||
|
d="M12 14.95c-.133 0-.258-.02-.375-.063a.876.876 0 0 1-.325-.212l-4.6-4.6a.948.948 0 0 1-.275-.7.95.95 0 0 1 .275-.7.948.948 0 0 1 .7-.275.95.95 0 0 1 .7.275l3.9 3.9 3.9-3.9a.948.948 0 0 1 .7-.275.95.95 0 0 1 .7.275.948.948 0 0 1 .275.7.948.948 0 0 1-.275.7l-4.6 4.6c-.1.1-.208.17-.325.212a1.106 1.106 0 0 1-.375.063Z"
|
||||||
|
/>
|
||||||
|
</svg>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -9,9 +9,18 @@ exports[`<DeviceExpandDetailsButton /> renders when expanded 1`] = `
|
||||||
role="button"
|
role="button"
|
||||||
tabindex="0"
|
tabindex="0"
|
||||||
>
|
>
|
||||||
<div
|
<svg
|
||||||
class="mx_DeviceExpandDetailsButton_icon"
|
class="mx_DeviceExpandDetailsButton_icon"
|
||||||
/>
|
fill="currentColor"
|
||||||
|
height="1em"
|
||||||
|
viewBox="0 0 24 24"
|
||||||
|
width="1em"
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
>
|
||||||
|
<path
|
||||||
|
d="M12 14.95c-.133 0-.258-.02-.375-.063a.876.876 0 0 1-.325-.212l-4.6-4.6a.948.948 0 0 1-.275-.7.95.95 0 0 1 .275-.7.948.948 0 0 1 .7-.275.95.95 0 0 1 .7.275l3.9 3.9 3.9-3.9a.948.948 0 0 1 .7-.275.95.95 0 0 1 .7.275.948.948 0 0 1 .275.7.948.948 0 0 1-.275.7l-4.6 4.6c-.1.1-.208.17-.325.212a1.106 1.106 0 0 1-.375.063Z"
|
||||||
|
/>
|
||||||
|
</svg>
|
||||||
</div>
|
</div>
|
||||||
</div>,
|
</div>,
|
||||||
}
|
}
|
||||||
|
@ -26,9 +35,18 @@ exports[`<DeviceExpandDetailsButton /> renders when not expanded 1`] = `
|
||||||
role="button"
|
role="button"
|
||||||
tabindex="0"
|
tabindex="0"
|
||||||
>
|
>
|
||||||
<div
|
<svg
|
||||||
class="mx_DeviceExpandDetailsButton_icon"
|
class="mx_DeviceExpandDetailsButton_icon"
|
||||||
/>
|
fill="currentColor"
|
||||||
|
height="1em"
|
||||||
|
viewBox="0 0 24 24"
|
||||||
|
width="1em"
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
>
|
||||||
|
<path
|
||||||
|
d="M12 14.95c-.133 0-.258-.02-.375-.063a.876.876 0 0 1-.325-.212l-4.6-4.6a.948.948 0 0 1-.275-.7.95.95 0 0 1 .275-.7.948.948 0 0 1 .7-.275.95.95 0 0 1 .7.275l3.9 3.9 3.9-3.9a.948.948 0 0 1 .7-.275.95.95 0 0 1 .7.275.948.948 0 0 1 .275.7.948.948 0 0 1-.275.7l-4.6 4.6c-.1.1-.208.17-.325.212a1.106 1.106 0 0 1-.375.063Z"
|
||||||
|
/>
|
||||||
|
</svg>
|
||||||
</div>
|
</div>
|
||||||
</div>,
|
</div>,
|
||||||
}
|
}
|
||||||
|
|
|
@ -163,9 +163,18 @@ exports[`<SessionManagerTab /> current session section renders current session s
|
||||||
role="button"
|
role="button"
|
||||||
tabindex="0"
|
tabindex="0"
|
||||||
>
|
>
|
||||||
<div
|
<svg
|
||||||
class="mx_DeviceExpandDetailsButton_icon"
|
class="mx_DeviceExpandDetailsButton_icon"
|
||||||
/>
|
fill="currentColor"
|
||||||
|
height="1em"
|
||||||
|
viewBox="0 0 24 24"
|
||||||
|
width="1em"
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
>
|
||||||
|
<path
|
||||||
|
d="M12 14.95c-.133 0-.258-.02-.375-.063a.876.876 0 0 1-.325-.212l-4.6-4.6a.948.948 0 0 1-.275-.7.95.95 0 0 1 .275-.7.948.948 0 0 1 .7-.275.95.95 0 0 1 .7.275l3.9 3.9 3.9-3.9a.948.948 0 0 1 .7-.275.95.95 0 0 1 .7.275.948.948 0 0 1 .275.7.948.948 0 0 1-.275.7l-4.6 4.6c-.1.1-.208.17-.325.212a1.106 1.106 0 0 1-.375.063Z"
|
||||||
|
/>
|
||||||
|
</svg>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -302,9 +311,18 @@ exports[`<SessionManagerTab /> current session section renders current session s
|
||||||
role="button"
|
role="button"
|
||||||
tabindex="0"
|
tabindex="0"
|
||||||
>
|
>
|
||||||
<div
|
<svg
|
||||||
class="mx_DeviceExpandDetailsButton_icon"
|
class="mx_DeviceExpandDetailsButton_icon"
|
||||||
/>
|
fill="currentColor"
|
||||||
|
height="1em"
|
||||||
|
viewBox="0 0 24 24"
|
||||||
|
width="1em"
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
>
|
||||||
|
<path
|
||||||
|
d="M12 14.95c-.133 0-.258-.02-.375-.063a.876.876 0 0 1-.325-.212l-4.6-4.6a.948.948 0 0 1-.275-.7.95.95 0 0 1 .275-.7.948.948 0 0 1 .7-.275.95.95 0 0 1 .7.275l3.9 3.9 3.9-3.9a.948.948 0 0 1 .7-.275.95.95 0 0 1 .7.275.948.948 0 0 1 .275.7.948.948 0 0 1-.275.7l-4.6 4.6c-.1.1-.208.17-.325.212a1.106 1.106 0 0 1-.375.063Z"
|
||||||
|
/>
|
||||||
|
</svg>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -8,7 +8,14 @@ Please see LICENSE files in the repository root for full details.
|
||||||
|
|
||||||
import { mocked, MockedObject } from "jest-mock";
|
import { mocked, MockedObject } from "jest-mock";
|
||||||
import { last } from "lodash";
|
import { last } from "lodash";
|
||||||
import { MatrixEvent, MatrixClient, ClientEvent, EventTimeline } from "matrix-js-sdk/src/matrix";
|
import {
|
||||||
|
MatrixEvent,
|
||||||
|
MatrixClient,
|
||||||
|
ClientEvent,
|
||||||
|
EventTimeline,
|
||||||
|
EventType,
|
||||||
|
MatrixEventEvent,
|
||||||
|
} from "matrix-js-sdk/src/matrix";
|
||||||
import { ClientWidgetApi, WidgetApiFromWidgetAction } from "matrix-widget-api";
|
import { ClientWidgetApi, WidgetApiFromWidgetAction } from "matrix-widget-api";
|
||||||
import { waitFor } from "jest-matrix-react";
|
import { waitFor } from "jest-matrix-react";
|
||||||
|
|
||||||
|
@ -134,6 +141,46 @@ describe("StopGapWidget", () => {
|
||||||
expect(messaging.feedEvent).toHaveBeenLastCalledWith(event2.getEffectiveEvent(), "!1:example.org");
|
expect(messaging.feedEvent).toHaveBeenLastCalledWith(event2.getEffectiveEvent(), "!1:example.org");
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it("feeds decrypted events asynchronously", async () => {
|
||||||
|
const event1Encrypted = new MatrixEvent({
|
||||||
|
event_id: event1.getId(),
|
||||||
|
type: EventType.RoomMessageEncrypted,
|
||||||
|
sender: event1.sender?.userId,
|
||||||
|
room_id: event1.getRoomId(),
|
||||||
|
content: {},
|
||||||
|
});
|
||||||
|
const decryptingSpy1 = jest.spyOn(event1Encrypted, "isBeingDecrypted").mockReturnValue(true);
|
||||||
|
client.emit(ClientEvent.Event, event1Encrypted);
|
||||||
|
const event2Encrypted = new MatrixEvent({
|
||||||
|
event_id: event2.getId(),
|
||||||
|
type: EventType.RoomMessageEncrypted,
|
||||||
|
sender: event2.sender?.userId,
|
||||||
|
room_id: event2.getRoomId(),
|
||||||
|
content: {},
|
||||||
|
});
|
||||||
|
const decryptingSpy2 = jest.spyOn(event2Encrypted, "isBeingDecrypted").mockReturnValue(true);
|
||||||
|
client.emit(ClientEvent.Event, event2Encrypted);
|
||||||
|
expect(messaging.feedEvent).not.toHaveBeenCalled();
|
||||||
|
|
||||||
|
// "Decrypt" the events, but in reverse order; first event 2…
|
||||||
|
event2Encrypted.event.type = event2.getType();
|
||||||
|
event2Encrypted.event.content = event2.getContent();
|
||||||
|
decryptingSpy2.mockReturnValue(false);
|
||||||
|
client.emit(MatrixEventEvent.Decrypted, event2Encrypted);
|
||||||
|
expect(messaging.feedEvent).toHaveBeenCalledTimes(1);
|
||||||
|
expect(messaging.feedEvent).toHaveBeenLastCalledWith(event2Encrypted.getEffectiveEvent(), "!1:example.org");
|
||||||
|
// …then event 1
|
||||||
|
event1Encrypted.event.type = event1.getType();
|
||||||
|
event1Encrypted.event.content = event1.getContent();
|
||||||
|
decryptingSpy1.mockReturnValue(false);
|
||||||
|
client.emit(MatrixEventEvent.Decrypted, event1Encrypted);
|
||||||
|
// The events should be fed in that same order so that event 2
|
||||||
|
// doesn't have to be blocked on the decryption of event 1 (or
|
||||||
|
// worse, dropped)
|
||||||
|
expect(messaging.feedEvent).toHaveBeenCalledTimes(2);
|
||||||
|
expect(messaging.feedEvent).toHaveBeenLastCalledWith(event1Encrypted.getEffectiveEvent(), "!1:example.org");
|
||||||
|
});
|
||||||
|
|
||||||
it("should not feed incoming event if not in timeline", () => {
|
it("should not feed incoming event if not in timeline", () => {
|
||||||
const event = mkEvent({
|
const event = mkEvent({
|
||||||
event: true,
|
event: true,
|
||||||
|
|
12
yarn.lock
|
@ -8309,7 +8309,7 @@ matrix-events-sdk@0.0.1:
|
||||||
|
|
||||||
"matrix-js-sdk@github:matrix-org/matrix-js-sdk#develop":
|
"matrix-js-sdk@github:matrix-org/matrix-js-sdk#develop":
|
||||||
version "34.10.0"
|
version "34.10.0"
|
||||||
resolved "https://codeload.github.com/matrix-org/matrix-js-sdk/tar.gz/5a1488ebd5552817b1e95265afe7b3baac1231a2"
|
resolved "https://codeload.github.com/matrix-org/matrix-js-sdk/tar.gz/6855ace6422082d173438cb23368d2fabc6a1086"
|
||||||
dependencies:
|
dependencies:
|
||||||
"@babel/runtime" "^7.12.5"
|
"@babel/runtime" "^7.12.5"
|
||||||
"@matrix-org/matrix-sdk-crypto-wasm" "^9.0.0"
|
"@matrix-org/matrix-sdk-crypto-wasm" "^9.0.0"
|
||||||
|
@ -8320,7 +8320,7 @@ matrix-events-sdk@0.0.1:
|
||||||
jwt-decode "^4.0.0"
|
jwt-decode "^4.0.0"
|
||||||
loglevel "^1.7.1"
|
loglevel "^1.7.1"
|
||||||
matrix-events-sdk "0.0.1"
|
matrix-events-sdk "0.0.1"
|
||||||
matrix-widget-api "^1.8.2"
|
matrix-widget-api "^1.10.0"
|
||||||
oidc-client-ts "^3.0.1"
|
oidc-client-ts "^3.0.1"
|
||||||
p-retry "4"
|
p-retry "4"
|
||||||
sdp-transform "^2.14.1"
|
sdp-transform "^2.14.1"
|
||||||
|
@ -8345,10 +8345,10 @@ matrix-web-i18n@^3.2.1:
|
||||||
minimist "^1.2.8"
|
minimist "^1.2.8"
|
||||||
walk "^2.3.15"
|
walk "^2.3.15"
|
||||||
|
|
||||||
matrix-widget-api@^1.8.2, matrix-widget-api@^1.9.0:
|
matrix-widget-api@^1.10.0:
|
||||||
version "1.9.0"
|
version "1.10.0"
|
||||||
resolved "https://registry.yarnpkg.com/matrix-widget-api/-/matrix-widget-api-1.9.0.tgz#884136b405bd3c56e4ea285095c9e01ec52b6b1f"
|
resolved "https://registry.yarnpkg.com/matrix-widget-api/-/matrix-widget-api-1.10.0.tgz#d31ea073a5871a1fb1a511ef900b0c125a37bf55"
|
||||||
integrity sha512-au8mqralNDqrEvaVAkU37bXOb8I9SCe+ACdPk11QWw58FKstVq31q2wRz+qWA6J+42KJ6s1DggWbG/S3fEs3jw==
|
integrity sha512-rkAJ29briYV7TJnfBVLVSKtpeBrBju15JZFSDP6wj8YdbCu1bdmlplJayQ+vYaw1x4fzI49Q+Nz3E85s46sRDw==
|
||||||
dependencies:
|
dependencies:
|
||||||
"@types/events" "^3.0.0"
|
"@types/events" "^3.0.0"
|
||||||
events "^3.2.0"
|
events "^3.2.0"
|
||||||
|
|