Render replies to 1:1 text stories

This commit is contained in:
Josh Perez 2023-03-07 17:59:44 -05:00 committed by GitHub
parent 7b1b1584f1
commit 78e3120d1a
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
5 changed files with 47 additions and 22 deletions

View file

@ -105,6 +105,7 @@ export type PropsType = {
retryMessageSend: (messageId: string) => unknown; retryMessageSend: (messageId: string) => unknown;
saveAttachment: SaveAttachmentActionCreatorType; saveAttachment: SaveAttachmentActionCreatorType;
setHasAllStoriesUnmuted: (isUnmuted: boolean) => unknown; setHasAllStoriesUnmuted: (isUnmuted: boolean) => unknown;
showContactModal: (contactId: string, conversationId?: string) => void;
showToast: ShowToastActionCreatorType; showToast: ShowToastActionCreatorType;
skinTone?: number; skinTone?: number;
story: StoryViewType; story: StoryViewType;
@ -158,6 +159,7 @@ export function StoryViewer({
retryMessageSend, retryMessageSend,
saveAttachment, saveAttachment,
setHasAllStoriesUnmuted, setHasAllStoriesUnmuted,
showContactModal,
showToast, showToast,
skinTone, skinTone,
story, story,
@ -948,6 +950,7 @@ export function StoryViewer({
recentEmojis={recentEmojis} recentEmojis={recentEmojis}
renderEmojiPicker={renderEmojiPicker} renderEmojiPicker={renderEmojiPicker}
replies={replies} replies={replies}
showContactModal={showContactModal}
skinTone={skinTone} skinTone={skinTone}
sortedGroupMembers={group?.sortedGroupMembers} sortedGroupMembers={group?.sortedGroupMembers}
views={views} views={views}

View file

@ -58,7 +58,6 @@ const MESSAGE_DEFAULT_PROPS = {
renderAudioAttachment: () => <div />, renderAudioAttachment: () => <div />,
saveAttachment: shouldNeverBeCalled, saveAttachment: shouldNeverBeCalled,
scrollToQuotedMessage: shouldNeverBeCalled, scrollToQuotedMessage: shouldNeverBeCalled,
showContactModal: shouldNeverBeCalled,
showConversation: noop, showConversation: noop,
showExpiredIncomingTapToViewToast: shouldNeverBeCalled, showExpiredIncomingTapToViewToast: shouldNeverBeCalled,
showExpiredOutgoingTapToViewToast: shouldNeverBeCalled, showExpiredOutgoingTapToViewToast: shouldNeverBeCalled,
@ -77,12 +76,15 @@ export enum StoryViewsNRepliesTab {
export type PropsType = { export type PropsType = {
authorTitle: string; authorTitle: string;
canReply: boolean; canReply: boolean;
deleteGroupStoryReply: (id: string) => void;
deleteGroupStoryReplyForEveryone: (id: string) => void;
getPreferredBadge: PreferredBadgeSelectorType; getPreferredBadge: PreferredBadgeSelectorType;
group: Pick<ConversationType, 'left'> | undefined;
hasViewReceiptSetting: boolean; hasViewReceiptSetting: boolean;
hasViewsCapability: boolean; hasViewsCapability: boolean;
i18n: LocalizerType; i18n: LocalizerType;
isInternalUser?: boolean; isInternalUser?: boolean;
group: Pick<ConversationType, 'left'> | undefined; onChangeViewTarget: (target: StoryViewTargetType) => unknown;
onClose: () => unknown; onClose: () => unknown;
onReact: (emoji: string) => unknown; onReact: (emoji: string) => unknown;
onReply: ( onReply: (
@ -97,24 +99,25 @@ export type PropsType = {
recentEmojis?: ReadonlyArray<string>; recentEmojis?: ReadonlyArray<string>;
renderEmojiPicker: (props: RenderEmojiPickerProps) => JSX.Element; renderEmojiPicker: (props: RenderEmojiPickerProps) => JSX.Element;
replies: ReadonlyArray<ReplyType>; replies: ReadonlyArray<ReplyType>;
showContactModal: (contactId: string, conversationId?: string) => void;
skinTone?: number; skinTone?: number;
sortedGroupMembers?: ReadonlyArray<ConversationType>; sortedGroupMembers?: ReadonlyArray<ConversationType>;
views: ReadonlyArray<StorySendStateType>; views: ReadonlyArray<StorySendStateType>;
viewTarget: StoryViewTargetType; viewTarget: StoryViewTargetType;
onChangeViewTarget: (target: StoryViewTargetType) => unknown;
deleteGroupStoryReply: (id: string) => void;
deleteGroupStoryReplyForEveryone: (id: string) => void;
}; };
export function StoryViewsNRepliesModal({ export function StoryViewsNRepliesModal({
authorTitle, authorTitle,
canReply, canReply,
deleteGroupStoryReply,
deleteGroupStoryReplyForEveryone,
getPreferredBadge, getPreferredBadge,
group,
hasViewReceiptSetting, hasViewReceiptSetting,
hasViewsCapability, hasViewsCapability,
i18n, i18n,
isInternalUser, isInternalUser,
group, onChangeViewTarget,
onClose, onClose,
onReact, onReact,
onReply, onReply,
@ -125,13 +128,11 @@ export function StoryViewsNRepliesModal({
recentEmojis, recentEmojis,
renderEmojiPicker, renderEmojiPicker,
replies, replies,
showContactModal,
skinTone, skinTone,
sortedGroupMembers, sortedGroupMembers,
views,
viewTarget, viewTarget,
onChangeViewTarget, views,
deleteGroupStoryReply,
deleteGroupStoryReplyForEveryone,
}: PropsType): JSX.Element | null { }: PropsType): JSX.Element | null {
const [deleteReplyId, setDeleteReplyId] = useState<string | undefined>( const [deleteReplyId, setDeleteReplyId] = useState<string | undefined>(
undefined undefined
@ -274,18 +275,19 @@ export function StoryViewsNRepliesModal({
return ( return (
<ReplyOrReactionMessage <ReplyOrReactionMessage
key={reply.id} key={reply.id}
id={reply.id} containerElementRef={containerElementRef}
i18n={i18n}
isInternalUser={isInternalUser}
reply={reply}
deleteGroupStoryReply={() => setDeleteReplyId(reply.id)} deleteGroupStoryReply={() => setDeleteReplyId(reply.id)}
deleteGroupStoryReplyForEveryone={() => deleteGroupStoryReplyForEveryone={() =>
setDeleteForEveryoneReplyId(reply.id) setDeleteForEveryoneReplyId(reply.id)
} }
getPreferredBadge={getPreferredBadge} getPreferredBadge={getPreferredBadge}
i18n={i18n}
id={reply.id}
isInternalUser={isInternalUser}
reply={reply}
shouldCollapseAbove={shouldCollapse(reply, replies[index - 1])} shouldCollapseAbove={shouldCollapse(reply, replies[index - 1])}
shouldCollapseBelow={shouldCollapse(reply, replies[index + 1])} shouldCollapseBelow={shouldCollapse(reply, replies[index + 1])}
containerElementRef={containerElementRef} showContactModal={showContactModal}
/> />
); );
})} })}
@ -452,17 +454,18 @@ export function StoryViewsNRepliesModal({
} }
type ReplyOrReactionMessageProps = { type ReplyOrReactionMessageProps = {
i18n: LocalizerType; containerElementRef: React.RefObject<HTMLElement>;
id: string;
isInternalUser?: boolean;
reply: ReplyType;
deleteGroupStoryReply: (replyId: string) => void; deleteGroupStoryReply: (replyId: string) => void;
deleteGroupStoryReplyForEveryone: (replyId: string) => void; deleteGroupStoryReplyForEveryone: (replyId: string) => void;
getPreferredBadge: PreferredBadgeSelectorType; getPreferredBadge: PreferredBadgeSelectorType;
i18n: LocalizerType;
id: string;
isInternalUser?: boolean;
onContextMenu?: (ev: React.MouseEvent) => void;
reply: ReplyType;
shouldCollapseAbove: boolean; shouldCollapseAbove: boolean;
shouldCollapseBelow: boolean; shouldCollapseBelow: boolean;
containerElementRef: React.RefObject<HTMLElement>; showContactModal: (contactId: string, conversationId?: string) => void;
onContextMenu?: (ev: React.MouseEvent) => void;
}; };
function ReplyOrReactionMessage({ function ReplyOrReactionMessage({
@ -476,6 +479,7 @@ function ReplyOrReactionMessage({
getPreferredBadge, getPreferredBadge,
shouldCollapseAbove, shouldCollapseAbove,
shouldCollapseBelow, shouldCollapseBelow,
showContactModal,
}: ReplyOrReactionMessageProps) { }: ReplyOrReactionMessageProps) {
const renderContent = (onContextMenu?: (ev: React.MouseEvent) => void) => { const renderContent = (onContextMenu?: (ev: React.MouseEvent) => void) => {
if (reply.reactionEmoji && !reply.deletedForEveryone) { if (reply.reactionEmoji && !reply.deletedForEveryone) {
@ -546,6 +550,7 @@ function ReplyOrReactionMessage({
shouldCollapseAbove={shouldCollapseAbove} shouldCollapseAbove={shouldCollapseAbove}
shouldCollapseBelow={shouldCollapseBelow} shouldCollapseBelow={shouldCollapseBelow}
shouldHideMetadata={false} shouldHideMetadata={false}
showContactModal={showContactModal}
text={reply.body} text={reply.body}
textDirection={TextDirection.Default} textDirection={TextDirection.Default}
timestamp={reply.timestamp} timestamp={reply.timestamp}

View file

@ -464,7 +464,7 @@ export class MessageModel extends window.Backbone.Model<MessageAttributesType> {
const attachments = getAttachmentsForMessage({ ...message }); const attachments = getAttachmentsForMessage({ ...message });
let attachment: AttachmentType | undefined = attachments?.[0]; let attachment: AttachmentType | undefined = attachments?.[0];
if (attachment && !attachment.url) { if (attachment && !attachment.url && !attachment.textAttachment) {
attachment = undefined; attachment = undefined;
} }

View file

@ -13,6 +13,7 @@ import type {
MessageChangedActionType, MessageChangedActionType,
MessageDeletedActionType, MessageDeletedActionType,
MessagesAddedActionType, MessagesAddedActionType,
SelectedConversationChangedActionType,
} from './conversations'; } from './conversations';
import type { NoopActionType } from './noop'; import type { NoopActionType } from './noop';
import type { StateType as RootStateType } from '../reducer'; import type { StateType as RootStateType } from '../reducer';
@ -20,6 +21,7 @@ import type { StoryViewTargetType, StoryViewType } from '../../types/Stories';
import type { SyncType } from '../../jobs/helpers/syncHelpers'; import type { SyncType } from '../../jobs/helpers/syncHelpers';
import type { UUIDStringType } from '../../types/UUID'; import type { UUIDStringType } from '../../types/UUID';
import * as log from '../../logging/log'; import * as log from '../../logging/log';
import { SELECTED_CONVERSATION_CHANGED } from './conversations';
import { SIGNAL_ACI } from '../../types/SignalConversation'; import { SIGNAL_ACI } from '../../types/SignalConversation';
import dataInterface from '../../sql/Client'; import dataInterface from '../../sql/Client';
import { ReadStatus } from '../../messages/MessageReadStatus'; import { ReadStatus } from '../../messages/MessageReadStatus';
@ -263,6 +265,7 @@ export type StoriesActionType =
| ViewStoryActionType | ViewStoryActionType
| StoryReplyDeletedActionType | StoryReplyDeletedActionType
| RemoveAllStoriesActionType | RemoveAllStoriesActionType
| SelectedConversationChangedActionType
| SetAddStoryDataType | SetAddStoryDataType
| SetStorySendingType | SetStorySendingType
| SetHasAllStoriesUnmutedType; | SetHasAllStoriesUnmutedType;
@ -1775,5 +1778,16 @@ export function reducer(
}; };
} }
if (action.type === SELECTED_CONVERSATION_CHANGED) {
return {
...state,
lastOpenedAtTimestamp: state.openedAtTimestamp || Date.now(),
openedAtTimestamp: undefined,
replyState: undefined,
sendStoryModalData: undefined,
selectedStoryData: undefined,
};
}
return state; return state;
} }

View file

@ -36,6 +36,7 @@ import { useConversationsActions } from '../ducks/conversations';
import { useRecentEmojis } from '../selectors/emojis'; import { useRecentEmojis } from '../selectors/emojis';
import { useActions as useItemsActions } from '../ducks/items'; import { useActions as useItemsActions } from '../ducks/items';
import { useAudioPlayerActions } from '../ducks/audioPlayer'; import { useAudioPlayerActions } from '../ducks/audioPlayer';
import { useGlobalModalActions } from '../ducks/globalModals';
import { useStoriesActions } from '../ducks/stories'; import { useStoriesActions } from '../ducks/stories';
import { useIsWindowActive } from '../../hooks/useIsWindowActive'; import { useIsWindowActive } from '../../hooks/useIsWindowActive';
@ -50,6 +51,7 @@ export function SmartStoryViewer(): JSX.Element | null {
} = useConversationsActions(); } = useConversationsActions();
const { onSetSkinTone } = useItemsActions(); const { onSetSkinTone } = useItemsActions();
const { showToast } = useToastActions(); const { showToast } = useToastActions();
const { showContactModal } = useGlobalModalActions();
const isWindowActive = useIsWindowActive(); const isWindowActive = useIsWindowActive();
@ -143,6 +145,7 @@ export function SmartStoryViewer(): JSX.Element | null {
renderEmojiPicker={renderEmojiPicker} renderEmojiPicker={renderEmojiPicker}
replyState={replyState} replyState={replyState}
retryMessageSend={retryMessageSend} retryMessageSend={retryMessageSend}
showContactModal={showContactModal}
showToast={showToast} showToast={showToast}
skinTone={skinTone} skinTone={skinTone}
story={storyView} story={storyView}