Use a hook for the ever-updating now
This commit is contained in:
parent
f8724e91da
commit
4e48d7792b
21 changed files with 34 additions and 78 deletions
|
@ -163,7 +163,6 @@ export const StoryListItem = ({
|
||||||
<MessageTimestamp
|
<MessageTimestamp
|
||||||
i18n={i18n}
|
i18n={i18n}
|
||||||
module="StoryListItem__info--timestamp"
|
module="StoryListItem__info--timestamp"
|
||||||
now={Date.now()}
|
|
||||||
timestamp={timestamp}
|
timestamp={timestamp}
|
||||||
/>
|
/>
|
||||||
</>
|
</>
|
||||||
|
|
|
@ -233,7 +233,6 @@ export const StoryViewer = ({
|
||||||
<MessageTimestamp
|
<MessageTimestamp
|
||||||
i18n={i18n}
|
i18n={i18n}
|
||||||
module="StoryViewer__meta--timestamp"
|
module="StoryViewer__meta--timestamp"
|
||||||
now={Date.now()}
|
|
||||||
timestamp={visibleStory.timestamp}
|
timestamp={visibleStory.timestamp}
|
||||||
/>
|
/>
|
||||||
<div className="StoryViewer__progress">
|
<div className="StoryViewer__progress">
|
||||||
|
|
|
@ -249,7 +249,6 @@ export const StoryViewsNRepliesModal = ({
|
||||||
<MessageTimestamp
|
<MessageTimestamp
|
||||||
i18n={i18n}
|
i18n={i18n}
|
||||||
module="StoryViewsNRepliesModal__reply--timestamp"
|
module="StoryViewsNRepliesModal__reply--timestamp"
|
||||||
now={Date.now()}
|
|
||||||
timestamp={reply.timestamp}
|
timestamp={reply.timestamp}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
@ -285,7 +284,6 @@ export const StoryViewsNRepliesModal = ({
|
||||||
<MessageTimestamp
|
<MessageTimestamp
|
||||||
i18n={i18n}
|
i18n={i18n}
|
||||||
module="StoryViewsNRepliesModal__reply--timestamp"
|
module="StoryViewsNRepliesModal__reply--timestamp"
|
||||||
now={Date.now()}
|
|
||||||
timestamp={reply.timestamp}
|
timestamp={reply.timestamp}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
@ -324,7 +322,6 @@ export const StoryViewsNRepliesModal = ({
|
||||||
<MessageTimestamp
|
<MessageTimestamp
|
||||||
i18n={i18n}
|
i18n={i18n}
|
||||||
module="StoryViewsNRepliesModal__view--timestamp"
|
module="StoryViewsNRepliesModal__view--timestamp"
|
||||||
now={Date.now()}
|
|
||||||
timestamp={view.timestamp}
|
timestamp={view.timestamp}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -32,13 +32,12 @@ type PropsHousekeeping = {
|
||||||
i18n: LocalizerType;
|
i18n: LocalizerType;
|
||||||
conversationId: string;
|
conversationId: string;
|
||||||
nextItem: undefined | TimelineItemType;
|
nextItem: undefined | TimelineItemType;
|
||||||
now: number;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
type PropsType = CallingNotificationType & PropsActionsType & PropsHousekeeping;
|
type PropsType = CallingNotificationType & PropsActionsType & PropsHousekeeping;
|
||||||
|
|
||||||
export const CallingNotification: React.FC<PropsType> = React.memo(props => {
|
export const CallingNotification: React.FC<PropsType> = React.memo(props => {
|
||||||
const { i18n, now } = props;
|
const { i18n } = props;
|
||||||
|
|
||||||
let timestamp: number;
|
let timestamp: number;
|
||||||
let wasMissed = false;
|
let wasMissed = false;
|
||||||
|
@ -67,7 +66,6 @@ export const CallingNotification: React.FC<PropsType> = React.memo(props => {
|
||||||
<MessageTimestamp
|
<MessageTimestamp
|
||||||
direction="outgoing"
|
direction="outgoing"
|
||||||
i18n={i18n}
|
i18n={i18n}
|
||||||
now={now}
|
|
||||||
timestamp={timestamp}
|
timestamp={timestamp}
|
||||||
withImageNoCaption={false}
|
withImageNoCaption={false}
|
||||||
withSticker={false}
|
withSticker={false}
|
||||||
|
|
|
@ -19,7 +19,6 @@ const i18n = setupI18n('en', enMessages);
|
||||||
|
|
||||||
story.add('Default', () => (
|
story.add('Default', () => (
|
||||||
<ChangeNumberNotification
|
<ChangeNumberNotification
|
||||||
now={Date.now()}
|
|
||||||
sender={getDefaultConversation()}
|
sender={getDefaultConversation()}
|
||||||
timestamp={1618894800000}
|
timestamp={1618894800000}
|
||||||
i18n={i18n}
|
i18n={i18n}
|
||||||
|
@ -28,7 +27,6 @@ story.add('Default', () => (
|
||||||
|
|
||||||
story.add('Long name', () => (
|
story.add('Long name', () => (
|
||||||
<ChangeNumberNotification
|
<ChangeNumberNotification
|
||||||
now={Date.now()}
|
|
||||||
sender={getDefaultConversation({
|
sender={getDefaultConversation({
|
||||||
firstName: '💅😇🖋'.repeat(50),
|
firstName: '💅😇🖋'.repeat(50),
|
||||||
})}
|
})}
|
||||||
|
|
|
@ -18,13 +18,12 @@ export type PropsData = {
|
||||||
|
|
||||||
export type PropsHousekeeping = {
|
export type PropsHousekeeping = {
|
||||||
i18n: LocalizerType;
|
i18n: LocalizerType;
|
||||||
now: number;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
export type Props = PropsData & PropsHousekeeping;
|
export type Props = PropsData & PropsHousekeeping;
|
||||||
|
|
||||||
export const ChangeNumberNotification: React.FC<Props> = props => {
|
export const ChangeNumberNotification: React.FC<Props> = props => {
|
||||||
const { i18n, now, sender, timestamp } = props;
|
const { i18n, sender, timestamp } = props;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<SystemMessage
|
<SystemMessage
|
||||||
|
@ -38,7 +37,7 @@ export const ChangeNumberNotification: React.FC<Props> = props => {
|
||||||
i18n={i18n}
|
i18n={i18n}
|
||||||
/>
|
/>
|
||||||
·
|
·
|
||||||
<MessageTimestamp i18n={i18n} now={now} timestamp={timestamp} />
|
<MessageTimestamp i18n={i18n} timestamp={timestamp} />
|
||||||
</>
|
</>
|
||||||
}
|
}
|
||||||
icon="phone"
|
icon="phone"
|
||||||
|
|
|
@ -84,7 +84,6 @@ const MessageAudioContainer: React.FC<AudioAttachmentProps> = props => {
|
||||||
audio={audio}
|
audio={audio}
|
||||||
computePeaks={computePeaks}
|
computePeaks={computePeaks}
|
||||||
setActiveAudioID={(id, context) => setActive({ id, context })}
|
setActiveAudioID={(id, context) => setActive({ id, context })}
|
||||||
now={Date.now()}
|
|
||||||
onFirstPlayed={action('onFirstPlayed')}
|
onFirstPlayed={action('onFirstPlayed')}
|
||||||
activeAudioID={active.id}
|
activeAudioID={active.id}
|
||||||
activeAudioContext={active.context}
|
activeAudioContext={active.context}
|
||||||
|
@ -134,7 +133,6 @@ const createProps = (overrideProps: Partial<Props> = {}): Props => ({
|
||||||
getPreferredBadge: overrideProps.getPreferredBadge || (() => undefined),
|
getPreferredBadge: overrideProps.getPreferredBadge || (() => undefined),
|
||||||
i18n,
|
i18n,
|
||||||
id: text('id', overrideProps.id || ''),
|
id: text('id', overrideProps.id || ''),
|
||||||
now: Date.now(),
|
|
||||||
renderingContext: 'storybook',
|
renderingContext: 'storybook',
|
||||||
interactionMode: overrideProps.interactionMode || 'keyboard',
|
interactionMode: overrideProps.interactionMode || 'keyboard',
|
||||||
isSticker: isBoolean(overrideProps.isSticker)
|
isSticker: isBoolean(overrideProps.isSticker)
|
||||||
|
|
|
@ -143,7 +143,6 @@ export type AudioAttachmentProps = {
|
||||||
expirationLength?: number;
|
expirationLength?: number;
|
||||||
expirationTimestamp?: number;
|
expirationTimestamp?: number;
|
||||||
id: string;
|
id: string;
|
||||||
now: number;
|
|
||||||
played: boolean;
|
played: boolean;
|
||||||
showMessageDetail: (id: string) => void;
|
showMessageDetail: (id: string) => void;
|
||||||
status?: MessageStatusType;
|
status?: MessageStatusType;
|
||||||
|
@ -240,7 +239,6 @@ export type PropsHousekeeping = {
|
||||||
disableScroll?: boolean;
|
disableScroll?: boolean;
|
||||||
getPreferredBadge: PreferredBadgeSelectorType;
|
getPreferredBadge: PreferredBadgeSelectorType;
|
||||||
i18n: LocalizerType;
|
i18n: LocalizerType;
|
||||||
now: number;
|
|
||||||
interactionMode: InteractionModeType;
|
interactionMode: InteractionModeType;
|
||||||
item?: TimelineItemType;
|
item?: TimelineItemType;
|
||||||
nextItem?: TimelineItemType;
|
nextItem?: TimelineItemType;
|
||||||
|
@ -709,7 +707,6 @@ export class Message extends React.PureComponent<Props, State> {
|
||||||
isTapToViewExpired,
|
isTapToViewExpired,
|
||||||
status,
|
status,
|
||||||
i18n,
|
i18n,
|
||||||
now,
|
|
||||||
text,
|
text,
|
||||||
textPending,
|
textPending,
|
||||||
timestamp,
|
timestamp,
|
||||||
|
@ -732,7 +729,6 @@ export class Message extends React.PureComponent<Props, State> {
|
||||||
isShowingImage={this.isShowingImage()}
|
isShowingImage={this.isShowingImage()}
|
||||||
isSticker={isStickerLike}
|
isSticker={isStickerLike}
|
||||||
isTapToViewExpired={isTapToViewExpired}
|
isTapToViewExpired={isTapToViewExpired}
|
||||||
now={now}
|
|
||||||
onWidthMeasured={isInline ? this.updateMetadataWidth : undefined}
|
onWidthMeasured={isInline ? this.updateMetadataWidth : undefined}
|
||||||
showMessageDetail={showMessageDetail}
|
showMessageDetail={showMessageDetail}
|
||||||
status={status}
|
status={status}
|
||||||
|
@ -786,7 +782,6 @@ export class Message extends React.PureComponent<Props, State> {
|
||||||
kickOffAttachmentDownload,
|
kickOffAttachmentDownload,
|
||||||
markAttachmentAsCorrupted,
|
markAttachmentAsCorrupted,
|
||||||
markViewed,
|
markViewed,
|
||||||
now,
|
|
||||||
quote,
|
quote,
|
||||||
readStatus,
|
readStatus,
|
||||||
reducedMotion,
|
reducedMotion,
|
||||||
|
@ -922,7 +917,6 @@ export class Message extends React.PureComponent<Props, State> {
|
||||||
expirationLength,
|
expirationLength,
|
||||||
expirationTimestamp,
|
expirationTimestamp,
|
||||||
id,
|
id,
|
||||||
now,
|
|
||||||
played,
|
played,
|
||||||
showMessageDetail,
|
showMessageDetail,
|
||||||
status,
|
status,
|
||||||
|
|
|
@ -28,7 +28,6 @@ export type Props = {
|
||||||
expirationLength?: number;
|
expirationLength?: number;
|
||||||
expirationTimestamp?: number;
|
expirationTimestamp?: number;
|
||||||
id: string;
|
id: string;
|
||||||
now: number;
|
|
||||||
played: boolean;
|
played: boolean;
|
||||||
showMessageDetail: (id: string) => void;
|
showMessageDetail: (id: string) => void;
|
||||||
status?: MessageStatusType;
|
status?: MessageStatusType;
|
||||||
|
@ -160,7 +159,6 @@ export const MessageAudio: React.FC<Props> = (props: Props) => {
|
||||||
expirationLength,
|
expirationLength,
|
||||||
expirationTimestamp,
|
expirationTimestamp,
|
||||||
id,
|
id,
|
||||||
now,
|
|
||||||
played,
|
played,
|
||||||
showMessageDetail,
|
showMessageDetail,
|
||||||
status,
|
status,
|
||||||
|
@ -543,7 +541,6 @@ export const MessageAudio: React.FC<Props> = (props: Props) => {
|
||||||
isShowingImage={false}
|
isShowingImage={false}
|
||||||
isSticker={false}
|
isSticker={false}
|
||||||
isTapToViewExpired={false}
|
isTapToViewExpired={false}
|
||||||
now={now}
|
|
||||||
showMessageDetail={showMessageDetail}
|
showMessageDetail={showMessageDetail}
|
||||||
status={status}
|
status={status}
|
||||||
textPending={textPending}
|
textPending={textPending}
|
||||||
|
|
|
@ -23,8 +23,6 @@ import { SendStatus } from '../../messages/MessageSendState';
|
||||||
import { WidthBreakpoint } from '../_util';
|
import { WidthBreakpoint } from '../_util';
|
||||||
import * as log from '../../logging/log';
|
import * as log from '../../logging/log';
|
||||||
import { formatDateTimeLong } from '../../util/timestamp';
|
import { formatDateTimeLong } from '../../util/timestamp';
|
||||||
import { clearTimeoutIfNecessary } from '../../util/clearTimeoutIfNecessary';
|
|
||||||
import { MINUTE } from '../../util/durations';
|
|
||||||
|
|
||||||
export type Contact = Pick<
|
export type Contact = Pick<
|
||||||
ConversationType,
|
ConversationType,
|
||||||
|
@ -101,20 +99,15 @@ export type PropsReduxActions = Pick<
|
||||||
export type ExternalProps = PropsData & PropsBackboneActions;
|
export type ExternalProps = PropsData & PropsBackboneActions;
|
||||||
export type Props = PropsData & PropsBackboneActions & PropsReduxActions;
|
export type Props = PropsData & PropsBackboneActions & PropsReduxActions;
|
||||||
|
|
||||||
type State = { nowThatUpdatesEveryMinute: number };
|
|
||||||
|
|
||||||
const contactSortCollator = new Intl.Collator();
|
const contactSortCollator = new Intl.Collator();
|
||||||
|
|
||||||
const _keyForError = (error: Error): string => {
|
const _keyForError = (error: Error): string => {
|
||||||
return `${error.name}-${error.message}`;
|
return `${error.name}-${error.message}`;
|
||||||
};
|
};
|
||||||
|
|
||||||
export class MessageDetail extends React.Component<Props, State> {
|
export class MessageDetail extends React.Component<Props> {
|
||||||
override state = { nowThatUpdatesEveryMinute: Date.now() };
|
|
||||||
|
|
||||||
private readonly focusRef = React.createRef<HTMLDivElement>();
|
private readonly focusRef = React.createRef<HTMLDivElement>();
|
||||||
private readonly messageContainerRef = React.createRef<HTMLDivElement>();
|
private readonly messageContainerRef = React.createRef<HTMLDivElement>();
|
||||||
private nowThatUpdatesEveryMinuteInterval?: ReturnType<typeof setInterval>;
|
|
||||||
|
|
||||||
public override componentDidMount(): void {
|
public override componentDidMount(): void {
|
||||||
// When this component is created, it's initially not part of the DOM, and then it's
|
// When this component is created, it's initially not part of the DOM, and then it's
|
||||||
|
@ -124,14 +117,6 @@ export class MessageDetail extends React.Component<Props, State> {
|
||||||
this.focusRef.current.focus();
|
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 {
|
public renderAvatar(contact: Contact): JSX.Element {
|
||||||
|
@ -314,7 +299,6 @@ export class MessageDetail extends React.Component<Props, State> {
|
||||||
showVisualAttachment,
|
showVisualAttachment,
|
||||||
theme,
|
theme,
|
||||||
} = this.props;
|
} = this.props;
|
||||||
const { nowThatUpdatesEveryMinute } = this.state;
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
// eslint-disable-next-line jsx-a11y/no-noninteractive-tabindex
|
// eslint-disable-next-line jsx-a11y/no-noninteractive-tabindex
|
||||||
|
@ -352,7 +336,6 @@ export class MessageDetail extends React.Component<Props, State> {
|
||||||
markAttachmentAsCorrupted={markAttachmentAsCorrupted}
|
markAttachmentAsCorrupted={markAttachmentAsCorrupted}
|
||||||
markViewed={markViewed}
|
markViewed={markViewed}
|
||||||
messageExpanded={noop}
|
messageExpanded={noop}
|
||||||
now={nowThatUpdatesEveryMinute}
|
|
||||||
openConversation={openConversation}
|
openConversation={openConversation}
|
||||||
openLink={openLink}
|
openLink={openLink}
|
||||||
reactToMessage={reactToMessage}
|
reactToMessage={reactToMessage}
|
||||||
|
|
|
@ -24,7 +24,6 @@ type PropsType = {
|
||||||
isShowingImage: boolean;
|
isShowingImage: boolean;
|
||||||
isSticker?: boolean;
|
isSticker?: boolean;
|
||||||
isTapToViewExpired?: boolean;
|
isTapToViewExpired?: boolean;
|
||||||
now: number;
|
|
||||||
onWidthMeasured?: (width: number) => unknown;
|
onWidthMeasured?: (width: number) => unknown;
|
||||||
showMessageDetail: (id: string) => void;
|
showMessageDetail: (id: string) => void;
|
||||||
status?: MessageStatusType;
|
status?: MessageStatusType;
|
||||||
|
@ -44,7 +43,6 @@ export const MessageMetadata = ({
|
||||||
isShowingImage,
|
isShowingImage,
|
||||||
isSticker,
|
isSticker,
|
||||||
isTapToViewExpired,
|
isTapToViewExpired,
|
||||||
now,
|
|
||||||
onWidthMeasured,
|
onWidthMeasured,
|
||||||
showMessageDetail,
|
showMessageDetail,
|
||||||
status,
|
status,
|
||||||
|
@ -106,7 +104,6 @@ export const MessageMetadata = ({
|
||||||
<MessageTimestamp
|
<MessageTimestamp
|
||||||
i18n={i18n}
|
i18n={i18n}
|
||||||
timestamp={timestamp}
|
timestamp={timestamp}
|
||||||
now={now}
|
|
||||||
direction={metadataDirection}
|
direction={metadataDirection}
|
||||||
withImageNoCaption={withImageNoCaption}
|
withImageNoCaption={withImageNoCaption}
|
||||||
withSticker={isSticker}
|
withSticker={isSticker}
|
||||||
|
|
|
@ -42,7 +42,6 @@ const times = (): Array<[string, number]> => [
|
||||||
const createProps = (overrideProps: Partial<Props> = {}): Props => ({
|
const createProps = (overrideProps: Partial<Props> = {}): Props => ({
|
||||||
i18n,
|
i18n,
|
||||||
timestamp: overrideProps.timestamp || Date.now(),
|
timestamp: overrideProps.timestamp || Date.now(),
|
||||||
now: Date.now(),
|
|
||||||
module: text('module', ''),
|
module: text('module', ''),
|
||||||
withImageNoCaption: boolean('withImageNoCaption', false),
|
withImageNoCaption: boolean('withImageNoCaption', false),
|
||||||
withSticker: boolean('withSticker', false),
|
withSticker: boolean('withSticker', false),
|
||||||
|
|
|
@ -9,9 +9,9 @@ import { formatTime } from '../../util/timestamp';
|
||||||
|
|
||||||
import type { LocalizerType } from '../../types/Util';
|
import type { LocalizerType } from '../../types/Util';
|
||||||
import { Time } from '../Time';
|
import { Time } from '../Time';
|
||||||
|
import { useNowThatUpdatesEveryMinute } from '../../hooks/useNowThatUpdatesEveryMinute';
|
||||||
|
|
||||||
export type Props = {
|
export type Props = {
|
||||||
now: number;
|
|
||||||
timestamp: number;
|
timestamp: number;
|
||||||
module?: string;
|
module?: string;
|
||||||
withImageNoCaption?: boolean;
|
withImageNoCaption?: boolean;
|
||||||
|
@ -25,12 +25,12 @@ export function MessageTimestamp({
|
||||||
direction,
|
direction,
|
||||||
i18n,
|
i18n,
|
||||||
module,
|
module,
|
||||||
now,
|
|
||||||
timestamp,
|
timestamp,
|
||||||
withImageNoCaption,
|
withImageNoCaption,
|
||||||
withSticker,
|
withSticker,
|
||||||
withTapToViewExpired,
|
withTapToViewExpired,
|
||||||
}: Readonly<Props>): ReactElement {
|
}: Readonly<Props>): ReactElement {
|
||||||
|
const now = useNowThatUpdatesEveryMinute();
|
||||||
const moduleName = module || 'module-timestamp';
|
const moduleName = module || 'module-timestamp';
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
|
|
@ -61,7 +61,6 @@ const defaultMessageProps: MessagesProps = {
|
||||||
getPreferredBadge: () => undefined,
|
getPreferredBadge: () => undefined,
|
||||||
i18n,
|
i18n,
|
||||||
id: 'messageId',
|
id: 'messageId',
|
||||||
now: Date.now(),
|
|
||||||
renderingContext: 'storybook',
|
renderingContext: 'storybook',
|
||||||
interactionMode: 'keyboard',
|
interactionMode: 'keyboard',
|
||||||
isBlocked: false,
|
isBlocked: false,
|
||||||
|
|
|
@ -414,13 +414,11 @@ const renderItem = ({
|
||||||
containerElementRef,
|
containerElementRef,
|
||||||
containerWidthBreakpoint,
|
containerWidthBreakpoint,
|
||||||
isOldestTimelineItem,
|
isOldestTimelineItem,
|
||||||
now,
|
|
||||||
}: {
|
}: {
|
||||||
messageId: string;
|
messageId: string;
|
||||||
containerElementRef: React.RefObject<HTMLElement>;
|
containerElementRef: React.RefObject<HTMLElement>;
|
||||||
containerWidthBreakpoint: WidthBreakpoint;
|
containerWidthBreakpoint: WidthBreakpoint;
|
||||||
isOldestTimelineItem: boolean;
|
isOldestTimelineItem: boolean;
|
||||||
now: number;
|
|
||||||
}) => (
|
}) => (
|
||||||
<TimelineItem
|
<TimelineItem
|
||||||
getPreferredBadge={() => undefined}
|
getPreferredBadge={() => undefined}
|
||||||
|
@ -432,7 +430,6 @@ const renderItem = ({
|
||||||
item={items[messageId]}
|
item={items[messageId]}
|
||||||
previousItem={undefined}
|
previousItem={undefined}
|
||||||
nextItem={undefined}
|
nextItem={undefined}
|
||||||
now={now}
|
|
||||||
i18n={i18n}
|
i18n={i18n}
|
||||||
interactionMode="keyboard"
|
interactionMode="keyboard"
|
||||||
theme={ThemeType.light}
|
theme={ThemeType.light}
|
||||||
|
|
|
@ -41,7 +41,6 @@ import {
|
||||||
scrollToBottom,
|
scrollToBottom,
|
||||||
setScrollBottom,
|
setScrollBottom,
|
||||||
} from '../../util/scrollUtil';
|
} from '../../util/scrollUtil';
|
||||||
import { MINUTE } from '../../util/durations';
|
|
||||||
|
|
||||||
const AT_BOTTOM_THRESHOLD = 15;
|
const AT_BOTTOM_THRESHOLD = 15;
|
||||||
const MIN_ROW_HEIGHT = 18;
|
const MIN_ROW_HEIGHT = 18;
|
||||||
|
@ -118,7 +117,6 @@ type PropsHousekeepingType = {
|
||||||
isOldestTimelineItem: boolean;
|
isOldestTimelineItem: boolean;
|
||||||
messageId: string;
|
messageId: string;
|
||||||
nextMessageId: undefined | string;
|
nextMessageId: undefined | string;
|
||||||
now: number;
|
|
||||||
previousMessageId: undefined | string;
|
previousMessageId: undefined | string;
|
||||||
unreadIndicatorPlacement: undefined | UnreadIndicatorPlacement;
|
unreadIndicatorPlacement: undefined | UnreadIndicatorPlacement;
|
||||||
}) => JSX.Element;
|
}) => JSX.Element;
|
||||||
|
@ -175,7 +173,6 @@ type StateType = {
|
||||||
hasRecentlyScrolled: boolean;
|
hasRecentlyScrolled: boolean;
|
||||||
lastMeasuredWarningHeight: number;
|
lastMeasuredWarningHeight: number;
|
||||||
newestFullyVisibleMessageId?: string;
|
newestFullyVisibleMessageId?: string;
|
||||||
nowThatUpdatesEveryMinute: number;
|
|
||||||
oldestPartiallyVisibleMessageId?: string;
|
oldestPartiallyVisibleMessageId?: string;
|
||||||
widthBreakpoint: WidthBreakpoint;
|
widthBreakpoint: WidthBreakpoint;
|
||||||
};
|
};
|
||||||
|
@ -269,12 +266,10 @@ export class Timeline extends React.Component<
|
||||||
|
|
||||||
private hasRecentlyScrolledTimeout?: NodeJS.Timeout;
|
private hasRecentlyScrolledTimeout?: NodeJS.Timeout;
|
||||||
private delayedPeekTimeout?: NodeJS.Timeout;
|
private delayedPeekTimeout?: NodeJS.Timeout;
|
||||||
private nowThatUpdatesEveryMinuteInterval?: NodeJS.Timeout;
|
|
||||||
|
|
||||||
override state: StateType = {
|
override state: StateType = {
|
||||||
hasRecentlyScrolled: true,
|
hasRecentlyScrolled: true,
|
||||||
hasDismissedDirectContactSpoofingWarning: false,
|
hasDismissedDirectContactSpoofingWarning: false,
|
||||||
nowThatUpdatesEveryMinute: Date.now(),
|
|
||||||
|
|
||||||
// These may be swiftly overridden.
|
// These may be swiftly overridden.
|
||||||
lastMeasuredWarningHeight: 0,
|
lastMeasuredWarningHeight: 0,
|
||||||
|
@ -512,17 +507,10 @@ export class Timeline extends React.Component<
|
||||||
const { id, peekGroupCallForTheFirstTime } = this.props;
|
const { id, peekGroupCallForTheFirstTime } = this.props;
|
||||||
peekGroupCallForTheFirstTime(id);
|
peekGroupCallForTheFirstTime(id);
|
||||||
}, 500);
|
}, 500);
|
||||||
|
|
||||||
this.nowThatUpdatesEveryMinuteInterval = setInterval(() => {
|
|
||||||
this.setState({ nowThatUpdatesEveryMinute: Date.now() });
|
|
||||||
}, MINUTE);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public override componentWillUnmount(): void {
|
public override componentWillUnmount(): void {
|
||||||
const {
|
const { delayedPeekTimeout } = this;
|
||||||
delayedPeekTimeout,
|
|
||||||
nowThatUpdatesEveryMinuteInterval: nowThatUpdatesEveryMinuteTimeout,
|
|
||||||
} = this;
|
|
||||||
|
|
||||||
window.unregisterForActive(this.markNewestFullyVisibleMessageRead);
|
window.unregisterForActive(this.markNewestFullyVisibleMessageRead);
|
||||||
|
|
||||||
|
@ -530,7 +518,6 @@ export class Timeline extends React.Component<
|
||||||
this.intersectionObserver?.disconnect();
|
this.intersectionObserver?.disconnect();
|
||||||
|
|
||||||
clearTimeoutIfNecessary(delayedPeekTimeout);
|
clearTimeoutIfNecessary(delayedPeekTimeout);
|
||||||
clearTimeoutIfNecessary(nowThatUpdatesEveryMinuteTimeout);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public override getSnapshotBeforeUpdate(
|
public override getSnapshotBeforeUpdate(
|
||||||
|
@ -774,7 +761,6 @@ export class Timeline extends React.Component<
|
||||||
hasRecentlyScrolled,
|
hasRecentlyScrolled,
|
||||||
lastMeasuredWarningHeight,
|
lastMeasuredWarningHeight,
|
||||||
newestFullyVisibleMessageId,
|
newestFullyVisibleMessageId,
|
||||||
nowThatUpdatesEveryMinute,
|
|
||||||
oldestPartiallyVisibleMessageId,
|
oldestPartiallyVisibleMessageId,
|
||||||
widthBreakpoint,
|
widthBreakpoint,
|
||||||
} = this.state;
|
} = this.state;
|
||||||
|
@ -883,7 +869,6 @@ export class Timeline extends React.Component<
|
||||||
isOldestTimelineItem: haveOldest && itemIndex === 0,
|
isOldestTimelineItem: haveOldest && itemIndex === 0,
|
||||||
messageId,
|
messageId,
|
||||||
nextMessageId,
|
nextMessageId,
|
||||||
now: nowThatUpdatesEveryMinute,
|
|
||||||
previousMessageId,
|
previousMessageId,
|
||||||
unreadIndicatorPlacement,
|
unreadIndicatorPlacement,
|
||||||
})}
|
})}
|
||||||
|
|
|
@ -155,7 +155,6 @@ type PropsLocalType = {
|
||||||
theme: ThemeType;
|
theme: ThemeType;
|
||||||
previousItem: undefined | TimelineItemType;
|
previousItem: undefined | TimelineItemType;
|
||||||
nextItem: undefined | TimelineItemType;
|
nextItem: undefined | TimelineItemType;
|
||||||
now: number;
|
|
||||||
unreadIndicatorPlacement?: undefined | UnreadIndicatorPlacement;
|
unreadIndicatorPlacement?: undefined | UnreadIndicatorPlacement;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -190,7 +189,6 @@ export class TimelineItem extends React.PureComponent<PropsType> {
|
||||||
i18n,
|
i18n,
|
||||||
theme,
|
theme,
|
||||||
nextItem,
|
nextItem,
|
||||||
now,
|
|
||||||
previousItem,
|
previousItem,
|
||||||
renderContact,
|
renderContact,
|
||||||
renderUniversalTimerNotification,
|
renderUniversalTimerNotification,
|
||||||
|
@ -237,7 +235,6 @@ export class TimelineItem extends React.PureComponent<PropsType> {
|
||||||
conversationId={conversationId}
|
conversationId={conversationId}
|
||||||
i18n={i18n}
|
i18n={i18n}
|
||||||
nextItem={nextItem}
|
nextItem={nextItem}
|
||||||
now={now}
|
|
||||||
returnToActiveCall={returnToActiveCall}
|
returnToActiveCall={returnToActiveCall}
|
||||||
startCallingLobby={startCallingLobby}
|
startCallingLobby={startCallingLobby}
|
||||||
{...item.data}
|
{...item.data}
|
||||||
|
|
27
ts/hooks/useNowThatUpdatesEveryMinute.ts
Normal file
27
ts/hooks/useNowThatUpdatesEveryMinute.ts
Normal file
|
@ -0,0 +1,27 @@
|
||||||
|
// Copyright 2022 Signal Messenger, LLC
|
||||||
|
// SPDX-License-Identifier: AGPL-3.0-only
|
||||||
|
|
||||||
|
import { EventEmitter } from 'events';
|
||||||
|
import { useEffect, useState } from 'react';
|
||||||
|
|
||||||
|
import { MINUTE } from '../util/durations';
|
||||||
|
|
||||||
|
const ev = new EventEmitter();
|
||||||
|
setInterval(() => ev.emit('tick'), MINUTE);
|
||||||
|
|
||||||
|
export function useNowThatUpdatesEveryMinute(): number {
|
||||||
|
const [now, setNow] = useState(Date.now());
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
const updateNow = () => setNow(Date.now());
|
||||||
|
updateNow();
|
||||||
|
|
||||||
|
ev.on('tick', updateNow);
|
||||||
|
|
||||||
|
return () => {
|
||||||
|
ev.off('tick', updateNow);
|
||||||
|
};
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
return now;
|
||||||
|
}
|
|
@ -29,7 +29,6 @@ export type Props = {
|
||||||
expirationLength?: number;
|
expirationLength?: number;
|
||||||
expirationTimestamp?: number;
|
expirationTimestamp?: number;
|
||||||
id: string;
|
id: string;
|
||||||
now: number;
|
|
||||||
played: boolean;
|
played: boolean;
|
||||||
showMessageDetail: (id: string) => void;
|
showMessageDetail: (id: string) => void;
|
||||||
status?: MessageStatusType;
|
status?: MessageStatusType;
|
||||||
|
|
|
@ -108,7 +108,6 @@ function renderItem({
|
||||||
isOldestTimelineItem,
|
isOldestTimelineItem,
|
||||||
messageId,
|
messageId,
|
||||||
nextMessageId,
|
nextMessageId,
|
||||||
now,
|
|
||||||
previousMessageId,
|
previousMessageId,
|
||||||
unreadIndicatorPlacement,
|
unreadIndicatorPlacement,
|
||||||
}: {
|
}: {
|
||||||
|
@ -119,7 +118,6 @@ function renderItem({
|
||||||
isOldestTimelineItem: boolean;
|
isOldestTimelineItem: boolean;
|
||||||
messageId: string;
|
messageId: string;
|
||||||
nextMessageId: undefined | string;
|
nextMessageId: undefined | string;
|
||||||
now: number;
|
|
||||||
previousMessageId: undefined | string;
|
previousMessageId: undefined | string;
|
||||||
unreadIndicatorPlacement: undefined | UnreadIndicatorPlacement;
|
unreadIndicatorPlacement: undefined | UnreadIndicatorPlacement;
|
||||||
}): JSX.Element {
|
}): JSX.Element {
|
||||||
|
@ -133,7 +131,6 @@ function renderItem({
|
||||||
messageId={messageId}
|
messageId={messageId}
|
||||||
previousMessageId={previousMessageId}
|
previousMessageId={previousMessageId}
|
||||||
nextMessageId={nextMessageId}
|
nextMessageId={nextMessageId}
|
||||||
now={now}
|
|
||||||
renderEmojiPicker={renderEmojiPicker}
|
renderEmojiPicker={renderEmojiPicker}
|
||||||
renderReactionPicker={renderReactionPicker}
|
renderReactionPicker={renderReactionPicker}
|
||||||
renderAudioAttachment={renderAudioAttachment}
|
renderAudioAttachment={renderAudioAttachment}
|
||||||
|
|
|
@ -28,7 +28,6 @@ type ExternalProps = {
|
||||||
messageId: string;
|
messageId: string;
|
||||||
nextMessageId: undefined | string;
|
nextMessageId: undefined | string;
|
||||||
previousMessageId: undefined | string;
|
previousMessageId: undefined | string;
|
||||||
now: number;
|
|
||||||
unreadIndicatorPlacement: undefined | UnreadIndicatorPlacement;
|
unreadIndicatorPlacement: undefined | UnreadIndicatorPlacement;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -48,7 +47,6 @@ const mapStateToProps = (state: StateType, props: ExternalProps) => {
|
||||||
messageId,
|
messageId,
|
||||||
nextMessageId,
|
nextMessageId,
|
||||||
previousMessageId,
|
previousMessageId,
|
||||||
now,
|
|
||||||
unreadIndicatorPlacement,
|
unreadIndicatorPlacement,
|
||||||
} = props;
|
} = props;
|
||||||
|
|
||||||
|
@ -71,7 +69,6 @@ const mapStateToProps = (state: StateType, props: ExternalProps) => {
|
||||||
item,
|
item,
|
||||||
previousItem,
|
previousItem,
|
||||||
nextItem,
|
nextItem,
|
||||||
now,
|
|
||||||
id: messageId,
|
id: messageId,
|
||||||
containerElementRef,
|
containerElementRef,
|
||||||
conversationId,
|
conversationId,
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue