Files
cinny/src/app/components/message/Reply.tsx
T

135 lines
4.4 KiB
TypeScript
Raw Normal View History

2023-10-06 13:44:06 +11:00
import { Box, Icon, Icons, Text, as, color, toRem } from 'folds';
2024-12-16 21:55:15 +11:00
import { EventTimelineSet, Room } from 'matrix-js-sdk';
import React, { MouseEventHandler, ReactNode, useCallback, useMemo } from 'react';
2023-10-06 13:44:06 +11:00
import classNames from 'classnames';
2023-10-18 13:15:30 +11:00
import { getMemberDisplayName, trimReplyFromBody } from '../../utils/room';
import { getMxIdLocalPart } from '../../utils/matrix';
2023-10-06 13:44:06 +11:00
import { LinePlaceholder } from './placeholder';
import { randomNumberBetween } from '../../utils/common';
import * as css from './Reply.css';
import { MessageBadEncryptedContent, MessageDeletedContent, MessageFailedContent } from './content';
import { scaleSystemEmoji } from '../../plugins/react-custom-html-parser';
2024-12-16 21:55:15 +11:00
import { useRoomEvent } from '../../hooks/useRoomEvent';
import { GetPowerLevelTag } from '../../hooks/usePowerLevelTags';
import colorMXID from '../../../util/colorMXID';
type ReplyLayoutProps = {
userColor?: string;
username?: ReactNode;
};
export const ReplyLayout = as<'div', ReplyLayoutProps>(
({ username, userColor, className, children, ...props }, ref) => (
<Box
className={classNames(css.Reply, className)}
alignItems="Center"
gap="100"
{...props}
ref={ref}
>
<Box style={{ color: userColor, maxWidth: toRem(200) }} alignItems="Center" shrink="No">
<Icon size="100" src={Icons.ReplyArrow} />
{username}
</Box>
<Box grow="Yes" className={css.ReplyContent}>
{children}
</Box>
</Box>
)
);
2023-10-06 13:44:06 +11:00
2024-08-15 16:52:32 +02:00
export const ThreadIndicator = as<'div'>(({ ...props }, ref) => (
<Box className={css.ThreadIndicator} alignItems="Center" {...props} ref={ref}>
2024-08-15 16:52:32 +02:00
<Icon className={css.ThreadIndicatorIcon} src={Icons.Message} />
<Text size="T200">Threaded reply</Text>
</Box>
));
2023-10-06 13:44:06 +11:00
type ReplyProps = {
room: Room;
2024-08-15 16:52:32 +02:00
timelineSet?: EventTimelineSet | undefined;
replyEventId: string;
threadRootId?: string | undefined;
onClick?: MouseEventHandler | undefined;
getPowerLevel?: (userId: string) => number;
getPowerLevelTag?: GetPowerLevelTag;
accessibleTagColors?: Map<string, string>;
legacyUsernameColor?: boolean;
2023-10-06 13:44:06 +11:00
};
2024-12-16 21:55:15 +11:00
export const Reply = as<'div', ReplyProps>(
(
{
room,
timelineSet,
replyEventId,
threadRootId,
onClick,
getPowerLevel,
getPowerLevelTag,
accessibleTagColors,
legacyUsernameColor,
...props
},
ref
) => {
2024-12-16 21:55:15 +11:00
const placeholderWidth = useMemo(() => randomNumberBetween(40, 400), []);
const getFromLocalTimeline = useCallback(
() => timelineSet?.findEventById(replyEventId),
[timelineSet, replyEventId]
);
const replyEvent = useRoomEvent(room, replyEventId, getFromLocalTimeline);
2023-10-06 13:44:06 +11:00
2024-12-16 21:55:15 +11:00
const { body } = replyEvent?.getContent() ?? {};
const sender = replyEvent?.getSender();
const senderPL = sender && getPowerLevel?.(sender);
const powerTag = typeof senderPL === 'number' ? getPowerLevelTag?.(senderPL) : undefined;
const tagColor = powerTag?.color ? accessibleTagColors?.get(powerTag.color) : undefined;
const usernameColor = legacyUsernameColor ? colorMXID(sender ?? replyEventId) : tagColor;
2023-10-06 13:44:06 +11:00
2024-12-16 21:55:15 +11:00
const fallbackBody = replyEvent?.isRedacted() ? (
<MessageDeletedContent />
) : (
<MessageFailedContent />
);
2023-10-06 13:44:06 +11:00
2024-12-16 21:55:15 +11:00
const badEncryption = replyEvent?.getContent().msgtype === 'm.bad.encrypted';
const bodyJSX = body ? scaleSystemEmoji(trimReplyFromBody(body)) : fallbackBody;
2023-10-06 13:44:06 +11:00
2024-12-16 21:55:15 +11:00
return (
<Box direction="Column" alignItems="Start" {...props} ref={ref}>
2024-12-16 21:55:15 +11:00
{threadRootId && (
<ThreadIndicator as="button" data-event-id={threadRootId} onClick={onClick} />
)}
<ReplyLayout
as="button"
userColor={usernameColor}
2024-12-16 21:55:15 +11:00
username={
sender && (
<Text size="T300" truncate>
<b>{getMemberDisplayName(room, sender) ?? getMxIdLocalPart(sender)}</b>
</Text>
)
}
data-event-id={replyEventId}
onClick={onClick}
>
{replyEvent !== undefined ? (
2024-08-15 16:52:32 +02:00
<Text size="T300" truncate>
2024-12-16 21:55:15 +11:00
{badEncryption ? <MessageBadEncryptedContent /> : bodyJSX}
2024-08-15 16:52:32 +02:00
</Text>
2024-12-16 21:55:15 +11:00
) : (
<LinePlaceholder
style={{
backgroundColor: color.SurfaceVariant.ContainerActive,
2025-02-10 20:47:21 +11:00
width: toRem(placeholderWidth),
maxWidth: '100%',
2024-12-16 21:55:15 +11:00
}}
/>
)}
</ReplyLayout>
</Box>
);
}
);