From 2c0806392538b928e911068ce4d4199d9c3ba418 Mon Sep 17 00:00:00 2001 From: Alexander <57069715+Odyssey346@users.noreply.github.com> Date: Sat, 30 Apr 2022 17:15:20 +0200 Subject: [PATCH 1/8] Add pointer if you hover over location map (#8451) --- res/css/views/messages/_MLocationBody.scss | 1 + 1 file changed, 1 insertion(+) diff --git a/res/css/views/messages/_MLocationBody.scss b/res/css/views/messages/_MLocationBody.scss index 9c7e2767d74..72202ca6e1b 100644 --- a/res/css/views/messages/_MLocationBody.scss +++ b/res/css/views/messages/_MLocationBody.scss @@ -21,6 +21,7 @@ limitations under the License. z-index: 0; // keeps the entire map under the message action bar border-radius: $timeline-image-border-radius; + cursor: pointer; } } From ad2d3a36831c01e6b33fc78a78ccb3a3a50c7e6f Mon Sep 17 00:00:00 2001 From: Yaya Usman <38439166+yaya-usman@users.noreply.github.com> Date: Sat, 30 Apr 2022 18:36:03 +0300 Subject: [PATCH 2/8] Fixes "space panel kebab menu is rendered out of view on sub spaces" (#8350) * fixes space kebab menu out of view * update-eslint: space kebab menu out of view * update: space kebap menu * update: space panel collapse behavior and kebap menu fix * update: space panel collapse behavior and kebap menu fix * updated fix --- res/css/structures/_SpacePanel.scss | 2 ++ 1 file changed, 2 insertions(+) diff --git a/res/css/structures/_SpacePanel.scss b/res/css/structures/_SpacePanel.scss index 1cbc7cd7352..9d23dd5fc93 100644 --- a/res/css/structures/_SpacePanel.scss +++ b/res/css/structures/_SpacePanel.scss @@ -155,6 +155,7 @@ $activeBorderColor: $primary-content; border-radius: 12px; padding: 4px; width: calc(100% - 32px); + min-width: 0; } .mx_SpaceButton_name { @@ -274,6 +275,7 @@ $activeBorderColor: $primary-content; display: flex; flex-direction: column; max-width: 250px; + min-width: 0; flex-grow: 1; .mx_BaseAvatar:not(.mx_UserMenu_userAvatar_BaseAvatar) .mx_BaseAvatar_initial { From cea75fde277ab051a23435e14fbfbeddab4cfef2 Mon Sep 17 00:00:00 2001 From: Suguru Hirahara Date: Mon, 2 May 2022 01:08:42 +0000 Subject: [PATCH 3/8] Fix text link buttons on UserInfo panel (#8247) * Fix text link buttons on UserInfo (right) panel - Fix link button styling - Replace className="mx_linkButton" with kind="link" - Remove style rules required for mx_linkButton - Align E2E icon and devices on the device list - Replace margin with gap Signed-off-by: Suguru Hirahara * Spacing variables Signed-off-by: Suguru Hirahara * Replace link_inline with link Signed-off-by: Suguru Hirahara * Remove a redundant rule Signed-off-by: Suguru Hirahara * Wrap verifyButton Signed-off-by: Suguru Hirahara --- res/css/views/right_panel/_UserInfo.scss | 27 +++---- src/components/views/right_panel/UserInfo.tsx | 70 +++++++++++++++---- 2 files changed, 71 insertions(+), 26 deletions(-) diff --git a/res/css/views/right_panel/_UserInfo.scss b/res/css/views/right_panel/_UserInfo.scss index 3b7a51797f9..15cf0cdc1ec 100644 --- a/res/css/views/right_panel/_UserInfo.scss +++ b/res/css/views/right_panel/_UserInfo.scss @@ -52,6 +52,10 @@ limitations under the License. .mx_UserInfo_container { padding: 8px 16px; + + .mx_UserInfo_container_verifyButton { + margin-top: $spacing-8; + } } .mx_UserInfo_separator { @@ -193,10 +197,7 @@ limitations under the License. } .mx_UserInfo_field { - cursor: pointer; - color: $accent; line-height: $font-16px; - margin: 8px 0; &.mx_UserInfo_destructive { color: $alert; @@ -228,14 +229,18 @@ limitations under the License. padding-bottom: 0; > :not(h3) { - margin-left: 8px; + margin-inline-start: $spacing-8; + display: flex; + flex-flow: column; + align-items: flex-start; + row-gap: $spacing-8; } } .mx_UserInfo_devices { .mx_UserInfo_device { display: flex; - margin: 8px 0; + margin: $spacing-8 0; &.mx_UserInfo_device_verified { .mx_UserInfo_device_trusted { @@ -250,7 +255,7 @@ limitations under the License. .mx_UserInfo_device_name { flex: 1; - margin-right: 5px; + margin: 0 5px; word-break: break-word; } } @@ -259,20 +264,16 @@ limitations under the License. .mx_E2EIcon { // don't squeeze flex: 0 0 auto; - margin: 2px 5px 0 0; + margin: 0; width: 12px; height: 12px; } .mx_UserInfo_expand { - display: flex; - margin-top: 11px; + column-gap: 5px; // cf: mx_UserInfo_device_name + margin-bottom: 11px; } } - - .mx_AccessibleButton.mx_AccessibleButton_hasKind { - padding: 8px 18px; - } } .mx_UserInfo.mx_UserInfo_smallAvatar { diff --git a/src/components/views/right_panel/UserInfo.tsx b/src/components/views/right_panel/UserInfo.tsx index c9685c24670..9d17c5237d1 100644 --- a/src/components/views/right_panel/UserInfo.tsx +++ b/src/components/views/right_panel/UserInfo.tsx @@ -292,13 +292,17 @@ function DevicesSection({ devices, userId, loading }: { devices: IDevice[], user let expandButton; if (expandSectionDevices.length) { if (isExpanded) { - expandButton = ( setExpanded(false)} >
{ expandHideCaption }
); } else { - expandButton = ( setExpanded(true)} >
@@ -331,6 +335,7 @@ const MessageButton = ({ userId }: { userId: string }) => { return ( { if (busy) return; setBusy(true); @@ -383,6 +388,7 @@ const UserOptionsSection: React.FC<{ ignoreButton = ( @@ -413,14 +419,22 @@ const UserOptionsSection: React.FC<{ const room = cli.getRoom(member.roomId); if (room?.getEventReadUpTo(member.userId)) { readReceiptButton = ( - + { _t('Jump to read receipt') } ); } insertPillButton = ( - + { _t('Mention') } ); @@ -448,7 +462,11 @@ const UserOptionsSection: React.FC<{ }; inviteUserButton = ( - + { _t('Invite') } ); @@ -456,7 +474,11 @@ const UserOptionsSection: React.FC<{ } const shareUserButton = ( - + { _t('Share Link to User') } ); @@ -624,7 +646,11 @@ const RoomKickButton = ({ room, member, startUpdating, stopUpdating }: Omit + return { kickLabel } ; }; @@ -642,7 +668,11 @@ const RedactMessagesButton: React.FC = ({ member }) => { }); }; - return + return { _t("Remove recent messages") } ; }; @@ -739,7 +769,11 @@ const BanToggleButton = ({ room, member, startUpdating, stopUpdating }: Omit + return { label } ; }; @@ -809,7 +843,11 @@ const MuteToggleButton: React.FC = ({ member, room, powerLevels, }); const muteLabel = muted ? _t("Unmute") : _t("Mute"); - return + return { muteLabel } ; }; @@ -1212,7 +1250,11 @@ const BasicUserInfo: React.FC<{ // FIXME this should be using cli instead of MatrixClientPeg.matrixClient if (isSynapseAdmin && member.userId.endsWith(`:${MatrixClientPeg.getHomeserverName()}`)) { synapseDeactivateButton = ( - + { _t("Deactivate user") } ); @@ -1290,8 +1332,9 @@ const BasicUserInfo: React.FC<{ if (canVerify) { if (hasCrossSigningKeys !== undefined) { // Note: mx_UserInfo_verifyButton is for the end-to-end tests - verifyButton = ( + verifyButton = (
{ if (hasCrossSigningKeys) { @@ -1303,7 +1346,7 @@ const BasicUserInfo: React.FC<{ > { _t("Verify") } - ); +
); } else if (!showDeviceListSpinner) { // HACK: only show a spinner if the device section spinner is not shown, // to avoid showing a double spinner @@ -1316,6 +1359,7 @@ const BasicUserInfo: React.FC<{ if (member.userId == cli.getUserId()) { editDevices = (
{ dis.dispatch({ From d294dad04da5e7126f705a1fea72f557c83e43ea Mon Sep 17 00:00:00 2001 From: Emmanuel <63562663+EECvision@users.noreply.github.com> Date: Mon, 2 May 2022 02:09:59 +0100 Subject: [PATCH 4/8] fix timeline search with empty text box should do nothing (#8262) * fix timeline search with empty text box should do nothing * test SearchBar component * fix lint error * Update SearchBar-test.tsx Co-authored-by: Travis Ralston --- src/components/views/rooms/SearchBar.tsx | 1 + .../components/views/rooms/SearchBar-test.tsx | 134 ++++++++++++++++++ 2 files changed, 135 insertions(+) create mode 100644 test/components/views/rooms/SearchBar-test.tsx diff --git a/src/components/views/rooms/SearchBar.tsx b/src/components/views/rooms/SearchBar.tsx index c42131bba04..20a9e081a0c 100644 --- a/src/components/views/rooms/SearchBar.tsx +++ b/src/components/views/rooms/SearchBar.tsx @@ -78,6 +78,7 @@ export default class SearchBar extends React.Component { } private onSearch = (): void => { + if (!this.searchTerm.current.value.trim()) return; this.props.onSearch(this.searchTerm.current.value, this.state.scope); }; diff --git a/test/components/views/rooms/SearchBar-test.tsx b/test/components/views/rooms/SearchBar-test.tsx new file mode 100644 index 00000000000..b72f838bb9a --- /dev/null +++ b/test/components/views/rooms/SearchBar-test.tsx @@ -0,0 +1,134 @@ +/* +Copyright 2022 Emmanuel Ezeka + +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 { mount } from "enzyme"; + +import DesktopBuildsNotice from "../../../../src/components/views/elements/DesktopBuildsNotice"; +import { PosthogScreenTracker } from "../../../../src/PosthogTrackers"; +import SearchBar, { SearchScope } from "../../../../src/components/views/rooms/SearchBar"; +import { KeyBindingAction } from "../../../../src/accessibility/KeyboardShortcuts"; + +let mockCurrentEvent = KeyBindingAction.Enter; +const mockWarningKind = true; +let wrapper: any = null; + +const searchProps = { + onCancelClick: jest.fn(), + onSearch: jest.fn(), + searchInProgress: false, + isRoomEncrypted: false, +}; + +jest.mock("../../../../src/KeyBindingsManager", () => ({ + __esModule: true, + getKeyBindingsManager: jest.fn(() => ( + { getAccessibilityAction: jest.fn(() => mockCurrentEvent) })), +})); + +/** mock out DesktopBuildsNotice component so it doesn't affect the result of our test */ +jest.mock('../../../../src/components/views/elements/DesktopBuildsNotice', () => ({ + __esModule: true, + WarningKind: { + get Search() { // eslint-disable-line @typescript-eslint/naming-convention + return mockWarningKind; + }, + }, + default: jest.fn(({ children }) => ( +
{ children }
+ )), +})); + +/** mock out PosthogTrackers component so it doesn't affect the result of our test */ +jest.mock('../../../../src/PosthogTrackers', () => ({ + __esModule: true, + PosthogScreenTracker: jest.fn(({ children }) => ( +
{ children }
+ )), +})); + +describe("SearchBar", () => { + beforeEach(() => { + wrapper = mount(); + }); + + afterEach(() => { + wrapper.unmount(); + searchProps.onCancelClick.mockClear(); + searchProps.onSearch.mockClear(); + }); + + it("must render child components and pass necessary props", () => { + const postHogScreenTracker = wrapper.find(PosthogScreenTracker); + const desktopBuildNotice = wrapper.find(DesktopBuildsNotice); + + expect(postHogScreenTracker.length).toBe(1); + expect(desktopBuildNotice.length).toBe(1); + expect(postHogScreenTracker.props().screenName).toEqual("RoomSearch"); + expect(desktopBuildNotice.props().isRoomEncrypted).toEqual(searchProps.isRoomEncrypted); + expect(desktopBuildNotice.props().kind).toEqual(mockWarningKind); + }); + + it("must not search when input value is empty", () => { + const roomButtons = wrapper.find(".mx_SearchBar_button"); + const searchButton = wrapper.find(".mx_SearchBar_searchButton"); + + expect(roomButtons.length).toEqual(4); + + searchButton.at(0).simulate("click"); + roomButtons.at(0).simulate("click"); + roomButtons.at(2).simulate("click"); + + expect(searchProps.onSearch).not.toHaveBeenCalled(); + }); + + it("must trigger onSearch when value is not empty", () => { + const searchValue = "abcd"; + + const roomButtons = wrapper.find(".mx_SearchBar_button"); + const searchButton = wrapper.find(".mx_SearchBar_searchButton"); + const input = wrapper.find(".mx_SearchBar_input input"); + input.instance().value = searchValue; + + expect(roomButtons.length).toEqual(4); + + searchButton.at(0).simulate("click"); + + expect(searchProps.onSearch).toHaveBeenCalledTimes(1); + expect(searchProps.onSearch).toHaveBeenNthCalledWith(1, searchValue, SearchScope.Room); + + roomButtons.at(0).simulate("click"); + + expect(searchProps.onSearch).toHaveBeenCalledTimes(2); + expect(searchProps.onSearch).toHaveBeenNthCalledWith(2, searchValue, SearchScope.Room); + + roomButtons.at(2).simulate("click"); + + expect(searchProps.onSearch).toHaveBeenCalledTimes(3); + expect(searchProps.onSearch).toHaveBeenNthCalledWith(3, searchValue, SearchScope.All); + }); + + it("cancel button and esc key should trigger onCancelClick", () => { + mockCurrentEvent = KeyBindingAction.Escape; + const cancelButton = wrapper.find(".mx_SearchBar_cancel"); + const input = wrapper.find(".mx_SearchBar_input input"); + input.simulate("focus"); + input.simulate("keydown", { key: "ESC" }); + cancelButton.at(0).simulate("click"); + + expect(searchProps.onCancelClick).toHaveBeenCalledTimes(2); + }); +}); From 3a245a0cbe59f17180671de28450ba75e08b14cd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=A0imon=20Brandner?= Date: Mon, 2 May 2022 03:38:36 +0200 Subject: [PATCH 5/8] Fix jump to bottom button being always displayed in non-overflowing timelines (#8460) --- src/components/structures/RoomView.tsx | 11 +++-------- src/components/structures/TimelinePanel.tsx | 2 +- src/contexts/RoomContext.ts | 2 -- .../views/rooms/MessageComposerButtons-test.tsx | 1 - 4 files changed, 4 insertions(+), 12 deletions(-) diff --git a/src/components/structures/RoomView.tsx b/src/components/structures/RoomView.tsx index 761dd9b496f..956fee0060c 100644 --- a/src/components/structures/RoomView.tsx +++ b/src/components/structures/RoomView.tsx @@ -179,9 +179,7 @@ export interface IRoomState { // this is true if we are fully scrolled-down, and are looking at // the end of the live timeline. It has the effect of hiding the // 'scroll to bottom' knob, among a couple of other things. - atEndOfLiveTimeline: boolean; - // used by componentDidUpdate to avoid unnecessary checks - atEndOfLiveTimelineInit: boolean; + atEndOfLiveTimeline?: boolean; showTopUnreadMessagesBar: boolean; statusBarVisible: boolean; // We load this later by asking the js-sdk to suggest a version for us. @@ -257,8 +255,6 @@ export class RoomView extends React.Component { isPeeking: false, showRightPanel: false, joining: false, - atEndOfLiveTimeline: true, - atEndOfLiveTimelineInit: false, showTopUnreadMessagesBar: false, statusBarVisible: false, canReact: false, @@ -692,9 +688,8 @@ export class RoomView extends React.Component { // in render() prevents the ref from being set on first mount, so we try and // catch the messagePanel when it does mount. Because we only want the ref once, // we use a boolean flag to avoid duplicate work. - if (this.messagePanel && !this.state.atEndOfLiveTimelineInit) { + if (this.messagePanel && this.state.atEndOfLiveTimeline === undefined) { this.setState({ - atEndOfLiveTimelineInit: true, atEndOfLiveTimeline: this.messagePanel.isAtEndOfLiveTimeline(), }); } @@ -2102,7 +2097,7 @@ export class RoomView extends React.Component { } let jumpToBottom; // Do not show JumpToBottomButton if we have search results showing, it makes no sense - if (!this.state.atEndOfLiveTimeline && !this.state.searchResults) { + if (this.state.atEndOfLiveTimeline === false && !this.state.searchResults) { jumpToBottom = ( 0} numUnreadMessages={this.state.numUnreadMessages} diff --git a/src/components/structures/TimelinePanel.tsx b/src/components/structures/TimelinePanel.tsx index 7201e3c6f2e..59b87c639ac 100644 --- a/src/components/structures/TimelinePanel.tsx +++ b/src/components/structures/TimelinePanel.tsx @@ -1043,7 +1043,7 @@ class TimelinePanel extends React.Component { /* return true if the content is fully scrolled down and we are * at the end of the live timeline. */ - public isAtEndOfLiveTimeline = (): boolean => { + public isAtEndOfLiveTimeline = (): boolean | undefined => { return this.messagePanel.current?.isAtBottom() && this.timelineWindow && !this.timelineWindow.canPaginate(EventTimeline.FORWARDS); diff --git a/src/contexts/RoomContext.ts b/src/contexts/RoomContext.ts index 9c44553a983..cbf56e8a4a8 100644 --- a/src/contexts/RoomContext.ts +++ b/src/contexts/RoomContext.ts @@ -41,8 +41,6 @@ const RoomContext = createContext({ isPeeking: false, showRightPanel: true, joining: false, - atEndOfLiveTimeline: true, - atEndOfLiveTimelineInit: false, showTopUnreadMessagesBar: false, statusBarVisible: false, canReact: false, diff --git a/test/components/views/rooms/MessageComposerButtons-test.tsx b/test/components/views/rooms/MessageComposerButtons-test.tsx index 4abe3e36373..1ec08e455d0 100644 --- a/test/components/views/rooms/MessageComposerButtons-test.tsx +++ b/test/components/views/rooms/MessageComposerButtons-test.tsx @@ -216,7 +216,6 @@ function createRoomState(room: Room, narrow: boolean): IRoomState { showRightPanel: true, joining: false, atEndOfLiveTimeline: true, - atEndOfLiveTimelineInit: false, showTopUnreadMessagesBar: false, statusBarVisible: false, canReact: false, From 633229ca26382f2372b2140c59b452d3ab86bec6 Mon Sep 17 00:00:00 2001 From: Michael Telatynski <7t3chguy@gmail.com> Date: Mon, 2 May 2022 03:23:43 +0100 Subject: [PATCH 6/8] Clear local storage settings handler cache on logout (#8454) --- src/Lifecycle.ts | 2 + .../AbstractLocalStorageSettingsHandler.ts | 56 +++++++++++-------- 2 files changed, 36 insertions(+), 22 deletions(-) diff --git a/src/Lifecycle.ts b/src/Lifecycle.ts index f91158c38aa..6b7268d57ed 100644 --- a/src/Lifecycle.ts +++ b/src/Lifecycle.ts @@ -61,6 +61,7 @@ import { setSentryUser } from "./sentry"; import SdkConfig from "./SdkConfig"; import { DialogOpener } from "./utils/DialogOpener"; import { Action } from "./dispatcher/actions"; +import AbstractLocalStorageSettingsHandler from "./settings/handlers/AbstractLocalStorageSettingsHandler"; const HOMESERVER_URL_KEY = "mx_hs_url"; const ID_SERVER_URL_KEY = "mx_is_url"; @@ -878,6 +879,7 @@ async function clearStorage(opts?: { deleteEverything?: boolean }): Promise(); - private objectCache = new Map(); + // Shared cache between all subclass instances + private static itemCache = new Map(); + private static objectCache = new Map(); + private static storageListenerBound = false; + + private static onStorageEvent = (e: StorageEvent) => { + if (e.key === null) { + AbstractLocalStorageSettingsHandler.clear(); + } else { + AbstractLocalStorageSettingsHandler.itemCache.delete(e.key); + AbstractLocalStorageSettingsHandler.objectCache.delete(e.key); + } + }; + + // Expose the clear event for Lifecycle to call, the storage listener only fires for changes from other tabs + public static clear() { + AbstractLocalStorageSettingsHandler.itemCache.clear(); + AbstractLocalStorageSettingsHandler.objectCache.clear(); + } protected constructor() { super(); - // Listen for storage changes from other tabs to bust the cache - window.addEventListener("storage", (e: StorageEvent) => { - if (e.key === null) { - this.itemCache.clear(); - this.objectCache.clear(); - } else { - this.itemCache.delete(e.key); - this.objectCache.delete(e.key); - } - }); + if (!AbstractLocalStorageSettingsHandler.storageListenerBound) { + AbstractLocalStorageSettingsHandler.storageListenerBound = true; + // Listen for storage changes from other tabs to bust the cache + window.addEventListener("storage", AbstractLocalStorageSettingsHandler.onStorageEvent); + } } protected getItem(key: string): any { - if (!this.itemCache.has(key)) { + if (!AbstractLocalStorageSettingsHandler.itemCache.has(key)) { const value = localStorage.getItem(key); - this.itemCache.set(key, value); + AbstractLocalStorageSettingsHandler.itemCache.set(key, value); return value; } - return this.itemCache.get(key); + return AbstractLocalStorageSettingsHandler.itemCache.get(key); } protected getObject(key: string): T | null { - if (!this.objectCache.has(key)) { + if (!AbstractLocalStorageSettingsHandler.objectCache.has(key)) { try { const value = JSON.parse(localStorage.getItem(key)); - this.objectCache.set(key, value); + AbstractLocalStorageSettingsHandler.objectCache.set(key, value); return value; } catch (err) { console.error("Failed to parse localStorage object", err); @@ -61,24 +73,24 @@ export default abstract class AbstractLocalStorageSettingsHandler extends Settin } } - return this.objectCache.get(key) as T; + return AbstractLocalStorageSettingsHandler.objectCache.get(key) as T; } protected setItem(key: string, value: any): void { - this.itemCache.set(key, value); + AbstractLocalStorageSettingsHandler.itemCache.set(key, value); localStorage.setItem(key, value); } protected setObject(key: string, value: object): void { - this.objectCache.set(key, value); + AbstractLocalStorageSettingsHandler.objectCache.set(key, value); localStorage.setItem(key, JSON.stringify(value)); } // handles both items and objects protected removeItem(key: string): void { localStorage.removeItem(key); - this.itemCache.delete(key); - this.objectCache.delete(key); + AbstractLocalStorageSettingsHandler.itemCache.delete(key); + AbstractLocalStorageSettingsHandler.objectCache.delete(key); } public isSupported(): boolean { From 4a04be6d1c9370a923bc45c6ebc8f42e30b22447 Mon Sep 17 00:00:00 2001 From: Kerry Date: Mon, 2 May 2022 09:57:35 +0200 Subject: [PATCH 7/8] Test typescriptification continued (#8327) * test/utils/MegolmExportEncryption-test.js -> test/utils/MegolmExportEncryption-test.ts Signed-off-by: Kerry Archibald * test/utils/ShieldUtils-test.js - test/utils/ShieldUtils-test.ts Signed-off-by: Kerry Archibald * type fixes for ShieldUtils-test Signed-off-by: Kerry Archibald * test/DecryptionFailureTracker-test.js -> test/DecryptionFailureTracker-test.ts Signed-off-by: Kerry Archibald * remove unsupported assertion failure messages Signed-off-by: Kerry Archibald * fix ts issues in DecryptionFailureTracker Signed-off-by: Kerry Archibald * add mock restores Signed-off-by: Kerry Archibald * newline Signed-off-by: Kerry Archibald * remove commented decriptionfailuretracker code and test Signed-off-by: Kerry Archibald * make should aggregate error codes correctly pass Signed-off-by: Kerry Archibald * cheaters types in MegolmExportEncryption Signed-off-by: Kerry Archibald * lint Signed-off-by: Kerry Archibald * Revert "fix ts issues in DecryptionFailureTracker" This reverts commit 1ae748cc51088d60722320dbefae04a62310e2e1. Signed-off-by: Kerry Archibald * Revert "remove unsupported assertion failure messages" This reverts commit 7bd93d075c4d8d45befcbfd59c889782c9a44b48. * Revert "test/DecryptionFailureTracker-test.js -> test/DecryptionFailureTracker-test.ts" This reverts commit 1670025bd2af9a355c2761998202f602d61f242e. * revert change to DecryptionFailureTracker Signed-off-by: Kerry Archibald --- ...test.js => MegolmExportEncryption-test.ts} | 10 +++- ...hieldUtils-test.js => ShieldUtils-test.ts} | 59 ++++++++++++++----- 2 files changed, 50 insertions(+), 19 deletions(-) rename test/utils/{MegolmExportEncryption-test.js => MegolmExportEncryption-test.ts} (95%) rename test/utils/{ShieldUtils-test.js => ShieldUtils-test.ts} (83%) diff --git a/test/utils/MegolmExportEncryption-test.js b/test/utils/MegolmExportEncryption-test.ts similarity index 95% rename from test/utils/MegolmExportEncryption-test.js rename to test/utils/MegolmExportEncryption-test.ts index 8b16085efcc..4fb92a8f8bb 100644 --- a/test/utils/MegolmExportEncryption-test.js +++ b/test/utils/MegolmExportEncryption-test.ts @@ -20,7 +20,8 @@ import { Crypto } from "@peculiar/webcrypto"; const webCrypto = new Crypto(); -function getRandomValues(buf) { +function getRandomValues(buf: T): T { + // @ts-ignore fussy generics return nodeCrypto.randomFillSync(buf); } @@ -64,7 +65,7 @@ const TEST_VECTORS=[ ], ]; -function stringToArray(s) { +function stringToArray(s: string): ArrayBufferLike { return new TextEncoder().encode(s).buffer; } @@ -72,7 +73,10 @@ describe('MegolmExportEncryption', function() { let MegolmExportEncryption; beforeAll(() => { - window.crypto = { subtle: webCrypto.subtle, getRandomValues }; + window.crypto = { + subtle: webCrypto.subtle, + getRandomValues, + }; MegolmExportEncryption = require("../../src/utils/MegolmExportEncryption"); }); diff --git a/test/utils/ShieldUtils-test.js b/test/utils/ShieldUtils-test.ts similarity index 83% rename from test/utils/ShieldUtils-test.js rename to test/utils/ShieldUtils-test.ts index 85f9de31508..10ccb80f966 100644 --- a/test/utils/ShieldUtils-test.js +++ b/test/utils/ShieldUtils-test.ts @@ -1,7 +1,28 @@ +/* +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 { + MatrixClient, + Room, +} from 'matrix-js-sdk/src/matrix'; + import { shieldStatusForRoom } from '../../src/utils/ShieldUtils'; import DMRoomMap from '../../src/utils/DMRoomMap'; -function mkClient(selfTrust) { +function mkClient(selfTrust = false) { return { getUserId: () => "@self:localhost", checkUserTrust: (userId) => ({ @@ -12,7 +33,7 @@ function mkClient(selfTrust) { isVerified: () => userId === "@self:localhost" ? selfTrust : userId[2] == "T", }), getStoredDevicesForUser: (userId) => ["DEVICE"], - }; + } as unknown as MatrixClient; } describe("mkClient self-test", function() { @@ -42,9 +63,14 @@ describe("mkClient self-test", function() { describe("shieldStatusForMembership self-trust behaviour", function() { beforeAll(() => { - DMRoomMap.sharedInstance = { + const mockInstance = { getUserIdForRoomId: (roomId) => roomId === "DM" ? "@any:h" : null, - }; + } as unknown as DMRoomMap; + jest.spyOn(DMRoomMap, 'shared').mockReturnValue(mockInstance); + }); + + afterAll(() => { + jest.spyOn(DMRoomMap, 'shared').mockRestore(); }); it.each( @@ -55,7 +81,7 @@ describe("shieldStatusForMembership self-trust behaviour", function() { const room = { roomId: dm ? "DM" : "other", getEncryptionTargetMembers: () => ["@self:localhost", "@FF1:h", "@FF2:h"].map((userId) => ({ userId })), - }; + } as unknown as Room; const status = await shieldStatusForRoom(client, room); expect(status).toEqual("normal"); }); @@ -68,7 +94,7 @@ describe("shieldStatusForMembership self-trust behaviour", function() { const room = { roomId: dm ? "DM" : "other", getEncryptionTargetMembers: () => ["@self:localhost", "@TT1:h", "@TT2:h"].map((userId) => ({ userId })), - }; + } as unknown as Room; const status = await shieldStatusForRoom(client, room); expect(status).toEqual(result); }); @@ -81,7 +107,7 @@ describe("shieldStatusForMembership self-trust behaviour", function() { const room = { roomId: dm ? "DM" : "other", getEncryptionTargetMembers: () => ["@self:localhost", "@TT1:h", "@FF2:h"].map((userId) => ({ userId })), - }; + } as unknown as Room; const status = await shieldStatusForRoom(client, room); expect(status).toEqual(result); }); @@ -94,7 +120,7 @@ describe("shieldStatusForMembership self-trust behaviour", function() { const room = { roomId: dm ? "DM" : "other", getEncryptionTargetMembers: () => ["@self:localhost"].map((userId) => ({ userId })), - }; + } as unknown as Room; const status = await shieldStatusForRoom(client, room); expect(status).toEqual(result); }); @@ -107,7 +133,7 @@ describe("shieldStatusForMembership self-trust behaviour", function() { const room = { roomId: dm ? "DM" : "other", getEncryptionTargetMembers: () => ["@self:localhost", "@TT:h"].map((userId) => ({ userId })), - }; + } as unknown as Room; const status = await shieldStatusForRoom(client, room); expect(status).toEqual(result); }); @@ -120,7 +146,7 @@ describe("shieldStatusForMembership self-trust behaviour", function() { const room = { roomId: dm ? "DM" : "other", getEncryptionTargetMembers: () => ["@self:localhost", "@FF:h"].map((userId) => ({ userId })), - }; + } as unknown as Room; const status = await shieldStatusForRoom(client, room); expect(status).toEqual(result); }); @@ -128,9 +154,10 @@ describe("shieldStatusForMembership self-trust behaviour", function() { describe("shieldStatusForMembership other-trust behaviour", function() { beforeAll(() => { - DMRoomMap.sharedInstance = { + const mockInstance = { getUserIdForRoomId: (roomId) => roomId === "DM" ? "@any:h" : null, - }; + } as unknown as DMRoomMap; + jest.spyOn(DMRoomMap, 'shared').mockReturnValue(mockInstance); }); it.each( @@ -140,7 +167,7 @@ describe("shieldStatusForMembership other-trust behaviour", function() { const room = { roomId: dm ? "DM" : "other", getEncryptionTargetMembers: () => ["@self:localhost", "@TF:h"].map((userId) => ({ userId })), - }; + } as unknown as Room; const status = await shieldStatusForRoom(client, room); expect(status).toEqual(result); }); @@ -152,7 +179,7 @@ describe("shieldStatusForMembership other-trust behaviour", function() { const room = { roomId: dm ? "DM" : "other", getEncryptionTargetMembers: () => ["@self:localhost", "@TF:h", "@TT:h"].map((userId) => ({ userId })), - }; + } as unknown as Room; const status = await shieldStatusForRoom(client, room); expect(status).toEqual(result); }); @@ -164,7 +191,7 @@ describe("shieldStatusForMembership other-trust behaviour", function() { const room = { roomId: dm ? "DM" : "other", getEncryptionTargetMembers: () => ["@self:localhost", "@FF:h", "@FT:h"].map((userId) => ({ userId })), - }; + } as unknown as Room; const status = await shieldStatusForRoom(client, room); expect(status).toEqual(result); }); @@ -176,7 +203,7 @@ describe("shieldStatusForMembership other-trust behaviour", function() { const room = { roomId: dm ? "DM" : "other", getEncryptionTargetMembers: () => ["@self:localhost", "@WF:h", "@FT:h"].map((userId) => ({ userId })), - }; + } as unknown as Room; const status = await shieldStatusForRoom(client, room); expect(status).toEqual(result); }); From 483112950d14855bdecd9b0e61cb6f0bf93370e8 Mon Sep 17 00:00:00 2001 From: Suguru Hirahara Date: Mon, 2 May 2022 08:36:59 +0000 Subject: [PATCH 8/8] Fix poll overflowing a reply tile on bubble message layout (#8459) Signed-off-by: Suguru Hirahara --- res/css/views/rooms/_EventBubbleTile.scss | 1 + 1 file changed, 1 insertion(+) diff --git a/res/css/views/rooms/_EventBubbleTile.scss b/res/css/views/rooms/_EventBubbleTile.scss index 4104ae42733..93b5c789d76 100644 --- a/res/css/views/rooms/_EventBubbleTile.scss +++ b/res/css/views/rooms/_EventBubbleTile.scss @@ -406,6 +406,7 @@ limitations under the License. .mx_MPollBody { width: 550px; // to prevent timestamp overlapping summary text + max-width: 100%; // prevent overflowing a reply tile .mx_MPollBody_totalVotes { // align summary text with corner timestamp