Skip to content
This repository has been archived by the owner on Sep 11, 2024. It is now read-only.

Commit

Permalink
Fix alignment of RTL messages
Browse files Browse the repository at this point in the history
Inspired by #5453 but
hopefully with the edited marker in the right place.

This is a PoC: types aren't correct and the style needs pulling
out to a class. Plus it would probably need more visual tests added.
If this looks acceptable, I can make these changes.
  • Loading branch information
dbkr committed Jul 29, 2024
1 parent a12c187 commit 0c6f24a
Show file tree
Hide file tree
Showing 3 changed files with 22 additions and 9 deletions.
1 change: 1 addition & 0 deletions res/css/views/rooms/_EventTile.pcss
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@ $left-gutter: 64px;

.mx_EventTile_body {
overflow-y: hidden;
text-align: start;
}

.mx_EventTile_receiptSent,
Expand Down
18 changes: 13 additions & 5 deletions src/HtmlUtils.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -252,7 +252,9 @@ interface IOpts {
stripReplyFallback?: boolean;
returnString?: boolean;
forComposerQuote?: boolean;
ref?: React.Ref<HTMLSpanElement>;
includeDir?: boolean; // whether to include the dir="auto" attribute
asElement?: "div" | "span"; // the element to render the body as. 'span' by default.
ref?: React.Ref<HTMLDivElement | HTMLSpanElement>;
}

export interface IOptsReturnNode extends IOpts {
Expand Down Expand Up @@ -318,6 +320,8 @@ export function formatEmojis(message: string | undefined, isHtmlMessage?: boolea
* opts.stripReplyFallback: optional argument specifying the event is a reply and so fallback needs removing
* opts.returnString: return an HTML string rather than JSX elements
* opts.forComposerQuote: optional param to lessen the url rewriting done by sanitization, for quoting into composer
* opts.includeDir: whether to include the dir="auto" attribute
* opts.asElement: the element to render the body as. 'span' by default.
* opts.ref: React ref to attach to any React components returned (not compatible with opts.returnString)
*/
export function bodyToHtml(content: IContent, highlights: Optional<string[]>, opts: IOptsReturnString): string;
Expand All @@ -332,6 +336,10 @@ export function bodyToHtml(content: IContent, highlights: Optional<string[]>, op
sanitizeParams = composerSanitizeHtmlParams;
}

if (opts.includeDir === undefined) opts.includeDir = true;

const AsElement = opts.asElement ?? "span";

let strippedBody: string;
let safeBody: string | undefined; // safe, sanitised HTML, preferred over `strippedBody` which is fully plaintext

Expand Down Expand Up @@ -433,17 +441,17 @@ export function bodyToHtml(content: IContent, highlights: Optional<string[]>, op
}

return safeBody ? (
<span
<AsElement
key="body"
ref={opts.ref}

Check failure on line 446 in src/HtmlUtils.tsx

View workflow job for this annotation

GitHub Actions / Typescript Syntax Check

Type 'Ref<HTMLDivElement | HTMLSpanElement> | undefined' is not assignable to type 'LegacyRef<HTMLDivElement> | undefined'.
className={className}
dangerouslySetInnerHTML={{ __html: safeBody }}
dir="auto"
dir={opts.includeDir ? "auto" : undefined}
/>
) : (
<span key="body" ref={opts.ref} className={className} dir="auto">
<AsElement key="body" ref={opts.ref} className={className} dir={opts.includeDir ? "auto" : undefined}>

Check failure on line 452 in src/HtmlUtils.tsx

View workflow job for this annotation

GitHub Actions / Typescript Syntax Check

Type 'Ref<HTMLDivElement | HTMLSpanElement> | undefined' is not assignable to type 'LegacyRef<HTMLDivElement> | undefined'.
{emojiBodyElements || strippedBody}
</span>
</AsElement>
);
}

Expand Down
12 changes: 8 additions & 4 deletions src/components/views/messages/TextualBody.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -569,6 +569,8 @@ export default class TextualBody extends React.Component<IBodyProps, IState> {
let isNotice = false;
let isEmote = false;

const willHaveWrapper = this.props.replacingEventId || this.props.isSeeingThroughMessageHiddenForModeration;

// only strip reply if this is the original replying event, edits thereafter do not have the fallback
const stripReply = !mxEvent.replacingEvent() && !!getParentEventId(mxEvent);
isEmote = content.msgtype === MsgType.Emote;
Expand All @@ -579,22 +581,24 @@ export default class TextualBody extends React.Component<IBodyProps, IState> {
stripReplyFallback: stripReply,
ref: this.contentRef,
returnString: false,
includeDir: !willHaveWrapper,
asElement: willHaveWrapper ? "span" : "div",
});

if (this.props.replacingEventId) {
body = (
<>
<div dir="auto" style={{ display: "flex" }}>
{body}
{this.renderEditedMarker()}
</>
</div>
);
}
if (this.props.isSeeingThroughMessageHiddenForModeration) {
body = (
<>
<div dir="auto" style={{ display: "flex" }}>
{body}
{this.renderPendingModerationMarker()}
</>
</div>
);
}

Expand Down

0 comments on commit 0c6f24a

Please sign in to comment.