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:
@@ -0,0 +1,71 @@
|
||||
import React, { MouseEventHandler } from 'react';
|
||||
import { Box, Icon, Icons, Text, as, toRem } from 'folds';
|
||||
import { MatrixClient } from 'matrix-js-sdk';
|
||||
import classNames from 'classnames';
|
||||
import * as css from './Reply.css';
|
||||
import { ForwardedMeta } from '../../features/room/message/forwardContent';
|
||||
import { getMemberDisplayName } from '../../utils/room';
|
||||
import { getMxIdLocalPart } from '../../utils/matrix';
|
||||
import { timeDayMonYear, timeHourMinute, today, yesterday } from '../../utils/time';
|
||||
|
||||
type ForwardedHeaderProps = {
|
||||
mx: MatrixClient;
|
||||
meta: ForwardedMeta;
|
||||
hour24Clock: boolean;
|
||||
dateFormatString: string;
|
||||
/** Present when the viewer can open the original (they are in the source room). */
|
||||
onJump?: MouseEventHandler;
|
||||
};
|
||||
|
||||
/**
|
||||
* "↪ Forwarded · from <sender> in <room> · <time>" — the provenance line above
|
||||
* a forwarded message (Gitea: forwards used to look like the forwarder's own
|
||||
* words). Renders in the same visual slot and style as a reply quote so it
|
||||
* reads as message context, not as content.
|
||||
*/
|
||||
export const ForwardedHeader = as<'div', ForwardedHeaderProps>(
|
||||
({ mx, meta, hour24Clock, dateFormatString, onJump, className, ...props }, ref) => {
|
||||
const sourceRoom = meta.room_id ? mx.getRoom(meta.room_id) : null;
|
||||
const senderName =
|
||||
(sourceRoom && getMemberDisplayName(sourceRoom, meta.sender)) ??
|
||||
mx.getUser(meta.sender)?.displayName ??
|
||||
getMxIdLocalPart(meta.sender) ??
|
||||
meta.sender;
|
||||
const ts = meta.origin_server_ts;
|
||||
const when = today(ts)
|
||||
? timeHourMinute(ts, hour24Clock)
|
||||
: yesterday(ts)
|
||||
? `Yesterday ${timeHourMinute(ts, hour24Clock)}`
|
||||
: `${timeDayMonYear(ts, dateFormatString)} ${timeHourMinute(ts, hour24Clock)}`;
|
||||
const canJump = !!sourceRoom && !!onJump;
|
||||
|
||||
return (
|
||||
<Box
|
||||
as={canJump ? 'button' : 'div'}
|
||||
className={classNames(css.Reply, className)}
|
||||
alignItems="Center"
|
||||
gap="100"
|
||||
onClick={canJump ? onJump : undefined}
|
||||
title={canJump ? 'Jump to the original message' : undefined}
|
||||
aria-label={`Forwarded from ${senderName}${sourceRoom ? ` in ${sourceRoom.name}` : ''}, ${when}`}
|
||||
{...props}
|
||||
ref={ref}
|
||||
>
|
||||
<Box alignItems="Center" gap="100" shrink="No" style={{ maxWidth: toRem(420) }}>
|
||||
<Icon size="100" src={Icons.ArrowGoRight} />
|
||||
<Text size="T300" priority="300" truncate>
|
||||
Forwarded from <b>{senderName}</b>
|
||||
{sourceRoom ? (
|
||||
<>
|
||||
{' in '}
|
||||
<b>{sourceRoom.name}</b>
|
||||
</>
|
||||
) : null}
|
||||
{' · '}
|
||||
{when}
|
||||
</Text>
|
||||
</Box>
|
||||
</Box>
|
||||
);
|
||||
},
|
||||
);
|
||||
@@ -8,3 +8,4 @@ export * from './Time';
|
||||
export * from './MsgTypeRenderers';
|
||||
export * from './FileHeader';
|
||||
export * from './RenderBody';
|
||||
export * from './ForwardedHeader';
|
||||
|
||||
Reference in New Issue
Block a user