Mark messages read on delay when timeline becomes visible
Co-authored-by: trevor-signal <131492920+trevor-signal@users.noreply.github.com>
This commit is contained in:
parent
1f10105b22
commit
da1777924b
3 changed files with 29 additions and 5 deletions
|
@ -462,6 +462,7 @@ const useProps = (overrideProps: Partial<PropsType> = {}): PropsType => ({
|
||||||
isBlocked: false,
|
isBlocked: false,
|
||||||
isConversationSelected: true,
|
isConversationSelected: true,
|
||||||
isIncomingMessageRequest: overrideProps.isIncomingMessageRequest ?? false,
|
isIncomingMessageRequest: overrideProps.isIncomingMessageRequest ?? false,
|
||||||
|
isInFullScreenCall: false,
|
||||||
items: overrideProps.items ?? Object.keys(items),
|
items: overrideProps.items ?? Object.keys(items),
|
||||||
messageChangeCounter: 0,
|
messageChangeCounter: 0,
|
||||||
messageLoadingState: null,
|
messageLoadingState: null,
|
||||||
|
|
|
@ -40,7 +40,7 @@ import {
|
||||||
setScrollBottom,
|
setScrollBottom,
|
||||||
} from '../../util/scrollUtil';
|
} from '../../util/scrollUtil';
|
||||||
import { LastSeenIndicator } from './LastSeenIndicator';
|
import { LastSeenIndicator } from './LastSeenIndicator';
|
||||||
import { MINUTE } from '../../util/durations';
|
import { MINUTE, SECOND } from '../../util/durations';
|
||||||
import { SizeObserver } from '../../hooks/useSizeObserver';
|
import { SizeObserver } from '../../hooks/useSizeObserver';
|
||||||
import {
|
import {
|
||||||
createScrollerLock,
|
createScrollerLock,
|
||||||
|
@ -54,6 +54,8 @@ const MIN_ROW_HEIGHT = 18;
|
||||||
const SCROLL_DOWN_BUTTON_THRESHOLD = 8;
|
const SCROLL_DOWN_BUTTON_THRESHOLD = 8;
|
||||||
const LOAD_NEWER_THRESHOLD = 5;
|
const LOAD_NEWER_THRESHOLD = 5;
|
||||||
|
|
||||||
|
const DELAY_BEFORE_MARKING_READ_AFTER_FOCUS = SECOND;
|
||||||
|
|
||||||
export type WarningType = ReadonlyDeep<
|
export type WarningType = ReadonlyDeep<
|
||||||
| {
|
| {
|
||||||
type: ContactSpoofingType.DirectConversationWithSameTitle;
|
type: ContactSpoofingType.DirectConversationWithSameTitle;
|
||||||
|
@ -84,6 +86,7 @@ type PropsHousekeepingType = {
|
||||||
isBlocked: boolean;
|
isBlocked: boolean;
|
||||||
isConversationSelected: boolean;
|
isConversationSelected: boolean;
|
||||||
isGroupV1AndDisabled?: boolean;
|
isGroupV1AndDisabled?: boolean;
|
||||||
|
isInFullScreenCall: boolean;
|
||||||
isIncomingMessageRequest: boolean;
|
isIncomingMessageRequest: boolean;
|
||||||
isSomeoneTyping: boolean;
|
isSomeoneTyping: boolean;
|
||||||
unreadCount?: number;
|
unreadCount?: number;
|
||||||
|
@ -517,6 +520,17 @@ export class Timeline extends React.Component<
|
||||||
}
|
}
|
||||||
}, 500);
|
}, 500);
|
||||||
|
|
||||||
|
// When the the window becomes active, or when a fullsceen call is ended, we mark read
|
||||||
|
// with a delay, to allow users to navigate away quickly without marking messages read
|
||||||
|
#markNewestBottomVisibleMessageReadAfterDelay = throttle(
|
||||||
|
this.#markNewestBottomVisibleMessageRead,
|
||||||
|
DELAY_BEFORE_MARKING_READ_AFTER_FOCUS,
|
||||||
|
{
|
||||||
|
leading: false,
|
||||||
|
trailing: true,
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
#setupGroupCallPeekTimeouts(): void {
|
#setupGroupCallPeekTimeouts(): void {
|
||||||
this.#cleanupGroupCallPeekTimeouts();
|
this.#cleanupGroupCallPeekTimeouts();
|
||||||
|
|
||||||
|
@ -558,7 +572,7 @@ export class Timeline extends React.Component<
|
||||||
this.#updateIntersectionObserver();
|
this.#updateIntersectionObserver();
|
||||||
|
|
||||||
window.SignalContext.activeWindowService.registerForActive(
|
window.SignalContext.activeWindowService.registerForActive(
|
||||||
this.#markNewestBottomVisibleMessageRead
|
this.#markNewestBottomVisibleMessageReadAfterDelay
|
||||||
);
|
);
|
||||||
|
|
||||||
if (conversationType === 'group') {
|
if (conversationType === 'group') {
|
||||||
|
@ -568,9 +582,10 @@ export class Timeline extends React.Component<
|
||||||
|
|
||||||
public override componentWillUnmount(): void {
|
public override componentWillUnmount(): void {
|
||||||
window.SignalContext.activeWindowService.unregisterForActive(
|
window.SignalContext.activeWindowService.unregisterForActive(
|
||||||
this.#markNewestBottomVisibleMessageRead
|
this.#markNewestBottomVisibleMessageReadAfterDelay
|
||||||
);
|
);
|
||||||
|
this.#markNewestBottomVisibleMessageReadAfterDelay.cancel();
|
||||||
|
this.#markNewestBottomVisibleMessageRead.cancel();
|
||||||
this.#intersectionObserver?.disconnect();
|
this.#intersectionObserver?.disconnect();
|
||||||
this.#cleanupGroupCallPeekTimeouts();
|
this.#cleanupGroupCallPeekTimeouts();
|
||||||
this.props.updateVisibleMessages?.([]);
|
this.props.updateVisibleMessages?.([]);
|
||||||
|
@ -625,6 +640,7 @@ export class Timeline extends React.Component<
|
||||||
): void {
|
): void {
|
||||||
const {
|
const {
|
||||||
conversationType: previousConversationType,
|
conversationType: previousConversationType,
|
||||||
|
isInFullScreenCall: previousIsInFullScreenCall,
|
||||||
items: oldItems,
|
items: oldItems,
|
||||||
messageChangeCounter: previousMessageChangeCounter,
|
messageChangeCounter: previousMessageChangeCounter,
|
||||||
messageLoadingState: previousMessageLoadingState,
|
messageLoadingState: previousMessageLoadingState,
|
||||||
|
@ -633,6 +649,7 @@ export class Timeline extends React.Component<
|
||||||
conversationType,
|
conversationType,
|
||||||
discardMessages,
|
discardMessages,
|
||||||
id,
|
id,
|
||||||
|
isInFullScreenCall,
|
||||||
items: newItems,
|
items: newItems,
|
||||||
messageChangeCounter,
|
messageChangeCounter,
|
||||||
messageLoadingState,
|
messageLoadingState,
|
||||||
|
@ -705,6 +722,10 @@ export class Timeline extends React.Component<
|
||||||
this.#markNewestBottomVisibleMessageRead();
|
this.#markNewestBottomVisibleMessageRead();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (previousIsInFullScreenCall && !isInFullScreenCall) {
|
||||||
|
this.#markNewestBottomVisibleMessageReadAfterDelay();
|
||||||
|
}
|
||||||
|
|
||||||
if (previousConversationType !== conversationType) {
|
if (previousConversationType !== conversationType) {
|
||||||
this.#cleanupGroupCallPeekTimeouts();
|
this.#cleanupGroupCallPeekTimeouts();
|
||||||
if (conversationType === 'group') {
|
if (conversationType === 'group') {
|
||||||
|
|
|
@ -43,6 +43,7 @@ import { SmartMiniPlayer } from './MiniPlayer';
|
||||||
import { SmartTimelineItem, type SmartTimelineItemProps } from './TimelineItem';
|
import { SmartTimelineItem, type SmartTimelineItemProps } from './TimelineItem';
|
||||||
import { SmartTypingBubble } from './TypingBubble';
|
import { SmartTypingBubble } from './TypingBubble';
|
||||||
import { AttachmentDownloadManager } from '../../jobs/AttachmentDownloadManager';
|
import { AttachmentDownloadManager } from '../../jobs/AttachmentDownloadManager';
|
||||||
|
import { isInFullScreenCall as getIsInFullScreenCall } from '../selectors/calling';
|
||||||
|
|
||||||
type ExternalProps = {
|
type ExternalProps = {
|
||||||
id: string;
|
id: string;
|
||||||
|
@ -166,7 +167,7 @@ export const SmartTimeline = memo(function SmartTimeline({
|
||||||
const selectedConversationId = useSelector(getSelectedConversationId);
|
const selectedConversationId = useSelector(getSelectedConversationId);
|
||||||
const targetedMessage = useSelector(getTargetedMessage);
|
const targetedMessage = useSelector(getTargetedMessage);
|
||||||
const theme = useSelector(getTheme);
|
const theme = useSelector(getTheme);
|
||||||
|
const isInFullScreenCall = useSelector(getIsInFullScreenCall);
|
||||||
const conversation = conversationSelector(id);
|
const conversation = conversationSelector(id);
|
||||||
const conversationMessages = conversationMessagesSelector(id);
|
const conversationMessages = conversationMessagesSelector(id);
|
||||||
|
|
||||||
|
@ -257,6 +258,7 @@ export const SmartTimeline = memo(function SmartTimeline({
|
||||||
isBlocked={isBlocked}
|
isBlocked={isBlocked}
|
||||||
isConversationSelected={isConversationSelected}
|
isConversationSelected={isConversationSelected}
|
||||||
isGroupV1AndDisabled={isGroupV1AndDisabled}
|
isGroupV1AndDisabled={isGroupV1AndDisabled}
|
||||||
|
isInFullScreenCall={isInFullScreenCall}
|
||||||
isIncomingMessageRequest={isIncomingMessageRequest}
|
isIncomingMessageRequest={isIncomingMessageRequest}
|
||||||
isNearBottom={isNearBottom}
|
isNearBottom={isNearBottom}
|
||||||
isSomeoneTyping={isSomeoneTyping}
|
isSomeoneTyping={isSomeoneTyping}
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue