Fix story reply box interactions
This commit is contained in:
parent
7775f7d806
commit
72f979ea1d
7 changed files with 180 additions and 128 deletions
|
@ -1,7 +1,7 @@
|
|||
// Copyright 2022 Signal Messenger, LLC
|
||||
// SPDX-License-Identifier: AGPL-3.0-only
|
||||
|
||||
import React, { useCallback, useState } from 'react';
|
||||
import React, { useCallback, useEffect, useRef, useState } from 'react';
|
||||
import classNames from 'classnames';
|
||||
import { usePopper } from 'react-popper';
|
||||
import type { AttachmentType } from '../types/Attachment';
|
||||
|
@ -52,6 +52,7 @@ export type PropsType = {
|
|||
authorTitle: string;
|
||||
getPreferredBadge: PreferredBadgeSelectorType;
|
||||
i18n: LocalizerType;
|
||||
isGroupStory?: boolean;
|
||||
isMyStory?: boolean;
|
||||
onClose: () => unknown;
|
||||
onReact: (emoji: string) => unknown;
|
||||
|
@ -76,6 +77,7 @@ export const StoryViewsNRepliesModal = ({
|
|||
authorTitle,
|
||||
getPreferredBadge,
|
||||
i18n,
|
||||
isGroupStory,
|
||||
isMyStory,
|
||||
onClose,
|
||||
onReact,
|
||||
|
@ -91,7 +93,8 @@ export const StoryViewsNRepliesModal = ({
|
|||
storyPreviewAttachment,
|
||||
views,
|
||||
}: PropsType): JSX.Element => {
|
||||
const inputApiRef = React.useRef<InputApi | undefined>();
|
||||
const inputApiRef = useRef<InputApi | undefined>();
|
||||
const [bottom, setBottom] = useState<HTMLDivElement | null>(null);
|
||||
const [messageBodyText, setMessageBodyText] = useState('');
|
||||
const [showReactionPicker, setShowReactionPicker] = useState(false);
|
||||
|
||||
|
@ -122,13 +125,19 @@ export const StoryViewsNRepliesModal = ({
|
|||
strategy: 'fixed',
|
||||
});
|
||||
|
||||
useEffect(() => {
|
||||
if (replies.length) {
|
||||
bottom?.scrollIntoView({ behavior: 'smooth' });
|
||||
}
|
||||
}, [bottom, replies.length]);
|
||||
|
||||
let composerElement: JSX.Element | undefined;
|
||||
|
||||
if (!isMyStory) {
|
||||
composerElement = (
|
||||
<div className="StoryViewsNRepliesModal__compose-container">
|
||||
<div className="StoryViewsNRepliesModal__composer">
|
||||
{!replies.length && (
|
||||
{!isGroupStory && (
|
||||
<Quote
|
||||
authorTitle={authorTitle}
|
||||
conversationColor="ultramarine"
|
||||
|
@ -154,7 +163,10 @@ export const StoryViewsNRepliesModal = ({
|
|||
setMessageBodyText(messageText);
|
||||
}}
|
||||
onPickEmoji={insertEmoji}
|
||||
onSubmit={onReply}
|
||||
onSubmit={(...args) => {
|
||||
inputApiRef.current?.reset();
|
||||
onReply(...args);
|
||||
}}
|
||||
onTextTooLong={onTextTooLong}
|
||||
placeholder={i18n('StoryViewsNRepliesModal__placeholder')}
|
||||
theme={ThemeType.dark}
|
||||
|
@ -204,12 +216,48 @@ export const StoryViewsNRepliesModal = ({
|
|||
);
|
||||
}
|
||||
|
||||
const repliesElement = replies.length ? (
|
||||
<div className="StoryViewsNRepliesModal__replies">
|
||||
{replies.map(reply =>
|
||||
reply.reactionEmoji ? (
|
||||
<div className="StoryViewsNRepliesModal__reaction" key={reply.id}>
|
||||
<div className="StoryViewsNRepliesModal__reaction--container">
|
||||
let repliesElement: JSX.Element | undefined;
|
||||
|
||||
if (replies.length) {
|
||||
repliesElement = (
|
||||
<div className="StoryViewsNRepliesModal__replies">
|
||||
{replies.map(reply =>
|
||||
reply.reactionEmoji ? (
|
||||
<div className="StoryViewsNRepliesModal__reaction" key={reply.id}>
|
||||
<div className="StoryViewsNRepliesModal__reaction--container">
|
||||
<Avatar
|
||||
acceptedMessageRequest={reply.acceptedMessageRequest}
|
||||
avatarPath={reply.avatarPath}
|
||||
badge={undefined}
|
||||
color={getAvatarColor(reply.color)}
|
||||
conversationType="direct"
|
||||
i18n={i18n}
|
||||
isMe={Boolean(reply.isMe)}
|
||||
name={reply.name}
|
||||
profileName={reply.profileName}
|
||||
sharedGroupNames={reply.sharedGroupNames || []}
|
||||
size={AvatarSize.TWENTY_EIGHT}
|
||||
title={reply.title}
|
||||
/>
|
||||
<div className="StoryViewsNRepliesModal__reaction--body">
|
||||
<div className="StoryViewsNRepliesModal__reply--title">
|
||||
<ContactName
|
||||
contactNameColor={reply.contactNameColor}
|
||||
title={reply.title}
|
||||
/>
|
||||
</div>
|
||||
{i18n('StoryViewsNRepliesModal__reacted')}
|
||||
<MessageTimestamp
|
||||
i18n={i18n}
|
||||
module="StoryViewsNRepliesModal__reply--timestamp"
|
||||
timestamp={reply.timestamp}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<Emojify text={reply.reactionEmoji} />
|
||||
</div>
|
||||
) : (
|
||||
<div className="StoryViewsNRepliesModal__reply" key={reply.id}>
|
||||
<Avatar
|
||||
acceptedMessageRequest={reply.acceptedMessageRequest}
|
||||
avatarPath={reply.avatarPath}
|
||||
|
@ -224,14 +272,32 @@ export const StoryViewsNRepliesModal = ({
|
|||
size={AvatarSize.TWENTY_EIGHT}
|
||||
title={reply.title}
|
||||
/>
|
||||
<div className="StoryViewsNRepliesModal__reaction--body">
|
||||
<div
|
||||
className={classNames(
|
||||
'StoryViewsNRepliesModal__message-bubble',
|
||||
{
|
||||
'StoryViewsNRepliesModal__message-bubble--doe': Boolean(
|
||||
reply.deletedForEveryone
|
||||
),
|
||||
}
|
||||
)}
|
||||
>
|
||||
<div className="StoryViewsNRepliesModal__reply--title">
|
||||
<ContactName
|
||||
contactNameColor={reply.contactNameColor}
|
||||
title={reply.title}
|
||||
/>
|
||||
</div>
|
||||
{i18n('StoryViewsNRepliesModal__reacted')}
|
||||
|
||||
<MessageBody
|
||||
i18n={i18n}
|
||||
text={
|
||||
reply.deletedForEveryone
|
||||
? i18n('message--deletedForEveryone')
|
||||
: String(reply.body)
|
||||
}
|
||||
/>
|
||||
|
||||
<MessageTimestamp
|
||||
i18n={i18n}
|
||||
module="StoryViewsNRepliesModal__reply--timestamp"
|
||||
|
@ -239,58 +305,18 @@ export const StoryViewsNRepliesModal = ({
|
|||
/>
|
||||
</div>
|
||||
</div>
|
||||
<Emojify text={reply.reactionEmoji} />
|
||||
</div>
|
||||
) : (
|
||||
<div className="StoryViewsNRepliesModal__reply" key={reply.id}>
|
||||
<Avatar
|
||||
acceptedMessageRequest={reply.acceptedMessageRequest}
|
||||
avatarPath={reply.avatarPath}
|
||||
badge={undefined}
|
||||
color={getAvatarColor(reply.color)}
|
||||
conversationType="direct"
|
||||
i18n={i18n}
|
||||
isMe={Boolean(reply.isMe)}
|
||||
name={reply.name}
|
||||
profileName={reply.profileName}
|
||||
sharedGroupNames={reply.sharedGroupNames || []}
|
||||
size={AvatarSize.TWENTY_EIGHT}
|
||||
title={reply.title}
|
||||
/>
|
||||
<div
|
||||
className={classNames('StoryViewsNRepliesModal__message-bubble', {
|
||||
'StoryViewsNRepliesModal__message-bubble--doe': Boolean(
|
||||
reply.deletedForEveryone
|
||||
),
|
||||
})}
|
||||
>
|
||||
<div className="StoryViewsNRepliesModal__reply--title">
|
||||
<ContactName
|
||||
contactNameColor={reply.contactNameColor}
|
||||
title={reply.title}
|
||||
/>
|
||||
</div>
|
||||
|
||||
<MessageBody
|
||||
i18n={i18n}
|
||||
text={
|
||||
reply.deletedForEveryone
|
||||
? i18n('message--deletedForEveryone')
|
||||
: String(reply.body)
|
||||
}
|
||||
/>
|
||||
|
||||
<MessageTimestamp
|
||||
i18n={i18n}
|
||||
module="StoryViewsNRepliesModal__reply--timestamp"
|
||||
timestamp={reply.timestamp}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
)}
|
||||
</div>
|
||||
) : undefined;
|
||||
)
|
||||
)}
|
||||
<div ref={setBottom} />
|
||||
</div>
|
||||
);
|
||||
} else if (isGroupStory) {
|
||||
repliesElement = (
|
||||
<div className="StoryViewsNRepliesModal__replies--none">
|
||||
{i18n('StoryViewsNRepliesModal__no-replies')}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
const viewsElement = views.length ? (
|
||||
<div className="StoryViewsNRepliesModal__views">
|
||||
|
@ -358,28 +384,26 @@ export const StoryViewsNRepliesModal = ({
|
|||
</Tabs>
|
||||
) : undefined;
|
||||
|
||||
const hasOnlyViewsElement =
|
||||
viewsElement && !repliesElement && !composerElement;
|
||||
|
||||
return (
|
||||
<Modal
|
||||
i18n={i18n}
|
||||
moduleClassName={classNames('StoryViewsNRepliesModal', {
|
||||
'StoryViewsNRepliesModal--group': Boolean(
|
||||
views.length && replies.length
|
||||
),
|
||||
})}
|
||||
moduleClassName="StoryViewsNRepliesModal"
|
||||
onClose={onClose}
|
||||
useFocusTrap={!hasOnlyViewsElement}
|
||||
useFocusTrap={Boolean(composerElement)}
|
||||
theme={Theme.Dark}
|
||||
>
|
||||
{tabsElement || (
|
||||
<>
|
||||
{viewsElement}
|
||||
{repliesElement}
|
||||
{composerElement}
|
||||
</>
|
||||
)}
|
||||
<div
|
||||
className={classNames({
|
||||
'StoryViewsNRepliesModal--group': Boolean(isGroupStory),
|
||||
})}
|
||||
>
|
||||
{tabsElement || (
|
||||
<>
|
||||
{viewsElement || repliesElement}
|
||||
{composerElement}
|
||||
</>
|
||||
)}
|
||||
</div>
|
||||
</Modal>
|
||||
);
|
||||
};
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue