[Dev] Fix eslint; Update dependencies (#1754)

This commit is contained in:
Alexander Zinchuk 2022-03-19 21:18:43 +01:00
parent 0d4ab658fc
commit 07ffad4425
61 changed files with 2032 additions and 1929 deletions

View File

@ -2,6 +2,7 @@
"extends": [ "extends": [
"react-app", "react-app",
"plugin:teactn/recommended", "plugin:teactn/recommended",
"airbnb",
"airbnb-typescript" "airbnb-typescript"
], ],
"plugins": [ "plugins": [
@ -34,11 +35,31 @@
"no-else-return": "off", "no-else-return": "off",
"no-plusplus": "off", "no-plusplus": "off",
"no-void": "off", "no-void": "off",
"no-continue": "off",
"default-case": "off", "default-case": "off",
"no-param-reassign": "off", "no-param-reassign": "off",
"no-prototype-builtins": "off", "no-prototype-builtins": "off",
"no-await-in-loop": "off", "no-await-in-loop": "off",
"no-nested-ternary": "off", "no-nested-ternary": "off",
"function-paren-newline": ["error", "consistent"],
"prefer-destructuring": "off",
// Allow for...of. Edited from:
// https://github.com/airbnb/javascript/blob/b4377fb03089dd7f08955242695860d47f9caab4/packages/eslint-config-airbnb-base/rules/style.js#L333
"no-restricted-syntax": [
"error",
{
"selector": "ForInStatement",
"message": "for..in loops iterate over the entire prototype chain, which is virtually never what you want. Use Object.{keys,values,entries}, and iterate over the resulting array."
},
{
"selector": "LabeledStatement",
"message": "Labels are a form of GOTO; using them makes code confusing and hard to maintain and understand."
},
{
"selector": "WithStatement",
"message": "`with` is disallowed in strict mode because it makes code impossible to predict and optimize."
}
],
"import/no-extraneous-dependencies": "off", "import/no-extraneous-dependencies": "off",
"import/prefer-default-export": "off", "import/prefer-default-export": "off",
"import/named": "off", "import/named": "off",
@ -47,13 +68,22 @@
"react/jsx-one-expression-per-line": "off", "react/jsx-one-expression-per-line": "off",
"react/button-has-type": "off", "react/button-has-type": "off",
"react/require-default-props": "off", "react/require-default-props": "off",
"react/function-component-definition": "off",
// Teact feature // Teact feature
"react/style-prop-object": "off", "react/style-prop-object": "off",
"react/jsx-no-bind": ["error", {
"ignoreRefs": true,
"allowArrowFunctions": true,
"allowFunctions": true,
"allowBind": false,
"ignoreDOMComponents": true
}],
"jsx-a11y/click-events-have-key-events": "off", "jsx-a11y/click-events-have-key-events": "off",
"jsx-a11y/no-static-element-interactions": "off", "jsx-a11y/no-static-element-interactions": "off",
"jsx-a11y/label-has-associated-control": "off", "jsx-a11y/label-has-associated-control": "off",
"jsx-a11y/anchor-is-valid": "off", "jsx-a11y/anchor-is-valid": "off",
"jsx-a11y/no-noninteractive-element-to-interactive-role": "off", "jsx-a11y/no-noninteractive-element-to-interactive-role": "off",
"jsx-a11y/media-has-caption": "off",
"no-async-without-await/no-async-without-await": 1, "no-async-without-await/no-async-without-await": 1,
"@typescript-eslint/no-use-before-define": [ "@typescript-eslint/no-use-before-define": [
"error", "error",
@ -64,6 +94,7 @@
"@typescript-eslint/camelcase": "off", "@typescript-eslint/camelcase": "off",
"@typescript-eslint/member-delimiter-style": "error", "@typescript-eslint/member-delimiter-style": "error",
"@typescript-eslint/default-param-last": "off", "@typescript-eslint/default-param-last": "off",
"@typescript-eslint/return-await": ["error", "in-try-catch"],
"teactn/prefer-separate-component-file": "off" "teactn/prefer-separate-component-file": "off"
}, },
"settings": { "settings": {
@ -71,8 +102,5 @@
}, },
"parserOptions": { "parserOptions": {
"project": "./tsconfig.json" "project": "./tsconfig.json"
},
"globals": {
"APP_REVISION": "readonly"
} }
} }

3443
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@ -34,36 +34,34 @@
"author": "Alexander Zinchuk (alexander@zinchuk.com)", "author": "Alexander Zinchuk (alexander@zinchuk.com)",
"license": "GPL-3.0-or-later", "license": "GPL-3.0-or-later",
"devDependencies": { "devDependencies": {
"@babel/core": "^7.17.0", "@babel/core": "^7.17.5",
"@babel/plugin-proposal-class-properties": "^7.16.7", "@babel/plugin-proposal-class-properties": "^7.16.7",
"@babel/plugin-proposal-nullish-coalescing-operator": "^7.16.7", "@babel/plugin-proposal-nullish-coalescing-operator": "^7.16.7",
"@babel/plugin-syntax-nullish-coalescing-operator": "^7.8.3", "@babel/plugin-syntax-nullish-coalescing-operator": "^7.8.3",
"@babel/preset-env": "^7.16.11", "@babel/preset-env": "^7.16.11",
"@babel/preset-react": "^7.16.7", "@babel/preset-react": "^7.16.7",
"@babel/preset-typescript": "^7.16.7", "@babel/preset-typescript": "^7.16.7",
"@peculiar/webcrypto": "^1.2.3", "@peculiar/webcrypto": "^1.3.2",
"@testing-library/jest-dom": "^5.16.2", "@testing-library/jest-dom": "^5.16.2",
"@types/croppie": "^2.6.1", "@types/croppie": "^2.6.1",
"@types/css-font-loading-module": "0.0.7", "@types/jest": "^27.4.1",
"@types/dom-mediacapture-record": "^1.0.11", "@types/react": "^17.0.40",
"@types/jest": "^27.4.0", "@types/react-dom": "^17.0.13",
"@types/react": "^17.0.39",
"@types/react-dom": "^17.0.11",
"@types/resize-observer-browser": "^0.1.7",
"@types/wicg-mediasession": "^1.1.3", "@types/wicg-mediasession": "^1.1.3",
"@typescript-eslint/eslint-plugin": "^5.11.0", "@typescript-eslint/eslint-plugin": "^5.14.0",
"@typescript-eslint/parser": "^5.11.0", "@typescript-eslint/parser": "^5.14.0",
"@webpack-cli/serve": "^1.6.1", "@webpack-cli/serve": "^1.6.1",
"autoprefixer": "^10.4.2", "autoprefixer": "^10.4.2",
"babel-loader": "^8.2.3", "babel-loader": "^8.2.3",
"browserlist": "^1.0.1", "browserlist": "^1.0.1",
"buffer": "^6.0.3", "buffer": "^6.0.3",
"cross-env": "^7.0.3", "cross-env": "^7.0.3",
"css-loader": "^6.6.0", "css-loader": "^6.7.1",
"css-minimizer-webpack-plugin": "^3.4.1", "css-minimizer-webpack-plugin": "^3.4.1",
"dotenv": "^16.0.0", "dotenv": "^16.0.0",
"eslint": "^8.8.0", "eslint": "^8.10.0",
"eslint-config-airbnb-typescript": "^16.1.0", "eslint-config-airbnb": "^19.0.4",
"eslint-config-airbnb-typescript": "^16.1.1",
"eslint-config-react-app": "^7.0.0", "eslint-config-react-app": "^7.0.0",
"eslint-import-resolver-webpack": "^0.13.2", "eslint-import-resolver-webpack": "^0.13.2",
"eslint-plugin-flowtype": "^8.0.3", "eslint-plugin-flowtype": "^8.0.3",
@ -71,30 +69,30 @@
"eslint-plugin-jsx-a11y": "^6.5.1", "eslint-plugin-jsx-a11y": "^6.5.1",
"eslint-plugin-no-async-without-await": "^1.2.0", "eslint-plugin-no-async-without-await": "^1.2.0",
"eslint-plugin-no-null": "^1.0.2", "eslint-plugin-no-null": "^1.0.2",
"eslint-plugin-react": "^7.28.0", "eslint-plugin-react": "^7.29.3",
"eslint-plugin-react-hooks": "^4.3.0", "eslint-plugin-react-hooks": "^4.3.0",
"eslint-plugin-teactn": "git+https://github.com/korenskoy/eslint-plugin-teactn#c2c39dd005d58c07c24c4361de804dce1c6261b5", "eslint-plugin-teactn": "git+https://github.com/korenskoy/eslint-plugin-teactn#c2c39dd005d58c07c24c4361de804dce1c6261b5",
"git-revision-webpack-plugin": "^5.0.0", "git-revision-webpack-plugin": "^5.0.0",
"html-webpack-plugin": "^5.5.0", "html-webpack-plugin": "^5.5.0",
"husky": "^7.0.4", "husky": "^7.0.4",
"jest": "^27.5.0", "jest": "^27.5.1",
"jest-raw-loader": "^1.0.1", "jest-raw-loader": "^1.0.1",
"lint-staged": "^12.3.3", "lint-staged": "^12.3.5",
"mini-css-extract-plugin": "^2.5.3", "mini-css-extract-plugin": "^2.6.0",
"postcss-loader": "^6.2.1", "postcss-loader": "^6.2.1",
"postcss-modules": "^4.3.0", "postcss-modules": "^4.3.1",
"react": "^17.0.2", "react": "^17.0.2",
"replace-in-file": "^6.3.2", "replace-in-file": "^6.3.2",
"sass": "^1.49.7", "sass": "^1.49.9",
"sass-loader": "^12.4.0", "sass-loader": "^12.6.0",
"style-loader": "^3.3.1", "style-loader": "^3.3.1",
"stylelint": "^14.3.0", "stylelint": "^14.5.3",
"stylelint-config-recommended-scss": "^5.0.2", "stylelint-config-recommended-scss": "^5.0.2",
"stylelint-declaration-block-no-ignored-properties": "^2.5.0", "stylelint-declaration-block-no-ignored-properties": "^2.5.0",
"stylelint-group-selectors": "^1.0.8", "stylelint-group-selectors": "^1.0.8",
"stylelint-high-performance-animation": "^1.6.0", "stylelint-high-performance-animation": "^1.6.0",
"typescript": "^4.5.5", "typescript": "^4.6.2",
"webpack": "^5.68.0", "webpack": "^5.70.0",
"webpack-bundle-analyzer": "^4.5.0", "webpack-bundle-analyzer": "^4.5.0",
"webpack-cli": "^4.9.2", "webpack-cli": "^4.9.2",
"webpack-dev-server": "^4.7.4", "webpack-dev-server": "^4.7.4",

View File

@ -1,6 +1,6 @@
declare const process: NodeJS.Process; declare const process: NodeJS.Process;
declare var APP_REVISION: string; declare const APP_REVISION: string;
declare namespace React { declare namespace React {
interface HTMLAttributes { interface HTMLAttributes {

View File

@ -598,7 +598,9 @@ function buildGeo(media: GramJs.MessageMediaGeo): ApiLocation | undefined {
} }
function buildVenue(media: GramJs.MessageMediaVenue): ApiLocation | undefined { function buildVenue(media: GramJs.MessageMediaVenue): ApiLocation | undefined {
const { geo, title, provider, address, venueId, venueType } = media; const {
geo, title, provider, address, venueId, venueType,
} = media;
const point = buildGeoPoint(geo); const point = buildGeoPoint(geo);
return point && { return point && {
type: 'venue', type: 'venue',
@ -624,7 +626,9 @@ function buildGeoLive(media: GramJs.MessageMediaGeoLive): ApiLocation | undefine
function buildGeoPoint(geo: GramJs.TypeGeoPoint): ApiLocation['geo'] | undefined { function buildGeoPoint(geo: GramJs.TypeGeoPoint): ApiLocation['geo'] | undefined {
if (geo instanceof GramJs.GeoPointEmpty) return undefined; if (geo instanceof GramJs.GeoPointEmpty) return undefined;
const { long, lat, accuracyRadius, accessHash } = geo; const {
long, lat, accuracyRadius, accessHash,
} = geo;
return { return {
long, long,
lat, lat,

View File

@ -229,8 +229,19 @@ export function sendMessage(
onProgress?: ApiOnProgress, onProgress?: ApiOnProgress,
) { ) {
const localMessage = buildLocalMessage( const localMessage = buildLocalMessage(
chat, text, entities, replyingTo, attachment, sticker, gif, poll, contact, groupedId, scheduledAt, chat,
sendAs, serverTimeOffset, text,
entities,
replyingTo,
attachment,
sticker,
gif,
poll,
contact,
groupedId,
scheduledAt,
sendAs,
serverTimeOffset,
); );
onUpdate({ onUpdate({
'@type': localMessage.isScheduled ? 'newScheduledMessage' : 'newMessage', '@type': localMessage.isScheduled ? 'newScheduledMessage' : 'newMessage',

View File

@ -776,7 +776,6 @@ export function updater(update: Update, originRequest?: GramJs.AnyRequest) {
return; return;
} }
if (_entities?.length) { if (_entities?.length) {
_entities _entities
.filter((e) => e instanceof GramJs.User && !e.contact) .filter((e) => e instanceof GramJs.User && !e.contact)

View File

@ -81,7 +81,7 @@ const CountryCodeInput: FC<OwnProps & StateProps> = ({
updateFilter(target.value); updateFilter(target.value);
}, [filter, updateFilter, value]); }, [filter, updateFilter, value]);
const CodeInput: FC<{ onTrigger: () => void; isOpen?: boolean }> = ({ onTrigger, isOpen }) => { const CodeInput: FC<{ onTrigger: () => void; isOpen?: boolean }> = useCallback(({ onTrigger, isOpen }) => {
const handleTrigger = () => { const handleTrigger = () => {
if (isOpen) { if (isOpen) {
return; return;
@ -126,7 +126,7 @@ const CountryCodeInput: FC<OwnProps & StateProps> = ({
)} )}
</div> </div>
); );
}; }, [filter, handleInput, handleInputKeyDown, id, isLoading, lang, value]);
return ( return (
<DropdownMenu <DropdownMenu

View File

@ -280,34 +280,32 @@ const Audio: FC<OwnProps> = ({
function renderWithTitle() { function renderWithTitle() {
return ( return (
<> <div className={contentClassName}>
<div className={contentClassName}> <div className="content-row">
<div className="content-row"> <p className="title" dir="auto" title={renderFirstLine()}>{renderText(renderFirstLine())}</p>
<p className="title" dir="auto" title={renderFirstLine()}>{renderText(renderFirstLine())}</p>
<div className="message-date"> <div className="message-date">
{date && ( {date && (
<Link <Link
className="date" className="date"
onClick={handleDateClick} onClick={handleDateClick}
> >
{formatPastTimeShort(lang, date * 1000)} {formatPastTimeShort(lang, date * 1000)}
</Link> </Link>
)} )}
</div>
</div> </div>
{withSeekline && (
<div className="meta search-result" dir={isRtl ? 'rtl' : undefined}>
<span className="duration with-seekline" dir="auto">
{playProgress < 1 && `${formatMediaDuration(duration * playProgress, duration)}`}
</span>
{renderSeekline(playProgress, bufferedProgress, seekerRef)}
</div>
)}
{!withSeekline && renderSecondLine()}
</div> </div>
</>
{withSeekline && (
<div className="meta search-result" dir={isRtl ? 'rtl' : undefined}>
<span className="duration with-seekline" dir="auto">
{playProgress < 1 && `${formatMediaDuration(duration * playProgress, duration)}`}
</span>
{renderSeekline(playProgress, bufferedProgress, seekerRef)}
</div>
)}
{!withSeekline && renderSecondLine()}
</div>
); );
} }
@ -356,8 +354,17 @@ const Audio: FC<OwnProps> = ({
)} )}
{origin === AudioOrigin.Search && renderWithTitle()} {origin === AudioOrigin.Search && renderWithTitle()}
{origin !== AudioOrigin.Search && audio && renderAudio( {origin !== AudioOrigin.Search && audio && renderAudio(
lang, audio, duration, isPlaying, playProgress, bufferedProgress, seekerRef, lang,
(isDownloading || isUploading), date, transferProgress, onDateClick ? handleDateClick : undefined, audio,
duration,
isPlaying,
playProgress,
bufferedProgress,
seekerRef,
(isDownloading || isUploading),
date,
transferProgress,
onDateClick ? handleDateClick : undefined,
)} )}
{origin === AudioOrigin.SharedMedia && (voice || video) && renderWithTitle()} {origin === AudioOrigin.SharedMedia && (voice || video) && renderWithTitle()}
{origin === AudioOrigin.Inline && voice && ( {origin === AudioOrigin.Inline && voice && (

View File

@ -1,4 +1,6 @@
import React, { FC, memo, useCallback, useRef } from '../../lib/teact/teact'; import React, {
FC, memo, useCallback, useRef,
} from '../../lib/teact/teact';
import { ApiMessage } from '../../api/types'; import { ApiMessage } from '../../api/types';

View File

@ -45,7 +45,7 @@ export function renderMessageSummary(
highlight?: string, highlight?: string,
truncateLength = TRUNCATED_SUMMARY_LENGTH, truncateLength = TRUNCATED_SUMMARY_LENGTH,
): TextPart[] { ): TextPart[] {
let { entities } = message.content.text || {}; const { entities } = message.content.text || {};
const hasSpoilers = entities?.some((e) => e.type === ApiMessageEntityTypes.Spoiler); const hasSpoilers = entities?.some((e) => e.type === ApiMessageEntityTypes.Spoiler);
if (!hasSpoilers) { if (!hasSpoilers) {

View File

@ -104,7 +104,9 @@ const SettingsFoldersChatFilters: FC<OwnProps> = ({
}, [mode, selectedChatIds, dispatch]); }, [mode, selectedChatIds, dispatch]);
useHistoryBack( useHistoryBack(
isActive, onReset, onScreenSelect, isActive,
onReset,
onScreenSelect,
mode === 'included' ? SettingsScreens.FoldersIncludedChats : SettingsScreens.FoldersExcludedChats, mode === 'included' ? SettingsScreens.FoldersIncludedChats : SettingsScreens.FoldersExcludedChats,
); );

View File

@ -163,17 +163,15 @@ const MediaViewerActions: FC<OwnProps & StateProps> = ({
return ( return (
<div className="MediaViewerActions"> <div className="MediaViewerActions">
{!isAvatar && !isProtected && ( {!isAvatar && !isProtected && (
<> <Button
<Button round
round size="smaller"
size="smaller" color="translucent-white"
color="translucent-white" ariaLabel={lang('Forward')}
ariaLabel={lang('Forward')} onClick={onForward}
onClick={onForward} >
> <i className="icon-forward" />
<i className="icon-forward" /> </Button>
</Button>
</>
)} )}
{renderDownloadButton()} {renderDownloadButton()}
<Button <Button

View File

@ -518,7 +518,8 @@ const MediaViewerSlides: FC<OwnProps> = ({
<MediaViewerContent <MediaViewerContent
/* eslint-disable-next-line react/jsx-props-no-spreading */ /* eslint-disable-next-line react/jsx-props-no-spreading */
{...rest} {...rest}
messageId={previousMessageId} /> messageId={previousMessageId}
/>
</div> </div>
)} )}
{activeMessageId && ( {activeMessageId && (
@ -543,7 +544,8 @@ const MediaViewerSlides: FC<OwnProps> = ({
<MediaViewerContent <MediaViewerContent
/* eslint-disable-next-line react/jsx-props-no-spreading */ /* eslint-disable-next-line react/jsx-props-no-spreading */
{...rest} {...rest}
messageId={nextMessageId}/> messageId={nextMessageId}
/>
</div> </div>
)} )}
</div> </div>
@ -571,4 +573,5 @@ function checkIfControlTarget(e: TouchEvent | MouseEvent) {
e.preventDefault(); e.preventDefault();
return true; return true;
} }
return false;
} }

View File

@ -1,12 +1,16 @@
import React, {
FC, memo, useCallback, useEffect, useRef, useState,
} from '../../lib/teact/teact';
import { ApiDimensions } from '../../api/types'; import { ApiDimensions } from '../../api/types';
import useBuffering from '../../hooks/useBuffering'; import useBuffering from '../../hooks/useBuffering';
import useFullscreenStatus from '../../hooks/useFullscreen'; import useFullscreenStatus from '../../hooks/useFullscreen';
import useShowTransition from '../../hooks/useShowTransition'; import useShowTransition from '../../hooks/useShowTransition';
import useVideoCleanup from '../../hooks/useVideoCleanup'; import useVideoCleanup from '../../hooks/useVideoCleanup';
import React, { FC, memo, useCallback, useEffect, useRef, useState } from '../../lib/teact/teact';
import { IS_IOS, IS_SINGLE_COLUMN_LAYOUT, IS_TOUCH_ENV } from '../../util/environment'; import { IS_IOS, IS_SINGLE_COLUMN_LAYOUT, IS_TOUCH_ENV } from '../../util/environment';
import safePlay from '../../util/safePlay'; import safePlay from '../../util/safePlay';
import Button from '../ui/Button'; import Button from '../ui/Button';
import ProgressSpinner from '../ui/ProgressSpinner'; import ProgressSpinner from '../ui/ProgressSpinner';

View File

@ -55,12 +55,11 @@ const VideoPlayerControls: FC<IProps> = ({
const isSeekingRef = useRef<boolean>(false); const isSeekingRef = useRef<boolean>(false);
const isSeeking = isSeekingRef.current; const isSeeking = isSeekingRef.current;
useEffect(() => { useEffect(() => {
let timeout: number | undefined; let timeout: number | undefined;
if (!isVisible || !isPlayed || isSeeking) { if (!isVisible || !isPlayed || isSeeking) {
if (timeout) window.clearTimeout(timeout); if (timeout) window.clearTimeout(timeout);
return; return undefined;
} }
timeout = window.setTimeout(() => { timeout = window.setTimeout(() => {
setVisibility(false); setVisibility(false);
@ -113,7 +112,6 @@ const VideoPlayerControls: FC<IProps> = ({
}); });
}, [isVisible, handleStartSeek, handleSeek, handleStopSeek]); }, [isVisible, handleStartSeek, handleSeek, handleStopSeek]);
return ( return (
<div <div
className={buildClassName('VideoPlayerControls', isForceMobileVersion && 'mobile', isVisible && 'active')} className={buildClassName('VideoPlayerControls', isForceMobileVersion && 'mobile', isVisible && 'active')}

View File

@ -6,8 +6,9 @@ import useModuleLoader from '../../hooks/useModuleLoader';
const EmojiInteractionAnimationAsync: FC<OwnProps> = (props) => { const EmojiInteractionAnimationAsync: FC<OwnProps> = (props) => {
const { activeEmojiInteraction } = props; const { activeEmojiInteraction } = props;
const EmojiInteractionAnimation = useModuleLoader(Bundles.Extra, 'EmojiInteractionAnimation', const EmojiInteractionAnimation = useModuleLoader(
!activeEmojiInteraction); Bundles.Extra, 'EmojiInteractionAnimation', !activeEmojiInteraction,
);
// eslint-disable-next-line react/jsx-props-no-spreading // eslint-disable-next-line react/jsx-props-no-spreading
return EmojiInteractionAnimation ? <EmojiInteractionAnimation {...props} /> : undefined; return EmojiInteractionAnimation ? <EmojiInteractionAnimation {...props} /> : undefined;

View File

@ -354,7 +354,10 @@ const MiddleColumn: FC<StateProps> = ({
useHistoryBack( useHistoryBack(
renderingChatId && renderingThreadId, renderingChatId && renderingThreadId,
closeChat, undefined, undefined, undefined, closeChat,
undefined,
undefined,
undefined,
messageLists?.map(createMessageHash) || [], messageLists?.map(createMessageHash) || [],
); );

View File

@ -1,4 +1,6 @@
import React, { FC, memo, useCallback, useState } from '../../lib/teact/teact'; import React, {
FC, memo, useCallback, useState,
} from '../../lib/teact/teact';
import { withGlobal, getDispatch } from '../../lib/teact/teactn'; import { withGlobal, getDispatch } from '../../lib/teact/teactn';
import { ApiUser } from '../../api/types'; import { ApiUser } from '../../api/types';
@ -61,10 +63,9 @@ const UserReportPanel: FC<OwnProps & StateProps> = ({ userId, user }) => {
]); ]);
if (!settings) { if (!settings) {
return; return undefined;
} }
return ( return (
<div className="UserReportPanel"> <div className="UserReportPanel">
{canAddContact && ( {canAddContact && (

View File

@ -57,7 +57,7 @@ const AttachMenu: FC<OwnProps> = ({
const lang = useLang(); const lang = useLang();
if (!isButtonVisible) { if (!isButtonVisible) {
return; return undefined;
} }
return ( return (

View File

@ -27,6 +27,7 @@ const StickerSetCover: FC<OwnProps> = ({ stickerSet, observeIntersection }) => {
const firstLetters = useMemo(() => { const firstLetters = useMemo(() => {
if ((isVideo && !IS_WEBM_SUPPORTED) || !mediaData) return getFirstLetters(stickerSet.title, 2); if ((isVideo && !IS_WEBM_SUPPORTED) || !mediaData) return getFirstLetters(stickerSet.title, 2);
return undefined;
}, [isVideo, mediaData, stickerSet.title]); }, [isVideo, mediaData, stickerSet.title]);
return ( return (

View File

@ -2,7 +2,7 @@ export default async function getFilesFromDataTransferItems(dataTransferItems: D
const files: File[] = []; const files: File[] = [];
function traverseFileTreePromise(entry: FileSystemEntry | File, item: DataTransferItem) { function traverseFileTreePromise(entry: FileSystemEntry | File, item: DataTransferItem) {
return new Promise(resolve => { return new Promise((resolve) => {
if (entry instanceof File) { if (entry instanceof File) {
files.push(entry); files.push(entry);
resolve(entry); resolve(entry);
@ -20,11 +20,11 @@ export default async function getFilesFromDataTransferItems(dataTransferItems: D
resolve(itemFile); resolve(itemFile);
}); });
} else if (entry.isDirectory) { } else if (entry.isDirectory) {
let dirReader = (entry as FileSystemDirectoryEntry).createReader(); const dirReader = (entry as FileSystemDirectoryEntry).createReader();
dirReader.readEntries((entries) => { dirReader.readEntries((entries) => {
let entriesPromises = []; const entriesPromises = [];
for (let entr of entries) { for (let i = 0; i < entries.length; i++) {
entriesPromises.push(traverseFileTreePromise(entr, item)); entriesPromises.push(traverseFileTreePromise(entries[i], item));
} }
resolve(Promise.all(entriesPromises)); resolve(Promise.all(entriesPromises));
}); });
@ -32,8 +32,9 @@ export default async function getFilesFromDataTransferItems(dataTransferItems: D
}); });
} }
let entriesPromises = []; const entriesPromises = [];
for (let item of dataTransferItems) { for (let i = 0; i < dataTransferItems.length; i++) {
const item = dataTransferItems[i];
if (item.kind === 'file') { if (item.kind === 'file') {
const entry = item.webkitGetAsEntry() || item.getAsFile(); const entry = item.webkitGetAsEntry() || item.getAsFile();
if (entry) { if (entry) {
@ -46,4 +47,3 @@ export default async function getFilesFromDataTransferItems(dataTransferItems: D
return files; return files;
} }

View File

@ -24,7 +24,6 @@ const useClipboardPaste = (
return; return;
} }
const pastedText = e.clipboardData.getData('text').substring(0, MAX_MESSAGE_LENGTH); const pastedText = e.clipboardData.getData('text').substring(0, MAX_MESSAGE_LENGTH);
const { items } = e.clipboardData; const { items } = e.clipboardData;
let files: File[] = []; let files: File[] = [];

View File

@ -36,10 +36,10 @@ const prepareLibraryMemo = memoized(prepareLibrary);
const searchInLibraryMemo = memoized(searchInLibrary); const searchInLibraryMemo = memoized(searchInLibrary);
try { try {
RE_EMOJI_SEARCH = new RegExp('(^|\\s):[-+_:\\p{L}\\p{N}]*$', 'gui'); RE_EMOJI_SEARCH = /(^|\s):[-+_:\p{L}\p{N}]*$/gui;
} catch (e) { } catch (e) {
// Support for older versions of firefox // Support for older versions of firefox
RE_EMOJI_SEARCH = new RegExp('(^|\\s):[-+_:\\d\\wа-яё]*$', 'gi'); RE_EMOJI_SEARCH = /(^|\s):[-+_:\d\wа-яё]*$/gi;
} }
export default function useEmojiTooltip( export default function useEmojiTooltip(

View File

@ -16,10 +16,10 @@ const runThrottled = throttle((cb) => cb(), 500, true);
let RE_USERNAME_SEARCH: RegExp; let RE_USERNAME_SEARCH: RegExp;
try { try {
RE_USERNAME_SEARCH = new RegExp('(^|\\s)@[-_\\p{L}\\p{M}\\p{N}]*$', 'gui'); RE_USERNAME_SEARCH = /(^|\s)@[-_\p{L}\p{M}\p{N}]*$/gui;
} catch (e) { } catch (e) {
// Support for older versions of firefox // Support for older versions of firefox
RE_USERNAME_SEARCH = new RegExp('(^|\\s)@[-_\\d\\wа-яё]*$', 'gi'); RE_USERNAME_SEARCH = /(^|\s)@[-_\d\wа-яё]*$/gi;
} }
export default function useMentionTooltip( export default function useMentionTooltip(

View File

@ -19,7 +19,9 @@ import { formatCountdownShort, formatLastUpdated } from '../../../util/dateForma
import useLang from '../../../hooks/useLang'; import useLang from '../../../hooks/useLang';
import useForceUpdate from '../../../hooks/useForceUpdate'; import useForceUpdate from '../../../hooks/useForceUpdate';
import useTimeout from '../../../hooks/useTimeout'; import useTimeout from '../../../hooks/useTimeout';
import { getMetersPerPixel, getVenueColor, getVenueIconUrl, prepareMapUrl } from '../../../util/map'; import {
getMetersPerPixel, getVenueColor, getVenueIconUrl, prepareMapUrl,
} from '../../../util/map';
import buildClassName from '../../../util/buildClassName'; import buildClassName from '../../../util/buildClassName';
import usePrevious from '../../../hooks/usePrevious'; import usePrevious from '../../../hooks/usePrevious';
import useInterval from '../../../hooks/useInterval'; import useInterval from '../../../hooks/useInterval';
@ -28,7 +30,7 @@ import { getServerTime } from '../../../util/serverTime';
import Avatar from '../../common/Avatar'; import Avatar from '../../common/Avatar';
import Skeleton from '../../ui/Skeleton'; import Skeleton from '../../ui/Skeleton';
import mapPin from '/src/assets/map-pin.svg'; import mapPin from '../../../assets/map-pin.svg';
import './Location.scss'; import './Location.scss';
const MOVE_THRESHOLD = 0.0001; // ~11m const MOVE_THRESHOLD = 0.0001; // ~11m
@ -79,7 +81,9 @@ const Location: FC<OwnProps> = ({
const [point, setPoint] = useState(geo); const [point, setPoint] = useState(geo);
const shouldRenderText = type === 'venue' || (type === 'geoLive' && !isExpired); const shouldRenderText = type === 'venue' || (type === 'geoLive' && !isExpired);
const { width, height, zoom, scale } = DEFAULT_MAP_CONFIG; const {
width, height, zoom, scale,
} = DEFAULT_MAP_CONFIG;
const mediaHash = Boolean(lastSyncTime) && buildStaticMapHash(point, width, height, zoom, scale); const mediaHash = Boolean(lastSyncTime) && buildStaticMapHash(point, width, height, zoom, scale);
const mediaBlobUrl = useMedia(mediaHash); const mediaBlobUrl = useMedia(mediaHash);
@ -206,13 +210,15 @@ const Location: FC<OwnProps> = ({
function renderMap() { function renderMap() {
if (!mapBlobUrl) return <Skeleton width={width} height={height} />; if (!mapBlobUrl) return <Skeleton width={width} height={height} />;
return <img return (
className="full-media map" <img
src={mapBlobUrl} className="full-media map"
alt="Location on a map" src={mapBlobUrl}
width={DEFAULT_MAP_CONFIG.width} alt="Location on a map"
height={DEFAULT_MAP_CONFIG.height} width={DEFAULT_MAP_CONFIG.width}
/>; height={DEFAULT_MAP_CONFIG.height}
/>
);
} }
function renderPin() { function renderPin() {
@ -279,4 +285,3 @@ const Location: FC<OwnProps> = ({
}; };
export default memo(Location); export default memo(Location);

View File

@ -964,7 +964,9 @@ const Message: FC<OwnProps & StateProps> = ({
export default memo(withGlobal<OwnProps>( export default memo(withGlobal<OwnProps>(
(global, ownProps): StateProps => { (global, ownProps): StateProps => {
const { focusedMessage, forwardMessages, lastSyncTime, serverTimeOffset } = global; const {
focusedMessage, forwardMessages, lastSyncTime, serverTimeOffset,
} = global;
const { const {
message, album, withSenderName, withAvatar, threadId, messageListType, isLastInDocumentGroup, message, album, withSenderName, withAvatar, threadId, messageListType, isLastInDocumentGroup,
} = ownProps; } = ownProps;

View File

@ -25,7 +25,9 @@ type OwnProps = {
const cn = createClassNameBuilder('ReactionSelectorReaction'); const cn = createClassNameBuilder('ReactionSelectorReaction');
const ReactionSelectorReaction: FC<OwnProps> = ({ reaction, previewIndex, onSendReaction, isReady }) => { const ReactionSelectorReaction: FC<OwnProps> = ({
reaction, previewIndex, onSendReaction, isReady,
}) => {
// eslint-disable-next-line no-null/no-null // eslint-disable-next-line no-null/no-null
const containerRef = useRef<HTMLDivElement>(null); const containerRef = useRef<HTMLDivElement>(null);

View File

@ -41,16 +41,17 @@ export default function withSelectControl(WrappedComponent: FC) {
}, [toggleMessageSelection, message]); }, [toggleMessageSelection, message]);
const newProps = useMemo(() => { const newProps = useMemo(() => {
const { dimensions: dims, onClick } = props;
return { return {
...props, ...props,
isInSelectMode, isInSelectMode,
isSelected, isSelected,
dimensions: { dimensions: {
...props.dimensions, ...dims,
x: 0, x: 0,
y: 0, y: 0,
}, },
onClick: isInSelectMode ? undefined : props.onClick, onClick: isInSelectMode ? undefined : onClick,
}; };
}, [props, isInSelectMode, isSelected]); }, [props, isInSelectMode, isSelected]);

View File

@ -167,8 +167,21 @@ const Profile: FC<OwnProps & StateProps> = ({
const tabType = tabs[activeTab].type as ProfileTabType; const tabType = tabs[activeTab].type as ProfileTabType;
const [resultType, viewportIds, getMore, noProfileInfo] = useProfileViewportIds( const [resultType, viewportIds, getMore, noProfileInfo] = useProfileViewportIds(
isRightColumnShown, loadMoreMembers, loadCommonChats, searchMediaMessagesLocal, tabType, mediaSearchType, members, isRightColumnShown,
commonChatIds, usersById, userStatusesById, chatsById, chatMessages, foundIds, chatId, lastSyncTime, loadMoreMembers,
loadCommonChats,
searchMediaMessagesLocal,
tabType,
mediaSearchType,
members,
commonChatIds,
usersById,
userStatusesById,
chatsById,
chatMessages,
foundIds,
chatId,
lastSyncTime,
serverTimeOffset, serverTimeOffset,
); );
const activeKey = tabs.findIndex(({ type }) => type === resultType); const activeKey = tabs.findIndex(({ type }) => type === resultType);

View File

@ -211,7 +211,7 @@ const RightColumn: FC<StateProps> = ({
// We need to clear profile state and management screen state, when changing chats // We need to clear profile state and management screen state, when changing chats
useLayoutEffectWithPrevDeps(([prevChatId]) => { useLayoutEffectWithPrevDeps(([prevChatId]) => {
if (prevChatId !== chatId ) { if (prevChatId !== chatId) {
setProfileState(ProfileState.Profile); setProfileState(ProfileState.Profile);
setManagementScreen(ManagementScreens.Initial); setManagementScreen(ManagementScreens.Initial);
} }

View File

@ -295,7 +295,7 @@ const ManageInvites: FC<OwnProps & StateProps> = ({
/> />
)} )}
</div> </div>
<p className='text-muted'>{isChannel ? lang('PrimaryLinkHelpChannel') : lang('PrimaryLinkHelp')}</p> <p className="text-muted">{isChannel ? lang('PrimaryLinkHelpChannel') : lang('PrimaryLinkHelp')}</p>
</div> </div>
{primaryInviteLink && ( {primaryInviteLink && (
<div className="section"> <div className="section">

View File

@ -148,7 +148,7 @@ const Statistics: FC<OwnProps & StateProps> = ({
))} ))}
</div> </div>
{Boolean(statistics.recentTopMessages?.length) && {Boolean(statistics.recentTopMessages?.length) && (
<div className="Statistics--messages"> <div className="Statistics--messages">
<h2 className="Statistics--messages-title">{lang('ChannelStats.Recent.Header')}</h2> <h2 className="Statistics--messages-title">{lang('ChannelStats.Recent.Header')}</h2>
@ -156,7 +156,7 @@ const Statistics: FC<OwnProps & StateProps> = ({
<StatisticsRecentMessage message={message as ApiMessage & StatisticsRecentMessageType} /> <StatisticsRecentMessage message={message as ApiMessage & StatisticsRecentMessageType} />
))} ))}
</div> </div>
} )}
</div> </div>
); );
}; };

View File

@ -44,7 +44,7 @@ const StatisticsRecentMessage: FC<OwnProps> = ({ message }) => {
{formatDateTimeToString(message.date * 1000, lang.code)} {formatDateTimeToString(message.date * 1000, lang.code)}
</div> </div>
<div className="StatisticsRecentMessage--meta"> <div className="StatisticsRecentMessage--meta">
{Boolean(message.forwards) ? lang('ChannelStats.SharesCount', message.forwards) : 'No shares'} {message.forwards ? lang('ChannelStats.SharesCount', message.forwards) : 'No shares'}
</div> </div>
</div> </div>
</p> </p>

View File

@ -137,7 +137,6 @@ const Button: FC<OwnProps> = ({
dir={isRtl ? 'rtl' : undefined} dir={isRtl ? 'rtl' : undefined}
aria-label={ariaLabel} aria-label={ariaLabel}
aria-controls={ariaControls} aria-controls={ariaControls}
aria-haspopup={hasPopup}
> >
{children} {children}
{!disabled && ripple && ( {!disabled && ripple && (
@ -148,7 +147,6 @@ const Button: FC<OwnProps> = ({
} }
return ( return (
// eslint-disable-next-line react/button-has-type
<button <button
ref={elementRef as RefObject<HTMLButtonElement>} ref={elementRef as RefObject<HTMLButtonElement>}
id={id} id={id}

View File

@ -1,4 +1,6 @@
import React, { FC, memo, useCallback, useRef } from '../../lib/teact/teact'; import React, {
FC, memo, useCallback, useRef,
} from '../../lib/teact/teact';
import useLang from '../../hooks/useLang'; import useLang from '../../hooks/useLang';
import { TextPart } from '../common/helpers/renderMessageText'; import { TextPart } from '../common/helpers/renderMessageText';

View File

@ -1,4 +1,6 @@
import { FC, useRef, useLayoutEffect, VirtualElement } from '../../lib/teact/teact'; import {
FC, useRef, useLayoutEffect, VirtualElement,
} from '../../lib/teact/teact';
import TeactDOM from '../../lib/teact/teact-dom'; import TeactDOM from '../../lib/teact/teact-dom';
type OwnProps = { type OwnProps = {

View File

@ -4,7 +4,6 @@ import useShowTransition from '../../hooks/useShowTransition';
import usePrevious from '../../hooks/usePrevious'; import usePrevious from '../../hooks/usePrevious';
import buildClassName from '../../util/buildClassName'; import buildClassName from '../../util/buildClassName';
type OwnProps = { type OwnProps = {
isOpen: boolean; isOpen: boolean;
isCustom?: boolean; isCustom?: boolean;

View File

@ -12,7 +12,9 @@ type OwnProps = {
className?: string; className?: string;
}; };
const Skeleton: FC<OwnProps> = ({ variant = 'rectangular', animation = 'wave', width, height, className }) => { const Skeleton: FC<OwnProps> = ({
variant = 'rectangular', animation = 'wave', width, height, className,
}) => {
const classNames = buildClassName('Skeleton', variant, animation, className); const classNames = buildClassName('Skeleton', variant, animation, className);
const style = (width ? `width: ${width}px;` : '') + (height ? `height: ${height}px;` : ''); const style = (width ? `width: ${width}px;` : '') + (height ? `height: ${height}px;` : '');
return ( return (

View File

@ -260,7 +260,8 @@ const Transition: FC<TransitionProps> = ({
typeof render === 'function' typeof render === 'function'
? render(key === activeKey, key === prevActiveKey, activeKey) ? render(key === activeKey, key === prevActiveKey, activeKey)
: render : render
}</div> }
</div>
); );
}); });

View File

@ -39,7 +39,7 @@ export const useResize = (
} }
useEffect(() => { useEffect(() => {
if (!isActive) return; if (!isActive) return undefined;
const handleMouseMove = (event: MouseEvent) => { const handleMouseMove = (event: MouseEvent) => {
const newWidth = Math.ceil(initialElementWidth + event.clientX - initialMouseX); const newWidth = Math.ceil(initialElementWidth + event.clientX - initialMouseX);

View File

@ -1,29 +1,34 @@
{ {
"rules": { "root": true,
"indent": [ "parser": "@typescript-eslint/parser",
"error", "plugins": [
4, "@typescript-eslint"
{ ],
"SwitchCase": 1 "rules": {
} "indent": [
], "error",
"@typescript-eslint/indent": [ 4,
"error", {
4, "SwitchCase": 1
{ }
"SwitchCase": 1 ],
} "@typescript-eslint/indent": [
], "error",
"max-len": [ 4,
"error", {
120 "SwitchCase": 1
], }
"no-bitwise": "off", ],
"no-underscore-dangle": "off", "max-len": [
"no-continue": "off", "error",
"no-restricted-syntax": "off", 120
"class-methods-use-this": "off", ],
"max-classes-per-file": "off", "no-bitwise": "off",
"camelcase": "error" "no-underscore-dangle": "off",
} "no-continue": "off",
"no-restricted-syntax": "off",
"class-methods-use-this": "off",
"max-classes-per-file": "off",
"camelcase": "off"
}
} }

View File

@ -186,7 +186,6 @@ class TelegramClient {
} }
// set defaults vars // set defaults vars
this._sender.userDisconnected = false; this._sender.userDisconnected = false;
// eslint-disable-next-line camelcase
this._sender._user_connected = false; this._sender._user_connected = false;
this._sender.isReconnecting = false; this._sender.isReconnecting = false;
this._sender._disconnected = true; this._sender._disconnected = true;

View File

@ -31,11 +31,11 @@
@keyframes lovely-chart--animation-fadeIn { @keyframes lovely-chart--animation-fadeIn {
from { from {
opacity: 0; opacity: 0;
transform: scale3d(.5, .5, 1); transform: scale3d(0.5, 0.5, 1);
} }
40% { 40% {
transform: scale3d(.75, .75, 1); transform: scale3d(0.75, 0.75, 1);
} }
to { to {
@ -51,12 +51,12 @@
} }
60% { 60% {
transform: scale3d(.75, .75, 1); transform: scale3d(0.75, 0.75, 1);
} }
to { to {
opacity: 0; opacity: 0;
transform: scale3d(.5, .5, 1); transform: scale3d(0.5, 0.5, 1);
} }
} }

View File

@ -1,6 +1,5 @@
.lovely-chart--button { .lovely-chart--button {
border: 1px solid #E6ECF0; border: 1px solid #E6ECF0;
background-color: transparent;
padding: 7px 7px; padding: 7px 7px;
display: inline-block; display: inline-block;
border-radius: 18px; border-radius: 18px;
@ -11,7 +10,7 @@
position: relative; position: relative;
border-color: var(--text-color); border-color: var(--text-color);
background: transparent; background-color: transparent;
color: var(--text-color); color: var(--text-color);
&.lovely-chart--state-checked { &.lovely-chart--state-checked {

View File

@ -169,7 +169,8 @@ addReducer('openTipsChat', (global, actions, payload) => {
addReducer('loadAllChats', (global, actions, payload) => { addReducer('loadAllChats', (global, actions, payload) => {
const listType = payload.listType as 'active' | 'archived'; const listType = payload.listType as 'active' | 'archived';
let { shouldReplace, onReplace } = payload; const { onReplace } = payload;
let { shouldReplace } = payload;
let i = 0; let i = 0;
(async () => { (async () => {

View File

@ -34,7 +34,9 @@ addReducer('loadStatistics', (global, actions, payload) => {
}); });
addReducer('loadStatisticsAsyncGraph', (global, actions, payload) => { addReducer('loadStatisticsAsyncGraph', (global, actions, payload) => {
const { chatId, token, name, isPercentage } = payload; const {
chatId, token, name, isPercentage,
} = payload;
const chat = selectChat(global, chatId); const chat = selectChat(global, chatId);
if (!chat?.fullInfo) { if (!chat?.fullInfo) {
return; return;

View File

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

View File

@ -687,7 +687,7 @@ addReducer('copySelectedMessages', (global) => {
copyTextForMessages(global, chatId, messageIds); copyTextForMessages(global, chatId, messageIds);
}); });
addReducer('copyMessagesByIds', (global, actions, payload: { messageIds?: number[] } ) => { addReducer('copyMessagesByIds', (global, actions, payload: { messageIds?: number[] }) => {
const { messageIds } = payload; const { messageIds } = payload;
const chat = selectCurrentChat(global); const chat = selectCurrentChat(global);
if (!messageIds || messageIds.length === 0 || !chat) { if (!messageIds || messageIds.length === 0 || !chat) {
@ -697,7 +697,6 @@ addReducer('copyMessagesByIds', (global, actions, payload: { messageIds?: number
copyTextForMessages(global, chat.id, messageIds); copyTextForMessages(global, chat.id, messageIds);
}); });
function copyTextForMessages(global: GlobalState, chatId: string, messageIds: number[]) { function copyTextForMessages(global: GlobalState, chatId: string, messageIds: number[]) {
const { threadId } = selectCurrentMessageList(global) || {}; const { threadId } = selectCurrentMessageList(global) || {};
const lang = langProvider.getTranslation; const lang = langProvider.getTranslation;
@ -712,7 +711,7 @@ function copyTextForMessages(global: GlobalState, chatId: string, messageIds: nu
const result = messages.reduce((acc, message) => { const result = messages.reduce((acc, message) => {
const sender = selectSender(global, message); const sender = selectSender(global, message);
acc.push(`> ${sender ? getSenderTitle(lang, sender) : ''}:`); acc.push(`> ${sender ? getSenderTitle(lang, sender) : ''}:`);
acc.push(getMessageSummaryText(lang, message, false, 0, undefined, true) + '\n'); acc.push(`${getMessageSummaryText(lang, message, false, 0, undefined, true)}\n`);
return acc; return acc;
}, [] as string[]); }, [] as string[]);

View File

@ -134,7 +134,9 @@ export function buildStaticMapHash(
zoom: number, zoom: number,
scale: number, scale: number,
) { ) {
const { long, lat, accessHash, accuracyRadius } = geo; const {
long, lat, accessHash, accuracyRadius,
} = geo;
// eslint-disable-next-line max-len // eslint-disable-next-line max-len
return `staticMap:${accessHash}?lat=${lat}&long=${long}&w=${width}&h=${height}&zoom=${zoom}&scale=${scale}&accuracyRadius=${accuracyRadius}`; return `staticMap:${accessHash}?lat=${lat}&long=${long}&w=${width}&h=${height}&zoom=${zoom}&scale=${scale}&accuracyRadius=${accuracyRadius}`;

View File

@ -48,7 +48,6 @@ export function getMessageTextWithSpoilers(message: ApiMessage) {
const spoiler = generateBrailleSpoiler(length); const spoiler = generateBrailleSpoiler(length);
return `${accText.substr(0, offset)}${spoiler}${accText.substr(offset + length, accText.length)}`; return `${accText.substr(0, offset)}${spoiler}${accText.substr(offset + length, accText.length)}`;
}, text); }, text);
} }
@ -160,12 +159,12 @@ export function getMessageSummaryDescription(
} }
if (invoice) { if (invoice) {
summary = lang('PaymentInvoice') + ': ' + invoice.text; summary = `${lang('PaymentInvoice')}: ${invoice.text}`;
} }
if (text) { if (text) {
if (isExtended && summary) { if (isExtended && summary) {
summary += '\n' + truncatedText; summary += `\n${truncatedText}`;
} else { } else {
summary = truncatedText; summary = truncatedText;
} }

View File

@ -8,10 +8,10 @@ export function checkIfReactionAdded(oldReactions?: ApiReactions, newReactions?:
if (!oldReactions || !oldReactions.recentReactions) return true; if (!oldReactions || !oldReactions.recentReactions) return true;
if (!newReactions || !newReactions.recentReactions) return false; if (!newReactions || !newReactions.recentReactions) return false;
// Skip reactions from yourself // Skip reactions from yourself
if (newReactions.recentReactions.every(reaction => reaction.userId === currentUserId)) return false; if (newReactions.recentReactions.every((reaction) => reaction.userId === currentUserId)) return false;
const oldReactionsMap = oldReactions.results.reduce<Record<string, number>>((acc, reaction) => { const oldReactionsMap = oldReactions.results.reduce<Record<string, number>>((acc, reaction) => {
acc[reaction.reaction] = reaction.count; acc[reaction.reaction] = reaction.count;
return acc; return acc;
}, {}); }, {});
return newReactions.results.some(r => !oldReactionsMap[r.reaction] || oldReactionsMap[r.reaction] < r.count); return newReactions.results.some((r) => !oldReactionsMap[r.reaction] || oldReactionsMap[r.reaction] < r.count);
} }

View File

@ -219,7 +219,7 @@ export function leaveChat(global: GlobalState, leftChatId: string): GlobalState
export function addChatMembers(global: GlobalState, chat: ApiChat, membersToAdd: ApiChatMember[]): GlobalState { export function addChatMembers(global: GlobalState, chat: ApiChat, membersToAdd: ApiChatMember[]): GlobalState {
const currentMembers = chat.fullInfo?.members; const currentMembers = chat.fullInfo?.members;
const newMemberIds = new Set(membersToAdd.map(m => m.userId)); const newMemberIds = new Set(membersToAdd.map((m) => m.userId));
const updatedMembers = [ const updatedMembers = [
...currentMembers?.filter((m) => !newMemberIds.has(m.userId)) || [], ...currentMembers?.filter((m) => !newMemberIds.has(m.userId)) || [],
...membersToAdd, ...membersToAdd,

View File

@ -1,6 +1,6 @@
import { DEBUG } from '../config'; import { DEBUG } from '../config';
export const CLIPBOARD_ITEM_SUPPORTED = navigator.clipboard && window.ClipboardItem; export const CLIPBOARD_ITEM_SUPPORTED = window.navigator.clipboard && window.ClipboardItem;
const textCopyEl = document.createElement('textarea'); const textCopyEl = document.createElement('textarea');
textCopyEl.setAttribute('readonly', ''); textCopyEl.setAttribute('readonly', '');
@ -51,7 +51,7 @@ async function copyBlobToClipboard(pngBlob: Blob | null) {
} }
try { try {
await navigator.clipboard.write?.([ await window.navigator.clipboard.write?.([
new ClipboardItem({ new ClipboardItem({
[pngBlob.type]: pngBlob, [pngBlob.type]: pngBlob,
}), }),

View File

@ -55,7 +55,7 @@ export const IS_TABLET_COLUMN_LAYOUT = !IS_SINGLE_COLUMN_LAYOUT && (
window.innerWidth <= MIN_SCREEN_WIDTH_FOR_STATIC_LEFT_COLUMN window.innerWidth <= MIN_SCREEN_WIDTH_FOR_STATIC_LEFT_COLUMN
); );
export const IS_VOICE_RECORDING_SUPPORTED = Boolean( export const IS_VOICE_RECORDING_SUPPORTED = Boolean(
navigator.mediaDevices && 'getUserMedia' in navigator.mediaDevices && ( window.navigator.mediaDevices && 'getUserMedia' in window.navigator.mediaDevices && (
window.AudioContext || (window as any).webkitAudioContext window.AudioContext || (window as any).webkitAudioContext
), ),
); );

View File

@ -203,9 +203,12 @@ function updateFolderManager(global: GlobalState) {
updateFolders(global, isAllFolderChanged, isArchivedFolderChanged, areFoldersChanged); updateFolders(global, isAllFolderChanged, isArchivedFolderChanged, areFoldersChanged);
affectedFolderIds = affectedFolderIds.concat(updateChats( affectedFolderIds = affectedFolderIds.concat(updateChats(
global, areFoldersChanged || isAllFolderChanged || isArchivedFolderChanged, global,
areNotifySettingsChanged, areNotifyExceptionsChanged, areFoldersChanged || isAllFolderChanged || isArchivedFolderChanged,
prevAllFolderListIds, prevArchivedFolderListIds, areNotifySettingsChanged,
areNotifyExceptionsChanged,
prevAllFolderListIds,
prevArchivedFolderListIds,
)); ));
updateResults(unique(affectedFolderIds)); updateResults(unique(affectedFolderIds));
@ -315,7 +318,7 @@ function buildFolderSummaryFromMainList(
return { return {
id: folderId, id: folderId,
listIds: new Set(listIds), listIds: new Set(listIds),
orderedPinnedIds: orderedPinnedIds, orderedPinnedIds,
pinnedChatIds: new Set(orderedPinnedIds), pinnedChatIds: new Set(orderedPinnedIds),
}; };
} }

View File

@ -4,7 +4,7 @@ export default function getMessageIdsForSelectedText() {
const selection = window.getSelection(); const selection = window.getSelection();
let selectedFragments = selection && selection.rangeCount ? selection.getRangeAt(0).cloneContents() : undefined; let selectedFragments = selection && selection.rangeCount ? selection.getRangeAt(0).cloneContents() : undefined;
if (!selectedFragments || selectedFragments.childElementCount === 0) { if (!selectedFragments || selectedFragments.childElementCount === 0) {
return; return undefined;
} }
const messageIds = Array.from(selectedFragments.children) const messageIds = Array.from(selectedFragments.children)
@ -19,7 +19,6 @@ export default function getMessageIdsForSelectedText() {
return result; return result;
}, [] as number[]); }, [] as number[]);
// Cleanup a document fragment because it is playing media content in the background // Cleanup a document fragment because it is playing media content in the background
while (selectedFragments.firstChild) { while (selectedFragments.firstChild) {
selectedFragments.removeChild(selectedFragments.firstChild); selectedFragments.removeChild(selectedFragments.firstChild);

View File

@ -5,23 +5,23 @@ const PROVIDER = 'http://maps.google.com/maps';
const VENUE_COLORS = new Map(Object.entries({ const VENUE_COLORS = new Map(Object.entries({
'building/medical': '#43b3f4', 'building/medical': '#43b3f4',
'building/gym': '#43b3f4', 'building/gym': '#43b3f4',
'arts_entertainment': '#e56dd6', 'education/cafeteria': '#f7943f',
'travel/bedandbreakfast': '#9987ff', 'travel/bedandbreakfast': '#9987ff',
'travel/hotel': '#9987ff', 'travel/hotel': '#9987ff',
'travel/hostel': '#9987ff', 'travel/hostel': '#9987ff',
'travel/resort': '#9987ff', 'travel/resort': '#9987ff',
'building': '#6e81b2',
'education': '#a57348',
'event': '#959595',
'food': '#f7943f',
'education/cafeteria': '#f7943f',
'nightlife': '#e56dd6',
'travel/hotel_bar': '#e56dd6', 'travel/hotel_bar': '#e56dd6',
'parks_outdoors': '#6cc039', arts_entertainment: '#e56dd6',
'shops': '#ffb300', building: '#6e81b2',
'travel': '#1c9fff', education: '#a57348',
'work': '#ad7854', event: '#959595',
'home': '#00aeef', food: '#f7943f',
home: '#00aeef',
nightlife: '#e56dd6',
parks_outdoors: '#6cc039',
shops: '#ffb300',
travel: '#1c9fff',
work: '#ad7854',
})); }));
const RANDOM_COLORS = [ const RANDOM_COLORS = [
@ -34,7 +34,7 @@ export function prepareMapUrl(lat: number, long: number, zoom: number) {
export function getMetersPerPixel(lat: number, zoom: number) { export function getMetersPerPixel(lat: number, zoom: number) {
// https://groups.google.com/g/google-maps-js-api-v3/c/hDRO4oHVSeM/m/osOYQYXg2oUJ // https://groups.google.com/g/google-maps-js-api-v3/c/hDRO4oHVSeM/m/osOYQYXg2oUJ
return 156543.03392 * Math.cos(lat * Math.PI / 180) / Math.pow(2, zoom); return (156543.03392 * Math.cos(lat * (Math.PI / 180))) / 2 ** zoom;
} }
export function getVenueIconUrl(type?: string) { export function getVenueIconUrl(type?: string) {
@ -52,6 +52,7 @@ export function getVenueColor(type?: string) {
} }
function stringToNumber(str: string) { function stringToNumber(str: string) {
return str.split('').reduce((prevHash, currVal) => return str.split('').reduce((prevHash, currVal) => (
(((prevHash << 5) - prevHash) + currVal.charCodeAt(0)) | 0, 0); // eslint-disable-next-line no-bitwise
(((prevHash << 5) - prevHash) + currVal.charCodeAt(0)) | 0), 0);
} }

View File

@ -1,5 +1,7 @@
import { callApi } from '../api/gramjs'; import { callApi } from '../api/gramjs';
import { ApiChat, ApiMediaFormat, ApiMessage, ApiUser, ApiUserReaction } from '../api/types'; import {
ApiChat, ApiMediaFormat, ApiMessage, ApiUser, ApiUserReaction,
} from '../api/types';
import { renderActionMessageText } from '../components/common/helpers/renderActionMessageText'; import { renderActionMessageText } from '../components/common/helpers/renderActionMessageText';
import { DEBUG, IS_TEST } from '../config'; import { DEBUG, IS_TEST } from '../config';
import { getDispatch, getGlobal, setGlobal } from '../lib/teact/teactn'; import { getDispatch, getGlobal, setGlobal } from '../lib/teact/teactn';
@ -264,9 +266,11 @@ function checkIfShouldNotify(chat: ApiChat) {
function getNotificationContent(chat: ApiChat, message: ApiMessage, reaction?: ApiUserReaction) { function getNotificationContent(chat: ApiChat, message: ApiMessage, reaction?: ApiUserReaction) {
const global = getGlobal(); const global = getGlobal();
const {
replyToMessageId,
} = message;
let { let {
senderId, senderId,
replyToMessageId,
} = message; } = message;
if (reaction) senderId = reaction.userId; if (reaction) senderId = reaction.userId;

View File

@ -1,10 +1,10 @@
let RE_NOT_LETTER: RegExp; let RE_NOT_LETTER: RegExp;
try { try {
RE_NOT_LETTER = new RegExp('[^\\p{L}\\p{M}]+', 'ui'); RE_NOT_LETTER = /[^\p{L}\p{M}]+/ui;
} catch (e) { } catch (e) {
// Support for older versions of firefox // Support for older versions of firefox
RE_NOT_LETTER = new RegExp('[^\\wа-яё]+', 'i'); RE_NOT_LETTER = /[^\wа-яё]+/i;
} }
export default function searchWords(haystack: string, needle: string | string[]) { export default function searchWords(haystack: string, needle: string | string[]) {

View File

@ -52,7 +52,7 @@ if (IS_SERVICE_WORKER_SUPPORTED) {
// eslint-disable-next-line no-console // eslint-disable-next-line no-console
console.log('[SW] Hard reload detected, re-enabling Service Worker'); console.log('[SW] Hard reload detected, re-enabling Service Worker');
} }
await Promise.all(registrations.map(r => r.unregister())); await Promise.all(registrations.map((r) => r.unregister()));
} }
} }