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

126 lines
3.6 KiB
TypeScript

import React, { ReactNode } from 'react';
import { Box, Text } from 'folds';
import { MatrixEvent, Room } from 'matrix-js-sdk';
import { useTranslation } from 'react-i18next';
import {
ChannelLayout,
ChannelMessageAvatar,
StreamLayout,
Time,
Username,
UsernameBold,
} from '../../../components/message';
import { Event } from './Message';
import { useMatrixClient } from '../../../hooks/useMatrixClient';
import { useDotColor } from '../../../hooks/useDotColor';
import { ScreenSize, useScreenSizeContext } from '../../../hooks/useScreenSize';
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 bubble adds no extra
// header or icon, 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,
streamRailStart,
streamRailEnd,
layout = 'stream',
channelHeaderInBubble,
...rest
}: SyslineMessageProps) {
const { t } = useTranslation();
const mx = useMatrixClient();
const isMobile = useScreenSizeContext() === ScreenSize.Mobile;
const dot = useDotColor(room, mEvent, true, hideReadReceipts);
const senderId = mEvent.getSender() ?? '';
const isOwnMessage = !!mx.getUserId() && senderId === mx.getUserId();
const peerBg = !isOwnMessage;
const senderName =
getMemberDisplayName(room, senderId) || getMxIdLocalPart(senderId) || senderId;
const bubbleBody = (
<Box style={{ minWidth: 0 }}>
<Text as="span" size="T300" priority="300">
{body}
</Text>
</Box>
);
return (
<Event
key={mEvent.getId()}
room={room}
mEvent={mEvent}
highlight={highlight}
canDelete={canDelete}
hideReadReceipts={hideReadReceipts}
showDeveloperTools={showDeveloperTools}
{...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>
<UsernameBold>
{isOwnMessage ? t('Direct.message_me_label') : senderName}
</UsernameBold>
</Text>
</Username>
<Time ts={mEvent.getTs()} compact size="T200" priority="300" />
</>
}
>
{bubbleBody}
</ChannelLayout>
) : (
<StreamLayout
time={<Time ts={mEvent.getTs()} compact />}
dotColor={dot.color}
dotOpacity={dot.opacity}
isOwn={isOwnMessage}
peerBg={peerBg}
compact={isMobile}
railStart={streamRailStart}
railEnd={streamRailEnd}
>
{bubbleBody}
</StreamLayout>
)}
</Event>
);
}