Merge branch 'develop' into feat/add-plain-text-mode
This commit is contained in:
commit
423f87a43a
65 changed files with 1008 additions and 296 deletions
4
.github/workflows/cypress.yaml
vendored
4
.github/workflows/cypress.yaml
vendored
|
@ -79,8 +79,8 @@ jobs:
|
|||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
# Run 3 instances in Parallel
|
||||
runner: [1, 2, 3]
|
||||
# Run 4 instances in Parallel
|
||||
runner: [1, 2, 3, 4]
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
with:
|
||||
|
|
50
CHANGELOG.md
50
CHANGELOG.md
|
@ -1,3 +1,53 @@
|
|||
Changes in [3.59.0](https://github.com/matrix-org/matrix-react-sdk/releases/tag/v3.59.0) (2022-10-25)
|
||||
=====================================================================================================
|
||||
|
||||
## ✨ Features
|
||||
* Include a file-safe room name and ISO date in chat exports ([\#9440](https://github.com/matrix-org/matrix-react-sdk/pull/9440)). Fixes vector-im/element-web#21812 and vector-im/element-web#19724.
|
||||
* Room call banner ([\#9378](https://github.com/matrix-org/matrix-react-sdk/pull/9378)). Fixes vector-im/element-web#23453. Contributed by @toger5.
|
||||
* Device manager - spinners while devices are signing out ([\#9433](https://github.com/matrix-org/matrix-react-sdk/pull/9433)). Fixes vector-im/element-web#15865.
|
||||
* Device manager - silence call ringers when local notifications are silenced ([\#9420](https://github.com/matrix-org/matrix-react-sdk/pull/9420)).
|
||||
* Pass the current language to Element Call ([\#9427](https://github.com/matrix-org/matrix-react-sdk/pull/9427)).
|
||||
* Hide screen-sharing button in Element Call on desktop ([\#9423](https://github.com/matrix-org/matrix-react-sdk/pull/9423)).
|
||||
* Add reply support to WysiwygComposer ([\#9422](https://github.com/matrix-org/matrix-react-sdk/pull/9422)). Contributed by @florianduros.
|
||||
* Disconnect other connected devices (of the same user) when joining an Element call ([\#9379](https://github.com/matrix-org/matrix-react-sdk/pull/9379)).
|
||||
* Device manager - device tile main click target ([\#9409](https://github.com/matrix-org/matrix-react-sdk/pull/9409)).
|
||||
* Add formatting buttons to the rich text editor ([\#9410](https://github.com/matrix-org/matrix-react-sdk/pull/9410)). Contributed by @florianduros.
|
||||
* Device manager - current session context menu ([\#9386](https://github.com/matrix-org/matrix-react-sdk/pull/9386)).
|
||||
* Remove piwik config fallback for privacy policy URL ([\#9390](https://github.com/matrix-org/matrix-react-sdk/pull/9390)).
|
||||
* Add the first step to integrate the matrix wysiwyg composer ([\#9374](https://github.com/matrix-org/matrix-react-sdk/pull/9374)). Contributed by @florianduros.
|
||||
* Device manager - UA parsing tweaks ([\#9382](https://github.com/matrix-org/matrix-react-sdk/pull/9382)).
|
||||
* Device manager - remove client information events when disabling setting ([\#9384](https://github.com/matrix-org/matrix-react-sdk/pull/9384)).
|
||||
* Add Element Call participant limit ([\#9358](https://github.com/matrix-org/matrix-react-sdk/pull/9358)).
|
||||
* Add Element Call room settings ([\#9347](https://github.com/matrix-org/matrix-react-sdk/pull/9347)).
|
||||
* Device manager - render extended device information ([\#9360](https://github.com/matrix-org/matrix-react-sdk/pull/9360)).
|
||||
* New group call experience: Room header and PiP designs ([\#9351](https://github.com/matrix-org/matrix-react-sdk/pull/9351)).
|
||||
* Pass language to Jitsi Widget ([\#9346](https://github.com/matrix-org/matrix-react-sdk/pull/9346)). Contributed by @Fox32.
|
||||
* Add notifications and toasts for Element Call calls ([\#9337](https://github.com/matrix-org/matrix-react-sdk/pull/9337)).
|
||||
* Device manager - device type icon ([\#9355](https://github.com/matrix-org/matrix-react-sdk/pull/9355)).
|
||||
* Delete the remainder of groups ([\#9357](https://github.com/matrix-org/matrix-react-sdk/pull/9357)). Fixes vector-im/element-web#22770.
|
||||
* Device manager - display client information in device details ([\#9315](https://github.com/matrix-org/matrix-react-sdk/pull/9315)).
|
||||
|
||||
## 🐛 Bug Fixes
|
||||
* Send Content-Type: application/json header for integration manager /register API ([\#9490](https://github.com/matrix-org/matrix-react-sdk/pull/9490)). Fixes vector-im/element-web#23580.
|
||||
* Device manager - put client/browser device metadata in correct section ([\#9447](https://github.com/matrix-org/matrix-react-sdk/pull/9447)).
|
||||
* update the room unread notification counter when the server changes the value without any related read receipt ([\#9438](https://github.com/matrix-org/matrix-react-sdk/pull/9438)).
|
||||
* Don't show call banners in video rooms ([\#9441](https://github.com/matrix-org/matrix-react-sdk/pull/9441)).
|
||||
* Prevent useContextMenu isOpen from being true if the button ref goes away ([\#9418](https://github.com/matrix-org/matrix-react-sdk/pull/9418)). Fixes matrix-org/element-web-rageshakes#15637.
|
||||
* Automatically focus the WYSIWYG composer when you enter a room ([\#9412](https://github.com/matrix-org/matrix-react-sdk/pull/9412)).
|
||||
* Improve the tooltips on the call lobby join button ([\#9428](https://github.com/matrix-org/matrix-react-sdk/pull/9428)).
|
||||
* Pass the homeserver's base URL to Element Call ([\#9429](https://github.com/matrix-org/matrix-react-sdk/pull/9429)). Fixes vector-im/element-web#23301.
|
||||
* Better accommodate long room names in call toasts ([\#9426](https://github.com/matrix-org/matrix-react-sdk/pull/9426)).
|
||||
* Hide virtual widgets from the room info panel ([\#9424](https://github.com/matrix-org/matrix-react-sdk/pull/9424)). Fixes vector-im/element-web#23494.
|
||||
* Inhibit clicking on sender avatar in threads list ([\#9417](https://github.com/matrix-org/matrix-react-sdk/pull/9417)). Fixes vector-im/element-web#23482.
|
||||
* Correct the dir parameter of MSC3715 ([\#9391](https://github.com/matrix-org/matrix-react-sdk/pull/9391)). Contributed by @dhenneke.
|
||||
* Use a more correct subset of users in `/remakeolm` developer command ([\#9402](https://github.com/matrix-org/matrix-react-sdk/pull/9402)).
|
||||
* use correct default for notification silencing ([\#9388](https://github.com/matrix-org/matrix-react-sdk/pull/9388)). Fixes vector-im/element-web#23456.
|
||||
* Device manager - eagerly create `m.local_notification_settings` events ([\#9353](https://github.com/matrix-org/matrix-react-sdk/pull/9353)).
|
||||
* Close incoming Element call toast when viewing the call lobby ([\#9375](https://github.com/matrix-org/matrix-react-sdk/pull/9375)).
|
||||
* Always allow enabling sending read receipts ([\#9367](https://github.com/matrix-org/matrix-react-sdk/pull/9367)). Fixes vector-im/element-web#23433.
|
||||
* Fixes (vector-im/element-web/issues/22609) where the white theme is not applied when `white -> dark -> white` sequence is done. ([\#9320](https://github.com/matrix-org/matrix-react-sdk/pull/9320)). Contributed by @florianduros.
|
||||
* Fix applying programmatically set height for "top" room layout ([\#9339](https://github.com/matrix-org/matrix-react-sdk/pull/9339)). Contributed by @Fox32.
|
||||
|
||||
Changes in [3.58.1](https://github.com/matrix-org/matrix-react-sdk/releases/tag/v3.58.1) (2022-10-11)
|
||||
=====================================================================================================
|
||||
|
||||
|
|
|
@ -64,6 +64,21 @@ describe("Composer", () => {
|
|||
cy.contains('.mx_EventTile_body strong', 'bold message');
|
||||
});
|
||||
|
||||
it("should allow user to input emoji via graphical picker", () => {
|
||||
cy.getComposer(false).within(() => {
|
||||
cy.get('[aria-label="Emoji"]').click();
|
||||
});
|
||||
|
||||
cy.get('[data-testid="mx_EmojiPicker"]').within(() => {
|
||||
cy.contains(".mx_EmojiPicker_item", "😇").click();
|
||||
});
|
||||
|
||||
cy.get(".mx_ContextualMenu_background").click(); // Close emoji picker
|
||||
cy.get('div[contenteditable=true]').type("{enter}"); // Send message
|
||||
|
||||
cy.contains(".mx_EventTile_body", "😇");
|
||||
});
|
||||
|
||||
describe("when Ctrl+Enter is required to send", () => {
|
||||
beforeEach(() => {
|
||||
cy.setSettingValue("MessageComposerInput.ctrlEnterToSend", null, SettingLevel.ACCOUNT, true);
|
||||
|
|
|
@ -235,7 +235,7 @@ describe("Sliding Sync", () => {
|
|||
"Test Room", "Dummy",
|
||||
]);
|
||||
|
||||
cy.contains(".mx_RoomTile", "Test Room").get(".mx_NotificationBadge").should("not.be.visible");
|
||||
cy.contains(".mx_RoomTile", "Test Room").get(".mx_NotificationBadge").should("not.exist");
|
||||
});
|
||||
|
||||
it("should update user settings promptly", () => {
|
||||
|
|
|
@ -77,7 +77,7 @@ async function proxyStart(synapse: SynapseInstance): Promise<ProxyInstance> {
|
|||
const port = await getFreePort();
|
||||
console.log(new Date(), "starting proxy container...");
|
||||
const containerId = await dockerRun({
|
||||
image: "ghcr.io/matrix-org/sliding-sync-proxy:v0.4.0",
|
||||
image: "ghcr.io/matrix-org/sliding-sync-proxy:v0.6.0",
|
||||
containerName: "react-sdk-cypress-sliding-sync-proxy",
|
||||
params: [
|
||||
"--rm",
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"name": "matrix-react-sdk",
|
||||
"version": "3.58.1",
|
||||
"version": "3.59.0",
|
||||
"description": "SDK for matrix.org using React",
|
||||
"author": "matrix.org",
|
||||
"repository": {
|
||||
|
|
|
@ -4,7 +4,6 @@
|
|||
@import "./_font-sizes.pcss";
|
||||
@import "./_font-weights.pcss";
|
||||
@import "./_spacing.pcss";
|
||||
@import "./compound/_Icon.pcss";
|
||||
@import "./components/views/beacon/_BeaconListItem.pcss";
|
||||
@import "./components/views/beacon/_BeaconStatus.pcss";
|
||||
@import "./components/views/beacon/_BeaconStatusTooltip.pcss";
|
||||
|
@ -19,6 +18,7 @@
|
|||
@import "./components/views/beacon/_StyledLiveBeaconIcon.pcss";
|
||||
@import "./components/views/context_menus/_KebabContextMenu.pcss";
|
||||
@import "./components/views/elements/_FilterDropdown.pcss";
|
||||
@import "./components/views/elements/_LearnMore.pcss";
|
||||
@import "./components/views/location/_EnableLiveShare.pcss";
|
||||
@import "./components/views/location/_LiveDurationDropdown.pcss";
|
||||
@import "./components/views/location/_LocationShareMenu.pcss";
|
||||
|
@ -44,6 +44,7 @@
|
|||
@import "./components/views/settings/shared/_SettingsSubsectionHeading.pcss";
|
||||
@import "./components/views/spaces/_QuickThemeSwitcher.pcss";
|
||||
@import "./components/views/typography/_Caption.pcss";
|
||||
@import "./compound/_Icon.pcss";
|
||||
@import "./structures/_AutoHideScrollbar.pcss";
|
||||
@import "./structures/_BackdropPanel.pcss";
|
||||
@import "./structures/_CompatibilityPage.pcss";
|
||||
|
@ -299,10 +300,10 @@
|
|||
@import "./views/rooms/_TopUnreadMessagesBar.pcss";
|
||||
@import "./views/rooms/_VoiceRecordComposerTile.pcss";
|
||||
@import "./views/rooms/_WhoIsTypingTile.pcss";
|
||||
@import "./views/rooms/wysiwyg_composer/_EditWysiwygComposer.pcss";
|
||||
@import "./views/rooms/wysiwyg_composer/_SendWysiwygComposer.pcss";
|
||||
@import "./views/rooms/wysiwyg_composer/components/_Editor.pcss";
|
||||
@import "./views/rooms/wysiwyg_composer/components/_FormattingButtons.pcss";
|
||||
@import "./views/rooms/wysiwyg_composer/_SendWysiwygComposer.pcss";
|
||||
@import "./views/rooms/wysiwyg_composer/_EditWysiwygComposer.pcss";
|
||||
@import "./views/settings/_AvatarSetting.pcss";
|
||||
@import "./views/settings/_CrossSigningPanel.pcss";
|
||||
@import "./views/settings/_CryptographyPanel.pcss";
|
||||
|
@ -373,6 +374,4 @@
|
|||
@import "./voice-broadcast/atoms/_PlaybackControlButton.pcss";
|
||||
@import "./voice-broadcast/atoms/_VoiceBroadcastControl.pcss";
|
||||
@import "./voice-broadcast/atoms/_VoiceBroadcastHeader.pcss";
|
||||
@import "./voice-broadcast/molecules/_VoiceBroadcastPlaybackBody.pcss";
|
||||
@import "./voice-broadcast/molecules/_VoiceBroadcastRecordingBody.pcss";
|
||||
@import "./voice-broadcast/molecules/_VoiceBroadcastRecordingPip.pcss";
|
||||
@import "./voice-broadcast/molecules/_VoiceBroadcastBody.pcss";
|
||||
|
|
|
@ -14,14 +14,6 @@ See the License for the specific language governing permissions and
|
|||
limitations under the License.
|
||||
*/
|
||||
|
||||
.mx_VoiceBroadcastPlaybackBody {
|
||||
background-color: $quinary-content;
|
||||
border-radius: 8px;
|
||||
display: inline-block;
|
||||
padding: 12px;
|
||||
}
|
||||
|
||||
.mx_VoiceBroadcastPlaybackBody_controls {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
.mx_LearnMore_button {
|
||||
margin-left: $spacing-4;
|
||||
}
|
|
@ -247,7 +247,7 @@ limitations under the License.
|
|||
}
|
||||
|
||||
&.mx_SpotlightDialog_result_multiline {
|
||||
align-items: start;
|
||||
align-items: flex-start;
|
||||
|
||||
.mx_AccessibleButton {
|
||||
padding: $spacing-4 $spacing-20;
|
||||
|
|
|
@ -65,7 +65,7 @@ limitations under the License.
|
|||
.mx_UseCaseSelection_skip {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-self: start;
|
||||
align-self: flex-start;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -16,7 +16,7 @@ limitations under the License.
|
|||
|
||||
.mx_FormattingButtons {
|
||||
display: flex;
|
||||
justify-content: start;
|
||||
justify-content: flex-start;
|
||||
|
||||
.mx_FormattingButtons_Button {
|
||||
--size: 28px;
|
||||
|
|
|
@ -14,22 +14,26 @@ See the License for the specific language governing permissions and
|
|||
limitations under the License.
|
||||
*/
|
||||
|
||||
.mx_VoiceBroadcastRecordingPip {
|
||||
background-color: $system;
|
||||
.mx_VoiceBroadcastBody {
|
||||
background-color: $quinary-content;
|
||||
border-radius: 8px;
|
||||
box-shadow: 0 2px 8px 0 #0000004a;
|
||||
display: inline-block;
|
||||
padding: $spacing-12;
|
||||
}
|
||||
|
||||
.mx_VoiceBroadcastRecordingPip_divider {
|
||||
.mx_VoiceBroadcastBody--pip {
|
||||
background-color: $system;
|
||||
box-shadow: 0 2px 8px 0 #0000004a;
|
||||
}
|
||||
|
||||
.mx_VoiceBroadcastBody_divider {
|
||||
background-color: $quinary-content;
|
||||
border: 0;
|
||||
height: 1px;
|
||||
margin: $spacing-12 0;
|
||||
}
|
||||
|
||||
.mx_VoiceBroadcastRecordingPip_controls {
|
||||
.mx_VoiceBroadcastBody_controls {
|
||||
display: flex;
|
||||
justify-content: space-around;
|
||||
}
|
|
@ -24,13 +24,6 @@ import { ILoginParams, LoginFlow } from "matrix-js-sdk/src/@types/auth";
|
|||
import { IMatrixClientCreds } from "./MatrixClientPeg";
|
||||
import SecurityCustomisations from "./customisations/Security";
|
||||
|
||||
export {
|
||||
IdentityProviderBrand,
|
||||
IIdentityProvider,
|
||||
ISSOFlow,
|
||||
LoginFlow,
|
||||
} from "matrix-js-sdk/src/@types/auth";
|
||||
|
||||
interface ILoginOptions {
|
||||
defaultDeviceDisplayName?: string;
|
||||
}
|
||||
|
|
|
@ -435,7 +435,16 @@ export const Notifier = {
|
|||
if (actions?.notify) {
|
||||
this._performCustomEventHandling(ev);
|
||||
|
||||
if (SdkContextClass.instance.roomViewStore.getRoomId() === room.roomId &&
|
||||
const store = SdkContextClass.instance.roomViewStore;
|
||||
const isViewingRoom = store.getRoomId() === room.roomId;
|
||||
const threadId: string | undefined = ev.getId() !== ev.threadRootId
|
||||
? ev.threadRootId
|
||||
: undefined;
|
||||
const isViewingThread = store.getThreadId() === threadId;
|
||||
|
||||
const isViewingEventTimeline = isViewingRoom && (!threadId || isViewingThread);
|
||||
|
||||
if (isViewingEventTimeline &&
|
||||
UserActivity.sharedInstance().userActiveRecently() &&
|
||||
!Modal.hasDialogs()
|
||||
) {
|
||||
|
|
|
@ -63,6 +63,15 @@ const DEFAULT_ROOM_SUBSCRIPTION_INFO = {
|
|||
required_state: [
|
||||
["*", "*"], // all events
|
||||
],
|
||||
include_old_rooms: {
|
||||
timeline_limit: 0,
|
||||
required_state: [ // state needed to handle space navigation and tombstone chains
|
||||
[EventType.RoomCreate, ""],
|
||||
[EventType.RoomTombstone, ""],
|
||||
[EventType.SpaceChild, "*"],
|
||||
[EventType.SpaceParent, "*"],
|
||||
],
|
||||
},
|
||||
};
|
||||
|
||||
export type PartialSlidingSyncRequest = {
|
||||
|
@ -121,6 +130,16 @@ export class SlidingSyncManager {
|
|||
[EventType.SpaceParent, "*"], // all space parents
|
||||
[EventType.RoomMember, this.client.getUserId()!], // lets the client calculate that we are in fact in the room
|
||||
],
|
||||
include_old_rooms: {
|
||||
timeline_limit: 0,
|
||||
required_state: [
|
||||
[EventType.RoomCreate, ""],
|
||||
[EventType.RoomTombstone, ""], // lets JS SDK hide rooms which are dead
|
||||
[EventType.SpaceChild, "*"], // all space children
|
||||
[EventType.SpaceParent, "*"], // all space parents
|
||||
[EventType.RoomMember, this.client.getUserId()!], // lets the client calculate that we are in fact in the room
|
||||
],
|
||||
},
|
||||
filters: {
|
||||
room_types: ["m.space"],
|
||||
},
|
||||
|
@ -176,7 +195,7 @@ export class SlidingSyncManager {
|
|||
list = {
|
||||
ranges: [[0, 20]],
|
||||
sort: [
|
||||
"by_highlight_count", "by_notification_count", "by_recency",
|
||||
"by_notification_level", "by_recency",
|
||||
],
|
||||
timeline_limit: 1, // most recent message display: though this seems to only be needed for favourites?
|
||||
required_state: [
|
||||
|
@ -187,6 +206,16 @@ export class SlidingSyncManager {
|
|||
[EventType.RoomCreate, ""], // for isSpaceRoom checks
|
||||
[EventType.RoomMember, this.client.getUserId()], // lets the client calculate that we are in fact in the room
|
||||
],
|
||||
include_old_rooms: {
|
||||
timeline_limit: 0,
|
||||
required_state: [
|
||||
[EventType.RoomCreate, ""],
|
||||
[EventType.RoomTombstone, ""], // lets JS SDK hide rooms which are dead
|
||||
[EventType.SpaceChild, "*"], // all space children
|
||||
[EventType.SpaceParent, "*"], // all space parents
|
||||
[EventType.RoomMember, this.client.getUserId()!], // lets the client calculate that we are in fact in the room
|
||||
],
|
||||
},
|
||||
};
|
||||
list = Object.assign(list, updateArgs);
|
||||
} else {
|
||||
|
|
|
@ -835,6 +835,13 @@ export default class MessagePanel extends React.Component<IProps, IState> {
|
|||
: room;
|
||||
|
||||
const receipts: IReadReceiptProps[] = [];
|
||||
|
||||
if (!receiptDestination) {
|
||||
logger.debug("Discarding request, could not find the receiptDestination for event: "
|
||||
+ this.context.threadId);
|
||||
return receipts;
|
||||
}
|
||||
|
||||
receiptDestination.getReceiptsForEvent(event).forEach((r) => {
|
||||
if (
|
||||
!r.userId ||
|
||||
|
|
|
@ -55,6 +55,7 @@ import Spinner from "../views/elements/Spinner";
|
|||
import { ComposerInsertPayload, ComposerType } from "../../dispatcher/payloads/ComposerInsertPayload";
|
||||
import Heading from '../views/typography/Heading';
|
||||
import { SdkContextClass } from '../../contexts/SDKContext';
|
||||
import { ThreadPayload } from '../../dispatcher/payloads/ThreadPayload';
|
||||
|
||||
interface IProps {
|
||||
room: Room;
|
||||
|
@ -132,6 +133,11 @@ export default class ThreadView extends React.Component<IProps, IState> {
|
|||
metricsTrigger: undefined, // room doesn't change
|
||||
});
|
||||
}
|
||||
|
||||
dis.dispatch<ThreadPayload>({
|
||||
action: Action.ViewThread,
|
||||
thread_id: null,
|
||||
});
|
||||
}
|
||||
|
||||
public componentDidUpdate(prevProps) {
|
||||
|
@ -225,6 +231,10 @@ export default class ThreadView extends React.Component<IProps, IState> {
|
|||
};
|
||||
|
||||
private async postThreadUpdate(thread: Thread): Promise<void> {
|
||||
dis.dispatch<ThreadPayload>({
|
||||
action: Action.ViewThread,
|
||||
thread_id: thread.id,
|
||||
});
|
||||
thread.emit(ThreadEvent.ViewThread);
|
||||
await thread.fetchInitialEvents();
|
||||
this.updateThreadRelation();
|
||||
|
|
|
@ -18,9 +18,10 @@ import React, { ReactNode } from 'react';
|
|||
import { ConnectionError, MatrixError } from "matrix-js-sdk/src/http-api";
|
||||
import classNames from "classnames";
|
||||
import { logger } from "matrix-js-sdk/src/logger";
|
||||
import { ISSOFlow, LoginFlow } from "matrix-js-sdk/src/@types/auth";
|
||||
|
||||
import { _t, _td } from '../../../languageHandler';
|
||||
import Login, { ISSOFlow, LoginFlow } from '../../../Login';
|
||||
import Login from '../../../Login';
|
||||
import SdkConfig from '../../../SdkConfig';
|
||||
import { messageForResourceLimitError } from '../../../utils/ErrorUtils';
|
||||
import AutoDiscoveryUtils from "../../../utils/AutoDiscoveryUtils";
|
||||
|
|
|
@ -19,6 +19,7 @@ import React, { Fragment, ReactNode } from 'react';
|
|||
import { MatrixClient } from "matrix-js-sdk/src/client";
|
||||
import classNames from "classnames";
|
||||
import { logger } from "matrix-js-sdk/src/logger";
|
||||
import { ISSOFlow } from "matrix-js-sdk/src/@types/auth";
|
||||
|
||||
import { _t, _td } from '../../../languageHandler';
|
||||
import { messageForResourceLimitError } from '../../../utils/ErrorUtils';
|
||||
|
@ -26,7 +27,7 @@ import AutoDiscoveryUtils from "../../../utils/AutoDiscoveryUtils";
|
|||
import * as Lifecycle from '../../../Lifecycle';
|
||||
import { IMatrixClientCreds, MatrixClientPeg } from "../../../MatrixClientPeg";
|
||||
import AuthPage from "../../views/auth/AuthPage";
|
||||
import Login, { ISSOFlow } from "../../../Login";
|
||||
import Login from "../../../Login";
|
||||
import dis from "../../../dispatcher/dispatcher";
|
||||
import SSOButtons from "../../views/elements/SSOButtons";
|
||||
import ServerPicker from '../../views/elements/ServerPicker';
|
||||
|
|
|
@ -17,13 +17,14 @@ limitations under the License.
|
|||
import React from 'react';
|
||||
import { logger } from "matrix-js-sdk/src/logger";
|
||||
import { Optional } from "matrix-events-sdk";
|
||||
import { ISSOFlow, LoginFlow } from "matrix-js-sdk/src/@types/auth";
|
||||
|
||||
import { _t } from '../../../languageHandler';
|
||||
import dis from '../../../dispatcher/dispatcher';
|
||||
import * as Lifecycle from '../../../Lifecycle';
|
||||
import Modal from '../../../Modal';
|
||||
import { MatrixClientPeg } from "../../../MatrixClientPeg";
|
||||
import { ISSOFlow, LoginFlow, sendLoginRequest } from "../../../Login";
|
||||
import { sendLoginRequest } from "../../../Login";
|
||||
import AuthPage from "../../views/auth/AuthPage";
|
||||
import { SSO_HOMESERVER_URL_KEY, SSO_ID_SERVER_URL_KEY } from "../../../BasePlatform";
|
||||
import SSOButtons from "../../views/elements/SSOButtons";
|
||||
|
|
|
@ -75,7 +75,7 @@ type IProps<T extends keyof JSX.IntrinsicElements> = DynamicHtmlElementProps<T>
|
|||
onClick: ((e: ButtonEvent) => void | Promise<void>) | null;
|
||||
};
|
||||
|
||||
interface IAccessibleButtonProps extends React.InputHTMLAttributes<Element> {
|
||||
export interface IAccessibleButtonProps extends React.InputHTMLAttributes<Element> {
|
||||
ref?: React.Ref<Element>;
|
||||
}
|
||||
|
||||
|
|
56
src/components/views/elements/LearnMore.tsx
Normal file
56
src/components/views/elements/LearnMore.tsx
Normal file
|
@ -0,0 +1,56 @@
|
|||
/*
|
||||
Copyright 2022 The Matrix.org Foundation C.I.C.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
import React from 'react';
|
||||
|
||||
import { _t } from '../../../languageHandler';
|
||||
import Modal from '../../../Modal';
|
||||
import InfoDialog from '../dialogs/InfoDialog';
|
||||
import AccessibleButton, { IAccessibleButtonProps } from './AccessibleButton';
|
||||
|
||||
interface Props extends IAccessibleButtonProps {
|
||||
title: string;
|
||||
description: string | React.ReactNode;
|
||||
}
|
||||
|
||||
const LearnMore: React.FC<Props> = ({
|
||||
title,
|
||||
description,
|
||||
...rest
|
||||
}) => {
|
||||
const onClick = () => {
|
||||
Modal.createDialog(
|
||||
InfoDialog,
|
||||
{
|
||||
title,
|
||||
description,
|
||||
button: _t('Got it'),
|
||||
hasCloseButton: true,
|
||||
},
|
||||
);
|
||||
};
|
||||
|
||||
return <AccessibleButton
|
||||
{...rest}
|
||||
kind='link_inline'
|
||||
onClick={onClick}
|
||||
className='mx_LearnMore_button'
|
||||
>
|
||||
{ _t('Learn more') }
|
||||
</AccessibleButton>;
|
||||
};
|
||||
|
||||
export default LearnMore;
|
|
@ -19,11 +19,11 @@ import { chunk } from "lodash";
|
|||
import classNames from "classnames";
|
||||
import { MatrixClient } from "matrix-js-sdk/src/client";
|
||||
import { Signup } from "@matrix-org/analytics-events/types/typescript/Signup";
|
||||
import { IdentityProviderBrand, IIdentityProvider, ISSOFlow } from "matrix-js-sdk/src/@types/auth";
|
||||
|
||||
import PlatformPeg from "../../../PlatformPeg";
|
||||
import AccessibleButton from "./AccessibleButton";
|
||||
import { _t } from "../../../languageHandler";
|
||||
import { IdentityProviderBrand, IIdentityProvider, ISSOFlow } from "../../../Login";
|
||||
import AccessibleTooltipButton from "./AccessibleTooltipButton";
|
||||
import { mediaFromMxc } from "../../../customisations/Media";
|
||||
import { PosthogAnalytics } from "../../../PosthogAnalytics";
|
||||
|
|
|
@ -111,7 +111,12 @@ export default class NotificationBadge extends React.PureComponent<XOR<IProps, I
|
|||
|
||||
public render(): React.ReactElement {
|
||||
/* eslint @typescript-eslint/no-unused-vars: ["error", { "ignoreRestSiblings": true }] */
|
||||
const { notification, showUnsentTooltip, onClick } = this.props;
|
||||
const { notification, showUnsentTooltip, forceCount, onClick } = this.props;
|
||||
|
||||
if (notification.isIdle) return null;
|
||||
if (forceCount) {
|
||||
if (!notification.hasUnreadCount) return null; // Can't render a badge
|
||||
}
|
||||
|
||||
let label: string;
|
||||
let tooltip: JSX.Element;
|
||||
|
|
|
@ -570,8 +570,7 @@ export default class RoomSublist extends React.Component<IProps, IState> {
|
|||
const slidingList = SlidingSyncManager.instance.slidingSync.getList(slidingSyncIndex);
|
||||
isAlphabetical = slidingList.sort[0] === "by_name";
|
||||
isUnreadFirst = (
|
||||
slidingList.sort[0] === "by_highlight_count" ||
|
||||
slidingList.sort[0] === "by_notification_count"
|
||||
slidingList.sort[0] === "by_notification_level"
|
||||
);
|
||||
}
|
||||
|
||||
|
|
|
@ -38,6 +38,7 @@ import {
|
|||
import { DevicesState } from './useOwnDevices';
|
||||
import FilteredDeviceListHeader from './FilteredDeviceListHeader';
|
||||
import Spinner from '../../elements/Spinner';
|
||||
import LearnMore from '../../elements/LearnMore';
|
||||
|
||||
interface Props {
|
||||
devices: DevicesDictionary;
|
||||
|
@ -73,48 +74,88 @@ const getFilteredSortedDevices = (devices: DevicesDictionary, filter?: DeviceSec
|
|||
const ALL_FILTER_ID = 'ALL';
|
||||
type DeviceFilterKey = DeviceSecurityVariation | typeof ALL_FILTER_ID;
|
||||
|
||||
const securityCardContent: Record<DeviceSecurityVariation, {
|
||||
title: string;
|
||||
description: string;
|
||||
learnMoreDescription: React.ReactNode | string;
|
||||
}> = {
|
||||
[DeviceSecurityVariation.Verified]: {
|
||||
title: _t('Verified sessions'),
|
||||
description: _t('For best security, sign out from any session that you don\'t recognize or use anymore.'),
|
||||
learnMoreDescription: <>
|
||||
<p>{ _t('Verified sessions have logged in with your credentials and then been verified, either using your secure passphrase or by cross-verifying.') }
|
||||
</p>
|
||||
<p>
|
||||
{ _t(
|
||||
`This means they hold encryption keys for your previous messages, ` +
|
||||
`and confirm to other users you are communicating with that these sessions are really you.`,
|
||||
)
|
||||
}
|
||||
</p>
|
||||
</>,
|
||||
},
|
||||
[DeviceSecurityVariation.Unverified]: {
|
||||
title: _t('Unverified sessions'),
|
||||
description: _t(
|
||||
`Verify your sessions for enhanced secure messaging or ` +
|
||||
`sign out from those you don't recognize or use anymore.`,
|
||||
),
|
||||
learnMoreDescription: <>
|
||||
<p>{ _t('Unverified sessions are sessions that have logged in with your credentials but have not been cross-verified.') }
|
||||
</p>
|
||||
<p>
|
||||
{ _t(
|
||||
`You should make especially certain that you recognise these sessions ` +
|
||||
`as they could represent an unauthorised use of your account.`,
|
||||
)
|
||||
}
|
||||
</p>
|
||||
</>,
|
||||
},
|
||||
[DeviceSecurityVariation.Inactive]: {
|
||||
title: _t('Inactive sessions'),
|
||||
description: _t(
|
||||
`Consider signing out from old sessions ` +
|
||||
`(%(inactiveAgeDays)s days or older) you don't use anymore.`,
|
||||
{ inactiveAgeDays: INACTIVE_DEVICE_AGE_DAYS },
|
||||
),
|
||||
learnMoreDescription: <>
|
||||
<p>{ _t('Inactive sessions are sessions you have not used in some time, but they continue to receive encryption keys.') }
|
||||
</p>
|
||||
<p>
|
||||
{ _t(
|
||||
`Removing inactive sessions improves security and performance, ` +
|
||||
`and makes it easier for you to identify if a new session is suspicious.`,
|
||||
)
|
||||
}
|
||||
</p>
|
||||
</>,
|
||||
},
|
||||
};
|
||||
|
||||
const isSecurityVariation = (filter?: DeviceFilterKey): filter is DeviceSecurityVariation =>
|
||||
Object.values<string>(DeviceSecurityVariation).includes(filter);
|
||||
|
||||
const FilterSecurityCard: React.FC<{ filter?: DeviceFilterKey }> = ({ filter }) => {
|
||||
switch (filter) {
|
||||
case DeviceSecurityVariation.Verified:
|
||||
return <div className='mx_FilteredDeviceList_securityCard'>
|
||||
<DeviceSecurityCard
|
||||
variation={DeviceSecurityVariation.Verified}
|
||||
heading={_t('Verified sessions')}
|
||||
description={_t(
|
||||
`For best security, sign out from any session` +
|
||||
` that you don't recognize or use anymore.`,
|
||||
)}
|
||||
/>
|
||||
</div>
|
||||
;
|
||||
case DeviceSecurityVariation.Unverified:
|
||||
return <div className='mx_FilteredDeviceList_securityCard'>
|
||||
<DeviceSecurityCard
|
||||
variation={DeviceSecurityVariation.Unverified}
|
||||
heading={_t('Unverified sessions')}
|
||||
description={_t(
|
||||
`Verify your sessions for enhanced secure messaging or sign out`
|
||||
+ ` from those you don't recognize or use anymore.`,
|
||||
)}
|
||||
/>
|
||||
</div>
|
||||
;
|
||||
case DeviceSecurityVariation.Inactive:
|
||||
return <div className='mx_FilteredDeviceList_securityCard'>
|
||||
<DeviceSecurityCard
|
||||
variation={DeviceSecurityVariation.Inactive}
|
||||
heading={_t('Inactive sessions')}
|
||||
description={_t(
|
||||
`Consider signing out from old sessions ` +
|
||||
`(%(inactiveAgeDays)s days or older) you don't use anymore`,
|
||||
{ inactiveAgeDays: INACTIVE_DEVICE_AGE_DAYS },
|
||||
)}
|
||||
/>
|
||||
</div>
|
||||
;
|
||||
default:
|
||||
return null;
|
||||
if (isSecurityVariation(filter)) {
|
||||
const { title, description, learnMoreDescription } = securityCardContent[filter];
|
||||
return <div className='mx_FilteredDeviceList_securityCard'>
|
||||
<DeviceSecurityCard
|
||||
variation={filter}
|
||||
heading={title}
|
||||
description={<span>
|
||||
{ description }
|
||||
<LearnMore
|
||||
title={title}
|
||||
description={learnMoreDescription}
|
||||
/>
|
||||
</span>}
|
||||
/>
|
||||
</div>
|
||||
;
|
||||
}
|
||||
|
||||
return null;
|
||||
};
|
||||
|
||||
const getNoResultsMessage = (filter?: DeviceSecurityVariation): string => {
|
||||
|
|
|
@ -116,6 +116,11 @@ export enum Action {
|
|||
*/
|
||||
ViewRoom = "view_room",
|
||||
|
||||
/**
|
||||
* Changes thread based on payload parameters. Should be used with ThreadPayload.
|
||||
*/
|
||||
ViewThread = "view_thread",
|
||||
|
||||
/**
|
||||
* Changes room based on room list order and payload parameters. Should be used with ViewRoomDeltaPayload.
|
||||
*/
|
||||
|
|
|
@ -14,16 +14,13 @@ See the License for the specific language governing permissions and
|
|||
limitations under the License.
|
||||
*/
|
||||
|
||||
.mx_VoiceBroadcastRecordingBody {
|
||||
align-items: flex-start;
|
||||
background-color: $quinary-content;
|
||||
border-radius: 8px;
|
||||
display: inline-flex;
|
||||
gap: $spacing-8;
|
||||
padding: 12px;
|
||||
}
|
||||
import { ActionPayload } from "../payloads";
|
||||
import { Action } from "../actions";
|
||||
|
||||
.mx_VoiceBroadcastRecordingBody_title {
|
||||
font-size: $font-12px;
|
||||
font-weight: $font-semi-bold;
|
||||
/* eslint-disable camelcase */
|
||||
export interface ThreadPayload extends Pick<ActionPayload, "action"> {
|
||||
action: Action.ViewThread;
|
||||
|
||||
thread_id: string | null;
|
||||
}
|
||||
/* eslint-enable camelcase */
|
|
@ -52,7 +52,6 @@ export const useSlidingSyncRoomSearch = () => {
|
|||
ranges: [[0, limit]],
|
||||
filters: {
|
||||
room_name_like: term,
|
||||
is_tombstoned: false,
|
||||
},
|
||||
});
|
||||
const rooms = [];
|
||||
|
|
|
@ -1778,10 +1778,16 @@
|
|||
"Verify session": "Verify session",
|
||||
"Verified sessions": "Verified sessions",
|
||||
"For best security, sign out from any session that you don't recognize or use anymore.": "For best security, sign out from any session that you don't recognize or use anymore.",
|
||||
"Verified sessions have logged in with your credentials and then been verified, either using your secure passphrase or by cross-verifying.": "Verified sessions have logged in with your credentials and then been verified, either using your secure passphrase or by cross-verifying.",
|
||||
"This means they hold encryption keys for your previous messages, and confirm to other users you are communicating with that these sessions are really you.": "This means they hold encryption keys for your previous messages, and confirm to other users you are communicating with that these sessions are really you.",
|
||||
"Unverified sessions": "Unverified sessions",
|
||||
"Verify your sessions for enhanced secure messaging or sign out from those you don't recognize or use anymore.": "Verify your sessions for enhanced secure messaging or sign out from those you don't recognize or use anymore.",
|
||||
"Unverified sessions are sessions that have logged in with your credentials but have not been cross-verified.": "Unverified sessions are sessions that have logged in with your credentials but have not been cross-verified.",
|
||||
"You should make especially certain that you recognise these sessions as they could represent an unauthorised use of your account.": "You should make especially certain that you recognise these sessions as they could represent an unauthorised use of your account.",
|
||||
"Inactive sessions": "Inactive sessions",
|
||||
"Consider signing out from old sessions (%(inactiveAgeDays)s days or older) you don't use anymore": "Consider signing out from old sessions (%(inactiveAgeDays)s days or older) you don't use anymore",
|
||||
"Consider signing out from old sessions (%(inactiveAgeDays)s days or older) you don't use anymore.": "Consider signing out from old sessions (%(inactiveAgeDays)s days or older) you don't use anymore.",
|
||||
"Inactive sessions are sessions you have not used in some time, but they continue to receive encryption keys.": "Inactive sessions are sessions you have not used in some time, but they continue to receive encryption keys.",
|
||||
"Removing inactive sessions improves security and performance, and makes it easier for you to identify if a new session is suspicious.": "Removing inactive sessions improves security and performance, and makes it easier for you to identify if a new session is suspicious.",
|
||||
"No verified sessions found.": "No verified sessions found.",
|
||||
"No unverified sessions found.": "No unverified sessions found.",
|
||||
"No inactive sessions found.": "No inactive sessions found.",
|
||||
|
@ -1801,6 +1807,7 @@
|
|||
"Security recommendations": "Security recommendations",
|
||||
"Improve your account security by following these recommendations": "Improve your account security by following these recommendations",
|
||||
"View all": "View all",
|
||||
"Consider signing out from old sessions (%(inactiveAgeDays)s days or older) you don't use anymore": "Consider signing out from old sessions (%(inactiveAgeDays)s days or older) you don't use anymore",
|
||||
"Failed to set pusher state": "Failed to set pusher state",
|
||||
"Unable to remove contact information": "Unable to remove contact information",
|
||||
"Remove %(email)s?": "Remove %(email)s?",
|
||||
|
|
|
@ -50,6 +50,7 @@ import { awaitRoomDownSync } from "../utils/RoomUpgrade";
|
|||
import { UPDATE_EVENT } from "./AsyncStore";
|
||||
import { SdkContextClass } from "../contexts/SDKContext";
|
||||
import { CallStore } from "./CallStore";
|
||||
import { ThreadPayload } from "../dispatcher/payloads/ThreadPayload";
|
||||
|
||||
const NUM_JOIN_RETRY = 5;
|
||||
|
||||
|
@ -66,6 +67,10 @@ interface State {
|
|||
* The ID of the room currently being viewed
|
||||
*/
|
||||
roomId: string | null;
|
||||
/**
|
||||
* The ID of the thread currently being viewed
|
||||
*/
|
||||
threadId: string | null;
|
||||
/**
|
||||
* The ID of the room being subscribed to (in Sliding Sync)
|
||||
*/
|
||||
|
@ -109,6 +114,7 @@ const INITIAL_STATE: State = {
|
|||
joining: false,
|
||||
joinError: null,
|
||||
roomId: null,
|
||||
threadId: null,
|
||||
subscribingRoomId: null,
|
||||
initialEventId: null,
|
||||
initialEventPixelOffset: null,
|
||||
|
@ -200,6 +206,9 @@ export class RoomViewStore extends EventEmitter {
|
|||
case Action.ViewRoom:
|
||||
this.viewRoom(payload);
|
||||
break;
|
||||
case Action.ViewThread:
|
||||
this.viewThread(payload);
|
||||
break;
|
||||
// for these events blank out the roomId as we are no longer in the RoomView
|
||||
case 'view_welcome_page':
|
||||
case Action.ViewHomePage:
|
||||
|
@ -430,6 +439,12 @@ export class RoomViewStore extends EventEmitter {
|
|||
}
|
||||
}
|
||||
|
||||
private viewThread(payload: ThreadPayload): void {
|
||||
this.setState({
|
||||
threadId: payload.thread_id,
|
||||
});
|
||||
}
|
||||
|
||||
private viewRoomError(payload: ViewRoomErrorPayload): void {
|
||||
this.setState({
|
||||
roomId: payload.room_id,
|
||||
|
@ -550,6 +565,10 @@ export class RoomViewStore extends EventEmitter {
|
|||
return this.state.roomId;
|
||||
}
|
||||
|
||||
public getThreadId(): Optional<string> {
|
||||
return this.state.threadId;
|
||||
}
|
||||
|
||||
// The event to scroll to when the room is first viewed
|
||||
public getInitialEventId(): Optional<string> {
|
||||
return this.state.initialEventId;
|
||||
|
|
|
@ -37,6 +37,9 @@ export class RoomNotificationState extends NotificationState implements IDestroy
|
|||
this.room.on(RoomEvent.Receipt, this.handleReadReceipt);
|
||||
this.room.on(RoomEvent.MyMembership, this.handleMembershipUpdate);
|
||||
this.room.on(RoomEvent.LocalEchoUpdated, this.handleLocalEchoUpdated);
|
||||
this.room.on(RoomEvent.Timeline, this.handleRoomEventUpdate);
|
||||
this.room.on(RoomEvent.Redaction, this.handleRoomEventUpdate);
|
||||
|
||||
this.room.on(RoomEvent.UnreadNotifications, this.handleNotificationCountUpdate); // for server-sent counts
|
||||
if (cli.canSupport.get(Feature.ThreadUnreadNotifications) === ServerSupport.Unsupported) {
|
||||
this.threadsState?.on(NotificationStateEvents.Update, this.handleThreadsUpdate);
|
||||
|
@ -56,6 +59,8 @@ export class RoomNotificationState extends NotificationState implements IDestroy
|
|||
this.room.removeListener(RoomEvent.Receipt, this.handleReadReceipt);
|
||||
this.room.removeListener(RoomEvent.MyMembership, this.handleMembershipUpdate);
|
||||
this.room.removeListener(RoomEvent.LocalEchoUpdated, this.handleLocalEchoUpdated);
|
||||
this.room.removeListener(RoomEvent.Timeline, this.handleRoomEventUpdate);
|
||||
this.room.removeListener(RoomEvent.Redaction, this.handleRoomEventUpdate);
|
||||
if (cli.canSupport.get(Feature.ThreadUnreadNotifications) === ServerSupport.Unsupported) {
|
||||
this.room.removeListener(RoomEvent.UnreadNotifications, this.handleNotificationCountUpdate);
|
||||
} else if (this.threadsState) {
|
||||
|
@ -93,6 +98,11 @@ export class RoomNotificationState extends NotificationState implements IDestroy
|
|||
this.updateNotificationState();
|
||||
};
|
||||
|
||||
private handleRoomEventUpdate = (event: MatrixEvent, room: Room | null) => {
|
||||
if (room?.roomId !== this.room.roomId) return; // ignore - not for us or notifications timeline
|
||||
this.updateNotificationState();
|
||||
};
|
||||
|
||||
private handleAccountDataUpdate = (ev: MatrixEvent) => {
|
||||
if (ev.getType() === "m.push_rules") {
|
||||
this.updateNotificationState();
|
||||
|
|
|
@ -24,7 +24,7 @@ import SettingsStore from "../../settings/SettingsStore";
|
|||
import { DefaultTagID, OrderedDefaultTagIDs, RoomUpdateCause, TagID } from "./models";
|
||||
import { IListOrderingMap, ITagMap, ITagSortingMap, ListAlgorithm, SortAlgorithm } from "./algorithms/models";
|
||||
import { ActionPayload } from "../../dispatcher/payloads";
|
||||
import defaultDispatcher from "../../dispatcher/dispatcher";
|
||||
import defaultDispatcher, { MatrixDispatcher } from "../../dispatcher/dispatcher";
|
||||
import { readReceiptChangeIsFor } from "../../utils/read-receipts";
|
||||
import { FILTER_CHANGED, IFilterCondition } from "./filters/IFilterCondition";
|
||||
import { Algorithm, LIST_UPDATED_EVENT } from "./algorithms/Algorithm";
|
||||
|
@ -65,8 +65,8 @@ export class RoomListStoreClass extends AsyncStoreWithClient<IState> implements
|
|||
this.emit(LISTS_UPDATE_EVENT);
|
||||
});
|
||||
|
||||
constructor() {
|
||||
super(defaultDispatcher);
|
||||
constructor(dis: MatrixDispatcher) {
|
||||
super(dis);
|
||||
this.setMaxListeners(20); // RoomList + LeftPanel + 8xRoomSubList + spares
|
||||
this.algorithm.start();
|
||||
}
|
||||
|
@ -613,11 +613,11 @@ export default class RoomListStore {
|
|||
if (!RoomListStore.internalInstance) {
|
||||
if (SettingsStore.getValue("feature_sliding_sync")) {
|
||||
logger.info("using SlidingRoomListStoreClass");
|
||||
const instance = new SlidingRoomListStoreClass();
|
||||
const instance = new SlidingRoomListStoreClass(defaultDispatcher, SdkContextClass.instance);
|
||||
instance.start();
|
||||
RoomListStore.internalInstance = instance;
|
||||
} else {
|
||||
const instance = new RoomListStoreClass();
|
||||
const instance = new RoomListStoreClass(defaultDispatcher);
|
||||
instance.start();
|
||||
RoomListStore.internalInstance = instance;
|
||||
}
|
||||
|
|
|
@ -21,12 +21,10 @@ import { MSC3575Filter, SlidingSyncEvent } from "matrix-js-sdk/src/sliding-sync"
|
|||
import { RoomUpdateCause, TagID, OrderedDefaultTagIDs, DefaultTagID } from "./models";
|
||||
import { ITagMap, ListAlgorithm, SortAlgorithm } from "./algorithms/models";
|
||||
import { ActionPayload } from "../../dispatcher/payloads";
|
||||
import defaultDispatcher from "../../dispatcher/dispatcher";
|
||||
import { MatrixDispatcher } from "../../dispatcher/dispatcher";
|
||||
import { IFilterCondition } from "./filters/IFilterCondition";
|
||||
import { AsyncStoreWithClient } from "../AsyncStoreWithClient";
|
||||
import { RoomListStore as Interface, RoomListStoreEvent } from "./Interface";
|
||||
import { SlidingSyncManager } from "../../SlidingSyncManager";
|
||||
import SpaceStore from "../spaces/SpaceStore";
|
||||
import { MetaSpace, SpaceKey, UPDATE_SELECTED_SPACE } from "../spaces";
|
||||
import { LISTS_LOADING_EVENT } from "./RoomListStore";
|
||||
import { UPDATE_EVENT } from "../AsyncStore";
|
||||
|
@ -38,7 +36,7 @@ interface IState {
|
|||
|
||||
export const SlidingSyncSortToFilter: Record<SortAlgorithm, string[]> = {
|
||||
[SortAlgorithm.Alphabetic]: ["by_name", "by_recency"],
|
||||
[SortAlgorithm.Recent]: ["by_highlight_count", "by_notification_count", "by_recency"],
|
||||
[SortAlgorithm.Recent]: ["by_notification_level", "by_recency"],
|
||||
[SortAlgorithm.Manual]: ["by_recency"],
|
||||
};
|
||||
|
||||
|
@ -48,21 +46,18 @@ const filterConditions: Record<TagID, MSC3575Filter> = {
|
|||
},
|
||||
[DefaultTagID.Favourite]: {
|
||||
tags: ["m.favourite"],
|
||||
is_tombstoned: false,
|
||||
},
|
||||
// TODO https://github.com/vector-im/element-web/issues/23207
|
||||
// DefaultTagID.SavedItems,
|
||||
[DefaultTagID.DM]: {
|
||||
is_dm: true,
|
||||
is_invite: false,
|
||||
is_tombstoned: false,
|
||||
// If a DM has a Favourite & Low Prio tag then it'll be shown in those lists instead
|
||||
not_tags: ["m.favourite", "m.lowpriority"],
|
||||
},
|
||||
[DefaultTagID.Untagged]: {
|
||||
is_dm: false,
|
||||
is_invite: false,
|
||||
is_tombstoned: false,
|
||||
not_room_types: ["m.space"],
|
||||
not_tags: ["m.favourite", "m.lowpriority"],
|
||||
// spaces filter added dynamically
|
||||
|
@ -71,7 +66,6 @@ const filterConditions: Record<TagID, MSC3575Filter> = {
|
|||
tags: ["m.lowpriority"],
|
||||
// If a room has both Favourite & Low Prio tags then it'll be shown under Favourites
|
||||
not_tags: ["m.favourite"],
|
||||
is_tombstoned: false,
|
||||
},
|
||||
// TODO https://github.com/vector-im/element-web/issues/23207
|
||||
// DefaultTagID.ServerNotice,
|
||||
|
@ -87,25 +81,25 @@ export class SlidingRoomListStoreClass extends AsyncStoreWithClient<IState> impl
|
|||
private counts: Record<TagID, number> = {};
|
||||
private stickyRoomId: string | null;
|
||||
|
||||
public constructor() {
|
||||
super(defaultDispatcher);
|
||||
public constructor(dis: MatrixDispatcher, private readonly context: SdkContextClass) {
|
||||
super(dis);
|
||||
this.setMaxListeners(20); // RoomList + LeftPanel + 8xRoomSubList + spares
|
||||
}
|
||||
|
||||
public async setTagSorting(tagId: TagID, sort: SortAlgorithm) {
|
||||
logger.info("SlidingRoomListStore.setTagSorting ", tagId, sort);
|
||||
this.tagIdToSortAlgo[tagId] = sort;
|
||||
const slidingSyncIndex = SlidingSyncManager.instance.getOrAllocateListIndex(tagId);
|
||||
const slidingSyncIndex = this.context.slidingSyncManager.getOrAllocateListIndex(tagId);
|
||||
switch (sort) {
|
||||
case SortAlgorithm.Alphabetic:
|
||||
await SlidingSyncManager.instance.ensureListRegistered(
|
||||
await this.context.slidingSyncManager.ensureListRegistered(
|
||||
slidingSyncIndex, {
|
||||
sort: SlidingSyncSortToFilter[SortAlgorithm.Alphabetic],
|
||||
},
|
||||
);
|
||||
break;
|
||||
case SortAlgorithm.Recent:
|
||||
await SlidingSyncManager.instance.ensureListRegistered(
|
||||
await this.context.slidingSyncManager.ensureListRegistered(
|
||||
slidingSyncIndex, {
|
||||
sort: SlidingSyncSortToFilter[SortAlgorithm.Recent],
|
||||
},
|
||||
|
@ -174,10 +168,13 @@ export class SlidingRoomListStoreClass extends AsyncStoreWithClient<IState> impl
|
|||
// check all lists for each tag we know about and see if the room is there
|
||||
const tags: TagID[] = [];
|
||||
for (const tagId in this.tagIdToSortAlgo) {
|
||||
const index = SlidingSyncManager.instance.getOrAllocateListIndex(tagId);
|
||||
const { roomIndexToRoomId } = SlidingSyncManager.instance.slidingSync.getListData(index);
|
||||
for (const roomIndex in roomIndexToRoomId) {
|
||||
const roomId = roomIndexToRoomId[roomIndex];
|
||||
const index = this.context.slidingSyncManager.getOrAllocateListIndex(tagId);
|
||||
const listData = this.context.slidingSyncManager.slidingSync.getListData(index);
|
||||
if (!listData) {
|
||||
continue;
|
||||
}
|
||||
for (const roomIndex in listData.roomIndexToRoomId) {
|
||||
const roomId = listData.roomIndexToRoomId[roomIndex];
|
||||
if (roomId === room.roomId) {
|
||||
tags.push(tagId);
|
||||
break;
|
||||
|
@ -207,7 +204,7 @@ export class SlidingRoomListStoreClass extends AsyncStoreWithClient<IState> impl
|
|||
|
||||
// this room will not move due to it being viewed: it is sticky. This can be null to indicate
|
||||
// no sticky room if you aren't viewing a room.
|
||||
this.stickyRoomId = SdkContextClass.instance.roomViewStore.getRoomId();
|
||||
this.stickyRoomId = this.context.roomViewStore.getRoomId();
|
||||
let stickyRoomNewIndex = -1;
|
||||
const stickyRoomOldIndex = (tagMap[tagId] || []).findIndex((room) => {
|
||||
return room.roomId === this.stickyRoomId;
|
||||
|
@ -264,7 +261,7 @@ export class SlidingRoomListStoreClass extends AsyncStoreWithClient<IState> impl
|
|||
}
|
||||
|
||||
private onSlidingSyncListUpdate(listIndex: number, joinCount: number, roomIndexToRoomId: Record<number, string>) {
|
||||
const tagId = SlidingSyncManager.instance.listIdForIndex(listIndex);
|
||||
const tagId = this.context.slidingSyncManager.listIdForIndex(listIndex);
|
||||
this.counts[tagId]= joinCount;
|
||||
this.refreshOrderedLists(tagId, roomIndexToRoomId);
|
||||
// let the UI update
|
||||
|
@ -273,7 +270,7 @@ export class SlidingRoomListStoreClass extends AsyncStoreWithClient<IState> impl
|
|||
|
||||
private onRoomViewStoreUpdated() {
|
||||
// we only care about this to know when the user has clicked on a room to set the stickiness value
|
||||
if (SdkContextClass.instance.roomViewStore.getRoomId() === this.stickyRoomId) {
|
||||
if (this.context.roomViewStore.getRoomId() === this.stickyRoomId) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -296,14 +293,17 @@ export class SlidingRoomListStoreClass extends AsyncStoreWithClient<IState> impl
|
|||
if (room) {
|
||||
// resort it based on the slidingSync view of the list. This may cause this old sticky
|
||||
// room to cease to exist.
|
||||
const index = SlidingSyncManager.instance.getOrAllocateListIndex(tagId);
|
||||
const { roomIndexToRoomId } = SlidingSyncManager.instance.slidingSync.getListData(index);
|
||||
this.refreshOrderedLists(tagId, roomIndexToRoomId);
|
||||
const index = this.context.slidingSyncManager.getOrAllocateListIndex(tagId);
|
||||
const listData = this.context.slidingSyncManager.slidingSync.getListData(index);
|
||||
if (!listData) {
|
||||
continue;
|
||||
}
|
||||
this.refreshOrderedLists(tagId, listData.roomIndexToRoomId);
|
||||
hasUpdatedAnyList = true;
|
||||
}
|
||||
}
|
||||
// in the event we didn't call refreshOrderedLists, it helps to still remember the sticky room ID.
|
||||
this.stickyRoomId = SdkContextClass.instance.roomViewStore.getRoomId();
|
||||
this.stickyRoomId = this.context.roomViewStore.getRoomId();
|
||||
|
||||
if (hasUpdatedAnyList) {
|
||||
this.emit(LISTS_UPDATE_EVENT);
|
||||
|
@ -313,11 +313,11 @@ export class SlidingRoomListStoreClass extends AsyncStoreWithClient<IState> impl
|
|||
protected async onReady(): Promise<any> {
|
||||
logger.info("SlidingRoomListStore.onReady");
|
||||
// permanent listeners: never get destroyed. Could be an issue if we want to test this in isolation.
|
||||
SlidingSyncManager.instance.slidingSync.on(SlidingSyncEvent.List, this.onSlidingSyncListUpdate.bind(this));
|
||||
SdkContextClass.instance.roomViewStore.addListener(UPDATE_EVENT, this.onRoomViewStoreUpdated.bind(this));
|
||||
SpaceStore.instance.on(UPDATE_SELECTED_SPACE, this.onSelectedSpaceUpdated.bind(this));
|
||||
if (SpaceStore.instance.activeSpace) {
|
||||
this.onSelectedSpaceUpdated(SpaceStore.instance.activeSpace, false);
|
||||
this.context.slidingSyncManager.slidingSync.on(SlidingSyncEvent.List, this.onSlidingSyncListUpdate.bind(this));
|
||||
this.context.roomViewStore.addListener(UPDATE_EVENT, this.onRoomViewStoreUpdated.bind(this));
|
||||
this.context.spaceStore.on(UPDATE_SELECTED_SPACE, this.onSelectedSpaceUpdated.bind(this));
|
||||
if (this.context.spaceStore.activeSpace) {
|
||||
this.onSelectedSpaceUpdated(this.context.spaceStore.activeSpace, false);
|
||||
}
|
||||
|
||||
// sliding sync has an initial response for spaces. Now request all the lists.
|
||||
|
@ -332,8 +332,8 @@ export class SlidingRoomListStoreClass extends AsyncStoreWithClient<IState> impl
|
|||
const sort = SortAlgorithm.Recent; // default to recency sort, TODO: read from config
|
||||
this.tagIdToSortAlgo[tagId] = sort;
|
||||
this.emit(LISTS_LOADING_EVENT, tagId, true);
|
||||
const index = SlidingSyncManager.instance.getOrAllocateListIndex(tagId);
|
||||
SlidingSyncManager.instance.ensureListRegistered(index, {
|
||||
const index = this.context.slidingSyncManager.getOrAllocateListIndex(tagId);
|
||||
this.context.slidingSyncManager.ensureListRegistered(index, {
|
||||
filters: filter,
|
||||
sort: SlidingSyncSortToFilter[sort],
|
||||
}).then(() => {
|
||||
|
@ -350,9 +350,18 @@ export class SlidingRoomListStoreClass extends AsyncStoreWithClient<IState> impl
|
|||
const oldSpace = filters.spaces?.[0];
|
||||
filters.spaces = (activeSpace && activeSpace != MetaSpace.Home) ? [activeSpace] : undefined;
|
||||
if (oldSpace !== activeSpace) {
|
||||
// include subspaces in this list
|
||||
this.context.spaceStore.traverseSpace(activeSpace, (roomId: string) => {
|
||||
if (roomId === activeSpace) {
|
||||
return;
|
||||
}
|
||||
filters.spaces.push(roomId); // add subspace
|
||||
}, false);
|
||||
|
||||
this.emit(LISTS_LOADING_EVENT, tagId, true);
|
||||
SlidingSyncManager.instance.ensureListRegistered(
|
||||
SlidingSyncManager.instance.getOrAllocateListIndex(tagId),
|
||||
const index = this.context.slidingSyncManager.getOrAllocateListIndex(tagId);
|
||||
this.context.slidingSyncManager.ensureListRegistered(
|
||||
index,
|
||||
{
|
||||
filters: filters,
|
||||
},
|
||||
|
|
|
@ -18,6 +18,7 @@ import { Room } from "matrix-js-sdk/src/models/room";
|
|||
import {
|
||||
ClientWidgetApi,
|
||||
IModalWidgetOpenRequest,
|
||||
IRoomEvent,
|
||||
IStickerActionRequest,
|
||||
IStickyActionRequest,
|
||||
ITemplateParams,
|
||||
|
@ -465,7 +466,7 @@ export class StopGapWidget extends EventEmitter {
|
|||
private onToDeviceEvent = async (ev: MatrixEvent) => {
|
||||
await this.client.decryptEventIfNeeded(ev);
|
||||
if (ev.isDecryptionFailure()) return;
|
||||
await this.messaging.feedToDevice(ev.getEffectiveEvent(), ev.isEncrypted());
|
||||
await this.messaging.feedToDevice(ev.getEffectiveEvent() as IRoomEvent, ev.isEncrypted());
|
||||
};
|
||||
|
||||
private feedEvent(ev: MatrixEvent) {
|
||||
|
@ -509,7 +510,7 @@ export class StopGapWidget extends EventEmitter {
|
|||
this.readUpToMap[ev.getRoomId()] = ev.getId();
|
||||
|
||||
const raw = ev.getEffectiveEvent();
|
||||
this.messaging.feedEvent(raw, this.eventListenerRoomId).catch(e => {
|
||||
this.messaging.feedEvent(raw as IRoomEvent, this.eventListenerRoomId).catch(e => {
|
||||
logger.error("Error sending event to widget: ", e);
|
||||
});
|
||||
}
|
||||
|
|
|
@ -34,7 +34,7 @@ import {
|
|||
} from "matrix-widget-api";
|
||||
import { ClientEvent, ITurnServer as IClientTurnServer } from "matrix-js-sdk/src/client";
|
||||
import { EventType } from "matrix-js-sdk/src/@types/event";
|
||||
import { IContent, IEvent, MatrixEvent } from "matrix-js-sdk/src/models/event";
|
||||
import { IContent, MatrixEvent } from "matrix-js-sdk/src/models/event";
|
||||
import { Room } from "matrix-js-sdk/src/models/room";
|
||||
import { logger } from "matrix-js-sdk/src/logger";
|
||||
import { THREAD_RELATION_TYPE } from "matrix-js-sdk/src/models/thread";
|
||||
|
@ -310,7 +310,7 @@ export class StopGapWidgetDriver extends WidgetDriver {
|
|||
limitPerRoom = limitPerRoom > 0 ? Math.min(limitPerRoom, Number.MAX_SAFE_INTEGER) : Number.MAX_SAFE_INTEGER; // relatively arbitrary
|
||||
|
||||
const rooms = this.pickRooms(roomIds);
|
||||
const allResults: IEvent[] = [];
|
||||
const allResults: IRoomEvent[] = [];
|
||||
for (const room of rooms) {
|
||||
const results: MatrixEvent[] = [];
|
||||
const events = room.getLiveTimeline().getEvents(); // timelines are most recent last
|
||||
|
@ -323,7 +323,7 @@ export class StopGapWidgetDriver extends WidgetDriver {
|
|||
results.push(ev);
|
||||
}
|
||||
|
||||
results.forEach(e => allResults.push(e.getEffectiveEvent()));
|
||||
results.forEach(e => allResults.push(e.getEffectiveEvent() as IRoomEvent));
|
||||
}
|
||||
return allResults;
|
||||
}
|
||||
|
@ -337,7 +337,7 @@ export class StopGapWidgetDriver extends WidgetDriver {
|
|||
limitPerRoom = limitPerRoom > 0 ? Math.min(limitPerRoom, Number.MAX_SAFE_INTEGER) : Number.MAX_SAFE_INTEGER; // relatively arbitrary
|
||||
|
||||
const rooms = this.pickRooms(roomIds);
|
||||
const allResults: IEvent[] = [];
|
||||
const allResults: IRoomEvent[] = [];
|
||||
for (const room of rooms) {
|
||||
const results: MatrixEvent[] = [];
|
||||
const state: Map<string, MatrixEvent> = room.currentState.events.get(eventType);
|
||||
|
@ -350,7 +350,7 @@ export class StopGapWidgetDriver extends WidgetDriver {
|
|||
}
|
||||
}
|
||||
|
||||
results.slice(0, limitPerRoom).forEach(e => allResults.push(e.getEffectiveEvent()));
|
||||
results.slice(0, limitPerRoom).forEach(e => allResults.push(e.getEffectiveEvent() as IRoomEvent));
|
||||
}
|
||||
return allResults;
|
||||
}
|
||||
|
@ -459,7 +459,7 @@ export class StopGapWidgetDriver extends WidgetDriver {
|
|||
});
|
||||
|
||||
return {
|
||||
chunk: events.map(e => e.getEffectiveEvent()),
|
||||
chunk: events.map(e => e.getEffectiveEvent() as IRoomEvent),
|
||||
nextBatch,
|
||||
prevBatch,
|
||||
};
|
||||
|
|
|
@ -74,14 +74,14 @@ export const VoiceBroadcastPlaybackBody: React.FC<VoiceBroadcastPlaybackBodyProp
|
|||
}
|
||||
|
||||
return (
|
||||
<div className="mx_VoiceBroadcastPlaybackBody">
|
||||
<div className="mx_VoiceBroadcastBody">
|
||||
<VoiceBroadcastHeader
|
||||
live={live}
|
||||
sender={sender}
|
||||
room={room}
|
||||
showBroadcast={true}
|
||||
/>
|
||||
<div className="mx_VoiceBroadcastPlaybackBody_controls">
|
||||
<div className="mx_VoiceBroadcastBody_controls">
|
||||
{ control }
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
@ -27,7 +27,7 @@ export const VoiceBroadcastRecordingBody: React.FC<VoiceBroadcastRecordingBodyPr
|
|||
} = useVoiceBroadcastRecording(recording);
|
||||
|
||||
return (
|
||||
<div className="mx_VoiceBroadcastRecordingBody">
|
||||
<div className="mx_VoiceBroadcastBody">
|
||||
<VoiceBroadcastHeader
|
||||
live={live}
|
||||
sender={sender}
|
||||
|
|
|
@ -52,15 +52,15 @@ export const VoiceBroadcastRecordingPip: React.FC<VoiceBroadcastRecordingPipProp
|
|||
: <VoiceBroadcastControl onClick={toggleRecording} icon={PauseIcon} label={_t("pause voice broadcast")} />;
|
||||
|
||||
return <div
|
||||
className="mx_VoiceBroadcastRecordingPip"
|
||||
className="mx_VoiceBroadcastBody mx_VoiceBroadcastBody--pip"
|
||||
>
|
||||
<VoiceBroadcastHeader
|
||||
live={live}
|
||||
sender={sender}
|
||||
room={room}
|
||||
/>
|
||||
<hr className="mx_VoiceBroadcastRecordingPip_divider" />
|
||||
<div className="mx_VoiceBroadcastRecordingPip_controls">
|
||||
<hr className="mx_VoiceBroadcastBody_divider" />
|
||||
<div className="mx_VoiceBroadcastBody_controls">
|
||||
{ toggleControl }
|
||||
<VoiceBroadcastControl
|
||||
icon={StopIcon}
|
||||
|
|
|
@ -68,7 +68,7 @@ export const useVoiceBroadcastRecording = (recording: VoiceBroadcastRecording) =
|
|||
const live = [
|
||||
VoiceBroadcastInfoState.Started,
|
||||
VoiceBroadcastInfoState.Paused,
|
||||
VoiceBroadcastInfoState.Running,
|
||||
VoiceBroadcastInfoState.Resumed,
|
||||
].includes(recordingState);
|
||||
|
||||
return {
|
||||
|
|
|
@ -49,7 +49,7 @@ export const VoiceBroadcastChunkEventType = "io.element.voice_broadcast_chunk";
|
|||
export enum VoiceBroadcastInfoState {
|
||||
Started = "started",
|
||||
Paused = "paused",
|
||||
Running = "running",
|
||||
Resumed = "resumed",
|
||||
Stopped = "stopped",
|
||||
}
|
||||
|
||||
|
|
|
@ -105,15 +105,15 @@ export class VoiceBroadcastRecording
|
|||
public async resume(): Promise<void> {
|
||||
if (this.state !== VoiceBroadcastInfoState.Paused) return;
|
||||
|
||||
this.setState(VoiceBroadcastInfoState.Running);
|
||||
this.setState(VoiceBroadcastInfoState.Resumed);
|
||||
await this.getRecorder().start();
|
||||
await this.sendInfoStateEvent(VoiceBroadcastInfoState.Running);
|
||||
await this.sendInfoStateEvent(VoiceBroadcastInfoState.Resumed);
|
||||
}
|
||||
|
||||
public toggle = async (): Promise<void> => {
|
||||
if (this.getState() === VoiceBroadcastInfoState.Paused) return this.resume();
|
||||
|
||||
if ([VoiceBroadcastInfoState.Started, VoiceBroadcastInfoState.Running].includes(this.getState())) {
|
||||
if ([VoiceBroadcastInfoState.Started, VoiceBroadcastInfoState.Resumed].includes(this.getState())) {
|
||||
return this.pause();
|
||||
}
|
||||
};
|
||||
|
|
|
@ -19,6 +19,7 @@ import { ClientEvent, MatrixClient } from "matrix-js-sdk/src/client";
|
|||
import { Room } from "matrix-js-sdk/src/models/room";
|
||||
import { MatrixEvent } from "matrix-js-sdk/src/models/event";
|
||||
import { SyncState } from "matrix-js-sdk/src/sync";
|
||||
import { waitFor } from "@testing-library/react";
|
||||
|
||||
import BasePlatform from "../src/BasePlatform";
|
||||
import { ElementCall } from "../src/models/Call";
|
||||
|
@ -29,8 +30,15 @@ import {
|
|||
createLocalNotificationSettingsIfNeeded,
|
||||
getLocalNotificationAccountDataEventType,
|
||||
} from "../src/utils/notifications";
|
||||
import { getMockClientWithEventEmitter, mkEvent, mkRoom, mockClientMethodsUser, mockPlatformPeg } from "./test-utils";
|
||||
import { getMockClientWithEventEmitter, mkEvent, mockClientMethodsUser, mockPlatformPeg } from "./test-utils";
|
||||
import { IncomingCallToast } from "../src/toasts/IncomingCallToast";
|
||||
import { SdkContextClass } from "../src/contexts/SDKContext";
|
||||
import UserActivity from "../src/UserActivity";
|
||||
import Modal from "../src/Modal";
|
||||
import { mkThread } from "./test-utils/threads";
|
||||
import dis from "../src/dispatcher/dispatcher";
|
||||
import { ThreadPayload } from "../src/dispatcher/payloads/ThreadPayload";
|
||||
import { Action } from "../src/dispatcher/actions";
|
||||
|
||||
jest.mock("../src/utils/notifications", () => ({
|
||||
// @ts-ignore
|
||||
|
@ -50,10 +58,12 @@ describe("Notifier", () => {
|
|||
|
||||
let MockPlatform: MockedObject<BasePlatform>;
|
||||
let mockClient: MockedObject<MatrixClient>;
|
||||
let testRoom: MockedObject<Room>;
|
||||
let testRoom: Room;
|
||||
let accountDataEventKey: string;
|
||||
let accountDataStore = {};
|
||||
|
||||
let mockSettings: Record<string, boolean> = {};
|
||||
|
||||
const userId = "@bob:example.org";
|
||||
|
||||
beforeEach(() => {
|
||||
|
@ -78,7 +88,7 @@ describe("Notifier", () => {
|
|||
};
|
||||
accountDataEventKey = getLocalNotificationAccountDataEventType(mockClient.deviceId);
|
||||
|
||||
testRoom = mkRoom(mockClient, roomId);
|
||||
testRoom = new Room(roomId, mockClient, mockClient.getUserId());
|
||||
|
||||
MockPlatform = mockPlatformPeg({
|
||||
supportsNotifications: jest.fn().mockReturnValue(true),
|
||||
|
@ -89,7 +99,9 @@ describe("Notifier", () => {
|
|||
|
||||
Notifier.isBodyEnabled = jest.fn().mockReturnValue(true);
|
||||
|
||||
mockClient.getRoom.mockReturnValue(testRoom);
|
||||
mockClient.getRoom.mockImplementation(id => {
|
||||
return id === roomId ? testRoom : new Room(id, mockClient, mockClient.getUserId());
|
||||
});
|
||||
});
|
||||
|
||||
describe('triggering notification from events', () => {
|
||||
|
@ -121,13 +133,14 @@ describe("Notifier", () => {
|
|||
},
|
||||
});
|
||||
|
||||
const enabledSettings = [
|
||||
'notificationsEnabled',
|
||||
'audioNotificationsEnabled',
|
||||
];
|
||||
mockSettings = {
|
||||
'notificationsEnabled': true,
|
||||
'audioNotificationsEnabled': true,
|
||||
};
|
||||
|
||||
// enable notifications by default
|
||||
jest.spyOn(SettingsStore, "getValue").mockImplementation(
|
||||
settingName => enabledSettings.includes(settingName),
|
||||
jest.spyOn(SettingsStore, "getValue").mockReset().mockImplementation(
|
||||
settingName => mockSettings[settingName] ?? false,
|
||||
);
|
||||
});
|
||||
|
||||
|
@ -253,16 +266,13 @@ describe("Notifier", () => {
|
|||
});
|
||||
|
||||
const callOnEvent = (type?: string) => {
|
||||
const callEvent = {
|
||||
getContent: () => { },
|
||||
getRoomId: () => roomId,
|
||||
isBeingDecrypted: () => false,
|
||||
isDecryptionFailure: () => false,
|
||||
getSender: () => "@alice:foo",
|
||||
getType: () => type ?? ElementCall.CALL_EVENT_TYPE.name,
|
||||
getStateKey: () => "state_key",
|
||||
} as unknown as MatrixEvent;
|
||||
|
||||
const callEvent = mkEvent({
|
||||
type: type ?? ElementCall.CALL_EVENT_TYPE.name,
|
||||
user: "@alice:foo",
|
||||
room: roomId,
|
||||
content: {},
|
||||
event: true,
|
||||
});
|
||||
Notifier.onEvent(callEvent);
|
||||
return callEvent;
|
||||
};
|
||||
|
@ -345,4 +355,72 @@ describe("Notifier", () => {
|
|||
expect(createLocalNotificationSettingsIfNeededMock).toHaveBeenCalled();
|
||||
});
|
||||
});
|
||||
|
||||
describe('_evaluateEvent', () => {
|
||||
beforeEach(() => {
|
||||
jest.spyOn(SdkContextClass.instance.roomViewStore, "getRoomId")
|
||||
.mockReturnValue(testRoom.roomId);
|
||||
|
||||
jest.spyOn(UserActivity.sharedInstance(), "userActiveRecently")
|
||||
.mockReturnValue(true);
|
||||
|
||||
jest.spyOn(Modal, "hasDialogs").mockReturnValue(false);
|
||||
|
||||
jest.spyOn(Notifier, "_displayPopupNotification").mockReset();
|
||||
jest.spyOn(Notifier, "isEnabled").mockReturnValue(true);
|
||||
|
||||
mockClient.getPushActionsForEvent.mockReturnValue({
|
||||
notify: true,
|
||||
tweaks: {
|
||||
sound: true,
|
||||
},
|
||||
});
|
||||
});
|
||||
|
||||
it("should show a pop-up", () => {
|
||||
expect(Notifier._displayPopupNotification).toHaveBeenCalledTimes(0);
|
||||
Notifier._evaluateEvent(testEvent);
|
||||
expect(Notifier._displayPopupNotification).toHaveBeenCalledTimes(0);
|
||||
|
||||
const eventFromOtherRoom = mkEvent({
|
||||
event: true,
|
||||
type: "m.room.message",
|
||||
user: "@user1:server",
|
||||
room: "!otherroom:example.org",
|
||||
content: {},
|
||||
});
|
||||
|
||||
Notifier._evaluateEvent(eventFromOtherRoom);
|
||||
expect(Notifier._displayPopupNotification).toHaveBeenCalledTimes(1);
|
||||
});
|
||||
|
||||
it("should a pop-up for thread event", async () => {
|
||||
const { events, rootEvent } = mkThread({
|
||||
room: testRoom,
|
||||
client: mockClient,
|
||||
authorId: "@bob:example.org",
|
||||
participantUserIds: ["@bob:example.org"],
|
||||
});
|
||||
|
||||
expect(Notifier._displayPopupNotification).toHaveBeenCalledTimes(0);
|
||||
|
||||
Notifier._evaluateEvent(rootEvent);
|
||||
expect(Notifier._displayPopupNotification).toHaveBeenCalledTimes(0);
|
||||
|
||||
Notifier._evaluateEvent(events[1]);
|
||||
expect(Notifier._displayPopupNotification).toHaveBeenCalledTimes(1);
|
||||
|
||||
dis.dispatch<ThreadPayload>({
|
||||
action: Action.ViewThread,
|
||||
thread_id: rootEvent.getId(),
|
||||
});
|
||||
|
||||
await waitFor(() =>
|
||||
expect(SdkContextClass.instance.roomViewStore.getThreadId()).toBe(rootEvent.getId()),
|
||||
);
|
||||
|
||||
Notifier._evaluateEvent(events[1]);
|
||||
expect(Notifier._displayPopupNotification).toHaveBeenCalledTimes(1);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
|
@ -28,6 +28,7 @@ import { act } from "react-dom/test-utils";
|
|||
import ThreadView from "../../../src/components/structures/ThreadView";
|
||||
import MatrixClientContext from "../../../src/contexts/MatrixClientContext";
|
||||
import RoomContext from "../../../src/contexts/RoomContext";
|
||||
import { SdkContextClass } from "../../../src/contexts/SDKContext";
|
||||
import { MatrixClientPeg } from "../../../src/MatrixClientPeg";
|
||||
import DMRoomMap from "../../../src/utils/DMRoomMap";
|
||||
import ResizeNotifier from "../../../src/utils/ResizeNotifier";
|
||||
|
@ -155,4 +156,13 @@ describe("ThreadView", () => {
|
|||
ROOM_ID, rootEvent2.getId(), expectedMessageBody(rootEvent2, "yolo"),
|
||||
);
|
||||
});
|
||||
|
||||
it("sets the correct thread in the room view store", async () => {
|
||||
// expect(SdkContextClass.instance.roomViewStore.getThreadId()).toBeNull();
|
||||
const { unmount } = await getComponent();
|
||||
expect(SdkContextClass.instance.roomViewStore.getThreadId()).toBe(rootEvent.getId());
|
||||
|
||||
unmount();
|
||||
await waitFor(() => expect(SdkContextClass.instance.roomViewStore.getThreadId()).toBeNull());
|
||||
});
|
||||
});
|
||||
|
|
57
test/components/views/elements/LearnMore-test.tsx
Normal file
57
test/components/views/elements/LearnMore-test.tsx
Normal file
|
@ -0,0 +1,57 @@
|
|||
/*
|
||||
Copyright 2022 The Matrix.org Foundation C.I.C.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
import React from 'react';
|
||||
import { fireEvent, render } from '@testing-library/react';
|
||||
|
||||
import LearnMore from '../../../../src/components/views/elements/LearnMore';
|
||||
import Modal from '../../../../src/Modal';
|
||||
import InfoDialog from '../../../../src/components/views/dialogs/InfoDialog';
|
||||
|
||||
describe('<LearnMore />', () => {
|
||||
const defaultProps = {
|
||||
title: 'Test',
|
||||
description: 'test test test',
|
||||
['data-testid']: 'testid',
|
||||
};
|
||||
const getComponent = (props = {}) =>
|
||||
(<LearnMore {...defaultProps} {...props} />);
|
||||
|
||||
const modalSpy = jest.spyOn(Modal, 'createDialog').mockReturnValue(undefined);
|
||||
|
||||
beforeEach(() => {
|
||||
jest.clearAllMocks();
|
||||
});
|
||||
|
||||
it('renders button', () => {
|
||||
const { container } = render(getComponent());
|
||||
expect(container).toMatchSnapshot();
|
||||
});
|
||||
|
||||
it('opens modal on click', async () => {
|
||||
const { getByTestId } = render(getComponent());
|
||||
fireEvent.click(getByTestId('testid'));
|
||||
|
||||
expect(modalSpy).toHaveBeenCalledWith(
|
||||
InfoDialog,
|
||||
{
|
||||
button: 'Got it',
|
||||
description: defaultProps.description,
|
||||
hasCloseButton: true,
|
||||
title: defaultProps.title,
|
||||
});
|
||||
});
|
||||
});
|
|
@ -0,0 +1,14 @@
|
|||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||
|
||||
exports[`<LearnMore /> renders button 1`] = `
|
||||
<div>
|
||||
<div
|
||||
class="mx_AccessibleButton mx_LearnMore_button mx_AccessibleButton_hasKind mx_AccessibleButton_kind_link_inline"
|
||||
data-testid="testid"
|
||||
role="button"
|
||||
tabindex="0"
|
||||
>
|
||||
Learn more
|
||||
</div>
|
||||
</div>
|
||||
`;
|
|
@ -37,7 +37,16 @@ HTMLCollection [
|
|||
<p
|
||||
class="mx_DeviceSecurityCard_description"
|
||||
>
|
||||
Consider signing out from old sessions (90 days or older) you don't use anymore
|
||||
<span>
|
||||
Consider signing out from old sessions (90 days or older) you don't use anymore.
|
||||
<div
|
||||
class="mx_AccessibleButton mx_LearnMore_button mx_AccessibleButton_hasKind mx_AccessibleButton_kind_link_inline"
|
||||
role="button"
|
||||
tabindex="0"
|
||||
>
|
||||
Learn more
|
||||
</div>
|
||||
</span>
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -72,7 +81,16 @@ HTMLCollection [
|
|||
<p
|
||||
class="mx_DeviceSecurityCard_description"
|
||||
>
|
||||
Verify your sessions for enhanced secure messaging or sign out from those you don't recognize or use anymore.
|
||||
<span>
|
||||
Verify your sessions for enhanced secure messaging or sign out from those you don't recognize or use anymore.
|
||||
<div
|
||||
class="mx_AccessibleButton mx_LearnMore_button mx_AccessibleButton_hasKind mx_AccessibleButton_kind_link_inline"
|
||||
role="button"
|
||||
tabindex="0"
|
||||
>
|
||||
Learn more
|
||||
</div>
|
||||
</span>
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -107,7 +125,16 @@ HTMLCollection [
|
|||
<p
|
||||
class="mx_DeviceSecurityCard_description"
|
||||
>
|
||||
For best security, sign out from any session that you don't recognize or use anymore.
|
||||
<span>
|
||||
For best security, sign out from any session that you don't recognize or use anymore.
|
||||
<div
|
||||
class="mx_AccessibleButton mx_LearnMore_button mx_AccessibleButton_hasKind mx_AccessibleButton_kind_link_inline"
|
||||
role="button"
|
||||
tabindex="0"
|
||||
>
|
||||
Learn more
|
||||
</div>
|
||||
</span>
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
@ -20,10 +20,6 @@ import * as languageHandler from "../../src/languageHandler";
|
|||
import en from "../../src/i18n/strings/en_EN.json";
|
||||
import de from "../../src/i18n/strings/de_DE.json";
|
||||
|
||||
fetchMock.config.overwriteRoutes = false;
|
||||
fetchMock.catch("");
|
||||
window.fetch = fetchMock.sandbox();
|
||||
|
||||
const lv = {
|
||||
"Save": "Saglabāt",
|
||||
"Uploading %(filename)s and %(count)s others|one": "Качване на %(filename)s и %(count)s друг",
|
||||
|
|
|
@ -14,6 +14,7 @@ See the License for the specific language governing permissions and
|
|||
limitations under the License.
|
||||
*/
|
||||
|
||||
import fetchMock from "fetch-mock-jest";
|
||||
import { TextDecoder, TextEncoder } from "util";
|
||||
|
||||
// jest 27 removes setImmediate from jsdom
|
||||
|
@ -54,3 +55,9 @@ global.TextDecoder = TextDecoder;
|
|||
|
||||
// prevent errors whenever a component tries to manually scroll.
|
||||
window.HTMLElement.prototype.scrollIntoView = jest.fn();
|
||||
|
||||
// set up fetch API mock
|
||||
fetchMock.config.overwriteRoutes = false;
|
||||
fetchMock.catch("");
|
||||
fetchMock.get("/image-file-stub", "image file stub");
|
||||
window.fetch = fetchMock.sandbox();
|
||||
|
|
319
test/stores/room-list/SlidingRoomListStore-test.ts
Normal file
319
test/stores/room-list/SlidingRoomListStore-test.ts
Normal file
|
@ -0,0 +1,319 @@
|
|||
/*
|
||||
Copyright 2022 The Matrix.org Foundation C.I.C.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
import { mocked } from 'jest-mock';
|
||||
import { SlidingSync, SlidingSyncEvent } from 'matrix-js-sdk/src/sliding-sync';
|
||||
import { Room } from 'matrix-js-sdk/src/matrix';
|
||||
|
||||
import {
|
||||
LISTS_UPDATE_EVENT,
|
||||
SlidingRoomListStoreClass,
|
||||
SlidingSyncSortToFilter,
|
||||
} from "../../../src/stores/room-list/SlidingRoomListStore";
|
||||
import { SpaceStoreClass } from "../../../src/stores/spaces/SpaceStore";
|
||||
import { MockEventEmitter, stubClient, untilEmission } from "../../test-utils";
|
||||
import { TestSdkContext } from '../../TestSdkContext';
|
||||
import { SlidingSyncManager } from '../../../src/SlidingSyncManager';
|
||||
import { RoomViewStore } from '../../../src/stores/RoomViewStore';
|
||||
import { MatrixDispatcher } from '../../../src/dispatcher/dispatcher';
|
||||
import { SortAlgorithm } from '../../../src/stores/room-list/algorithms/models';
|
||||
import { DefaultTagID, TagID } from '../../../src/stores/room-list/models';
|
||||
import { UPDATE_SELECTED_SPACE } from '../../../src/stores/spaces';
|
||||
import { LISTS_LOADING_EVENT } from '../../../src/stores/room-list/RoomListStore';
|
||||
import { UPDATE_EVENT } from '../../../src/stores/AsyncStore';
|
||||
|
||||
jest.mock('../../../src/SlidingSyncManager');
|
||||
const MockSlidingSyncManager = <jest.Mock<SlidingSyncManager>><unknown>SlidingSyncManager;
|
||||
|
||||
describe("SlidingRoomListStore", () => {
|
||||
let store: SlidingRoomListStoreClass;
|
||||
let context: TestSdkContext;
|
||||
let dis: MatrixDispatcher;
|
||||
let activeSpace: string;
|
||||
let tagIdToIndex = {};
|
||||
|
||||
beforeEach(async () => {
|
||||
context = new TestSdkContext();
|
||||
context.client = stubClient();
|
||||
context._SpaceStore = new MockEventEmitter<SpaceStoreClass>({
|
||||
traverseSpace: jest.fn(),
|
||||
get activeSpace() {
|
||||
return activeSpace;
|
||||
},
|
||||
}) as SpaceStoreClass;
|
||||
context._SlidingSyncManager = new MockSlidingSyncManager();
|
||||
context._SlidingSyncManager.slidingSync = mocked(new MockEventEmitter({
|
||||
getListData: jest.fn(),
|
||||
}) as unknown as SlidingSync);
|
||||
context._RoomViewStore = mocked(new MockEventEmitter({
|
||||
getRoomId: jest.fn(),
|
||||
}) as unknown as RoomViewStore);
|
||||
|
||||
// mock implementations to allow the store to map tag IDs to sliding sync list indexes and vice versa
|
||||
let index = 0;
|
||||
tagIdToIndex = {};
|
||||
mocked(context._SlidingSyncManager.getOrAllocateListIndex).mockImplementation((listId: string): number => {
|
||||
if (tagIdToIndex[listId] != null) {
|
||||
return tagIdToIndex[listId];
|
||||
}
|
||||
tagIdToIndex[listId] = index;
|
||||
index++;
|
||||
return index;
|
||||
});
|
||||
mocked(context.slidingSyncManager.listIdForIndex).mockImplementation((i) => {
|
||||
for (const tagId in tagIdToIndex) {
|
||||
const j = tagIdToIndex[tagId];
|
||||
if (i === j) {
|
||||
return tagId;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
});
|
||||
mocked(context._SlidingSyncManager.ensureListRegistered).mockResolvedValue({
|
||||
ranges: [[0, 10]],
|
||||
});
|
||||
|
||||
dis = new MatrixDispatcher();
|
||||
store = new SlidingRoomListStoreClass(dis, context);
|
||||
});
|
||||
|
||||
describe("spaces", () => {
|
||||
it("alters 'filters.spaces' on the DefaultTagID.Untagged list when the selected space changes", async () => {
|
||||
await store.start(); // call onReady
|
||||
const spaceRoomId = "!foo:bar";
|
||||
|
||||
const p = untilEmission(store, LISTS_LOADING_EVENT, (listName, isLoading) => {
|
||||
return listName === DefaultTagID.Untagged && !isLoading;
|
||||
});
|
||||
|
||||
// change the active space
|
||||
activeSpace = spaceRoomId;
|
||||
context._SpaceStore.emit(UPDATE_SELECTED_SPACE, spaceRoomId, false);
|
||||
await p;
|
||||
|
||||
expect(context._SlidingSyncManager.ensureListRegistered).toHaveBeenCalledWith(
|
||||
tagIdToIndex[DefaultTagID.Untagged],
|
||||
{
|
||||
filters: expect.objectContaining({
|
||||
spaces: [spaceRoomId],
|
||||
}),
|
||||
},
|
||||
);
|
||||
});
|
||||
|
||||
it("alters 'filters.spaces' on the DefaultTagID.Untagged list if it loads with an active space", async () => {
|
||||
// change the active space before we are ready
|
||||
const spaceRoomId = "!foo2:bar";
|
||||
activeSpace = spaceRoomId;
|
||||
const p = untilEmission(store, LISTS_LOADING_EVENT, (listName, isLoading) => {
|
||||
return listName === DefaultTagID.Untagged && !isLoading;
|
||||
});
|
||||
await store.start(); // call onReady
|
||||
await p;
|
||||
expect(context._SlidingSyncManager.ensureListRegistered).toHaveBeenCalledWith(
|
||||
tagIdToIndex[DefaultTagID.Untagged],
|
||||
expect.objectContaining({
|
||||
filters: expect.objectContaining({
|
||||
spaces: [spaceRoomId],
|
||||
}),
|
||||
}),
|
||||
);
|
||||
});
|
||||
|
||||
it("includes subspaces in 'filters.spaces' when the selected space has subspaces", async () => {
|
||||
await store.start(); // call onReady
|
||||
const spaceRoomId = "!foo:bar";
|
||||
const subSpace1 = "!ss1:bar";
|
||||
const subSpace2 = "!ss2:bar";
|
||||
|
||||
const p = untilEmission(store, LISTS_LOADING_EVENT, (listName, isLoading) => {
|
||||
return listName === DefaultTagID.Untagged && !isLoading;
|
||||
});
|
||||
|
||||
mocked(context._SpaceStore.traverseSpace).mockImplementation(
|
||||
(spaceId: string, fn: (roomId: string) => void) => {
|
||||
if (spaceId === spaceRoomId) {
|
||||
fn(subSpace1);
|
||||
fn(subSpace2);
|
||||
}
|
||||
},
|
||||
);
|
||||
|
||||
// change the active space
|
||||
activeSpace = spaceRoomId;
|
||||
context._SpaceStore.emit(UPDATE_SELECTED_SPACE, spaceRoomId, false);
|
||||
await p;
|
||||
|
||||
expect(context._SlidingSyncManager.ensureListRegistered).toHaveBeenCalledWith(
|
||||
tagIdToIndex[DefaultTagID.Untagged],
|
||||
{
|
||||
filters: expect.objectContaining({
|
||||
spaces: [spaceRoomId, subSpace1, subSpace2],
|
||||
}),
|
||||
},
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
it("setTagSorting alters the 'sort' option in the list", async () => {
|
||||
mocked(context._SlidingSyncManager.getOrAllocateListIndex).mockReturnValue(0);
|
||||
const tagId: TagID = "foo";
|
||||
await store.setTagSorting(tagId, SortAlgorithm.Alphabetic);
|
||||
expect(context._SlidingSyncManager.ensureListRegistered).toBeCalledWith(0, {
|
||||
sort: SlidingSyncSortToFilter[SortAlgorithm.Alphabetic],
|
||||
});
|
||||
expect(store.getTagSorting(tagId)).toEqual(SortAlgorithm.Alphabetic);
|
||||
|
||||
await store.setTagSorting(tagId, SortAlgorithm.Recent);
|
||||
expect(context._SlidingSyncManager.ensureListRegistered).toBeCalledWith(0, {
|
||||
sort: SlidingSyncSortToFilter[SortAlgorithm.Recent],
|
||||
});
|
||||
expect(store.getTagSorting(tagId)).toEqual(SortAlgorithm.Recent);
|
||||
});
|
||||
|
||||
it("getTagsForRoom gets the tags for the room", async () => {
|
||||
await store.start();
|
||||
const untaggedIndex = context._SlidingSyncManager.getOrAllocateListIndex(DefaultTagID.Untagged);
|
||||
const favIndex = context._SlidingSyncManager.getOrAllocateListIndex(DefaultTagID.Favourite);
|
||||
const roomA = "!a:localhost";
|
||||
const roomB = "!b:localhost";
|
||||
const indexToListData = {
|
||||
[untaggedIndex]: {
|
||||
joinedCount: 10,
|
||||
roomIndexToRoomId: {
|
||||
0: roomA,
|
||||
1: roomB,
|
||||
},
|
||||
},
|
||||
[favIndex]: {
|
||||
joinedCount: 2,
|
||||
roomIndexToRoomId: {
|
||||
0: roomB,
|
||||
},
|
||||
},
|
||||
};
|
||||
mocked(context._SlidingSyncManager.slidingSync.getListData).mockImplementation((i: number) => {
|
||||
return indexToListData[i] || null;
|
||||
});
|
||||
|
||||
expect(store.getTagsForRoom(new Room(roomA, context.client, context.client.getUserId()))).toEqual(
|
||||
[DefaultTagID.Untagged],
|
||||
);
|
||||
expect(store.getTagsForRoom(new Room(roomB, context.client, context.client.getUserId()))).toEqual(
|
||||
[DefaultTagID.Favourite, DefaultTagID.Untagged],
|
||||
);
|
||||
});
|
||||
|
||||
it("emits LISTS_UPDATE_EVENT when slidingSync lists update", async () => {
|
||||
await store.start();
|
||||
const roomA = "!a:localhost";
|
||||
const roomB = "!b:localhost";
|
||||
const roomC = "!c:localhost";
|
||||
const tagId = DefaultTagID.Favourite;
|
||||
const listIndex = context.slidingSyncManager.getOrAllocateListIndex(tagId);
|
||||
const joinCount = 10;
|
||||
const roomIndexToRoomId = { // mixed to ensure we sort
|
||||
1: roomB,
|
||||
2: roomC,
|
||||
0: roomA,
|
||||
};
|
||||
const rooms = [
|
||||
new Room(roomA, context.client, context.client.getUserId()),
|
||||
new Room(roomB, context.client, context.client.getUserId()),
|
||||
new Room(roomC, context.client, context.client.getUserId()),
|
||||
];
|
||||
mocked(context.client.getRoom).mockImplementation((roomId: string) => {
|
||||
switch (roomId) {
|
||||
case roomA:
|
||||
return rooms[0];
|
||||
case roomB:
|
||||
return rooms[1];
|
||||
case roomC:
|
||||
return rooms[2];
|
||||
}
|
||||
return null;
|
||||
});
|
||||
const p = untilEmission(store, LISTS_UPDATE_EVENT);
|
||||
context.slidingSyncManager.slidingSync.emit(SlidingSyncEvent.List, listIndex, joinCount, roomIndexToRoomId);
|
||||
await p;
|
||||
expect(store.getCount(tagId)).toEqual(joinCount);
|
||||
expect(store.orderedLists[tagId]).toEqual(rooms);
|
||||
});
|
||||
|
||||
it("sets the sticky room on the basis of the viewed room in RoomViewStore", async () => {
|
||||
await store.start();
|
||||
// seed the store with 3 rooms
|
||||
const roomIdA = "!a:localhost";
|
||||
const roomIdB = "!b:localhost";
|
||||
const roomIdC = "!c:localhost";
|
||||
const tagId = DefaultTagID.Favourite;
|
||||
const listIndex = context.slidingSyncManager.getOrAllocateListIndex(tagId);
|
||||
const joinCount = 10;
|
||||
const roomIndexToRoomId = { // mixed to ensure we sort
|
||||
1: roomIdB,
|
||||
2: roomIdC,
|
||||
0: roomIdA,
|
||||
};
|
||||
const roomA = new Room(roomIdA, context.client, context.client.getUserId());
|
||||
const roomB = new Room(roomIdB, context.client, context.client.getUserId());
|
||||
const roomC = new Room(roomIdC, context.client, context.client.getUserId());
|
||||
mocked(context.client.getRoom).mockImplementation((roomId: string) => {
|
||||
switch (roomId) {
|
||||
case roomIdA:
|
||||
return roomA;
|
||||
case roomIdB:
|
||||
return roomB;
|
||||
case roomIdC:
|
||||
return roomC;
|
||||
}
|
||||
return null;
|
||||
});
|
||||
mocked(context._SlidingSyncManager.slidingSync.getListData).mockImplementation((i: number) => {
|
||||
if (i !== listIndex) {
|
||||
return null;
|
||||
}
|
||||
return {
|
||||
roomIndexToRoomId: roomIndexToRoomId,
|
||||
joinedCount: joinCount,
|
||||
};
|
||||
});
|
||||
let p = untilEmission(store, LISTS_UPDATE_EVENT);
|
||||
context.slidingSyncManager.slidingSync.emit(SlidingSyncEvent.List, listIndex, joinCount, roomIndexToRoomId);
|
||||
await p;
|
||||
expect(store.orderedLists[tagId]).toEqual([roomA, roomB, roomC]);
|
||||
|
||||
// make roomB sticky and inform the store
|
||||
mocked(context.roomViewStore.getRoomId).mockReturnValue(roomIdB);
|
||||
context.roomViewStore.emit(UPDATE_EVENT);
|
||||
|
||||
// bump room C to the top, room B should not move from i=1 despite the list update saying to
|
||||
roomIndexToRoomId[0] = roomIdC;
|
||||
roomIndexToRoomId[1] = roomIdA;
|
||||
roomIndexToRoomId[2] = roomIdB;
|
||||
p = untilEmission(store, LISTS_UPDATE_EVENT);
|
||||
context.slidingSyncManager.slidingSync.emit(SlidingSyncEvent.List, listIndex, joinCount, roomIndexToRoomId);
|
||||
await p;
|
||||
|
||||
// check that B didn't move and that A was put below B
|
||||
expect(store.orderedLists[tagId]).toEqual([roomC, roomB, roomA]);
|
||||
|
||||
// make room C sticky: rooms should move as a result, without needing an additional list update
|
||||
mocked(context.roomViewStore.getRoomId).mockReturnValue(roomIdC);
|
||||
p = untilEmission(store, LISTS_UPDATE_EVENT);
|
||||
context.roomViewStore.emit(UPDATE_EVENT);
|
||||
await p;
|
||||
expect(store.orderedLists[tagId].map((r) => r.roomId)).toEqual([roomC, roomA, roomB].map((r) => r.roomId));
|
||||
});
|
||||
});
|
|
@ -6,7 +6,7 @@ Array [
|
|||
Array [
|
||||
Object {
|
||||
"deviceInfo": DeviceInfo {
|
||||
"algorithms": undefined,
|
||||
"algorithms": Array [],
|
||||
"deviceId": "aliceWeb",
|
||||
"keys": Object {},
|
||||
"known": false,
|
||||
|
@ -18,7 +18,7 @@ Array [
|
|||
},
|
||||
Object {
|
||||
"deviceInfo": DeviceInfo {
|
||||
"algorithms": undefined,
|
||||
"algorithms": Array [],
|
||||
"deviceId": "aliceMobile",
|
||||
"keys": Object {},
|
||||
"known": false,
|
||||
|
@ -37,7 +37,7 @@ Array [
|
|||
Array [
|
||||
Object {
|
||||
"deviceInfo": DeviceInfo {
|
||||
"algorithms": undefined,
|
||||
"algorithms": Array [],
|
||||
"deviceId": "bobDesktop",
|
||||
"keys": Object {},
|
||||
"known": false,
|
||||
|
|
|
@ -21,6 +21,26 @@ import { MatrixClient, User } from "matrix-js-sdk/src/matrix";
|
|||
|
||||
import { MatrixClientPeg } from "../../src/MatrixClientPeg";
|
||||
|
||||
/**
|
||||
* Mocked generic class with a real EventEmitter.
|
||||
* Useful for mocks which need event emitters.
|
||||
*/
|
||||
export class MockEventEmitter<T> extends EventEmitter {
|
||||
/**
|
||||
* Construct a new event emitter with additional properties/functions. The event emitter functions
|
||||
* like .emit and .on will be real.
|
||||
* @param mockProperties An object with the mock property or function implementations. 'getters'
|
||||
* are correctly cloned to this event emitter.
|
||||
*/
|
||||
constructor(mockProperties: Partial<Record<MethodKeysOf<T>|PropertyKeysOf<T>, unknown>> = {}) {
|
||||
super();
|
||||
// We must use defineProperties and not assign as the former clones getters correctly,
|
||||
// whereas the latter invokes the getter and sets the return value permanently on the
|
||||
// destination object.
|
||||
Object.defineProperties(this, Object.getOwnPropertyDescriptors(mockProperties));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Mock client with real event emitter
|
||||
* useful for testing code that listens
|
||||
|
|
|
@ -28,7 +28,7 @@ import { isSelfLocation } from "../../../src/utils/location";
|
|||
|
||||
describe("isSelfLocation", () => {
|
||||
it("Returns true for a full m.asset event", () => {
|
||||
const content = makeLocationContent("", '0');
|
||||
const content = makeLocationContent("", '0', Date.now());
|
||||
expect(isSelfLocation(content)).toBe(true);
|
||||
});
|
||||
|
||||
|
|
|
@ -50,7 +50,7 @@ describe("VoiceBroadcastRecordingBody", () => {
|
|||
room: roomId,
|
||||
user: userId,
|
||||
});
|
||||
recording = new VoiceBroadcastRecording(infoEvent, client, VoiceBroadcastInfoState.Running);
|
||||
recording = new VoiceBroadcastRecording(infoEvent, client, VoiceBroadcastInfoState.Resumed);
|
||||
});
|
||||
|
||||
describe("when rendering a live broadcast", () => {
|
||||
|
|
|
@ -118,7 +118,7 @@ describe("VoiceBroadcastRecordingPip", () => {
|
|||
});
|
||||
|
||||
it("should resume the recording", () => {
|
||||
expect(recording.getState()).toBe(VoiceBroadcastInfoState.Running);
|
||||
expect(recording.getState()).toBe(VoiceBroadcastInfoState.Resumed);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
exports[`VoiceBroadcastPlaybackBody when rendering a 0 broadcast should render as expected 1`] = `
|
||||
<div>
|
||||
<div
|
||||
class="mx_VoiceBroadcastPlaybackBody"
|
||||
class="mx_VoiceBroadcastBody"
|
||||
>
|
||||
<div
|
||||
class="mx_VoiceBroadcastHeader"
|
||||
|
@ -51,7 +51,7 @@ exports[`VoiceBroadcastPlaybackBody when rendering a 0 broadcast should render a
|
|||
</div>
|
||||
</div>
|
||||
<div
|
||||
class="mx_VoiceBroadcastPlaybackBody_controls"
|
||||
class="mx_VoiceBroadcastBody_controls"
|
||||
>
|
||||
<div
|
||||
aria-label="resume voice broadcast"
|
||||
|
@ -71,7 +71,7 @@ exports[`VoiceBroadcastPlaybackBody when rendering a 0 broadcast should render a
|
|||
exports[`VoiceBroadcastPlaybackBody when rendering a 1 broadcast should render as expected 1`] = `
|
||||
<div>
|
||||
<div
|
||||
class="mx_VoiceBroadcastPlaybackBody"
|
||||
class="mx_VoiceBroadcastBody"
|
||||
>
|
||||
<div
|
||||
class="mx_VoiceBroadcastHeader"
|
||||
|
@ -119,7 +119,7 @@ exports[`VoiceBroadcastPlaybackBody when rendering a 1 broadcast should render a
|
|||
</div>
|
||||
</div>
|
||||
<div
|
||||
class="mx_VoiceBroadcastPlaybackBody_controls"
|
||||
class="mx_VoiceBroadcastBody_controls"
|
||||
>
|
||||
<div
|
||||
aria-label="pause voice broadcast"
|
||||
|
@ -139,7 +139,7 @@ exports[`VoiceBroadcastPlaybackBody when rendering a 1 broadcast should render a
|
|||
exports[`VoiceBroadcastPlaybackBody when rendering a buffering voice broadcast should render as expected 1`] = `
|
||||
<div>
|
||||
<div
|
||||
class="mx_VoiceBroadcastPlaybackBody"
|
||||
class="mx_VoiceBroadcastBody"
|
||||
>
|
||||
<div
|
||||
class="mx_VoiceBroadcastHeader"
|
||||
|
@ -187,7 +187,7 @@ exports[`VoiceBroadcastPlaybackBody when rendering a buffering voice broadcast s
|
|||
</div>
|
||||
</div>
|
||||
<div
|
||||
class="mx_VoiceBroadcastPlaybackBody_controls"
|
||||
class="mx_VoiceBroadcastBody_controls"
|
||||
>
|
||||
<div
|
||||
class="mx_Spinner"
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
exports[`VoiceBroadcastRecordingBody when rendering a live broadcast should render the expected HTML 1`] = `
|
||||
<div>
|
||||
<div
|
||||
class="mx_VoiceBroadcastRecordingBody"
|
||||
class="mx_VoiceBroadcastBody"
|
||||
>
|
||||
<div
|
||||
class="mx_VoiceBroadcastHeader"
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
exports[`VoiceBroadcastRecordingPip when rendering a paused recording should render as expected 1`] = `
|
||||
<div>
|
||||
<div
|
||||
class="mx_VoiceBroadcastRecordingPip"
|
||||
class="mx_VoiceBroadcastBody mx_VoiceBroadcastBody--pip"
|
||||
>
|
||||
<div
|
||||
class="mx_VoiceBroadcastHeader"
|
||||
|
@ -43,10 +43,10 @@ exports[`VoiceBroadcastRecordingPip when rendering a paused recording should ren
|
|||
</div>
|
||||
</div>
|
||||
<hr
|
||||
class="mx_VoiceBroadcastRecordingPip_divider"
|
||||
class="mx_VoiceBroadcastBody_divider"
|
||||
/>
|
||||
<div
|
||||
class="mx_VoiceBroadcastRecordingPip_controls"
|
||||
class="mx_VoiceBroadcastBody_controls"
|
||||
>
|
||||
<div
|
||||
aria-label="resume voice broadcast"
|
||||
|
@ -76,7 +76,7 @@ exports[`VoiceBroadcastRecordingPip when rendering a paused recording should ren
|
|||
exports[`VoiceBroadcastRecordingPip when rendering a started recording should render as expected 1`] = `
|
||||
<div>
|
||||
<div
|
||||
class="mx_VoiceBroadcastRecordingPip"
|
||||
class="mx_VoiceBroadcastBody mx_VoiceBroadcastBody--pip"
|
||||
>
|
||||
<div
|
||||
class="mx_VoiceBroadcastHeader"
|
||||
|
@ -116,10 +116,10 @@ exports[`VoiceBroadcastRecordingPip when rendering a started recording should re
|
|||
</div>
|
||||
</div>
|
||||
<hr
|
||||
class="mx_VoiceBroadcastRecordingPip_divider"
|
||||
class="mx_VoiceBroadcastBody_divider"
|
||||
/>
|
||||
<div
|
||||
class="mx_VoiceBroadcastRecordingPip_controls"
|
||||
class="mx_VoiceBroadcastBody_controls"
|
||||
>
|
||||
<div
|
||||
aria-label="pause voice broadcast"
|
||||
|
|
|
@ -190,9 +190,9 @@ describe("VoiceBroadcastPlayback", () => {
|
|||
onStateChanged = jest.fn();
|
||||
});
|
||||
|
||||
describe("when there is a running broadcast without chunks yet", () => {
|
||||
describe(`when there is a ${VoiceBroadcastInfoState.Resumed} broadcast without chunks yet`, () => {
|
||||
beforeEach(() => {
|
||||
infoEvent = mkInfoEvent(VoiceBroadcastInfoState.Running);
|
||||
infoEvent = mkInfoEvent(VoiceBroadcastInfoState.Resumed);
|
||||
playback = mkPlayback();
|
||||
setUpChunkEvents([]);
|
||||
});
|
||||
|
@ -236,9 +236,9 @@ describe("VoiceBroadcastPlayback", () => {
|
|||
});
|
||||
});
|
||||
|
||||
describe("when there is a running voice broadcast with some chunks", () => {
|
||||
describe(`when there is a ${VoiceBroadcastInfoState.Resumed} voice broadcast with some chunks`, () => {
|
||||
beforeEach(() => {
|
||||
infoEvent = mkInfoEvent(VoiceBroadcastInfoState.Running);
|
||||
infoEvent = mkInfoEvent(VoiceBroadcastInfoState.Resumed);
|
||||
playback = mkPlayback();
|
||||
setUpChunkEvents([chunk2Event, chunk0Event, chunk1Event]);
|
||||
});
|
||||
|
|
|
@ -423,15 +423,15 @@ describe("VoiceBroadcastRecording", () => {
|
|||
await action();
|
||||
});
|
||||
|
||||
itShouldBeInState(VoiceBroadcastInfoState.Running);
|
||||
itShouldSendAnInfoEvent(VoiceBroadcastInfoState.Running);
|
||||
itShouldBeInState(VoiceBroadcastInfoState.Resumed);
|
||||
itShouldSendAnInfoEvent(VoiceBroadcastInfoState.Resumed);
|
||||
|
||||
it("should start the recorder", () => {
|
||||
expect(mocked(voiceBroadcastRecorder.start)).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it("should emit a running state changed event", () => {
|
||||
expect(onStateChanged).toHaveBeenCalledWith(VoiceBroadcastInfoState.Running);
|
||||
it(`should emit a ${VoiceBroadcastInfoState.Resumed} state changed event`, () => {
|
||||
expect(onStateChanged).toHaveBeenCalledWith(VoiceBroadcastInfoState.Resumed);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
|
@ -121,7 +121,7 @@ describe("hasRoomLiveVoiceBroadcast", () => {
|
|||
// all there are kind of live states
|
||||
VoiceBroadcastInfoState.Started,
|
||||
VoiceBroadcastInfoState.Paused,
|
||||
VoiceBroadcastInfoState.Running,
|
||||
VoiceBroadcastInfoState.Resumed,
|
||||
])("when there is a live broadcast (%s) from the current user", (state: VoiceBroadcastInfoState) => {
|
||||
beforeEach(() => {
|
||||
addVoiceBroadcastInfoEvent(state, client.getUserId());
|
||||
|
@ -132,7 +132,7 @@ describe("hasRoomLiveVoiceBroadcast", () => {
|
|||
|
||||
describe("when there was a live broadcast, that has been stopped", () => {
|
||||
beforeEach(() => {
|
||||
addVoiceBroadcastInfoEvent(VoiceBroadcastInfoState.Running, client.getUserId());
|
||||
addVoiceBroadcastInfoEvent(VoiceBroadcastInfoState.Resumed, client.getUserId());
|
||||
addVoiceBroadcastInfoEvent(VoiceBroadcastInfoState.Stopped, client.getUserId());
|
||||
});
|
||||
|
||||
|
@ -141,7 +141,7 @@ describe("hasRoomLiveVoiceBroadcast", () => {
|
|||
|
||||
describe("when there is a live broadcast from another user", () => {
|
||||
beforeEach(() => {
|
||||
addVoiceBroadcastInfoEvent(VoiceBroadcastInfoState.Running, otherUserId);
|
||||
addVoiceBroadcastInfoEvent(VoiceBroadcastInfoState.Resumed, otherUserId);
|
||||
});
|
||||
|
||||
itShouldReturnTrueFalse();
|
||||
|
|
|
@ -40,7 +40,7 @@ const testCases = [
|
|||
[
|
||||
"@user1:example.com",
|
||||
"@user1:example.com",
|
||||
VoiceBroadcastInfoState.Running,
|
||||
VoiceBroadcastInfoState.Resumed,
|
||||
true,
|
||||
],
|
||||
[
|
||||
|
|
|
@ -128,7 +128,7 @@ describe("shouldDisplayAsVoiceBroadcastTile", () => {
|
|||
describe.each(
|
||||
[
|
||||
VoiceBroadcastInfoState.Paused,
|
||||
VoiceBroadcastInfoState.Running,
|
||||
VoiceBroadcastInfoState.Resumed,
|
||||
VoiceBroadcastInfoState.Stopped,
|
||||
],
|
||||
)("when a voice broadcast info event in state %s occurs", (state: VoiceBroadcastInfoState) => {
|
||||
|
|
|
@ -161,7 +161,7 @@ describe("startNewVoiceBroadcastRecording", () => {
|
|||
room.currentState.setStateEvents([
|
||||
mkVoiceBroadcastInfoStateEvent(
|
||||
roomId,
|
||||
VoiceBroadcastInfoState.Running,
|
||||
VoiceBroadcastInfoState.Resumed,
|
||||
client.getUserId(),
|
||||
client.getDeviceId(),
|
||||
),
|
||||
|
@ -184,7 +184,7 @@ describe("startNewVoiceBroadcastRecording", () => {
|
|||
room.currentState.setStateEvents([
|
||||
mkVoiceBroadcastInfoStateEvent(
|
||||
roomId,
|
||||
VoiceBroadcastInfoState.Running,
|
||||
VoiceBroadcastInfoState.Resumed,
|
||||
otherUserId,
|
||||
"ASD123",
|
||||
),
|
||||
|
|
83
yarn.lock
83
yarn.lock
|
@ -2674,7 +2674,7 @@ ajv-keywords@^3.5.2:
|
|||
resolved "https://registry.yarnpkg.com/ajv-keywords/-/ajv-keywords-3.5.2.tgz#31f29da5ab6e00d1c2d329acf7b5929614d5014d"
|
||||
integrity sha512-5p6WTN0DdTGVQk6VjcEju19IgaHudalcfabD7yhDGeA6bcQnmL+CpveLJq/3hvfwd1aof6L386Ougkx6RfyMIQ==
|
||||
|
||||
ajv@^6.10.0, ajv@^6.12.3, ajv@^6.12.4, ajv@^6.12.5:
|
||||
ajv@^6.10.0, ajv@^6.12.4, ajv@^6.12.5:
|
||||
version "6.12.6"
|
||||
resolved "https://registry.yarnpkg.com/ajv/-/ajv-6.12.6.tgz#baf5a62e802b07d977034586f8c3baf5adf26df4"
|
||||
integrity sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==
|
||||
|
@ -3198,11 +3198,6 @@ browser-process-hrtime@^1.0.0:
|
|||
resolved "https://registry.yarnpkg.com/browser-process-hrtime/-/browser-process-hrtime-1.0.0.tgz#3c9b4b7d782c8121e56f10106d84c0d0ffc94626"
|
||||
integrity sha512-9o5UecI3GhkpM6DrXr69PblIuWxPKk9Y0jHBRhdocZ2y7YECBFCsHm79Pr3OyR2AvjhDkabFJaDJMYRazHgsow==
|
||||
|
||||
browser-request@^0.3.3:
|
||||
version "0.3.3"
|
||||
resolved "https://registry.yarnpkg.com/browser-request/-/browser-request-0.3.3.tgz#9ece5b5aca89a29932242e18bf933def9876cc17"
|
||||
integrity sha512-YyNI4qJJ+piQG6MMEuo7J3Bzaqssufx04zpEKYfSrl/1Op59HWali9zMtBpXnkmqMcOuWJPZvudrm9wISmnCbg==
|
||||
|
||||
browserslist@^4.20.2, browserslist@^4.21.3:
|
||||
version "4.21.3"
|
||||
resolved "https://registry.yarnpkg.com/browserslist/-/browserslist-4.21.3.tgz#5df277694eb3c48bc5c4b05af3e8b7e09c5a6d1a"
|
||||
|
@ -5285,19 +5280,6 @@ grid-index@^1.1.0:
|
|||
resolved "https://registry.yarnpkg.com/grid-index/-/grid-index-1.1.0.tgz#97f8221edec1026c8377b86446a7c71e79522ea7"
|
||||
integrity sha512-HZRwumpOGUrHyxO5bqKZL0B0GlUpwtCAzZ42sgxUPniu33R1LSFH5yrIcBCHjkctCAh3mtWKcKd9J4vDDdeVHA==
|
||||
|
||||
har-schema@^2.0.0:
|
||||
version "2.0.0"
|
||||
resolved "https://registry.yarnpkg.com/har-schema/-/har-schema-2.0.0.tgz#a94c2224ebcac04782a0d9035521f24735b7ec92"
|
||||
integrity sha512-Oqluz6zhGX8cyRaTQlFMPw80bSJVG2x/cFb8ZPhUILGgHka9SsokCCOQgpveePerqidZOrT14ipqfJb7ILcW5Q==
|
||||
|
||||
har-validator@~5.1.3:
|
||||
version "5.1.5"
|
||||
resolved "https://registry.yarnpkg.com/har-validator/-/har-validator-5.1.5.tgz#1f0803b9f8cb20c0fa13822df1ecddb36bde1efd"
|
||||
integrity sha512-nmT2T0lljbxdQZfspsno9hgrG3Uir6Ks5afism62poxqBM6sDnMEuPmzTq8XN0OEwqKLLdh1jQI3qyE66Nzb3w==
|
||||
dependencies:
|
||||
ajv "^6.12.3"
|
||||
har-schema "^2.0.0"
|
||||
|
||||
hard-rejection@^2.1.0:
|
||||
version "2.1.0"
|
||||
resolved "https://registry.yarnpkg.com/hard-rejection/-/hard-rejection-2.1.0.tgz#1c6eda5c1685c63942766d79bb40ae773cecd883"
|
||||
|
@ -5458,15 +5440,6 @@ http-proxy-agent@^4.0.1:
|
|||
agent-base "6"
|
||||
debug "4"
|
||||
|
||||
http-signature@~1.2.0:
|
||||
version "1.2.0"
|
||||
resolved "https://registry.yarnpkg.com/http-signature/-/http-signature-1.2.0.tgz#9aecd925114772f3d95b65a60abb8f7c18fbace1"
|
||||
integrity sha512-CAbnr6Rz4CYQkLYUtSNXxQPUH2gK8f3iWexVlsnMeD+GjlsQ0Xsy1cOX+mN3dtxYomRy21CiOzU8Uhw6OwncEQ==
|
||||
dependencies:
|
||||
assert-plus "^1.0.0"
|
||||
jsprim "^1.2.2"
|
||||
sshpk "^1.7.0"
|
||||
|
||||
http-signature@~1.3.6:
|
||||
version "1.3.6"
|
||||
resolved "https://registry.yarnpkg.com/http-signature/-/http-signature-1.3.6.tgz#cb6fbfdf86d1c974f343be94e87f7fc128662cf9"
|
||||
|
@ -6695,16 +6668,6 @@ jsonfile@^6.0.1:
|
|||
optionalDependencies:
|
||||
graceful-fs "^4.1.6"
|
||||
|
||||
jsprim@^1.2.2:
|
||||
version "1.4.2"
|
||||
resolved "https://registry.yarnpkg.com/jsprim/-/jsprim-1.4.2.tgz#712c65533a15c878ba59e9ed5f0e26d5b77c5feb"
|
||||
integrity sha512-P2bSOMAc/ciLz6DzgjVlGJP9+BrJWu5UDGK70C2iweC5QBIeFf0ZXRvGjEj2uYgrY2MkAAhsSWHDWlFtEroZWw==
|
||||
dependencies:
|
||||
assert-plus "1.0.0"
|
||||
extsprintf "1.3.0"
|
||||
json-schema "0.4.0"
|
||||
verror "1.10.0"
|
||||
|
||||
jsprim@^2.0.2:
|
||||
version "2.0.2"
|
||||
resolved "https://registry.yarnpkg.com/jsprim/-/jsprim-2.0.2.tgz#77ca23dbcd4135cd364800d22ff82c2185803d4d"
|
||||
|
@ -7065,19 +7028,17 @@ matrix-events-sdk@^0.0.1-beta.7:
|
|||
integrity sha512-9jl4wtWanUFSy2sr2lCjErN/oC8KTAtaeaozJtrgot1JiQcEI4Rda9OLgQ7nLKaqb4Z/QUx/fR3XpDzm5Jy1JA==
|
||||
|
||||
"matrix-js-sdk@github:matrix-org/matrix-js-sdk#develop":
|
||||
version "20.1.0"
|
||||
resolved "https://codeload.github.com/matrix-org/matrix-js-sdk/tar.gz/8eed354e17001cd25e3cafe81f74dab499a9882e"
|
||||
version "21.0.0"
|
||||
resolved "https://codeload.github.com/matrix-org/matrix-js-sdk/tar.gz/4b3e6939d6dbfb72c9637d18d6346796bc6f997f"
|
||||
dependencies:
|
||||
"@babel/runtime" "^7.12.5"
|
||||
another-json "^0.2.0"
|
||||
browser-request "^0.3.3"
|
||||
bs58 "^5.0.0"
|
||||
content-type "^1.0.4"
|
||||
loglevel "^1.7.1"
|
||||
matrix-events-sdk "^0.0.1-beta.7"
|
||||
p-retry "4"
|
||||
qs "^6.9.6"
|
||||
request "^2.88.2"
|
||||
unhomoglyph "^1.0.6"
|
||||
|
||||
matrix-mock-request@^2.5.0:
|
||||
|
@ -7396,11 +7357,6 @@ nwsapi@^2.2.0:
|
|||
resolved "https://registry.yarnpkg.com/nwsapi/-/nwsapi-2.2.1.tgz#10a9f268fbf4c461249ebcfe38e359aa36e2577c"
|
||||
integrity sha512-JYOWTeFoS0Z93587vRJgASD5Ut11fYl5NyihP3KrYBvMe1FRRs6RN7m20SA/16GM4P6hTnZjT+UmDOt38UeXNg==
|
||||
|
||||
oauth-sign@~0.9.0:
|
||||
version "0.9.0"
|
||||
resolved "https://registry.yarnpkg.com/oauth-sign/-/oauth-sign-0.9.0.tgz#47a7b016baa68b5fa0ecf3dee08a85c679ac6455"
|
||||
integrity sha512-fexhUFFPTGV8ybAtSIGbV6gOkSv8UtRbDBnAyLQw4QPKkgNlsH2ByPGtMUqdWkos6YCRmAqViwgZrJc/mRDzZQ==
|
||||
|
||||
object-assign@^4.1.0, object-assign@^4.1.1:
|
||||
version "4.1.1"
|
||||
resolved "https://registry.yarnpkg.com/object-assign/-/object-assign-4.1.1.tgz#2109adc7965887cfc05cbbd442cac8bfbb360863"
|
||||
|
@ -8303,32 +8259,6 @@ request-progress@^3.0.0:
|
|||
dependencies:
|
||||
throttleit "^1.0.0"
|
||||
|
||||
request@^2.88.2:
|
||||
version "2.88.2"
|
||||
resolved "https://registry.yarnpkg.com/request/-/request-2.88.2.tgz#d73c918731cb5a87da047e207234146f664d12b3"
|
||||
integrity sha512-MsvtOrfG9ZcrOwAW+Qi+F6HbD0CWXEh9ou77uOb7FM2WPhwT7smM833PzanhJLsgXjN89Ir6V2PczXNnMpwKhw==
|
||||
dependencies:
|
||||
aws-sign2 "~0.7.0"
|
||||
aws4 "^1.8.0"
|
||||
caseless "~0.12.0"
|
||||
combined-stream "~1.0.6"
|
||||
extend "~3.0.2"
|
||||
forever-agent "~0.6.1"
|
||||
form-data "~2.3.2"
|
||||
har-validator "~5.1.3"
|
||||
http-signature "~1.2.0"
|
||||
is-typedarray "~1.0.0"
|
||||
isstream "~0.1.2"
|
||||
json-stringify-safe "~5.0.1"
|
||||
mime-types "~2.1.19"
|
||||
oauth-sign "~0.9.0"
|
||||
performance-now "^2.1.0"
|
||||
qs "~6.5.2"
|
||||
safe-buffer "^5.1.2"
|
||||
tough-cookie "~2.5.0"
|
||||
tunnel-agent "^0.6.0"
|
||||
uuid "^3.3.2"
|
||||
|
||||
require-directory@^2.1.1:
|
||||
version "2.1.1"
|
||||
resolved "https://registry.yarnpkg.com/require-directory/-/require-directory-2.1.1.tgz#8c64ad5fd30dab1c976e2344ffe7f792a6a6df42"
|
||||
|
@ -8795,7 +8725,7 @@ sprintf-js@~1.0.2:
|
|||
resolved "https://registry.yarnpkg.com/sprintf-js/-/sprintf-js-1.0.3.tgz#04e6926f662895354f3dd015203633b857297e2c"
|
||||
integrity sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g==
|
||||
|
||||
sshpk@^1.14.1, sshpk@^1.7.0:
|
||||
sshpk@^1.14.1:
|
||||
version "1.17.0"
|
||||
resolved "https://registry.yarnpkg.com/sshpk/-/sshpk-1.17.0.tgz#578082d92d4fe612b13007496e543fa0fbcbe4c5"
|
||||
integrity sha512-/9HIEs1ZXGhSPE8X6Ccm7Nam1z8KcoCqPdI7ecm1N33EzAetWahvQWVqLZtaZQ+IDKX4IyA2o0gBzqIMkAagHQ==
|
||||
|
@ -9509,11 +9439,6 @@ util-deprecate@^1.0.2, util-deprecate@~1.0.1:
|
|||
resolved "https://registry.yarnpkg.com/util-deprecate/-/util-deprecate-1.0.2.tgz#450d4dc9fa70de732762fbd2d4a28981419a0ccf"
|
||||
integrity sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==
|
||||
|
||||
uuid@^3.3.2:
|
||||
version "3.4.0"
|
||||
resolved "https://registry.yarnpkg.com/uuid/-/uuid-3.4.0.tgz#b23e4358afa8a202fe7a100af1f5f883f02007ee"
|
||||
integrity sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A==
|
||||
|
||||
uuid@^8.3.2:
|
||||
version "8.3.2"
|
||||
resolved "https://registry.yarnpkg.com/uuid/-/uuid-8.3.2.tgz#80d5b5ced271bb9af6c445f21a1a04c606cefbe2"
|
||||
|
|
Loading…
Reference in a new issue