Topics: Fix loading after refresh (#6967)
This commit is contained in:
parent
73b5ebeeba
commit
d7082efd93
@ -17,7 +17,7 @@ import {
|
|||||||
selectMonoforumChannel,
|
selectMonoforumChannel,
|
||||||
selectPeer,
|
selectPeer,
|
||||||
selectTabState,
|
selectTabState,
|
||||||
selectTopics,
|
selectTopicsInfo,
|
||||||
selectUserStatus,
|
selectUserStatus,
|
||||||
} from '../../../global/selectors';
|
} from '../../../global/selectors';
|
||||||
import { selectAnimationLevel } from '../../../global/selectors/sharedState';
|
import { selectAnimationLevel } from '../../../global/selectors/sharedState';
|
||||||
@ -135,15 +135,16 @@ const ChatOrUserPicker = ({
|
|||||||
useInputFocusOnOpen(searchRef, isOpen && activeKey === CHAT_LIST_SLIDE, resetSearch);
|
useInputFocusOnOpen(searchRef, isOpen && activeKey === CHAT_LIST_SLIDE, resetSearch);
|
||||||
useInputFocusOnOpen(topicSearchRef, isOpen && activeKey === TOPIC_LIST_SLIDE);
|
useInputFocusOnOpen(topicSearchRef, isOpen && activeKey === TOPIC_LIST_SLIDE);
|
||||||
|
|
||||||
const selectTopicsById = useCallback((global: GlobalState) => {
|
const selectForumTopicsInfo = useCallback((global: GlobalState) => {
|
||||||
if (!forumId) {
|
if (!forumId) {
|
||||||
return undefined;
|
return undefined;
|
||||||
}
|
}
|
||||||
|
|
||||||
return selectTopics(global, forumId);
|
return selectTopicsInfo(global, forumId);
|
||||||
}, [forumId]);
|
}, [forumId]);
|
||||||
|
|
||||||
const forumTopicsById = useSelector(selectTopicsById);
|
const topicsInfo = useSelector(selectForumTopicsInfo);
|
||||||
|
const forumTopicsById = topicsInfo?.topicsById;
|
||||||
|
|
||||||
const [topicIds, topics] = useMemo(() => {
|
const [topicIds, topics] = useMemo(() => {
|
||||||
const global = getGlobal();
|
const global = getGlobal();
|
||||||
@ -194,7 +195,7 @@ const ChatOrUserPicker = ({
|
|||||||
const chatId = viewportIds[index === -1 ? 0 : index];
|
const chatId = viewportIds[index === -1 ? 0 : index];
|
||||||
const chat = chatsById[chatId];
|
const chat = chatsById[chatId];
|
||||||
if (chat?.isForum) {
|
if (chat?.isForum) {
|
||||||
if (!forumTopicsById) loadTopics({ chatId });
|
if (!topicsInfo || topicsInfo.isCache) loadTopics({ chatId });
|
||||||
setForumId(chatId);
|
setForumId(chatId);
|
||||||
} else {
|
} else {
|
||||||
onSelectChatOrUser(chatId);
|
onSelectChatOrUser(chatId);
|
||||||
@ -218,7 +219,7 @@ const ChatOrUserPicker = ({
|
|||||||
const chatsById = getGlobal().chats.byId;
|
const chatsById = getGlobal().chats.byId;
|
||||||
const chat = chatsById?.[chatId];
|
const chat = chatsById?.[chatId];
|
||||||
if (chat?.isForum) {
|
if (chat?.isForum) {
|
||||||
if (!forumTopicsById) loadTopics({ chatId });
|
if (!topicsInfo || topicsInfo.isCache) loadTopics({ chatId });
|
||||||
setForumId(chatId);
|
setForumId(chatId);
|
||||||
resetSearch();
|
resetSearch();
|
||||||
} else {
|
} else {
|
||||||
|
|||||||
@ -18,7 +18,7 @@ import type {
|
|||||||
import type { ObserveFn } from '../../../hooks/useIntersectionObserver';
|
import type { ObserveFn } from '../../../hooks/useIntersectionObserver';
|
||||||
import type { ChatAnimationTypes } from './hooks';
|
import type { ChatAnimationTypes } from './hooks';
|
||||||
import { MAIN_THREAD_ID } from '../../../api/types';
|
import { MAIN_THREAD_ID } from '../../../api/types';
|
||||||
import { StoryViewerOrigin } from '../../../types';
|
import { StoryViewerOrigin, type TopicsInfo } from '../../../types';
|
||||||
|
|
||||||
import { ALL_FOLDER_ID, UNMUTE_TIMESTAMP } from '../../../config';
|
import { ALL_FOLDER_ID, UNMUTE_TIMESTAMP } from '../../../config';
|
||||||
import {
|
import {
|
||||||
@ -106,7 +106,7 @@ type StateProps = {
|
|||||||
chat?: ApiChat;
|
chat?: ApiChat;
|
||||||
monoforumChannel?: ApiChat;
|
monoforumChannel?: ApiChat;
|
||||||
lastMessageStory?: ApiTypeStory;
|
lastMessageStory?: ApiTypeStory;
|
||||||
listedTopicIds?: number[];
|
topicsInfo?: TopicsInfo;
|
||||||
isMuted?: boolean;
|
isMuted?: boolean;
|
||||||
user?: ApiUser;
|
user?: ApiUser;
|
||||||
userStatus?: ApiUserStatus;
|
userStatus?: ApiUserStatus;
|
||||||
@ -139,7 +139,7 @@ const Chat: FC<OwnProps & StateProps> = ({
|
|||||||
shiftDiff,
|
shiftDiff,
|
||||||
animationType,
|
animationType,
|
||||||
isPinned,
|
isPinned,
|
||||||
listedTopicIds,
|
topicsInfo,
|
||||||
observeIntersection,
|
observeIntersection,
|
||||||
chat,
|
chat,
|
||||||
monoforumChannel,
|
monoforumChannel,
|
||||||
@ -204,6 +204,7 @@ const Chat: FC<OwnProps & StateProps> = ({
|
|||||||
|
|
||||||
const { isForum, isForumAsMessages, isMonoforum } = chat || {};
|
const { isForum, isForumAsMessages, isMonoforum } = chat || {};
|
||||||
|
|
||||||
|
const listedTopicIds = topicsInfo?.listedTopicIds;
|
||||||
const shouldForceNonForumView = chat?.isBotForum && listedTopicIds && !listedTopicIds.length;
|
const shouldForceNonForumView = chat?.isBotForum && listedTopicIds && !listedTopicIds.length;
|
||||||
|
|
||||||
useEnsureMessage(isSavedDialog ? currentUserId : chatId, lastMessageId, lastMessage);
|
useEnsureMessage(isSavedDialog ? currentUserId : chatId, lastMessageId, lastMessage);
|
||||||
@ -373,10 +374,10 @@ const Chat: FC<OwnProps & StateProps> = ({
|
|||||||
|
|
||||||
// Load the forum topics to display unread count badge
|
// Load the forum topics to display unread count badge
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (isIntersecting && isForum && isSynced && listedTopicIds === undefined) {
|
if (isIntersecting && isForum && isSynced && (!topicsInfo || topicsInfo.isCache)) {
|
||||||
loadTopics({ chatId });
|
loadTopics({ chatId });
|
||||||
}
|
}
|
||||||
}, [chatId, listedTopicIds, isSynced, isForum, isIntersecting]);
|
}, [chatId, topicsInfo, isSynced, isForum, isIntersecting]);
|
||||||
|
|
||||||
const isOnline = user && userStatus && isUserOnline(user, userStatus);
|
const isOnline = user && userStatus && isUserOnline(user, userStatus);
|
||||||
const { hasShownClass: isAvatarOnlineShown } = useShowTransitionDeprecated(isOnline);
|
const { hasShownClass: isAvatarOnlineShown } = useShowTransitionDeprecated(isOnline);
|
||||||
@ -598,7 +599,7 @@ export default memo(withGlobal<OwnProps>(
|
|||||||
lastMessage,
|
lastMessage,
|
||||||
lastMessageId,
|
lastMessageId,
|
||||||
currentUserId: global.currentUserId!,
|
currentUserId: global.currentUserId!,
|
||||||
listedTopicIds: topicsInfo?.listedTopicIds,
|
topicsInfo,
|
||||||
isSynced: global.isSynced,
|
isSynced: global.isSynced,
|
||||||
lastMessageStory,
|
lastMessageStory,
|
||||||
isAccountFrozen,
|
isAccountFrozen,
|
||||||
|
|||||||
@ -34,7 +34,6 @@ import { mapTruthyValues, mapValues } from '../../../../util/iteratees';
|
|||||||
|
|
||||||
import useSelector from '../../../../hooks/data/useSelector';
|
import useSelector from '../../../../hooks/data/useSelector';
|
||||||
import useAppLayout from '../../../../hooks/useAppLayout';
|
import useAppLayout from '../../../../hooks/useAppLayout';
|
||||||
import useEffectWithPrevDeps from '../../../../hooks/useEffectWithPrevDeps';
|
|
||||||
import useHistoryBack from '../../../../hooks/useHistoryBack';
|
import useHistoryBack from '../../../../hooks/useHistoryBack';
|
||||||
import useInfiniteScroll from '../../../../hooks/useInfiniteScroll';
|
import useInfiniteScroll from '../../../../hooks/useInfiniteScroll';
|
||||||
import { useIntersectionObserver, useOnIntersect } from '../../../../hooks/useIntersectionObserver';
|
import { useIntersectionObserver, useOnIntersect } from '../../../../hooks/useIntersectionObserver';
|
||||||
@ -97,13 +96,11 @@ const ForumPanel = ({
|
|||||||
const { isMobile } = useAppLayout();
|
const { isMobile } = useAppLayout();
|
||||||
const chatId = chat?.id;
|
const chatId = chat?.id;
|
||||||
|
|
||||||
useEffectWithPrevDeps(([prevIsSynced]) => {
|
useEffect(() => {
|
||||||
if (!isSynced) return;
|
if (!chatId || !isSynced) return;
|
||||||
const hasJustSynced = prevIsSynced === false;
|
if (topicsInfo && !topicsInfo.isCache) return;
|
||||||
if (chatId && (hasJustSynced || !topicsInfo)) {
|
loadTopics({ chatId });
|
||||||
loadTopics({ chatId, force: hasJustSynced });
|
}, [chatId, topicsInfo, isSynced]);
|
||||||
}
|
|
||||||
}, [isSynced, chatId, topicsInfo]);
|
|
||||||
|
|
||||||
const [isScrolled, setIsScrolled] = useState(false);
|
const [isScrolled, setIsScrolled] = useState(false);
|
||||||
const lang = useLang();
|
const lang = useLang();
|
||||||
|
|||||||
@ -2622,12 +2622,17 @@ addActionHandler('loadTopics', async (global, actions, payload): Promise<void> =
|
|||||||
if (!chat) return;
|
if (!chat) return;
|
||||||
|
|
||||||
const topicsInfo = selectTopicsInfo(global, chatId);
|
const topicsInfo = selectTopicsInfo(global, chatId);
|
||||||
|
const shouldRefreshFromStart = force || topicsInfo?.isCache;
|
||||||
|
|
||||||
if (!force && topicsInfo?.listedTopicIds && topicsInfo.listedTopicIds.length === topicsInfo.totalCount) {
|
if (
|
||||||
|
!shouldRefreshFromStart
|
||||||
|
&& topicsInfo?.listedTopicIds
|
||||||
|
&& topicsInfo.listedTopicIds.length === topicsInfo.totalCount
|
||||||
|
) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const offsetTopic = !force ? topicsInfo?.listedTopicIds?.reduce((acc, el) => {
|
const offsetTopic = !shouldRefreshFromStart ? topicsInfo?.listedTopicIds?.reduce((acc, el) => {
|
||||||
const topicThreadInfo = selectThreadInfo(global, chatId, el);
|
const topicThreadInfo = selectThreadInfo(global, chatId, el);
|
||||||
const accTopicThreadInfo = selectThreadInfo(global, chatId, acc);
|
const accTopicThreadInfo = selectThreadInfo(global, chatId, acc);
|
||||||
if (!topicThreadInfo?.lastMessageId) return acc;
|
if (!topicThreadInfo?.lastMessageId) return acc;
|
||||||
@ -2649,12 +2654,22 @@ addActionHandler('loadTopics', async (global, actions, payload): Promise<void> =
|
|||||||
if (!result) return;
|
if (!result) return;
|
||||||
|
|
||||||
global = getGlobal();
|
global = getGlobal();
|
||||||
|
const updatedTopicsInfo = selectTopicsInfo(global, chatId);
|
||||||
|
if (updatedTopicsInfo?.isCache) { // Reset local state
|
||||||
|
global = updateTopicsInfo(global, chatId, {
|
||||||
|
topicsById: {},
|
||||||
|
listedTopicIds: [],
|
||||||
|
orderedPinnedTopicIds: undefined,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
global = addMessages(global, result.messages);
|
global = addMessages(global, result.messages);
|
||||||
result.topics.forEach((topic) => {
|
result.topics.forEach((topic) => {
|
||||||
global = updateTopicWithState(global, chatId, topic);
|
global = updateTopicWithState(global, chatId, topic);
|
||||||
});
|
});
|
||||||
global = updateTopicsInfo(global, chatId, {
|
global = updateTopicsInfo(global, chatId, {
|
||||||
totalCount: result.count,
|
totalCount: result.count,
|
||||||
|
isCache: undefined,
|
||||||
});
|
});
|
||||||
global = updateListedTopicIds(global, chatId, result.topics.map((topicState) => topicState.topic.id));
|
global = updateListedTopicIds(global, chatId, result.topics.map((topicState) => topicState.topic.id));
|
||||||
Object.entries(result.draftsById || {}).forEach(([threadId, draft]) => {
|
Object.entries(result.draftsById || {}).forEach(([threadId, draft]) => {
|
||||||
|
|||||||
@ -5,7 +5,7 @@ import type {
|
|||||||
ApiAvailableReaction,
|
ApiAvailableReaction,
|
||||||
ApiMessage,
|
ApiMessage,
|
||||||
} from '../api/types';
|
} from '../api/types';
|
||||||
import type { MessageList, ThreadId } from '../types';
|
import type { MessageList, ThreadId, TopicsInfo } from '../types';
|
||||||
import type { ActionReturnType, GlobalState, SharedState } from './types';
|
import type { ActionReturnType, GlobalState, SharedState } from './types';
|
||||||
import { ApiMessageEntityTypes, MAIN_THREAD_ID } from '../api/types';
|
import { ApiMessageEntityTypes, MAIN_THREAD_ID } from '../api/types';
|
||||||
|
|
||||||
@ -649,10 +649,25 @@ function reduceChats<T extends GlobalState>(global: T): GlobalState['chats'] {
|
|||||||
all: pickTruthy(global.chats.lastMessageIds.all || {}, idsToSave),
|
all: pickTruthy(global.chats.lastMessageIds.all || {}, idsToSave),
|
||||||
saved: global.chats.lastMessageIds.saved,
|
saved: global.chats.lastMessageIds.saved,
|
||||||
},
|
},
|
||||||
topicsInfoById: pickTruthy(global.chats.topicsInfoById, currentChatIds),
|
topicsInfoById: reduceTopicsInfo(global.chats.topicsInfoById, currentChatIds),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function reduceTopicsInfo(
|
||||||
|
topicsInfoById: Record<string, TopicsInfo>, chatIds: string[],
|
||||||
|
): GlobalState['chats']['topicsInfoById'] {
|
||||||
|
const topicsInfoToSave = pickTruthy(topicsInfoById, chatIds);
|
||||||
|
|
||||||
|
return Object.entries(topicsInfoToSave).reduce((acc, [chatId, topicsInfo]) => {
|
||||||
|
acc[chatId] = {
|
||||||
|
...topicsInfo,
|
||||||
|
isCache: true,
|
||||||
|
};
|
||||||
|
|
||||||
|
return acc;
|
||||||
|
}, {} as GlobalState['chats']['topicsInfoById']);
|
||||||
|
}
|
||||||
|
|
||||||
function getTopPeerIds<T extends GlobalState>(global: T) {
|
function getTopPeerIds<T extends GlobalState>(global: T) {
|
||||||
return unique(Object.values(global.topPeerCategories).flatMap((category) => category?.peerIds || []));
|
return unique(Object.values(global.topPeerCategories).flatMap((category) => category?.peerIds || []));
|
||||||
}
|
}
|
||||||
|
|||||||
@ -674,6 +674,7 @@ export interface ServiceNotification {
|
|||||||
|
|
||||||
export interface TopicsInfo {
|
export interface TopicsInfo {
|
||||||
totalCount: number;
|
totalCount: number;
|
||||||
|
isCache?: true;
|
||||||
topicsById: Record<ThreadId, ApiTopic>;
|
topicsById: Record<ThreadId, ApiTopic>;
|
||||||
listedTopicIds?: number[];
|
listedTopicIds?: number[];
|
||||||
orderedPinnedTopicIds?: number[];
|
orderedPinnedTopicIds?: number[];
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user