diff --git a/_locales/en/messages.json b/_locales/en/messages.json index f6c73bc2ad42..0c83b3fd7376 100644 --- a/_locales/en/messages.json +++ b/_locales/en/messages.json @@ -7026,6 +7026,10 @@ "message": "Type a reply...", "description": "Placeholder text for the story reply modal" }, + "StoryViewsNRepliesModal__no-replies": { + "message": "No replies yet", + "description": "Placeholder text for when there are no replies" + }, "StoryViewsNRepliesModal__tab--views": { "message": "Views", "description": "Title for views tab" diff --git a/stylesheets/components/StoryViewsNRepliesModal.scss b/stylesheets/components/StoryViewsNRepliesModal.scss index a7989d385235..b8e291c6914d 100644 --- a/stylesheets/components/StoryViewsNRepliesModal.scss +++ b/stylesheets/components/StoryViewsNRepliesModal.scss @@ -3,9 +3,27 @@ .StoryViewsNRepliesModal { min-width: 320px; + overflow: hidden; &--group { - min-height: 360px; + display: flex; + flex-direction: column; + min-height: 400px; + } + + &__replies { + flex: 1; + max-height: 75vh; + overflow-y: overlay; + + &--none { + align-items: center; + color: $color-gray-45; + display: flex; + flex: 1; + justify-content: center; + user-select: none; + } } &__overlay-container { diff --git a/stylesheets/components/TextAttachment.scss b/stylesheets/components/TextAttachment.scss index a4f48bc22a96..0fac488441ca 100644 --- a/stylesheets/components/TextAttachment.scss +++ b/stylesheets/components/TextAttachment.scss @@ -6,7 +6,6 @@ &__story { align-items: center; - border-radius: 12px; display: flex; flex-direction: column; height: 1280px; diff --git a/ts/components/StoryViewer.tsx b/ts/components/StoryViewer.tsx index 5dc369ad423c..be95a03b3813 100644 --- a/ts/components/StoryViewer.tsx +++ b/ts/components/StoryViewer.tsx @@ -60,6 +60,7 @@ export type PropsType = { replyState?: ReplyStateType; skinTone?: number; stories: Array; + views?: Array; }; const CAPTION_BUFFER = 20; @@ -88,6 +89,7 @@ export const StoryViewer = ({ replyState, skinTone, stories, + views, }: PropsType): JSX.Element => { const [currentStoryIndex, setCurrentStoryIndex] = useState(0); const [storyDuration, setStoryDuration] = useState(); @@ -268,7 +270,7 @@ export const StoryViewer = ({ const replies = replyState && replyState.messageId === messageId ? replyState.replies : []; - const viewCount = 0; + const viewCount = (views || []).length; const replyCount = replies.length; return ( @@ -388,49 +390,46 @@ export const StoryViewer = ({ ))}
- {isMe ? ( - <> - {viewCount && - (viewCount === 1 ? ( - {viewCount}]} - /> - ) : ( - {viewCount}]} - /> - ))} - {viewCount && replyCount && ' '} - {replyCount && - (replyCount === 1 ? ( - {replyCount}]} - /> - ) : ( - {replyCount}]} - /> - ))} - - ) : ( - canReply && ( - - ) + {canReply && ( + )}
@@ -454,13 +453,16 @@ export const StoryViewer = ({ authorTitle={title} getPreferredBadge={getPreferredBadge} i18n={i18n} + isGroupStory={isGroupStory} isMyStory={isMe} onClose={() => setHasReplyModal(false)} onReact={emoji => { onReactToStory(emoji, visibleStory); }} onReply={(message, mentions, replyTimestamp) => { - setHasReplyModal(false); + if (!isGroupStory) { + setHasReplyModal(false); + } onReplyToStory(message, mentions, replyTimestamp, visibleStory); }} onSetSkinTone={onSetSkinTone} diff --git a/ts/components/StoryViewsNRepliesModal.stories.tsx b/ts/components/StoryViewsNRepliesModal.stories.tsx index a7faa3fe3634..72cf82721f68 100644 --- a/ts/components/StoryViewsNRepliesModal.stories.tsx +++ b/ts/components/StoryViewsNRepliesModal.stories.tsx @@ -112,12 +112,17 @@ story.add('Views only', () => ( /> )); +story.add('In a group (no replies)', () => ( + +)); + story.add('In a group', () => { const { views, replies } = getViewsAndReplies(); return ( diff --git a/ts/components/StoryViewsNRepliesModal.tsx b/ts/components/StoryViewsNRepliesModal.tsx index 6d1bfa16e70d..15e12120c1d6 100644 --- a/ts/components/StoryViewsNRepliesModal.tsx +++ b/ts/components/StoryViewsNRepliesModal.tsx @@ -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(); + const inputApiRef = useRef(); + const [bottom, setBottom] = useState(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 = (
- {!replies.length && ( + {!isGroupStory && ( { + 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 ? ( -
- {replies.map(reply => - reply.reactionEmoji ? ( -
-
+ let repliesElement: JSX.Element | undefined; + + if (replies.length) { + repliesElement = ( +
+ {replies.map(reply => + reply.reactionEmoji ? ( +
+
+ +
+
+ +
+ {i18n('StoryViewsNRepliesModal__reacted')} + +
+
+ +
+ ) : ( +
-
+
- {i18n('StoryViewsNRepliesModal__reacted')} + + +
- -
- ) : ( -
- -
-
- -
- - - - -
-
- ) - )} -
- ) : undefined; + ) + )} +
+
+ ); + } else if (isGroupStory) { + repliesElement = ( +
+ {i18n('StoryViewsNRepliesModal__no-replies')} +
+ ); + } const viewsElement = views.length ? (
@@ -358,28 +384,26 @@ export const StoryViewsNRepliesModal = ({ ) : undefined; - const hasOnlyViewsElement = - viewsElement && !repliesElement && !composerElement; - return ( - {tabsElement || ( - <> - {viewsElement} - {repliesElement} - {composerElement} - - )} +
+ {tabsElement || ( + <> + {viewsElement || repliesElement} + {composerElement} + + )} +
); }; diff --git a/ts/util/lint/exceptions.json b/ts/util/lint/exceptions.json index 6bbb9cbc0e05..d071fdf1d3ec 100644 --- a/ts/util/lint/exceptions.json +++ b/ts/util/lint/exceptions.json @@ -7702,7 +7702,7 @@ { "rule": "React-useRef", "path": "ts/components/StoryViewsNRepliesModal.tsx", - "line": " const inputApiRef = React.useRef();", + "line": " const inputApiRef = useRef();", "reasonCategory": "usageTrusted", "updated": "2022-02-15T17:57:06.507Z" },