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

View file

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

View file

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

View file

@ -13,6 +13,7 @@ import type {
MessageChangedActionType,
MessageDeletedActionType,
MessagesAddedActionType,
SelectedConversationChangedActionType,
} from './conversations';
import type { NoopActionType } from './noop';
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 { UUIDStringType } from '../../types/UUID';
import * as log from '../../logging/log';
import { SELECTED_CONVERSATION_CHANGED } from './conversations';
import { SIGNAL_ACI } from '../../types/SignalConversation';
import dataInterface from '../../sql/Client';
import { ReadStatus } from '../../messages/MessageReadStatus';
@ -263,6 +265,7 @@ export type StoriesActionType =
| ViewStoryActionType
| StoryReplyDeletedActionType
| RemoveAllStoriesActionType
| SelectedConversationChangedActionType
| SetAddStoryDataType
| SetStorySendingType
| 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;
}

View file

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