diff --git a/ts/components/conversation/message/message-item/ExpirableReadableMessage.tsx b/ts/components/conversation/message/message-item/ExpirableReadableMessage.tsx index 0ef6db91f..84cdfcba7 100644 --- a/ts/components/conversation/message/message-item/ExpirableReadableMessage.tsx +++ b/ts/components/conversation/message/message-item/ExpirableReadableMessage.tsx @@ -82,8 +82,7 @@ const StyledReadableMessage = styled(ReadableMessage)<{ flex-direction: column; `; -export interface ExpirableReadableMessageProps - extends Omit { +export interface ExpirableReadableMessageProps extends Omit { messageId: string; isControlMessage?: boolean; } @@ -130,7 +129,6 @@ export const ExpirableReadableMessage = (props: ExpirableReadableMessageProps) = const { messageId, direction: _direction, - receivedAt, isUnread, expirationDurationMs, expirationTimestamp, @@ -143,7 +141,6 @@ export const ExpirableReadableMessage = (props: ExpirableReadableMessageProps) = return ( { const isGroup = !useSelectedIsPrivate(); const isCommunity = useSelectedIsPublic(); const isUnread = useMessageIsUnread(messageId) || false; - const receivedAt = useMessageReceivedAt(messageId); const interactionNotification = useMessageInteractionNotification(messageId); if (!convoId || !messageId || !interactionNotification) { @@ -80,7 +75,6 @@ export const InteractionNotification = (props: WithMessageId) => { return ( { const conversationId = useSelectedConversationKey(); - const receivedAt = useMessageReceivedAt(messageId); const isUnread = useMessageIsUnread(messageId) || false; const isUs = useMessageAuthorIsUs(messageId); @@ -27,7 +22,6 @@ export const MessageRequestResponse = ({ messageId }: WithMessageId) => { return ( ; onDoubleClickCapture?: MouseEventHandler; @@ -70,12 +70,36 @@ const debouncedTriggerLoadMoreBottom = debounce( 100 ); +async function markReadFromMessageId({ + conversationId, + messageId, + isUnread, +}: WithMessageId & WithConvoId & { isUnread: boolean }) { + // isUnread comes from the redux store in memory, so pretty fast and allows us to fetch from the DB too often + if (!isUnread) { + return; + } + const found = await Data.getMessageById(messageId); + + if (!found) { + return; + } + + if (found.isUnread()) { + ConvoHub.use() + .get(conversationId) + ?.markConversationRead({ + newestUnreadDate: found.get('sent_at') || found.get('serverTimestamp') || Date.now(), + fromConfigMessage: false, + }); + } +} + export const ReadableMessage = (props: ReadableMessageProps) => { const { messageId, onContextMenu, className, - receivedAt, isUnread, onClick, onDoubleClickCapture, @@ -127,9 +151,12 @@ export const ReadableMessage = (props: ReadableMessageProps) => { // make sure the app is focused, because we mark message as read here if (inView === true && isAppFocused) { dispatch(showScrollToBottomButton(false)); - ConvoHub.use() - .get(selectedConversationKey) - ?.markConversationRead({ newestUnreadDate: receivedAt || 0, fromConfigMessage: false }); // TODOLATER this should be `sentAt || serverTimestamp` I think + // TODO this is pretty expensive and should instead use values from the redux store + await markReadFromMessageId({ + messageId, + conversationId: selectedConversationKey, + isUnread, + }); dispatch(markConversationFullyRead(selectedConversationKey)); } else if (inView === false) { @@ -147,23 +174,12 @@ export const ReadableMessage = (props: ReadableMessageProps) => { // this part is just handling the marking of the message as read if needed if (inView) { - if (isUnread) { - // TODOLATER this is pretty expensive and should instead use values from the redux store - const found = await Data.getMessageById(messageId); - - if (found && Boolean(found.get('unread'))) { - const foundSentAt = found.get('sent_at') || found.get('serverTimestamp'); - // we should stack those and send them in a single message once every 5secs or something. - // this would be part of an redesign of the sending pipeline - // mark the whole conversation as read until this point. - // this will trigger the expire timer. - if (foundSentAt) { - ConvoHub.use() - .get(selectedConversationKey) - ?.markConversationRead({ newestUnreadDate: foundSentAt, fromConfigMessage: false }); - } - } - } + // TODO this is pretty expensive and should instead use values from the redux store + await markReadFromMessageId({ + messageId, + conversationId: selectedConversationKey, + isUnread, + }); } }, [ @@ -173,10 +189,9 @@ export const ReadableMessage = (props: ReadableMessageProps) => { oldestMessageId, fetchingMoreInProgress, isAppFocused, - receivedAt, messageId, - isUnread, youngestMessageId, + isUnread, ] ); diff --git a/ts/models/conversation.ts b/ts/models/conversation.ts index 06ed0e844..929006bf7 100644 --- a/ts/models/conversation.ts +++ b/ts/models/conversation.ts @@ -494,6 +494,7 @@ export class ConversationModel extends Backbone.Model { * Fetches from the Database an update of what are the memory only informations like mentionedUs and the unreadCount, etc */ public async refreshInMemoryDetails(providedMemoryDetails?: SaveConversationReturn) { + if (!SessionUtilConvoInfoVolatile.isConvoToStoreInWrapper(this)) { return; } diff --git a/ts/models/message.ts b/ts/models/message.ts index a3977d204..e1befb6b9 100644 --- a/ts/models/message.ts +++ b/ts/models/message.ts @@ -129,7 +129,6 @@ export class MessageModel extends Backbone.Model { const propsForGroupUpdateMessage = this.getPropsForGroupUpdateMessage(); const propsForTimerNotification = this.getPropsForTimerNotification(); const isMessageResponse = this.isMessageRequestResponse(); - const propsForQuote = this.getPropsForQuote(); const callNotificationType = this.get('callNotificationType'); const interactionNotification = this.getInteractionNotification(); @@ -151,9 +150,6 @@ export class MessageModel extends Backbone.Model { if (propsForTimerNotification) { messageProps.propsForTimerNotification = propsForTimerNotification; } - if (propsForQuote) { - messageProps.propsForQuote = propsForQuote; - } if (callNotificationType) { const propsForCallNotification: PropsForCallNotification = { @@ -198,7 +194,9 @@ export class MessageModel extends Backbone.Model { } public isUnread() { - return !!this.get('unread'); + const unreadField = this.get('unread'); + + return !!unreadField; } // Important to allow for this.set({ unread}), save to db, then fetch() diff --git a/ts/state/ducks/conversations.ts b/ts/state/ducks/conversations.ts index 3c8d60669..c87e61cca 100644 --- a/ts/state/ducks/conversations.ts +++ b/ts/state/ducks/conversations.ts @@ -39,7 +39,6 @@ export type MessageModelPropsWithoutConvoProps = { propsForGroupUpdateMessage?: PropsForGroupUpdate; // plop: cleaned up propsForCallNotification?: PropsForCallNotification; // plop: cleaned up propsForMessageRequestResponse?: PropsForMessageRequestResponse; // plop: cleaned up - propsForQuote?: PropsForQuote; propsForInteractionNotification?: PropsForInteractionNotification; // plop: cleaned up }; @@ -151,7 +150,7 @@ export type PropsForMessageWithoutConvoProps = { id: string; // messageId direction: MessageModelType; timestamp: number; - sender: string; // this is the sender + sender: string; // this is the sender/author convoId: string; // this is the conversation in which this message was sent text?: string; receivedAt?: number; @@ -170,6 +169,11 @@ export type PropsForMessageWithoutConvoProps = { expirationDurationMs?: number; expirationTimestamp?: number | null; isExpired?: boolean; + /** + * true if the sender of that message is trusted for auto attachment downloads. + * Note: we keep it in the PropsForMessageWithoutConvoProps because it is a per-sender setting + * rather than a per-convo setting (especially for groups) + */ isTrustedForAttachmentDownload?: boolean; };