[Dev] Bump dependencies (#1677)

This commit is contained in:
Alexander Zinchuk 2022-02-02 22:48:08 +01:00
parent 23b96505df
commit 51b2893b39
100 changed files with 7917 additions and 27074 deletions

View File

@ -63,6 +63,7 @@
], ],
"@typescript-eslint/camelcase": "off", "@typescript-eslint/camelcase": "off",
"@typescript-eslint/member-delimiter-style": "error", "@typescript-eslint/member-delimiter-style": "error",
"@typescript-eslint/default-param-last": "off",
"teactn/prefer-separate-component-file": "off" "teactn/prefer-separate-component-file": "off"
}, },
"settings": { "settings": {

View File

@ -1,30 +0,0 @@
# This workflow will do a clean install of node dependencies, build the source code and run tests across different versions of node
# For more information see: https://help.github.com/actions/language-and-framework-guides/using-nodejs-with-github-actions
name: Tests
on:
pull_request:
branches: [ master ]
jobs:
build_and_test:
runs-on: ubuntu-latest
strategy:
matrix:
node-version: [12.x]
steps:
- uses: actions/checkout@v2
- name: Use Node.js ${{ matrix.node-version }}
uses: actions/setup-node@v1
with:
node-version: ${{ matrix.node-version }}
- run: npm ci
- run: npm run gramjs:tl
- run: npm test
env:
TELEGRAM_T_API_ID: ${{ secrets.TELEGRAM_API_ID }}
TELEGRAM_T_API_HASH: ${{ secrets.TELEGRAM_API_HASH }}
TEST_SESSION: ${{ secrets.TEST_SESSION }}

34380
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@ -10,7 +10,6 @@
"build:production": "npm i && rm -rf dist/ && APP_VERSION=$(npm run inc_version --silent) APP_ENV=production npm run build && ./deploy/copy_to_dist.sh", "build:production": "npm i && rm -rf dist/ && APP_VERSION=$(npm run inc_version --silent) APP_ENV=production npm run build && ./deploy/copy_to_dist.sh",
"deploy:production": "npm run build:production && git add -A && git commit -a -m '[Build]' --no-verify && git push", "deploy:production": "npm run build:production && git add -A && git commit -a -m '[Build]' --no-verify && git push",
"inc_version": "echo $((`cat .patch-version` + 1)) > .patch-version && echo \"$(node -p -e \"require('./package.json').version.match(/^\\d+\\.\\d+/)[0]\").$(cat .patch-version)\"", "inc_version": "echo $((`cat .patch-version` + 1)) > .patch-version && echo \"$(node -p -e \"require('./package.json').version.match(/^\\d+\\.\\d+/)[0]\").$(cat .patch-version)\"",
"perf:serve": "cross-env APP_ENV=perf parcel src/index-perf.html",
"lint": "tsc && eslint . --ext .ts,.tsx --ignore-pattern src/lib/gramjs", "lint": "tsc && eslint . --ext .ts,.tsx --ignore-pattern src/lib/gramjs",
"lint:fix": "npm run lint -- --fix", "lint:fix": "npm run lint -- --fix",
"gramjs:tl": "node ./src/lib/gramjs/tl/generateModules.js", "gramjs:tl": "node ./src/lib/gramjs/tl/generateModules.js",
@ -33,83 +32,80 @@
"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.15.0", "@babel/core": "^7.16.12",
"@babel/plugin-proposal-class-properties": "^7.14.5", "@babel/plugin-proposal-class-properties": "^7.16.7",
"@babel/plugin-proposal-nullish-coalescing-operator": "^7.14.5", "@babel/plugin-proposal-nullish-coalescing-operator": "^7.16.7",
"@babel/plugin-syntax-nullish-coalescing-operator": "^7.8.3", "@babel/plugin-syntax-nullish-coalescing-operator": "^7.8.3",
"@babel/preset-env": "^7.15.0", "@babel/preset-env": "^7.16.11",
"@babel/preset-react": "^7.14.5", "@babel/preset-react": "^7.16.7",
"@babel/preset-typescript": "^7.15.0", "@babel/preset-typescript": "^7.16.7",
"@peculiar/webcrypto": "^1.1.7", "@peculiar/webcrypto": "^1.2.3",
"@testing-library/jest-dom": "^5.14.1", "@testing-library/jest-dom": "^5.16.1",
"@types/croppie": "^2.6.1", "@types/croppie": "^2.6.1",
"@types/css-font-loading-module": "0.0.6", "@types/css-font-loading-module": "0.0.7",
"@types/dom-mediacapture-record": "^1.0.10", "@types/dom-mediacapture-record": "^1.0.11",
"@types/jest": "^27.0.1", "@types/jest": "^27.4.0",
"@types/react": "^17.0.19", "@types/react": "^17.0.38",
"@types/react-dom": "^17.0.9", "@types/react-dom": "^17.0.11",
"@types/resize-observer-browser": "^0.1.6", "@types/resize-observer-browser": "^0.1.6",
"@types/wicg-mediasession": "^1.1.2", "@types/wicg-mediasession": "^1.1.3",
"@typescript-eslint/eslint-plugin": "^4.29.1", "@typescript-eslint/eslint-plugin": "^5.10.1",
"@typescript-eslint/parser": "^4.29.1", "@typescript-eslint/parser": "^5.10.1",
"@webpack-cli/serve": "^1.5.1", "@webpack-cli/serve": "^1.6.1",
"autoprefixer": "^10.3.7", "autoprefixer": "^10.4.2",
"babel-eslint": "^10.1.0", "babel-loader": "^8.2.3",
"babel-loader": "^8.2.2",
"browserlist": "^1.0.1", "browserlist": "^1.0.1",
"buffer": "^6.0.3", "buffer": "^6.0.3",
"cross-env": "^7.0.3", "cross-env": "^7.0.3",
"css-loader": "^6.2.0", "css-loader": "^6.5.1",
"css-minimizer-webpack-plugin": "^3.0.2", "css-minimizer-webpack-plugin": "^3.4.1",
"dotenv": "^10.0.0", "dotenv": "^14.3.2",
"eslint": "^7.32.0", "eslint": "^8.7.0",
"eslint-config-airbnb-typescript": "^12.3.1", "eslint-config-airbnb-typescript": "^16.1.0",
"eslint-config-react-app": "^6.0.0", "eslint-config-react-app": "^7.0.0",
"eslint-import-resolver-webpack": "^0.13.1", "eslint-import-resolver-webpack": "^0.13.2",
"eslint-plugin-flowtype": "^5.9.0", "eslint-plugin-flowtype": "^8.0.3",
"eslint-plugin-import": "^2.24.0", "eslint-plugin-import": "^2.25.4",
"eslint-plugin-jsx-a11y": "^6.4.1", "eslint-plugin-jsx-a11y": "^6.5.1",
"eslint-plugin-no-async-without-await": "^1.2.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.24.0", "eslint-plugin-react": "^7.28.0",
"eslint-plugin-react-hooks": "^4.2.0", "eslint-plugin-react-hooks": "^4.3.0",
"eslint-plugin-teactn": "github:korenskoy/eslint-plugin-teactn#8fbd2b9", "eslint-plugin-teactn": "github:korenskoy/eslint-plugin-teactn#8fbd2b9",
"file-loader": "^6.2.0", "file-loader": "^6.2.0",
"html-webpack-plugin": "^5.3.2", "html-webpack-plugin": "^5.5.0",
"husky": "^7.0.1", "husky": "^7.0.4",
"jest": "^27.0.6", "jest": "^27.4.7",
"jest-raw-loader": "^1.0.1", "jest-raw-loader": "^1.0.1",
"lint-staged": "^11.1.2", "lint-staged": "^12.3.2",
"mini-css-extract-plugin": "^2.2.0", "mini-css-extract-plugin": "^2.5.3",
"node-sass": "^6.0.1", "postcss-loader": "^6.2.1",
"optimize-css-assets-webpack-plugin": "^6.0.1", "postcss-modules": "^4.3.0",
"postcss-loader": "^6.1.1",
"postcss-modules": "^4.2.2",
"raw-loader": "^4.0.2", "raw-loader": "^4.0.2",
"react": "^17.0.2", "react": "^17.0.2",
"replace-in-file": "^6.2.0", "replace-in-file": "^6.3.2",
"sass": "^1.38.0", "sass": "^1.49.0",
"sass-loader": "^12.1.0", "sass-loader": "^12.4.0",
"style-loader": "^3.2.1", "style-loader": "^3.3.1",
"terser": "^5.7.1", "terser": "^5.10.0",
"terser-webpack-plugin": "^5.1.4", "terser-webpack-plugin": "^5.3.0",
"ts-node": "^10.2.1", "ts-node": "^10.4.0",
"typescript": "^4.3.5", "typescript": "^4.5.5",
"url-loader": "^4.1.1", "url-loader": "^4.1.1",
"webpack": "^5.51.0", "webpack": "^5.67.0",
"webpack-bundle-analyzer": "^4.4.2", "webpack-bundle-analyzer": "^4.5.0",
"webpack-cli": "^4.8.0", "webpack-cli": "^4.9.2",
"webpack-dev-server": "^3.11.2", "webpack-dev-server": "^4.7.3",
"webpack-merge": "^5.8.0" "webpack-merge": "^5.8.0"
}, },
"dependencies": { "dependencies": {
"@cryptography/aes": "^0.1.1", "@cryptography/aes": "^0.1.1",
"async-mutex": "^0.3.1", "async-mutex": "^0.3.2",
"big-integer": "github:painor/BigInteger.js", "big-integer": "github:painor/BigInteger.js",
"croppie": "^2.6.5", "croppie": "^2.6.5",
"emoji-data-ios": "github:korenskoy/emoji-data-ios#162c821", "emoji-data-ios": "github:korenskoy/emoji-data-ios#162c821",
"events": "^3.3.0", "events": "^3.3.0",
"idb-keyval": "^5.1.3", "idb-keyval": "^6.1.0",
"opus-recorder": "github:Ajaxy/opus-recorder", "opus-recorder": "github:Ajaxy/opus-recorder",
"os-browserify": "^0.3.0", "os-browserify": "^0.3.0",
"pako": "^2.0.4", "pako": "^2.0.4",

View File

@ -11,10 +11,6 @@ declare namespace React {
teactOrderKey?: number; teactOrderKey?: number;
} }
interface ImgHTMLAttributes<T> extends HTMLAttributes<T> {
loading?: 'auto' | 'eager' | 'lazy';
}
interface VideoHTMLAttributes { interface VideoHTMLAttributes {
srcObject?: MediaStream; srcObject?: MediaStream;
} }
@ -55,10 +51,6 @@ declare module 'pako/dist/pako_inflate' {
function inflate(...args: any[]): string; function inflate(...args: any[]): string;
} }
type WindowWithPerf =
typeof window
& { perf: AnyLiteral };
interface TEncodedImage { interface TEncodedImage {
result: Uint8ClampedArray; result: Uint8ClampedArray;
width: number; width: number;
@ -70,15 +62,6 @@ interface IWebpWorker extends Worker {
requests: Map<string, (value: PromiseLike<TEncodedImage>) => void>; requests: Map<string, (value: PromiseLike<TEncodedImage>) => void>;
} }
interface Window {
ClipboardItem?: any;
requestIdleCallback: (cb: AnyToVoidFunction, options: { timeout?: number }) => void;
}
interface Clipboard {
write?: any;
}
interface Document { interface Document {
mozFullScreenElement: any; mozFullScreenElement: any;
webkitFullscreenElement: any; webkitFullscreenElement: any;
@ -97,3 +80,20 @@ interface Navigator {
// PWA badging extensions https://w3c.github.io/badging/ // PWA badging extensions https://w3c.github.io/badging/
setAppBadge?(count: number): Promise<void>; setAppBadge?(count: number): Promise<void>;
} }
// Fix to make Boolean() work as !!
// https://github.com/microsoft/TypeScript/issues/16655
type Falsy = false | 0 | '' | null | undefined;
interface BooleanConstructor {
new<T>(value: T | Falsy): value is T;
<T>(value: T | Falsy): value is T;
readonly prototype: Boolean;
}
interface Array<T> {
filter<S extends T>(predicate: BooleanConstructor, thisArg?: any): Exclude<S, Falsy>[];
}
interface ReadonlyArray<T> {
filter<S extends T>(predicate: BooleanConstructor, thisArg?: any): Exclude<S, Falsy>[];
}

View File

@ -191,7 +191,7 @@ export async function searchChats({ query }: { query: string }) {
const allChats = result.chats.concat(result.users) const allChats = result.chats.concat(result.users)
.map((user) => buildApiChatFromPreview(user)) .map((user) => buildApiChatFromPreview(user))
.filter<ApiChat>(Boolean as any); .filter<ApiChat>(Boolean as any);
const allUsers = result.users.map(buildApiUser).filter((user) => !!user && !user.isSelf) as ApiUser[]; const allUsers = result.users.map(buildApiUser).filter((user) => Boolean(user) && !user.isSelf) as ApiUser[];
return { return {
localChats: allChats.filter((r) => localPeerIds.includes(r.id)), localChats: allChats.filter((r) => localPeerIds.includes(r.id)),

View File

@ -92,7 +92,7 @@ export async function init(_onUpdate: OnApiUpdate, initialArgs: ApiInitialArgs)
onError: onAuthError, onError: onAuthError,
initialMethod: platform === 'iOS' || platform === 'Android' ? 'phoneNumber' : 'qrCode', initialMethod: platform === 'iOS' || platform === 'Android' ? 'phoneNumber' : 'qrCode',
}); });
} catch (err) { } catch (err: any) {
// eslint-disable-next-line no-console // eslint-disable-next-line no-console
console.error(err); console.error(err);
@ -207,7 +207,7 @@ export async function invokeRequest<T extends GramJs.AnyRequest>(
} }
return shouldReturnTrue ? result && true : result; return shouldReturnTrue ? result && true : result;
} catch (err) { } catch (err: any) {
if (DEBUG) { if (DEBUG) {
// eslint-disable-next-line no-console // eslint-disable-next-line no-console
console.log(`[GramJs/client] INVOKE ERROR ${request.className}`); console.log(`[GramJs/client] INVOKE ERROR ${request.className}`);
@ -323,7 +323,7 @@ async function handleTerminatedSession() {
await invokeRequest(new GramJs.users.GetFullUser({ await invokeRequest(new GramJs.users.GetFullUser({
id: new GramJs.InputUserSelf(), id: new GramJs.InputUserSelf(),
}), undefined, true); }), undefined, true);
} catch (err) { } catch (err: any) {
if (err.message === 'AUTH_KEY_UNREGISTERED') { if (err.message === 'AUTH_KEY_UNREGISTERED') {
onUpdate({ onUpdate({
'@type': 'updateConnectionState', '@type': 'updateConnectionState',

View File

@ -94,7 +94,7 @@ export async function fetchMessages({
}), }),
...pagination, ...pagination,
}), undefined, true); }), undefined, true);
} catch (err) { } catch (err: any) {
if (err.message === 'CHANNEL_PRIVATE') { if (err.message === 'CHANNEL_PRIVATE') {
onUpdate({ onUpdate({
'@type': 'updateChat', '@type': 'updateChat',
@ -146,7 +146,7 @@ export async function fetchMessage({ chat, messageId }: { chat: ApiChat; message
undefined, undefined,
true, true,
); );
} catch (err) { } catch (err: any) {
const { message } = err; const { message } = err;
// When fetching messages for the bot @replies, there may be situations when the user was banned // When fetching messages for the bot @replies, there may be situations when the user was banned

View File

@ -53,7 +53,7 @@ export async function checkPassword(currentPassword: string) {
await updateTwoFaSettings({ isCheckPassword: true, currentPassword }); await updateTwoFaSettings({ isCheckPassword: true, currentPassword });
return true; return true;
} catch (err) { } catch (err: any) {
onError(err); onError(err);
return false; return false;
@ -65,7 +65,7 @@ export async function clearPassword(currentPassword: string) {
await updateTwoFaSettings({ currentPassword }); await updateTwoFaSettings({ currentPassword });
return true; return true;
} catch (err) { } catch (err: any) {
onError(err); onError(err);
return false; return false;
@ -84,7 +84,7 @@ export async function updatePassword(currentPassword: string, password: string,
}); });
return true; return true;
} catch (err) { } catch (err: any) {
onError(err); onError(err);
return false; return false;
@ -102,7 +102,7 @@ export async function updateRecoveryEmail(currentPassword: string, email: string
}); });
return true; return true;
} catch (err) { } catch (err: any) {
onError(err); onError(err);
return false; return false;

View File

@ -98,7 +98,7 @@ export async function fetchTopUsers() {
return undefined; return undefined;
} }
const users = topPeers.users.map(buildApiUser).filter((user) => !!user && !user.isSelf) as ApiUser[]; const users = topPeers.users.map(buildApiUser).filter((user) => Boolean(user) && !user.isSelf) as ApiUser[];
const ids = users.map(({ id }) => id); const ids = users.map(({ id }) => id);
return { return {

View File

@ -230,7 +230,7 @@ export function updater(update: Update, originRequest?: GramJs.AnyRequest) {
} else if (action instanceof GramJs.MessageActionChatDeleteUser) { } else if (action instanceof GramJs.MessageActionChatDeleteUser) {
// eslint-disable-next-line no-underscore-dangle // 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 && !!e.self && e.id === action.userId e instanceof GramJs.User && Boolean(e.self) && e.id === action.userId
))) { ))) {
onUpdate({ onUpdate({
'@type': 'updateChat', '@type': 'updateChat',
@ -248,7 +248,7 @@ export function updater(update: Update, originRequest?: GramJs.AnyRequest) {
} else if (action instanceof GramJs.MessageActionChatAddUser) { } else if (action instanceof GramJs.MessageActionChatAddUser) {
// eslint-disable-next-line no-underscore-dangle // 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 && !!e.self && action.users.includes(e.id) e instanceof GramJs.User && Boolean(e.self) && action.users.includes(e.id)
))) { ))) {
onUpdate({ onUpdate({
'@type': 'updateChatJoin', '@type': 'updateChatJoin',

View File

@ -58,7 +58,7 @@ onmessage = async (message: OriginMessageEvent) => {
response, response,
}, arrayBuffer); }, arrayBuffer);
} }
} catch (error) { } catch (error: any) {
if (DEBUG) { if (DEBUG) {
// eslint-disable-next-line no-console // eslint-disable-next-line no-console
console.error(error); console.error(error);

View File

@ -402,7 +402,7 @@ export default memo(withGlobal<OwnProps>(
isSpeakerEnabled: !isSpeakerDisabled, isSpeakerEnabled: !isSpeakerDisabled,
participantsCount, participantsCount,
meParticipant: selectGroupCallParticipant(global, groupCallId, global.currentUserId!), meParticipant: selectGroupCallParticipant(global, groupCallId, global.currentUserId!),
isGroupCallPanelHidden: !!global.groupCalls.isGroupCallPanelHidden, isGroupCallPanelHidden: Boolean(global.groupCalls.isGroupCallPanelHidden),
isAdmin: selectIsAdminInActiveGroupCall(global), isAdmin: selectIsAdminInActiveGroupCall(global),
participants, participants,
}; };

View File

@ -16,7 +16,7 @@ const OutlinedMicrophoneIcon: FC<OwnProps> = ({
const isSpeaking = (participant.amplitude || 0) > THRESHOLD; const isSpeaking = (participant.amplitude || 0) > THRESHOLD;
const isRaiseHand = Boolean(participant.raiseHandRating); const isRaiseHand = Boolean(participant.raiseHandRating);
const prevIsRaiseHand = usePrevious(isRaiseHand); const prevIsRaiseHand = usePrevious(isRaiseHand);
const canSelfUnmute = !!participant?.canSelfUnmute; const canSelfUnmute = Boolean(participant?.canSelfUnmute);
const shouldRaiseHand = !canSelfUnmute && isMuted; const shouldRaiseHand = !canSelfUnmute && isMuted;
const prevIsMuted = usePrevious(isMuted); const prevIsMuted = usePrevious(isMuted);

View File

@ -301,7 +301,7 @@ const CalendarModal: FC<OwnProps> = ({
selectedDay === formatDay(currentYear, currentMonth, gridDate) && 'selected', selectedDay === formatDay(currentYear, currentMonth, gridDate) && 'selected',
)} )}
> >
{!!gridDate && ( {Boolean(gridDate) && (
<span>{gridDate}</span> <span>{gridDate}</span>
)} )}
</div> </div>

View File

@ -85,7 +85,7 @@ const ChatExtra: FC<OwnProps & StateProps> = ({
return ( return (
<div className="ChatExtra"> <div className="ChatExtra">
{formattedNumber && !!formattedNumber.length && ( {formattedNumber && Boolean(formattedNumber.length) && (
<ListItem icon="phone" multiline narrow ripple onClick={() => copy(formattedNumber, lang('Phone'))}> <ListItem icon="phone" multiline narrow ripple onClick={() => copy(formattedNumber, lang('Phone'))}>
<span className="title" dir="auto">{formattedNumber}</span> <span className="title" dir="auto">{formattedNumber}</span>
<span className="subtitle">{lang('Phone')}</span> <span className="subtitle">{lang('Phone')}</span>
@ -103,7 +103,7 @@ const ChatExtra: FC<OwnProps & StateProps> = ({
<span className="subtitle">{lang('Username')}</span> <span className="subtitle">{lang('Username')}</span>
</ListItem> </ListItem>
)} )}
{description && !!description.length && ( {description && Boolean(description.length) && (
<ListItem <ListItem
icon="info" icon="info"
multiline multiline

View File

@ -102,9 +102,9 @@ export default memo(withGlobal<OwnProps>(
const isPrivateChat = isUserId(chatId); const isPrivateChat = isUserId(chatId);
const isChatWithSelf = selectIsChatWithSelf(global, chatId); const isChatWithSelf = selectIsChatWithSelf(global, chatId);
const chat = selectChat(global, chatId); const chat = selectChat(global, chatId);
const isChannel = !!chat && isChatChannel(chat); const isChannel = Boolean(chat) && isChatChannel(chat);
const isGroup = !!chat && isChatBasicGroup(chat); const isGroup = Boolean(chat) && isChatBasicGroup(chat);
const isSuperGroup = !!chat && isChatSuperGroup(chat); const isSuperGroup = Boolean(chat) && isChatSuperGroup(chat);
const canPinForAll = (isPrivateChat && !isChatWithSelf) || isSuperGroup || isGroup; const canPinForAll = (isPrivateChat && !isChatWithSelf) || isSuperGroup || isGroup;
const contactName = chat && isUserId(chat.id) const contactName = chat && isUserId(chat.id)
? getUserFirstOrLastName(selectUser(global, getPrivateChatUserId(chat)!)) ? getUserFirstOrLastName(selectUser(global, getPrivateChatUserId(chat)!))

View File

@ -5,7 +5,7 @@ const DETECT_UP_TO = 3;
const MAX_LENGTH = DETECT_UP_TO * 8; // Maximum 8 per one emoji. const MAX_LENGTH = DETECT_UP_TO * 8; // Maximum 8 per one emoji.
const RE_EMOJI_ONLY = new RegExp(`^(?:${twemojiRegex.source})+$`, ''); const RE_EMOJI_ONLY = new RegExp(`^(?:${twemojiRegex.source})+$`, '');
export default (text: string): number | false => { const parseEmojiOnlyString = (text: string): number | false => {
if (text.length > MAX_LENGTH) { if (text.length > MAX_LENGTH) {
return false; return false;
} }
@ -27,3 +27,5 @@ export default (text: string): number | false => {
return emojiCount; return emojiCount;
}; };
export default parseEmojiOnlyString;

View File

@ -54,7 +54,7 @@ export function renderMessageSummary(
emojiWithSpace, emojiWithSpace,
...(Array.isArray(description) ? description : [description]), ...(Array.isArray(description) ? description : [description]),
shouldAddEllipsis && '...', shouldAddEllipsis && '...',
].filter(Boolean); ].filter<TextPart>(Boolean);
} }
export function renderMessageText( export function renderMessageText(

View File

@ -116,7 +116,7 @@ const ChatMessageResults: FC<OwnProps & StateProps> = ({
description={lang('ChatList.Search.NoResultsDescription')} description={lang('ChatList.Search.NoResultsDescription')}
/> />
)} )}
{!!foundMessages.length && foundMessages.map(renderFoundMessage)} {foundMessages.map(renderFoundMessage)}
</InfiniteScroll> </InfiniteScroll>
</div> </div>
); );

View File

@ -204,7 +204,7 @@ const ChatResults: FC<OwnProps & StateProps> = ({
description={lang('ChatList.Search.NoResultsDescription')} description={lang('ChatList.Search.NoResultsDescription')}
/> />
)} )}
{!!localResults.length && ( {Boolean(localResults.length) && (
<div className="chat-selection no-selection no-scrollbar" dir={lang.isRtl ? 'rtl' : undefined}> <div className="chat-selection no-selection no-scrollbar" dir={lang.isRtl ? 'rtl' : undefined}>
{localResults.map((id) => ( {localResults.map((id) => (
<PickerSelectedItem <PickerSelectedItem
@ -215,7 +215,7 @@ const ChatResults: FC<OwnProps & StateProps> = ({
))} ))}
</div> </div>
)} )}
{!!localResults.length && ( {Boolean(localResults.length) && (
<div className="search-section"> <div className="search-section">
<h3 className="section-heading" dir={lang.isRtl ? 'auto' : undefined}> <h3 className="section-heading" dir={lang.isRtl ? 'auto' : undefined}>
{localResults.length > LESS_LIST_ITEMS_AMOUNT && ( {localResults.length > LESS_LIST_ITEMS_AMOUNT && (
@ -239,7 +239,7 @@ const ChatResults: FC<OwnProps & StateProps> = ({
})} })}
</div> </div>
)} )}
{!!globalResults.length && ( {Boolean(globalResults.length) && (
<div className="search-section"> <div className="search-section">
<h3 className="section-heading" dir={lang.isRtl ? 'auto' : undefined}> <h3 className="section-heading" dir={lang.isRtl ? 'auto' : undefined}>
{globalResults.length > LESS_LIST_ITEMS_AMOUNT && ( {globalResults.length > LESS_LIST_ITEMS_AMOUNT && (
@ -264,7 +264,7 @@ const ChatResults: FC<OwnProps & StateProps> = ({
})} })}
</div> </div>
)} )}
{!!foundMessages.length && ( {Boolean(foundMessages.length) && (
<div className="search-section"> <div className="search-section">
<h3 className="section-heading" dir={lang.isRtl ? 'auto' : undefined}>{lang('SearchMessages')}</h3> <h3 className="section-heading" dir={lang.isRtl ? 'auto' : undefined}>{lang('SearchMessages')}</h3>
{foundMessages.map(renderFoundMessage)} {foundMessages.map(renderFoundMessage)}

View File

@ -298,7 +298,7 @@ const SettingsFoldersEdit: FC<OwnProps & StateProps> = ({
</div> </div>
<FloatingActionButton <FloatingActionButton
isShown={!!state.isTouched} isShown={Boolean(state.isTouched)}
disabled={state.isLoading} disabled={state.isLoading}
onClick={handleSubmit} onClick={handleSubmit}
ariaLabel={state.mode === 'edit' ? 'Save changes' : 'Create folder'} ariaLabel={state.mode === 'edit' ? 'Save changes' : 'Create folder'}

View File

@ -189,7 +189,7 @@ const SettingsFoldersMain: FC<OwnProps & StateProps> = ({
) : <Loading />} ) : <Loading />}
</div> </div>
{(recommendedChatFolders && !!recommendedChatFolders.length) && ( {(recommendedChatFolders && Boolean(recommendedChatFolders.length)) && (
<div className="settings-item pt-3"> <div className="settings-item pt-3">
<h4 className="settings-item-header mb-3" dir={lang.isRtl ? 'rtl' : undefined}> <h4 className="settings-item-header mb-3" dir={lang.isRtl ? 'rtl' : undefined}>
{lang('FilterRecommended')} {lang('FilterRecommended')}

View File

@ -1,7 +1,9 @@
const units = ['bytes', 'kB', 'MB', 'GB', 'TB', 'PB']; const units = ['bytes', 'kB', 'MB', 'GB', 'TB', 'PB'];
export default (bytes: number) => { const formatFileSize = (bytes: number) => {
const number = bytes === 0 ? 0 : Math.floor(Math.log(bytes) / Math.log(1024)); const number = bytes === 0 ? 0 : Math.floor(Math.log(bytes) / Math.log(1024));
return `${(bytes / 1024 ** Math.floor(number)).toFixed(1)} ${units[number]}`; return `${(bytes / 1024 ** Math.floor(number)).toFixed(1)} ${units[number]}`;
}; };
export default formatFileSize;

View File

@ -126,7 +126,7 @@ const MessageSelectToolbar: FC<OwnProps & StateProps> = ({
{formattedMessagesCount} {formattedMessagesCount}
</span> </span>
{!!selectedMessagesCount && ( {Boolean(selectedMessagesCount) && (
<div className="MessageSelectToolbar-actions"> <div className="MessageSelectToolbar-actions">
{messageListType !== 'scheduled' && ( {messageListType !== 'scheduled' && (
renderButton( renderButton(

View File

@ -376,8 +376,8 @@ const MiddleHeader: FC<OwnProps & StateProps> = ({
<GroupCallTopPane <GroupCallTopPane
hasPinnedOffset={ hasPinnedOffset={
(shouldRenderPinnedMessage && !!renderingPinnedMessage) (shouldRenderPinnedMessage && Boolean(renderingPinnedMessage))
|| (shouldRenderAudioPlayer && !!renderingAudioMessage) || (shouldRenderAudioPlayer && Boolean(renderingAudioMessage))
} }
chatId={chatId} chatId={chatId}
/> />

View File

@ -682,7 +682,7 @@ const Composer: FC<OwnProps & StateProps> = ({
+ (isWhenOnline ? 0 : serverTimeOffset); + (isWhenOnline ? 0 : serverTimeOffset);
if (!scheduledMessageArgs || Object.keys(restArgs).length === 0) { if (!scheduledMessageArgs || Object.keys(restArgs).length === 0) {
void handleSend(!!isSilent, scheduledAt); void handleSend(Boolean(isSilent), scheduledAt);
} else { } else {
sendMessage({ sendMessage({
...scheduledMessageArgs, ...scheduledMessageArgs,

View File

@ -180,7 +180,7 @@ const TextFormatter: FC<OwnProps> = ({
if (key === 'monospace' || key === 'strikethrough') { if (key === 'monospace' || key === 'strikethrough') {
if (Object.keys(selectedTextFormats).some( if (Object.keys(selectedTextFormats).some(
(fKey) => fKey !== key && !!selectedTextFormats[fKey as keyof ISelectedTextFormats], (fKey) => fKey !== key && Boolean(selectedTextFormats[fKey as keyof ISelectedTextFormats]),
)) { )) {
return 'disabled'; return 'disabled';
} }
@ -224,7 +224,7 @@ const TextFormatter: FC<OwnProps> = ({
// Somehow re-applying 'bold' command to already bold text doesn't work // Somehow re-applying 'bold' command to already bold text doesn't work
document.execCommand(selectedFormats.bold ? 'removeFormat' : 'bold'); document.execCommand(selectedFormats.bold ? 'removeFormat' : 'bold');
Object.keys(selectedFormats).forEach((key) => { Object.keys(selectedFormats).forEach((key) => {
if ((key === 'italic' || key === 'underline') && !!selectedFormats[key]) { if ((key === 'italic' || key === 'underline') && Boolean(selectedFormats[key])) {
document.execCommand(key); document.execCommand(key);
} }
}); });
@ -395,7 +395,7 @@ const TextFormatter: FC<OwnProps> = ({
const linkUrlConfirmClassName = buildClassName( const linkUrlConfirmClassName = buildClassName(
'TextFormatter-link-url-confirm', 'TextFormatter-link-url-confirm',
!!linkUrl.length && 'shown', Boolean(linkUrl.length) && 'shown',
); );
const style = anchorPosition const style = anchorPosition

View File

@ -7,7 +7,7 @@ import { EDITABLE_INPUT_ID, EDITABLE_INPUT_MODAL_ID } from '../../../../config';
const CLIPBOARD_ACCEPTED_TYPES = ['image/png', 'image/jpeg', 'image/gif']; const CLIPBOARD_ACCEPTED_TYPES = ['image/png', 'image/jpeg', 'image/gif'];
const MAX_MESSAGE_LENGTH = 4096; const MAX_MESSAGE_LENGTH = 4096;
export default ( const useClipboardPaste = (
insertTextAndUpdateCursor: (text: string, inputId?: string) => void, insertTextAndUpdateCursor: (text: string, inputId?: string) => void,
setAttachments: StateHookSetter<ApiAttachment[]>, setAttachments: StateHookSetter<ApiAttachment[]>,
editedMessage: ApiMessage | undefined, editedMessage: ApiMessage | undefined,
@ -55,3 +55,5 @@ export default (
}; };
}, [insertTextAndUpdateCursor, editedMessage, setAttachments]); }, [insertTextAndUpdateCursor, editedMessage, setAttachments]);
}; };
export default useClipboardPaste;

View File

@ -17,7 +17,7 @@ import { IS_TOUCH_ENV } from '../../../../util/environment';
let currentChatId: string | undefined; let currentChatId: string | undefined;
let currentThreadId: number | undefined; let currentThreadId: number | undefined;
export default ( const useDraft = (
draft: ApiFormattedText | undefined, draft: ApiFormattedText | undefined,
chatId: string, chatId: string,
threadId: number, threadId: number,
@ -105,3 +105,5 @@ export default (
useBackgroundMode(handleBlur); useBackgroundMode(handleBlur);
useBeforeUnload(handleBlur); useBeforeUnload(handleBlur);
}; };
export default useDraft;

View File

@ -9,7 +9,7 @@ import getMessageTextAsHtml from '../helpers/getMessageTextAsHtml';
import focusEditableElement from '../../../../util/focusEditableElement'; import focusEditableElement from '../../../../util/focusEditableElement';
import { hasMessageMedia } from '../../../../modules/helpers'; import { hasMessageMedia } from '../../../../modules/helpers';
export default ( const useEditing = (
htmlRef: { current: string }, htmlRef: { current: string },
setHtml: (html: string) => void, setHtml: (html: string) => void,
editedMessage: ApiMessage | undefined, editedMessage: ApiMessage | undefined,
@ -57,3 +57,5 @@ export default (
return handleEditComplete; return handleEditComplete;
}; };
export default useEditing;

View File

@ -8,7 +8,7 @@ import captureEscKeyListener from '../../../../util/captureEscKeyListener';
type ActiveVoiceRecording = { stop: () => Promise<voiceRecording.Result>; pause: NoneToVoidFunction } | undefined; type ActiveVoiceRecording = { stop: () => Promise<voiceRecording.Result>; pause: NoneToVoidFunction } | undefined;
export default () => { const useVoiceRecording = () => {
// eslint-disable-next-line no-null/no-null // eslint-disable-next-line no-null/no-null
const recordButtonRef = useRef<HTMLButtonElement>(null); const recordButtonRef = useRef<HTMLButtonElement>(null);
const [activeVoiceRecording, setActiveVoiceRecording] = useState<ActiveVoiceRecording>(); const [activeVoiceRecording, setActiveVoiceRecording] = useState<ActiveVoiceRecording>();
@ -93,3 +93,5 @@ export default () => {
startRecordTimeRef, startRecordTimeRef,
}; };
}; };
export default useVoiceRecording;

View File

@ -1041,7 +1041,9 @@ export default memo(withGlobal<OwnProps>(
isInSelectMode: selectIsInSelectMode(global), isInSelectMode: selectIsInSelectMode(global),
isSelected, isSelected,
isGroupSelected: ( isGroupSelected: (
!!message.groupedId && !message.isInAlbum && selectIsDocumentGroupSelected(global, chatId, message.groupedId) Boolean(message.groupedId)
&& !message.isInAlbum
&& selectIsDocumentGroupSelected(global, chatId, message.groupedId)
), ),
threadId, threadId,
isDownloading, isDownloading,

View File

@ -202,7 +202,7 @@ const ManageGroupAdminRights: FC<OwnProps & StateProps> = ({
<div className="ListItem no-selection"> <div className="ListItem no-selection">
<Checkbox <Checkbox
name="changeInfo" name="changeInfo"
checked={!!permissions.changeInfo} checked={Boolean(permissions.changeInfo)}
label={lang(isChannel ? 'EditAdminChangeChannelInfo' : 'EditAdminChangeGroupInfo')} label={lang(isChannel ? 'EditAdminChangeChannelInfo' : 'EditAdminChangeGroupInfo')}
blocking blocking
disabled={getControlIsDisabled('changeInfo')} disabled={getControlIsDisabled('changeInfo')}
@ -213,7 +213,7 @@ const ManageGroupAdminRights: FC<OwnProps & StateProps> = ({
<div className="ListItem no-selection"> <div className="ListItem no-selection">
<Checkbox <Checkbox
name="postMessages" name="postMessages"
checked={!!permissions.postMessages} checked={Boolean(permissions.postMessages)}
label={lang('EditAdminPostMessages')} label={lang('EditAdminPostMessages')}
blocking blocking
disabled={getControlIsDisabled('postMessages')} disabled={getControlIsDisabled('postMessages')}
@ -225,7 +225,7 @@ const ManageGroupAdminRights: FC<OwnProps & StateProps> = ({
<div className="ListItem no-selection"> <div className="ListItem no-selection">
<Checkbox <Checkbox
name="editMessages" name="editMessages"
checked={!!permissions.editMessages} checked={Boolean(permissions.editMessages)}
label={lang('EditAdminEditMessages')} label={lang('EditAdminEditMessages')}
blocking blocking
disabled={getControlIsDisabled('editMessages')} disabled={getControlIsDisabled('editMessages')}
@ -236,7 +236,7 @@ const ManageGroupAdminRights: FC<OwnProps & StateProps> = ({
<div className="ListItem no-selection"> <div className="ListItem no-selection">
<Checkbox <Checkbox
name="deleteMessages" name="deleteMessages"
checked={!!permissions.deleteMessages} checked={Boolean(permissions.deleteMessages)}
label={lang(isChannel ? 'EditAdminDeleteMessages' : 'EditAdminGroupDeleteMessages')} label={lang(isChannel ? 'EditAdminDeleteMessages' : 'EditAdminGroupDeleteMessages')}
blocking blocking
disabled={getControlIsDisabled('deleteMessages')} disabled={getControlIsDisabled('deleteMessages')}
@ -247,7 +247,7 @@ const ManageGroupAdminRights: FC<OwnProps & StateProps> = ({
<div className="ListItem no-selection"> <div className="ListItem no-selection">
<Checkbox <Checkbox
name="banUsers" name="banUsers"
checked={!!permissions.banUsers} checked={Boolean(permissions.banUsers)}
label={lang('EditAdminBanUsers')} label={lang('EditAdminBanUsers')}
blocking blocking
disabled={getControlIsDisabled('banUsers')} disabled={getControlIsDisabled('banUsers')}
@ -258,7 +258,7 @@ const ManageGroupAdminRights: FC<OwnProps & StateProps> = ({
<div className="ListItem no-selection"> <div className="ListItem no-selection">
<Checkbox <Checkbox
name="inviteUsers" name="inviteUsers"
checked={!!permissions.inviteUsers} checked={Boolean(permissions.inviteUsers)}
label={lang('EditAdminAddUsers')} label={lang('EditAdminAddUsers')}
blocking blocking
disabled={getControlIsDisabled('inviteUsers')} disabled={getControlIsDisabled('inviteUsers')}
@ -269,7 +269,7 @@ const ManageGroupAdminRights: FC<OwnProps & StateProps> = ({
<div className="ListItem no-selection"> <div className="ListItem no-selection">
<Checkbox <Checkbox
name="pinMessages" name="pinMessages"
checked={!!permissions.pinMessages} checked={Boolean(permissions.pinMessages)}
label={lang('EditAdminPinMessages')} label={lang('EditAdminPinMessages')}
blocking blocking
disabled={getControlIsDisabled('pinMessages')} disabled={getControlIsDisabled('pinMessages')}
@ -280,7 +280,7 @@ const ManageGroupAdminRights: FC<OwnProps & StateProps> = ({
<div className="ListItem no-selection"> <div className="ListItem no-selection">
<Checkbox <Checkbox
name="addAdmins" name="addAdmins"
checked={!!permissions.addAdmins} checked={Boolean(permissions.addAdmins)}
label={lang('EditAdminAddAdmins')} label={lang('EditAdminAddAdmins')}
blocking blocking
disabled={getControlIsDisabled('addAdmins')} disabled={getControlIsDisabled('addAdmins')}
@ -290,7 +290,7 @@ const ManageGroupAdminRights: FC<OwnProps & StateProps> = ({
<div className="ListItem no-selection"> <div className="ListItem no-selection">
<Checkbox <Checkbox
name="manageCall" name="manageCall"
checked={!!permissions.manageCall} checked={Boolean(permissions.manageCall)}
label={lang('StartVoipChatPermission')} label={lang('StartVoipChatPermission')}
blocking blocking
disabled={getControlIsDisabled('manageCall')} disabled={getControlIsDisabled('manageCall')}
@ -301,7 +301,7 @@ const ManageGroupAdminRights: FC<OwnProps & StateProps> = ({
<div className="ListItem no-selection"> <div className="ListItem no-selection">
<Checkbox <Checkbox
name="anonymous" name="anonymous"
checked={!!permissions.anonymous} checked={Boolean(permissions.anonymous)}
label={lang('EditAdminSendAnonymously')} label={lang('EditAdminSendAnonymously')}
blocking blocking
disabled={getControlIsDisabled('anonymous')} disabled={getControlIsDisabled('anonymous')}

View File

@ -95,7 +95,7 @@ const ManageGroupMembers: FC<OwnProps & StateProps> = ({
// No need for expensive global updates on users, so we avoid them // No need for expensive global updates on users, so we avoid them
const usersById = getGlobal().users.byId; const usersById = getGlobal().users.byId;
const chatsById = getGlobal().chats.byId; const chatsById = getGlobal().chats.byId;
const shouldUseSearchResults = !!searchQuery; const shouldUseSearchResults = Boolean(searchQuery);
const listedIds = !shouldUseSearchResults const listedIds = !shouldUseSearchResults
? memberIds ? memberIds
: (localContactIds ? filterUsersByName(localContactIds, usersById, searchQuery) : []); : (localContactIds ? filterUsersByName(localContactIds, usersById, searchQuery) : []);

View File

@ -131,7 +131,7 @@ const ManageGroupPermissions: FC<OwnProps & StateProps> = ({
return []; return [];
} }
return chat.fullInfo.members.filter(({ bannedRights }) => !!bannedRights); return chat.fullInfo.members.filter(({ bannedRights }) => Boolean(bannedRights));
}, [chat]); }, [chat]);
const getMemberExceptions = useCallback((member: ApiChatMember) => { const getMemberExceptions = useCallback((member: ApiChatMember) => {

View File

@ -71,7 +71,7 @@ const DropdownMenu: FC<OwnProps> = ({
<Menu <Menu
ref={menuRef} ref={menuRef}
containerRef={dropdownRef} containerRef={dropdownRef}
isOpen={isOpen || !!forceOpen} isOpen={isOpen || Boolean(forceOpen)}
className={className || ''} className={className || ''}
positionX={positionX} positionX={positionX}
positionY={positionY} positionY={positionY}

View File

@ -79,7 +79,7 @@ const Tab: FC<OwnProps> = ({
> >
<span> <span>
{renderText(title)} {renderText(title)}
{!!badgeCount && ( {Boolean(badgeCount) && (
<span className={buildClassName('badge', isBadgeActive && classNames.badgeActive)}>{badgeCount}</span> <span className={buildClassName('badge', isBadgeActive && classNames.badgeActive)}>{badgeCount}</span>
)} )}
<i /> <i />

View File

@ -238,6 +238,8 @@ const foldersReducer: StateReducer<FoldersState, FoldersActions> = (
} }
}; };
export default () => { const useFoldersReducer = () => {
return useReducer(foldersReducer, INITIAL_STATE); return useReducer(foldersReducer, INITIAL_STATE);
}; };
export default useFoldersReducer;

View File

@ -224,6 +224,8 @@ function getBillingCountry(countryCode: string) {
return country ? country.name : ''; return country ? country.name : '';
} }
export default () => { const usePaymentReducer = () => {
return useReducer(reducer, INITIAL_STATE); return useReducer(reducer, INITIAL_STATE);
}; };
export default usePaymentReducer;

View File

@ -56,6 +56,8 @@ const twoFaReducer: StateReducer<TwoFaState, TwoFaActions> = (
} }
}; };
export default () => { const useTwoFaReducer = () => {
return useReducer(twoFaReducer, INITIAL_STATE); return useReducer(twoFaReducer, INITIAL_STATE);
}; };
export default useTwoFaReducer;

View File

@ -15,7 +15,7 @@ type Handler = (e: Event) => void;
const DEFAULT_SKIP_TIME = 10; const DEFAULT_SKIP_TIME = 10;
export default ( const useAudioPlayer = (
trackId: TrackId, trackId: TrackId,
originalDuration: number, // Sometimes incorrect for voice messages originalDuration: number, // Sometimes incorrect for voice messages
trackType: Track['type'], trackType: Track['type'],
@ -54,7 +54,7 @@ export default (
registerMediaSession(metadata, makeMediaHandlers(controllerRef)); registerMediaSession(metadata, makeMediaHandlers(controllerRef));
setPlaybackState('playing'); setPlaybackState('playing');
setVolume(getGlobal().audioPlayer.volume); setVolume(getGlobal().audioPlayer.volume);
toggleMuted(!!getGlobal().audioPlayer.isMuted); toggleMuted(Boolean(getGlobal().audioPlayer.isMuted));
if (trackType === 'voice') { if (trackType === 'voice') {
setPlaybackRate(getGlobal().audioPlayer.playbackRate); setPlaybackRate(getGlobal().audioPlayer.playbackRate);
} }
@ -242,3 +242,5 @@ function makeMediaHandlers(controllerRef: React.RefObject<ReturnType<typeof regi
} }
return mediaHandlers; return mediaHandlers;
} }
export default useAudioPlayer;

View File

@ -8,7 +8,7 @@ const MIN_READY_STATE = 3;
// Avoid flickering when re-mounting previously buffered video // Avoid flickering when re-mounting previously buffered video
const DEBOUNCE = 200; const DEBOUNCE = 200;
export default (noInitiallyBuffered = false) => { const useBuffering = (noInitiallyBuffered = false) => {
const [isBuffered, setIsBuffered] = useState(!noInitiallyBuffered); const [isBuffered, setIsBuffered] = useState(!noInitiallyBuffered);
const [bufferedProgress, setBufferedProgress] = useState(0); const [bufferedProgress, setBufferedProgress] = useState(0);
@ -46,3 +46,5 @@ export default (noInitiallyBuffered = false) => {
}, },
}; };
}; };
export default useBuffering;

View File

@ -1,6 +1,6 @@
import { useCallback, useState } from '../lib/teact/teact'; import { useCallback, useState } from '../lib/teact/teact';
export default () => { const useCacheBuster = () => {
const [cacheBuster, setCacheBuster] = useState<boolean>(false); const [cacheBuster, setCacheBuster] = useState<boolean>(false);
const updateCacheBuster = useCallback(() => { const updateCacheBuster = useCallback(() => {
@ -9,3 +9,5 @@ export default () => {
return [cacheBuster, updateCacheBuster] as const; return [cacheBuster, updateCacheBuster] as const;
}; };
export default useCacheBuster;

View File

@ -9,7 +9,7 @@ import {
import { compact } from '../util/iteratees'; import { compact } from '../util/iteratees';
import useLang from './useLang'; import useLang from './useLang';
export default ({ const useChatContextActions = ({
chat, chat,
user, user,
handleDelete, handleDelete,
@ -107,3 +107,5 @@ export default ({
chat, canChangeFolder, lang, handleChatFolderChange, isPinned, isInSearch, isMuted, handleDelete, folderId, isSelf, chat, canChangeFolder, lang, handleChatFolderChange, isPinned, isInSearch, isMuted, handleDelete, folderId, isSelf,
]); ]);
}; };
export default useChatContextActions;

View File

@ -19,7 +19,7 @@ function stopEvent(e: Event) {
e.stopPropagation(); e.stopPropagation();
} }
export default ( const useContextMenuHandlers = (
elementRef: RefObject<HTMLElement>, elementRef: RefObject<HTMLElement>,
isMenuDisabled?: boolean, isMenuDisabled?: boolean,
shouldDisableOnLink?: boolean, shouldDisableOnLink?: boolean,
@ -140,3 +140,5 @@ export default (
handleContextMenuHide, handleContextMenuHide,
}; };
}; };
export default useContextMenuHandlers;

View File

@ -6,7 +6,7 @@ import { CUSTOM_BG_CACHE_NAME } from '../config';
import * as cacheApi from '../util/cacheApi'; import * as cacheApi from '../util/cacheApi';
import { preloadImage } from '../util/files'; import { preloadImage } from '../util/files';
export default (theme: ThemeKey, settingValue?: string) => { const useCustomBackground = (theme: ThemeKey, settingValue?: string) => {
const [value, setValue] = useState(settingValue); const [value, setValue] = useState(settingValue);
useEffect(() => { useEffect(() => {
@ -30,3 +30,5 @@ export default (theme: ThemeKey, settingValue?: string) => {
return settingValue ? value : undefined; return settingValue ? value : undefined;
}; };
export default useCustomBackground;

View File

@ -1,7 +1,7 @@
import { useEffect } from '../lib/teact/teact'; import { useEffect } from '../lib/teact/teact';
import usePrevious from './usePrevious'; import usePrevious from './usePrevious';
export default <T extends any[], PT = T>(cb: (args: PT) => void, dependencies: T, debugKey?: string) => { const useEffectWithPrevDeps = <T extends any[], PT = T>(cb: (args: PT) => void, dependencies: T, debugKey?: string) => {
const prevDeps = usePrevious<T>(dependencies); const prevDeps = usePrevious<T>(dependencies);
return useEffect(() => { return useEffect(() => {
// @ts-ignore (workaround for "could be instantiated with a different subtype" issue) // @ts-ignore (workaround for "could be instantiated with a different subtype" issue)
@ -9,3 +9,5 @@ export default <T extends any[], PT = T>(cb: (args: PT) => void, dependencies: T
// eslint-disable-next-line react-hooks/exhaustive-deps // eslint-disable-next-line react-hooks/exhaustive-deps
}, dependencies, debugKey); }, dependencies, debugKey);
}; };
export default useEffectWithPrevDeps;

View File

@ -5,7 +5,7 @@ import { ApiMessage } from '../api/types';
import { throttle } from '../util/schedulers'; import { throttle } from '../util/schedulers';
export default ( const useEnsureMessage = (
chatId: string, chatId: string,
messageId?: number, messageId?: number,
message?: ApiMessage, message?: ApiMessage,
@ -25,3 +25,5 @@ export default (
} }
}); });
}; };
export default useEnsureMessage;

View File

@ -1,6 +1,6 @@
import { useCallback, useState } from '../lib/teact/teact'; import { useCallback, useState } from '../lib/teact/teact';
export default (initial = false): [boolean, AnyToVoidFunction, AnyToVoidFunction] => { const useFlag = (initial = false): [boolean, AnyToVoidFunction, AnyToVoidFunction] => {
const [value, setValue] = useState(initial); const [value, setValue] = useState(initial);
const setTrue = useCallback(() => { const setTrue = useCallback(() => {
@ -13,3 +13,5 @@ export default (initial = false): [boolean, AnyToVoidFunction, AnyToVoidFunction
return [value, setTrue, setFalse]; return [value, setTrue, setFalse];
}; };
export default useFlag;

View File

@ -1,9 +1,11 @@
import { useCallback, useState } from '../lib/teact/teact'; import { useCallback, useState } from '../lib/teact/teact';
export default () => { const useForceUpdate = () => {
const [, setTrigger] = useState<boolean>(false); const [, setTrigger] = useState<boolean>(false);
return useCallback(() => { return useCallback(() => {
setTrigger((trigger) => !trigger); setTrigger((trigger) => !trigger);
}, []); }, []);
}; };
export default useForceUpdate;

View File

@ -9,7 +9,7 @@ let isAnimating = false;
// Make sure to end even if end callback was not called (which was some hardly-reproducible bug) // Make sure to end even if end callback was not called (which was some hardly-reproducible bug)
const AUTO_END_TIMEOUT = 1000; const AUTO_END_TIMEOUT = 1000;
export default ( const useHeavyAnimationCheck = (
handleAnimationStart: AnyToVoidFunction, handleAnimationStart: AnyToVoidFunction,
handleAnimationEnd: AnyToVoidFunction, handleAnimationEnd: AnyToVoidFunction,
isDisabled = false, isDisabled = false,
@ -63,3 +63,5 @@ export function dispatchHeavyAnimationEvent(duration = AUTO_END_TIMEOUT) {
return onEnd; return onEnd;
} }
export default useHeavyAnimationCheck;

View File

@ -1,6 +1,6 @@
import { useEffect } from '../lib/teact/teact'; import { useEffect } from '../lib/teact/teact';
export default (container: HTMLElement | null, isDisabled?: boolean) => { const useHorizontalScroll = (container: HTMLElement | null, isDisabled?: boolean) => {
useEffect(() => { useEffect(() => {
if (!container || isDisabled) { if (!container || isDisabled) {
return undefined; return undefined;
@ -20,3 +20,5 @@ export default (container: HTMLElement | null, isDisabled?: boolean) => {
}; };
}, [container, isDisabled]); }, [container, isDisabled]);
}; };
export default useHorizontalScroll;

View File

@ -10,7 +10,7 @@ type LoadMoreBackwards = (args: { offsetId?: string | number }) => void;
const DEFAULT_LIST_SLICE = 30; const DEFAULT_LIST_SLICE = 30;
export default <ListId extends string | number>( const useInfiniteScroll = <ListId extends string | number>(
loadMoreBackwards?: LoadMoreBackwards, loadMoreBackwards?: LoadMoreBackwards,
listIds?: ListId[], listIds?: ListId[],
isDisabled = false, isDisabled = false,
@ -120,3 +120,5 @@ function getViewportSlice<ListId extends string | number>(
return { newViewportIds, areSomeLocal, areAllLocal }; return { newViewportIds, areSomeLocal, areAllLocal };
} }
export default useInfiniteScroll;

View File

@ -1,7 +1,7 @@
import { RefObject } from 'react'; import { RefObject } from 'react';
import { useState, useCallback, useEffect } from '../lib/teact/teact'; import { useState, useCallback, useEffect } from '../lib/teact/teact';
export default ( const useKeyboardListNavigation = (
elementRef: RefObject<HTMLElement>, elementRef: RefObject<HTMLElement>,
isOpen: boolean, isOpen: boolean,
onSelectWithEnter?: (index: number) => void, onSelectWithEnter?: (index: number) => void,
@ -60,3 +60,5 @@ export default (
return handleKeyDown; return handleKeyDown;
}; };
export default useKeyboardListNavigation;

View File

@ -4,7 +4,7 @@ import useOnChange from './useOnChange';
export type LangFn = typeof langProvider.getTranslation; export type LangFn = typeof langProvider.getTranslation;
export default (): LangFn => { const useLang = (): LangFn => {
const forceUpdate = useForceUpdate(); const forceUpdate = useForceUpdate();
useOnChange(() => { useOnChange(() => {
@ -13,3 +13,5 @@ export default (): LangFn => {
return langProvider.getTranslation; return langProvider.getTranslation;
}; };
export default useLang;

View File

@ -1,7 +1,7 @@
import * as langProvider from '../util/langProvider'; import * as langProvider from '../util/langProvider';
import { useState } from '../lib/teact/teact'; import { useState } from '../lib/teact/teact';
export default (langCode: string | undefined, key: string): string | undefined => { const useLangString = (langCode: string | undefined, key: string): string | undefined => {
const [translation, setTranslation] = useState<string>(); const [translation, setTranslation] = useState<string>();
if (langCode) { if (langCode) {
@ -12,3 +12,5 @@ export default (langCode: string | undefined, key: string): string | undefined =
return translation; return translation;
}; };
export default useLangString;

View File

@ -1,7 +1,9 @@
import { useLayoutEffect } from '../lib/teact/teact'; import { useLayoutEffect } from '../lib/teact/teact';
import usePrevious from './usePrevious'; import usePrevious from './usePrevious';
export default <T extends any[], PT = T>(cb: (args: PT) => void, dependencies: T, debugKey?: string) => { const useLayoutEffectWithPrevDeps = <T extends any[], PT = T>(
cb: (args: PT) => void, dependencies: T, debugKey?: string,
) => {
const prevDeps = usePrevious<T>(dependencies); const prevDeps = usePrevious<T>(dependencies);
return useLayoutEffect(() => { return useLayoutEffect(() => {
// @ts-ignore (workaround for "could be instantiated with a different subtype" issue) // @ts-ignore (workaround for "could be instantiated with a different subtype" issue)
@ -9,3 +11,5 @@ export default <T extends any[], PT = T>(cb: (args: PT) => void, dependencies: T
// eslint-disable-next-line react-hooks/exhaustive-deps // eslint-disable-next-line react-hooks/exhaustive-deps
}, dependencies, debugKey); }, dependencies, debugKey);
}; };
export default useLayoutEffectWithPrevDeps;

View File

@ -5,7 +5,7 @@ import { ApiMediaFormat } from '../api/types';
import * as mediaLoader from '../util/mediaLoader'; import * as mediaLoader from '../util/mediaLoader';
import useForceUpdate from './useForceUpdate'; import useForceUpdate from './useForceUpdate';
export default <T extends ApiMediaFormat = ApiMediaFormat.BlobUrl>( const useMedia = <T extends ApiMediaFormat = ApiMediaFormat.BlobUrl>(
mediaHash: string | false | undefined, mediaHash: string | false | undefined,
noLoad = false, noLoad = false,
// @ts-ignore (workaround for "could be instantiated with a different subtype" issue) // @ts-ignore (workaround for "could be instantiated with a different subtype" issue)
@ -33,3 +33,5 @@ export default <T extends ApiMediaFormat = ApiMediaFormat.BlobUrl>(
return mediaData; return mediaData;
}; };
export default useMedia;

View File

@ -21,7 +21,9 @@ const LOGO_DIMENSIONS = { width: 200, height: 200 };
const MINIMAL_SIZE = 115; // spec says 100, but on Chrome 93 it's not showing const MINIMAL_SIZE = 115; // spec says 100, but on Chrome 93 it's not showing
// TODO Add support for video in future // TODO Add support for video in future
export default (message: ApiMessage, sender?: ApiUser | ApiChat, chat?: ApiChat): MediaMetadata | undefined => { const useMessageMediaMetadata = (
message: ApiMessage, sender?: ApiUser | ApiChat, chat?: ApiChat,
): MediaMetadata | undefined => {
const { audio, voice } = getMessageContent(message); const { audio, voice } = getMessageContent(message);
const title = audio ? (audio.title || audio.fileName) : voice ? 'Voice message' : ''; const title = audio ? (audio.title || audio.fileName) : voice ? 'Voice message' : '';
const artist = (audio && audio.performer) || (sender && getSenderTitle(getTranslation, sender)); const artist = (audio && audio.performer) || (sender && getSenderTitle(getTranslation, sender));
@ -71,3 +73,5 @@ function getCoverSize(audio?: ApiAudio, voice?: ApiVoice, url?: string) {
return undefined; return undefined;
} }
export default useMessageMediaMetadata;

View File

@ -6,7 +6,7 @@ import {
import useForceUpdate from './useForceUpdate'; import useForceUpdate from './useForceUpdate';
export default <B extends Bundles, M extends BundleModules<B>>( const useModuleLoader = <B extends Bundles, M extends BundleModules<B>>(
bundleName: B, moduleName: M, noLoad = false, autoUpdate = false, bundleName: B, moduleName: M, noLoad = false, autoUpdate = false,
) => { ) => {
const module = getModuleFromMemory(bundleName, moduleName); const module = getModuleFromMemory(bundleName, moduleName);
@ -28,3 +28,5 @@ export default <B extends Bundles, M extends BundleModules<B>>(
return module; return module;
}; };
export default useModuleLoader;

View File

@ -1,9 +1,11 @@
import usePrevious from './usePrevious'; import usePrevious from './usePrevious';
export default <T extends any[], PT = T>(cb: (args: PT) => void, dependencies: T) => { const useOnChange = <T extends any[], PT = T>(cb: (args: PT) => void, dependencies: T) => {
const prevDeps = usePrevious<T>(dependencies); const prevDeps = usePrevious<T>(dependencies);
if (!prevDeps || dependencies.some((d, i) => d !== prevDeps[i])) { if (!prevDeps || dependencies.some((d, i) => d !== prevDeps[i])) {
// @ts-ignore (workaround for "could be instantiated with a different subtype" issue) // @ts-ignore (workaround for "could be instantiated with a different subtype" issue)
cb(prevDeps || []); cb(prevDeps || []);
} }
}; };
export default useOnChange;

View File

@ -1,6 +1,6 @@
import { useCallback, useEffect, useRef } from '../lib/teact/teact'; import { useCallback, useEffect, useRef } from '../lib/teact/teact';
export default ( const useSendWithEnter = (
onSelect: NoneToVoidFunction, onSelect: NoneToVoidFunction,
) => { ) => {
// eslint-disable-next-line no-null/no-null // eslint-disable-next-line no-null/no-null
@ -23,3 +23,5 @@ export default (
return buttonRef; return buttonRef;
}; };
export default useSendWithEnter;

View File

@ -6,10 +6,12 @@ import { ApiSendMessageAction } from '../api/types';
import { SEND_MESSAGE_ACTION_INTERVAL } from '../config'; import { SEND_MESSAGE_ACTION_INTERVAL } from '../config';
import { throttle } from '../util/schedulers'; import { throttle } from '../util/schedulers';
export default (chatId: string, threadId?: number) => { const useSendMessageAction = (chatId: string, threadId?: number) => {
return useMemo(() => { return useMemo(() => {
return throttle((action: ApiSendMessageAction) => { return throttle((action: ApiSendMessageAction) => {
getDispatch().sendMessageAction({ chatId, threadId, action }); getDispatch().sendMessageAction({ chatId, threadId, action });
}, SEND_MESSAGE_ACTION_INTERVAL); }, SEND_MESSAGE_ACTION_INTERVAL);
}, [chatId, threadId]); }, [chatId, threadId]);
}; };
export default useSendMessageAction;

View File

@ -3,7 +3,7 @@ import buildClassName from '../util/buildClassName';
const CLOSE_DURATION = 350; const CLOSE_DURATION = 350;
export default ( const useShowTransition = (
isOpen = false, isOpen = false,
onCloseTransitionEnd?: () => void, onCloseTransitionEnd?: () => void,
noOpenTransition = false, noOpenTransition = false,
@ -62,3 +62,5 @@ export default (
transitionClassNames, transitionClassNames,
}; };
}; };
export default useShowTransition;

View File

@ -2,8 +2,10 @@ import { useMemo } from '../lib/teact/teact';
import { throttle } from '../util/schedulers'; import { throttle } from '../util/schedulers';
export default (ms: number, noFirst = false) => { const useThrottle = (ms: number, noFirst = false) => {
return useMemo(() => { return useMemo(() => {
return throttle((cb) => cb(), ms, !noFirst); return throttle((cb) => cb(), ms, !noFirst);
}, [ms, noFirst]); }, [ms, noFirst]);
}; };
export default useThrottle;

View File

@ -3,7 +3,7 @@ import generateIdFor from '../util/generateIdFor';
const store: Record<string, boolean> = {}; const store: Record<string, boolean> = {};
export default () => { const useUniqueId = () => {
const idRef = useRef<string>(); const idRef = useRef<string>();
if (!idRef.current) { if (!idRef.current) {
@ -13,3 +13,5 @@ export default () => {
return idRef.current; return idRef.current;
}; };
export default useUniqueId;

View File

@ -6,7 +6,7 @@ import { ApiDimensions } from '../api/types';
const THROTTLE = 250; const THROTTLE = 250;
export default () => { const useWindowSize = () => {
const [size, setSize] = useState<ApiDimensions>(windowSize.get()); const [size, setSize] = useState<ApiDimensions>(windowSize.get());
useEffect(() => { useEffect(() => {
@ -23,3 +23,5 @@ export default () => {
return size; return size;
}; };
export default useWindowSize;

View File

@ -594,7 +594,7 @@ Duplicate ?
function isListLike(item) { function isListLike(item) {
return ( return (
Array.isArray(item) || Array.isArray(item) ||
(!!item && (Boolean(item) &&
typeof item === 'object' && typeof item === 'object' &&
typeof (item.length) === 'number' && typeof (item.length) === 'number' &&
(item.length === 0 || (item.length === 0 ||

View File

@ -112,7 +112,7 @@ export async function updateTwoFaSettings(
await client.invoke(new Api.account.ConfirmPasswordEmail({ code })); await client.invoke(new Api.account.ConfirmPasswordEmail({ code }));
break; break;
} catch (err) { } catch (err: any) {
onEmailCodeError!(err); onEmailCodeError!(err);
} }
} }

View File

@ -369,7 +369,7 @@ class TelegramClient {
async _connectSender(sender, dcId) { async _connectSender(sender, dcId) {
// if we don't already have an auth key we want to use normal DCs not -1 // if we don't already have an auth key we want to use normal DCs not -1
const dc = utils.getDC(dcId, !!sender.authKey.getKey()); const dc = utils.getDC(dcId, Boolean(sender.authKey.getKey()));
while (true) { while (true) {
try { try {

View File

@ -54,7 +54,7 @@ export async function checkAuthorization(client: TelegramClient) {
try { try {
await client.invoke(new Api.updates.GetState()); await client.invoke(new Api.updates.GetState());
return true; return true;
} catch (e) { } catch (e: any) {
if (e.message === 'Disconnect') throw e; if (e.message === 'Disconnect') throw e;
return false; return false;
} }
@ -73,7 +73,7 @@ async function signInUser(
if (typeof authParams.phoneNumber === 'function') { if (typeof authParams.phoneNumber === 'function') {
try { try {
phoneNumber = await authParams.phoneNumber(); phoneNumber = await authParams.phoneNumber();
} catch (err) { } catch (err: any) {
if (err.message === 'RESTART_AUTH_WITH_QR') { if (err.message === 'RESTART_AUTH_WITH_QR') {
return signInUserWithQrCode(client, apiCredentials, authParams); return signInUserWithQrCode(client, apiCredentials, authParams);
} }
@ -92,7 +92,7 @@ async function signInUser(
} }
break; break;
} catch (err) { } catch (err: any) {
if (typeof authParams.phoneNumber !== 'function') { if (typeof authParams.phoneNumber !== 'function') {
throw err; throw err;
} }
@ -110,7 +110,7 @@ async function signInUser(
try { try {
try { try {
phoneCode = await authParams.phoneCode(isCodeViaApp); phoneCode = await authParams.phoneCode(isCodeViaApp);
} catch (err) { } catch (err: any) {
// This is the support for changing phone number from the phone code screen. // This is the support for changing phone number from the phone code screen.
if (err.message === 'RESTART_AUTH') { if (err.message === 'RESTART_AUTH') {
return signInUser(client, apiCredentials, authParams); return signInUser(client, apiCredentials, authParams);
@ -136,7 +136,7 @@ async function signInUser(
} }
return result.user; return result.user;
} catch (err) { } catch (err: any) {
if (err.message === 'SESSION_PASSWORD_NEEDED') { if (err.message === 'SESSION_PASSWORD_NEEDED') {
return signInWithPassword(client, apiCredentials, authParams); return signInWithPassword(client, apiCredentials, authParams);
} else { } else {
@ -167,7 +167,7 @@ async function signInUser(
} }
return user; return user;
} catch (err) { } catch (err: any) {
authParams.onError(err); authParams.onError(err);
} }
} }
@ -219,7 +219,7 @@ async function signInUserWithQrCode(
// Either we receive an update that QR is successfully scanned, // Either we receive an update that QR is successfully scanned,
// or we receive a rejection caused by user going back to the regular auth form // or we receive a rejection caused by user going back to the regular auth form
await Promise.race([updatePromise, inputPromise]); await Promise.race([updatePromise, inputPromise]);
} catch (err) { } catch (err: any) {
if (err.message === 'RESTART_AUTH') { if (err.message === 'RESTART_AUTH') {
return await signInUser(client, apiCredentials, authParams); return await signInUser(client, apiCredentials, authParams);
} }
@ -249,7 +249,7 @@ async function signInUserWithQrCode(
return migratedResult.authorization.user; return migratedResult.authorization.user;
} }
} }
} catch (err) { } catch (err: any) {
if (err.message === 'SESSION_PASSWORD_NEEDED') { if (err.message === 'SESSION_PASSWORD_NEEDED') {
return signInWithPassword(client, apiCredentials, authParams); return signInWithPassword(client, apiCredentials, authParams);
} }
@ -258,7 +258,7 @@ async function signInUserWithQrCode(
} }
// This is a workaround for TypeScript (never actually reached) // This is a workaround for TypeScript (never actually reached)
// eslint-disable-next-line no-throw-literal // eslint-disable-next-line @typescript-eslint/no-throw-literal
throw undefined; throw undefined;
} }
@ -294,7 +294,7 @@ async function sendCode(
phoneCodeHash: resendResult.phoneCodeHash, phoneCodeHash: resendResult.phoneCodeHash,
isCodeViaApp: resendResult.type instanceof Api.auth.SentCodeTypeApp, isCodeViaApp: resendResult.type instanceof Api.auth.SentCodeTypeApp,
}; };
} catch (err) { } catch (err: any) {
if (err.message === 'AUTH_RESTART') { if (err.message === 'AUTH_RESTART') {
return sendCode(client, apiCredentials, phoneNumber, forceSMS); return sendCode(client, apiCredentials, phoneNumber, forceSMS);
} else { } else {
@ -321,7 +321,7 @@ async function signInWithPassword(
})) as Api.auth.Authorization; })) as Api.auth.Authorization;
return user; return user;
} catch (err) { } catch (err: any) {
authParams.onError(err); authParams.onError(err);
} }
} }

View File

@ -480,4 +480,5 @@ function DEBUG_addToVirtualTreeSize($current: VirtualRealElement | VirtualDomHea
}); });
} }
export default { render }; const TeactDOM = { render };
export default TeactDOM;

View File

@ -298,7 +298,7 @@ export function renderComponent(componentInstance: ComponentInstance) {
DEBUG_components[componentName].renderTimes.push(duration); DEBUG_components[componentName].renderTimes.push(duration);
DEBUG_components[componentName].renderCount++; DEBUG_components[componentName].renderCount++;
} }
} catch (err) { } catch (err: any) {
handleError(err); handleError(err);
newRenderedValue = componentInstance.renderedValue; newRenderedValue = componentInstance.renderedValue;
@ -382,7 +382,7 @@ function unmountComponent(componentInstance: ComponentInstance) {
if (typeof cleanup === 'function') { if (typeof cleanup === 'function') {
try { try {
cleanup(); cleanup();
} catch (err) { } catch (err: any) {
handleError(err); handleError(err);
} }
} }
@ -552,7 +552,7 @@ function useLayoutEffectBase(
); );
} }
} }
} catch (err) { } catch (err: any) {
handleError(err); handleError(err);
} }
} }
@ -698,7 +698,9 @@ export function memo<T extends FC>(Component: T, areEqual = arePropsShallowEqual
} }
// We need to keep it here for JSX. // We need to keep it here for JSX.
export default { const Teact = {
createElement, createElement,
Fragment, Fragment,
}; };
export default Teact;

View File

@ -99,7 +99,7 @@ function updateContainers() {
try { try {
newMappedProps = mapStateToProps(currentGlobal, ownProps); newMappedProps = mapStateToProps(currentGlobal, ownProps);
} catch (err) { } catch (err: any) {
handleError(err); handleError(err);
return; return;
@ -205,7 +205,7 @@ export function withGlobal<OwnProps>(
try { try {
container.mappedProps = mapStateToProps(currentGlobal, props); container.mappedProps = mapStateToProps(currentGlobal, props);
} catch (err) { } catch (err: any) {
handleError(err); handleError(err);
} }
} }

View File

@ -713,7 +713,7 @@ addReducer('updateChatMemberBannedRights', (global, actions, payload) => {
const { members, kickedMembers } = chatAfterUpdate.fullInfo; const { members, kickedMembers } = chatAfterUpdate.fullInfo;
const isBanned = !!bannedRights.viewMessages; const isBanned = Boolean(bannedRights.viewMessages);
const isUnblocked = !Object.keys(bannedRights).length; const isUnblocked = !Object.keys(bannedRights).length;
setGlobal(updateChat(newGlobal, chatId, { setGlobal(updateChat(newGlobal, chatId, {
@ -1184,7 +1184,7 @@ async function createGroupChat(title: string, users: ApiUser[], photo?: File) {
photo, photo,
}); });
} }
} catch (e) { } catch (e: any) {
if (e.message === 'USERS_TOO_FEW') { if (e.message === 'USERS_TOO_FEW') {
const global = getGlobal(); const global = getGlobal();
setGlobal({ setGlobal({

View File

@ -44,7 +44,7 @@ addReducer('apiUpdate', (global, actions, update: ApiUpdate) => {
participants.forEach((participant) => { participants.forEach((participant) => {
if (participant.id) { if (participant.id) {
global = updateGroupCallParticipant(global, groupCallId, participant.id, participant, global = updateGroupCallParticipant(global, groupCallId, participant.id, participant,
!!nextOffset || currentUserId === participant.id); Boolean(nextOffset) || currentUserId === participant.id);
} }
}); });
if (nextOffset) { if (nextOffset) {

View File

@ -158,7 +158,7 @@ addReducer('createGroupCallInviteLink', (global, actions) => {
return; return;
} }
const canInvite = !!chat && !!chat.username; const canInvite = Boolean(chat.username);
(async () => { (async () => {
let { inviteLink } = chat.fullInfo!; let { inviteLink } = chat.fullInfo!;

View File

@ -360,9 +360,9 @@ function filterChatFolder(
} }
} }
} else if (type === 'chatTypeChannel') { } else if (type === 'chatTypeChannel') {
return !!folder.channels; return Boolean(folder.channels);
} else if (type === 'chatTypeBasicGroup' || type === 'chatTypeSuperGroup') { } else if (type === 'chatTypeBasicGroup' || type === 'chatTypeSuperGroup') {
return !!folder.groups; return Boolean(folder.groups);
} }
return false; return false;

View File

@ -144,7 +144,7 @@ export function isForwardedMessage(message: ApiMessage) {
} }
export function isActionMessage(message: ApiMessage) { export function isActionMessage(message: ApiMessage) {
return !!message.content.action; return Boolean(message.content.action);
} }
export function isServiceNotificationMessage(message: ApiMessage) { export function isServiceNotificationMessage(message: ApiMessage) {

View File

@ -26,7 +26,7 @@ export function selectIsAdminInActiveGroupCall(global: GlobalState): boolean {
const chat = selectChat(global, chatId); const chat = selectChat(global, chatId);
if (!chat) return false; if (!chat) return false;
return (isChatBasicGroup(chat) && chat.isCreator) || !!chat.adminRights?.manageCall; return (isChatBasicGroup(chat) && chat.isCreator) || Boolean(chat.adminRights?.manageCall);
} }
export function selectActiveGroupCall(global: GlobalState) { export function selectActiveGroupCall(global: GlobalState) {

View File

@ -133,17 +133,17 @@ export function selectIsChatPinned(global: GlobalState, chatId: string, folderId
const { active, archived } = global.chats.orderedPinnedIds; const { active, archived } = global.chats.orderedPinnedIds;
if (folderId === ALL_FOLDER_ID) { if (folderId === ALL_FOLDER_ID) {
return !!active && active.includes(chatId); return Boolean(active?.includes(chatId));
} }
if (folderId === ARCHIVED_FOLDER_ID) { if (folderId === ARCHIVED_FOLDER_ID) {
return !!archived && archived.includes(chatId); return Boolean(archived?.includes(chatId));
} }
const { byId: chatFoldersById } = global.chatFolders; const { byId: chatFoldersById } = global.chatFolders;
const { pinnedChatIds } = chatFoldersById[folderId] || {}; const { pinnedChatIds } = chatFoldersById[folderId] || {};
return !!pinnedChatIds && pinnedChatIds.includes(chatId); return Boolean(pinnedChatIds?.includes(chatId));
} }
// Slow, not to be used in `withGlobal` // Slow, not to be used in `withGlobal`

View File

@ -253,6 +253,6 @@ export function handleClientMessage(e: ExtendableMessageEvent) {
} }
} }
self.onsync = () => { self.addEventListener('sync', () => {
lastSyncAt = new Date().valueOf(); lastSyncAt = Date.now();
}; });

View File

@ -1,4 +1,6 @@
export default { const oggToWav = {
oggToWav() { oggToWav() {
}, },
}; };
export default oggToWav;

View File

@ -1,6 +1,8 @@
export default { const webpToPng = {
webpToPng() { webpToPng() {
}, },
webpToPngBase64() { webpToPngBase64() {
}, },
}; };
export default webpToPng;

View File

@ -51,8 +51,8 @@ async function copyBlobToClipboard(pngBlob: Blob | null) {
} }
try { try {
await navigator.clipboard.write([ await navigator.clipboard.write?.([
new window.ClipboardItem({ new ClipboardItem({
[pngBlob.type]: pngBlob, [pngBlob.type]: pngBlob,
}), }),
]); ]);

View File

@ -43,7 +43,7 @@ export default function createInterface(api: Record<string, Function>) {
arrayBuffers, arrayBuffers,
); );
} }
} catch (error) { } catch (error: any) {
if (DEBUG) { if (DEBUG) {
// eslint-disable-next-line no-console // eslint-disable-next-line no-console
console.error(error); console.error(error);

View File

@ -1,4 +1,4 @@
export default (store: AnyLiteral) => { const generateIdFor = (store: AnyLiteral) => {
let id; let id;
do { do {
@ -7,3 +7,5 @@ export default (store: AnyLiteral) => {
return id; return id;
}; };
export default generateIdFor;

View File

@ -4,7 +4,10 @@ export function scaleImage(image: string | Blob, ratio: number, outputType: stri
return new Promise((resolve) => { return new Promise((resolve) => {
img.onload = () => { img.onload = () => {
scale(img, img.width * ratio, img.height * ratio, outputType) scale(img, img.width * ratio, img.height * ratio, outputType)
.then((blob) => URL.createObjectURL(blob)) .then((blob) => {
if (!blob) throw new Error('Image resize failed!');
return URL.createObjectURL(blob);
})
.then(resolve) .then(resolve)
.finally(() => { .finally(() => {
if (image instanceof Blob) { if (image instanceof Blob) {
@ -24,7 +27,10 @@ export function resizeImage(
return new Promise((resolve) => { return new Promise((resolve) => {
img.onload = () => { img.onload = () => {
scale(img, width, height, outputType) scale(img, width, height, outputType)
.then((blob) => URL.createObjectURL(blob)) .then((blob) => {
if (!blob) throw new Error('Image resize failed!');
return URL.createObjectURL(blob);
})
.then(resolve) .then(resolve)
.finally(() => { .finally(() => {
if (image instanceof Blob) { if (image instanceof Blob) {
@ -38,7 +44,7 @@ export function resizeImage(
async function scale( async function scale(
img: HTMLImageElement, width: number, height: number, outputType: string = 'image/png', img: HTMLImageElement, width: number, height: number, outputType: string = 'image/png',
) { ): Promise<Blob | null> {
// Safari does not have built-in resize method with quality control // Safari does not have built-in resize method with quality control
if ('createImageBitmap' in window) { if ('createImageBitmap' in window) {
try { try {

View File

@ -95,7 +95,7 @@ export function unique<T extends any>(array: T[]): T[] {
return Array.from(new Set(array)); return Array.from(new Set(array));
} }
export function compact(array: any[]) { export function compact<T extends any>(array: T[]) {
return array.filter(Boolean); return array.filter(Boolean);
} }

View File

@ -213,7 +213,7 @@ export async function subscribe() {
await callApi('registerDevice', deviceToken); await callApi('registerDevice', deviceToken);
getDispatch() getDispatch()
.setDeviceToken(deviceToken); .setDeviceToken(deviceToken);
} catch (error) { } catch (error: any) {
if (Notification.permission === 'denied' as NotificationPermission) { if (Notification.permission === 'denied' as NotificationPermission) {
// The user denied the notification permission which // The user denied the notification permission which
// means we failed to subscribe and the user will need // means we failed to subscribe and the user will need

View File

@ -1,7 +1,7 @@
import { IS_IOS } from './environment'; import { IS_IOS } from './environment';
import forceReflow from './forceReflow'; import forceReflow from './forceReflow';
export default (container: HTMLDivElement, scrollTop?: number) => { const resetScroll = (container: HTMLDivElement, scrollTop?: number) => {
if (IS_IOS) { if (IS_IOS) {
container.style.overflow = 'hidden'; container.style.overflow = 'hidden';
} }
@ -21,3 +21,5 @@ export function patchChromiumScroll(element: HTMLElement) {
forceReflow(element); forceReflow(element);
element.style.display = ''; element.style.display = '';
} }
export default resetScroll;

View File

@ -18,7 +18,7 @@ export const parseLocationHash = (value: string): MessageList | undefined => {
return { return {
chatId, chatId,
type: !!typeOrThreadId && isType ? (typeOrThreadId as MessageListType) : 'thread', type: Boolean(typeOrThreadId) && isType ? (typeOrThreadId as MessageListType) : 'thread',
threadId: !!typeOrThreadId && !isType ? Number(typeOrThreadId) : MAIN_THREAD_ID, threadId: Boolean(typeOrThreadId) && !isType ? Number(typeOrThreadId) : MAIN_THREAD_ID,
}; };
}; };

View File

@ -1,6 +1,6 @@
import { DEBUG } from '../config'; import { DEBUG } from '../config';
export default (mediaEl: HTMLMediaElement) => { const safePlay = (mediaEl: HTMLMediaElement) => {
mediaEl.play().catch((err) => { mediaEl.play().catch((err) => {
if (DEBUG) { if (DEBUG) {
// eslint-disable-next-line no-console // eslint-disable-next-line no-console
@ -8,3 +8,5 @@ export default (mediaEl: HTMLMediaElement) => {
} }
}); });
}; };
export default safePlay;

View File

@ -1,6 +1,8 @@
import React from '../lib/teact/teact'; import React from '../lib/teact/teact';
export default (e: React.UIEvent | Event) => { const stopEvent = (e: React.UIEvent | Event) => {
e.stopPropagation(); e.stopPropagation();
e.preventDefault(); e.preventDefault();
}; };
export default stopEvent;

View File

@ -31,7 +31,7 @@ const colors = (Object.keys(themeColors) as Array<keyof typeof themeColors>).map
colors: [hexToRgb(themeColors[property][0]), hexToRgb(themeColors[property][1])], colors: [hexToRgb(themeColors[property][0]), hexToRgb(themeColors[property][1])],
})); }));
export default (theme: ISettings['theme'], withAnimation: boolean) => { const switchTheme = (theme: ISettings['theme'], withAnimation: boolean) => {
const isDarkTheme = theme === 'dark'; const isDarkTheme = theme === 'dark';
const shouldAnimate = isInitialized && withAnimation; const shouldAnimate = isInitialized && withAnimation;
const startIndex = isDarkTheme ? 0 : 1; const startIndex = isDarkTheme ? 0 : 1;
@ -102,3 +102,5 @@ function applyColorAnimationStep(startIndex: number, endIndex: number, interpola
} }
}); });
} }
export default switchTheme;

View File

@ -34,7 +34,7 @@ export const forceWebsync = (authed: boolean) => {
return new Promise<void>((resolve, reject) => { return new Promise<void>((resolve, reject) => {
const script = document.createElement('script'); const script = document.createElement('script');
const removeElement = () => !!document.body.removeChild(script); const removeElement = () => Boolean(document.body.removeChild(script));
script.src = url + new URLSearchParams({ script.src = url + new URLSearchParams({
authed: Number(authed).toString(), authed: Number(authed).toString(),

View File

@ -14,7 +14,7 @@ type IDimensions = {
const IS_LANDSCAPE = IS_SINGLE_COLUMN_LAYOUT && isLandscape(); const IS_LANDSCAPE = IS_SINGLE_COLUMN_LAYOUT && isLandscape();
const initialHeight = window.innerHeight; const initialHeight = window.innerHeight;
let windowSize = updateSizes(); let currentWindowSize = updateSizes();
let isRefreshDisabled = false; let isRefreshDisabled = false;
function disableRefresh() { function disableRefresh() {
@ -26,7 +26,7 @@ function enableRefresh() {
} }
const handleResize = throttle(() => { const handleResize = throttle(() => {
windowSize = updateSizes(); currentWindowSize = updateSizes();
if (!isRefreshDisabled && ( if (!isRefreshDisabled && (
isMobileScreen() !== IS_SINGLE_COLUMN_LAYOUT isMobileScreen() !== IS_SINGLE_COLUMN_LAYOUT
@ -61,8 +61,9 @@ export function updateSizes(): IDimensions {
} }
function isMobileScreen() { function isMobileScreen() {
return windowSize.width <= MOBILE_SCREEN_MAX_WIDTH || ( return currentWindowSize.width <= MOBILE_SCREEN_MAX_WIDTH || (
windowSize.width <= MOBILE_SCREEN_LANDSCAPE_MAX_WIDTH && windowSize.height <= MOBILE_SCREEN_LANDSCAPE_MAX_HEIGHT currentWindowSize.width <= MOBILE_SCREEN_LANDSCAPE_MAX_WIDTH
&& currentWindowSize.height <= MOBILE_SCREEN_LANDSCAPE_MAX_HEIGHT
); );
} }
@ -78,9 +79,11 @@ function isLandscape() {
return window.matchMedia('screen and (min-device-aspect-ratio: 1/1) and (orientation: landscape)').matches; return window.matchMedia('screen and (min-device-aspect-ratio: 1/1) and (orientation: landscape)').matches;
} }
export default { const windowSize = {
get: () => windowSize, get: () => currentWindowSize,
getIsKeyboardVisible: () => initialHeight > windowSize.height, getIsKeyboardVisible: () => initialHeight > currentWindowSize.height,
disableRefresh, disableRefresh,
enableRefresh, enableRefresh,
}; };
export default windowSize;

View File

@ -1,6 +1,6 @@
{ {
"compilerOptions": { "compilerOptions": {
// We don't care about this since Parcel runs Babel after TypeScript // We don't care about this since Webpack runs Babel after TypeScript
"target": "esnext", "target": "esnext",
"lib": [ "lib": [
"dom", "dom",

View File

@ -19,18 +19,32 @@ module.exports = (env = {}, argv = {}) => {
entry: './src/index.tsx', entry: './src/index.tsx',
target: 'web', target: 'web',
devServer: { devServer: {
contentBase: [
path.resolve(__dirname, 'public'),
path.resolve(__dirname, 'node_modules/emoji-data-ios'),
path.resolve(__dirname, 'node_modules/opus-recorder/dist'),
path.resolve(__dirname, 'src/lib/webp'),
path.resolve(__dirname, 'src/lib/rlottie'),
path.resolve(__dirname, 'src/lib/secret-sauce'),
],
port: 1234, port: 1234,
host: '0.0.0.0', host: '0.0.0.0',
disableHostCheck: true, allowedHosts: "all",
stats: 'minimal' static: [
{
directory: path.resolve(__dirname, 'public'),
},
{
directory: path.resolve(__dirname, 'node_modules/emoji-data-ios'),
},
{
directory: path.resolve(__dirname, 'node_modules/opus-recorder/dist'),
},
{
directory: path.resolve(__dirname, 'src/lib/webp'),
},
{
directory: path.resolve(__dirname, 'src/lib/rlottie'),
},
{
directory: path.resolve(__dirname, 'src/lib/secret-sauce'),
},
],
devMiddleware: {
stats: 'minimal',
},
}, },
output: { output: {
filename: '[name].[contenthash].js', filename: '[name].[contenthash].js',