[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/member-delimiter-style": "error",
"@typescript-eslint/default-param-last": "off",
"teactn/prefer-separate-component-file": "off"
},
"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",
"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)\"",
"perf:serve": "cross-env APP_ENV=perf parcel src/index-perf.html",
"lint": "tsc && eslint . --ext .ts,.tsx --ignore-pattern src/lib/gramjs",
"lint:fix": "npm run lint -- --fix",
"gramjs:tl": "node ./src/lib/gramjs/tl/generateModules.js",
@ -33,83 +32,80 @@
"author": "Alexander Zinchuk (alexander@zinchuk.com)",
"license": "GPL-3.0-or-later",
"devDependencies": {
"@babel/core": "^7.15.0",
"@babel/plugin-proposal-class-properties": "^7.14.5",
"@babel/plugin-proposal-nullish-coalescing-operator": "^7.14.5",
"@babel/core": "^7.16.12",
"@babel/plugin-proposal-class-properties": "^7.16.7",
"@babel/plugin-proposal-nullish-coalescing-operator": "^7.16.7",
"@babel/plugin-syntax-nullish-coalescing-operator": "^7.8.3",
"@babel/preset-env": "^7.15.0",
"@babel/preset-react": "^7.14.5",
"@babel/preset-typescript": "^7.15.0",
"@peculiar/webcrypto": "^1.1.7",
"@testing-library/jest-dom": "^5.14.1",
"@babel/preset-env": "^7.16.11",
"@babel/preset-react": "^7.16.7",
"@babel/preset-typescript": "^7.16.7",
"@peculiar/webcrypto": "^1.2.3",
"@testing-library/jest-dom": "^5.16.1",
"@types/croppie": "^2.6.1",
"@types/css-font-loading-module": "0.0.6",
"@types/dom-mediacapture-record": "^1.0.10",
"@types/jest": "^27.0.1",
"@types/react": "^17.0.19",
"@types/react-dom": "^17.0.9",
"@types/css-font-loading-module": "0.0.7",
"@types/dom-mediacapture-record": "^1.0.11",
"@types/jest": "^27.4.0",
"@types/react": "^17.0.38",
"@types/react-dom": "^17.0.11",
"@types/resize-observer-browser": "^0.1.6",
"@types/wicg-mediasession": "^1.1.2",
"@typescript-eslint/eslint-plugin": "^4.29.1",
"@typescript-eslint/parser": "^4.29.1",
"@webpack-cli/serve": "^1.5.1",
"autoprefixer": "^10.3.7",
"babel-eslint": "^10.1.0",
"babel-loader": "^8.2.2",
"@types/wicg-mediasession": "^1.1.3",
"@typescript-eslint/eslint-plugin": "^5.10.1",
"@typescript-eslint/parser": "^5.10.1",
"@webpack-cli/serve": "^1.6.1",
"autoprefixer": "^10.4.2",
"babel-loader": "^8.2.3",
"browserlist": "^1.0.1",
"buffer": "^6.0.3",
"cross-env": "^7.0.3",
"css-loader": "^6.2.0",
"css-minimizer-webpack-plugin": "^3.0.2",
"dotenv": "^10.0.0",
"eslint": "^7.32.0",
"eslint-config-airbnb-typescript": "^12.3.1",
"eslint-config-react-app": "^6.0.0",
"eslint-import-resolver-webpack": "^0.13.1",
"eslint-plugin-flowtype": "^5.9.0",
"eslint-plugin-import": "^2.24.0",
"eslint-plugin-jsx-a11y": "^6.4.1",
"css-loader": "^6.5.1",
"css-minimizer-webpack-plugin": "^3.4.1",
"dotenv": "^14.3.2",
"eslint": "^8.7.0",
"eslint-config-airbnb-typescript": "^16.1.0",
"eslint-config-react-app": "^7.0.0",
"eslint-import-resolver-webpack": "^0.13.2",
"eslint-plugin-flowtype": "^8.0.3",
"eslint-plugin-import": "^2.25.4",
"eslint-plugin-jsx-a11y": "^6.5.1",
"eslint-plugin-no-async-without-await": "^1.2.0",
"eslint-plugin-no-null": "^1.0.2",
"eslint-plugin-react": "^7.24.0",
"eslint-plugin-react-hooks": "^4.2.0",
"eslint-plugin-react": "^7.28.0",
"eslint-plugin-react-hooks": "^4.3.0",
"eslint-plugin-teactn": "github:korenskoy/eslint-plugin-teactn#8fbd2b9",
"file-loader": "^6.2.0",
"html-webpack-plugin": "^5.3.2",
"husky": "^7.0.1",
"jest": "^27.0.6",
"html-webpack-plugin": "^5.5.0",
"husky": "^7.0.4",
"jest": "^27.4.7",
"jest-raw-loader": "^1.0.1",
"lint-staged": "^11.1.2",
"mini-css-extract-plugin": "^2.2.0",
"node-sass": "^6.0.1",
"optimize-css-assets-webpack-plugin": "^6.0.1",
"postcss-loader": "^6.1.1",
"postcss-modules": "^4.2.2",
"lint-staged": "^12.3.2",
"mini-css-extract-plugin": "^2.5.3",
"postcss-loader": "^6.2.1",
"postcss-modules": "^4.3.0",
"raw-loader": "^4.0.2",
"react": "^17.0.2",
"replace-in-file": "^6.2.0",
"sass": "^1.38.0",
"sass-loader": "^12.1.0",
"style-loader": "^3.2.1",
"terser": "^5.7.1",
"terser-webpack-plugin": "^5.1.4",
"ts-node": "^10.2.1",
"typescript": "^4.3.5",
"replace-in-file": "^6.3.2",
"sass": "^1.49.0",
"sass-loader": "^12.4.0",
"style-loader": "^3.3.1",
"terser": "^5.10.0",
"terser-webpack-plugin": "^5.3.0",
"ts-node": "^10.4.0",
"typescript": "^4.5.5",
"url-loader": "^4.1.1",
"webpack": "^5.51.0",
"webpack-bundle-analyzer": "^4.4.2",
"webpack-cli": "^4.8.0",
"webpack-dev-server": "^3.11.2",
"webpack": "^5.67.0",
"webpack-bundle-analyzer": "^4.5.0",
"webpack-cli": "^4.9.2",
"webpack-dev-server": "^4.7.3",
"webpack-merge": "^5.8.0"
},
"dependencies": {
"@cryptography/aes": "^0.1.1",
"async-mutex": "^0.3.1",
"async-mutex": "^0.3.2",
"big-integer": "github:painor/BigInteger.js",
"croppie": "^2.6.5",
"emoji-data-ios": "github:korenskoy/emoji-data-ios#162c821",
"events": "^3.3.0",
"idb-keyval": "^5.1.3",
"idb-keyval": "^6.1.0",
"opus-recorder": "github:Ajaxy/opus-recorder",
"os-browserify": "^0.3.0",
"pako": "^2.0.4",

View File

@ -11,10 +11,6 @@ declare namespace React {
teactOrderKey?: number;
}
interface ImgHTMLAttributes<T> extends HTMLAttributes<T> {
loading?: 'auto' | 'eager' | 'lazy';
}
interface VideoHTMLAttributes {
srcObject?: MediaStream;
}
@ -55,10 +51,6 @@ declare module 'pako/dist/pako_inflate' {
function inflate(...args: any[]): string;
}
type WindowWithPerf =
typeof window
& { perf: AnyLiteral };
interface TEncodedImage {
result: Uint8ClampedArray;
width: number;
@ -70,15 +62,6 @@ interface IWebpWorker extends Worker {
requests: Map<string, (value: PromiseLike<TEncodedImage>) => void>;
}
interface Window {
ClipboardItem?: any;
requestIdleCallback: (cb: AnyToVoidFunction, options: { timeout?: number }) => void;
}
interface Clipboard {
write?: any;
}
interface Document {
mozFullScreenElement: any;
webkitFullscreenElement: any;
@ -97,3 +80,20 @@ interface Navigator {
// PWA badging extensions https://w3c.github.io/badging/
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)
.map((user) => buildApiChatFromPreview(user))
.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 {
localChats: allChats.filter((r) => localPeerIds.includes(r.id)),

View File

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

View File

@ -94,7 +94,7 @@ export async function fetchMessages({
}),
...pagination,
}), undefined, true);
} catch (err) {
} catch (err: any) {
if (err.message === 'CHANNEL_PRIVATE') {
onUpdate({
'@type': 'updateChat',
@ -146,7 +146,7 @@ export async function fetchMessage({ chat, messageId }: { chat: ApiChat; message
undefined,
true,
);
} catch (err) {
} catch (err: any) {
const { message } = err;
// 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 });
return true;
} catch (err) {
} catch (err: any) {
onError(err);
return false;
@ -65,7 +65,7 @@ export async function clearPassword(currentPassword: string) {
await updateTwoFaSettings({ currentPassword });
return true;
} catch (err) {
} catch (err: any) {
onError(err);
return false;
@ -84,7 +84,7 @@ export async function updatePassword(currentPassword: string, password: string,
});
return true;
} catch (err) {
} catch (err: any) {
onError(err);
return false;
@ -102,7 +102,7 @@ export async function updateRecoveryEmail(currentPassword: string, email: string
});
return true;
} catch (err) {
} catch (err: any) {
onError(err);
return false;

View File

@ -98,7 +98,7 @@ export async function fetchTopUsers() {
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);
return {

View File

@ -230,7 +230,7 @@ export function updater(update: Update, originRequest?: GramJs.AnyRequest) {
} else if (action instanceof GramJs.MessageActionChatDeleteUser) {
// eslint-disable-next-line no-underscore-dangle
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({
'@type': 'updateChat',
@ -248,7 +248,7 @@ export function updater(update: Update, originRequest?: GramJs.AnyRequest) {
} else if (action instanceof GramJs.MessageActionChatAddUser) {
// eslint-disable-next-line no-underscore-dangle
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({
'@type': 'updateChatJoin',

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -102,9 +102,9 @@ export default memo(withGlobal<OwnProps>(
const isPrivateChat = isUserId(chatId);
const isChatWithSelf = selectIsChatWithSelf(global, chatId);
const chat = selectChat(global, chatId);
const isChannel = !!chat && isChatChannel(chat);
const isGroup = !!chat && isChatBasicGroup(chat);
const isSuperGroup = !!chat && isChatSuperGroup(chat);
const isChannel = Boolean(chat) && isChatChannel(chat);
const isGroup = Boolean(chat) && isChatBasicGroup(chat);
const isSuperGroup = Boolean(chat) && isChatSuperGroup(chat);
const canPinForAll = (isPrivateChat && !isChatWithSelf) || isSuperGroup || isGroup;
const contactName = chat && isUserId(chat.id)
? 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 RE_EMOJI_ONLY = new RegExp(`^(?:${twemojiRegex.source})+$`, '');
export default (text: string): number | false => {
const parseEmojiOnlyString = (text: string): number | false => {
if (text.length > MAX_LENGTH) {
return false;
}
@ -27,3 +27,5 @@ export default (text: string): number | false => {
return emojiCount;
};
export default parseEmojiOnlyString;

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -1,7 +1,9 @@
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));
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}
</span>
{!!selectedMessagesCount && (
{Boolean(selectedMessagesCount) && (
<div className="MessageSelectToolbar-actions">
{messageListType !== 'scheduled' && (
renderButton(

View File

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

View File

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

View File

@ -180,7 +180,7 @@ const TextFormatter: FC<OwnProps> = ({
if (key === 'monospace' || key === 'strikethrough') {
if (Object.keys(selectedTextFormats).some(
(fKey) => fKey !== key && !!selectedTextFormats[fKey as keyof ISelectedTextFormats],
(fKey) => fKey !== key && Boolean(selectedTextFormats[fKey as keyof ISelectedTextFormats]),
)) {
return 'disabled';
}
@ -224,7 +224,7 @@ const TextFormatter: FC<OwnProps> = ({
// Somehow re-applying 'bold' command to already bold text doesn't work
document.execCommand(selectedFormats.bold ? 'removeFormat' : 'bold');
Object.keys(selectedFormats).forEach((key) => {
if ((key === 'italic' || key === 'underline') && !!selectedFormats[key]) {
if ((key === 'italic' || key === 'underline') && Boolean(selectedFormats[key])) {
document.execCommand(key);
}
});
@ -395,7 +395,7 @@ const TextFormatter: FC<OwnProps> = ({
const linkUrlConfirmClassName = buildClassName(
'TextFormatter-link-url-confirm',
!!linkUrl.length && 'shown',
Boolean(linkUrl.length) && 'shown',
);
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 MAX_MESSAGE_LENGTH = 4096;
export default (
const useClipboardPaste = (
insertTextAndUpdateCursor: (text: string, inputId?: string) => void,
setAttachments: StateHookSetter<ApiAttachment[]>,
editedMessage: ApiMessage | undefined,
@ -55,3 +55,5 @@ export default (
};
}, [insertTextAndUpdateCursor, editedMessage, setAttachments]);
};
export default useClipboardPaste;

View File

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

View File

@ -9,7 +9,7 @@ import getMessageTextAsHtml from '../helpers/getMessageTextAsHtml';
import focusEditableElement from '../../../../util/focusEditableElement';
import { hasMessageMedia } from '../../../../modules/helpers';
export default (
const useEditing = (
htmlRef: { current: string },
setHtml: (html: string) => void,
editedMessage: ApiMessage | undefined,
@ -57,3 +57,5 @@ export default (
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;
export default () => {
const useVoiceRecording = () => {
// eslint-disable-next-line no-null/no-null
const recordButtonRef = useRef<HTMLButtonElement>(null);
const [activeVoiceRecording, setActiveVoiceRecording] = useState<ActiveVoiceRecording>();
@ -93,3 +93,5 @@ export default () => {
startRecordTimeRef,
};
};
export default useVoiceRecording;

View File

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

View File

@ -202,7 +202,7 @@ const ManageGroupAdminRights: FC<OwnProps & StateProps> = ({
<div className="ListItem no-selection">
<Checkbox
name="changeInfo"
checked={!!permissions.changeInfo}
checked={Boolean(permissions.changeInfo)}
label={lang(isChannel ? 'EditAdminChangeChannelInfo' : 'EditAdminChangeGroupInfo')}
blocking
disabled={getControlIsDisabled('changeInfo')}
@ -213,7 +213,7 @@ const ManageGroupAdminRights: FC<OwnProps & StateProps> = ({
<div className="ListItem no-selection">
<Checkbox
name="postMessages"
checked={!!permissions.postMessages}
checked={Boolean(permissions.postMessages)}
label={lang('EditAdminPostMessages')}
blocking
disabled={getControlIsDisabled('postMessages')}
@ -225,7 +225,7 @@ const ManageGroupAdminRights: FC<OwnProps & StateProps> = ({
<div className="ListItem no-selection">
<Checkbox
name="editMessages"
checked={!!permissions.editMessages}
checked={Boolean(permissions.editMessages)}
label={lang('EditAdminEditMessages')}
blocking
disabled={getControlIsDisabled('editMessages')}
@ -236,7 +236,7 @@ const ManageGroupAdminRights: FC<OwnProps & StateProps> = ({
<div className="ListItem no-selection">
<Checkbox
name="deleteMessages"
checked={!!permissions.deleteMessages}
checked={Boolean(permissions.deleteMessages)}
label={lang(isChannel ? 'EditAdminDeleteMessages' : 'EditAdminGroupDeleteMessages')}
blocking
disabled={getControlIsDisabled('deleteMessages')}
@ -247,7 +247,7 @@ const ManageGroupAdminRights: FC<OwnProps & StateProps> = ({
<div className="ListItem no-selection">
<Checkbox
name="banUsers"
checked={!!permissions.banUsers}
checked={Boolean(permissions.banUsers)}
label={lang('EditAdminBanUsers')}
blocking
disabled={getControlIsDisabled('banUsers')}
@ -258,7 +258,7 @@ const ManageGroupAdminRights: FC<OwnProps & StateProps> = ({
<div className="ListItem no-selection">
<Checkbox
name="inviteUsers"
checked={!!permissions.inviteUsers}
checked={Boolean(permissions.inviteUsers)}
label={lang('EditAdminAddUsers')}
blocking
disabled={getControlIsDisabled('inviteUsers')}
@ -269,7 +269,7 @@ const ManageGroupAdminRights: FC<OwnProps & StateProps> = ({
<div className="ListItem no-selection">
<Checkbox
name="pinMessages"
checked={!!permissions.pinMessages}
checked={Boolean(permissions.pinMessages)}
label={lang('EditAdminPinMessages')}
blocking
disabled={getControlIsDisabled('pinMessages')}
@ -280,7 +280,7 @@ const ManageGroupAdminRights: FC<OwnProps & StateProps> = ({
<div className="ListItem no-selection">
<Checkbox
name="addAdmins"
checked={!!permissions.addAdmins}
checked={Boolean(permissions.addAdmins)}
label={lang('EditAdminAddAdmins')}
blocking
disabled={getControlIsDisabled('addAdmins')}
@ -290,7 +290,7 @@ const ManageGroupAdminRights: FC<OwnProps & StateProps> = ({
<div className="ListItem no-selection">
<Checkbox
name="manageCall"
checked={!!permissions.manageCall}
checked={Boolean(permissions.manageCall)}
label={lang('StartVoipChatPermission')}
blocking
disabled={getControlIsDisabled('manageCall')}
@ -301,7 +301,7 @@ const ManageGroupAdminRights: FC<OwnProps & StateProps> = ({
<div className="ListItem no-selection">
<Checkbox
name="anonymous"
checked={!!permissions.anonymous}
checked={Boolean(permissions.anonymous)}
label={lang('EditAdminSendAnonymously')}
blocking
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
const usersById = getGlobal().users.byId;
const chatsById = getGlobal().chats.byId;
const shouldUseSearchResults = !!searchQuery;
const shouldUseSearchResults = Boolean(searchQuery);
const listedIds = !shouldUseSearchResults
? memberIds
: (localContactIds ? filterUsersByName(localContactIds, usersById, searchQuery) : []);

View File

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

View File

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

View File

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

View File

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

View File

@ -224,6 +224,8 @@ function getBillingCountry(countryCode: string) {
return country ? country.name : '';
}
export default () => {
const usePaymentReducer = () => {
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);
};
export default useTwoFaReducer;

View File

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

View File

@ -8,7 +8,7 @@ const MIN_READY_STATE = 3;
// Avoid flickering when re-mounting previously buffered video
const DEBOUNCE = 200;
export default (noInitiallyBuffered = false) => {
const useBuffering = (noInitiallyBuffered = false) => {
const [isBuffered, setIsBuffered] = useState(!noInitiallyBuffered);
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';
export default () => {
const useCacheBuster = () => {
const [cacheBuster, setCacheBuster] = useState<boolean>(false);
const updateCacheBuster = useCallback(() => {
@ -9,3 +9,5 @@ export default () => {
return [cacheBuster, updateCacheBuster] as const;
};
export default useCacheBuster;

View File

@ -9,7 +9,7 @@ import {
import { compact } from '../util/iteratees';
import useLang from './useLang';
export default ({
const useChatContextActions = ({
chat,
user,
handleDelete,
@ -107,3 +107,5 @@ export default ({
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();
}
export default (
const useContextMenuHandlers = (
elementRef: RefObject<HTMLElement>,
isMenuDisabled?: boolean,
shouldDisableOnLink?: boolean,
@ -140,3 +140,5 @@ export default (
handleContextMenuHide,
};
};
export default useContextMenuHandlers;

View File

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

View File

@ -1,7 +1,7 @@
import { useEffect } from '../lib/teact/teact';
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);
return useEffect(() => {
// @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
}, dependencies, debugKey);
};
export default useEffectWithPrevDeps;

View File

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

View File

@ -1,6 +1,6 @@
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 setTrue = useCallback(() => {
@ -13,3 +13,5 @@ export default (initial = false): [boolean, AnyToVoidFunction, AnyToVoidFunction
return [value, setTrue, setFalse];
};
export default useFlag;

View File

@ -1,9 +1,11 @@
import { useCallback, useState } from '../lib/teact/teact';
export default () => {
const useForceUpdate = () => {
const [, setTrigger] = useState<boolean>(false);
return useCallback(() => {
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)
const AUTO_END_TIMEOUT = 1000;
export default (
const useHeavyAnimationCheck = (
handleAnimationStart: AnyToVoidFunction,
handleAnimationEnd: AnyToVoidFunction,
isDisabled = false,
@ -63,3 +63,5 @@ export function dispatchHeavyAnimationEvent(duration = AUTO_END_TIMEOUT) {
return onEnd;
}
export default useHeavyAnimationCheck;

View File

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

View File

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

View File

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

View File

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

View File

@ -1,7 +1,7 @@
import * as langProvider from '../util/langProvider';
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>();
if (langCode) {
@ -12,3 +12,5 @@ export default (langCode: string | undefined, key: string): string | undefined =
return translation;
};
export default useLangString;

View File

@ -1,7 +1,9 @@
import { useLayoutEffect } from '../lib/teact/teact';
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);
return useLayoutEffect(() => {
// @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
}, dependencies, debugKey);
};
export default useLayoutEffectWithPrevDeps;

View File

@ -5,7 +5,7 @@ import { ApiMediaFormat } from '../api/types';
import * as mediaLoader from '../util/mediaLoader';
import useForceUpdate from './useForceUpdate';
export default <T extends ApiMediaFormat = ApiMediaFormat.BlobUrl>(
const useMedia = <T extends ApiMediaFormat = ApiMediaFormat.BlobUrl>(
mediaHash: string | false | undefined,
noLoad = false,
// @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;
};
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
// 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 title = audio ? (audio.title || audio.fileName) : voice ? 'Voice message' : '';
const artist = (audio && audio.performer) || (sender && getSenderTitle(getTranslation, sender));
@ -71,3 +73,5 @@ function getCoverSize(audio?: ApiAudio, voice?: ApiVoice, url?: string) {
return undefined;
}
export default useMessageMediaMetadata;

View File

@ -6,7 +6,7 @@ import {
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,
) => {
const module = getModuleFromMemory(bundleName, moduleName);
@ -28,3 +28,5 @@ export default <B extends Bundles, M extends BundleModules<B>>(
return module;
};
export default useModuleLoader;

View File

@ -1,9 +1,11 @@
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);
if (!prevDeps || dependencies.some((d, i) => d !== prevDeps[i])) {
// @ts-ignore (workaround for "could be instantiated with a different subtype" issue)
cb(prevDeps || []);
}
};
export default useOnChange;

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -369,7 +369,7 @@ class TelegramClient {
async _connectSender(sender, dcId) {
// 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) {
try {

View File

@ -54,7 +54,7 @@ export async function checkAuthorization(client: TelegramClient) {
try {
await client.invoke(new Api.updates.GetState());
return true;
} catch (e) {
} catch (e: any) {
if (e.message === 'Disconnect') throw e;
return false;
}
@ -73,7 +73,7 @@ async function signInUser(
if (typeof authParams.phoneNumber === 'function') {
try {
phoneNumber = await authParams.phoneNumber();
} catch (err) {
} catch (err: any) {
if (err.message === 'RESTART_AUTH_WITH_QR') {
return signInUserWithQrCode(client, apiCredentials, authParams);
}
@ -92,7 +92,7 @@ async function signInUser(
}
break;
} catch (err) {
} catch (err: any) {
if (typeof authParams.phoneNumber !== 'function') {
throw err;
}
@ -110,7 +110,7 @@ async function signInUser(
try {
try {
phoneCode = await authParams.phoneCode(isCodeViaApp);
} catch (err) {
} catch (err: any) {
// This is the support for changing phone number from the phone code screen.
if (err.message === 'RESTART_AUTH') {
return signInUser(client, apiCredentials, authParams);
@ -136,7 +136,7 @@ async function signInUser(
}
return result.user;
} catch (err) {
} catch (err: any) {
if (err.message === 'SESSION_PASSWORD_NEEDED') {
return signInWithPassword(client, apiCredentials, authParams);
} else {
@ -167,7 +167,7 @@ async function signInUser(
}
return user;
} catch (err) {
} catch (err: any) {
authParams.onError(err);
}
}
@ -219,7 +219,7 @@ async function signInUserWithQrCode(
// 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
await Promise.race([updatePromise, inputPromise]);
} catch (err) {
} catch (err: any) {
if (err.message === 'RESTART_AUTH') {
return await signInUser(client, apiCredentials, authParams);
}
@ -249,7 +249,7 @@ async function signInUserWithQrCode(
return migratedResult.authorization.user;
}
}
} catch (err) {
} catch (err: any) {
if (err.message === 'SESSION_PASSWORD_NEEDED') {
return signInWithPassword(client, apiCredentials, authParams);
}
@ -258,7 +258,7 @@ async function signInUserWithQrCode(
}
// 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;
}
@ -294,7 +294,7 @@ async function sendCode(
phoneCodeHash: resendResult.phoneCodeHash,
isCodeViaApp: resendResult.type instanceof Api.auth.SentCodeTypeApp,
};
} catch (err) {
} catch (err: any) {
if (err.message === 'AUTH_RESTART') {
return sendCode(client, apiCredentials, phoneNumber, forceSMS);
} else {
@ -321,7 +321,7 @@ async function signInWithPassword(
})) as Api.auth.Authorization;
return user;
} catch (err) {
} catch (err: any) {
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].renderCount++;
}
} catch (err) {
} catch (err: any) {
handleError(err);
newRenderedValue = componentInstance.renderedValue;
@ -382,7 +382,7 @@ function unmountComponent(componentInstance: ComponentInstance) {
if (typeof cleanup === 'function') {
try {
cleanup();
} catch (err) {
} catch (err: any) {
handleError(err);
}
}
@ -552,7 +552,7 @@ function useLayoutEffectBase(
);
}
}
} catch (err) {
} catch (err: any) {
handleError(err);
}
}
@ -698,7 +698,9 @@ export function memo<T extends FC>(Component: T, areEqual = arePropsShallowEqual
}
// We need to keep it here for JSX.
export default {
const Teact = {
createElement,
Fragment,
};
export default Teact;

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -26,7 +26,7 @@ export function selectIsAdminInActiveGroupCall(global: GlobalState): boolean {
const chat = selectChat(global, chatId);
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) {

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -1,4 +1,4 @@
export default (store: AnyLiteral) => {
const generateIdFor = (store: AnyLiteral) => {
let id;
do {
@ -7,3 +7,5 @@ export default (store: AnyLiteral) => {
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) => {
img.onload = () => {
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)
.finally(() => {
if (image instanceof Blob) {
@ -24,7 +27,10 @@ export function resizeImage(
return new Promise((resolve) => {
img.onload = () => {
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)
.finally(() => {
if (image instanceof Blob) {
@ -38,7 +44,7 @@ export function resizeImage(
async function scale(
img: HTMLImageElement, width: number, height: number, outputType: string = 'image/png',
) {
): Promise<Blob | null> {
// Safari does not have built-in resize method with quality control
if ('createImageBitmap' in window) {
try {

View File

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

View File

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

View File

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

View File

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

View File

@ -1,6 +1,6 @@
import { DEBUG } from '../config';
export default (mediaEl: HTMLMediaElement) => {
const safePlay = (mediaEl: HTMLMediaElement) => {
mediaEl.play().catch((err) => {
if (DEBUG) {
// 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';
export default (e: React.UIEvent | Event) => {
const stopEvent = (e: React.UIEvent | Event) => {
e.stopPropagation();
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])],
}));
export default (theme: ISettings['theme'], withAnimation: boolean) => {
const switchTheme = (theme: ISettings['theme'], withAnimation: boolean) => {
const isDarkTheme = theme === 'dark';
const shouldAnimate = isInitialized && withAnimation;
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) => {
const script = document.createElement('script');
const removeElement = () => !!document.body.removeChild(script);
const removeElement = () => Boolean(document.body.removeChild(script));
script.src = url + new URLSearchParams({
authed: Number(authed).toString(),

View File

@ -14,7 +14,7 @@ type IDimensions = {
const IS_LANDSCAPE = IS_SINGLE_COLUMN_LAYOUT && isLandscape();
const initialHeight = window.innerHeight;
let windowSize = updateSizes();
let currentWindowSize = updateSizes();
let isRefreshDisabled = false;
function disableRefresh() {
@ -26,7 +26,7 @@ function enableRefresh() {
}
const handleResize = throttle(() => {
windowSize = updateSizes();
currentWindowSize = updateSizes();
if (!isRefreshDisabled && (
isMobileScreen() !== IS_SINGLE_COLUMN_LAYOUT
@ -61,8 +61,9 @@ export function updateSizes(): IDimensions {
}
function isMobileScreen() {
return windowSize.width <= MOBILE_SCREEN_MAX_WIDTH || (
windowSize.width <= MOBILE_SCREEN_LANDSCAPE_MAX_WIDTH && windowSize.height <= MOBILE_SCREEN_LANDSCAPE_MAX_HEIGHT
return currentWindowSize.width <= MOBILE_SCREEN_MAX_WIDTH || (
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;
}
export default {
get: () => windowSize,
getIsKeyboardVisible: () => initialHeight > windowSize.height,
const windowSize = {
get: () => currentWindowSize,
getIsKeyboardVisible: () => initialHeight > currentWindowSize.height,
disableRefresh,
enableRefresh,
};
export default windowSize;

View File

@ -1,6 +1,6 @@
{
"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",
"lib": [
"dom",

View File

@ -19,18 +19,32 @@ module.exports = (env = {}, argv = {}) => {
entry: './src/index.tsx',
target: 'web',
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,
host: '0.0.0.0',
disableHostCheck: true,
stats: 'minimal'
allowedHosts: "all",
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: {
filename: '[name].[contenthash].js',