Message List: Various fixes for sending messages in empty chat
This commit is contained in:
parent
f4fad13266
commit
5a444dbb50
@ -4,12 +4,12 @@ import React, {
|
|||||||
import { getGlobal, withGlobal } from '../../lib/teact/teactn';
|
import { getGlobal, withGlobal } from '../../lib/teact/teactn';
|
||||||
|
|
||||||
import {
|
import {
|
||||||
ApiAction, ApiMessage, ApiRestrictionReason, MAIN_THREAD_ID,
|
ApiMessage, ApiRestrictionReason, MAIN_THREAD_ID,
|
||||||
} from '../../api/types';
|
} from '../../api/types';
|
||||||
import { GlobalActions, MessageListType } from '../../global/types';
|
import { GlobalActions, MessageListType } from '../../global/types';
|
||||||
import { LoadMoreDirection } from '../../types';
|
import { LoadMoreDirection } from '../../types';
|
||||||
|
|
||||||
import { ANIMATION_END_DELAY, MESSAGE_LIST_SLICE } from '../../config';
|
import { ANIMATION_END_DELAY, LOCAL_MESSAGE_ID_BASE, MESSAGE_LIST_SLICE } from '../../config';
|
||||||
import {
|
import {
|
||||||
selectChatMessages,
|
selectChatMessages,
|
||||||
selectIsViewportNewest,
|
selectIsViewportNewest,
|
||||||
@ -32,7 +32,7 @@ import { fastRaf, debounce, onTickEnd } from '../../util/schedulers';
|
|||||||
import useLayoutEffectWithPrevDeps from '../../hooks/useLayoutEffectWithPrevDeps';
|
import useLayoutEffectWithPrevDeps from '../../hooks/useLayoutEffectWithPrevDeps';
|
||||||
import useEffectWithPrevDeps from '../../hooks/useEffectWithPrevDeps';
|
import useEffectWithPrevDeps from '../../hooks/useEffectWithPrevDeps';
|
||||||
import buildClassName from '../../util/buildClassName';
|
import buildClassName from '../../util/buildClassName';
|
||||||
import { groupMessages, MessageDateGroup } from './helpers/groupMessages';
|
import { groupMessages } from './helpers/groupMessages';
|
||||||
import { preventMessageInputBlur } from './helpers/preventMessageInputBlur';
|
import { preventMessageInputBlur } from './helpers/preventMessageInputBlur';
|
||||||
import useOnChange from '../../hooks/useOnChange';
|
import useOnChange from '../../hooks/useOnChange';
|
||||||
import useStickyDates from './hooks/useStickyDates';
|
import useStickyDates from './hooks/useStickyDates';
|
||||||
@ -259,7 +259,7 @@ const MessageList: FC<OwnProps & StateProps & DispatchProps> = ({
|
|||||||
if (isReady) {
|
if (isReady) {
|
||||||
containerRef.current!.dataset.normalHeight = String(containerRef.current!.offsetHeight);
|
containerRef.current!.dataset.normalHeight = String(containerRef.current!.offsetHeight);
|
||||||
}
|
}
|
||||||
}, [windowHeight, isReady]);
|
}, [windowHeight, isReady, canPost]);
|
||||||
|
|
||||||
// Initial message loading
|
// Initial message loading
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
@ -267,6 +267,12 @@ const MessageList: FC<OwnProps & StateProps & DispatchProps> = ({
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Loading history while sending a message can return the same message and cause ambiguity
|
||||||
|
const isFirstMessageLocal = messageIds && messageIds[0] >= LOCAL_MESSAGE_ID_BASE;
|
||||||
|
if (isFirstMessageLocal) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
const container = containerRef.current!;
|
const container = containerRef.current!;
|
||||||
|
|
||||||
if (!messageIds || (
|
if (!messageIds || (
|
||||||
@ -331,11 +337,13 @@ const MessageList: FC<OwnProps & StateProps & DispatchProps> = ({
|
|||||||
}, FOCUSING_DURATION);
|
}, FOCUSING_DURATION);
|
||||||
}
|
}
|
||||||
|
|
||||||
const hasFirstMessageChanged = messageIds && prevMessageIds && messageIds[0] !== prevMessageIds[0];
|
|
||||||
const hasLastMessageChanged = (
|
const hasLastMessageChanged = (
|
||||||
messageIds && prevMessageIds && messageIds[messageIds.length - 1] !== prevMessageIds[prevMessageIds.length - 1]
|
messageIds && prevMessageIds && messageIds[messageIds.length - 1] !== prevMessageIds[prevMessageIds.length - 1]
|
||||||
);
|
);
|
||||||
const wasMessageAdded = hasLastMessageChanged && !hasFirstMessageChanged;
|
const hasViewportShifted = (
|
||||||
|
messageIds?.[0] !== prevMessageIds?.[0] && messageIds?.length === (MESSAGE_LIST_SLICE / 2 + 1)
|
||||||
|
);
|
||||||
|
const wasMessageAdded = hasLastMessageChanged && !hasViewportShifted;
|
||||||
const isAlreadyFocusing = messageIds && memoFocusingIdRef.current === messageIds[messageIds.length - 1];
|
const isAlreadyFocusing = messageIds && memoFocusingIdRef.current === messageIds[messageIds.length - 1];
|
||||||
|
|
||||||
const { scrollTop, scrollHeight, offsetHeight } = container;
|
const { scrollTop, scrollHeight, offsetHeight } = container;
|
||||||
@ -442,11 +450,12 @@ const MessageList: FC<OwnProps & StateProps & DispatchProps> = ({
|
|||||||
// Used to avoid flickering when deleting a greeting that has just been sent
|
// Used to avoid flickering when deleting a greeting that has just been sent
|
||||||
&& (!listItemElementsRef.current || listItemElementsRef.current.length === 0)
|
&& (!listItemElementsRef.current || listItemElementsRef.current.length === 0)
|
||||||
)
|
)
|
||||||
|| checkSingleMessageActionByType('contactSignUp', messageGroups)
|
|| (messageIds?.length === 1 && messagesById?.[messageIds[0]]?.content.action?.type === 'contactSignUp')
|
||||||
|| (lastMessage?.content.action && lastMessage.content.action.type === 'contactSignUp')
|
|| (lastMessage?.content?.action?.type === 'contactSignUp')
|
||||||
);
|
);
|
||||||
|
|
||||||
const isGroupChatJustCreated = isGroupChat && isCreator
|
const isGroupChatJustCreated = isGroupChat && isCreator
|
||||||
&& checkSingleMessageActionByType('chatCreate', messageGroups);
|
&& messageIds?.length === 1 && messagesById?.[messageIds[0]]?.content.action?.type === 'chatCreate';
|
||||||
|
|
||||||
const className = buildClassName(
|
const className = buildClassName(
|
||||||
'MessageList custom-scroll',
|
'MessageList custom-scroll',
|
||||||
@ -512,16 +521,6 @@ const MessageList: FC<OwnProps & StateProps & DispatchProps> = ({
|
|||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
function checkSingleMessageActionByType(type: ApiAction['type'], messageGroups?: MessageDateGroup[]) {
|
|
||||||
return messageGroups
|
|
||||||
&& messageGroups.length === 1
|
|
||||||
&& messageGroups[0].senderGroups.length === 1
|
|
||||||
&& messageGroups[0].senderGroups[0].length === 1
|
|
||||||
&& 'content' in messageGroups[0].senderGroups[0][0]
|
|
||||||
&& messageGroups[0].senderGroups[0][0].content.action
|
|
||||||
&& messageGroups[0].senderGroups[0][0].content.action.type === type;
|
|
||||||
}
|
|
||||||
|
|
||||||
export default memo(withGlobal<OwnProps>(
|
export default memo(withGlobal<OwnProps>(
|
||||||
(global, { chatId, threadId, type }): StateProps => {
|
(global, { chatId, threadId, type }): StateProps => {
|
||||||
const chat = selectChat(global, chatId);
|
const chat = selectChat(global, chatId);
|
||||||
|
|||||||
@ -1,13 +1,13 @@
|
|||||||
import { RefObject } from 'react';
|
import { RefObject } from 'react';
|
||||||
import { getDispatch } from '../../../lib/teact/teactn';
|
import { getDispatch } from '../../../lib/teact/teactn';
|
||||||
import { useCallback, useMemo, useRef } from '../../../lib/teact/teact';
|
import { useMemo, useRef } from '../../../lib/teact/teact';
|
||||||
|
|
||||||
import { LoadMoreDirection } from '../../../types';
|
import { LoadMoreDirection } from '../../../types';
|
||||||
import { MessageListType } from '../../../global/types';
|
import { MessageListType } from '../../../global/types';
|
||||||
|
|
||||||
import { debounce } from '../../../util/schedulers';
|
import { debounce } from '../../../util/schedulers';
|
||||||
import { useIntersectionObserver, useOnIntersect } from '../../../hooks/useIntersectionObserver';
|
import { useIntersectionObserver, useOnIntersect } from '../../../hooks/useIntersectionObserver';
|
||||||
import { MESSAGE_LIST_SENSITIVE_AREA } from '../../../config';
|
import { LOCAL_MESSAGE_ID_BASE, MESSAGE_LIST_SENSITIVE_AREA } from '../../../config';
|
||||||
import resetScroll from '../../../util/resetScroll';
|
import resetScroll from '../../../util/resetScroll';
|
||||||
import useOnChange from '../../../hooks/useOnChange';
|
import useOnChange from '../../../hooks/useOnChange';
|
||||||
|
|
||||||
@ -42,7 +42,7 @@ export default function useScrollHooks(
|
|||||||
// eslint-disable-next-line no-null/no-null
|
// eslint-disable-next-line no-null/no-null
|
||||||
const fabTriggerRef = useRef<HTMLDivElement>(null);
|
const fabTriggerRef = useRef<HTMLDivElement>(null);
|
||||||
|
|
||||||
const toggleScrollTools = useCallback(() => {
|
function toggleScrollTools() {
|
||||||
if (!isActive) return;
|
if (!isActive) return;
|
||||||
|
|
||||||
if (!messageIds || !messageIds.length) {
|
if (!messageIds || !messageIds.length) {
|
||||||
@ -64,7 +64,7 @@ export default function useScrollHooks(
|
|||||||
|
|
||||||
onFabToggle(isUnread ? !isAtBottom : !isNearBottom);
|
onFabToggle(isUnread ? !isAtBottom : !isNearBottom);
|
||||||
onNotchToggle(!isAtBottom);
|
onNotchToggle(!isAtBottom);
|
||||||
}, [isActive, messageIds, isViewportNewest, containerRef, onFabToggle, isUnread, onNotchToggle]);
|
}
|
||||||
|
|
||||||
const {
|
const {
|
||||||
observe: observeIntersection,
|
observe: observeIntersection,
|
||||||
@ -76,6 +76,12 @@ export default function useScrollHooks(
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Loading history while sending a message can return the same message and cause ambiguity
|
||||||
|
const isFirstMessageLocal = messageIds[0] >= LOCAL_MESSAGE_ID_BASE;
|
||||||
|
if (isFirstMessageLocal) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
const triggerEntry = entries.find(({ isIntersecting }) => isIntersecting);
|
const triggerEntry = entries.find(({ isIntersecting }) => isIntersecting);
|
||||||
if (!triggerEntry) {
|
if (!triggerEntry) {
|
||||||
return;
|
return;
|
||||||
|
|||||||
@ -671,6 +671,7 @@ async function loadViewportMessages(
|
|||||||
global = isOutlying
|
global = isOutlying
|
||||||
? updateOutlyingIds(global, chatId, threadId, ids)
|
? updateOutlyingIds(global, chatId, threadId, ids)
|
||||||
: updateListedIds(global, chatId, threadId, ids);
|
: updateListedIds(global, chatId, threadId, ids);
|
||||||
|
|
||||||
global = addUsers(global, buildCollectionByKey(users, 'id'));
|
global = addUsers(global, buildCollectionByKey(users, 'id'));
|
||||||
global = addChats(global, buildCollectionByKey(chats, 'id'));
|
global = addChats(global, buildCollectionByKey(chats, 'id'));
|
||||||
global = updateThreadInfos(global, chatId, threadInfos);
|
global = updateThreadInfos(global, chatId, threadInfos);
|
||||||
@ -679,7 +680,7 @@ async function loadViewportMessages(
|
|||||||
const outlyingIds = selectOutlyingIds(global, chatId, threadId);
|
const outlyingIds = selectOutlyingIds(global, chatId, threadId);
|
||||||
|
|
||||||
if (isOutlying && listedIds && outlyingIds) {
|
if (isOutlying && listedIds && outlyingIds) {
|
||||||
if (areSortedArraysIntersecting(listedIds, outlyingIds)) {
|
if (!outlyingIds.length || areSortedArraysIntersecting(listedIds, outlyingIds)) {
|
||||||
global = updateListedIds(global, chatId, threadId, outlyingIds);
|
global = updateListedIds(global, chatId, threadId, outlyingIds);
|
||||||
listedIds = selectListedIds(global, chatId, threadId);
|
listedIds = selectListedIds(global, chatId, threadId);
|
||||||
global = replaceThreadParam(global, chatId, threadId, 'outlyingIds', undefined);
|
global = replaceThreadParam(global, chatId, threadId, 'outlyingIds', undefined);
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user