feat(forward): Discord-style provenance — 'Forwarded from <sender> in <room> · <time>' with jump to the original
CI / Build & Quality Checks (push) Successful in 1m31s
CI / Docker image build & smoke test (push) Skipped
CI / Secret scan (gitleaks) (push) Successful in 7s
CI / Trigger Desktop Build (push) Successful in 5s
CI / Playwright smoke (e2e) (push) Successful in 2m7s
CI / Build & Quality Checks (push) Successful in 1m31s
CI / Docker image build & smoke test (push) Skipped
CI / Secret scan (gitleaks) (push) Successful in 7s
CI / Trigger Desktop Build (push) Successful in 5s
CI / Playwright smoke (e2e) (push) Successful in 2m7s
Forwarded events carried no origin at all; they read as the forwarder's own words. buildForwardContent now stamps io.lotus.forwarded (sender, ts, room_id, event_id; re-forwards keep the original stamp) and the main and thread timelines render a reply-style header above the message that jumps to the original when the viewer is in the source room (sender + time only otherwise — the source room's name is not leaked). Unit-tested; verified end to end with Playwright (header text, event content, jump, re-forward). Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01PPmy3tPq869XDW4njjVaKA
This commit is contained in:
@@ -1,7 +1,7 @@
|
||||
import { test } from 'node:test';
|
||||
import assert from 'node:assert/strict';
|
||||
import { MatrixClient, MatrixEvent } from 'matrix-js-sdk';
|
||||
import { buildForwardContent } from './forwardContent';
|
||||
import { buildForwardContent, getForwardedMeta } from './forwardContent';
|
||||
|
||||
// Pure content builder buildForwardContent: refuses undecryptable events, forwards
|
||||
// the latest edit (`m.new_content`), and strips reply fallbacks + `m.relates_to`.
|
||||
@@ -136,3 +136,38 @@ test('edited message forwards m.new_content', () => {
|
||||
assert.equal(content['m.new_content'], undefined);
|
||||
assert.equal(content['m.relates_to'], undefined);
|
||||
});
|
||||
|
||||
test('forward stamps io.lotus.forwarded with the original sender / time / room / event', () => {
|
||||
const mx = makeClient();
|
||||
const mEvent = makeEvent({ content: { msgtype: 'm.text', body: 'prov' } });
|
||||
const content = buildForwardContent(mx, mEvent);
|
||||
assert.ok(content);
|
||||
const meta = getForwardedMeta(content);
|
||||
assert.ok(meta);
|
||||
assert.equal(meta.sender, mEvent.getSender());
|
||||
assert.equal(meta.origin_server_ts, mEvent.getTs());
|
||||
assert.equal(meta.room_id, mEvent.getRoomId());
|
||||
assert.equal(meta.event_id, mEvent.getId());
|
||||
});
|
||||
|
||||
test('forwarding a forward keeps the ORIGINAL provenance', () => {
|
||||
const mx = makeClient();
|
||||
const original = {
|
||||
sender: '@origin:example.org',
|
||||
origin_server_ts: 1000,
|
||||
room_id: '!origin:example.org',
|
||||
event_id: '$origin:example.org',
|
||||
};
|
||||
const mEvent = makeEvent({
|
||||
content: { msgtype: 'm.text', body: 'again', 'io.lotus.forwarded': original },
|
||||
});
|
||||
const content = buildForwardContent(mx, mEvent);
|
||||
assert.ok(content);
|
||||
assert.deepEqual(getForwardedMeta(content), original);
|
||||
});
|
||||
|
||||
test('getForwardedMeta rejects malformed metadata', () => {
|
||||
assert.equal(getForwardedMeta({ 'io.lotus.forwarded': 'nope' }), undefined);
|
||||
assert.equal(getForwardedMeta({ 'io.lotus.forwarded': { sender: 1 } }), undefined);
|
||||
assert.equal(getForwardedMeta({}), undefined);
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user