[Perf] Fix global updates with interval when in background (#2707)

This commit is contained in:
Alexander Zinchuk 2023-03-03 14:30:56 +01:00
parent 2cde76ea94
commit e480499a80
4 changed files with 36 additions and 74 deletions

View File

@ -50,7 +50,7 @@ const App: FC<StateProps> = ({
hasWebAuthTokenFailed, hasWebAuthTokenFailed,
isInactiveAuth, isInactiveAuth,
}) => { }) => {
const { disconnect, updatePageTitle } = getActions(); const { disconnect } = getActions();
const [isInactive, markInactive, unmarkInactive] = useFlag(false); const [isInactive, markInactive, unmarkInactive] = useFlag(false);
const { isMobile } = useAppLayout(); const { isMobile } = useAppLayout();
@ -157,7 +157,7 @@ const App: FC<StateProps> = ({
markInactive(); markInactive();
}); });
}, [activeKey, disconnect, markInactive, updatePageTitle]); }, [activeKey, disconnect, markInactive]);
useEffect(() => { useEffect(() => {
if (isInactiveAuth) { if (isInactiveAuth) {
@ -166,7 +166,7 @@ const App: FC<StateProps> = ({
} else { } else {
unmarkInactive(); unmarkInactive();
} }
}, [isInactiveAuth, markInactive, unmarkInactive, updatePageTitle]); }, [isInactiveAuth, markInactive, unmarkInactive]);
const prevActiveKey = usePrevious(activeKey); const prevActiveKey = usePrevious(activeKey);

View File

@ -1,11 +1,10 @@
import { addCallback } from '../../../lib/teact/teactn';
import { import {
addActionHandler, getActions, getGlobal, setGlobal, addActionHandler, getGlobal, setGlobal,
} from '../../index'; } from '../../index';
import type { ApiError, ApiNotification } from '../../../api/types'; import type { ApiError, ApiNotification } from '../../../api/types';
import { MAIN_THREAD_ID } from '../../../api/types'; import { MAIN_THREAD_ID } from '../../../api/types';
import type { ActionReturnType, GlobalState } from '../../types'; import type { ActionReturnType } from '../../types';
import { import {
APP_VERSION, DEBUG, GLOBAL_STATE_CACHE_CUSTOM_EMOJI_LIMIT, INACTIVE_MARKER, PAGE_TITLE, APP_VERSION, DEBUG, GLOBAL_STATE_CACHE_CUSTOM_EMOJI_LIMIT, INACTIVE_MARKER, PAGE_TITLE,
@ -19,7 +18,7 @@ import { compact, unique } from '../../../util/iteratees';
import { getAllMultitabTokens, getCurrentTabId, reestablishMasterToSelf } from '../../../util/establishMultitabRole'; import { getAllMultitabTokens, getCurrentTabId, reestablishMasterToSelf } from '../../../util/establishMultitabRole';
import { getAllNotificationsCount } from '../../../util/folderManager'; import { getAllNotificationsCount } from '../../../util/folderManager';
import updateIcon from '../../../util/updateIcon'; import updateIcon from '../../../util/updateIcon';
import setPageTitle from '../../../util/updatePageTitle'; import { setPageTitle, setPageTitleInstant } from '../../../util/updatePageTitle';
import { updateTabState } from '../../reducers/tabs'; import { updateTabState } from '../../reducers/tabs';
import { getIsMobile, getIsTablet } from '../../../hooks/useAppLayout'; import { getIsMobile, getIsTablet } from '../../../hooks/useAppLayout';
import * as langProvider from '../../../util/langProvider'; import * as langProvider from '../../../util/langProvider';
@ -606,47 +605,34 @@ addActionHandler('afterHangUp', (global): ActionReturnType => {
let notificationInterval: NodeJS.Timeout | undefined; let notificationInterval: NodeJS.Timeout | undefined;
const NOTIFICATION_INTERVAL = 1000; const NOTIFICATION_INTERVAL = 500;
addActionHandler('onTabFocusChange', (global, actions, payload): ActionReturnType => { addActionHandler('onTabFocusChange', (global, actions, payload): ActionReturnType => {
const { isBlurred } = payload; const { isBlurred, tabId = getCurrentTabId() } = payload;
const token = getCurrentTabId();
if (!isBlurred) { if (!isBlurred) {
actions.updateIsOnline(true); actions.updateIsOnline(true);
} }
const blurredTabTokens = unique(isBlurred const blurredTabTokens = unique(isBlurred
? [...global.blurredTabTokens, token] ? [...global.blurredTabTokens, tabId]
: global.blurredTabTokens.filter((t) => t !== token)); : global.blurredTabTokens.filter((t) => t !== tabId));
if (blurredTabTokens.length === getAllMultitabTokens().length) { if (blurredTabTokens.length === getAllMultitabTokens().length) {
actions.updateIsOnline(false); actions.updateIsOnline(false);
} }
const isNewlyBlurred = isBlurred && blurredTabTokens.length === 1; if (isBlurred) {
if (isNewlyBlurred) {
if (notificationInterval) clearInterval(notificationInterval); if (notificationInterval) clearInterval(notificationInterval);
notificationInterval = setInterval(() => { notificationInterval = setInterval(() => {
global = getGlobal(); actions.updatePageTitle({
global = { tabId,
...global, });
notificationIndex: (global.notificationIndex || 0) + 1,
allNotificationsCount: getAllNotificationsCount(),
};
setGlobal(global);
}, NOTIFICATION_INTERVAL); }, NOTIFICATION_INTERVAL);
} } else {
if (!blurredTabTokens.length && notificationInterval) {
clearInterval(notificationInterval); clearInterval(notificationInterval);
notificationInterval = undefined; notificationInterval = undefined;
global = {
...global,
notificationIndex: undefined,
};
} }
return { return {
@ -657,20 +643,30 @@ addActionHandler('onTabFocusChange', (global, actions, payload): ActionReturnTyp
}); });
addActionHandler('updatePageTitle', (global, actions, payload): ActionReturnType => { addActionHandler('updatePageTitle', (global, actions, payload): ActionReturnType => {
const { isInactive, notificationCount, tabId = getCurrentTabId() } = payload || {}; const { tabId = getCurrentTabId() } = payload || {};
const { canDisplayChatInTitle } = global.settings.byKey; const { canDisplayChatInTitle } = global.settings.byKey;
const currentUserId = global.currentUserId; const currentUserId = global.currentUserId;
if (isInactive) { if (document.title.includes(INACTIVE_MARKER)) {
setPageTitle(`${PAGE_TITLE} ${INACTIVE_MARKER}`); updateIcon(false);
setPageTitleInstant(`${PAGE_TITLE} ${INACTIVE_MARKER}`);
return; return;
} }
if (notificationCount) { if (global.initialUnreadNotifications && Math.round(Date.now() / 1000) % 2 === 0) {
setPageTitle(`${notificationCount} notification${notificationCount > 1 ? 's' : ''}`); const notificationCount = getAllNotificationsCount();
return;
const newUnread = notificationCount - global.initialUnreadNotifications;
if (newUnread > 0) {
setPageTitleInstant(`${newUnread} notification${newUnread > 1 ? 's' : ''}`);
updateIcon(true);
return;
}
} }
updateIcon(false);
const messageList = selectCurrentMessageList(global, tabId); const messageList = selectCurrentMessageList(global, tabId);
if (messageList && canDisplayChatInTitle) { if (messageList && canDisplayChatInTitle) {
const { chatId, threadId } = messageList; const { chatId, threadId } = messageList;
@ -687,34 +683,5 @@ addActionHandler('updatePageTitle', (global, actions, payload): ActionReturnType
} }
} }
setPageTitle(PAGE_TITLE); setPageTitleInstant(PAGE_TITLE);
});
addCallback((global: GlobalState) => {
if (global.notificationIndex === undefined || global.allNotificationsCount === undefined) return;
// eslint-disable-next-line eslint-multitab-tt/no-getactions-in-actions
const { updatePageTitle } = getActions();
const index = global.notificationIndex;
const allNotificationsCount = global.allNotificationsCount;
if (document.title.includes(INACTIVE_MARKER) || !global.initialUnreadNotifications) {
updateIcon(false);
updatePageTitle();
return;
}
if (index % 2 === 0) {
const newUnread = allNotificationsCount - global.initialUnreadNotifications;
if (newUnread > 0) {
updatePageTitle({
notificationCount: newUnread,
});
updateIcon(true);
return;
}
}
updatePageTitle();
updateIcon(false);
}); });

View File

@ -560,8 +560,6 @@ export type GlobalState = {
leftColumnWidth?: number; leftColumnWidth?: number;
lastIsChatInfoShown?: boolean; lastIsChatInfoShown?: boolean;
initialUnreadNotifications?: number; initialUnreadNotifications?: number;
notificationIndex?: number;
allNotificationsCount?: number;
audioPlayer: { audioPlayer: {
lastPlaybackRate: number; lastPlaybackRate: number;
@ -1533,7 +1531,7 @@ export interface ActionPayloads {
}; };
onTabFocusChange: { onTabFocusChange: {
isBlurred: boolean; isBlurred: boolean;
}; } & WithTabId;
onSomeTabSwitchedMultitabRole: undefined; onSomeTabSwitchedMultitabRole: undefined;
afterHangUp: undefined; afterHangUp: undefined;
requestMasterAndCallAction: CallbackAction & WithTabId; requestMasterAndCallAction: CallbackAction & WithTabId;
@ -2169,10 +2167,7 @@ export interface ActionPayloads {
} & WithTabId; } & WithTabId;
dismissNotification: { localId: string } & WithTabId; dismissNotification: { localId: string } & WithTabId;
updatePageTitle: { updatePageTitle: WithTabId | undefined;
isInactive?: boolean;
notificationCount?: number;
} & WithTabId | undefined;
// Calls // Calls
joinGroupCall: { joinGroupCall: {

View File

@ -4,11 +4,11 @@ const UPDATE_DEBOUNCE_MS = 200;
// For some reason setting `document.title` to the same value // For some reason setting `document.title` to the same value
// causes increment of Chrome Dev Tools > Performance Monitor > DOM Nodes counter // causes increment of Chrome Dev Tools > Performance Monitor > DOM Nodes counter
function updatePageTitle(nextTitle: string) { export function setPageTitleInstant(nextTitle: string) {
if (document.title !== nextTitle) { if (document.title !== nextTitle) {
document.title = nextTitle; document.title = nextTitle;
} }
} }
// Synchronous page title update has conflicts with History API in Chrome // Synchronous page title update has conflicts with History API in Chrome
export default debounce(updatePageTitle, UPDATE_DEBOUNCE_MS, false); export const setPageTitle = debounce(setPageTitleInstant, UPDATE_DEBOUNCE_MS, false);