126 lines
3.6 KiB
TypeScript
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>
|
|
);
|
|
}
|