Sync: Fix missing chats in folders, refactor and simplify
This commit is contained in:
parent
57dd323b78
commit
0e7f5658c4
@ -28,7 +28,7 @@ export type OwnProps = {
|
||||
filterRef: RefObject<HTMLInputElement>;
|
||||
filterPlaceholder: string;
|
||||
filter: string;
|
||||
loadMore: NoneToVoidFunction;
|
||||
loadMore?: NoneToVoidFunction;
|
||||
onFilterChange: (filter: string) => void;
|
||||
onSelectChatOrUser: (chatOrUserId: string) => void;
|
||||
onClose: NoneToVoidFunction;
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
import React, {
|
||||
FC, memo, useMemo, useCallback, useEffect,
|
||||
FC, memo, useMemo, useEffect,
|
||||
} from '../../../lib/teact/teact';
|
||||
import { getDispatch } from '../../../lib/teact/teactn';
|
||||
|
||||
@ -7,7 +7,6 @@ import { SettingsScreens } from '../../../types';
|
||||
import { FolderEditDispatch } from '../../../hooks/reducers/useFoldersReducer';
|
||||
|
||||
import {
|
||||
ALL_CHATS_PRELOAD_DISABLED,
|
||||
ALL_FOLDER_ID,
|
||||
ARCHIVED_FOLDER_ID,
|
||||
CHAT_HEIGHT_PX,
|
||||
@ -39,17 +38,10 @@ const ChatList: FC<OwnProps> = ({
|
||||
folderType,
|
||||
folderId,
|
||||
isActive,
|
||||
lastSyncTime,
|
||||
foldersDispatch,
|
||||
onScreenSelect,
|
||||
}) => {
|
||||
const {
|
||||
loadMoreChats,
|
||||
preloadTopChatMessages,
|
||||
preloadArchivedChats,
|
||||
openChat,
|
||||
openNextChat,
|
||||
} = getDispatch();
|
||||
const { openChat, openNextChat } = getDispatch();
|
||||
|
||||
const resolvedFolderId = (
|
||||
folderType === 'all' ? ALL_FOLDER_ID : folderType === 'archived' ? ARCHIVED_FOLDER_ID : folderId!
|
||||
@ -80,24 +72,7 @@ const ChatList: FC<OwnProps> = ({
|
||||
});
|
||||
}, [orderById, prevOrderById]);
|
||||
|
||||
const loadMoreOfType = useCallback(() => {
|
||||
loadMoreChats({ listType: folderType === 'archived' ? 'archived' : 'active' });
|
||||
}, [loadMoreChats, folderType]);
|
||||
|
||||
const [viewportIds, getMore] = useInfiniteScroll(
|
||||
lastSyncTime ? loadMoreOfType : undefined,
|
||||
orderedIds,
|
||||
undefined,
|
||||
CHAT_LIST_SLICE,
|
||||
folderType === 'all' && !ALL_CHATS_PRELOAD_DISABLED,
|
||||
);
|
||||
|
||||
useEffect(() => {
|
||||
if (lastSyncTime && folderType === 'all') {
|
||||
preloadTopChatMessages();
|
||||
preloadArchivedChats();
|
||||
}
|
||||
}, [lastSyncTime, folderType, preloadTopChatMessages, preloadArchivedChats]);
|
||||
const [viewportIds, getMore] = useInfiniteScroll(undefined, orderedIds, undefined, CHAT_LIST_SLICE);
|
||||
|
||||
// Support <Cmd>+<Digit> and <Alt>+<Up/Down> to navigate between chats
|
||||
useEffect(() => {
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
import React, {
|
||||
FC, memo, useMemo, useCallback,
|
||||
} from '../../../../lib/teact/teact';
|
||||
import { getDispatch, getGlobal } from '../../../../lib/teact/teactn';
|
||||
import { getGlobal } from '../../../../lib/teact/teactn';
|
||||
|
||||
import { SettingsScreens } from '../../../../types';
|
||||
|
||||
@ -38,8 +38,6 @@ const SettingsFoldersChatFilters: FC<OwnProps> = ({
|
||||
onScreenSelect,
|
||||
onReset,
|
||||
}) => {
|
||||
const { loadMoreChats } = getDispatch();
|
||||
|
||||
const { chatFilter } = state;
|
||||
const { selectedChatIds, selectedChatTypes } = selectChatFilters(state, mode, true);
|
||||
|
||||
@ -124,7 +122,6 @@ const SettingsFoldersChatFilters: FC<OwnProps> = ({
|
||||
onSelectedIdsChange={handleSelectedIdsChange}
|
||||
onSelectedChatTypesChange={handleSelectedChatTypesChange}
|
||||
onFilterChange={handleFilterChange}
|
||||
onLoadMore={loadMoreChats}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
@ -32,7 +32,6 @@ type OwnProps = {
|
||||
onSelectedIdsChange: (ids: string[]) => void;
|
||||
onSelectedChatTypesChange: (types: string[]) => void;
|
||||
onFilterChange: (value: string) => void;
|
||||
onLoadMore: () => void;
|
||||
};
|
||||
|
||||
// Focus slows down animation, also it breaks transition layout in Chrome
|
||||
@ -51,7 +50,6 @@ const SettingsFoldersChatsPicker: FC<OwnProps> = ({
|
||||
onSelectedIdsChange,
|
||||
onSelectedChatTypesChange,
|
||||
onFilterChange,
|
||||
onLoadMore,
|
||||
}) => {
|
||||
// eslint-disable-next-line no-null/no-null
|
||||
const inputRef = useRef<HTMLInputElement>(null);
|
||||
@ -156,7 +154,7 @@ const SettingsFoldersChatsPicker: FC<OwnProps> = ({
|
||||
);
|
||||
}
|
||||
|
||||
const [viewportIds, getMore] = useInfiniteScroll(onLoadMore, chatIds, Boolean(filterValue));
|
||||
const [viewportIds, getMore] = useInfiniteScroll(undefined, chatIds, Boolean(filterValue));
|
||||
|
||||
return (
|
||||
<div className="Picker SettingsFoldersChatsPicker">
|
||||
|
||||
@ -66,7 +66,6 @@ const SettingsFoldersEdit: FC<OwnProps & StateProps> = ({
|
||||
const {
|
||||
editChatFolder,
|
||||
addChatFolder,
|
||||
loadMoreChats,
|
||||
} = getDispatch();
|
||||
|
||||
const [animationData, setAnimationData] = useState<string>();
|
||||
@ -119,21 +118,6 @@ const SettingsFoldersEdit: FC<OwnProps & StateProps> = ({
|
||||
loadedActiveChatIds, loadedArchivedChatIds,
|
||||
]);
|
||||
|
||||
useEffect(() => {
|
||||
if (
|
||||
visibleIncludedChatIds.length < includedChatIds.length
|
||||
|| visibleExcludedChatIds.length < excludedChatIds.length
|
||||
) {
|
||||
loadMoreChats({ listType: 'active' });
|
||||
}
|
||||
}, [
|
||||
loadMoreChats,
|
||||
excludedChatIds.length,
|
||||
includedChatIds.length,
|
||||
visibleExcludedChatIds.length,
|
||||
visibleIncludedChatIds.length,
|
||||
]);
|
||||
|
||||
const lang = useLang();
|
||||
|
||||
useHistoryBack(isActive, onBack, onScreenSelect, state.mode === 'edit'
|
||||
|
||||
@ -43,7 +43,6 @@ const ForwardPicker: FC<OwnProps & StateProps> = ({
|
||||
const {
|
||||
setForwardChatId,
|
||||
exitForwardMode,
|
||||
loadMoreChats,
|
||||
} = getDispatch();
|
||||
|
||||
const lang = useLang();
|
||||
@ -105,7 +104,6 @@ const ForwardPicker: FC<OwnProps & StateProps> = ({
|
||||
filterPlaceholder={lang('ForwardTo')}
|
||||
filter={filter}
|
||||
onFilterChange={setFilter}
|
||||
loadMore={loadMoreChats}
|
||||
onSelectChatOrUser={handleSelectUser}
|
||||
onClose={exitForwardMode}
|
||||
onCloseAnimationEnd={unmarkIsShown}
|
||||
|
||||
@ -4,7 +4,7 @@ import React, {
|
||||
import { getDispatch, withGlobal } from '../../lib/teact/teactn';
|
||||
|
||||
import { LangCode } from '../../types';
|
||||
import { ApiMessage } from '../../api/types';
|
||||
import { ApiMessage, ApiUpdateAuthorizationStateType, ApiUpdateConnectionStateType } from '../../api/types';
|
||||
|
||||
import '../../modules/actions/all';
|
||||
import {
|
||||
@ -52,6 +52,8 @@ import CallFallbackConfirm from '../calls/CallFallbackConfirm.async';
|
||||
import './Main.scss';
|
||||
|
||||
type StateProps = {
|
||||
connectionState?: ApiUpdateConnectionStateType;
|
||||
authState?: ApiUpdateAuthorizationStateType;
|
||||
lastSyncTime?: number;
|
||||
isLeftColumnShown: boolean;
|
||||
isRightColumnShown: boolean;
|
||||
@ -81,6 +83,8 @@ let notificationInterval: number | undefined;
|
||||
let DEBUG_isLogged = false;
|
||||
|
||||
const Main: FC<StateProps> = ({
|
||||
connectionState,
|
||||
authState,
|
||||
lastSyncTime,
|
||||
isLeftColumnShown,
|
||||
isRightColumnShown,
|
||||
@ -102,6 +106,7 @@ const Main: FC<StateProps> = ({
|
||||
addedSetIds,
|
||||
}) => {
|
||||
const {
|
||||
sync,
|
||||
loadAnimatedEmojis,
|
||||
loadNotificationSettings,
|
||||
loadNotificationExceptions,
|
||||
@ -125,6 +130,12 @@ const Main: FC<StateProps> = ({
|
||||
console.log('>>> RENDER MAIN');
|
||||
}
|
||||
|
||||
useEffect(() => {
|
||||
if (connectionState === 'connectionStateReady' && authState === 'authorizationStateReady') {
|
||||
sync();
|
||||
}
|
||||
}, [connectionState, authState, sync]);
|
||||
|
||||
// Initial API calls
|
||||
useEffect(() => {
|
||||
if (lastSyncTime) {
|
||||
@ -356,6 +367,8 @@ export default memo(withGlobal(
|
||||
: undefined;
|
||||
|
||||
return {
|
||||
connectionState: global.connectionState,
|
||||
authState: global.authState,
|
||||
lastSyncTime: global.lastSyncTime,
|
||||
isLeftColumnShown: global.isLeftColumnShown,
|
||||
isRightColumnShown: selectIsRightColumnShown(global),
|
||||
|
||||
@ -61,7 +61,6 @@ export const GROUP_CALL_PARTICIPANTS_LIMIT = 100;
|
||||
export const REACTION_LIST_LIMIT = 100;
|
||||
|
||||
export const TOP_CHAT_MESSAGES_PRELOAD_LIMIT = 20;
|
||||
export const ALL_CHATS_PRELOAD_DISABLED = false;
|
||||
|
||||
export const SPONSORED_MESSAGE_CACHE_MS = 300000; // 5 min
|
||||
|
||||
|
||||
@ -503,7 +503,7 @@ export type GlobalState = {
|
||||
|
||||
export type ActionTypes = (
|
||||
// system
|
||||
'init' | 'reset' | 'disconnect' | 'initApi' | 'apiUpdate' | 'sync' | 'saveSession' | 'afterSync' |
|
||||
'init' | 'reset' | 'disconnect' | 'initApi' | 'apiUpdate' | 'sync' | 'saveSession' |
|
||||
'showNotification' | 'dismissNotification' | 'showDialog' | 'dismissDialog' |
|
||||
// ui
|
||||
'toggleChatInfo' | 'setIsUiReady' | 'addRecentEmoji' | 'addRecentSticker' | 'toggleLeftColumn' |
|
||||
@ -515,8 +515,7 @@ export type ActionTypes = (
|
||||
'setAuthPhoneNumber' | 'setAuthCode' | 'setAuthPassword' | 'signUp' | 'returnToAuthPhoneNumber' | 'signOut' |
|
||||
'setAuthRememberMe' | 'clearAuthError' | 'uploadProfilePhoto' | 'goToAuthQrCode' | 'clearCache' |
|
||||
// chats
|
||||
'preloadTopChatMessages' | 'preloadArchivedChats' | 'loadChats' | 'loadMoreChats' | 'openChat' |
|
||||
'openChatWithInfo' | 'openLinkedChat' |
|
||||
'preloadTopChatMessages' | 'loadAllChats' | 'openChat' | 'openChatWithInfo' | 'openLinkedChat' |
|
||||
'openSupportChat' | 'openTipsChat' | 'focusMessageInComments' |
|
||||
'loadFullChat' | 'loadTopChats' | 'requestChatUpdate' | 'updateChatMutedState' |
|
||||
'joinChannel' | 'leaveChannel' | 'deleteChannel' | 'toggleChatPinned' | 'toggleChatArchived' | 'toggleChatUnread' |
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
import { useCallback, useEffect, useRef } from '../lib/teact/teact';
|
||||
import { useCallback, useRef } from '../lib/teact/teact';
|
||||
import { LoadMoreDirection } from '../types';
|
||||
|
||||
import { areSortedArraysEqual } from '../util/iteratees';
|
||||
@ -15,7 +15,6 @@ const useInfiniteScroll = <ListId extends string | number>(
|
||||
listIds?: ListId[],
|
||||
isDisabled = false,
|
||||
listSlice = DEFAULT_LIST_SLICE,
|
||||
forceFullPreload = false,
|
||||
): [ListId[]?, GetMore?] => {
|
||||
const lastParamsRef = useRef<{
|
||||
direction?: LoadMoreDirection;
|
||||
@ -49,13 +48,6 @@ const useInfiniteScroll = <ListId extends string | number>(
|
||||
}
|
||||
}
|
||||
|
||||
useEffect(() => {
|
||||
if (listIds && !isDisabled && loadMoreBackwards && forceFullPreload) {
|
||||
const viewportIds = viewportIdsRef.current!;
|
||||
loadMoreBackwards({ offsetId: viewportIds[viewportIds.length - 1] });
|
||||
}
|
||||
}, [listIds, isDisabled, loadMoreBackwards, forceFullPreload]);
|
||||
|
||||
const getMore: GetMore = useCallback(({
|
||||
direction,
|
||||
noScroll,
|
||||
|
||||
@ -20,28 +20,16 @@ import {
|
||||
} from '../../../config';
|
||||
import { callApi } from '../../../api/gramjs';
|
||||
import {
|
||||
addChats,
|
||||
addUsers,
|
||||
addUserStatuses,
|
||||
replaceThreadParam,
|
||||
updateChatListIds,
|
||||
updateChats,
|
||||
updateChat,
|
||||
updateChatListSecondaryInfo,
|
||||
updateManagementProgress,
|
||||
leaveChat,
|
||||
addChats, addUsers, addUserStatuses, replaceThreadParam,
|
||||
updateChatListIds, updateChats, updateChat, updateChatListSecondaryInfo,
|
||||
updateManagementProgress, leaveChat, replaceUsers, replaceUserStatuses,
|
||||
replaceChats, replaceChatListIds,
|
||||
} from '../../reducers';
|
||||
import {
|
||||
selectChat,
|
||||
selectUser,
|
||||
selectChatListType,
|
||||
selectIsChatPinned,
|
||||
selectChatFolder,
|
||||
selectSupportChat,
|
||||
selectChatByUsername,
|
||||
selectThreadTopMessageId,
|
||||
selectCurrentMessageList,
|
||||
selectThreadInfo, selectCurrentChat, selectLastServiceNotification,
|
||||
selectChat, selectUser, selectChatListType, selectIsChatPinned,
|
||||
selectChatFolder, selectSupportChat, selectChatByUsername, selectThreadTopMessageId,
|
||||
selectCurrentMessageList, selectThreadInfo, selectCurrentChat, selectLastServiceNotification,
|
||||
selectThreadParam, selectChatMessage,
|
||||
} from '../../selectors';
|
||||
import { buildCollectionByKey, omit } from '../../../util/iteratees';
|
||||
import { debounce, pause, throttle } from '../../../util/schedulers';
|
||||
@ -54,9 +42,7 @@ import { selectGroupCall } from '../../selectors/calls';
|
||||
import { getOrderedIds } from '../../../util/folderManager';
|
||||
|
||||
const TOP_CHAT_MESSAGES_PRELOAD_INTERVAL = 100;
|
||||
const CHATS_PRELOAD_INTERVAL = 300;
|
||||
|
||||
const runThrottledForLoadChats = throttle((cb) => cb(), CHATS_PRELOAD_INTERVAL, true);
|
||||
const runThrottledForLoadTopChats = throttle((cb) => cb(), 3000, true);
|
||||
const runDebouncedForLoadFullChat = debounce((cb) => cb(), 500, false, true);
|
||||
|
||||
@ -179,43 +165,30 @@ addReducer('openTipsChat', (global, actions, payload) => {
|
||||
actions.openChatByUsername({ username: `${TIPS_USERNAME}${usernamePostfix}` });
|
||||
});
|
||||
|
||||
addReducer('loadMoreChats', (global, actions, payload) => {
|
||||
const { listType = 'active' } = payload!;
|
||||
const listIds = global.chats.listIds[listType as ('active' | 'archived')];
|
||||
const isFullyLoaded = global.chats.isFullyLoaded[listType as ('active' | 'archived')];
|
||||
addReducer('loadAllChats', (global, actions, payload) => {
|
||||
const listType = payload.listType as 'active' | 'archived';
|
||||
let { shouldReplace, onReplace } = payload;
|
||||
|
||||
if (isFullyLoaded) {
|
||||
return;
|
||||
}
|
||||
|
||||
const oldestChat = listIds
|
||||
? listIds
|
||||
.map((id) => global.chats.byId[id])
|
||||
.filter((chat) => Boolean(chat?.lastMessage) && !selectIsChatPinned(global, chat.id))
|
||||
.sort((chat1, chat2) => (chat1.lastMessage!.date - chat2.lastMessage!.date))[0]
|
||||
: undefined;
|
||||
|
||||
if (oldestChat) {
|
||||
runThrottledForLoadChats(() => loadChats(listType, oldestChat.id, oldestChat.lastMessage!.date));
|
||||
} else {
|
||||
runThrottledForLoadChats(() => loadChats(listType));
|
||||
}
|
||||
});
|
||||
|
||||
addReducer('preloadArchivedChats', () => {
|
||||
(async () => {
|
||||
while (!getGlobal().chats.isFullyLoaded.archived) {
|
||||
const currentGlobal = getGlobal();
|
||||
const listIds = currentGlobal.chats.listIds.archived;
|
||||
while (shouldReplace || !global.chats.isFullyLoaded[listType]) {
|
||||
const listIds = !shouldReplace && global.chats.listIds[listType];
|
||||
const oldestChat = listIds
|
||||
? listIds
|
||||
.map((id) => currentGlobal.chats.byId[id])
|
||||
.filter((chat) => Boolean(chat?.lastMessage) && !selectIsChatPinned(currentGlobal, chat.id))
|
||||
/* eslint-disable @typescript-eslint/no-loop-func */
|
||||
.map((id) => global.chats.byId[id])
|
||||
.filter((chat) => Boolean(chat?.lastMessage) && !selectIsChatPinned(global, chat.id))
|
||||
/* eslint-enable @typescript-eslint/no-loop-func */
|
||||
.sort((chat1, chat2) => (chat1.lastMessage!.date - chat2.lastMessage!.date))[0]
|
||||
: undefined;
|
||||
|
||||
await loadChats('archived', oldestChat?.id, oldestChat?.lastMessage!.date);
|
||||
await pause(CHATS_PRELOAD_INTERVAL);
|
||||
await loadChats(listType, oldestChat?.id, oldestChat?.lastMessage!.date, shouldReplace);
|
||||
|
||||
if (shouldReplace) {
|
||||
onReplace?.();
|
||||
}
|
||||
|
||||
global = getGlobal();
|
||||
shouldReplace = false;
|
||||
}
|
||||
})();
|
||||
});
|
||||
@ -1011,14 +984,16 @@ addReducer('setChatEnabledReactions', (global, actions, payload) => {
|
||||
})();
|
||||
});
|
||||
|
||||
async function loadChats(listType: 'active' | 'archived', offsetId?: string, offsetDate?: number) {
|
||||
async function loadChats(
|
||||
listType: 'active' | 'archived', offsetId?: string, offsetDate?: number, shouldReplace = false,
|
||||
) {
|
||||
let global = getGlobal();
|
||||
|
||||
const result = await callApi('fetchChats', {
|
||||
limit: CHAT_LIST_LOAD_SLICE,
|
||||
offsetDate,
|
||||
archived: listType === 'archived',
|
||||
withPinned: global.chats.orderedPinnedIds[listType] === undefined,
|
||||
withPinned: shouldReplace,
|
||||
serverTimeOffset: global.serverTimeOffset,
|
||||
lastLocalServiceMessage: selectLastServiceNotification(global)?.message,
|
||||
});
|
||||
@ -1035,11 +1010,44 @@ async function loadChats(listType: 'active' | 'archived', offsetId?: string, off
|
||||
|
||||
global = getGlobal();
|
||||
|
||||
global = addUsers(global, buildCollectionByKey(result.users, 'id'));
|
||||
global = addUserStatuses(global, result.userStatusesById);
|
||||
if (shouldReplace && listType === 'active') {
|
||||
const visibleChats = [];
|
||||
const visibleUsers = [];
|
||||
|
||||
const currentChat = selectCurrentChat(global);
|
||||
if (currentChat) {
|
||||
const { threadId } = selectCurrentMessageList(global)!;
|
||||
visibleChats.push(currentChat);
|
||||
const messageIds = selectThreadParam(global, currentChat.id, threadId, 'viewportIds');
|
||||
const messageSenders = messageIds ? messageIds
|
||||
.map((messageId) => {
|
||||
const { senderId } = selectChatMessage(global, currentChat.id, messageId) || {};
|
||||
return senderId ? selectUser(global, senderId) : undefined;
|
||||
})
|
||||
.filter(Boolean) : [];
|
||||
visibleUsers.push(...messageSenders);
|
||||
}
|
||||
|
||||
if (global.currentUserId && global.users.byId[global.currentUserId]) {
|
||||
visibleUsers.push(global.users.byId[global.currentUserId]);
|
||||
}
|
||||
|
||||
global = replaceUsers(global, buildCollectionByKey(visibleUsers.concat(result.users), 'id'));
|
||||
global = replaceUserStatuses(global, result.userStatusesById);
|
||||
global = replaceChats(global, buildCollectionByKey(visibleChats.concat(result.chats), 'id'));
|
||||
global = replaceChatListIds(global, listType, chatIds);
|
||||
} else if (shouldReplace && listType === 'archived') {
|
||||
global = addUsers(global, buildCollectionByKey(result.users, 'id'));
|
||||
global = addUserStatuses(global, result.userStatusesById);
|
||||
global = updateChats(global, buildCollectionByKey(result.chats, 'id'));
|
||||
global = replaceChatListIds(global, listType, chatIds);
|
||||
} else {
|
||||
global = addUsers(global, buildCollectionByKey(result.users, 'id'));
|
||||
global = addUserStatuses(global, result.userStatusesById);
|
||||
global = updateChats(global, buildCollectionByKey(result.chats, 'id'));
|
||||
global = updateChatListIds(global, listType, chatIds);
|
||||
}
|
||||
|
||||
global = updateChats(global, buildCollectionByKey(result.chats, 'id'));
|
||||
global = updateChatListIds(global, listType, chatIds);
|
||||
global = updateChatListSecondaryInfo(global, listType, result);
|
||||
|
||||
Object.keys(result.draftsById).forEach((chatId) => {
|
||||
|
||||
@ -3,23 +3,17 @@ import {
|
||||
} from '../../../lib/teact/teactn';
|
||||
|
||||
import {
|
||||
ApiChat, ApiFormattedText, ApiMessage, ApiUser, MAIN_THREAD_ID,
|
||||
ApiChat, ApiFormattedText, ApiMessage, MAIN_THREAD_ID,
|
||||
} from '../../../api/types';
|
||||
|
||||
import {
|
||||
CHAT_LIST_LOAD_SLICE, DEBUG, MESSAGE_LIST_SLICE, SERVICE_NOTIFICATIONS_USER_ID,
|
||||
DEBUG, MESSAGE_LIST_SLICE, SERVICE_NOTIFICATIONS_USER_ID,
|
||||
} from '../../../config';
|
||||
import { callApi } from '../../../api/gramjs';
|
||||
import { buildCollectionByKey } from '../../../util/iteratees';
|
||||
import {
|
||||
replaceChatListIds,
|
||||
replaceChats,
|
||||
replaceUsers,
|
||||
replaceUserStatuses,
|
||||
updateUsers,
|
||||
addUserStatuses,
|
||||
updateChats,
|
||||
updateChatListSecondaryInfo,
|
||||
updateThreadInfos,
|
||||
replaceThreadParam,
|
||||
updateListedIds,
|
||||
@ -27,29 +21,18 @@ import {
|
||||
addChatMessagesById,
|
||||
} from '../../reducers';
|
||||
import {
|
||||
selectUser,
|
||||
selectChat,
|
||||
selectCurrentMessageList,
|
||||
selectDraft,
|
||||
selectChatMessage,
|
||||
selectThreadInfo,
|
||||
selectLastServiceNotification,
|
||||
} from '../../selectors';
|
||||
import { isUserId } from '../../helpers';
|
||||
|
||||
addReducer('sync', (global, actions) => {
|
||||
void sync(actions.afterSync);
|
||||
});
|
||||
|
||||
addReducer('afterSync', () => {
|
||||
void afterSync();
|
||||
});
|
||||
import { init as initFolderManager } from '../../../util/folderManager';
|
||||
|
||||
const RELEASE_STATUS_TIMEOUT = 15000; // 10 sec;
|
||||
|
||||
let releaseStatusTimeout: number | undefined;
|
||||
|
||||
async function sync(afterSyncCallback: () => void) {
|
||||
addReducer('sync', () => {
|
||||
if (DEBUG) {
|
||||
// eslint-disable-next-line no-console
|
||||
console.log('>>> START SYNC');
|
||||
@ -67,149 +50,35 @@ async function sync(afterSyncCallback: () => void) {
|
||||
releaseStatusTimeout = undefined;
|
||||
}, RELEASE_STATUS_TIMEOUT);
|
||||
|
||||
await callApi('fetchCurrentUser');
|
||||
const { loadAllChats, preloadTopChatMessages } = getDispatch();
|
||||
|
||||
// This fetches only active chats and clears archived chats, which will be fetched in `afterSync`
|
||||
const savedUsers = await loadAndReplaceChats();
|
||||
await loadAndReplaceMessages(savedUsers);
|
||||
loadAllChats({
|
||||
listType: 'active',
|
||||
shouldReplace: true,
|
||||
onReplace: async () => {
|
||||
await loadAndReplaceMessages();
|
||||
|
||||
setGlobal({
|
||||
...getGlobal(),
|
||||
lastSyncTime: Date.now(),
|
||||
isSyncing: false,
|
||||
});
|
||||
setGlobal({
|
||||
...getGlobal(),
|
||||
lastSyncTime: Date.now(),
|
||||
isSyncing: false,
|
||||
});
|
||||
|
||||
if (DEBUG) {
|
||||
// eslint-disable-next-line no-console
|
||||
console.log('>>> FINISH SYNC');
|
||||
}
|
||||
|
||||
afterSyncCallback();
|
||||
}
|
||||
|
||||
async function afterSync() {
|
||||
if (DEBUG) {
|
||||
// eslint-disable-next-line no-console
|
||||
console.log('>>> START AFTER-SYNC');
|
||||
}
|
||||
|
||||
await Promise.all([
|
||||
loadAndUpdateUsers(),
|
||||
loadAndReplaceArchivedChats(),
|
||||
]);
|
||||
|
||||
await callApi('fetchCurrentUser');
|
||||
|
||||
if (DEBUG) {
|
||||
// eslint-disable-next-line no-console
|
||||
console.log('>>> FINISH AFTER-SYNC');
|
||||
}
|
||||
}
|
||||
|
||||
async function loadAndReplaceChats() {
|
||||
let global = getGlobal();
|
||||
|
||||
const result = await callApi('fetchChats', {
|
||||
limit: CHAT_LIST_LOAD_SLICE,
|
||||
withPinned: true,
|
||||
serverTimeOffset: global.serverTimeOffset,
|
||||
lastLocalServiceMessage: selectLastServiceNotification(global)?.message,
|
||||
});
|
||||
|
||||
if (!result) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
global = getGlobal();
|
||||
|
||||
const { recentlyFoundChatIds } = global.globalSearch;
|
||||
const { userIds: contactIds } = global.contactList || {};
|
||||
const { currentUserId } = global;
|
||||
|
||||
const savedPrivateChatIds = [
|
||||
...(recentlyFoundChatIds || []),
|
||||
...(contactIds || []),
|
||||
...(currentUserId ? [currentUserId] : []),
|
||||
];
|
||||
|
||||
const savedUsers = savedPrivateChatIds
|
||||
.map((id) => selectUser(global, id))
|
||||
.filter<ApiUser>(Boolean as any);
|
||||
|
||||
const savedChats = savedPrivateChatIds
|
||||
.map((id) => selectChat(global, id))
|
||||
.filter<ApiChat>(Boolean as any);
|
||||
|
||||
const { chatId: currentChatId } = selectCurrentMessageList(global) || {};
|
||||
if (currentChatId) {
|
||||
const selectedChat = selectChat(global, currentChatId);
|
||||
if (selectedChat && !savedPrivateChatIds.includes(currentChatId)) {
|
||||
savedChats.push(selectedChat);
|
||||
}
|
||||
|
||||
if (isUserId(currentChatId)) {
|
||||
const selectedChatUser = selectUser(global, currentChatId);
|
||||
if (selectedChatUser && !savedPrivateChatIds.includes(currentChatId)) {
|
||||
savedUsers.push(selectedChatUser);
|
||||
if (DEBUG) {
|
||||
// eslint-disable-next-line no-console
|
||||
console.log('>>> FINISH SYNC');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
savedUsers.push(...result.users);
|
||||
savedChats.push(...result.chats);
|
||||
|
||||
global = replaceUserStatuses(global, result.userStatusesById);
|
||||
|
||||
global = replaceChats(global, buildCollectionByKey(savedChats, 'id'));
|
||||
global = replaceChatListIds(global, 'active', result.chatIds);
|
||||
global = updateChatListSecondaryInfo(global, 'active', result);
|
||||
|
||||
Object.keys(result.draftsById).forEach((chatId) => {
|
||||
global = replaceThreadParam(global, chatId, MAIN_THREAD_ID, 'draft', result.draftsById[chatId]);
|
||||
initFolderManager();
|
||||
loadAllChats({ listType: 'archived', shouldReplace: true });
|
||||
void callApi('fetchCurrentUser');
|
||||
preloadTopChatMessages();
|
||||
},
|
||||
});
|
||||
});
|
||||
|
||||
Object.keys(result.replyingToById).forEach((chatId) => {
|
||||
global = replaceThreadParam(
|
||||
global, chatId, MAIN_THREAD_ID, 'replyingToId', result.replyingToById[chatId],
|
||||
);
|
||||
});
|
||||
|
||||
setGlobal(global);
|
||||
|
||||
if (currentChatId && !global.chats.byId[currentChatId]) {
|
||||
getDispatch().openChat({ id: undefined });
|
||||
}
|
||||
|
||||
return savedUsers;
|
||||
}
|
||||
|
||||
async function loadAndReplaceArchivedChats() {
|
||||
const result = await callApi('fetchChats', {
|
||||
limit: CHAT_LIST_LOAD_SLICE,
|
||||
archived: true,
|
||||
withPinned: true,
|
||||
serverTimeOffset: getGlobal().serverTimeOffset,
|
||||
});
|
||||
|
||||
if (!result) {
|
||||
return;
|
||||
}
|
||||
|
||||
let global = getGlobal();
|
||||
|
||||
global = updateUsers(global, buildCollectionByKey(result.users, 'id'));
|
||||
global = addUserStatuses(global, result.userStatusesById);
|
||||
|
||||
global = updateChats(global, buildCollectionByKey(result.chats, 'id'));
|
||||
global = replaceChatListIds(global, 'archived', result.chatIds);
|
||||
global = updateChatListSecondaryInfo(global, 'archived', result);
|
||||
|
||||
setGlobal(global);
|
||||
}
|
||||
|
||||
async function loadAndReplaceMessages(savedUsers?: ApiUser[]) {
|
||||
async function loadAndReplaceMessages() {
|
||||
let areMessagesLoaded = false;
|
||||
let users = savedUsers || [];
|
||||
|
||||
let global = getGlobal();
|
||||
const { chatId: currentChatId, threadId: currentThreadId } = selectCurrentMessageList(global) || {};
|
||||
@ -294,10 +163,10 @@ async function loadAndReplaceMessages(savedUsers?: ApiUser[]) {
|
||||
}
|
||||
|
||||
global = updateChats(global, buildCollectionByKey(result.chats, 'id'));
|
||||
global = updateUsers(global, buildCollectionByKey(result.users, 'id'));
|
||||
global = updateThreadInfos(global, currentChatId, result.threadInfos);
|
||||
|
||||
areMessagesLoaded = true;
|
||||
users = Array.prototype.concat(users, result.users);
|
||||
}
|
||||
}
|
||||
|
||||
@ -316,13 +185,6 @@ async function loadAndReplaceMessages(savedUsers?: ApiUser[]) {
|
||||
global = replaceThreadParam(global, chatId, MAIN_THREAD_ID, 'draft', draftsByChatId[chatId]);
|
||||
});
|
||||
|
||||
if (savedUsers) {
|
||||
global = replaceUsers(global, buildCollectionByKey(users, 'id'));
|
||||
} else if (users) {
|
||||
// If `fetchChats` has failed for some reason, we don't have saved chats, thus we can not replace
|
||||
global = updateUsers(global, buildCollectionByKey(users, 'id'));
|
||||
}
|
||||
|
||||
setGlobal(global);
|
||||
|
||||
const { chatId: audioChatId, messageId: audioMessageId } = global.audioPlayer;
|
||||
@ -331,35 +193,6 @@ async function loadAndReplaceMessages(savedUsers?: ApiUser[]) {
|
||||
}
|
||||
}
|
||||
|
||||
async function loadAndUpdateUsers() {
|
||||
let global = getGlobal();
|
||||
const { recentlyFoundChatIds } = global.globalSearch;
|
||||
const { userIds: contactIds } = global.contactList || {};
|
||||
if (
|
||||
(!contactIds || !contactIds.length)
|
||||
&& (!recentlyFoundChatIds || !recentlyFoundChatIds.length)
|
||||
) {
|
||||
return;
|
||||
}
|
||||
|
||||
const users = [
|
||||
...(recentlyFoundChatIds || []),
|
||||
...(contactIds || []),
|
||||
].map((id) => selectUser(global, id)).filter<ApiUser>(Boolean as any);
|
||||
|
||||
const result = await callApi('fetchUsers', { users });
|
||||
if (!result) {
|
||||
return;
|
||||
}
|
||||
|
||||
const { users: updatedUsers, userStatusesById } = result;
|
||||
|
||||
global = getGlobal();
|
||||
global = updateUsers(global, buildCollectionByKey(updatedUsers, 'id'));
|
||||
global = addUserStatuses(global, userStatusesById);
|
||||
setGlobal(global);
|
||||
}
|
||||
|
||||
function loadTopMessages(chat: ApiChat) {
|
||||
return callApi('fetchMessages', {
|
||||
chat,
|
||||
|
||||
@ -134,7 +134,6 @@ function onUpdateAuthorizationState(update: ApiUpdateAuthorizationState) {
|
||||
setGlobal({
|
||||
...global,
|
||||
isLoggingOut: false,
|
||||
lastSyncTime: Date.now(),
|
||||
});
|
||||
|
||||
break;
|
||||
@ -162,9 +161,7 @@ function onUpdateConnectionState(update: ApiUpdateConnectionState) {
|
||||
connectionState,
|
||||
});
|
||||
|
||||
if (connectionState === 'connectionStateReady' && global.authState === 'authorizationStateReady') {
|
||||
getDispatch().sync();
|
||||
} else if (connectionState === 'connectionStateBroken') {
|
||||
if (connectionState === 'connectionStateBroken') {
|
||||
getDispatch().signOut();
|
||||
}
|
||||
}
|
||||
|
||||
@ -93,7 +93,9 @@ const updateFolderManagerThrottled = throttle(() => {
|
||||
|
||||
let inited = false;
|
||||
|
||||
function init() {
|
||||
/* Getters */
|
||||
|
||||
export function init() {
|
||||
inited = true;
|
||||
|
||||
addCallback(updateFolderManagerThrottled);
|
||||
@ -102,8 +104,6 @@ function init() {
|
||||
updateFolderManager(getGlobal());
|
||||
}
|
||||
|
||||
/* Getters */
|
||||
|
||||
export function getOrderedIds(folderId: number) {
|
||||
if (!inited) init();
|
||||
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user