Skip to content
This repository was archived by the owner on Sep 11, 2024. It is now read-only.

Commit ecfd173

Browse files
grimhiltdbkr
andauthored
combine search results when the query is present in multiple successive messages (#9855)
* merge successives messages * add tests * fix styles * update test to match the expected parameters * fix types errors * fix tsc types errors Co-authored-by: grimhilt <grimhilt@users.noreply.github.com> Co-authored-by: David Baker <dbkr@users.noreply.github.com>
1 parent f34c160 commit ecfd173

File tree

4 files changed

+195
-56
lines changed

4 files changed

+195
-56
lines changed

src/components/structures/RoomSearchView.tsx

Lines changed: 43 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ import { ISearchResults } from "matrix-js-sdk/src/@types/search";
1919
import { IThreadBundledRelationship } from "matrix-js-sdk/src/models/event";
2020
import { THREAD_RELATION_TYPE } from "matrix-js-sdk/src/models/thread";
2121
import { logger } from "matrix-js-sdk/src/logger";
22+
import { MatrixEvent } from "matrix-js-sdk/src/models/event";
2223

2324
import ScrollPanel from "./ScrollPanel";
2425
import { SearchScope } from "../views/rooms/SearchBar";
@@ -214,6 +215,8 @@ export const RoomSearchView = forwardRef<ScrollPanel, Props>(
214215
};
215216

216217
let lastRoomId: string;
218+
let mergedTimeline: MatrixEvent[] = [];
219+
let ourEventsIndexes: number[] = [];
217220

218221
for (let i = (results?.results?.length || 0) - 1; i >= 0; i--) {
219222
const result = results.results[i];
@@ -251,16 +254,54 @@ export const RoomSearchView = forwardRef<ScrollPanel, Props>(
251254

252255
const resultLink = "#/room/" + roomId + "/" + mxEv.getId();
253256

257+
// merging two successive search result if the query is present in both of them
258+
const currentTimeline = result.context.getTimeline();
259+
const nextTimeline = i > 0 ? results.results[i - 1].context.getTimeline() : [];
260+
261+
if (i > 0 && currentTimeline[currentTimeline.length - 1].getId() == nextTimeline[0].getId()) {
262+
// if this is the first searchResult we merge then add all values of the current searchResult
263+
if (mergedTimeline.length == 0) {
264+
for (let j = mergedTimeline.length == 0 ? 0 : 1; j < result.context.getTimeline().length; j++) {
265+
mergedTimeline.push(currentTimeline[j]);
266+
}
267+
ourEventsIndexes.push(result.context.getOurEventIndex());
268+
}
269+
270+
// merge the events of the next searchResult
271+
for (let j = 1; j < nextTimeline.length; j++) {
272+
mergedTimeline.push(nextTimeline[j]);
273+
}
274+
275+
// add the index of the matching event of the next searchResult
276+
ourEventsIndexes.push(
277+
ourEventsIndexes[ourEventsIndexes.length - 1] +
278+
results.results[i - 1].context.getOurEventIndex() +
279+
1,
280+
);
281+
282+
continue;
283+
}
284+
285+
if (mergedTimeline.length == 0) {
286+
mergedTimeline = result.context.getTimeline();
287+
ourEventsIndexes = [];
288+
ourEventsIndexes.push(result.context.getOurEventIndex());
289+
}
290+
254291
ret.push(
255292
<SearchResultTile
256293
key={mxEv.getId()}
257-
searchResult={result}
258-
searchHighlights={highlights}
294+
timeline={mergedTimeline}
295+
ourEventsIndexes={ourEventsIndexes}
296+
searchHighlights={highlights ?? []}
259297
resultLink={resultLink}
260298
permalinkCreator={permalinkCreator}
261299
onHeightChanged={onHeightChanged}
262300
/>,
263301
);
302+
303+
ourEventsIndexes = [];
304+
mergedTimeline = [];
264305
}
265306

266307
return (

src/components/views/rooms/SearchResultTile.tsx

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,6 @@ limitations under the License.
1616
*/
1717

1818
import React from "react";
19-
import { SearchResult } from "matrix-js-sdk/src/models/search-result";
2019
import { MatrixEvent } from "matrix-js-sdk/src/models/event";
2120

2221
import RoomContext, { TimelineRenderingType } from "../../../contexts/RoomContext";
@@ -30,12 +29,14 @@ import LegacyCallEventGrouper, { buildLegacyCallEventGroupers } from "../../stru
3029
import { haveRendererForEvent } from "../../../events/EventTileFactory";
3130

3231
interface IProps {
33-
// a matrix-js-sdk SearchResult containing the details of this result
34-
searchResult: SearchResult;
3532
// a list of strings to be highlighted in the results
3633
searchHighlights?: string[];
3734
// href for the highlights in this result
3835
resultLink?: string;
36+
// timeline of the search result
37+
timeline: MatrixEvent[];
38+
// indexes of the matching events (not contextual ones)
39+
ourEventsIndexes: number[];
3940
onHeightChanged?: () => void;
4041
permalinkCreator?: RoomPermalinkCreator;
4142
}
@@ -50,16 +51,16 @@ export default class SearchResultTile extends React.Component<IProps> {
5051
public constructor(props, context) {
5152
super(props, context);
5253

53-
this.buildLegacyCallEventGroupers(this.props.searchResult.context.getTimeline());
54+
this.buildLegacyCallEventGroupers(this.props.timeline);
5455
}
5556

5657
private buildLegacyCallEventGroupers(events?: MatrixEvent[]): void {
5758
this.callEventGroupers = buildLegacyCallEventGroupers(this.callEventGroupers, events);
5859
}
5960

6061
public render() {
61-
const result = this.props.searchResult;
62-
const resultEvent = result.context.getEvent();
62+
const timeline = this.props.timeline;
63+
const resultEvent = timeline[this.props.ourEventsIndexes[0]];
6364
const eventId = resultEvent.getId();
6465

6566
const ts1 = resultEvent.getTs();
@@ -69,11 +70,10 @@ export default class SearchResultTile extends React.Component<IProps> {
6970
const alwaysShowTimestamps = SettingsStore.getValue("alwaysShowTimestamps");
7071
const threadsEnabled = SettingsStore.getValue("feature_threadstable");
7172

72-
const timeline = result.context.getTimeline();
7373
for (let j = 0; j < timeline.length; j++) {
7474
const mxEv = timeline[j];
7575
let highlights;
76-
const contextual = j != result.context.getOurEventIndex();
76+
const contextual = !this.props.ourEventsIndexes.includes(j);
7777
if (!contextual) {
7878
highlights = this.props.searchHighlights;
7979
}

test/components/structures/RoomSearchView-test.tsx

Lines changed: 111 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -326,4 +326,115 @@ describe("<RoomSearchView/>", () => {
326326
await screen.findByText("Search failed");
327327
await screen.findByText("Some error");
328328
});
329+
330+
it("should combine search results when the query is present in multiple sucessive messages", async () => {
331+
const searchResults: ISearchResults = {
332+
results: [
333+
SearchResult.fromJson(
334+
{
335+
rank: 1,
336+
result: {
337+
room_id: room.roomId,
338+
event_id: "$4",
339+
sender: client.getUserId() ?? "",
340+
origin_server_ts: 1,
341+
content: { body: "Foo2", msgtype: "m.text" },
342+
type: EventType.RoomMessage,
343+
},
344+
context: {
345+
profile_info: {},
346+
events_before: [
347+
{
348+
room_id: room.roomId,
349+
event_id: "$3",
350+
sender: client.getUserId() ?? "",
351+
origin_server_ts: 1,
352+
content: { body: "Between", msgtype: "m.text" },
353+
type: EventType.RoomMessage,
354+
},
355+
],
356+
events_after: [
357+
{
358+
room_id: room.roomId,
359+
event_id: "$5",
360+
sender: client.getUserId() ?? "",
361+
origin_server_ts: 1,
362+
content: { body: "After", msgtype: "m.text" },
363+
type: EventType.RoomMessage,
364+
},
365+
],
366+
},
367+
},
368+
eventMapper,
369+
),
370+
SearchResult.fromJson(
371+
{
372+
rank: 1,
373+
result: {
374+
room_id: room.roomId,
375+
event_id: "$2",
376+
sender: client.getUserId() ?? "",
377+
origin_server_ts: 1,
378+
content: { body: "Foo", msgtype: "m.text" },
379+
type: EventType.RoomMessage,
380+
},
381+
context: {
382+
profile_info: {},
383+
events_before: [
384+
{
385+
room_id: room.roomId,
386+
event_id: "$1",
387+
sender: client.getUserId() ?? "",
388+
origin_server_ts: 1,
389+
content: { body: "Before", msgtype: "m.text" },
390+
type: EventType.RoomMessage,
391+
},
392+
],
393+
events_after: [
394+
{
395+
room_id: room.roomId,
396+
event_id: "$3",
397+
sender: client.getUserId() ?? "",
398+
origin_server_ts: 1,
399+
content: { body: "Between", msgtype: "m.text" },
400+
type: EventType.RoomMessage,
401+
},
402+
],
403+
},
404+
},
405+
eventMapper,
406+
),
407+
],
408+
highlights: [],
409+
next_batch: "",
410+
count: 1,
411+
};
412+
413+
render(
414+
<MatrixClientContext.Provider value={client}>
415+
<RoomSearchView
416+
term="search term"
417+
scope={SearchScope.All}
418+
promise={Promise.resolve(searchResults)}
419+
resizeNotifier={resizeNotifier}
420+
permalinkCreator={permalinkCreator}
421+
className="someClass"
422+
onUpdate={jest.fn()}
423+
/>
424+
</MatrixClientContext.Provider>,
425+
);
426+
427+
const beforeNode = await screen.findByText("Before");
428+
const fooNode = await screen.findByText("Foo");
429+
const betweenNode = await screen.findByText("Between");
430+
const foo2Node = await screen.findByText("Foo2");
431+
const afterNode = await screen.findByText("After");
432+
433+
expect((await screen.findAllByText("Between")).length).toBe(1);
434+
435+
expect(beforeNode.compareDocumentPosition(fooNode) == Node.DOCUMENT_POSITION_FOLLOWING).toBeTruthy();
436+
expect(fooNode.compareDocumentPosition(betweenNode) == Node.DOCUMENT_POSITION_FOLLOWING).toBeTruthy();
437+
expect(betweenNode.compareDocumentPosition(foo2Node) == Node.DOCUMENT_POSITION_FOLLOWING).toBeTruthy();
438+
expect(foo2Node.compareDocumentPosition(afterNode) == Node.DOCUMENT_POSITION_FOLLOWING).toBeTruthy();
439+
});
329440
});

test/components/views/rooms/SearchResultTile-test.tsx

Lines changed: 33 additions & 46 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,6 @@ limitations under the License.
1515
*/
1616

1717
import * as React from "react";
18-
import { SearchResult } from "matrix-js-sdk/src/models/search-result";
1918
import { MatrixEvent } from "matrix-js-sdk/src/models/event";
2019
import { EventType } from "matrix-js-sdk/src/@types/event";
2120
import { render } from "@testing-library/react";
@@ -39,53 +38,41 @@ describe("SearchResultTile", () => {
3938
it("Sets up appropriate callEventGrouper for m.call. events", () => {
4039
const { container } = render(
4140
<SearchResultTile
42-
searchResult={SearchResult.fromJson(
43-
{
44-
rank: 0.00424866,
45-
result: {
46-
content: {
47-
body: "This is an example text message",
48-
format: "org.matrix.custom.html",
49-
formatted_body: "<b>This is an example text message</b>",
50-
msgtype: "m.text",
51-
},
52-
event_id: "$144429830826TWwbB:localhost",
53-
origin_server_ts: 1432735824653,
54-
room_id: ROOM_ID,
55-
sender: "@example:example.org",
56-
type: "m.room.message",
57-
unsigned: {
58-
age: 1234,
59-
},
41+
timeline={[
42+
new MatrixEvent({
43+
type: EventType.CallInvite,
44+
sender: "@user1:server",
45+
room_id: ROOM_ID,
46+
origin_server_ts: 1432735824652,
47+
content: { call_id: "call.1" },
48+
event_id: "$1:server",
49+
}),
50+
new MatrixEvent({
51+
content: {
52+
body: "This is an example text message",
53+
format: "org.matrix.custom.html",
54+
formatted_body: "<b>This is an example text message</b>",
55+
msgtype: "m.text",
6056
},
61-
context: {
62-
end: "",
63-
start: "",
64-
profile_info: {},
65-
events_before: [
66-
{
67-
type: EventType.CallInvite,
68-
sender: "@user1:server",
69-
room_id: ROOM_ID,
70-
origin_server_ts: 1432735824652,
71-
content: { call_id: "call.1" },
72-
event_id: "$1:server",
73-
},
74-
],
75-
events_after: [
76-
{
77-
type: EventType.CallAnswer,
78-
sender: "@user2:server",
79-
room_id: ROOM_ID,
80-
origin_server_ts: 1432735824654,
81-
content: { call_id: "call.1" },
82-
event_id: "$2:server",
83-
},
84-
],
57+
event_id: "$144429830826TWwbB:localhost",
58+
origin_server_ts: 1432735824653,
59+
room_id: ROOM_ID,
60+
sender: "@example:example.org",
61+
type: "m.room.message",
62+
unsigned: {
63+
age: 1234,
8564
},
86-
},
87-
(o) => new MatrixEvent(o),
88-
)}
65+
}),
66+
new MatrixEvent({
67+
type: EventType.CallAnswer,
68+
sender: "@user2:server",
69+
room_id: ROOM_ID,
70+
origin_server_ts: 1432735824654,
71+
content: { call_id: "call.1" },
72+
event_id: "$2:server",
73+
}),
74+
]}
75+
ourEventsIndexes={[1]}
8976
/>,
9077
);
9178

0 commit comments

Comments
 (0)