fix: fix the control messages not being marked as read sometimes

pull/3281/head
Audric Ackermann 3 months ago
parent e33f2ed734
commit b3fffc9cae
No known key found for this signature in database

@ -82,8 +82,7 @@ const StyledReadableMessage = styled(ReadableMessage)<{
flex-direction: column; flex-direction: column;
`; `;
export interface ExpirableReadableMessageProps export interface ExpirableReadableMessageProps extends Omit<ReadableMessageProps, 'isUnread'> {
extends Omit<ReadableMessageProps, 'receivedAt' | 'isUnread'> {
messageId: string; messageId: string;
isControlMessage?: boolean; isControlMessage?: boolean;
} }
@ -130,7 +129,6 @@ export const ExpirableReadableMessage = (props: ExpirableReadableMessageProps) =
const { const {
messageId, messageId,
direction: _direction, direction: _direction,
receivedAt,
isUnread, isUnread,
expirationDurationMs, expirationDurationMs,
expirationTimestamp, expirationTimestamp,
@ -143,7 +141,6 @@ export const ExpirableReadableMessage = (props: ExpirableReadableMessageProps) =
return ( return (
<StyledReadableMessage <StyledReadableMessage
messageId={messageId} messageId={messageId}
receivedAt={receivedAt}
isUnread={!!isUnread} isUnread={!!isUnread}
isIncoming={isIncoming} isIncoming={isIncoming}
onClick={onClick} onClick={onClick}

@ -13,11 +13,7 @@ import {
useSelectedIsPrivate, useSelectedIsPrivate,
useSelectedIsPublic, useSelectedIsPublic,
} from '../../../../state/selectors/selectedConversation'; } from '../../../../state/selectors/selectedConversation';
import { import { useMessageInteractionNotification, useMessageIsUnread } from '../../../../state/selectors';
useMessageInteractionNotification,
useMessageIsUnread,
useMessageReceivedAt,
} from '../../../../state/selectors';
import type { WithMessageId } from '../../../../session/types/with'; import type { WithMessageId } from '../../../../session/types/with';
const StyledFailText = styled.div` const StyledFailText = styled.div`
@ -32,7 +28,6 @@ export const InteractionNotification = (props: WithMessageId) => {
const isGroup = !useSelectedIsPrivate(); const isGroup = !useSelectedIsPrivate();
const isCommunity = useSelectedIsPublic(); const isCommunity = useSelectedIsPublic();
const isUnread = useMessageIsUnread(messageId) || false; const isUnread = useMessageIsUnread(messageId) || false;
const receivedAt = useMessageReceivedAt(messageId);
const interactionNotification = useMessageInteractionNotification(messageId); const interactionNotification = useMessageInteractionNotification(messageId);
if (!convoId || !messageId || !interactionNotification) { if (!convoId || !messageId || !interactionNotification) {
@ -80,7 +75,6 @@ export const InteractionNotification = (props: WithMessageId) => {
return ( return (
<ReadableMessage <ReadableMessage
messageId={messageId} messageId={messageId}
receivedAt={receivedAt}
isUnread={isUnread} isUnread={isUnread}
key={`readable-message-${messageId}`} key={`readable-message-${messageId}`}
dataTestId="interaction-notification" dataTestId="interaction-notification"

@ -1,10 +1,6 @@
import { useNicknameOrProfileNameOrShortenedPubkey } from '../../../../hooks/useParamSelector'; import { useNicknameOrProfileNameOrShortenedPubkey } from '../../../../hooks/useParamSelector';
import type { WithMessageId } from '../../../../session/types/with'; import type { WithMessageId } from '../../../../session/types/with';
import { import { useMessageAuthorIsUs, useMessageIsUnread } from '../../../../state/selectors';
useMessageAuthorIsUs,
useMessageIsUnread,
useMessageReceivedAt,
} from '../../../../state/selectors';
import { useSelectedConversationKey } from '../../../../state/selectors/selectedConversation'; import { useSelectedConversationKey } from '../../../../state/selectors/selectedConversation';
import { Flex } from '../../../basic/Flex'; import { Flex } from '../../../basic/Flex';
import { Localizer } from '../../../basic/Localizer'; import { Localizer } from '../../../basic/Localizer';
@ -14,7 +10,6 @@ import { ReadableMessage } from './ReadableMessage';
// Note: this should not respond to the disappearing message conversation setting so we use the ReadableMessage directly // Note: this should not respond to the disappearing message conversation setting so we use the ReadableMessage directly
export const MessageRequestResponse = ({ messageId }: WithMessageId) => { export const MessageRequestResponse = ({ messageId }: WithMessageId) => {
const conversationId = useSelectedConversationKey(); const conversationId = useSelectedConversationKey();
const receivedAt = useMessageReceivedAt(messageId);
const isUnread = useMessageIsUnread(messageId) || false; const isUnread = useMessageIsUnread(messageId) || false;
const isUs = useMessageAuthorIsUs(messageId); const isUs = useMessageAuthorIsUs(messageId);
@ -27,7 +22,6 @@ export const MessageRequestResponse = ({ messageId }: WithMessageId) => {
return ( return (
<ReadableMessage <ReadableMessage
messageId={messageId} messageId={messageId}
receivedAt={receivedAt}
isUnread={isUnread} isUnread={isUnread}
dataTestId="message-request-response-message" dataTestId="message-request-response-message"
key={`readable-message-${messageId}`} key={`readable-message-${messageId}`}

@ -31,12 +31,12 @@ import {
} from '../../../../state/selectors/conversations'; } from '../../../../state/selectors/conversations';
import { getIsAppFocused } from '../../../../state/selectors/section'; import { getIsAppFocused } from '../../../../state/selectors/section';
import { useSelectedConversationKey } from '../../../../state/selectors/selectedConversation'; import { useSelectedConversationKey } from '../../../../state/selectors/selectedConversation';
import type { WithConvoId, WithMessageId } from '../../../../session/types/with';
export type ReadableMessageProps = { export type ReadableMessageProps = {
children: ReactNode; children: ReactNode;
messageId: string; messageId: string;
className?: string; className?: string;
receivedAt: number | undefined;
isUnread: boolean; isUnread: boolean;
onClick?: MouseEventHandler<HTMLElement>; onClick?: MouseEventHandler<HTMLElement>;
onDoubleClickCapture?: MouseEventHandler<HTMLElement>; onDoubleClickCapture?: MouseEventHandler<HTMLElement>;
@ -70,12 +70,36 @@ const debouncedTriggerLoadMoreBottom = debounce(
100 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) => { export const ReadableMessage = (props: ReadableMessageProps) => {
const { const {
messageId, messageId,
onContextMenu, onContextMenu,
className, className,
receivedAt,
isUnread, isUnread,
onClick, onClick,
onDoubleClickCapture, onDoubleClickCapture,
@ -127,9 +151,12 @@ export const ReadableMessage = (props: ReadableMessageProps) => {
// make sure the app is focused, because we mark message as read here // make sure the app is focused, because we mark message as read here
if (inView === true && isAppFocused) { if (inView === true && isAppFocused) {
dispatch(showScrollToBottomButton(false)); dispatch(showScrollToBottomButton(false));
ConvoHub.use() // TODO this is pretty expensive and should instead use values from the redux store
.get(selectedConversationKey) await markReadFromMessageId({
?.markConversationRead({ newestUnreadDate: receivedAt || 0, fromConfigMessage: false }); // TODOLATER this should be `sentAt || serverTimestamp` I think messageId,
conversationId: selectedConversationKey,
isUnread,
});
dispatch(markConversationFullyRead(selectedConversationKey)); dispatch(markConversationFullyRead(selectedConversationKey));
} else if (inView === false) { } 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 // this part is just handling the marking of the message as read if needed
if (inView) { if (inView) {
if (isUnread) { // TODO this is pretty expensive and should instead use values from the redux store
// TODOLATER this is pretty expensive and should instead use values from the redux store await markReadFromMessageId({
const found = await Data.getMessageById(messageId); messageId,
conversationId: selectedConversationKey,
if (found && Boolean(found.get('unread'))) { isUnread,
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 });
}
}
}
} }
}, },
[ [
@ -173,10 +189,9 @@ export const ReadableMessage = (props: ReadableMessageProps) => {
oldestMessageId, oldestMessageId,
fetchingMoreInProgress, fetchingMoreInProgress,
isAppFocused, isAppFocused,
receivedAt,
messageId, messageId,
isUnread,
youngestMessageId, youngestMessageId,
isUnread,
] ]
); );

@ -494,6 +494,7 @@ export class ConversationModel extends Backbone.Model<ConversationAttributes> {
* Fetches from the Database an update of what are the memory only informations like mentionedUs and the unreadCount, etc * Fetches from the Database an update of what are the memory only informations like mentionedUs and the unreadCount, etc
*/ */
public async refreshInMemoryDetails(providedMemoryDetails?: SaveConversationReturn) { public async refreshInMemoryDetails(providedMemoryDetails?: SaveConversationReturn) {
if (!SessionUtilConvoInfoVolatile.isConvoToStoreInWrapper(this)) { if (!SessionUtilConvoInfoVolatile.isConvoToStoreInWrapper(this)) {
return; return;
} }

@ -129,7 +129,6 @@ export class MessageModel extends Backbone.Model<MessageAttributes> {
const propsForGroupUpdateMessage = this.getPropsForGroupUpdateMessage(); const propsForGroupUpdateMessage = this.getPropsForGroupUpdateMessage();
const propsForTimerNotification = this.getPropsForTimerNotification(); const propsForTimerNotification = this.getPropsForTimerNotification();
const isMessageResponse = this.isMessageRequestResponse(); const isMessageResponse = this.isMessageRequestResponse();
const propsForQuote = this.getPropsForQuote();
const callNotificationType = this.get('callNotificationType'); const callNotificationType = this.get('callNotificationType');
const interactionNotification = this.getInteractionNotification(); const interactionNotification = this.getInteractionNotification();
@ -151,9 +150,6 @@ export class MessageModel extends Backbone.Model<MessageAttributes> {
if (propsForTimerNotification) { if (propsForTimerNotification) {
messageProps.propsForTimerNotification = propsForTimerNotification; messageProps.propsForTimerNotification = propsForTimerNotification;
} }
if (propsForQuote) {
messageProps.propsForQuote = propsForQuote;
}
if (callNotificationType) { if (callNotificationType) {
const propsForCallNotification: PropsForCallNotification = { const propsForCallNotification: PropsForCallNotification = {
@ -198,7 +194,9 @@ export class MessageModel extends Backbone.Model<MessageAttributes> {
} }
public isUnread() { 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() // Important to allow for this.set({ unread}), save to db, then fetch()

@ -39,7 +39,6 @@ export type MessageModelPropsWithoutConvoProps = {
propsForGroupUpdateMessage?: PropsForGroupUpdate; // plop: cleaned up propsForGroupUpdateMessage?: PropsForGroupUpdate; // plop: cleaned up
propsForCallNotification?: PropsForCallNotification; // plop: cleaned up propsForCallNotification?: PropsForCallNotification; // plop: cleaned up
propsForMessageRequestResponse?: PropsForMessageRequestResponse; // plop: cleaned up propsForMessageRequestResponse?: PropsForMessageRequestResponse; // plop: cleaned up
propsForQuote?: PropsForQuote;
propsForInteractionNotification?: PropsForInteractionNotification; // plop: cleaned up propsForInteractionNotification?: PropsForInteractionNotification; // plop: cleaned up
}; };
@ -151,7 +150,7 @@ export type PropsForMessageWithoutConvoProps = {
id: string; // messageId id: string; // messageId
direction: MessageModelType; direction: MessageModelType;
timestamp: number; 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 convoId: string; // this is the conversation in which this message was sent
text?: string; text?: string;
receivedAt?: number; receivedAt?: number;
@ -170,6 +169,11 @@ export type PropsForMessageWithoutConvoProps = {
expirationDurationMs?: number; expirationDurationMs?: number;
expirationTimestamp?: number | null; expirationTimestamp?: number | null;
isExpired?: boolean; 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; isTrustedForAttachmentDownload?: boolean;
}; };

Loading…
Cancel
Save