Remove React Virtualized from <Timeline>
This commit is contained in:
parent
1eafe79905
commit
0c31ad25ef
40 changed files with 798 additions and 2512 deletions
|
@ -1,11 +1,11 @@
|
|||
// Copyright 2021 Signal Messenger, LLC
|
||||
// Copyright 2021-2022 Signal Messenger, LLC
|
||||
// SPDX-License-Identifier: AGPL-3.0-only
|
||||
|
||||
import type { FunctionComponent, ReactNode } from 'react';
|
||||
import React, { useRef, useEffect, Children } from 'react';
|
||||
|
||||
import { usePrevious } from '../hooks/usePrevious';
|
||||
import { scrollToBottom } from '../util/scrollToBottom';
|
||||
import { scrollToBottom } from '../util/scrollUtil';
|
||||
|
||||
type PropsType = {
|
||||
children?: ReactNode;
|
||||
|
|
|
@ -21,7 +21,7 @@ const SampleMessage = ({
|
|||
direction,
|
||||
i18n,
|
||||
text,
|
||||
timestamp,
|
||||
timestampDeltaFromNow,
|
||||
status,
|
||||
style,
|
||||
}: {
|
||||
|
@ -29,7 +29,7 @@ const SampleMessage = ({
|
|||
direction: 'incoming' | 'outgoing';
|
||||
i18n: LocalizerType;
|
||||
text: string;
|
||||
timestamp: number;
|
||||
timestampDeltaFromNow: number;
|
||||
status: 'delivered' | 'read' | 'sent';
|
||||
style?: CSSProperties;
|
||||
}): JSX.Element => (
|
||||
|
@ -51,7 +51,7 @@ const SampleMessage = ({
|
|||
<span
|
||||
className={`module-message__metadata__date module-message__metadata__date--${direction}`}
|
||||
>
|
||||
{formatTime(i18n, timestamp)}
|
||||
{formatTime(i18n, Date.now() - timestampDeltaFromNow, Date.now())}
|
||||
</span>
|
||||
{direction === 'outgoing' && (
|
||||
<div
|
||||
|
@ -78,7 +78,7 @@ export const SampleMessageBubbles = ({
|
|||
direction={includeAnotherBubble ? 'outgoing' : 'incoming'}
|
||||
i18n={i18n}
|
||||
text={i18n('ChatColorPicker__sampleBubble1')}
|
||||
timestamp={Date.now() - A_FEW_DAYS_AGO}
|
||||
timestampDeltaFromNow={A_FEW_DAYS_AGO}
|
||||
status="read"
|
||||
style={firstBubbleStyle}
|
||||
/>
|
||||
|
@ -91,7 +91,7 @@ export const SampleMessageBubbles = ({
|
|||
direction="incoming"
|
||||
i18n={i18n}
|
||||
text={i18n('ChatColorPicker__sampleBubble2')}
|
||||
timestamp={Date.now() - A_FEW_DAYS_AGO / 2}
|
||||
timestampDeltaFromNow={A_FEW_DAYS_AGO / 2}
|
||||
status="read"
|
||||
/>
|
||||
<br />
|
||||
|
@ -103,7 +103,7 @@ export const SampleMessageBubbles = ({
|
|||
direction="outgoing"
|
||||
i18n={i18n}
|
||||
text={i18n('ChatColorPicker__sampleBubble3')}
|
||||
timestamp={Date.now()}
|
||||
timestampDeltaFromNow={0}
|
||||
status="delivered"
|
||||
style={backgroundStyle}
|
||||
/>
|
||||
|
|
|
@ -19,8 +19,8 @@ const getCommonProps = () => ({
|
|||
conversationId: 'fake-conversation-id',
|
||||
i18n,
|
||||
messageId: 'fake-message-id',
|
||||
messageSizeChanged: action('messageSizeChanged'),
|
||||
nextItem: undefined,
|
||||
now: Date.now(),
|
||||
returnToActiveCall: action('returnToActiveCall'),
|
||||
startCallingLobby: action('startCallingLobby'),
|
||||
});
|
||||
|
|
|
@ -2,8 +2,7 @@
|
|||
// SPDX-License-Identifier: AGPL-3.0-only
|
||||
|
||||
import type { ReactNode } from 'react';
|
||||
import React, { useState, useEffect } from 'react';
|
||||
import Measure from 'react-measure';
|
||||
import React from 'react';
|
||||
import { noop } from 'lodash';
|
||||
|
||||
import { SystemMessage } from './SystemMessage';
|
||||
|
@ -16,14 +15,12 @@ import {
|
|||
getCallingIcon,
|
||||
getCallingNotificationText,
|
||||
} from '../../util/callingNotification';
|
||||
import { usePrevious } from '../../hooks/usePrevious';
|
||||
import { missingCaseError } from '../../util/missingCaseError';
|
||||
import { Tooltip, TooltipPlacement } from '../Tooltip';
|
||||
import type { TimelineItemType } from './TimelineItem';
|
||||
import * as log from '../../logging/log';
|
||||
|
||||
export type PropsActionsType = {
|
||||
messageSizeChanged: (messageId: string, conversationId: string) => void;
|
||||
returnToActiveCall: () => void;
|
||||
startCallingLobby: (_: {
|
||||
conversationId: string;
|
||||
|
@ -34,27 +31,14 @@ export type PropsActionsType = {
|
|||
type PropsHousekeeping = {
|
||||
i18n: LocalizerType;
|
||||
conversationId: string;
|
||||
messageId: string;
|
||||
nextItem: undefined | TimelineItemType;
|
||||
now: number;
|
||||
};
|
||||
|
||||
type PropsType = CallingNotificationType & PropsActionsType & PropsHousekeeping;
|
||||
|
||||
export const CallingNotification: React.FC<PropsType> = React.memo(props => {
|
||||
const { conversationId, i18n, messageId, messageSizeChanged } = props;
|
||||
|
||||
const [height, setHeight] = useState<null | number>(null);
|
||||
const previousHeight = usePrevious<null | number>(null, height);
|
||||
|
||||
useEffect(() => {
|
||||
if (height === null) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (previousHeight !== null && height !== previousHeight) {
|
||||
messageSizeChanged(messageId, conversationId);
|
||||
}
|
||||
}, [height, previousHeight, conversationId, messageId, messageSizeChanged]);
|
||||
const { i18n, now } = props;
|
||||
|
||||
let timestamp: number;
|
||||
let wasMissed = false;
|
||||
|
@ -75,38 +59,25 @@ export const CallingNotification: React.FC<PropsType> = React.memo(props => {
|
|||
const icon = getCallingIcon(props);
|
||||
|
||||
return (
|
||||
<Measure
|
||||
bounds
|
||||
onResize={({ bounds }) => {
|
||||
if (!bounds) {
|
||||
log.error('We should be measuring the bounds');
|
||||
return;
|
||||
}
|
||||
setHeight(bounds.height);
|
||||
}}
|
||||
>
|
||||
{({ measureRef }) => (
|
||||
<SystemMessage
|
||||
button={renderCallingNotificationButton(props)}
|
||||
contents={
|
||||
<>
|
||||
{getCallingNotificationText(props, i18n)} ·{' '}
|
||||
<MessageTimestamp
|
||||
direction="outgoing"
|
||||
i18n={i18n}
|
||||
timestamp={timestamp}
|
||||
withImageNoCaption={false}
|
||||
withSticker={false}
|
||||
withTapToViewExpired={false}
|
||||
/>
|
||||
</>
|
||||
}
|
||||
icon={icon}
|
||||
isError={wasMissed}
|
||||
ref={measureRef}
|
||||
/>
|
||||
)}
|
||||
</Measure>
|
||||
<SystemMessage
|
||||
button={renderCallingNotificationButton(props)}
|
||||
contents={
|
||||
<>
|
||||
{getCallingNotificationText(props, i18n)} ·{' '}
|
||||
<MessageTimestamp
|
||||
direction="outgoing"
|
||||
i18n={i18n}
|
||||
now={now}
|
||||
timestamp={timestamp}
|
||||
withImageNoCaption={false}
|
||||
withSticker={false}
|
||||
withTapToViewExpired={false}
|
||||
/>
|
||||
</>
|
||||
}
|
||||
icon={icon}
|
||||
isError={wasMissed}
|
||||
/>
|
||||
);
|
||||
});
|
||||
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
// Copyright 2021 Signal Messenger, LLC
|
||||
// Copyright 2021-2022 Signal Messenger, LLC
|
||||
// SPDX-License-Identifier: AGPL-3.0-only
|
||||
|
||||
import * as React from 'react';
|
||||
|
@ -19,6 +19,7 @@ const i18n = setupI18n('en', enMessages);
|
|||
|
||||
story.add('Default', () => (
|
||||
<ChangeNumberNotification
|
||||
now={Date.now()}
|
||||
sender={getDefaultConversation()}
|
||||
timestamp={1618894800000}
|
||||
i18n={i18n}
|
||||
|
@ -27,6 +28,7 @@ story.add('Default', () => (
|
|||
|
||||
story.add('Long name', () => (
|
||||
<ChangeNumberNotification
|
||||
now={Date.now()}
|
||||
sender={getDefaultConversation({
|
||||
firstName: '💅😇🖋'.repeat(50),
|
||||
})}
|
||||
|
|
|
@ -18,12 +18,13 @@ export type PropsData = {
|
|||
|
||||
export type PropsHousekeeping = {
|
||||
i18n: LocalizerType;
|
||||
now: number;
|
||||
};
|
||||
|
||||
export type Props = PropsData & PropsHousekeeping;
|
||||
|
||||
export const ChangeNumberNotification: React.FC<Props> = props => {
|
||||
const { i18n, sender, timestamp } = props;
|
||||
const { i18n, now, sender, timestamp } = props;
|
||||
|
||||
return (
|
||||
<SystemMessage
|
||||
|
@ -37,7 +38,7 @@ export const ChangeNumberNotification: React.FC<Props> = props => {
|
|||
i18n={i18n}
|
||||
/>
|
||||
·
|
||||
<MessageTimestamp i18n={i18n} timestamp={timestamp} />
|
||||
<MessageTimestamp i18n={i18n} now={now} timestamp={timestamp} />
|
||||
</>
|
||||
}
|
||||
icon="phone"
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
// Copyright 2020-2021 Signal Messenger, LLC
|
||||
// Copyright 2020-2022 Signal Messenger, LLC
|
||||
// SPDX-License-Identifier: AGPL-3.0-only
|
||||
|
||||
import * as React from 'react';
|
||||
|
@ -55,7 +55,6 @@ storiesOf('Components/Conversation/ConversationHero', module)
|
|||
'Fifth',
|
||||
]}
|
||||
unblurAvatar={action('unblurAvatar')}
|
||||
onHeightChange={action('onHeightChange')}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
|
@ -83,7 +82,6 @@ storiesOf('Components/Conversation/ConversationHero', module)
|
|||
'Fourth',
|
||||
]}
|
||||
unblurAvatar={action('unblurAvatar')}
|
||||
onHeightChange={action('onHeightChange')}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
|
@ -106,7 +104,6 @@ storiesOf('Components/Conversation/ConversationHero', module)
|
|||
updateSharedGroups={updateSharedGroups}
|
||||
sharedGroupNames={['NYC Rock Climbers', 'Dinner Party', 'Friends 🌿']}
|
||||
unblurAvatar={action('unblurAvatar')}
|
||||
onHeightChange={action('onHeightChange')}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
|
@ -129,7 +126,6 @@ storiesOf('Components/Conversation/ConversationHero', module)
|
|||
updateSharedGroups={updateSharedGroups}
|
||||
sharedGroupNames={['NYC Rock Climbers', 'Dinner Party']}
|
||||
unblurAvatar={action('unblurAvatar')}
|
||||
onHeightChange={action('onHeightChange')}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
|
@ -152,7 +148,6 @@ storiesOf('Components/Conversation/ConversationHero', module)
|
|||
updateSharedGroups={updateSharedGroups}
|
||||
sharedGroupNames={['NYC Rock Climbers']}
|
||||
unblurAvatar={action('unblurAvatar')}
|
||||
onHeightChange={action('onHeightChange')}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
|
@ -175,7 +170,6 @@ storiesOf('Components/Conversation/ConversationHero', module)
|
|||
updateSharedGroups={updateSharedGroups}
|
||||
sharedGroupNames={[]}
|
||||
unblurAvatar={action('unblurAvatar')}
|
||||
onHeightChange={action('onHeightChange')}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
|
@ -198,7 +192,6 @@ storiesOf('Components/Conversation/ConversationHero', module)
|
|||
updateSharedGroups={updateSharedGroups}
|
||||
sharedGroupNames={[]}
|
||||
unblurAvatar={action('unblurAvatar')}
|
||||
onHeightChange={action('onHeightChange')}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
|
@ -221,7 +214,6 @@ storiesOf('Components/Conversation/ConversationHero', module)
|
|||
updateSharedGroups={updateSharedGroups}
|
||||
sharedGroupNames={[]}
|
||||
unblurAvatar={action('unblurAvatar')}
|
||||
onHeightChange={action('onHeightChange')}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
|
@ -243,7 +235,6 @@ storiesOf('Components/Conversation/ConversationHero', module)
|
|||
sharedGroupNames={[]}
|
||||
unblurAvatar={action('unblurAvatar')}
|
||||
updateSharedGroups={updateSharedGroups}
|
||||
onHeightChange={action('onHeightChange')}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
|
@ -265,7 +256,6 @@ storiesOf('Components/Conversation/ConversationHero', module)
|
|||
sharedGroupNames={[]}
|
||||
unblurAvatar={action('unblurAvatar')}
|
||||
updateSharedGroups={updateSharedGroups}
|
||||
onHeightChange={action('onHeightChange')}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
|
@ -285,7 +275,6 @@ storiesOf('Components/Conversation/ConversationHero', module)
|
|||
sharedGroupNames={[]}
|
||||
unblurAvatar={action('unblurAvatar')}
|
||||
updateSharedGroups={updateSharedGroups}
|
||||
onHeightChange={action('onHeightChange')}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
|
@ -305,7 +294,6 @@ storiesOf('Components/Conversation/ConversationHero', module)
|
|||
sharedGroupNames={[]}
|
||||
unblurAvatar={action('unblurAvatar')}
|
||||
updateSharedGroups={updateSharedGroups}
|
||||
onHeightChange={action('onHeightChange')}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
|
@ -326,7 +314,6 @@ storiesOf('Components/Conversation/ConversationHero', module)
|
|||
sharedGroupNames={[]}
|
||||
unblurAvatar={action('unblurAvatar')}
|
||||
updateSharedGroups={updateSharedGroups}
|
||||
onHeightChange={action('onHeightChange')}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
|
@ -347,7 +334,6 @@ storiesOf('Components/Conversation/ConversationHero', module)
|
|||
sharedGroupNames={[]}
|
||||
unblurAvatar={action('unblurAvatar')}
|
||||
updateSharedGroups={updateSharedGroups}
|
||||
onHeightChange={action('onHeightChange')}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
|
@ -367,7 +353,6 @@ storiesOf('Components/Conversation/ConversationHero', module)
|
|||
sharedGroupNames={[]}
|
||||
unblurAvatar={action('unblurAvatar')}
|
||||
updateSharedGroups={updateSharedGroups}
|
||||
onHeightChange={action('onHeightChange')}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
|
@ -386,7 +371,6 @@ storiesOf('Components/Conversation/ConversationHero', module)
|
|||
sharedGroupNames={[]}
|
||||
unblurAvatar={action('unblurAvatar')}
|
||||
updateSharedGroups={updateSharedGroups}
|
||||
onHeightChange={action('onHeightChange')}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
// Copyright 2020-2021 Signal Messenger, LLC
|
||||
// Copyright 2020-2022 Signal Messenger, LLC
|
||||
// SPDX-License-Identifier: AGPL-3.0-only
|
||||
|
||||
import React, { useEffect, useRef, useState } from 'react';
|
||||
import React, { useEffect, useState } from 'react';
|
||||
import type { Props as AvatarProps } from '../Avatar';
|
||||
import { Avatar, AvatarBlur } from '../Avatar';
|
||||
import { ContactName } from './ContactName';
|
||||
|
@ -12,7 +12,6 @@ import type { LocalizerType, ThemeType } from '../../types/Util';
|
|||
import { ConfirmationDialog } from '../ConfirmationDialog';
|
||||
import { Button, ButtonSize, ButtonVariant } from '../Button';
|
||||
import { shouldBlurAvatar } from '../../util/shouldBlurAvatar';
|
||||
import * as log from '../../logging/log';
|
||||
import { openLinkInWebBrowser } from '../../util/openLinkInWebBrowser';
|
||||
|
||||
export type Props = {
|
||||
|
@ -22,7 +21,6 @@ export type Props = {
|
|||
i18n: LocalizerType;
|
||||
isMe: boolean;
|
||||
membersCount?: number;
|
||||
onHeightChange: () => unknown;
|
||||
phoneNumber?: string;
|
||||
sharedGroupNames?: Array<string>;
|
||||
unblurAvatar: () => void;
|
||||
|
@ -111,13 +109,10 @@ export const ConversationHero = ({
|
|||
profileName,
|
||||
theme,
|
||||
title,
|
||||
onHeightChange,
|
||||
unblurAvatar,
|
||||
unblurredAvatarPath,
|
||||
updateSharedGroups,
|
||||
}: Props): JSX.Element => {
|
||||
const firstRenderRef = useRef(true);
|
||||
|
||||
const [isShowingMessageRequestWarning, setIsShowingMessageRequestWarning] =
|
||||
useState(false);
|
||||
const closeMessageRequestWarning = () => {
|
||||
|
@ -129,30 +124,6 @@ export const ConversationHero = ({
|
|||
updateSharedGroups();
|
||||
}, [updateSharedGroups]);
|
||||
|
||||
const sharedGroupNamesStringified = JSON.stringify(sharedGroupNames);
|
||||
useEffect(() => {
|
||||
const isFirstRender = firstRenderRef.current;
|
||||
if (isFirstRender) {
|
||||
firstRenderRef.current = false;
|
||||
return;
|
||||
}
|
||||
|
||||
log.info('ConversationHero: calling onHeightChange');
|
||||
onHeightChange();
|
||||
}, [
|
||||
about,
|
||||
conversationType,
|
||||
groupDescription,
|
||||
isMe,
|
||||
membersCount,
|
||||
name,
|
||||
onHeightChange,
|
||||
phoneNumber,
|
||||
profileName,
|
||||
title,
|
||||
sharedGroupNamesStringified,
|
||||
]);
|
||||
|
||||
let avatarBlur: AvatarBlur;
|
||||
let avatarOnClick: undefined | (() => void);
|
||||
if (
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
// Copyright 2020-2021 Signal Messenger, LLC
|
||||
// Copyright 2020-2022 Signal Messenger, LLC
|
||||
// SPDX-License-Identifier: AGPL-3.0-only
|
||||
|
||||
import * as React from 'react';
|
||||
|
@ -83,6 +83,7 @@ const MessageAudioContainer: React.FC<AudioAttachmentProps> = props => {
|
|||
audio={audio}
|
||||
computePeaks={computePeaks}
|
||||
setActiveAudioID={(id, context) => setActive({ id, context })}
|
||||
now={Date.now()}
|
||||
onFirstPlayed={action('onFirstPlayed')}
|
||||
activeAudioID={active.id}
|
||||
activeAudioContext={active.context}
|
||||
|
@ -131,6 +132,7 @@ const createProps = (overrideProps: Partial<Props> = {}): Props => ({
|
|||
getPreferredBadge: overrideProps.getPreferredBadge || (() => undefined),
|
||||
i18n,
|
||||
id: text('id', overrideProps.id || ''),
|
||||
now: Date.now(),
|
||||
renderingContext: 'storybook',
|
||||
interactionMode: overrideProps.interactionMode || 'keyboard',
|
||||
isSticker: isBoolean(overrideProps.isSticker)
|
||||
|
@ -149,7 +151,6 @@ const createProps = (overrideProps: Partial<Props> = {}): Props => ({
|
|||
markAttachmentAsCorrupted: action('markAttachmentAsCorrupted'),
|
||||
markViewed: action('markViewed'),
|
||||
messageExpanded: action('messageExpanded'),
|
||||
onHeightChange: action('onHeightChange'),
|
||||
openConversation: action('openConversation'),
|
||||
openLink: action('openLink'),
|
||||
previews: overrideProps.previews || [],
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
// Copyright 2018-2022 Signal Messenger, LLC
|
||||
// SPDX-License-Identifier: AGPL-3.0-only
|
||||
|
||||
import type { RefObject } from 'react';
|
||||
import type { ReactNode, RefObject } from 'react';
|
||||
import React from 'react';
|
||||
import ReactDOM, { createPortal } from 'react-dom';
|
||||
import classNames from 'classnames';
|
||||
|
@ -118,6 +118,7 @@ export type AudioAttachmentProps = {
|
|||
expirationLength?: number;
|
||||
expirationTimestamp?: number;
|
||||
id: string;
|
||||
now: number;
|
||||
played: boolean;
|
||||
showMessageDetail: (id: string) => void;
|
||||
status?: MessageStatusType;
|
||||
|
@ -210,6 +211,7 @@ export type PropsHousekeeping = {
|
|||
containerWidthBreakpoint: WidthBreakpoint;
|
||||
getPreferredBadge: PreferredBadgeSelectorType;
|
||||
i18n: LocalizerType;
|
||||
now: number;
|
||||
interactionMode: InteractionModeType;
|
||||
theme: ThemeType;
|
||||
disableMenu?: boolean;
|
||||
|
@ -224,7 +226,6 @@ export type PropsHousekeeping = {
|
|||
export type PropsActions = {
|
||||
clearSelectedMessage: () => unknown;
|
||||
doubleCheckMissingQuoteReference: (messageId: string) => unknown;
|
||||
onHeightChange: () => unknown;
|
||||
messageExpanded: (id: string, displayLimit: number) => unknown;
|
||||
checkForAccount: (identifier: string) => unknown;
|
||||
|
||||
|
@ -471,7 +472,6 @@ export class Message extends React.PureComponent<Props, State> {
|
|||
}
|
||||
|
||||
this.checkExpired();
|
||||
this.checkForHeightChange(prevProps);
|
||||
|
||||
if (
|
||||
prevProps.status === 'sending' &&
|
||||
|
@ -491,24 +491,6 @@ export class Message extends React.PureComponent<Props, State> {
|
|||
}
|
||||
}
|
||||
|
||||
public checkForHeightChange(prevProps: Props): void {
|
||||
const { contact, onHeightChange } = this.props;
|
||||
const willRenderSendMessageButton = Boolean(
|
||||
contact && contact.firstNumber && contact.isNumberOnSignal
|
||||
);
|
||||
|
||||
const { contact: previousContact } = prevProps;
|
||||
const previouslyRenderedSendMessageButton = Boolean(
|
||||
previousContact &&
|
||||
previousContact.firstNumber &&
|
||||
previousContact.isNumberOnSignal
|
||||
);
|
||||
|
||||
if (willRenderSendMessageButton !== previouslyRenderedSendMessageButton) {
|
||||
onHeightChange();
|
||||
}
|
||||
}
|
||||
|
||||
public startSelectedTimer(): void {
|
||||
const { clearSelectedMessage, interactionMode } = this.props;
|
||||
const { isSelected } = this.state;
|
||||
|
@ -609,6 +591,7 @@ export class Message extends React.PureComponent<Props, State> {
|
|||
isTapToViewExpired,
|
||||
status,
|
||||
i18n,
|
||||
now,
|
||||
text,
|
||||
textPending,
|
||||
timestamp,
|
||||
|
@ -640,6 +623,7 @@ export class Message extends React.PureComponent<Props, State> {
|
|||
isShowingImage={this.isShowingImage()}
|
||||
isSticker={isStickerLike}
|
||||
isTapToViewExpired={isTapToViewExpired}
|
||||
now={now}
|
||||
showMessageDetail={showMessageDetail}
|
||||
status={status}
|
||||
textPending={textPending}
|
||||
|
@ -705,6 +689,7 @@ export class Message extends React.PureComponent<Props, State> {
|
|||
kickOffAttachmentDownload,
|
||||
markAttachmentAsCorrupted,
|
||||
markViewed,
|
||||
now,
|
||||
quote,
|
||||
readStatus,
|
||||
reducedMotion,
|
||||
|
@ -834,6 +819,7 @@ export class Message extends React.PureComponent<Props, State> {
|
|||
expirationLength,
|
||||
expirationTimestamp,
|
||||
id,
|
||||
now,
|
||||
played,
|
||||
showMessageDetail,
|
||||
status,
|
||||
|
@ -1238,7 +1224,6 @@ export class Message extends React.PureComponent<Props, State> {
|
|||
i18n,
|
||||
id,
|
||||
messageExpanded,
|
||||
onHeightChange,
|
||||
openConversation,
|
||||
status,
|
||||
text,
|
||||
|
@ -1276,7 +1261,6 @@ export class Message extends React.PureComponent<Props, State> {
|
|||
id={id}
|
||||
messageExpanded={messageExpanded}
|
||||
openConversation={openConversation}
|
||||
onHeightChange={onHeightChange}
|
||||
text={contents || ''}
|
||||
textPending={textPending}
|
||||
/>
|
||||
|
@ -1284,13 +1268,9 @@ export class Message extends React.PureComponent<Props, State> {
|
|||
);
|
||||
}
|
||||
|
||||
public renderError(isCorrectSide: boolean): JSX.Element | null {
|
||||
private renderError(): ReactNode {
|
||||
const { status, direction } = this.props;
|
||||
|
||||
if (!isCorrectSide) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if (
|
||||
status !== 'paused' &&
|
||||
status !== 'error' &&
|
||||
|
@ -1312,10 +1292,7 @@ export class Message extends React.PureComponent<Props, State> {
|
|||
);
|
||||
}
|
||||
|
||||
public renderMenu(
|
||||
isCorrectSide: boolean,
|
||||
triggerId: string
|
||||
): JSX.Element | null {
|
||||
private renderMenu(triggerId: string): ReactNode {
|
||||
const {
|
||||
attachments,
|
||||
canDownload,
|
||||
|
@ -1334,7 +1311,7 @@ export class Message extends React.PureComponent<Props, State> {
|
|||
selectedReaction,
|
||||
} = this.props;
|
||||
|
||||
if (!isCorrectSide || disableMenu) {
|
||||
if (disableMenu) {
|
||||
return null;
|
||||
}
|
||||
|
||||
|
@ -2462,12 +2439,10 @@ export class Message extends React.PureComponent<Props, State> {
|
|||
onFocus={this.handleFocus}
|
||||
ref={this.focusRef}
|
||||
>
|
||||
{this.renderError(direction === 'incoming')}
|
||||
{this.renderMenu(direction === 'outgoing', triggerId)}
|
||||
{this.renderError()}
|
||||
{this.renderAvatar()}
|
||||
{this.renderContainer()}
|
||||
{this.renderError(direction === 'outgoing')}
|
||||
{this.renderMenu(direction === 'incoming', triggerId)}
|
||||
{this.renderMenu(triggerId)}
|
||||
{this.renderContextMenu(triggerId)}
|
||||
</div>
|
||||
);
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
// Copyright 2021 Signal Messenger, LLC
|
||||
// Copyright 2021-2022 Signal Messenger, LLC
|
||||
// SPDX-License-Identifier: AGPL-3.0-only
|
||||
|
||||
import React, { useRef, useEffect, useState } from 'react';
|
||||
|
@ -27,6 +27,7 @@ export type Props = {
|
|||
expirationLength?: number;
|
||||
expirationTimestamp?: number;
|
||||
id: string;
|
||||
now: number;
|
||||
played: boolean;
|
||||
showMessageDetail: (id: string) => void;
|
||||
status?: MessageStatusType;
|
||||
|
@ -157,6 +158,7 @@ export const MessageAudio: React.FC<Props> = (props: Props) => {
|
|||
expirationLength,
|
||||
expirationTimestamp,
|
||||
id,
|
||||
now,
|
||||
played,
|
||||
showMessageDetail,
|
||||
status,
|
||||
|
@ -539,6 +541,7 @@ export const MessageAudio: React.FC<Props> = (props: Props) => {
|
|||
isShowingImage={false}
|
||||
isSticker={false}
|
||||
isTapToViewExpired={false}
|
||||
now={now}
|
||||
showMessageDetail={showMessageDetail}
|
||||
status={status}
|
||||
textPending={textPending}
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
// Copyright 2021 Signal Messenger, LLC
|
||||
// Copyright 2021-2022 Signal Messenger, LLC
|
||||
// SPDX-License-Identifier: AGPL-3.0-only
|
||||
|
||||
import React, { useState } from 'react';
|
||||
|
@ -23,7 +23,6 @@ const createProps = (overrideProps: Partial<Props> = {}): Props => ({
|
|||
i18n,
|
||||
id: 'some-id',
|
||||
messageExpanded: action('messageExpanded'),
|
||||
onHeightChange: action('onHeightChange'),
|
||||
text: text('text', overrideProps.text || ''),
|
||||
});
|
||||
|
||||
|
|
|
@ -1,11 +1,10 @@
|
|||
// Copyright 2021 Signal Messenger, LLC
|
||||
// Copyright 2021-2022 Signal Messenger, LLC
|
||||
// SPDX-License-Identifier: AGPL-3.0-only
|
||||
|
||||
import React, { useEffect } from 'react';
|
||||
import React from 'react';
|
||||
|
||||
import type { Props as MessageBodyPropsType } from './MessageBody';
|
||||
import { MessageBody } from './MessageBody';
|
||||
import { usePrevious } from '../../hooks/usePrevious';
|
||||
|
||||
export type Props = Pick<
|
||||
MessageBodyPropsType,
|
||||
|
@ -20,7 +19,6 @@ export type Props = Pick<
|
|||
id: string;
|
||||
displayLimit?: number;
|
||||
messageExpanded: (id: string, displayLimit: number) => unknown;
|
||||
onHeightChange: () => unknown;
|
||||
};
|
||||
|
||||
const INITIAL_LENGTH = 800;
|
||||
|
@ -70,19 +68,11 @@ export function MessageBodyReadMore({
|
|||
i18n,
|
||||
id,
|
||||
messageExpanded,
|
||||
onHeightChange,
|
||||
openConversation,
|
||||
text,
|
||||
textPending,
|
||||
}: Props): JSX.Element {
|
||||
const maxLength = displayLimit || INITIAL_LENGTH;
|
||||
const previousMaxLength = usePrevious(maxLength, maxLength);
|
||||
|
||||
useEffect(() => {
|
||||
if (previousMaxLength !== maxLength) {
|
||||
onHeightChange();
|
||||
}
|
||||
}, [maxLength, previousMaxLength, onHeightChange]);
|
||||
|
||||
const { hasReadMore, text: slicedText } = graphemeAwareSlice(text, maxLength);
|
||||
|
||||
|
|
|
@ -23,6 +23,8 @@ import { SendStatus } from '../../messages/MessageSendState';
|
|||
import { WidthBreakpoint } from '../_util';
|
||||
import * as log from '../../logging/log';
|
||||
import { formatDateTimeLong } from '../../util/timestamp';
|
||||
import { clearTimeoutIfNecessary } from '../../util/clearTimeoutIfNecessary';
|
||||
import { MINUTE } from '../../util/durations';
|
||||
|
||||
export type Contact = Pick<
|
||||
ConversationType,
|
||||
|
@ -98,16 +100,20 @@ export type PropsReduxActions = Pick<
|
|||
export type ExternalProps = PropsData & PropsBackboneActions;
|
||||
export type Props = PropsData & PropsBackboneActions & PropsReduxActions;
|
||||
|
||||
type State = { nowThatUpdatesEveryMinute: number };
|
||||
|
||||
const contactSortCollator = new Intl.Collator();
|
||||
|
||||
const _keyForError = (error: Error): string => {
|
||||
return `${error.name}-${error.message}`;
|
||||
};
|
||||
|
||||
export class MessageDetail extends React.Component<Props> {
|
||||
private readonly focusRef = React.createRef<HTMLDivElement>();
|
||||
export class MessageDetail extends React.Component<Props, State> {
|
||||
override state = { nowThatUpdatesEveryMinute: Date.now() };
|
||||
|
||||
private readonly focusRef = React.createRef<HTMLDivElement>();
|
||||
private readonly messageContainerRef = React.createRef<HTMLDivElement>();
|
||||
private nowThatUpdatesEveryMinuteInterval?: ReturnType<typeof setInterval>;
|
||||
|
||||
public override componentDidMount(): void {
|
||||
// When this component is created, it's initially not part of the DOM, and then it's
|
||||
|
@ -117,6 +123,14 @@ export class MessageDetail extends React.Component<Props> {
|
|||
this.focusRef.current.focus();
|
||||
}
|
||||
});
|
||||
|
||||
this.nowThatUpdatesEveryMinuteInterval = setInterval(() => {
|
||||
this.setState({ nowThatUpdatesEveryMinute: Date.now() });
|
||||
}, MINUTE);
|
||||
}
|
||||
|
||||
public override componentWillUnmount(): void {
|
||||
clearTimeoutIfNecessary(this.nowThatUpdatesEveryMinuteInterval);
|
||||
}
|
||||
|
||||
public renderAvatar(contact: Contact): JSX.Element {
|
||||
|
@ -298,6 +312,7 @@ export class MessageDetail extends React.Component<Props> {
|
|||
showVisualAttachment,
|
||||
theme,
|
||||
} = this.props;
|
||||
const { nowThatUpdatesEveryMinute } = this.state;
|
||||
|
||||
return (
|
||||
// eslint-disable-next-line jsx-a11y/no-noninteractive-tabindex
|
||||
|
@ -335,7 +350,7 @@ export class MessageDetail extends React.Component<Props> {
|
|||
markAttachmentAsCorrupted={markAttachmentAsCorrupted}
|
||||
markViewed={markViewed}
|
||||
messageExpanded={noop}
|
||||
onHeightChange={noop}
|
||||
now={nowThatUpdatesEveryMinute}
|
||||
openConversation={openConversation}
|
||||
openLink={openLink}
|
||||
reactToMessage={reactToMessage}
|
||||
|
|
|
@ -22,6 +22,7 @@ type PropsType = {
|
|||
isShowingImage: boolean;
|
||||
isSticker?: boolean;
|
||||
isTapToViewExpired?: boolean;
|
||||
now: number;
|
||||
showMessageDetail: (id: string) => void;
|
||||
status?: MessageStatusType;
|
||||
textPending?: boolean;
|
||||
|
@ -40,6 +41,7 @@ export const MessageMetadata: FunctionComponent<PropsType> = props => {
|
|||
isShowingImage,
|
||||
isSticker,
|
||||
isTapToViewExpired,
|
||||
now,
|
||||
showMessageDetail,
|
||||
status,
|
||||
textPending,
|
||||
|
@ -97,6 +99,7 @@ export const MessageMetadata: FunctionComponent<PropsType> = props => {
|
|||
<MessageTimestamp
|
||||
i18n={i18n}
|
||||
timestamp={timestamp}
|
||||
now={now}
|
||||
direction={metadataDirection}
|
||||
withImageNoCaption={withImageNoCaption}
|
||||
withSticker={isSticker}
|
||||
|
|
|
@ -42,6 +42,7 @@ const times = (): Array<[string, number]> => [
|
|||
const createProps = (overrideProps: Partial<Props> = {}): Props => ({
|
||||
i18n,
|
||||
timestamp: overrideProps.timestamp,
|
||||
now: Date.now(),
|
||||
module: text('module', ''),
|
||||
withImageNoCaption: boolean('withImageNoCaption', false),
|
||||
withSticker: boolean('withSticker', false),
|
||||
|
|
|
@ -1,16 +1,17 @@
|
|||
// Copyright 2018-2022 Signal Messenger, LLC
|
||||
// SPDX-License-Identifier: AGPL-3.0-only
|
||||
|
||||
import type { ReactElement } from 'react';
|
||||
import React from 'react';
|
||||
import classNames from 'classnames';
|
||||
import moment from 'moment';
|
||||
|
||||
import { formatTime } from '../../util/timestamp';
|
||||
import { clearTimeoutIfNecessary } from '../../util/clearTimeoutIfNecessary';
|
||||
|
||||
import type { LocalizerType } from '../../types/Util';
|
||||
import { Time } from '../Time';
|
||||
|
||||
export type Props = {
|
||||
now: number;
|
||||
timestamp?: number;
|
||||
module?: string;
|
||||
withImageNoCaption?: boolean;
|
||||
|
@ -20,63 +21,36 @@ export type Props = {
|
|||
i18n: LocalizerType;
|
||||
};
|
||||
|
||||
const UPDATE_FREQUENCY = 60 * 1000;
|
||||
export function MessageTimestamp({
|
||||
direction,
|
||||
i18n,
|
||||
module,
|
||||
now,
|
||||
timestamp,
|
||||
withImageNoCaption,
|
||||
withSticker,
|
||||
withTapToViewExpired,
|
||||
}: Readonly<Props>): null | ReactElement {
|
||||
const moduleName = module || 'module-timestamp';
|
||||
|
||||
export class MessageTimestamp extends React.Component<Props> {
|
||||
private interval: NodeJS.Timeout | null;
|
||||
|
||||
constructor(props: Props) {
|
||||
super(props);
|
||||
|
||||
this.interval = null;
|
||||
if (timestamp === null || timestamp === undefined) {
|
||||
return null;
|
||||
}
|
||||
|
||||
public override componentDidMount(): void {
|
||||
const update = () => {
|
||||
this.setState({
|
||||
// Used to trigger renders
|
||||
// eslint-disable-next-line react/no-unused-state
|
||||
lastUpdated: Date.now(),
|
||||
});
|
||||
};
|
||||
this.interval = setInterval(update, UPDATE_FREQUENCY);
|
||||
}
|
||||
|
||||
public override componentWillUnmount(): void {
|
||||
clearTimeoutIfNecessary(this.interval);
|
||||
}
|
||||
|
||||
public override render(): JSX.Element | null {
|
||||
const {
|
||||
direction,
|
||||
i18n,
|
||||
module,
|
||||
timestamp,
|
||||
withImageNoCaption,
|
||||
withSticker,
|
||||
withTapToViewExpired,
|
||||
} = this.props;
|
||||
const moduleName = module || 'module-timestamp';
|
||||
|
||||
if (timestamp === null || timestamp === undefined) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return (
|
||||
<span
|
||||
className={classNames(
|
||||
moduleName,
|
||||
direction ? `${moduleName}--${direction}` : null,
|
||||
withTapToViewExpired && direction
|
||||
? `${moduleName}--${direction}-with-tap-to-view-expired`
|
||||
: null,
|
||||
withImageNoCaption ? `${moduleName}--with-image-no-caption` : null,
|
||||
withSticker ? `${moduleName}--with-sticker` : null
|
||||
)}
|
||||
title={moment(timestamp).format('llll')}
|
||||
>
|
||||
{formatTime(i18n, timestamp)}
|
||||
</span>
|
||||
);
|
||||
}
|
||||
return (
|
||||
<Time
|
||||
className={classNames(
|
||||
moduleName,
|
||||
direction ? `${moduleName}--${direction}` : null,
|
||||
withTapToViewExpired && direction
|
||||
? `${moduleName}--${direction}-with-tap-to-view-expired`
|
||||
: null,
|
||||
withImageNoCaption ? `${moduleName}--with-image-no-caption` : null,
|
||||
withSticker ? `${moduleName}--with-sticker` : null
|
||||
)}
|
||||
timestamp={timestamp}
|
||||
>
|
||||
{formatTime(i18n, timestamp, now)}
|
||||
</Time>
|
||||
);
|
||||
}
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
// Copyright 2020-2021 Signal Messenger, LLC
|
||||
// Copyright 2020-2022 Signal Messenger, LLC
|
||||
// SPDX-License-Identifier: AGPL-3.0-only
|
||||
|
||||
import * as React from 'react';
|
||||
|
@ -59,6 +59,7 @@ const defaultMessageProps: MessagesProps = {
|
|||
getPreferredBadge: () => undefined,
|
||||
i18n,
|
||||
id: 'messageId',
|
||||
now: Date.now(),
|
||||
renderingContext: 'storybook',
|
||||
interactionMode: 'keyboard',
|
||||
isBlocked: false,
|
||||
|
@ -67,7 +68,6 @@ const defaultMessageProps: MessagesProps = {
|
|||
markAttachmentAsCorrupted: action('default--markAttachmentAsCorrupted'),
|
||||
markViewed: action('default--markViewed'),
|
||||
messageExpanded: action('default--message-expanded'),
|
||||
onHeightChange: action('default--onHeightChange'),
|
||||
openConversation: action('default--openConversation'),
|
||||
openLink: action('default--openLink'),
|
||||
previews: [],
|
||||
|
|
|
@ -324,11 +324,9 @@ const actions = () => ({
|
|||
'acknowledgeGroupMemberNameCollisions'
|
||||
),
|
||||
checkForAccount: action('checkForAccount'),
|
||||
clearChangedMessages: action('clearChangedMessages'),
|
||||
clearInvitedUuidsForNewlyCreatedGroup: action(
|
||||
'clearInvitedUuidsForNewlyCreatedGroup'
|
||||
),
|
||||
setLoadCountdownStart: action('setLoadCountdownStart'),
|
||||
setIsNearBottom: action('setIsNearBottom'),
|
||||
learnMoreAboutDeliveryIssue: action('learnMoreAboutDeliveryIssue'),
|
||||
loadAndScroll: action('loadAndScroll'),
|
||||
|
@ -358,7 +356,6 @@ const actions = () => ({
|
|||
displayTapToViewMessage: action('displayTapToViewMessage'),
|
||||
doubleCheckMissingQuoteReference: action('doubleCheckMissingQuoteReference'),
|
||||
|
||||
onHeightChange: action('onHeightChange'),
|
||||
openLink: action('openLink'),
|
||||
scrollToQuotedMessage: action('scrollToQuotedMessage'),
|
||||
showExpiredIncomingTapToViewToast: action(
|
||||
|
@ -373,7 +370,6 @@ const actions = () => ({
|
|||
|
||||
downloadNewVersion: action('downloadNewVersion'),
|
||||
|
||||
messageSizeChanged: action('messageSizeChanged'),
|
||||
startCallingLobby: action('startCallingLobby'),
|
||||
returnToActiveCall: action('returnToActiveCall'),
|
||||
|
||||
|
@ -401,11 +397,13 @@ const renderItem = ({
|
|||
containerElementRef,
|
||||
containerWidthBreakpoint,
|
||||
isOldestTimelineItem,
|
||||
now,
|
||||
}: {
|
||||
messageId: string;
|
||||
containerElementRef: React.RefObject<HTMLElement>;
|
||||
containerWidthBreakpoint: WidthBreakpoint;
|
||||
isOldestTimelineItem: boolean;
|
||||
now: number;
|
||||
}) => (
|
||||
<TimelineItem
|
||||
getPreferredBadge={() => undefined}
|
||||
|
@ -417,6 +415,7 @@ const renderItem = ({
|
|||
item={items[messageId]}
|
||||
previousItem={undefined}
|
||||
nextItem={undefined}
|
||||
now={now}
|
||||
i18n={i18n}
|
||||
interactionMode="keyboard"
|
||||
theme={ThemeType.light}
|
||||
|
@ -460,7 +459,6 @@ const renderHeroRow = () => {
|
|||
profileName={getProfileName()}
|
||||
phoneNumber={getPhoneNumber()}
|
||||
conversationType="direct"
|
||||
onHeightChange={action('onHeightChange in ConversationHero')}
|
||||
sharedGroupNames={['NYC Rock Climbers', 'Dinner Party']}
|
||||
theme={theme}
|
||||
unblurAvatar={action('unblurAvatar')}
|
||||
|
@ -486,6 +484,7 @@ const renderTypingBubble = () => (
|
|||
);
|
||||
|
||||
const useProps = (overrideProps: Partial<PropsType> = {}): PropsType => ({
|
||||
discardMessages: action('discardMessages'),
|
||||
getPreferredBadge: () => undefined,
|
||||
i18n,
|
||||
theme: React.useContext(StorybookThemeContext),
|
||||
|
@ -493,6 +492,7 @@ const useProps = (overrideProps: Partial<PropsType> = {}): PropsType => ({
|
|||
getTimestampForMessage: Date.now,
|
||||
haveNewest: boolean('haveNewest', overrideProps.haveNewest !== false),
|
||||
haveOldest: boolean('haveOldest', overrideProps.haveOldest !== false),
|
||||
isConversationSelected: true,
|
||||
isIncomingMessageRequest: boolean(
|
||||
'isIncomingMessageRequest',
|
||||
overrideProps.isIncomingMessageRequest === true
|
||||
|
@ -502,7 +502,6 @@ const useProps = (overrideProps: Partial<PropsType> = {}): PropsType => ({
|
|||
overrideProps.isLoadingMessages === false
|
||||
),
|
||||
items: overrideProps.items || Object.keys(items),
|
||||
resetCounter: 0,
|
||||
scrollToIndex: overrideProps.scrollToIndex,
|
||||
scrollToIndexCounter: 0,
|
||||
totalUnread: number('totalUnread', overrideProps.totalUnread || 0),
|
||||
|
|
File diff suppressed because it is too large
Load diff
|
@ -86,16 +86,15 @@ const getDefaultProps = () => ({
|
|||
showExpiredOutgoingTapToViewToast: action(
|
||||
'showExpiredIncomingTapToViewToast'
|
||||
),
|
||||
onHeightChange: action('onHeightChange'),
|
||||
openLink: action('openLink'),
|
||||
scrollToQuotedMessage: action('scrollToQuotedMessage'),
|
||||
downloadNewVersion: action('downloadNewVersion'),
|
||||
showIdentity: action('showIdentity'),
|
||||
messageSizeChanged: action('messageSizeChanged'),
|
||||
startCallingLobby: action('startCallingLobby'),
|
||||
returnToActiveCall: action('returnToActiveCall'),
|
||||
previousItem: undefined,
|
||||
nextItem: undefined,
|
||||
now: Date.now(),
|
||||
|
||||
renderContact,
|
||||
renderUniversalTimerNotification,
|
||||
|
|
|
@ -54,7 +54,6 @@ import type { SmartContactRendererType } from '../../groupChange';
|
|||
import { ResetSessionNotification } from './ResetSessionNotification';
|
||||
import type { PropsType as ProfileChangeNotificationPropsType } from './ProfileChangeNotification';
|
||||
import { ProfileChangeNotification } from './ProfileChangeNotification';
|
||||
import * as log from '../../logging/log';
|
||||
import type { FullJSXType } from '../Intl';
|
||||
|
||||
type CallHistoryType = {
|
||||
|
@ -156,6 +155,7 @@ type PropsLocalType = {
|
|||
theme: ThemeType;
|
||||
previousItem: undefined | TimelineItemType;
|
||||
nextItem: undefined | TimelineItemType;
|
||||
now: number;
|
||||
};
|
||||
|
||||
type PropsActionsType = MessageActionsType &
|
||||
|
@ -188,8 +188,8 @@ export class TimelineItem extends React.PureComponent<PropsType> {
|
|||
item,
|
||||
i18n,
|
||||
theme,
|
||||
messageSizeChanged,
|
||||
nextItem,
|
||||
now,
|
||||
previousItem,
|
||||
renderContact,
|
||||
renderUniversalTimerNotification,
|
||||
|
@ -199,8 +199,12 @@ export class TimelineItem extends React.PureComponent<PropsType> {
|
|||
} = this.props;
|
||||
|
||||
if (!item) {
|
||||
log.warn(`TimelineItem: item ${id} provided was falsey`);
|
||||
|
||||
// This can happen under normal conditions.
|
||||
//
|
||||
// `<Timeline>` and `<TimelineItem>` are connected to Redux separately. If a
|
||||
// timeline item is removed from Redux, `<TimelineItem>` might re-render before
|
||||
// `<Timeline>` does, which means we'll try to render nothing. This should resolve
|
||||
// itself quickly, as soon as `<Timeline>` re-renders.
|
||||
return null;
|
||||
}
|
||||
|
||||
|
@ -229,9 +233,8 @@ export class TimelineItem extends React.PureComponent<PropsType> {
|
|||
<CallingNotification
|
||||
conversationId={conversationId}
|
||||
i18n={i18n}
|
||||
messageId={id}
|
||||
messageSizeChanged={messageSizeChanged}
|
||||
nextItem={nextItem}
|
||||
now={now}
|
||||
returnToActiveCall={returnToActiveCall}
|
||||
startCallingLobby={startCallingLobby}
|
||||
{...item.data}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue