[Dev] TeactN: Assert when setting global asynchronously

This commit is contained in:
Alexander Zinchuk 2022-04-09 01:17:42 +02:00
parent fbe03b556b
commit a7e2695f2e
5 changed files with 53 additions and 31 deletions

View File

@ -65,7 +65,7 @@ import {
selectSendAs, selectSendAs,
selectSponsoredMessage, selectSponsoredMessage,
} from '../../selectors'; } from '../../selectors';
import { debounce, rafPromise } from '../../../util/schedulers'; import { debounce, onTickEnd, rafPromise } from '../../../util/schedulers';
import { isServiceNotificationMessage } from '../../helpers'; import { isServiceNotificationMessage } from '../../helpers';
const uploadProgressCallbacks = new Map<number, ApiOnProgress>(); const uploadProgressCallbacks = new Map<number, ApiOnProgress>();
@ -113,7 +113,9 @@ addActionHandler('loadViewportMessages', (global, actions, payload) => {
} }
if (!areAllLocal) { if (!areAllLocal) {
void loadViewportMessages(chat, threadId, offsetId, LoadMoreDirection.Around, isOutlying, isBudgetPreload); onTickEnd(() => {
void loadViewportMessages(chat, threadId, offsetId, LoadMoreDirection.Around, isOutlying, isBudgetPreload);
});
} }
} else { } else {
const offsetId = direction === LoadMoreDirection.Backwards ? viewportIds[0] : viewportIds[viewportIds.length - 1]; const offsetId = direction === LoadMoreDirection.Backwards ? viewportIds[0] : viewportIds[viewportIds.length - 1];
@ -127,7 +129,9 @@ addActionHandler('loadViewportMessages', (global, actions, payload) => {
global = safeReplaceViewportIds(global, chatId, threadId, newViewportIds); global = safeReplaceViewportIds(global, chatId, threadId, newViewportIds);
} }
void loadWithBudget(actions, areAllLocal, isOutlying, isBudgetPreload, chat, threadId, direction, offsetId); onTickEnd(() => {
void loadWithBudget(actions, areAllLocal, isOutlying, isBudgetPreload, chat, threadId, direction, offsetId);
});
if (isBudgetPreload) { if (isBudgetPreload) {
return undefined; return undefined;
@ -149,8 +153,6 @@ async function loadWithBudget(
} }
if (!isBudgetPreload) { if (!isBudgetPreload) {
// Let reducer return and update global
await Promise.resolve();
actions.loadViewportMessages({ actions.loadViewportMessages({
chatId: chat.id, threadId, direction, isBudgetPreload: true, chatId: chat.id, threadId, direction, isBudgetPreload: true,
}); });

View File

@ -47,6 +47,7 @@ import {
import { import {
getMessageContent, isUserId, isMessageLocal, getMessageText, checkIfReactionAdded, getMessageContent, isUserId, isMessageLocal, getMessageText, checkIfReactionAdded,
} from '../../helpers'; } from '../../helpers';
import { onTickEnd } from '../../../util/schedulers';
const ANIMATION_DELAY = 350; const ANIMATION_DELAY = 350;
@ -497,10 +498,12 @@ addActionHandler('apiUpdate', (global, actions, update) => {
if (shouldNotify) { if (shouldNotify) {
const newMessage = selectChatMessage(global, chatId, id); const newMessage = selectChatMessage(global, chatId, id);
if (!chat || !newMessage) return; if (!chat || !newMessage) return;
notifyAboutMessage({ onTickEnd(() => {
chat, notifyAboutMessage({
message: newMessage, chat,
isReaction: true, message: newMessage,
isReaction: true,
});
}); });
} }
@ -548,13 +551,15 @@ function updateThreadUnread(global: GlobalState, actions: GlobalActions, message
if (originMessage) { if (originMessage) {
global = updateThreadUnreadFromForwardedMessage(global, originMessage, chatId, message.id, isDeleting); global = updateThreadUnreadFromForwardedMessage(global, originMessage, chatId, message.id, isDeleting);
} else { } else {
actions.loadMessage({ onTickEnd(() => {
chatId, actions.loadMessage({
messageId: message.replyToMessageId, chatId,
threadUpdate: { messageId: message.replyToMessageId,
isDeleting, threadUpdate: {
lastMessageId: message.id, isDeleting,
}, lastMessageId: message.id,
},
});
}); });
} }
} }

View File

@ -6,7 +6,10 @@ import { cloneDeep } from '../util/iteratees';
initCache(); initCache();
addActionHandler('init', () => { addActionHandler('init', (global) => {
const initial = cloneDeep(INITIAL_STATE); const initial = cloneDeep(INITIAL_STATE);
return loadCache(initial) || initial; return {
...global,
...(loadCache(initial) || initial),
};
}); });

View File

@ -13,7 +13,9 @@ import { isHeavyAnimating } from '../../hooks/useHeavyAnimationCheck';
export default React; export default React;
type GlobalState = AnyLiteral; type GlobalState =
AnyLiteral
& { DEBUG_id: number };
type ActionNames = string; type ActionNames = string;
type ActionPayload = any; type ActionPayload = any;
@ -35,6 +37,13 @@ type MapStateToProps<OwnProps = undefined> = ((global: GlobalState, ownProps: Ow
let currentGlobal = {} as GlobalState; let currentGlobal = {} as GlobalState;
// eslint-disable-next-line @typescript-eslint/naming-convention
let DEBUG_currentGlobalId: number | undefined;
// eslint-disable-next-line @typescript-eslint/naming-convention
const DEBUG_resetIdThrottled = throttleWithTickEnd(() => {
DEBUG_currentGlobalId = Math.random();
});
const actionHandlers: Record<string, ActionHandler[]> = {}; const actionHandlers: Record<string, ActionHandler[]> = {};
const callbacks: Function[] = [updateContainers]; const callbacks: Function[] = [updateContainers];
const actions = {} as Actions; const actions = {} as Actions;
@ -61,6 +70,12 @@ function runCallbacks(forceOnHeavyAnimation = false) {
export function setGlobal(newGlobal?: GlobalState, options?: ActionOptions) { export function setGlobal(newGlobal?: GlobalState, options?: ActionOptions) {
if (typeof newGlobal === 'object' && newGlobal !== currentGlobal) { if (typeof newGlobal === 'object' && newGlobal !== currentGlobal) {
if (DEBUG) {
if (newGlobal.DEBUG_id !== DEBUG_currentGlobalId) {
throw new Error('[TeactN.setGlobal] Attempt to set an outdated global');
}
}
currentGlobal = newGlobal; currentGlobal = newGlobal;
if (options?.forceSyncOnIOs) { if (options?.forceSyncOnIOs) {
runCallbacks(true); runCallbacks(true);
@ -71,6 +86,12 @@ export function setGlobal(newGlobal?: GlobalState, options?: ActionOptions) {
} }
export function getGlobal() { export function getGlobal() {
if (DEBUG) {
DEBUG_currentGlobalId = Math.random();
currentGlobal.DEBUG_id = DEBUG_currentGlobalId;
DEBUG_resetIdThrottled();
}
return currentGlobal; return currentGlobal;
} }
@ -80,12 +101,12 @@ export function getActions() {
function handleAction(name: string, payload?: ActionPayload, options?: ActionOptions) { function handleAction(name: string, payload?: ActionPayload, options?: ActionOptions) {
actionHandlers[name]?.forEach((handler) => { actionHandlers[name]?.forEach((handler) => {
const response = handler(currentGlobal, actions, payload); const response = handler(DEBUG ? getGlobal() : currentGlobal, actions, payload);
if (!response || typeof response.then === 'function') { if (!response || typeof response.then === 'function') {
return; return;
} }
setGlobal(response, options); setGlobal(response as GlobalState, options);
}); });
} }

View File

@ -1,7 +1,6 @@
type Scheduler = type Scheduler =
typeof requestAnimationFrame typeof requestAnimationFrame
| typeof onTickEnd | typeof onTickEnd;
| typeof runNow;
export function debounce<F extends AnyToVoidFunction>( export function debounce<F extends AnyToVoidFunction>(
fn: F, fn: F,
@ -77,10 +76,6 @@ export function throttleWithTickEnd<F extends AnyToVoidFunction>(fn: F) {
return throttleWith(onTickEnd, fn); return throttleWith(onTickEnd, fn);
} }
export function throttleWithNow<F extends AnyToVoidFunction>(fn: F) {
return throttleWith(runNow, fn);
}
export function throttleWith<F extends AnyToVoidFunction>(schedulerFn: Scheduler, fn: F) { export function throttleWith<F extends AnyToVoidFunction>(schedulerFn: Scheduler, fn: F) {
let waiting = false; let waiting = false;
let args: Parameters<F>; let args: Parameters<F>;
@ -109,10 +104,6 @@ export function onIdle(cb: NoneToVoidFunction, timeout?: number) {
} }
} }
function runNow(fn: NoneToVoidFunction) {
fn();
}
export const pause = (ms: number) => new Promise<void>((resolve) => { export const pause = (ms: number) => new Promise<void>((resolve) => {
setTimeout(() => resolve(), ms); setTimeout(() => resolve(), ms);
}); });