vojo/src/app/features/room/message/SyslineMessage.tsx

108 lines
3.4 KiB
TypeScript

import React, { ReactNode } from 'react';
import { Box, Icons, Text } from 'folds';
import { MatrixEvent, Room } from 'matrix-js-sdk';
import {
BubbleSysline,
ChannelLayout,
ChannelMessageAvatar,
Time,
Username,
UsernameBold,
} from '../../../components/message';
import { Event } from './Message';
import { useMatrixClient } from '../../../hooks/useMatrixClient';
import { getMemberDisplayName } from '../../../utils/room';
import { getMxIdLocalPart } from '../../../utils/matrix';
export type SyslineMessageProps = {
room: Room;
mEvent: MatrixEvent;
// Pre-rendered body — already includes any sender names / interpolation
// the per-type renderer wants to show. The sysline row adds no extra
// header, so the body is the only visible content.
body: ReactNode;
highlight: boolean;
canDelete?: boolean;
hideReadReceipts?: boolean;
showDeveloperTools?: boolean;
streamRailStart?: boolean;
streamRailEnd?: boolean;
layout?: 'stream' | 'channel';
channelHeaderInBubble?: boolean;
// Spread onto Event wrapper (data-message-item / data-message-id) so the
// virtual paginator can locate the row.
[dataAttr: `data-${string}`]: string | number | undefined;
};
export function SyslineMessage({
room,
mEvent,
body,
highlight,
canDelete,
hideReadReceipts,
showDeveloperTools,
// Vestigial Stream-rail props — accepted but unused by the bubble/channel
// layouts (pending the stream-rail cleanup).
streamRailStart: _streamRailStart,
streamRailEnd: _streamRailEnd,
layout = 'stream',
channelHeaderInBubble,
...rest
}: SyslineMessageProps) {
const mx = useMatrixClient();
const senderId = mEvent.getSender() ?? '';
const isOwnMessage = !!mx.getUserId() && senderId === mx.getUserId();
const senderName = getMemberDisplayName(room, senderId) || getMxIdLocalPart(senderId) || senderId;
return (
<Event
key={mEvent.getId()}
room={room}
mEvent={mEvent}
highlight={highlight}
canDelete={canDelete}
hideReadReceipts={hideReadReceipts}
showDeveloperTools={showDeveloperTools}
bubble={layout !== 'channel'}
{...rest}
>
{layout === 'channel' ? (
<ChannelLayout
isOwn={isOwnMessage}
headerInBubble={channelHeaderInBubble}
avatar={
<ChannelMessageAvatar room={room} senderId={senderId} senderDisplayName={senderName} />
}
header={
<>
<Username as="span">
<Text as="span" size={channelHeaderInBubble ? 'T200' : 'T400'} truncate>
{/* Own events show the user's own nick too (not a «me» label). */}
<UsernameBold>{senderName}</UsernameBold>
</Text>
</Username>
<Time ts={mEvent.getTs()} compact size="T200" priority="300" />
</>
}
>
<Box style={{ minWidth: 0 }}>
<Text as="span" size="T300" priority="300">
{body}
</Text>
</Box>
</ChannelLayout>
) : (
// System notices (room name / topic / avatar changes) render as a
// muted, centred one-liner in the bubble DM — no rail, no bubble,
// matching the AI-bot chat's understated tone.
<BubbleSysline
iconSrc={Icons.Info}
content={body}
time={<Time ts={mEvent.getTs()} compact />}
/>
)}
</Event>
);
}