[Dev] Bump dependencies (#1677)
This commit is contained in:
parent
23b96505df
commit
51b2893b39
@ -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": {
|
||||
|
||||
30
.github/workflows/tests.yml
vendored
30
.github/workflows/tests.yml
vendored
@ -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
34380
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
108
package.json
108
package.json
@ -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",
|
||||
|
||||
34
src/@types/global.d.ts
vendored
34
src/@types/global.d.ts
vendored
@ -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>[];
|
||||
}
|
||||
|
||||
@ -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)),
|
||||
|
||||
@ -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',
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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;
|
||||
|
||||
@ -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 {
|
||||
|
||||
@ -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',
|
||||
|
||||
@ -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);
|
||||
|
||||
@ -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,
|
||||
};
|
||||
|
||||
@ -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);
|
||||
|
||||
|
||||
@ -301,7 +301,7 @@ const CalendarModal: FC<OwnProps> = ({
|
||||
selectedDay === formatDay(currentYear, currentMonth, gridDate) && 'selected',
|
||||
)}
|
||||
>
|
||||
{!!gridDate && (
|
||||
{Boolean(gridDate) && (
|
||||
<span>{gridDate}</span>
|
||||
)}
|
||||
</div>
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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)!))
|
||||
|
||||
@ -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;
|
||||
|
||||
@ -54,7 +54,7 @@ export function renderMessageSummary(
|
||||
emojiWithSpace,
|
||||
...(Array.isArray(description) ? description : [description]),
|
||||
shouldAddEllipsis && '...',
|
||||
].filter(Boolean);
|
||||
].filter<TextPart>(Boolean);
|
||||
}
|
||||
|
||||
export function renderMessageText(
|
||||
|
||||
@ -116,7 +116,7 @@ const ChatMessageResults: FC<OwnProps & StateProps> = ({
|
||||
description={lang('ChatList.Search.NoResultsDescription')}
|
||||
/>
|
||||
)}
|
||||
{!!foundMessages.length && foundMessages.map(renderFoundMessage)}
|
||||
{foundMessages.map(renderFoundMessage)}
|
||||
</InfiniteScroll>
|
||||
</div>
|
||||
);
|
||||
|
||||
@ -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)}
|
||||
|
||||
@ -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'}
|
||||
|
||||
@ -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')}
|
||||
|
||||
@ -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;
|
||||
|
||||
@ -126,7 +126,7 @@ const MessageSelectToolbar: FC<OwnProps & StateProps> = ({
|
||||
{formattedMessagesCount}
|
||||
</span>
|
||||
|
||||
{!!selectedMessagesCount && (
|
||||
{Boolean(selectedMessagesCount) && (
|
||||
<div className="MessageSelectToolbar-actions">
|
||||
{messageListType !== 'scheduled' && (
|
||||
renderButton(
|
||||
|
||||
@ -376,8 +376,8 @@ const MiddleHeader: FC<OwnProps & StateProps> = ({
|
||||
|
||||
<GroupCallTopPane
|
||||
hasPinnedOffset={
|
||||
(shouldRenderPinnedMessage && !!renderingPinnedMessage)
|
||||
|| (shouldRenderAudioPlayer && !!renderingAudioMessage)
|
||||
(shouldRenderPinnedMessage && Boolean(renderingPinnedMessage))
|
||||
|| (shouldRenderAudioPlayer && Boolean(renderingAudioMessage))
|
||||
}
|
||||
chatId={chatId}
|
||||
/>
|
||||
|
||||
@ -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,
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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;
|
||||
|
||||
@ -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;
|
||||
|
||||
@ -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;
|
||||
|
||||
@ -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;
|
||||
|
||||
@ -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,
|
||||
|
||||
@ -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')}
|
||||
|
||||
@ -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) : []);
|
||||
|
||||
@ -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) => {
|
||||
|
||||
@ -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}
|
||||
|
||||
@ -79,7 +79,7 @@ const Tab: FC<OwnProps> = ({
|
||||
>
|
||||
<span>
|
||||
{renderText(title)}
|
||||
{!!badgeCount && (
|
||||
{Boolean(badgeCount) && (
|
||||
<span className={buildClassName('badge', isBadgeActive && classNames.badgeActive)}>{badgeCount}</span>
|
||||
)}
|
||||
<i />
|
||||
|
||||
@ -238,6 +238,8 @@ const foldersReducer: StateReducer<FoldersState, FoldersActions> = (
|
||||
}
|
||||
};
|
||||
|
||||
export default () => {
|
||||
const useFoldersReducer = () => {
|
||||
return useReducer(foldersReducer, INITIAL_STATE);
|
||||
};
|
||||
|
||||
export default useFoldersReducer;
|
||||
|
||||
@ -224,6 +224,8 @@ function getBillingCountry(countryCode: string) {
|
||||
return country ? country.name : '';
|
||||
}
|
||||
|
||||
export default () => {
|
||||
const usePaymentReducer = () => {
|
||||
return useReducer(reducer, INITIAL_STATE);
|
||||
};
|
||||
|
||||
export default usePaymentReducer;
|
||||
|
||||
@ -56,6 +56,8 @@ const twoFaReducer: StateReducer<TwoFaState, TwoFaActions> = (
|
||||
}
|
||||
};
|
||||
|
||||
export default () => {
|
||||
const useTwoFaReducer = () => {
|
||||
return useReducer(twoFaReducer, INITIAL_STATE);
|
||||
};
|
||||
|
||||
export default useTwoFaReducer;
|
||||
|
||||
@ -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;
|
||||
|
||||
@ -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;
|
||||
|
||||
@ -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;
|
||||
|
||||
@ -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;
|
||||
|
||||
@ -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;
|
||||
|
||||
@ -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;
|
||||
|
||||
@ -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;
|
||||
|
||||
@ -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;
|
||||
|
||||
@ -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;
|
||||
|
||||
@ -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;
|
||||
|
||||
@ -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;
|
||||
|
||||
@ -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;
|
||||
|
||||
@ -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;
|
||||
|
||||
@ -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;
|
||||
|
||||
@ -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;
|
||||
|
||||
@ -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;
|
||||
|
||||
@ -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;
|
||||
|
||||
@ -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;
|
||||
|
||||
@ -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;
|
||||
|
||||
@ -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;
|
||||
|
||||
@ -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;
|
||||
|
||||
@ -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;
|
||||
|
||||
@ -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;
|
||||
|
||||
@ -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;
|
||||
|
||||
@ -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;
|
||||
|
||||
@ -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;
|
||||
|
||||
@ -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;
|
||||
|
||||
@ -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 ||
|
||||
|
||||
@ -112,7 +112,7 @@ export async function updateTwoFaSettings(
|
||||
|
||||
await client.invoke(new Api.account.ConfirmPasswordEmail({ code }));
|
||||
break;
|
||||
} catch (err) {
|
||||
} catch (err: any) {
|
||||
onEmailCodeError!(err);
|
||||
}
|
||||
}
|
||||
|
||||
@ -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 {
|
||||
|
||||
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
@ -480,4 +480,5 @@ function DEBUG_addToVirtualTreeSize($current: VirtualRealElement | VirtualDomHea
|
||||
});
|
||||
}
|
||||
|
||||
export default { render };
|
||||
const TeactDOM = { render };
|
||||
export default TeactDOM;
|
||||
|
||||
@ -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;
|
||||
|
||||
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
@ -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({
|
||||
|
||||
@ -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) {
|
||||
|
||||
@ -158,7 +158,7 @@ addReducer('createGroupCallInviteLink', (global, actions) => {
|
||||
return;
|
||||
}
|
||||
|
||||
const canInvite = !!chat && !!chat.username;
|
||||
const canInvite = Boolean(chat.username);
|
||||
|
||||
(async () => {
|
||||
let { inviteLink } = chat.fullInfo!;
|
||||
|
||||
@ -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;
|
||||
|
||||
@ -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) {
|
||||
|
||||
@ -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) {
|
||||
|
||||
@ -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`
|
||||
|
||||
@ -253,6 +253,6 @@ export function handleClientMessage(e: ExtendableMessageEvent) {
|
||||
}
|
||||
}
|
||||
|
||||
self.onsync = () => {
|
||||
lastSyncAt = new Date().valueOf();
|
||||
};
|
||||
self.addEventListener('sync', () => {
|
||||
lastSyncAt = Date.now();
|
||||
});
|
||||
|
||||
@ -1,4 +1,6 @@
|
||||
export default {
|
||||
const oggToWav = {
|
||||
oggToWav() {
|
||||
},
|
||||
};
|
||||
|
||||
export default oggToWav;
|
||||
|
||||
@ -1,6 +1,8 @@
|
||||
export default {
|
||||
const webpToPng = {
|
||||
webpToPng() {
|
||||
},
|
||||
webpToPngBase64() {
|
||||
},
|
||||
};
|
||||
|
||||
export default webpToPng;
|
||||
|
||||
@ -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,
|
||||
}),
|
||||
]);
|
||||
|
||||
@ -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);
|
||||
|
||||
@ -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;
|
||||
|
||||
@ -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 {
|
||||
|
||||
@ -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);
|
||||
}
|
||||
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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;
|
||||
|
||||
@ -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,
|
||||
};
|
||||
};
|
||||
|
||||
@ -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;
|
||||
|
||||
@ -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;
|
||||
|
||||
@ -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;
|
||||
|
||||
@ -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(),
|
||||
|
||||
@ -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;
|
||||
|
||||
@ -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",
|
||||
|
||||
@ -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',
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user