Fix rendering of messages with primarily RTL text

Co-authored-by: Scott Nonnenberg <scott@signal.org>
This commit is contained in:
automated-signal 2022-03-22 17:41:14 -07:00 committed by GitHub
parent 468920cce8
commit 272f3fb2a5
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 11 additions and 92 deletions

View File

@ -222,6 +222,14 @@ story.add('Plain Message', () => {
return renderBothDirections(props);
});
story.add('Plain RTL Message', () => {
const props = createProps({
text: 'الأسانسير، علشان القطط ماتاكلش منها. وننساها، ونعود الى أوراقنا موصدين الباب بإحكام. نتنحنح، ونقول: البتاع. كلمة تدلّ على لا شيء، وعلى كلّ شيء. وهي مركز أبحاث شعبية كثيرة، تتعجّب من غرابتها والقومية المصرية الخاصة التي تعكسها، الى جانب الشيء الكثير من العفوية وحلاوة الروح. نعم، نحن قرأنا وسمعنا وعرفنا كل هذا. لكنه محلّ اهتمامنا اليوم لأسباب غير تلك الأسباب. كذلك، فإننا لعاقدون عزمنا على أن نتجاوز قضية الفصحى والعامية، وثنائية النخبة والرعاع، التي كثيراً ما ينحو نحوها الحديث عن الكلمة المذكورة. وفوق هذا كله، لسنا بصدد تفسير معاني "البتاع" كما تأتي في قصيدة الحاج أحمد فؤاد نجم، ولا التحذلق والتفذلك في الألغاز والأسرار المكنونة. هذا البتاع - أم هذه البت',
});
return renderBothDirections(props);
});
story.add('Emoji Messages', () => (
<>
<Message {...createProps({ text: '😀' })} />

View File

@ -25,7 +25,6 @@ import {
MessageBodyReadMore,
} from './MessageBodyReadMore';
import { MessageMetadata } from './MessageMetadata';
import { MessageTextMetadataSpacer } from './MessageTextMetadataSpacer';
import { ImageGrid } from './ImageGrid';
import { GIF } from './GIF';
import { Image } from './Image';
@ -92,19 +91,6 @@ type Trigger = {
handleContextClick: (event: React.MouseEvent<HTMLDivElement>) => void;
};
const GUESS_METADATA_WIDTH_TIMESTAMP_SIZE = 10;
const GUESS_METADATA_WIDTH_EXPIRE_TIMER_SIZE = 18;
const GUESS_METADATA_WIDTH_OUTGOING_SIZE: Record<MessageStatusType, number> = {
delivered: 24,
error: 24,
paused: 18,
'partial-sent': 24,
read: 24,
sending: 18,
sent: 24,
viewed: 24,
};
const EXPIRATION_CHECK_MINIMUM = 2000;
const EXPIRED_DELAY = 600;
const GROUP_AVATAR_SIZE = AvatarSize.TWENTY_EIGHT;
@ -123,7 +109,6 @@ const SENT_STATUSES = new Set<MessageStatusType>([
enum MetadataPlacement {
NotRendered,
RenderedByMessageAudioComponent,
InlineWithText,
Bottom,
}
@ -326,8 +311,6 @@ export type Props = PropsData &
Pick<ReactionPickerProps, 'renderEmojiPicker'>;
type State = {
metadataWidth: number;
expiring: boolean;
expired: boolean;
imageBroken: boolean;
@ -365,8 +348,6 @@ export class Message extends React.PureComponent<Props, State> {
super(props);
this.state = {
metadataWidth: this.guessMetadataWidth(),
expiring: false,
expired: false,
imageBroken: false,
@ -562,33 +543,7 @@ export class Message extends React.PureComponent<Props, State> {
return MetadataPlacement.Bottom;
}
return MetadataPlacement.InlineWithText;
}
/**
* A lot of the time, we add an invisible inline spacer for messages. This spacer is the
* same size as the message metadata. Unfortunately, we don't know how wide it is until
* we render it.
*
* This will probably guess wrong, but it's valuable to get close to the real value
* because it can reduce layout jumpiness.
*/
private guessMetadataWidth(): number {
const { direction, expirationLength, expirationTimestamp, status } =
this.props;
let result = GUESS_METADATA_WIDTH_TIMESTAMP_SIZE;
const hasExpireTimer = Boolean(expirationLength && expirationTimestamp);
if (hasExpireTimer) {
result += GUESS_METADATA_WIDTH_EXPIRE_TIMER_SIZE;
}
if (direction === 'outgoing' && status) {
result += GUESS_METADATA_WIDTH_OUTGOING_SIZE[status];
}
return result;
return MetadataPlacement.Bottom;
}
public startSelectedTimer(): void {
@ -710,33 +665,7 @@ export class Message extends React.PureComponent<Props, State> {
);
}
private updateMetadataWidth = (newMetadataWidth: number): void => {
this.setState(({ metadataWidth }) => ({
// We don't want text to jump around if the metadata shrinks, but we want to make
// sure we have enough room.
metadataWidth: Math.max(metadataWidth, newMetadataWidth),
}));
};
private renderMetadata(): ReactNode {
let isInline: boolean;
const metadataPlacement = this.getMetadataPlacement();
switch (metadataPlacement) {
case MetadataPlacement.NotRendered:
case MetadataPlacement.RenderedByMessageAudioComponent:
return null;
case MetadataPlacement.InlineWithText:
isInline = true;
break;
case MetadataPlacement.Bottom:
isInline = false;
break;
default:
log.error(missingCaseError(metadataPlacement));
isInline = false;
break;
}
const {
deletedForEveryone,
direction,
@ -764,11 +693,9 @@ export class Message extends React.PureComponent<Props, State> {
hasText={Boolean(text)}
i18n={i18n}
id={id}
isInline={isInline}
isShowingImage={this.isShowingImage()}
isSticker={isStickerLike}
isTapToViewExpired={isTapToViewExpired}
onWidthMeasured={isInline ? this.updateMetadataWidth : undefined}
showMessageDetail={showMessageDetail}
status={status}
textPending={textPending}
@ -1384,7 +1311,6 @@ export class Message extends React.PureComponent<Props, State> {
text,
textPending,
} = this.props;
const { metadataWidth } = this.state;
// eslint-disable-next-line no-nested-ternary
const contents = deletedForEveryone
@ -1406,6 +1332,7 @@ export class Message extends React.PureComponent<Props, State> {
? 'module-message__text--error'
: null
)}
dir="auto"
>
<MessageBodyReadMore
bodyRanges={bodyRanges}
@ -1419,9 +1346,6 @@ export class Message extends React.PureComponent<Props, State> {
text={contents || ''}
textPending={textPending}
/>
{this.getMetadataPlacement() === MetadataPlacement.InlineWithText && (
<MessageTextMetadataSpacer metadataWidth={metadataWidth} />
)}
</div>
);
}

View File

@ -104,7 +104,7 @@ export function MessageBody({
};
return (
<span dir="auto">
<span>
{disableLinks ? (
renderEmoji({
i18n,

View File

@ -1,13 +0,0 @@
// Copyright 2022 Signal Messenger, LLC
// SPDX-License-Identifier: AGPL-3.0-only
import type { ReactElement } from 'react';
import React from 'react';
const SPACING = 10;
export const MessageTextMetadataSpacer = ({
metadataWidth,
}: Readonly<{ metadataWidth: number }>): ReactElement => (
<span style={{ display: 'inline-block', width: metadataWidth + SPACING }} />
);