Bump dependencies

This commit is contained in:
Alexander Zinchuk 2025-06-04 20:36:48 +02:00
parent daea14f37c
commit f38f7d65cc
884 changed files with 19367 additions and 23494 deletions

View File

@ -1,22 +0,0 @@
src/lib/rlottie/rlottie-wasm.js
src/lib/video-preview/polyfill
src/lib/fasttextweb/fasttext-wasm.js
src/lib/gramjs/tl/types-generator/template.ts
src/lib/gramjs/tl/api.d.ts
src/lib/gramjs/tl/apiTl.ts
src/lib/gramjs/tl/schemaTl.ts
src/lib/lovely-chart
src/lib/music-metadata-browser
jest.config.js
src/lib/secret-sauce/
playwright.config.ts
dist
dist-electron
public
deploy/update_version.js

212
.eslintrc
View File

@ -1,212 +0,0 @@
{
"extends": [
"teact-app",
"airbnb",
"airbnb-typescript"
],
"plugins": [
"no-async-without-await",
"no-null",
"simple-import-sort",
"react-hooks-static-deps",
"eslint-multitab-tt"
],
"rules": {
"eslint-multitab-tt/no-immediate-global": "error",
"eslint-multitab-tt/must-update-global-after-await": "off",
"eslint-multitab-tt/set-global-only-variable": "error",
"eslint-multitab-tt/no-getactions-in-actions": "error",
"eslint-multitab-tt/must-specify-action-handler-return-type": "error",
"indent": [
"error",
2,
{
"SwitchCase": 1
}
],
"max-len": [
"error",
{
"code": 120,
"ignoreComments": true,
"ignorePattern": "\\sd=\".+\"" // Ignore lines with "d" attribute
}
],
"array-bracket-newline": [
2,
"consistent"
],
"no-null/no-null": 2,
"no-console": "error",
"semi": "error",
"no-implicit-coercion": "error",
"react-hooks/exhaustive-deps": "off",
"react-hooks-static-deps/exhaustive-deps": [
"error",
{
"additionalHooks": "(useSyncEffect|useAsync|useDebouncedCallback|useThrottledCallback|useEffectWithPrevDeps|useLayoutEffectWithPrevDeps|useDerivedState|useDerivedSignal|useThrottledResolver|useDebouncedResolver|useThrottleForHeavyAnimation)$",
"staticHooks": {
"getActions": true,
"useFlag": [false, true, true],
"useForceUpdate": true,
"useReducer": [false, true],
"useLastCallback": true
}
}
],
"arrow-body-style": "off",
"no-else-return": "off",
"no-plusplus": "off",
"no-void": "off",
"no-continue": "off",
"default-case": "off",
"no-param-reassign": "off",
"no-prototype-builtins": "off",
"no-await-in-loop": "off",
"no-nested-ternary": "off",
"function-paren-newline": [
"error",
"consistent"
],
"prefer-destructuring": "off",
// Allow for...of. Edited from:
// https://github.com/airbnb/javascript/blob/b4377fb03089dd7f08955242695860d47f9caab4/packages/eslint-config-airbnb-base/rules/style.js#L333
"no-restricted-syntax": [
"error",
{
"selector": "ForInStatement",
"message": "for..in loops iterate over the entire prototype chain, which is virtually never what you want. Use Object.{keys,values,entries}, and iterate over the resulting array."
},
{
"selector": "LabeledStatement",
"message": "Labels are a form of GOTO; using them makes code confusing and hard to maintain and understand."
},
{
"selector": "WithStatement",
"message": "`with` is disallowed in strict mode because it makes code impossible to predict and optimize."
}
],
"import/no-extraneous-dependencies": "off",
"import/prefer-default-export": "off",
"import/named": "off",
"import/no-webpack-loader-syntax": "off",
"import/no-cycle": [
"error",
{
"allowUnsafeDynamicCyclicDependency": true // TODO: Fix this
}
],
"react/prop-types": "off",
"react/jsx-one-expression-per-line": "off",
"react/button-has-type": "off",
"react/require-default-props": "off",
"react/function-component-definition": "off",
// Teact feature
"react/style-prop-object": "off",
"react/no-unknown-property": "off",
"react/jsx-no-bind": [
"error",
{
"ignoreRefs": true,
"allowArrowFunctions": false,
"allowFunctions": false,
"allowBind": false,
"ignoreDOMComponents": true
}
],
"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",
"no-async-without-await/no-async-without-await": 1,
"@typescript-eslint/no-use-before-define": [
"error",
{
"functions": false
}
],
"@typescript-eslint/camelcase": "off",
"@typescript-eslint/member-delimiter-style": "error",
"@typescript-eslint/default-param-last": "off",
"@typescript-eslint/return-await": [
"error",
"in-try-catch"
],
"@typescript-eslint/consistent-type-imports": [
"error",
{
"prefer": "type-imports",
"disallowTypeAnnotations": false
}
],
"simple-import-sort/imports": [
"error",
{
"groups": [
// Side effect imports
["^\\u0000"],
// Lib and global imports
[
"^react",
"^@?\\w",
"dist(/.*|$)",
"^(\\.+/)+(lib/(teact|gramjs))(/.*|$)",
"^(\\.+/)+global$"
],
// Type imports
[
"^(\\.+/)+.+\\u0000$",
"^(\\.+/|\\w+/)+(types)(/.*|$)",
"^(\\.+/|\\w+/)+(types)\\u0000"
],
// Config, utils, helpers
[
"^(\\.+/)+config",
"^(\\.+/)+(lib)(?!/(gramjs|teact))(/.*|$)",
"^(\\.+/)+global/.+",
"^(\\.+/)+(util)(/.*|$)",
"^\\.\\.(?!/?$)",
"^\\.\\./?$",
"^\\./(?=.*/)(?!/?$)",
"^\\.(?!/?$)",
"^\\./?$"
],
// Hooks
[
".+(/hooks/)(.*|$)"
],
// Components
[
"\/[A-Z](([a-z]+[A-Z]?)*)"
],
// Styles and CSS modules
[
"^.+\\.s?css$"
],
// Assets: images, stickers, etc
[
"^(\\.+/)+(assets)(/.*|$)"
]
]
}
]
},
"settings": {
"import/resolver": {
"webpack": {
"extensions": [".js", ".jsx", ".ts", ".tsx"]
}
},
"import/core-modules": [
"fs",
"path",
"crypto"
]
},
"parserOptions": {
"project": "./tsconfig.json"
}
}

View File

@ -1,4 +1,4 @@
module.exports = { export default {
inputDir: './src/assets/font-icons', inputDir: './src/assets/font-icons',
outputDir: './src/styles', outputDir: './src/styles',
name: 'icons', name: 'icons',
@ -7,7 +7,7 @@ module.exports = {
tag: '', tag: '',
// Use a custom Handlebars template // Use a custom Handlebars template
templates: { templates: {
scss: './dev/icons.scss.hbs' scss: './dev/icons.scss.hbs',
}, },
formatOptions: { formatOptions: {
ts: { ts: {

View File

@ -11,7 +11,8 @@ name: Package and publish
on: on:
workflow_dispatch: workflow_dispatch:
push: push:
branches: master branches:
- master
env: env:
APP_NAME: Telegram A APP_NAME: Telegram A
@ -67,6 +68,7 @@ jobs:
TELEGRAM_API_HASH: ${{ secrets.TELEGRAM_API_HASH }} TELEGRAM_API_HASH: ${{ secrets.TELEGRAM_API_HASH }}
APPLE_ID: ${{ secrets.APPLE_ID }} APPLE_ID: ${{ secrets.APPLE_ID }}
APPLE_TEAM_ID: ${{ secrets.APPLE_TEAM_ID }}
APPLE_APP_SPECIFIC_PASSWORD: ${{ secrets.APPLE_APP_SPECIFIC_PASSWORD }} APPLE_APP_SPECIFIC_PASSWORD: ${{ secrets.APPLE_APP_SPECIFIC_PASSWORD }}
GH_TOKEN: ${{ secrets.GH_TOKEN }} GH_TOKEN: ${{ secrets.GH_TOKEN }}

1
.node-version Normal file
View File

@ -0,0 +1 @@
24

1
.npmrc Normal file
View File

@ -0,0 +1 @@
node-options="--no-experimental-strip-types"

View File

@ -13,7 +13,7 @@
"stylelint-high-performance-animation", "stylelint-high-performance-animation",
"stylelint-group-selectors", "stylelint-group-selectors",
"stylelint-selector-tag-no-without-class", "stylelint-selector-tag-no-without-class",
"./dev/wholePixel.js" "./dev/wholePixel.mjs"
], ],
"rules": { "rules": {
"property-no-unknown": [ "property-no-unknown": [

View File

@ -1,22 +1,21 @@
const isTest = process.env.APP_ENV === 'test'; export default function (api) {
const isMocked = Boolean(process.env.APP_MOCKED_CLIENT); api.cache(true);
module.exports = { const isTest = process.env.APP_ENV === 'test';
presets: [ const isMocked = Boolean(process.env.APP_MOCKED_CLIENT);
[
'@babel/typescript', const presets = [
], '@babel/typescript',
[ '@babel/preset-env',
'@babel/preset-env', '@babel/preset-react',
], ];
[
'@babel/preset-react', const plugins = [
],
],
plugins: [
'@babel/plugin-transform-class-properties',
'@babel/plugin-syntax-nullish-coalescing-operator',
'@babel/plugin-transform-logical-assignment-operators',
...(isTest && !isMocked ? ['babel-plugin-transform-import-meta'] : []), ...(isTest && !isMocked ? ['babel-plugin-transform-import-meta'] : []),
], ];
};
return {
presets,
plugins,
};
}

View File

@ -1,6 +1,4 @@
/* eslint-env node */ export default async ({ github, context, body }) => {
module.exports = async ({ github, context, body }) => {
await github.rest.issues.createComment({ await github.rest.issues.createComment({
issue_number: context.issue.number, issue_number: context.issue.number,
owner: context.repo.owner, owner: context.repo.owner,

View File

@ -1,11 +1,10 @@
/* eslint-env node */ import { readFileSync } from 'fs';
const fs = require('fs'); import template from '../.github/workflows/statoscope-comment.js';
const createPRComment = require('./createPRComment'); import createPRComment from './createPRComment.js';
const template = require('../.github/workflows/statoscope-comment');
module.exports = async ({ github, context }) => { export default async ({ github, context }) => {
const data = JSON.parse(fs.readFileSync('result.json', 'utf8')); const data = JSON.parse(readFileSync('result.json', 'utf8'));
data.prNumber = context.issue.number; data.prNumber = context.issue.number;
const body = template(data); const body = template(data);

View File

@ -1,19 +0,0 @@
"use strict";
module.exports = {
root: true,
extends: [
"eslint:recommended",
"plugin:eslint-plugin/recommended",
"plugin:node/recommended",
],
env: {
node: true,
},
overrides: [
{
files: ["tests/**/*.js"],
env: { mocha: true },
},
],
};

View File

@ -1,22 +0,0 @@
/**
* @fileoverview eslint-multitab-tt
* @author undrfined
*/
"use strict";
//------------------------------------------------------------------------------
// Requirements
//------------------------------------------------------------------------------
const requireIndex = require("requireindex");
//------------------------------------------------------------------------------
// Plugin Definition
//------------------------------------------------------------------------------
// import all rules in lib/rules
module.exports.rules = requireIndex(__dirname + "/rules");

View File

@ -1,39 +0,0 @@
/**
* @fileoverview Must specify action handler return type
* @author undrfined
*/
"use strict";
//------------------------------------------------------------------------------
// Rule Definition
//------------------------------------------------------------------------------
/** @type {import('eslint').Rule.RuleModule} */
module.exports = {
meta: {
type: "problem",
docs: {
description: "Must specify action handler return type",
recommended: false,
url: null,
},
fixable: null,
schema: [],
messages: {
mustSpecifyActionHandlerReturnType: "Must specify action handler return type",
}
},
create(context) {
return {
ArrowFunctionExpression: (node) => {
if(node.parent.type === "CallExpression" && node.parent.callee.name === 'addActionHandler' && !node.returnType) {
context.report({
node,
messageId: "mustSpecifyActionHandlerReturnType",
})
}
}
};
},
};

View File

@ -1,108 +0,0 @@
/**
* @fileoverview Must update global after await
* @author undrfined
*/
"use strict";
//------------------------------------------------------------------------------
// Rule Definition
//------------------------------------------------------------------------------
/** @type {import('eslint').Rule.RuleModule} */
// TODO This rule is not working properly
module.exports = {
meta: {
type: "problem",
docs: {
description: "Must update global after await",
recommended: false,
url: null,
},
fixable: null,
schema: [],
messages: {
mustUpdateGlobalAfterAwait: "Global is outdated because of await here -> {{before}}, use global = getGlobal() to update",
}
},
create(context) {
let hasAssignmentOnBlockLevel;
let blocks = 0;
let d;
let hasAwait = false;
let hasAwaitOnBlockLevel;
let assigned;
//----------------------------------------------------------------------
// Helpers
//----------------------------------------------------------------------
function endFunction() {
hasAwait = false;
assigned = undefined;
d = undefined;
hasAssignmentOnBlockLevel = undefined;
hasAwaitOnBlockLevel = undefined;
}
//----------------------------------------------------------------------
// Public
//----------------------------------------------------------------------
return {
'FunctionDeclaration:exit': endFunction,
'FunctionExpression:exit': endFunction,
'ArrowFunctionExpression:exit': endFunction,
'AwaitExpression:exit': (node) => {
if(!node) return;
hasAwait = true;
hasAwaitOnBlockLevel = blocks;
d = node;
},
'BlockStatement': () => {
blocks += 1;
},
'BlockStatement:exit': () => {
blocks -= 1;
if(hasAwaitOnBlockLevel && blocks === hasAwaitOnBlockLevel) {
hasAwaitOnBlockLevel = undefined;
}
},
'ReturnStatement:exit': (node) => {
if(hasAwait && hasAwaitOnBlockLevel && blocks === hasAwaitOnBlockLevel && node.parent.type === 'BlockExpression') {
endFunction();
}
},
'AssignmentExpression': (node) => {
if(node.left.type !== "Identifier" || node.left.name !== "global") return;
if(node.right.type !== "CallExpression" || node.right.callee.name !== "getGlobal") return;
if(hasAwaitOnBlockLevel && blocks === hasAwaitOnBlockLevel) {
hasAwait = false;
hasAwaitOnBlockLevel = undefined;
d = undefined;
} else {
hasAssignmentOnBlockLevel = blocks;
assigned = node;
}
},
Identifier: (node) => {
if(node.name !== "global") return;
if(node.parent === assigned) return;
if(hasAwait) {
if(hasAssignmentOnBlockLevel !== undefined && hasAssignmentOnBlockLevel <= blocks) {
endFunction();
return;
}
context.report({
node,
messageId: "mustUpdateGlobalAfterAwait",
data: {
before: d ? d.loc.start.line + ':' + d.loc.start.column : 'unknown'
},
})
}
},
"Program:exit": endFunction,
};
},
};

View File

@ -1,40 +0,0 @@
/**
* @fileoverview Forbid usage of getActions in actions
* @author undrfined
*/
"use strict";
//------------------------------------------------------------------------------
// Rule Definition
//------------------------------------------------------------------------------
/** @type {import('eslint').Rule.RuleModule} */
module.exports = {
meta: {
type: "problem",
docs: {
description: "Forbid usage of getActions in action handlers",
recommended: false,
url: null,
},
fixable: null,
schema: [],
messages: {
noGetActionsInActions: "Do not use getActions inside action handlers, instead use the second argument of the action handler",
}
},
create(context) {
return {
CallExpression: (node) => {
if(!context.getPhysicalFilename().substring(context.getCwd().length).startsWith('/src/global')) return;
if(node.callee.name === 'getActions') {
context.report({
node,
messageId: 'noGetActionsInActions',
})
}
}
};
},
};

View File

@ -1,42 +0,0 @@
/**
* @fileoverview No immediate global
* @author undrfined
*/
"use strict";
//------------------------------------------------------------------------------
// Rule Definition
//------------------------------------------------------------------------------
/** @type {import('eslint').Rule.RuleModule} */
module.exports = {
meta: {
type: "problem",
docs: {
description: "No immediate global",
recommended: false,
url: null,
},
fixable: null,
schema: [],
messages: {
noImmediateGlobal: "Only use getGlobal() to assign to global variable",
}
},
create(context) {
return {
CallExpression: (node) => {
if(!context.getPhysicalFilename().substring(context.getCwd().length).startsWith('/src/global')) return;
if(node.callee.name === 'getGlobal'
&& node.parent.type !== 'AssignmentExpression'
) {
context.report({
node,
messageId: "noImmediateGlobal",
})
}
}
};
},
};

View File

@ -1,53 +0,0 @@
/**
* @fileoverview setGlobal must only be used with 'global' variable
* @author undrfined
*/
"use strict";
//------------------------------------------------------------------------------
// Rule Definition
//------------------------------------------------------------------------------
/** @type {import('eslint').Rule.RuleModule} */
module.exports = {
meta: {
type: "problem",
docs: {
description: "setGlobal must only be used with 'global' variable",
recommended: false,
url: null,
},
fixable: null,
schema: [],
hasSuggestions: true,
messages: {
setGlobalOnlyVariable: "setGlobal must only be used with 'global' variable",
}
},
create(context) {
return {
CallExpression: (node) => {
if(node.callee.name === 'setGlobal') {
if(node.arguments[0] && node.arguments[0].type !== 'Identifier' || node.arguments[0].name !== 'global') {
context.report({
node,
messageId: 'setGlobalOnlyVariable',
...(node.parent.type === 'ExpressionStatement' && {
suggest: [{
desc: "Move the global assignment before the setGlobal call",
*fix(fixer) {
const sc = context.getSourceCode();
const parent = node.parent;
yield fixer.insertTextBefore(parent, 'global = ' + sc.getText(node.arguments[0]) + ';\n');
yield fixer.replaceText(node.arguments[0], 'global');
},
}]
}),
})
}
}
}
};
},
};

File diff suppressed because it is too large Load Diff

View File

@ -1,39 +0,0 @@
{
"name": "eslint-plugin-eslint-multitab-tt",
"version": "0.0.0",
"description": "eslint-multitab-tt",
"keywords": [
"eslint",
"eslintplugin",
"eslint-plugin"
],
"author": "undrfined",
"main": "./lib/index.js",
"exports": "./lib/index.js",
"scripts": {
"lint": "npm-run-all \"lint:*\"",
"lint:eslint-docs": "npm-run-all \"update:eslint-docs -- --check\"",
"lint:js": "eslint .",
"test": "mocha tests --recursive",
"update:eslint-docs": "eslint-doc-generator"
},
"dependencies": {
"requireindex": "^1.2.0"
},
"devDependencies": {
"chalk": "^5.3.0",
"eslint": "^8.57.0",
"eslint-doc-generator": "^1.7.0",
"eslint-plugin-eslint-plugin": "^6.1.0",
"eslint-plugin-node": "^11.1.0",
"mocha": "^10.4.0",
"npm-run-all": "^4.1.5"
},
"engines": {
"node": "^20 || ^22"
},
"peerDependencies": {
"eslint": ">=8"
},
"license": "ISC"
}

View File

@ -67,5 +67,5 @@ function formatKeyWithVariables(isPlural: boolean, keysWithVars: Record<string,
const typeName = isPlural ? 'LangPairPlural' : 'LangPair'; const typeName = isPlural ? 'LangPairPlural' : 'LangPair';
return `export interface ${typeName} {\n${entries}}\n return `export interface ${typeName} {\n${entries}}\n
export interface ${typeName}WithVariables<V extends unknown = LangVariable> {\n${variableEntries}}\n`; export interface ${typeName}WithVariables<V = LangVariable> {\n${variableEntries}}\n`;
} }

View File

@ -1,11 +1,12 @@
/* eslint-disable max-len */ /* eslint-disable @stylistic/max-len */
const Telegraph = require('telegraph-node'); import gitlog from 'gitlog';
const { JSDOM } = require('jsdom'); import { JSDOM } from 'jsdom';
const { gitlogPromise } = require('gitlog'); import Telegraph from 'telegraph-node';
import packageJson from '../package.json' with { type: 'json' };
// CONSTANTS // CONSTANTS
const AUTH_TOKEN = process.env.TELEGRAPH_TOKEN; const AUTH_TOKEN = process.env.TELEGRAPH_TOKEN;
const version = require('../package.json').version;
const gitOptions = { const gitOptions = {
repo: '.', repo: '.',
@ -17,7 +18,7 @@ const gitOptions = {
const pageTemplate = ` const pageTemplate = `
<body>\ <body>\
<aside><img src="https://tga.dev/icon-dev-512x512.png" /></aside> <aside><img src="https://tga.dev/icon-dev-512x512.png" /></aside>
<h3>Commits since ${version}</h3>\ <h3>Commits since ${packageJson.version}</h3>\
<p><i>This list is automatically updated when a new commit pushed to the beta repo</i></p>\ <p><i>This list is automatically updated when a new commit pushed to the beta repo</i></p>\
<ul id="list"></ul>\ <ul id="list"></ul>\
<aside><a href="https://t.me/webatalks">Web A Discussion</a> <b>|</b> <a href="https://t.me/webatalksru">Web A Обсуждение</a> <b>|</b> <a href="https://t.me/webatalksuk">Web A Обговорення</a></aside>\ <aside><a href="https://t.me/webatalks">Web A Discussion</a> <b>|</b> <a href="https://t.me/webatalksru">Web A Обсуждение</a> <b>|</b> <a href="https://t.me/webatalksuk">Web A Обговорення</a></aside>\
@ -51,7 +52,7 @@ async function updateTelegraph(dom) {
async function preparePage() { async function preparePage() {
const dom = new JSDOM(pageTemplate); const dom = new JSDOM(pageTemplate);
const commits = await getCommitsSince(version); const commits = await getCommitsSince(packageJson.version);
commits.forEach((commit) => ( commits.forEach((commit) => (
dom.window.document.getElementById('list').appendChild(renderCommit(dom, commit)) dom.window.document.getElementById('list').appendChild(renderCommit(dom, commit))
@ -59,7 +60,7 @@ async function preparePage() {
if (!commits?.length) { if (!commits?.length) {
const li = dom.window.document.createElement('li'); const li = dom.window.document.createElement('li');
li.innerHTML = `<p>Nothing changed since ${version}</p>`; li.innerHTML = `<p>Nothing changed since ${packageJson.version}</p>`;
dom.window.document.getElementById('list').appendChild(li); dom.window.document.getElementById('list').appendChild(li);
} }
@ -74,7 +75,7 @@ function renderCommit(dom, commit) {
} }
function getCommits() { function getCommits() {
return gitlogPromise(gitOptions).then((log) => { return gitlog(gitOptions).then((log) => {
return log.map((commit) => { return log.map((commit) => {
return { return {
hash: commit.hash, hash: commit.hash,

View File

@ -1,10 +1,10 @@
const fs = require('fs'); import { readFileSync, writeFileSync } from 'fs';
const path = require('path'); import { resolve } from 'path';
const API_TL_PATH = path.resolve('./src/lib/gramjs/tl/static/api.tl'); const API_TL_PATH = resolve('./src/lib/gramjs/tl/static/api.tl');
function rehash() { function rehash() {
const data = fs.readFileSync(API_TL_PATH, 'utf8'); const data = readFileSync(API_TL_PATH, 'utf8');
const lines = data.split('\n'); const lines = data.split('\n');
// eslint-disable-next-line no-console // eslint-disable-next-line no-console
@ -30,7 +30,7 @@ function rehash() {
// eslint-disable-next-line no-console // eslint-disable-next-line no-console
console.log(`Writing ${resultLines.length} lines`); console.log(`Writing ${resultLines.length} lines`);
fs.writeFileSync(API_TL_PATH, resultLines.join('\n')); writeFileSync(API_TL_PATH, resultLines.join('\n'));
} }
function getHash(line) { function getHash(line) {

View File

@ -1,6 +1,5 @@
const stylelint = require('stylelint'); import { list } from 'postcss';
// eslint-disable-next-line import/no-unresolved -- TS BUG import stylelint from 'stylelint';
const postcss = require('postcss');
const ruleName = 'plugin/whole-pixel'; const ruleName = 'plugin/whole-pixel';
@ -14,7 +13,7 @@ const PX_PER_REM = 16;
const unitRegex = /(px|rem)$/; const unitRegex = /(px|rem)$/;
const numberRegex = /^([-0-9.]+)/; const numberRegex = /^([-0-9.]+)/;
module.exports = stylelint.createPlugin(ruleName, (primaryOption, secondaryOptionObject, context) => { const plugin = stylelint.createPlugin(ruleName, (primaryOption, secondaryOptionObject, context) => {
const secondaryOptions = secondaryOptionObject || {}; const secondaryOptions = secondaryOptionObject || {};
return (root, result) => { return (root, result) => {
const validOptions = stylelint.utils.validateOptions( const validOptions = stylelint.utils.validateOptions(
@ -74,12 +73,12 @@ module.exports = stylelint.createPlugin(ruleName, (primaryOption, secondaryOptio
root.walkDecls((decl) => { root.walkDecls((decl) => {
if (!decl.value || ignoreList.includes(decl.prop)) return; if (!decl.value || ignoreList.includes(decl.prop)) return;
const values = postcss.list.space(decl.value); const values = list.space(decl.value);
if (!values?.length) return; if (!values?.length) return;
values.forEach((value) => handleValue(decl, value)); values.forEach((value) => handleValue(decl, value));
}); });
}; };
}); });
module.exports.ruleName = ruleName; export { ruleName, messages };
module.exports.messages = messages; export default plugin;

204
eslint.config.mjs Normal file
View File

@ -0,0 +1,204 @@
import eslint from '@eslint/js';
import stylisticJs from '@stylistic/eslint-plugin';
import { globalIgnores } from 'eslint/config';
import importsPlugin from 'eslint-plugin-import';
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(
eslint.configs.recommended,
tseslint.configs.recommendedTypeChecked,
reactPlugin.configs.flat.recommended,
reactXPlugin.configs['recommended-type-checked'],
jsxA11yPlugin.flatConfigs.recommended,
ttMultitabPlugin.configs.recommended,
stylisticJs.configs.customize({
semi: true,
arrowParens: 'always',
braceStyle: '1tbs',
quoteProps: 'as-needed',
}),
globalIgnores([
'src/lib/rlottie/rlottie-wasm.js',
'src/lib/video-preview/polyfill',
'src/lib/fasttextweb/fasttext-wasm.cjs',
'src/lib/gramjs/tl/',
'src/lib/lovely-chart',
'src/lib/music-metadata-browser',
'src/lib/secret-sauce/',
'src/types/language.d.ts',
'dist/',
'dist-electron/',
'public/',
'deploy/update_version.js',
]),
{
name: 'teact-config',
files: ['**/*.{js,mjs,cjs,jsx,mjsx,ts,tsx,mtsx}'],
settings: {
react: {
version: '19',
},
},
rules: {
'no-null/no-null': 'error',
'no-console': 'error',
'@stylistic/max-len': ['error', {
code: 120,
ignoreComments: true,
ignorePattern: '\\sd=".+"', // Ignore lines with "d" attribute
}],
'@stylistic/indent': ['error', 2, {
SwitchCase: 1,
flatTernaryExpressions: false,
}],
'@stylistic/multiline-ternary': 'off',
'no-prototype-builtins': 'off',
'no-undef': 'off',
'no-unused-vars': 'off',
'simple-import-sort/imports': [
'error',
{
groups: [
// Side effect imports
['^\\u0000'],
// Lib and global imports
[
'^react',
'^@?\\w',
'dist(/.*|$)',
'^(\\.+/)+(lib/(teact|gramjs))(/.*|$)',
'^(\\.+/)+global$',
],
// Type imports
[
'^(\\.+/)+.+\\u0000$',
'^(\\.+/|\\w+/)+(types)(/.*|$)',
'^(\\.+/|\\w+/)+(types)\\u0000',
],
// Config, utils, helpers
[
'^(\\.+/)+config',
'^(\\.+/)+(lib)(?!/(gramjs|teact))(/.*|$)',
'^(\\.+/)+global/.+',
'^(\\.+/)+(util)(/.*|$)',
'^\\.\\.(?!/?$)',
'^\\.\\./?$',
'^\\./(?=.*/)(?!/?$)',
'^\\.(?!/?$)',
'^\\./?$',
],
// Hooks
[
'.+(/hooks/)(.*|$)',
],
// Components
[
'/[A-Z](([a-z]+[A-Z]?)*)',
],
// Styles and CSS modules
[
'^.+\\.s?css$',
],
// Assets: images, stickers, etc
[
'^(\\.+/)+(assets)(/.*|$)',
],
],
},
],
// TypeScript type imports preference
'@typescript-eslint/consistent-type-imports': [
'error',
{
prefer: 'type-imports',
disallowTypeAnnotations: false,
},
],
'@typescript-eslint/no-unsafe-argument': 'off',
'@typescript-eslint/no-unsafe-assignment': 'off',
'@typescript-eslint/no-unsafe-call': 'off',
'@typescript-eslint/no-unsafe-member-access': 'off',
'@typescript-eslint/no-unsafe-return': 'off',
'@typescript-eslint/no-floating-promises': 'off',
'@typescript-eslint/no-explicit-any': 'off',
'@typescript-eslint/no-misused-promises': [
'error',
{
checksVoidReturn: false,
},
],
'@typescript-eslint/no-unused-vars': [
'error',
{
args: 'none',
caughtErrors: 'none',
destructuredArrayIgnorePattern: '^_',
varsIgnorePattern: '^_',
ignoreRestSiblings: true,
},
],
'@typescript-eslint/ban-ts-comment': 'off',
'@typescript-eslint/prefer-promise-reject-errors': 'off',
'@typescript-eslint/unbound-method': 'off',
'unused-imports/no-unused-imports': 'error',
'react-hooks/exhaustive-deps': 'off',
'react-hooks-static-deps/exhaustive-deps': [
'error',
{
// eslint-disable-next-line @stylistic/max-len
additionalHooks: '(useSyncEffect|useAsync|useDebouncedCallback|useThrottledCallback|useEffectWithPrevDeps|useLayoutEffectWithPrevDeps|useDerivedState|useDerivedSignal|useThrottledResolver|useDebouncedResolver|useThrottleForHeavyAnimation)$',
staticHooks: {
getActions: true,
useFlag: [false, true, true],
useForceUpdate: true,
useReducer: [false, true],
useLastCallback: true,
},
},
],
'react/prop-types': 'off',
'react/no-unknown-property': 'off',
'react/display-name': 'off',
'react/jsx-key': 'off',
'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-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',
},
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,
},
languageOptions: {
parserOptions: {
projectService: true,
tsconfigRootDir: import.meta.dirname,
},
globals: jestPlugin.environments.globals.globals,
},
},
);

View File

@ -1,4 +1,4 @@
module.exports = { export default {
setupFilesAfterEnv: ['./tests/init.js'], setupFilesAfterEnv: ['./tests/init.js'],
moduleNameMapper: { moduleNameMapper: {
'\\.(css|scss|wasm|jpg|jpeg|png|gif|eot|otf|webp|svg|ttf|woff|woff2|mp4|webm|wav|mp3|m4a|aac|oga|tgs)$': '\\.(css|scss|wasm|jpg|jpeg|png|gif|eot|otf|webp|svg|ttf|woff|woff2|mp4|webm|wav|mp3|m4a|aac|oga|tgs)$':
@ -16,7 +16,7 @@ module.exports = {
'\\.txt$': '@glen/jest-raw-loader', '\\.txt$': '@glen/jest-raw-loader',
}, },
globals: { globals: {
APP_REVISION: "jest-test", APP_REVISION: 'jest-test',
APP_VERSION: "0.0.1", APP_VERSION: '0.0.1',
}, },
}; };

19336
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@ -2,6 +2,7 @@
"name": "telegram-t", "name": "telegram-t",
"version": "10.9.51", "version": "10.9.51",
"description": "", "description": "",
"type": "module",
"main": "index.js", "main": "index.js",
"scripts": { "scripts": {
"dev": "cross-env APP_ENV=development webpack serve --mode development", "dev": "cross-env APP_ENV=development webpack serve --mode development",
@ -11,19 +12,17 @@
"build:mocked": "cross-env APP_ENV=test APP_MOCKED_CLIENT=1 npm run build:dev", "build:mocked": "cross-env APP_ENV=test APP_MOCKED_CLIENT=1 npm run build:dev",
"build:production": "webpack && bash ./deploy/copy_to_dist.sh", "build:production": "webpack && bash ./deploy/copy_to_dist.sh",
"web:release:production": "npm i && npm run build:production && git add -A && git commit -a -m '[Build]' --no-verify && git push", "web:release:production": "npm i && npm run build:production && git add -A && git commit -a -m '[Build]' --no-verify && git push",
"electron:dev": "npm run electron:webpack && IS_PACKAGED_ELECTRON=true concurrently -n main,renderer,electron \"npm run electron:webpack -- --watch\" \"npm run dev\" \"electronmon dist/electron\"", "electron:dev": "npm run electron:webpack && IS_PACKAGED_ELECTRON=true concurrently -n main,renderer,electron \"npm run electron:webpack -- --watch\" \"npm run dev\" \"electronmon dist/electron.cjs\"",
"electron:webpack": "cross-env APP_ENV=$ENV webpack --config ./webpack-electron.config.ts", "electron:webpack": "cross-env APP_ENV=$ENV webpack --config ./webpack-electron.config.ts",
"electron:build": "IS_PACKAGED_ELECTRON=true npm run build:$ENV && electron-builder install-app-deps && electron-rebuild && ENV=$ENV npm run electron:webpack", "electron:build": "IS_PACKAGED_ELECTRON=true npm run build:$ENV && electron-builder install-app-deps && electron-rebuild && ENV=$ENV npm run electron:webpack",
"electron:package": "npm run electron:build && npx rimraf dist-electron && electron-builder build --win --mac --linux --config src/electron/config.yml", "electron:package": "npm run electron:build && npx rimraf dist-electron && electron-builder build --win --mac --linux --config src/electron/config.yml",
"electron:package:staging": "ENV=staging npm run electron:package -- -p never", "electron:package:staging": "ENV=staging npm run electron:package -- -p never",
"electron:release:production": "ENV=production npm run electron:package -- -p always", "electron:release:production": "ENV=production npm run electron:package -- -p always",
"telegraph:update_changelog": "node ./dev/telegraphChangelog.js", "telegraph:update_changelog": "node ./dev/telegraphChangelog.js",
"check": "tsc && stylelint \"**/*.{css,scss}\" && eslint . --ext .ts,.tsx,.js --ignore-pattern src/lib/gramjs", "check": "tsc && stylelint \"**/*.{css,scss}\" && eslint .",
"check:fix": "npm run check -- --fix", "check:fix": "npm run check -- --fix",
"tl:rehash": "node ./dev/tlHash.js", "tl:rehash": "node ./dev/tlHash.js",
"gramjs:tl": "tsx ./src/lib/gramjs/tl/generateModules.ts", "gramjs:tl": "tsx ./src/lib/gramjs/tl/generateModules.ts",
"gramjs:lint": "cd src/lib/gramjs && eslint --ext .ts,.tsx,.js .",
"gramjs:lint:fix": "npm run gramjs:lint -- --fix",
"lang:ts": "tsx ./dev/generateLangTypes.js", "lang:ts": "tsx ./dev/generateLangTypes.js",
"lang:initial": "tsx ./dev/generateInitialLangFallback.js", "lang:initial": "tsx ./dev/generateInitialLangFallback.js",
"icons:build": "fantasticon", "icons:build": "fantasticon",
@ -31,12 +30,11 @@
"test:playwright": "playwright test", "test:playwright": "playwright test",
"test:record": "playwright codegen localhost:1235", "test:record": "playwright codegen localhost:1235",
"statoscope:validate-diff": "statoscope validate --input input.json --reference reference.json", "statoscope:validate-diff": "statoscope validate --input input.json --reference reference.json",
"postinstall": "(cd dev/eslint-multitab && npm i)",
"postversion": "echo $(node -p \"require('./package.json').version\") > public/version.txt && git commit --amend --no-verify --no-edit public/version.txt" "postversion": "echo $(node -p \"require('./package.json').version\") > public/version.txt && git commit --amend --no-verify --no-edit public/version.txt"
}, },
"engines": { "engines": {
"node": "^20 || ^22", "node": "^22.6 || ^24",
"npm": "^9 || ^10" "npm": "^10.8 || ^11"
}, },
"lint-staged": { "lint-staged": {
"*.{ts,tsx,js}": "eslint --fix", "*.{ts,tsx,js}": "eslint --fix",
@ -48,90 +46,89 @@
"author": "Alexander Zinchuk (alexander@zinchuk.com)", "author": "Alexander Zinchuk (alexander@zinchuk.com)",
"license": "GPL-3.0-or-later", "license": "GPL-3.0-or-later",
"devDependencies": { "devDependencies": {
"@babel/core": "^7.24.5", "@babel/core": "^7.27.1",
"@babel/plugin-syntax-nullish-coalescing-operator": "^7.8.3", "@babel/preset-env": "^7.27.2",
"@babel/plugin-transform-class-properties": "^7.24.1", "@babel/preset-react": "^7.27.1",
"@babel/plugin-transform-logical-assignment-operators": "^7.24.1", "@babel/preset-typescript": "^7.27.1",
"@babel/preset-env": "^7.24.5", "@babel/register": "^7.27.1",
"@babel/preset-react": "^7.24.1", "@electron/rebuild": "^4.0.1",
"@babel/preset-typescript": "^7.24.1", "@eslint/js": "^9.26.0",
"@babel/register": "^7.23.7",
"@electron/rebuild": "^3.6.0",
"@glen/jest-raw-loader": "^2.0.0", "@glen/jest-raw-loader": "^2.0.0",
"@playwright/test": "^1.44.0", "@playwright/test": "^1.52.0",
"@statoscope/cli": "5.28.2", "@statoscope/cli": "5.29.0",
"@statoscope/webpack-plugin": "5.28.2", "@statoscope/webpack-plugin": "5.29.0",
"@stylistic/stylelint-config": "^1.0.1", "@stylistic/eslint-plugin": "^4.2.0",
"@stylistic/stylelint-plugin": "^2.1.2", "@stylistic/stylelint-config": "^2.0.0",
"@testing-library/jest-dom": "^6.4.5", "@stylistic/stylelint-plugin": "^3.1.2",
"@twbs/fantasticon": "^3.0.0", "@testing-library/jest-dom": "^6.6.3",
"@twbs/fantasticon": "^3.1.0",
"@types/croppie": "^2.6.4", "@types/croppie": "^2.6.4",
"@types/dom-view-transitions": "^1.0.6", "@types/dom-view-transitions": "^1.0.6",
"@types/hast": "^3.0.4", "@types/hast": "^3.0.4",
"@types/jest": "^29.5.12", "@types/jest": "^29.5.14",
"@types/react": "^18.3.1", "@types/node": "^22.15.17",
"@types/react-dom": "^18.3.0", "@types/react": "^19.1.4",
"@types/react-dom": "^19.1.5",
"@types/webpack": "^5.28.5", "@types/webpack": "^5.28.5",
"@typescript-eslint/eslint-plugin": "^7.8.0", "@typescript-eslint/eslint-plugin": "^8.32.1",
"@typescript-eslint/parser": "^7.8.0", "@typescript-eslint/parser": "^8.32.1",
"@webpack-cli/serve": "^2.0.5", "@webpack-cli/serve": "^3.0.1",
"autoprefixer": "^10.4.19", "autoprefixer": "^10.4.21",
"babel-loader": "^9.1.3", "babel-loader": "^10.0.0",
"babel-plugin-transform-import-meta": "^2.2.1", "babel-plugin-transform-import-meta": "^2.3.2",
"bindings": "git+https://github.com/zubiden/node-bindings#6b9b0711a12e9df9cbf59a3271c25bf34c306907",
"browserlist": "^1.0.1", "browserlist": "^1.0.1",
"buffer": "^6.0.3", "buffer": "^6.0.3",
"concurrently": "^8.2.2", "concurrently": "^9.1.2",
"cross-env": "^7.0.3", "cross-env": "^7.0.3",
"css-loader": "^7.1.1", "css-loader": "^7.1.2",
"dotenv": "^16.4.5", "dotenv": "^16.5.0",
"electron": "^22.3.27", "electron": "^36.2.0",
"electron-builder": "^24.13.3", "electron-builder": "^26.0.12",
"electron-context-menu": "^3.6.1", "electron-conf": "^1.3.0",
"electron-store": "^8.2.0", "electron-context-menu": "^4.0.5",
"electron-updater": "^6.1.8", "electron-drag-click": "git+https://github.com/zubiden/electron-drag-click#cf6918ddb648e13ebcf6cf1e7aa008258edc06ad",
"electron-updater": "^6.6.2",
"electronmon": "^2.0.3", "electronmon": "^2.0.3",
"eslint": "^8.57.0", "eslint": "^9.26.0",
"eslint-config-airbnb": "^19.0.4", "eslint-plugin-import": "^2.31.0",
"eslint-config-airbnb-typescript": "^18.0.0", "eslint-plugin-jest": "^28.11.0",
"eslint-config-teact-app": "git+https://github.com/zubiden/eslint-config-teact-app#ebb14df4114e549fe9855d7ed0ca6cf11098e9fe", "eslint-plugin-jsx-a11y": "^6.10.2",
"eslint-import-resolver-webpack": "^0.13.8",
"eslint-plugin-eslint-multitab-tt": "file:dev/eslint-multitab",
"eslint-plugin-flowtype": "^8.0.3",
"eslint-plugin-jsx-a11y": "^6.8.0",
"eslint-plugin-no-async-without-await": "^1.2.0",
"eslint-plugin-no-null": "^1.0.2", "eslint-plugin-no-null": "^1.0.2",
"eslint-plugin-react": "^7.34.1", "eslint-plugin-react": "^7.37.5",
"eslint-plugin-react-hooks": "^4.6.2", "eslint-plugin-react-hooks-static-deps": "git+https://github.com/zubiden/eslint-plugin-react-hooks-static-deps#c16f35bf2e6e364cbc692c73cc350c1c5d46cc6e",
"eslint-plugin-react-hooks-static-deps": "^1.0.7", "eslint-plugin-react-x": "^1.49.0",
"eslint-plugin-simple-import-sort": "^12.1.0", "eslint-plugin-simple-import-sort": "^12.1.1",
"eslint-plugin-teactn": "git+https://github.com/korenskoy/eslint-plugin-teactn#c2c39dd005d58c07c24c4361de804dce1c6261b5", "eslint-plugin-tt-multitab": "git+https://github.com/zubiden/eslint-plugin-tt-multitab#15d542004d39ec7c29d50385484511bab0b77ea9",
"fake-indexeddb": "^6.0.0", "eslint-plugin-unused-imports": "^4.1.4",
"fake-indexeddb": "^6.0.1",
"git-revision-webpack-plugin": "^5.0.0", "git-revision-webpack-plugin": "^5.0.0",
"gitlog": "^4.0.8", "gitlog": "^5.1.0",
"html-webpack-plugin": "^5.6.0", "html-webpack-plugin": "^5.6.3",
"husky": "^9.0.11", "husky": "^9.1.7",
"jest": "^29.7.0", "jest": "^29.7.0",
"jest-environment-jsdom": "^29.7.0", "jest-environment-jsdom": "^29.7.0",
"lint-staged": "^15.2.2", "lint-staged": "^16.0.0",
"mini-css-extract-plugin": "^2.9.0", "mini-css-extract-plugin": "^2.9.2",
"postcss-loader": "^8.1.1", "postcss-loader": "^8.1.1",
"postcss-modules": "^6.0.0", "postcss-modules": "^6.0.1",
"react": "^18.3.1", "react": "^19.1.0",
"sass": "^1.77.0", "sass": "^1.88.0",
"sass-loader": "^14.2.1", "sass-loader": "^16.0.5",
"script-loader": "^0.7.2", "script-loader": "^0.7.2",
"serve": "^14.2.3", "serve": "^14.2.4",
"stylelint": "^16.5.0", "stylelint": "^16.19.1",
"stylelint-config-recommended-scss": "^14.0.0", "stylelint-config-recommended-scss": "^15.0.0",
"stylelint-declaration-block-no-ignored-properties": "^2.8.0", "stylelint-declaration-block-no-ignored-properties": "^2.8.0",
"stylelint-group-selectors": "^1.0.10", "stylelint-group-selectors": "^1.0.10",
"stylelint-high-performance-animation": "^1.10.0", "stylelint-high-performance-animation": "^1.11.0",
"stylelint-selector-tag-no-without-class": "^3.0.1", "stylelint-selector-tag-no-without-class": "^3.0.1",
"telegraph-node": "^1.0.4", "telegraph-node": "^1.0.4",
"tsx": "^4.11.0", "tsx": "^4.19.4",
"typescript": "^5.4.5", "typescript": "^5.8.3",
"webpack": "^5.91.0", "typescript-eslint": "^8.32.1",
"webpack-dev-server": "^5.0.4" "webpack": "^5.99.8",
"webpack-dev-server": "^5.2.1"
}, },
"dependencies": { "dependencies": {
"@cryptography/aes": "^0.1.1", "@cryptography/aes": "^0.1.1",
@ -139,18 +136,20 @@
"big-integer": "github:painor/BigInteger.js", "big-integer": "github:painor/BigInteger.js",
"croppie": "^2.6.5", "croppie": "^2.6.5",
"emoji-data-ios": "git+https://github.com/korenskoy/emoji-data-ios#443f1c9d7b16a82e7ee53f7f226d7d9a9920a105", "emoji-data-ios": "git+https://github.com/korenskoy/emoji-data-ios#443f1c9d7b16a82e7ee53f7f226d7d9a9920a105",
"idb-keyval": "^6.2.1", "idb-keyval": "^6.2.2",
"lowlight": "^2.9.0", "lowlight": "^3.3.0",
"mp4box": "^0.5.2", "mp4box": "^0.5.4",
"opus-recorder": "github:Ajaxy/opus-recorder", "opus-recorder": "github:Ajaxy/opus-recorder",
"os-browserify": "^0.3.0", "os-browserify": "^0.3.0",
"pako": "^2.1.0", "pako": "^2.1.0",
"path-browserify": "^1.0.1", "path-browserify": "^1.0.1",
"qr-code-styling": "github:zubiden/qr-code-styling#dbbfed0", "qr-code-styling": "^1.9.2"
"v8-compile-cache": "^2.4.0"
}, },
"optionalDependencies": { "optionalDependencies": {
"dmg-license": "^1.0.11", "dmg-license": "^1.0.11",
"fsevents": "2.3.3" "fsevents": "2.3.3"
},
"overrides": {
"bindings": "$bindings"
} }
} }

View File

@ -1,4 +1,4 @@
import { PlaywrightTestConfig, devices } from '@playwright/test'; import { devices, type PlaywrightTestConfig } from '@playwright/test';
const config: PlaywrightTestConfig = { const config: PlaywrightTestConfig = {
testDir: 'tests/playwright', testDir: 'tests/playwright',

View File

@ -1 +1 @@
10.4.6 10.4.51

View File

@ -12,9 +12,14 @@ declare namespace React {
teactExperimentControlled?: boolean; teactExperimentControlled?: boolean;
} }
// Teact feature // Teact features
// eslint-disable-next-line @typescript-eslint/no-empty-object-type, @typescript-eslint/no-wrapper-object-types
interface CSSProperties extends String {} interface CSSProperties extends String {}
interface ClassAttributes<T> extends RefAttributes<T> {
ref?: ((instance: T | undefined) => void) | React.RefObject<T | undefined> | undefined; // Teact ref
}
interface Attributes { interface Attributes {
// Optimization for DOM nodes reordering. Requires `teactFastList` for parent // Optimization for DOM nodes reordering. Requires `teactFastList` for parent
teactOrderKey?: number; teactOrderKey?: number;
@ -60,17 +65,35 @@ type EmojiWithSkins = Record<number, Emoji>;
type AllEmojis = Record<string, Emoji | EmojiWithSkins>; type AllEmojis = Record<string, Emoji | EmojiWithSkins>;
// Declare supported for import formats as modules // Declare supported formats as modules
declare module '*.png'; declare module '*.png' {
declare module '*.jpg'; const url: string;
declare module '*.svg'; export default url;
declare module '*.tgs'; }
declare module '*.wasm';
declare module '*.strings';
declare module '*.jpg' {
const url: string;
export default url;
}
declare module '*.svg' {
const url: string;
export default url;
}
declare module '*.txt' { declare module '*.txt' {
const content: string; const url: string;
export default content; export default url;
}
declare module '*.tgs' {
const url: string;
export default url;
}
declare module '*.wasm' {
const url: string;
export default url;
}
declare module '*.strings' {
const url: string;
export default url;
} }
declare module 'pako/dist/pako_inflate' { declare module 'pako/dist/pako_inflate' {
@ -79,6 +102,7 @@ declare module 'pako/dist/pako_inflate' {
declare module 'opus-recorder' { declare module 'opus-recorder' {
export interface IOpusRecorder extends Omit<MediaRecorder, 'start' | 'ondataavailable'> { export interface IOpusRecorder extends Omit<MediaRecorder, 'start' | 'ondataavailable'> {
// eslint-disable-next-line @typescript-eslint/no-misused-new
new(options: AnyLiteral): IOpusRecorder; new(options: AnyLiteral): IOpusRecorder;
start(stream?: MediaStreamAudioSourceNode): void; start(stream?: MediaStreamAudioSourceNode): void;
@ -104,8 +128,8 @@ interface IWebpWorker extends Worker {
} }
interface Document { interface Document {
mozFullScreenElement: any; mozFullScreenElement: HTMLElement;
webkitFullscreenElement: any; webkitFullscreenElement: HTMLElement;
mozCancelFullScreen?: () => Promise<void>; mozCancelFullScreen?: () => Promise<void>;
webkitCancelFullScreen?: () => Promise<void>; webkitCancelFullScreen?: () => Promise<void>;
webkitExitFullscreen?: () => Promise<void>; webkitExitFullscreen?: () => Promise<void>;
@ -138,14 +162,15 @@ type Falsy = false | 0 | '' | null | undefined;
interface BooleanConstructor { interface BooleanConstructor {
new<T>(value: T | Falsy): value is T; new<T>(value: T | Falsy): value is T;
<T>(value: T | Falsy): value is T; <T>(value: T | Falsy): value is T;
// eslint-disable-next-line @typescript-eslint/no-wrapper-object-types
readonly prototype: Boolean; readonly prototype: Boolean;
} }
interface Array<T> { interface Array<T> {
filter<S extends T>(predicate: BooleanConstructor, thisArg?: any): Exclude<S, Falsy>[]; filter<S extends T>(predicate: BooleanConstructor, thisArg?: unknown): Exclude<S, Falsy>[];
} }
interface ReadonlyArray<T> { interface ReadonlyArray<T> {
filter<S extends T>(predicate: BooleanConstructor, thisArg?: any): Exclude<S, Falsy>[]; filter<S extends T>(predicate: BooleanConstructor, thisArg?: unknown): Exclude<S, Falsy>[];
} }
// Missing type definitions for OPFS (Origin Private File System) API // Missing type definitions for OPFS (Origin Private File System) API
@ -162,7 +187,7 @@ interface FileSystemSyncAccessHandle {
truncate: (size: number) => Promise<undefined>; truncate: (size: number) => Promise<undefined>;
getSize: () => Promise<number>; getSize: () => Promise<number>;
flush: () => Promise<undefined> ; flush: () => Promise<undefined>;
close: () => Promise<undefined>; close: () => Promise<undefined>;
} }

View File

@ -1,4 +1,3 @@
/* eslint-disable @typescript-eslint/naming-convention */
import BigInt from 'big-integer'; import BigInt from 'big-integer';
import { Api as GramJs } from '../../../lib/gramjs'; import { Api as GramJs } from '../../../lib/gramjs';

View File

@ -144,7 +144,7 @@ export function buildPhoneCall(call: GramJs.TypePhoneCall): ApiPhoneCall {
keyFingerprint: keyFingerprint.toString(), keyFingerprint: keyFingerprint.toString(),
startDate, startDate,
isP2pAllowed: Boolean(p2pAllowed), isP2pAllowed: Boolean(p2pAllowed),
connections: connections.map(buildApiCallConnection).filter(Boolean) as ApiPhoneCallConnection[], connections: connections.map(buildApiCallConnection).filter(Boolean),
}; };
} }

View File

@ -1,4 +1,3 @@
import type BigInt from 'big-integer';
import { Api as GramJs } from '../../../lib/gramjs'; import { Api as GramJs } from '../../../lib/gramjs';
import type { Entity } from '../../../lib/gramjs/types'; import type { Entity } from '../../../lib/gramjs/types';
@ -298,11 +297,11 @@ export function getApiChatTypeFromPeerEntity(peerEntity: GramJs.TypeChat | GramJ
export function getPeerKey(peer: GramJs.TypePeer) { export function getPeerKey(peer: GramJs.TypePeer) {
if (isMtpPeerUser(peer)) { if (isMtpPeerUser(peer)) {
return `user${peer.userId}`; return `user${peer.userId.toString()}`;
} else if (isMtpPeerChat(peer)) { } else if (isMtpPeerChat(peer)) {
return `chat${peer.chatId}`; return `chat${peer.chatId.toString()}`;
} else { } else {
return `chat${peer.channelId}`; return `chat${peer.channelId.toString()}`;
} }
} }
@ -330,7 +329,7 @@ export function buildChatMember(
return { return {
userId, userId,
inviterId: 'inviterId' in member && member.inviterId inviterId: 'inviterId' in member && member.inviterId
? buildApiPeerId(member.inviterId as BigInt.BigInteger, 'user') ? buildApiPeerId(member.inviterId, 'user')
: undefined, : undefined,
joinedDate: 'date' in member ? member.date : undefined, joinedDate: 'date' in member ? member.date : undefined,
kickedByUserId: 'kickedBy' in member && member.kickedBy ? buildApiPeerId(member.kickedBy, 'user') : undefined, kickedByUserId: 'kickedBy' in member && member.kickedBy ? buildApiPeerId(member.kickedBy, 'user') : undefined,

View File

@ -16,7 +16,7 @@ export function bytesToDataUri(bytes: Buffer, shouldOmitPrefix = false, mimeType
return `${prefix}${btoa(String.fromCharCode(...bytes))}`; return `${prefix}${btoa(String.fromCharCode(...bytes))}`;
} }
export function omitVirtualClassFields<T extends GramJs.VirtualClass<T> & { flags?: any }>( export function omitVirtualClassFields<T extends GramJs.VirtualClass<T> & { flags?: unknown }>(
instance: T, instance: T,
): OmitVirtualFields<T> { ): OmitVirtualFields<T> {
const { const {

View File

@ -214,7 +214,6 @@ export function buildVideoFromDocument(document: GramJs.Document, params?: {
id, mimeType, thumbs, size, videoThumbs, attributes, id, mimeType, thumbs, size, videoThumbs, attributes,
} = document; } = document;
// eslint-disable-next-line no-restricted-globals
if (mimeType === VIDEO_WEBM_TYPE && !(self as any).isWebmSupported) { if (mimeType === VIDEO_WEBM_TYPE && !(self as any).isWebmSupported) {
return undefined; return undefined;
} }

View File

@ -441,7 +441,7 @@ export function buildLocalMessage(
const emojiOnlyCount = getEmojiOnlyCountForMessage(message.content, message.groupedId); const emojiOnlyCount = getEmojiOnlyCountForMessage(message.content, message.groupedId);
const finalMessage : ApiMessage = { const finalMessage: ApiMessage = {
...message, ...message,
...(emojiOnlyCount && { emojiOnlyCount }), ...(emojiOnlyCount && { emojiOnlyCount }),
}; };

View File

@ -1,6 +1,3 @@
/* eslint-disable no-bitwise */
// eslint-disable-next-line max-len
const TEMPLATE = '<?xml version="1.0" encoding="utf-8"?><svg version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" viewBox="0 0 {{width}} {{height}}" xml:space="preserve"><path fill-opacity="0.1" d="{{path}}" /></svg>'; const TEMPLATE = '<?xml version="1.0" encoding="utf-8"?><svg version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" viewBox="0 0 {{width}} {{height}}" xml:space="preserve"><path fill-opacity="0.1" d="{{path}}" /></svg>';
const LOOKUP = 'AACAAAAHAAALMAAAQASTAVAAAZaacaaaahaaalmaaaqastava.az0123456789-,'; const LOOKUP = 'AACAAAAHAAALMAAAQASTAVAAAZaacaaaahaaalmaaaqastava.az0123456789-,';

View File

@ -32,7 +32,7 @@ export function buildApiPeerId(id: BigInt.BigInteger, type: 'user' | 'chat' | 'c
return `-1${id.toString().padStart(CHANNEL_ID_LENGTH - 2, '0')}`; return `-1${id.toString().padStart(CHANNEL_ID_LENGTH - 2, '0')}`;
} }
return `-${id}`; return `-${id.toString()}`;
} }
export function getApiChatIdFromMtpPeer(peer: TypePeerOrInput) { export function getApiChatIdFromMtpPeer(peer: TypePeerOrInput) {

View File

@ -166,7 +166,7 @@ export function buildApiAvailableEffect(availableEffect: GramJs.AvailableEffect)
}; };
} }
export function buildApiPaidReactionPrivacy(privacy: GramJs.TypePaidReactionPrivacy) : ApiPaidReactionPrivacyType { export function buildApiPaidReactionPrivacy(privacy: GramJs.TypePaidReactionPrivacy): ApiPaidReactionPrivacyType {
if (privacy instanceof GramJs.PaidReactionPrivacyAnonymous) { if (privacy instanceof GramJs.PaidReactionPrivacyAnonymous) {
return { type: 'anonymous' }; return { type: 'anonymous' };
} }

View File

@ -53,7 +53,6 @@ export function buildStickerFromDocument(document: GramJs.TypeDocument,
(s): s is GramJs.PhotoCachedSize => s instanceof GramJs.PhotoCachedSize, (s): s is GramJs.PhotoCachedSize => s instanceof GramJs.PhotoCachedSize,
); );
// eslint-disable-next-line no-restricted-globals
if (mimeType === VIDEO_STICKER_MIME_TYPE && !(self as any).isWebmSupported && !cachedThumb) { if (mimeType === VIDEO_STICKER_MIME_TYPE && !(self as any).isWebmSupported && !cachedThumb) {
const staticThumb = document.thumbs && document.thumbs.find( const staticThumb = document.thumbs && document.thumbs.find(
(s): s is GramJs.PhotoSize => s instanceof GramJs.PhotoSize, (s): s is GramJs.PhotoSize => s instanceof GramJs.PhotoSize,
@ -78,7 +77,7 @@ export function buildStickerFromDocument(document: GramJs.TypeDocument,
const { w: width, h: height } = cachedThumb as GramJs.PhotoCachedSize || sizeAttribute || {}; const { w: width, h: height } = cachedThumb as GramJs.PhotoCachedSize || sizeAttribute || {};
const hasEffect = !isNoPremium && videoThumbs && compact(videoThumbs const hasEffect = !isNoPremium && videoThumbs && compact(videoThumbs
?.filter((thumb) => thumb instanceof GramJs.VideoSize) as GramJs.VideoSize[]) ?.filter((thumb) => thumb instanceof GramJs.VideoSize))
.some(({ type }) => type === 'f'); .some(({ type }) => type === 'f');
return { return {

View File

@ -118,7 +118,7 @@ export function addDocumentToLocalDb(document: GramJs.TypeDocument) {
export function addStoryRepairInfo<T extends GramJs.TypeDocument | GramJs.TypeWebDocument | GramJs.TypePhoto>( export function addStoryRepairInfo<T extends GramJs.TypeDocument | GramJs.TypeWebDocument | GramJs.TypePhoto>(
media: T, peerId: string, story: GramJs.TypeStoryItem, media: T, peerId: string, story: GramJs.TypeStoryItem,
) : T & RepairInfo { ): T & RepairInfo {
if (!(media instanceof GramJs.Document && media instanceof GramJs.Photo)) return media; if (!(media instanceof GramJs.Document && media instanceof GramJs.Photo)) return media;
const repairableMedia = media as T & RepairInfo; const repairableMedia = media as T & RepairInfo;
repairableMedia.localRepairInfo = { repairableMedia.localRepairInfo = {
@ -131,7 +131,7 @@ export function addStoryRepairInfo<T extends GramJs.TypeDocument | GramJs.TypeWe
export function addMessageRepairInfo<T extends GramJs.TypeDocument | GramJs.TypeWebDocument | GramJs.TypePhoto>( export function addMessageRepairInfo<T extends GramJs.TypeDocument | GramJs.TypeWebDocument | GramJs.TypePhoto>(
media: T, context?: MediaRepairContext, media: T, context?: MediaRepairContext,
) : T & RepairInfo { ): T & RepairInfo {
if (!context?.peerId) return media; if (!context?.peerId) return media;
if (!(media instanceof GramJs.Document || media instanceof GramJs.Photo || media instanceof GramJs.WebDocument)) { if (!(media instanceof GramJs.Document || media instanceof GramJs.Photo || media instanceof GramJs.WebDocument)) {
return media; return media;

View File

@ -70,7 +70,7 @@ export function deserializeBytes(value: string) {
} }
export function log(suffix: keyof typeof LOG_SUFFIX, ...data: any) { export function log(suffix: keyof typeof LOG_SUFFIX, ...data: any) {
/* eslint-disable max-len */ /* eslint-disable @stylistic/max-len */
/* eslint-disable no-console */ /* eslint-disable no-console */
const func = suffix === 'UNEXPECTED RESPONSE' ? console.error const func = suffix === 'UNEXPECTED RESPONSE' ? console.error
: suffix === 'INVOKE ERROR' || suffix === 'UNEXPECTED UPDATE' ? console.warn : console.log; : suffix === 'INVOKE ERROR' || suffix === 'UNEXPECTED UPDATE' ? console.warn : console.log;
@ -81,7 +81,7 @@ export function log(suffix: keyof typeof LOG_SUFFIX, ...data: any) {
`color: ${LOG_SUFFIX[suffix]}; background: ${LOG_BACKGROUND}; padding: 0.25rem; border-radius: 0.25rem; margin-left: 0.25rem;`, `color: ${LOG_SUFFIX[suffix]}; background: ${LOG_BACKGROUND}; padding: 0.25rem; border-radius: 0.25rem; margin-left: 0.25rem;`,
...data, ...data,
); );
/* eslint-enable max-len */ /* eslint-enable @stylistic/max-len */
} }
export function isResponseUpdate<T extends GramJs.AnyRequest>(result: T['__response']): result is GramJs.TypeUpdate { export function isResponseUpdate<T extends GramJs.AnyRequest>(result: T['__response']): result is GramJs.TypeUpdate {

View File

@ -73,7 +73,7 @@ export async function changeSessionTtl({
return result; return result;
} }
export async function resolveBusinessChatLink({ slug } : { slug: string }) { export async function resolveBusinessChatLink({ slug }: { slug: string }) {
const result = await invokeRequest(new GramJs.account.ResolveBusinessChatLink({ const result = await invokeRequest(new GramJs.account.ResolveBusinessChatLink({
slug, slug,
}), { }), {

View File

@ -9,8 +9,8 @@ import { wrapError } from '../helpers/misc';
import { sendApiUpdate } from '../updates/apiUpdateEmitter'; import { sendApiUpdate } from '../updates/apiUpdateEmitter';
const authController: { const authController: {
resolve?: Function; resolve?: AnyToVoidFunction;
reject?: Function; reject?: (error: Error) => void;
} = {}; } = {};
export function onWebAuthTokenFailed() { export function onWebAuthTokenFailed() {

View File

@ -530,13 +530,13 @@ export async function acceptLinkUrlAuth({ url, isWriteAllowed }: { url: string;
return authResult; return authResult;
} }
export function fetchBotCanSendMessage({ bot } : { bot: ApiUser }) { export function fetchBotCanSendMessage({ bot }: { bot: ApiUser }) {
return invokeRequest(new GramJs.bots.CanSendMessage({ return invokeRequest(new GramJs.bots.CanSendMessage({
bot: buildInputUser(bot.id, bot.accessHash), bot: buildInputUser(bot.id, bot.accessHash),
})); }));
} }
export function allowBotSendMessages({ bot } : { bot: ApiUser }) { export function allowBotSendMessages({ bot }: { bot: ApiUser }) {
return invokeRequest(new GramJs.bots.AllowSendMessage({ return invokeRequest(new GramJs.bots.AllowSendMessage({
bot: buildInputUser(bot.id, bot.accessHash), bot: buildInputUser(bot.id, bot.accessHash),
}), { }), {
@ -553,10 +553,10 @@ export async function invokeWebViewCustomMethod({
customMethod: string; customMethod: string;
parameters: string; parameters: string;
}): Promise<{ }): Promise<{
result: object; result: object;
} | { } | {
error: string; error: string;
}> { }> {
try { try {
const result = await invokeRequest(new GramJs.bots.InvokeWebViewCustomMethod({ const result = await invokeRequest(new GramJs.bots.InvokeWebViewCustomMethod({
bot: buildInputUser(bot.id, bot.accessHash), bot: buildInputUser(bot.id, bot.accessHash),
@ -579,7 +579,7 @@ export async function invokeWebViewCustomMethod({
} }
} }
export async function fetchPreviewMedias({ bot } : { bot: ApiUser }) { export async function fetchPreviewMedias({ bot }: { bot: ApiUser }) {
const result = await invokeRequest(new GramJs.bots.GetPreviewMedias({ const result = await invokeRequest(new GramJs.bots.GetPreviewMedias({
bot: buildInputUser(bot.id, bot.accessHash), bot: buildInputUser(bot.id, bot.accessHash),
})); }));
@ -613,7 +613,7 @@ export function checkBotDownloadFileParams({
}); });
} }
export function toggleUserEmojiStatusPermission({ bot, isEnabled } : { bot: ApiUser; isEnabled: boolean }) { export function toggleUserEmojiStatusPermission({ bot, isEnabled }: { bot: ApiUser; isEnabled: boolean }) {
return invokeRequest(new GramJs.bots.ToggleUserEmojiStatusPermission({ return invokeRequest(new GramJs.bots.ToggleUserEmojiStatusPermission({
bot: buildInputUser(bot.id, bot.accessHash), bot: buildInputUser(bot.id, bot.accessHash),
enabled: isEnabled, enabled: isEnabled,

View File

@ -965,7 +965,7 @@ export async function editChatPhoto({
return invokeRequest( return invokeRequest(
chatType === 'channel' chatType === 'channel'
? new GramJs.channels.EditPhoto({ ? new GramJs.channels.EditPhoto({
channel: buildInputChannel(chatId, accessHash!), channel: buildInputChannel(chatId, accessHash),
photo: inputPhoto, photo: inputPhoto,
}) })
: new GramJs.messages.EditChatPhoto({ : new GramJs.messages.EditChatPhoto({
@ -1319,7 +1319,7 @@ export async function fetchMembers({
memberFilter = 'recent', memberFilter = 'recent',
offset, offset,
query = '', query = '',
} : { }: {
chat: ApiChat; chat: ApiChat;
memberFilter?: ChannelMembersFilter; memberFilter?: ChannelMembersFilter;
offset?: number; offset?: number;
@ -1571,13 +1571,13 @@ export function toggleJoinRequest(chat: ApiChat, isEnabled: boolean) {
function preparePeers( function preparePeers(
result: GramJs.messages.Dialogs | GramJs.messages.DialogsSlice | GramJs.messages.PeerDialogs | result: GramJs.messages.Dialogs | GramJs.messages.DialogsSlice | GramJs.messages.PeerDialogs |
GramJs.messages.SavedDialogs | GramJs.messages.SavedDialogsSlice, GramJs.messages.SavedDialogs | GramJs.messages.SavedDialogsSlice,
currentStore?: Record<string, GramJs.TypeChat | GramJs.TypeUser>, currentStore?: Record<string, GramJs.TypeChat | GramJs.TypeUser>,
) { ) {
const store: Record<string, GramJs.TypeChat | GramJs.TypeUser> = {}; const store: Record<string, GramJs.TypeChat | GramJs.TypeUser> = {};
result.chats?.forEach((chat) => { result.chats?.forEach((chat) => {
const key = `chat${chat.id}`; const key = `chat${chat.id.toString()}`;
if (currentStore?.[key] && 'min' in chat && chat.min) { if (currentStore?.[key] && 'min' in chat && chat.min) {
return; return;
@ -1587,7 +1587,7 @@ function preparePeers(
}); });
result.users?.forEach((user) => { result.users?.forEach((user) => {
const key = `user${user.id}`; const key = `user${user.id.toString()}`;
if (currentStore?.[key] && 'min' in user && user.min) { if (currentStore?.[key] && 'min' in user && user.min) {
return; return;
@ -1702,13 +1702,13 @@ export async function fetchTopics({
offsetDate?: number; offsetDate?: number;
limit?: number; limit?: number;
}): Promise<{ }): Promise<{
topics: ApiTopic[]; topics: ApiTopic[];
messages: ApiMessage[]; messages: ApiMessage[];
count: number; count: number;
shouldOrderByCreateDate?: boolean; shouldOrderByCreateDate?: boolean;
draftsById: Record<number, ReturnType<typeof buildMessageDraft>>; draftsById: Record<number, ReturnType<typeof buildMessageDraft>>;
readInboxMessageIdByTopicId: Record<number, number>; readInboxMessageIdByTopicId: Record<number, number>;
} | undefined> { } | undefined> {
const { id, accessHash } = chat; const { id, accessHash } = chat;
const result = await invokeRequest(new GramJs.channels.GetForumTopics({ const result = await invokeRequest(new GramJs.channels.GetForumTopics({
@ -1756,9 +1756,9 @@ export async function fetchTopicById({
chat: ApiChat; chat: ApiChat;
topicId: number; topicId: number;
}): Promise<{ }): Promise<{
topic: ApiTopic; topic: ApiTopic;
messages: ApiMessage[]; messages: ApiMessage[];
} | undefined> { } | undefined> {
const { id, accessHash } = chat; const { id, accessHash } = chat;
const result = await invokeRequest(new GramJs.channels.GetForumTopicsByID({ const result = await invokeRequest(new GramJs.channels.GetForumTopicsByID({

View File

@ -81,9 +81,8 @@ export async function init(initialArgs: ApiInitialArgs) {
const session = new sessions.CallbackSession(sessionData, onSessionUpdate); const session = new sessions.CallbackSession(sessionData, onSessionUpdate);
// eslint-disable-next-line no-restricted-globals
(self as any).isWebmSupported = isWebmSupported; (self as any).isWebmSupported = isWebmSupported;
// eslint-disable-next-line no-restricted-globals
(self as any).maxBufferSize = maxBufferSize; (self as any).maxBufferSize = maxBufferSize;
client = new TelegramClient( client = new TelegramClient(
@ -113,9 +112,8 @@ export async function init(initialArgs: ApiInitialArgs) {
if (DEBUG) { if (DEBUG) {
log('CONNECTING'); log('CONNECTING');
// eslint-disable-next-line no-restricted-globals
(self as any).invoke = invokeRequest; (self as any).invoke = invokeRequest;
// eslint-disable-next-line no-restricted-globals
(self as any).GramJs = GramJs; (self as any).GramJs = GramJs;
} }
@ -218,7 +216,7 @@ export function handleGramJsUpdate(update: any) {
const updates = 'updates' in update ? update.updates : [update]; const updates = 'updates' in update ? update.updates : [update];
updates.forEach((nestedUpdate: any) => { updates.forEach((nestedUpdate: any) => {
if (!(nestedUpdate instanceof GramJs.UpdateConfig)) return; if (!(nestedUpdate instanceof GramJs.UpdateConfig)) return;
// eslint-disable-next-line no-underscore-dangle
const currentUser = (nestedUpdate as UpdateConfig)._entities const currentUser = (nestedUpdate as UpdateConfig)._entities
?.find((entity) => entity instanceof GramJs.User && buildApiPeerId(entity.id, 'user') === currentUserId); ?.find((entity) => entity instanceof GramJs.User && buildApiPeerId(entity.id, 'user') === currentUserId);
if (!(currentUser instanceof GramJs.User)) return; if (!(currentUser instanceof GramJs.User)) return;

View File

@ -16,6 +16,7 @@ export function initApi(_onUpdate: OnApiUpdate, initialArgs: ApiInitialArgs, ini
if (initialLocalDb) updateFullLocalDb(initialLocalDb); if (initialLocalDb) updateFullLocalDb(initialLocalDb);
// IMPORTANT: Do not await this, or login will not work
initClient(initialArgs); initClient(initialArgs);
} }

View File

@ -42,7 +42,7 @@ export default async function downloadMedia(
return undefined; return undefined;
} }
const parsed = await parseMedia(data, mediaFormat, mimeType); const parsed = parseMedia(data, mediaFormat, mimeType);
if (!parsed) { if (!parsed) {
return undefined; return undefined;
} }
@ -184,10 +184,9 @@ async function download(
} }
} }
// eslint-disable-next-line no-async-without-await/no-async-without-await function parseMedia(
async function parseMedia(
data: Buffer | File, mediaFormat: ApiMediaFormat, mimeType?: string, data: Buffer | File, mediaFormat: ApiMediaFormat, mimeType?: string,
): Promise<ApiParsedMedia | undefined> { ): ApiParsedMedia | undefined {
if (data instanceof File) { if (data instanceof File) {
return data; return data;
} }
@ -243,7 +242,7 @@ export function parseMediaUrl(url: string) {
: url.startsWith('webDocument') : url.startsWith('webDocument')
? url.match(/(webDocument):(.+)/) ? url.match(/(webDocument):(.+)/)
: url.match( : url.match(
// eslint-disable-next-line max-len
/(avatar|profile|photo|stickerSet|sticker|wallpaper|document)([-\d\w./]+)(?::\d+)?(\?size=\w+)?/, /(avatar|profile|photo|stickerSet|sticker|wallpaper|document)([-\d\w./]+)(?::\d+)?(\?size=\w+)?/,
); );
if (!mediaMatch) { if (!mediaMatch) {

View File

@ -297,7 +297,7 @@ export function sendMessageLocal(
wasDrafted, wasDrafted,
}); });
return localMessage; return Promise.resolve(localMessage);
} }
export function sendApiMessage( export function sendApiMessage(
@ -422,11 +422,11 @@ export function sendApiMessage(
return messagePromise; return messagePromise;
} }
export function sendMessage( export async function sendMessage(
params: SendMessageParams, params: SendMessageParams,
onProgress?: ApiOnProgress, onProgress?: ApiOnProgress,
) { ) {
const localMessage = params.localMessage || sendMessageLocal(params); const localMessage = params.localMessage || await sendMessageLocal(params);
return localMessage ? sendApiMessage(params, localMessage, onProgress) : undefined; return localMessage ? sendApiMessage(params, localMessage, onProgress) : undefined;
} }
@ -495,7 +495,7 @@ function sendGroupedMedia(
const inputMedia = await fetchInputMedia( const inputMedia = await fetchInputMedia(
buildInputPeer(chat.id, chat.accessHash), buildInputPeer(chat.id, chat.accessHash),
media as GramJs.InputMediaUploadedPhoto | GramJs.InputMediaUploadedDocument, media,
); );
await prevMediaQueue; await prevMediaQueue;
@ -1557,7 +1557,7 @@ export function forwardMessagesLocal(params: ForwardMessagesParams) {
wasDrafted, wasDrafted,
}); });
}); });
return { messageIds, localMessages }; return Promise.resolve({ messageIds, localMessages });
} }
export async function forwardApiMessages(params: ForwardMessagesParams) { export async function forwardApiMessages(params: ForwardMessagesParams) {
@ -1617,7 +1617,7 @@ export async function forwardMessages(params: ForwardMessagesParams) {
} else { } else {
const newParams = { const newParams = {
...params, ...params,
forwardedLocalMessagesSlice: forwardMessagesLocal(params), forwardedLocalMessagesSlice: await forwardMessagesLocal(params),
}; };
await forwardApiMessages(newParams); await forwardApiMessages(newParams);
} }
@ -2114,7 +2114,7 @@ export async function fetchOutboxReadDate({ chat, messageId }: { chat: ApiChat;
const peer = buildInputPeer(id, accessHash); const peer = buildInputPeer(id, accessHash);
const result = await invokeRequest(new GramJs.messages.GetOutboxReadDate({ const result = await invokeRequest(new GramJs.messages.GetOutboxReadDate({
peer: peer as GramJs.TypeInputPeer, peer: peer,
msgId: messageId, msgId: messageId,
}), { shouldThrow: true }); }), { shouldThrow: true });

View File

@ -49,9 +49,9 @@ export async function validateRequestedInfo({
requestInfo: GramJs.TypePaymentRequestedInfo; requestInfo: GramJs.TypePaymentRequestedInfo;
shouldSave?: boolean; shouldSave?: boolean;
}): Promise<{ }): Promise<{
id: string; id: string;
shippingOptions: any; shippingOptions: any;
} | undefined> { } | undefined> {
const result = await invokeRequest(new GramJs.payments.ValidateRequestedInfo({ const result = await invokeRequest(new GramJs.payments.ValidateRequestedInfo({
invoice: buildInputInvoice(inputInvoice), invoice: buildInputInvoice(inputInvoice),
save: shouldSave || undefined, save: shouldSave || undefined,
@ -258,7 +258,7 @@ export async function fetchMyBoosts() {
export async function applyBoost({ export async function applyBoost({
chat, chat,
slots, slots,
} : { }: {
chat: ApiChat; chat: ApiChat;
slots: number[]; slots: number[];
}) { }) {

View File

@ -117,7 +117,7 @@ class PhoneCallState {
}); });
} }
const message = await this.state.decryptMessageData(Buffer.from(data)); const message = await this.state.decryptMessageData(Buffer.from(data)) as Buffer;
return JSON.parse(message.toString()); return JSON.parse(message.toString());
} }
@ -161,7 +161,7 @@ export function destroyPhoneCallState() {
} }
type FunctionPropertyOf<T> = { type FunctionPropertyOf<T> = {
[P in keyof T]: T[P] extends Function [P in keyof T]: T[P] extends AnyFunction
? P ? P
: never : never
}[keyof T]; }[keyof T];

View File

@ -115,8 +115,8 @@ export async function fetchAvailableEffects() {
const effects = result.effects.map(buildApiAvailableEffect); const effects = result.effects.map(buildApiAvailableEffect);
const stickers : ApiSticker[] = []; const stickers: ApiSticker[] = [];
const emojis : ApiSticker[] = []; const emojis: ApiSticker[] = [];
for (const effect of effects) { for (const effect of effects) {
if (effect.effectAnimationId) { if (effect.effectAnimationId) {
@ -126,7 +126,9 @@ export async function fetchAvailableEffects() {
} else { } else {
const document = localDb.documents[effect.effectStickerId]; const document = localDb.documents[effect.effectStickerId];
const sticker = buildStickerFromDocument(document); const sticker = buildStickerFromDocument(document);
if (sticker) { stickers.push(sticker); } if (sticker) {
stickers.push(sticker);
}
} }
} }

View File

@ -57,7 +57,7 @@ export async function fetchSavedStarGifts({
}) { }) {
type GetSavedStarGiftsParams = ConstructorParameters<typeof GramJs.payments.GetSavedStarGifts>[0]; type GetSavedStarGiftsParams = ConstructorParameters<typeof GramJs.payments.GetSavedStarGifts>[0];
const params : GetSavedStarGiftsParams = { const params: GetSavedStarGiftsParams = {
peer: buildInputPeer(peer.id, peer.accessHash), peer: buildInputPeer(peer.id, peer.accessHash),
offset, offset,
limit, limit,

View File

@ -110,10 +110,10 @@ export async function fetchMessagePublicForwards({
dcId?: number; dcId?: number;
offset?: string; offset?: string;
}): Promise<{ }): Promise<{
forwards?: ApiMessagePublicForward[]; forwards?: ApiMessagePublicForward[];
count?: number; count?: number;
nextOffset?: string; nextOffset?: string;
} | undefined> { } | undefined> {
const result = await invokeRequest(new GramJs.stats.GetMessagePublicForwards({ const result = await invokeRequest(new GramJs.stats.GetMessagePublicForwards({
channel: buildInputChannel(chat.id, chat.accessHash), channel: buildInputChannel(chat.id, chat.accessHash),
msgId: messageId, msgId: messageId,
@ -193,10 +193,10 @@ export async function fetchStoryPublicForwards({
dcId?: number; dcId?: number;
offset?: string; offset?: string;
}): Promise<{ }): Promise<{
publicForwards: (ApiMessagePublicForward | ApiStoryPublicForward)[] | undefined; publicForwards: (ApiMessagePublicForward | ApiStoryPublicForward)[] | undefined;
count?: number; count?: number;
nextOffset?: string; nextOffset?: string;
} | undefined> { } | undefined> {
const result = await invokeRequest(new GramJs.stats.GetStoryPublicForwards({ const result = await invokeRequest(new GramJs.stats.GetStoryPublicForwards({
peer: buildInputPeer(chat.id, chat.accessHash), peer: buildInputPeer(chat.id, chat.accessHash),
id: storyId, id: storyId,

View File

@ -67,11 +67,11 @@ export async function fetchAllStories({
const peerId = getApiChatIdFromMtpPeer(peerStories.peer); const peerId = getApiChatIdFromMtpPeer(peerStories.peer);
const stories = buildApiPeerStories(peerStories); const stories = buildApiPeerStories(peerStories);
const { profileIds, orderedIds, lastUpdatedAt } = Object.values(stories).reduce< const { profileIds, orderedIds, lastUpdatedAt } = Object.values(stories).reduce<
{ {
profileIds: number[]; profileIds: number[];
orderedIds: number[]; orderedIds: number[];
lastUpdatedAt?: number; lastUpdatedAt?: number;
} }
>((dataAcc, story) => { >((dataAcc, story) => {
if ('isInProfile' in story && story.isInProfile) { if ('isInProfile' in story && story.isInProfile) {
dataAcc.profileIds.push(story.id); dataAcc.profileIds.push(story.id);
@ -317,7 +317,7 @@ export async function fetchStoriesViews({
}; };
} }
export async function fetchStoryLink({ peer, storyId }: { peer: ApiPeer ; storyId: number }) { export async function fetchStoryLink({ peer, storyId }: { peer: ApiPeer; storyId: number }) {
const result = await invokeRequest(new GramJs.stories.ExportStoryLink({ const result = await invokeRequest(new GramJs.stories.ExportStoryLink({
peer: buildInputPeer(peer.id, peer.accessHash), peer: buildInputPeer(peer.id, peer.accessHash),
id: storyId, id: storyId,

View File

@ -7,8 +7,8 @@ import {
} from './client'; } from './client';
const emailCodeController: { const emailCodeController: {
resolve?: Function; resolve?: (code: string) => void;
reject?: Function; reject?: (error: Error) => void;
} = {}; } = {};
export async function getPasswordInfo() { export async function getPasswordInfo() {

View File

@ -1,8 +1,7 @@
import BigInt from 'big-integer'; import BigInt from 'big-integer';
import { Api as GramJs } from '../../../lib/gramjs'; import { Api as GramJs } from '../../../lib/gramjs';
import type { import type { ApiEmojiStatusType, ApiPeer, ApiUser,
ApiChat, ApiEmojiStatusType, ApiPeer, ApiUser,
} from '../../types'; } from '../../types';
import { buildApiChatFromPreview } from '../apiBuilders/chats'; import { buildApiChatFromPreview } from '../apiBuilders/chats';
@ -147,7 +146,7 @@ export async function fetchContactList() {
return undefined; return undefined;
} }
const users = result.users.map(buildApiUser).filter(Boolean) as ApiUser[]; const users = result.users.map(buildApiUser).filter(Boolean);
const userStatusesById = buildApiUserStatuses(result.users); const userStatusesById = buildApiUserStatuses(result.users);
return { return {
@ -164,7 +163,7 @@ export async function fetchUsers({ users }: { users: ApiUser[] }) {
return undefined; return undefined;
} }
const apiUsers = result.map(buildApiUser).filter(Boolean) as ApiUser[]; const apiUsers = result.map(buildApiUser).filter(Boolean);
const userStatusesById = buildApiUserStatuses(result); const userStatusesById = buildApiUserStatuses(result);
return { return {
@ -278,7 +277,7 @@ export async function fetchProfilePhotos({
offset?: number; offset?: number;
limit?: number; limit?: number;
}) { }) {
const chat = 'title' in peer ? peer as ApiChat : undefined; const chat = 'title' in peer ? peer : undefined;
const user = !chat ? peer as ApiUser : undefined; const user = !chat ? peer as ApiUser : undefined;
if (user) { if (user) {
const { id, accessHash } = user; const { id, accessHash } = user;

View File

@ -1,4 +1,3 @@
/* eslint-disable max-classes-per-file */
import type { BigInteger } from 'big-integer'; import type { BigInteger } from 'big-integer';
export class LocalUpdatePts { export class LocalUpdatePts {

View File

@ -44,7 +44,7 @@ function flushUpdates(throttleId: number) {
return; return;
} }
const currentUpdates = pendingUpdates!; const currentUpdates = pendingUpdates;
pendingUpdates = undefined; pendingUpdates = undefined;
currentUpdates.forEach(onUpdate); currentUpdates.forEach(onUpdate);
} }

View File

@ -217,7 +217,6 @@ export function updater(update: Update) {
peerId: message.chatId, peerId: message.chatId,
}); });
} else if (action instanceof GramJs.MessageActionChatDeleteUser) { } else if (action instanceof GramJs.MessageActionChatDeleteUser) {
// eslint-disable-next-line no-underscore-dangle
if (update._entities && update._entities.some((e): e is GramJs.User => ( if (update._entities && update._entities.some((e): e is GramJs.User => (
e instanceof GramJs.User && Boolean(e.self) && e.id === action.userId e instanceof GramJs.User && Boolean(e.self) && e.id === action.userId
))) { ))) {
@ -231,7 +230,6 @@ export function updater(update: Update) {
}); });
} }
} else if (action instanceof GramJs.MessageActionChatAddUser) { } else if (action instanceof GramJs.MessageActionChatAddUser) {
// eslint-disable-next-line no-underscore-dangle
if (update._entities && update._entities.some((e): e is GramJs.User => ( if (update._entities && update._entities.some((e): e is GramJs.User => (
e instanceof GramJs.User && Boolean(e.self) && action.users.includes(e.id) e instanceof GramJs.User && Boolean(e.self) && action.users.includes(e.id)
))) { ))) {
@ -263,13 +261,13 @@ export function updater(update: Update) {
sendApiUpdate({ sendApiUpdate({
'@type': 'updateTopic', '@type': 'updateTopic',
chatId: getApiChatIdFromMtpPeer(update.message.peerId!), chatId: getApiChatIdFromMtpPeer(update.message.peerId),
topicId, topicId,
}); });
} else if (action instanceof GramJs.MessageActionTopicCreate) { } else if (action instanceof GramJs.MessageActionTopicCreate) {
sendApiUpdate({ sendApiUpdate({
'@type': 'updateTopics', '@type': 'updateTopics',
chatId: getApiChatIdFromMtpPeer(update.message.peerId!), chatId: getApiChatIdFromMtpPeer(update.message.peerId),
}); });
} }
} }
@ -659,7 +657,6 @@ export function updater(update: Update) {
typingStatus: buildChatTypingStatus(update), typingStatus: buildChatTypingStatus(update),
}); });
} else if (update instanceof GramJs.UpdateChannel) { } else if (update instanceof GramJs.UpdateChannel) {
// eslint-disable-next-line @typescript-eslint/naming-convention
const { _entities } = update; const { _entities } = update;
if (!_entities) { if (!_entities) {
return; return;

View File

@ -76,7 +76,6 @@ export function processUpdate(update: Update, isFromDifference?: boolean, should
if (update instanceof GramJs.Updates || update instanceof GramJs.UpdatesCombined) { if (update instanceof GramJs.Updates || update instanceof GramJs.UpdatesCombined) {
if (isFromDifference) { if (isFromDifference) {
// eslint-disable-next-line no-underscore-dangle
(update as SeqUpdate)._isFromDifference = true; (update as SeqUpdate)._isFromDifference = true;
} }
@ -90,7 +89,6 @@ export function processUpdate(update: Update, isFromDifference?: boolean, should
return; return;
} }
if (isFromDifference) { if (isFromDifference) {
// eslint-disable-next-line no-underscore-dangle
(update as PtsUpdate)._isFromDifference = true; (update as PtsUpdate)._isFromDifference = true;
} }
savePtsUpdate(update, shouldOnlySave); savePtsUpdate(update, shouldOnlySave);
@ -145,7 +143,6 @@ function applyUpdate(updateObject: SeqUpdate | PtsUpdate) {
updateObject.updates.forEach((update) => { updateObject.updates.forEach((update) => {
if (entities) { if (entities) {
// eslint-disable-next-line no-underscore-dangle
(update as any)._entities = entities; (update as any)._entities = entities;
} }
@ -180,7 +177,6 @@ function popSeqQueue() {
const localSeq = localDb.commonBoxState.seq; const localSeq = localDb.commonBoxState.seq;
const seqStart = 'seqStart' in update ? update.seqStart : update.seq; const seqStart = 'seqStart' in update ? update.seqStart : update.seq;
// eslint-disable-next-line no-underscore-dangle
if (seqStart === 0 || (update._isFromDifference && seqStart >= localSeq + 1)) { if (seqStart === 0 || (update._isFromDifference && seqStart >= localSeq + 1)) {
applyUpdate(update); applyUpdate(update);
} else if (seqStart === localSeq + 1) { } else if (seqStart === localSeq + 1) {
@ -210,13 +206,12 @@ function popPtsQueue(channelId: string) {
if (localPts === undefined) { if (localPts === undefined) {
if (DEBUG) { if (DEBUG) {
// Uncomment to debug missing updates // Uncomment to debug missing updates
// eslint-disable-next-line no-console
// console.error('[UpdateManager] Got pts update without local state', channelId); // console.error('[UpdateManager] Got pts update without local state', channelId);
} }
return; return;
} }
// eslint-disable-next-line no-underscore-dangle
if (update._isFromDifference && pts >= localPts + ptsCount) { if (update._isFromDifference && pts >= localPts + ptsCount) {
applyUpdate(update); applyUpdate(update);
} else if (pts === localPts + ptsCount) { } else if (pts === localPts + ptsCount) {

View File

@ -15,12 +15,14 @@ import { pause, throttleWithTickEnd } from '../../../util/schedulers';
type RequestState = { type RequestState = {
messageId: string; messageId: string;
resolve: Function; resolve: AnyToVoidFunction;
reject: Function; reject: AnyToVoidFunction;
callback?: AnyToVoidFunction; callback?: AnyToVoidFunction;
DEBUG_payload?: any; DEBUG_payload?: any;
}; };
type EnsurePromise<T> = Promise<Awaited<T>>;
const HEALTH_CHECK_TIMEOUT = 150; const HEALTH_CHECK_TIMEOUT = 150;
const HEALTH_CHECK_MIN_DELAY = 5 * 1000; // 5 sec const HEALTH_CHECK_MIN_DELAY = 5 * 1000; // 5 sec
const NO_QUEUE_BEFORE_INIT = new Set(['destroy']); const NO_QUEUE_BEFORE_INIT = new Set(['destroy']);
@ -153,16 +155,18 @@ export function setShouldEnableDebugLog(value: boolean) {
* Call a worker method on this tab's worker, without transferring to master tab * Call a worker method on this tab's worker, without transferring to master tab
* Mostly needed to disconnect worker when re-electing master * Mostly needed to disconnect worker when re-electing master
*/ */
export function callApiLocal<T extends keyof Methods>(fnName: T, ...args: MethodArgs<T>) { export function callApiLocal<T extends keyof Methods>(
fnName: T, ...args: MethodArgs<T>
): EnsurePromise<MethodResponse<T>> {
if (!isInited) { if (!isInited) {
if (NO_QUEUE_BEFORE_INIT.has(fnName)) { if (NO_QUEUE_BEFORE_INIT.has(fnName)) {
return Promise.resolve(undefined) as MethodResponse<T>; return Promise.resolve(undefined) as EnsurePromise<MethodResponse<T>>;
} }
const deferred = new Deferred(); const deferred = new Deferred();
localApiRequestsQueue.push({ fnName, args, deferred }); localApiRequestsQueue.push({ fnName, args, deferred });
return deferred.promise as MethodResponse<T>; return deferred.promise as EnsurePromise<MethodResponse<T>>;
} }
const promise = makeRequest({ const promise = makeRequest({
@ -194,19 +198,19 @@ export function callApiLocal<T extends keyof Methods>(fnName: T, ...args: Method
})(); })();
} }
return promise as MethodResponse<T>; return promise as EnsurePromise<MethodResponse<T>>;
} }
export function callApi<T extends keyof Methods>(fnName: T, ...args: MethodArgs<T>) { export function callApi<T extends keyof Methods>(fnName: T, ...args: MethodArgs<T>): EnsurePromise<MethodResponse<T>> {
if (!isInited && isMasterTab) { if (!isInited && isMasterTab) {
if (NO_QUEUE_BEFORE_INIT.has(fnName)) { if (NO_QUEUE_BEFORE_INIT.has(fnName)) {
return Promise.resolve(undefined) as MethodResponse<T>; return Promise.resolve(undefined) as EnsurePromise<MethodResponse<T>>;
} }
const deferred = new Deferred(); const deferred = new Deferred();
apiRequestsQueue.push({ fnName, args, deferred }); apiRequestsQueue.push({ fnName, args, deferred });
return deferred.promise as MethodResponse<T>; return deferred.promise as EnsurePromise<MethodResponse<T>>;
} }
const promise = isMasterTab ? makeRequest({ const promise = isMasterTab ? makeRequest({
@ -241,7 +245,7 @@ export function callApi<T extends keyof Methods>(fnName: T, ...args: MethodArgs<
})(); })();
} }
return promise as MethodResponse<T>; return promise as EnsurePromise<MethodResponse<T>>;
} }
export function cancelApiProgress(progressCallback: ApiOnProgress) { export function cancelApiProgress(progressCallback: ApiOnProgress) {
@ -274,7 +278,6 @@ function subscribeToWorker(onUpdate: OnApiUpdate) {
worker?.addEventListener('message', ({ data }: WorkerMessageEvent) => { worker?.addEventListener('message', ({ data }: WorkerMessageEvent) => {
data?.payloads.forEach((payload) => { data?.payloads.forEach((payload) => {
if (payload.type === 'updates') { if (payload.type === 'updates') {
// eslint-disable-next-line @typescript-eslint/naming-convention
let DEBUG_startAt: number | undefined; let DEBUG_startAt: number | undefined;
if (DEBUG) { if (DEBUG) {
DEBUG_startAt = performance.now(); DEBUG_startAt = performance.now();

View File

@ -54,7 +54,7 @@ onmessage = ({ data }: OriginMessageEvent) => {
switch (payload.type) { switch (payload.type) {
case 'initApi': { case 'initApi': {
const { messageId, args } = payload; const { messageId, args } = payload;
await initApi(onUpdate, args[0], args[1]); initApi(onUpdate, args[0], args[1]);
if (messageId) { if (messageId) {
sendToOrigin({ sendToOrigin({
type: 'methodResponse', type: 'methodResponse',

View File

@ -253,7 +253,7 @@ export interface ApiTopic {
isPinned?: boolean; isPinned?: boolean;
isHidden?: boolean; isHidden?: boolean;
isOwner?: boolean; isOwner?: boolean;
// eslint-disable-next-line max-len
// TODO[forums] https://github.com/telegramdesktop/tdesktop/blob/1aece79a471d99a8b63d826b1bce1f36a04d7293/Telegram/SourceFiles/data/data_forum_topic.cpp#L318 // TODO[forums] https://github.com/telegramdesktop/tdesktop/blob/1aece79a471d99a8b63d826b1bce1f36a04d7293/Telegram/SourceFiles/data/data_forum_topic.cpp#L318
isMin?: boolean; isMin?: boolean;
date: number; date: number;

View File

@ -271,13 +271,13 @@ export interface ApiMessageActionExpiredContent extends ActionMediaType {
export interface ApiMessageActionPaidMessagesRefunded extends ActionMediaType { export interface ApiMessageActionPaidMessagesRefunded extends ActionMediaType {
type: 'paidMessagesRefunded'; type: 'paidMessagesRefunded';
count:number; count: number;
stars:number; stars: number;
} }
export interface ApiMessageActionPaidMessagesPrice extends ActionMediaType { export interface ApiMessageActionPaidMessagesPrice extends ActionMediaType {
type: 'paidMessagesPrice'; type: 'paidMessagesPrice';
stars:number; stars: number;
} }
export interface ApiMessageActionUnsupported extends ActionMediaType { export interface ApiMessageActionUnsupported extends ActionMediaType {
@ -285,16 +285,16 @@ export interface ApiMessageActionUnsupported extends ActionMediaType {
} }
export type ApiMessageAction = ApiMessageActionUnsupported | ApiMessageActionChatCreate | ApiMessageActionChatEditTitle export type ApiMessageAction = ApiMessageActionUnsupported | ApiMessageActionChatCreate | ApiMessageActionChatEditTitle
| ApiMessageActionChatEditPhoto | ApiMessageActionChatDeletePhoto | ApiMessageActionChatAddUser | ApiMessageActionChatEditPhoto | ApiMessageActionChatDeletePhoto | ApiMessageActionChatAddUser
| ApiMessageActionChatDeleteUser | ApiMessageActionChatJoinedByLink | ApiMessageActionChannelCreate | ApiMessageActionChatDeleteUser | ApiMessageActionChatJoinedByLink | ApiMessageActionChannelCreate
| ApiMessageActionChatMigrateTo | ApiMessageActionChannelMigrateFrom | ApiMessageActionPinMessage | ApiMessageActionChatMigrateTo | ApiMessageActionChannelMigrateFrom | ApiMessageActionPinMessage
| ApiMessageActionHistoryClear | ApiMessageActionGameScore | ApiMessageActionPaymentSent | ApiMessageActionPhoneCall | ApiMessageActionHistoryClear | ApiMessageActionGameScore | ApiMessageActionPaymentSent | ApiMessageActionPhoneCall
| ApiMessageActionScreenshotTaken | ApiMessageActionCustomAction | ApiMessageActionBotAllowed | ApiMessageActionScreenshotTaken | ApiMessageActionCustomAction | ApiMessageActionBotAllowed
| ApiMessageActionBoostApply | ApiMessageActionContactSignUp | ApiMessageActionExpiredContent | ApiMessageActionBoostApply | ApiMessageActionContactSignUp | ApiMessageActionExpiredContent
| ApiMessageActionGroupCall | ApiMessageActionInviteToGroupCall | ApiMessageActionGroupCallScheduled | ApiMessageActionGroupCall | ApiMessageActionInviteToGroupCall | ApiMessageActionGroupCallScheduled
| ApiMessageActionChatJoinedByRequest | ApiMessageActionWebViewDataSent | ApiMessageActionGiftPremium | ApiMessageActionChatJoinedByRequest | ApiMessageActionWebViewDataSent | ApiMessageActionGiftPremium
| ApiMessageActionTopicCreate | ApiMessageActionTopicEdit | ApiMessageActionSuggestProfilePhoto | ApiMessageActionTopicCreate | ApiMessageActionTopicEdit | ApiMessageActionSuggestProfilePhoto
| ApiMessageActionChannelJoined | ApiMessageActionGiftCode | ApiMessageActionGiveawayLaunch | ApiMessageActionChannelJoined | ApiMessageActionGiftCode | ApiMessageActionGiveawayLaunch
| ApiMessageActionGiveawayResults | ApiMessageActionPaymentRefunded | ApiMessageActionGiftStars | ApiMessageActionGiveawayResults | ApiMessageActionPaymentRefunded | ApiMessageActionGiftStars
| ApiMessageActionPrizeStars | ApiMessageActionStarGift | ApiMessageActionStarGiftUnique | ApiMessageActionPrizeStars | ApiMessageActionStarGift | ApiMessageActionStarGiftUnique
| ApiMessageActionPaidMessagesRefunded | ApiMessageActionPaidMessagesPrice; | ApiMessageActionPaidMessagesRefunded | ApiMessageActionPaidMessagesPrice;

View File

@ -474,8 +474,8 @@ export type ApiMessageEntityQuoteFocus = {
}; };
export type ApiMessageEntity = ApiMessageEntityDefault | ApiMessageEntityPre | ApiMessageEntityTextUrl | export type ApiMessageEntity = ApiMessageEntityDefault | ApiMessageEntityPre | ApiMessageEntityTextUrl |
ApiMessageEntityMentionName | ApiMessageEntityCustomEmoji | ApiMessageEntityBlockquote | ApiMessageEntityTimestamp | ApiMessageEntityMentionName | ApiMessageEntityCustomEmoji | ApiMessageEntityBlockquote | ApiMessageEntityTimestamp |
ApiMessageEntityQuoteFocus; ApiMessageEntityQuoteFocus;
export enum ApiMessageEntityTypes { export enum ApiMessageEntityTypes {
Bold = 'MessageEntityBold', Bold = 'MessageEntityBold',
@ -695,7 +695,7 @@ export type ApiSavedReactionTag = {
}; };
export type ApiPaidReactionPrivacyType = ApiPaidReactionPrivacyDefault | export type ApiPaidReactionPrivacyType = ApiPaidReactionPrivacyDefault |
ApiPaidReactionPrivacyAnonymous | PaidReactionPrivacyPeer; ApiPaidReactionPrivacyAnonymous | PaidReactionPrivacyPeer;
export type ApiPaidReactionPrivacyDefault = { export type ApiPaidReactionPrivacyDefault = {
type: 'default'; type: 'default';
@ -870,7 +870,7 @@ export type ApiGlobalMessageSearchType = 'text' | 'channels' | 'media' | 'docume
export type ApiMessageSearchContext = 'all' | 'users' | 'groups' | 'channels'; export type ApiMessageSearchContext = 'all' | 'users' | 'groups' | 'channels';
export type ApiReportReason = 'spam' | 'violence' | 'pornography' | 'childAbuse' export type ApiReportReason = 'spam' | 'violence' | 'pornography' | 'childAbuse'
| 'copyright' | 'geoIrrelevant' | 'fake' | 'illegalDrugs' | 'personalDetails' | 'other'; | 'copyright' | 'geoIrrelevant' | 'fake' | 'illegalDrugs' | 'personalDetails' | 'other';
export type ApiSendMessageAction = { export type ApiSendMessageAction = {
type: 'cancel' | 'typing' | 'recordAudio' | 'chooseSticker' | 'playingGame'; type: 'cancel' | 'typing' | 'recordAudio' | 'chooseSticker' | 'playingGame';

View File

@ -189,7 +189,7 @@ export type ApiInputStorePaymentStarsGiveaway = {
}; };
export type ApiInputStorePaymentPurpose = ApiInputStorePaymentGiveaway | ApiInputStorePaymentGiftcode | export type ApiInputStorePaymentPurpose = ApiInputStorePaymentGiveaway | ApiInputStorePaymentGiftcode |
ApiInputStorePaymentStarsTopup | ApiInputStorePaymentStarsGift | ApiInputStorePaymentStarsGiveaway; ApiInputStorePaymentStarsTopup | ApiInputStorePaymentStarsGift | ApiInputStorePaymentStarsGiveaway;
export interface ApiPremiumGiftCodeOption { export interface ApiPremiumGiftCodeOption {
users: number; users: number;
@ -399,9 +399,9 @@ export type ApiInputInvoiceStarGiftTransfer = {
}; };
export type ApiInputInvoice = ApiInputInvoiceMessage | ApiInputInvoiceSlug | ApiInputInvoiceGiveaway export type ApiInputInvoice = ApiInputInvoiceMessage | ApiInputInvoiceSlug | ApiInputInvoiceGiveaway
| ApiInputInvoiceGiftCode | ApiInputInvoicePremiumGiftStars | ApiInputInvoiceStars | ApiInputInvoiceStarsGift | ApiInputInvoiceGiftCode | ApiInputInvoicePremiumGiftStars | ApiInputInvoiceStars | ApiInputInvoiceStarsGift
| ApiInputInvoiceStarsGiveaway | ApiInputInvoiceStarGift | ApiInputInvoiceChatInviteSubscription | ApiInputInvoiceStarsGiveaway | ApiInputInvoiceStarGift | ApiInputInvoiceChatInviteSubscription
| ApiInputInvoiceStarGiftUpgrade | ApiInputInvoiceStarGiftTransfer | ApiInputInvoiceStarGiftResale; | ApiInputInvoiceStarGiftUpgrade | ApiInputInvoiceStarGiftTransfer | ApiInputInvoiceStarGiftResale;
/* Used for Invoice request */ /* Used for Invoice request */
export type ApiRequestInputInvoiceMessage = { export type ApiRequestInputInvoiceMessage = {
@ -471,7 +471,7 @@ export type ApiRequestInputInvoiceStarGiftTransfer = {
}; };
export type ApiRequestInputInvoice = ApiRequestInputInvoiceMessage | ApiRequestInputInvoiceSlug export type ApiRequestInputInvoice = ApiRequestInputInvoiceMessage | ApiRequestInputInvoiceSlug
| ApiRequestInputInvoiceGiveaway | ApiRequestInputInvoiceStars | ApiRequestInputInvoiceStarsGiveaway | ApiRequestInputInvoiceGiveaway | ApiRequestInputInvoiceStars | ApiRequestInputInvoiceStarsGiveaway
| ApiRequestInputInvoiceChatInviteSubscription | ApiRequestInputInvoiceStarGift | ApiRequestInputInvoiceStarGiftUpgrade | ApiRequestInputInvoiceChatInviteSubscription | ApiRequestInputInvoiceStarGift
| ApiRequestInputInvoiceStarGiftTransfer | ApiRequestInputInvoicePremiumGiftStars | ApiRequestInputInvoiceStarGiftUpgrade | ApiRequestInputInvoiceStarGiftTransfer
| ApiRequestInputInvoiceStarGiftResale; | ApiRequestInputInvoicePremiumGiftStars | ApiRequestInputInvoiceStarGiftResale;

View File

@ -2,7 +2,7 @@ import type { ApiChat } from './chats';
import type { ApiUser } from './users'; import type { ApiUser } from './users';
export type ApiPrivacyKey = 'phoneNumber' | 'addByPhone' | 'lastSeen' | 'profilePhoto' | 'voiceMessages' | export type ApiPrivacyKey = 'phoneNumber' | 'addByPhone' | 'lastSeen' | 'profilePhoto' | 'voiceMessages' |
'forwards' | 'chatInvite' | 'phoneCall' | 'phoneP2P' | 'bio' | 'birthday' | 'gifts' | 'noPaidMessages'; 'forwards' | 'chatInvite' | 'phoneCall' | 'phoneP2P' | 'bio' | 'birthday' | 'gifts' | 'noPaidMessages';
export type PrivacyVisibility = 'everybody' | 'contacts' | 'closeFriends' | 'nonContacts' | 'nobody'; export type PrivacyVisibility = 'everybody' | 'contacts' | 'closeFriends' | 'nonContacts' | 'nobody';
export type BotsPrivacyType = 'allow' | 'disallow' | 'none'; export type BotsPrivacyType = 'allow' | 'disallow' | 'none';

View File

@ -71,7 +71,7 @@ export interface ApiStarGiftAttributeOriginalDetails {
} }
export type ApiStarGiftAttribute = ApiStarGiftAttributeModel | ApiStarGiftAttributePattern export type ApiStarGiftAttribute = ApiStarGiftAttributeModel | ApiStarGiftAttributePattern
| ApiStarGiftAttributeBackdrop | ApiStarGiftAttributeOriginalDetails; | ApiStarGiftAttributeBackdrop | ApiStarGiftAttributeOriginalDetails;
export interface ApiSavedStarGift { export interface ApiSavedStarGift {
isNameHidden?: boolean; isNameHidden?: boolean;
@ -155,14 +155,14 @@ export interface ApiStarsTransactionPeerPeer {
} }
export type ApiStarsTransactionPeer = export type ApiStarsTransactionPeer =
| ApiStarsTransactionPeerUnsupported | ApiStarsTransactionPeerUnsupported
| ApiStarsTransactionPeerAppStore | ApiStarsTransactionPeerAppStore
| ApiStarsTransactionPeerPlayMarket | ApiStarsTransactionPeerPlayMarket
| ApiStarsTransactionPeerPremiumBot | ApiStarsTransactionPeerPremiumBot
| ApiStarsTransactionPeerFragment | ApiStarsTransactionPeerFragment
| ApiStarsTransactionPeerAds | ApiStarsTransactionPeerAds
| ApiStarsTransactionApi | ApiStarsTransactionApi
| ApiStarsTransactionPeerPeer; | ApiStarsTransactionPeerPeer;
export interface ApiStarsTransaction { export interface ApiStarsTransaction {
id?: string; id?: string;

View File

@ -183,4 +183,4 @@ export type ApiMediaAreaUniqueGift = {
}; };
export type ApiMediaArea = ApiMediaAreaVenue | ApiMediaAreaGeoPoint | ApiMediaAreaSuggestedReaction export type ApiMediaArea = ApiMediaAreaVenue | ApiMediaAreaGeoPoint | ApiMediaAreaSuggestedReaction
| ApiMediaAreaChannelPost | ApiMediaAreaUrl | ApiMediaAreaWeather | ApiMediaAreaUniqueGift; | ApiMediaAreaChannelPost | ApiMediaAreaUrl | ApiMediaAreaWeather | ApiMediaAreaUniqueGift;

View File

@ -1588,6 +1588,9 @@
"PrivacyEnablePremiumGifts" = "Enable Premium Gifts"; "PrivacyEnablePremiumGifts" = "Enable Premium Gifts";
"DisplayGiftsButton" = "Display Gifts Button"; "DisplayGiftsButton" = "Display Gifts Button";
"HideGiftsButton" = "Hide Gifts Button"; "HideGiftsButton" = "Hide Gifts Button";
"Review" = "Review";
"CheckPrivacyCallsText" = "You've restricted who can message you, but anyone can still call you. Would you like to review these settings?";
"CheckPrivacyInviteText" = "You've restricted who can message you, but anyone can still invite you to groups and channels. Would you like to review these settings?";
"CustomShareGiftsInfo" = "You can add users or entire groups as exceptions that will override the settings above."; "CustomShareGiftsInfo" = "You can add users or entire groups as exceptions that will override the settings above.";
"StarsSubscribeBotText_one" = "Do you want to subscribe to **{name}** in **{bot}** for **{amount}** star per month?"; "StarsSubscribeBotText_one" = "Do you want to subscribe to **{name}** in **{bot}** for **{amount}** star per month?";
"StarsSubscribeBotText_other" = "Do you want to subscribe to **{name}** in **{bot}** for **{amount}** stars per month?"; "StarsSubscribeBotText_other" = "Do you want to subscribe to **{name}** in **{bot}** for **{amount}** stars per month?";

View File

@ -128,7 +128,7 @@ const App: FC<StateProps> = ({
// return <Test />; // return <Test />;
let activeKey: number; let activeKey: AppScreens;
let page: UiLoaderPage | undefined; let page: UiLoaderPage | undefined;
if (isInactive) { if (isInactive) {
@ -200,7 +200,6 @@ const App: FC<StateProps> = ({
const prevActiveKey = usePreviousDeprecated(activeKey); const prevActiveKey = usePreviousDeprecated(activeKey);
// eslint-disable-next-line consistent-return
function renderContent() { function renderContent() {
switch (activeKey) { switch (activeKey) {
case AppScreens.auth: case AppScreens.auth:

View File

@ -46,8 +46,7 @@ const Auth: FC<StateProps> = ({
onBack: handleChangeAuthorizationMethod, onBack: handleChangeAuthorizationMethod,
}); });
// eslint-disable-next-line no-null/no-null const containerRef = useRef<HTMLDivElement>();
const containerRef = useRef<HTMLDivElement>(null);
useElectronDrag(containerRef); useElectronDrag(containerRef);
// For animation purposes // For animation purposes

View File

@ -35,8 +35,7 @@ const AuthCode: FC<StateProps> = ({
} = getActions(); } = getActions();
const lang = useLang(); const lang = useLang();
// eslint-disable-next-line no-null/no-null const inputRef = useRef<HTMLInputElement>();
const inputRef = useRef<HTMLInputElement>(null);
const [code, setCode] = useState<string>(''); const [code, setCode] = useState<string>('');
const [isTracking, setIsTracking] = useState(false); const [isTracking, setIsTracking] = useState(false);

View File

@ -73,8 +73,7 @@ const AuthPhoneNumber: FC<StateProps> = ({
} = getActions(); } = getActions();
const lang = useLang(); const lang = useLang();
// eslint-disable-next-line no-null/no-null const inputRef = useRef<HTMLInputElement>();
const inputRef = useRef<HTMLInputElement>(null);
const suggestedLanguage = getSuggestedLanguage(); const suggestedLanguage = getSuggestedLanguage();
const isConnected = connectionState === 'connectionStateReady'; const isConnected = connectionState === 'connectionStateReady';
@ -132,8 +131,8 @@ const AuthPhoneNumber: FC<StateProps> = ({
// Any phone numbers should be allowed, in some cases ignoring formatting // Any phone numbers should be allowed, in some cases ignoring formatting
const selectedCountry = !country const selectedCountry = !country
|| (suggestedCountry && suggestedCountry.iso2 !== country.iso2) || (suggestedCountry && suggestedCountry.iso2 !== country.iso2)
|| (!suggestedCountry && newFullNumber.length) || (!suggestedCountry && newFullNumber.length)
? suggestedCountry ? suggestedCountry
: country; : country;
@ -207,11 +206,11 @@ const AuthPhoneNumber: FC<StateProps> = ({
IS_SAFARI && country && fullNumber !== undefined IS_SAFARI && country && fullNumber !== undefined
&& value.length - fullNumber.length > 1 && !isJustPastedRef.current && value.length - fullNumber.length > 1 && !isJustPastedRef.current
); );
parseFullNumber(shouldFixSafariAutoComplete ? `${country!.countryCode} ${value}` : value); parseFullNumber(shouldFixSafariAutoComplete ? `${country.countryCode} ${value}` : value);
}); });
const handleKeepSessionChange = useLastCallback((e: ChangeEvent<HTMLInputElement>) => { const handleKeepSessionChange = useLastCallback((e: ChangeEvent<HTMLInputElement>) => {
setAuthRememberMe(e.target.checked); setAuthRememberMe({ value: e.target.checked });
}); });
function handleSubmit(event: React.FormEvent<HTMLFormElement>) { function handleSubmit(event: React.FormEvent<HTMLFormElement>) {

View File

@ -40,7 +40,7 @@ const QR_SIZE = 280;
const QR_PLANE_SIZE = 54; const QR_PLANE_SIZE = 54;
const QR_CODE_MUTATION_DURATION = 50; // The library is asynchronous and we need to wait for its mutation code const QR_CODE_MUTATION_DURATION = 50; // The library is asynchronous and we need to wait for its mutation code
let qrCodeStylingPromise: Promise<typeof import('qr-code-styling')>; let qrCodeStylingPromise: Promise<typeof import('qr-code-styling')> | undefined;
function ensureQrCodeStyling() { function ensureQrCodeStyling() {
if (!qrCodeStylingPromise) { if (!qrCodeStylingPromise) {
@ -62,8 +62,7 @@ const AuthCode = ({
const suggestedLanguage = getSuggestedLanguage(); const suggestedLanguage = getSuggestedLanguage();
const lang = useLang(); const lang = useLang();
// eslint-disable-next-line no-null/no-null const qrCodeRef = useRef<HTMLDivElement>();
const qrCodeRef = useRef<HTMLDivElement>(null);
const isConnected = connectionState === 'connectionStateReady'; const isConnected = connectionState === 'connectionStateReady';
const continueText = useLangString('AuthContinueOnThisLanguage', suggestedLanguage); const continueText = useLangString('AuthContinueOnThisLanguage', suggestedLanguage);
@ -133,7 +132,7 @@ const AuthCode = ({
} }
return undefined; return undefined;
}, [isConnected, authQrCode, isQrMounted, markQrMounted, unmarkQrMounted, qrCode]); }, [isConnected, authQrCode, isQrMounted, qrCode]);
const handleBackNavigation = useLastCallback(() => { const handleBackNavigation = useLastCallback(() => {
navigateBack(); navigateBack();

View File

@ -44,8 +44,7 @@ const CountryCodeInput: FC<OwnProps & StateProps> = ({
phoneCodeList, phoneCodeList,
}) => { }) => {
const lang = useLang(); const lang = useLang();
// eslint-disable-next-line no-null/no-null const inputRef = useRef<HTMLInputElement>();
const inputRef = useRef<HTMLInputElement>(null);
const [filter, setFilter] = useState<string | undefined>(); const [filter, setFilter] = useState<string | undefined>();
const [filteredList, setFilteredList] = useState<ApiCountryCode[]>([]); const [filteredList, setFilteredList] = useState<ApiCountryCode[]>([]);
@ -105,7 +104,7 @@ const CountryCodeInput: FC<OwnProps & StateProps> = ({
handleTrigger(); handleTrigger();
}; };
const emoji = value && IS_EMOJI_SUPPORTED && renderText(isoToEmoji(value.iso2), ['hq_emoji']); const emoji = value && IS_EMOJI_SUPPORTED && isoToEmoji(value.iso2);
const name = value?.name || value?.defaultName || ''; const name = value?.name || value?.defaultName || '';
const inputValue = filter ?? [emoji, name].filter(Boolean).join(' '); const inputValue = filter ?? [emoji, name].filter(Boolean).join(' ');
@ -143,12 +142,15 @@ const CountryCodeInput: FC<OwnProps & StateProps> = ({
<MenuItem <MenuItem
key={`${country.iso2}-${country.countryCode}`} key={`${country.iso2}-${country.countryCode}`}
className={value && country.iso2 === value.iso2 ? 'selected' : ''} className={value && country.iso2 === value.iso2 ? 'selected' : ''}
// eslint-disable-next-line react/jsx-no-bind
onClick={() => handleChange(country)} onClick={() => handleChange(country)}
> >
<span className="country-flag">{renderText(isoToEmoji(country.iso2), ['hq_emoji'])}</span> <span className="country-flag">{renderText(isoToEmoji(country.iso2), ['hq_emoji'])}</span>
<span className="country-name">{country.name || country.defaultName}</span> <span className="country-name">{country.name || country.defaultName}</span>
<span className="country-code">+{country.countryCode}</span> <span className="country-code">
+
{country.countryCode}
</span>
</MenuItem> </MenuItem>
))} ))}
{!filteredList.length && ( {!filteredList.length && (

View File

@ -29,11 +29,11 @@ const ActiveCallHeader: FC<StateProps> = ({
useEffect(() => { useEffect(() => {
document.body.classList.toggle('has-call-header', Boolean(isCallPanelVisible)); document.body.classList.toggle('has-call-header', Boolean(isCallPanelVisible));
window.electron?.setTrafficLightPosition(isCallPanelVisible ? 'lowered' : 'standard'); window.electron?.setWindowButtonsPosition(isCallPanelVisible ? 'lowered' : 'standard');
return () => { return () => {
document.body.classList.toggle('has-call-header', false); document.body.classList.toggle('has-call-header', false);
window.electron?.setTrafficLightPosition('standard'); window.electron?.setWindowButtonsPosition('standard');
}; };
}, [isCallPanelVisible]); }, [isCallPanelVisible]);

View File

@ -11,7 +11,6 @@ const GroupCallAsync: FC<OwnProps> = (props) => {
const { groupCallId } = props; const { groupCallId } = props;
const GroupCall = useModuleLoader(Bundles.Calls, 'GroupCall', !groupCallId); const GroupCall = useModuleLoader(Bundles.Calls, 'GroupCall', !groupCallId);
// eslint-disable-next-line react/jsx-props-no-spreading
return GroupCall ? <GroupCall {...props} /> : undefined; return GroupCall ? <GroupCall {...props} /> : undefined;
}; };

View File

@ -80,18 +80,13 @@ const GroupCall: FC<OwnProps & StateProps> = ({
} = getActions(); } = getActions();
const lang = useOldLang(); const lang = useOldLang();
// eslint-disable-next-line no-null/no-null const containerRef = useRef<HTMLDivElement>();
const containerRef = useRef<HTMLDivElement>(null);
// eslint-disable-next-line no-null/no-null const primaryVideoContainerRef = useRef<HTMLDivElement>();
const primaryVideoContainerRef = useRef<HTMLDivElement>(null); const secondaryVideoContainerRef = useRef<HTMLDivElement>();
// eslint-disable-next-line no-null/no-null
const secondaryVideoContainerRef = useRef<HTMLDivElement>(null);
// eslint-disable-next-line no-null/no-null const panelScrollTriggerRef = useRef<HTMLDivElement>();
const panelScrollTriggerRef = useRef<HTMLDivElement>(null); const panelRef = useRef<HTMLDivElement>();
// eslint-disable-next-line no-null/no-null
const panelRef = useRef<HTMLDivElement>(null);
const [isLeaving, setIsLeaving] = useState(false); const [isLeaving, setIsLeaving] = useState(false);
const isOpen = !isCallPanelVisible && !isLeaving; const isOpen = !isCallPanelVisible && !isLeaving;
@ -560,7 +555,7 @@ export default memo(withGlobal<OwnProps>(
(global, { groupCallId }): StateProps => { (global, { groupCallId }): StateProps => {
const { const {
connectionState, title, participants, participantsCount, chatId, connectionState, title, participants, participantsCount, chatId,
} = selectGroupCall(global, groupCallId)! || {}; } = selectGroupCall(global, groupCallId) || {};
const chat = chatId ? selectChat(global, chatId) : undefined; const chat = chatId ? selectChat(global, chatId) : undefined;

View File

@ -38,10 +38,8 @@ const GroupCallParticipant: FC<OwnProps & StateProps> = ({
participant, participant,
peer, peer,
}) => { }) => {
// eslint-disable-next-line no-null/no-null const ref = useRef<HTMLDivElement>();
const ref = useRef<HTMLDivElement>(null); const menuRef = useRef<HTMLDivElement>();
// eslint-disable-next-line no-null/no-null
const menuRef = useRef<HTMLDivElement>(null);
const lang = useOldLang(); const lang = useOldLang();
const { const {

View File

@ -75,9 +75,9 @@ const GroupCallParticipantList: FC<OwnProps & StateProps> = ({
function compareParticipants(a: TypeGroupCallParticipant, b: TypeGroupCallParticipant) { function compareParticipants(a: TypeGroupCallParticipant, b: TypeGroupCallParticipant) {
return compareFields(!a.isMuted, !b.isMuted) return compareFields(!a.isMuted, !b.isMuted)
|| compareFields(a.presentation, b.presentation) || compareFields(a.presentation, b.presentation)
|| compareFields(a.video, b.video) || compareFields(a.video, b.video)
|| compareFields(a.raiseHandRating, b.raiseHandRating); || compareFields(a.raiseHandRating, b.raiseHandRating);
} }
export default memo(withGlobal<OwnProps>( export default memo(withGlobal<OwnProps>(

View File

@ -1,4 +1,4 @@
import type { FC } from '../../../lib/teact/teact'; import type { ElementRef, FC } from '../../../lib/teact/teact';
import React, { memo, useEffect, useState } from '../../../lib/teact/teact'; import React, { memo, useEffect, useState } from '../../../lib/teact/teact';
import { getActions, withGlobal } from '../../../global'; import { getActions, withGlobal } from '../../../global';
@ -31,7 +31,7 @@ type OwnProps =
onCloseAnimationEnd: VoidFunction; onCloseAnimationEnd: VoidFunction;
onClose: VoidFunction; onClose: VoidFunction;
isDropdownOpen: boolean; isDropdownOpen: boolean;
menuRef?: React.RefObject<HTMLDivElement>; menuRef?: ElementRef<HTMLDivElement>;
} }
& MenuPositionOptions; & MenuPositionOptions;
@ -172,7 +172,7 @@ const GroupCallParticipantMenu: FC<OwnProps & StateProps> = ({
onClose={onClose} onClose={onClose}
onCloseAnimationEnd={onCloseAnimationEnd} onCloseAnimationEnd={onCloseAnimationEnd}
className="participant-menu with-menu-transitions" className="participant-menu with-menu-transitions"
// eslint-disable-next-line react/jsx-props-no-spreading
{...menuPositionOptions} {...menuPositionOptions}
> >
{!isSelf && !shouldRaiseHand && ( {!isSelf && !shouldRaiseHand && (
@ -199,7 +199,10 @@ const GroupCallParticipantMenu: FC<OwnProps & StateProps> = ({
playSegment={speakerIconPlaySegment} playSegment={speakerIconPlaySegment}
size={SPEAKER_ICON_SIZE} size={SPEAKER_ICON_SIZE}
/> />
<span>{localVolume}%</span> <span>
{localVolume}
%
</span>
</div> </div>
</div> </div>
</div> </div>

View File

@ -63,12 +63,9 @@ const GroupCallParticipantVideo: FC<OwnProps & StateProps> = ({
}) => { }) => {
const lang = useOldLang(); const lang = useOldLang();
// eslint-disable-next-line no-null/no-null const thumbnailRef = useRef<HTMLCanvasElement>();
const thumbnailRef = useRef<HTMLCanvasElement>(null); const videoRef = useRef<HTMLVideoElement>();
// eslint-disable-next-line no-null/no-null const videoFallbackRef = useRef<HTMLCanvasElement>();
const videoRef = useRef<HTMLVideoElement>(null);
// eslint-disable-next-line no-null/no-null
const videoFallbackRef = useRef<HTMLCanvasElement>(null);
const { const {
x, y, width, height, noAnimate, isRemoved, x, y, width, height, noAnimate, isRemoved,
@ -202,10 +199,8 @@ const GroupCallParticipantVideo: FC<OwnProps & StateProps> = ({
}; };
}, [stream]); }, [stream]);
// eslint-disable-next-line no-null/no-null const ref = useRef<HTMLDivElement>();
const ref = useRef<HTMLDivElement>(null); const menuRef = useRef<HTMLDivElement>();
// eslint-disable-next-line no-null/no-null
const menuRef = useRef<HTMLDivElement>(null);
const { const {
isContextMenuOpen, isContextMenuOpen,

View File

@ -1,4 +1,5 @@
import type { RefObject } from 'react'; import type {
ElementRef } from '../../../../lib/teact/teact';
import { import {
useEffect, useMemo, useState, useEffect, useMemo, useState,
} from '../../../../lib/teact/teact'; } from '../../../../lib/teact/teact';
@ -37,8 +38,8 @@ export default function useGroupCallVideoLayout({
isLandscapeLayout, isLandscapeLayout,
pinnedVideo, pinnedVideo,
}: { }: {
primaryContainerRef: RefObject<HTMLDivElement>; primaryContainerRef: ElementRef<HTMLDivElement>;
secondaryContainerRef: RefObject<HTMLDivElement>; secondaryContainerRef: ElementRef<HTMLDivElement>;
videoParticipants: VideoParticipant[]; videoParticipants: VideoParticipant[];
isLandscapeLayout: boolean; isLandscapeLayout: boolean;
pinnedVideo: VideoParticipant | undefined; pinnedVideo: VideoParticipant | undefined;

View File

@ -55,8 +55,7 @@ const PhoneCall: FC<StateProps> = ({
const { const {
hangUp, requestMasterAndAcceptCall, playGroupCallSound, toggleGroupCallPanel, connectToActivePhoneCall, hangUp, requestMasterAndAcceptCall, playGroupCallSound, toggleGroupCallPanel, connectToActivePhoneCall,
} = getActions(); } = getActions();
// eslint-disable-next-line no-null/no-null const containerRef = useRef<HTMLDivElement>();
const containerRef = useRef<HTMLDivElement>(null);
const [isFullscreen, openFullscreen, closeFullscreen] = useFlag(); const [isFullscreen, openFullscreen, closeFullscreen] = useFlag();
const { isMobile } = useAppLayout(); const { isMobile } = useAppLayout();

View File

@ -11,7 +11,6 @@ const RatePhoneCallModalAsync: FC<OwnProps> = (props) => {
const { isOpen } = props; const { isOpen } = props;
const RatePhoneCallModal = useModuleLoader(Bundles.Calls, 'RatePhoneCallModal', !isOpen); const RatePhoneCallModal = useModuleLoader(Bundles.Calls, 'RatePhoneCallModal', !isOpen);
// eslint-disable-next-line react/jsx-props-no-spreading
return RatePhoneCallModal ? <RatePhoneCallModal {...props} /> : undefined; return RatePhoneCallModal ? <RatePhoneCallModal {...props} /> : undefined;
}; };

View File

@ -24,8 +24,7 @@ const RatePhoneCallModal: FC<OwnProps> = ({
}) => { }) => {
const { closeCallRatingModal, setCallRating } = getActions(); const { closeCallRatingModal, setCallRating } = getActions();
// eslint-disable-next-line no-null/no-null const inputRef = useRef<HTMLInputElement>();
const inputRef = useRef<HTMLInputElement>(null);
const lang = useOldLang(); const lang = useOldLang();
const [rating, setRating] = useState<number | undefined>(); const [rating, setRating] = useState<number | undefined>();

View File

@ -11,7 +11,6 @@ const AboutMonetizationModalAsync: FC<OwnProps> = (props) => {
const { isOpen } = props; const { isOpen } = props;
const AboutMonetizationModal = useModuleLoader(Bundles.Extra, 'AboutMonetizationModal', !isOpen); const AboutMonetizationModal = useModuleLoader(Bundles.Extra, 'AboutMonetizationModal', !isOpen);
// eslint-disable-next-line react/jsx-props-no-spreading
return AboutMonetizationModal ? <AboutMonetizationModal {...props} /> : undefined; return AboutMonetizationModal ? <AboutMonetizationModal {...props} /> : undefined;
}; };

View File

@ -1,4 +1,4 @@
import type { FC } from '../../lib/teact/teact'; import type { ElementRef, FC } from '../../lib/teact/teact';
import React, { memo, useEffect, useMemo } from '../../lib/teact/teact'; import React, { memo, useEffect, useMemo } from '../../lib/teact/teact';
import { getGlobal } from '../../global'; import { getGlobal } from '../../global';
@ -16,7 +16,7 @@ type OwnProps = {
text: string; text: string;
className?: string; className?: string;
isDisabled?: boolean; isDisabled?: boolean;
ref?: React.RefObject<HTMLSpanElement>; ref?: ElementRef<HTMLSpanElement>;
}; };
const ANIMATION_TIME = 200; const ANIMATION_TIME = 200;

View File

@ -54,7 +54,7 @@ function AnimatedIcon(props: OwnProps) {
noLoop={noLoop} noLoop={noLoop}
onClick={!nonInteractive ? handleClick : undefined} onClick={!nonInteractive ? handleClick : undefined}
onLoad={handleLoad} onLoad={handleLoad}
/* eslint-disable-next-line react/jsx-props-no-spreading */
{...otherProps} {...otherProps}
/> />
); );

View File

@ -33,7 +33,7 @@ function AnimatedIconFromSticker(props: OwnProps) {
tgsUrl={tgsUrl} tgsUrl={tgsUrl}
previewUrl={previewBlobUrl} previewUrl={previewBlobUrl}
thumbDataUri={thumbDataUri} thumbDataUri={thumbDataUri}
// eslint-disable-next-line react/jsx-props-no-spreading
{...otherProps} {...otherProps}
/> />
); );

View File

@ -65,7 +65,7 @@ function AnimatedIconWithPreview(props: OwnProps) {
onLoad={handlePreviewLoad} onLoad={handlePreviewLoad}
/> />
)} )}
{/* eslint-disable-next-line react/jsx-props-no-spreading */} { }
<AnimatedIcon {...otherProps} onLoad={handleAnimationReady} /> <AnimatedIcon {...otherProps} onLoad={handleAnimationReady} />
</div> </div>
); );

View File

@ -1,5 +1,4 @@
import type { RefObject } from 'react'; import type { ElementRef, FC } from '../../lib/teact/teact';
import type { FC } from '../../lib/teact/teact';
import React, { import React, {
getIsHeavyAnimating, getIsHeavyAnimating,
memo, memo,
@ -33,7 +32,7 @@ import useUniqueId from '../../hooks/useUniqueId';
import useBackgroundMode, { isBackgroundModeActive } from '../../hooks/window/useBackgroundMode'; import useBackgroundMode, { isBackgroundModeActive } from '../../hooks/window/useBackgroundMode';
export type OwnProps = { export type OwnProps = {
ref?: RefObject<HTMLDivElement>; ref?: ElementRef<HTMLDivElement>;
renderId?: string; renderId?: string;
className?: string; className?: string;
style?: string; style?: string;
@ -81,8 +80,7 @@ const AnimatedSticker: FC<OwnProps> = ({
onEnded, onEnded,
onLoop, onLoop,
}) => { }) => {
// eslint-disable-next-line no-null/no-null let containerRef = useRef<HTMLDivElement>();
let containerRef = useRef<HTMLDivElement>(null);
if (ref) { if (ref) {
containerRef = ref; containerRef = ref;
} }

View File

@ -1,4 +1,4 @@
import type { FC } from '../../lib/teact/teact'; import type { ElementRef, FC } from '../../lib/teact/teact';
import React, { import React, {
memo, useEffect, useLayoutEffect, useMemo, useRef, useState, memo, useEffect, useLayoutEffect, useMemo, useRef, useState,
} from '../../lib/teact/teact'; } from '../../lib/teact/teact';
@ -123,8 +123,7 @@ const Audio: FC<OwnProps> = ({
const mediaSource = (voice || video); const mediaSource = (voice || video);
const isVoice = Boolean(voice || video); const isVoice = Boolean(voice || video);
const isSeeking = useRef<boolean>(false); const isSeeking = useRef<boolean>(false);
// eslint-disable-next-line no-null/no-null const seekerRef = useRef<HTMLDivElement>();
const seekerRef = useRef<HTMLDivElement>(null);
const lang = useOldLang(); const lang = useOldLang();
const { isRtl } = lang; const { isRtl } = lang;
@ -504,7 +503,7 @@ function renderAudio(
isPlaying: boolean, isPlaying: boolean,
playProgress: number, playProgress: number,
bufferedRanges: BufferedRange[], bufferedRanges: BufferedRange[],
seekerRef: React.Ref<HTMLElement>, seekerRef: ElementRef<HTMLDivElement>,
showProgress?: boolean, showProgress?: boolean,
date?: number, date?: number,
progress?: number, progress?: number,
@ -529,7 +528,8 @@ function renderAudio(
)} )}
{!showSeekline && showProgress && ( {!showSeekline && showProgress && (
<div className="meta" dir={isRtl ? 'rtl' : undefined}> <div className="meta" dir={isRtl ? 'rtl' : undefined}>
{progress ? `${getFileSizeString(audio!.size * progress)} / ` : undefined}{getFileSizeString(audio!.size)} {progress ? `${getFileSizeString(audio.size * progress)} / ` : undefined}
{getFileSizeString(audio.size)}
</div> </div>
)} )}
{!showSeekline && !showProgress && ( {!showSeekline && !showProgress && (
@ -557,8 +557,8 @@ function renderAudio(
function renderVoice( function renderVoice(
media: ApiVoice | ApiVideo, media: ApiVoice | ApiVideo,
seekerRef: React.Ref<HTMLDivElement>, seekerRef: ElementRef<HTMLDivElement>,
waveformCanvasRef: React.Ref<HTMLCanvasElement>, waveformCanvasRef: ElementRef<HTMLCanvasElement>,
playProgress: number, playProgress: number,
isMediaUnread?: boolean, isMediaUnread?: boolean,
isTranscribing?: boolean, isTranscribing?: boolean,
@ -580,7 +580,7 @@ function renderVoice(
<canvas ref={waveformCanvasRef} /> <canvas ref={waveformCanvasRef} />
</div> </div>
{onClickTranscribe && ( {onClickTranscribe && (
// eslint-disable-next-line react/jsx-no-bind
<Button onClick={() => { <Button onClick={() => {
if ((isTranscribed || isTranscriptionError) && onHideTranscription) { if ((isTranscribed || isTranscriptionError) && onHideTranscription) {
onHideTranscription(!isTranscriptionHidden); onHideTranscription(!isTranscriptionHidden);
@ -621,7 +621,7 @@ function renderVoice(
dir="auto" dir="auto"
> >
{playProgress === 0 || playProgress === 1 {playProgress === 0 || playProgress === 1
? formatMediaDuration(media!.duration) : formatMediaDuration(media!.duration * playProgress)} ? formatMediaDuration(media.duration) : formatMediaDuration(media.duration * playProgress)}
</p> </p>
</div> </div>
); );
@ -636,8 +636,7 @@ function useWaveformCanvas(
isMobile = false, isMobile = false,
isReverse = false, isReverse = false,
) { ) {
// eslint-disable-next-line no-null/no-null const canvasRef = useRef<HTMLCanvasElement>();
const canvasRef = useRef<HTMLCanvasElement>(null);
const { data: spikes, peak } = useMemo(() => { const { data: spikes, peak } = useMemo(() => {
if (!media) { if (!media) {
@ -688,12 +687,12 @@ function useWaveformCanvas(
function renderSeekline( function renderSeekline(
playProgress: number, playProgress: number,
bufferedRanges: BufferedRange[], bufferedRanges: BufferedRange[],
seekerRef: React.Ref<HTMLElement>, seekerRef: ElementRef<HTMLDivElement>,
) { ) {
return ( return (
<div <div
className="seekline" className="seekline"
ref={seekerRef as React.Ref<HTMLDivElement>} ref={seekerRef}
> >
{bufferedRanges.map(({ start, end }) => ( {bufferedRanges.map(({ start, end }) => (
<div <div

View File

@ -21,11 +21,11 @@ import {
isAnonymousForwardsChat, isAnonymousForwardsChat,
isChatWithRepliesBot, isChatWithRepliesBot,
isDeletedUser, isDeletedUser,
isUserId,
} from '../../global/helpers'; } from '../../global/helpers';
import { isApiPeerChat, isApiPeerUser } from '../../global/helpers/peers'; import { isApiPeerChat, isApiPeerUser } from '../../global/helpers/peers';
import buildClassName, { createClassNameBuilder } from '../../util/buildClassName'; import buildClassName, { createClassNameBuilder } from '../../util/buildClassName';
import buildStyle from '../../util/buildStyle'; import buildStyle from '../../util/buildStyle';
import { isUserId } from '../../util/entities/ids';
import { getFirstLetters } from '../../util/textFormat'; import { getFirstLetters } from '../../util/textFormat';
import { REM } from './helpers/mediaDimensions'; import { REM } from './helpers/mediaDimensions';
import { getPeerColorClass } from './helpers/peerColor'; import { getPeerColorClass } from './helpers/peerColor';
@ -115,8 +115,7 @@ const Avatar: FC<OwnProps> = ({
}) => { }) => {
const { openStoryViewer } = getActions(); const { openStoryViewer } = getActions();
// eslint-disable-next-line no-null/no-null const ref = useRef<HTMLDivElement>();
const ref = useRef<HTMLDivElement>(null);
const videoLoopCountRef = useRef(0); const videoLoopCountRef = useRef(0);
const isCustomPeer = peer && 'isCustomPeer' in peer; const isCustomPeer = peer && 'isCustomPeer' in peer;
const realPeer = peer && !isCustomPeer ? peer : undefined; const realPeer = peer && !isCustomPeer ? peer : undefined;
@ -246,7 +245,7 @@ const Avatar: FC<OwnProps> = ({
} }
const isRoundedRect = (isCustomPeer && peer.isAvatarSquare) const isRoundedRect = (isCustomPeer && peer.isAvatarSquare)
|| (isForum && !((withStory || withStorySolid) && realPeer?.hasStories)); || (isForum && !((withStory || withStorySolid) && realPeer?.hasStories));
const isPremiumGradient = isCustomPeer && peer.withPremiumGradient; const isPremiumGradient = isCustomPeer && peer.withPremiumGradient;
const customColor = isCustomPeer && peer.customPeerAvatarColor; const customColor = isCustomPeer && peer.customPeerAvatarColor;

View File

@ -13,7 +13,7 @@ import { REM } from './helpers/mediaDimensions';
import useDevicePixelRatio from '../../hooks/window/useDevicePixelRatio'; import useDevicePixelRatio from '../../hooks/window/useDevicePixelRatio';
interface OwnProps { interface OwnProps {
// eslint-disable-next-line react/no-unused-prop-types
peerId: string; peerId: string;
className?: string; className?: string;
size: number; size: number;
@ -53,8 +53,7 @@ function AvatarStoryCircle({
withExtraGap, withExtraGap,
appTheme, appTheme,
}: OwnProps & StateProps) { }: OwnProps & StateProps) {
// eslint-disable-next-line no-null/no-null const ref = useRef<HTMLCanvasElement>();
const ref = useRef<HTMLCanvasElement>(null);
const dpr = useDevicePixelRatio(); const dpr = useDevicePixelRatio();

View File

@ -23,8 +23,7 @@ type OwnProps = {
const MAX_LINES = 4; const MAX_LINES = 4;
const Blockquote = ({ canBeCollapsible, isToggleDisabled, children }: OwnProps) => { const Blockquote = ({ canBeCollapsible, isToggleDisabled, children }: OwnProps) => {
// eslint-disable-next-line no-null/no-null const ref = useRef<HTMLQuoteElement>();
const ref = useRef<HTMLQuoteElement>(null);
const { const {
isCollapsed, isCollapsible, setIsCollapsed, isCollapsed, isCollapsible, setIsCollapsed,
} = useCollapsibleLines(ref, MAX_LINES, undefined, !canBeCollapsible); } = useCollapsibleLines(ref, MAX_LINES, undefined, !canBeCollapsible);

View File

@ -11,7 +11,6 @@ const CalendarModalAsync: FC<OwnProps> = (props) => {
const { isOpen } = props; const { isOpen } = props;
const CalendarModal = useModuleLoader(Bundles.Extra, 'CalendarModal', !isOpen); const CalendarModal = useModuleLoader(Bundles.Extra, 'CalendarModal', !isOpen);
// eslint-disable-next-line react/jsx-props-no-spreading
return CalendarModal ? <CalendarModal {...props} /> : undefined; return CalendarModal ? <CalendarModal {...props} /> : undefined;
}; };

Some files were not shown because too many files have changed in this diff Show More