From f0a7b2886ec1058741d45696787ce0302ea42fa6 Mon Sep 17 00:00:00 2001 From: Germain Date: Wed, 18 Jan 2023 16:43:18 +0000 Subject: [PATCH 01/11] replace .at() with array.length-1 --- src/Unread.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Unread.ts b/src/Unread.ts index cbd30b2bb8..6e7218cad1 100644 --- a/src/Unread.ts +++ b/src/Unread.ts @@ -85,7 +85,7 @@ export function doesRoomOrThreadHaveUnreadMessages(roomOrThread: Room | Thread): // https://github.com/vector-im/element-web/issues/2427 // ...and possibly some of the others at // https://github.com/vector-im/element-web/issues/3363 - if (roomOrThread.timeline.at(-1)?.getSender() === myUserId) { + if (roomOrThread.timeline[roomOrThread.timeline.length - 1]?.getSender() === myUserId) { return false; } From b49f83f3ae2cf7205531d85badad666706100438 Mon Sep 17 00:00:00 2001 From: Germain Date: Wed, 18 Jan 2023 16:43:18 +0000 Subject: [PATCH 02/11] replace .at() with array.length-1 --- src/Unread.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Unread.ts b/src/Unread.ts index cbd30b2bb8..6e7218cad1 100644 --- a/src/Unread.ts +++ b/src/Unread.ts @@ -85,7 +85,7 @@ export function doesRoomOrThreadHaveUnreadMessages(roomOrThread: Room | Thread): // https://github.com/vector-im/element-web/issues/2427 // ...and possibly some of the others at // https://github.com/vector-im/element-web/issues/3363 - if (roomOrThread.timeline.at(-1)?.getSender() === myUserId) { + if (roomOrThread.timeline[roomOrThread.timeline.length - 1]?.getSender() === myUserId) { return false; } From 8d4705647bfe5bf9a7afe79301707f3e98272dfb Mon Sep 17 00:00:00 2001 From: RiotRobot Date: Wed, 18 Jan 2023 21:57:00 +0000 Subject: [PATCH 03/11] Prepare changelog for v3.64.1 --- CHANGELOG.md | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index b8e23bda8a..c6a0f92e41 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,9 @@ +Changes in [3.64.1](https://github.com/matrix-org/matrix-react-sdk/releases/tag/v3.64.1) (2023-01-18) +===================================================================================================== + +## 🐛 Bug Fixes + * Fix crash in older browsers (replace .at() with array.length-1) ([\#9933](https://github.com/matrix-org/matrix-react-sdk/pull/9933)). Fixes matrix-org/element-web-rageshakes#19281. + Changes in [3.64.0](https://github.com/matrix-org/matrix-react-sdk/releases/tag/v3.64.0) (2023-01-18) ===================================================================================================== From 8219e4459681d26b9241bf396b37cbfc40e93a70 Mon Sep 17 00:00:00 2001 From: RiotRobot Date: Wed, 18 Jan 2023 21:57:02 +0000 Subject: [PATCH 04/11] v3.64.1 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 1591fed735..f9e9a87255 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "matrix-react-sdk", - "version": "3.64.0", + "version": "3.64.1", "description": "SDK for matrix.org using React", "author": "matrix.org", "repository": { From 576d10315996c0b69d21965d06f2d069491f6d5a Mon Sep 17 00:00:00 2001 From: RiotRobot Date: Wed, 18 Jan 2023 21:57:43 +0000 Subject: [PATCH 05/11] Reset matrix-js-sdk back to develop branch --- yarn.lock | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/yarn.lock b/yarn.lock index 62d88362d3..c90986604e 100644 --- a/yarn.lock +++ b/yarn.lock @@ -6493,7 +6493,7 @@ matrix-events-sdk@0.0.1: "matrix-js-sdk@github:matrix-org/matrix-js-sdk#develop": version "23.1.0" - resolved "https://codeload.github.com/matrix-org/matrix-js-sdk/tar.gz/2fcc4811dd913bb774dd1c7f67cb693c4456d71e" + resolved "https://codeload.github.com/matrix-org/matrix-js-sdk/tar.gz/83563c7a01bbeaf7f83f4b7feccc03647b536e7c" dependencies: "@babel/runtime" "^7.12.5" "@matrix-org/matrix-sdk-crypto-js" "^0.1.0-alpha.2" From eb43f3449eeda289352061b36081bec1bfaf4cc2 Mon Sep 17 00:00:00 2001 From: Michael Weimann Date: Thu, 19 Jan 2023 09:03:48 +0100 Subject: [PATCH 06/11] Fix the problem that the password reset email has to be confirmed twice (#9926) --- .../structures/auth/ForgotPassword.tsx | 2 ++ .../structures/auth/ForgotPassword-test.tsx | 31 +++++++++++++++++++ 2 files changed, 33 insertions(+) diff --git a/src/components/structures/auth/ForgotPassword.tsx b/src/components/structures/auth/ForgotPassword.tsx index 2c8f922e39..94399bf88c 100644 --- a/src/components/structures/auth/ForgotPassword.tsx +++ b/src/components/structures/auth/ForgotPassword.tsx @@ -262,6 +262,8 @@ export default class ForgotPassword extends React.Component { try { await this.reset.setNewPassword(this.state.password); + this.setState({ phase: Phase.Done }); + return; } catch (err: any) { if (err.httpStatus !== 401) { // 401 = waiting for email verification, else unknown error diff --git a/test/components/structures/auth/ForgotPassword-test.tsx b/test/components/structures/auth/ForgotPassword-test.tsx index 2e1742fcee..5a8dbc2359 100644 --- a/test/components/structures/auth/ForgotPassword-test.tsx +++ b/test/components/structures/auth/ForgotPassword-test.tsx @@ -288,6 +288,37 @@ describe("", () => { }); }); + describe("and confirm the email link and submitting the new password", () => { + beforeEach(async () => { + // fake link confirmed by resolving client.setPassword instead of raising an error + mocked(client.setPassword).mockResolvedValue({}); + await click(screen.getByText("Reset password")); + }); + + it("should send the new password (once)", () => { + expect(client.setPassword).toHaveBeenCalledWith( + { + type: "m.login.email.identity", + threepid_creds: { + client_secret: expect.any(String), + sid: testSid, + }, + threepidCreds: { + client_secret: expect.any(String), + sid: testSid, + }, + }, + testPassword, + false, + ); + + // be sure that the next attempt to set the password would have been sent + jest.advanceTimersByTime(3000); + // it should not retry to set the password + expect(client.setPassword).toHaveBeenCalledTimes(1); + }); + }); + describe("and submitting it", () => { beforeEach(async () => { await click(screen.getByText("Reset password")); From b47588fc5cfd86afc0753a37941b2a43c5ac0baf Mon Sep 17 00:00:00 2001 From: Florian Duros Date: Thu, 19 Jan 2023 10:17:18 +0100 Subject: [PATCH 07/11] Fix {enter} press in RTE (#9927) Fix enter combination in RTE --- .../hooks/useInputEventProcessor.ts | 16 +++++--- .../components/WysiwygComposer-test.tsx | 40 +++++++++++++++++++ 2 files changed, 51 insertions(+), 5 deletions(-) diff --git a/src/components/views/rooms/wysiwyg_composer/hooks/useInputEventProcessor.ts b/src/components/views/rooms/wysiwyg_composer/hooks/useInputEventProcessor.ts index fb36bbf2d1..3a223fb6b1 100644 --- a/src/components/views/rooms/wysiwyg_composer/hooks/useInputEventProcessor.ts +++ b/src/components/views/rooms/wysiwyg_composer/hooks/useInputEventProcessor.ts @@ -19,6 +19,12 @@ import { useCallback } from "react"; import { useSettingValue } from "../../../../../hooks/useSettings"; +function isEnterPressed(event: KeyboardEvent): boolean { + // Ugly but here we need to send the message only if Enter is pressed + // And we need to stop the event propagation on enter to avoid the composer to grow + return event.key === "Enter" && !event.shiftKey && !event.ctrlKey && !event.metaKey && !event.altKey; +} + export function useInputEventProcessor(onSend: () => void): (event: WysiwygEvent) => WysiwygEvent | null { const isCtrlEnter = useSettingValue("MessageComposerInput.ctrlEnterToSend"); return useCallback( @@ -28,12 +34,12 @@ export function useInputEventProcessor(onSend: () => void): (event: WysiwygEvent } const isKeyboardEvent = event instanceof KeyboardEvent; - const isEnterPress = - !isCtrlEnter && (isKeyboardEvent ? event.key === "Enter" : event.inputType === "insertParagraph"); - // sendMessage is sent when ctrl+enter is pressed - const isSendMessage = !isKeyboardEvent && event.inputType === "sendMessage"; + const isEnterPress = !isCtrlEnter && isKeyboardEvent && isEnterPressed(event); + const isInsertParagraph = !isCtrlEnter && !isKeyboardEvent && event.inputType === "insertParagraph"; + // sendMessage is sent when cmd+enter is pressed + const isSendMessage = isCtrlEnter && !isKeyboardEvent && event.inputType === "sendMessage"; - if (isEnterPress || isSendMessage) { + if (isEnterPress || isInsertParagraph || isSendMessage) { event.stopPropagation?.(); event.preventDefault?.(); onSend(); diff --git a/test/components/views/rooms/wysiwyg_composer/components/WysiwygComposer-test.tsx b/test/components/views/rooms/wysiwyg_composer/components/WysiwygComposer-test.tsx index 43dce76c7f..7353acb6b2 100644 --- a/test/components/views/rooms/wysiwyg_composer/components/WysiwygComposer-test.tsx +++ b/test/components/views/rooms/wysiwyg_composer/components/WysiwygComposer-test.tsx @@ -17,6 +17,7 @@ limitations under the License. import "@testing-library/jest-dom"; import React from "react"; import { fireEvent, render, screen, waitFor } from "@testing-library/react"; +import userEvent from "@testing-library/user-event"; import { WysiwygComposer } from "../../../../../../src/components/views/rooms/wysiwyg_composer/components/WysiwygComposer"; import SettingsStore from "../../../../../../src/settings/SettingsStore"; @@ -87,6 +88,45 @@ describe("WysiwygComposer", () => { // Then it sends a message await waitFor(() => expect(onSend).toBeCalledTimes(1)); }); + + it("Should not call onSend when Shift+Enter is pressed ", async () => { + //When + await userEvent.type(screen.getByRole("textbox"), "{shift>}{enter}"); + + // Then it sends a message + await waitFor(() => expect(onSend).toBeCalledTimes(0)); + }); + + it("Should not call onSend when ctrl+Enter is pressed ", async () => { + //When + // Using userEvent.type or .keyboard wasn't working as expected in the case of ctrl+enter + fireEvent( + screen.getByRole("textbox"), + new KeyboardEvent("keydown", { + ctrlKey: true, + code: "Enter", + }), + ); + + // Then it sends a message + await waitFor(() => expect(onSend).toBeCalledTimes(0)); + }); + + it("Should not call onSend when alt+Enter is pressed ", async () => { + //When + await userEvent.type(screen.getByRole("textbox"), "{alt>}{enter}"); + + // Then it sends a message + await waitFor(() => expect(onSend).toBeCalledTimes(0)); + }); + + it("Should not call onSend when meta+Enter is pressed ", async () => { + //When + await userEvent.type(screen.getByRole("textbox"), "{meta>}{enter}"); + + // Then it sends a message + await waitFor(() => expect(onSend).toBeCalledTimes(0)); + }); }); describe("When settings require Ctrl+Enter to send", () => { From 8a2e3865316636a47974b1c9595f1e0034ada63a Mon Sep 17 00:00:00 2001 From: alunturner <56027671+alunturner@users.noreply.github.com> Date: Thu, 19 Jan 2023 09:24:29 +0000 Subject: [PATCH 08/11] Add disabled button state to rich text editor (#9930) * add disabled css state * conditionally apply disabled css state * hides disabled tooltips --- .../components/_FormattingButtons.pcss | 6 ++++ .../components/FormattingButtons.tsx | 28 ++++++++++--------- .../components/FormattingButtons-test.tsx | 27 ++++++++++++++++-- 3 files changed, 46 insertions(+), 15 deletions(-) diff --git a/res/css/views/rooms/wysiwyg_composer/components/_FormattingButtons.pcss b/res/css/views/rooms/wysiwyg_composer/components/_FormattingButtons.pcss index fa8078279f..8e3dd22c99 100644 --- a/res/css/views/rooms/wysiwyg_composer/components/_FormattingButtons.pcss +++ b/res/css/views/rooms/wysiwyg_composer/components/_FormattingButtons.pcss @@ -50,6 +50,12 @@ limitations under the License. } } + .mx_FormattingButtons_disabled { + .mx_FormattingButtons_Icon { + color: $quinary-content; + } + } + .mx_FormattingButtons_Icon { --size: 16px; height: var(--size); diff --git a/src/components/views/rooms/wysiwyg_composer/components/FormattingButtons.tsx b/src/components/views/rooms/wysiwyg_composer/components/FormattingButtons.tsx index 7c1601b441..11c797d646 100644 --- a/src/components/views/rooms/wysiwyg_composer/components/FormattingButtons.tsx +++ b/src/components/views/rooms/wysiwyg_composer/components/FormattingButtons.tsx @@ -15,7 +15,7 @@ limitations under the License. */ import React, { MouseEventHandler, ReactNode } from "react"; -import { FormattingFunctions, AllActionStates } from "@matrix-org/matrix-wysiwyg"; +import { FormattingFunctions, AllActionStates, ActionState } from "@matrix-org/matrix-wysiwyg"; import classNames from "classnames"; import { Icon as BoldIcon } from "../../../../../../res/img/element-icons/room/composer/bold.svg"; @@ -53,21 +53,23 @@ function Tooltip({ label, keyCombo }: TooltipProps): JSX.Element { interface ButtonProps extends TooltipProps { icon: ReactNode; - isActive: boolean; + actionState: ActionState; onClick: MouseEventHandler; } -function Button({ label, keyCombo, onClick, isActive, icon }: ButtonProps): JSX.Element { +function Button({ label, keyCombo, onClick, actionState, icon }: ButtonProps): JSX.Element { return ( void} title={label} className={classNames("mx_FormattingButtons_Button", { - mx_FormattingButtons_active: isActive, - mx_FormattingButtons_Button_hover: !isActive, + mx_FormattingButtons_active: actionState === "reversed", + mx_FormattingButtons_Button_hover: actionState === "enabled", + mx_FormattingButtons_disabled: actionState === "disabled", })} tooltip={keyCombo && } + forceHide={actionState === "disabled"} alignment={Alignment.Top} > {icon} @@ -85,53 +87,53 @@ export function FormattingButtons({ composer, actionStates }: FormattingButtonsP return (