uniformized props for Message

This commit is contained in:
Audric Ackermann 2021-07-08 16:43:32 +10:00
parent ee4a0b9b1e
commit f0fad6edfa
No known key found for this signature in database
GPG key ID: 999F434D76324AD4
10 changed files with 59 additions and 69 deletions

View file

@ -5,7 +5,7 @@ import { Emojify } from './Emojify';
type Props = { type Props = {
phoneNumber: string; phoneNumber: string;
name?: string; name?: string | null;
profileName?: string; profileName?: string;
module?: string; module?: string;
boldProfileName?: Boolean; boldProfileName?: Boolean;

View file

@ -29,7 +29,6 @@ import _ from 'lodash';
import { animation, contextMenu, Item, Menu } from 'react-contexify'; import { animation, contextMenu, Item, Menu } from 'react-contexify';
import uuid from 'uuid'; import uuid from 'uuid';
import { InView } from 'react-intersection-observer'; import { InView } from 'react-intersection-observer';
import { withTheme } from 'styled-components';
import { MessageMetadata } from './message/MessageMetadata'; import { MessageMetadata } from './message/MessageMetadata';
import { PubKey } from '../../session/types'; import { PubKey } from '../../session/types';
import { MessageRegularProps } from '../../models/messageType'; import { MessageRegularProps } from '../../models/messageType';
@ -56,7 +55,7 @@ interface State {
const EXPIRATION_CHECK_MINIMUM = 2000; const EXPIRATION_CHECK_MINIMUM = 2000;
const EXPIRED_DELAY = 600; const EXPIRED_DELAY = 600;
class MessageInner extends React.PureComponent<MessageRegularProps, State> { export class Message extends React.PureComponent<MessageRegularProps, State> {
public expirationCheckInterval: any; public expirationCheckInterval: any;
public expiredTimeout: any; public expiredTimeout: any;
public ctxMenuID: string; public ctxMenuID: string;
@ -279,14 +278,7 @@ class MessageInner extends React.PureComponent<MessageRegularProps, State> {
// tslint:disable-next-line cyclomatic-complexity // tslint:disable-next-line cyclomatic-complexity
public renderPreview() { public renderPreview() {
const { const { attachments, conversationType, direction, previews, quote } = this.props;
attachments,
conversationType,
direction,
onClickLinkPreview,
previews,
quote,
} = this.props;
// Attachments take precedence over Link Previews // Attachments take precedence over Link Previews
if (attachments && attachments.length) { if (attachments && attachments.length) {
@ -316,11 +308,6 @@ class MessageInner extends React.PureComponent<MessageRegularProps, State> {
'module-message__link-preview', 'module-message__link-preview',
withContentAbove ? 'module-message__link-preview--with-content-above' : null withContentAbove ? 'module-message__link-preview--with-content-above' : null
)} )}
onClick={() => {
if (onClickLinkPreview) {
onClickLinkPreview(first.url);
}
}}
> >
{first.image && previewHasImage && isFullSizeImage ? ( {first.image && previewHasImage && isFullSizeImage ? (
<ImageGrid <ImageGrid
@ -898,5 +885,3 @@ class MessageInner extends React.PureComponent<MessageRegularProps, State> {
await removeSenderFromModerator(this.props.authorPhoneNumber, this.props.convoId); await removeSenderFromModerator(this.props.authorPhoneNumber, this.props.convoId);
} }
} }
export const Message = withTheme(MessageInner);

View file

@ -4,25 +4,24 @@ import { OutgoingMessageStatus } from './OutgoingMessageStatus';
import { MetadataBadges } from './MetadataBadge'; import { MetadataBadges } from './MetadataBadge';
import { Timestamp } from '../Timestamp'; import { Timestamp } from '../Timestamp';
import { ExpireTimer } from '../ExpireTimer'; import { ExpireTimer } from '../ExpireTimer';
import styled, { DefaultTheme } from 'styled-components'; import styled, { DefaultTheme, useTheme } from 'styled-components';
import { MessageDeliveryStatus, MessageModelType } from '../../../models/messageType'; import { MessageDeliveryStatus, MessageModelType } from '../../../models/messageType';
type Props = { type Props = {
disableMenu?: boolean; disableMenu?: boolean;
isAdmin?: boolean; isAdmin?: boolean;
isDeletable: boolean; isDeletable: boolean;
text?: string; text?: string | null;
id: string; id: string;
collapseMetadata?: boolean; collapseMetadata?: boolean;
direction: MessageModelType; direction: MessageModelType;
timestamp: number; timestamp: number;
serverTimestamp?: number; serverTimestamp?: number;
status?: MessageDeliveryStatus; status?: MessageDeliveryStatus | null;
expirationLength?: number; expirationLength?: number;
expirationTimestamp?: number; expirationTimestamp?: number;
isPublic?: boolean; isPublic?: boolean;
isShowingImage: boolean; isShowingImage: boolean;
theme: DefaultTheme;
}; };
// for some reason, we have to extend a styled component as this: // for some reason, we have to extend a styled component as this:
// props => <OpacityMetadataComponent {...props}/> // props => <OpacityMetadataComponent {...props}/>
@ -68,9 +67,10 @@ export const MessageMetadata = (props: Props) => {
isShowingImage, isShowingImage,
isPublic, isPublic,
isAdmin, isAdmin,
theme,
} = props; } = props;
const theme = useTheme();
if (collapseMetadata) { if (collapseMetadata) {
return null; return null;
} }
@ -80,7 +80,7 @@ export const MessageMetadata = (props: Props) => {
const showError = status === 'error' && isOutgoing; const showError = status === 'error' && isOutgoing;
const showStatus = Boolean(status?.length && isOutgoing); const showStatus = Boolean(status?.length && isOutgoing);
const messageStatusColor = withImageNoCaption ? 'white' : props.theme.colors.sentMessageText; const messageStatusColor = withImageNoCaption ? 'white' : theme.colors.sentMessageText;
return ( return (
<MetadatasContainer withImageNoCaption={withImageNoCaption} {...props}> <MetadatasContainer withImageNoCaption={withImageNoCaption} {...props}>
{showError ? ( {showError ? (

View file

@ -61,7 +61,7 @@ const MessageStatusError = () => {
}; };
export const OutgoingMessageStatus = (props: { export const OutgoingMessageStatus = (props: {
status?: MessageDeliveryStatus; status?: MessageDeliveryStatus | null;
iconColor: string; iconColor: string;
isInMessageView?: boolean; isInMessageView?: boolean;
}) => { }) => {

View file

@ -26,6 +26,7 @@ import {
ReduxConversationType, ReduxConversationType,
PropsForMessage, PropsForMessage,
SortedMessageModelProps, SortedMessageModelProps,
fetchMessagesForConversation,
} from '../../../state/ducks/conversations'; } from '../../../state/ducks/conversations';
import { MessageView } from '../../MainViewController'; import { MessageView } from '../../MainViewController';
import { pushUnblockToSend } from '../../../session/utils/Toast'; import { pushUnblockToSend } from '../../../session/utils/Toast';
@ -304,7 +305,7 @@ export class SessionConversation extends React.Component<Props, State> {
Constants.CONVERSATION.DEFAULT_MESSAGE_FETCH_COUNT, Constants.CONVERSATION.DEFAULT_MESSAGE_FETCH_COUNT,
unreadCount unreadCount
); );
window.inboxStore?.dispatch( (window.inboxStore?.dispatch as any)(
fetchMessagesForConversation({ fetchMessagesForConversation({
conversationKey: selectedConversationKey, conversationKey: selectedConversationKey,
count: messagesToFetch, count: messagesToFetch,
@ -991,6 +992,3 @@ export class SessionConversation extends React.Component<Props, State> {
window.inboxStore?.dispatch(updateMentionsMembers(allMembers)); window.inboxStore?.dispatch(updateMentionsMembers(allMembers));
} }
} }
function fetchMessagesForConversation(arg0: { conversationKey: string; count: number }): any {
throw new Error('Function not implemented.');
}

View file

@ -10,7 +10,11 @@ import { contextMenu } from 'react-contexify';
import { AttachmentType } from '../../../types/Attachment'; import { AttachmentType } from '../../../types/Attachment';
import { GroupNotification } from '../../conversation/GroupNotification'; import { GroupNotification } from '../../conversation/GroupNotification';
import { GroupInvitation } from '../../conversation/GroupInvitation'; import { GroupInvitation } from '../../conversation/GroupInvitation';
import { ReduxConversationType, SortedMessageModelProps } from '../../../state/ducks/conversations'; import {
fetchMessagesForConversation,
ReduxConversationType,
SortedMessageModelProps,
} from '../../../state/ducks/conversations';
import { SessionLastSeenIndicator } from './SessionLastSeenIndicator'; import { SessionLastSeenIndicator } from './SessionLastSeenIndicator';
import { ToastUtils } from '../../../session/utils'; import { ToastUtils } from '../../../session/utils';
import { TypingBubble } from '../../conversation/TypingBubble'; import { TypingBubble } from '../../conversation/TypingBubble';
@ -303,20 +307,12 @@ export class SessionMessagesList extends React.Component<Props, State> {
multiSelectMode: boolean, multiSelectMode: boolean,
playableMessageIndex: number playableMessageIndex: number
) { ) {
const regularProps: MessageRegularProps = { ...messageProps.propsForMessage };
const messageId = messageProps.propsForMessage.id; const messageId = messageProps.propsForMessage.id;
const selected = const selected =
!!messageProps?.propsForMessage.id && this.props.selectedMessages.includes(messageId); !!messageProps?.propsForMessage.id && this.props.selectedMessages.includes(messageId);
regularProps.selected = selected; const onShowDetail = async () => {
regularProps.firstMessageOfSeries = firstMessageOfSeries;
regularProps.multiSelectMode = multiSelectMode;
regularProps.onSelectMessage = this.props.selectMessage;
regularProps.onDeleteMessage = this.props.deleteMessage;
regularProps.onReply = this.props.replyToMessage;
regularProps.onShowDetail = async () => {
const found = await getMessageById(messageId); const found = await getMessageById(messageId);
if (found) { if (found) {
const messageDetailsProps = await found.getPropsForMessageDetail(); const messageDetailsProps = await found.getPropsForMessageDetail();
@ -327,10 +323,16 @@ export class SessionMessagesList extends React.Component<Props, State> {
} }
}; };
regularProps.onClickAttachment = (attachment: AttachmentType) => { const onClickAttachment = (attachment: AttachmentType) => {
this.props.onClickAttachment(attachment, messageProps.propsForMessage); this.props.onClickAttachment(attachment, messageProps.propsForMessage);
}; };
regularProps.onDownload = (attachment: AttachmentType) => {
// tslint:disable-next-line: no-async-without-await
const onQuoteClick = messageProps.propsForMessage.quote
? this.scrollToQuoteMessage
: async () => {};
const onDownload = (attachment: AttachmentType) => {
const messageTimestamp = const messageTimestamp =
messageProps.propsForMessage.timestamp || messageProps.propsForMessage.timestamp ||
messageProps.propsForMessage.serverTimestamp || messageProps.propsForMessage.serverTimestamp ||
@ -343,14 +345,23 @@ export class SessionMessagesList extends React.Component<Props, State> {
}); });
}; };
regularProps.isQuotedMessageToAnimate = messageId === this.state.animateQuotedMessageId; const regularProps: MessageRegularProps = {
...messageProps.propsForMessage,
// tslint:disable-next-line: no-async-without-await selected,
const onQuoteClick = regularProps.quote ? this.scrollToQuoteMessage : async () => {}; firstMessageOfSeries,
multiSelectMode,
regularProps.nextMessageToPlay = this.state.nextMessageToPlay; isQuotedMessageToAnimate: messageId === this.state.animateQuotedMessageId,
regularProps.playableMessageIndex = playableMessageIndex; nextMessageToPlay: this.state.nextMessageToPlay,
regularProps.playNextMessage = this.playNextMessage; playableMessageIndex,
onSelectMessage: this.props.selectMessage,
onDeleteMessage: this.props.deleteMessage,
onReply: this.props.replyToMessage,
onShowDetail,
onClickAttachment,
onDownload,
playNextMessage: this.playNextMessage,
onQuoteClick,
};
return <Message {...regularProps} onQuoteClick={onQuoteClick} key={messageId} />; return <Message {...regularProps} onQuoteClick={onQuoteClick} key={messageId} />;
} }
@ -461,7 +472,7 @@ export class SessionMessagesList extends React.Component<Props, State> {
const oldLen = messagesProps.length; const oldLen = messagesProps.length;
const previousTopMessage = messagesProps[oldLen - 1]?.propsForMessage.id; const previousTopMessage = messagesProps[oldLen - 1]?.propsForMessage.id;
window.inboxStore?.dispatch( (window.inboxStore?.dispatch as any)(
fetchMessagesForConversation({ conversationKey, count: numMessages }) fetchMessagesForConversation({ conversationKey, count: numMessages })
); );
if (previousTopMessage && oldLen !== messagesProps.length) { if (previousTopMessage && oldLen !== messagesProps.length) {
@ -623,6 +634,3 @@ export class SessionMessagesList extends React.Component<Props, State> {
return scrollHeight - scrollTop - clientHeight; return scrollHeight - scrollTop - clientHeight;
} }
} }
function fetchMessagesForConversation(arg0: { conversationKey: string; count: number }): any {
throw new Error('Function not implemented.');
}

View file

@ -111,7 +111,7 @@ export function unbanUser(userToUnBan: string, conversationId: string) {
); );
} }
export function copyBodyToClipboard(body?: string) { export function copyBodyToClipboard(body?: string | null) {
window.clipboard.writeText(body); window.clipboard.writeText(body);
ToastUtils.pushCopiedToClipBoard(); ToastUtils.pushCopiedToClipBoard();

View file

@ -548,7 +548,7 @@ export class MessageModel extends Backbone.Model<MessageAttributes> {
text: this.createNonBreakingLastSeparator(this.get('body')), text: this.createNonBreakingLastSeparator(this.get('body')),
id: this.id as string, id: this.id as string,
direction: (this.isIncoming() ? 'incoming' : 'outgoing') as MessageModelType, direction: (this.isIncoming() ? 'incoming' : 'outgoing') as MessageModelType,
timestamp: this.get('sent_at'), timestamp: this.get('sent_at') || 0,
receivedAt: this.get('received_at'), receivedAt: this.get('received_at'),
serverTimestamp: this.get('serverTimestamp'), serverTimestamp: this.get('serverTimestamp'),
serverId: this.get('serverId'), serverId: this.get('serverId'),
@ -574,6 +574,7 @@ export class MessageModel extends Backbone.Model<MessageAttributes> {
weAreAdmin, weAreAdmin,
isDeletable, isDeletable,
isSenderAdmin, isSenderAdmin,
isExpired: this.isExpired(),
}; };
return props; return props;

View file

@ -51,7 +51,7 @@ export interface MessageAttributes {
* timestamp is the sent_at timestamp, which is the envelope.timestamp * timestamp is the sent_at timestamp, which is the envelope.timestamp
*/ */
timestamp?: number; timestamp?: number;
status: MessageDeliveryStatus; status?: MessageDeliveryStatus;
dataMessage: any; dataMessage: any;
sent_to: any; sent_to: any;
sent: boolean; sent: boolean;
@ -202,20 +202,20 @@ export interface MessageRegularProps {
isDeletable: boolean; isDeletable: boolean;
isAdmin?: boolean; isAdmin?: boolean;
weAreAdmin?: boolean; weAreAdmin?: boolean;
text?: string; text: string | null;
id: string; id: string;
collapseMetadata?: boolean; collapseMetadata?: boolean;
direction: MessageModelType; direction: MessageModelType;
timestamp: number; timestamp: number;
serverTimestamp?: number; serverTimestamp?: number;
status?: MessageDeliveryStatus; status?: MessageDeliveryStatus | null;
// What if changed this over to a single contact like quote, and put the events on it? // What if changed this over to a single contact like quote, and put the events on it?
contact?: Contact & { contact?: Contact & {
onSendMessage?: () => void; onSendMessage?: () => void;
onClick?: () => void; onClick?: () => void;
}; };
authorName?: string; authorName?: string | null;
authorProfileName?: string; authorProfileName?: string | null;
/** Note: this should be formatted for display */ /** Note: this should be formatted for display */
authorPhoneNumber: string; authorPhoneNumber: string;
conversationType: ConversationTypeEnum; conversationType: ConversationTypeEnum;
@ -247,16 +247,13 @@ export interface MessageRegularProps {
isQuotedMessageToAnimate?: boolean; isQuotedMessageToAnimate?: boolean;
isTrustedForAttachmentDownload: boolean; isTrustedForAttachmentDownload: boolean;
onClickAttachment?: (attachment: AttachmentType) => void; onClickAttachment: (attachment: AttachmentType) => void;
onClickLinkPreview?: (url: string) => void;
onSelectMessage: (messageId: string) => void; onSelectMessage: (messageId: string) => void;
onReply?: (messagId: number) => void; onReply: (messagId: number) => void;
onDownload?: (attachment: AttachmentType) => void; onDownload: (attachment: AttachmentType) => void;
onDeleteMessage: (messageId: string) => void; onDeleteMessage: (messageId: string) => void;
onShowDetail: () => void; onShowDetail: () => void;
markRead: (readAt: number) => Promise<void>;
onQuoteClick: (options: QuoteClickOptions) => Promise<void>; onQuoteClick: (options: QuoteClickOptions) => Promise<void>;
theme: DefaultTheme;
playableMessageIndex?: number; playableMessageIndex?: number;
nextMessageToPlay?: number; nextMessageToPlay?: number;

View file

@ -137,7 +137,7 @@ export type PropsForMessage = {
text: string | null; text: string | null;
id: string; id: string;
direction: MessageModelType; direction: MessageModelType;
timestamp: number | undefined; timestamp: number;
receivedAt: number | undefined; receivedAt: number | undefined;
serverTimestamp: number | undefined; serverTimestamp: number | undefined;
serverId: number | undefined; serverId: number | undefined;
@ -161,6 +161,7 @@ export type PropsForMessage = {
weAreAdmin: boolean; weAreAdmin: boolean;
isSenderAdmin: boolean; isSenderAdmin: boolean;
isDeletable: boolean; isDeletable: boolean;
isExpired: boolean;
}; };
export type LastMessageType = { export type LastMessageType = {
@ -287,7 +288,7 @@ type FetchedMessageResults = {
messagesProps: Array<SortedMessageModelProps>; messagesProps: Array<SortedMessageModelProps>;
}; };
const fetchMessagesForConversation = createAsyncThunk( export const fetchMessagesForConversation = createAsyncThunk(
'messages/fetchByConversationKey', 'messages/fetchByConversationKey',
async ({ async ({
conversationKey, conversationKey,