Changes for v3.69.0
This commit is contained in:
parent
2d1d1fb585
commit
c30b2637c5
13 changed files with 175 additions and 121 deletions
|
@ -235,7 +235,7 @@ export default class MessagePanel extends React.Component<IProps, IState> {
|
||||||
// This is recomputed on each render. It's only stored on the component
|
// This is recomputed on each render. It's only stored on the component
|
||||||
// for ease of passing the data around since it's computed in one pass
|
// for ease of passing the data around since it's computed in one pass
|
||||||
// over all events.
|
// over all events.
|
||||||
private readReceiptsByEvent: Record<string, IReadReceiptProps[]> = {};
|
private readReceiptsByEvent: Map<string, IReadReceiptProps[]> = new Map();
|
||||||
|
|
||||||
// Track read receipts by user ID. For each user ID we've ever shown a
|
// Track read receipts by user ID. For each user ID we've ever shown a
|
||||||
// a read receipt for, we store an object:
|
// a read receipt for, we store an object:
|
||||||
|
@ -254,7 +254,7 @@ export default class MessagePanel extends React.Component<IProps, IState> {
|
||||||
// This is recomputed on each render, using the data from the previous
|
// This is recomputed on each render, using the data from the previous
|
||||||
// render as our fallback for any user IDs we can't match a receipt to a
|
// render as our fallback for any user IDs we can't match a receipt to a
|
||||||
// displayed event in the current render cycle.
|
// displayed event in the current render cycle.
|
||||||
private readReceiptsByUserId: Record<string, IReadReceiptForUser> = {};
|
private readReceiptsByUserId: Map<string, IReadReceiptForUser> = new Map();
|
||||||
|
|
||||||
private readonly _showHiddenEvents: boolean;
|
private readonly _showHiddenEvents: boolean;
|
||||||
private isMounted = false;
|
private isMounted = false;
|
||||||
|
@ -624,7 +624,7 @@ export default class MessagePanel extends React.Component<IProps, IState> {
|
||||||
// Note: the EventTile might still render a "sent/sending receipt" independent of
|
// Note: the EventTile might still render a "sent/sending receipt" independent of
|
||||||
// this information. When not providing read receipt information, the tile is likely
|
// this information. When not providing read receipt information, the tile is likely
|
||||||
// to assume that sent receipts are to be shown more often.
|
// to assume that sent receipts are to be shown more often.
|
||||||
this.readReceiptsByEvent = {};
|
this.readReceiptsByEvent = new Map();
|
||||||
if (this.props.showReadReceipts) {
|
if (this.props.showReadReceipts) {
|
||||||
this.readReceiptsByEvent = this.getReadReceiptsByShownEvent();
|
this.readReceiptsByEvent = this.getReadReceiptsByShownEvent();
|
||||||
}
|
}
|
||||||
|
@ -727,7 +727,7 @@ export default class MessagePanel extends React.Component<IProps, IState> {
|
||||||
const eventId = mxEv.getId();
|
const eventId = mxEv.getId();
|
||||||
const highlight = eventId === this.props.highlightedEventId;
|
const highlight = eventId === this.props.highlightedEventId;
|
||||||
|
|
||||||
const readReceipts = this.readReceiptsByEvent[eventId];
|
const readReceipts = this.readReceiptsByEvent.get(eventId);
|
||||||
|
|
||||||
let isLastSuccessful = false;
|
let isLastSuccessful = false;
|
||||||
const isSentState = (s: EventStatus): boolean => !s || s === EventStatus.SENT;
|
const isSentState = (s: EventStatus): boolean => !s || s === EventStatus.SENT;
|
||||||
|
@ -846,17 +846,11 @@ export default class MessagePanel extends React.Component<IProps, IState> {
|
||||||
// Get an object that maps from event ID to a list of read receipts that
|
// Get an object that maps from event ID to a list of read receipts that
|
||||||
// should be shown next to that event. If a hidden event has read receipts,
|
// should be shown next to that event. If a hidden event has read receipts,
|
||||||
// they are folded into the receipts of the last shown event.
|
// they are folded into the receipts of the last shown event.
|
||||||
private getReadReceiptsByShownEvent(): Record<string, IReadReceiptProps[]> {
|
private getReadReceiptsByShownEvent(): Map<string, IReadReceiptProps[]> {
|
||||||
const receiptsByEvent: Record<string, IReadReceiptProps[]> = {};
|
const receiptsByEvent: Map<string, IReadReceiptProps[]> = new Map();
|
||||||
const receiptsByUserId: Record<
|
const receiptsByUserId: Map<string, IReadReceiptForUser> = new Map();
|
||||||
string,
|
|
||||||
{
|
|
||||||
lastShownEventId: string;
|
|
||||||
receipt: IReadReceiptProps;
|
|
||||||
}
|
|
||||||
> = {};
|
|
||||||
|
|
||||||
let lastShownEventId;
|
let lastShownEventId: string;
|
||||||
for (const event of this.props.events) {
|
for (const event of this.props.events) {
|
||||||
if (this.shouldShowEvent(event)) {
|
if (this.shouldShowEvent(event)) {
|
||||||
lastShownEventId = event.getId();
|
lastShownEventId = event.getId();
|
||||||
|
@ -865,9 +859,9 @@ export default class MessagePanel extends React.Component<IProps, IState> {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
const existingReceipts = receiptsByEvent[lastShownEventId] || [];
|
const existingReceipts = receiptsByEvent.get(lastShownEventId) || [];
|
||||||
const newReceipts = this.getReadReceiptsForEvent(event);
|
const newReceipts = this.getReadReceiptsForEvent(event);
|
||||||
receiptsByEvent[lastShownEventId] = existingReceipts.concat(newReceipts);
|
receiptsByEvent.set(lastShownEventId, existingReceipts.concat(newReceipts));
|
||||||
|
|
||||||
// Record these receipts along with their last shown event ID for
|
// Record these receipts along with their last shown event ID for
|
||||||
// each associated user ID.
|
// each associated user ID.
|
||||||
|
@ -885,21 +879,21 @@ export default class MessagePanel extends React.Component<IProps, IState> {
|
||||||
// someone which had one in the last. By looking through our previous
|
// someone which had one in the last. By looking through our previous
|
||||||
// mapping of receipts by user ID, we can cover recover any receipts
|
// mapping of receipts by user ID, we can cover recover any receipts
|
||||||
// that would have been lost by using the same event ID from last time.
|
// that would have been lost by using the same event ID from last time.
|
||||||
for (const userId in this.readReceiptsByUserId) {
|
for (const userId of this.readReceiptsByUserId.keys()) {
|
||||||
if (receiptsByUserId[userId]) {
|
if (receiptsByUserId.get(userId)) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
const { lastShownEventId, receipt } = this.readReceiptsByUserId[userId];
|
const { lastShownEventId, receipt } = this.readReceiptsByUserId.get(userId);
|
||||||
const existingReceipts = receiptsByEvent[lastShownEventId] || [];
|
const existingReceipts = receiptsByEvent.get(lastShownEventId) || [];
|
||||||
receiptsByEvent[lastShownEventId] = existingReceipts.concat(receipt);
|
receiptsByEvent.set(lastShownEventId, existingReceipts.concat(receipt));
|
||||||
receiptsByUserId[userId] = { lastShownEventId, receipt };
|
receiptsByUserId.set(userId, { lastShownEventId, receipt });
|
||||||
}
|
}
|
||||||
this.readReceiptsByUserId = receiptsByUserId;
|
this.readReceiptsByUserId = receiptsByUserId;
|
||||||
|
|
||||||
// After grouping receipts by shown events, do another pass to sort each
|
// After grouping receipts by shown events, do another pass to sort each
|
||||||
// receipt list.
|
// receipt list.
|
||||||
for (const eventId in receiptsByEvent) {
|
for (const receipts of receiptsByEvent.values()) {
|
||||||
receiptsByEvent[eventId].sort((r1, r2) => {
|
receipts.sort((r1, r2) => {
|
||||||
return r2.ts - r1.ts;
|
return r2.ts - r1.ts;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
|
@ -51,7 +51,7 @@ export const RoomAccountDataEventEditor: React.FC<IEditorProps> = ({ mxEvent, on
|
||||||
};
|
};
|
||||||
|
|
||||||
interface IProps extends IDevtoolsProps {
|
interface IProps extends IDevtoolsProps {
|
||||||
events: Record<string, MatrixEvent>;
|
events: Map<string, MatrixEvent>;
|
||||||
Editor: React.FC<IEditorProps>;
|
Editor: React.FC<IEditorProps>;
|
||||||
actionLabel: string;
|
actionLabel: string;
|
||||||
}
|
}
|
||||||
|
@ -74,7 +74,7 @@ const BaseAccountDataExplorer: React.FC<IProps> = ({ events, Editor, actionLabel
|
||||||
return (
|
return (
|
||||||
<BaseTool onBack={onBack} actionLabel={actionLabel} onAction={onAction}>
|
<BaseTool onBack={onBack} actionLabel={actionLabel} onAction={onAction}>
|
||||||
<FilteredList query={query} onChange={setQuery}>
|
<FilteredList query={query} onChange={setQuery}>
|
||||||
{Object.entries(events).map(([eventType, ev]) => {
|
{Array.from(events.entries()).map(([eventType, ev]) => {
|
||||||
const onClick = (): void => {
|
const onClick = (): void => {
|
||||||
setEvent(ev);
|
setEvent(ev);
|
||||||
};
|
};
|
||||||
|
|
|
@ -21,6 +21,7 @@ import counterpart from "counterpart";
|
||||||
import React from "react";
|
import React from "react";
|
||||||
import { logger } from "matrix-js-sdk/src/logger";
|
import { logger } from "matrix-js-sdk/src/logger";
|
||||||
import { Optional } from "matrix-events-sdk";
|
import { Optional } from "matrix-events-sdk";
|
||||||
|
import { MapWithDefault, safeSet } from "matrix-js-sdk/src/utils";
|
||||||
|
|
||||||
import SettingsStore from "./settings/SettingsStore";
|
import SettingsStore from "./settings/SettingsStore";
|
||||||
import PlatformPeg from "./PlatformPeg";
|
import PlatformPeg from "./PlatformPeg";
|
||||||
|
@ -629,21 +630,16 @@ export class CustomTranslationOptions {
|
||||||
function doRegisterTranslations(customTranslations: ICustomTranslations): void {
|
function doRegisterTranslations(customTranslations: ICustomTranslations): void {
|
||||||
// We convert the operator-friendly version into something counterpart can
|
// We convert the operator-friendly version into something counterpart can
|
||||||
// consume.
|
// consume.
|
||||||
const langs: {
|
// Map: lang → Record: string → translation
|
||||||
// same structure, just flipped key order
|
const langs: MapWithDefault<string, Record<string, string>> = new MapWithDefault(() => ({}));
|
||||||
[lang: string]: {
|
|
||||||
[str: string]: string;
|
|
||||||
};
|
|
||||||
} = {};
|
|
||||||
for (const [str, translations] of Object.entries(customTranslations)) {
|
for (const [str, translations] of Object.entries(customTranslations)) {
|
||||||
for (const [lang, newStr] of Object.entries(translations)) {
|
for (const [lang, newStr] of Object.entries(translations)) {
|
||||||
if (!langs[lang]) langs[lang] = {};
|
safeSet(langs.getOrCreate(lang), str, newStr);
|
||||||
langs[lang][str] = newStr;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Finally, tell counterpart about our translations
|
// Finally, tell counterpart about our translations
|
||||||
for (const [lang, translations] of Object.entries(langs)) {
|
for (const [lang, translations] of langs) {
|
||||||
counterpart.registerTranslations(lang, translations);
|
counterpart.registerTranslations(lang, translations);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -14,11 +14,13 @@ See the License for the specific language governing permissions and
|
||||||
limitations under the License.
|
limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
import { safeSet } from "matrix-js-sdk/src/utils";
|
||||||
import { TranslationStringsObject } from "@matrix-org/react-sdk-module-api/lib/types/translations";
|
import { TranslationStringsObject } from "@matrix-org/react-sdk-module-api/lib/types/translations";
|
||||||
import { AnyLifecycle } from "@matrix-org/react-sdk-module-api/lib/lifecycles/types";
|
import { AnyLifecycle } from "@matrix-org/react-sdk-module-api/lib/lifecycles/types";
|
||||||
|
|
||||||
import { AppModule } from "./AppModule";
|
import { AppModule } from "./AppModule";
|
||||||
import { ModuleFactory } from "./ModuleFactory";
|
import { ModuleFactory } from "./ModuleFactory";
|
||||||
|
|
||||||
import "./ModuleComponents";
|
import "./ModuleComponents";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -53,9 +55,10 @@ export class ModuleRunner {
|
||||||
if (!i18n) continue;
|
if (!i18n) continue;
|
||||||
|
|
||||||
for (const [lang, strings] of Object.entries(i18n)) {
|
for (const [lang, strings] of Object.entries(i18n)) {
|
||||||
if (!merged[lang]) merged[lang] = {};
|
safeSet(merged, lang, merged[lang] || {});
|
||||||
|
|
||||||
for (const [str, val] of Object.entries(strings)) {
|
for (const [str, val] of Object.entries(strings)) {
|
||||||
merged[lang][str] = val;
|
safeSet(merged[lang], str, val);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -15,6 +15,8 @@ See the License for the specific language governing permissions and
|
||||||
limitations under the License.
|
limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
import { safeSet } from "matrix-js-sdk/src/utils";
|
||||||
|
|
||||||
import { SettingLevel } from "../SettingLevel";
|
import { SettingLevel } from "../SettingLevel";
|
||||||
import { WatchManager } from "../WatchManager";
|
import { WatchManager } from "../WatchManager";
|
||||||
import AbstractLocalStorageSettingsHandler from "./AbstractLocalStorageSettingsHandler";
|
import AbstractLocalStorageSettingsHandler from "./AbstractLocalStorageSettingsHandler";
|
||||||
|
@ -48,7 +50,7 @@ export default class RoomDeviceSettingsHandler extends AbstractLocalStorageSetti
|
||||||
let value = this.read("mx_local_settings");
|
let value = this.read("mx_local_settings");
|
||||||
if (!value) value = {};
|
if (!value) value = {};
|
||||||
if (!value["blacklistUnverifiedDevicesPerRoom"]) value["blacklistUnverifiedDevicesPerRoom"] = {};
|
if (!value["blacklistUnverifiedDevicesPerRoom"]) value["blacklistUnverifiedDevicesPerRoom"] = {};
|
||||||
value["blacklistUnverifiedDevicesPerRoom"][roomId] = newValue;
|
safeSet(value["blacklistUnverifiedDevicesPerRoom"], roomId, newValue);
|
||||||
this.setObject("mx_local_settings", value);
|
this.setObject("mx_local_settings", value);
|
||||||
this.watchers.notifyUpdate(settingName, roomId, SettingLevel.ROOM_DEVICE, newValue);
|
this.watchers.notifyUpdate(settingName, roomId, SettingLevel.ROOM_DEVICE, newValue);
|
||||||
return Promise.resolve();
|
return Promise.resolve();
|
||||||
|
|
|
@ -135,9 +135,10 @@ export default class AutoRageshakeStore extends AsyncStoreWithClient<IState> {
|
||||||
...eventInfo,
|
...eventInfo,
|
||||||
recipient_rageshake: rageshakeURL,
|
recipient_rageshake: rageshakeURL,
|
||||||
};
|
};
|
||||||
this.matrixClient.sendToDevice(AUTO_RS_REQUEST, {
|
this.matrixClient.sendToDevice(
|
||||||
[messageContent.user_id]: { [messageContent.device_id]: messageContent },
|
AUTO_RS_REQUEST,
|
||||||
});
|
new Map([["messageContent.user_id", new Map([[messageContent.device_id, messageContent]])]]),
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -275,7 +275,7 @@ export class StopGapWidgetDriver extends WidgetDriver {
|
||||||
if (deviceId === "*") {
|
if (deviceId === "*") {
|
||||||
// Send the message to all devices we have keys for
|
// Send the message to all devices we have keys for
|
||||||
await client.encryptAndSendToDevices(
|
await client.encryptAndSendToDevices(
|
||||||
Object.values(deviceInfoMap[userId]).map((deviceInfo) => ({
|
Array.from(deviceInfoMap.get(userId).values()).map((deviceInfo) => ({
|
||||||
userId,
|
userId,
|
||||||
deviceInfo,
|
deviceInfo,
|
||||||
})),
|
})),
|
||||||
|
@ -284,7 +284,7 @@ export class StopGapWidgetDriver extends WidgetDriver {
|
||||||
} else {
|
} else {
|
||||||
// Send the message to a specific device
|
// Send the message to a specific device
|
||||||
await client.encryptAndSendToDevices(
|
await client.encryptAndSendToDevices(
|
||||||
[{ userId, deviceInfo: deviceInfoMap[userId][deviceId] }],
|
[{ userId, deviceInfo: deviceInfoMap.get(userId).get(deviceId) }],
|
||||||
content,
|
content,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
@ -18,7 +18,7 @@ import { Room } from "matrix-js-sdk/src/models/room";
|
||||||
import { MatrixEvent } from "matrix-js-sdk/src/models/event";
|
import { MatrixEvent } from "matrix-js-sdk/src/models/event";
|
||||||
import { RoomStateEvent } from "matrix-js-sdk/src/models/room-state";
|
import { RoomStateEvent } from "matrix-js-sdk/src/models/room-state";
|
||||||
import { Optional } from "matrix-events-sdk";
|
import { Optional } from "matrix-events-sdk";
|
||||||
import { compare } from "matrix-js-sdk/src/utils";
|
import { compare, MapWithDefault, recursiveMapToObject } from "matrix-js-sdk/src/utils";
|
||||||
|
|
||||||
import SettingsStore from "../../settings/SettingsStore";
|
import SettingsStore from "../../settings/SettingsStore";
|
||||||
import WidgetStore, { IApp } from "../WidgetStore";
|
import WidgetStore, { IApp } from "../WidgetStore";
|
||||||
|
@ -92,19 +92,17 @@ export const MAX_PINNED = 3;
|
||||||
const MIN_WIDGET_WIDTH_PCT = 10; // 10%
|
const MIN_WIDGET_WIDTH_PCT = 10; // 10%
|
||||||
const MIN_WIDGET_HEIGHT_PCT = 2; // 2%
|
const MIN_WIDGET_HEIGHT_PCT = 2; // 2%
|
||||||
|
|
||||||
|
interface ContainerValue {
|
||||||
|
ordered: IApp[];
|
||||||
|
height?: number;
|
||||||
|
distributions?: number[];
|
||||||
|
}
|
||||||
|
|
||||||
export class WidgetLayoutStore extends ReadyWatchingStore {
|
export class WidgetLayoutStore extends ReadyWatchingStore {
|
||||||
private static internalInstance: WidgetLayoutStore;
|
private static internalInstance: WidgetLayoutStore;
|
||||||
|
|
||||||
private byRoom: {
|
// Map: room Id → container → ContainerValue
|
||||||
[roomId: string]: Partial<{
|
private byRoom: MapWithDefault<string, Map<Container, ContainerValue>> = new MapWithDefault(() => new Map());
|
||||||
[container in Container]: {
|
|
||||||
ordered: IApp[];
|
|
||||||
height?: number | null;
|
|
||||||
distributions?: number[];
|
|
||||||
};
|
|
||||||
}>;
|
|
||||||
} = {};
|
|
||||||
|
|
||||||
private pinnedRef: string;
|
private pinnedRef: string;
|
||||||
private layoutRef: string;
|
private layoutRef: string;
|
||||||
|
|
||||||
|
@ -138,7 +136,7 @@ export class WidgetLayoutStore extends ReadyWatchingStore {
|
||||||
}
|
}
|
||||||
|
|
||||||
protected async onNotReady(): Promise<void> {
|
protected async onNotReady(): Promise<void> {
|
||||||
this.byRoom = {};
|
this.byRoom = new MapWithDefault(() => new Map());
|
||||||
|
|
||||||
this.matrixClient?.off(RoomStateEvent.Events, this.updateRoomFromState);
|
this.matrixClient?.off(RoomStateEvent.Events, this.updateRoomFromState);
|
||||||
SettingsStore.unwatchSetting(this.pinnedRef);
|
SettingsStore.unwatchSetting(this.pinnedRef);
|
||||||
|
@ -147,7 +145,8 @@ export class WidgetLayoutStore extends ReadyWatchingStore {
|
||||||
}
|
}
|
||||||
|
|
||||||
private updateAllRooms = (): void => {
|
private updateAllRooms = (): void => {
|
||||||
this.byRoom = {};
|
this.byRoom = new MapWithDefault(() => new Map());
|
||||||
|
|
||||||
for (const room of this.matrixClient.getVisibleRooms()) {
|
for (const room of this.matrixClient.getVisibleRooms()) {
|
||||||
this.recalculateRoom(room);
|
this.recalculateRoom(room);
|
||||||
}
|
}
|
||||||
|
@ -180,12 +179,13 @@ export class WidgetLayoutStore extends ReadyWatchingStore {
|
||||||
public recalculateRoom(room: Room): void {
|
public recalculateRoom(room: Room): void {
|
||||||
const widgets = WidgetStore.instance.getApps(room.roomId);
|
const widgets = WidgetStore.instance.getApps(room.roomId);
|
||||||
if (!widgets?.length) {
|
if (!widgets?.length) {
|
||||||
this.byRoom[room.roomId] = {};
|
this.byRoom.set(room.roomId, new Map());
|
||||||
this.emitFor(room);
|
this.emitFor(room);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const beforeChanges = JSON.stringify(this.byRoom[room.roomId]);
|
const roomContainers = this.byRoom.getOrCreate(room.roomId);
|
||||||
|
const beforeChanges = JSON.stringify(recursiveMapToObject(roomContainers));
|
||||||
|
|
||||||
const layoutEv = room.currentState.getStateEvents(WIDGET_LAYOUT_EVENT_TYPE, "");
|
const layoutEv = room.currentState.getStateEvents(WIDGET_LAYOUT_EVENT_TYPE, "");
|
||||||
const legacyPinned = SettingsStore.getValue("Widgets.pinned", room.roomId);
|
const legacyPinned = SettingsStore.getValue("Widgets.pinned", room.roomId);
|
||||||
|
@ -321,33 +321,35 @@ export class WidgetLayoutStore extends ReadyWatchingStore {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Finally, fill in our cache and update
|
// Finally, fill in our cache and update
|
||||||
this.byRoom[room.roomId] = {};
|
const newRoomContainers = new Map();
|
||||||
|
this.byRoom.set(room.roomId, newRoomContainers);
|
||||||
if (topWidgets.length) {
|
if (topWidgets.length) {
|
||||||
this.byRoom[room.roomId][Container.Top] = {
|
newRoomContainers.set(Container.Top, {
|
||||||
ordered: topWidgets,
|
ordered: topWidgets,
|
||||||
distributions: widths,
|
distributions: widths,
|
||||||
height: maxHeight,
|
height: maxHeight,
|
||||||
};
|
});
|
||||||
}
|
}
|
||||||
if (rightWidgets.length) {
|
if (rightWidgets.length) {
|
||||||
this.byRoom[room.roomId][Container.Right] = {
|
newRoomContainers.set(Container.Right, {
|
||||||
ordered: rightWidgets,
|
ordered: rightWidgets,
|
||||||
};
|
});
|
||||||
}
|
}
|
||||||
if (centerWidgets.length) {
|
if (centerWidgets.length) {
|
||||||
this.byRoom[room.roomId][Container.Center] = {
|
newRoomContainers.set(Container.Center, {
|
||||||
ordered: centerWidgets,
|
ordered: centerWidgets,
|
||||||
};
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
const afterChanges = JSON.stringify(this.byRoom[room.roomId]);
|
const afterChanges = JSON.stringify(recursiveMapToObject(newRoomContainers));
|
||||||
|
|
||||||
if (afterChanges !== beforeChanges) {
|
if (afterChanges !== beforeChanges) {
|
||||||
this.emitFor(room);
|
this.emitFor(room);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public getContainerWidgets(room: Optional<Room>, container: Container): IApp[] {
|
public getContainerWidgets(room: Optional<Room>, container: Container): IApp[] {
|
||||||
return this.byRoom[room?.roomId]?.[container]?.ordered || [];
|
return this.byRoom.get(room?.roomId)?.get(container)?.ordered || [];
|
||||||
}
|
}
|
||||||
|
|
||||||
public isInContainer(room: Optional<Room>, widget: IApp, container: Container): boolean {
|
public isInContainer(room: Optional<Room>, widget: IApp, container: Container): boolean {
|
||||||
|
@ -367,7 +369,7 @@ export class WidgetLayoutStore extends ReadyWatchingStore {
|
||||||
|
|
||||||
public getResizerDistributions(room: Room, container: Container): string[] {
|
public getResizerDistributions(room: Room, container: Container): string[] {
|
||||||
// yes, string.
|
// yes, string.
|
||||||
let distributions = this.byRoom[room.roomId]?.[container]?.distributions;
|
let distributions = this.byRoom.get(room.roomId)?.get(container)?.distributions;
|
||||||
if (!distributions || distributions.length < 2) return [];
|
if (!distributions || distributions.length < 2) return [];
|
||||||
|
|
||||||
// The distributor actually expects to be fed N-1 sizes and expands the middle section
|
// The distributor actually expects to be fed N-1 sizes and expands the middle section
|
||||||
|
@ -396,19 +398,19 @@ export class WidgetLayoutStore extends ReadyWatchingStore {
|
||||||
container: container,
|
container: container,
|
||||||
width: numbers[i],
|
width: numbers[i],
|
||||||
index: i,
|
index: i,
|
||||||
height: this.byRoom[room.roomId]?.[container]?.height || MIN_WIDGET_HEIGHT_PCT,
|
height: this.byRoom.get(room.roomId)?.get(container)?.height || MIN_WIDGET_HEIGHT_PCT,
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
this.updateUserLayout(room, localLayout);
|
this.updateUserLayout(room, localLayout);
|
||||||
}
|
}
|
||||||
|
|
||||||
public getContainerHeight(room: Room, container: Container): number | null {
|
public getContainerHeight(room: Room, container: Container): number | null {
|
||||||
return this.byRoom[room.roomId]?.[container]?.height ?? null; // let the default get returned if needed
|
return this.byRoom.get(room.roomId)?.get(container)?.height ?? null; // let the default get returned if needed
|
||||||
}
|
}
|
||||||
|
|
||||||
public setContainerHeight(room: Room, container: Container, height?: number): void {
|
public setContainerHeight(room: Room, container: Container, height?: number): void {
|
||||||
const widgets = this.getContainerWidgets(room, container);
|
const widgets = this.getContainerWidgets(room, container);
|
||||||
const widths = this.byRoom[room.roomId]?.[container]?.distributions;
|
const widths = this.byRoom.get(room.roomId)?.get(container)?.distributions;
|
||||||
const localLayout: Record<string, IStoredLayout> = {};
|
const localLayout: Record<string, IStoredLayout> = {};
|
||||||
widgets.forEach((w, i) => {
|
widgets.forEach((w, i) => {
|
||||||
localLayout[w.id] = {
|
localLayout[w.id] = {
|
||||||
|
@ -430,8 +432,8 @@ export class WidgetLayoutStore extends ReadyWatchingStore {
|
||||||
const newIdx = clamp(currentIdx + delta, 0, widgets.length);
|
const newIdx = clamp(currentIdx + delta, 0, widgets.length);
|
||||||
widgets.splice(newIdx, 0, widget);
|
widgets.splice(newIdx, 0, widget);
|
||||||
|
|
||||||
const widths = this.byRoom[room.roomId]?.[container]?.distributions;
|
const widths = this.byRoom.get(room.roomId)?.get(container)?.distributions;
|
||||||
const height = this.byRoom[room.roomId]?.[container]?.height;
|
const height = this.byRoom.get(room.roomId)?.get(container)?.height;
|
||||||
const localLayout: Record<string, IStoredLayout> = {};
|
const localLayout: Record<string, IStoredLayout> = {};
|
||||||
widgets.forEach((w, i) => {
|
widgets.forEach((w, i) => {
|
||||||
localLayout[w.id] = {
|
localLayout[w.id] = {
|
||||||
|
@ -498,8 +500,8 @@ export class WidgetLayoutStore extends ReadyWatchingStore {
|
||||||
if (container === Container.Top) {
|
if (container === Container.Top) {
|
||||||
const containerWidgets = this.getContainerWidgets(room, container);
|
const containerWidgets = this.getContainerWidgets(room, container);
|
||||||
const idx = containerWidgets.findIndex((w) => w.id === widget.id);
|
const idx = containerWidgets.findIndex((w) => w.id === widget.id);
|
||||||
const widths = this.byRoom[room.roomId]?.[container]?.distributions;
|
const widths = this.byRoom.get(room.roomId)?.get(container)?.distributions;
|
||||||
const height = this.byRoom[room.roomId]?.[container]?.height;
|
const height = this.byRoom.get(room.roomId)?.get(container)?.height;
|
||||||
evContent.widgets[widget.id] = {
|
evContent.widgets[widget.id] = {
|
||||||
...evContent.widgets[widget.id],
|
...evContent.widgets[widget.id],
|
||||||
height: height ? Math.round(height) : undefined,
|
height: height ? Math.round(height) : undefined,
|
||||||
|
@ -512,12 +514,12 @@ export class WidgetLayoutStore extends ReadyWatchingStore {
|
||||||
}
|
}
|
||||||
|
|
||||||
private getAllWidgets(room: Room): [IApp, Container][] {
|
private getAllWidgets(room: Room): [IApp, Container][] {
|
||||||
const containers = this.byRoom[room.roomId];
|
const containers = this.byRoom.get(room.roomId);
|
||||||
if (!containers) return [];
|
if (!containers) return [];
|
||||||
|
|
||||||
const ret: [IApp, Container][] = [];
|
const ret: [IApp, Container][] = [];
|
||||||
for (const container in containers) {
|
for (const [container, containerValue] of containers) {
|
||||||
const widgets = containers[container as Container].ordered;
|
const widgets = containerValue.ordered;
|
||||||
for (const widget of widgets) {
|
for (const widget of widgets) {
|
||||||
ret.push([widget, container as Container]);
|
ret.push([widget, container as Container]);
|
||||||
}
|
}
|
||||||
|
@ -531,12 +533,12 @@ export class WidgetLayoutStore extends ReadyWatchingStore {
|
||||||
for (const [widget, container] of allWidgets) {
|
for (const [widget, container] of allWidgets) {
|
||||||
const containerWidgets = this.getContainerWidgets(room, container);
|
const containerWidgets = this.getContainerWidgets(room, container);
|
||||||
const idx = containerWidgets.findIndex((w) => w.id === widget.id);
|
const idx = containerWidgets.findIndex((w) => w.id === widget.id);
|
||||||
const widths = this.byRoom[room.roomId]?.[container]?.distributions;
|
const widths = this.byRoom.get(room.roomId)?.get(container)?.distributions;
|
||||||
if (!newLayout[widget.id]) {
|
if (!newLayout[widget.id]) {
|
||||||
newLayout[widget.id] = {
|
newLayout[widget.id] = {
|
||||||
container: container,
|
container: container,
|
||||||
index: idx,
|
index: idx,
|
||||||
height: this.byRoom[room.roomId]?.[container]?.height,
|
height: this.byRoom.get(room.roomId)?.get(container)?.height,
|
||||||
width: widths?.[idx],
|
width: widths?.[idx],
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
|
@ -71,7 +71,7 @@ export const recordClientInformation = async (
|
||||||
* client information for devices NOT in this list will be removed
|
* client information for devices NOT in this list will be removed
|
||||||
*/
|
*/
|
||||||
export const pruneClientInformation = (validDeviceIds: string[], matrixClient: MatrixClient): void => {
|
export const pruneClientInformation = (validDeviceIds: string[], matrixClient: MatrixClient): void => {
|
||||||
Object.values(matrixClient.store.accountData).forEach((event) => {
|
Array.from(matrixClient.store.accountData.values()).forEach((event) => {
|
||||||
if (!event.getType().startsWith(clientInformationEventPrefix)) {
|
if (!event.getType().startsWith(clientInformationEventPrefix)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
|
@ -75,7 +75,10 @@ describe("<SessionManagerTab />", () => {
|
||||||
const mockCrossSigningInfo = {
|
const mockCrossSigningInfo = {
|
||||||
checkDeviceTrust: jest.fn(),
|
checkDeviceTrust: jest.fn(),
|
||||||
};
|
};
|
||||||
const mockVerificationRequest = { cancel: jest.fn(), on: jest.fn() } as unknown as VerificationRequest;
|
const mockVerificationRequest = {
|
||||||
|
cancel: jest.fn(),
|
||||||
|
on: jest.fn(),
|
||||||
|
} as unknown as VerificationRequest;
|
||||||
const mockClient = getMockClientWithEventEmitter({
|
const mockClient = getMockClientWithEventEmitter({
|
||||||
...mockClientMethodsUser(aliceId),
|
...mockClientMethodsUser(aliceId),
|
||||||
getStoredCrossSigningForUser: jest.fn().mockReturnValue(mockCrossSigningInfo),
|
getStoredCrossSigningForUser: jest.fn().mockReturnValue(mockCrossSigningInfo),
|
||||||
|
@ -184,7 +187,7 @@ describe("<SessionManagerTab />", () => {
|
||||||
});
|
});
|
||||||
|
|
||||||
// @ts-ignore mock
|
// @ts-ignore mock
|
||||||
mockClient.store = { accountData: {} };
|
mockClient.store = { accountData: new Map() };
|
||||||
|
|
||||||
mockClient.getAccountData.mockReset().mockImplementation((eventType) => {
|
mockClient.getAccountData.mockReset().mockImplementation((eventType) => {
|
||||||
if (eventType.startsWith(LOCAL_NOTIFICATION_SETTINGS_PREFIX.name)) {
|
if (eventType.startsWith(LOCAL_NOTIFICATION_SETTINGS_PREFIX.name)) {
|
||||||
|
@ -221,7 +224,9 @@ describe("<SessionManagerTab />", () => {
|
||||||
|
|
||||||
it("does not fail when checking device verification fails", async () => {
|
it("does not fail when checking device verification fails", async () => {
|
||||||
const logSpy = jest.spyOn(logger, "error").mockImplementation(() => {});
|
const logSpy = jest.spyOn(logger, "error").mockImplementation(() => {});
|
||||||
mockClient.getDevices.mockResolvedValue({ devices: [alicesDevice, alicesMobileDevice] });
|
mockClient.getDevices.mockResolvedValue({
|
||||||
|
devices: [alicesDevice, alicesMobileDevice],
|
||||||
|
});
|
||||||
const noCryptoError = new Error("End-to-end encryption disabled");
|
const noCryptoError = new Error("End-to-end encryption disabled");
|
||||||
mockClient.getStoredDevice.mockImplementation(() => {
|
mockClient.getStoredDevice.mockImplementation(() => {
|
||||||
throw noCryptoError;
|
throw noCryptoError;
|
||||||
|
@ -276,7 +281,9 @@ describe("<SessionManagerTab />", () => {
|
||||||
});
|
});
|
||||||
|
|
||||||
it("extends device with client information when available", async () => {
|
it("extends device with client information when available", async () => {
|
||||||
mockClient.getDevices.mockResolvedValue({ devices: [alicesDevice, alicesMobileDevice] });
|
mockClient.getDevices.mockResolvedValue({
|
||||||
|
devices: [alicesDevice, alicesMobileDevice],
|
||||||
|
});
|
||||||
mockClient.getAccountData.mockImplementation((eventType: string) => {
|
mockClient.getAccountData.mockImplementation((eventType: string) => {
|
||||||
const content = {
|
const content = {
|
||||||
name: "Element Web",
|
name: "Element Web",
|
||||||
|
@ -304,7 +311,9 @@ describe("<SessionManagerTab />", () => {
|
||||||
});
|
});
|
||||||
|
|
||||||
it("renders devices without available client information without error", async () => {
|
it("renders devices without available client information without error", async () => {
|
||||||
mockClient.getDevices.mockResolvedValue({ devices: [alicesDevice, alicesMobileDevice] });
|
mockClient.getDevices.mockResolvedValue({
|
||||||
|
devices: [alicesDevice, alicesMobileDevice],
|
||||||
|
});
|
||||||
|
|
||||||
const { getByTestId, queryByTestId } = render(getComponent());
|
const { getByTestId, queryByTestId } = render(getComponent());
|
||||||
|
|
||||||
|
@ -342,7 +351,9 @@ describe("<SessionManagerTab />", () => {
|
||||||
});
|
});
|
||||||
|
|
||||||
it("goes to filtered list from security recommendations", async () => {
|
it("goes to filtered list from security recommendations", async () => {
|
||||||
mockClient.getDevices.mockResolvedValue({ devices: [alicesDevice, alicesMobileDevice] });
|
mockClient.getDevices.mockResolvedValue({
|
||||||
|
devices: [alicesDevice, alicesMobileDevice],
|
||||||
|
});
|
||||||
const { getByTestId, container } = render(getComponent());
|
const { getByTestId, container } = render(getComponent());
|
||||||
|
|
||||||
await act(async () => {
|
await act(async () => {
|
||||||
|
@ -375,7 +386,9 @@ describe("<SessionManagerTab />", () => {
|
||||||
});
|
});
|
||||||
|
|
||||||
it("renders current session section with an unverified session", async () => {
|
it("renders current session section with an unverified session", async () => {
|
||||||
mockClient.getDevices.mockResolvedValue({ devices: [alicesDevice, alicesMobileDevice] });
|
mockClient.getDevices.mockResolvedValue({
|
||||||
|
devices: [alicesDevice, alicesMobileDevice],
|
||||||
|
});
|
||||||
const { getByTestId } = render(getComponent());
|
const { getByTestId } = render(getComponent());
|
||||||
|
|
||||||
await act(async () => {
|
await act(async () => {
|
||||||
|
@ -386,7 +399,9 @@ describe("<SessionManagerTab />", () => {
|
||||||
});
|
});
|
||||||
|
|
||||||
it("opens encryption setup dialog when verifiying current session", async () => {
|
it("opens encryption setup dialog when verifiying current session", async () => {
|
||||||
mockClient.getDevices.mockResolvedValue({ devices: [alicesDevice, alicesMobileDevice] });
|
mockClient.getDevices.mockResolvedValue({
|
||||||
|
devices: [alicesDevice, alicesMobileDevice],
|
||||||
|
});
|
||||||
const { getByTestId } = render(getComponent());
|
const { getByTestId } = render(getComponent());
|
||||||
const modalSpy = jest.spyOn(Modal, "createDialog");
|
const modalSpy = jest.spyOn(Modal, "createDialog");
|
||||||
|
|
||||||
|
@ -401,7 +416,9 @@ describe("<SessionManagerTab />", () => {
|
||||||
});
|
});
|
||||||
|
|
||||||
it("renders current session section with a verified session", async () => {
|
it("renders current session section with a verified session", async () => {
|
||||||
mockClient.getDevices.mockResolvedValue({ devices: [alicesDevice, alicesMobileDevice] });
|
mockClient.getDevices.mockResolvedValue({
|
||||||
|
devices: [alicesDevice, alicesMobileDevice],
|
||||||
|
});
|
||||||
mockClient.getStoredDevice.mockImplementation(() => new DeviceInfo(alicesDevice.device_id));
|
mockClient.getStoredDevice.mockImplementation(() => new DeviceInfo(alicesDevice.device_id));
|
||||||
mockCrossSigningInfo.checkDeviceTrust.mockReturnValue(new DeviceTrustLevel(true, true, false, false));
|
mockCrossSigningInfo.checkDeviceTrust.mockReturnValue(new DeviceTrustLevel(true, true, false, false));
|
||||||
|
|
||||||
|
@ -415,7 +432,9 @@ describe("<SessionManagerTab />", () => {
|
||||||
});
|
});
|
||||||
|
|
||||||
it("expands current session details", async () => {
|
it("expands current session details", async () => {
|
||||||
mockClient.getDevices.mockResolvedValue({ devices: [alicesDevice, alicesMobileDevice] });
|
mockClient.getDevices.mockResolvedValue({
|
||||||
|
devices: [alicesDevice, alicesMobileDevice],
|
||||||
|
});
|
||||||
const { getByTestId } = render(getComponent());
|
const { getByTestId } = render(getComponent());
|
||||||
|
|
||||||
await act(async () => {
|
await act(async () => {
|
||||||
|
@ -499,7 +518,9 @@ describe("<SessionManagerTab />", () => {
|
||||||
const modalSpy = jest.spyOn(Modal, "createDialog");
|
const modalSpy = jest.spyOn(Modal, "createDialog");
|
||||||
|
|
||||||
// make the current device verified
|
// make the current device verified
|
||||||
mockClient.getDevices.mockResolvedValue({ devices: [alicesDevice, alicesMobileDevice] });
|
mockClient.getDevices.mockResolvedValue({
|
||||||
|
devices: [alicesDevice, alicesMobileDevice],
|
||||||
|
});
|
||||||
mockClient.getStoredDevice.mockImplementation((_userId, deviceId) => new DeviceInfo(deviceId));
|
mockClient.getStoredDevice.mockImplementation((_userId, deviceId) => new DeviceInfo(deviceId));
|
||||||
mockCrossSigningInfo.checkDeviceTrust.mockImplementation((_userId, { deviceId }) => {
|
mockCrossSigningInfo.checkDeviceTrust.mockImplementation((_userId, { deviceId }) => {
|
||||||
if (deviceId === alicesDevice.device_id) {
|
if (deviceId === alicesDevice.device_id) {
|
||||||
|
@ -524,7 +545,9 @@ describe("<SessionManagerTab />", () => {
|
||||||
});
|
});
|
||||||
|
|
||||||
it("does not allow device verification on session that do not support encryption", async () => {
|
it("does not allow device verification on session that do not support encryption", async () => {
|
||||||
mockClient.getDevices.mockResolvedValue({ devices: [alicesDevice, alicesMobileDevice] });
|
mockClient.getDevices.mockResolvedValue({
|
||||||
|
devices: [alicesDevice, alicesMobileDevice],
|
||||||
|
});
|
||||||
mockClient.getStoredDevice.mockImplementation((_userId, deviceId) => new DeviceInfo(deviceId));
|
mockClient.getStoredDevice.mockImplementation((_userId, deviceId) => new DeviceInfo(deviceId));
|
||||||
mockCrossSigningInfo.checkDeviceTrust.mockImplementation((_userId, { deviceId }) => {
|
mockCrossSigningInfo.checkDeviceTrust.mockImplementation((_userId, { deviceId }) => {
|
||||||
// current session verified = able to verify other sessions
|
// current session verified = able to verify other sessions
|
||||||
|
@ -556,7 +579,9 @@ describe("<SessionManagerTab />", () => {
|
||||||
const modalSpy = jest.spyOn(Modal, "createDialog");
|
const modalSpy = jest.spyOn(Modal, "createDialog");
|
||||||
|
|
||||||
// make the current device verified
|
// make the current device verified
|
||||||
mockClient.getDevices.mockResolvedValue({ devices: [alicesDevice, alicesMobileDevice] });
|
mockClient.getDevices.mockResolvedValue({
|
||||||
|
devices: [alicesDevice, alicesMobileDevice],
|
||||||
|
});
|
||||||
mockClient.getStoredDevice.mockImplementation((_userId, deviceId) => new DeviceInfo(deviceId));
|
mockClient.getStoredDevice.mockImplementation((_userId, deviceId) => new DeviceInfo(deviceId));
|
||||||
mockCrossSigningInfo.checkDeviceTrust.mockImplementation((_userId, { deviceId }) => {
|
mockCrossSigningInfo.checkDeviceTrust.mockImplementation((_userId, { deviceId }) => {
|
||||||
if (deviceId === alicesDevice.device_id) {
|
if (deviceId === alicesDevice.device_id) {
|
||||||
|
@ -594,7 +619,9 @@ describe("<SessionManagerTab />", () => {
|
||||||
it("Signs out of current device", async () => {
|
it("Signs out of current device", async () => {
|
||||||
const modalSpy = jest.spyOn(Modal, "createDialog");
|
const modalSpy = jest.spyOn(Modal, "createDialog");
|
||||||
|
|
||||||
mockClient.getDevices.mockResolvedValue({ devices: [alicesDevice] });
|
mockClient.getDevices.mockResolvedValue({
|
||||||
|
devices: [alicesDevice],
|
||||||
|
});
|
||||||
const { getByTestId } = render(getComponent());
|
const { getByTestId } = render(getComponent());
|
||||||
|
|
||||||
await act(async () => {
|
await act(async () => {
|
||||||
|
@ -613,7 +640,9 @@ describe("<SessionManagerTab />", () => {
|
||||||
|
|
||||||
it("Signs out of current device from kebab menu", async () => {
|
it("Signs out of current device from kebab menu", async () => {
|
||||||
const modalSpy = jest.spyOn(Modal, "createDialog");
|
const modalSpy = jest.spyOn(Modal, "createDialog");
|
||||||
mockClient.getDevices.mockResolvedValue({ devices: [alicesDevice] });
|
mockClient.getDevices.mockResolvedValue({
|
||||||
|
devices: [alicesDevice],
|
||||||
|
});
|
||||||
const { getByTestId, getByLabelText } = render(getComponent());
|
const { getByTestId, getByLabelText } = render(getComponent());
|
||||||
|
|
||||||
await act(async () => {
|
await act(async () => {
|
||||||
|
@ -628,7 +657,9 @@ describe("<SessionManagerTab />", () => {
|
||||||
});
|
});
|
||||||
|
|
||||||
it("does not render sign out other devices option when only one device", async () => {
|
it("does not render sign out other devices option when only one device", async () => {
|
||||||
mockClient.getDevices.mockResolvedValue({ devices: [alicesDevice] });
|
mockClient.getDevices.mockResolvedValue({
|
||||||
|
devices: [alicesDevice],
|
||||||
|
});
|
||||||
const { getByTestId, queryByLabelText } = render(getComponent());
|
const { getByTestId, queryByLabelText } = render(getComponent());
|
||||||
|
|
||||||
await act(async () => {
|
await act(async () => {
|
||||||
|
@ -670,9 +701,7 @@ describe("<SessionManagerTab />", () => {
|
||||||
// @ts-ignore setup mock
|
// @ts-ignore setup mock
|
||||||
mockClient.store = {
|
mockClient.store = {
|
||||||
// @ts-ignore setup mock
|
// @ts-ignore setup mock
|
||||||
accountData: {
|
accountData: new Map([[mobileDeviceClientInfo.getType(), mobileDeviceClientInfo]]),
|
||||||
[mobileDeviceClientInfo.getType()]: mobileDeviceClientInfo,
|
|
||||||
},
|
|
||||||
};
|
};
|
||||||
|
|
||||||
mockClient.getDevices
|
mockClient.getDevices
|
||||||
|
@ -702,7 +731,10 @@ describe("<SessionManagerTab />", () => {
|
||||||
});
|
});
|
||||||
|
|
||||||
describe("other devices", () => {
|
describe("other devices", () => {
|
||||||
const interactiveAuthError = { httpStatus: 401, data: { flows: [{ stages: ["m.login.password"] }] } };
|
const interactiveAuthError = {
|
||||||
|
httpStatus: 401,
|
||||||
|
data: { flows: [{ stages: ["m.login.password"] }] },
|
||||||
|
};
|
||||||
|
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
mockClient.deleteMultipleDevices.mockReset();
|
mockClient.deleteMultipleDevices.mockReset();
|
||||||
|
@ -711,9 +743,13 @@ describe("<SessionManagerTab />", () => {
|
||||||
it("deletes a device when interactive auth is not required", async () => {
|
it("deletes a device when interactive auth is not required", async () => {
|
||||||
mockClient.deleteMultipleDevices.mockResolvedValue({});
|
mockClient.deleteMultipleDevices.mockResolvedValue({});
|
||||||
mockClient.getDevices
|
mockClient.getDevices
|
||||||
.mockResolvedValueOnce({ devices: [alicesDevice, alicesMobileDevice, alicesOlderMobileDevice] })
|
.mockResolvedValueOnce({
|
||||||
|
devices: [alicesDevice, alicesMobileDevice, alicesOlderMobileDevice],
|
||||||
|
})
|
||||||
// pretend it was really deleted on refresh
|
// pretend it was really deleted on refresh
|
||||||
.mockResolvedValueOnce({ devices: [alicesDevice, alicesOlderMobileDevice] });
|
.mockResolvedValueOnce({
|
||||||
|
devices: [alicesDevice, alicesOlderMobileDevice],
|
||||||
|
});
|
||||||
|
|
||||||
const { getByTestId } = render(getComponent());
|
const { getByTestId } = render(getComponent());
|
||||||
|
|
||||||
|
@ -784,9 +820,13 @@ describe("<SessionManagerTab />", () => {
|
||||||
.mockResolvedValueOnce({});
|
.mockResolvedValueOnce({});
|
||||||
|
|
||||||
mockClient.getDevices
|
mockClient.getDevices
|
||||||
.mockResolvedValueOnce({ devices: [alicesDevice, alicesMobileDevice, alicesOlderMobileDevice] })
|
.mockResolvedValueOnce({
|
||||||
|
devices: [alicesDevice, alicesMobileDevice, alicesOlderMobileDevice],
|
||||||
|
})
|
||||||
// pretend it was really deleted on refresh
|
// pretend it was really deleted on refresh
|
||||||
.mockResolvedValueOnce({ devices: [alicesDevice, alicesOlderMobileDevice] });
|
.mockResolvedValueOnce({
|
||||||
|
devices: [alicesDevice, alicesOlderMobileDevice],
|
||||||
|
});
|
||||||
|
|
||||||
const { getByTestId, getByLabelText } = render(getComponent());
|
const { getByTestId, getByLabelText } = render(getComponent());
|
||||||
|
|
||||||
|
@ -820,7 +860,9 @@ describe("<SessionManagerTab />", () => {
|
||||||
|
|
||||||
// fill password and submit for interactive auth
|
// fill password and submit for interactive auth
|
||||||
act(() => {
|
act(() => {
|
||||||
fireEvent.change(getByLabelText("Password"), { target: { value: "topsecret" } });
|
fireEvent.change(getByLabelText("Password"), {
|
||||||
|
target: { value: "topsecret" },
|
||||||
|
});
|
||||||
fireEvent.submit(getByLabelText("Password"));
|
fireEvent.submit(getByLabelText("Password"));
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -1061,7 +1103,9 @@ describe("<SessionManagerTab />", () => {
|
||||||
|
|
||||||
await updateDeviceName(getByTestId, alicesDevice, "");
|
await updateDeviceName(getByTestId, alicesDevice, "");
|
||||||
|
|
||||||
expect(mockClient.setDeviceDetails).toHaveBeenCalledWith(alicesDevice.device_id, { display_name: "" });
|
expect(mockClient.setDeviceDetails).toHaveBeenCalledWith(alicesDevice.device_id, {
|
||||||
|
display_name: "",
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
it("displays an error when session display name fails to save", async () => {
|
it("displays an error when session display name fails to save", async () => {
|
||||||
|
|
|
@ -16,7 +16,7 @@ limitations under the License.
|
||||||
|
|
||||||
import { mocked, Mocked } from "jest-mock";
|
import { mocked, Mocked } from "jest-mock";
|
||||||
import { MatrixClient } from "matrix-js-sdk/src/matrix";
|
import { MatrixClient } from "matrix-js-sdk/src/matrix";
|
||||||
import { IDevice } from "matrix-js-sdk/src/crypto/deviceinfo";
|
import { DeviceInfo } from "matrix-js-sdk/src/crypto/deviceinfo";
|
||||||
import { RoomType } from "matrix-js-sdk/src/@types/event";
|
import { RoomType } from "matrix-js-sdk/src/@types/event";
|
||||||
|
|
||||||
import { stubClient, setupAsyncStoreWithClient, mockPlatformPeg } from "./test-utils";
|
import { stubClient, setupAsyncStoreWithClient, mockPlatformPeg } from "./test-utils";
|
||||||
|
@ -147,12 +147,16 @@ describe("createRoom", () => {
|
||||||
});
|
});
|
||||||
|
|
||||||
describe("canEncryptToAllUsers", () => {
|
describe("canEncryptToAllUsers", () => {
|
||||||
const trueUser = {
|
const trueUser = new Map([
|
||||||
"@goodUser:localhost": {
|
[
|
||||||
DEV1: {} as unknown as IDevice,
|
"@goodUser:localhost",
|
||||||
DEV2: {} as unknown as IDevice,
|
new Map([
|
||||||
},
|
["DEV1", {} as unknown as DeviceInfo],
|
||||||
};
|
["DEV2", {} as unknown as DeviceInfo],
|
||||||
|
]),
|
||||||
|
],
|
||||||
|
]);
|
||||||
|
|
||||||
const falseUser = {
|
const falseUser = {
|
||||||
"@badUser:localhost": {},
|
"@badUser:localhost": {},
|
||||||
};
|
};
|
||||||
|
|
|
@ -79,9 +79,9 @@ describe("AutoRageshakeStore", () => {
|
||||||
[
|
[
|
||||||
[
|
[
|
||||||
"im.vector.auto_rs_request",
|
"im.vector.auto_rs_request",
|
||||||
{
|
Map {
|
||||||
"@userId:matrix.org": {
|
"messageContent.user_id" => Map {
|
||||||
"undefined": {
|
undefined => {
|
||||||
"device_id": undefined,
|
"device_id": undefined,
|
||||||
"event_id": "utd_event_id",
|
"event_id": "utd_event_id",
|
||||||
"recipient_rageshake": undefined,
|
"recipient_rageshake": undefined,
|
||||||
|
|
|
@ -184,10 +184,18 @@ describe("StopGapWidgetDriver", () => {
|
||||||
const aliceMobile = new DeviceInfo("aliceMobile");
|
const aliceMobile = new DeviceInfo("aliceMobile");
|
||||||
const bobDesktop = new DeviceInfo("bobDesktop");
|
const bobDesktop = new DeviceInfo("bobDesktop");
|
||||||
|
|
||||||
mocked(client.crypto!.deviceList).downloadKeys.mockResolvedValue({
|
mocked(client.crypto.deviceList).downloadKeys.mockResolvedValue(
|
||||||
"@alice:example.org": { aliceWeb, aliceMobile },
|
new Map([
|
||||||
"@bob:example.org": { bobDesktop },
|
[
|
||||||
});
|
"@alice:example.org",
|
||||||
|
new Map([
|
||||||
|
["aliceWeb", aliceWeb],
|
||||||
|
["aliceMobile", aliceMobile],
|
||||||
|
]),
|
||||||
|
],
|
||||||
|
["@bob:example.org", new Map([["bobDesktop", bobDesktop]])],
|
||||||
|
]),
|
||||||
|
);
|
||||||
|
|
||||||
await driver.sendToDevice("org.example.foo", true, contentMap);
|
await driver.sendToDevice("org.example.foo", true, contentMap);
|
||||||
expect(client.encryptAndSendToDevices.mock.calls).toMatchSnapshot();
|
expect(client.encryptAndSendToDevices.mock.calls).toMatchSnapshot();
|
||||||
|
|
Loading…
Reference in a new issue