Use <Message /> for group story replies
This commit is contained in:
parent
dca848389c
commit
5dc42122a8
8 changed files with 169 additions and 78 deletions
|
@ -34,7 +34,6 @@
|
|||
@include color-svg('../images/icons/v2/x-24.svg', $color-gray-15);
|
||||
}
|
||||
right: 28px;
|
||||
top: 0;
|
||||
z-index: $z-index-above-above-base;
|
||||
}
|
||||
|
||||
|
|
|
@ -13,6 +13,7 @@
|
|||
|
||||
&__replies {
|
||||
flex: 1;
|
||||
margin: 0 -16px;
|
||||
max-height: 75vh;
|
||||
overflow-y: overlay;
|
||||
|
||||
|
@ -127,7 +128,7 @@
|
|||
align-items: center;
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
padding: 12px 0;
|
||||
padding: 12px 16px;
|
||||
|
||||
&--container {
|
||||
display: flex;
|
||||
|
|
|
@ -10,6 +10,7 @@ import enMessages from '../../_locales/en/messages.json';
|
|||
import { IMAGE_JPEG } from '../types/MIME';
|
||||
import { SendStatus } from '../messages/MessageSendState';
|
||||
import { StoryViewsNRepliesModal } from './StoryViewsNRepliesModal';
|
||||
import { UUID } from '../types/UUID';
|
||||
import { fakeAttachment } from '../test-both/helpers/fakeAttachment';
|
||||
import { getDefaultConversation } from '../test-both/helpers/getDefaultConversation';
|
||||
import { setupI18n } from '../util/setupI18n';
|
||||
|
@ -69,6 +70,9 @@ function getViewsAndReplies() {
|
|||
const p3 = getDefaultConversation();
|
||||
const p4 = getDefaultConversation();
|
||||
const p5 = getDefaultConversation();
|
||||
const p6 = getDefaultConversation({
|
||||
isMe: true,
|
||||
});
|
||||
|
||||
const views = [
|
||||
{
|
||||
|
@ -100,20 +104,52 @@ function getViewsAndReplies() {
|
|||
|
||||
const replies = [
|
||||
{
|
||||
...p2,
|
||||
author: p2,
|
||||
body: 'So cute ❤️',
|
||||
conversationId: p2.id,
|
||||
id: UUID.generate().toString(),
|
||||
timestamp: Date.now() - 24 * durations.MINUTE,
|
||||
},
|
||||
{
|
||||
...p3,
|
||||
author: p3,
|
||||
body: "That's awesome",
|
||||
conversationId: p3.id,
|
||||
id: UUID.generate().toString(),
|
||||
timestamp: Date.now() - 13 * durations.MINUTE,
|
||||
},
|
||||
{
|
||||
...p4,
|
||||
author: p3,
|
||||
body: 'Very awesome',
|
||||
conversationId: p3.id,
|
||||
id: UUID.generate().toString(),
|
||||
timestamp: Date.now() - 13 * durations.MINUTE,
|
||||
},
|
||||
{
|
||||
author: p3,
|
||||
body: 'Did I mention how awesome this is?',
|
||||
conversationId: p3.id,
|
||||
id: UUID.generate().toString(),
|
||||
timestamp: Date.now() - 12 * durations.MINUTE,
|
||||
},
|
||||
{
|
||||
author: p4,
|
||||
conversationId: p4.id,
|
||||
id: UUID.generate().toString(),
|
||||
reactionEmoji: '❤️',
|
||||
timestamp: Date.now() - 5 * durations.MINUTE,
|
||||
},
|
||||
{
|
||||
author: p6,
|
||||
body: 'Thanks everyone!',
|
||||
conversationId: p6.id,
|
||||
id: UUID.generate().toString(),
|
||||
sendStateByConversationId: {
|
||||
[p1.id]: {
|
||||
status: SendStatus.Pending,
|
||||
},
|
||||
},
|
||||
timestamp: Date.now(),
|
||||
},
|
||||
];
|
||||
|
||||
return {
|
||||
|
|
|
@ -16,7 +16,7 @@ import { CompositionInput } from './CompositionInput';
|
|||
import { ContactName } from './conversation/ContactName';
|
||||
import { EmojiButton } from './emoji/EmojiButton';
|
||||
import { Emojify } from './conversation/Emojify';
|
||||
import { MessageBody } from './conversation/MessageBody';
|
||||
import { Message, TextDirection } from './conversation/Message';
|
||||
import { MessageTimestamp } from './conversation/MessageTimestamp';
|
||||
import { Modal } from './Modal';
|
||||
import { Quote } from './conversation/Quote';
|
||||
|
@ -24,8 +24,58 @@ import { ReactionPicker } from './conversation/ReactionPicker';
|
|||
import { Tabs } from './Tabs';
|
||||
import { Theme } from '../util/theme';
|
||||
import { ThemeType } from '../types/Util';
|
||||
import { WidthBreakpoint } from './_util';
|
||||
import { getAvatarColor } from '../types/Colors';
|
||||
import { getStoryReplyText } from '../util/getStoryReplyText';
|
||||
import { shouldNeverBeCalled } from '../util/shouldNeverBeCalled';
|
||||
|
||||
// Menu is disabled so these actions are inaccessible. We also don't support
|
||||
// link previews, tap to view messages, attachments, or gifts. Just regular
|
||||
// text messages and reactions.
|
||||
const MESSAGE_DEFAULT_PROPS = {
|
||||
canDeleteForEveryone: false,
|
||||
canDownload: false,
|
||||
canReact: false,
|
||||
canReply: false,
|
||||
canRetry: false,
|
||||
canRetryDeleteForEveryone: false,
|
||||
checkForAccount: shouldNeverBeCalled,
|
||||
clearSelectedMessage: shouldNeverBeCalled,
|
||||
containerWidthBreakpoint: WidthBreakpoint.Medium,
|
||||
deleteMessage: shouldNeverBeCalled,
|
||||
deleteMessageForEveryone: shouldNeverBeCalled,
|
||||
displayTapToViewMessage: shouldNeverBeCalled,
|
||||
doubleCheckMissingQuoteReference: shouldNeverBeCalled,
|
||||
downloadAttachment: shouldNeverBeCalled,
|
||||
isBlocked: false,
|
||||
isMessageRequestAccepted: true,
|
||||
kickOffAttachmentDownload: shouldNeverBeCalled,
|
||||
markAttachmentAsCorrupted: shouldNeverBeCalled,
|
||||
markViewed: shouldNeverBeCalled,
|
||||
messageExpanded: shouldNeverBeCalled,
|
||||
openConversation: shouldNeverBeCalled,
|
||||
openGiftBadge: shouldNeverBeCalled,
|
||||
openLink: shouldNeverBeCalled,
|
||||
previews: [],
|
||||
reactToMessage: shouldNeverBeCalled,
|
||||
renderAudioAttachment: () => <div />,
|
||||
renderEmojiPicker: () => <div />,
|
||||
renderReactionPicker: () => <div />,
|
||||
replyToMessage: shouldNeverBeCalled,
|
||||
retryDeleteForEveryone: shouldNeverBeCalled,
|
||||
retrySend: shouldNeverBeCalled,
|
||||
scrollToQuotedMessage: shouldNeverBeCalled,
|
||||
showContactDetail: shouldNeverBeCalled,
|
||||
showContactModal: shouldNeverBeCalled,
|
||||
showExpiredIncomingTapToViewToast: shouldNeverBeCalled,
|
||||
showExpiredOutgoingTapToViewToast: shouldNeverBeCalled,
|
||||
showForwardMessageModal: shouldNeverBeCalled,
|
||||
showMessageDetail: shouldNeverBeCalled,
|
||||
showVisualAttachment: shouldNeverBeCalled,
|
||||
startConversation: shouldNeverBeCalled,
|
||||
theme: ThemeType.dark,
|
||||
viewStory: shouldNeverBeCalled,
|
||||
};
|
||||
|
||||
enum Tab {
|
||||
Replies = 'Replies',
|
||||
|
@ -79,6 +129,7 @@ export const StoryViewsNRepliesModal = ({
|
|||
storyPreviewAttachment,
|
||||
views,
|
||||
}: PropsType): JSX.Element | null => {
|
||||
const containerElementRef = useRef<HTMLDivElement | null>(null);
|
||||
const inputApiRef = useRef<InputApi | undefined>();
|
||||
const [bottom, setBottom] = useState<HTMLDivElement | null>(null);
|
||||
const [messageBodyText, setMessageBodyText] = useState('');
|
||||
|
@ -211,30 +262,34 @@ export const StoryViewsNRepliesModal = ({
|
|||
|
||||
if (replies.length) {
|
||||
repliesElement = (
|
||||
<div className="StoryViewsNRepliesModal__replies">
|
||||
{replies.map(reply =>
|
||||
<div
|
||||
className="StoryViewsNRepliesModal__replies"
|
||||
ref={containerElementRef}
|
||||
>
|
||||
{replies.map((reply, index) =>
|
||||
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)}
|
||||
acceptedMessageRequest={reply.author.acceptedMessageRequest}
|
||||
avatarPath={reply.author.avatarPath}
|
||||
badge={getPreferredBadge(reply.author.badges)}
|
||||
color={getAvatarColor(reply.author.color)}
|
||||
conversationType="direct"
|
||||
i18n={i18n}
|
||||
isMe={Boolean(reply.isMe)}
|
||||
name={reply.name}
|
||||
profileName={reply.profileName}
|
||||
sharedGroupNames={reply.sharedGroupNames || []}
|
||||
isMe={Boolean(reply.author.isMe)}
|
||||
name={reply.author.name}
|
||||
profileName={reply.author.profileName}
|
||||
sharedGroupNames={reply.author.sharedGroupNames || []}
|
||||
size={AvatarSize.TWENTY_EIGHT}
|
||||
title={reply.title}
|
||||
theme={ThemeType.dark}
|
||||
title={reply.author.title}
|
||||
/>
|
||||
<div className="StoryViewsNRepliesModal__reaction--body">
|
||||
<div className="StoryViewsNRepliesModal__reply--title">
|
||||
<ContactName
|
||||
contactNameColor={reply.contactNameColor}
|
||||
title={reply.title}
|
||||
title={reply.author.title}
|
||||
/>
|
||||
</div>
|
||||
{i18n('StoryViewsNRepliesModal__reacted')}
|
||||
|
@ -249,54 +304,34 @@ export const StoryViewsNRepliesModal = ({
|
|||
<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"
|
||||
<div key={reply.id}>
|
||||
<Message
|
||||
{...MESSAGE_DEFAULT_PROPS}
|
||||
author={reply.author}
|
||||
containerElementRef={containerElementRef}
|
||||
conversationColor="ultramarine"
|
||||
conversationId={reply.conversationId}
|
||||
conversationTitle={reply.author.title}
|
||||
conversationType="group"
|
||||
direction="incoming"
|
||||
disableMenu
|
||||
getPreferredBadge={getPreferredBadge}
|
||||
i18n={i18n}
|
||||
isMe={Boolean(reply.isMe)}
|
||||
name={reply.name}
|
||||
profileName={reply.profileName}
|
||||
sharedGroupNames={reply.sharedGroupNames || []}
|
||||
size={AvatarSize.TWENTY_EIGHT}
|
||||
title={reply.title}
|
||||
id={reply.id}
|
||||
interactionMode="mouse"
|
||||
readStatus={reply.readStatus}
|
||||
renderingContext="StoryViewsNRepliesModal"
|
||||
shouldCollapseAbove={
|
||||
reply.conversationId === replies[index - 1]?.conversationId
|
||||
}
|
||||
shouldCollapseBelow={
|
||||
reply.conversationId === replies[index + 1]?.conversationId
|
||||
}
|
||||
shouldHideMetadata={false}
|
||||
text={reply.body}
|
||||
textDirection={TextDirection.Default}
|
||||
timestamp={reply.timestamp}
|
||||
/>
|
||||
<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
|
||||
disableJumbomoji
|
||||
i18n={i18n}
|
||||
text={
|
||||
reply.deletedForEveryone
|
||||
? i18n('message--deletedForEveryone')
|
||||
: String(reply.body)
|
||||
}
|
||||
/>
|
||||
|
||||
<MessageTimestamp
|
||||
i18n={i18n}
|
||||
module="StoryViewsNRepliesModal__reply--timestamp"
|
||||
timestamp={reply.timestamp}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
)}
|
||||
|
|
|
@ -263,7 +263,7 @@ export type PropsData = {
|
|||
isTapToViewExpired?: boolean;
|
||||
isTapToViewError?: boolean;
|
||||
|
||||
readStatus: ReadStatus;
|
||||
readStatus?: ReadStatus;
|
||||
|
||||
expirationLength?: number;
|
||||
expirationTimestamp?: number;
|
||||
|
|
|
@ -80,8 +80,10 @@ function getAvatarData(
|
|||
ConversationType,
|
||||
| 'acceptedMessageRequest'
|
||||
| 'avatarPath'
|
||||
| 'badges'
|
||||
| 'color'
|
||||
| 'isMe'
|
||||
| 'id'
|
||||
| 'name'
|
||||
| 'profileName'
|
||||
| 'sharedGroupNames'
|
||||
|
@ -90,8 +92,10 @@ function getAvatarData(
|
|||
return pick(conversation, [
|
||||
'acceptedMessageRequest',
|
||||
'avatarPath',
|
||||
'badges',
|
||||
'color',
|
||||
'isMe',
|
||||
'id',
|
||||
'name',
|
||||
'profileName',
|
||||
'sharedGroupNames',
|
||||
|
@ -212,11 +216,12 @@ export const getStoryReplies = createSelector(
|
|||
const conversation = conversationSelector(reaction.fromId);
|
||||
|
||||
return {
|
||||
...getAvatarData(conversation),
|
||||
author: getAvatarData(conversation),
|
||||
contactNameColor: contactNameColorSelector(
|
||||
foundStory.conversationId,
|
||||
conversation.id
|
||||
),
|
||||
conversationId: reaction.fromId,
|
||||
id: getReactionUniqueId(reaction),
|
||||
reactionEmoji: reaction.emoji,
|
||||
timestamp: reaction.timestamp,
|
||||
|
@ -231,12 +236,14 @@ export const getStoryReplies = createSelector(
|
|||
: conversationSelector(reply.sourceUuid || reply.source);
|
||||
|
||||
return {
|
||||
...getAvatarData(conversation),
|
||||
author: getAvatarData(conversation),
|
||||
...pick(reply, ['body', 'deletedForEveryone', 'id', 'timestamp']),
|
||||
contactNameColor: contactNameColorSelector(
|
||||
reply.conversationId,
|
||||
conversation.id
|
||||
),
|
||||
conversationId: conversation.id,
|
||||
readStatus: reply.readStatus,
|
||||
};
|
||||
});
|
||||
|
||||
|
|
|
@ -5,25 +5,31 @@ import type { AttachmentType } from './Attachment';
|
|||
import type { ContactNameColorType } from './Colors';
|
||||
import type { ConversationType } from '../state/ducks/conversations';
|
||||
import type { LocalizerType } from './Util';
|
||||
import type { ReadStatus } from '../messages/MessageReadStatus';
|
||||
import type { SendStatus } from '../messages/MessageSendState';
|
||||
import type { StoryDistributionListDataType } from '../state/ducks/storyDistributionLists';
|
||||
|
||||
export type ReplyType = Pick<
|
||||
ConversationType,
|
||||
| 'acceptedMessageRequest'
|
||||
| 'avatarPath'
|
||||
| 'color'
|
||||
| 'isMe'
|
||||
| 'name'
|
||||
| 'profileName'
|
||||
| 'sharedGroupNames'
|
||||
| 'title'
|
||||
> & {
|
||||
export type ReplyType = {
|
||||
author: Pick<
|
||||
ConversationType,
|
||||
| 'acceptedMessageRequest'
|
||||
| 'avatarPath'
|
||||
| 'badges'
|
||||
| 'color'
|
||||
| 'id'
|
||||
| 'isMe'
|
||||
| 'name'
|
||||
| 'profileName'
|
||||
| 'sharedGroupNames'
|
||||
| 'title'
|
||||
>;
|
||||
body?: string;
|
||||
contactNameColor?: ContactNameColorType;
|
||||
conversationId: string;
|
||||
deletedForEveryone?: boolean;
|
||||
id: string;
|
||||
reactionEmoji?: string;
|
||||
readStatus?: ReadStatus;
|
||||
timestamp: number;
|
||||
};
|
||||
|
||||
|
|
|
@ -9231,6 +9231,13 @@
|
|||
"reasonCategory": "usageTrusted",
|
||||
"updated": "2022-02-15T17:57:06.507Z"
|
||||
},
|
||||
{
|
||||
"rule": "React-useRef",
|
||||
"path": "ts/components/StoryViewsNRepliesModal.tsx",
|
||||
"line": " const containerElementRef = useRef<HTMLDivElement | null>(null);",
|
||||
"reasonCategory": "usageTrusted",
|
||||
"updated": "2022-08-04T00:52:01.080Z"
|
||||
},
|
||||
{
|
||||
"rule": "React-useRef",
|
||||
"path": "ts/components/TextAttachment.tsx",
|
||||
|
|
Loading…
Reference in a new issue