diff --git a/eslint.config.mjs b/eslint.config.mjs index 1b85c3fd6..fe7ce0af2 100644 --- a/eslint.config.mjs +++ b/eslint.config.mjs @@ -16,6 +16,7 @@ import tseslint from 'typescript-eslint'; export default tseslint.config( eslint.configs.recommended, tseslint.configs.recommendedTypeChecked, + tseslint.configs.stylistic, reactPlugin.configs.flat.recommended, reactXPlugin.configs['recommended-type-checked'], jsxA11yPlugin.flatConfigs.recommended, @@ -51,6 +52,20 @@ export default tseslint.config( rules: { 'no-null/no-null': 'error', 'no-console': 'error', + 'no-template-curly-in-string': 'error', + 'object-shorthand': 'error', + curly: ['error', 'multi-line'], + 'no-implicit-coercion': [ + 'error', + { + boolean: true, + disallowTemplateShorthand: true, + }, + ], + 'no-prototype-builtins': 'off', + 'no-undef': 'off', + 'no-unused-vars': 'off', + '@stylistic/multiline-ternary': 'off', '@stylistic/max-len': ['error', { code: 120, ignoreComments: true, @@ -60,10 +75,6 @@ export default tseslint.config( SwitchCase: 1, flatTernaryExpressions: false, }], - '@stylistic/multiline-ternary': 'off', - 'no-prototype-builtins': 'off', - 'no-undef': 'off', - 'no-unused-vars': 'off', 'simple-import-sort/imports': [ 'error', { @@ -115,7 +126,6 @@ export default tseslint.config( ], }, ], - // TypeScript type imports preference '@typescript-eslint/consistent-type-imports': [ 'error', { @@ -130,6 +140,11 @@ export default tseslint.config( '@typescript-eslint/no-unsafe-return': 'off', '@typescript-eslint/no-floating-promises': 'off', '@typescript-eslint/no-explicit-any': 'off', + '@typescript-eslint/no-inferrable-types': 'off', + '@typescript-eslint/consistent-type-definitions': 'off', + '@typescript-eslint/no-empty-function': 'off', + '@typescript-eslint/prefer-for-of': 'off', + '@typescript-eslint/array-type': 'off', '@typescript-eslint/no-misused-promises': [ 'error', { @@ -169,6 +184,15 @@ export default tseslint.config( 'react/no-unknown-property': 'off', 'react/display-name': 'off', 'react/jsx-key': 'off', + 'react/jsx-curly-spacing': [ + 'error', + { + when: 'never', + attributes: true, + children: true, + allowMultiline: true, + }, + ], 'react-x/no-use-context': 'off', 'react-x/no-context-provider': 'off', 'react-x/no-array-index-key': 'off', diff --git a/playwright.config.ts b/playwright.config.ts index 8126c3673..81b66a9f9 100644 --- a/playwright.config.ts +++ b/playwright.config.ts @@ -3,7 +3,7 @@ import { devices, type PlaywrightTestConfig } from '@playwright/test'; const config: PlaywrightTestConfig = { testDir: 'tests/playwright', timeout: process.env.CI ? 60 * 5 * 1000 : 30 * 1000, - forbidOnly: !!process.env.CI, + forbidOnly: Boolean(process.env.CI), retries: process.env.CI ? 2 : 0, webServer: { command: 'npm run build:mocked && serve -l 1235 dist', diff --git a/src/api/gramjs/methods/chats.ts b/src/api/gramjs/methods/chats.ts index b9c0ccd6c..ab1c75363 100644 --- a/src/api/gramjs/methods/chats.ts +++ b/src/api/gramjs/methods/chats.ts @@ -86,7 +86,7 @@ import { handleGramJsUpdate, invokeRequest, uploadFile } from './client'; type FullChatData = { fullInfo: ApiChatFullInfo; chats: ApiChat[]; - userStatusesById: { [userId: string]: ApiUserStatus }; + userStatusesById: Record; groupCall?: Partial; membersCount?: number; isForumAsMessages?: true; diff --git a/src/api/gramjs/methods/media.ts b/src/api/gramjs/methods/media.ts index 23d7987c4..9ef257f61 100644 --- a/src/api/gramjs/methods/media.ts +++ b/src/api/gramjs/methods/media.ts @@ -18,7 +18,7 @@ import * as cacheApi from '../../../util/cacheApi'; import { getEntityTypeById } from '../gramjsBuilders'; import localDb from '../localDb'; -const MEDIA_ENTITY_TYPES: Set = new Set([ +const MEDIA_ENTITY_TYPES = new Set([ 'sticker', 'wallpaper', 'photo', 'webDocument', 'document', ]); diff --git a/src/api/gramjs/methods/messages.ts b/src/api/gramjs/methods/messages.ts index c536a9267..5be1ea104 100644 --- a/src/api/gramjs/methods/messages.ts +++ b/src/api/gramjs/methods/messages.ts @@ -2114,7 +2114,7 @@ export async function fetchOutboxReadDate({ chat, messageId }: { chat: ApiChat; const peer = buildInputPeer(id, accessHash); const result = await invokeRequest(new GramJs.messages.GetOutboxReadDate({ - peer: peer, + peer, msgId: messageId, }), { shouldThrow: true }); diff --git a/src/api/gramjs/worker/connector.ts b/src/api/gramjs/worker/connector.ts index 3b98ee61a..a83e14f5a 100644 --- a/src/api/gramjs/worker/connector.ts +++ b/src/api/gramjs/worker/connector.ts @@ -184,7 +184,7 @@ export function callApiLocal( | (Api.VirtualClass | undefined)[]; type ForbiddenResponses = ForbiddenTypes - | (AnyLiteral & { [k: string]: ForbiddenTypes }); + | (AnyLiteral & Record); // Unwrap all chained promises const response = await promise; @@ -231,7 +231,7 @@ export function callApi(fnName: T, ...args: MethodArgs< | (Api.VirtualClass | undefined)[]; type ForbiddenResponses = ForbiddenTypes - | (AnyLiteral & { [k: string]: ForbiddenTypes }); + | (AnyLiteral & Record); // Unwrap all chained promises const response = await promise; @@ -346,7 +346,7 @@ function makeRequestToMaster(message: { const requestState = { messageId } as RequestState; // Re-wrap type because of `postMessage` - const promise: Promise> = new Promise((resolve, reject) => { + const promise = new Promise>((resolve, reject) => { Object.assign(requestState, { resolve, reject }); }); @@ -385,7 +385,7 @@ function makeRequest(message: OriginPayload) { const requestState = { messageId } as RequestState; // Re-wrap type because of `postMessage` - const promise: Promise> = new Promise((resolve, reject) => { + const promise = new Promise>((resolve, reject) => { Object.assign(requestState, { resolve, reject }); }); diff --git a/src/api/types/messages.ts b/src/api/types/messages.ts index d7dbb51e1..d6827c9a3 100644 --- a/src/api/types/messages.ts +++ b/src/api/types/messages.ts @@ -855,9 +855,7 @@ export type ApiReplyKeyboard = { keyboardPlaceholder?: string; isKeyboardSingleUse?: boolean; isKeyboardSelective?: boolean; -} & { - [K in 'inlineButtons' | 'keyboardButtons']?: ApiKeyboardButtons; -}; +} & Partial>; export type ApiTranscription = { text: string; diff --git a/src/api/types/misc.ts b/src/api/types/misc.ts index 6372385cf..08d98c0d5 100644 --- a/src/api/types/misc.ts +++ b/src/api/types/misc.ts @@ -266,13 +266,11 @@ export interface ApiConfig { export type ApiPeerColorSet = string[]; export interface ApiPeerColors { - general: { - [key: number]: { - isHidden?: true; - colors?: ApiPeerColorSet; - darkColors?: ApiPeerColorSet; - }; - }; + general: Record; generalHash?: number; } diff --git a/src/components/common/AnimatedIconWithPreview.tsx b/src/components/common/AnimatedIconWithPreview.tsx index d5eca4aad..32ab30cec 100644 --- a/src/components/common/AnimatedIconWithPreview.tsx +++ b/src/components/common/AnimatedIconWithPreview.tsx @@ -65,7 +65,6 @@ function AnimatedIconWithPreview(props: OwnProps) { onLoad={handlePreviewLoad} /> )} - { } ); diff --git a/src/components/common/Audio.tsx b/src/components/common/Audio.tsx index c9abc2a84..d45b835de 100644 --- a/src/components/common/Audio.tsx +++ b/src/components/common/Audio.tsx @@ -364,7 +364,7 @@ const Audio: FC = ({ {withSeekline && (
- {playProgress < 1 && `${formatMediaDuration(duration * playProgress, duration)}`} + {playProgress < 1 && formatMediaDuration(duration * playProgress, duration)} {renderSeekline(playProgress, bufferedRanges, seekerRef)}
diff --git a/src/components/common/CalendarModal.tsx b/src/components/common/CalendarModal.tsx index 8e312d7b4..2d58ee001 100644 --- a/src/components/common/CalendarModal.tsx +++ b/src/components/common/CalendarModal.tsx @@ -312,7 +312,7 @@ const CalendarModal: FC = ({ currentYear, currentMonth, gridDate, minDate, maxDate, ) ? 'disabled' - : `${gridDate ? 'clickable' : ''}`, + : gridDate ? 'clickable' : '', selectedDay === formatDay(currentYear, currentMonth, gridDate) && 'selected', )} > diff --git a/src/components/common/DeleteMessageModal.tsx b/src/components/common/DeleteMessageModal.tsx index 21984a8fc..cc1cd8eb9 100644 --- a/src/components/common/DeleteMessageModal.tsx +++ b/src/components/common/DeleteMessageModal.tsx @@ -136,7 +136,7 @@ const DeleteMessageModal: FC = ({ const buildNestedOptionListWithAvatars = useLastCallback(() => { return peerList.map((member) => { return { - value: `${member.id}`, + value: member.id, label: getPeerTitle(lang, member) || '', leftElement: , }; diff --git a/src/components/common/StickerButton.tsx b/src/components/common/StickerButton.tsx index cfb7f9c52..38f5bceec 100644 --- a/src/components/common/StickerButton.tsx +++ b/src/components/common/StickerButton.tsx @@ -291,7 +291,7 @@ const StickerButton = - {withSparkles && } + {withSparkles && } {isIntesectingForShowing && ( = new Set(); + const organizedEntityIndexes = new Set(); const organizedEntities: IOrganizedEntity[] = []; entities.forEach((entity, index) => { diff --git a/src/components/common/hooks/useAnimatedEmoji.ts b/src/components/common/hooks/useAnimatedEmoji.ts index 39e15297d..cc8f03c95 100644 --- a/src/components/common/hooks/useAnimatedEmoji.ts +++ b/src/components/common/hooks/useAnimatedEmoji.ts @@ -83,7 +83,7 @@ export default function useAnimatedEmoji( const { x, y } = container.getBoundingClientRect(); interactWithAnimatedEmoji({ - emoji: emoji, + emoji, x, y, startSize: size, diff --git a/src/components/common/pickers/PeerPicker.tsx b/src/components/common/pickers/PeerPicker.tsx index d65c09857..2d584a58d 100644 --- a/src/components/common/pickers/PeerPicker.tsx +++ b/src/components/common/pickers/PeerPicker.tsx @@ -245,12 +245,14 @@ const PeerPicker = ({ const peerOrCategory = peer || category; if (!peerOrCategory) { - if (DEBUG) return ( -
- No peer or category with ID - {id} -
- ); + if (DEBUG) { + return ( +
+ No peer or category with ID + {id} +
+ ); + } return undefined; } diff --git a/src/components/common/profile/ChatExtra.tsx b/src/components/common/profile/ChatExtra.tsx index 79c717b77..68f54da6d 100644 --- a/src/components/common/profile/ChatExtra.tsx +++ b/src/components/common/profile/ChatExtra.tsx @@ -395,7 +395,7 @@ const ChatExtra: FC = ({ {birthday && ( )} - { hasMainMiniApp && ( + {hasMainMiniApp && ( = new Map(); +const revealByContainerId = new Map(); const buildClassName = createClassNameBuilder('Spoiler'); diff --git a/src/components/left/search/LeftSearch.scss b/src/components/left/search/LeftSearch.scss index 82331b81b..75ce8f790 100644 --- a/src/components/left/search/LeftSearch.scss +++ b/src/components/left/search/LeftSearch.scss @@ -190,9 +190,9 @@ } .search-sponsored-badge { + cursor: pointer; display: flex; align-self: flex-start; - cursor: pointer; &:hover { filter: brightness(1.1); } diff --git a/src/components/main/Main.tsx b/src/components/main/Main.tsx index ef134d5c1..beab24714 100644 --- a/src/components/main/Main.tsx +++ b/src/components/main/Main.tsx @@ -550,7 +550,7 @@ const Main = ({ }); // Online status and browser tab indicators - useBackgroundMode(handleBlur, handleFocus, !!IS_ELECTRON); + useBackgroundMode(handleBlur, handleFocus, Boolean(IS_ELECTRON)); useBeforeUnload(handleBlur); usePreventPinchZoomGesture(isMediaViewerOpen || isStoryViewerOpen); diff --git a/src/components/main/premium/GiveawayModal.tsx b/src/components/main/premium/GiveawayModal.tsx index dd22b4474..57f3e8970 100644 --- a/src/components/main/premium/GiveawayModal.tsx +++ b/src/components/main/premium/GiveawayModal.tsx @@ -103,7 +103,7 @@ const DEFAULT_CUSTOM_EXPIRE_DATE = 86400 * 3 * 1000; // 3 days const MAX_ADDITIONAL_CHANNELS = 9; const DEFAULT_BOOST_COUNT = 5; -const GIVEAWAY_IMG_LIST: { [key: number]: string } = { +const GIVEAWAY_IMG_LIST: Partial> = { 3: GiftGreenRound, 6: GiftBlueRound, 12: GiftRedRound, @@ -721,7 +721,11 @@ const GiveawayModal: FC = ({ {dataStarsPrepaidGiveaway ? ( ) : ( - + )}
diff --git a/src/components/main/premium/GiveawayTypeOption.tsx b/src/components/main/premium/GiveawayTypeOption.tsx index 70f73e945..1d75d15f2 100644 --- a/src/components/main/premium/GiveawayTypeOption.tsx +++ b/src/components/main/premium/GiveawayTypeOption.tsx @@ -37,7 +37,7 @@ const GiveawayTypeOption: FC = ({ let displayText: string | undefined = lang(text); if (isLink && selectedMemberIds?.length) { - displayText = selectedMemberIds.length > 2 ? `${selectedMemberIds.length}` : userNames; + displayText = selectedMemberIds.length > 2 ? selectedMemberIds.length.toString() : userNames; } const handleChange = useLastCallback((e: ChangeEvent) => { @@ -70,7 +70,7 @@ const GiveawayTypeOption: FC = ({

- {lang(`${name}`)} + {lang(name)}

{isLink ? (
diff --git a/src/components/middle/composer/StickerPicker.tsx b/src/components/middle/composer/StickerPicker.tsx index 62913cd05..ea94177fb 100644 --- a/src/components/middle/composer/StickerPicker.tsx +++ b/src/components/middle/composer/StickerPicker.tsx @@ -352,14 +352,14 @@ const StickerPicker: FC = ({ return (
- { !isForEffects && ( + {!isForEffects && (
{allSets.map(renderCover)}
- ) } + )}
= new Set([ +const SINGLE_LINE_ACTIONS = new Set([ 'pinMessage', 'chatEditPhoto', 'chatDeletePhoto', 'unsupported', ]); -const HIDDEN_TEXT_ACTIONS: Set = new Set(['giftCode', 'prizeStars', 'suggestProfilePhoto']); +const HIDDEN_TEXT_ACTIONS = new Set(['giftCode', 'prizeStars', 'suggestProfilePhoto']); const ActionMessage = ({ message, diff --git a/src/components/middle/message/CommentButton.tsx b/src/components/middle/message/CommentButton.tsx index 313f7c04e..882d08254 100644 --- a/src/components/middle/message/CommentButton.tsx +++ b/src/components/middle/message/CommentButton.tsx @@ -137,7 +137,7 @@ const CommentButton: FC = ({ )} color={isCustomShape ? 'white' : 'blue'} /> - ) } + )} )} - { } + {}
); diff --git a/src/components/modals/gift/GiftModal.tsx b/src/components/modals/gift/GiftModal.tsx index cfe9b9c5d..0bf4f4295 100644 --- a/src/components/modals/gift/GiftModal.tsx +++ b/src/components/modals/gift/GiftModal.tsx @@ -246,7 +246,7 @@ const GiftModal: FC = ({ return !isLimited && !isSoldOut; } if (areUnlimitedStarGiftsDisallowed && areLimitedStarGiftsDisallowed) { - return Boolean(isLimited && !!upgradeStars); + return Boolean(isLimited && Boolean(upgradeStars)); } return true; diff --git a/src/components/modals/paidReaction/PaidReactionModal.tsx b/src/components/modals/paidReaction/PaidReactionModal.tsx index 81d1e4fcf..fd3d75d74 100644 --- a/src/components/modals/paidReaction/PaidReactionModal.tsx +++ b/src/components/modals/paidReaction/PaidReactionModal.tsx @@ -342,7 +342,7 @@ const PaidReactionModal = ({ })}
)} - {topReactors && () } + {topReactors && ()} !media.duration); - const areAllVideos = extendedMedia.every((media) => !!media.duration); + const areAllVideos = extendedMedia.every((media) => Boolean(media.duration)); const mediaText = areAllPhotos ? oldLang('Stars.Transfer.Photos', extendedMedia.length) : areAllVideos ? oldLang('Stars.Transfer.Videos', extendedMedia.length) diff --git a/src/components/modals/webApp/MinimizedWebAppModal.tsx b/src/components/modals/webApp/MinimizedWebAppModal.tsx index f1a782463..5025d3c34 100644 --- a/src/components/modals/webApp/MinimizedWebAppModal.tsx +++ b/src/components/modals/webApp/MinimizedWebAppModal.tsx @@ -68,14 +68,14 @@ const MinimizedWebAppModal = ({ function renderTitle() { const activeTabName = peers.length > 0 && peers[0]?.firstName; const title = openedTabsCount && activeTabName && openedTabsCount > 1 - ? `${lang('MiniAppsMoreTabs', + ? lang('MiniAppsMoreTabs', { botName: activeTabName, count: openedTabsCount - 1, }, { pluralValue: openedTabsCount - 1, - })}` + }) : activeTabName; return ( diff --git a/src/components/modals/webApp/WebAppModal.tsx b/src/components/modals/webApp/WebAppModal.tsx index 66295cf4e..fe9d17682 100644 --- a/src/components/modals/webApp/WebAppModal.tsx +++ b/src/components/modals/webApp/WebAppModal.tsx @@ -713,7 +713,7 @@ const WebAppModal: FC = ({ modalHeight={currentHeight} /> ))} - { isMoreAppsTabActive && ()} + {isMoreAppsTabActive && ()} ); }; diff --git a/src/components/modals/webApp/WebAppModalTabContent.tsx b/src/components/modals/webApp/WebAppModalTabContent.tsx index 7cf1c2e05..fee2f35ca 100644 --- a/src/components/modals/webApp/WebAppModalTabContent.tsx +++ b/src/components/modals/webApp/WebAppModalTabContent.tsx @@ -1109,7 +1109,7 @@ const WebAppModalTabContent: FC = ({ {mainButton?.isProgressVisible && }
- ) } + )} {popupParameters && ( = ({ />
) : undefined} - { needName || needEmail || needPhone ? ( + {needName || needEmail || needPhone ? (
{oldLang('PaymentShippingReceiver')}
- ) : undefined } - { needName && ( + ) : undefined} + {needName && ( = ({ tabIndex={0} error={formErrors.fullName && lang.withRegular(formErrors.fullName)} /> - ) } - { needEmail && ( + )} + {needEmail && ( = ({ tabIndex={0} error={formErrors.email && lang.withRegular(formErrors.email)} /> - ) } - { needPhone && ( + )} + {needPhone && ( = ({ error={formErrors.phone && lang.withRegular(formErrors.phone)} ref={phoneRef} /> - ) } + )} = ({ ))} {!isCurrentUserPremium && ( <> - { } + {}