diff --git a/src/components/structures/CallEventGrouper.ts b/src/components/structures/CallEventGrouper.ts index c439e69a26..2d2eb87fc0 100644 --- a/src/components/structures/CallEventGrouper.ts +++ b/src/components/structures/CallEventGrouper.ts @@ -44,6 +44,30 @@ export enum CustomCallState { Missed = "missed", } +export function buildCallEventGroupers( + callEventGroupers: Map, + events?: MatrixEvent[], +): Map { + const newCallEventGroupers = new Map(); + events?.forEach(ev => { + if (!ev.getType().startsWith("m.call.") && !ev.getType().startsWith("org.matrix.call.")) { + return; + } + + const callId = ev.getContent().call_id; + if (!newCallEventGroupers.has(callId)) { + if (callEventGroupers.has(callId)) { + // reuse the CallEventGrouper object where possible + newCallEventGroupers.set(callId, callEventGroupers.get(callId)); + } else { + newCallEventGroupers.set(callId, new CallEventGrouper()); + } + } + newCallEventGroupers.get(callId).add(ev); + }); + return newCallEventGroupers; +} + export default class CallEventGrouper extends EventEmitter { private events: Set = new Set(); private call: MatrixCall; diff --git a/src/components/structures/TimelinePanel.tsx b/src/components/structures/TimelinePanel.tsx index 32ed88103e..4db1c56c3a 100644 --- a/src/components/structures/TimelinePanel.tsx +++ b/src/components/structures/TimelinePanel.tsx @@ -51,7 +51,7 @@ import { RoomPermalinkCreator } from "../../utils/permalinks/Permalinks"; import Spinner from "../views/elements/Spinner"; import EditorStateTransfer from '../../utils/EditorStateTransfer'; import ErrorDialog from '../views/dialogs/ErrorDialog'; -import CallEventGrouper from "./CallEventGrouper"; +import CallEventGrouper, { buildCallEventGroupers } from "./CallEventGrouper"; import { ViewRoomPayload } from "../../dispatcher/payloads/ViewRoomPayload"; const PAGINATE_SIZE = 20; @@ -1546,24 +1546,7 @@ class TimelinePanel extends React.Component { ) => this.props.timelineSet.getRelationsForEvent(eventId, relationType, eventType); private buildCallEventGroupers(events?: MatrixEvent[]): void { - const oldCallEventGroupers = this.callEventGroupers; - this.callEventGroupers = new Map(); - events?.forEach(ev => { - if (!ev.getType().startsWith("m.call.") && !ev.getType().startsWith("org.matrix.call.")) { - return; - } - - const callId = ev.getContent().call_id; - if (!this.callEventGroupers.has(callId)) { - if (oldCallEventGroupers.has(callId)) { - // reuse the CallEventGrouper object where possible - this.callEventGroupers.set(callId, oldCallEventGroupers.get(callId)); - } else { - this.callEventGroupers.set(callId, new CallEventGrouper()); - } - } - this.callEventGroupers.get(callId).add(ev); - }); + this.callEventGroupers = buildCallEventGroupers(this.callEventGroupers, events); } render() { diff --git a/src/components/views/rooms/SearchResultTile.tsx b/src/components/views/rooms/SearchResultTile.tsx index 756a6ff601..6cc09b72e8 100644 --- a/src/components/views/rooms/SearchResultTile.tsx +++ b/src/components/views/rooms/SearchResultTile.tsx @@ -17,6 +17,7 @@ limitations under the License. import React from "react"; import { SearchResult } from "matrix-js-sdk/src/models/search-result"; +import { MatrixEvent } from "matrix-js-sdk/src/models/event"; import RoomContext, { TimelineRenderingType } from "../../../contexts/RoomContext"; import SettingsStore from "../../../settings/SettingsStore"; @@ -27,6 +28,7 @@ import DateSeparator from "../messages/DateSeparator"; import EventTile, { haveTileForEvent } from "./EventTile"; import { shouldFormContinuation } from "../../structures/MessagePanel"; import { wantsDateSeparator } from "../../../DateUtils"; +import CallEventGrouper, { buildCallEventGroupers } from "../../structures/CallEventGrouper"; interface IProps { // a matrix-js-sdk SearchResult containing the details of this result @@ -42,6 +44,20 @@ interface IProps { @replaceableComponent("views.rooms.SearchResultTile") export default class SearchResultTile extends React.Component { static contextType = RoomContext; + public context!: React.ContextType; + + // A map of + private callEventGroupers = new Map(); + + constructor(props, context) { + super(props, context); + + this.buildCallEventGroupers(this.props.searchResult.context.getTimeline()); + } + + private buildCallEventGroupers(events?: MatrixEvent[]): void { + this.callEventGroupers = buildCallEventGroupers(this.callEventGroupers, events); + } public render() { const result = this.props.searchResult; @@ -109,6 +125,7 @@ export default class SearchResultTile extends React.Component { timelineRenderingType={TimelineRenderingType.Search} lastInSection={lastInSection} continuation={continuation} + callEventGrouper={this.callEventGroupers.get(mxEv.getContent().call_id)} />, ); } diff --git a/test/components/views/rooms/SearchResultTile-test.tsx b/test/components/views/rooms/SearchResultTile-test.tsx new file mode 100644 index 0000000000..159948afd6 --- /dev/null +++ b/test/components/views/rooms/SearchResultTile-test.tsx @@ -0,0 +1,87 @@ +/* +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 * as React from "react"; +import { mount } from "enzyme"; +import { SearchResult } from "matrix-js-sdk/src/models/search-result"; +import { MatrixEvent } from "matrix-js-sdk/src/models/event"; +import { EventType } from "matrix-js-sdk/src/@types/event"; + +import sdk from "../../../skinned-sdk"; +import { createTestClient } from "../../../test-utils"; +import EventTile from "../../../../src/components/views/rooms/EventTile"; +import { MatrixClientPeg } from "../../../../src/MatrixClientPeg"; + +const SearchResultTile = sdk.getComponent("views.rooms.SearchResultTile"); + +describe("SearchResultTile", () => { + beforeAll(() => { + MatrixClientPeg.get = () => createTestClient(); + }); + + it("Sets up appropriate callEventGrouper for m.call. events", () => { + const wrapper = mount( + This is an example text message", + msgtype: "m.text", + }, + event_id: "$144429830826TWwbB:localhost", + origin_server_ts: 1432735824653, + room_id: "!qPewotXpIctQySfjSy:localhost", + sender: "@example:example.org", + type: "m.room.message", + unsigned: { + age: 1234, + }, + }, + context: { + end: "", + start: "", + profile_info: {}, + events_before: [{ + type: EventType.CallInvite, + sender: "@user1:server", + room_id: "!qPewotXpIctQySfjSy:localhost", + origin_server_ts: 1432735824652, + content: { call_id: "call.1" }, + event_id: "$1:server", + }], + events_after: [{ + type: EventType.CallAnswer, + sender: "@user2:server", + room_id: "!qPewotXpIctQySfjSy:localhost", + origin_server_ts: 1432735824654, + content: { call_id: "call.1" }, + event_id: "$2:server", + }], + }, + }, o => new MatrixEvent(o))} + />, + ); + + const tiles = wrapper.find(EventTile); + expect(tiles.length).toEqual(2); + expect(tiles.at(0).prop("mxEvent").getId()).toBe("$1:server"); + expect(tiles.at(0).prop("callEventGrouper").events.size).toBe(2); + expect(tiles.at(1).prop("mxEvent").getId()).toBe("$144429830826TWwbB:localhost"); + }); +});