diff --git a/ts/components/conversation/Message.tsx b/ts/components/conversation/Message.tsx index 86cd3063e..6a5d5a83d 100644 --- a/ts/components/conversation/Message.tsx +++ b/ts/components/conversation/Message.tsx @@ -379,9 +379,11 @@ class MessageInner extends React.PureComponent { multiSelectMode, } = this.props; - if (!quote || !quote.authorPhoneNumber) { + if (!quote || !quote.authorPhoneNumber || !quote.messageId) { return null; } + const quoteId = _.toNumber(quote.messageId); + const { authorPhoneNumber, referencedMessageNotFound } = quote; const withContentAbove = conversationType === 'group' && direction === 'incoming'; @@ -398,8 +400,7 @@ class MessageInner extends React.PureComponent { this.props.onSelectMessage(id); return; } - const { authorPhoneNumber, messageId: quoteId, referencedMessageNotFound } = quote; - quote?.onClick({ + void this.props.onQuoteClick?.({ quoteAuthor: authorPhoneNumber, quoteId, referencedMessageNotFound, @@ -812,7 +813,19 @@ class MessageInner extends React.PureComponent { {this.renderAttachment()} {this.renderPreview()} {this.renderText()} - + {this.renderError(!isIncoming)} {this.renderContextMenu()} diff --git a/ts/components/conversation/Quote.tsx b/ts/components/conversation/Quote.tsx index ac1904f4c..e34310371 100644 --- a/ts/components/conversation/Quote.tsx +++ b/ts/components/conversation/Quote.tsx @@ -13,7 +13,7 @@ import { ConversationTypeEnum } from '../../models/conversation'; import { useEncryptedFileFetch } from '../../hooks/useEncryptedFileFetch'; -interface QuoteProps { +export type QuotePropsWithoutListener = { attachment?: QuotedAttachmentType; authorPhoneNumber: string; authorProfileName?: string; @@ -24,10 +24,13 @@ interface QuoteProps { convoId: string; isPublic?: boolean; withContentAbove: boolean; - onClick?: (e: any) => void; text: string; referencedMessageNotFound: boolean; -} +}; + +export type QuotePropsWithListener = QuotePropsWithoutListener & { + onClick?: (e: any) => void; +}; export interface QuotedAttachmentType { contentType: MIME.MIMEType; @@ -43,7 +46,7 @@ interface Attachment { objectUrl?: string; } -function validateQuote(quote: QuoteProps): boolean { +function validateQuote(quote: QuotePropsWithoutListener): boolean { if (quote.text) { return true; } @@ -295,7 +298,7 @@ export const QuoteReferenceWarning = (props: any) => { ); }; -export const Quote = (props: QuoteProps) => { +export const Quote = (props: QuotePropsWithListener) => { const [imageBroken, setImageBroken] = useState(false); const handleImageErrorBound = null; diff --git a/ts/components/session/LeftPaneSettingSection.tsx b/ts/components/session/LeftPaneSettingSection.tsx index fe4c1874f..ff3233d5d 100644 --- a/ts/components/session/LeftPaneSettingSection.tsx +++ b/ts/components/session/LeftPaneSettingSection.tsx @@ -39,11 +39,6 @@ const getCategories = () => { title: window.i18n('blockedSettingsTitle'), hidden: false, }, - { - id: SessionSettingCategory.Permissions, - title: window.i18n('permissionSettingsTitle'), - hidden: true, - }, { id: SessionSettingCategory.Notifications, title: window.i18n('notificationsSettingsTitle'), diff --git a/ts/components/session/SessionConfirm.tsx b/ts/components/session/SessionConfirm.tsx index 190c11632..7317ad80a 100644 --- a/ts/components/session/SessionConfirm.tsx +++ b/ts/components/session/SessionConfirm.tsx @@ -27,7 +27,7 @@ export interface SessionConfirmDialogProps { sessionIcon?: SessionIconType; iconSize?: SessionIconSize; theme?: DefaultTheme; - shouldShowConfirm?: () => boolean | undefined; + shouldShowConfirm?: boolean | undefined; } const SessionConfirmInner = (props: SessionConfirmDialogProps) => { @@ -70,7 +70,7 @@ const SessionConfirmInner = (props: SessionConfirmDialogProps) => { window.inboxStore?.dispatch(updateConfirmModal(null)); }; - if (shouldShowConfirm && !shouldShowConfirm()) { + if (shouldShowConfirm && !shouldShowConfirm) { return null; } diff --git a/ts/components/session/SessionToggle.tsx b/ts/components/session/SessionToggle.tsx index 378b18a46..9afc88164 100644 --- a/ts/components/session/SessionToggle.tsx +++ b/ts/components/session/SessionToggle.tsx @@ -25,7 +25,7 @@ export const SessionToggle = (props: Props) => { props.onClick(); }; - if (props.confirmationDialogParams && props.confirmationDialogParams.shouldShowConfirm()) { + if (props.confirmationDialogParams && props.confirmationDialogParams.shouldShowConfirm) { // If item needs a confirmation dialog to turn ON, render it const closeConfirmModal = () => { dispatch(updateConfirmModal(null)); diff --git a/ts/components/session/conversation/SessionCompositionBox.tsx b/ts/components/session/conversation/SessionCompositionBox.tsx index 22df902de..76d2c68f7 100644 --- a/ts/components/session/conversation/SessionCompositionBox.tsx +++ b/ts/components/session/conversation/SessionCompositionBox.tsx @@ -242,7 +242,7 @@ export class SessionCompositionBox extends React.Component { if (this.isURL(pastedText)) { window.inboxStore?.dispatch( updateConfirmModal({ - shouldShowConfirm: () => !window.getSettingValue('link-preview-setting'), + shouldShowConfirm: !window.getSettingValue('link-preview-setting'), title: window.i18n('linkPreviewsTitle'), message: window.i18n('linkPreviewsConfirmMessage'), okTheme: SessionButtonColor.Danger, diff --git a/ts/components/session/conversation/SessionMessagesList.tsx b/ts/components/session/conversation/SessionMessagesList.tsx index 5df0aeef5..d8c747412 100644 --- a/ts/components/session/conversation/SessionMessagesList.tsx +++ b/ts/components/session/conversation/SessionMessagesList.tsx @@ -20,7 +20,7 @@ import { ToastUtils } from '../../../session/utils'; import { TypingBubble } from '../../conversation/TypingBubble'; import { getConversationController } from '../../../session/conversations'; import { MessageModel } from '../../../models/message'; -import { MessageRegularProps } from '../../../models/messageType'; +import { MessageRegularProps, QuoteClickOptions } from '../../../models/messageType'; import { getMessageById, getMessagesBySentAt } from '../../../data/data'; import autoBind from 'auto-bind'; import { ConversationTypeEnum } from '../../../models/conversation'; @@ -238,7 +238,6 @@ export class SessionMessagesList extends React.Component { key={`unread-indicator-${messageProps.propsForMessage.id}`} /> ); - console.warn('key', messageProps.propsForMessage.id); currentMessageIndex = currentMessageIndex + 1; if (groupNotificationProps) { @@ -349,21 +348,14 @@ export class SessionMessagesList extends React.Component { regularProps.isQuotedMessageToAnimate = messageId === this.state.animateQuotedMessageId; - if (regularProps.quote) { - regularProps.quote.onClick = (options: { - quoteAuthor: string; - quoteId: any; - referencedMessageNotFound: boolean; - }) => { - void this.scrollToQuoteMessage(options); - }; - } + // tslint:disable-next-line: no-async-without-await + const onQuoteClick = regularProps.quote ? this.scrollToQuoteMessage : async () => {}; regularProps.nextMessageToPlay = this.state.nextMessageToPlay; regularProps.playableMessageIndex = playableMessageIndex; regularProps.playNextMessage = this.playNextMessage; - return ; + return ; } // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ @@ -563,7 +555,7 @@ export class SessionMessagesList extends React.Component { this.updateReadMessages(); } - private async scrollToQuoteMessage(options: any = {}) { + private async scrollToQuoteMessage(options: QuoteClickOptions) { const { quoteAuthor, quoteId, referencedMessageNotFound } = options; // For simplicity's sake, we show the 'not found' toast no matter what if we were diff --git a/ts/components/session/settings/SessionSettings.tsx b/ts/components/session/settings/SessionSettings.tsx index abcff8bb7..a643f5dff 100644 --- a/ts/components/session/settings/SessionSettings.tsx +++ b/ts/components/session/settings/SessionSettings.tsx @@ -50,7 +50,7 @@ interface State { } interface ConfirmationDialogParams extends SessionConfirmDialogProps { - shouldShowConfirm: () => boolean | undefined; + shouldShowConfirm: boolean | undefined; } interface LocalSettingType { @@ -344,7 +344,7 @@ class SettingsViewInner extends React.Component { comparisonValue: undefined, onClick: undefined, confirmationDialogParams: { - shouldShowConfirm: () => !window.getSettingValue('link-preview-setting'), + shouldShowConfirm: !window.getSettingValue('link-preview-setting'), title: window.i18n('linkPreviewsTitle'), message: window.i18n('linkPreviewsConfirmMessage'), okTheme: SessionButtonColor.Danger, @@ -405,19 +405,6 @@ class SettingsViewInner extends React.Component { }, confirmationDialogParams: undefined, }, - { - id: 'media-permissions', - title: window.i18n('mediaPermissionsTitle'), - description: window.i18n('mediaPermissionsDescription'), - hidden: false, - type: SessionSettingType.Toggle, - category: SessionSettingCategory.Permissions, - setFn: window.toggleMediaPermissions, - content: undefined, - comparisonValue: undefined, - onClick: undefined, - confirmationDialogParams: undefined, - }, { id: 'zoom-factor-setting', title: window.i18n('zoomFactorSettingTitle'), diff --git a/ts/components/session/settings/SessionSettingsHeader.tsx b/ts/components/session/settings/SessionSettingsHeader.tsx index 828b9b43d..a2817e56e 100644 --- a/ts/components/session/settings/SessionSettingsHeader.tsx +++ b/ts/components/session/settings/SessionSettingsHeader.tsx @@ -6,10 +6,9 @@ interface Props extends SettingsViewProps { // tslint:disable-next-line: react-unused-props-and-state categoryTitle: string; // tslint:disable-next-line: react-unused-props-and-state - theme: DefaultTheme; } -const SettingsHeaderInner = (props: Props) => { +export const SettingsHeader = (props: Props) => { const { categoryTitle } = props; return (
@@ -17,5 +16,3 @@ const SettingsHeaderInner = (props: Props) => {
); }; - -export const SettingsHeader = withTheme(SettingsHeaderInner); diff --git a/ts/models/conversation.ts b/ts/models/conversation.ts index b9dfb055a..b9a9bbdf0 100644 --- a/ts/models/conversation.ts +++ b/ts/models/conversation.ts @@ -943,7 +943,7 @@ export class ConversationModel extends Backbone.Model { const allProps: Array = []; for (const nowRead of oldUnreadNowRead) { - allProps.push(nowRead.generateProps(false)); + allProps.push(nowRead.getProps()); } window.inboxStore?.dispatch(conversationActions.messagesChanged(allProps)); diff --git a/ts/models/message.ts b/ts/models/message.ts index 48416b100..577ba4d44 100644 --- a/ts/models/message.ts +++ b/ts/models/message.ts @@ -80,34 +80,29 @@ export class MessageModel extends Backbone.Model { window.contextMenuShown = false; - // this.generateProps(false); + this.getProps(); } public getProps(): MessageModelProps { - const propsForTimerNotification = this.getPropsForTimerNotification(); - const propsForGroupNotification = this.getPropsForGroupNotification(); - const propsForGroupInvitation = this.getPropsForGroupInvitation(); - const propsForDataExtractionNotification = this.getPropsForDataExtractionNotification(); - const propsForSearchResult = this.getPropsForSearchResult(); - const propsForMessage = this.getPropsForMessage(); - + perfStart(`getPropsMessage-${this.id}`); const messageProps: MessageModelProps = { - propsForMessage, - propsForSearchResult, - propsForDataExtractionNotification, - propsForGroupInvitation, - propsForGroupNotification, - propsForTimerNotification, + propsForMessage: this.getPropsForMessage(), + propsForSearchResult: this.getPropsForSearchResult(), + propsForDataExtractionNotification: this.getPropsForDataExtractionNotification(), + propsForGroupInvitation: this.getPropsForGroupInvitation(), + propsForGroupNotification: this.getPropsForGroupNotification(), + propsForTimerNotification: this.getPropsForTimerNotification(), }; + perfEnd(`getPropsMessage-${this.id}`, 'getPropsMessage'); + return messageProps; } // Keep props ready - public generateProps(triggerEvent = true): MessageModelProps { + public generateProps(): MessageModelProps { const messageProps = this.getProps(); - if (triggerEvent) { - window.inboxStore?.dispatch(conversationActions.messageChanged(messageProps)); - } + window.inboxStore?.dispatch(conversationActions.messageChanged(messageProps)); + return messageProps; } diff --git a/ts/models/messageType.ts b/ts/models/messageType.ts index 9cf7745cc..3b0790767 100644 --- a/ts/models/messageType.ts +++ b/ts/models/messageType.ts @@ -192,6 +192,11 @@ export const fillMessageAttributesWithDefaults = ( return defaulted; }; +export type QuoteClickOptions = { + quoteAuthor: string; + quoteId: number; + referencedMessageNotFound: boolean; +}; export interface MessageRegularProps { disableMenu?: boolean; isDeletable: boolean; @@ -251,6 +256,7 @@ export interface MessageRegularProps { onDeleteMessage: (messageId: string) => void; onShowDetail: () => void; markRead: (readAt: number) => Promise; + onQuoteClick: (options: QuoteClickOptions) => Promise; theme: DefaultTheme; playableMessageIndex?: number; diff --git a/ts/receiver/configMessage.ts b/ts/receiver/configMessage.ts index 6c04ee45b..54ea9cc50 100644 --- a/ts/receiver/configMessage.ts +++ b/ts/receiver/configMessage.ts @@ -61,7 +61,7 @@ async function handleGroupsAndContactsFromConfigMessage( const didWeHandleAConfigurationMessageAlready = (await getItemById(hasSyncedInitialConfigurationItem))?.value || false; if (didWeHandleAConfigurationMessageAlready) { - window?.log?.warn( + window?.log?.info( 'Dropping configuration contacts/groups change as we already handled one... ' ); return; @@ -73,7 +73,7 @@ async function handleGroupsAndContactsFromConfigMessage( const numberClosedGroup = configMessage.closedGroups?.length || 0; - window?.log?.warn( + window?.log?.info( `Received ${numberClosedGroup} closed group on configuration. Creating them... ` ); diff --git a/ts/state/ducks/conversations.ts b/ts/state/ducks/conversations.ts index 64f1db8c5..cee2ef625 100644 --- a/ts/state/ducks/conversations.ts +++ b/ts/state/ducks/conversations.ts @@ -198,8 +198,9 @@ async function getMessages( }); // Set first member of series here. - const messageModelsProps: Array = messageSet.models.map(m => { - return { ...m.getProps(), firstMessageOfSeries: true }; + const messageModelsProps: Array = []; + messageSet.models.forEach(m => { + messageModelsProps.push({ ...m.getProps(), firstMessageOfSeries: true }); }); const isPublic = conversation.isPublic(); @@ -262,14 +263,15 @@ const fetchMessagesForConversation = createAsyncThunk( const time = afterTimestamp - beforeTimestamp; window?.log?.info(`Loading ${messagesProps.length} messages took ${time}ms to load.`); + const mapped = messagesProps.map(m => { + return { + ...m, + firstMessageOfSeries: true, + }; + }); return { conversationKey, - messagesProps: messagesProps.map(m => { - return { - ...m, - firstMessageOfSeries: true, - }; - }), + messagesProps: mapped, }; } ); @@ -521,9 +523,9 @@ function sortMessages( // we order by serverTimestamp for public convos // be sure to update the sorting order to fetch messages from the DB too at getMessagesByConversation if (isPublic) { - return messages.sort( - (a: any, b: any) => b.attributes.serverTimestamp - a.attributes.serverTimestamp - ); + return messages.sort((a, b) => { + return (b.propsForMessage.serverTimestamp || 0) - (a.propsForMessage.serverTimestamp || 0); + }); } if (messages.some(n => !n.propsForMessage.timestamp && !n.propsForMessage.receivedAt)) { throw new Error('Found some messages without any timestamp set'); @@ -532,9 +534,9 @@ function sortMessages( // for non public convos, we order by sent_at or received_at timestamp. // we assume that a message has either a sent_at or a received_at field set. const messagesSorted = messages.sort( - (a: any, b: any) => - (b.attributes.sent_at || b.attributes.received_at) - - (a.attributes.sent_at || a.attributes.received_at) + (a, b) => + (b.propsForMessage.timestamp || b.propsForMessage.receivedAt || 0) - + (a.propsForMessage.timestamp || a.propsForMessage.receivedAt || 0) ); return messagesSorted; @@ -737,7 +739,7 @@ export function reducer( if (conversationKey === state.selectedConversation) { return { ...state, - messages: { ...messagesProps }, + messages: messagesProps, }; } return state;