Compare commits
1
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
0d08ba0a55 |
@@ -1,6 +1,7 @@
|
|||||||
import { test } from 'node:test';
|
import { test } from 'node:test';
|
||||||
import assert from 'node:assert/strict';
|
import assert from 'node:assert/strict';
|
||||||
import {
|
import {
|
||||||
|
foldEdits,
|
||||||
filterGroupsByDateRange,
|
filterGroupsByDateRange,
|
||||||
filterGroupsByMsgType,
|
filterGroupsByMsgType,
|
||||||
filterGroupsByPinned,
|
filterGroupsByPinned,
|
||||||
@@ -109,3 +110,47 @@ test('filterGroupsByPinned: keeps pinned items and drops empty groups', () => {
|
|||||||
['$2'],
|
['$2'],
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const sr = (eventId: string, ts: number, content: Record<string, unknown>) =>
|
||||||
|
({
|
||||||
|
rank: 1,
|
||||||
|
result: {
|
||||||
|
event_id: eventId,
|
||||||
|
room_id: '!r:x',
|
||||||
|
origin_server_ts: ts,
|
||||||
|
type: 'm.room.message',
|
||||||
|
content,
|
||||||
|
},
|
||||||
|
}) as unknown as Parameters<typeof foldEdits>[0][number];
|
||||||
|
|
||||||
|
test('foldEdits: one row per message, showing the latest edit (#288)', () => {
|
||||||
|
const edit = (id: string, ts: number, body: string) =>
|
||||||
|
sr(id, ts, {
|
||||||
|
body: `* ${body}`,
|
||||||
|
'm.new_content': { body },
|
||||||
|
'm.relates_to': { rel_type: 'm.replace', event_id: '$orig' },
|
||||||
|
});
|
||||||
|
const out = foldEdits([
|
||||||
|
edit('$e2', 30, 'Tuesday 11am room B'),
|
||||||
|
sr('$orig', 10, { body: 'Monday 10am room A' }),
|
||||||
|
edit('$e1', 20, 'Tuesday 10am room A'),
|
||||||
|
sr('$other', 5, { body: 'meeting notes' }),
|
||||||
|
]);
|
||||||
|
assert.deepEqual(
|
||||||
|
out.map((r) => r.result.event_id),
|
||||||
|
['$orig', '$other'],
|
||||||
|
);
|
||||||
|
assert.deepEqual(out[0].result.content['m.new_content'], { body: 'Tuesday 11am room B' });
|
||||||
|
assert.equal(out[0].result.content.body, 'Monday 10am room A');
|
||||||
|
});
|
||||||
|
|
||||||
|
test('foldEdits: an edit whose original did not match stands in for it', () => {
|
||||||
|
const out = foldEdits([
|
||||||
|
sr('$e1', 20, {
|
||||||
|
'm.new_content': { body: 'x' },
|
||||||
|
'm.relates_to': { rel_type: 'm.replace', event_id: '$orig' },
|
||||||
|
}),
|
||||||
|
]);
|
||||||
|
assert.equal(out.length, 1);
|
||||||
|
assert.equal(out[0].result.event_id, '$orig');
|
||||||
|
});
|
||||||
|
|||||||
@@ -96,10 +96,58 @@ export const filterGroupsByDateRange = (
|
|||||||
.filter((group) => group.items.length > 0);
|
.filter((group) => group.items.length > 0);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* [Gitea #288] Server search also returns `m.replace` events, so an edited
|
||||||
|
* message showed once per version. Fold each edit into its message: one row,
|
||||||
|
* at the first position any version ranked, showing the latest text and
|
||||||
|
* pointing at the original event (so "jump" lands on the real message).
|
||||||
|
*/
|
||||||
|
export const foldEdits = (results: ISearchResult[]): ISearchResult[] => {
|
||||||
|
const targetOf = (r: ISearchResult): string => {
|
||||||
|
const rel = r.result.content?.['m.relates_to'];
|
||||||
|
return rel?.rel_type === 'm.replace' && typeof rel.event_id === 'string'
|
||||||
|
? rel.event_id
|
||||||
|
: r.result.event_id;
|
||||||
|
};
|
||||||
|
const isEdit = (r: ISearchResult) => targetOf(r) !== r.result.event_id;
|
||||||
|
const order: string[] = [];
|
||||||
|
const originals = new Map<string, ISearchResult>();
|
||||||
|
const latestEdit = new Map<string, ISearchResult>();
|
||||||
|
results.forEach((r) => {
|
||||||
|
const target = targetOf(r);
|
||||||
|
if (!originals.has(target) && !latestEdit.has(target)) order.push(target);
|
||||||
|
if (!isEdit(r)) {
|
||||||
|
originals.set(target, r);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const prev = latestEdit.get(target);
|
||||||
|
if (!prev || r.result.origin_server_ts > prev.result.origin_server_ts)
|
||||||
|
latestEdit.set(target, r);
|
||||||
|
});
|
||||||
|
return order.map((target) => {
|
||||||
|
const original = originals.get(target);
|
||||||
|
const edit = latestEdit.get(target);
|
||||||
|
const newContent = edit?.result.content?.['m.new_content'];
|
||||||
|
if (original && !newContent) return original;
|
||||||
|
if (original) {
|
||||||
|
return {
|
||||||
|
...original,
|
||||||
|
result: {
|
||||||
|
...original.result,
|
||||||
|
content: { ...original.result.content, 'm.new_content': newContent },
|
||||||
|
},
|
||||||
|
};
|
||||||
|
}
|
||||||
|
// Only an edit matched: show it as its message.
|
||||||
|
const only = edit as ISearchResult;
|
||||||
|
return { ...only, result: { ...only.result, event_id: target } };
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
const groupSearchResult = (results: ISearchResult[]): ResultGroup[] => {
|
const groupSearchResult = (results: ISearchResult[]): ResultGroup[] => {
|
||||||
const groups: ResultGroup[] = [];
|
const groups: ResultGroup[] = [];
|
||||||
|
|
||||||
results.forEach((item) => {
|
foldEdits(results).forEach((item) => {
|
||||||
const roomId = item.result.room_id;
|
const roomId = item.result.room_id;
|
||||||
const resultItem: ResultItem = {
|
const resultItem: ResultItem = {
|
||||||
rank: item.rank,
|
rank: item.rank,
|
||||||
|
|||||||
Reference in New Issue
Block a user