diff --git a/src/i18n/strings/en_EN.json b/src/i18n/strings/en_EN.json index e82e8f18fe6c..1cb589923f6d 100644 --- a/src/i18n/strings/en_EN.json +++ b/src/i18n/strings/en_EN.json @@ -650,6 +650,8 @@ "Someone else is already recording a voice broadcast. Wait for their voice broadcast to end to start a new one.": "Someone else is already recording a voice broadcast. Wait for their voice broadcast to end to start a new one.", "You ended a voice broadcast": "You ended a voice broadcast", "%(senderName)s ended a voice broadcast": "%(senderName)s ended a voice broadcast", + "You ended a voice broadcast": "You ended a voice broadcast", + "%(senderName)s ended a voice broadcast": "%(senderName)s ended a voice broadcast", "Stop live broadcasting?": "Stop live broadcasting?", "Are you sure you want to stop your live broadcast?This will end the broadcast and the full recording will be available in the room.": "Are you sure you want to stop your live broadcast?This will end the broadcast and the full recording will be available in the room.", "Yes, stop broadcast": "Yes, stop broadcast", diff --git a/src/stores/room-list/MessagePreviewStore.ts b/src/stores/room-list/MessagePreviewStore.ts index 955298a83739..035e34274712 100644 --- a/src/stores/room-list/MessagePreviewStore.ts +++ b/src/stores/room-list/MessagePreviewStore.ts @@ -32,6 +32,8 @@ import { StickerEventPreview } from "./previews/StickerEventPreview"; import { ReactionEventPreview } from "./previews/ReactionEventPreview"; import { UPDATE_EVENT } from "../AsyncStore"; import { IPreview } from "./previews/IPreview"; +import { VoiceBroadcastInfoEventType } from "../../voice-broadcast"; +import { VoiceBroadcastPreview } from "./previews/VoiceBroadcastPreview"; // Emitted event for when a room's preview has changed. First argument will the room for which // the change happened. @@ -76,6 +78,10 @@ const PREVIEWS: Record< isState: false, previewer: new PollStartEventPreview(), }, + [VoiceBroadcastInfoEventType]: { + isState: true, + previewer: new VoiceBroadcastPreview(), + }, }; // The maximum number of events we're willing to look back on to get a preview. diff --git a/src/stores/room-list/previews/MessageEventPreview.ts b/src/stores/room-list/previews/MessageEventPreview.ts index e33560e73e2b..ea81543cd6b7 100644 --- a/src/stores/room-list/previews/MessageEventPreview.ts +++ b/src/stores/room-list/previews/MessageEventPreview.ts @@ -28,6 +28,9 @@ export class MessageEventPreview implements IPreview { public getTextFor(event: MatrixEvent, tagId?: TagID, isThread?: boolean): string { let eventContent = event.getContent(); + // no preview for broadcast chunks + if (eventContent["io.element.voice_broadcast_chunk"]) return null; + if (event.isRelation(RelationType.Replace)) { // It's an edit, generate the preview on the new text eventContent = event.getContent()["m.new_content"]; diff --git a/src/stores/room-list/previews/VoiceBroadcastPreview.ts b/src/stores/room-list/previews/VoiceBroadcastPreview.ts new file mode 100644 index 000000000000..9f2ad034ae74 --- /dev/null +++ b/src/stores/room-list/previews/VoiceBroadcastPreview.ts @@ -0,0 +1,30 @@ +/* +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 { MatrixEvent } from "matrix-js-sdk/src/matrix"; + +import { textForVoiceBroadcastStoppedEventWithoutLink, VoiceBroadcastInfoState } from "../../../voice-broadcast"; +import { IPreview } from "./IPreview"; + +export class VoiceBroadcastPreview implements IPreview { + getTextFor(event: MatrixEvent, tagId?: string, isThread?: boolean): string | null { + if (!event.isRedacted() && event.getContent()?.state === VoiceBroadcastInfoState.Stopped) { + return textForVoiceBroadcastStoppedEventWithoutLink(event); + } + + return null; + } +} diff --git a/src/voice-broadcast/index.ts b/src/voice-broadcast/index.ts index f71ce077ad8d..d917ff991140 100644 --- a/src/voice-broadcast/index.ts +++ b/src/voice-broadcast/index.ts @@ -53,6 +53,7 @@ export * from "./utils/shouldDisplayAsVoiceBroadcastTile"; export * from "./utils/shouldDisplayAsVoiceBroadcastStoppedText"; export * from "./utils/startNewVoiceBroadcastRecording"; export * from "./utils/textForVoiceBroadcastStoppedEvent"; +export * from "./utils/textForVoiceBroadcastStoppedEventWithoutLink"; export * from "./utils/VoiceBroadcastResumer"; export const VoiceBroadcastInfoEventType = "io.element.voice_broadcast_info"; diff --git a/src/voice-broadcast/utils/textForVoiceBroadcastStoppedEventWithoutLink.ts b/src/voice-broadcast/utils/textForVoiceBroadcastStoppedEventWithoutLink.ts new file mode 100644 index 000000000000..3664cbcd0b0c --- /dev/null +++ b/src/voice-broadcast/utils/textForVoiceBroadcastStoppedEventWithoutLink.ts @@ -0,0 +1,31 @@ +/* +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 { MatrixEvent } from "matrix-js-sdk/src/matrix"; + +import { _t } from "../../languageHandler"; +import { MatrixClientPeg } from "../../MatrixClientPeg"; +import { getSenderName } from "../../TextForEvent"; + +export const textForVoiceBroadcastStoppedEventWithoutLink = (event: MatrixEvent): string => { + const ownUserId = MatrixClientPeg.get()?.getUserId(); + + if (ownUserId && ownUserId === event.getSender()) { + return _t("You ended a voice broadcast", {}); + } + + return _t("%(senderName)s ended a voice broadcast", { senderName: getSenderName(event) }); +}; diff --git a/test/stores/room-list/previews/MessageEventPreview-test.ts b/test/stores/room-list/previews/MessageEventPreview-test.ts new file mode 100644 index 000000000000..48f9c030365e --- /dev/null +++ b/test/stores/room-list/previews/MessageEventPreview-test.ts @@ -0,0 +1,96 @@ +/* +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 { RelationType } from "matrix-js-sdk/src/matrix"; + +import { MessageEventPreview } from "../../../../src/stores/room-list/previews/MessageEventPreview"; +import { mkEvent, stubClient } from "../../../test-utils"; + +describe("MessageEventPreview", () => { + const preview = new MessageEventPreview(); + const userId = "@user:example.com"; + + beforeAll(() => { + stubClient(); + }); + + describe("getTextFor", () => { + it("when called with an event with empty content should return null", () => { + const event = mkEvent({ + event: true, + content: {}, + user: userId, + type: "m.room.message", + }); + expect(preview.getTextFor(event)).toBeNull(); + }); + + it("when called with an event with empty body should return null", () => { + const event = mkEvent({ + event: true, + content: { + body: "", + }, + user: userId, + type: "m.room.message", + }); + expect(preview.getTextFor(event)).toBeNull(); + }); + + it("when called with an event with body should return »user: body«", () => { + const event = mkEvent({ + event: true, + content: { + body: "test body", + }, + user: userId, + type: "m.room.message", + }); + expect(preview.getTextFor(event)).toBe(`${userId}: test body`); + }); + + it("when called for a replaced event with new content should return the new content body", () => { + const event = mkEvent({ + event: true, + content: { + ["m.new_content"]: { + body: "test new content body", + }, + ["m.relates_to"]: { + rel_type: RelationType.Replace, + event_id: "$asd123", + }, + }, + user: userId, + type: "m.room.message", + }); + expect(preview.getTextFor(event)).toBe(`${userId}: test new content body`); + }); + + it("when called with a broadcast chunk event it should return null", () => { + const event = mkEvent({ + event: true, + content: { + body: "test body", + ["io.element.voice_broadcast_chunk"]: {}, + }, + user: userId, + type: "m.room.message", + }); + expect(preview.getTextFor(event)).toBeNull(); + }); + }); +}); diff --git a/test/test-utils/test-utils.ts b/test/test-utils/test-utils.ts index d0c7d34b4522..21de3983fb98 100644 --- a/test/test-utils/test-utils.ts +++ b/test/test-utils/test-utils.ts @@ -88,6 +88,7 @@ export function createTestClient(): MatrixClient { getIdentityServerUrl: jest.fn(), getDomain: jest.fn().mockReturnValue("matrix.org"), getUserId: jest.fn().mockReturnValue("@userId:matrix.org"), + getSafeUserId: jest.fn().mockReturnValue("@userId:matrix.org"), getUserIdLocalpart: jest.fn().mockResolvedValue("userId"), getUser: jest.fn().mockReturnValue({ on: jest.fn() }), getDeviceId: jest.fn().mockReturnValue("ABCDEFGHI"), diff --git a/test/voice-broadcast/utils/textForVoiceBroadcastStoppedEventWithoutLink-test.ts b/test/voice-broadcast/utils/textForVoiceBroadcastStoppedEventWithoutLink-test.ts new file mode 100644 index 000000000000..8f17e3c43a22 --- /dev/null +++ b/test/voice-broadcast/utils/textForVoiceBroadcastStoppedEventWithoutLink-test.ts @@ -0,0 +1,55 @@ +/* +Copyright 2022 The Matrix.org Foundation C.I.C. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +import { mocked } from "jest-mock"; +import { MatrixClient } from "matrix-js-sdk/src/client"; + +import { textForVoiceBroadcastStoppedEventWithoutLink, VoiceBroadcastInfoState } from "../../../src/voice-broadcast"; +import { stubClient } from "../../test-utils"; +import { mkVoiceBroadcastInfoStateEvent } from "./test-utils"; + +describe("textForVoiceBroadcastStoppedEventWithoutLink", () => { + const otherUserId = "@other:example.com"; + const roomId = "!room:example.com"; + let client: MatrixClient; + + beforeAll(() => { + client = stubClient(); + }); + + const getText = (senderId: string, startEventId?: string) => { + const event = mkVoiceBroadcastInfoStateEvent( + roomId, + VoiceBroadcastInfoState.Stopped, + senderId, + client.deviceId!, + ); + return textForVoiceBroadcastStoppedEventWithoutLink(event); + }; + + it("when called for an own broadcast it should return the expected text", () => { + expect(getText(client.getUserId()!)).toBe("You ended a voice broadcast"); + }); + + it("when called for other ones broadcast it should return the expected text", () => { + expect(getText(otherUserId)).toBe(`${otherUserId} ended a voice broadcast`); + }); + + it("when not logged in it should return the exptected text", () => { + mocked(client.getUserId).mockReturnValue(null); + expect(getText(otherUserId)).toBe(`${otherUserId} ended a voice broadcast`); + }); +});