Bump dependencies (#6714)
This commit is contained in:
parent
0d9d4bbebf
commit
319c0396a4
@ -1,26 +1,23 @@
|
||||
import eslint from '@eslint/js';
|
||||
import eslintReact from '@eslint-react/eslint-plugin';
|
||||
import stylisticJs from '@stylistic/eslint-plugin';
|
||||
import { globalIgnores } from 'eslint/config';
|
||||
import importsPlugin from 'eslint-plugin-import';
|
||||
import { defineConfig, globalIgnores } from 'eslint/config';
|
||||
import { importX } from 'eslint-plugin-import-x';
|
||||
import jestPlugin from 'eslint-plugin-jest';
|
||||
import jsxA11yPlugin from 'eslint-plugin-jsx-a11y';
|
||||
import noNullPlugin from 'eslint-plugin-no-null';
|
||||
import reactPlugin from 'eslint-plugin-react';
|
||||
import reactHooksStaticDeps from 'eslint-plugin-react-hooks-static-deps';
|
||||
import reactXPlugin from 'eslint-plugin-react-x';
|
||||
import simpleImportSortPlugin from 'eslint-plugin-simple-import-sort';
|
||||
import ttMultitabPlugin from 'eslint-plugin-tt-multitab';
|
||||
import unusedImports from 'eslint-plugin-unused-imports';
|
||||
import tseslint from 'typescript-eslint';
|
||||
|
||||
export default tseslint.config(
|
||||
export default defineConfig(
|
||||
eslint.configs.recommended,
|
||||
tseslint.configs.recommendedTypeChecked,
|
||||
tseslint.configs.stylistic,
|
||||
reactPlugin.configs.flat.recommended,
|
||||
reactPlugin.configs.flat['jsx-runtime'],
|
||||
reactXPlugin.configs['recommended-type-checked'],
|
||||
jsxA11yPlugin.flatConfigs.recommended,
|
||||
eslintReact.configs['recommended-typescript'],
|
||||
importX.flatConfigs.recommended,
|
||||
importX.flatConfigs.typescript,
|
||||
ttMultitabPlugin.configs.recommended,
|
||||
stylisticJs.configs.customize({
|
||||
semi: true,
|
||||
@ -68,6 +65,16 @@ export default tseslint.config(
|
||||
'no-prototype-builtins': 'off',
|
||||
'no-undef': 'off',
|
||||
'no-unused-vars': 'off',
|
||||
'@stylistic/comma-dangle': ['error', {
|
||||
arrays: 'always-multiline',
|
||||
objects: 'always-multiline',
|
||||
imports: 'always-multiline',
|
||||
exports: 'always-multiline',
|
||||
functions: 'always-multiline',
|
||||
enums: 'always-multiline',
|
||||
tuples: 'always-multiline',
|
||||
generics: 'ignore',
|
||||
}],
|
||||
'@stylistic/multiline-ternary': 'off',
|
||||
'@stylistic/operator-linebreak': 'off',
|
||||
'@stylistic/max-len': ['error', {
|
||||
@ -170,6 +177,8 @@ export default tseslint.config(
|
||||
'@typescript-eslint/prefer-promise-reject-errors': 'off',
|
||||
'@typescript-eslint/unbound-method': 'off',
|
||||
'unused-imports/no-unused-imports': 'error',
|
||||
'import-x/namespace': ['error', { allowComputed: true }],
|
||||
'import-x/no-named-as-default-member': 'off',
|
||||
'react-hooks/exhaustive-deps': 'off',
|
||||
'react-hooks-static-deps/exhaustive-deps': [
|
||||
'error',
|
||||
@ -185,40 +194,24 @@ export default tseslint.config(
|
||||
},
|
||||
},
|
||||
],
|
||||
'react/prop-types': 'off',
|
||||
'react/no-unknown-property': 'off',
|
||||
'react/display-name': 'off',
|
||||
'react/jsx-key': 'off',
|
||||
'react/jsx-curly-spacing': [
|
||||
'error',
|
||||
{
|
||||
when: 'never',
|
||||
attributes: true,
|
||||
children: true,
|
||||
allowMultiline: true,
|
||||
},
|
||||
],
|
||||
'react-x/no-use-context': 'off',
|
||||
'react-x/no-context-provider': 'off',
|
||||
'react-x/no-array-index-key': 'off',
|
||||
'react-x/no-missing-key': 'off',
|
||||
'react-x/no-nested-component-definitions': 'off',
|
||||
'react-x/no-unused-props': 'off',
|
||||
'react-x/no-leaked-conditional-rendering': 'error',
|
||||
'jsx-a11y/click-events-have-key-events': 'off',
|
||||
'jsx-a11y/mouse-events-have-key-events': 'off',
|
||||
'jsx-a11y/no-static-element-interactions': 'off',
|
||||
'jsx-a11y/label-has-associated-control': 'off',
|
||||
'jsx-a11y/anchor-is-valid': 'off',
|
||||
'jsx-a11y/no-noninteractive-element-to-interactive-role': 'off',
|
||||
'jsx-a11y/media-has-caption': 'off',
|
||||
'@eslint-react/exhaustive-deps': 'off',
|
||||
'@eslint-react/set-state-in-effect': 'off',
|
||||
'@eslint-react/unsupported-syntax': 'off',
|
||||
'@eslint-react/no-clone-element': 'off',
|
||||
'@eslint-react/component-hook-factories': 'off',
|
||||
'@eslint-react/no-use-context': 'off',
|
||||
'@eslint-react/no-context-provider': 'off',
|
||||
'@eslint-react/no-array-index-key': 'off',
|
||||
'@eslint-react/web-api/no-leaked-timeout': 'off',
|
||||
'@eslint-react/no-missing-key': 'off',
|
||||
'@eslint-react/no-nested-component-definitions': 'off',
|
||||
'@eslint-react/no-unused-props': 'off',
|
||||
'@eslint-react/no-leaked-conditional-rendering': 'error',
|
||||
},
|
||||
plugins: {
|
||||
'no-null': noNullPlugin,
|
||||
'simple-import-sort': simpleImportSortPlugin,
|
||||
import: importsPlugin,
|
||||
'unused-imports': unusedImports,
|
||||
react: reactPlugin,
|
||||
'react-hooks-static-deps': reactHooksStaticDeps,
|
||||
jest: jestPlugin,
|
||||
'tt-multitab': ttMultitabPlugin,
|
||||
|
||||
5656
package-lock.json
generated
5656
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
94
package.json
94
package.json
@ -42,100 +42,100 @@
|
||||
},
|
||||
"devDependencies": {
|
||||
"@babel/core": "^7.29.0",
|
||||
"@babel/preset-env": "^7.29.0",
|
||||
"@babel/preset-env": "^7.29.2",
|
||||
"@babel/preset-react": "^7.28.5",
|
||||
"@babel/preset-typescript": "^7.28.5",
|
||||
"@babel/register": "^7.28.6",
|
||||
"@eslint/js": "^9.39.2",
|
||||
"@eslint-react/eslint-plugin": "^3.0.0",
|
||||
"@eslint/js": "^10.0.1",
|
||||
"@glen/jest-raw-loader": "^2.0.0",
|
||||
"@mytonwallet/stylelint-whole-pixel": "github:mytonwallet-org/stylelint-whole-pixel#18b0b8c",
|
||||
"@mytonwallet/webpack-watch-file-plugin": "github:mytonwallet-org/webpack-watch-file-plugin#0e36009",
|
||||
"@playwright/test": "^1.58.1",
|
||||
"@playwright/test": "^1.58.2",
|
||||
"@statoscope/cli": "5.29.0",
|
||||
"@statoscope/webpack-plugin": "5.29.0",
|
||||
"@stylistic/eslint-plugin": "^5.7.1",
|
||||
"@stylistic/stylelint-config": "^4.0.0",
|
||||
"@stylistic/stylelint-plugin": "^5.0.1",
|
||||
"@tauri-apps/cli": "^2.9.6",
|
||||
"@stylistic/eslint-plugin": "^5.10.0",
|
||||
"@stylistic/stylelint-config": "^5.0.0",
|
||||
"@stylistic/stylelint-plugin": "^5.1.0",
|
||||
"@tauri-apps/cli": "^2.10.1",
|
||||
"@testing-library/jest-dom": "^6.9.1",
|
||||
"@types/dom-chromium-ai": "^0.0.13",
|
||||
"@twbs/fantasticon": "^3.1.0",
|
||||
"@types/dom-chromium-ai": "^0.0.16",
|
||||
"@types/dom-view-transitions": "^1.0.6",
|
||||
"@types/hast": "^3.0.4",
|
||||
"@types/jest": "^30.0.0",
|
||||
"@types/node": "^25.1.0",
|
||||
"@types/react": "^19.2.10",
|
||||
"@types/node": "^25.5.0",
|
||||
"@types/react": "^19.2.14",
|
||||
"@types/react-dom": "^19.2.3",
|
||||
"@types/webpack": "^5.28.5",
|
||||
"@typescript-eslint/eslint-plugin": "^8.54.0",
|
||||
"@typescript-eslint/parser": "^8.54.0",
|
||||
"@typescript-eslint/eslint-plugin": "^8.58.0",
|
||||
"@typescript-eslint/parser": "^8.58.0",
|
||||
"@webpack-cli/serve": "^3.0.1",
|
||||
"autoprefixer": "^10.4.24",
|
||||
"babel-loader": "^10.0.0",
|
||||
"autoprefixer": "^10.4.27",
|
||||
"babel-loader": "^10.1.1",
|
||||
"babel-plugin-transform-import-meta": "^2.3.3",
|
||||
"buffer": "^6.0.3",
|
||||
"concurrently": "^9.2.1",
|
||||
"copy-webpack-plugin": "^13.0.1",
|
||||
"copy-webpack-plugin": "^14.0.0",
|
||||
"cross-env": "^10.1.0",
|
||||
"css-loader": "^7.1.3",
|
||||
"dotenv": "^17.2.3",
|
||||
"eslint": "^9.39.2",
|
||||
"eslint-plugin-import": "^2.32.0",
|
||||
"eslint-plugin-jest": "^29.12.1",
|
||||
"eslint-plugin-jsx-a11y": "^6.10.2",
|
||||
"css-loader": "^7.1.4",
|
||||
"dotenv": "^17.3.1",
|
||||
"eslint": "^10.1.0",
|
||||
"eslint-import-resolver-typescript": "^4.4.4",
|
||||
"eslint-plugin-import-x": "^4.16.2",
|
||||
"eslint-plugin-jest": "^29.15.1",
|
||||
"eslint-plugin-no-null": "^1.0.2",
|
||||
"eslint-plugin-react": "^7.37.5",
|
||||
"eslint-plugin-react-hooks-static-deps": "git+https://github.com/zubiden/eslint-plugin-react-hooks-static-deps#c16f35b",
|
||||
"eslint-plugin-react-x": "^2.8.1",
|
||||
"eslint-plugin-react-hooks-static-deps": "github:zubiden/eslint-plugin-react-hooks-static-deps#160ac4a",
|
||||
"eslint-plugin-react-x": "^3.0.0",
|
||||
"eslint-plugin-simple-import-sort": "^12.1.1",
|
||||
"eslint-plugin-tt-multitab": "git+https://github.com/zubiden/eslint-plugin-tt-multitab#137cf50",
|
||||
"eslint-plugin-unused-imports": "^4.3.0",
|
||||
"eslint-plugin-tt-multitab": "github:zubiden/eslint-plugin-tt-multitab#137cf50",
|
||||
"eslint-plugin-unused-imports": "^4.4.1",
|
||||
"fake-indexeddb": "^6.2.5",
|
||||
"@twbs/fantasticon": "^3.1.0",
|
||||
"git-revision-webpack-plugin": "^5.0.0",
|
||||
"gitlog": "^5.1.0",
|
||||
"html-webpack-plugin": "^5.6.6",
|
||||
"husky": "^9.1.7",
|
||||
"jest": "^30.2.0",
|
||||
"jest-environment-jsdom": "^30.2.0",
|
||||
"lint-staged": "^16.2.7",
|
||||
"mini-css-extract-plugin": "^2.10.0",
|
||||
"postcss": "^8.5.6",
|
||||
"jest": "^30.3.0",
|
||||
"jest-environment-jsdom": "^30.3.0",
|
||||
"lint-staged": "^16.4.0",
|
||||
"mini-css-extract-plugin": "^2.10.2",
|
||||
"postcss": "^8.5.8",
|
||||
"postcss-load-config": "^6.0.1",
|
||||
"postcss-loader": "^8.2.0",
|
||||
"postcss-loader": "^8.2.1",
|
||||
"postcss-modules": "^6.0.1",
|
||||
"react": "^19.2.4",
|
||||
"sass": "^1.97.3",
|
||||
"sass-loader": "^16.0.6",
|
||||
"sass": "^1.98.0",
|
||||
"sass-loader": "^16.0.7",
|
||||
"script-loader": "^0.7.2",
|
||||
"serve": "^14.2.5",
|
||||
"stylelint": "^17.1.0",
|
||||
"stylelint-config-clean-order": "^8.0.0",
|
||||
"serve": "^14.2.6",
|
||||
"stylelint": "^17.6.0",
|
||||
"stylelint-config-clean-order": "^8.0.1",
|
||||
"stylelint-config-recommended-scss": "^17.0.0",
|
||||
"stylelint-declaration-block-no-ignored-properties": "^3.0.0",
|
||||
"stylelint-group-selectors": "^1.0.10",
|
||||
"stylelint-high-performance-animation": "^2.0.0",
|
||||
"stylelint-plugin-use-baseline": "^1.2.1",
|
||||
"stylelint-plugin-use-baseline": "^1.4.1",
|
||||
"telegraph-node": "^1.0.4",
|
||||
"tsx": "^4.21.0",
|
||||
"typescript": "5.9.3",
|
||||
"typescript-eslint": "^8.54.0",
|
||||
"user-agent-data-types": "^0.4.2",
|
||||
"webpack": "^5.104.1",
|
||||
"typescript": "^6.0.2",
|
||||
"typescript-eslint": "^8.58.0",
|
||||
"user-agent-data-types": "^0.4.3",
|
||||
"webpack": "^5.105.4",
|
||||
"webpack-dev-server": "^5.2.3"
|
||||
},
|
||||
"dependencies": {
|
||||
"@cryptography/aes": "^0.1.1",
|
||||
"@tauri-apps/api": "^2.9.1",
|
||||
"@tauri-apps/api": "^2.10.1",
|
||||
"@tauri-apps/plugin-notification": "^2.3.3",
|
||||
"@tauri-apps/plugin-process": "^2.3.1",
|
||||
"@tauri-apps/plugin-shell": "^2.3.4",
|
||||
"@tauri-apps/plugin-updater": "^2.9.0",
|
||||
"@tauri-apps/plugin-shell": "^2.3.5",
|
||||
"@tauri-apps/plugin-updater": "^2.10.0",
|
||||
"async-mutex": "^0.5.0",
|
||||
"emoji-data-ios": "git+https://github.com/korenskoy/emoji-data-ios#30529a2",
|
||||
"fflate": "^0.8.2",
|
||||
"idb-keyval": "^6.2.2",
|
||||
"lowlight": "^3.3.0",
|
||||
"music-metadata": "^11.11.1",
|
||||
"music-metadata": "^11.12.3",
|
||||
"opus-recorder": "github:Ajaxy/opus-recorder#116830a",
|
||||
"os-browserify": "^0.3.0",
|
||||
"path-browserify": "^1.0.1",
|
||||
|
||||
3
src/@types/global.d.ts
vendored
3
src/@types/global.d.ts
vendored
@ -2,7 +2,8 @@
|
||||
|
||||
declare const process: NodeJS.Process;
|
||||
|
||||
declare module '*.module.scss';
|
||||
declare module '*.css';
|
||||
declare module '*.scss';
|
||||
|
||||
declare const APP_VERSION: string;
|
||||
declare const APP_REVISION: string;
|
||||
|
||||
@ -26,8 +26,7 @@ import { buildApiChatFromPreview } from '../apiBuilders/chats';
|
||||
import { addDocumentToLocalDb } from '../helpers/localDb';
|
||||
import { buildApiFormattedText } from './common';
|
||||
import { buildApiCurrencyAmount } from './payments';
|
||||
import { buildApiPeerId } from './peers';
|
||||
import { getApiChatIdFromMtpPeer } from './peers';
|
||||
import { buildApiPeerId, getApiChatIdFromMtpPeer } from './peers';
|
||||
import { buildStickerFromDocument } from './symbols';
|
||||
import { buildApiUser } from './users';
|
||||
|
||||
|
||||
@ -15,8 +15,7 @@ import type {
|
||||
} from '../../types';
|
||||
|
||||
import { buildCollectionByCallback, omitUndefined } from '../../../util/iteratees';
|
||||
import { addDocumentToLocalDb } from '../helpers/localDb';
|
||||
import { addPhotoToLocalDb } from '../helpers/localDb';
|
||||
import { addDocumentToLocalDb, addPhotoToLocalDb } from '../helpers/localDb';
|
||||
import { buildApiPhoto, buildPrivacyRules } from './common';
|
||||
import { buildApiDocument, buildGeoPoint, buildMessageMediaContent, buildMessageTextContent } from './messageContent';
|
||||
import { buildApiMessage } from './messages';
|
||||
|
||||
@ -3,17 +3,17 @@ import { Api as GramJs } from '../../../lib/gramjs';
|
||||
import { base64UrlToBuffer, base64UrlToString } from '../../../util/encoding/base64';
|
||||
|
||||
export function buildInputPasskeyCredential(
|
||||
credentialJson: PublicKeyCredentialJSON,
|
||||
credentialJson: RegistrationResponseJSON | AuthenticationResponseJSON,
|
||||
): GramJs.TypeInputPasskeyCredential {
|
||||
let response: GramJs.TypeInputPasskeyResponse;
|
||||
const clientData = base64UrlToString(credentialJson.response.clientDataJSON);
|
||||
if (credentialJson.response.attestationObject) {
|
||||
if ('attestationObject' in credentialJson.response) {
|
||||
response = new GramJs.InputPasskeyResponseRegister({
|
||||
clientData: new GramJs.DataJSON({ data: clientData }),
|
||||
attestationData: base64UrlToBuffer(credentialJson.response.attestationObject),
|
||||
});
|
||||
} else {
|
||||
const userHandle = base64UrlToString(credentialJson.response.userHandle);
|
||||
const userHandle = base64UrlToString(credentialJson.response.userHandle!);
|
||||
|
||||
response = new GramJs.InputPasskeyResponseLogin({
|
||||
clientData: new GramJs.DataJSON({ data: clientData }),
|
||||
|
||||
@ -171,7 +171,7 @@ export function restartAuthWithQr() {
|
||||
authController.reject(new Error('RESTART_AUTH_WITH_QR'));
|
||||
}
|
||||
|
||||
export function restartAuthWithPasskey(credentialJson: PublicKeyCredentialJSON) {
|
||||
export function restartAuthWithPasskey(credentialJson: AuthenticationResponseJSON) {
|
||||
if (!authController.reject) {
|
||||
return;
|
||||
}
|
||||
|
||||
@ -1702,7 +1702,7 @@ export async function searchMessagesGlobal({
|
||||
const messages = result.messages.map(buildApiMessage).filter(Boolean);
|
||||
const topics = result.topics.map(buildApiTopicWithState).filter(Boolean);
|
||||
|
||||
let totalCount = messages.length;
|
||||
let totalCount;
|
||||
if (result instanceof GramJs.messages.MessagesSlice || result instanceof GramJs.messages.ChannelMessages) {
|
||||
totalCount = result.count;
|
||||
} else {
|
||||
@ -1761,7 +1761,7 @@ export async function searchPublicPosts({
|
||||
const messages = result.messages.map(buildApiMessage).filter(Boolean);
|
||||
const topics = result.topics.map(buildApiTopicWithState).filter(Boolean);
|
||||
|
||||
let totalCount = messages.length;
|
||||
let totalCount;
|
||||
if (result instanceof GramJs.messages.MessagesSlice || result instanceof GramJs.messages.ChannelMessages) {
|
||||
totalCount = result.count;
|
||||
} else {
|
||||
|
||||
@ -800,7 +800,7 @@ export async function initPasskeyRegistration() {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
export async function registerPasskey(credentialJson: PublicKeyCredentialJSON) {
|
||||
export async function registerPasskey(credentialJson: RegistrationResponseJSON) {
|
||||
const result = await invokeRequest(new GramJs.account.RegisterPasskey({
|
||||
credential: buildInputPasskeyCredential(credentialJson),
|
||||
}));
|
||||
|
||||
@ -1,6 +1,5 @@
|
||||
import type { ApiGroupCall, ApiPhoneCallDiscardReason } from './calls';
|
||||
import type { ApiBotApp, ApiFormattedText, ApiPhoto } from './messages';
|
||||
import type { ApiTodoItem } from './messages';
|
||||
import type { ApiBotApp, ApiFormattedText, ApiPhoto, ApiTodoItem } from './messages';
|
||||
import type { ApiStarGiftRegular, ApiStarGiftUnique, ApiTypeCurrencyAmount } from './stars';
|
||||
|
||||
interface ActionMediaType {
|
||||
|
||||
@ -127,7 +127,7 @@ const AuthCode = ({
|
||||
}
|
||||
|
||||
if (STRICTERDOM_ENABLED) {
|
||||
setTimeout(() => {
|
||||
window.setTimeout(() => {
|
||||
enableStrict();
|
||||
}, QR_CODE_MUTATION_DURATION);
|
||||
}
|
||||
|
||||
@ -52,7 +52,7 @@ const MicrophoneButton: FC<OwnProps & StateProps> = ({
|
||||
} = getActions();
|
||||
|
||||
const lang = useOldLang();
|
||||
const muteMouseDownState = useRef('up');
|
||||
const muteMouseDownStateRef = useRef('up');
|
||||
|
||||
const [isRequestingToSpeak, setIsRequestingToSpeak] = useState(false);
|
||||
const isConnecting = connectionState !== 'connected';
|
||||
@ -113,11 +113,11 @@ const MicrophoneButton: FC<OwnProps & StateProps> = ({
|
||||
}, REQUEST_TO_SPEAK_THROTTLE);
|
||||
return;
|
||||
}
|
||||
muteMouseDownState.current = 'down';
|
||||
muteMouseDownStateRef.current = 'down';
|
||||
if (noAudioStream) {
|
||||
setTimeout(() => {
|
||||
if (muteMouseDownState.current === 'down') {
|
||||
muteMouseDownState.current = 'hold';
|
||||
if (muteMouseDownStateRef.current === 'down') {
|
||||
muteMouseDownStateRef.current = 'hold';
|
||||
toggleMute();
|
||||
}
|
||||
}, HOLD_TO_SPEAK_TIME);
|
||||
@ -129,7 +129,7 @@ const MicrophoneButton: FC<OwnProps & StateProps> = ({
|
||||
return;
|
||||
}
|
||||
toggleMute();
|
||||
muteMouseDownState.current = 'up';
|
||||
muteMouseDownStateRef.current = 'up';
|
||||
}, [shouldRaiseHand, toggleMute]);
|
||||
|
||||
return (
|
||||
|
||||
@ -218,9 +218,13 @@ const PhoneCall = ({
|
||||
|
||||
useEffect(() => {
|
||||
if (phoneCall?.state === 'discarded') {
|
||||
setTimeout(hangUp, 250);
|
||||
const timeout = setTimeout(hangUp, 250);
|
||||
return () => {
|
||||
clearTimeout(timeout);
|
||||
};
|
||||
}
|
||||
}, [hangUp, phoneCall?.reason, phoneCall?.state]);
|
||||
return undefined;
|
||||
}, [phoneCall?.reason, phoneCall?.state]);
|
||||
|
||||
return (
|
||||
<Modal
|
||||
|
||||
@ -58,11 +58,9 @@ function AnimatedIconWithPreview(props: OwnProps) {
|
||||
style={buildStyle(size !== undefined && `width: ${size}px; height: ${size}px;`)}
|
||||
>
|
||||
{thumbDataUri && !isAnimationReady && (
|
||||
// eslint-disable-next-line jsx-a11y/alt-text
|
||||
<img src={thumbDataUri} className={buildClassName(styles.preview, thumbClassNames)} draggable={false} />
|
||||
)}
|
||||
{previewUrl && !isAnimationReady && (
|
||||
// eslint-disable-next-line jsx-a11y/alt-text
|
||||
<img
|
||||
src={previewUrl}
|
||||
className={buildClassName(styles.preview, previewClassNames)}
|
||||
|
||||
@ -97,7 +97,7 @@ const AnimatedSticker = ({
|
||||
|
||||
const [animation, setAnimation] = useState<RLottieInstance>();
|
||||
const animationRef = useRef<RLottieInstance>();
|
||||
const isFirstRender = useRef(true);
|
||||
const isFirstRenderRef = useRef(true);
|
||||
|
||||
const shouldUseColorFilter = !sharedCanvas && color;
|
||||
const colorFilter = useColorFilter(shouldUseColorFilter ? color : undefined);
|
||||
@ -106,7 +106,7 @@ const AnimatedSticker = ({
|
||||
const playRef = useStateRef(play);
|
||||
const playSegmentRef = useStateRef(playSegment);
|
||||
|
||||
const rgbColor = useRef<[number, number, number] | undefined>();
|
||||
const rgbColorRef = useRef<[number, number, number] | undefined>();
|
||||
|
||||
const shouldForceOnHeavyAnimation = forceAlways || forceOnHeavyAnimation;
|
||||
// Delay initialization until heavy animation ends
|
||||
@ -121,9 +121,9 @@ const AnimatedSticker = ({
|
||||
useSyncEffect(() => {
|
||||
if (color && !shouldUseColorFilter) {
|
||||
const { r, g, b } = hex2rgbaObj(color);
|
||||
rgbColor.current = [r, g, b];
|
||||
rgbColorRef.current = [r, g, b];
|
||||
} else {
|
||||
rgbColor.current = undefined;
|
||||
rgbColorRef.current = undefined;
|
||||
}
|
||||
}, [color, shouldUseColorFilter]);
|
||||
|
||||
@ -160,7 +160,7 @@ const AnimatedSticker = ({
|
||||
coords: sharedCanvasCoords,
|
||||
},
|
||||
viewId,
|
||||
rgbColor.current,
|
||||
rgbColorRef.current,
|
||||
onLoad,
|
||||
onEnded,
|
||||
onLoop,
|
||||
@ -192,7 +192,7 @@ const AnimatedSticker = ({
|
||||
useSharedIntersectionObserver(sharedCanvas, throttledInit);
|
||||
|
||||
useEffect(() => {
|
||||
animation?.setColor(rgbColor.current);
|
||||
animation?.setColor(rgbColorRef.current);
|
||||
}, [color, animation]);
|
||||
|
||||
useEffect(() => {
|
||||
@ -261,8 +261,8 @@ const AnimatedSticker = ({
|
||||
|
||||
useEffect(() => {
|
||||
if (animation) {
|
||||
if (isFirstRender.current) {
|
||||
isFirstRender.current = false;
|
||||
if (isFirstRenderRef.current) {
|
||||
isFirstRenderRef.current = false;
|
||||
} else if (tgsUrl) {
|
||||
animation.changeData(tgsUrl);
|
||||
playAnimation();
|
||||
|
||||
@ -133,7 +133,7 @@ const Audio = ({
|
||||
const media = (voice || video || audio)!;
|
||||
const mediaSource = (voice || video);
|
||||
const isVoice = Boolean(voice || video);
|
||||
const isSeeking = useRef<boolean>(false);
|
||||
const isSeekingRef = useRef<boolean>(false);
|
||||
const seekerRef = useRef<HTMLDivElement>();
|
||||
const oldLang = useOldLang();
|
||||
const lang = useLang();
|
||||
@ -266,7 +266,7 @@ const Audio = ({
|
||||
});
|
||||
|
||||
const handleSeek = useLastCallback((e: MouseEvent | TouchEvent) => {
|
||||
if (isSeeking.current && seekerRef.current) {
|
||||
if (isSeekingRef.current && seekerRef.current) {
|
||||
const { width, left } = seekerRef.current.getBoundingClientRect();
|
||||
const clientX = e instanceof MouseEvent ? e.clientX : e.targetTouches[0].clientX;
|
||||
e.stopPropagation(); // Prevent Slide-to-Reply activation
|
||||
@ -277,12 +277,12 @@ const Audio = ({
|
||||
|
||||
const handleStartSeek = useLastCallback((e: MouseEvent | TouchEvent) => {
|
||||
if (e instanceof MouseEvent && e.button === 2) return;
|
||||
isSeeking.current = true;
|
||||
isSeekingRef.current = true;
|
||||
handleSeek(e);
|
||||
});
|
||||
|
||||
const handleStopSeek = useLastCallback(() => {
|
||||
isSeeking.current = false;
|
||||
isSeekingRef.current = false;
|
||||
});
|
||||
|
||||
const handleDateClick = useLastCallback(() => {
|
||||
|
||||
@ -92,6 +92,7 @@ const CalendarModal: FC<OwnProps & StateProps> = ({
|
||||
|
||||
const oldLang = useOldLang();
|
||||
const lang = useLang();
|
||||
// eslint-disable-next-line @eslint-react/purity
|
||||
const now = new Date();
|
||||
|
||||
const {
|
||||
|
||||
@ -499,7 +499,7 @@ const Composer = ({
|
||||
const [getHtml, setHtml] = useSignal('');
|
||||
const [isMounted, setIsMounted] = useState(false);
|
||||
const getSelectionRange = useGetSelectionRange(editableInputCssSelector);
|
||||
const lastMessageSendTimeSeconds = useRef<number>();
|
||||
const lastMessageSendTimeSecondsRef = useRef<number>();
|
||||
const prevDropAreaState = usePreviousDeprecated(dropAreaState);
|
||||
const { width: windowWidth } = windowSize.get();
|
||||
const forceUpdate = useForceUpdate();
|
||||
@ -526,7 +526,7 @@ const Composer = ({
|
||||
|
||||
useEffect(processMessageInputForCustomEmoji, [getHtml]);
|
||||
|
||||
const customEmojiNotificationNumber = useRef(0);
|
||||
const customEmojiNotificationNumberRef = useRef(0);
|
||||
|
||||
const [requestCalendar, calendar] = useSchedule(
|
||||
isInMessageList && canScheduleUntilOnline,
|
||||
@ -544,7 +544,7 @@ const Composer = ({
|
||||
}, [isInMessageList, storyId]);
|
||||
|
||||
useEffect(() => {
|
||||
lastMessageSendTimeSeconds.current = undefined;
|
||||
lastMessageSendTimeSecondsRef.current = undefined;
|
||||
}, [chatId]);
|
||||
|
||||
useEffect(() => {
|
||||
@ -708,7 +708,7 @@ const Composer = ({
|
||||
shouldSendInHighQuality: attachmentSettings.shouldSendInHighQuality,
|
||||
});
|
||||
|
||||
const mediaEditRequestRef = useRef(Date.now());
|
||||
const mediaEditRequestRef = useRef<number>();
|
||||
useEffect(() => {
|
||||
if (!shouldOpenMessageMediaEditor) return;
|
||||
const targetMessage = editingMessage || replyToMessage;
|
||||
@ -945,7 +945,7 @@ const Composer = ({
|
||||
&& !isForwarding && !isReplying && !draft?.suggestedPostInfo;
|
||||
|
||||
const showCustomEmojiPremiumNotification = useLastCallback(() => {
|
||||
const notificationNumber = customEmojiNotificationNumber.current;
|
||||
const notificationNumber = customEmojiNotificationNumberRef.current;
|
||||
if (!notificationNumber) {
|
||||
showNotification({
|
||||
message: oldLang('UnlockPremiumEmojiHint'),
|
||||
@ -965,7 +965,7 @@ const Composer = ({
|
||||
actionText: oldLang('Open'),
|
||||
});
|
||||
}
|
||||
customEmojiNotificationNumber.current = Number(!notificationNumber);
|
||||
customEmojiNotificationNumberRef.current = Number(!notificationNumber);
|
||||
});
|
||||
|
||||
const mainButtonState = useDerivedState(() => {
|
||||
@ -1073,12 +1073,12 @@ const Composer = ({
|
||||
const messageInput = document.querySelector<HTMLDivElement>(editableInputCssSelector);
|
||||
|
||||
const nowSeconds = getServerTime();
|
||||
const secondsSinceLastMessage = lastMessageSendTimeSeconds.current
|
||||
&& Math.floor(nowSeconds - lastMessageSendTimeSeconds.current);
|
||||
const secondsSinceLastMessage = lastMessageSendTimeSecondsRef.current
|
||||
&& Math.floor(nowSeconds - lastMessageSendTimeSecondsRef.current);
|
||||
const nextSendDateNotReached = slowMode.nextSendDate && slowMode.nextSendDate > nowSeconds;
|
||||
|
||||
if (
|
||||
(secondsSinceLastMessage && secondsSinceLastMessage < slowMode.seconds)
|
||||
(secondsSinceLastMessage !== undefined && secondsSinceLastMessage < slowMode.seconds)
|
||||
|| nextSendDateNotReached
|
||||
) {
|
||||
const secondsRemaining = nextSendDateNotReached
|
||||
@ -1168,7 +1168,7 @@ const Composer = ({
|
||||
});
|
||||
}
|
||||
|
||||
lastMessageSendTimeSeconds.current = getServerTime();
|
||||
lastMessageSendTimeSecondsRef.current = getServerTime();
|
||||
|
||||
clearDraft({ chatId, threadId, isLocalOnly: true });
|
||||
|
||||
@ -1277,7 +1277,7 @@ const Composer = ({
|
||||
});
|
||||
}
|
||||
|
||||
lastMessageSendTimeSeconds.current = getServerTime();
|
||||
lastMessageSendTimeSecondsRef.current = getServerTime();
|
||||
clearDraft({
|
||||
chatId, threadId, isLocalOnly: true, shouldKeepReply: isForwarding,
|
||||
});
|
||||
@ -1743,15 +1743,21 @@ const Composer = ({
|
||||
}, [isRightColumnShown, closeSymbolMenu, isMobile]);
|
||||
|
||||
useEffect(() => {
|
||||
if (!isReady) return;
|
||||
if (!isReady) return undefined;
|
||||
|
||||
let timeout: number | undefined;
|
||||
if (isSelectModeActive) {
|
||||
disableHover();
|
||||
} else {
|
||||
setTimeout(() => {
|
||||
timeout = window.setTimeout(() => {
|
||||
enableHover();
|
||||
}, SELECT_MODE_TRANSITION_MS);
|
||||
}
|
||||
return () => {
|
||||
if (timeout) {
|
||||
clearTimeout(timeout);
|
||||
}
|
||||
};
|
||||
}, [isSelectModeActive, enableHover, disableHover, isReady]);
|
||||
|
||||
const hasText = useDerivedState(() => Boolean(getHtml()), [getHtml]);
|
||||
@ -1887,13 +1893,6 @@ const Composer = ({
|
||||
}
|
||||
});
|
||||
|
||||
const scheduledDefaultDate = new Date();
|
||||
scheduledDefaultDate.setSeconds(0);
|
||||
scheduledDefaultDate.setMilliseconds(0);
|
||||
|
||||
const scheduledMaxDate = new Date();
|
||||
scheduledMaxDate.setFullYear(scheduledMaxDate.getFullYear() + 1);
|
||||
|
||||
let sendButtonAriaLabel = 'SendMessage';
|
||||
switch (mainButtonState) {
|
||||
case MainButtonState.Forward:
|
||||
|
||||
@ -1,5 +1,4 @@
|
||||
import type { FC } from '../../../lib/teact/teact';
|
||||
import { memo, useCallback, useState } from '../../../lib/teact/teact';
|
||||
import { memo, useState } from '../../../lib/teact/teact';
|
||||
|
||||
import { ApiMessageEntityTypes } from '../../../api/types';
|
||||
|
||||
@ -7,6 +6,7 @@ import buildClassName from '../../../util/buildClassName';
|
||||
import { getPrettyCodeLanguageName } from '../../../util/prettyCodeLanguageNames';
|
||||
|
||||
import useAsync from '../../../hooks/useAsync';
|
||||
import useLastCallback from '../../../hooks/useLastCallback';
|
||||
|
||||
import PeerColorWrapper from '../PeerColorWrapper';
|
||||
import CodeOverlay from './CodeOverlay';
|
||||
@ -19,8 +19,8 @@ export type OwnProps = {
|
||||
noCopy?: boolean;
|
||||
};
|
||||
|
||||
const CodeBlock: FC<OwnProps> = ({ text, language, noCopy }) => {
|
||||
const [isWordWrap, setWordWrap] = useState(true);
|
||||
const CodeBlock = ({ text, language, noCopy }: OwnProps) => {
|
||||
const [isWordWrap, setIsWordWrap] = useState(true);
|
||||
|
||||
const { result: highlighted } = useAsync(() => {
|
||||
if (!language) return Promise.resolve(undefined);
|
||||
@ -28,9 +28,9 @@ const CodeBlock: FC<OwnProps> = ({ text, language, noCopy }) => {
|
||||
.then((lib) => lib.default(text, language));
|
||||
}, [language, text]);
|
||||
|
||||
const handleWordWrapToggle = useCallback((wrap) => {
|
||||
setWordWrap(wrap);
|
||||
}, []);
|
||||
const handleWordWrapToggle = useLastCallback((wrap) => {
|
||||
setIsWordWrap(wrap);
|
||||
});
|
||||
|
||||
const blockClass = buildClassName(
|
||||
'code-block',
|
||||
|
||||
@ -1,5 +1,4 @@
|
||||
import type { ElementRef } from '@teact';
|
||||
import { memo, type TeactNode, useRef } from '@teact';
|
||||
import { type ElementRef, memo, type TeactNode, useRef } from '@teact';
|
||||
|
||||
import buildClassName from '../../../util/buildClassName';
|
||||
import buildStyle from '../../../util/buildStyle';
|
||||
|
||||
@ -5,8 +5,7 @@ import type {
|
||||
ApiEmojiStatusCollectible, ApiEmojiStatusType, ApiSavedStarGift, ApiStarGift,
|
||||
} from '../../../api/types';
|
||||
|
||||
import { DEFAULT_STATUS_ICON_ID, TME_LINK_PREFIX } from '../../../config';
|
||||
import { STARS_CURRENCY_CODE } from '../../../config';
|
||||
import { DEFAULT_STATUS_ICON_ID, STARS_CURRENCY_CODE, TME_LINK_PREFIX } from '../../../config';
|
||||
import { copyTextToClipboard } from '../../../util/clipboard';
|
||||
import { formatDateAtTime } from '../../../util/dates/oldDateFormat';
|
||||
import { getServerTime } from '../../../util/serverTime';
|
||||
|
||||
@ -38,8 +38,8 @@ export default function useAnimatedEmoji(
|
||||
const size = preferredSize || SIZE;
|
||||
const style = buildStyle(`width: ${size}px`, `height: ${size}px`, emoji && !IS_TAURI && 'cursor: pointer');
|
||||
|
||||
const interactions = useRef<number[] | undefined>(undefined);
|
||||
const startedInteractions = useRef<number | undefined>(undefined);
|
||||
const interactionsRef = useRef<number[] | undefined>(undefined);
|
||||
const startedInteractionsRef = useRef<number | undefined>(undefined);
|
||||
const sendInteractionBunch = useLastCallback(() => {
|
||||
const container = ref.current;
|
||||
|
||||
@ -49,10 +49,10 @@ export default function useAnimatedEmoji(
|
||||
chatId: chatId!,
|
||||
messageId: messageId!,
|
||||
emoji: emoji!,
|
||||
interactions: interactions.current!,
|
||||
interactions: interactionsRef.current!,
|
||||
});
|
||||
startedInteractions.current = undefined;
|
||||
interactions.current = undefined;
|
||||
startedInteractionsRef.current = undefined;
|
||||
interactionsRef.current = undefined;
|
||||
});
|
||||
|
||||
const play = useLastCallback(() => {
|
||||
@ -90,14 +90,14 @@ export default function useAnimatedEmoji(
|
||||
isReversed: !isOwn,
|
||||
});
|
||||
|
||||
if (!interactions.current) {
|
||||
interactions.current = [];
|
||||
startedInteractions.current = performance.now();
|
||||
if (!interactionsRef.current) {
|
||||
interactionsRef.current = [];
|
||||
startedInteractionsRef.current = performance.now();
|
||||
setTimeout(sendInteractionBunch, INTERACTION_BUNCH_TIME);
|
||||
}
|
||||
|
||||
interactions.current.push(startedInteractions.current
|
||||
? (performance.now() - startedInteractions.current) / MS_DIVIDER
|
||||
interactionsRef.current.push(startedInteractionsRef.current
|
||||
? (performance.now() - startedInteractionsRef.current) / MS_DIVIDER
|
||||
: TIME_DEFAULT);
|
||||
});
|
||||
|
||||
|
||||
@ -80,11 +80,16 @@ export function useStickerPickerObservers(
|
||||
freezeForSet();
|
||||
freezeForShowingItems();
|
||||
} else {
|
||||
setTimeout(() => {
|
||||
const timeout = window.setTimeout(() => {
|
||||
unfreezeForShowingItems();
|
||||
unfreezeForSet();
|
||||
}, SLIDE_TRANSITION_DURATION);
|
||||
|
||||
return () => {
|
||||
clearTimeout(timeout);
|
||||
};
|
||||
}
|
||||
return undefined;
|
||||
}, [freezeForSet, freezeForShowingItems, isHidden, unfreezeForSet, unfreezeForShowingItems]);
|
||||
|
||||
const selectStickerSet = useLastCallback((index: number) => {
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
import { memo, useCallback } from '@teact';
|
||||
import { memo } from '@teact';
|
||||
|
||||
import { type ApiChatFolder, ApiMessageEntityTypes } from '../../../api/types';
|
||||
|
||||
@ -33,7 +33,7 @@ const ChatTags = ({
|
||||
const visibleFolderIds = orderedFolderIds.slice(0, MAX_VISIBLE_TAGS);
|
||||
const remainingCount = orderedFolderIds.length - visibleFolderIds.length;
|
||||
|
||||
const getFolderTitle = useCallback((folder: ApiChatFolder) => {
|
||||
function getFolderTitle(folder: ApiChatFolder) {
|
||||
let text = folder.title.text;
|
||||
let entities = folder.title.entities;
|
||||
|
||||
@ -56,7 +56,7 @@ const ChatTags = ({
|
||||
noCustomEmojiPlayback: folder.noTitleAnimations,
|
||||
emojiSize: CUSTOM_EMOJI_SIZE,
|
||||
});
|
||||
}, [isFoldersSidebarShown]);
|
||||
};
|
||||
|
||||
return (
|
||||
<div className={styles.wrapper}>
|
||||
|
||||
@ -86,18 +86,18 @@ const LeftMain: FC<OwnProps> = ({
|
||||
transitionClassNames: updateButtonClassNames,
|
||||
} = useShowTransitionDeprecated(isAppUpdateAvailable || Boolean(tauriUpdate));
|
||||
|
||||
const isMouseInside = useRef(false);
|
||||
const isMouseInsideRef = useRef(false);
|
||||
|
||||
const handleMouseEnter = useLastCallback(() => {
|
||||
if (content !== LeftColumnContent.ChatList) {
|
||||
return;
|
||||
}
|
||||
isMouseInside.current = true;
|
||||
isMouseInsideRef.current = true;
|
||||
setIsNewChatButtonShown(true);
|
||||
});
|
||||
|
||||
const handleMouseLeave = useLastCallback(() => {
|
||||
isMouseInside.current = false;
|
||||
isMouseInsideRef.current = false;
|
||||
|
||||
if (closeTimeout) {
|
||||
clearTimeout(closeTimeout);
|
||||
@ -105,7 +105,7 @@ const LeftMain: FC<OwnProps> = ({
|
||||
}
|
||||
|
||||
closeTimeout = window.setTimeout(() => {
|
||||
if (!isMouseInside.current) {
|
||||
if (!isMouseInsideRef.current) {
|
||||
setIsNewChatButtonShown(false);
|
||||
}
|
||||
}, BUTTON_CLOSE_DELAY_MS);
|
||||
@ -148,7 +148,7 @@ const LeftMain: FC<OwnProps> = ({
|
||||
autoCloseTimeout = window.setTimeout(() => {
|
||||
setIsNewChatButtonShown(false);
|
||||
}, BUTTON_CLOSE_DELAY_MS);
|
||||
} else if (isMouseInside.current || IS_TOUCH_ENV) {
|
||||
} else if (isMouseInsideRef.current || IS_TOUCH_ENV) {
|
||||
setIsNewChatButtonShown(true);
|
||||
}
|
||||
|
||||
|
||||
@ -25,8 +25,7 @@ import {
|
||||
import { selectTabState, selectTheme, selectUser } from '../../../global/selectors';
|
||||
import { selectPremiumLimit } from '../../../global/selectors/limits';
|
||||
import { selectSharedSettings } from '../../../global/selectors/sharedState';
|
||||
import { IS_MULTIACCOUNT_SUPPORTED } from '../../../util/browser/globalEnvironment';
|
||||
import { IS_TAURI } from '../../../util/browser/globalEnvironment';
|
||||
import { IS_MULTIACCOUNT_SUPPORTED, IS_TAURI } from '../../../util/browser/globalEnvironment';
|
||||
import { getPromptInstall } from '../../../util/installPrompt';
|
||||
import { switchPermanentWebVersion } from '../../../util/permanentWebVersion';
|
||||
import { getSystemTheme } from '../../../util/systemTheme';
|
||||
|
||||
@ -36,9 +36,10 @@ const StatusPickerMenu = ({
|
||||
}: OwnProps & StateProps) => {
|
||||
const { loadFeaturedEmojiStickers } = getActions();
|
||||
|
||||
const transformOriginX = useRef<number>(0);
|
||||
const transformOriginXRef = useRef<number>(0);
|
||||
useEffect(() => {
|
||||
transformOriginX.current = statusButtonRef.current!.getBoundingClientRect().right;
|
||||
if (!statusButtonRef.current) return;
|
||||
transformOriginXRef.current = statusButtonRef.current.getBoundingClientRect().right;
|
||||
}, [isOpen, statusButtonRef]);
|
||||
|
||||
useEffect(() => {
|
||||
@ -60,7 +61,7 @@ const StatusPickerMenu = ({
|
||||
positionX="left"
|
||||
bubbleClassName={styles.menuContent}
|
||||
onClose={onClose}
|
||||
transformOriginX={transformOriginX.current}
|
||||
transformOriginX={transformOriginXRef.current}
|
||||
>
|
||||
<CustomEmojiPicker
|
||||
idPrefix="status-emoji-set-"
|
||||
|
||||
@ -1,6 +1,5 @@
|
||||
import { memo, useCallback, useMemo } from '../../../lib/teact/teact';
|
||||
import { getActions, withGlobal } from '../../../global';
|
||||
import { getGlobal } from '../../../global';
|
||||
import { getActions, getGlobal, withGlobal } from '../../../global';
|
||||
|
||||
import type { ApiMessage, ApiSearchPostsFlood } from '../../../api/types';
|
||||
import type { AnimationLevel } from '../../../types';
|
||||
|
||||
@ -260,7 +260,6 @@ const SettingsHeader: FC<OwnProps> = ({
|
||||
default:
|
||||
return (
|
||||
<div className="settings-main-header">
|
||||
{/* eslint-disable-next-line jsx-a11y/no-noninteractive-element-interactions */}
|
||||
<h3 onClick={handleMultiClick}>
|
||||
{oldLang('SETTINGS')}
|
||||
</h3>
|
||||
|
||||
@ -10,7 +10,6 @@ import type { ApiSticker } from '../../../../api/types';
|
||||
import { selectAnimatedEmoji, selectTabState } from '../../../../global/selectors';
|
||||
import { IS_TOUCH_ENV } from '../../../../util/browser/windowEnvironment';
|
||||
|
||||
import useAppLayout from '../../../../hooks/useAppLayout';
|
||||
import useHistoryBack from '../../../../hooks/useHistoryBack';
|
||||
import useOldLang from '../../../../hooks/useOldLang';
|
||||
|
||||
@ -47,18 +46,14 @@ const SettingsTwoFaEmailCode: FC<OwnProps & StateProps> = ({
|
||||
recoveryEmail,
|
||||
}) => {
|
||||
const inputRef = useRef<HTMLInputElement>();
|
||||
const { isMobile } = useAppLayout();
|
||||
const focusDelayTimeoutMs = isMobile ? 550 : 400;
|
||||
|
||||
const [value, setValue] = useState<string>('');
|
||||
|
||||
useEffect(() => {
|
||||
if (!IS_TOUCH_ENV) {
|
||||
setTimeout(() => {
|
||||
inputRef.current!.focus();
|
||||
}, focusDelayTimeoutMs);
|
||||
if (!IS_TOUCH_ENV && isActive) {
|
||||
inputRef.current!.focus();
|
||||
}
|
||||
}, [focusDelayTimeoutMs]);
|
||||
}, [isActive]);
|
||||
|
||||
const lang = useOldLang();
|
||||
|
||||
|
||||
@ -11,7 +11,6 @@ import { selectAnimatedEmoji } from '../../../../global/selectors';
|
||||
import { IS_TOUCH_ENV } from '../../../../util/browser/windowEnvironment';
|
||||
import renderText from '../../../common/helpers/renderText';
|
||||
|
||||
import useAppLayout from '../../../../hooks/useAppLayout';
|
||||
import useFlag from '../../../../hooks/useFlag';
|
||||
import useHistoryBack from '../../../../hooks/useHistoryBack';
|
||||
import useOldLang from '../../../../hooks/useOldLang';
|
||||
@ -53,19 +52,15 @@ const SettingsTwoFaSkippableForm: FC<OwnProps & StateProps> = ({
|
||||
onReset,
|
||||
}) => {
|
||||
const inputRef = useRef<HTMLInputElement>();
|
||||
const { isMobile } = useAppLayout();
|
||||
|
||||
const focusDelayTimeoutMs = isMobile ? 550 : 400;
|
||||
const [value, setValue] = useState<string>('');
|
||||
const [isConfirmShown, markIsConfirmShown, unmarkIsConfirmShown] = useFlag(false);
|
||||
|
||||
useEffect(() => {
|
||||
if (!IS_TOUCH_ENV) {
|
||||
setTimeout(() => {
|
||||
inputRef.current!.focus();
|
||||
}, focusDelayTimeoutMs);
|
||||
if (!IS_TOUCH_ENV && isActive) {
|
||||
inputRef.current!.focus();
|
||||
}
|
||||
}, [focusDelayTimeoutMs]);
|
||||
}, [isActive]);
|
||||
|
||||
const handleInputChange = useCallback((e: React.ChangeEvent<HTMLInputElement>) => {
|
||||
if (error && clearError) {
|
||||
|
||||
@ -81,11 +81,12 @@ const DownloadManager = ({
|
||||
const url = new URL(result, window.document.baseURI);
|
||||
url.searchParams.set('filename', encodeURIComponent(filename));
|
||||
const downloadWindow = window.open(url.toString());
|
||||
// eslint-disable-next-line @eslint-react/web-api/no-leaked-event-listener
|
||||
downloadWindow?.addEventListener('beforeunload', () => {
|
||||
showNotification({
|
||||
message: 'Download started. Please, do not close the app before it is finished.',
|
||||
});
|
||||
});
|
||||
}, { once: true });
|
||||
} else if (result) {
|
||||
download(result, filename);
|
||||
}
|
||||
|
||||
@ -81,7 +81,7 @@ const GameModal: FC<OwnProps & StateProps> = ({ openedGame, gameTitle, canPost }
|
||||
onLoad={handleLoad}
|
||||
src={url}
|
||||
title={lang('AttachGame')}
|
||||
sandbox="allow-scripts allow-same-origin allow-orientation-lock"
|
||||
sandbox="allow-scripts allow-orientation-lock"
|
||||
allow="fullscreen"
|
||||
/>
|
||||
)}
|
||||
|
||||
@ -57,6 +57,7 @@ const LockScreen: FC<OwnProps & StateProps> = ({
|
||||
const [isSignOutDialogOpen, openSignOutConfirmation, closeSignOutConfirmation] = useFlag(false);
|
||||
const { shouldRender } = useShowTransitionDeprecated(isLocked);
|
||||
|
||||
// eslint-disable-next-line @eslint-react/purity
|
||||
useTimeout(resetInvalidUnlockAttempts, timeoutUntil ? timeoutUntil - Date.now() : undefined);
|
||||
|
||||
const handleClearError = useCallback(() => {
|
||||
|
||||
@ -271,7 +271,7 @@ const Main = ({
|
||||
|
||||
if (DEBUG && !DEBUG_isLogged) {
|
||||
DEBUG_isLogged = true;
|
||||
// eslint-disable-next-line no-console
|
||||
// eslint-disable-next-line no-console, @eslint-react/purity
|
||||
console.log('>>> RENDER MAIN');
|
||||
}
|
||||
|
||||
|
||||
@ -28,8 +28,6 @@ import TextArea from '../ui/TextArea';
|
||||
|
||||
import './NewContactModal.scss';
|
||||
|
||||
const ANIMATION_DURATION = 200;
|
||||
|
||||
export type OwnProps = {
|
||||
isOpen: boolean;
|
||||
userId?: string;
|
||||
@ -86,9 +84,7 @@ const NewContactModal: FC<OwnProps & StateProps> = ({
|
||||
|
||||
useEffect(() => {
|
||||
if (!IS_TOUCH_ENV && isShown) {
|
||||
setTimeout(() => {
|
||||
inputRef.current?.focus();
|
||||
}, ANIMATION_DURATION);
|
||||
inputRef.current?.focus();
|
||||
}
|
||||
}, [isShown]);
|
||||
|
||||
|
||||
@ -159,9 +159,9 @@ const GiveawayModal: FC<OwnProps & StateProps> = ({
|
||||
}
|
||||
|
||||
const [customExpireDate, setCustomExpireDate] = useState<number>(() => Date.now() + DEFAULT_CUSTOM_EXPIRE_DATE);
|
||||
const [isHeaderHidden, setHeaderHidden] = useState(true);
|
||||
const [isHeaderHidden, setIsHeaderHidden] = useState(true);
|
||||
const [selectedRandomUserCount, setSelectedRandomUserCount] = useState<number>(DEFAULT_BOOST_COUNT);
|
||||
const [selectedGiveawayOption, setGiveawayOption] = useState<ApiGiveawayType>(TYPE_OPTIONS[0].value);
|
||||
const [selectedGiveawayOption, setSelectedGiveawayOption] = useState<ApiGiveawayType>(TYPE_OPTIONS[0].value);
|
||||
const [selectedStarOption, setSelectedStarOption] = useState<ApiStarGiveawayOption | undefined>();
|
||||
const [selectedSubscriberOption, setSelectedSubscriberOption] = useState<SubscribersType>('all');
|
||||
const [selectedMonthOption, setSelectedMonthOption] = useState<number | undefined>();
|
||||
@ -390,7 +390,7 @@ const GiveawayModal: FC<OwnProps & StateProps> = ({
|
||||
function handleScroll(e: React.UIEvent<HTMLDivElement>) {
|
||||
const { scrollTop } = e.currentTarget;
|
||||
|
||||
setHeaderHidden(scrollTop <= 150);
|
||||
setIsHeaderHidden(scrollTop <= 150);
|
||||
}
|
||||
|
||||
const handleChangeSubscriberOption = useLastCallback((value) => {
|
||||
@ -398,7 +398,7 @@ const GiveawayModal: FC<OwnProps & StateProps> = ({
|
||||
});
|
||||
|
||||
const handleChangeTypeOption = useLastCallback((value: ApiGiveawayType) => {
|
||||
setGiveawayOption(value);
|
||||
setSelectedGiveawayOption(value);
|
||||
setSelectedUserIds([]);
|
||||
setSelectedRandomUserCount(DEFAULT_BOOST_COUNT);
|
||||
});
|
||||
@ -415,7 +415,7 @@ const GiveawayModal: FC<OwnProps & StateProps> = ({
|
||||
const handleSelectedUserIdsChange = useLastCallback((newSelectedIds: string[]) => {
|
||||
setSelectedUserIds(newSelectedIds);
|
||||
if (!newSelectedIds.length) {
|
||||
setGiveawayOption('premium_giveaway');
|
||||
setSelectedGiveawayOption('premium_giveaway');
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
@ -150,13 +150,13 @@ const PremiumMainModal: FC<OwnProps & StateProps> = ({
|
||||
|
||||
const oldLang = useOldLang();
|
||||
const lang = useLang();
|
||||
const [isHeaderHidden, setHeaderHidden] = useState(true);
|
||||
const [isHeaderHidden, setIsHeaderHidden] = useState(true);
|
||||
const [currentSection, setCurrentSection] = useState<ApiPremiumSection | undefined>(initialSection);
|
||||
const [selectedSubscriptionOption, setSubscriptionOption] = useState<ApiPremiumSubscriptionOption>();
|
||||
const [selectedSubscriptionOption, setSelectedSubscriptionOption] = useState<ApiPremiumSubscriptionOption>();
|
||||
|
||||
useEffect(() => {
|
||||
if (!isOpen) {
|
||||
setHeaderHidden(true);
|
||||
setIsHeaderHidden(true);
|
||||
setCurrentSection(undefined);
|
||||
} else if (initialSection) {
|
||||
setCurrentSection(initialSection);
|
||||
@ -174,7 +174,7 @@ const PremiumMainModal: FC<OwnProps & StateProps> = ({
|
||||
function handleScroll(e: React.UIEvent<HTMLDivElement>) {
|
||||
const { scrollTop } = e.currentTarget;
|
||||
|
||||
setHeaderHidden(scrollTop <= 150);
|
||||
setIsHeaderHidden(scrollTop <= 150);
|
||||
}
|
||||
|
||||
const handleClickWithStartParam = useLastCallback((startParam?: string) => {
|
||||
@ -204,7 +204,7 @@ const PremiumMainModal: FC<OwnProps & StateProps> = ({
|
||||
|
||||
const handleChangeSubscriptionOption = useLastCallback((months: number) => {
|
||||
const foundOption = promo?.options.find((option) => option.months === months);
|
||||
setSubscriptionOption(foundOption);
|
||||
setSelectedSubscriptionOption(foundOption);
|
||||
});
|
||||
|
||||
const showConfetti = useLastCallback(() => {
|
||||
@ -250,7 +250,7 @@ const PremiumMainModal: FC<OwnProps & StateProps> = ({
|
||||
|
||||
useEffect(() => {
|
||||
const [defaultOption] = promo?.options ?? [];
|
||||
setSubscriptionOption(defaultOption);
|
||||
setSelectedSubscriptionOption(defaultOption);
|
||||
}, [promo]);
|
||||
|
||||
const handleOpenStatusSet = useLastCallback(() => {
|
||||
|
||||
@ -195,6 +195,7 @@ const ConfettiContainer = ({ confetti }: StateProps) => {
|
||||
// eslint-disable-next-line react-hooks-static-deps/exhaustive-deps -- Old timeout should be cleared only if new confetti is generated
|
||||
}, [lastConfettiTime, forceUpdate, updateCanvas]);
|
||||
|
||||
// eslint-disable-next-line @eslint-react/purity
|
||||
if (!lastConfettiTime || Date.now() - lastConfettiTime > CONFETTI_FADEOUT_TIMEOUT) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
@ -143,7 +143,7 @@ const MediaViewer = ({
|
||||
const { media, isSingle } = viewableMedia || {};
|
||||
|
||||
/* Animation */
|
||||
const animationKey = useRef<number>();
|
||||
const animationKeyRef = useRef<number>();
|
||||
const senderId = message?.senderId || avatarOwner?.id || message?.chatId;
|
||||
const prevSenderId = usePreviousDeprecated<string | undefined>(senderId);
|
||||
const headerAnimation = withAnimation ? 'slideFade' : 'none';
|
||||
@ -178,8 +178,8 @@ const MediaViewer = ({
|
||||
: getMessageContentIds(chatMessages || {}, collectedMessageIds || [], contentType || 'media');
|
||||
}, [chatMessages, collectedMessageIds, contentType, withDynamicLoading]);
|
||||
|
||||
if (isOpen && (!prevSenderId || prevSenderId !== senderId || animationKey.current === undefined)) {
|
||||
animationKey.current = isSingle ? 0 : (messageId || mediaIndex);
|
||||
if (isOpen && (!prevSenderId || prevSenderId !== senderId || animationKeyRef.current === undefined)) {
|
||||
animationKeyRef.current = isSingle ? 0 : (messageId || mediaIndex);
|
||||
}
|
||||
|
||||
const [getIsPictureInPicture] = PICTURE_IN_PICTURE_SIGNAL;
|
||||
@ -438,7 +438,7 @@ const MediaViewer = ({
|
||||
onClick={handleClose}
|
||||
/>
|
||||
)}
|
||||
<Transition activeKey={animationKey.current!} name={headerAnimation}>
|
||||
<Transition activeKey={animationKeyRef.current!} name={headerAnimation}>
|
||||
<SenderInfo
|
||||
key={media?.id}
|
||||
item={currentItem}
|
||||
|
||||
@ -279,9 +279,6 @@ const MediaViewerSlides: FC<OwnProps> = ({
|
||||
const initialContentRect = initialContentRectRef.current;
|
||||
if (!initialContentRect) return [{ x, y, scale }, true, true];
|
||||
// Get current content boundaries
|
||||
let inBoundsX = true;
|
||||
let inBoundsY = true;
|
||||
|
||||
const centerX = (windowWidth - windowWidth * scale) / 2;
|
||||
const centerY = (windowHeight - windowHeight * scale) / 2;
|
||||
|
||||
@ -289,12 +286,12 @@ const MediaViewerSlides: FC<OwnProps> = ({
|
||||
// based on initial content rect and current scale
|
||||
const minOffsetX = Math.max(-initialContentRect.left * scale, centerX);
|
||||
const maxOffsetX = windowWidth - initialContentRect.right * scale;
|
||||
inBoundsX = isBetween(x, maxOffsetX, minOffsetX);
|
||||
const inBoundsX = isBetween(x, maxOffsetX, minOffsetX);
|
||||
x = clamp(x, maxOffsetX, minOffsetX);
|
||||
|
||||
const minOffsetY = Math.max(-initialContentRect.top * scale + offsetTop, centerY);
|
||||
const maxOffsetY = windowHeight - initialContentRect.bottom * scale;
|
||||
inBoundsY = isBetween(y, maxOffsetY, minOffsetY);
|
||||
const inBoundsY = isBetween(y, maxOffsetY, minOffsetY);
|
||||
y = clamp(y, maxOffsetY, minOffsetY);
|
||||
|
||||
return [{ x, y, scale }, inBoundsX, inBoundsY];
|
||||
|
||||
@ -64,13 +64,13 @@ const SeekLine = ({
|
||||
const [getPreviewOffset, setPreviewOffset] = useSignal(0);
|
||||
const [getPreviewTime, setPreviewTime] = useSignal(0);
|
||||
const isLockedRef = useRef<boolean>(false);
|
||||
const [isPreviewVisible, setPreviewVisible] = useState(false);
|
||||
const [isPreviewVisible, setIsPreviewVisible] = useState(false);
|
||||
const [isSeeking, setIsSeeking] = useState(false);
|
||||
const previewContainerRef = useRef<HTMLDivElement>();
|
||||
const previewRef = useRef<HTMLDivElement>();
|
||||
const progressRef = useRef<HTMLDivElement>();
|
||||
const previewTimeRef = useRef<HTMLDivElement>();
|
||||
const storyboardParser = useRef<StoryboardParser>();
|
||||
const storyboardParserRef = useRef<StoryboardParser>();
|
||||
|
||||
const storyboardHash = storyboardInfo && getDocumentMediaHash(storyboardInfo.storyboardFile, 'full');
|
||||
const storyboardMapHash = storyboardInfo && getDocumentMediaHash(storyboardInfo.storyboardMapFile, 'full');
|
||||
@ -79,13 +79,13 @@ const SeekLine = ({
|
||||
const storyboardMapData = useMedia(storyboardMapHash, !isReady, ApiMediaFormat.Text);
|
||||
|
||||
useEffect(() => {
|
||||
setPreviewVisible(false);
|
||||
setIsPreviewVisible(false);
|
||||
}, [isActive]);
|
||||
|
||||
useEffect(() => {
|
||||
if (!storyboardMapData) return;
|
||||
try {
|
||||
storyboardParser.current = new StoryboardParser(storyboardMapData);
|
||||
storyboardParserRef.current = new StoryboardParser(storyboardMapData);
|
||||
} catch (error) {
|
||||
if (DEBUG) {
|
||||
// eslint-disable-next-line no-console
|
||||
@ -96,8 +96,8 @@ const SeekLine = ({
|
||||
|
||||
const setPreview = useLastCallback((time: number) => {
|
||||
const previewContainer = previewContainerRef.current;
|
||||
if (!storyboardParser.current || !previewContainer) return;
|
||||
const frame = storyboardParser.current.getNearestPreview(time);
|
||||
if (!storyboardParserRef.current || !previewContainer) return;
|
||||
const frame = storyboardParserRef.current.getNearestPreview(time);
|
||||
|
||||
setPreviewTime(Math.floor(frame.time));
|
||||
|
||||
@ -189,7 +189,7 @@ const SeekLine = ({
|
||||
|
||||
const handleSeek = (e: MouseEvent | TouchEvent) => {
|
||||
stopAnimation();
|
||||
setPreviewVisible(true);
|
||||
setIsPreviewVisible(true);
|
||||
([time, offset] = getPreviewProps(e));
|
||||
void setPreview(time);
|
||||
setPreviewOffset(offset);
|
||||
@ -198,7 +198,7 @@ const SeekLine = ({
|
||||
|
||||
const handleStartSeek = () => {
|
||||
stopAnimation();
|
||||
setPreviewVisible(true);
|
||||
setIsPreviewVisible(true);
|
||||
setIsSeeking(true);
|
||||
onSeekStart();
|
||||
};
|
||||
@ -206,7 +206,7 @@ const SeekLine = ({
|
||||
const handleStopSeek = () => {
|
||||
stopAnimation();
|
||||
isLockedRef.current = true;
|
||||
setPreviewVisible(false);
|
||||
setIsPreviewVisible(false);
|
||||
setIsSeeking(false);
|
||||
setSelectedTime(time);
|
||||
onSeek(time);
|
||||
@ -228,14 +228,14 @@ const SeekLine = ({
|
||||
}
|
||||
|
||||
const handleSeekMouseMove = (e: MouseEvent) => {
|
||||
setPreviewVisible(true);
|
||||
setIsPreviewVisible(true);
|
||||
([time, offset] = getPreviewProps(e));
|
||||
setPreviewOffset(offset);
|
||||
void setPreview(time);
|
||||
};
|
||||
|
||||
const handleSeekMouseLeave = () => {
|
||||
setPreviewVisible(false);
|
||||
setIsPreviewVisible(false);
|
||||
};
|
||||
|
||||
seeker.addEventListener('mousemove', handleSeekMouseMove);
|
||||
|
||||
@ -113,11 +113,11 @@ const VideoPlayer: FC<OwnProps> = ({
|
||||
|
||||
const [, toggleControls, lockControls] = useControlsSignal();
|
||||
const [getIsSeeking, setIsSeeking] = useSignal(false);
|
||||
const lastMousePosition = useRef({ x: 0, y: 0 });
|
||||
const lastMousePositionRef = useRef({ x: 0, y: 0 });
|
||||
|
||||
useEffect(() => {
|
||||
const updateMousePosition = (e: MouseEvent | TouchEvent) => {
|
||||
lastMousePosition.current = getPointerPosition(e);
|
||||
lastMousePositionRef.current = getPointerPosition(e);
|
||||
};
|
||||
|
||||
window.addEventListener('mousemove', updateMousePosition);
|
||||
@ -151,7 +151,7 @@ const VideoPlayer: FC<OwnProps> = ({
|
||||
const handleSeekingChange = useLastCallback((isSeeking: boolean) => {
|
||||
setIsSeeking(isSeeking);
|
||||
if (!isSeeking) {
|
||||
const { x, y } = lastMousePosition.current;
|
||||
const { x, y } = lastMousePositionRef.current;
|
||||
checkMousePositionAndToggleControls(x, y);
|
||||
}
|
||||
});
|
||||
|
||||
@ -80,7 +80,12 @@ const EmojiInteractionAnimation: FC<OwnProps & StateProps> = ({
|
||||
stop();
|
||||
endHeavyAnimation();
|
||||
}, PLAYING_DURATION);
|
||||
}, [stop]);
|
||||
|
||||
return () => {
|
||||
clearTimeout(timeoutRef.current);
|
||||
endHeavyAnimation();
|
||||
};
|
||||
}, []);
|
||||
|
||||
const effectHash = effectAnimationId && `sticker${effectAnimationId}`;
|
||||
const effectTgsUrl = useMedia(effectHash, !effectAnimationId);
|
||||
|
||||
@ -251,7 +251,7 @@ function MiddleColumn({
|
||||
const oldLang = useOldLang();
|
||||
const lang = useLang();
|
||||
const [dropAreaState, setDropAreaState] = useState(DropAreaState.None);
|
||||
const [isScrollDownNeeded, setIsScrollDownShown] = useState(false);
|
||||
const [isScrollDownNeeded, setIsScrollDownNeeded] = useState(false);
|
||||
const isScrollDownShown = isScrollDownNeeded && (!isMobile || !hasActiveMiddleSearch);
|
||||
const [isNotchShown, setIsNotchShown] = useState<boolean | undefined>();
|
||||
const [isUnpinModalOpen, setIsUnpinModalOpen] = useState(false);
|
||||
@ -571,7 +571,7 @@ function MiddleColumn({
|
||||
type={renderingMessageListType!}
|
||||
isComments={isComments}
|
||||
canPost={renderingCanPost!}
|
||||
onScrollDownToggle={setIsScrollDownShown}
|
||||
onScrollDownToggle={setIsScrollDownNeeded}
|
||||
onNotchToggle={setIsNotchShown}
|
||||
isReady={isReady}
|
||||
isContactRequirePremium={isContactRequirePremium}
|
||||
@ -900,18 +900,23 @@ function useIsReady(
|
||||
const forceUpdate = useForceUpdate();
|
||||
|
||||
const willSwitchMessageList = prevTransitionKey !== undefined && prevTransitionKey !== currentTransitionKey;
|
||||
if (willSwitchMessageList) {
|
||||
if (withAnimations) {
|
||||
setIsReady(false);
|
||||
|
||||
// Make sure to end even if end callback was not called (which was some hardly-reproducible bug)
|
||||
setTimeout(() => {
|
||||
setIsReady(true);
|
||||
}, LAYER_ANIMATION_DURATION_MS);
|
||||
} else {
|
||||
useSyncEffect(() => {
|
||||
if (!willSwitchMessageList) return;
|
||||
if (!withAnimations) {
|
||||
forceUpdate();
|
||||
return undefined;
|
||||
}
|
||||
}
|
||||
setIsReady(false);
|
||||
|
||||
// Make sure to end even if end callback was not called (which was some hardly-reproducible bug)
|
||||
const timeout = setTimeout(() => {
|
||||
setIsReady(true);
|
||||
}, LAYER_ANIMATION_DURATION_MS);
|
||||
|
||||
return () => {
|
||||
clearTimeout(timeout);
|
||||
};
|
||||
}, [willSwitchMessageList, withAnimations]);
|
||||
|
||||
useSyncEffect(() => {
|
||||
if (!withAnimations) {
|
||||
|
||||
@ -129,7 +129,7 @@ const MiddleHeader: FC<OwnProps & StateProps> = ({
|
||||
} = getActions();
|
||||
|
||||
const lang = useOldLang();
|
||||
const isBackButtonActive = useRef(true);
|
||||
const isBackButtonActiveRef = useRef(true);
|
||||
const { isDesktop, isTablet } = useAppLayout();
|
||||
|
||||
const { width: windowWidth } = useWindowSize();
|
||||
@ -164,7 +164,7 @@ const MiddleHeader: FC<OwnProps & StateProps> = ({
|
||||
|
||||
const setBackButtonActive = useLastCallback(() => {
|
||||
setTimeout(() => {
|
||||
isBackButtonActive.current = true;
|
||||
isBackButtonActiveRef.current = true;
|
||||
}, BACK_BUTTON_INACTIVE_TIME);
|
||||
});
|
||||
|
||||
@ -187,10 +187,10 @@ const MiddleHeader: FC<OwnProps & StateProps> = ({
|
||||
});
|
||||
|
||||
const handleBackClick = useLastCallback((e: React.MouseEvent<HTMLElement, MouseEvent>) => {
|
||||
if (!isBackButtonActive.current) return;
|
||||
if (!isBackButtonActiveRef.current) return;
|
||||
|
||||
// Workaround for missing UI when quickly clicking the Back button
|
||||
isBackButtonActive.current = false;
|
||||
isBackButtonActiveRef.current = false;
|
||||
if (isMobile) {
|
||||
const messageInput = document.querySelector<HTMLDivElement>(EDITABLE_INPUT_CSS_SELECTOR);
|
||||
messageInput?.blur();
|
||||
|
||||
@ -109,9 +109,13 @@ const CustomSendMenu: FC<OwnProps> = ({
|
||||
return;
|
||||
}
|
||||
|
||||
setTimeout(() => {
|
||||
const timeout = setTimeout(() => {
|
||||
markIsReady();
|
||||
}, ANIMATION_DURATION);
|
||||
|
||||
return () => {
|
||||
clearTimeout(timeout);
|
||||
};
|
||||
}, [isOpen, markIsReady, unmarkIsReady]);
|
||||
|
||||
return (
|
||||
|
||||
@ -147,7 +147,7 @@ const EmojiPicker: FC<OwnProps & StateProps> = ({
|
||||
|
||||
// Initialize data on first render.
|
||||
useEffect(() => {
|
||||
setTimeout(() => {
|
||||
const timeout = setTimeout(() => {
|
||||
const exec = () => {
|
||||
setCategories(emojiData.categories);
|
||||
|
||||
@ -161,6 +161,10 @@ const EmojiPicker: FC<OwnProps & StateProps> = ({
|
||||
.then(exec);
|
||||
}
|
||||
}, OPEN_ANIMATION_DELAY);
|
||||
|
||||
return () => {
|
||||
clearTimeout(timeout);
|
||||
};
|
||||
}, []);
|
||||
|
||||
const selectCategory = useLastCallback((index: number) => {
|
||||
|
||||
@ -5,8 +5,7 @@ import {
|
||||
} from '../../../lib/teact/teact';
|
||||
import { getActions, withGlobal } from '../../../global';
|
||||
|
||||
import type { ApiNewMediaTodo } from '../../../api/types';
|
||||
import type { ApiMessage } from '../../../api/types';
|
||||
import type { ApiMessage, ApiNewMediaTodo } from '../../../api/types';
|
||||
import type { TabState } from '../../../global/types/tabState';
|
||||
|
||||
import { requestMeasure, requestNextMutation } from '../../../lib/fasterdom/fasterdom';
|
||||
|
||||
@ -53,14 +53,14 @@ export default function useInputCustomEmojis(
|
||||
const customColor = useDynamicColorListener(inputRef, undefined, !isReady);
|
||||
const colorFilter = useColorFilter(customColor, true);
|
||||
const dpr = useDevicePixelRatio();
|
||||
const playersById = useRef<Map<string, CustomEmojiPlayer>>(new Map());
|
||||
const playersByIdRef = useRef<Map<string, CustomEmojiPlayer>>(new Map());
|
||||
|
||||
const clearPlayers = useLastCallback((ids: string[]) => {
|
||||
ids.forEach((id) => {
|
||||
const player = playersById.current.get(id);
|
||||
const player = playersByIdRef.current.get(id);
|
||||
if (player) {
|
||||
player.destroy();
|
||||
playersById.current.delete(id);
|
||||
playersByIdRef.current.delete(id);
|
||||
}
|
||||
});
|
||||
});
|
||||
@ -69,7 +69,7 @@ export default function useInputCustomEmojis(
|
||||
if (!isReady || !inputRef.current || !sharedCanvasRef.current || !sharedCanvasHqRef.current) return;
|
||||
|
||||
const global = getGlobal();
|
||||
const playerIdsToClear = new Set(playersById.current.keys());
|
||||
const playerIdsToClear = new Set(playersByIdRef.current.keys());
|
||||
const customEmojis = Array.from(inputRef.current.querySelectorAll<HTMLElement>('.custom-emoji'));
|
||||
|
||||
customEmojis.forEach((element) => {
|
||||
@ -91,8 +91,8 @@ export default function useInputCustomEmojis(
|
||||
const x = round((elementBounds.left - canvasBounds.left) / canvasBounds.width, 4);
|
||||
const y = round((elementBounds.top - canvasBounds.top) / canvasBounds.height, 4);
|
||||
|
||||
if (playersById.current.has(playerId)) {
|
||||
const player = playersById.current.get(playerId)!;
|
||||
if (playersByIdRef.current.has(playerId)) {
|
||||
const player = playersByIdRef.current.get(playerId)!;
|
||||
player.updatePosition(x, y);
|
||||
return;
|
||||
}
|
||||
@ -123,7 +123,7 @@ export default function useInputCustomEmojis(
|
||||
animation.play();
|
||||
}
|
||||
|
||||
playersById.current.set(playerId, animation);
|
||||
playersByIdRef.current.set(playerId, animation);
|
||||
});
|
||||
});
|
||||
|
||||
@ -135,7 +135,7 @@ export default function useInputCustomEmojis(
|
||||
}, [synchronizeElements]);
|
||||
|
||||
useEffect(() => {
|
||||
const activePlayersById = playersById.current;
|
||||
const activePlayersById = playersByIdRef.current;
|
||||
// Always clear players on unmount
|
||||
return () => {
|
||||
clearPlayers(Array.from(activePlayersById.keys()));
|
||||
@ -144,7 +144,7 @@ export default function useInputCustomEmojis(
|
||||
|
||||
useEffect(() => {
|
||||
if (!getHtml() || !inputRef.current || !sharedCanvasRef.current || !isActive || !isReady) {
|
||||
clearPlayers(Array.from(playersById.current.keys()));
|
||||
clearPlayers(Array.from(playersByIdRef.current.keys()));
|
||||
return;
|
||||
}
|
||||
|
||||
@ -173,13 +173,13 @@ export default function useInputCustomEmojis(
|
||||
useResizeObserver(sharedCanvasRef, throttledSynchronizeElements);
|
||||
useEffectWithPrevDeps(([prevDpr]) => {
|
||||
if (dpr !== prevDpr) {
|
||||
clearPlayers(Array.from(playersById.current.keys()));
|
||||
clearPlayers(Array.from(playersByIdRef.current.keys()));
|
||||
synchronizeElements();
|
||||
}
|
||||
}, [dpr, synchronizeElements]);
|
||||
|
||||
const freezeAnimation = useLastCallback(() => {
|
||||
playersById.current.forEach((player) => {
|
||||
playersByIdRef.current.forEach((player) => {
|
||||
player.pause();
|
||||
});
|
||||
});
|
||||
@ -189,7 +189,7 @@ export default function useInputCustomEmojis(
|
||||
return;
|
||||
}
|
||||
|
||||
playersById.current?.forEach((player) => {
|
||||
playersByIdRef.current.forEach((player) => {
|
||||
player.play();
|
||||
});
|
||||
});
|
||||
|
||||
@ -14,8 +14,7 @@ export default function usePaidMessageConfirmation(
|
||||
shouldPaidMessageAutoApprove,
|
||||
} = getGlobal().settings.byKey;
|
||||
|
||||
const [shouldAutoApprove,
|
||||
setAutoApprove] = useState(Boolean(shouldPaidMessageAutoApprove));
|
||||
const [shouldAutoApprove, setShouldAutoApprove] = useState(Boolean(shouldPaidMessageAutoApprove));
|
||||
const [isWaitingStarsTopup, setIsWaitingStarsTopup] = useState(false);
|
||||
const confirmPaymentHandlerRef = useRef<NoneToVoidFunction | undefined>(undefined);
|
||||
|
||||
@ -78,6 +77,6 @@ export default function usePaidMessageConfirmation(
|
||||
handleWithConfirmation,
|
||||
dialogHandler,
|
||||
shouldAutoApprove,
|
||||
setAutoApprove,
|
||||
setAutoApprove: setShouldAutoApprove,
|
||||
};
|
||||
}
|
||||
|
||||
@ -23,7 +23,7 @@ export default function useContainerHeight(containerRef: ElementRef<HTMLDivEleme
|
||||
}
|
||||
}, [isComposerVisible, containerRef, getContainerHeight]);
|
||||
|
||||
const prevContainerHeight = useRef<number>();
|
||||
const prevContainerHeightRef = useRef<number>();
|
||||
|
||||
return [getContainerHeight, prevContainerHeight] as const;
|
||||
return [getContainerHeight, prevContainerHeightRef] as const;
|
||||
}
|
||||
|
||||
@ -309,8 +309,11 @@ const ActionMessage = ({
|
||||
return;
|
||||
}
|
||||
|
||||
setTimeout(markShown, appearanceOrder * MESSAGE_APPEARANCE_DELAY);
|
||||
}, [appearanceOrder, markShown, noAppearanceAnimation]);
|
||||
const timeout = setTimeout(markShown, appearanceOrder * MESSAGE_APPEARANCE_DELAY);
|
||||
return () => {
|
||||
clearTimeout(timeout);
|
||||
};
|
||||
}, [appearanceOrder, noAppearanceAnimation]);
|
||||
|
||||
const { ref: refWithTransition } = useShowTransition({
|
||||
isOpen: isShown,
|
||||
|
||||
@ -12,9 +12,11 @@ import {
|
||||
} from '../../../config';
|
||||
import {
|
||||
getMainUsername,
|
||||
getMessageInvoice, getMessageTextWithFallback, isChatChannel,
|
||||
getMessageContent,
|
||||
getMessageInvoice,
|
||||
getMessageTextWithFallback,
|
||||
isChatChannel,
|
||||
} from '../../../global/helpers';
|
||||
import { getMessageContent } from '../../../global/helpers';
|
||||
import { getPeerTitle } from '../../../global/helpers/peers';
|
||||
import { getMessageReplyInfo } from '../../../global/helpers/replies';
|
||||
import {
|
||||
@ -30,8 +32,7 @@ import { ensureProtocol } from '../../../util/browser/url';
|
||||
import {
|
||||
formatDateTimeToString, formatScheduledDateTime, formatShortDuration,
|
||||
} from '../../../util/dates/oldDateFormat';
|
||||
import { formatCurrency } from '../../../util/formatCurrency';
|
||||
import { convertTonFromNanos } from '../../../util/formatCurrency';
|
||||
import { convertTonFromNanos, formatCurrency } from '../../../util/formatCurrency';
|
||||
import { formatCurrencyAmountAsText, formatStarsAsText, formatTonAsText } from '../../../util/localization/format';
|
||||
import { conjuctionWithNodes } from '../../../util/localization/utils';
|
||||
import { getServerTime } from '../../../util/serverTime';
|
||||
|
||||
@ -692,9 +692,6 @@ const ContextMenuContainer: FC<OwnProps & StateProps> = ({
|
||||
return undefined;
|
||||
}
|
||||
|
||||
const scheduledMaxDate = new Date();
|
||||
scheduledMaxDate.setFullYear(scheduledMaxDate.getFullYear() + 1);
|
||||
|
||||
return (
|
||||
<div ref={containerRef} className={buildClassName('ContextMenuContainer', className)}>
|
||||
<MessageContextMenu
|
||||
|
||||
@ -61,7 +61,7 @@ const Giveaway = ({
|
||||
}: OwnProps & StateProps) => {
|
||||
const { openChat } = getActions();
|
||||
|
||||
const isLoadingInfo = useRef(false);
|
||||
const isLoadingInfoRef = useRef(false);
|
||||
const [giveawayInfo, setGiveawayInfo] = useState<ApiGiveawayInfo | undefined>();
|
||||
|
||||
const lang = useOldLang();
|
||||
@ -90,15 +90,15 @@ const Giveaway = ({
|
||||
});
|
||||
|
||||
const handleShowInfoClick = useLastCallback(async () => {
|
||||
if (isLoadingInfo.current) return;
|
||||
if (isLoadingInfoRef.current) return;
|
||||
|
||||
isLoadingInfo.current = true;
|
||||
isLoadingInfoRef.current = true;
|
||||
const result = await callApi('fetchGiveawayInfo', {
|
||||
peer: chat,
|
||||
messageId: message.id,
|
||||
});
|
||||
setGiveawayInfo(result);
|
||||
isLoadingInfo.current = false;
|
||||
isLoadingInfoRef.current = false;
|
||||
});
|
||||
|
||||
const handleCloseInfo = useLastCallback(() => {
|
||||
@ -240,7 +240,7 @@ const Giveaway = ({
|
||||
? lang('BoostingGiveawayHowItWorksIncludeText', [chatTitle, quantity, prizeDescription], undefined, quantity)
|
||||
: undefined;
|
||||
|
||||
let secondKey = '';
|
||||
let secondKey;
|
||||
if (isResultsInfo) {
|
||||
secondKey = isSeveral ? 'BoostingGiveawayHowItWorksSubTextSeveralEnd' : 'BoostingGiveawayHowItWorksSubTextEnd';
|
||||
} else {
|
||||
|
||||
@ -489,7 +489,7 @@ const Message = ({
|
||||
const oldLang = useOldLang();
|
||||
const lang = useLang();
|
||||
|
||||
const [isTranscriptionHidden, setTranscriptionHidden] = useState(false);
|
||||
const [isTranscriptionHidden, setIsTranscriptionHidden] = useState(false);
|
||||
const [isPlayingSnapAnimation, setIsPlayingSnapAnimation] = useState(false);
|
||||
const [isPlayingDeleteAnimation, setIsPlayingDeleteAnimation] = useState(false);
|
||||
const [shouldPlayEffect, requestEffect, hideEffect] = useFlag();
|
||||
@ -530,8 +530,11 @@ const Message = ({
|
||||
return;
|
||||
}
|
||||
|
||||
setTimeout(markShown, appearanceOrder * MESSAGE_APPEARANCE_DELAY);
|
||||
}, [appearanceOrder, markShown, noAppearanceAnimation]);
|
||||
const timeout = setTimeout(markShown, appearanceOrder * MESSAGE_APPEARANCE_DELAY);
|
||||
return () => {
|
||||
clearTimeout(timeout);
|
||||
};
|
||||
}, [appearanceOrder, noAppearanceAnimation]);
|
||||
|
||||
useShowTransition({
|
||||
ref,
|
||||
@ -1300,7 +1303,7 @@ const Message = ({
|
||||
canAutoLoad={canAutoLoadMedia}
|
||||
isDownloading={isDownloading}
|
||||
onReadMedia={shouldReadMedia ? handleReadMedia : undefined}
|
||||
onHideTranscription={setTranscriptionHidden}
|
||||
onHideTranscription={setIsTranscriptionHidden}
|
||||
isTranscriptionError={isTranscriptionError}
|
||||
isTranscribed={Boolean(transcribedText)}
|
||||
canTranscribe={canTranscribeVoice && !hasTtl}
|
||||
@ -1326,7 +1329,7 @@ const Message = ({
|
||||
isTranscribed={Boolean(transcribedText)}
|
||||
isTranscriptionError={isTranscriptionError}
|
||||
canDownload={!isProtected}
|
||||
onHideTranscription={setTranscriptionHidden}
|
||||
onHideTranscription={setIsTranscriptionHidden}
|
||||
canTranscribe={canTranscribeVoice && !hasTtl}
|
||||
/>
|
||||
)}
|
||||
@ -1670,7 +1673,6 @@ const Message = ({
|
||||
shouldSkipRenderForwardTitle: boolean = false, shouldSkipRenderAdminTitle: boolean = false,
|
||||
) {
|
||||
let senderTitle;
|
||||
let senderColor;
|
||||
if (senderPeer && !(isCustomShape && viaBotId)) {
|
||||
senderTitle = getPeerFullTitle(oldLang, senderPeer);
|
||||
} else if (forwardInfo?.hiddenUserName) {
|
||||
@ -1689,7 +1691,6 @@ const Message = ({
|
||||
className={buildClassName(
|
||||
'message-title-name-container',
|
||||
forwardInfo?.hiddenUserName ? 'sender-hidden' : 'interactive',
|
||||
senderColor,
|
||||
)}
|
||||
dir="ltr"
|
||||
>
|
||||
|
||||
@ -337,10 +337,14 @@ const MessageContextMenu: FC<OwnProps> = ({
|
||||
return;
|
||||
}
|
||||
|
||||
setTimeout(() => {
|
||||
const timeout = setTimeout(() => {
|
||||
markIsReady();
|
||||
}, ANIMATION_DURATION);
|
||||
}, [isOpen, markIsReady, unmarkIsReady]);
|
||||
|
||||
return () => {
|
||||
clearTimeout(timeout);
|
||||
};
|
||||
}, [isOpen]);
|
||||
|
||||
useEffect(() => {
|
||||
return disableScrolling(scrollableRef.current, '.ReactionPicker');
|
||||
|
||||
@ -107,7 +107,7 @@ const Poll: FC<OwnProps> = ({
|
||||
|
||||
useLayoutEffect(() => {
|
||||
if (closePeriod > 0) {
|
||||
setTimeout(() => setClosePeriod(closePeriod - 1), TIMER_UPDATE_INTERVAL);
|
||||
window.setTimeout(() => setClosePeriod(closePeriod - 1), TIMER_UPDATE_INTERVAL);
|
||||
}
|
||||
if (!timerCircleRef.current) return;
|
||||
|
||||
|
||||
@ -1,4 +1,3 @@
|
||||
import type { FC } from '../../../lib/teact/teact';
|
||||
import {
|
||||
memo, useEffect, useLayoutEffect,
|
||||
useMemo,
|
||||
@ -102,7 +101,7 @@ const INLINE_MEMBER_COUNT = 5;
|
||||
|
||||
const runDebouncedForSearch = debounce((cb) => cb(), 200, false);
|
||||
|
||||
const MiddleSearch: FC<OwnProps & StateProps> = ({
|
||||
const MiddleSearch = ({
|
||||
isActive,
|
||||
chat,
|
||||
monoforumChat,
|
||||
@ -122,7 +121,7 @@ const MiddleSearch: FC<OwnProps & StateProps> = ({
|
||||
currentUserId,
|
||||
fromPeerId,
|
||||
isGroupChat,
|
||||
}) => {
|
||||
}: OwnProps & StateProps) => {
|
||||
const {
|
||||
updateMiddleSearch,
|
||||
resetMiddleSearch,
|
||||
@ -414,7 +413,9 @@ const MiddleSearch: FC<OwnProps & StateProps> = ({
|
||||
});
|
||||
}
|
||||
|
||||
// eslint-disable-next-line @eslint-react/web-api/no-leaked-event-listener
|
||||
window.addEventListener('touchend', focus);
|
||||
// eslint-disable-next-line @eslint-react/web-api/no-leaked-event-listener
|
||||
window.addEventListener('mouseup', focus);
|
||||
|
||||
window.addEventListener('touchstart', removeListeners);
|
||||
|
||||
@ -42,6 +42,10 @@ const MiddleSearchResult = ({
|
||||
}: OwnProps) => {
|
||||
const lang = useLang();
|
||||
|
||||
const handleClick = useLastCallback(() => {
|
||||
onClick(message);
|
||||
});
|
||||
|
||||
if (peer && !message) {
|
||||
const username = getMainUsername(peer);
|
||||
|
||||
@ -83,10 +87,6 @@ const MiddleSearchResult = ({
|
||||
const senderPeer = shouldShowChat ? messageChat : peer;
|
||||
const senderName = shouldShowChat && peer ? getMessageSenderName(lang, message.chatId, peer) : undefined;
|
||||
|
||||
const handleClick = useLastCallback(() => {
|
||||
onClick(message);
|
||||
});
|
||||
|
||||
return (
|
||||
<div
|
||||
role="button"
|
||||
|
||||
@ -1,41 +0,0 @@
|
||||
import type { TeactNode } from '../../../lib/teact/teact';
|
||||
import { memo } from '../../../lib/teact/teact';
|
||||
|
||||
import { LOCAL_TGS_URLS } from '../../common/helpers/animatedAssets';
|
||||
|
||||
import AnimatedIconWithPreview from '../../common/AnimatedIconWithPreview';
|
||||
import Link from '../../ui/Link';
|
||||
|
||||
import styles from './GiftEmptyState.module.scss';
|
||||
|
||||
type OwnProps = {
|
||||
description: TeactNode;
|
||||
linkText?: TeactNode;
|
||||
onLinkClick?: NoneToVoidFunction;
|
||||
};
|
||||
|
||||
const GiftEmptyState = ({ description, linkText, onLinkClick }: OwnProps) => {
|
||||
return (
|
||||
<div className={styles.root}>
|
||||
<AnimatedIconWithPreview
|
||||
size={160}
|
||||
tgsUrl={LOCAL_TGS_URLS.SearchingDuck}
|
||||
nonInteractive
|
||||
noLoop
|
||||
/>
|
||||
<div className={styles.description}>
|
||||
{description}
|
||||
</div>
|
||||
{Boolean(linkText && onLinkClick) && (
|
||||
<Link
|
||||
className={styles.link}
|
||||
onClick={onLinkClick}
|
||||
>
|
||||
{linkText}
|
||||
</Link>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default memo(GiftEmptyState);
|
||||
@ -20,8 +20,7 @@ import type { ResaleGiftsFilterOptions, StarGiftCategory } from '../../../types'
|
||||
import { STARS_CURRENCY_CODE } from '../../../config';
|
||||
import { getUserFullName } from '../../../global/helpers';
|
||||
import { getPeerTitle, isApiPeerChat, isApiPeerUser } from '../../../global/helpers/peers';
|
||||
import { selectTabState } from '../../../global/selectors';
|
||||
import { selectPeer, selectUserFullInfo } from '../../../global/selectors';
|
||||
import { selectPeer, selectTabState, selectUserFullInfo } from '../../../global/selectors';
|
||||
import buildClassName from '../../../util/buildClassName';
|
||||
import { getNextArrowReplacement } from '../../../util/localization/format';
|
||||
import { throttle } from '../../../util/schedulers';
|
||||
|
||||
@ -7,8 +7,7 @@ import type {
|
||||
ApiSavedStarGift,
|
||||
} from '../../../api/types';
|
||||
|
||||
import { DEFAULT_STATUS_ICON_ID } from '../../../config';
|
||||
import { STARS_CURRENCY_CODE } from '../../../config';
|
||||
import { DEFAULT_STATUS_ICON_ID, STARS_CURRENCY_CODE } from '../../../config';
|
||||
import { selectTabState, selectUser } from '../../../global/selectors';
|
||||
import { formatDateAtTime } from '../../../util/dates/oldDateFormat';
|
||||
import { getServerTime } from '../../../util/serverTime';
|
||||
|
||||
@ -380,7 +380,7 @@ const GiftCraftModal = ({ modal, craftAttributePermilles }: OwnProps & StateProp
|
||||
|
||||
// DOM refs
|
||||
const cubeRef = useRef<HTMLDivElement>();
|
||||
const faceRefs = useRef<Record<FaceName, HTMLDivElement | undefined>>({
|
||||
const facesRef = useRef<Record<FaceName, HTMLDivElement | undefined>>({
|
||||
front: undefined,
|
||||
back: undefined,
|
||||
left: undefined,
|
||||
@ -388,8 +388,8 @@ const GiftCraftModal = ({ modal, craftAttributePermilles }: OwnProps & StateProp
|
||||
top: undefined,
|
||||
bottom: undefined,
|
||||
});
|
||||
const slotRefs = useRef<(HTMLDivElement | undefined)[]>([]);
|
||||
const slotInnerRefs = useRef<(HTMLDivElement | undefined)[]>([]);
|
||||
const slotsRef = useRef<(HTMLDivElement | undefined)[]>([]);
|
||||
const slotsInnerRef = useRef<(HTMLDivElement | undefined)[]>([]);
|
||||
const backfaceRemovedRef = useRef(false);
|
||||
|
||||
// Physics state refs (mutable, not triggering re-renders)
|
||||
@ -473,7 +473,7 @@ const GiftCraftModal = ({ modal, craftAttributePermilles }: OwnProps & StateProp
|
||||
}
|
||||
|
||||
// Reset face backgrounds and backface-visibility
|
||||
Object.values(faceRefs.current).forEach((face) => {
|
||||
Object.values(facesRef.current).forEach((face) => {
|
||||
if (face) {
|
||||
face.style.background = '';
|
||||
face.style.backfaceVisibility = '';
|
||||
@ -481,7 +481,7 @@ const GiftCraftModal = ({ modal, craftAttributePermilles }: OwnProps & StateProp
|
||||
});
|
||||
|
||||
// Reset slot transforms
|
||||
slotRefs.current.forEach((slot) => {
|
||||
slotsRef.current.forEach((slot) => {
|
||||
if (slot) {
|
||||
slot.style.transform = '';
|
||||
slot.style.transition = '';
|
||||
@ -520,7 +520,7 @@ const GiftCraftModal = ({ modal, craftAttributePermilles }: OwnProps & StateProp
|
||||
// Fade out slots when result rotation is complete
|
||||
useEffect(() => {
|
||||
if (isRotationStarted) {
|
||||
slotRefs.current.forEach((slot) => {
|
||||
slotsRef.current.forEach((slot) => {
|
||||
if (slot) {
|
||||
requestMutation(() => {
|
||||
slot.classList.add(styles.craftSlotHidden);
|
||||
@ -817,7 +817,7 @@ const GiftCraftModal = ({ modal, craftAttributePermilles }: OwnProps & StateProp
|
||||
// Slot Flight Animation
|
||||
// ===================
|
||||
const flySlotToCube = useLastCallback((index: number) => {
|
||||
const slot = slotRefs.current[index];
|
||||
const slot = slotsRef.current[index];
|
||||
const cube = cubeRef.current;
|
||||
if (!slot || !cube || !faceTransformsRef.current) return;
|
||||
|
||||
@ -913,7 +913,7 @@ const GiftCraftModal = ({ modal, craftAttributePermilles }: OwnProps & StateProp
|
||||
setAnimatingSlots((prev) => new Set(prev).add(index));
|
||||
|
||||
// Pre-rotate slot and counter-rotate inner content to avoid visible rotation jump
|
||||
const slotInner = slotInnerRefs.current[index];
|
||||
const slotInner = slotsInnerRef.current[index];
|
||||
const finalTransformStr = finalTransform.toString();
|
||||
|
||||
requestMutation(() => {
|
||||
@ -947,7 +947,7 @@ const GiftCraftModal = ({ modal, craftAttributePermilles }: OwnProps & StateProp
|
||||
// Remove backface-visibility from all faces on first slot stick
|
||||
if (!backfaceRemovedRef.current) {
|
||||
backfaceRemovedRef.current = true;
|
||||
Object.values(faceRefs.current).forEach((face) => {
|
||||
Object.values(facesRef.current).forEach((face) => {
|
||||
if (face) {
|
||||
face.style.backfaceVisibility = 'visible';
|
||||
}
|
||||
@ -1156,11 +1156,11 @@ const GiftCraftModal = ({ modal, craftAttributePermilles }: OwnProps & StateProp
|
||||
});
|
||||
|
||||
const slotRefCallbacks = useMemo(() => [0, 1, 2, 3].map((i) => (el: HTMLDivElement | undefined) => {
|
||||
slotRefs.current[i] = el;
|
||||
slotsRef.current[i] = el;
|
||||
}), []);
|
||||
|
||||
const slotInnerRefCallbacks = useMemo(() => [0, 1, 2, 3].map((i) => (el: HTMLDivElement | undefined) => {
|
||||
slotInnerRefs.current[i] = el;
|
||||
slotsInnerRef.current[i] = el;
|
||||
}), []);
|
||||
|
||||
function renderCraftSlot(index: number) {
|
||||
@ -1260,7 +1260,7 @@ const GiftCraftModal = ({ modal, craftAttributePermilles }: OwnProps & StateProp
|
||||
return (
|
||||
<div
|
||||
key={face.name}
|
||||
ref={(el) => { faceRefs.current[face.name] = el || undefined; }}
|
||||
ref={(el) => { facesRef.current[face.name] = el || undefined; }}
|
||||
className={buildClassName(
|
||||
styles.face,
|
||||
shouldHide && styles.faceHidden,
|
||||
@ -1486,7 +1486,7 @@ const GiftCraftModal = ({ modal, craftAttributePermilles }: OwnProps & StateProp
|
||||
}
|
||||
|
||||
function renderInfoContent() {
|
||||
let activeKey = 0;
|
||||
let activeKey;
|
||||
let content;
|
||||
|
||||
if (failedFace) {
|
||||
|
||||
@ -9,8 +9,9 @@ import {
|
||||
selectStarsGiftResaleCommission,
|
||||
selectTonGiftResaleCommission,
|
||||
} from '../../../../global/selectors';
|
||||
import { convertTonFromNanos, convertTonToNanos } from '../../../../util/formatCurrency';
|
||||
import { convertTonToUsd, formatCurrencyAsString } from '../../../../util/formatCurrency';
|
||||
import {
|
||||
convertTonFromNanos, convertTonToNanos, convertTonToUsd, formatCurrencyAsString,
|
||||
} from '../../../../util/formatCurrency';
|
||||
import { formatStarsAsIcon, formatStarsAsText, formatTonAsIcon,
|
||||
formatTonAsText } from '../../../../util/localization/format';
|
||||
|
||||
|
||||
@ -97,7 +97,7 @@ const ProfileRatingModal = ({
|
||||
const pendingLevel = !showFutureRating && renderingPendingRating
|
||||
? renderingPendingRating.level : renderingStarsRating.level;
|
||||
|
||||
let levelProgress = 0;
|
||||
let levelProgress;
|
||||
|
||||
if (!nextLevelStars) {
|
||||
levelProgress = 1;
|
||||
|
||||
@ -81,7 +81,7 @@ const StarsBalanceModal = ({
|
||||
const oldLang = useOldLang();
|
||||
const lang = useLang();
|
||||
|
||||
const [isHeaderHidden, setHeaderHidden] = useState(true);
|
||||
const [isHeaderHidden, setIsHeaderHidden] = useState(true);
|
||||
const [areTabsPinned, pinTabs, unpinTabs] = useFlag(false);
|
||||
const [selectedTabIndex, setSelectedTabIndex] = useState(0);
|
||||
const [areBuyOptionsShown, showBuyOptions, hideBuyOptions] = useFlag();
|
||||
@ -162,7 +162,7 @@ const StarsBalanceModal = ({
|
||||
|
||||
useEffect(() => {
|
||||
if (!isOpen) {
|
||||
setHeaderHidden(true);
|
||||
setIsHeaderHidden(true);
|
||||
setSelectedTabIndex(0);
|
||||
hideBuyOptions();
|
||||
unpinTabs();
|
||||
@ -280,7 +280,7 @@ const StarsBalanceModal = ({
|
||||
function handleScroll(e: React.UIEvent<HTMLDivElement>) {
|
||||
const { scrollTop } = e.currentTarget;
|
||||
|
||||
setHeaderHidden(scrollTop <= 150);
|
||||
setIsHeaderHidden(scrollTop <= 150);
|
||||
|
||||
if (tabsRef.current) {
|
||||
const { top: tabsTop } = tabsRef.current.getBoundingClientRect();
|
||||
|
||||
@ -59,11 +59,11 @@ const StarsGiftModal: FC<OwnProps & StateProps> = ({
|
||||
const oldLang = useOldLang();
|
||||
|
||||
const [selectedOption, setSelectedOption] = useState<ApiStarTopupOption | undefined>();
|
||||
const [isHeaderHidden, setHeaderHidden] = useState(true);
|
||||
const [isHeaderHidden, setIsHeaderHidden] = useState(true);
|
||||
|
||||
useEffect(() => {
|
||||
if (!isOpen) {
|
||||
setHeaderHidden(true);
|
||||
setIsHeaderHidden(true);
|
||||
}
|
||||
}, [isOpen]);
|
||||
|
||||
@ -115,7 +115,7 @@ const StarsGiftModal: FC<OwnProps & StateProps> = ({
|
||||
function handleScroll(e: React.UIEvent<HTMLDivElement>) {
|
||||
const { scrollTop } = e.currentTarget;
|
||||
|
||||
setHeaderHidden(scrollTop <= 150);
|
||||
setIsHeaderHidden(scrollTop <= 150);
|
||||
}
|
||||
|
||||
const handleClose = useLastCallback(() => {
|
||||
|
||||
@ -10,6 +10,7 @@ import { NNBSP } from '../../../../config';
|
||||
import { getPeerTitle } from '../../../../global/helpers/peers';
|
||||
import { selectPeer } from '../../../../global/selectors';
|
||||
import { formatDateToString } from '../../../../util/dates/oldDateFormat';
|
||||
import { getServerTime } from '../../../../util/serverTime';
|
||||
import { formatInteger } from '../../../../util/textFormat';
|
||||
import renderText from '../../../common/helpers/renderText';
|
||||
|
||||
@ -51,7 +52,7 @@ const StarsSubscriptionItem = ({ subscription }: OwnProps) => {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
const hasExpired = until < Date.now() / 1000;
|
||||
const hasExpired = until < getServerTime();
|
||||
const formattedDate = formatDateToString(until * 1000, lang.code, true, 'long');
|
||||
|
||||
return (
|
||||
|
||||
@ -1,11 +1,9 @@
|
||||
import type React from '../../../lib/teact/teact';
|
||||
import {
|
||||
memo, useEffect,
|
||||
useState } from '../../../lib/teact/teact';
|
||||
memo, useEffect, useState,
|
||||
} from '../../../lib/teact/teact';
|
||||
import { getActions, withGlobal } from '../../../global';
|
||||
|
||||
import type { ApiDraft, ApiStarsAmount, ApiTypeCurrencyAmount } from '../../../api/types';
|
||||
import type { ApiPeer } from '../../../api/types';
|
||||
import type { ApiDraft, ApiPeer, ApiStarsAmount, ApiTypeCurrencyAmount } from '../../../api/types';
|
||||
import type { TabState } from '../../../global/types';
|
||||
import { MAIN_THREAD_ID } from '../../../api/types';
|
||||
|
||||
@ -108,7 +106,7 @@ const SuggestMessageModal = ({
|
||||
const oldLang = useOldLang();
|
||||
|
||||
const isCurrencyStars = selectedCurrency === STARS_CURRENCY_CODE;
|
||||
const now = Math.floor(Date.now() / 1000);
|
||||
const now = getServerTime();
|
||||
const minAt = (now + futureMin) * 1000;
|
||||
const maxAt = (now + futureMax) * 1000;
|
||||
const defaultSelectedTime = (now + futureMin * 2) * 1000;
|
||||
|
||||
@ -13,6 +13,7 @@ import { selectChatMessage, selectIsMonoforumAdmin, selectSender } from '../../.
|
||||
import { formatScheduledDateTime, formatShortDuration } from '../../../util/dates/oldDateFormat';
|
||||
import { convertTonFromNanos } from '../../../util/formatCurrency';
|
||||
import { formatStarsAsText, formatTonAsText } from '../../../util/localization/format';
|
||||
import { getServerTime } from '../../../util/serverTime';
|
||||
import renderText from '../../common/helpers/renderText';
|
||||
|
||||
import useFlag from '../../../hooks/useFlag';
|
||||
@ -62,7 +63,7 @@ const SuggestedPostApprovalModal = ({
|
||||
const oldLang = useOldLang();
|
||||
const [isCalendarOpened, openCalendar, closeCalendar] = useFlag();
|
||||
|
||||
const now = Math.floor(Date.now() / 1000);
|
||||
const now = getServerTime();
|
||||
const minAt = (now + futureMin) * 1000;
|
||||
const maxAt = (now + futureMax) * 1000;
|
||||
const defaultSelectedTime = now + futureMin * 2;
|
||||
|
||||
@ -64,7 +64,7 @@ const UrlAuthModal = ({
|
||||
);
|
||||
const isOpen = Boolean(modal?.url && modal?.request) && !isMatchCodePrecheckPending;
|
||||
|
||||
const [isWriteAccessChecked, setWriteAccessChecked] = useState(
|
||||
const [isWriteAccessChecked, setIsWriteAccessChecked] = useState(
|
||||
() => Boolean(modalRequest?.shouldRequestWriteAccess),
|
||||
);
|
||||
const [selectedMatchCode, setSelectedMatchCode] = useState<string | undefined>();
|
||||
@ -84,7 +84,7 @@ const UrlAuthModal = ({
|
||||
return;
|
||||
}
|
||||
|
||||
setWriteAccessChecked(Boolean(modalRequest.shouldRequestWriteAccess));
|
||||
setIsWriteAccessChecked(Boolean(modalRequest.shouldRequestWriteAccess));
|
||||
setSelectedMatchCode(undefined);
|
||||
setDialogState('closed');
|
||||
}, [modalRequest]);
|
||||
@ -152,7 +152,7 @@ const UrlAuthModal = ({
|
||||
});
|
||||
|
||||
const handleTriggerWriteAccess = useLastCallback(() => {
|
||||
setWriteAccessChecked(!isWriteAccessChecked);
|
||||
setIsWriteAccessChecked(!isWriteAccessChecked);
|
||||
});
|
||||
|
||||
if (!renderingRequest) {
|
||||
|
||||
@ -234,27 +234,27 @@ const WebAppModal: FC<OwnProps & StateProps> = ({
|
||||
}, queryId ? PROLONG_INTERVAL : undefined, true);
|
||||
|
||||
// eslint-disable-next-line no-null/no-null
|
||||
const sendEventCallback = useRef<((event: WebAppOutboundEvent) => void) | null>(null);
|
||||
const sendEventCallbackRef = useRef<((event: WebAppOutboundEvent) => void) | null>(null);
|
||||
// eslint-disable-next-line no-null/no-null
|
||||
const reloadFrameCallback = useRef<((url: string) => void) | null>(null);
|
||||
const reloadFrameCallbackRef = useRef<((url: string) => void) | null>(null);
|
||||
|
||||
const registerSendEventCallback = useLastCallback((callback: (event: WebAppOutboundEvent) => void) => {
|
||||
sendEventCallback.current = callback;
|
||||
sendEventCallbackRef.current = callback;
|
||||
});
|
||||
|
||||
const sendEvent = useLastCallback((event: WebAppOutboundEvent) => {
|
||||
if (sendEventCallback.current) {
|
||||
sendEventCallback.current(event);
|
||||
if (sendEventCallbackRef.current) {
|
||||
sendEventCallbackRef.current(event);
|
||||
}
|
||||
});
|
||||
|
||||
const registerReloadFrameCallback = useLastCallback((callback: (url: string) => void) => {
|
||||
reloadFrameCallback.current = callback;
|
||||
reloadFrameCallbackRef.current = callback;
|
||||
});
|
||||
|
||||
const reloadFrame = useLastCallback((url: string) => {
|
||||
if (reloadFrameCallback.current) {
|
||||
reloadFrameCallback.current(url);
|
||||
if (reloadFrameCallbackRef.current) {
|
||||
reloadFrameCallbackRef.current(url);
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
@ -94,7 +94,6 @@ const POPUP_RESET_DELAY = 2000; // 2s
|
||||
const APP_NAME_DISPLAY_DURATION = 3800;
|
||||
const SANDBOX_ATTRIBUTES = [
|
||||
'allow-scripts',
|
||||
'allow-same-origin',
|
||||
'allow-popups',
|
||||
'allow-forms',
|
||||
'allow-modals',
|
||||
@ -857,79 +856,79 @@ const WebAppModalTabContent: FC<OwnProps & StateProps> = ({
|
||||
const hideDirection = (isHorizontalLayout
|
||||
&& (!shouldHideMainButton && !shouldHideSecondaryButton)) ? 'horizontal' : 'vertical';
|
||||
|
||||
const mainButtonChangeTimeout = useRef<ReturnType<typeof setTimeout>>();
|
||||
const mainButtonFastTimeout = useRef<ReturnType<typeof setTimeout>>();
|
||||
const secondaryButtonChangeTimeout = useRef<ReturnType<typeof setTimeout>>();
|
||||
const secondaryButtonFastTimeout = useRef<ReturnType<typeof setTimeout>>();
|
||||
const appNameDisplayTimeout = useRef<ReturnType<typeof setTimeout>>();
|
||||
const mainButtonChangeTimeoutRef = useRef<ReturnType<typeof setTimeout>>();
|
||||
const mainButtonFastTimeoutRef = useRef<ReturnType<typeof setTimeout>>();
|
||||
const secondaryButtonChangeTimeoutRef = useRef<ReturnType<typeof setTimeout>>();
|
||||
const secondaryButtonFastTimeoutRef = useRef<ReturnType<typeof setTimeout>>();
|
||||
const appNameDisplayTimeoutRef = useRef<ReturnType<typeof setTimeout>>();
|
||||
|
||||
useEffect(() => {
|
||||
if (isFullscreen && isOpen && Boolean(activeWebAppName)) {
|
||||
setShouldShowAppNameInFullscreen(true);
|
||||
|
||||
if (appNameDisplayTimeout.current) {
|
||||
clearTimeout(appNameDisplayTimeout.current);
|
||||
if (appNameDisplayTimeoutRef.current) {
|
||||
clearTimeout(appNameDisplayTimeoutRef.current);
|
||||
}
|
||||
|
||||
appNameDisplayTimeout.current = setTimeout(() => {
|
||||
appNameDisplayTimeoutRef.current = setTimeout(() => {
|
||||
setShouldShowAppNameInFullscreen(false);
|
||||
appNameDisplayTimeout.current = undefined;
|
||||
appNameDisplayTimeoutRef.current = undefined;
|
||||
}, APP_NAME_DISPLAY_DURATION);
|
||||
} else {
|
||||
setShouldShowAppNameInFullscreen(false);
|
||||
|
||||
if (appNameDisplayTimeout.current) {
|
||||
clearTimeout(appNameDisplayTimeout.current);
|
||||
appNameDisplayTimeout.current = undefined;
|
||||
if (appNameDisplayTimeoutRef.current) {
|
||||
clearTimeout(appNameDisplayTimeoutRef.current);
|
||||
appNameDisplayTimeoutRef.current = undefined;
|
||||
}
|
||||
}
|
||||
|
||||
return () => {
|
||||
if (appNameDisplayTimeout.current) {
|
||||
clearTimeout(appNameDisplayTimeout.current);
|
||||
if (appNameDisplayTimeoutRef.current) {
|
||||
clearTimeout(appNameDisplayTimeoutRef.current);
|
||||
}
|
||||
};
|
||||
}, [isFullscreen, isOpen, activeWebAppName]);
|
||||
|
||||
useEffect(() => {
|
||||
if (mainButtonChangeTimeout.current) clearTimeout(mainButtonChangeTimeout.current);
|
||||
if (mainButtonFastTimeout.current) clearTimeout(mainButtonFastTimeout.current);
|
||||
if (mainButtonChangeTimeoutRef.current) clearTimeout(mainButtonChangeTimeoutRef.current);
|
||||
if (mainButtonFastTimeoutRef.current) clearTimeout(mainButtonFastTimeoutRef.current);
|
||||
|
||||
if (isMainButtonVisible) {
|
||||
mainButtonFastTimeout.current = setTimeout(() => {
|
||||
mainButtonFastTimeoutRef.current = setTimeout(() => {
|
||||
setShouldShowMainButton(true);
|
||||
}, 35);
|
||||
setShouldHideMainButton(false);
|
||||
mainButtonChangeTimeout.current = setTimeout(() => {
|
||||
mainButtonChangeTimeoutRef.current = setTimeout(() => {
|
||||
setShouldDecreaseWebFrameSize(true);
|
||||
}, MAIN_BUTTON_ANIMATION_TIME);
|
||||
}
|
||||
|
||||
if (!isMainButtonVisible) {
|
||||
setShouldShowMainButton(false);
|
||||
mainButtonChangeTimeout.current = setTimeout(() => {
|
||||
mainButtonChangeTimeoutRef.current = setTimeout(() => {
|
||||
setShouldHideMainButton(true);
|
||||
}, MAIN_BUTTON_ANIMATION_TIME);
|
||||
}
|
||||
}, [isMainButtonVisible]);
|
||||
|
||||
useEffect(() => {
|
||||
if (secondaryButtonChangeTimeout.current) clearTimeout(secondaryButtonChangeTimeout.current);
|
||||
if (secondaryButtonFastTimeout.current) clearTimeout(secondaryButtonFastTimeout.current);
|
||||
if (secondaryButtonChangeTimeoutRef.current) clearTimeout(secondaryButtonChangeTimeoutRef.current);
|
||||
if (secondaryButtonFastTimeoutRef.current) clearTimeout(secondaryButtonFastTimeoutRef.current);
|
||||
|
||||
if (isSecondaryButtonVisible) {
|
||||
secondaryButtonFastTimeout.current = setTimeout(() => {
|
||||
secondaryButtonFastTimeoutRef.current = setTimeout(() => {
|
||||
setShouldShowSecondaryButton(true);
|
||||
}, 35);
|
||||
setShouldHideSecondaryButton(false);
|
||||
secondaryButtonChangeTimeout.current = setTimeout(() => {
|
||||
secondaryButtonChangeTimeoutRef.current = setTimeout(() => {
|
||||
setShouldDecreaseWebFrameSize(true);
|
||||
}, MAIN_BUTTON_ANIMATION_TIME);
|
||||
}
|
||||
|
||||
if (!isSecondaryButtonVisible) {
|
||||
setShouldShowSecondaryButton(false);
|
||||
secondaryButtonChangeTimeout.current = setTimeout(() => {
|
||||
secondaryButtonChangeTimeoutRef.current = setTimeout(() => {
|
||||
setShouldHideSecondaryButton(true);
|
||||
}, MAIN_BUTTON_ANIMATION_TIME);
|
||||
}
|
||||
|
||||
@ -4,26 +4,26 @@ import useLastCallback from '../../../../hooks/useLastCallback';
|
||||
|
||||
export default function usePopupLimit(sequentialLimit: number, resetAfter: number) {
|
||||
const [unlockPopupsAt, setUnlockPopupsAt] = useState(0);
|
||||
const sequentialCalls = useRef(0);
|
||||
const lastClosedDate = useRef(0);
|
||||
const sequentialCallsRef = useRef(0);
|
||||
const lastClosedDateRef = useRef(0);
|
||||
|
||||
const handlePopupOpened = useLastCallback(() => {
|
||||
const now = Date.now();
|
||||
|
||||
if (now - lastClosedDate.current > resetAfter) {
|
||||
sequentialCalls.current = 0;
|
||||
if (now - lastClosedDateRef.current > resetAfter) {
|
||||
sequentialCallsRef.current = 0;
|
||||
}
|
||||
|
||||
sequentialCalls.current += 1;
|
||||
sequentialCallsRef.current += 1;
|
||||
|
||||
if (sequentialCalls.current >= sequentialLimit) {
|
||||
if (sequentialCallsRef.current >= sequentialLimit) {
|
||||
setUnlockPopupsAt(now + resetAfter);
|
||||
}
|
||||
});
|
||||
|
||||
const handlePopupClosed = useLastCallback(() => {
|
||||
if (unlockPopupsAt < Date.now()) { // Prevent confused user from extending lock time
|
||||
lastClosedDate.current = Date.now();
|
||||
lastClosedDateRef.current = Date.now();
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
@ -54,8 +54,8 @@ const useWebAppFrame = (
|
||||
updateContentSettings,
|
||||
} = getActions();
|
||||
|
||||
const isReloadSupported = useRef<boolean>(false);
|
||||
const reloadTimeout = useRef<ReturnType<typeof setTimeout>>();
|
||||
const isReloadSupportedRef = useRef<boolean>(false);
|
||||
const reloadTimeoutRef = useRef<ReturnType<typeof setTimeout>>();
|
||||
const ignoreEventsRef = useRef<boolean>(false);
|
||||
const lastFrameSizeRef = useRef<{ width: number; height: number; isResizing?: boolean }>();
|
||||
const windowSize = useWindowSize();
|
||||
@ -98,11 +98,11 @@ const useWebAppFrame = (
|
||||
});
|
||||
|
||||
const reloadFrame = useCallback((url: string) => {
|
||||
if (isReloadSupported.current) {
|
||||
if (isReloadSupportedRef.current) {
|
||||
sendEvent({
|
||||
eventType: 'reload_iframe',
|
||||
});
|
||||
reloadTimeout.current = setTimeout(() => {
|
||||
reloadTimeoutRef.current = setTimeout(() => {
|
||||
forceReloadFrame(url);
|
||||
}, RELOAD_TIMEOUT);
|
||||
return;
|
||||
@ -213,11 +213,11 @@ const useWebAppFrame = (
|
||||
if (eventType === 'iframe_ready') {
|
||||
const scrollbarColor = getComputedStyle(document.body).getPropertyValue('--color-scrollbar');
|
||||
sendCustomStyle(SCROLLBAR_STYLE.replace(/%SCROLLBAR_COLOR%/g, scrollbarColor));
|
||||
isReloadSupported.current = Boolean(eventData.reload_supported);
|
||||
isReloadSupportedRef.current = Boolean(eventData.reload_supported);
|
||||
}
|
||||
|
||||
if (eventType === 'iframe_will_reload') {
|
||||
clearTimeout(reloadTimeout.current);
|
||||
clearTimeout(reloadTimeoutRef.current);
|
||||
}
|
||||
|
||||
if (eventType === 'web_app_data_send') {
|
||||
|
||||
@ -79,7 +79,7 @@ const ConfirmPayment: FC<OwnProps> = ({
|
||||
src={url}
|
||||
title={lang('Checkout.WebConfirmation.Title')}
|
||||
allow="payment"
|
||||
sandbox="allow-modals allow-forms allow-scripts allow-same-origin allow-top-navigation"
|
||||
sandbox="allow-modals allow-forms allow-scripts allow-top-navigation"
|
||||
className="ConfirmPayment__content"
|
||||
/>
|
||||
</div>
|
||||
|
||||
@ -46,22 +46,22 @@ const StarGiftCollectionList = ({
|
||||
}
|
||||
});
|
||||
|
||||
if (!collections || collections.length === 0) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
const items: TabItem[] = useMemo(() => [
|
||||
{
|
||||
id: 'all',
|
||||
title: lang('AllGiftsCategory'),
|
||||
},
|
||||
...collections.map((collection) => ({
|
||||
...(collections || []).map((collection) => ({
|
||||
id: String(collection.collectionId),
|
||||
title: collection.title,
|
||||
sticker: collection.icon,
|
||||
})),
|
||||
], [collections, lang]);
|
||||
|
||||
if (!collections || collections.length === 0) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
const selectedItemId = activeCollectionId ? String(activeCollectionId) : 'all';
|
||||
|
||||
return (
|
||||
|
||||
@ -43,8 +43,10 @@ const JoinRequest: FC<OwnProps & StateProps> = ({
|
||||
const lang = useOldLang();
|
||||
|
||||
const fullName = getUserFullName(user);
|
||||
// eslint-disable-next-line @eslint-react/purity
|
||||
const fixedDate = (date - getServerTime()) * 1000 + Date.now();
|
||||
|
||||
// eslint-disable-next-line @eslint-react/purity
|
||||
const dateString = isToday(new Date(fixedDate))
|
||||
? formatTime(lang, fixedDate) : formatHumanDate(lang, fixedDate, true, false, true);
|
||||
|
||||
|
||||
@ -1,4 +1,3 @@
|
||||
import type { FC } from '../../../lib/teact/teact';
|
||||
import { memo, useEffect } from '../../../lib/teact/teact';
|
||||
import { getActions, withGlobal } from '../../../global';
|
||||
|
||||
@ -32,7 +31,7 @@ type StateProps = {
|
||||
|
||||
const BULLET = '\u2022';
|
||||
|
||||
const ManageInviteInfo: FC<OwnProps & StateProps> = ({
|
||||
const ManageInviteInfo = ({
|
||||
chatId,
|
||||
invite,
|
||||
importers,
|
||||
@ -40,7 +39,7 @@ const ManageInviteInfo: FC<OwnProps & StateProps> = ({
|
||||
isChannel,
|
||||
isActive,
|
||||
onClose,
|
||||
}) => {
|
||||
}: OwnProps & StateProps) => {
|
||||
const {
|
||||
loadChatInviteImporters,
|
||||
loadChatInviteRequesters,
|
||||
@ -51,6 +50,7 @@ const ManageInviteInfo: FC<OwnProps & StateProps> = ({
|
||||
const {
|
||||
usage = 0, usageLimit, link, adminId,
|
||||
} = invite || {};
|
||||
// eslint-disable-next-line @eslint-react/purity
|
||||
const expireDate = invite?.expireDate && (invite.expireDate - getServerTime()) * 1000 + Date.now();
|
||||
const isExpired = ((invite?.expireDate || 0) - getServerTime()) < 0;
|
||||
|
||||
|
||||
@ -183,7 +183,7 @@ const ManageInvites: FC<OwnProps & StateProps> = ({
|
||||
const {
|
||||
usage = 0, usageLimit, expireDate, isPermanent, requested, isRevoked,
|
||||
} = invite;
|
||||
let text = '';
|
||||
let text;
|
||||
if (!isRevoked && usageLimit && usage < usageLimit) {
|
||||
text = oldLang('CanJoin', usageLimit - usage);
|
||||
} else if (usage) {
|
||||
|
||||
@ -1,7 +1,4 @@
|
||||
import type { FC } from '../../../lib/teact/teact.ts';
|
||||
import type React from '../../../lib/teact/teact.ts';
|
||||
import { useState } from '../../../lib/teact/teact.ts';
|
||||
import { memo } from '../../../lib/teact/teact.ts';
|
||||
import { memo, useState } from '../../../lib/teact/teact';
|
||||
|
||||
import type { ApiChat } from '../../../api/types/index';
|
||||
import type { ManagementScreens } from '../../../types/index';
|
||||
@ -10,13 +7,13 @@ import { ChatCreationProgress } from '../../../types/index';
|
||||
import { getActions, withGlobal } from '../../../global/index';
|
||||
import { selectChat, selectTabState } from '../../../global/selectors/index';
|
||||
|
||||
import useHistoryBack from '../../../hooks/useHistoryBack.ts';
|
||||
import useLang from '../../../hooks/useLang.ts';
|
||||
import useLastCallback from '../../../hooks/useLastCallback.ts';
|
||||
import useHistoryBack from '../../../hooks/useHistoryBack';
|
||||
import useLang from '../../../hooks/useLang';
|
||||
import useLastCallback from '../../../hooks/useLastCallback';
|
||||
|
||||
import AvatarEditable from '../../ui/AvatarEditable.tsx';
|
||||
import FloatingActionButton from '../../ui/FloatingActionButton.tsx';
|
||||
import InputText from '../../ui/InputText.tsx';
|
||||
import AvatarEditable from '../../ui/AvatarEditable';
|
||||
import FloatingActionButton from '../../ui/FloatingActionButton';
|
||||
import InputText from '../../ui/InputText';
|
||||
|
||||
type OwnProps = {
|
||||
chatId: string;
|
||||
@ -31,13 +28,13 @@ type StateProps = {
|
||||
creationError?: string;
|
||||
};
|
||||
|
||||
const NewDiscussionGroup: FC<OwnProps & StateProps> = ({
|
||||
const NewDiscussionGroup = ({
|
||||
chat,
|
||||
onClose,
|
||||
isActive,
|
||||
creationProgress,
|
||||
creationError,
|
||||
}) => {
|
||||
}: OwnProps & StateProps) => {
|
||||
const { createChannel } = getActions();
|
||||
const lang = useLang();
|
||||
|
||||
|
||||
@ -65,8 +65,8 @@ function MessageStatistics({
|
||||
const lang = useOldLang();
|
||||
const containerRef = useRef<HTMLDivElement>();
|
||||
const [isReady, setIsReady] = useState(false);
|
||||
const loadedCharts = useRef<Set<string>>(new Set());
|
||||
const errorCharts = useRef<Set<string>>(new Set());
|
||||
const loadedChartsRef = useRef<Set<string>>(new Set());
|
||||
const errorChartsRef = useRef<Set<string>>(new Set());
|
||||
|
||||
const { loadMessageStatistics, loadMessagePublicForwards, loadStatisticsAsyncGraph } = getActions();
|
||||
const forceUpdate = useForceUpdate();
|
||||
@ -79,8 +79,8 @@ function MessageStatistics({
|
||||
|
||||
useEffect(() => {
|
||||
if (!isActive || messageId) {
|
||||
loadedCharts.current.clear();
|
||||
errorCharts.current.clear();
|
||||
loadedChartsRef.current.clear();
|
||||
errorChartsRef.current.clear();
|
||||
setIsReady(false);
|
||||
}
|
||||
}, [isActive, messageId]);
|
||||
@ -125,13 +125,13 @@ function MessageStatistics({
|
||||
const isAsync = graph.graphType === 'async';
|
||||
const isError = graph.graphType === 'error';
|
||||
|
||||
if (isAsync || loadedCharts.current.has(name)) {
|
||||
if (isAsync || loadedChartsRef.current.has(name)) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (isError) {
|
||||
loadedCharts.current.add(name);
|
||||
errorCharts.current.add(name);
|
||||
loadedChartsRef.current.add(name);
|
||||
errorChartsRef.current.add(name);
|
||||
|
||||
return;
|
||||
}
|
||||
@ -150,7 +150,7 @@ function MessageStatistics({
|
||||
},
|
||||
);
|
||||
|
||||
loadedCharts.current.add(name);
|
||||
loadedChartsRef.current.add(name);
|
||||
});
|
||||
|
||||
forceUpdate();
|
||||
@ -176,11 +176,11 @@ function MessageStatistics({
|
||||
>
|
||||
<StatisticsOverview statistics={statistics} type="message" title={lang('StatisticOverview')} />
|
||||
|
||||
{(!loadedCharts.current.size || !statistics.publicForwardsData) && <Loading />}
|
||||
{(!loadedChartsRef.current.size || !statistics.publicForwardsData) && <Loading />}
|
||||
|
||||
<div ref={containerRef}>
|
||||
{GRAPHS.map((graph) => {
|
||||
const isGraphReady = loadedCharts.current.has(graph) && !errorCharts.current.has(graph);
|
||||
const isGraphReady = loadedChartsRef.current.has(graph) && !errorChartsRef.current.has(graph);
|
||||
return (
|
||||
<div className={buildClassName(styles.graph, !isGraphReady && styles.hidden)} />
|
||||
);
|
||||
|
||||
@ -70,8 +70,8 @@ const MonetizationStatistics = ({
|
||||
|
||||
const containerRef = useRef<HTMLDivElement>();
|
||||
const [isReady, setIsReady] = useState(false);
|
||||
const loadedCharts = useRef<Set<string>>(new Set());
|
||||
const errorCharts = useRef<Set<string>>(new Set());
|
||||
const loadedChartsRef = useRef<Set<string>>(new Set());
|
||||
const errorChartsRef = useRef<Set<string>>(new Set());
|
||||
|
||||
const forceUpdate = useForceUpdate();
|
||||
const [isAboutMonetizationModalOpen, openAboutMonetizationModal, closeAboutMonetizationModal] = useFlag(false);
|
||||
@ -104,8 +104,8 @@ const MonetizationStatistics = ({
|
||||
});
|
||||
}
|
||||
|
||||
loadedCharts.current.clear();
|
||||
errorCharts.current.clear();
|
||||
loadedChartsRef.current.clear();
|
||||
errorChartsRef.current.clear();
|
||||
|
||||
if (!statistics || !containerRef.current) {
|
||||
return;
|
||||
@ -119,13 +119,13 @@ const MonetizationStatistics = ({
|
||||
const isAsync = graph.graphType === 'async';
|
||||
const isError = graph.graphType === 'error';
|
||||
|
||||
if (isAsync || loadedCharts.current.has(name)) {
|
||||
if (isAsync || loadedChartsRef.current.has(name)) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (isError) {
|
||||
loadedCharts.current.add(name);
|
||||
errorCharts.current.add(name);
|
||||
loadedChartsRef.current.add(name);
|
||||
errorChartsRef.current.add(name);
|
||||
|
||||
return;
|
||||
}
|
||||
@ -135,7 +135,7 @@ const MonetizationStatistics = ({
|
||||
...graph,
|
||||
});
|
||||
|
||||
loadedCharts.current.add(name);
|
||||
loadedChartsRef.current.add(name);
|
||||
|
||||
containerRef.current!.children[index].classList.remove(styles.hidden);
|
||||
});
|
||||
@ -243,7 +243,7 @@ const MonetizationStatistics = ({
|
||||
}
|
||||
/>
|
||||
|
||||
{!loadedCharts.current.size && <Loading />}
|
||||
{!loadedChartsRef.current.size && <Loading />}
|
||||
|
||||
<div ref={containerRef} className={styles.section}>
|
||||
{MONETIZATION_GRAPHS.filter(Boolean).map((graph) => (
|
||||
|
||||
@ -96,8 +96,8 @@ const Statistics = ({
|
||||
const lang = useOldLang();
|
||||
const containerRef = useRef<HTMLDivElement>();
|
||||
const [isReady, setIsReady] = useState(false);
|
||||
const loadedCharts = useRef<Set<string>>(new Set());
|
||||
const errorCharts = useRef<Set<string>>(new Set());
|
||||
const loadedChartsRef = useRef<Set<string>>(new Set());
|
||||
const errorChartsRef = useRef<Set<string>>(new Set());
|
||||
|
||||
const { loadStatistics, loadStatisticsAsyncGraph } = getActions();
|
||||
const forceUpdate = useForceUpdate();
|
||||
@ -161,13 +161,13 @@ const Statistics = ({
|
||||
const isAsync = graph.graphType === 'async';
|
||||
const isError = graph.graphType === 'error';
|
||||
|
||||
if (isAsync || loadedCharts.current.has(name)) {
|
||||
if (isAsync || loadedChartsRef.current.has(name)) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (isError) {
|
||||
loadedCharts.current.add(name);
|
||||
errorCharts.current.add(name);
|
||||
loadedChartsRef.current.add(name);
|
||||
errorChartsRef.current.add(name);
|
||||
|
||||
return;
|
||||
}
|
||||
@ -186,7 +186,7 @@ const Statistics = ({
|
||||
},
|
||||
);
|
||||
|
||||
loadedCharts.current.add(name);
|
||||
loadedChartsRef.current.add(name);
|
||||
|
||||
containerRef.current!.children[index].classList.remove(styles.hidden);
|
||||
});
|
||||
@ -207,11 +207,11 @@ const Statistics = ({
|
||||
/>
|
||||
)}
|
||||
|
||||
{!loadedCharts.current.size && <Loading />}
|
||||
{!loadedChartsRef.current.size && <Loading />}
|
||||
|
||||
<div ref={containerRef}>
|
||||
{graphs.map((graph) => {
|
||||
const isGraphReady = loadedCharts.current.has(graph) && !errorCharts.current.has(graph);
|
||||
const isGraphReady = loadedChartsRef.current.has(graph) && !errorChartsRef.current.has(graph);
|
||||
return (
|
||||
<div className={buildClassName(styles.graph, !isGraphReady && styles.hidden)} />
|
||||
);
|
||||
|
||||
@ -70,8 +70,8 @@ function StoryStatistics({
|
||||
const lang = useOldLang();
|
||||
const containerRef = useRef<HTMLDivElement>();
|
||||
const [isReady, setIsReady] = useState(false);
|
||||
const loadedCharts = useRef<Set<string>>(new Set());
|
||||
const errorCharts = useRef<Set<string>>(new Set());
|
||||
const loadedChartsRef = useRef<Set<string>>(new Set());
|
||||
const errorChartsRef = useRef<Set<string>>(new Set());
|
||||
|
||||
const { loadStoryStatistics, loadStoryPublicForwards, loadStatisticsAsyncGraph } = getActions();
|
||||
const forceUpdate = useForceUpdate();
|
||||
@ -84,8 +84,8 @@ function StoryStatistics({
|
||||
|
||||
useEffect(() => {
|
||||
if (!isActive || storyId) {
|
||||
loadedCharts.current.clear();
|
||||
errorCharts.current.clear();
|
||||
loadedChartsRef.current.clear();
|
||||
errorChartsRef.current.clear();
|
||||
setIsReady(false);
|
||||
}
|
||||
}, [isActive, storyId]);
|
||||
@ -130,13 +130,13 @@ function StoryStatistics({
|
||||
const isAsync = graph.graphType === 'async';
|
||||
const isError = graph.graphType === 'error';
|
||||
|
||||
if (isAsync || loadedCharts.current.has(name)) {
|
||||
if (isAsync || loadedChartsRef.current.has(name)) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (isError) {
|
||||
loadedCharts.current.add(name);
|
||||
errorCharts.current.add(name);
|
||||
loadedChartsRef.current.add(name);
|
||||
errorChartsRef.current.add(name);
|
||||
|
||||
return;
|
||||
}
|
||||
@ -155,7 +155,7 @@ function StoryStatistics({
|
||||
},
|
||||
);
|
||||
|
||||
loadedCharts.current.add(name);
|
||||
loadedChartsRef.current.add(name);
|
||||
});
|
||||
|
||||
forceUpdate();
|
||||
@ -181,11 +181,11 @@ function StoryStatistics({
|
||||
>
|
||||
<StatisticsOverview statistics={statistics} type="story" title={lang('StatisticOverview')} />
|
||||
|
||||
{!loadedCharts.current.size && <Loading />}
|
||||
{!loadedChartsRef.current.size && <Loading />}
|
||||
|
||||
<div ref={containerRef}>
|
||||
{GRAPHS.map((graph) => {
|
||||
const isGraphReady = loadedCharts.current.has(graph) && !errorCharts.current.has(graph);
|
||||
const isGraphReady = loadedChartsRef.current.has(graph) && !errorChartsRef.current.has(graph);
|
||||
return (
|
||||
<div className={buildClassName(styles.graph, !isGraphReady && styles.hidden)} />
|
||||
);
|
||||
|
||||
@ -47,21 +47,21 @@ const StoryAlbumList = ({
|
||||
}
|
||||
});
|
||||
|
||||
if (!albums?.length) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
const items: TabItem[] = useMemo(() => [
|
||||
{
|
||||
id: 'all',
|
||||
title: lang('AllStoriesCategory'),
|
||||
},
|
||||
...albums.map((album) => ({
|
||||
...(albums || []).map((album) => ({
|
||||
id: String(album.albumId),
|
||||
title: album.title,
|
||||
})),
|
||||
], [albums, lang]);
|
||||
|
||||
if (!albums?.length) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
const selectedItemId = selectedAlbumId ? String(selectedAlbumId) : 'all';
|
||||
|
||||
return (
|
||||
|
||||
@ -1,3 +1,4 @@
|
||||
/* eslint-disable @eslint-react/purity */
|
||||
import type { FC } from '../../lib/teact/teact';
|
||||
import { withGlobal } from '../../global';
|
||||
|
||||
|
||||
@ -1,3 +1,4 @@
|
||||
/* eslint-disable @eslint-react/purity */
|
||||
import type { FC } from '../../lib/teact/teact';
|
||||
import { useState } from '../../lib/teact/teact';
|
||||
|
||||
|
||||
@ -1,3 +1,4 @@
|
||||
/* eslint-disable @eslint-react/purity */
|
||||
import { useState } from '../../lib/teact/teact';
|
||||
import { withGlobal } from '../../global';
|
||||
|
||||
|
||||
@ -1,3 +1,4 @@
|
||||
/* eslint-disable @eslint-react/purity */
|
||||
import {
|
||||
createContext, memo, useState,
|
||||
} from '../../lib/teact/teact';
|
||||
|
||||
@ -1,3 +1,4 @@
|
||||
/* eslint-disable @eslint-react/purity */
|
||||
import { useRef, useState } from '../../lib/teact/teact';
|
||||
|
||||
export function App() {
|
||||
|
||||
@ -1,3 +1,4 @@
|
||||
/* eslint-disable @eslint-react/purity */
|
||||
import type { FC } from '../../lib/teact/teact';
|
||||
import { getGlobal, setGlobal, withGlobal } from '../../global';
|
||||
|
||||
|
||||
@ -1,3 +1,4 @@
|
||||
/* eslint-disable @eslint-react/purity */
|
||||
import type { ChangeEvent } from 'react';
|
||||
import { useRef, useState } from '../../lib/teact/teact';
|
||||
|
||||
@ -93,7 +94,7 @@ function MyComponent({
|
||||
onChange: (newValue: string) => void;
|
||||
onDelete: NoneToVoidFunction;
|
||||
}) {
|
||||
const id = useRef(String(Math.random()).slice(-3));
|
||||
const idRef = useRef(String(Math.random()).slice(-3));
|
||||
|
||||
const handleChange = (e: ChangeEvent<HTMLInputElement>) => {
|
||||
onChange(e.target.value);
|
||||
@ -103,7 +104,7 @@ function MyComponent({
|
||||
<li>
|
||||
<input type="text" value={value} size={3} onChange={handleChange} />
|
||||
<input type="button" value="x" onClick={onDelete} />
|
||||
{id.current}
|
||||
{idRef.current}
|
||||
</li>
|
||||
);
|
||||
}
|
||||
|
||||
@ -1,3 +1,4 @@
|
||||
/* eslint-disable @eslint-react/purity */
|
||||
import { useState } from '../../lib/teact/teact';
|
||||
|
||||
import Portal from '../ui/Portal';
|
||||
|
||||
@ -136,7 +136,6 @@ const Checkbox: FC<OwnProps> = ({
|
||||
|
||||
return (
|
||||
<>
|
||||
{/* eslint-disable-next-line jsx-a11y/no-noninteractive-element-interactions */}
|
||||
<label
|
||||
className={labelClassName}
|
||||
dir={lang.isRtl ? 'rtl' : undefined}
|
||||
|
||||
@ -35,9 +35,9 @@ const ImageCropper = ({
|
||||
}: OwnProps) => {
|
||||
const [imagePosition, setImagePosition] = useState({ x: 0, y: 0 });
|
||||
const [zoom, setZoom] = useState(MIN_ZOOM);
|
||||
const isDragging = useRef(false);
|
||||
const lastMousePosition = useRef({ x: 0, y: 0 });
|
||||
const lastImagePosition = useRef({ x: 0, y: 0 });
|
||||
const isDraggingRef = useRef(false);
|
||||
const lastMousePositionRef = useRef({ x: 0, y: 0 });
|
||||
const lastImagePositionRef = useRef({ x: 0, y: 0 });
|
||||
|
||||
const { width: windowWidth } = useWindowSize();
|
||||
const previewContainerSize = Math.min(PREVIEW_SIZE, windowWidth - MODAL_INLINE_PADDING * 2);
|
||||
@ -71,9 +71,9 @@ const ImageCropper = ({
|
||||
};
|
||||
|
||||
const startDrag = (e: any) => {
|
||||
isDragging.current = true;
|
||||
lastMousePosition.current = getPointerPosition(e);
|
||||
lastImagePosition.current = { ...imagePosition };
|
||||
isDraggingRef.current = true;
|
||||
lastMousePositionRef.current = getPointerPosition(e);
|
||||
lastImagePositionRef.current = { ...imagePosition };
|
||||
document.addEventListener('mousemove', moveDrag);
|
||||
document.addEventListener('touchmove', moveDrag);
|
||||
document.addEventListener('mouseup', endDrag);
|
||||
@ -81,19 +81,19 @@ const ImageCropper = ({
|
||||
};
|
||||
const moveDrag = useLastCallback((e: any) => {
|
||||
if ('touches' in e && e.touches.length > 1) return;
|
||||
if (!isDragging.current) return;
|
||||
if (!isDraggingRef.current) return;
|
||||
const { x: mouseX, y: mouseY } = getPointerPosition(e);
|
||||
const deltaX = mouseX - lastMousePosition.current.x;
|
||||
const deltaY = mouseY - lastMousePosition.current.y;
|
||||
const deltaX = mouseX - lastMousePositionRef.current.x;
|
||||
const deltaY = mouseY - lastMousePositionRef.current.y;
|
||||
const newPosition = clampPosition(
|
||||
lastImagePosition.current.x + deltaX,
|
||||
lastImagePosition.current.y + deltaY,
|
||||
lastImagePositionRef.current.x + deltaX,
|
||||
lastImagePositionRef.current.y + deltaY,
|
||||
previewImageSize,
|
||||
);
|
||||
setImagePosition(newPosition);
|
||||
});
|
||||
const endDrag = useLastCallback(() => {
|
||||
isDragging.current = false;
|
||||
isDraggingRef.current = false;
|
||||
document.removeEventListener('mousemove', moveDrag);
|
||||
document.removeEventListener('touchmove', moveDrag);
|
||||
document.removeEventListener('mouseup', endDrag);
|
||||
|
||||
@ -1,9 +1,5 @@
|
||||
import type {
|
||||
ElementRef } from '../../lib/teact/teact';
|
||||
import type React from '../../lib/teact/teact';
|
||||
import {
|
||||
beginHeavyAnimation,
|
||||
type FC, memo, useEffect, useRef,
|
||||
beginHeavyAnimation, type ElementRef, memo, useEffect, useRef,
|
||||
} from '../../lib/teact/teact';
|
||||
|
||||
import type { MenuPositionOptions } from '../../hooks/useMenuPosition';
|
||||
@ -56,7 +52,7 @@ type OwnProps =
|
||||
|
||||
const ANIMATION_DURATION = 200;
|
||||
|
||||
const Menu: FC<OwnProps> = ({
|
||||
const Menu = ({
|
||||
ref: externalRef,
|
||||
shouldCloseFast,
|
||||
isOpen,
|
||||
@ -78,7 +74,7 @@ const Menu: FC<OwnProps> = ({
|
||||
onMouseEnterBackdrop,
|
||||
nested,
|
||||
...positionOptions
|
||||
}) => {
|
||||
}: OwnProps) => {
|
||||
const { isTouchScreen } = useAppLayout();
|
||||
|
||||
const containerRef = useRef<HTMLDivElement>();
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Loading…
x
Reference in New Issue
Block a user