[Memory] Fixes and optimizations (#3453)
This commit is contained in:
parent
a136a66e61
commit
23eef3034d
@ -1,17 +1,17 @@
|
||||
import { Api as GramJs } from '../../../lib/gramjs';
|
||||
|
||||
import type {
|
||||
ApiConfig,
|
||||
ApiCountry, ApiSession, ApiUrlAuthResult, ApiWallpaper, ApiWebSession,
|
||||
ApiConfig, ApiCountry, ApiSession, ApiUrlAuthResult, ApiWallpaper, ApiWebSession, ApiLangString,
|
||||
} from '../../types';
|
||||
import type { ApiPrivacySettings, ApiPrivacyKey, PrivacyVisibility } from '../../../types';
|
||||
|
||||
import { buildApiDocument, buildApiReaction } from './messages';
|
||||
import { buildApiPeerId, getApiChatIdFromMtpPeer } from './peers';
|
||||
import { pick } from '../../../util/iteratees';
|
||||
import { omit, pick } from '../../../util/iteratees';
|
||||
import { getServerTime } from '../../../util/serverTime';
|
||||
import { buildApiUser } from './users';
|
||||
import { addUserToLocalDb } from '../helpers';
|
||||
import { omitVirtualClassFields } from './helpers';
|
||||
|
||||
export function buildApiWallpaper(wallpaper: GramJs.TypeWallPaper): ApiWallpaper | undefined {
|
||||
if (wallpaper instanceof GramJs.WallPaperNoFile) {
|
||||
@ -253,3 +253,18 @@ export function buildApiConfig(config: GramJs.Config): ApiConfig {
|
||||
autologinToken: config.autologinToken,
|
||||
};
|
||||
}
|
||||
|
||||
export function buildLangPack(mtpLangPack: GramJs.LangPackDifference) {
|
||||
return mtpLangPack.strings.reduce<Record<string, ApiLangString | undefined>>((acc, mtpString) => {
|
||||
acc[mtpString.key] = buildLangPackString(mtpString);
|
||||
return acc;
|
||||
}, {});
|
||||
}
|
||||
|
||||
export function buildLangPackString(mtpString: GramJs.TypeLangPackString) {
|
||||
return mtpString instanceof GramJs.LangPackString
|
||||
? mtpString.value
|
||||
: mtpString instanceof GramJs.LangPackStringPluralized
|
||||
? omit(omitVirtualClassFields(mtpString), ['key'])
|
||||
: undefined;
|
||||
}
|
||||
|
||||
@ -5,9 +5,10 @@ import type {
|
||||
ApiAppConfig,
|
||||
ApiConfig,
|
||||
ApiError,
|
||||
ApiLangString,
|
||||
ApiLanguage,
|
||||
ApiNotifyException, ApiPhoto, ApiUser,
|
||||
ApiNotifyException,
|
||||
ApiPhoto,
|
||||
ApiUser,
|
||||
} from '../../types';
|
||||
import type { ApiPrivacyKey, InputPrivacyRules, LangCode } from '../../../types';
|
||||
import type { LANG_PACKS } from '../../../config';
|
||||
@ -20,7 +21,7 @@ import {
|
||||
buildApiNotifyException,
|
||||
buildApiSession,
|
||||
buildApiWallpaper,
|
||||
buildApiWebSession,
|
||||
buildApiWebSession, buildLangPack, buildLangPackString,
|
||||
buildPrivacyRules,
|
||||
} from '../apiBuilders/misc';
|
||||
|
||||
@ -426,12 +427,7 @@ export async function fetchLangPack({ sourceLangPacks, langCode }: {
|
||||
}));
|
||||
}));
|
||||
|
||||
const collections = results
|
||||
.filter(Boolean)
|
||||
.map((result) => {
|
||||
return buildCollectionByKey(result.strings.map<ApiLangString>(omitVirtualClassFields), 'key');
|
||||
});
|
||||
|
||||
const collections = results.filter(Boolean).map(buildLangPack);
|
||||
if (!collections.length) {
|
||||
return undefined;
|
||||
}
|
||||
@ -452,7 +448,7 @@ export async function fetchLangStrings({ langPack, langCode, keys }: {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
return result.map(omitVirtualClassFields);
|
||||
return result.map(buildLangPackString);
|
||||
}
|
||||
|
||||
export async function fetchPrivacySettings(privacyKey: ApiPrivacyKey) {
|
||||
|
||||
@ -7,7 +7,7 @@ import type { TypedBroadcastChannel } from '../../../util/multitab';
|
||||
|
||||
import { IS_MULTITAB_SUPPORTED } from '../../../util/windowEnvironment';
|
||||
import { DATA_BROADCAST_CHANNEL_NAME, DEBUG } from '../../../config';
|
||||
import generateIdFor from '../../../util/generateIdFor';
|
||||
import generateUniqueId from '../../../util/generateUniqueId';
|
||||
import { pause } from '../../../util/schedulers';
|
||||
import { getCurrentTabId, subscribeToMasterChange } from '../../../util/establishMultitabRole';
|
||||
import Deferred from '../../../util/Deferred';
|
||||
@ -284,7 +284,7 @@ function makeRequestToMaster(message: {
|
||||
args: MethodArgs<keyof Methods>;
|
||||
withCallback?: boolean;
|
||||
}) {
|
||||
const messageId = generateIdFor(requestStates);
|
||||
const messageId = generateUniqueId();
|
||||
const payload = {
|
||||
messageId,
|
||||
...message,
|
||||
@ -323,7 +323,7 @@ function makeRequestToMaster(message: {
|
||||
}
|
||||
|
||||
function makeRequest(message: OriginRequest) {
|
||||
const messageId = generateIdFor(requestStates);
|
||||
const messageId = generateUniqueId();
|
||||
const payload: OriginRequest = {
|
||||
messageId,
|
||||
...message,
|
||||
|
||||
@ -12,15 +12,13 @@ export interface ApiLanguage {
|
||||
translationsUrl: string;
|
||||
}
|
||||
|
||||
export interface ApiLangString {
|
||||
key: string;
|
||||
value?: string;
|
||||
export type ApiLangString = string | {
|
||||
zeroValue?: string;
|
||||
oneValue?: string;
|
||||
twoValue?: string;
|
||||
fewValue?: string;
|
||||
manyValue?: string;
|
||||
otherValue?: string;
|
||||
}
|
||||
};
|
||||
|
||||
export type ApiLangPack = Record<string, ApiLangString>;
|
||||
export type ApiLangPack = Record<string, ApiLangString | undefined>;
|
||||
|
||||
@ -5,12 +5,12 @@ import { requestMeasure } from '../../lib/fasterdom/fasterdom';
|
||||
import { ensureRLottie, getRLottie } from '../../lib/rlottie/RLottie.async';
|
||||
|
||||
import React, {
|
||||
useEffect, useRef, memo, useState, useMemo,
|
||||
useEffect, useRef, memo, useState,
|
||||
} from '../../lib/teact/teact';
|
||||
|
||||
import buildClassName from '../../util/buildClassName';
|
||||
import buildStyle from '../../util/buildStyle';
|
||||
import generateIdFor from '../../util/generateIdFor';
|
||||
import generateUniqueId from '../../util/generateUniqueId';
|
||||
import { hexToRgb } from '../../util/switchTheme';
|
||||
|
||||
import useLastCallback from '../../hooks/useLastCallback';
|
||||
@ -23,6 +23,7 @@ import useSharedIntersectionObserver from '../../hooks/useSharedIntersectionObse
|
||||
import useThrottledCallback from '../../hooks/useThrottledCallback';
|
||||
import useColorFilter from '../../hooks/stickers/useColorFilter';
|
||||
import useSyncEffect from '../../hooks/useSyncEffect';
|
||||
import useUniqueId from '../../hooks/useUniqueId';
|
||||
|
||||
export type OwnProps = {
|
||||
ref?: RefObject<HTMLDivElement>;
|
||||
@ -49,7 +50,6 @@ export type OwnProps = {
|
||||
};
|
||||
|
||||
const THROTTLE_MS = 150;
|
||||
const ID_STORE = {};
|
||||
|
||||
const AnimatedSticker: FC<OwnProps> = ({
|
||||
ref,
|
||||
@ -80,7 +80,7 @@ const AnimatedSticker: FC<OwnProps> = ({
|
||||
containerRef = ref;
|
||||
}
|
||||
|
||||
const viewId = useMemo(() => generateIdFor(ID_STORE, true), []);
|
||||
const viewId = useUniqueId();
|
||||
|
||||
const [animation, setAnimation] = useState<RLottieInstance>();
|
||||
const animationRef = useRef<RLottieInstance>();
|
||||
@ -129,7 +129,7 @@ const AnimatedSticker: FC<OwnProps> = ({
|
||||
const newAnimation = getRLottie().init(
|
||||
tgsUrl,
|
||||
container,
|
||||
renderId || generateIdFor(ID_STORE, true),
|
||||
renderId || generateUniqueId(),
|
||||
{
|
||||
size,
|
||||
noLoop,
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
import type { FC } from '../../lib/teact/teact';
|
||||
import React, { memo, useMemo } from '../../lib/teact/teact';
|
||||
|
||||
import generateIdFor from '../../util/generateIdFor';
|
||||
import generateUniqueId from '../../util/generateUniqueId';
|
||||
|
||||
import buildClassName from '../../util/buildClassName';
|
||||
|
||||
@ -9,7 +9,6 @@ import './PremiumIcon.scss';
|
||||
|
||||
// eslint-disable-next-line max-len
|
||||
const PREMIUM_ICON = { __html: '<svg width="14" height="15" viewBox="0 0 14 15" fill="none" xmlns="http://www.w3.org/2000/svg"><path fill-rule="evenodd" clip-rule="evenodd" d="M6.63869 12.1902L3.50621 14.1092C3.18049 14.3087 2.75468 14.2064 2.55515 13.8807C2.45769 13.7216 2.42864 13.5299 2.47457 13.3491L2.95948 11.4405C3.13452 10.7515 3.60599 10.1756 4.24682 9.86791L7.6642 8.22716C7.82352 8.15067 7.89067 7.95951 7.81418 7.80019C7.75223 7.67116 7.61214 7.59896 7.47111 7.62338L3.66713 8.28194C2.89387 8.41581 2.1009 8.20228 1.49941 7.69823L0.297703 6.69116C0.00493565 6.44581 -0.0335059 6.00958 0.211842 5.71682C0.33117 5.57442 0.502766 5.48602 0.687982 5.47153L4.35956 5.18419C4.61895 5.16389 4.845 4.99974 4.94458 4.75937L6.36101 1.3402C6.5072 0.987302 6.91179 0.819734 7.26469 0.965925C7.43413 1.03612 7.56876 1.17075 7.63896 1.3402L9.05539 4.75937C9.15496 4.99974 9.38101 5.16389 9.6404 5.18419L13.3322 5.47311C13.713 5.50291 13.9975 5.83578 13.9677 6.2166C13.9534 6.39979 13.8667 6.56975 13.7269 6.68896L10.9114 9.08928C10.7131 9.25826 10.6267 9.52425 10.6876 9.77748L11.5532 13.3733C11.6426 13.7447 11.414 14.1182 11.0427 14.2076C10.8642 14.2506 10.676 14.2208 10.5195 14.1249L7.36128 12.1902C7.13956 12.0544 6.8604 12.0544 6.63869 12.1902Z" fill="var(--color-fill)"/></svg>' };
|
||||
const store: Record<string, boolean> = {};
|
||||
|
||||
type OwnProps = {
|
||||
withGradient?: boolean;
|
||||
@ -41,7 +40,7 @@ const PremiumIcon: FC<OwnProps> = ({
|
||||
};
|
||||
|
||||
function getPremiumIconGradient() {
|
||||
const id = generateIdFor(store);
|
||||
const id = generateUniqueId();
|
||||
return {
|
||||
// eslint-disable-next-line max-len
|
||||
__html: `<svg width="14" height="15" viewBox="0 0 14 15" fill="none" xmlns="http://www.w3.org/2000/svg"><defs><linearGradient id="${id}" x1="3" y1="63.5001" x2="84.1475" y2="-1.32262" gradientUnits="userSpaceOnUse"><stop stop-color="#6B93FF"/><stop offset="0.439058" stop-color="#976FFF"/><stop offset="1" stop-color="#E46ACE"/></linearGradient></defs><path fill-rule="evenodd" clip-rule="evenodd" d="M6.63869 12.1902L3.50621 14.1092C3.18049 14.3087 2.75468 14.2064 2.55515 13.8807C2.45769 13.7216 2.42864 13.5299 2.47457 13.3491L2.95948 11.4405C3.13452 10.7515 3.60599 10.1756 4.24682 9.86791L7.6642 8.22716C7.82352 8.15067 7.89067 7.95951 7.81418 7.80019C7.75223 7.67116 7.61214 7.59896 7.47111 7.62338L3.66713 8.28194C2.89387 8.41581 2.1009 8.20228 1.49941 7.69823L0.297703 6.69116C0.00493565 6.44581 -0.0335059 6.00958 0.211842 5.71682C0.33117 5.57442 0.502766 5.48602 0.687982 5.47153L4.35956 5.18419C4.61895 5.16389 4.845 4.99974 4.94458 4.75937L6.36101 1.3402C6.5072 0.987302 6.91179 0.819734 7.26469 0.965925C7.43413 1.03612 7.56876 1.17075 7.63896 1.3402L9.05539 4.75937C9.15496 4.99974 9.38101 5.16389 9.6404 5.18419L13.3322 5.47311C13.713 5.50291 13.9975 5.83578 13.9677 6.2166C13.9534 6.39979 13.8667 6.56975 13.7269 6.68896L10.9114 9.08928C10.7131 9.25826 10.6267 9.52425 10.6876 9.77748L11.5532 13.3733C11.6426 13.7447 11.414 14.1182 11.0427 14.2076C10.8642 14.2506 10.676 14.2208 10.5195 14.1249L7.36128 12.1902C7.13956 12.0544 6.8604 12.0544 6.63869 12.1902Z" fill="url(#${id})"/></svg>`,
|
||||
|
||||
@ -1,6 +1,4 @@
|
||||
import React, {
|
||||
memo, useMemo,
|
||||
} from '../../lib/teact/teact';
|
||||
import React, { memo } from '../../lib/teact/teact';
|
||||
import { getGlobal } from '../../global';
|
||||
|
||||
import type { FC } from '../../lib/teact/teact';
|
||||
@ -10,7 +8,6 @@ import type { ApiSticker } from '../../api/types';
|
||||
import { IS_ANDROID, IS_WEBM_SUPPORTED } from '../../util/windowEnvironment';
|
||||
import * as mediaLoader from '../../util/mediaLoader';
|
||||
import buildClassName from '../../util/buildClassName';
|
||||
import generateIdFor from '../../util/generateIdFor';
|
||||
import { getStickerPreviewHash } from '../../global/helpers';
|
||||
import { selectIsAlwaysHighPriorityEmoji } from '../../global/selectors';
|
||||
|
||||
@ -22,6 +19,7 @@ import useFlag from '../../hooks/useFlag';
|
||||
import useCoordsInSharedCanvas from '../../hooks/useCoordsInSharedCanvas';
|
||||
import useHeavyAnimationCheck, { isHeavyAnimating } from '../../hooks/useHeavyAnimationCheck';
|
||||
import useColorFilter from '../../hooks/stickers/useColorFilter';
|
||||
import useUniqueId from '../../hooks/useUniqueId';
|
||||
|
||||
import AnimatedSticker from './AnimatedSticker';
|
||||
import OptimizedVideo from '../ui/OptimizedVideo';
|
||||
@ -54,7 +52,6 @@ type OwnProps = {
|
||||
|
||||
const SHARED_PREFIX = 'shared';
|
||||
const STICKER_SIZE = 24;
|
||||
const ID_STORE = {};
|
||||
|
||||
const StickerView: FC<OwnProps> = ({
|
||||
containerRef,
|
||||
@ -127,7 +124,7 @@ const StickerView: FC<OwnProps> = ({
|
||||
// Preload preview for Message Input and local message
|
||||
useMedia(previewMediaHash, !shouldLoad || !shouldPreloadPreview);
|
||||
|
||||
const randomIdPrefix = useMemo(() => generateIdFor(ID_STORE, true), []);
|
||||
const randomIdPrefix = useUniqueId();
|
||||
const renderId = [
|
||||
(withSharedAnimation ? SHARED_PREFIX : randomIdPrefix),
|
||||
id,
|
||||
|
||||
@ -11,11 +11,10 @@ import useLastCallback from '../../../hooks/useLastCallback';
|
||||
import useEnsureCustomEmoji from '../../../hooks/useEnsureCustomEmoji';
|
||||
|
||||
export default function useCustomEmoji(documentId?: string) {
|
||||
const global = getGlobal();
|
||||
const [customEmoji, setCustomEmoji] = useState<ApiSticker | undefined>(
|
||||
documentId ? global.customEmojis.byId[documentId] : undefined,
|
||||
documentId ? getGlobal().customEmojis.byId[documentId] : undefined,
|
||||
);
|
||||
const [canPlay, setCanPlay] = useState(selectCanPlayAnimatedEmojis(global));
|
||||
const [canPlay, setCanPlay] = useState(selectCanPlayAnimatedEmojis(getGlobal()));
|
||||
|
||||
useEnsureCustomEmoji(documentId);
|
||||
|
||||
|
||||
@ -122,13 +122,10 @@ const ChatFolders: FC<OwnProps & StateProps> = ({
|
||||
return undefined;
|
||||
}
|
||||
|
||||
const global = getGlobal();
|
||||
|
||||
return displayedFolders.map((folder, i) => {
|
||||
const { id, title } = folder;
|
||||
const isBlocked = id !== ALL_FOLDER_ID && i > maxFolders - 1;
|
||||
const canShareFolder = selectCanShareFolder(global, id);
|
||||
|
||||
const canShareFolder = selectCanShareFolder(getGlobal(), id);
|
||||
const contextActions = [];
|
||||
|
||||
if (canShareFolder) {
|
||||
|
||||
@ -322,12 +322,13 @@ addActionHandler('loadAllChats', async (global, actions, payload): Promise<void>
|
||||
.sort((chat1, chat2) => getOrderDate(chat1)! - getOrderDate(chat2)!)[0]
|
||||
: undefined;
|
||||
|
||||
await loadChats(global,
|
||||
await loadChats(
|
||||
listType,
|
||||
oldestChat?.id,
|
||||
oldestChat ? getOrderDate(oldestChat) : undefined,
|
||||
shouldReplace,
|
||||
true);
|
||||
true,
|
||||
);
|
||||
|
||||
if (shouldReplace) {
|
||||
onReplace?.();
|
||||
@ -352,10 +353,10 @@ addActionHandler('loadFullChat', (global, actions, payload): ActionReturnType =>
|
||||
}
|
||||
});
|
||||
|
||||
addActionHandler('loadTopChats', (global): ActionReturnType => {
|
||||
addActionHandler('loadTopChats', (): ActionReturnType => {
|
||||
runThrottledForLoadTopChats(() => {
|
||||
loadChats(global, 'active');
|
||||
loadChats(global, 'archived');
|
||||
loadChats('active');
|
||||
loadChats('archived');
|
||||
});
|
||||
});
|
||||
|
||||
@ -2157,15 +2158,15 @@ addActionHandler('openDeleteChatFolderModal', async (global, actions, payload):
|
||||
setGlobal(global);
|
||||
});
|
||||
|
||||
async function loadChats<T extends GlobalState>(
|
||||
global: T,
|
||||
async function loadChats(
|
||||
listType: 'active' | 'archived',
|
||||
offsetId?: string,
|
||||
offsetDate?: number,
|
||||
shouldReplace = false,
|
||||
isFullDraftSync?: boolean,
|
||||
) {
|
||||
global = getGlobal();
|
||||
// eslint-disable-next-line eslint-multitab-tt/no-immediate-global
|
||||
let global = getGlobal();
|
||||
let lastLocalServiceMessage = selectLastServiceNotification(global)?.message;
|
||||
const result = await callApi('fetchChats', {
|
||||
limit: CHAT_LIST_LOAD_SLICE,
|
||||
|
||||
@ -604,7 +604,6 @@ addActionHandler('setStickerSearchQuery', (global, actions, payload): ActionRetu
|
||||
if (query) {
|
||||
void searchThrottled(async () => {
|
||||
const result = await callApi('searchStickers', { query });
|
||||
|
||||
if (!result) {
|
||||
return;
|
||||
}
|
||||
@ -643,6 +642,7 @@ addActionHandler('setGifSearchQuery', (global, actions, payload): ActionReturnTy
|
||||
|
||||
if (typeof query === 'string') {
|
||||
void searchThrottled(() => {
|
||||
global = getGlobal();
|
||||
searchGifs(global, query, global.config?.gifSearchUsername, undefined, tabId);
|
||||
});
|
||||
}
|
||||
@ -654,6 +654,7 @@ addActionHandler('searchMoreGifs', (global, actions, payload): ActionReturnType
|
||||
|
||||
if (typeof query === 'string') {
|
||||
void searchThrottled(() => {
|
||||
global = getGlobal();
|
||||
searchGifs(global, query, global.config?.gifSearchUsername, offset, tabId);
|
||||
});
|
||||
}
|
||||
|
||||
@ -19,7 +19,7 @@ import {
|
||||
selectIsTrustedBot,
|
||||
selectTabState,
|
||||
} from '../../selectors';
|
||||
import generateIdFor from '../../../util/generateIdFor';
|
||||
import generateUniqueId from '../../../util/generateUniqueId';
|
||||
import { compact, unique } from '../../../util/iteratees';
|
||||
import { getAllMultitabTokens, getCurrentTabId, reestablishMasterToSelf } from '../../../util/establishMultitabRole';
|
||||
import { getAllNotificationsCount } from '../../../util/folderManager';
|
||||
@ -273,7 +273,7 @@ addActionHandler('reorderStickerSets', (global, actions, payload): ActionReturnT
|
||||
|
||||
addActionHandler('showNotification', (global, actions, payload): ActionReturnType => {
|
||||
const { tabId = getCurrentTabId(), ...notification } = payload;
|
||||
notification.localId = generateIdFor({});
|
||||
notification.localId = generateUniqueId();
|
||||
|
||||
const newNotifications = [...selectTabState(global, tabId).notifications];
|
||||
const existingNotificationIndex = newNotifications.findIndex((n) => n.message === notification.message);
|
||||
|
||||
@ -7,6 +7,7 @@ import { IS_IOS } from '../util/windowEnvironment';
|
||||
|
||||
import useSyncEffect from './useSyncEffect';
|
||||
import useEffectOnce from './useEffectOnce';
|
||||
import useLastCallback from './useLastCallback';
|
||||
|
||||
const PATH_BASE = `${window.location.pathname}${window.location.search}`;
|
||||
// Carefully selected by swiping and observing visual changes
|
||||
@ -241,6 +242,8 @@ export default function useHistoryBack({
|
||||
shouldResetUrlHash?: boolean;
|
||||
onBack: VoidFunction;
|
||||
}) {
|
||||
const lastOnBack = useLastCallback(onBack);
|
||||
|
||||
// Active index of the record
|
||||
const indexRef = useRef<number>();
|
||||
const wasReplaced = useRef(false);
|
||||
@ -262,7 +265,7 @@ export default function useHistoryBack({
|
||||
|
||||
historyState[indexRef.current] = {
|
||||
index: indexRef.current,
|
||||
onBack,
|
||||
onBack: lastOnBack,
|
||||
shouldBeReplaced,
|
||||
markReplaced: () => {
|
||||
wasReplaced.current = true;
|
||||
@ -283,7 +286,7 @@ export default function useHistoryBack({
|
||||
// Space is a hack to make the browser completely remove the hash
|
||||
hash: hash ? `#${hash}` : (shouldResetUrlHash ? ' ' : undefined),
|
||||
});
|
||||
}, [hash, onBack, shouldBeReplaced, shouldResetUrlHash]);
|
||||
}, [hash, shouldBeReplaced, shouldResetUrlHash]);
|
||||
|
||||
const processBack = useCallback(() => {
|
||||
// Only process back on open records
|
||||
|
||||
@ -1,15 +1,16 @@
|
||||
import * as langProvider from '../util/langProvider';
|
||||
|
||||
import useForceUpdate from './useForceUpdate';
|
||||
import useSyncEffect from './useSyncEffect';
|
||||
import useEffectOnce from './useEffectOnce';
|
||||
|
||||
export type LangFn = langProvider.LangFn;
|
||||
|
||||
const useLang = (): LangFn => {
|
||||
const forceUpdate = useForceUpdate();
|
||||
|
||||
useSyncEffect(() => {
|
||||
useEffectOnce(() => {
|
||||
return langProvider.addCallback(forceUpdate);
|
||||
}, [forceUpdate]);
|
||||
});
|
||||
|
||||
return langProvider.getTranslationFn();
|
||||
};
|
||||
|
||||
@ -1,10 +1,22 @@
|
||||
import usePrevious from './usePrevious';
|
||||
import { useRef } from '../lib/teact/teact';
|
||||
import useEffectOnce from './useEffectOnce';
|
||||
|
||||
const useSyncEffect = <const T extends readonly any[]>(cb: (args: T | readonly []) => void, dependencies: T) => {
|
||||
export default function useSyncEffect<const T extends readonly any[]>(
|
||||
effect: (args: T | readonly []) => NoneToVoidFunction | void,
|
||||
dependencies: T,
|
||||
) {
|
||||
const prevDeps = usePrevious<T>(dependencies);
|
||||
if (!prevDeps || dependencies.some((d, i) => d !== prevDeps[i])) {
|
||||
cb(prevDeps || []);
|
||||
}
|
||||
};
|
||||
const cleanupRef = useRef<NoneToVoidFunction>();
|
||||
|
||||
export default useSyncEffect;
|
||||
if (!prevDeps || dependencies.some((d, i) => d !== prevDeps[i])) {
|
||||
cleanupRef.current?.();
|
||||
cleanupRef.current = effect(prevDeps || []) ?? undefined;
|
||||
}
|
||||
|
||||
useEffectOnce(() => {
|
||||
return () => {
|
||||
cleanupRef.current?.();
|
||||
};
|
||||
});
|
||||
}
|
||||
|
||||
@ -1,17 +1,12 @@
|
||||
import { useRef } from '../lib/teact/teact';
|
||||
import generateIdFor from '../util/generateIdFor';
|
||||
import generateUniqueId from '../util/generateUniqueId';
|
||||
|
||||
const store: Record<string, boolean> = {};
|
||||
|
||||
const useUniqueId = () => {
|
||||
export default function useUniqueId() {
|
||||
const idRef = useRef<string>();
|
||||
|
||||
if (!idRef.current) {
|
||||
idRef.current = generateIdFor(store);
|
||||
store[idRef.current] = true;
|
||||
idRef.current = generateUniqueId();
|
||||
}
|
||||
|
||||
return idRef.current;
|
||||
};
|
||||
|
||||
export default useUniqueId;
|
||||
}
|
||||
|
||||
@ -5,7 +5,7 @@ import {
|
||||
} from '../../util/windowEnvironment';
|
||||
import { animate } from '../../util/animation';
|
||||
import cycleRestrict from '../../util/cycleRestrict';
|
||||
import generateIdFor from '../../util/generateIdFor';
|
||||
import generateUniqueId from '../../util/generateUniqueId';
|
||||
import launchMediaWorkers, { MAX_WORKERS } from '../../util/launchMediaWorkers';
|
||||
|
||||
interface Params {
|
||||
@ -27,7 +27,6 @@ const LOW_PRIORITY_QUALITY = IS_ANDROID ? 0.5 : 0.75;
|
||||
const LOW_PRIORITY_QUALITY_SIZE_THRESHOLD = 24;
|
||||
const HIGH_PRIORITY_CACHE_MODULO = IS_SAFARI ? 2 : 4;
|
||||
const LOW_PRIORITY_CACHE_MODULO = 0;
|
||||
const ID_STORE = {};
|
||||
|
||||
const workers = launchMediaWorkers().map(({ connector }) => connector);
|
||||
const instancesByRenderId = new Map<string, RLottie>();
|
||||
@ -92,7 +91,7 @@ class RLottie {
|
||||
, canvas,
|
||||
renderId,
|
||||
params,
|
||||
viewId = generateIdFor(ID_STORE, true), ,
|
||||
viewId = generateUniqueId(), ,
|
||||
onLoad,
|
||||
] = args;
|
||||
let instance = instancesByRenderId.get(renderId);
|
||||
@ -113,7 +112,7 @@ class RLottie {
|
||||
private container: HTMLDivElement | HTMLCanvasElement,
|
||||
private renderId: string,
|
||||
private params: Params,
|
||||
viewId: string = generateIdFor(ID_STORE, true),
|
||||
viewId: string = generateUniqueId(),
|
||||
private customColor?: [number, number, number],
|
||||
private onLoad?: NoneToVoidFunction | undefined,
|
||||
private onEnded?: (isDestroyed?: boolean) => void,
|
||||
|
||||
@ -397,7 +397,7 @@ export function renderComponent(componentInstance: ComponentInstance) {
|
||||
DEBUG_components[componentName] = {
|
||||
componentName,
|
||||
renderCount: 0,
|
||||
renderTimes: [],
|
||||
avgRenderTime: 0,
|
||||
};
|
||||
}
|
||||
|
||||
@ -420,7 +420,9 @@ export function renderComponent(componentInstance: ComponentInstance) {
|
||||
// eslint-disable-next-line no-console
|
||||
console.warn(`[Teact] Slow component render: ${componentName}, ${Math.round(duration)} ms`);
|
||||
}
|
||||
DEBUG_components[componentName].renderTimes.push(duration);
|
||||
|
||||
const { renderCount, avgRenderTime } = DEBUG_components[componentName];
|
||||
DEBUG_components[componentName].avgRenderTime = (avgRenderTime * renderCount + duration) / (renderCount + 1);
|
||||
DEBUG_components[componentName].renderCount++;
|
||||
DEBUG_components.TOTAL.renderCount++;
|
||||
|
||||
|
||||
@ -1,15 +1,16 @@
|
||||
/* eslint-disable eslint-multitab-tt/set-global-only-variable */
|
||||
import type { FC, FC_withDebug, Props } from './teact';
|
||||
import React, { useEffect, useState } from './teact';
|
||||
import React, { useEffect } from './teact';
|
||||
import { requestMeasure } from '../fasterdom/fasterdom';
|
||||
|
||||
import { DEBUG, DEBUG_MORE } from '../../config';
|
||||
import useForceUpdate from '../../hooks/useForceUpdate';
|
||||
import generateIdFor from '../../util/generateIdFor';
|
||||
import { throttleWithTickEnd } from '../../util/schedulers';
|
||||
import arePropsShallowEqual, { getUnequalProps } from '../../util/arePropsShallowEqual';
|
||||
import { orderBy } from '../../util/iteratees';
|
||||
import { handleError } from '../../util/handleError';
|
||||
|
||||
import useForceUpdate from '../../hooks/useForceUpdate';
|
||||
import useUniqueId from '../../hooks/useUniqueId';
|
||||
import { isHeavyAnimating } from '../../hooks/useHeavyAnimationCheck';
|
||||
|
||||
export default React;
|
||||
@ -252,7 +253,7 @@ export function withGlobal<OwnProps extends AnyLiteral>(
|
||||
return function TeactNContainer(props: OwnProps) {
|
||||
(TeactNContainer as FC_withDebug).DEBUG_contentComponentName = Component.name;
|
||||
|
||||
const [id] = useState(generateIdFor(containers));
|
||||
const id = useUniqueId();
|
||||
const forceUpdate = useForceUpdate();
|
||||
|
||||
useEffect(() => {
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
import generateIdFor from '../../util/generateIdFor';
|
||||
import generateUniqueId from '../../util/generateUniqueId';
|
||||
import { pause } from '../../util/schedulers';
|
||||
|
||||
declare const self: WorkerGlobalScope;
|
||||
@ -15,7 +15,7 @@ const PART_TIMEOUT = 30000;
|
||||
const requestStates = new Map<string, RequestStates>();
|
||||
|
||||
export function requestPart(params: RequestPartParams): Promise<ArrayBuffer | undefined> {
|
||||
const messageId = generateIdFor(requestStates);
|
||||
const messageId = generateUniqueId();
|
||||
const requestState = {} as RequestStates;
|
||||
|
||||
let isResolved = false;
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
import { pause } from '../util/schedulers';
|
||||
import generateIdFor from '../util/generateIdFor';
|
||||
import generateUniqueId from '../util/generateUniqueId';
|
||||
import {
|
||||
DEBUG,
|
||||
MEDIA_CACHE_MAX_BYTES,
|
||||
@ -149,7 +149,7 @@ export async function requestPart(
|
||||
return undefined;
|
||||
}
|
||||
|
||||
const messageId = generateIdFor(requestStates);
|
||||
const messageId = generateUniqueId();
|
||||
const requestState = {} as RequestStates;
|
||||
|
||||
let isResolved = false;
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
import generateIdFor from './generateIdFor';
|
||||
import generateUniqueId from './generateUniqueId';
|
||||
|
||||
export interface CancellableCallback {
|
||||
(
|
||||
@ -108,7 +108,7 @@ class ConnectorClass<T extends InputRequestTypes> {
|
||||
request(messageData: RequestTypes<T>) {
|
||||
const { requestStates, requestStatesByCallback } = this;
|
||||
|
||||
const messageId = generateIdFor(requestStates);
|
||||
const messageId = generateUniqueId();
|
||||
const payload: CallMethodData = {
|
||||
type: 'callMethod',
|
||||
messageId,
|
||||
|
||||
@ -10,7 +10,7 @@ import { selectCanPlayAnimatedEmojis } from '../global/selectors';
|
||||
import { getStickerPreviewHash } from '../global/helpers';
|
||||
import * as mediaLoader from './mediaLoader';
|
||||
import { throttle } from './schedulers';
|
||||
import generateIdFor from './generateIdFor';
|
||||
import generateUniqueId from './generateUniqueId';
|
||||
import { IS_WEBM_SUPPORTED } from './windowEnvironment';
|
||||
import { createCallbackManager } from './callbacks';
|
||||
|
||||
@ -20,7 +20,6 @@ import blankSrc from '../assets/blank.png';
|
||||
type CustomEmojiLoadCallback = (customEmojis: GlobalState['customEmojis']) => void;
|
||||
type CustomEmojiInputRenderCallback = (emojiId: string) => void;
|
||||
|
||||
const ID_STORE = {};
|
||||
const DOM_PROCESS_THROTTLE = 500;
|
||||
|
||||
const INPUT_WAITING_CUSTOM_EMOJI_IDS: Set<string> = new Set();
|
||||
@ -120,7 +119,7 @@ export function getInputCustomEmojiParams(customEmoji?: ApiSticker) {
|
||||
const isUsingSharedCanvas = customEmoji.isLottie || (customEmoji.isVideo && !shouldUseStaticFallback);
|
||||
if (isUsingSharedCanvas) {
|
||||
fetchAndProcess(`sticker${customEmoji.id}`);
|
||||
return [false, blankSrc, generateIdFor(ID_STORE, true)];
|
||||
return [false, blankSrc, generateUniqueId()];
|
||||
}
|
||||
|
||||
const mediaData = getCustomEmojiMediaDataForInput(customEmoji.id, shouldUseStaticFallback);
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@ -3,112 +3,31 @@
|
||||
import type { ApiLangPack } from '../api/types';
|
||||
|
||||
export const fallbackLangPackInitial = {
|
||||
WrongNumber: {
|
||||
key: 'WrongNumber',
|
||||
value: 'Wrong number?',
|
||||
},
|
||||
SentAppCode: {
|
||||
key: 'SentAppCode',
|
||||
value: 'We\'ve sent the code to the **Telegram** app on your other device.',
|
||||
},
|
||||
'Login.JustSentSms': {
|
||||
key: 'Login.JustSentSms',
|
||||
value: 'We have sent you a code via SMS. Please enter it above.',
|
||||
},
|
||||
'Login.Header.Password': {
|
||||
key: 'Login.Header.Password',
|
||||
value: 'Enter Password',
|
||||
},
|
||||
'Login.EnterPasswordDescription': {
|
||||
key: 'Login.EnterPasswordDescription',
|
||||
value: 'You have Two-Step Verification enabled, so your account is protected with an additional password.',
|
||||
},
|
||||
StartText: {
|
||||
key: 'StartText',
|
||||
value: 'Please confirm your country code and enter your phone number.',
|
||||
},
|
||||
'Login.PhonePlaceholder': {
|
||||
key: 'Login.PhonePlaceholder',
|
||||
value: 'Your phone number',
|
||||
},
|
||||
'Login.Next': {
|
||||
key: 'Login.Next',
|
||||
value: 'Next',
|
||||
},
|
||||
'Login.QR.Login': {
|
||||
key: 'Login.QR.Login',
|
||||
value: 'Log in by QR Code',
|
||||
},
|
||||
'Login.QR.Title': {
|
||||
key: 'Login.QR.Title',
|
||||
value: 'Log in to Telegram by QR Code',
|
||||
},
|
||||
'Login.QR.Help1': {
|
||||
key: 'Login.QR.Help1',
|
||||
value: 'Open Telegram on your phone',
|
||||
},
|
||||
'Login.QR.Help2': {
|
||||
key: 'Login.QR.Help2',
|
||||
value: 'Go to **Settings** > **Devices** > **Link Desktop Device**',
|
||||
},
|
||||
'Login.QR2.Help2': {
|
||||
key: 'Login.QR.Help2',
|
||||
value: 'Go to **Settings** → **Devices** → **Link Desktop Device**',
|
||||
},
|
||||
'Login.QR.Help3': {
|
||||
key: 'Login.QR.Help3',
|
||||
value: 'Point your phone at this screen to confirm login',
|
||||
},
|
||||
'Login.QR.Cancel': {
|
||||
key: 'Login.QR.Cancel',
|
||||
value: 'Log in by phone Number',
|
||||
},
|
||||
YourName: {
|
||||
key: 'YourName',
|
||||
value: 'Your Name',
|
||||
},
|
||||
'Login.Register.Desc': {
|
||||
key: 'Login.Register.Desc',
|
||||
value: 'Enter your name and add a profile picture.',
|
||||
},
|
||||
'Login.Register.FirstName.Placeholder': {
|
||||
key: 'Login.Register.FirstName.Placeholder',
|
||||
value: 'First Name',
|
||||
},
|
||||
'Login.Register.LastName.Placeholder': {
|
||||
key: 'Login.Register.LastName.Placeholder',
|
||||
value: 'Last Name',
|
||||
},
|
||||
'Login.SelectCountry.Title': {
|
||||
key: 'Login.SelectCountry.Title',
|
||||
value: 'Country',
|
||||
},
|
||||
lng_country_none: {
|
||||
key: 'lng_country_none',
|
||||
value: 'Country not found',
|
||||
},
|
||||
PleaseEnterPassword: {
|
||||
key: 'PleaseEnterPassword',
|
||||
value: 'Enter your new password',
|
||||
},
|
||||
PHONE_NUMBER_INVALID: {
|
||||
key: 'PHONE_NUMBER_INVALID',
|
||||
value: 'Invalid phone number',
|
||||
},
|
||||
PHONE_CODE_INVALID: {
|
||||
key: 'PHONE_CODE_INVALID',
|
||||
value: 'Invalid code',
|
||||
},
|
||||
PASSWORD_HASH_INVALID: {
|
||||
key: 'PASSWORD_HASH_INVALID',
|
||||
value: 'Incorrect password',
|
||||
},
|
||||
PHONE_PASSWORD_FLOOD: {
|
||||
key: 'PHONE_PASSWORD_FLOOD',
|
||||
value: 'Limit exceeded. Please try again later.',
|
||||
},
|
||||
PHONE_NUMBER_BANNED: {
|
||||
key: 'PHONE_NUMBER_BANNED',
|
||||
value: 'This phone number is banned.',
|
||||
},
|
||||
WrongNumber: 'Wrong number?',
|
||||
SentAppCode: 'We\'ve sent the code to the **Telegram** app on your other device.',
|
||||
'Login.JustSentSms': 'We have sent you a code via SMS. Please enter it above.',
|
||||
'Login.Header.Password': 'Enter Password',
|
||||
'Login.EnterPasswordDescription': 'You have Two-Step Verification enabled, so your account is protected with an additional password.',
|
||||
StartText: 'Please confirm your country code and enter your phone number.',
|
||||
'Login.PhonePlaceholder': 'Your phone number',
|
||||
'Login.Next': 'Next',
|
||||
'Login.QR.Login': 'Log in by QR Code',
|
||||
'Login.QR.Title': 'Log in to Telegram by QR Code',
|
||||
'Login.QR.Help1': 'Open Telegram on your phone',
|
||||
'Login.QR.Help2': 'Go to **Settings** > **Devices** > **Link Desktop Device**',
|
||||
'Login.QR2.Help2': 'Go to **Settings** → **Devices** → **Link Desktop Device**',
|
||||
'Login.QR.Help3': 'Point your phone at this screen to confirm login',
|
||||
'Login.QR.Cancel': 'Log in by phone Number',
|
||||
YourName: 'Your Name',
|
||||
'Login.Register.Desc': 'Enter your name and add a profile picture.',
|
||||
'Login.Register.FirstName.Placeholder': 'First Name',
|
||||
'Login.Register.LastName.Placeholder': 'Last Name',
|
||||
'Login.SelectCountry.Title': 'Country',
|
||||
lng_country_none: 'Country not found',
|
||||
PleaseEnterPassword: 'Enter your new password',
|
||||
PHONE_NUMBER_INVALID: 'Invalid phone number',
|
||||
PHONE_CODE_INVALID: 'Invalid code',
|
||||
PASSWORD_HASH_INVALID: 'Incorrect password',
|
||||
PHONE_PASSWORD_FLOOD: 'Limit exceeded. Please try again later.',
|
||||
PHONE_NUMBER_BANNED: 'This phone number is banned.',
|
||||
} as ApiLangPack;
|
||||
|
||||
@ -1,13 +0,0 @@
|
||||
export default function generateIdFor(store: AnyLiteral, withAutoUpdate = false) {
|
||||
let id;
|
||||
|
||||
do {
|
||||
id = String(Math.random()).replace('0.', 'id');
|
||||
} while (store[id]);
|
||||
|
||||
if (withAutoUpdate) {
|
||||
store[id] = true;
|
||||
}
|
||||
|
||||
return id;
|
||||
}
|
||||
3
src/util/generateUniqueId.ts
Normal file
3
src/util/generateUniqueId.ts
Normal file
@ -0,0 +1,3 @@
|
||||
export default function generateUniqueId() {
|
||||
return Date.now().toString(36) + Math.random().toString(36).slice(2);
|
||||
}
|
||||
@ -29,7 +29,10 @@ const PLURAL_RULES = {
|
||||
en: (n: number) => (n !== 1 ? 6 : 2),
|
||||
ar: (n: number) => (n === 0 ? 1 : n === 1 ? 2 : n === 2 ? 3 : n % 100 >= 3 && n % 100 <= 10 ? 4 : n % 100 >= 11 ? 5 : 6),
|
||||
be: (n: number) => {
|
||||
const s = String(n).split('.'); const t0 = Number(s[0]) === n; const n10 = t0 ? Number(s[0].slice(-1)) : 0; const n100 = t0 ? Number(s[0].slice(-2)) : 0;
|
||||
const s = String(n).split('.');
|
||||
const t0 = Number(s[0]) === n;
|
||||
const n10 = t0 ? Number(s[0].slice(-1)) : 0;
|
||||
const n100 = t0 ? Number(s[0].slice(-2)) : 0;
|
||||
return n10 === 1 && n100 !== 11 ? 2
|
||||
: (n10 >= 2 && n10 <= 4) && (n100 < 12 || n100 > 14) ? 4
|
||||
: (t0 && n10 === 0) || (n10 >= 5 && n10 <= 9) || (n100 >= 11 && n100 <= 14) ? 5
|
||||
@ -37,7 +40,9 @@ const PLURAL_RULES = {
|
||||
},
|
||||
ca: (n: number) => (n !== 1 ? 6 : 2),
|
||||
cs: (n: number) => {
|
||||
const s = String(n).split('.'); const i = Number(s[0]); const v0 = !s[1];
|
||||
const s = String(n).split('.');
|
||||
const i = Number(s[0]);
|
||||
const v0 = !s[1];
|
||||
return n === 1 && v0 ? 2 : (i >= 2 && i <= 4) && v0 ? 4 : !v0 ? 5 : 6;
|
||||
},
|
||||
de: (n: number) => (n !== 1 ? 6 : 2),
|
||||
@ -48,8 +53,14 @@ const PLURAL_RULES = {
|
||||
id: () => 0,
|
||||
it: (n: number) => (n !== 1 ? 6 : 2),
|
||||
hr: (n: number) => {
|
||||
const s = String(n).split('.'); const i = s[0]; const f = s[1] || ''; const v0 = !s[1]; const i10 = Number(i.slice(-1));
|
||||
const i100 = Number(i.slice(-2)); const f10 = Number(f.slice(-1)); const f100 = Number(f.slice(-2));
|
||||
const s = String(n).split('.');
|
||||
const i = s[0];
|
||||
const f = s[1] || '';
|
||||
const v0 = !s[1];
|
||||
const i10 = Number(i.slice(-1));
|
||||
const i100 = Number(i.slice(-2));
|
||||
const f10 = Number(f.slice(-1));
|
||||
const f100 = Number(f.slice(-2));
|
||||
return (v0 && i10 === 1 && i100 !== 11) || (f10 === 1 && f100 !== 11) ? 2
|
||||
: (v0 && (i10 >= 2 && i10 <= 4) && (i100 < 12 || i100 > 14)) || ((f10 >= 2 && f10 <= 4) && (f100 < 12 || f100 > 14)) ? 4
|
||||
: 6;
|
||||
@ -63,12 +74,20 @@ const PLURAL_RULES = {
|
||||
'pt-br': (n: number) => (n > 1 ? 6 : 2),
|
||||
ru: (n: number) => (n % 10 === 1 && n % 100 !== 11 ? 2 : n % 10 >= 2 && n % 10 <= 4 && (n % 100 < 10 || n % 100 >= 20) ? 4 : 5),
|
||||
sk: (n: number) => {
|
||||
const s = String(n).split('.'); const i = Number(s[0]); const v0 = !s[1];
|
||||
const s = String(n).split('.');
|
||||
const i = Number(s[0]);
|
||||
const v0 = !s[1];
|
||||
return n === 1 && v0 ? 2 : (i >= 2 && i <= 4) && v0 ? 4 : !v0 ? 5 : 6;
|
||||
},
|
||||
sr: (n: number) => {
|
||||
const s = String(n).split('.'); const i = s[0]; const f = s[1] || ''; const v0 = !s[1]; const i10 = Number(i.slice(-1));
|
||||
const i100 = Number(i.slice(-2)); const f10 = Number(f.slice(-1)); const f100 = Number(f.slice(-2));
|
||||
const s = String(n).split('.');
|
||||
const i = s[0];
|
||||
const f = s[1] || '';
|
||||
const v0 = !s[1];
|
||||
const i10 = Number(i.slice(-1));
|
||||
const i100 = Number(i.slice(-2));
|
||||
const f10 = Number(f.slice(-1));
|
||||
const f100 = Number(f.slice(-2));
|
||||
return (v0 && i10 === 1 && i100 !== 11) || (f10 === 1 && f100 !== 11) ? 2
|
||||
: (v0 && (i10 >= 2 && i10 <= 4) && (i100 < 12 || i100 > 14)) || ((f10 >= 2 && f10 <= 4) && (f100 < 12 || f100 > 14)) ? 4
|
||||
: 6;
|
||||
@ -224,7 +243,7 @@ async function fetchRemoteString(
|
||||
});
|
||||
|
||||
if (remote?.length) {
|
||||
await cacheApi.save(LANG_CACHE_NAME, `${remoteLangPack}_${langCode}_${key}`, remote[0]);
|
||||
await cacheApi.save(LANG_CACHE_NAME, `${remoteLangPack}_${langCode}_${key}`, remote[0] || '');
|
||||
|
||||
return remote[0];
|
||||
}
|
||||
@ -257,10 +276,16 @@ function processTranslation(
|
||||
const preferredPluralOption = typeof value === 'number' || pluralValue !== undefined
|
||||
? getPluralOption(pluralValue ?? value)
|
||||
: 'value';
|
||||
const template = langString ? (
|
||||
langString[preferredPluralOption] || langString.otherValue || langString.value
|
||||
) : undefined;
|
||||
if (!template || !template.trim()) {
|
||||
const template = typeof langString === 'string'
|
||||
? langString
|
||||
: preferredPluralOption === 'value'
|
||||
// Support cached older `langString` interface
|
||||
? (typeof langString === 'object' ? (langString as any).value : langString)
|
||||
: typeof langString === 'object'
|
||||
? langString[preferredPluralOption] || langString.otherValue
|
||||
: undefined;
|
||||
|
||||
if (!template?.trim()) {
|
||||
const parts = key.split('.');
|
||||
|
||||
return parts[parts.length - 1];
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user