Receive support for editing messages
This commit is contained in:
parent
2781e621ad
commit
36e21c0134
46 changed files with 2053 additions and 405 deletions
112
ts/components/EditHistoryMessagesModal.tsx
Normal file
112
ts/components/EditHistoryMessagesModal.tsx
Normal file
|
@ -0,0 +1,112 @@
|
|||
// Copyright 2023 Signal Messenger, LLC
|
||||
// SPDX-License-Identifier: AGPL-3.0-only
|
||||
|
||||
import React, { useCallback, useRef } from 'react';
|
||||
import { noop } from 'lodash';
|
||||
|
||||
import type { AttachmentType } from '../types/Attachment';
|
||||
import type { LocalizerType } from '../types/Util';
|
||||
import type { MessagePropsType } from '../state/selectors/message';
|
||||
import type { PreferredBadgeSelectorType } from '../state/selectors/badges';
|
||||
import { Message, TextDirection } from './conversation/Message';
|
||||
import { Modal } from './Modal';
|
||||
import { WidthBreakpoint } from './_util';
|
||||
import { shouldNeverBeCalled } from '../util/shouldNeverBeCalled';
|
||||
import { useTheme } from '../hooks/useTheme';
|
||||
|
||||
export type PropsType = {
|
||||
closeEditHistoryModal: () => unknown;
|
||||
editHistoryMessages: Array<MessagePropsType>;
|
||||
getPreferredBadge: PreferredBadgeSelectorType;
|
||||
i18n: LocalizerType;
|
||||
kickOffAttachmentDownload: (options: {
|
||||
attachment: AttachmentType;
|
||||
messageId: string;
|
||||
}) => void;
|
||||
showLightbox: (options: {
|
||||
attachment: AttachmentType;
|
||||
messageId: string;
|
||||
}) => void;
|
||||
};
|
||||
|
||||
const MESSAGE_DEFAULT_PROPS = {
|
||||
canDeleteForEveryone: false,
|
||||
checkForAccount: shouldNeverBeCalled,
|
||||
clearSelectedMessage: shouldNeverBeCalled,
|
||||
clearTargetedMessage: shouldNeverBeCalled,
|
||||
containerWidthBreakpoint: WidthBreakpoint.Medium,
|
||||
doubleCheckMissingQuoteReference: shouldNeverBeCalled,
|
||||
interactionMode: 'mouse' as const,
|
||||
isBlocked: false,
|
||||
isMessageRequestAccepted: true,
|
||||
markAttachmentAsCorrupted: shouldNeverBeCalled,
|
||||
messageExpanded: shouldNeverBeCalled,
|
||||
onReplyToMessage: shouldNeverBeCalled,
|
||||
onToggleSelect: shouldNeverBeCalled,
|
||||
openGiftBadge: shouldNeverBeCalled,
|
||||
openLink: shouldNeverBeCalled,
|
||||
previews: [],
|
||||
pushPanelForConversation: shouldNeverBeCalled,
|
||||
renderAudioAttachment: () => <div />,
|
||||
renderingContext: 'EditHistoryMessagesModal',
|
||||
saveAttachment: shouldNeverBeCalled,
|
||||
scrollToQuotedMessage: shouldNeverBeCalled,
|
||||
shouldCollapseAbove: false,
|
||||
shouldCollapseBelow: false,
|
||||
shouldHideMetadata: true,
|
||||
showContactModal: shouldNeverBeCalled,
|
||||
showConversation: noop,
|
||||
showEditHistoryModal: shouldNeverBeCalled,
|
||||
showExpiredIncomingTapToViewToast: shouldNeverBeCalled,
|
||||
showExpiredOutgoingTapToViewToast: shouldNeverBeCalled,
|
||||
showLightboxForViewOnceMedia: shouldNeverBeCalled,
|
||||
startConversation: shouldNeverBeCalled,
|
||||
textDirection: TextDirection.Default,
|
||||
viewStory: shouldNeverBeCalled,
|
||||
};
|
||||
|
||||
export function EditHistoryMessagesModal({
|
||||
closeEditHistoryModal,
|
||||
getPreferredBadge,
|
||||
editHistoryMessages,
|
||||
i18n,
|
||||
kickOffAttachmentDownload,
|
||||
showLightbox,
|
||||
}: PropsType): JSX.Element {
|
||||
const containerElementRef = useRef<HTMLDivElement | null>(null);
|
||||
const theme = useTheme();
|
||||
|
||||
const closeAndShowLightbox = useCallback(
|
||||
(options: { attachment: AttachmentType; messageId: string }) => {
|
||||
closeEditHistoryModal();
|
||||
showLightbox(options);
|
||||
},
|
||||
[closeEditHistoryModal, showLightbox]
|
||||
);
|
||||
|
||||
return (
|
||||
<Modal
|
||||
hasXButton
|
||||
i18n={i18n}
|
||||
modalName="EditHistoryMessagesModal"
|
||||
onClose={closeEditHistoryModal}
|
||||
title={i18n('icu:EditHistoryMessagesModal__title')}
|
||||
>
|
||||
<div ref={containerElementRef}>
|
||||
{editHistoryMessages.map(messageAttributes => (
|
||||
<Message
|
||||
{...MESSAGE_DEFAULT_PROPS}
|
||||
{...messageAttributes}
|
||||
containerElementRef={containerElementRef}
|
||||
getPreferredBadge={getPreferredBadge}
|
||||
i18n={i18n}
|
||||
key={messageAttributes.timestamp}
|
||||
kickOffAttachmentDownload={kickOffAttachmentDownload}
|
||||
showLightbox={closeAndShowLightbox}
|
||||
theme={theme}
|
||||
/>
|
||||
))}
|
||||
</div>
|
||||
</Modal>
|
||||
);
|
||||
}
|
|
@ -3,11 +3,12 @@
|
|||
|
||||
import React from 'react';
|
||||
import type {
|
||||
ContactModalStateType,
|
||||
UserNotFoundModalStateType,
|
||||
SafetyNumberChangedBlockingDataType,
|
||||
AuthorizeArtCreatorDataType,
|
||||
ContactModalStateType,
|
||||
EditHistoryMessagesType,
|
||||
ForwardMessagesPropsType,
|
||||
SafetyNumberChangedBlockingDataType,
|
||||
UserNotFoundModalStateType,
|
||||
} from '../state/ducks/globalModals';
|
||||
import type { LocalizerType, ThemeType } from '../types/Util';
|
||||
import { missingCaseError } from '../util/missingCaseError';
|
||||
|
@ -28,6 +29,9 @@ export type PropsType = {
|
|||
// ContactModal
|
||||
contactModalState: ContactModalStateType | undefined;
|
||||
renderContactModal: () => JSX.Element;
|
||||
// EditHistoryMessagesModal
|
||||
editHistoryMessages: EditHistoryMessagesType | undefined;
|
||||
renderEditHistoryMessagesModal: () => JSX.Element;
|
||||
// ErrorModal
|
||||
errorModalProps: { description?: string; title?: string } | undefined;
|
||||
renderErrorModal: (opts: {
|
||||
|
@ -82,6 +86,9 @@ export function GlobalModalContainer({
|
|||
// ContactModal
|
||||
contactModalState,
|
||||
renderContactModal,
|
||||
// EditHistoryMessages
|
||||
editHistoryMessages,
|
||||
renderEditHistoryMessagesModal,
|
||||
// ErrorModal
|
||||
errorModalProps,
|
||||
renderErrorModal,
|
||||
|
@ -147,6 +154,10 @@ export function GlobalModalContainer({
|
|||
return renderContactModal();
|
||||
}
|
||||
|
||||
if (editHistoryMessages) {
|
||||
return renderEditHistoryMessagesModal();
|
||||
}
|
||||
|
||||
if (forwardMessagesProps) {
|
||||
return renderForwardMessagesModal();
|
||||
}
|
||||
|
|
|
@ -207,6 +207,7 @@ export type PropsData = {
|
|||
text?: string;
|
||||
textDirection: TextDirection;
|
||||
textAttachment?: AttachmentType;
|
||||
isEditedMessage?: boolean;
|
||||
isSticker?: boolean;
|
||||
isTargeted?: boolean;
|
||||
isTargetedCounter?: number;
|
||||
|
@ -338,6 +339,7 @@ export type PropsActions = {
|
|||
}) => void;
|
||||
targetMessage?: (messageId: string, conversationId: string) => unknown;
|
||||
|
||||
showEditHistoryModal?: (id: string) => unknown;
|
||||
showExpiredIncomingTapToViewToast: () => unknown;
|
||||
showExpiredOutgoingTapToViewToast: () => unknown;
|
||||
viewStory: ViewStoryActionCreatorType;
|
||||
|
@ -768,9 +770,11 @@ export class Message extends React.PureComponent<Props, State> {
|
|||
expirationTimestamp,
|
||||
i18n,
|
||||
id,
|
||||
isEditedMessage,
|
||||
isSticker,
|
||||
isTapToViewExpired,
|
||||
pushPanelForConversation,
|
||||
showEditHistoryModal,
|
||||
status,
|
||||
text,
|
||||
textAttachment,
|
||||
|
@ -788,12 +792,14 @@ export class Message extends React.PureComponent<Props, State> {
|
|||
hasText={Boolean(text)}
|
||||
i18n={i18n}
|
||||
id={id}
|
||||
isEditedMessage={isEditedMessage}
|
||||
isInline={isInline}
|
||||
isShowingImage={this.isShowingImage()}
|
||||
isSticker={isStickerLike}
|
||||
isTapToViewExpired={isTapToViewExpired}
|
||||
onWidthMeasured={isInline ? this.updateMetadataWidth : undefined}
|
||||
pushPanelForConversation={pushPanelForConversation}
|
||||
showEditHistoryModal={showEditHistoryModal}
|
||||
status={status}
|
||||
textPending={textAttachment?.pending}
|
||||
timestamp={timestamp}
|
||||
|
|
|
@ -22,12 +22,14 @@ type PropsType = {
|
|||
hasText: boolean;
|
||||
i18n: LocalizerType;
|
||||
id: string;
|
||||
isEditedMessage?: boolean;
|
||||
isInline?: boolean;
|
||||
isShowingImage: boolean;
|
||||
isSticker?: boolean;
|
||||
isTapToViewExpired?: boolean;
|
||||
onWidthMeasured?: (width: number) => unknown;
|
||||
pushPanelForConversation: PushPanelForConversationActionType;
|
||||
showEditHistoryModal?: (id: string) => unknown;
|
||||
status?: MessageStatusType;
|
||||
textPending?: boolean;
|
||||
timestamp: number;
|
||||
|
@ -41,12 +43,14 @@ export function MessageMetadata({
|
|||
hasText,
|
||||
i18n,
|
||||
id,
|
||||
isEditedMessage,
|
||||
isInline,
|
||||
isShowingImage,
|
||||
isSticker,
|
||||
isTapToViewExpired,
|
||||
onWidthMeasured,
|
||||
pushPanelForConversation,
|
||||
showEditHistoryModal,
|
||||
status,
|
||||
textPending,
|
||||
timestamp,
|
||||
|
@ -130,6 +134,15 @@ export function MessageMetadata({
|
|||
);
|
||||
const children = (
|
||||
<>
|
||||
{isEditedMessage && showEditHistoryModal && (
|
||||
<button
|
||||
className="module-message__metadata__edited"
|
||||
onClick={() => showEditHistoryModal(id)}
|
||||
type="button"
|
||||
>
|
||||
{i18n('icu:MessageMetadata__edited')}
|
||||
</button>
|
||||
)}
|
||||
{timestampNode}
|
||||
{expirationLength ? (
|
||||
<ExpireTimer
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue