[dev] More stylistic eslint rules (#5961)

This commit is contained in:
zubiden 2025-06-04 20:41:03 +02:00 committed by Alexander Zinchuk
parent ee7f2e1c8c
commit bd096e2a01
56 changed files with 120 additions and 138 deletions

View File

@ -16,6 +16,7 @@ import tseslint from 'typescript-eslint';
export default tseslint.config( export default tseslint.config(
eslint.configs.recommended, eslint.configs.recommended,
tseslint.configs.recommendedTypeChecked, tseslint.configs.recommendedTypeChecked,
tseslint.configs.stylistic,
reactPlugin.configs.flat.recommended, reactPlugin.configs.flat.recommended,
reactXPlugin.configs['recommended-type-checked'], reactXPlugin.configs['recommended-type-checked'],
jsxA11yPlugin.flatConfigs.recommended, jsxA11yPlugin.flatConfigs.recommended,
@ -51,6 +52,20 @@ export default tseslint.config(
rules: { rules: {
'no-null/no-null': 'error', 'no-null/no-null': 'error',
'no-console': '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', { '@stylistic/max-len': ['error', {
code: 120, code: 120,
ignoreComments: true, ignoreComments: true,
@ -60,10 +75,6 @@ export default tseslint.config(
SwitchCase: 1, SwitchCase: 1,
flatTernaryExpressions: false, flatTernaryExpressions: false,
}], }],
'@stylistic/multiline-ternary': 'off',
'no-prototype-builtins': 'off',
'no-undef': 'off',
'no-unused-vars': 'off',
'simple-import-sort/imports': [ 'simple-import-sort/imports': [
'error', 'error',
{ {
@ -115,7 +126,6 @@ export default tseslint.config(
], ],
}, },
], ],
// TypeScript type imports preference
'@typescript-eslint/consistent-type-imports': [ '@typescript-eslint/consistent-type-imports': [
'error', 'error',
{ {
@ -130,6 +140,11 @@ export default tseslint.config(
'@typescript-eslint/no-unsafe-return': 'off', '@typescript-eslint/no-unsafe-return': 'off',
'@typescript-eslint/no-floating-promises': 'off', '@typescript-eslint/no-floating-promises': 'off',
'@typescript-eslint/no-explicit-any': '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': [ '@typescript-eslint/no-misused-promises': [
'error', 'error',
{ {
@ -169,6 +184,15 @@ export default tseslint.config(
'react/no-unknown-property': 'off', 'react/no-unknown-property': 'off',
'react/display-name': 'off', 'react/display-name': 'off',
'react/jsx-key': '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-use-context': 'off',
'react-x/no-context-provider': 'off', 'react-x/no-context-provider': 'off',
'react-x/no-array-index-key': 'off', 'react-x/no-array-index-key': 'off',

View File

@ -3,7 +3,7 @@ import { devices, type PlaywrightTestConfig } from '@playwright/test';
const config: PlaywrightTestConfig = { const config: PlaywrightTestConfig = {
testDir: 'tests/playwright', testDir: 'tests/playwright',
timeout: process.env.CI ? 60 * 5 * 1000 : 30 * 1000, timeout: process.env.CI ? 60 * 5 * 1000 : 30 * 1000,
forbidOnly: !!process.env.CI, forbidOnly: Boolean(process.env.CI),
retries: process.env.CI ? 2 : 0, retries: process.env.CI ? 2 : 0,
webServer: { webServer: {
command: 'npm run build:mocked && serve -l 1235 dist', command: 'npm run build:mocked && serve -l 1235 dist',

View File

@ -86,7 +86,7 @@ import { handleGramJsUpdate, invokeRequest, uploadFile } from './client';
type FullChatData = { type FullChatData = {
fullInfo: ApiChatFullInfo; fullInfo: ApiChatFullInfo;
chats: ApiChat[]; chats: ApiChat[];
userStatusesById: { [userId: string]: ApiUserStatus }; userStatusesById: Record<string, ApiUserStatus>;
groupCall?: Partial<ApiGroupCall>; groupCall?: Partial<ApiGroupCall>;
membersCount?: number; membersCount?: number;
isForumAsMessages?: true; isForumAsMessages?: true;

View File

@ -18,7 +18,7 @@ import * as cacheApi from '../../../util/cacheApi';
import { getEntityTypeById } from '../gramjsBuilders'; import { getEntityTypeById } from '../gramjsBuilders';
import localDb from '../localDb'; import localDb from '../localDb';
const MEDIA_ENTITY_TYPES: Set<EntityType> = new Set([ const MEDIA_ENTITY_TYPES = new Set<EntityType>([
'sticker', 'wallpaper', 'photo', 'webDocument', 'document', 'sticker', 'wallpaper', 'photo', 'webDocument', 'document',
]); ]);

View File

@ -2114,7 +2114,7 @@ export async function fetchOutboxReadDate({ chat, messageId }: { chat: ApiChat;
const peer = buildInputPeer(id, accessHash); const peer = buildInputPeer(id, accessHash);
const result = await invokeRequest(new GramJs.messages.GetOutboxReadDate({ const result = await invokeRequest(new GramJs.messages.GetOutboxReadDate({
peer: peer, peer,
msgId: messageId, msgId: messageId,
}), { shouldThrow: true }); }), { shouldThrow: true });

View File

@ -184,7 +184,7 @@ export function callApiLocal<T extends keyof Methods>(
| (Api.VirtualClass<any> | undefined)[]; | (Api.VirtualClass<any> | undefined)[];
type ForbiddenResponses = type ForbiddenResponses =
ForbiddenTypes ForbiddenTypes
| (AnyLiteral & { [k: string]: ForbiddenTypes }); | (AnyLiteral & Record<string, ForbiddenTypes>);
// Unwrap all chained promises // Unwrap all chained promises
const response = await promise; const response = await promise;
@ -231,7 +231,7 @@ export function callApi<T extends keyof Methods>(fnName: T, ...args: MethodArgs<
| (Api.VirtualClass<any> | undefined)[]; | (Api.VirtualClass<any> | undefined)[];
type ForbiddenResponses = type ForbiddenResponses =
ForbiddenTypes ForbiddenTypes
| (AnyLiteral & { [k: string]: ForbiddenTypes }); | (AnyLiteral & Record<string, ForbiddenTypes>);
// Unwrap all chained promises // Unwrap all chained promises
const response = await promise; const response = await promise;
@ -346,7 +346,7 @@ function makeRequestToMaster(message: {
const requestState = { messageId } as RequestState; const requestState = { messageId } as RequestState;
// Re-wrap type because of `postMessage` // Re-wrap type because of `postMessage`
const promise: Promise<MethodResponse<keyof Methods>> = new Promise((resolve, reject) => { const promise = new Promise<MethodResponse<keyof Methods>>((resolve, reject) => {
Object.assign(requestState, { resolve, reject }); Object.assign(requestState, { resolve, reject });
}); });
@ -385,7 +385,7 @@ function makeRequest(message: OriginPayload) {
const requestState = { messageId } as RequestState; const requestState = { messageId } as RequestState;
// Re-wrap type because of `postMessage` // Re-wrap type because of `postMessage`
const promise: Promise<MethodResponse<keyof Methods>> = new Promise((resolve, reject) => { const promise = new Promise<MethodResponse<keyof Methods>>((resolve, reject) => {
Object.assign(requestState, { resolve, reject }); Object.assign(requestState, { resolve, reject });
}); });

View File

@ -855,9 +855,7 @@ export type ApiReplyKeyboard = {
keyboardPlaceholder?: string; keyboardPlaceholder?: string;
isKeyboardSingleUse?: boolean; isKeyboardSingleUse?: boolean;
isKeyboardSelective?: boolean; isKeyboardSelective?: boolean;
} & { } & Partial<Record<'inlineButtons' | 'keyboardButtons', ApiKeyboardButtons>>;
[K in 'inlineButtons' | 'keyboardButtons']?: ApiKeyboardButtons;
};
export type ApiTranscription = { export type ApiTranscription = {
text: string; text: string;

View File

@ -266,13 +266,11 @@ export interface ApiConfig {
export type ApiPeerColorSet = string[]; export type ApiPeerColorSet = string[];
export interface ApiPeerColors { export interface ApiPeerColors {
general: { general: Record<number, {
[key: number]: { isHidden?: true;
isHidden?: true; colors?: ApiPeerColorSet;
colors?: ApiPeerColorSet; darkColors?: ApiPeerColorSet;
darkColors?: ApiPeerColorSet; }>;
};
};
generalHash?: number; generalHash?: number;
} }

View File

@ -65,7 +65,6 @@ function AnimatedIconWithPreview(props: OwnProps) {
onLoad={handlePreviewLoad} onLoad={handlePreviewLoad}
/> />
)} )}
{ }
<AnimatedIcon {...otherProps} onLoad={handleAnimationReady} /> <AnimatedIcon {...otherProps} onLoad={handleAnimationReady} />
</div> </div>
); );

View File

@ -364,7 +364,7 @@ const Audio: FC<OwnProps> = ({
{withSeekline && ( {withSeekline && (
<div className="meta search-result" dir={isRtl ? 'rtl' : undefined}> <div className="meta search-result" dir={isRtl ? 'rtl' : undefined}>
<span className="duration with-seekline" dir="auto"> <span className="duration with-seekline" dir="auto">
{playProgress < 1 && `${formatMediaDuration(duration * playProgress, duration)}`} {playProgress < 1 && formatMediaDuration(duration * playProgress, duration)}
</span> </span>
{renderSeekline(playProgress, bufferedRanges, seekerRef)} {renderSeekline(playProgress, bufferedRanges, seekerRef)}
</div> </div>

View File

@ -312,7 +312,7 @@ const CalendarModal: FC<OwnProps> = ({
currentYear, currentMonth, gridDate, minDate, maxDate, currentYear, currentMonth, gridDate, minDate, maxDate,
) )
? 'disabled' ? 'disabled'
: `${gridDate ? 'clickable' : ''}`, : gridDate ? 'clickable' : '',
selectedDay === formatDay(currentYear, currentMonth, gridDate) && 'selected', selectedDay === formatDay(currentYear, currentMonth, gridDate) && 'selected',
)} )}
> >

View File

@ -136,7 +136,7 @@ const DeleteMessageModal: FC<OwnProps & StateProps> = ({
const buildNestedOptionListWithAvatars = useLastCallback(() => { const buildNestedOptionListWithAvatars = useLastCallback(() => {
return peerList.map((member) => { return peerList.map((member) => {
return { return {
value: `${member.id}`, value: member.id,
label: getPeerTitle(lang, member) || '', label: getPeerTitle(lang, member) || '',
leftElement: <Avatar size="small" peer={member} />, leftElement: <Avatar size="small" peer={member} />,
}; };

View File

@ -291,7 +291,7 @@ const StickerButton = <T extends number | ApiSticker | ApiBotInlineMediaResult |
onClick={handleClick} onClick={handleClick}
onContextMenu={handleContextMenu} onContextMenu={handleContextMenu}
> >
{withSparkles && <Sparkles preset="button" /> } {withSparkles && <Sparkles preset="button" />}
{isIntesectingForShowing && ( {isIntesectingForShowing && (
<StickerView <StickerView
containerRef={ref} containerRef={ref}

View File

@ -356,7 +356,7 @@ export function insertTextEntity(entities: ApiMessageEntity[], newEntity: ApiMes
// Organize entities in a tree-like structure to better represent how the text will be displayed // Organize entities in a tree-like structure to better represent how the text will be displayed
function organizeEntities(entities: ApiMessageEntity[]) { function organizeEntities(entities: ApiMessageEntity[]) {
const organizedEntityIndexes: Set<number> = new Set(); const organizedEntityIndexes = new Set<number>();
const organizedEntities: IOrganizedEntity[] = []; const organizedEntities: IOrganizedEntity[] = [];
entities.forEach((entity, index) => { entities.forEach((entity, index) => {

View File

@ -83,7 +83,7 @@ export default function useAnimatedEmoji(
const { x, y } = container.getBoundingClientRect(); const { x, y } = container.getBoundingClientRect();
interactWithAnimatedEmoji({ interactWithAnimatedEmoji({
emoji: emoji, emoji,
x, x,
y, y,
startSize: size, startSize: size,

View File

@ -245,12 +245,14 @@ const PeerPicker = <CategoryType extends string = CustomPeerType>({
const peerOrCategory = peer || category; const peerOrCategory = peer || category;
if (!peerOrCategory) { if (!peerOrCategory) {
if (DEBUG) return ( if (DEBUG) {
<div key={id}> return (
No peer or category with ID <div key={id}>
{id} No peer or category with ID
</div> {id}
); </div>
);
}
return undefined; return undefined;
} }

View File

@ -395,7 +395,7 @@ const ChatExtra: FC<OwnProps & StateProps> = ({
{birthday && ( {birthday && (
<UserBirthday key={peerId} birthday={birthday} user={user!} isInSettings={isInSettings} /> <UserBirthday key={peerId} birthday={birthday} user={user!} isInSettings={isInSettings} />
)} )}
{ hasMainMiniApp && ( {hasMainMiniApp && (
<ListItem <ListItem
multiline multiline
isStatic isStatic

View File

@ -16,7 +16,7 @@ type OwnProps = {
containerId?: string; containerId?: string;
}; };
const revealByContainerId: Map<string, VoidFunction[]> = new Map(); const revealByContainerId = new Map<string, VoidFunction[]>();
const buildClassName = createClassNameBuilder('Spoiler'); const buildClassName = createClassNameBuilder('Spoiler');

View File

@ -190,9 +190,9 @@
} }
.search-sponsored-badge { .search-sponsored-badge {
cursor: pointer;
display: flex; display: flex;
align-self: flex-start; align-self: flex-start;
cursor: pointer;
&:hover { &:hover {
filter: brightness(1.1); filter: brightness(1.1);
} }

View File

@ -550,7 +550,7 @@ const Main = ({
}); });
// Online status and browser tab indicators // Online status and browser tab indicators
useBackgroundMode(handleBlur, handleFocus, !!IS_ELECTRON); useBackgroundMode(handleBlur, handleFocus, Boolean(IS_ELECTRON));
useBeforeUnload(handleBlur); useBeforeUnload(handleBlur);
usePreventPinchZoomGesture(isMediaViewerOpen || isStoryViewerOpen); usePreventPinchZoomGesture(isMediaViewerOpen || isStoryViewerOpen);

View File

@ -103,7 +103,7 @@ const DEFAULT_CUSTOM_EXPIRE_DATE = 86400 * 3 * 1000; // 3 days
const MAX_ADDITIONAL_CHANNELS = 9; const MAX_ADDITIONAL_CHANNELS = 9;
const DEFAULT_BOOST_COUNT = 5; const DEFAULT_BOOST_COUNT = 5;
const GIVEAWAY_IMG_LIST: { [key: number]: string } = { const GIVEAWAY_IMG_LIST: Partial<Record<number, string>> = {
3: GiftGreenRound, 3: GiftGreenRound,
6: GiftBlueRound, 6: GiftBlueRound,
12: GiftRedRound, 12: GiftRedRound,
@ -721,7 +721,11 @@ const GiveawayModal: FC<OwnProps & StateProps> = ({
{dataStarsPrepaidGiveaway ? ( {dataStarsPrepaidGiveaway ? (
<img className={styles.prepaidImg} src={GiftStar} alt="" /> <img className={styles.prepaidImg} src={GiftStar} alt="" />
) : ( ) : (
<img className={styles.prepaidImg} src={GIVEAWAY_IMG_LIST[dataPrepaidGiveaway!.months]} alt="" /> <img
className={styles.prepaidImg}
src={GIVEAWAY_IMG_LIST[dataPrepaidGiveaway!.months] || GIVEAWAY_IMG_LIST[3]}
alt=""
/>
)} )}
</div> </div>
<div className={styles.info}> <div className={styles.info}>

View File

@ -37,7 +37,7 @@ const GiveawayTypeOption: FC<OwnProps> = ({
let displayText: string | undefined = lang(text); let displayText: string | undefined = lang(text);
if (isLink && selectedMemberIds?.length) { if (isLink && selectedMemberIds?.length) {
displayText = selectedMemberIds.length > 2 ? `${selectedMemberIds.length}` : userNames; displayText = selectedMemberIds.length > 2 ? selectedMemberIds.length.toString() : userNames;
} }
const handleChange = useLastCallback((e: ChangeEvent<HTMLInputElement>) => { const handleChange = useLastCallback((e: ChangeEvent<HTMLInputElement>) => {
@ -70,7 +70,7 @@ const GiveawayTypeOption: FC<OwnProps> = ({
<img className={styles.optionImg} src={img} alt="" draggable={false} /> <img className={styles.optionImg} src={img} alt="" draggable={false} />
<div className={styles.giveaway}> <div className={styles.giveaway}>
<h3 className={styles.title}> <h3 className={styles.title}>
{lang(`${name}`)} {lang(name)}
</h3> </h3>
{isLink ? ( {isLink ? (
<div className={styles.link} onClick={handleClick}> <div className={styles.link} onClick={handleClick}>

View File

@ -352,14 +352,14 @@ const StickerPicker: FC<OwnProps & StateProps> = ({
return ( return (
<div className={fullClassName}> <div className={fullClassName}>
{ !isForEffects && ( {!isForEffects && (
<div ref={headerRef} className={headerClassName}> <div ref={headerRef} className={headerClassName}>
<div className="shared-canvas-container"> <div className="shared-canvas-container">
<canvas ref={sharedCanvasRef} className="shared-canvas" /> <canvas ref={sharedCanvasRef} className="shared-canvas" />
{allSets.map(renderCover)} {allSets.map(renderCover)}
</div> </div>
</div> </div>
) } )}
<div <div
ref={containerRef} ref={containerRef}
onMouseMove={handleMouseMove} onMouseMove={handleMouseMove}

View File

@ -68,7 +68,7 @@ export default async function buildAttachment(
const { videoWidth: width, videoHeight: height, duration } = await preloadVideo(blobUrl); const { videoWidth: width, videoHeight: height, duration } = await preloadVideo(blobUrl);
shouldSendAsFile = !validateAspectRatio(width, height); shouldSendAsFile = !validateAspectRatio(width, height);
if (!shouldSendAsFile) { if (!shouldSendAsFile) {
quick = { width: width, height: height, duration: duration }; quick = { width, height, duration };
} }
} catch (err) { } catch (err) {
shouldSendAsFile = true; shouldSendAsFile = true;

View File

@ -89,13 +89,13 @@ type StateProps = {
isAccountFrozen?: boolean; isAccountFrozen?: boolean;
}; };
const SINGLE_LINE_ACTIONS: Set<ApiMessageAction['type']> = new Set([ const SINGLE_LINE_ACTIONS = new Set<ApiMessageAction['type']>([
'pinMessage', 'pinMessage',
'chatEditPhoto', 'chatEditPhoto',
'chatDeletePhoto', 'chatDeletePhoto',
'unsupported', 'unsupported',
]); ]);
const HIDDEN_TEXT_ACTIONS: Set<ApiMessageAction['type']> = new Set(['giftCode', 'prizeStars', 'suggestProfilePhoto']); const HIDDEN_TEXT_ACTIONS = new Set<ApiMessageAction['type']>(['giftCode', 'prizeStars', 'suggestProfilePhoto']);
const ActionMessage = ({ const ActionMessage = ({
message, message,

View File

@ -137,7 +137,7 @@ const CommentButton: FC<OwnProps> = ({
)} )}
color={isCustomShape ? 'white' : 'blue'} color={isCustomShape ? 'white' : 'blue'}
/> />
) } )}
<Icon <Icon
name="next" name="next"
className={buildClassName( className={buildClassName(

View File

@ -70,7 +70,7 @@ export default function withSelectControl(WrappedComponent: FC) {
)} )}
</div> </div>
)} )}
{ } {}
<WrappedComponent {...newProps} /> <WrappedComponent {...newProps} />
</div> </div>
); );

View File

@ -246,7 +246,7 @@ const GiftModal: FC<OwnProps & StateProps> = ({
return !isLimited && !isSoldOut; return !isLimited && !isSoldOut;
} }
if (areUnlimitedStarGiftsDisallowed && areLimitedStarGiftsDisallowed) { if (areUnlimitedStarGiftsDisallowed && areLimitedStarGiftsDisallowed) {
return Boolean(isLimited && !!upgradeStars); return Boolean(isLimited && Boolean(upgradeStars));
} }
return true; return true;

View File

@ -342,7 +342,7 @@ const PaidReactionModal = ({
})} })}
</div> </div>
)} )}
{topReactors && (<Separator className={styles.separator} />) } {topReactors && (<Separator className={styles.separator} />)}
<Checkbox <Checkbox
className={buildClassName(styles.checkBox, 'dialog-checkbox')} className={buildClassName(styles.checkBox, 'dialog-checkbox')}
checked={!shouldSendAsAnonymous} checked={!shouldSendAsAnonymous}

View File

@ -87,7 +87,7 @@ const StarPaymentModal = ({
if (paidMediaMessage) { if (paidMediaMessage) {
const extendedMedia = paidMediaMessage.content.paidMedia!.extendedMedia as ApiMediaExtendedPreview[]; const extendedMedia = paidMediaMessage.content.paidMedia!.extendedMedia as ApiMediaExtendedPreview[];
const areAllPhotos = extendedMedia.every((media) => !media.duration); const areAllPhotos = extendedMedia.every((media) => !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) const mediaText = areAllPhotos ? oldLang('Stars.Transfer.Photos', extendedMedia.length)
: areAllVideos ? oldLang('Stars.Transfer.Videos', extendedMedia.length) : areAllVideos ? oldLang('Stars.Transfer.Videos', extendedMedia.length)

View File

@ -68,14 +68,14 @@ const MinimizedWebAppModal = ({
function renderTitle() { function renderTitle() {
const activeTabName = peers.length > 0 && peers[0]?.firstName; const activeTabName = peers.length > 0 && peers[0]?.firstName;
const title = openedTabsCount && activeTabName && openedTabsCount > 1 const title = openedTabsCount && activeTabName && openedTabsCount > 1
? `${lang('MiniAppsMoreTabs', ? lang('MiniAppsMoreTabs',
{ {
botName: activeTabName, botName: activeTabName,
count: openedTabsCount - 1, count: openedTabsCount - 1,
}, },
{ {
pluralValue: openedTabsCount - 1, pluralValue: openedTabsCount - 1,
})}` })
: activeTabName; : activeTabName;
return ( return (

View File

@ -713,7 +713,7 @@ const WebAppModal: FC<OwnProps & StateProps> = ({
modalHeight={currentHeight} modalHeight={currentHeight}
/> />
))} ))}
{ isMoreAppsTabActive && (<MoreAppsTabContent />)} {isMoreAppsTabActive && (<MoreAppsTabContent />)}
</Modal> </Modal>
); );
}; };

View File

@ -1109,7 +1109,7 @@ const WebAppModalTabContent: FC<OwnProps & StateProps> = ({
{mainButton?.isProgressVisible && <Spinner className={styles.mainButtonSpinner} color="white" />} {mainButton?.isProgressVisible && <Spinner className={styles.mainButtonSpinner} color="white" />}
</Button> </Button>
</div> </div>
) } )}
{popupParameters && ( {popupParameters && (
<Modal <Modal
isOpen={Boolean(popupParameters)} isOpen={Boolean(popupParameters)}

View File

@ -167,10 +167,10 @@ const ShippingInfo: FC<OwnProps> = ({
/> />
</div> </div>
) : undefined} ) : undefined}
{ needName || needEmail || needPhone ? ( {needName || needEmail || needPhone ? (
<h5>{oldLang('PaymentShippingReceiver')}</h5> <h5>{oldLang('PaymentShippingReceiver')}</h5>
) : undefined } ) : undefined}
{ needName && ( {needName && (
<InputText <InputText
label={oldLang('PaymentShippingName')} label={oldLang('PaymentShippingName')}
onChange={handleFullNameChange} onChange={handleFullNameChange}
@ -179,8 +179,8 @@ const ShippingInfo: FC<OwnProps> = ({
tabIndex={0} tabIndex={0}
error={formErrors.fullName && lang.withRegular(formErrors.fullName)} error={formErrors.fullName && lang.withRegular(formErrors.fullName)}
/> />
) } )}
{ needEmail && ( {needEmail && (
<InputText <InputText
label={oldLang('PaymentShippingEmailPlaceholder')} label={oldLang('PaymentShippingEmailPlaceholder')}
onChange={handleEmailChange} onChange={handleEmailChange}
@ -189,8 +189,8 @@ const ShippingInfo: FC<OwnProps> = ({
tabIndex={0} tabIndex={0}
error={formErrors.email && lang.withRegular(formErrors.email)} error={formErrors.email && lang.withRegular(formErrors.email)}
/> />
) } )}
{ needPhone && ( {needPhone && (
<InputText <InputText
label={oldLang('PaymentShippingPhoneNumber')} label={oldLang('PaymentShippingPhoneNumber')}
onChange={handlePhoneChange} onChange={handlePhoneChange}
@ -200,7 +200,7 @@ const ShippingInfo: FC<OwnProps> = ({
error={formErrors.phone && lang.withRegular(formErrors.phone)} error={formErrors.phone && lang.withRegular(formErrors.phone)}
ref={phoneRef} ref={phoneRef}
/> />
) } )}
<Checkbox <Checkbox
label={oldLang('PaymentShippingSave')} label={oldLang('PaymentShippingSave')}
subLabel={oldLang('PaymentShippingSaveInfo')} subLabel={oldLang('PaymentShippingSaveInfo')}

View File

@ -782,7 +782,7 @@ const Profile: FC<OwnProps & StateProps> = ({
))} ))}
{!isCurrentUserPremium && ( {!isCurrentUserPremium && (
<> <>
{ } {}
<Button className="show-more-channels" size="smaller" onClick={() => openPremiumModal()}> <Button className="show-more-channels" size="smaller" onClick={() => openPremiumModal()}>
{oldLang('UnlockSimilar')} {oldLang('UnlockSimilar')}
<Icon name="unlock-badge" /> <Icon name="unlock-badge" />
@ -821,7 +821,7 @@ const Profile: FC<OwnProps & StateProps> = ({
))} ))}
{!isCurrentUserPremium && ( {!isCurrentUserPremium && (
<> <>
{ } {}
<Button className="show-more-bots" size="smaller" onClick={() => openPremiumModal()}> <Button className="show-more-bots" size="smaller" onClick={() => openPremiumModal()}>
{lang('UnlockMoreSimilarBots')} {lang('UnlockMoreSimilarBots')}
<Icon name="unlock-badge" /> <Icon name="unlock-badge" />

View File

@ -167,7 +167,7 @@ const ManageBot: FC<OwnProps & StateProps> = ({
}); });
const handleChangeSettings = useLastCallback(() => { const handleChangeSettings = useLastCallback(() => {
startBotFatherConversation({ param: `${username}` }); startBotFatherConversation({ param: username! });
}); });
const inputRef = useRef<HTMLInputElement>(); const inputRef = useRef<HTMLInputElement>();

View File

@ -200,7 +200,7 @@ const ManageChannel: FC<OwnProps & StateProps> = ({
const enabledLength = chatFullInfo.enabledReactions.allowed.length; const enabledLength = chatFullInfo.enabledReactions.allowed.length;
const totalLength = availableReactions?.filter((reaction) => !reaction.isInactive).length || 0; const totalLength = availableReactions?.filter((reaction) => !reaction.isInactive).length || 0;
return totalLength ? `${enabledLength} / ${totalLength}` : `${enabledLength}`; return totalLength ? `${enabledLength} / ${totalLength}` : enabledLength.toString();
}, [availableReactions, chatFullInfo?.enabledReactions, lang]); }, [availableReactions, chatFullInfo?.enabledReactions, lang]);
const isChannelPublic = useMemo(() => isChatPublic(chat), [chat]); const isChannelPublic = useMemo(() => isChatPublic(chat), [chat]);

View File

@ -270,7 +270,7 @@ const ManageGroup: FC<OwnProps & StateProps> = ({
return totalLength return totalLength
? `${enabledLength} / ${totalLength}` ? `${enabledLength} / ${totalLength}`
: `${enabledLength}`; : enabledLength.toString();
}, [availableReactions, chatFullInfo?.enabledReactions, lang]); }, [availableReactions, chatFullInfo?.enabledReactions, lang]);
const enabledPermissionsCount = useMemo(() => { const enabledPermissionsCount = useMemo(() => {

View File

@ -192,7 +192,7 @@ const ManageReactions: FC<OwnProps & StateProps> = ({
return ( return (
<div className="Management"> <div className="Management">
<div className="panel-content custom-scroll"> <div className="panel-content custom-scroll">
{ Boolean(localReactionsLimit && shouldShowReactionsLimit) && ( {Boolean(localReactionsLimit && shouldShowReactionsLimit) && (
<div className="section"> <div className="section">
<h3 className="section-heading"> <h3 className="section-heading">
{lang('MaximumReactionsHeader')} {lang('MaximumReactionsHeader')}

View File

@ -50,7 +50,7 @@ type StateProps = {
isChannel?: boolean; isChannel?: boolean;
}; };
const GIVEAWAY_IMG_LIST: { [key: number]: string } = { const GIVEAWAY_IMG_LIST: Partial<Record<number, string>> = {
3: GiftGreenRound, 3: GiftGreenRound,
6: GiftBlueRound, 6: GiftBlueRound,
12: GiftRedRound, 12: GiftRedRound,
@ -319,7 +319,7 @@ const BoostStatistics = ({
/> />
) : ( ) : (
<img <img
src={GIVEAWAY_IMG_LIST[prepaidGiveaway.months]} src={GIVEAWAY_IMG_LIST[prepaidGiveaway.months] || GIVEAWAY_IMG_LIST[3]}
className={styles.giveawayIcon} className={styles.giveawayIcon}
alt={lang('Giveaway')} alt={lang('Giveaway')}
/> />

View File

@ -13,7 +13,7 @@ const RIBBON_Z_INDEX = 11;
const STROKE_OFFSET = 0.1875 * REM; const STROKE_OFFSET = 0.1875 * REM;
const CANVAS_OFFSET = 0.125 * REM; const CANVAS_OFFSET = 0.125 * REM;
const callbacks: Set<NoneToVoidFunction> = new Set(); const callbacks = new Set<NoneToVoidFunction>();
export function animateOpening(isArchived?: boolean) { export function animateOpening(isArchived?: boolean) {
cancelDelayedCallbacks(); cancelDelayedCallbacks();
@ -99,7 +99,7 @@ export function animateOpening(isArchived?: boolean) {
applyStyles(ghost, { applyStyles(ghost, {
top: `${toTop}px`, top: `${toTop}px`,
left: `${toLeft}px`, left: `${toLeft}px`,
zIndex: `${zIndex}`, zIndex: String(zIndex),
opacity: ghost2 ? '0' : '', opacity: ghost2 ? '0' : '',
transform: `translate3d(${fromTranslateX}px, ${fromTranslateY}px, 0) scale(${fromScale})`, transform: `translate3d(${fromTranslateX}px, ${fromTranslateY}px, 0) scale(${fromScale})`,
}); });
@ -108,7 +108,7 @@ export function animateOpening(isArchived?: boolean) {
applyStyles(ghost2, { applyStyles(ghost2, {
top: `${fromTop}px`, top: `${fromTop}px`,
left: `${fromLeft}px`, left: `${fromLeft}px`,
zIndex: `${zIndex}`, zIndex: String(zIndex),
}); });
} }
@ -238,14 +238,14 @@ export function animateClosing(isArchived?: boolean) {
top: `${fromTop}px`, top: `${fromTop}px`,
left: `${fromLeft}px`, left: `${fromLeft}px`,
width: `${fromWidth}px`, width: `${fromWidth}px`,
zIndex: `${zIndex}`, zIndex: String(zIndex),
}); });
if (ghost2) { if (ghost2) {
applyStyles(ghost2, { applyStyles(ghost2, {
top: `${toTop}px`, top: `${toTop}px`,
left: `${toLeft}px`, left: `${toLeft}px`,
zIndex: `${zIndex}`, zIndex: String(zIndex),
opacity: '0', opacity: '0',
transform: `translate3d(${fromTranslateX}px, ${fromTranslateY}px, 0) scale(${fromScale})`, transform: `translate3d(${fromTranslateX}px, ${fromTranslateY}px, 0) scale(${fromScale})`,
}); });

View File

@ -1,3 +1,4 @@
/* eslint-disable no-template-curly-in-string */
const config = { const config = {
productName: 'Telegram A', productName: 'Telegram A',
artifactName: '${productName}-${arch}.${ext}', artifactName: '${productName}-${arch}.${ext}',

View File

@ -337,7 +337,7 @@ addActionHandler('queryInlineBot', async (global, actions, payload): Promise<voi
void runDebouncedForSearch(() => { void runDebouncedForSearch(() => {
searchInlineBot(global, { searchInlineBot(global, {
username, username,
inlineBotData: inlineBotData, inlineBotData,
chatId, chatId,
query, query,
offset, offset,

View File

@ -427,7 +427,7 @@ addActionHandler('openThread', async (global, actions, payload): Promise<void> =
if (focusMessageId) { if (focusMessageId) {
actions.focusMessage({ actions.focusMessage({
chatId, chatId,
threadId: threadId, threadId,
messageId: focusMessageId, messageId: focusMessageId,
tabId, tabId,
}); });
@ -464,7 +464,7 @@ addActionHandler('openThread', async (global, actions, payload): Promise<void> =
actions.processOpenChatOrThread({ actions.processOpenChatOrThread({
chatId, chatId,
type, type,
threadId: threadId, threadId,
tabId, tabId,
isComments, isComments,
noForumTopicPanel, noForumTopicPanel,

View File

@ -15,7 +15,7 @@ export default function useEnsureMessage(
useEffect(() => { useEffect(() => {
if (isDisabled) return; if (isDisabled) return;
if (messageId && !message) { if (messageId && !message) {
loadMessage({ chatId, messageId: messageId, replyOriginForId: replyOriginForId! }); loadMessage({ chatId, messageId, replyOriginForId: replyOriginForId! });
} }
}, [isDisabled, chatId, message, messageId, replyOriginForId]); }, [isDisabled, chatId, message, messageId, replyOriginForId]);
} }

View File

@ -9,4 +9,4 @@ insert_final_newline = true
trim_trailing_whitespace = true trim_trailing_whitespace = true
charset = utf-8 charset = utf-8
indent_style = space indent_style = space
indent_size = 4 indent_size = 2

View File

@ -1,5 +0,0 @@
tl/api.d.ts
tl/apiTl.ts
tl/schemaTl.ts
tl/apiTl.full.ts
tl/schemaTl.full.ts

View File

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

View File

@ -45,7 +45,7 @@ const HTML_NAMESPACE = 'http://www.w3.org/1999/xhtml';
const FILTERED_ATTRIBUTES = new Set(['key', 'ref', 'teactFastList', 'teactOrderKey']); const FILTERED_ATTRIBUTES = new Set(['key', 'ref', 'teactFastList', 'teactOrderKey']);
const HTML_ATTRIBUTES = new Set(['dir', 'role', 'form']); const HTML_ATTRIBUTES = new Set(['dir', 'role', 'form']);
const CONTROLLABLE_TAGS = ['INPUT', 'TEXTAREA', 'SELECT']; const CONTROLLABLE_TAGS = ['INPUT', 'TEXTAREA', 'SELECT'];
const MAPPED_ATTRIBUTES: { [k: string]: string } = { const MAPPED_ATTRIBUTES: Partial<Record<string, string>> = {
autoCapitalize: 'autocapitalize', autoCapitalize: 'autocapitalize',
autoComplete: 'autocomplete', autoComplete: 'autocomplete',
autoCorrect: 'autocorrect', autoCorrect: 'autocorrect',

View File

@ -15,7 +15,7 @@ const connections = launchMediaWorkers();
let videoPreview: VideoPreview | undefined; let videoPreview: VideoPreview | undefined;
export class VideoPreview { export class VideoPreview {
frames: Map<number, ImageBitmap> = new Map(); frames = new Map<number, ImageBitmap>();
currentTime = 0; currentTime = 0;

View File

@ -61,9 +61,7 @@ export type SharedSessionData = {
date?: number; date?: number;
dcId: number; dcId: number;
isTest?: true; isTest?: true;
} & { } & Partial<Record<`dc${DcId}_${'auth_key' | 'server_salt'}`, string>> & SessionUserInfo;
[K in `dc${DcId}_${'auth_key' | 'server_salt'}`]?: string;
} & SessionUserInfo;
export type AccountInfo = { export type AccountInfo = {
isTest?: true; isTest?: true;
@ -103,9 +101,7 @@ export type PerformanceTypeKey = (
| 'animatedEmoji' | 'loopAnimatedStickers' | 'reactionEffects' | 'stickerEffects' | 'autoplayGifs' | 'autoplayVideos' | 'animatedEmoji' | 'loopAnimatedStickers' | 'reactionEffects' | 'stickerEffects' | 'autoplayGifs' | 'autoplayVideos'
| 'storyRibbonAnimations' | 'snapEffect' | 'storyRibbonAnimations' | 'snapEffect'
); );
export type PerformanceType = { export type PerformanceType = Record<PerformanceTypeKey, boolean>;
[key in PerformanceTypeKey]: boolean;
};
export interface IThemeSettings { export interface IThemeSettings {
background?: string; background?: string;

View File

@ -140,7 +140,7 @@ class ConnectorClass<T extends InputRequestTypes> {
const requestState = { messageId } as RequestState; const requestState = { messageId } as RequestState;
// Re-wrap type because of `postMessage` // Re-wrap type because of `postMessage`
const promise: Promise<any> = new Promise((resolve, reject) => { const promise = new Promise<any>((resolve, reject) => {
Object.assign(requestState, { resolve, reject }); Object.assign(requestState, { resolve, reject });
}); });

View File

@ -6,7 +6,7 @@ import { animate } from './animation';
const DEFAULT_DURATION = 300; const DEFAULT_DURATION = 300;
const stopById: Map<string, VoidFunction> = new Map(); const stopById = new Map<string, VoidFunction>();
export default function animateHorizontalScroll(container: HTMLElement, left: number, duration = DEFAULT_DURATION) { export default function animateHorizontalScroll(container: HTMLElement, left: number, duration = DEFAULT_DURATION) {
if (!selectCanAnimateInterface(getGlobal())) { if (!selectCanAnimateInterface(getGlobal())) {

View File

@ -22,7 +22,7 @@ type CustomEmojiInputRenderCallback = (emojiId: string) => void;
const DOM_PROCESS_THROTTLE = 500; const DOM_PROCESS_THROTTLE = 500;
const INPUT_WAITING_CUSTOM_EMOJI_IDS: Set<string> = new Set(); const INPUT_WAITING_CUSTOM_EMOJI_IDS = new Set<string>();
const handlers = new Map<CustomEmojiLoadCallback, string>(); const handlers = new Map<CustomEmojiLoadCallback, string>();
const renderCallbacks = createCallbackManager<CustomEmojiInputRenderCallback>(); const renderCallbacks = createCallbackManager<CustomEmojiInputRenderCallback>();

View File

@ -4,9 +4,7 @@ type OrderDirection =
'asc' 'asc'
| 'desc'; | 'desc';
interface OrderCallback<T> { type OrderCallback<T> = (member: T) => unknown;
(member: T): any;
}
export function buildCollectionByKey<T extends AnyLiteral>(collection: T[], key: keyof T) { export function buildCollectionByKey<T extends AnyLiteral>(collection: T[], key: keyof T) {
return collection.reduce((byKey: CollectionByKey<T>, member: T) => { return collection.reduce((byKey: CollectionByKey<T>, member: T) => {
@ -82,6 +80,7 @@ export function orderBy<T>(
const aValue = (typeof currentOrderRule === 'function' ? currentOrderRule(a) : a[currentOrderRule]) || 0; const aValue = (typeof currentOrderRule === 'function' ? currentOrderRule(a) : a[currentOrderRule]) || 0;
const bValue = (typeof currentOrderRule === 'function' ? currentOrderRule(b) : b[currentOrderRule]) || 0; const bValue = (typeof currentOrderRule === 'function' ? currentOrderRule(b) : b[currentOrderRule]) || 0;
// @ts-expect-error Rely on the JS to handle the comparison
return isAsc ? aValue - bValue : bValue - aValue; return isAsc ? aValue - bValue : bValue - aValue;
} }

View File

@ -15,6 +15,7 @@
"strict": true, "strict": true,
"forceConsistentCasingInFileNames": true, "forceConsistentCasingInFileNames": true,
"allowImportingTsExtensions": true, "allowImportingTsExtensions": true,
"noImplicitReturns": true,
"module": "esnext", "module": "esnext",
"moduleResolution": "node", "moduleResolution": "node",
"resolveJsonModule": true, "resolveJsonModule": true,