mirror of
https://github.com/cinnyapp/cinny.git
synced 2025-11-11 17:50:29 +03:00
Add basic m.thread support (#1349)
* Add basic `m.thread` support * Fix types * Update to v4 * Fix auto formatting mess * Add threaded reply indicators * Fix reply overflow * Fix replying to edited threaded replies * Add thread indicator to room input * Fix editing encrypted events * Use `toRem` function for converting units --------- Co-authored-by: Ajay Bura <32841439+ajbura@users.noreply.github.com>
This commit is contained in:
parent
7e7bee8f48
commit
830d05e217
8 changed files with 140 additions and 85 deletions
|
|
@ -5,6 +5,25 @@ export const ReplyBend = style({
|
|||
flexShrink: 0,
|
||||
});
|
||||
|
||||
export const ThreadIndicator = style({
|
||||
opacity: config.opacity.P300,
|
||||
gap: toRem(2),
|
||||
|
||||
selectors: {
|
||||
'button&': {
|
||||
cursor: 'pointer',
|
||||
},
|
||||
':hover&': {
|
||||
opacity: config.opacity.P500,
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
export const ThreadIndicatorIcon = style({
|
||||
width: toRem(14),
|
||||
height: toRem(14),
|
||||
});
|
||||
|
||||
export const Reply = style({
|
||||
marginBottom: toRem(1),
|
||||
minWidth: 0,
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
import { Box, Icon, Icons, Text, as, color, toRem } from 'folds';
|
||||
import { EventTimelineSet, MatrixClient, MatrixEvent, Room } from 'matrix-js-sdk';
|
||||
import { CryptoBackend } from 'matrix-js-sdk/lib/common-crypto/CryptoBackend';
|
||||
import React, { ReactNode, useEffect, useMemo, useState } from 'react';
|
||||
import React, { MouseEventHandler, ReactNode, useEffect, useMemo, useState } from 'react';
|
||||
import to from 'await-to-js';
|
||||
import classNames from 'classnames';
|
||||
import colorMXID from '../../../util/colorMXID';
|
||||
|
|
@ -22,6 +22,7 @@ export const ReplyLayout = as<'div', ReplyLayoutProps>(
|
|||
<Box
|
||||
className={classNames(css.Reply, className)}
|
||||
alignItems="Center"
|
||||
alignSelf="Start"
|
||||
gap="100"
|
||||
{...props}
|
||||
ref={ref}
|
||||
|
|
@ -37,16 +38,26 @@ export const ReplyLayout = as<'div', ReplyLayoutProps>(
|
|||
)
|
||||
);
|
||||
|
||||
export const ThreadIndicator = as<'div'>(({ ...props }, ref) => (
|
||||
<Box className={css.ThreadIndicator} alignItems="Center" alignSelf="Start" {...props} ref={ref}>
|
||||
<Icon className={css.ThreadIndicatorIcon} src={Icons.Message} />
|
||||
<Text size="T200">Threaded reply</Text>
|
||||
</Box>
|
||||
));
|
||||
|
||||
type ReplyProps = {
|
||||
mx: MatrixClient;
|
||||
room: Room;
|
||||
timelineSet?: EventTimelineSet;
|
||||
eventId: string;
|
||||
timelineSet?: EventTimelineSet | undefined;
|
||||
replyEventId: string;
|
||||
threadRootId?: string | undefined;
|
||||
onClick?: MouseEventHandler | undefined;
|
||||
};
|
||||
|
||||
export const Reply = as<'div', ReplyProps>(({ mx, room, timelineSet, eventId, ...props }, ref) => {
|
||||
export const Reply = as<'div', ReplyProps>((_, ref) => {
|
||||
const { mx, room, timelineSet, replyEventId, threadRootId, onClick, ...props } = _;
|
||||
const [replyEvent, setReplyEvent] = useState<MatrixEvent | null | undefined>(
|
||||
timelineSet?.findEventById(eventId)
|
||||
timelineSet?.findEventById(replyEventId)
|
||||
);
|
||||
const placeholderWidth = useMemo(() => randomNumberBetween(40, 400), []);
|
||||
|
||||
|
|
@ -62,7 +73,7 @@ export const Reply = as<'div', ReplyProps>(({ mx, room, timelineSet, eventId, ..
|
|||
useEffect(() => {
|
||||
let disposed = false;
|
||||
const loadEvent = async () => {
|
||||
const [err, evt] = await to(mx.fetchRoomEvent(room.roomId, eventId));
|
||||
const [err, evt] = await to(mx.fetchRoomEvent(room.roomId, replyEventId));
|
||||
const mEvent = new MatrixEvent(evt);
|
||||
if (disposed) return;
|
||||
if (err) {
|
||||
|
|
@ -78,37 +89,43 @@ export const Reply = as<'div', ReplyProps>(({ mx, room, timelineSet, eventId, ..
|
|||
return () => {
|
||||
disposed = true;
|
||||
};
|
||||
}, [replyEvent, mx, room, eventId]);
|
||||
}, [replyEvent, mx, room, replyEventId]);
|
||||
|
||||
const badEncryption = replyEvent?.getContent().msgtype === 'm.bad.encrypted';
|
||||
const bodyJSX = body ? scaleSystemEmoji(trimReplyFromBody(body)) : fallbackBody;
|
||||
|
||||
return (
|
||||
<ReplyLayout
|
||||
userColor={sender ? colorMXID(sender) : undefined}
|
||||
username={
|
||||
sender && (
|
||||
<Text size="T300" truncate>
|
||||
<b>{getMemberDisplayName(room, sender) ?? getMxIdLocalPart(sender)}</b>
|
||||
</Text>
|
||||
)
|
||||
}
|
||||
{...props}
|
||||
ref={ref}
|
||||
>
|
||||
{replyEvent !== undefined ? (
|
||||
<Text size="T300" truncate>
|
||||
{badEncryption ? <MessageBadEncryptedContent /> : bodyJSX}
|
||||
</Text>
|
||||
) : (
|
||||
<LinePlaceholder
|
||||
style={{
|
||||
backgroundColor: color.SurfaceVariant.ContainerActive,
|
||||
maxWidth: toRem(placeholderWidth),
|
||||
width: '100%',
|
||||
}}
|
||||
/>
|
||||
<Box direction="Column" {...props} ref={ref}>
|
||||
{threadRootId && (
|
||||
<ThreadIndicator as="button" data-event-id={threadRootId} onClick={onClick} />
|
||||
)}
|
||||
</ReplyLayout>
|
||||
<ReplyLayout
|
||||
as="button"
|
||||
userColor={sender ? colorMXID(sender) : undefined}
|
||||
username={
|
||||
sender && (
|
||||
<Text size="T300" truncate>
|
||||
<b>{getMemberDisplayName(room, sender) ?? getMxIdLocalPart(sender)}</b>
|
||||
</Text>
|
||||
)
|
||||
}
|
||||
data-event-id={replyEventId}
|
||||
onClick={onClick}
|
||||
>
|
||||
{replyEvent !== undefined ? (
|
||||
<Text size="T300" truncate>
|
||||
{badEncryption ? <MessageBadEncryptedContent /> : bodyJSX}
|
||||
</Text>
|
||||
) : (
|
||||
<LinePlaceholder
|
||||
style={{
|
||||
backgroundColor: color.SurfaceVariant.ContainerActive,
|
||||
maxWidth: toRem(placeholderWidth),
|
||||
width: '100%',
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
</ReplyLayout>
|
||||
</Box>
|
||||
);
|
||||
});
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue