Forum: Fix empty topic preview after reload (#4981)
This commit is contained in:
parent
73d9fe9756
commit
ba762fd0ad
@ -1,4 +1,3 @@
|
||||
import type { ThreadId } from '../../types';
|
||||
import type { ApiBotCommand } from './bots';
|
||||
import type {
|
||||
ApiChatReactions, ApiFormattedText, ApiPhoto, ApiStickerSet,
|
||||
@ -48,10 +47,6 @@ export interface ApiChat {
|
||||
emojiStatus?: ApiEmojiStatus;
|
||||
isForum?: boolean;
|
||||
isForumAsMessages?: true;
|
||||
topics?: Record<ThreadId, ApiTopic>;
|
||||
listedTopicIds?: number[];
|
||||
topicsCount?: number;
|
||||
orderedPinnedTopicIds?: number[];
|
||||
boostLevel?: number;
|
||||
|
||||
// Calls
|
||||
|
||||
@ -8,7 +8,7 @@ import React, {
|
||||
} from '../../lib/teact/teact';
|
||||
import { getActions } from '../../global';
|
||||
|
||||
import type { ApiChat } from '../../api/types';
|
||||
import type { ApiChat, ApiTopic } from '../../api/types';
|
||||
import type { ObserveFn } from '../../hooks/useIntersectionObserver';
|
||||
|
||||
import { getOrderedTopics } from '../../global/helpers';
|
||||
@ -26,6 +26,7 @@ import styles from './ChatForumLastMessage.module.scss';
|
||||
|
||||
type OwnProps = {
|
||||
chat: ApiChat;
|
||||
topics?: Record<number, ApiTopic>;
|
||||
renderLastMessage: () => React.ReactNode;
|
||||
observeIntersection?: ObserveFn;
|
||||
};
|
||||
@ -35,6 +36,7 @@ const MAX_TOPICS = 3;
|
||||
|
||||
const ChatForumLastMessage: FC<OwnProps> = ({
|
||||
chat,
|
||||
topics,
|
||||
renderLastMessage,
|
||||
observeIntersection,
|
||||
}) => {
|
||||
@ -48,12 +50,12 @@ const ChatForumLastMessage: FC<OwnProps> = ({
|
||||
const lang = useOldLang();
|
||||
|
||||
const [lastActiveTopic, ...otherTopics] = useMemo(() => {
|
||||
if (!chat.topics) {
|
||||
if (!topics) {
|
||||
return [];
|
||||
}
|
||||
|
||||
return getOrderedTopics(Object.values(chat.topics), undefined, true).slice(0, MAX_TOPICS);
|
||||
}, [chat.topics]);
|
||||
return getOrderedTopics(Object.values(topics), undefined, true).slice(0, MAX_TOPICS);
|
||||
}, [topics]);
|
||||
|
||||
const [isReversedCorner, setIsReversedCorner] = useState(false);
|
||||
const [overwrittenWidth, setOverwrittenWidth] = useState<number | undefined>(undefined);
|
||||
|
||||
@ -20,6 +20,7 @@ import {
|
||||
selectChatOnlineCount,
|
||||
selectThreadInfo,
|
||||
selectThreadMessagesCount,
|
||||
selectTopic,
|
||||
selectUser,
|
||||
} from '../../global/selectors';
|
||||
import buildClassName from '../../util/buildClassName';
|
||||
@ -267,7 +268,7 @@ export default memo(withGlobal<OwnProps>(
|
||||
const threadInfo = threadId ? selectThreadInfo(global, chatId, threadId) : undefined;
|
||||
const onlineCount = chat ? selectChatOnlineCount(global, chat) : undefined;
|
||||
const areMessagesLoaded = Boolean(selectChatMessages(global, chatId));
|
||||
const topic = threadId ? chat?.topics?.[threadId] : undefined;
|
||||
const topic = threadId ? selectTopic(global, chatId, threadId) : undefined;
|
||||
const messagesCount = topic && selectThreadMessagesCount(global, chatId, threadId!);
|
||||
const self = selectUser(global, global.currentUserId!);
|
||||
|
||||
|
||||
@ -16,6 +16,7 @@ import {
|
||||
selectPeerPhotos,
|
||||
selectTabState,
|
||||
selectThreadMessagesCount,
|
||||
selectTopic,
|
||||
selectUser,
|
||||
selectUserStatus,
|
||||
} from '../../global/selectors';
|
||||
@ -373,7 +374,7 @@ export default memo(withGlobal<OwnProps>(
|
||||
const { mediaIndex, chatId: avatarOwnerId } = selectTabState(global).mediaViewer;
|
||||
const isForum = chat?.isForum;
|
||||
const { threadId: currentTopicId } = selectCurrentMessageList(global) || {};
|
||||
const topic = isForum && currentTopicId ? chat?.topics?.[currentTopicId] : undefined;
|
||||
const topic = isForum && currentTopicId ? selectTopic(global, peerId, currentTopicId) : undefined;
|
||||
|
||||
const emojiStatus = (user || chat)?.emojiStatus;
|
||||
const emojiStatusSticker = emojiStatus ? global.customEmojis.byId[emojiStatus.documentId] : undefined;
|
||||
|
||||
@ -4,7 +4,6 @@ import { getGlobal, withGlobal } from '../../global';
|
||||
|
||||
import type { ApiChatType } from '../../api/types';
|
||||
import type { ThreadId } from '../../types';
|
||||
import { MAIN_THREAD_ID } from '../../api/types';
|
||||
|
||||
import { API_CHAT_TYPES } from '../../config';
|
||||
import {
|
||||
@ -80,7 +79,7 @@ const RecipientPicker: FC<OwnProps & StateProps> = ({
|
||||
const user = usersById[id];
|
||||
if (user && isDeletedUser(user)) return false;
|
||||
|
||||
return chat && getCanPostInChat(chat, MAIN_THREAD_ID, undefined, chatFullInfoById[id]);
|
||||
return chat && getCanPostInChat(chat, undefined, undefined, chatFullInfoById[id]);
|
||||
});
|
||||
|
||||
const sorted = sortChatIds(
|
||||
|
||||
@ -19,6 +19,7 @@ import {
|
||||
selectShouldSchedule,
|
||||
selectStickerSet,
|
||||
selectThreadInfo,
|
||||
selectTopic,
|
||||
} from '../../global/selectors';
|
||||
import buildClassName from '../../util/buildClassName';
|
||||
import { copyTextToClipboard } from '../../util/clipboard';
|
||||
@ -263,8 +264,9 @@ export default memo(withGlobal<OwnProps>(
|
||||
const sendOptions = chat ? getAllowedAttachmentOptions(chat, chatFullInfo) : undefined;
|
||||
const threadInfo = chatId && threadId ? selectThreadInfo(global, chatId, threadId) : undefined;
|
||||
const isMessageThread = Boolean(!threadInfo?.isCommentsInfo && threadInfo?.fromChannelId);
|
||||
const topic = chatId && threadId ? selectTopic(global, chatId, threadId) : undefined;
|
||||
const canSendStickers = Boolean(
|
||||
chat && threadId && getCanPostInChat(chat, threadId, isMessageThread, chatFullInfo)
|
||||
chat && threadId && getCanPostInChat(chat, topic, isMessageThread, chatFullInfo)
|
||||
&& sendOptions?.canSendStickers,
|
||||
);
|
||||
const isSavedMessages = Boolean(chatId) && selectIsChatWithSelf(global, chatId);
|
||||
|
||||
@ -13,7 +13,7 @@ import {
|
||||
getCanPostInChat, getGroupStatus, getUserStatus, isUserOnline,
|
||||
} from '../../../global/helpers';
|
||||
import { isApiPeerChat } from '../../../global/helpers/peers';
|
||||
import { selectChat, selectPeer, selectUserStatus } from '../../../global/selectors';
|
||||
import { selectPeer, selectTopics, selectUserStatus } from '../../../global/selectors';
|
||||
import buildClassName from '../../../util/buildClassName';
|
||||
import { REM } from '../helpers/mediaDimensions';
|
||||
import renderText from '../helpers/renderText';
|
||||
@ -94,15 +94,15 @@ const ChatOrUserPicker: FC<OwnProps> = ({
|
||||
useInputFocusOnOpen(searchRef, isOpen && activeKey === CHAT_LIST_SLIDE, resetSearch);
|
||||
useInputFocusOnOpen(topicSearchRef, isOpen && activeKey === TOPIC_LIST_SLIDE);
|
||||
|
||||
const selectTopics = useLastCallback((global: GlobalState) => {
|
||||
const selectTopicsById = useLastCallback((global: GlobalState) => {
|
||||
if (!forumId) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
return selectChat(global, forumId)?.topics;
|
||||
return selectTopics(global, forumId);
|
||||
});
|
||||
|
||||
const forumTopics = useSelector(selectTopics);
|
||||
const forumTopicsById = useSelector(selectTopicsById);
|
||||
|
||||
const [topicIds, topics] = useMemo(() => {
|
||||
const global = getGlobal();
|
||||
@ -111,16 +111,16 @@ const ChatOrUserPicker: FC<OwnProps> = ({
|
||||
|
||||
const chat = chatsById[forumId!];
|
||||
|
||||
if (!chat || !forumTopics) {
|
||||
if (!chat || !forumTopicsById) {
|
||||
return [undefined, undefined];
|
||||
}
|
||||
|
||||
const searchTitle = topicSearch.toLowerCase();
|
||||
|
||||
const result = forumTopics
|
||||
? Object.values(forumTopics).reduce((acc, topic) => {
|
||||
const result = forumTopicsById
|
||||
? Object.values(forumTopicsById).reduce((acc, topic) => {
|
||||
if (
|
||||
getCanPostInChat(chat, topic.id, undefined, chatFullInfoById[forumId!])
|
||||
getCanPostInChat(chat, topic, undefined, chatFullInfoById[forumId!])
|
||||
&& (!searchTitle || topic.title.toLowerCase().includes(searchTitle))
|
||||
) {
|
||||
acc[topic.id] = topic;
|
||||
@ -128,10 +128,10 @@ const ChatOrUserPicker: FC<OwnProps> = ({
|
||||
|
||||
return acc;
|
||||
}, {} as Record<number, ApiTopic>)
|
||||
: forumTopics;
|
||||
: forumTopicsById;
|
||||
|
||||
return [Object.keys(result).map(Number), result];
|
||||
}, [forumId, topicSearch, forumTopics]);
|
||||
}, [forumId, topicSearch, forumTopicsById]);
|
||||
|
||||
const handleHeaderBackClick = useLastCallback(() => {
|
||||
setForumId(undefined);
|
||||
@ -153,7 +153,7 @@ const ChatOrUserPicker: FC<OwnProps> = ({
|
||||
const chatId = viewportIds[index === -1 ? 0 : index];
|
||||
const chat = chatsById[chatId];
|
||||
if (chat?.isForum) {
|
||||
if (!chat.topics) loadTopics({ chatId });
|
||||
if (!forumTopicsById) loadTopics({ chatId });
|
||||
setForumId(chatId);
|
||||
} else {
|
||||
onSelectChatOrUser(chatId);
|
||||
@ -171,7 +171,7 @@ const ChatOrUserPicker: FC<OwnProps> = ({
|
||||
const chatsById = getGlobal().chats.byId;
|
||||
const chat = chatsById?.[chatId];
|
||||
if (chat?.isForum) {
|
||||
if (!chat.topics) loadTopics({ chatId });
|
||||
if (!forumTopicsById) loadTopics({ chatId });
|
||||
setForumId(chatId);
|
||||
resetSearch();
|
||||
} else {
|
||||
|
||||
@ -43,6 +43,7 @@ import {
|
||||
selectTabState,
|
||||
selectThreadParam,
|
||||
selectTopicFromMessage,
|
||||
selectTopicsInfo,
|
||||
selectUser,
|
||||
selectUserStatus,
|
||||
} from '../../../global/selectors';
|
||||
@ -90,6 +91,8 @@ type OwnProps = {
|
||||
|
||||
type StateProps = {
|
||||
chat?: ApiChat;
|
||||
listedTopicIds?: number[];
|
||||
topics?: Record<number, ApiTopic>;
|
||||
isMuted?: boolean;
|
||||
user?: ApiUser;
|
||||
userStatus?: ApiUserStatus;
|
||||
@ -118,6 +121,8 @@ const Chat: FC<OwnProps & StateProps> = ({
|
||||
orderDiff,
|
||||
animationType,
|
||||
isPinned,
|
||||
listedTopicIds,
|
||||
topics,
|
||||
observeIntersection,
|
||||
chat,
|
||||
isMuted,
|
||||
@ -190,6 +195,7 @@ const Chat: FC<OwnProps & StateProps> = ({
|
||||
orderDiff,
|
||||
isSavedDialog,
|
||||
isPreview,
|
||||
topics,
|
||||
});
|
||||
|
||||
const getIsForumPanelClosed = useSelectorSignal(selectIsForumPanelClosed);
|
||||
@ -284,10 +290,10 @@ const Chat: FC<OwnProps & StateProps> = ({
|
||||
|
||||
// Load the forum topics to display unread count badge
|
||||
useEffect(() => {
|
||||
if (isIntersecting && isForum && chat && chat.listedTopicIds === undefined) {
|
||||
if (isIntersecting && isForum && listedTopicIds === undefined) {
|
||||
loadTopics({ chatId });
|
||||
}
|
||||
}, [chat, chatId, isForum, isIntersecting]);
|
||||
}, [chatId, listedTopicIds, isForum, isIntersecting]);
|
||||
|
||||
const isOnline = user && userStatus && isUserOnline(user, userStatus);
|
||||
const { hasShownClass: isAvatarOnlineShown } = useShowTransitionDeprecated(isOnline);
|
||||
@ -343,7 +349,13 @@ const Chat: FC<OwnProps & StateProps> = ({
|
||||
/>
|
||||
<div className="avatar-badge-wrapper">
|
||||
<div className={buildClassName('avatar-online', isAvatarOnlineShown && 'avatar-online-shown')} />
|
||||
<ChatBadge chat={chat} isMuted={isMuted} shouldShowOnlyMostImportant forceHidden={getIsForumPanelClosed} />
|
||||
<ChatBadge
|
||||
chat={chat}
|
||||
isMuted={isMuted}
|
||||
shouldShowOnlyMostImportant
|
||||
forceHidden={getIsForumPanelClosed}
|
||||
topics={topics}
|
||||
/>
|
||||
</div>
|
||||
{chat.isCallActive && chat.isCallNotEmpty && (
|
||||
<ChatCallStatus isMobile={isMobile} isSelected={isSelected} isActive={withInterfaceAnimations} />
|
||||
@ -376,6 +388,7 @@ const Chat: FC<OwnProps & StateProps> = ({
|
||||
isPinned={isPinned}
|
||||
isMuted={isMuted}
|
||||
isSavedDialog={isSavedDialog}
|
||||
topics={topics}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
@ -460,6 +473,8 @@ export default memo(withGlobal<OwnProps>(
|
||||
|
||||
const typingStatus = selectThreadParam(global, chatId, MAIN_THREAD_ID, 'typingStatus');
|
||||
|
||||
const topicsInfo = selectTopicsInfo(global, chatId);
|
||||
|
||||
return {
|
||||
chat,
|
||||
isMuted: selectIsChatMuted(chat, selectNotifySettings(global), selectNotifyExceptions(global)),
|
||||
@ -484,6 +499,8 @@ export default memo(withGlobal<OwnProps>(
|
||||
lastMessage,
|
||||
lastMessageId,
|
||||
currentUserId: global.currentUserId!,
|
||||
listedTopicIds: topicsInfo?.listedTopicIds,
|
||||
topics: topicsInfo?.topicsById,
|
||||
};
|
||||
},
|
||||
)(Chat));
|
||||
|
||||
@ -24,10 +24,19 @@ type OwnProps = {
|
||||
isSavedDialog?: boolean;
|
||||
shouldShowOnlyMostImportant?: boolean;
|
||||
forceHidden?: boolean | Signal<boolean>;
|
||||
topics?: Record<number, ApiTopic>;
|
||||
};
|
||||
|
||||
const ChatBadge: FC<OwnProps> = ({
|
||||
topic, chat, isPinned, isMuted, shouldShowOnlyMostImportant, wasTopicOpened, forceHidden, isSavedDialog,
|
||||
topic,
|
||||
topics,
|
||||
chat,
|
||||
isPinned,
|
||||
isMuted,
|
||||
shouldShowOnlyMostImportant,
|
||||
wasTopicOpened,
|
||||
forceHidden,
|
||||
isSavedDialog,
|
||||
}) => {
|
||||
const {
|
||||
unreadMentionsCount = 0, unreadReactionsCount = 0,
|
||||
@ -36,8 +45,8 @@ const ChatBadge: FC<OwnProps> = ({
|
||||
const isTopicUnopened = !isPinned && topic && !wasTopicOpened;
|
||||
const isForum = chat.isForum && !topic;
|
||||
const topicsWithUnread = useMemo(() => (
|
||||
isForum && chat?.topics ? Object.values(chat.topics).filter(({ unreadCount }) => unreadCount) : undefined
|
||||
), [chat, isForum]);
|
||||
isForum && topics ? Object.values(topics).filter(({ unreadCount }) => unreadCount) : undefined
|
||||
), [topics, isForum]);
|
||||
|
||||
const unreadCount = useMemo(() => (
|
||||
isForum
|
||||
@ -48,11 +57,11 @@ const ChatBadge: FC<OwnProps> = ({
|
||||
), [chat, topic, topicsWithUnread, isForum, isMuted]);
|
||||
|
||||
const shouldBeMuted = useMemo(() => {
|
||||
const hasUnmutedUnreadTopics = chat.topics
|
||||
&& Object.values(chat.topics).some((acc) => !acc.isMuted && acc.unreadCount);
|
||||
const hasUnmutedUnreadTopics = topics
|
||||
&& Object.values(topics).some((acc) => !acc.isMuted && acc.unreadCount);
|
||||
|
||||
return isMuted || (chat.topics && !hasUnmutedUnreadTopics);
|
||||
}, [chat, isMuted]);
|
||||
return isMuted || (topics && !hasUnmutedUnreadTopics);
|
||||
}, [topics, isMuted]);
|
||||
|
||||
const hasUnreadMark = topic ? false : chat.hasUnreadMark;
|
||||
|
||||
|
||||
@ -6,6 +6,7 @@ import React, {
|
||||
import { getActions, withGlobal } from '../../../global';
|
||||
|
||||
import type { ApiChat } from '../../../api/types';
|
||||
import type { TopicsInfo } from '../../../global/types';
|
||||
import { MAIN_THREAD_ID } from '../../../api/types';
|
||||
|
||||
import {
|
||||
@ -14,7 +15,12 @@ import {
|
||||
import { requestNextMutation } from '../../../lib/fasterdom/fasterdom';
|
||||
import { getOrderedTopics } from '../../../global/helpers';
|
||||
import {
|
||||
selectCanAnimateInterface, selectChat, selectCurrentMessageList, selectIsForumPanelOpen, selectTabState,
|
||||
selectCanAnimateInterface,
|
||||
selectChat,
|
||||
selectCurrentMessageList,
|
||||
selectIsForumPanelOpen,
|
||||
selectTabState,
|
||||
selectTopicsInfo,
|
||||
} from '../../../global/selectors';
|
||||
import buildClassName from '../../../util/buildClassName';
|
||||
import captureEscKeyListener from '../../../util/captureEscKeyListener';
|
||||
@ -52,6 +58,7 @@ type OwnProps = {
|
||||
|
||||
type StateProps = {
|
||||
chat?: ApiChat;
|
||||
topicsInfo?: TopicsInfo;
|
||||
currentTopicId?: number;
|
||||
withInterfaceAnimations?: boolean;
|
||||
};
|
||||
@ -63,6 +70,7 @@ const ForumPanel: FC<OwnProps & StateProps> = ({
|
||||
currentTopicId,
|
||||
isOpen,
|
||||
isHidden,
|
||||
topicsInfo,
|
||||
onTopicSearch,
|
||||
onCloseAnimationEnd,
|
||||
onOpenAnimationStart,
|
||||
@ -80,12 +88,13 @@ const ForumPanel: FC<OwnProps & StateProps> = ({
|
||||
// eslint-disable-next-line no-null/no-null
|
||||
const scrollTopHandlerRef = useRef<HTMLDivElement>(null);
|
||||
const { isMobile } = useAppLayout();
|
||||
const chatId = chat?.id;
|
||||
|
||||
useEffect(() => {
|
||||
if (chat && !chat.topics) {
|
||||
loadTopics({ chatId: chat.id });
|
||||
if (chatId && !topicsInfo) {
|
||||
loadTopics({ chatId });
|
||||
}
|
||||
}, [chat, loadTopics]);
|
||||
}, [topicsInfo, chatId]);
|
||||
|
||||
const [isScrolled, setIsScrolled] = useState(false);
|
||||
const lang = useOldLang();
|
||||
@ -115,17 +124,20 @@ const ForumPanel: FC<OwnProps & StateProps> = ({
|
||||
});
|
||||
|
||||
const orderedIds = useMemo(() => {
|
||||
return chat?.topics
|
||||
? getOrderedTopics(Object.values(chat.topics), chat.orderedPinnedTopicIds).map(({ id }) => id)
|
||||
return topicsInfo
|
||||
? getOrderedTopics(
|
||||
Object.values(topicsInfo.topicsById),
|
||||
topicsInfo.orderedPinnedTopicIds,
|
||||
).map(({ id }) => id)
|
||||
: [];
|
||||
}, [chat]);
|
||||
}, [topicsInfo]);
|
||||
|
||||
const { orderDiffById, getAnimationType } = useOrderDiff(orderedIds, chat?.id);
|
||||
|
||||
const [viewportIds, getMore] = useInfiniteScroll(() => {
|
||||
if (!chat) return;
|
||||
loadTopics({ chatId: chat.id });
|
||||
}, orderedIds, !chat?.topicsCount || orderedIds.length >= chat.topicsCount, TOPICS_SLICE);
|
||||
}, orderedIds, !topicsInfo?.totalCount || orderedIds.length >= topicsInfo.totalCount, TOPICS_SLICE);
|
||||
|
||||
const shouldRenderRef = useRef(false);
|
||||
const isVisible = isOpen && !isHidden;
|
||||
@ -191,7 +203,7 @@ const ForumPanel: FC<OwnProps & StateProps> = ({
|
||||
<Topic
|
||||
key={id}
|
||||
chatId={chat!.id}
|
||||
topic={chat!.topics![id]}
|
||||
topic={topicsInfo!.topicsById[id]}
|
||||
style={`top: ${(viewportOffset + i) * TOPIC_HEIGHT_PX}px;`}
|
||||
isSelected={currentTopicId === id}
|
||||
observeIntersection={observe}
|
||||
@ -201,7 +213,7 @@ const ForumPanel: FC<OwnProps & StateProps> = ({
|
||||
));
|
||||
}
|
||||
|
||||
const isLoading = chat?.topics === undefined;
|
||||
const isLoading = topicsInfo === undefined;
|
||||
|
||||
return (
|
||||
<div
|
||||
@ -271,7 +283,7 @@ const ForumPanel: FC<OwnProps & StateProps> = ({
|
||||
)}
|
||||
</InfiniteScroll>
|
||||
{!isLoading && viewportIds?.length === 1 && viewportIds[0] === GENERAL_TOPIC_ID && (
|
||||
<EmptyForum chatId={chat.id} />
|
||||
<EmptyForum chatId={chatId!} />
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
@ -285,11 +297,13 @@ export default memo(withGlobal<OwnProps>(
|
||||
chatId: currentChatId,
|
||||
threadId: currentThreadId,
|
||||
} = selectCurrentMessageList(global) || {};
|
||||
const topicsInfo = chatId ? selectTopicsInfo(global, chatId) : undefined;
|
||||
|
||||
return {
|
||||
chat,
|
||||
currentTopicId: chatId === currentChatId ? Number(currentThreadId) : undefined,
|
||||
withInterfaceAnimations: selectCanAnimateInterface(global),
|
||||
topicsInfo,
|
||||
};
|
||||
},
|
||||
(global) => selectIsForumPanelOpen(global),
|
||||
|
||||
@ -22,6 +22,7 @@ import {
|
||||
selectOutgoingStatus,
|
||||
selectThreadInfo,
|
||||
selectThreadParam,
|
||||
selectTopics,
|
||||
selectUser,
|
||||
} from '../../../global/selectors';
|
||||
import buildClassName from '../../../util/buildClassName';
|
||||
@ -68,6 +69,7 @@ type StateProps = {
|
||||
canScrollDown?: boolean;
|
||||
wasTopicOpened?: boolean;
|
||||
withInterfaceAnimations?: boolean;
|
||||
topics?: Record<number, ApiTopic>;
|
||||
};
|
||||
|
||||
const Topic: FC<OwnProps & StateProps> = ({
|
||||
@ -91,6 +93,7 @@ const Topic: FC<OwnProps & StateProps> = ({
|
||||
typingStatus,
|
||||
draft,
|
||||
wasTopicOpened,
|
||||
topics,
|
||||
}) => {
|
||||
const {
|
||||
openThread,
|
||||
@ -138,6 +141,7 @@ const Topic: FC<OwnProps & StateProps> = ({
|
||||
observeIntersection,
|
||||
isTopic: true,
|
||||
typingStatus,
|
||||
topics,
|
||||
|
||||
animationType,
|
||||
withInterfaceAnimations,
|
||||
@ -208,6 +212,7 @@ const Topic: FC<OwnProps & StateProps> = ({
|
||||
isMuted={isMuted}
|
||||
topic={topic}
|
||||
wasTopicOpened={wasTopicOpened}
|
||||
topics={topics}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
@ -253,6 +258,7 @@ export default memo(withGlobal<OwnProps>(
|
||||
const draft = selectDraft(global, chatId, topic.id);
|
||||
const threadInfo = selectThreadInfo(global, chatId, topic.id);
|
||||
const wasTopicOpened = Boolean(threadInfo?.lastReadInboxMessageId);
|
||||
const topics = selectTopics(global, chatId);
|
||||
|
||||
const { chatId: currentChatId, threadId: currentThreadId } = selectCurrentMessageList(global) || {};
|
||||
|
||||
@ -272,6 +278,7 @@ export default memo(withGlobal<OwnProps>(
|
||||
}),
|
||||
canScrollDown: isSelected && chat?.id === currentChatId && currentThreadId === topic.id,
|
||||
wasTopicOpened,
|
||||
topics,
|
||||
};
|
||||
},
|
||||
)(Topic));
|
||||
|
||||
@ -43,6 +43,7 @@ const ANIMATION_DURATION = 200;
|
||||
|
||||
export default function useChatListEntry({
|
||||
chat,
|
||||
topics,
|
||||
lastMessage,
|
||||
chatId,
|
||||
typingStatus,
|
||||
@ -61,6 +62,7 @@ export default function useChatListEntry({
|
||||
isPreview,
|
||||
}: {
|
||||
chat?: ApiChat;
|
||||
topics?: Record<number, ApiTopic>;
|
||||
lastMessage?: ApiMessage;
|
||||
chatId: string;
|
||||
typingStatus?: ApiTypingStatus;
|
||||
@ -192,6 +194,7 @@ export default function useChatListEntry({
|
||||
chat={chat}
|
||||
renderLastMessage={renderLastMessageOrTyping}
|
||||
observeIntersection={observeIntersection}
|
||||
topics={topics}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
@ -4,7 +4,7 @@ import { withGlobal } from '../../../global';
|
||||
|
||||
import type { ApiTopic } from '../../../api/types';
|
||||
|
||||
import { selectChat } from '../../../global/selectors';
|
||||
import { selectTopic } from '../../../global/selectors';
|
||||
import { REM } from '../../common/helpers/mediaDimensions';
|
||||
import renderText from '../../common/helpers/renderText';
|
||||
|
||||
@ -60,8 +60,7 @@ const LeftSearchResultTopic: FC<OwnProps & StateProps> = ({
|
||||
|
||||
export default memo(withGlobal<OwnProps>(
|
||||
(global, { chatId, topicId }): StateProps => {
|
||||
const chat = selectChat(global, chatId);
|
||||
const topic = chat?.topics?.[topicId];
|
||||
const topic = selectTopic(global, chatId, topicId);
|
||||
|
||||
return {
|
||||
topic,
|
||||
|
||||
@ -4,7 +4,6 @@ import { getActions } from '../../lib/teact/teactn';
|
||||
import { withGlobal } from '../../global';
|
||||
|
||||
import type { TabState } from '../../global/types';
|
||||
import { MAIN_THREAD_ID } from '../../api/types';
|
||||
|
||||
import { getCanPostInChat } from '../../global/helpers';
|
||||
import { selectChat, selectChatFullInfo } from '../../global/selectors';
|
||||
@ -94,7 +93,7 @@ export default memo(withGlobal<OwnProps>(
|
||||
const { chatId } = openedGame || {};
|
||||
const chat = chatId && selectChat(global, chatId);
|
||||
const chatFullInfo = chatId ? selectChatFullInfo(global, chatId) : undefined;
|
||||
const canPost = Boolean(chat) && getCanPostInChat(chat, MAIN_THREAD_ID, undefined, chatFullInfo);
|
||||
const canPost = Boolean(chat) && getCanPostInChat(chat, undefined, undefined, chatFullInfo);
|
||||
|
||||
return {
|
||||
canPost,
|
||||
|
||||
@ -35,6 +35,7 @@ import {
|
||||
selectNotifyExceptions,
|
||||
selectNotifySettings,
|
||||
selectTabState,
|
||||
selectTopic,
|
||||
selectUser,
|
||||
selectUserFullInfo,
|
||||
} from '../../global/selectors';
|
||||
@ -760,7 +761,7 @@ export default memo(withGlobal<OwnProps>(
|
||||
&& !selectIsPremiumPurchaseBlocked(global),
|
||||
);
|
||||
|
||||
const topic = chat?.topics?.[threadId];
|
||||
const topic = selectTopic(global, chatId, threadId);
|
||||
const canCreateTopic = chat.isForum && (
|
||||
chat.isCreator || !isUserRightBanned(chat, 'manageTopics') || getHasAdminRight(chat, 'manageTopics')
|
||||
);
|
||||
|
||||
@ -54,6 +54,7 @@ import {
|
||||
selectScrollOffset,
|
||||
selectTabState,
|
||||
selectThreadInfo,
|
||||
selectTopic,
|
||||
selectUserFullInfo,
|
||||
} from '../../global/selectors';
|
||||
import animateScroll, { isAnimatingScroll, restartCurrentScrollAnimation } from '../../util/animateScroll';
|
||||
@ -772,7 +773,7 @@ export default memo(withGlobal<OwnProps>(
|
||||
|
||||
const chatBot = selectBot(global, chatId);
|
||||
|
||||
const topic = chat.topics?.[threadId];
|
||||
const topic = selectTopic(global, chatId, threadId);
|
||||
const chatFullInfo = !isUserId(chatId) ? selectChatFullInfo(global, chatId) : undefined;
|
||||
const isEmptyThread = !selectThreadInfo(global, chatId, threadId)?.messagesCount;
|
||||
|
||||
|
||||
@ -4,7 +4,9 @@ import React, {
|
||||
} from '../../lib/teact/teact';
|
||||
import { getActions, withGlobal } from '../../global';
|
||||
|
||||
import type { ApiChat, ApiChatBannedRights, ApiInputMessageReplyInfo } from '../../api/types';
|
||||
import type {
|
||||
ApiChat, ApiChatBannedRights, ApiInputMessageReplyInfo, ApiTopic,
|
||||
} from '../../api/types';
|
||||
import type {
|
||||
ActiveEmojiInteraction,
|
||||
MessageListType,
|
||||
@ -55,6 +57,8 @@ import {
|
||||
selectTabState,
|
||||
selectTheme,
|
||||
selectThreadInfo,
|
||||
selectTopic,
|
||||
selectTopics,
|
||||
selectUserFullInfo,
|
||||
} from '../../global/selectors';
|
||||
import buildClassName from '../../util/buildClassName';
|
||||
@ -156,6 +160,7 @@ type StateProps = {
|
||||
isSavedDialog?: boolean;
|
||||
canShowOpenChatButton?: boolean;
|
||||
isContactRequirePremium?: boolean;
|
||||
topics?: Record<number, ApiTopic>;
|
||||
};
|
||||
|
||||
function isImage(item: DataTransferItem) {
|
||||
@ -217,6 +222,7 @@ function MiddleColumn({
|
||||
isSavedDialog,
|
||||
canShowOpenChatButton,
|
||||
isContactRequirePremium,
|
||||
topics,
|
||||
}: OwnProps & StateProps) {
|
||||
const {
|
||||
openChat,
|
||||
@ -455,7 +461,7 @@ function MiddleColumn({
|
||||
const messageSendingRestrictionReason = getMessageSendingRestrictionReason(
|
||||
lang, currentUserBannedRights, defaultBannedRights,
|
||||
);
|
||||
const forumComposerPlaceholder = getForumComposerPlaceholder(lang, chat, threadId, Boolean(draftReplyInfo));
|
||||
const forumComposerPlaceholder = getForumComposerPlaceholder(lang, chat, threadId, topics, Boolean(draftReplyInfo));
|
||||
|
||||
const composerRestrictionMessage = messageSendingRestrictionReason
|
||||
?? forumComposerPlaceholder
|
||||
@ -775,7 +781,8 @@ export default memo(withGlobal<OwnProps>(
|
||||
|
||||
const threadInfo = selectThreadInfo(global, chatId, threadId);
|
||||
const isMessageThread = Boolean(!threadInfo?.isCommentsInfo && threadInfo?.fromChannelId);
|
||||
const canPost = chat && getCanPostInChat(chat, threadId, isMessageThread, chatFullInfo);
|
||||
const topic = selectTopic(global, chatId, threadId);
|
||||
const canPost = chat && getCanPostInChat(chat, topic, isMessageThread, chatFullInfo);
|
||||
const isBotNotStarted = selectIsChatBotNotStarted(global, chatId);
|
||||
const isPinnedMessageList = messageListType === 'pinned';
|
||||
const isMainThread = messageListType === 'thread' && threadId === MAIN_THREAD_ID;
|
||||
@ -794,11 +801,12 @@ export default memo(withGlobal<OwnProps>(
|
||||
);
|
||||
const draftReplyInfo = selectDraft(global, chatId, threadId)?.replyInfo;
|
||||
const shouldBlockSendInForum = chat?.isForum
|
||||
? threadId === MAIN_THREAD_ID && !draftReplyInfo && (chat.topics?.[GENERAL_TOPIC_ID]?.isClosed)
|
||||
? threadId === MAIN_THREAD_ID && !draftReplyInfo && (selectTopic(global, chatId, GENERAL_TOPIC_ID)?.isClosed)
|
||||
: false;
|
||||
const audioMessage = audioChatId && audioMessageId
|
||||
? selectChatMessage(global, audioChatId, audioMessageId)
|
||||
: undefined;
|
||||
const topics = selectTopics(global, chatId);
|
||||
|
||||
const isSavedDialog = getIsSavedDialog(chatId, threadId, global.currentUserId);
|
||||
const canShowOpenChatButton = isSavedDialog && threadId !== ANONYMOUS_USER_ID;
|
||||
@ -854,6 +862,7 @@ export default memo(withGlobal<OwnProps>(
|
||||
isSavedDialog,
|
||||
canShowOpenChatButton,
|
||||
isContactRequirePremium,
|
||||
topics,
|
||||
};
|
||||
},
|
||||
)(MiddleColumn));
|
||||
|
||||
@ -54,6 +54,7 @@ import {
|
||||
selectRequestedMessageTranslationLanguage,
|
||||
selectStickerSet,
|
||||
selectThreadInfo,
|
||||
selectTopic,
|
||||
selectUserStatus,
|
||||
} from '../../../global/selectors';
|
||||
import { copyTextToClipboard } from '../../../util/clipboard';
|
||||
@ -730,10 +731,11 @@ export default memo(withGlobal<OwnProps>(
|
||||
|
||||
const threadInfo = threadId && selectThreadInfo(global, message.chatId, threadId);
|
||||
const isMessageThread = Boolean(threadInfo && !threadInfo?.isCommentsInfo && threadInfo?.fromChannelId);
|
||||
const topic = threadId ? selectTopic(global, message.chatId, threadId) : undefined;
|
||||
|
||||
const canSendText = chat && !isUserRightBanned(chat, 'sendPlain', chatFullInfo);
|
||||
|
||||
const canReplyInChat = chat && threadId ? getCanPostInChat(chat, threadId, isMessageThread, chatFullInfo)
|
||||
const canReplyInChat = chat && threadId ? getCanPostInChat(chat, topic, isMessageThread, chatFullInfo)
|
||||
&& canSendText : false;
|
||||
|
||||
const isLocal = isMessageLocal(message);
|
||||
|
||||
@ -31,7 +31,7 @@ import type { PinnedIntersectionChangedCallback } from '../hooks/usePinnedMessag
|
||||
import { MAIN_THREAD_ID } from '../../../api/types';
|
||||
import { AudioOrigin } from '../../../types';
|
||||
|
||||
import { EMOJI_STATUS_LOOP_LIMIT, GENERAL_TOPIC_ID } from '../../../config';
|
||||
import { EMOJI_STATUS_LOOP_LIMIT } from '../../../config';
|
||||
import {
|
||||
areReactionsEmpty,
|
||||
getIsDownloading,
|
||||
@ -1766,8 +1766,7 @@ export default memo(withGlobal<OwnProps>(
|
||||
const hasUnreadReaction = chat?.unreadReactions?.includes(message.id);
|
||||
|
||||
const hasTopicChip = threadId === MAIN_THREAD_ID && chat?.isForum && isFirstInGroup;
|
||||
const messageTopic = hasTopicChip ? (selectTopicFromMessage(global, message) || chat?.topics?.[GENERAL_TOPIC_ID])
|
||||
: undefined;
|
||||
const messageTopic = hasTopicChip ? selectTopicFromMessage(global, message) : undefined;
|
||||
|
||||
const chatTranslations = selectChatTranslations(global, chatId);
|
||||
|
||||
|
||||
@ -8,7 +8,9 @@ import type { ApiChat, ApiSticker, ApiTopic } from '../../api/types';
|
||||
import type { TabState } from '../../global/types';
|
||||
|
||||
import { DEFAULT_TOPIC_ICON_STICKER_ID, GENERAL_TOPIC_ID } from '../../config';
|
||||
import { selectChat, selectIsCurrentUserPremium, selectTabState } from '../../global/selectors';
|
||||
import {
|
||||
selectChat, selectIsCurrentUserPremium, selectTabState, selectTopic,
|
||||
} from '../../global/selectors';
|
||||
import buildClassName from '../../util/buildClassName';
|
||||
import { REM } from '../common/helpers/mediaDimensions';
|
||||
|
||||
@ -185,7 +187,8 @@ export default memo(withGlobal(
|
||||
(global): StateProps => {
|
||||
const { editTopicPanel } = selectTabState(global);
|
||||
const chat = editTopicPanel?.chatId ? selectChat(global, editTopicPanel.chatId) : undefined;
|
||||
const topic = editTopicPanel?.topicId ? chat?.topics?.[editTopicPanel?.topicId] : undefined;
|
||||
const topic = editTopicPanel?.chatId && editTopicPanel?.topicId
|
||||
? selectTopic(global, editTopicPanel.chatId, editTopicPanel.topicId) : undefined;
|
||||
|
||||
return {
|
||||
chat,
|
||||
|
||||
@ -14,6 +14,7 @@ import {
|
||||
selectCurrentMessageList,
|
||||
selectIsChatWithBot,
|
||||
selectIsChatWithSelf, selectThreadInfo,
|
||||
selectTopic,
|
||||
} from '../../global/selectors';
|
||||
import buildClassName from '../../util/buildClassName';
|
||||
import { IS_TOUCH_ENV } from '../../util/windowEnvironment';
|
||||
@ -174,8 +175,9 @@ export default memo(withGlobal(
|
||||
const isSavedMessages = Boolean(chatId) && selectIsChatWithSelf(global, chatId);
|
||||
const threadInfo = chatId && threadId ? selectThreadInfo(global, chatId, threadId) : undefined;
|
||||
const isMessageThread = Boolean(!threadInfo?.isCommentsInfo && threadInfo?.fromChannelId);
|
||||
const topic = chatId && threadId ? selectTopic(global, chatId, threadId) : undefined;
|
||||
const canPostInChat = Boolean(chat) && Boolean(threadId)
|
||||
&& getCanPostInChat(chat, threadId, isMessageThread, chatFullInfo);
|
||||
&& getCanPostInChat(chat, topic, isMessageThread, chatFullInfo);
|
||||
|
||||
return {
|
||||
query,
|
||||
|
||||
@ -18,6 +18,7 @@ import {
|
||||
selectCurrentStickerSearch,
|
||||
selectIsChatWithSelf,
|
||||
selectTabState,
|
||||
selectTopic,
|
||||
selectUser,
|
||||
} from '../../global/selectors';
|
||||
import buildClassName from '../../util/buildClassName';
|
||||
@ -582,7 +583,7 @@ export default withGlobal<OwnProps>(
|
||||
const user = isProfile && chatId && isUserId(chatId) ? selectUser(global, chatId) : undefined;
|
||||
const isChannel = chat && isChatChannel(chat);
|
||||
const isInsideTopic = chat?.isForum && Boolean(threadId && threadId !== MAIN_THREAD_ID);
|
||||
const topic = isInsideTopic ? chat.topics?.[threadId!] : undefined;
|
||||
const topic = isInsideTopic ? selectTopic(global, chatId!, threadId!) : undefined;
|
||||
const canEditTopic = isInsideTopic && topic && getCanManageTopic(chat, topic);
|
||||
const isBot = user && isUserBot(user);
|
||||
const isSavedMessages = chatId ? selectIsChatWithSelf(global, chatId) : undefined;
|
||||
|
||||
@ -112,6 +112,9 @@ import {
|
||||
selectTabState,
|
||||
selectThread,
|
||||
selectThreadInfo,
|
||||
selectTopic,
|
||||
selectTopics,
|
||||
selectTopicsInfo,
|
||||
selectUser,
|
||||
selectUserByPhoneNumber,
|
||||
} from '../../selectors';
|
||||
@ -1169,7 +1172,9 @@ addActionHandler('markTopicRead', (global, actions, payload): ActionReturnType =
|
||||
const chat = selectChat(global, chatId);
|
||||
if (!chat) return;
|
||||
|
||||
const lastTopicMessageId = chat.topics?.[topicId]?.lastMessageId;
|
||||
const topic = selectTopic(global, chatId, topicId);
|
||||
|
||||
const lastTopicMessageId = topic?.lastMessageId;
|
||||
if (!lastTopicMessageId) return;
|
||||
|
||||
void callApi('markMessageListRead', {
|
||||
@ -2074,13 +2079,15 @@ addActionHandler('loadTopics', async (global, actions, payload): Promise<void> =
|
||||
const chat = selectChat(global, chatId);
|
||||
if (!chat) return;
|
||||
|
||||
if (!force && chat.listedTopicIds && chat.listedTopicIds.length === chat.topicsCount) {
|
||||
const topicsInfo = selectTopicsInfo(global, chatId);
|
||||
|
||||
if (!force && topicsInfo?.listedTopicIds && topicsInfo.listedTopicIds.length === topicsInfo.totalCount) {
|
||||
return;
|
||||
}
|
||||
|
||||
const offsetTopic = !force && chat.listedTopicIds ? chat.listedTopicIds.reduce((acc, el) => {
|
||||
const topic = chat.topics?.[el];
|
||||
const accTopic = chat.topics?.[acc];
|
||||
const offsetTopic = !force ? topicsInfo?.listedTopicIds?.reduce((acc, el) => {
|
||||
const topic = selectTopic(global, chatId, el);
|
||||
const accTopic = selectTopic(global, chatId, acc);
|
||||
if (!topic) return acc;
|
||||
if (!accTopic || topic.lastMessageId < accTopic.lastMessageId) {
|
||||
return el;
|
||||
@ -2089,7 +2096,7 @@ addActionHandler('loadTopics', async (global, actions, payload): Promise<void> =
|
||||
}) : undefined;
|
||||
|
||||
const { id: offsetTopicId, date: offsetDate, lastMessageId: offsetId } = (offsetTopic
|
||||
&& chat.topics?.[offsetTopic]) || {};
|
||||
&& selectTopic(global, chatId, offsetTopic)) || {};
|
||||
const result = await callApi('fetchTopics', {
|
||||
chat, offsetTopicId, offsetId, offsetDate, limit: offsetTopicId ? TOPICS_SLICE : TOPICS_SLICE_SECOND_LOAD,
|
||||
});
|
||||
@ -2231,7 +2238,7 @@ addActionHandler('editTopic', async (global, actions, payload): Promise<void> =>
|
||||
chatId, topicId, tabId = getCurrentTabId(), ...rest
|
||||
} = payload;
|
||||
const chat = selectChat(global, chatId);
|
||||
const topic = chat?.topics?.[topicId];
|
||||
const topic = selectTopic(global, chatId, topicId);
|
||||
if (!chat || !topic) return;
|
||||
|
||||
if (selectTabState(global, tabId).editTopicPanel) {
|
||||
@ -2262,9 +2269,10 @@ addActionHandler('toggleTopicPinned', (global, actions, payload): ActionReturnTy
|
||||
|
||||
const { topicsPinnedLimit } = global.appConfig || {};
|
||||
const chat = selectChat(global, chatId);
|
||||
if (!chat || !chat.topics || !topicsPinnedLimit) return;
|
||||
const topics = selectTopics(global, chatId);
|
||||
if (!chat || !topics || !topicsPinnedLimit) return;
|
||||
|
||||
if (isPinned && Object.values(chat.topics).filter((topic) => topic.isPinned).length >= topicsPinnedLimit) {
|
||||
if (isPinned && Object.values(topics).filter((topic) => topic.isPinned).length >= topicsPinnedLimit) {
|
||||
actions.showNotification({
|
||||
message: langProvider.oldTranslate('LimitReachedPinnedTopics', topicsPinnedLimit, 'i'),
|
||||
tabId,
|
||||
|
||||
@ -127,6 +127,7 @@ import {
|
||||
selectSponsoredMessage,
|
||||
selectTabState,
|
||||
selectThreadIdFromMessage,
|
||||
selectTopic,
|
||||
selectTranslationLanguage,
|
||||
selectUser,
|
||||
selectUserFullInfo,
|
||||
@ -907,8 +908,8 @@ addActionHandler('markMessageListRead', (global, actions, payload): ActionReturn
|
||||
return global;
|
||||
}
|
||||
|
||||
if (chat.isForum && chat.topics?.[threadId]) {
|
||||
const topic = chat.topics[threadId];
|
||||
const topic = selectTopic(global, chatId, threadId);
|
||||
if (chat.isForum && topic) {
|
||||
global = updateThreadInfo(global, chatId, threadId, {
|
||||
lastReadInboxMessageId: maxId,
|
||||
});
|
||||
|
||||
@ -36,6 +36,7 @@ import {
|
||||
selectEditingId,
|
||||
selectTabState,
|
||||
selectThreadInfo,
|
||||
selectTopics,
|
||||
} from '../../selectors';
|
||||
|
||||
const RELEASE_STATUS_TIMEOUT = 15000; // 15 sec;
|
||||
@ -160,10 +161,10 @@ async function loadAndReplaceMessages<T extends GlobalState>(global: T, actions:
|
||||
const localMessages = currentChatId === SERVICE_NOTIFICATIONS_USER_ID
|
||||
? global.serviceNotifications.filter(({ isDeleted }) => !isDeleted).map(({ message }) => message)
|
||||
: [];
|
||||
const topicLastMessages = currentChat.isForum && currentChat.topics
|
||||
? Object.values(currentChat.topics)
|
||||
.map(({ lastMessageId }) => currentChatMessages[lastMessageId])
|
||||
.filter(Boolean)
|
||||
const topics = selectTopics(global, currentChatId);
|
||||
const topicLastMessages = topics ? Object.values(topics)
|
||||
.map(({ lastMessageId }) => currentChatMessages[lastMessageId])
|
||||
.filter(Boolean)
|
||||
: [];
|
||||
|
||||
const resultMessageIds = result.messages.map(({ id }) => id);
|
||||
|
||||
@ -17,6 +17,7 @@ import {
|
||||
leaveChat,
|
||||
removeUnreadMentions,
|
||||
replacePeerPhotos,
|
||||
replacePinnedTopicIds,
|
||||
replaceThreadParam,
|
||||
updateChat,
|
||||
updateChatFullInfo,
|
||||
@ -482,9 +483,7 @@ addActionHandler('apiUpdate', (global, actions, update): ActionReturnType => {
|
||||
const chat = global.chats.byId[chatId];
|
||||
if (!chat) return undefined;
|
||||
|
||||
global = updateChat(global, chatId, {
|
||||
orderedPinnedTopicIds: order,
|
||||
});
|
||||
global = replacePinnedTopicIds(global, chatId, order);
|
||||
setGlobal(global);
|
||||
|
||||
return undefined;
|
||||
|
||||
@ -78,6 +78,7 @@ import {
|
||||
selectThreadByMessage,
|
||||
selectThreadIdFromMessage,
|
||||
selectThreadInfo,
|
||||
selectTopic,
|
||||
selectTopicFromMessage,
|
||||
selectViewportIds,
|
||||
} from '../../selectors';
|
||||
@ -1061,7 +1062,7 @@ export function deleteMessages<T extends GlobalState>(
|
||||
isDeleting: true,
|
||||
});
|
||||
|
||||
if (chat.topics?.[id]) {
|
||||
if (selectTopic(global, chatId, id)) {
|
||||
global = deleteTopic(global, chatId, id);
|
||||
}
|
||||
|
||||
@ -1091,7 +1092,12 @@ export function deleteMessages<T extends GlobalState>(
|
||||
if (!threadInfo?.lastMessageId || !idsSet.has(threadInfo.lastMessageId)) return;
|
||||
|
||||
const newLastMessage = findLastMessage(global, chatId, threadId);
|
||||
if (!newLastMessage) return;
|
||||
if (!newLastMessage) {
|
||||
if (chat.isForum && threadId !== MAIN_THREAD_ID) {
|
||||
actions.loadTopicById({ chatId, topicId: Number(threadId) });
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
if (threadId === MAIN_THREAD_ID) {
|
||||
global = updateChatLastMessage(global, chatId, newLastMessage, true);
|
||||
|
||||
@ -33,6 +33,7 @@ import {
|
||||
selectIsTrustedBot,
|
||||
selectSender,
|
||||
selectTabState,
|
||||
selectTopic,
|
||||
} from '../../selectors';
|
||||
|
||||
import { getIsMobile, getIsTablet } from '../../../hooks/useAppLayout';
|
||||
@ -773,8 +774,9 @@ addActionHandler('updatePageTitle', (global, actions, payload): ActionReturnType
|
||||
const currentChat = selectChat(global, chatId);
|
||||
if (currentChat) {
|
||||
const title = getChatTitle(langProvider.oldTranslate, currentChat, chatId === currentUserId);
|
||||
if (currentChat.isForum && currentChat.topics?.[threadId]) {
|
||||
setPageTitle(`${title} › ${currentChat.topics[threadId].title}`);
|
||||
const topic = selectTopic(global, chatId, threadId);
|
||||
if (currentChat.isForum && topic) {
|
||||
setPageTitle(`${title} › ${topic.title}`);
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
@ -36,10 +36,10 @@ import { addActionHandler, getGlobal } from './index';
|
||||
import { INITIAL_GLOBAL_STATE, INITIAL_PERFORMANCE_STATE_MID, INITIAL_PERFORMANCE_STATE_MIN } from './initialState';
|
||||
import { clearGlobalForLockScreen } from './reducers';
|
||||
import {
|
||||
selectChat,
|
||||
selectChatLastMessageId,
|
||||
selectChatMessages,
|
||||
selectCurrentMessageList,
|
||||
selectTopics,
|
||||
selectViewportIds,
|
||||
selectVisibleUsers,
|
||||
} from './selectors';
|
||||
@ -248,6 +248,9 @@ function unsafeMigrateCache(cached: GlobalState, initialState: GlobalState) {
|
||||
if (!cached.users.commonChatsById) {
|
||||
cached.users.commonChatsById = initialState.users.commonChatsById;
|
||||
}
|
||||
if (!cached.chats.topicsInfoById) {
|
||||
cached.chats.topicsInfoById = initialState.chats.topicsInfoById;
|
||||
}
|
||||
}
|
||||
|
||||
function updateCache(force?: boolean) {
|
||||
@ -446,6 +449,7 @@ function reduceChats<T extends GlobalState>(global: T): GlobalState['chats'] {
|
||||
all: pickTruthy(global.chats.lastMessageIds.all || {}, idsToSave),
|
||||
saved: global.chats.lastMessageIds.saved,
|
||||
},
|
||||
topicsInfoById: pickTruthy(global.chats.topicsInfoById, currentChatIds),
|
||||
};
|
||||
}
|
||||
|
||||
@ -486,7 +490,6 @@ function reduceMessages<T extends GlobalState>(global: T): GlobalState['messages
|
||||
return;
|
||||
}
|
||||
|
||||
const chat = selectChat(global, chatId);
|
||||
const chatLastMessageId = selectChatLastMessageId(global, chatId);
|
||||
|
||||
const openedThreadIds = Array.from(openedChatThreadIds[chatId] || []);
|
||||
@ -498,8 +501,9 @@ function reduceMessages<T extends GlobalState>(global: T): GlobalState['messages
|
||||
const threadsToSave = pickTruthy(current.threadsById, [MAIN_THREAD_ID, ...threadIds]);
|
||||
|
||||
const viewportIdsToSave = unique(Object.values(threadsToSave).flatMap((thread) => thread.lastViewportIds || []));
|
||||
const topicLastMessageIds = chat?.topics ? Object.values(chat.topics).map(({ lastMessageId }) => lastMessageId)
|
||||
: [];
|
||||
const topics = selectTopics(global, chatId);
|
||||
const topicLastMessageIds = topics && forumPanelChatIds.includes(chatId)
|
||||
? Object.values(topics).map(({ lastMessageId }) => lastMessageId) : [];
|
||||
const savedLastMessageIds = chatId === currentUserId && global.chats.lastMessageIds.saved
|
||||
? Object.values(global.chats.lastMessageIds.saved) : [];
|
||||
const lastMessageIdsToSave = [chatLastMessageId].concat(topicLastMessageIds).concat(savedLastMessageIds)
|
||||
|
||||
@ -145,15 +145,14 @@ export function isUserRightBanned(chat: ApiChat, key: keyof ApiChatBannedRights,
|
||||
}
|
||||
|
||||
export function getCanPostInChat(
|
||||
chat: ApiChat, threadId: ThreadId, isMessageThread?: boolean, chatFullInfo?: ApiChatFullInfo,
|
||||
chat: ApiChat, topic?: ApiTopic, isMessageThread?: boolean, chatFullInfo?: ApiChatFullInfo,
|
||||
) {
|
||||
if (threadId !== MAIN_THREAD_ID) {
|
||||
if (topic) {
|
||||
if (chat.isForum) {
|
||||
if (chat.isNotJoined) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const topic = chat.topics?.[threadId];
|
||||
if (topic?.isClosed && !topic.isOwner && !getHasAdminRight(chat, 'manageTopics')) {
|
||||
return false;
|
||||
}
|
||||
@ -264,18 +263,22 @@ export function getMessageSendingRestrictionReason(
|
||||
}
|
||||
|
||||
export function getForumComposerPlaceholder(
|
||||
lang: LangFn, chat?: ApiChat, threadId: ThreadId = MAIN_THREAD_ID, isReplying?: boolean,
|
||||
lang: LangFn,
|
||||
chat?: ApiChat,
|
||||
threadId: ThreadId = MAIN_THREAD_ID,
|
||||
topics?: Record<number, ApiTopic>,
|
||||
isReplying?: boolean,
|
||||
) {
|
||||
if (!chat?.isForum) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
if (threadId === MAIN_THREAD_ID) {
|
||||
if (isReplying || (chat.topics && !chat.topics[GENERAL_TOPIC_ID]?.isClosed)) return undefined;
|
||||
if (isReplying || (topics && !topics[GENERAL_TOPIC_ID]?.isClosed)) return undefined;
|
||||
return lang('lng_forum_replies_only');
|
||||
}
|
||||
|
||||
const topic = chat.topics?.[threadId];
|
||||
const topic = topics?.[Number(threadId)];
|
||||
if (!topic) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
@ -110,6 +110,7 @@ export const INITIAL_GLOBAL_STATE: GlobalState = {
|
||||
byId: {},
|
||||
fullInfoById: {},
|
||||
similarChannelsById: {},
|
||||
topicsInfoById: {},
|
||||
loadingParameters: {
|
||||
active: {},
|
||||
archived: {},
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
import type {
|
||||
ApiChat, ApiChatFullInfo, ApiChatMember, ApiTopic,
|
||||
ApiChat, ApiChatFullInfo, ApiChatMember,
|
||||
} from '../../api/types';
|
||||
import type { ChatListType, GlobalState } from '../types';
|
||||
|
||||
@ -8,8 +8,7 @@ import { areDeepEqual } from '../../util/areDeepEqual';
|
||||
import {
|
||||
areSortedArraysEqual, buildCollectionByKey, omit, pick, unique,
|
||||
} from '../../util/iteratees';
|
||||
import { selectChat, selectChatFullInfo } from '../selectors';
|
||||
import { updateThread, updateThreadInfo } from './messages';
|
||||
import { selectChatFullInfo } from '../selectors';
|
||||
|
||||
const DEFAULT_CHAT_LISTS: ChatListType[] = ['active', 'archived'];
|
||||
|
||||
@ -423,95 +422,6 @@ export function addChatMembers<T extends GlobalState>(global: T, chat: ApiChat,
|
||||
});
|
||||
}
|
||||
|
||||
export function updateListedTopicIds<T extends GlobalState>(
|
||||
global: T, chatId: string, topicIds: number[],
|
||||
): T {
|
||||
return updateChat(global, chatId, {
|
||||
listedTopicIds: unique([
|
||||
...(global.chats.byId[chatId]?.listedTopicIds || []),
|
||||
...topicIds,
|
||||
]),
|
||||
});
|
||||
}
|
||||
|
||||
export function updateTopics<T extends GlobalState>(
|
||||
global: T, chatId: string, topicsCount: number, topics: ApiTopic[],
|
||||
): T {
|
||||
const chat = selectChat(global, chatId);
|
||||
|
||||
const newTopics = buildCollectionByKey(topics, 'id');
|
||||
|
||||
global = updateChat(global, chatId, {
|
||||
topics: {
|
||||
...chat?.topics,
|
||||
...newTopics,
|
||||
},
|
||||
topicsCount,
|
||||
});
|
||||
|
||||
topics.forEach((topic) => {
|
||||
global = updateThread(global, chatId, topic.id, {
|
||||
firstMessageId: topic.id,
|
||||
});
|
||||
|
||||
global = updateThreadInfo(global, chatId, topic.id, {
|
||||
lastMessageId: topic.lastMessageId,
|
||||
threadId: topic.id,
|
||||
chatId,
|
||||
});
|
||||
});
|
||||
|
||||
return global;
|
||||
}
|
||||
|
||||
export function updateTopic<T extends GlobalState>(
|
||||
global: T, chatId: string, topicId: number, update: Partial<ApiTopic>,
|
||||
): T {
|
||||
const chat = selectChat(global, chatId);
|
||||
|
||||
if (!chat) return global;
|
||||
|
||||
const topic = chat?.topics?.[topicId];
|
||||
|
||||
const updatedTopic = {
|
||||
...topic,
|
||||
...update,
|
||||
} as ApiTopic;
|
||||
|
||||
if (!updatedTopic.id) return global;
|
||||
|
||||
global = updateChat(global, chatId, {
|
||||
topics: {
|
||||
...(chat.topics || {}),
|
||||
[topicId]: updatedTopic,
|
||||
},
|
||||
});
|
||||
|
||||
global = updateThread(global, chatId, updatedTopic.id, {
|
||||
firstMessageId: updatedTopic.id,
|
||||
});
|
||||
|
||||
global = updateThreadInfo(global, chatId, updatedTopic.id, {
|
||||
lastMessageId: updatedTopic.lastMessageId,
|
||||
threadId: updatedTopic.id,
|
||||
chatId,
|
||||
});
|
||||
|
||||
return global;
|
||||
}
|
||||
|
||||
export function deleteTopic<T extends GlobalState>(
|
||||
global: T, chatId: string, topicId: number,
|
||||
) {
|
||||
const chat = selectChat(global, chatId);
|
||||
const topics = chat?.topics || {};
|
||||
global = updateChat(global, chatId, {
|
||||
topics: omit(topics, [topicId]),
|
||||
});
|
||||
|
||||
return global;
|
||||
}
|
||||
|
||||
export function addSimilarChannels<T extends GlobalState>(
|
||||
global: T,
|
||||
chatId: string,
|
||||
|
||||
@ -14,3 +14,4 @@ export * from './stories';
|
||||
export * from './translations';
|
||||
export * from './peers';
|
||||
export * from './password';
|
||||
export * from './topics';
|
||||
|
||||
@ -21,7 +21,6 @@ import {
|
||||
mergeIdRanges, orderHistoryIds, orderPinnedIds,
|
||||
} from '../helpers';
|
||||
import {
|
||||
selectChat,
|
||||
selectChatMessage,
|
||||
selectChatMessages,
|
||||
selectChatScheduledMessages,
|
||||
@ -43,10 +42,7 @@ import { removeIdFromSearchResults } from './middleSearch';
|
||||
import { updateTabState } from './tabs';
|
||||
import { clearMessageTranslation } from './translations';
|
||||
|
||||
type MessageStoreSections = {
|
||||
byId: Record<number, ApiMessage>;
|
||||
threadsById: Record<number, Thread>;
|
||||
};
|
||||
type MessageStoreSections = GlobalState['messages']['byChatId'][string];
|
||||
|
||||
export function updateCurrentMessageList<T extends GlobalState>(
|
||||
global: T,
|
||||
@ -128,7 +124,7 @@ export function updateThread<T extends GlobalState>(
|
||||
});
|
||||
}
|
||||
|
||||
function updateMessageStore<T extends GlobalState>(
|
||||
export function updateMessageStore<T extends GlobalState>(
|
||||
global: T, chatId: string, update: Partial<MessageStoreSections>,
|
||||
): T {
|
||||
const current = global.messages.byChatId[chatId] || { byId: {}, threadsById: {} };
|
||||
@ -823,32 +819,6 @@ export function updateThreadUnreadFromForwardedMessage<T extends GlobalState>(
|
||||
return global;
|
||||
}
|
||||
|
||||
export function updateTopicLastMessageId<T extends GlobalState>(
|
||||
global: T, chatId: string, threadId: ThreadId, lastMessageId: number,
|
||||
) {
|
||||
const chat = selectChat(global, chatId);
|
||||
if (!chat?.topics?.[threadId]) return global;
|
||||
return {
|
||||
...global,
|
||||
chats: {
|
||||
...global.chats,
|
||||
byId: {
|
||||
...global.chats.byId,
|
||||
[chatId]: {
|
||||
...chat,
|
||||
topics: {
|
||||
...chat.topics,
|
||||
[threadId]: {
|
||||
...chat.topics[threadId],
|
||||
lastMessageId,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
export function addActiveMediaDownload<T extends GlobalState>(
|
||||
global: T,
|
||||
mediaHash: string,
|
||||
|
||||
138
src/global/reducers/topics.ts
Normal file
138
src/global/reducers/topics.ts
Normal file
@ -0,0 +1,138 @@
|
||||
import type { ApiTopic } from '../../api/types';
|
||||
import type { GlobalState, TopicsInfo } from '../types';
|
||||
|
||||
import { buildCollectionByKey, omit, unique } from '../../util/iteratees';
|
||||
import {
|
||||
selectChat, selectTopic, selectTopics,
|
||||
selectTopicsInfo,
|
||||
} from '../selectors';
|
||||
import { updateThread, updateThreadInfo } from './messages';
|
||||
|
||||
function updateTopicsStore<T extends GlobalState>(
|
||||
global: T, chatId: string, update: Partial<TopicsInfo>,
|
||||
) {
|
||||
const info = global.chats.topicsInfoById[chatId] || {};
|
||||
|
||||
global = {
|
||||
...global,
|
||||
chats: {
|
||||
...global.chats,
|
||||
topicsInfoById: {
|
||||
...global.chats.topicsInfoById,
|
||||
[chatId]: {
|
||||
...info,
|
||||
...update,
|
||||
},
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
return global;
|
||||
}
|
||||
|
||||
export function updateListedTopicIds<T extends GlobalState>(
|
||||
global: T, chatId: string, topicIds: number[],
|
||||
): T {
|
||||
const listedIds = selectTopicsInfo(global, chatId)?.listedTopicIds || [];
|
||||
return updateTopicsStore(global, chatId, {
|
||||
listedTopicIds: unique([
|
||||
...listedIds,
|
||||
...topicIds,
|
||||
]),
|
||||
});
|
||||
}
|
||||
|
||||
export function updateTopics<T extends GlobalState>(
|
||||
global: T, chatId: string, topicsCount: number, topics: ApiTopic[],
|
||||
): T {
|
||||
const oldTopics = selectTopics(global, chatId);
|
||||
const newTopics = buildCollectionByKey(topics, 'id');
|
||||
|
||||
global = updateTopicsStore(global, chatId, {
|
||||
topicsById: {
|
||||
...oldTopics,
|
||||
...newTopics,
|
||||
},
|
||||
totalCount: topicsCount,
|
||||
});
|
||||
|
||||
topics.forEach((topic) => {
|
||||
global = updateThread(global, chatId, topic.id, {
|
||||
firstMessageId: topic.id,
|
||||
});
|
||||
|
||||
global = updateThreadInfo(global, chatId, topic.id, {
|
||||
lastMessageId: topic.lastMessageId,
|
||||
threadId: topic.id,
|
||||
chatId,
|
||||
});
|
||||
});
|
||||
|
||||
return global;
|
||||
}
|
||||
|
||||
export function updateTopic<T extends GlobalState>(
|
||||
global: T, chatId: string, topicId: number, update: Partial<ApiTopic>,
|
||||
): T {
|
||||
const chat = selectChat(global, chatId);
|
||||
|
||||
if (!chat) return global;
|
||||
|
||||
const topic = selectTopic(global, chatId, topicId);
|
||||
const oldTopics = selectTopics(global, chatId);
|
||||
|
||||
const updatedTopic = {
|
||||
...topic,
|
||||
...update,
|
||||
} as ApiTopic;
|
||||
|
||||
if (!updatedTopic.id) return global;
|
||||
|
||||
global = updateTopicsStore(global, chatId, {
|
||||
topicsById: {
|
||||
...oldTopics,
|
||||
[topicId]: updatedTopic,
|
||||
},
|
||||
});
|
||||
|
||||
global = updateThread(global, chatId, updatedTopic.id, {
|
||||
firstMessageId: updatedTopic.id,
|
||||
});
|
||||
|
||||
global = updateThreadInfo(global, chatId, updatedTopic.id, {
|
||||
lastMessageId: updatedTopic.lastMessageId,
|
||||
threadId: updatedTopic.id,
|
||||
chatId,
|
||||
});
|
||||
|
||||
return global;
|
||||
}
|
||||
|
||||
export function deleteTopic<T extends GlobalState>(
|
||||
global: T, chatId: string, topicId: number,
|
||||
) {
|
||||
const topics = selectTopics(global, chatId);
|
||||
if (!topics) return global;
|
||||
|
||||
global = updateTopicsStore(global, chatId, {
|
||||
topicsById: omit(topics, [topicId]),
|
||||
});
|
||||
|
||||
return global;
|
||||
}
|
||||
|
||||
export function updateTopicLastMessageId<T extends GlobalState>(
|
||||
global: T, chatId: string, threadId: number, lastMessageId: number,
|
||||
) {
|
||||
return updateTopic(global, chatId, threadId, {
|
||||
lastMessageId,
|
||||
});
|
||||
}
|
||||
|
||||
export function replacePinnedTopicIds<T extends GlobalState>(
|
||||
global: T, chatId: string, pinnedTopicIds: number[],
|
||||
) {
|
||||
return updateTopicsStore(global, chatId, {
|
||||
orderedPinnedTopicIds: pinnedTopicIds,
|
||||
});
|
||||
}
|
||||
@ -12,3 +12,4 @@ export * from './statistics';
|
||||
export * from './stories';
|
||||
export * from './tabs';
|
||||
export * from './peers';
|
||||
export * from './topics';
|
||||
|
||||
@ -70,6 +70,7 @@ import { selectPeer } from './peers';
|
||||
import { selectPeerStory } from './stories';
|
||||
import { selectIsStickerFavorite } from './symbols';
|
||||
import { selectTabState } from './tabs';
|
||||
import { selectTopic } from './topics';
|
||||
import {
|
||||
selectBot, selectIsCurrentUserPremium, selectUser, selectUserStatus,
|
||||
} from './users';
|
||||
@ -483,17 +484,21 @@ export function selectForwardedSender<T extends GlobalState>(
|
||||
return undefined;
|
||||
}
|
||||
|
||||
export function selectTopicFromMessage<T extends GlobalState>(global: T, message: ApiMessage) {
|
||||
const { chatId } = message;
|
||||
const chat = selectChat(global, chatId);
|
||||
if (!chat?.isForum) return undefined;
|
||||
|
||||
const threadId = selectThreadIdFromMessage(global, message);
|
||||
return selectTopic(global, chatId, threadId);
|
||||
}
|
||||
|
||||
const MAX_MESSAGES_TO_DELETE_OWNER_TOPIC = 10;
|
||||
export function selectCanDeleteOwnerTopic<T extends GlobalState>(global: T, chatId: string, topicId: number) {
|
||||
const chat = selectChat(global, chatId);
|
||||
if (!chat) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (chat.topics?.[topicId] && !chat.topics?.[topicId].isOwner) return false;
|
||||
|
||||
const thread = global.messages.byChatId[chatId]?.threadsById[topicId];
|
||||
const topic = selectTopic(global, chatId, topicId);
|
||||
if (topic && !topic.isOwner) return false;
|
||||
|
||||
const thread = selectThread(global, chatId, topicId);
|
||||
if (!thread) return false;
|
||||
|
||||
const { listedIds } = thread;
|
||||
@ -576,15 +581,6 @@ export function selectThreadIdFromMessage<T extends GlobalState>(global: T, mess
|
||||
return replyToTopId || replyToMsgId || GENERAL_TOPIC_ID;
|
||||
}
|
||||
|
||||
export function selectTopicFromMessage<T extends GlobalState>(global: T, message: ApiMessage) {
|
||||
const { chatId } = message;
|
||||
const chat = selectChat(global, chatId);
|
||||
if (!chat?.isForum) return undefined;
|
||||
|
||||
const threadId = selectThreadIdFromMessage(global, message);
|
||||
return chat.topics?.[threadId];
|
||||
}
|
||||
|
||||
export function selectCanReplyToMessage<T extends GlobalState>(global: T, message: ApiMessage, threadId: ThreadId) {
|
||||
const chat = selectChat(global, message.chatId);
|
||||
if (!chat || chat.isRestricted || chat.isForbidden) return false;
|
||||
@ -597,7 +593,8 @@ export function selectCanReplyToMessage<T extends GlobalState>(global: T, messag
|
||||
const threadInfo = selectThreadInfo(global, message.chatId, threadId);
|
||||
const isMessageThread = Boolean(!threadInfo?.isCommentsInfo && threadInfo?.fromChannelId);
|
||||
const chatFullInfo = selectChatFullInfo(global, chat.id);
|
||||
const canPostInChat = getCanPostInChat(chat, threadId, isMessageThread, chatFullInfo);
|
||||
const topic = selectTopic(global, chat.id, threadId);
|
||||
const canPostInChat = getCanPostInChat(chat, topic, isMessageThread, chatFullInfo);
|
||||
if (!canPostInChat) return false;
|
||||
|
||||
const messageTopic = selectTopicFromMessage(global, message);
|
||||
|
||||
14
src/global/selectors/topics.ts
Normal file
14
src/global/selectors/topics.ts
Normal file
@ -0,0 +1,14 @@
|
||||
import type { ThreadId } from '../../types';
|
||||
import type { GlobalState, TopicsInfo } from '../types';
|
||||
|
||||
export function selectTopicsInfo<T extends GlobalState>(global: T, chatId: string): TopicsInfo | undefined {
|
||||
return global.chats.topicsInfoById[chatId];
|
||||
}
|
||||
|
||||
export function selectTopics<T extends GlobalState>(global: T, chatId: string) {
|
||||
return selectTopicsInfo(global, chatId)?.topicsById;
|
||||
}
|
||||
|
||||
export function selectTopic<T extends GlobalState>(global: T, chatId: string, threadId: ThreadId) {
|
||||
return selectTopicsInfo(global, chatId)?.topicsById?.[threadId];
|
||||
}
|
||||
@ -74,6 +74,7 @@ import type {
|
||||
ApiThemeParameters,
|
||||
ApiThreadInfo,
|
||||
ApiTimezone,
|
||||
ApiTopic,
|
||||
ApiTranscription,
|
||||
ApiTypeStoryView,
|
||||
ApiTypingStatus,
|
||||
@ -213,6 +214,13 @@ export interface ServiceNotification {
|
||||
isDeleted?: boolean;
|
||||
}
|
||||
|
||||
export interface TopicsInfo {
|
||||
totalCount: number;
|
||||
topicsById: Record<ThreadId, ApiTopic>;
|
||||
listedTopicIds?: number[];
|
||||
orderedPinnedTopicIds?: number[];
|
||||
}
|
||||
|
||||
export type ApiLimitType =
|
||||
| 'uploadMaxFileparts'
|
||||
| 'stickersFaved'
|
||||
@ -968,6 +976,7 @@ export type GlobalState = {
|
||||
all?: Record<string, number>;
|
||||
saved?: Record<string, number>;
|
||||
};
|
||||
topicsInfoById: Record<string, TopicsInfo>;
|
||||
loadingParameters: Record<ChatListType, {
|
||||
nextOffsetId?: number;
|
||||
nextOffsetPeerId?: string;
|
||||
|
||||
@ -18,6 +18,7 @@ import {
|
||||
selectNotifyExceptions,
|
||||
selectNotifySettings,
|
||||
selectTabState,
|
||||
selectTopics,
|
||||
} from '../global/selectors';
|
||||
import arePropsShallowEqual from './arePropsShallowEqual';
|
||||
import { createCallbackManager } from './callbacks';
|
||||
@ -74,6 +75,7 @@ let prevGlobal: {
|
||||
isSavedFolderFullyLoaded?: boolean;
|
||||
lastAllMessageIds?: GlobalState['chats']['lastMessageIds']['all'];
|
||||
lastSavedMessageIds?: GlobalState['chats']['lastMessageIds']['saved'];
|
||||
topicsInfoById: GlobalState['chats']['topicsInfoById'];
|
||||
chatsById: Record<string, ApiChat>;
|
||||
foldersById: Record<string, ApiChatFolder>;
|
||||
usersById: Record<string, ApiUser>;
|
||||
@ -215,6 +217,7 @@ function updateFolderManager(global: GlobalState) {
|
||||
const areChatsChanged = global.chats.byId !== prevGlobal.chatsById;
|
||||
const areSavedLastMessageIdsChanged = global.chats.lastMessageIds.saved !== prevGlobal.lastSavedMessageIds;
|
||||
const areAllLastMessageIdsChanged = global.chats.lastMessageIds.all !== prevGlobal.lastAllMessageIds;
|
||||
const areTopicsChanged = global.chats.topicsInfoById !== prevGlobal.topicsInfoById;
|
||||
const areUsersChanged = global.users.byId !== prevGlobal.usersById;
|
||||
const areNotifySettingsChanged = selectNotifySettings(global) !== prevGlobal.notifySettings;
|
||||
const areNotifyExceptionsChanged = selectNotifyExceptions(global) !== prevGlobal.notifyExceptions;
|
||||
@ -229,7 +232,7 @@ function updateFolderManager(global: GlobalState) {
|
||||
|
||||
if (!(
|
||||
isAllFolderChanged || isArchivedFolderChanged || isSavedFolderChanged || areFoldersChanged
|
||||
|| areChatsChanged || areUsersChanged || areNotifySettingsChanged || areNotifyExceptionsChanged
|
||||
|| areChatsChanged || areUsersChanged || areTopicsChanged || areNotifySettingsChanged || areNotifyExceptionsChanged
|
||||
|| areSavedLastMessageIdsChanged || areAllLastMessageIdsChanged
|
||||
)
|
||||
) {
|
||||
@ -515,8 +518,9 @@ function buildChatSummary<T extends GlobalState>(
|
||||
const {
|
||||
id, type, isRestricted, isNotJoined, migratedTo, folderId,
|
||||
unreadCount: chatUnreadCount, unreadMentionsCount: chatUnreadMentionsCount, hasUnreadMark,
|
||||
isForum, topics,
|
||||
isForum,
|
||||
} = chat;
|
||||
const topics = selectTopics(global, chat.id);
|
||||
|
||||
const { unreadCount, unreadMentionsCount } = isForum
|
||||
? Object.values(topics || {}).reduce((acc, topic) => {
|
||||
@ -805,6 +809,7 @@ function buildInitials() {
|
||||
foldersById: {},
|
||||
chatsById: {},
|
||||
usersById: {},
|
||||
topicsInfoById: {},
|
||||
notifySettings: {} as NotifySettings,
|
||||
notifyExceptions: {},
|
||||
},
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user