Comments: Fix thread bugs (#6698)
This commit is contained in:
parent
fec5e07b7d
commit
fc498a222b
@ -2,7 +2,9 @@ import { Api as GramJs } from '../../../lib/gramjs';
|
||||
|
||||
import type {
|
||||
ApiAttachment,
|
||||
ApiBaseThreadInfo,
|
||||
ApiChat,
|
||||
ApiCommentsInfo,
|
||||
ApiContact,
|
||||
ApiDice,
|
||||
ApiDraft,
|
||||
@ -15,6 +17,7 @@ import type {
|
||||
ApiMessageEntity,
|
||||
ApiMessageForwardInfo,
|
||||
ApiMessageReportResult,
|
||||
ApiMessageThreadInfo,
|
||||
ApiNewMediaTodo,
|
||||
ApiNewPoll,
|
||||
ApiPeer,
|
||||
@ -759,44 +762,51 @@ export function buildApiThreadInfoFromMessage(
|
||||
return undefined;
|
||||
}
|
||||
|
||||
return buildApiThreadInfo(mtpMessage.replies, mtpMessage.id, chatId);
|
||||
return buildApiThreadInfo(chatId, mtpMessage.id, mtpMessage.replies, mtpMessage.fwdFrom);
|
||||
}
|
||||
|
||||
export function buildApiThreadInfo(
|
||||
messageReplies: GramJs.TypeMessageReplies, messageId: number, chatId: string,
|
||||
chatId: string,
|
||||
messageId: number,
|
||||
messageReplies: GramJs.TypeMessageReplies,
|
||||
messageForwardInfo?: GramJs.MessageFwdHeader,
|
||||
): ApiThreadInfo | undefined {
|
||||
const {
|
||||
channelId, replies, maxId, readMaxId, recentRepliers, comments,
|
||||
channelId, replies, maxId, recentRepliers, comments, readMaxId,
|
||||
} = messageReplies;
|
||||
|
||||
const { fromId, channelPost } = messageForwardInfo || {};
|
||||
|
||||
const apiChannelId = channelId ? buildApiPeerId(channelId, 'channel') : undefined;
|
||||
if (apiChannelId === DELETED_COMMENTS_CHANNEL_ID) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
const baseThreadInfo = {
|
||||
const baseThreadInfo: Partial<ApiBaseThreadInfo> = {
|
||||
messagesCount: replies,
|
||||
...(maxId && { lastMessageId: maxId }),
|
||||
...(readMaxId && { lastReadMessageId: readMaxId }),
|
||||
...(recentRepliers && { recentReplierIds: recentRepliers.map(getApiChatIdFromMtpPeer) }),
|
||||
lastMessageId: maxId,
|
||||
recentReplierIds: recentRepliers?.map(getApiChatIdFromMtpPeer),
|
||||
};
|
||||
|
||||
if (comments) {
|
||||
return {
|
||||
return omitUndefined<ApiCommentsInfo>({
|
||||
...baseThreadInfo,
|
||||
isCommentsInfo: true,
|
||||
chatId: apiChannelId!,
|
||||
originChannelId: chatId,
|
||||
originMessageId: messageId,
|
||||
};
|
||||
hasUnread: Boolean(readMaxId && maxId && readMaxId < maxId),
|
||||
});
|
||||
}
|
||||
|
||||
return {
|
||||
return omitUndefined<ApiMessageThreadInfo>({
|
||||
...baseThreadInfo,
|
||||
isCommentsInfo: false,
|
||||
chatId,
|
||||
threadId: messageId,
|
||||
};
|
||||
fromChannelId: fromId && channelPost ? getApiChatIdFromMtpPeer(fromId) : undefined,
|
||||
fromMessageId: channelPost,
|
||||
});
|
||||
}
|
||||
|
||||
export function buildApiQuickReply(reply: GramJs.TypeQuickReply): ApiQuickReply {
|
||||
|
||||
@ -74,6 +74,7 @@ import {
|
||||
buildApiSearchPostsFlood,
|
||||
buildApiSponsoredMessage,
|
||||
buildApiThreadInfo,
|
||||
buildApiThreadInfoFromMessage,
|
||||
buildLocalForwardedMessage,
|
||||
buildLocalMessage,
|
||||
buildPreparedInlineMessage,
|
||||
@ -1383,7 +1384,7 @@ export async function fetchMessageViews({
|
||||
id,
|
||||
views,
|
||||
forwards,
|
||||
threadInfo: replies ? buildApiThreadInfo(replies, id, chat.id) : undefined,
|
||||
threadInfo: replies ? buildApiThreadInfo(chat.id, id, replies) : undefined,
|
||||
};
|
||||
});
|
||||
|
||||
@ -1456,16 +1457,23 @@ export async function fetchDiscussionMessage({
|
||||
const messages = topMessages.concat(replies.messages);
|
||||
const threadId = result.messages[result.messages.length - 1]?.id;
|
||||
|
||||
if (!threadId) return undefined;
|
||||
const chatId = topMessages[0]?.chatId;
|
||||
if (!chatId || !threadId) return undefined;
|
||||
|
||||
const { maxId } = result;
|
||||
const threadReadState = buildThreadReadState(result);
|
||||
|
||||
const topMessageWithReplies = result.messages.find((message): message is GramJs.Message => (
|
||||
message instanceof GramJs.Message && Boolean(message.replies)
|
||||
))!;
|
||||
const threadInfo = buildApiThreadInfoFromMessage(topMessageWithReplies);
|
||||
|
||||
return {
|
||||
messages,
|
||||
topMessages,
|
||||
threadId,
|
||||
threadReadState,
|
||||
threadInfo,
|
||||
lastMessageId: maxId,
|
||||
chatId: topMessages[0]?.chatId,
|
||||
firstMessageId: replies.messages[0]?.id,
|
||||
|
||||
@ -826,6 +826,7 @@ export interface ApiCommentsInfo extends ApiBaseThreadInfo {
|
||||
threadId?: never;
|
||||
originChannelId: string;
|
||||
originMessageId: number;
|
||||
hasUnread?: boolean;
|
||||
}
|
||||
|
||||
export interface ApiMessageThreadInfo extends ApiBaseThreadInfo {
|
||||
|
||||
@ -239,7 +239,7 @@ const MiddleHeader: FC<OwnProps & StateProps> = ({
|
||||
|
||||
return (
|
||||
<>
|
||||
{renderBackButton()}
|
||||
{renderBackButton(currentTransitionKey === 0)}
|
||||
<h3>
|
||||
{messagesCount !== undefined ? (
|
||||
messageListType === 'thread' ? (
|
||||
|
||||
@ -242,7 +242,7 @@
|
||||
|
||||
width: 0.5rem;
|
||||
height: 0.5rem;
|
||||
margin-inline-start: 0.75rem;
|
||||
margin-inline-start: 0.5rem;
|
||||
border-radius: 50%;
|
||||
|
||||
background: var(--accent-color);
|
||||
|
||||
@ -2,7 +2,6 @@ import { memo, useMemo } from '../../../lib/teact/teact';
|
||||
import { getActions, getGlobal } from '../../../global';
|
||||
|
||||
import type { ApiCommentsInfo } from '../../../api/types';
|
||||
import type { ThreadReadState } from '../../../types';
|
||||
|
||||
import { selectIsCurrentUserFrozen, selectPeer } from '../../../global/selectors';
|
||||
import buildClassName from '../../../util/buildClassName';
|
||||
@ -22,7 +21,6 @@ import './CommentButton.scss';
|
||||
|
||||
type OwnProps = {
|
||||
threadInfo?: ApiCommentsInfo;
|
||||
threadReadState?: ThreadReadState;
|
||||
disabled?: boolean;
|
||||
isLoading?: boolean;
|
||||
isCustomShape?: boolean;
|
||||
@ -33,7 +31,6 @@ const SHOW_LOADER_DELAY = 450;
|
||||
const CommentButton = ({
|
||||
isCustomShape,
|
||||
threadInfo,
|
||||
threadReadState,
|
||||
disabled,
|
||||
isLoading,
|
||||
}: OwnProps) => {
|
||||
@ -44,9 +41,8 @@ const CommentButton = ({
|
||||
const oldLang = useOldLang();
|
||||
const lang = useLang();
|
||||
const {
|
||||
originMessageId, chatId, messagesCount, lastMessageId, recentReplierIds, originChannelId,
|
||||
originMessageId, chatId, messagesCount, recentReplierIds, originChannelId, hasUnread,
|
||||
} = threadInfo || {};
|
||||
const { lastReadInboxMessageId } = threadReadState || {};
|
||||
|
||||
const handleClick = useLastCallback(() => {
|
||||
const global = getGlobal();
|
||||
@ -93,8 +89,6 @@ const CommentButton = ({
|
||||
);
|
||||
}
|
||||
|
||||
const hasUnread = Boolean(lastReadInboxMessageId && lastMessageId && lastReadInboxMessageId < lastMessageId);
|
||||
|
||||
const commentsText = messagesCount ? (oldLang('CommentsCount', '%COMMENTS_COUNT%', undefined, messagesCount))
|
||||
.split('%')
|
||||
.map((s) => {
|
||||
|
||||
@ -39,7 +39,6 @@ import type {
|
||||
TextSummary,
|
||||
ThemeKey,
|
||||
ThreadId,
|
||||
ThreadReadState,
|
||||
} from '../../../types';
|
||||
import type { Signal } from '../../../util/signals';
|
||||
import { MAIN_THREAD_ID } from '../../../api/types';
|
||||
@ -125,7 +124,7 @@ import {
|
||||
selectMessageTimestampableDuration,
|
||||
} from '../../../global/selectors/media';
|
||||
import { selectSharedSettings } from '../../../global/selectors/sharedState';
|
||||
import { selectThread, selectThreadReadState } from '../../../global/selectors/threads';
|
||||
import { selectThreadInfo, selectThreadReadState } from '../../../global/selectors/threads';
|
||||
import { IS_TAURI } from '../../../util/browser/globalEnvironment';
|
||||
import { IS_ANDROID, IS_TRANSLATION_SUPPORTED } from '../../../util/browser/windowEnvironment';
|
||||
import buildClassName from '../../../util/buildClassName';
|
||||
@ -295,7 +294,6 @@ type StateProps = {
|
||||
shouldLoopStickers?: boolean;
|
||||
autoLoadFileMaxSizeMb: number;
|
||||
repliesThreadInfo?: ApiThreadInfo;
|
||||
repliesReadState?: ThreadReadState;
|
||||
reactionMessage?: ApiMessage;
|
||||
availableReactions?: ApiAvailableReaction[];
|
||||
defaultReaction?: ApiReaction;
|
||||
@ -429,7 +427,6 @@ const Message = ({
|
||||
shouldLoopStickers,
|
||||
autoLoadFileMaxSizeMb,
|
||||
repliesThreadInfo,
|
||||
repliesReadState,
|
||||
hasUnreadReaction,
|
||||
memoFirstUnreadIdRef,
|
||||
senderAdminMember,
|
||||
@ -842,7 +839,6 @@ const Message = ({
|
||||
const phoneCall = action?.type === 'phoneCall' ? action : undefined;
|
||||
|
||||
const commentsThreadInfo = repliesThreadInfo?.isCommentsInfo ? repliesThreadInfo : undefined;
|
||||
const commentsReadState = repliesThreadInfo?.isCommentsInfo ? repliesReadState : undefined;
|
||||
const isLocalWithCommentButton = hasLinkedChat && isChannel && isLocal;
|
||||
|
||||
const isMediaWithCommentButton = (commentsThreadInfo || isLocalWithCommentButton)
|
||||
@ -1895,7 +1891,6 @@ const Message = ({
|
||||
{withCommentButton && isCustomShape && (
|
||||
<CommentButton
|
||||
threadInfo={commentsThreadInfo}
|
||||
threadReadState={commentsReadState}
|
||||
disabled={noComments || !commentsThreadInfo}
|
||||
isLoading={isLoadingComments}
|
||||
isCustomShape
|
||||
@ -1927,7 +1922,6 @@ const Message = ({
|
||||
{withCommentButton && !isCustomShape && (
|
||||
<CommentButton
|
||||
threadInfo={commentsThreadInfo}
|
||||
threadReadState={commentsReadState}
|
||||
disabled={noComments || !commentsThreadInfo}
|
||||
isLoading={isLoadingComments}
|
||||
/>
|
||||
@ -2095,8 +2089,7 @@ export default memo(withGlobal<OwnProps>(
|
||||
const downloadableMedia = selectMessageDownloadableMedia(global, message);
|
||||
const isDownloading = downloadableMedia && getIsDownloading(activeDownloads, downloadableMedia);
|
||||
|
||||
const repliesThread = selectThread(global, chatId, album?.commentsMessage?.id || id);
|
||||
const { threadInfo: repliesThreadInfo, readState: repliesReadState } = repliesThread || {};
|
||||
const repliesThreadInfo = selectThreadInfo(global, chatId, album?.commentsMessage?.id || id);
|
||||
|
||||
const isInDocumentGroup = Boolean(message.groupedId) && !message.isInAlbum;
|
||||
const documentGroupFirstMessageId = isInDocumentGroup
|
||||
@ -2200,7 +2193,6 @@ export default memo(withGlobal<OwnProps>(
|
||||
autoLoadFileMaxSizeMb: global.settings.byKey.autoLoadFileMaxSizeMb,
|
||||
shouldLoopStickers: selectShouldLoopStickers(global),
|
||||
repliesThreadInfo,
|
||||
repliesReadState,
|
||||
availableReactions: global.reactions.availableReactions,
|
||||
defaultReaction: isMessageLocal(message) || messageListType === 'scheduled'
|
||||
? undefined : selectDefaultReaction(global, chatId),
|
||||
|
||||
@ -422,22 +422,9 @@ addActionHandler('openThread', async (global, actions, payload): Promise<void> =
|
||||
|
||||
global = getGlobal();
|
||||
global = addMessages(global, result.messages);
|
||||
global = updateThreadInfo(global, result.threadInfo);
|
||||
global = updateThreadReadState(global, chatId, result.threadId, result.threadReadState);
|
||||
global = updateThreadInfoLastMessageId(global, chatId, result.threadId, result.lastMessageId);
|
||||
|
||||
if (isComments) {
|
||||
const lastMessageId = threadInfo?.lastMessageId !== undefined ? threadInfo.lastMessageId
|
||||
: threadInfo?.messagesCount === 0 ? result.threadId : undefined;
|
||||
|
||||
global = updateThreadInfo(global, {
|
||||
isCommentsInfo: false,
|
||||
threadId,
|
||||
chatId,
|
||||
fromChannelId: loadingChatId,
|
||||
fromMessageId: loadingThreadId,
|
||||
lastMessageId,
|
||||
});
|
||||
}
|
||||
global = replaceThreadLocalStateParam(global, chatId, threadId, 'firstMessageId', result.firstMessageId);
|
||||
setGlobal(global);
|
||||
|
||||
|
||||
@ -247,6 +247,13 @@ addActionHandler('loadViewportMessages', (global, actions, payload): ActionRetur
|
||||
// Prevent unnecessary requests in threads
|
||||
if (offsetId === threadId && direction === LoadMoreDirection.Backwards) return;
|
||||
|
||||
if (direction === LoadMoreDirection.Forwards && offsetId) {
|
||||
const threadInfo = selectThreadInfo(global, chatId, threadId);
|
||||
if (threadInfo?.lastMessageId && offsetId >= threadInfo.lastMessageId) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
const isOutlying = Boolean(listedIds && offsetId && !listedIds.includes(offsetId));
|
||||
const historyIds = (isOutlying
|
||||
? selectOutlyingListByMessageId(global, chatId, threadId, offsetId!) : listedIds)!;
|
||||
|
||||
@ -23,7 +23,12 @@ import {
|
||||
updateUsers,
|
||||
} from '../../reducers';
|
||||
import { updateTabState } from '../../reducers/tabs';
|
||||
import { updateThreadInfo, updateThreadLocalState } from '../../reducers/threads';
|
||||
import {
|
||||
replaceThreadLocalStateParam,
|
||||
updateThreadInfo,
|
||||
updateThreadLocalState,
|
||||
updateThreadReadState,
|
||||
} from '../../reducers/threads';
|
||||
import {
|
||||
selectChat,
|
||||
selectChatMessage,
|
||||
@ -179,6 +184,14 @@ async function loadAndReplaceMessages<T extends GlobalState>(global: T, actions:
|
||||
}
|
||||
|
||||
global = addChatMessagesById(global, currentChatId, byId);
|
||||
if (resultDiscussion) {
|
||||
global = updateThreadInfo(global, resultDiscussion.threadInfo);
|
||||
global = updateThreadReadState(global, currentChatId, activeThreadId, resultDiscussion.threadReadState);
|
||||
global = replaceThreadLocalStateParam(
|
||||
global, currentChatId, activeThreadId, 'firstMessageId', resultDiscussion.firstMessageId,
|
||||
);
|
||||
global = addChatMessagesById(global, currentChatId, buildCollectionByKey(resultDiscussion.topMessages, 'id'));
|
||||
}
|
||||
global = updateListedIds(global, currentChatId, activeThreadId, listedIds);
|
||||
|
||||
Object.entries(messagesThreads).forEach(([id, thread]) => {
|
||||
|
||||
@ -41,7 +41,7 @@ import {
|
||||
updateFocusedMessage,
|
||||
} from '../../reducers';
|
||||
import { updateTabState } from '../../reducers/tabs';
|
||||
import { replaceTabThreadParam, replaceThreadLocalStateParam } from '../../reducers/threads';
|
||||
import { replaceTabThreadParam, replaceThreadLocalStateParam, updateThreadReadState } from '../../reducers/threads';
|
||||
import {
|
||||
selectAllowedMessageActionsSlow,
|
||||
selectCanForwardMessage,
|
||||
@ -495,6 +495,7 @@ addActionHandler('scrollMessageListToBottom', (global, actions, payload): Action
|
||||
blurTimeout = window.setTimeout(() => {
|
||||
global = getGlobal();
|
||||
global = updateFocusedMessage(global, undefined, tabId);
|
||||
global = updateThreadReadState(global, chatId, threadId, { unreadCount: 0 });
|
||||
setGlobal(global);
|
||||
}, FOCUS_NO_HIGHLIGHT_DURATION);
|
||||
|
||||
|
||||
@ -140,7 +140,7 @@ export function updateLinkedThreadInfo<T extends GlobalState>(
|
||||
return global;
|
||||
}
|
||||
|
||||
const valuesToUpdate = pick(update, ['messagesCount', 'lastMessageId', 'recentReplierIds']);
|
||||
const valuesToUpdate = pick(update, ['messagesCount', 'lastMessageId']);
|
||||
const newThreadInfo: ApiThreadInfo = {
|
||||
...threadInfo,
|
||||
...valuesToUpdate,
|
||||
|
||||
@ -783,6 +783,11 @@ export function selectRealLastReadId<T extends GlobalState>(global: T, chatId: s
|
||||
|
||||
// `lastReadInboxMessageId` is empty for new chats
|
||||
if (!readState.lastReadInboxMessageId) {
|
||||
// For new comments, mark thread start as the last read
|
||||
if (threadId !== MAIN_THREAD_ID && readState.unreadCount && typeof threadId === 'number') {
|
||||
return threadId;
|
||||
}
|
||||
|
||||
return undefined;
|
||||
}
|
||||
|
||||
|
||||
@ -11,7 +11,7 @@ import {
|
||||
DEBUG, STRICTERDOM_ENABLED,
|
||||
} from './config';
|
||||
import { enableStrict, requestMutation } from './lib/fasterdom/fasterdom';
|
||||
import { selectTabState } from './global/selectors';
|
||||
import { selectChat, selectChatFullInfo, selectCurrentMessageList, selectTabState } from './global/selectors';
|
||||
import { selectSharedSettings } from './global/selectors/sharedState';
|
||||
import { betterView } from './util/betterView';
|
||||
import { IS_TAURI } from './util/browser/globalEnvironment';
|
||||
@ -104,10 +104,21 @@ async function init() {
|
||||
|
||||
if (DEBUG) {
|
||||
document.addEventListener('dblclick', () => {
|
||||
const currentGlobal = getGlobal();
|
||||
const currentMessageList = selectCurrentMessageList(currentGlobal);
|
||||
// eslint-disable-next-line no-console
|
||||
console.warn('TAB STATE', selectTabState(getGlobal()));
|
||||
console.warn('TAB STATE', selectTabState(currentGlobal));
|
||||
// eslint-disable-next-line no-console
|
||||
console.warn('GLOBAL STATE', getGlobal());
|
||||
console.warn('GLOBAL STATE', currentGlobal);
|
||||
if (currentMessageList) {
|
||||
// eslint-disable-next-line no-console
|
||||
console.warn(
|
||||
'CURRENT MESSAGE LIST',
|
||||
selectChat(currentGlobal, currentMessageList.chatId),
|
||||
selectChatFullInfo(currentGlobal, currentMessageList.chatId),
|
||||
currentGlobal.messages.byChatId[currentMessageList.chatId],
|
||||
);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user