make edited and redacted message work in replies
This commit is contained in:
parent
bd2d565422
commit
ab9a4089bb
8 changed files with 182 additions and 113 deletions
40
src/app/components/MessageContent.tsx
Normal file
40
src/app/components/MessageContent.tsx
Normal file
|
|
@ -0,0 +1,40 @@
|
|||
import React from 'react';
|
||||
import { MessageEventWithoutRoomId } from 'neurone/lib/api/types';
|
||||
import { DecryptionManager } from '../../client/decryption';
|
||||
import { MessageEvent } from '../../types/matrix/room';
|
||||
import { EncryptedMessageEvent } from './encrypted-event';
|
||||
|
||||
type MessageContentProps = {
|
||||
event: MessageEventWithoutRoomId;
|
||||
editedEvent?: MessageEventWithoutRoomId;
|
||||
roomId: string;
|
||||
decryptionManager: DecryptionManager;
|
||||
reply?: boolean;
|
||||
children: (event: MessageEventWithoutRoomId) => React.ReactNode;
|
||||
};
|
||||
|
||||
export function MessageContent({
|
||||
event,
|
||||
editedEvent,
|
||||
roomId,
|
||||
decryptionManager,
|
||||
reply,
|
||||
children,
|
||||
}: MessageContentProps) {
|
||||
const effectiveEvent = editedEvent ?? event;
|
||||
|
||||
if (effectiveEvent.type === MessageEvent.RoomMessageEncrypted) {
|
||||
return (
|
||||
<EncryptedMessageEvent
|
||||
roomId={roomId}
|
||||
decryptionManager={decryptionManager}
|
||||
event={effectiveEvent}
|
||||
reply={reply}
|
||||
>
|
||||
{(decryptedEvent) => children(decryptedEvent)}
|
||||
</EncryptedMessageEvent>
|
||||
);
|
||||
}
|
||||
|
||||
return children(effectiveEvent);
|
||||
}
|
||||
|
|
@ -43,12 +43,14 @@ type EncryptedMessageEventProps = {
|
|||
decryptionManager: DecryptionManager;
|
||||
roomId: string;
|
||||
event: MessageEventWithoutRoomId;
|
||||
reply?: boolean;
|
||||
children: (decryptedEvent: MessageEventWithoutRoomId) => ReactNode;
|
||||
};
|
||||
export function EncryptedMessageEvent({
|
||||
decryptionManager,
|
||||
roomId,
|
||||
event,
|
||||
reply,
|
||||
children,
|
||||
}: EncryptedMessageEventProps) {
|
||||
const encryptedEvent = decryptionManager.decryptableMessageEvent(roomId, event);
|
||||
|
|
@ -59,7 +61,7 @@ export function EncryptedMessageEvent({
|
|||
|
||||
if (decryption instanceof EventDecryptionIdle) {
|
||||
return (
|
||||
<Text>
|
||||
<Text size={reply ? 'T300' : 'T400'} truncate={reply}>
|
||||
<MessageDecryptionIdle />
|
||||
</Text>
|
||||
);
|
||||
|
|
@ -67,7 +69,7 @@ export function EncryptedMessageEvent({
|
|||
|
||||
if (decryption instanceof EventDecryptionPending) {
|
||||
return (
|
||||
<Text>
|
||||
<Text size={reply ? 'T300' : 'T400'} truncate={reply}>
|
||||
<MessageDecryptionPending />
|
||||
</Text>
|
||||
);
|
||||
|
|
@ -81,7 +83,7 @@ export function EncryptedMessageEvent({
|
|||
|
||||
if (cause) {
|
||||
return (
|
||||
<Text>
|
||||
<Text size={reply ? 'T300' : 'T400'} truncate={reply}>
|
||||
<MessageDecryptionError
|
||||
message={
|
||||
cause instanceof Error
|
||||
|
|
|
|||
|
|
@ -1,16 +1,17 @@
|
|||
import { Box, Text, color, config, toRem } from 'folds';
|
||||
import React, { MouseEventHandler, ReactNode, useCallback, useMemo } from 'react';
|
||||
import { ClientEventWithoutRoomId } from 'neurone/lib/api/types';
|
||||
import { HTMLReactParserOptions } from 'html-react-parser';
|
||||
import { Opts } from 'linkifyjs';
|
||||
import { LinePlaceholder } from './placeholder';
|
||||
import { randomNumberBetween } from '../../utils/common';
|
||||
import * as css from './Reply.css';
|
||||
import { MessageBadEncryptedContent } from './content';
|
||||
import { MessageFailedContent } from './content';
|
||||
import { useRoomEvent } from '../../hooks/useRoomEvent';
|
||||
import colorMXID from '../../../util/colorMXID';
|
||||
import { GetMemberPowerTag } from '../../hooks/useMemberPowerTag';
|
||||
import { GetRoomMemberNameCallback } from '../../hooks/useRoomMembers';
|
||||
import { rUnsigned } from '../../../client/reader';
|
||||
import { rContent, rUnsigned } from '../../../client/reader';
|
||||
import { TimelineView } from '../../../client/room';
|
||||
import { RenderReplyContent } from '../RenderReplyContent';
|
||||
import { Time } from './Time';
|
||||
|
|
@ -18,6 +19,11 @@ import { BubbleLayout, CompactLayout, MessageBase, ModernLayout } from './layout
|
|||
import { MessageLayout, MessageSpacing } from '../../state/settings';
|
||||
import { RedactedContent } from './MsgTypeRenderers';
|
||||
import { HoverUnderline } from '../../styles/Text.css';
|
||||
import { useObservableValueOptionally } from '../../hooks/useObservableValue';
|
||||
import { getLatestEditEvent } from '../../utils/room';
|
||||
import { replaced } from '../../../client/room/relations';
|
||||
import { MessageContent } from '../MessageContent';
|
||||
import { useClient } from '../../hooks/useClient';
|
||||
|
||||
type ReplyMessageProps = {
|
||||
username?: ReactNode;
|
||||
|
|
@ -125,7 +131,6 @@ type ReplyProps = {
|
|||
accessibleTagColors?: Map<string, string>;
|
||||
legacyUsernameColor?: boolean;
|
||||
getMemberName: GetRoomMemberNameCallback;
|
||||
redacted: boolean;
|
||||
timeline?: TimelineView;
|
||||
htmlReactParserOptions: HTMLReactParserOptions;
|
||||
linkifyOpts: Opts;
|
||||
|
|
@ -145,7 +150,6 @@ export function Reply({
|
|||
accessibleTagColors,
|
||||
legacyUsernameColor,
|
||||
getMemberName,
|
||||
redacted,
|
||||
timeline,
|
||||
htmlReactParserOptions,
|
||||
linkifyOpts,
|
||||
|
|
@ -154,7 +158,12 @@ export function Reply({
|
|||
messageLayout,
|
||||
messageSpacing,
|
||||
}: ReplyProps) {
|
||||
const ct = useClient();
|
||||
const placeholderWidth = useMemo(() => randomNumberBetween(40, 400), []);
|
||||
|
||||
const redactedEvents = useObservableValueOptionally(timeline?.relations.redacted);
|
||||
const edits = useObservableValueOptionally(timeline?.relations.edits.eventToRelations);
|
||||
|
||||
const replyEvent = useRoomEvent(
|
||||
roomId,
|
||||
eventId,
|
||||
|
|
@ -167,9 +176,50 @@ export function Reply({
|
|||
|
||||
const usernameColor = legacyUsernameColor ? colorMXID(sender ?? eventId) : tagColor;
|
||||
|
||||
const redactedEvent = redacted || !!rUnsigned.redactedBecause(replyEvent?.unsigned);
|
||||
const redacted =
|
||||
redactedEvents?.has(eventId) || !!rUnsigned.redactedBecause(replyEvent?.unsigned);
|
||||
|
||||
const renderEventContent = (event: ClientEventWithoutRoomId) => {
|
||||
const aggregatedEdit = replaced(event);
|
||||
const editEvents = edits?.get(eventId);
|
||||
|
||||
const editedEvent =
|
||||
editEvents && redactedEvents
|
||||
? getLatestEditEvent(
|
||||
event.sender,
|
||||
aggregatedEdit ? [aggregatedEdit, ...editEvents] : editEvents,
|
||||
redactedEvents
|
||||
)
|
||||
: undefined;
|
||||
|
||||
return (
|
||||
<Box direction="Column" alignSelf="Start" style={{ maxWidth: '100%' }} as="div">
|
||||
{redacted ? (
|
||||
<RedactedContent size="T300" />
|
||||
) : (
|
||||
<MessageContent
|
||||
event={event}
|
||||
editedEvent={editedEvent}
|
||||
roomId={roomId}
|
||||
decryptionManager={ct.decryption}
|
||||
reply
|
||||
>
|
||||
{(contentEvent) => (
|
||||
<RenderReplyContent
|
||||
displayName={getMemberName(event.sender)}
|
||||
eventType={event.type}
|
||||
edited={!!editedEvent}
|
||||
content={rContent.newContent(contentEvent.content) ?? contentEvent.content}
|
||||
htmlReactParserOptions={htmlReactParserOptions}
|
||||
linkifyOpts={linkifyOpts}
|
||||
/>
|
||||
)}
|
||||
</MessageContent>
|
||||
)}
|
||||
</Box>
|
||||
);
|
||||
};
|
||||
|
||||
// TODO: make edit message appear in reply
|
||||
return (
|
||||
<MessageBase space={messageSpacing}>
|
||||
<ReplyMessage
|
||||
|
|
@ -205,26 +255,13 @@ export function Reply({
|
|||
}
|
||||
data-event-id={eventId}
|
||||
>
|
||||
{replyEvent !== undefined ? (
|
||||
<Box direction="Column" alignSelf="Start" style={{ maxWidth: '100%' }} as="div">
|
||||
{redactedEvent && <RedactedContent size="T300" />}
|
||||
{!redactedEvent &&
|
||||
(replyEvent === null ? (
|
||||
<Text size="B300">
|
||||
<MessageBadEncryptedContent />
|
||||
</Text>
|
||||
) : (
|
||||
<RenderReplyContent
|
||||
displayName={getMemberName(replyEvent.sender)}
|
||||
eventType={replyEvent.type}
|
||||
edited={false} // TODO:
|
||||
content={replyEvent.content}
|
||||
htmlReactParserOptions={htmlReactParserOptions}
|
||||
linkifyOpts={linkifyOpts}
|
||||
/>
|
||||
))}
|
||||
</Box>
|
||||
) : (
|
||||
{replyEvent && renderEventContent(replyEvent)}
|
||||
{replyEvent === null && (
|
||||
<Text size="T300">
|
||||
<MessageFailedContent />
|
||||
</Text>
|
||||
)}
|
||||
{replyEvent === undefined && (
|
||||
<LinePlaceholder
|
||||
style={{
|
||||
backgroundColor: color.SurfaceVariant.ContainerActive,
|
||||
|
|
|
|||
|
|
@ -212,7 +212,6 @@ function MessageSending({
|
|||
export function OutboxView({
|
||||
messageBatch,
|
||||
senders,
|
||||
redacted,
|
||||
messageLayout,
|
||||
messageSpacing,
|
||||
membersAPI,
|
||||
|
|
@ -231,7 +230,6 @@ export function OutboxView({
|
|||
}: {
|
||||
messageBatch: MessageBatch;
|
||||
senders: readonly LocalEventSender[];
|
||||
redacted: Set<string>;
|
||||
messageLayout: MessageLayout;
|
||||
messageSpacing: MessageSpacing;
|
||||
membersAPI: RoomMembersAPI;
|
||||
|
|
@ -244,7 +242,7 @@ export function OutboxView({
|
|||
urlPreview?: boolean;
|
||||
hour24Clock: boolean;
|
||||
dateFormatString: string;
|
||||
onReplyClick?: MouseEventHandler | undefined;
|
||||
onReplyClick?: () => void;
|
||||
htmlReactParserOptions: HTMLReactParserOptions;
|
||||
linkifyOpts: Opts;
|
||||
}) {
|
||||
|
|
@ -290,7 +288,6 @@ export function OutboxView({
|
|||
getMemberPowerTag={getMemberPowerTag}
|
||||
accessibleTagColors={accessibleTagColors}
|
||||
legacyUsernameColor={legacyUsernameColor || direct}
|
||||
redacted={redacted.has(replyEventId)}
|
||||
getMemberName={membersAPI.getName}
|
||||
timeline={timeline}
|
||||
htmlReactParserOptions={htmlReactParserOptions}
|
||||
|
|
@ -299,7 +296,6 @@ export function OutboxView({
|
|||
dateFormatString={dateFormatString}
|
||||
messageLayout={messageLayout}
|
||||
messageSpacing={messageSpacing}
|
||||
mediaAutoLoad={mediaAutoLoad}
|
||||
/>
|
||||
);
|
||||
|
||||
|
|
|
|||
|
|
@ -71,9 +71,9 @@ import {
|
|||
UnreadControls,
|
||||
} from './components';
|
||||
import { OutboxView } from './OutboxView';
|
||||
import { EncryptedMessageEvent } from '../../components/encrypted-event';
|
||||
import { useUserReadReceipt } from '../../hooks/useReadReceipt';
|
||||
import { useMessageParserOptions } from '../../hooks/useMessageParserOptions';
|
||||
import { MessageContent } from '../../components/MessageContent';
|
||||
|
||||
type RoomTimelineProps = {
|
||||
eventId?: string;
|
||||
|
|
@ -413,47 +413,31 @@ export function RoomTimeline({ eventId, roomInputRef, editor, timeline }: RoomTi
|
|||
hour24Clock={hour24Clock}
|
||||
dateFormatString={dateFormatString}
|
||||
>
|
||||
{redacted.has(event.event_id) && <RedactedContent />}
|
||||
{!redacted.has(event.event_id) &&
|
||||
((editedEvent ?? event).type === MessageEvent.RoomMessageEncrypted ? (
|
||||
<EncryptedMessageEvent
|
||||
roomId={roomId}
|
||||
decryptionManager={ct.decryption}
|
||||
event={editedEvent ?? event}
|
||||
>
|
||||
{(decryptedEvent) => (
|
||||
<RenderMessageContent
|
||||
displayName={membersAPI.getName(event.sender)}
|
||||
eventType={decryptedEvent.type}
|
||||
ts={event.origin_server_ts}
|
||||
edited={!!editedEvent}
|
||||
content={rContent.newContent(decryptedEvent.content) ?? decryptedEvent.content}
|
||||
mediaAutoLoad={mediaAutoLoad}
|
||||
urlPreview={showUrlPreview}
|
||||
htmlReactParserOptions={htmlReactParserOptions}
|
||||
linkifyOpts={linkifyOpts}
|
||||
outlineAttachment={messageLayout === MessageLayout.Bubble}
|
||||
/>
|
||||
)}
|
||||
</EncryptedMessageEvent>
|
||||
) : (
|
||||
<RenderMessageContent
|
||||
displayName={membersAPI.getName(event.sender)}
|
||||
eventType={event.type}
|
||||
ts={event.origin_server_ts}
|
||||
edited={!!editedEvent}
|
||||
content={
|
||||
editedEvent
|
||||
? rContent.newContent(editedEvent.content) ?? editedEvent.content
|
||||
: event.content
|
||||
}
|
||||
mediaAutoLoad={mediaAutoLoad}
|
||||
urlPreview={showUrlPreview}
|
||||
htmlReactParserOptions={htmlReactParserOptions}
|
||||
linkifyOpts={linkifyOpts}
|
||||
outlineAttachment={messageLayout === MessageLayout.Bubble}
|
||||
/>
|
||||
))}
|
||||
{redacted.has(event.event_id) ? (
|
||||
<RedactedContent />
|
||||
) : (
|
||||
<MessageContent
|
||||
event={event}
|
||||
editedEvent={editedEvent}
|
||||
roomId={roomId}
|
||||
decryptionManager={ct.decryption}
|
||||
>
|
||||
{(contentEvent) => (
|
||||
<RenderMessageContent
|
||||
displayName={membersAPI.getName(event.sender)}
|
||||
eventType={contentEvent.type}
|
||||
ts={event.origin_server_ts}
|
||||
edited={!!editedEvent}
|
||||
content={rContent.newContent(contentEvent.content) ?? contentEvent.content}
|
||||
mediaAutoLoad={mediaAutoLoad}
|
||||
urlPreview={showUrlPreview}
|
||||
htmlReactParserOptions={htmlReactParserOptions}
|
||||
linkifyOpts={linkifyOpts}
|
||||
outlineAttachment={messageLayout === MessageLayout.Bubble}
|
||||
/>
|
||||
)}
|
||||
</MessageContent>
|
||||
)}
|
||||
{reactionJSX}
|
||||
{threadTimeline && (
|
||||
<ThreadSelector
|
||||
|
|
@ -524,7 +508,6 @@ export function RoomTimeline({ eventId, roomInputRef, editor, timeline }: RoomTi
|
|||
getMemberPowerTag={getMemberPowerTag}
|
||||
accessibleTagColors={accessiblePowerTagColors}
|
||||
legacyUsernameColor={legacyUsernameColor || direct}
|
||||
redacted={redacted.has(replyEventId)}
|
||||
getMemberName={membersAPI.getName}
|
||||
timeline={timeline}
|
||||
htmlReactParserOptions={htmlReactParserInlineOptions}
|
||||
|
|
@ -596,7 +579,6 @@ export function RoomTimeline({ eventId, roomInputRef, editor, timeline }: RoomTi
|
|||
senders={localEventSenders}
|
||||
messageBatch={messageBatch}
|
||||
membersAPI={membersAPI}
|
||||
redacted={redacted}
|
||||
messageLayout={messageLayout}
|
||||
messageSpacing={messageSpacing}
|
||||
onUserClick={handleUserClick}
|
||||
|
|
|
|||
|
|
@ -1,12 +1,4 @@
|
|||
import React, {
|
||||
MouseEventHandler,
|
||||
useCallback,
|
||||
useEffect,
|
||||
useLayoutEffect,
|
||||
useMemo,
|
||||
useRef,
|
||||
useState,
|
||||
} from 'react';
|
||||
import React, { useCallback, useEffect, useLayoutEffect, useMemo, useRef, useState } from 'react';
|
||||
import { ReactEditor } from 'slate-react';
|
||||
import { Box, Scroll, Text, config } from 'folds';
|
||||
import { MessageEventWithoutRoomId } from 'neurone/lib/api/types';
|
||||
|
|
@ -18,6 +10,7 @@ import {
|
|||
CompactPlaceholder,
|
||||
Reply,
|
||||
DividerTime,
|
||||
RedactedContent,
|
||||
} from '../../../components/message';
|
||||
import { fallbackReply, getLatestEditEvent, getReplyId } from '../../../utils/room';
|
||||
import { useSetting } from '../../../state/hooks/settings';
|
||||
|
|
@ -70,6 +63,7 @@ import {
|
|||
import { OutboxView } from '../OutboxView';
|
||||
import { useUserReadReceipt } from '../../../hooks/useReadReceipt';
|
||||
import { useMessageParserOptions } from '../../../hooks/useMessageParserOptions';
|
||||
import { MessageContent } from '../../../components/MessageContent';
|
||||
|
||||
type TimelineViewProps = {
|
||||
threadTimeline: ThreadTimeline;
|
||||
|
|
@ -93,7 +87,6 @@ export function TimelineView({ threadTimeline }: TimelineViewProps) {
|
|||
const [urlPreview] = useSetting(settingsAtom, 'urlPreview');
|
||||
const [encUrlPreview] = useSetting(settingsAtom, 'encUrlPreview');
|
||||
const showUrlPreview = encryptedRooms.has(roomId) ? encUrlPreview : urlPreview;
|
||||
const [showHiddenEvents] = useSetting(settingsAtom, 'showHiddenEvents');
|
||||
const [showDeveloperTools] = useSetting(settingsAtom, 'developerTools');
|
||||
|
||||
const [hour24Clock] = useSetting(settingsAtom, 'hour24Clock');
|
||||
|
|
@ -294,11 +287,6 @@ export function TimelineView({ threadTimeline }: TimelineViewProps) {
|
|||
/>
|
||||
);
|
||||
|
||||
const content = editedEvent
|
||||
? rContent.newContent(editedEvent.content) ?? event.content
|
||||
: event.content;
|
||||
|
||||
// FIXME: Redacted message
|
||||
return (
|
||||
<Message
|
||||
key={event.event_id}
|
||||
|
|
@ -332,18 +320,31 @@ export function TimelineView({ threadTimeline }: TimelineViewProps) {
|
|||
hour24Clock={hour24Clock}
|
||||
dateFormatString={dateFormatString}
|
||||
>
|
||||
<RenderMessageContent
|
||||
displayName={membersAPI.getName(event.sender)}
|
||||
eventType={event.type}
|
||||
ts={event.origin_server_ts}
|
||||
edited={!!editedEvent}
|
||||
content={content}
|
||||
mediaAutoLoad={mediaAutoLoad}
|
||||
urlPreview={showUrlPreview}
|
||||
htmlReactParserOptions={htmlReactParserOptions}
|
||||
linkifyOpts={linkifyOpts}
|
||||
outlineAttachment={messageLayout === MessageLayout.Bubble}
|
||||
/>
|
||||
{redacted.has(event.event_id) ? (
|
||||
<RedactedContent />
|
||||
) : (
|
||||
<MessageContent
|
||||
event={event}
|
||||
editedEvent={editedEvent}
|
||||
roomId={roomId}
|
||||
decryptionManager={ct.decryption}
|
||||
>
|
||||
{(contentEvent) => (
|
||||
<RenderMessageContent
|
||||
displayName={membersAPI.getName(event.sender)}
|
||||
eventType={contentEvent.type}
|
||||
ts={event.origin_server_ts}
|
||||
edited={!!editedEvent}
|
||||
content={rContent.newContent(contentEvent.content) ?? contentEvent.content}
|
||||
mediaAutoLoad={mediaAutoLoad}
|
||||
urlPreview={showUrlPreview}
|
||||
htmlReactParserOptions={htmlReactParserOptions}
|
||||
linkifyOpts={linkifyOpts}
|
||||
outlineAttachment={messageLayout === MessageLayout.Bubble}
|
||||
/>
|
||||
)}
|
||||
</MessageContent>
|
||||
)}
|
||||
{reactionJSX}
|
||||
</Message>
|
||||
);
|
||||
|
|
@ -363,10 +364,6 @@ export function TimelineView({ threadTimeline }: TimelineViewProps) {
|
|||
if (event.sender && ignoredUsersSet.has(event.sender)) {
|
||||
return null;
|
||||
}
|
||||
// FIXME: dont hide redacted event for threads
|
||||
if (redacted.has(event.event_id) && !showHiddenEvents) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const newDivider = getNewDivider(event) && event.sender !== ct.session.userId;
|
||||
const dayDivider = getDayDivider(event);
|
||||
|
|
@ -403,7 +400,6 @@ export function TimelineView({ threadTimeline }: TimelineViewProps) {
|
|||
getMemberPowerTag={getMemberPowerTag}
|
||||
accessibleTagColors={accessiblePowerTagColors}
|
||||
legacyUsernameColor={legacyUsernameColor || direct}
|
||||
redacted={redacted.has(replyEventId)}
|
||||
getMemberName={membersAPI.getName}
|
||||
timeline={threadTimeline}
|
||||
htmlReactParserOptions={htmlReactParserInlineOptions}
|
||||
|
|
@ -495,7 +491,6 @@ export function TimelineView({ threadTimeline }: TimelineViewProps) {
|
|||
senders={localEventSenders}
|
||||
messageBatch={messageBatch}
|
||||
membersAPI={membersAPI}
|
||||
redacted={redacted}
|
||||
messageLayout={messageLayout}
|
||||
messageSpacing={messageSpacing}
|
||||
onUserClick={handleUserClick}
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@ import { Disposable, Disposer } from 'neurone/lib/utils/disposable';
|
|||
import { MessageEvent } from '../../../types/matrix/room';
|
||||
import { Relations } from './Relations';
|
||||
import { RelationType, ReactionContent } from '../../../types/matrix/common';
|
||||
import { getRedactedEventId, redacted, reference, relation, replacement } from './utils';
|
||||
import { getRedactedEventId, redacted, reference, relation, replaced, replacement } from './utils';
|
||||
|
||||
export class RelationsProcessor implements Disposable {
|
||||
private readonly publishKey = crypto.randomUUID();
|
||||
|
|
@ -64,6 +64,12 @@ export class RelationsProcessor implements Disposable {
|
|||
redactedEvents.push(event.event_id);
|
||||
}
|
||||
|
||||
// Check if event itself has replacement
|
||||
const replacedWith = replaced(event);
|
||||
if (replacedWith) {
|
||||
editEvents.push(event);
|
||||
}
|
||||
|
||||
// Handle relation events
|
||||
if (relation(event)) {
|
||||
if (event.type === MessageEvent.Reaction) {
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
import { MessageEventWithoutRoomId } from 'neurone/lib/api/types';
|
||||
import { rContent, rUnsigned, rEvent, rRelatesTo } from '../../reader';
|
||||
import { rContent, rUnsigned, rEvent, rRelatesTo, rRelations } from '../../reader';
|
||||
import { RelationType } from '../../../types/matrix/common';
|
||||
|
||||
export const getRedactedEventId = (event: MessageEventWithoutRoomId): string | undefined => {
|
||||
|
|
@ -14,6 +14,17 @@ export const redacted = (event: MessageEventWithoutRoomId): boolean => {
|
|||
return rUnsigned.redactedBecause(unsigned) !== undefined;
|
||||
};
|
||||
|
||||
export const replaced = (
|
||||
event: MessageEventWithoutRoomId
|
||||
): MessageEventWithoutRoomId | undefined => {
|
||||
const { unsigned } = event;
|
||||
|
||||
const relations = rUnsigned.relations(unsigned);
|
||||
const replacementEvent = rRelations.replace(relations);
|
||||
|
||||
return replacementEvent as MessageEventWithoutRoomId | undefined;
|
||||
};
|
||||
|
||||
export const relation = (event: MessageEventWithoutRoomId): boolean =>
|
||||
rRelatesTo.relType(rContent.relatesTo(event.content)) !== undefined;
|
||||
|
||||
|
|
|
|||
Loading…
Reference in a new issue