diff --git a/cypress/e2e/14-timeline/timeline.spec.ts b/cypress/e2e/14-timeline/timeline.spec.ts index 09b15b5afe..b1b5450666 100644 --- a/cypress/e2e/14-timeline/timeline.spec.ts +++ b/cypress/e2e/14-timeline/timeline.spec.ts @@ -71,26 +71,27 @@ describe("Timeline", () => { let oldAvatarUrl: string; let newAvatarUrl: string; + beforeEach(() => { + cy.startSynapse("default").then(data => { + synapse = data; + cy.initTestUser(synapse, OLD_NAME).then(() => + cy.window({ log: false }).then(() => { + cy.createRoom({ name: ROOM_NAME }).then(_room1Id => { + roomId = _room1Id; + }); + }), + ); + }); + }); + describe("useOnlyCurrentProfiles", () => { beforeEach(() => { - cy.startSynapse("default").then(data => { - synapse = data; - cy.initTestUser(synapse, OLD_NAME).then(() => - cy.window({ log: false }).then(() => { - cy.createRoom({ name: ROOM_NAME }).then(_room1Id => { - roomId = _room1Id; - }); - }), - ).then(() => { - cy.uploadContent(OLD_AVATAR).then((url) => { - oldAvatarUrl = url; - cy.setAvatarUrl(url); - }); - }).then(() => { - cy.uploadContent(NEW_AVATAR).then((url) => { - newAvatarUrl = url; - }); - }); + cy.uploadContent(OLD_AVATAR).then((url) => { + oldAvatarUrl = url; + cy.setAvatarUrl(url); + }); + cy.uploadContent(NEW_AVATAR).then((url) => { + newAvatarUrl = url; }); }); @@ -142,7 +143,9 @@ describe("Timeline", () => { expectAvatar(e, newAvatarUrl); }); }); + }); + describe("message displaying", () => { it("should create and configure a room on IRC layout", () => { cy.visit("/#/room/" + roomId); cy.setSettingValue("layout", null, SettingLevel.DEVICE, Layout.IRC); @@ -216,4 +219,45 @@ describe("Timeline", () => { cy.get(".mx_GenericEventListSummary_toggle[aria-expanded=false]"); }); }); + + describe("message sending", () => { + const MESSAGE = "Hello world"; + const viewRoomSendMessageAndSetupReply = () => { + // View room + cy.visit("/#/room/" + roomId); + + // Send a message + cy.getComposer().type(`${MESSAGE}{enter}`); + + // Reply to the message + cy.get(".mx_RoomView_body .mx_EventTile").contains(".mx_EventTile_line", "Hello world").within(() => { + cy.get('[aria-label="Reply"]').click({ force: true }); // Cypress has no ability to hover + }); + }; + + it("can reply with a text message", () => { + const reply = "Reply"; + viewRoomSendMessageAndSetupReply(); + + cy.getComposer().type(`${reply}{enter}`); + + cy.get(".mx_RoomView_body .mx_EventTile .mx_EventTile_line").find(".mx_ReplyTile .mx_MTextBody") + .should("contain", MESSAGE); + cy.get(".mx_RoomView_body .mx_EventTile > .mx_EventTile_line > .mx_MTextBody").contains(reply) + .should("have.length", 1); + }); + + it("can reply with a voice message", () => { + viewRoomSendMessageAndSetupReply(); + + cy.openMessageComposerOptions().find(`[aria-label="Voice Message"]`).click(); + cy.wait(3000); + cy.getComposer().find(".mx_MessageComposer_sendMessage").click(); + + cy.get(".mx_RoomView_body .mx_EventTile .mx_EventTile_line").find(".mx_ReplyTile .mx_MTextBody") + .should("contain", MESSAGE); + cy.get(".mx_RoomView_body .mx_EventTile > .mx_EventTile_line > .mx_MVoiceMessageBody") + .should("have.length", 1); + }); + }); }); diff --git a/cypress/e2e/5-threads/threads.spec.ts b/cypress/e2e/5-threads/threads.spec.ts index 66cde418c6..1cc6848ec5 100644 --- a/cypress/e2e/5-threads/threads.spec.ts +++ b/cypress/e2e/5-threads/threads.spec.ts @@ -213,6 +213,34 @@ describe("Threads", () => { .should("contain", "I'm very good thanks :)"); }); + it("can send voice messages", () => { + // Increase viewport size and right-panel size, so that voice messages fit + cy.viewport(1280, 720); + cy.window().then((window) => { + window.localStorage.setItem("mx_rhs_size", "600"); + }); + + let roomId: string; + cy.createRoom({}).then(_roomId => { + roomId = _roomId; + cy.visit("/#/room/" + roomId); + }); + + // Send message + cy.get(".mx_RoomView_body .mx_BasicMessageComposer_input").type("Hello Mr. Bot{enter}"); + + // Create thread + cy.get(".mx_RoomView_body .mx_EventTile").contains(".mx_EventTile[data-scroll-tokens]", "Hello Mr. Bot") + .realHover().find(".mx_MessageActionBar_threadButton").click(); + cy.get(".mx_ThreadView_timelinePanelWrapper").should("have.length", 1); + + cy.openMessageComposerOptions(true).find(`[aria-label="Voice Message"]`).click(); + cy.wait(3000); + cy.getComposer(true).find(".mx_MessageComposer_sendMessage").click(); + + cy.get(".mx_ThreadView .mx_MVoiceMessageBody").should("have.length", 1); + }); + it("right panel behaves correctly", () => { // Create room let roomId: string; diff --git a/src/components/views/rooms/MessageComposer.tsx b/src/components/views/rooms/MessageComposer.tsx index 0c7fb84da1..d3dfd06d90 100644 --- a/src/components/views/rooms/MessageComposer.tsx +++ b/src/components/views/rooms/MessageComposer.tsx @@ -383,7 +383,10 @@ export default class MessageComposer extends React.Component { controls.push(); + room={this.props.room} + permalinkCreator={this.props.permalinkCreator} + relation={this.props.relation} + replyToEvent={this.props.replyToEvent} />); } else if (this.context.tombstone) { const replacementRoomId = this.context.tombstone.getContent()['replacement_room']; diff --git a/src/components/views/rooms/VoiceRecordComposerTile.tsx b/src/components/views/rooms/VoiceRecordComposerTile.tsx index 11b5274ee3..338995c3b9 100644 --- a/src/components/views/rooms/VoiceRecordComposerTile.tsx +++ b/src/components/views/rooms/VoiceRecordComposerTile.tsx @@ -19,6 +19,7 @@ import { Room } from "matrix-js-sdk/src/models/room"; import { MsgType } from "matrix-js-sdk/src/@types/event"; import { logger } from "matrix-js-sdk/src/logger"; import { Optional } from "matrix-events-sdk"; +import { IEventRelation, MatrixEvent } from "matrix-js-sdk/src/models/event"; import AccessibleTooltipButton from "../elements/AccessibleTooltipButton"; import { _t } from "../../../languageHandler"; @@ -38,9 +39,17 @@ import { NotificationColor } from "../../../stores/notifications/NotificationCol import InlineSpinner from "../elements/InlineSpinner"; import { PlaybackManager } from "../../../audio/PlaybackManager"; import { doMaybeLocalRoomAction } from "../../../utils/local-room"; +import defaultDispatcher from "../../../dispatcher/dispatcher"; +import { attachRelation } from "./SendMessageComposer"; +import { addReplyToMessageContent } from "../../../utils/Reply"; +import { RoomPermalinkCreator } from "../../../utils/permalinks/Permalinks"; +import RoomContext from "../../../contexts/RoomContext"; interface IProps { room: Room; + permalinkCreator?: RoomPermalinkCreator; + relation?: IEventRelation; + replyToEvent?: MatrixEvent; } interface IState { @@ -53,7 +62,10 @@ interface IState { * Container tile for rendering the voice message recorder in the composer. */ export default class VoiceRecordComposerTile extends React.PureComponent { - public constructor(props) { + static contextType = RoomContext; + public context!: React.ContextType; + + public constructor(props: IProps) { super(props); this.state = { @@ -88,6 +100,8 @@ export default class VoiceRecordComposerTile extends React.PureComponent MatrixClientPeg.get().sendMessage(actualRoomId, content),