[Refactoring] ESLint: Add addCallback where possible (#1780)
This commit is contained in:
parent
f198cd96b6
commit
41a67c9baf
@ -73,8 +73,8 @@
|
||||
"react/style-prop-object": "off",
|
||||
"react/jsx-no-bind": ["error", {
|
||||
"ignoreRefs": true,
|
||||
"allowArrowFunctions": true,
|
||||
"allowFunctions": true,
|
||||
"allowArrowFunctions": false,
|
||||
"allowFunctions": false,
|
||||
"allowBind": false,
|
||||
"ignoreDOMComponents": true
|
||||
}],
|
||||
|
||||
@ -1,5 +1,7 @@
|
||||
import { ChangeEvent } from 'react';
|
||||
import React, { FC, useState, memo } from '../../lib/teact/teact';
|
||||
import React, {
|
||||
FC, useState, memo, useCallback,
|
||||
} from '../../lib/teact/teact';
|
||||
import { getActions, withGlobal } from '../../global';
|
||||
|
||||
import { GlobalState } from '../../global/types';
|
||||
@ -24,7 +26,7 @@ const AuthRegister: FC<StateProps> = ({
|
||||
const [firstName, setFirstName] = useState('');
|
||||
const [lastName, setLastName] = useState('');
|
||||
|
||||
function handleFirstNameChange(event: ChangeEvent<HTMLInputElement>) {
|
||||
const handleFirstNameChange = useCallback((event: ChangeEvent<HTMLInputElement>) => {
|
||||
if (authError) {
|
||||
clearAuthError();
|
||||
}
|
||||
@ -33,13 +35,13 @@ const AuthRegister: FC<StateProps> = ({
|
||||
|
||||
setFirstName(target.value);
|
||||
setIsButtonShown(target.value.length > 0);
|
||||
}
|
||||
}, [authError, clearAuthError]);
|
||||
|
||||
function handleLastNameChange(event: ChangeEvent<HTMLInputElement>) {
|
||||
const handleLastNameChange = useCallback((event: ChangeEvent<HTMLInputElement>) => {
|
||||
const { target } = event;
|
||||
|
||||
setLastName(target.value);
|
||||
}
|
||||
}, []);
|
||||
|
||||
function handleSubmit(event: React.FormEvent<HTMLFormElement>) {
|
||||
event.preventDefault();
|
||||
|
||||
@ -138,6 +138,7 @@ const CountryCodeInput: FC<OwnProps & StateProps> = ({
|
||||
<MenuItem
|
||||
key={`${country.iso2}-${country.countryCode}`}
|
||||
className={value && country.iso2 === value.iso2 ? 'selected' : ''}
|
||||
// eslint-disable-next-line react/jsx-no-bind
|
||||
onClick={() => handleChange(country)}
|
||||
>
|
||||
<span className="country-flag">{renderText(isoToEmoji(country.iso2), ['hq_emoji'])}</span>
|
||||
|
||||
@ -1,4 +1,6 @@
|
||||
import React, { FC, memo, useState } from '../../lib/teact/teact';
|
||||
import React, {
|
||||
FC, memo, useCallback, useState,
|
||||
} from '../../lib/teact/teact';
|
||||
import { getActions, withGlobal } from '../../global';
|
||||
|
||||
import ConfirmDialog from '../ui/ConfirmDialog';
|
||||
@ -30,13 +32,15 @@ const CallFallbackConfirm: FC<OwnProps & StateProps> = ({
|
||||
const [shouldRemove, setShouldRemove] = useState(true);
|
||||
const renderingUserFullName = useCurrentOrPrev(userFullName, true);
|
||||
|
||||
const handleConfirm = useCallback(() => {
|
||||
inviteToCallFallback({ shouldRemove });
|
||||
}, [inviteToCallFallback, shouldRemove]);
|
||||
|
||||
return (
|
||||
<ConfirmDialog
|
||||
title="Start Call"
|
||||
isOpen={isOpen}
|
||||
confirmHandler={() => {
|
||||
inviteToCallFallback({ shouldRemove });
|
||||
}}
|
||||
confirmHandler={handleConfirm}
|
||||
onClose={closeCallFallbackConfirm}
|
||||
>
|
||||
<p>The call will be started in a private channel <b>{channelTitle}</b>.</p>
|
||||
|
||||
@ -123,10 +123,10 @@ const GroupCall: FC<OwnProps & StateProps> = ({
|
||||
}
|
||||
}, [connectionState, playGroupCallSound]);
|
||||
|
||||
const handleCloseConfirmLeaveModal = () => {
|
||||
const handleCloseConfirmLeaveModal = useCallback(() => {
|
||||
closeConfirmLeaveModal();
|
||||
setIsEndGroupCallModal(false);
|
||||
};
|
||||
}, [closeConfirmLeaveModal]);
|
||||
|
||||
const MainButton: FC<{ onTrigger: () => void; isOpen?: boolean }> = useMemo(() => {
|
||||
return ({ onTrigger, isOpen }) => (
|
||||
@ -153,13 +153,13 @@ const GroupCall: FC<OwnProps & StateProps> = ({
|
||||
}
|
||||
}, [closeFullscreen, isFullscreen, openFullscreen]);
|
||||
|
||||
const handleToggleSidebar = () => {
|
||||
const handleToggleSidebar = useCallback(() => {
|
||||
if (isSidebarOpen) {
|
||||
closeSidebar();
|
||||
} else {
|
||||
openSidebar();
|
||||
}
|
||||
};
|
||||
}, [closeSidebar, isSidebarOpen, openSidebar]);
|
||||
|
||||
const handleStreamsDoubleClick = useCallback(() => {
|
||||
if (!IS_REQUEST_FULLSCREEN_SUPPORTED) return;
|
||||
@ -180,12 +180,12 @@ const GroupCall: FC<OwnProps & StateProps> = ({
|
||||
}
|
||||
}, [closeFullscreen, isFullscreen, openFullscreen]);
|
||||
|
||||
const handleClose = () => {
|
||||
const handleClose = useCallback(() => {
|
||||
toggleGroupCallPanel();
|
||||
if (isFullscreen) {
|
||||
closeFullscreen();
|
||||
}
|
||||
};
|
||||
}, [closeFullscreen, isFullscreen, toggleGroupCallPanel]);
|
||||
|
||||
useEffect(() => {
|
||||
if (!IS_REQUEST_FULLSCREEN_SUPPORTED) return undefined;
|
||||
@ -211,16 +211,16 @@ const GroupCall: FC<OwnProps & StateProps> = ({
|
||||
connectToActiveGroupCall();
|
||||
}, [connectToActiveGroupCall, groupCallId]);
|
||||
|
||||
const endGroupCall = () => {
|
||||
const endGroupCall = useCallback(() => {
|
||||
setIsEndGroupCallModal(true);
|
||||
setShouldEndGroupCall(true);
|
||||
openConfirmLeaveModal();
|
||||
if (isFullscreen) {
|
||||
handleToggleFullscreen();
|
||||
}
|
||||
};
|
||||
}, [handleToggleFullscreen, isFullscreen, openConfirmLeaveModal]);
|
||||
|
||||
const handleLeaveGroupCall = () => {
|
||||
const handleLeaveGroupCall = useCallback(() => {
|
||||
if (isAdmin && !isConfirmLeaveModalOpen) {
|
||||
openConfirmLeaveModal();
|
||||
if (isFullscreen) {
|
||||
@ -231,15 +231,18 @@ const GroupCall: FC<OwnProps & StateProps> = ({
|
||||
playGroupCallSound({ sound: 'leave' });
|
||||
setIsLeaving(true);
|
||||
closeConfirmLeaveModal();
|
||||
};
|
||||
}, [
|
||||
closeConfirmLeaveModal, handleToggleFullscreen, isAdmin, isConfirmLeaveModalOpen, isFullscreen,
|
||||
openConfirmLeaveModal, playGroupCallSound,
|
||||
]);
|
||||
|
||||
const handleCloseAnimationEnd = () => {
|
||||
const handleCloseAnimationEnd = useCallback(() => {
|
||||
if (isLeaving) {
|
||||
leaveGroupCall({
|
||||
shouldDiscard: shouldEndGroupCall,
|
||||
});
|
||||
}
|
||||
};
|
||||
}, [isLeaving, leaveGroupCall, shouldEndGroupCall]);
|
||||
|
||||
return (
|
||||
<Modal
|
||||
|
||||
@ -163,9 +163,9 @@ const CalendarModal: FC<OwnProps> = ({
|
||||
});
|
||||
}
|
||||
|
||||
function handleSubmit() {
|
||||
const handleSubmit = useCallback(() => {
|
||||
onSubmit(selectedDate);
|
||||
}
|
||||
}, [onSubmit, selectedDate]);
|
||||
|
||||
const handleChangeHours = useCallback((e: React.ChangeEvent<HTMLInputElement>) => {
|
||||
const value = e.target.value.replace(/[^\d]+/g, '');
|
||||
|
||||
@ -86,6 +86,7 @@ const ChatExtra: FC<OwnProps & StateProps> = ({
|
||||
return (
|
||||
<div className="ChatExtra">
|
||||
{formattedNumber && Boolean(formattedNumber.length) && (
|
||||
// eslint-disable-next-line react/jsx-no-bind
|
||||
<ListItem icon="phone" multiline narrow ripple onClick={() => copy(formattedNumber, lang('Phone'))}>
|
||||
<span className="title" dir="auto">{formattedNumber}</span>
|
||||
<span className="subtitle">{lang('Phone')}</span>
|
||||
@ -97,6 +98,7 @@ const ChatExtra: FC<OwnProps & StateProps> = ({
|
||||
multiline
|
||||
narrow
|
||||
ripple
|
||||
// eslint-disable-next-line react/jsx-no-bind
|
||||
onClick={() => copy(`@${username}`, lang('Username'))}
|
||||
>
|
||||
<span className="title" dir="auto">{renderText(username)}</span>
|
||||
@ -122,6 +124,7 @@ const ChatExtra: FC<OwnProps & StateProps> = ({
|
||||
multiline
|
||||
narrow
|
||||
ripple
|
||||
// eslint-disable-next-line react/jsx-no-bind
|
||||
onClick={() => copy(link, lang('SetUrlPlaceholder'))}
|
||||
>
|
||||
<div className="title">{link}</div>
|
||||
|
||||
@ -113,6 +113,7 @@ const ChatOrUserPicker: FC<OwnProps> = ({
|
||||
key={id}
|
||||
className="chat-item-clickable force-rounded-corners"
|
||||
style={`top: ${(viewportOffset + i) * CHAT_HEIGHT_PX}px;`}
|
||||
// eslint-disable-next-line react/jsx-no-bind
|
||||
onClick={() => onSelectChatOrUser(id)}
|
||||
>
|
||||
{isUserId(id) ? (
|
||||
|
||||
@ -115,6 +115,7 @@ const Picker: FC<OwnProps> = ({
|
||||
<ListItem
|
||||
key={id}
|
||||
className="chat-item-clickable picker-list-item"
|
||||
// eslint-disable-next-line react/jsx-no-bind
|
||||
onClick={() => handleItemClick(id)}
|
||||
ripple
|
||||
>
|
||||
|
||||
@ -33,11 +33,11 @@ const ReportMessageModal: FC<OwnProps> = ({
|
||||
const [selectedReason, setSelectedReason] = useState<ApiReportReason>('spam');
|
||||
const [description, setDescription] = useState('');
|
||||
|
||||
const handleReport = () => {
|
||||
const handleReport = useCallback(() => {
|
||||
reportMessages({ messageIds, reason: selectedReason, description });
|
||||
exitMessageSelectMode();
|
||||
onClose();
|
||||
};
|
||||
}, [description, exitMessageSelectMode, messageIds, onClose, reportMessages, selectedReason]);
|
||||
|
||||
const handleSelectReason = useCallback((value: string) => {
|
||||
setSelectedReason(value as ApiReportReason);
|
||||
|
||||
@ -53,6 +53,7 @@ const SeenByModal: FC<OwnProps & StateProps> = ({
|
||||
<ListItem
|
||||
key={userId}
|
||||
className="chat-item-clickable scroll-item small-icon"
|
||||
// eslint-disable-next-line react/jsx-no-bind
|
||||
onClick={() => handleClick(userId)}
|
||||
>
|
||||
<PrivateChatInfo userId={userId} noStatusOrTyping />
|
||||
|
||||
@ -140,28 +140,28 @@ const StickerButton = <T extends number | ApiSticker | ApiBotInlineMediaResult |
|
||||
handleBeforeContextMenu(e);
|
||||
};
|
||||
|
||||
const handleUnfaveClick = (e: ReactMouseEvent<HTMLButtonElement, MouseEvent>) => {
|
||||
const handleUnfaveClick = useCallback((e: ReactMouseEvent<HTMLButtonElement, MouseEvent>) => {
|
||||
e.stopPropagation();
|
||||
e.preventDefault();
|
||||
|
||||
onUnfaveClick!(sticker);
|
||||
};
|
||||
}, [onUnfaveClick, sticker]);
|
||||
|
||||
const handleContextUnfave = () => {
|
||||
const handleContextUnfave = useCallback(() => {
|
||||
onUnfaveClick!(sticker);
|
||||
};
|
||||
}, [onUnfaveClick, sticker]);
|
||||
|
||||
const handleContextFave = () => {
|
||||
const handleContextFave = useCallback(() => {
|
||||
onFaveClick!(sticker);
|
||||
};
|
||||
}, [onFaveClick, sticker]);
|
||||
|
||||
const handleSendQuiet = () => {
|
||||
const handleSendQuiet = useCallback(() => {
|
||||
onClick?.(clickArg, true);
|
||||
};
|
||||
}, [clickArg, onClick]);
|
||||
|
||||
const handleSendScheduled = () => {
|
||||
const handleSendScheduled = useCallback(() => {
|
||||
onClick?.(clickArg, undefined, true);
|
||||
};
|
||||
}, [clickArg, onClick]);
|
||||
|
||||
const fullClassName = buildClassName(
|
||||
'StickerButton',
|
||||
|
||||
@ -286,10 +286,10 @@ const LeftColumn: FC<StateProps> = ({
|
||||
initResize, resetResize, handleMouseUp,
|
||||
} = useResize(resizeRef, setLeftColumnWidth, resetLeftColumnWidth, leftColumnWidth);
|
||||
|
||||
const handleSettingsScreenSelect = (screen: SettingsScreens) => {
|
||||
const handleSettingsScreenSelect = useCallback((screen: SettingsScreens) => {
|
||||
setContent(LeftColumnContent.Settings);
|
||||
setSettingsScreen(screen);
|
||||
};
|
||||
}, []);
|
||||
|
||||
return (
|
||||
<div
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
import React, {
|
||||
FC, useState, useEffect, memo,
|
||||
FC, useState, useEffect, memo, useCallback,
|
||||
} from '../../lib/teact/teact';
|
||||
|
||||
import buildClassName from '../../util/buildClassName';
|
||||
@ -40,13 +40,13 @@ const NewChatButton: FC<OwnProps> = ({
|
||||
isMenuOpen && 'menu-is-open',
|
||||
);
|
||||
|
||||
const toggleIsMenuOpen = () => {
|
||||
const toggleIsMenuOpen = useCallback(() => {
|
||||
setIsMenuOpen(!isMenuOpen);
|
||||
};
|
||||
}, [isMenuOpen]);
|
||||
|
||||
const handleClose = () => {
|
||||
const handleClose = useCallback(() => {
|
||||
setIsMenuOpen(false);
|
||||
};
|
||||
}, []);
|
||||
|
||||
return (
|
||||
<div className={fabClassName}>
|
||||
|
||||
@ -83,6 +83,7 @@ const ContactList: FC<OwnProps & StateProps> = ({
|
||||
<ListItem
|
||||
key={id}
|
||||
className="chat-item-clickable"
|
||||
// eslint-disable-next-line react/jsx-no-bind
|
||||
onClick={() => handleClick(id)}
|
||||
ripple={!IS_SINGLE_COLUMN_LAYOUT}
|
||||
>
|
||||
|
||||
@ -134,6 +134,7 @@ const LeftMainHeader: FC<OwnProps & StateProps> = ({
|
||||
size="smaller"
|
||||
color="translucent"
|
||||
className={isOpen ? 'active' : ''}
|
||||
// eslint-disable-next-line react/jsx-no-bind
|
||||
onClick={hasMenu ? onTrigger : () => onReset()}
|
||||
ariaLabel={hasMenu ? lang('AccDescrOpenMenu2') : 'Return to chat list'}
|
||||
>
|
||||
@ -181,15 +182,15 @@ const LeftMainHeader: FC<OwnProps & StateProps> = ({
|
||||
setSettingOption({ animationLevel: newLevel });
|
||||
}, [animationLevel, setSettingOption]);
|
||||
|
||||
const handleSwitchToWebK = () => {
|
||||
const handleSwitchToWebK = useCallback(() => {
|
||||
setPermanentWebVersion('K');
|
||||
clearWebsync();
|
||||
disableHistoryBack();
|
||||
};
|
||||
}, []);
|
||||
|
||||
const handleOpenTipsChat = () => {
|
||||
const handleOpenTipsChat = useCallback(() => {
|
||||
openTipsChat({ langCode: lang.code });
|
||||
};
|
||||
}, [lang.code, openTipsChat]);
|
||||
|
||||
const isSearchFocused = (
|
||||
Boolean(globalSearchChatId)
|
||||
|
||||
@ -125,6 +125,7 @@ const NewChatStep2: FC<OwnProps & StateProps > = ({
|
||||
round
|
||||
size="smaller"
|
||||
color="translucent"
|
||||
// eslint-disable-next-line react/jsx-no-bind
|
||||
onClick={() => onReset()}
|
||||
ariaLabel="Return to member selection"
|
||||
>
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
import React, {
|
||||
FC, memo,
|
||||
FC, memo, useCallback,
|
||||
} from '../../../lib/teact/teact';
|
||||
import { withGlobal } from '../../../global';
|
||||
|
||||
@ -56,9 +56,9 @@ const LeftSearchResultChat: FC<OwnProps & StateProps> = ({
|
||||
handleChatFolderChange: openChatFolderModal,
|
||||
}, true);
|
||||
|
||||
const handleClick = () => {
|
||||
const handleClick = useCallback(() => {
|
||||
onClick(chatId);
|
||||
};
|
||||
}, [chatId, onClick]);
|
||||
|
||||
const buttonRef = useSelectWithEnter(handleClick);
|
||||
|
||||
|
||||
@ -69,6 +69,14 @@ const SettingsDataStorage: FC<OwnProps & StateProps> = ({
|
||||
setSettingOption({ autoLoadFileMaxSizeMb: AUTODOWNLOAD_FILESIZE_MB_LIMITS[value] });
|
||||
}, [setSettingOption]);
|
||||
|
||||
const handleCanAutoPlayGifsChange = useCallback((value: boolean) => {
|
||||
setSettingOption({ canAutoPlayGifs: value });
|
||||
}, [setSettingOption]);
|
||||
|
||||
const handleCanAutoPlayVideosChange = useCallback((value: boolean) => {
|
||||
setSettingOption({ canAutoPlayVideos: value });
|
||||
}, [setSettingOption]);
|
||||
|
||||
function renderContentSizeSlider() {
|
||||
const value = AUTODOWNLOAD_FILESIZE_MB_LIMITS.indexOf(autoLoadFileMaxSizeMb);
|
||||
|
||||
@ -101,21 +109,26 @@ const SettingsDataStorage: FC<OwnProps & StateProps> = ({
|
||||
<Checkbox
|
||||
label={lang('AutoDownloadSettings.Contacts')}
|
||||
checked={canAutoLoadFromContacts}
|
||||
// TODO rewrite to support `useCallback`
|
||||
// eslint-disable-next-line react/jsx-no-bind
|
||||
onCheck={(isChecked) => setSettingOption({ [`canAutoLoad${key}FromContacts`]: isChecked })}
|
||||
/>
|
||||
<Checkbox
|
||||
label={lang('AutoDownloadSettings.PrivateChats')}
|
||||
checked={canAutoLoadInPrivateChats}
|
||||
// eslint-disable-next-line react/jsx-no-bind
|
||||
onCheck={(isChecked) => setSettingOption({ [`canAutoLoad${key}InPrivateChats`]: isChecked })}
|
||||
/>
|
||||
<Checkbox
|
||||
label={lang('AutoDownloadSettings.GroupChats')}
|
||||
checked={canAutoLoadInGroups}
|
||||
// eslint-disable-next-line react/jsx-no-bind
|
||||
onCheck={(isChecked) => setSettingOption({ [`canAutoLoad${key}InGroups`]: isChecked })}
|
||||
/>
|
||||
<Checkbox
|
||||
label={lang('AutoDownloadSettings.Channels')}
|
||||
checked={canAutoLoadInChannels}
|
||||
// eslint-disable-next-line react/jsx-no-bind
|
||||
onCheck={(isChecked) => setSettingOption({ [`canAutoLoad${key}InChannels`]: isChecked })}
|
||||
/>
|
||||
|
||||
@ -157,12 +170,12 @@ const SettingsDataStorage: FC<OwnProps & StateProps> = ({
|
||||
<Checkbox
|
||||
label={lang('GifsTab2')}
|
||||
checked={canAutoPlayGifs}
|
||||
onCheck={(isChecked) => setSettingOption({ canAutoPlayGifs: isChecked })}
|
||||
onCheck={handleCanAutoPlayGifsChange}
|
||||
/>
|
||||
<Checkbox
|
||||
label={lang('DataAndStorage.Autoplay.Videos')}
|
||||
checked={canAutoPlayVideos}
|
||||
onCheck={(isChecked) => setSettingOption({ canAutoPlayVideos: isChecked })}
|
||||
onCheck={handleCanAutoPlayVideosChange}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@ -122,6 +122,18 @@ const SettingsGeneral: FC<OwnProps & StateProps> = ({
|
||||
openModal();
|
||||
}, [openModal]);
|
||||
|
||||
const handleMessageSendComboChange = useCallback((newCombo: string) => {
|
||||
setSettingOption({ messageSendKeyCombo: newCombo });
|
||||
}, [setSettingOption]);
|
||||
|
||||
const handleSuggestStickersChange = useCallback((newValue: boolean) => {
|
||||
setSettingOption({ shouldSuggestStickers: newValue });
|
||||
}, [setSettingOption]);
|
||||
|
||||
const handleShouldLoopStickersChange = useCallback((newValue: boolean) => {
|
||||
setSettingOption({ shouldLoopStickers: newValue });
|
||||
}, [setSettingOption]);
|
||||
|
||||
const stickerSets = stickerSetIds && stickerSetIds.map((id: string) => {
|
||||
return stickerSetsById?.[id]?.installedDate ? stickerSetsById[id] : false;
|
||||
}).filter<ApiStickerSet>(Boolean as any);
|
||||
@ -143,6 +155,7 @@ const SettingsGeneral: FC<OwnProps & StateProps> = ({
|
||||
|
||||
<ListItem
|
||||
icon="photo"
|
||||
// eslint-disable-next-line react/jsx-no-bind
|
||||
onClick={() => onScreenSelect(SettingsScreens.GeneralChatBackground)}
|
||||
>
|
||||
{lang('ChatBackground')}
|
||||
@ -183,7 +196,7 @@ const SettingsGeneral: FC<OwnProps & StateProps> = ({
|
||||
<RadioGroup
|
||||
name="keyboard-send-settings"
|
||||
options={KEYBOARD_SEND_OPTIONS}
|
||||
onChange={(value) => setSettingOption({ messageSendKeyCombo: value })}
|
||||
onChange={handleMessageSendComboChange}
|
||||
selected={messageSendKeyCombo}
|
||||
/>
|
||||
</div>
|
||||
@ -195,6 +208,7 @@ const SettingsGeneral: FC<OwnProps & StateProps> = ({
|
||||
{defaultReaction && (
|
||||
<ListItem
|
||||
className="SettingsDefaultReaction"
|
||||
// eslint-disable-next-line react/jsx-no-bind
|
||||
onClick={() => onScreenSelect(SettingsScreens.QuickReaction)}
|
||||
>
|
||||
<ReactionStaticEmoji reaction={defaultReaction} />
|
||||
@ -205,12 +219,12 @@ const SettingsGeneral: FC<OwnProps & StateProps> = ({
|
||||
<Checkbox
|
||||
label={lang('SuggestStickers')}
|
||||
checked={shouldSuggestStickers}
|
||||
onCheck={(isChecked) => setSettingOption({ shouldSuggestStickers: isChecked })}
|
||||
onCheck={handleSuggestStickersChange}
|
||||
/>
|
||||
<Checkbox
|
||||
label={lang('LoopAnimatedStickers')}
|
||||
checked={shouldLoopStickers}
|
||||
onCheck={(isChecked) => setSettingOption({ shouldLoopStickers: isChecked })}
|
||||
onCheck={handleShouldLoopStickersChange}
|
||||
/>
|
||||
|
||||
<div className="mt-4" ref={stickerSettingsRef}>
|
||||
|
||||
@ -215,6 +215,7 @@ const SettingsHeader: FC<OwnProps> = ({
|
||||
ripple={!IS_SINGLE_COLUMN_LAYOUT}
|
||||
size="smaller"
|
||||
color="translucent"
|
||||
// eslint-disable-next-line react/jsx-no-bind
|
||||
onClick={() => onScreenSelect(SettingsScreens.EditProfile)}
|
||||
ariaLabel={lang('lng_settings_information')}
|
||||
>
|
||||
|
||||
@ -60,36 +60,42 @@ const SettingsMain: FC<OwnProps & StateProps> = ({
|
||||
)}
|
||||
<ListItem
|
||||
icon="settings"
|
||||
// eslint-disable-next-line react/jsx-no-bind
|
||||
onClick={() => onScreenSelect(SettingsScreens.General)}
|
||||
>
|
||||
{lang('Telegram.GeneralSettingsViewController')}
|
||||
</ListItem>
|
||||
<ListItem
|
||||
icon="unmute"
|
||||
// eslint-disable-next-line react/jsx-no-bind
|
||||
onClick={() => onScreenSelect(SettingsScreens.Notifications)}
|
||||
>
|
||||
{lang('Notifications')}
|
||||
</ListItem>
|
||||
<ListItem
|
||||
icon="lock"
|
||||
// eslint-disable-next-line react/jsx-no-bind
|
||||
onClick={() => onScreenSelect(SettingsScreens.Privacy)}
|
||||
>
|
||||
{lang('PrivacySettings')}
|
||||
</ListItem>
|
||||
<ListItem
|
||||
icon="data"
|
||||
// eslint-disable-next-line react/jsx-no-bind
|
||||
onClick={() => onScreenSelect(SettingsScreens.DataStorage)}
|
||||
>
|
||||
{lang('DataSettings')}
|
||||
</ListItem>
|
||||
<ListItem
|
||||
icon="folder"
|
||||
// eslint-disable-next-line react/jsx-no-bind
|
||||
onClick={() => onScreenSelect(SettingsScreens.Folders)}
|
||||
>
|
||||
{lang('Filters')}
|
||||
</ListItem>
|
||||
<ListItem
|
||||
icon="language"
|
||||
// eslint-disable-next-line react/jsx-no-bind
|
||||
onClick={() => onScreenSelect(SettingsScreens.Language)}
|
||||
>
|
||||
{lang('Language')}
|
||||
|
||||
@ -85,12 +85,55 @@ const SettingsNotifications: FC<OwnProps & StateProps> = ({
|
||||
updateNotificationSettings,
|
||||
]);
|
||||
|
||||
const handleWebNotificationsChange = useCallback((e: ChangeEvent<HTMLInputElement>) => {
|
||||
updateWebNotificationSettings({
|
||||
hasWebNotifications: e.target.checked,
|
||||
});
|
||||
}, [updateWebNotificationSettings]);
|
||||
|
||||
const handlePushNotificationsChange = useCallback((e: ChangeEvent<HTMLInputElement>) => {
|
||||
updateWebNotificationSettings({
|
||||
hasPushNotifications: e.target.checked,
|
||||
});
|
||||
}, [updateWebNotificationSettings]);
|
||||
|
||||
const handlePrivateChatsNotificationsChange = useCallback((e: ChangeEvent<HTMLInputElement>) => {
|
||||
handleSettingsChange(e, 'contact', 'silent');
|
||||
}, [handleSettingsChange]);
|
||||
|
||||
const handlePrivateChatsPreviewChange = useCallback((e: ChangeEvent<HTMLInputElement>) => {
|
||||
handleSettingsChange(e, 'contact', 'showPreviews');
|
||||
}, [handleSettingsChange]);
|
||||
|
||||
const handleGroupsNotificationsChange = useCallback((e: ChangeEvent<HTMLInputElement>) => {
|
||||
handleSettingsChange(e, 'group', 'silent');
|
||||
}, [handleSettingsChange]);
|
||||
|
||||
const handleGroupsPreviewChange = useCallback((e: ChangeEvent<HTMLInputElement>) => {
|
||||
handleSettingsChange(e, 'group', 'showPreviews');
|
||||
}, [handleSettingsChange]);
|
||||
|
||||
const handleChannelsNotificationsChange = useCallback((e: ChangeEvent<HTMLInputElement>) => {
|
||||
handleSettingsChange(e, 'broadcast', 'silent');
|
||||
}, [handleSettingsChange]);
|
||||
|
||||
const handleChannelsPreviewChange = useCallback((e: ChangeEvent<HTMLInputElement>) => {
|
||||
handleSettingsChange(e, 'broadcast', 'showPreviews');
|
||||
}, [handleSettingsChange]);
|
||||
|
||||
const handleContactNotificationChange = useCallback((e: ChangeEvent<HTMLInputElement>) => {
|
||||
updateContactSignUpNotification({
|
||||
isSilent: !e.target.checked,
|
||||
});
|
||||
}, [updateContactSignUpNotification]);
|
||||
|
||||
const handleVolumeChange = useCallback((volume: number) => {
|
||||
updateWebNotificationSettings({
|
||||
notificationSoundVolume: volume,
|
||||
});
|
||||
runDebounced(() => playNotifySound(undefined, volume));
|
||||
}, [runDebounced, updateWebNotificationSettings]);
|
||||
|
||||
const lang = useLang();
|
||||
|
||||
useHistoryBack(isActive, onReset, onScreenSelect, SettingsScreens.Notifications);
|
||||
@ -106,9 +149,7 @@ const SettingsNotifications: FC<OwnProps & StateProps> = ({
|
||||
// eslint-disable-next-line max-len
|
||||
subLabel={lang(hasWebNotifications ? 'UserInfo.NotificationsEnabled' : 'UserInfo.NotificationsDisabled')}
|
||||
checked={hasWebNotifications}
|
||||
onChange={(e) => {
|
||||
updateWebNotificationSettings({ hasWebNotifications: e.target.checked });
|
||||
}}
|
||||
onChange={handleWebNotificationsChange}
|
||||
/>
|
||||
<Checkbox
|
||||
label="Offline notifications"
|
||||
@ -116,9 +157,7 @@ const SettingsNotifications: FC<OwnProps & StateProps> = ({
|
||||
// eslint-disable-next-line max-len
|
||||
subLabel={lang(hasPushNotifications ? 'UserInfo.NotificationsEnabled' : 'UserInfo.NotificationsDisabled')}
|
||||
checked={hasPushNotifications}
|
||||
onChange={(e) => {
|
||||
updateWebNotificationSettings({ hasPushNotifications: e.target.checked });
|
||||
}}
|
||||
onChange={handlePushNotificationsChange}
|
||||
/>
|
||||
<div className="settings-item-slider">
|
||||
<RangeSlider
|
||||
@ -126,10 +165,7 @@ const SettingsNotifications: FC<OwnProps & StateProps> = ({
|
||||
min={0}
|
||||
max={10}
|
||||
value={notificationSoundVolume}
|
||||
onChange={(volume) => {
|
||||
updateWebNotificationSettings({ notificationSoundVolume: volume });
|
||||
runDebounced(() => playNotifySound(undefined, volume));
|
||||
}}
|
||||
onChange={handleVolumeChange}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
@ -143,9 +179,7 @@ const SettingsNotifications: FC<OwnProps & StateProps> = ({
|
||||
// eslint-disable-next-line max-len
|
||||
subLabel={lang(hasPrivateChatsNotifications ? 'UserInfo.NotificationsEnabled' : 'UserInfo.NotificationsDisabled')}
|
||||
checked={hasPrivateChatsNotifications}
|
||||
onChange={(e) => {
|
||||
handleSettingsChange(e, 'contact', 'silent');
|
||||
}}
|
||||
onChange={handlePrivateChatsNotificationsChange}
|
||||
/>
|
||||
<Checkbox
|
||||
label={lang('MessagePreview')}
|
||||
@ -153,9 +187,7 @@ const SettingsNotifications: FC<OwnProps & StateProps> = ({
|
||||
// eslint-disable-next-line max-len
|
||||
subLabel={lang(hasPrivateChatsMessagePreview ? 'UserInfo.NotificationsEnabled' : 'UserInfo.NotificationsDisabled')}
|
||||
checked={hasPrivateChatsMessagePreview}
|
||||
onChange={(e) => {
|
||||
handleSettingsChange(e, 'contact', 'showPreviews');
|
||||
}}
|
||||
onChange={handlePrivateChatsPreviewChange}
|
||||
/>
|
||||
</div>
|
||||
|
||||
@ -166,18 +198,14 @@ const SettingsNotifications: FC<OwnProps & StateProps> = ({
|
||||
label={lang('NotificationsForGroups')}
|
||||
subLabel={lang(hasGroupNotifications ? 'UserInfo.NotificationsEnabled' : 'UserInfo.NotificationsDisabled')}
|
||||
checked={hasGroupNotifications}
|
||||
onChange={(e) => {
|
||||
handleSettingsChange(e, 'group', 'silent');
|
||||
}}
|
||||
onChange={handleGroupsNotificationsChange}
|
||||
/>
|
||||
<Checkbox
|
||||
label={lang('MessagePreview')}
|
||||
disabled={!hasGroupNotifications}
|
||||
subLabel={lang(hasGroupMessagePreview ? 'UserInfo.NotificationsEnabled' : 'UserInfo.NotificationsDisabled')}
|
||||
checked={hasGroupMessagePreview}
|
||||
onChange={(e) => {
|
||||
handleSettingsChange(e, 'group', 'showPreviews');
|
||||
}}
|
||||
onChange={handleGroupsPreviewChange}
|
||||
/>
|
||||
</div>
|
||||
|
||||
@ -189,9 +217,7 @@ const SettingsNotifications: FC<OwnProps & StateProps> = ({
|
||||
// eslint-disable-next-line max-len
|
||||
subLabel={lang(hasBroadcastNotifications ? 'UserInfo.NotificationsEnabled' : 'UserInfo.NotificationsDisabled')}
|
||||
checked={hasBroadcastNotifications}
|
||||
onChange={(e) => {
|
||||
handleSettingsChange(e, 'broadcast', 'silent');
|
||||
}}
|
||||
onChange={handleChannelsNotificationsChange}
|
||||
/>
|
||||
<Checkbox
|
||||
label={lang('MessagePreview')}
|
||||
@ -199,9 +225,7 @@ const SettingsNotifications: FC<OwnProps & StateProps> = ({
|
||||
// eslint-disable-next-line max-len
|
||||
subLabel={lang(hasBroadcastMessagePreview ? 'UserInfo.NotificationsEnabled' : 'UserInfo.NotificationsDisabled')}
|
||||
checked={hasBroadcastMessagePreview}
|
||||
onChange={(e) => {
|
||||
handleSettingsChange(e, 'broadcast', 'showPreviews');
|
||||
}}
|
||||
onChange={handleChannelsPreviewChange}
|
||||
/>
|
||||
</div>
|
||||
|
||||
|
||||
@ -84,6 +84,7 @@ const SettingsPrivacy: FC<OwnProps & StateProps> = ({
|
||||
<ListItem
|
||||
icon="delete-user"
|
||||
narrow
|
||||
// eslint-disable-next-line react/jsx-no-bind
|
||||
onClick={() => onScreenSelect(SettingsScreens.PrivacyBlockedUsers)}
|
||||
>
|
||||
<div className="multiline-menu-item">
|
||||
@ -98,6 +99,7 @@ const SettingsPrivacy: FC<OwnProps & StateProps> = ({
|
||||
<ListItem
|
||||
icon="lock"
|
||||
narrow
|
||||
// eslint-disable-next-line react/jsx-no-bind
|
||||
onClick={() => onScreenSelect(
|
||||
hasPassword ? SettingsScreens.TwoFaEnabled : SettingsScreens.TwoFaDisabled,
|
||||
)}
|
||||
@ -112,6 +114,7 @@ const SettingsPrivacy: FC<OwnProps & StateProps> = ({
|
||||
<ListItem
|
||||
icon="active-sessions"
|
||||
narrow
|
||||
// eslint-disable-next-line react/jsx-no-bind
|
||||
onClick={() => onScreenSelect(SettingsScreens.PrivacyActiveSessions)}
|
||||
>
|
||||
<div className="multiline-menu-item">
|
||||
@ -131,6 +134,7 @@ const SettingsPrivacy: FC<OwnProps & StateProps> = ({
|
||||
<ListItem
|
||||
narrow
|
||||
className="no-icon"
|
||||
// eslint-disable-next-line react/jsx-no-bind
|
||||
onClick={() => onScreenSelect(SettingsScreens.PrivacyPhoneNumber)}
|
||||
>
|
||||
<div className="multiline-menu-item">
|
||||
@ -143,6 +147,7 @@ const SettingsPrivacy: FC<OwnProps & StateProps> = ({
|
||||
<ListItem
|
||||
narrow
|
||||
className="no-icon"
|
||||
// eslint-disable-next-line react/jsx-no-bind
|
||||
onClick={() => onScreenSelect(SettingsScreens.PrivacyLastSeen)}
|
||||
>
|
||||
<div className="multiline-menu-item">
|
||||
@ -155,6 +160,7 @@ const SettingsPrivacy: FC<OwnProps & StateProps> = ({
|
||||
<ListItem
|
||||
narrow
|
||||
className="no-icon"
|
||||
// eslint-disable-next-line react/jsx-no-bind
|
||||
onClick={() => onScreenSelect(SettingsScreens.PrivacyProfilePhoto)}
|
||||
>
|
||||
<div className="multiline-menu-item">
|
||||
@ -167,6 +173,7 @@ const SettingsPrivacy: FC<OwnProps & StateProps> = ({
|
||||
<ListItem
|
||||
narrow
|
||||
className="no-icon"
|
||||
// eslint-disable-next-line react/jsx-no-bind
|
||||
onClick={() => onScreenSelect(SettingsScreens.PrivacyForwarding)}
|
||||
>
|
||||
<div className="multiline-menu-item">
|
||||
@ -179,6 +186,7 @@ const SettingsPrivacy: FC<OwnProps & StateProps> = ({
|
||||
<ListItem
|
||||
narrow
|
||||
className="no-icon"
|
||||
// eslint-disable-next-line react/jsx-no-bind
|
||||
onClick={() => onScreenSelect(SettingsScreens.PrivacyGroupChats)}
|
||||
>
|
||||
<div className="multiline-menu-item">
|
||||
|
||||
@ -176,6 +176,7 @@ const SettingsPrivacyVisibility: FC<OwnProps & StateProps> = ({
|
||||
<ListItem
|
||||
narrow
|
||||
icon="add-user"
|
||||
// eslint-disable-next-line react/jsx-no-bind
|
||||
onClick={() => {
|
||||
onScreenSelect(allowedContactsScreen);
|
||||
}}
|
||||
@ -191,6 +192,7 @@ const SettingsPrivacyVisibility: FC<OwnProps & StateProps> = ({
|
||||
<ListItem
|
||||
narrow
|
||||
icon="delete-user"
|
||||
// eslint-disable-next-line react/jsx-no-bind
|
||||
onClick={() => {
|
||||
onScreenSelect(deniedContactsScreen);
|
||||
}}
|
||||
|
||||
@ -40,6 +40,7 @@ const SettingsStickerSet: FC<OwnProps> = ({
|
||||
narrow
|
||||
className="SettingsStickerSet"
|
||||
inactive={!firstSticker}
|
||||
// eslint-disable-next-line react/jsx-no-bind
|
||||
onClick={() => firstSticker && onClick(firstSticker)}
|
||||
>
|
||||
<Button
|
||||
@ -71,6 +72,7 @@ const SettingsStickerSet: FC<OwnProps> = ({
|
||||
<ListItem
|
||||
narrow
|
||||
className="SettingsStickerSet"
|
||||
// eslint-disable-next-line react/jsx-no-bind
|
||||
onClick={() => onClick(firstSticker)}
|
||||
>
|
||||
<StickerButton
|
||||
|
||||
@ -115,6 +115,7 @@ const SettingsFoldersChatsPicker: FC<OwnProps> = ({
|
||||
<ListItem
|
||||
key={type.key}
|
||||
className="chat-item-clickable picker-list-item chat-type-item"
|
||||
// eslint-disable-next-line react/jsx-no-bind
|
||||
onClick={() => handleChatTypeClick(type.key)}
|
||||
ripple
|
||||
>
|
||||
@ -136,6 +137,7 @@ const SettingsFoldersChatsPicker: FC<OwnProps> = ({
|
||||
<ListItem
|
||||
key={id}
|
||||
className="chat-item-clickable picker-list-item chat-item"
|
||||
// eslint-disable-next-line react/jsx-no-bind
|
||||
onClick={() => handleItemClick(id)}
|
||||
ripple
|
||||
disabled={!isSelected && hasMaxChats}
|
||||
|
||||
@ -124,12 +124,12 @@ const SettingsFoldersEdit: FC<OwnProps & StateProps> = ({
|
||||
? SettingsScreens.FoldersEditFolder
|
||||
: SettingsScreens.FoldersCreateFolder);
|
||||
|
||||
function handleChange(event: React.ChangeEvent<HTMLInputElement>) {
|
||||
const handleChange = useCallback((event: React.ChangeEvent<HTMLInputElement>) => {
|
||||
const { currentTarget } = event;
|
||||
dispatch({ type: 'setTitle', payload: currentTarget.value.trim() });
|
||||
}
|
||||
}, [dispatch]);
|
||||
|
||||
function handleSubmit() {
|
||||
const handleSubmit = useCallback(() => {
|
||||
const { title } = state.folder;
|
||||
|
||||
if (!title) {
|
||||
@ -152,7 +152,7 @@ const SettingsFoldersEdit: FC<OwnProps & StateProps> = ({
|
||||
setTimeout(() => {
|
||||
onReset();
|
||||
}, SUBMIT_TIMEOUT);
|
||||
}
|
||||
}, [addChatFolder, dispatch, editChatFolder, includedChatIds.length, includedChatTypes, onReset, state]);
|
||||
|
||||
function renderChatType(key: string, mode: 'included' | 'excluded') {
|
||||
const chatType = mode === 'included'
|
||||
@ -207,6 +207,7 @@ const SettingsFoldersEdit: FC<OwnProps & StateProps> = ({
|
||||
<ShowMoreButton
|
||||
count={leftChatsCount}
|
||||
itemName="chat"
|
||||
// eslint-disable-next-line react/jsx-no-bind
|
||||
onClick={clickHandler}
|
||||
/>
|
||||
)}
|
||||
|
||||
@ -165,6 +165,7 @@ const SettingsFoldersMain: FC<OwnProps & StateProps> = ({
|
||||
className="mb-2 no-icon"
|
||||
narrow
|
||||
multiline
|
||||
// eslint-disable-next-line react/jsx-no-bind
|
||||
onClick={() => onEditFolder(foldersById[folder.id])}
|
||||
>
|
||||
<span className="title">{folder.title}</span>
|
||||
@ -187,6 +188,7 @@ const SettingsFoldersMain: FC<OwnProps & StateProps> = ({
|
||||
<ListItem
|
||||
className="mb-2"
|
||||
narrow
|
||||
// eslint-disable-next-line react/jsx-no-bind
|
||||
onClick={() => handleCreateFolderFromRecommended(folder)}
|
||||
>
|
||||
<div className="settings-folders-recommended-item">
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
import React, { FC, memo } from '../../../../lib/teact/teact';
|
||||
import React, { FC, memo, useCallback } from '../../../../lib/teact/teact';
|
||||
import { withGlobal } from '../../../../global';
|
||||
|
||||
import { ApiSticker } from '../../../../api/types';
|
||||
@ -26,9 +26,9 @@ const SettingsTwoFaCongratulations: FC<OwnProps & StateProps> = ({
|
||||
}) => {
|
||||
const lang = useLang();
|
||||
|
||||
const handleClick = () => {
|
||||
const handleClick = useCallback(() => {
|
||||
onScreenSelect(SettingsScreens.Privacy);
|
||||
};
|
||||
}, [onScreenSelect]);
|
||||
|
||||
useHistoryBack(isActive, onReset, onScreenSelect, SettingsScreens.TwoFaCongratulations);
|
||||
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
import React, {
|
||||
FC, memo, useEffect, useRef, useState,
|
||||
FC, memo, useCallback, useEffect, useRef, useState,
|
||||
} from '../../../../lib/teact/teact';
|
||||
import { withGlobal } from '../../../../global';
|
||||
|
||||
@ -62,7 +62,7 @@ const SettingsTwoFaEmailCode: FC<OwnProps & StateProps> = ({
|
||||
|
||||
useHistoryBack(isActive, onReset, onScreenSelect, screen);
|
||||
|
||||
const handleInputChange = (e: React.ChangeEvent<HTMLInputElement>) => {
|
||||
const handleInputChange = useCallback((e: React.ChangeEvent<HTMLInputElement>) => {
|
||||
if (error && clearError) {
|
||||
clearError();
|
||||
}
|
||||
@ -75,7 +75,7 @@ const SettingsTwoFaEmailCode: FC<OwnProps & StateProps> = ({
|
||||
|
||||
setValue(newValue);
|
||||
e.target.value = newValue;
|
||||
};
|
||||
}, [clearError, codeLength, error, onSubmit]);
|
||||
|
||||
return (
|
||||
<div className="settings-content two-fa custom-scroll">
|
||||
|
||||
@ -42,18 +42,21 @@ const SettingsTwoFaEnabled: FC<OwnProps & StateProps> = ({
|
||||
<div className="settings-item pt-0 no-border">
|
||||
<ListItem
|
||||
icon="edit"
|
||||
// eslint-disable-next-line react/jsx-no-bind
|
||||
onClick={() => onScreenSelect(SettingsScreens.TwoFaChangePasswordCurrent)}
|
||||
>
|
||||
{lang('ChangePassword')}
|
||||
</ListItem>
|
||||
<ListItem
|
||||
icon="password-off"
|
||||
// eslint-disable-next-line react/jsx-no-bind
|
||||
onClick={() => onScreenSelect(SettingsScreens.TwoFaTurnOff)}
|
||||
>
|
||||
{lang('TurnPasswordOff')}
|
||||
</ListItem>
|
||||
<ListItem
|
||||
icon="email"
|
||||
// eslint-disable-next-line react/jsx-no-bind
|
||||
onClick={() => onScreenSelect(SettingsScreens.TwoFaRecoveryEmailCurrentPassword)}
|
||||
>
|
||||
{lang('SetRecoveryEmail')}
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
import React, {
|
||||
FC, memo, useEffect, useRef, useState,
|
||||
FC, memo, useCallback, useEffect, useRef, useState,
|
||||
} from '../../../../lib/teact/teact';
|
||||
import { withGlobal } from '../../../../global';
|
||||
|
||||
@ -67,13 +67,13 @@ const SettingsTwoFaSkippableForm: FC<OwnProps & StateProps> = ({
|
||||
}
|
||||
}, []);
|
||||
|
||||
const handleInputChange = (e: React.ChangeEvent<HTMLInputElement>) => {
|
||||
const handleInputChange = useCallback((e: React.ChangeEvent<HTMLInputElement>) => {
|
||||
if (error && clearError) {
|
||||
clearError();
|
||||
}
|
||||
|
||||
setValue(e.target.value);
|
||||
};
|
||||
}, [clearError, error]);
|
||||
|
||||
const handleSubmit = (e: React.FormEvent<HTMLFormElement>) => {
|
||||
e.preventDefault();
|
||||
@ -85,14 +85,14 @@ const SettingsTwoFaSkippableForm: FC<OwnProps & StateProps> = ({
|
||||
onSubmit(value);
|
||||
};
|
||||
|
||||
const handleSkip = () => {
|
||||
const handleSkip = useCallback(() => {
|
||||
onSubmit();
|
||||
};
|
||||
}, [onSubmit]);
|
||||
|
||||
const handleSkipConfirm = () => {
|
||||
const handleSkipConfirm = useCallback(() => {
|
||||
unmarkIsConfirmShown();
|
||||
onSubmit();
|
||||
};
|
||||
}, [onSubmit, unmarkIsConfirmShown]);
|
||||
|
||||
const lang = useLang();
|
||||
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
import React, { FC } from '../../lib/teact/teact';
|
||||
import React, { FC, useCallback } from '../../lib/teact/teact';
|
||||
|
||||
import Button from '../ui/Button';
|
||||
|
||||
@ -6,9 +6,9 @@ import appInactivePath from '../../assets/app-inactive.png';
|
||||
import './AppInactive.scss';
|
||||
|
||||
const AppInactive: FC = () => {
|
||||
const handleReload = () => {
|
||||
const handleReload = useCallback(() => {
|
||||
window.location.reload();
|
||||
};
|
||||
}, []);
|
||||
|
||||
return (
|
||||
<div id="AppInactive">
|
||||
@ -21,7 +21,9 @@ const AppInactive: FC = () => {
|
||||
Please reload this page to continue using this tab or close it.
|
||||
</div>
|
||||
<div className="actions">
|
||||
<Button isText ripple onClick={handleReload}>Reload app</Button>
|
||||
<Button isText ripple onClick={handleReload}>
|
||||
Reload app
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@ -96,7 +96,12 @@ const Dialogs: FC<StateProps> = ({ dialogs }) => {
|
||||
: lang('MemberRequests.RequestToJoinDescriptionGroup')}
|
||||
</p>
|
||||
)}
|
||||
<Button isText className="confirm-dialog-button" onClick={handleJoinClick}>
|
||||
<Button
|
||||
isText
|
||||
className="confirm-dialog-button"
|
||||
// eslint-disable-next-line react/jsx-no-bind
|
||||
onClick={handleJoinClick}
|
||||
>
|
||||
{isRequestNeeded ? requestToJoinText : joinText}
|
||||
</Button>
|
||||
<Button isText className="confirm-dialog-button" onClick={closeModal}>{lang('Cancel')}</Button>
|
||||
@ -122,7 +127,14 @@ const Dialogs: FC<StateProps> = ({ dialogs }) => {
|
||||
>
|
||||
{lang('AreYouSureShareMyContactInfoBot')}
|
||||
<div>
|
||||
<Button className="confirm-dialog-button" isText onClick={handleConfirm}>{lang('OK')}</Button>
|
||||
<Button
|
||||
className="confirm-dialog-button"
|
||||
isText
|
||||
// eslint-disable-next-line react/jsx-no-bind
|
||||
onClick={handleConfirm}
|
||||
>
|
||||
{lang('OK')}
|
||||
</Button>
|
||||
<Button className="confirm-dialog-button" isText onClick={closeModal}>{lang('Cancel')}</Button>
|
||||
</div>
|
||||
</Modal>
|
||||
|
||||
@ -24,6 +24,7 @@ const Notifications: FC<StateProps> = ({ notifications }) => {
|
||||
{notifications.map(({ message, localId }) => (
|
||||
<Notification
|
||||
message={renderText(message, ['emoji', 'br', 'links', 'simple_markdown'])}
|
||||
// eslint-disable-next-line react/jsx-no-bind
|
||||
onDismiss={() => dismissNotification({ localId })}
|
||||
/>
|
||||
))}
|
||||
|
||||
@ -220,6 +220,7 @@ const VideoPlayerControls: FC<OwnProps> = ({
|
||||
onClose={closePlaybackMenu}
|
||||
>
|
||||
{PLAYBACK_RATES.map((rate) => (
|
||||
// eslint-disable-next-line react/jsx-no-bind
|
||||
<MenuItem disabled={playbackRate === rate} onClick={() => onPlaybackRateChange(rate)}>
|
||||
{`${rate}x`}
|
||||
</MenuItem>
|
||||
|
||||
@ -36,17 +36,17 @@ const ZoomControls: FC<OwnProps> = ({ isShown, onChangeZoom }) => {
|
||||
}
|
||||
}, [isShown, prevIsShown]);
|
||||
|
||||
const handleZoomOut = () => {
|
||||
const handleZoomOut = useCallback(() => {
|
||||
if (inputRef.current) {
|
||||
setZoomLevel(Math.max(MIN_ZOOM_LEVEL, zoomLevel - 0.5));
|
||||
}
|
||||
};
|
||||
}, [zoomLevel]);
|
||||
|
||||
const handleZoomIn = () => {
|
||||
const handleZoomIn = useCallback(() => {
|
||||
if (inputRef.current) {
|
||||
setZoomLevel(Math.min(MAX_ZOOM_LEVEL, zoomLevel + 0.5));
|
||||
}
|
||||
};
|
||||
}, [zoomLevel]);
|
||||
|
||||
const handleStartSeek = useCallback(() => {
|
||||
isSeeking.current = true;
|
||||
|
||||
@ -166,6 +166,7 @@ const MobileSearchFooter: FC<StateProps> = ({
|
||||
round
|
||||
size="smaller"
|
||||
color="translucent"
|
||||
// eslint-disable-next-line react/jsx-no-bind
|
||||
onClick={() => openHistoryCalendar({ selectedAt: getDayStartAt(Date.now()) })}
|
||||
ariaLabel="Search messages by date"
|
||||
>
|
||||
|
||||
@ -124,6 +124,7 @@ const ReactorListModal: FC<OwnProps & StateProps> = ({
|
||||
className={buildClassName(!chosenTab && 'chosen')}
|
||||
size="tiny"
|
||||
ripple
|
||||
// eslint-disable-next-line react/jsx-no-bind
|
||||
onClick={() => setChosenTab(undefined)}
|
||||
>
|
||||
<i className="icon-reaction-filled" />
|
||||
@ -136,6 +137,7 @@ const ReactorListModal: FC<OwnProps & StateProps> = ({
|
||||
className={buildClassName(chosenTab === reaction && 'chosen')}
|
||||
size="tiny"
|
||||
ripple
|
||||
// eslint-disable-next-line react/jsx-no-bind
|
||||
onClick={() => setChosenTab(reaction)}
|
||||
>
|
||||
<ReactionStaticEmoji reaction={reaction} className="reaction-filter-emoji" />
|
||||
@ -162,6 +164,7 @@ const ReactorListModal: FC<OwnProps & StateProps> = ({
|
||||
<ListItem
|
||||
key={userId}
|
||||
className="chat-item-clickable reactors-list-item"
|
||||
// eslint-disable-next-line react/jsx-no-bind
|
||||
onClick={() => handleClick(userId)}
|
||||
>
|
||||
<Avatar user={user} size="medium" />
|
||||
|
||||
@ -30,6 +30,7 @@ const BotCommand: FC<OwnProps> = ({
|
||||
key={botCommand.command}
|
||||
className={buildClassName('BotCommand chat-item-clickable scroll-item', withAvatar && 'with-avatar')}
|
||||
multiline
|
||||
// eslint-disable-next-line react/jsx-no-bind
|
||||
onClick={() => onClick(botCommand)}
|
||||
focus={focus}
|
||||
>
|
||||
|
||||
@ -1,4 +1,6 @@
|
||||
import React, { FC, memo, useEffect } from '../../../lib/teact/teact';
|
||||
import React, {
|
||||
FC, memo, useCallback, useEffect,
|
||||
} from '../../../lib/teact/teact';
|
||||
import { getActions, withGlobal } from '../../../global';
|
||||
|
||||
import { ApiMessage } from '../../../api/types';
|
||||
@ -32,10 +34,10 @@ const BotKeyboardMenu: FC<OwnProps & StateProps> = ({
|
||||
const { isKeyboardSingleUse } = message || {};
|
||||
const [forceOpen, markForceOpen, unmarkForceOpen] = useFlag(true);
|
||||
|
||||
const handleClose = () => {
|
||||
const handleClose = useCallback(() => {
|
||||
unmarkForceOpen();
|
||||
onClose();
|
||||
};
|
||||
}, [onClose, unmarkForceOpen]);
|
||||
|
||||
useEffect(() => {
|
||||
markForceOpen();
|
||||
@ -65,6 +67,7 @@ const BotKeyboardMenu: FC<OwnProps & StateProps> = ({
|
||||
<Button
|
||||
ripple
|
||||
disabled={button.type === 'NOT_SUPPORTED'}
|
||||
// eslint-disable-next-line react/jsx-no-bind
|
||||
onClick={() => clickInlineButton({ button })}
|
||||
>
|
||||
{button.text}
|
||||
|
||||
@ -174,6 +174,7 @@ const EmojiPicker: FC<OwnProps & StateProps> = ({
|
||||
round
|
||||
faded
|
||||
color="translucent"
|
||||
// eslint-disable-next-line react/jsx-no-bind
|
||||
onClick={() => selectCategory(index)}
|
||||
ariaLabel={category.name}
|
||||
>
|
||||
|
||||
@ -92,6 +92,7 @@ const MentionTooltip: FC<OwnProps> = ({
|
||||
<ListItem
|
||||
key={id}
|
||||
className="chat-item-clickable scroll-item"
|
||||
// eslint-disable-next-line react/jsx-no-bind
|
||||
onClick={() => handleUserSelect(id)}
|
||||
focus={selectedMentionIndex === index}
|
||||
>
|
||||
|
||||
@ -209,6 +209,10 @@ const PollModal: FC<OwnProps> = ({
|
||||
}
|
||||
}, [handleCreate]);
|
||||
|
||||
const handleQuestionChange = useCallback((e: ChangeEvent<HTMLInputElement>) => {
|
||||
setQuestion(e.target.value);
|
||||
}, []);
|
||||
|
||||
const getQuestionError = useCallback(() => {
|
||||
if (hasErrors && !question.trim().length) {
|
||||
return lang('lng_polls_choose_question');
|
||||
@ -253,6 +257,7 @@ const PollModal: FC<OwnProps> = ({
|
||||
: lang('CreatePoll.AddOption')}
|
||||
error={getOptionsError(index)}
|
||||
value={option}
|
||||
// eslint-disable-next-line react/jsx-no-bind
|
||||
onChange={(e) => updateOption(index, e.currentTarget.value)}
|
||||
onKeyPress={handleKeyPress}
|
||||
/>
|
||||
@ -263,6 +268,7 @@ const PollModal: FC<OwnProps> = ({
|
||||
color="translucent"
|
||||
size="smaller"
|
||||
ariaLabel={lang('Delete')}
|
||||
// eslint-disable-next-line react/jsx-no-bind
|
||||
onClick={() => removeOption(index)}
|
||||
>
|
||||
<i className="icon-close" />
|
||||
@ -292,7 +298,7 @@ const PollModal: FC<OwnProps> = ({
|
||||
label={lang('AskAQuestion')}
|
||||
value={question}
|
||||
error={getQuestionError()}
|
||||
onChange={(e) => setQuestion(e.currentTarget.value)}
|
||||
onChange={handleQuestionChange}
|
||||
onKeyPress={handleKeyPress}
|
||||
/>
|
||||
<div className="options-divider" />
|
||||
|
||||
@ -98,6 +98,7 @@ const SendAsMenu: FC<OwnProps> = ({
|
||||
<ListItem
|
||||
key={id}
|
||||
className="SendAsItem chat-item-clickable scroll-item with-avatar"
|
||||
// eslint-disable-next-line react/jsx-no-bind
|
||||
onClick={() => handleUserSelect(id)}
|
||||
focus={selectedSendAsIndex === index}
|
||||
>
|
||||
|
||||
@ -204,6 +204,7 @@ const StickerPicker: FC<OwnProps & StateProps> = ({
|
||||
round
|
||||
faded={stickerSet.id === 'recent' || stickerSet.id === 'favorite'}
|
||||
color="translucent"
|
||||
// eslint-disable-next-line react/jsx-no-bind
|
||||
onClick={() => selectStickerSet(index)}
|
||||
>
|
||||
{stickerSet.id === 'recent' ? (
|
||||
|
||||
@ -37,6 +37,7 @@ const SymbolMenuFooter: FC<OwnProps> = ({
|
||||
return (
|
||||
<Button
|
||||
className={`symbol-tab-button ${activeTab === tab ? 'activated' : ''}`}
|
||||
// eslint-disable-next-line react/jsx-no-bind
|
||||
onClick={() => onSwitchTab(tab)}
|
||||
ariaLabel={SYMBOL_MENU_TAB_TITLES[tab]}
|
||||
round
|
||||
|
||||
@ -109,7 +109,7 @@ const TextFormatter: FC<OwnProps> = ({
|
||||
setSelectedTextFormats(selectedFormats);
|
||||
}, [isOpen, selectedRange, openLinkControl]);
|
||||
|
||||
function restoreSelection() {
|
||||
const restoreSelection = useCallback(() => {
|
||||
if (!selectedRange) {
|
||||
return;
|
||||
}
|
||||
@ -119,7 +119,7 @@ const TextFormatter: FC<OwnProps> = ({
|
||||
selection.removeAllRanges();
|
||||
selection.addRange(selectedRange);
|
||||
}
|
||||
}
|
||||
}, [selectedRange]);
|
||||
|
||||
const updateSelectedRange = useCallback(() => {
|
||||
const selection = window.getSelection();
|
||||
@ -311,7 +311,7 @@ const TextFormatter: FC<OwnProps> = ({
|
||||
getSelectedElement, getSelectedText, onClose, selectedRange, selectedTextFormats.monospace,
|
||||
]);
|
||||
|
||||
function handleLinkUrlConfirm() {
|
||||
const handleLinkUrlConfirm = useCallback(() => {
|
||||
const formattedLinkUrl = encodeURI(ensureProtocol(linkUrl) || '');
|
||||
|
||||
if (isEditingLink) {
|
||||
@ -335,7 +335,7 @@ const TextFormatter: FC<OwnProps> = ({
|
||||
`<a href=${formattedLinkUrl} class="text-entity-link" dir="auto">${text}</a>`,
|
||||
);
|
||||
onClose();
|
||||
}
|
||||
}, [getSelectedElement, getSelectedText, isEditingLink, linkUrl, onClose, restoreSelection]);
|
||||
|
||||
const handleKeyDown = useCallback((e: KeyboardEvent) => {
|
||||
const HANDLERS_BY_KEY: Record<string, AnyToVoidFunction> = {
|
||||
|
||||
@ -1,4 +1,6 @@
|
||||
import React, { FC, memo, useEffect } from '../../../lib/teact/teact';
|
||||
import React, {
|
||||
FC, memo, useCallback, useEffect,
|
||||
} from '../../../lib/teact/teact';
|
||||
import { getActions, withGlobal } from '../../../global';
|
||||
|
||||
import { ApiMessage, ApiMessageEntityTypes, ApiWebPage } from '../../../api/types';
|
||||
@ -84,14 +86,14 @@ const WebPagePreview: FC<OwnProps & StateProps> = ({
|
||||
|
||||
const renderingWebPage = useCurrentOrPrev(webPagePreview, true);
|
||||
|
||||
const handleClearWebpagePreview = useCallback(() => {
|
||||
toggleMessageWebPage({ chatId, threadId, noWebPage: true });
|
||||
}, [chatId, threadId, toggleMessageWebPage]);
|
||||
|
||||
if (!shouldRender || !renderingWebPage) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
const handleClearWebpagePreview = () => {
|
||||
toggleMessageWebPage({ chatId, threadId, noWebPage: true });
|
||||
};
|
||||
|
||||
// TODO Refactor so `WebPage` can be used without message
|
||||
const { photo, ...webPageWithoutPhoto } = renderingWebPage;
|
||||
const messageStub = {
|
||||
|
||||
@ -27,6 +27,7 @@ const InlineButtons: FC<OwnProps> = ({ message, onClick }) => {
|
||||
size="tiny"
|
||||
ripple
|
||||
disabled={button.type === 'NOT_SUPPORTED'}
|
||||
// eslint-disable-next-line react/jsx-no-bind
|
||||
onClick={() => onClick({ button })}
|
||||
>
|
||||
{renderText(lang(button.text))}
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
import { RefObject } from 'react';
|
||||
import React, {
|
||||
FC, memo, useEffect, useRef,
|
||||
FC, memo, useCallback, useEffect, useRef,
|
||||
} from '../../../lib/teact/teact';
|
||||
import { getActions, withGlobal } from '../../../global';
|
||||
|
||||
@ -64,11 +64,8 @@ const SponsoredMessage: FC<OwnProps & StateProps> = ({
|
||||
}) : undefined;
|
||||
}, [chatId, shouldObserve, observeIntersection, viewSponsoredMessage]);
|
||||
|
||||
if (!message) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
const handleClick = () => {
|
||||
const handleClick = useCallback(() => {
|
||||
if (!message) return;
|
||||
if (message.chatInviteHash) {
|
||||
openChatByInvite({ hash: message.chatInviteHash });
|
||||
} else if (message.channelPostId) {
|
||||
@ -83,7 +80,11 @@ const SponsoredMessage: FC<OwnProps & StateProps> = ({
|
||||
});
|
||||
}
|
||||
}
|
||||
};
|
||||
}, [focusMessage, message, openChat, openChatByInvite, startBot]);
|
||||
|
||||
if (!message) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="SponsoredMessage Message open" key="sponsored-message">
|
||||
|
||||
@ -103,6 +103,7 @@ const PollAnswerResults: FC<OwnProps & StateProps> = ({
|
||||
<ListItem
|
||||
key={id}
|
||||
className="chat-item-clickable"
|
||||
// eslint-disable-next-line react/jsx-no-bind
|
||||
onClick={() => handleMemberClick(id)}
|
||||
>
|
||||
<PrivateChatInfo
|
||||
|
||||
@ -406,6 +406,7 @@ const Profile: FC<OwnProps & StateProps> = ({
|
||||
key={id}
|
||||
teactOrderKey={i}
|
||||
className="chat-item-clickable scroll-item small-icon"
|
||||
// eslint-disable-next-line react/jsx-no-bind
|
||||
onClick={() => handleMemberClick(id)}
|
||||
contextActions={getMemberContextAction(id)}
|
||||
>
|
||||
@ -418,6 +419,7 @@ const Profile: FC<OwnProps & StateProps> = ({
|
||||
key={id}
|
||||
teactOrderKey={i}
|
||||
className="chat-item-clickable scroll-item small-icon"
|
||||
// eslint-disable-next-line react/jsx-no-bind
|
||||
onClick={() => openChat({ id })}
|
||||
>
|
||||
<GroupChatInfo chatId={id} />
|
||||
|
||||
@ -269,6 +269,7 @@ const RightHeader: FC<OwnProps & StateProps> = ({
|
||||
round
|
||||
size="smaller"
|
||||
color="translucent"
|
||||
// eslint-disable-next-line react/jsx-no-bind
|
||||
onClick={() => openHistoryCalendar({ selectedAt: getDayStartAt(Date.now()) })}
|
||||
ariaLabel="Search messages by date"
|
||||
>
|
||||
|
||||
@ -120,13 +120,13 @@ const ManageChannel: FC<OwnProps & StateProps> = ({
|
||||
onScreenSelect(ManagementScreens.ChatAdministrators);
|
||||
}, [onScreenSelect]);
|
||||
|
||||
const handleClickInvites = () => {
|
||||
const handleClickInvites = useCallback(() => {
|
||||
onScreenSelect(ManagementScreens.Invites);
|
||||
};
|
||||
}, [onScreenSelect]);
|
||||
|
||||
const handleClickRequests = () => {
|
||||
const handleClickRequests = useCallback(() => {
|
||||
onScreenSelect(ManagementScreens.JoinRequests);
|
||||
};
|
||||
}, [onScreenSelect]);
|
||||
|
||||
const handleSetPhoto = useCallback((file: File) => {
|
||||
setPhoto(file);
|
||||
|
||||
@ -42,9 +42,9 @@ const ManageChatAdministrators: FC<OwnProps & StateProps> = ({
|
||||
|
||||
useHistoryBack(isActive, onClose);
|
||||
|
||||
function handleRecentActionsClick() {
|
||||
const handleRecentActionsClick = useCallback(() => {
|
||||
onScreenSelect(ManagementScreens.GroupRecentActions);
|
||||
}
|
||||
}, [onScreenSelect]);
|
||||
|
||||
const adminMembers = useMemo(() => {
|
||||
if (!chat.fullInfo || !chat.fullInfo.adminMembers) {
|
||||
@ -112,6 +112,7 @@ const ManageChatAdministrators: FC<OwnProps & StateProps> = ({
|
||||
<ListItem
|
||||
key={member.userId}
|
||||
className="chat-item-clickable"
|
||||
// eslint-disable-next-line react/jsx-no-bind
|
||||
onClick={() => handleAdminMemberClick(member)}
|
||||
>
|
||||
<PrivateChatInfo
|
||||
|
||||
@ -210,6 +210,7 @@ const ManageDiscussion: FC<OwnProps & StateProps> = ({
|
||||
key={id}
|
||||
teactOrderKey={i + 1}
|
||||
className="chat-item-clickable scroll-item"
|
||||
// eslint-disable-next-line react/jsx-no-bind
|
||||
onClick={() => {
|
||||
onDiscussionClick(id);
|
||||
}}
|
||||
|
||||
@ -133,13 +133,13 @@ const ManageGroup: FC<OwnProps & StateProps> = ({
|
||||
onScreenSelect(ManagementScreens.ChatAdministrators);
|
||||
}, [onScreenSelect]);
|
||||
|
||||
const handleClickInvites = () => {
|
||||
const handleClickInvites = useCallback(() => {
|
||||
onScreenSelect(ManagementScreens.Invites);
|
||||
};
|
||||
}, [onScreenSelect]);
|
||||
|
||||
const handleClickRequests = () => {
|
||||
const handleClickRequests = useCallback(() => {
|
||||
onScreenSelect(ManagementScreens.JoinRequests);
|
||||
};
|
||||
}, [onScreenSelect]);
|
||||
|
||||
const handleSetPhoto = useCallback((file: File) => {
|
||||
setPhoto(file);
|
||||
|
||||
@ -172,6 +172,7 @@ const ManageGroupMembers: FC<OwnProps & StateProps> = ({
|
||||
<ListItem
|
||||
key={id}
|
||||
className="chat-item-clickable scroll-item"
|
||||
// eslint-disable-next-line react/jsx-no-bind
|
||||
onClick={() => handleMemberClick(id)}
|
||||
>
|
||||
<PrivateChatInfo userId={id} forceShowSelf />
|
||||
|
||||
@ -269,6 +269,7 @@ const ManageGroupPermissions: FC<OwnProps & StateProps> = ({
|
||||
<ListItem
|
||||
key={member.userId}
|
||||
className="chat-item-clickable exceptions-member"
|
||||
// eslint-disable-next-line react/jsx-no-bind
|
||||
onClick={() => handleExceptionMemberClick(member)}
|
||||
>
|
||||
<PrivateChatInfo
|
||||
|
||||
@ -72,6 +72,7 @@ const ManageGroupUserPermissionsCreate: FC<OwnProps & StateProps> = ({
|
||||
key={id}
|
||||
teactOrderKey={i}
|
||||
className="chat-item-clickable scroll-item"
|
||||
// eslint-disable-next-line react/jsx-no-bind
|
||||
onClick={() => handleExceptionMemberClick(id)}
|
||||
>
|
||||
<PrivateChatInfo userId={id} forceShowSelf />
|
||||
|
||||
@ -86,6 +86,7 @@ const ManageInviteInfo: FC<OwnProps & StateProps> = ({
|
||||
{importers.map((importer) => (
|
||||
<ListItem
|
||||
className="chat-item-clickable scroll-item small-icon"
|
||||
// eslint-disable-next-line react/jsx-no-bind
|
||||
onClick={() => openChat({ id: importer.userId })}
|
||||
>
|
||||
<PrivateChatInfo
|
||||
@ -111,6 +112,7 @@ const ManageInviteInfo: FC<OwnProps & StateProps> = ({
|
||||
{requesters.map((requester) => (
|
||||
<ListItem
|
||||
className="chat-item-clickable scroll-item small-icon"
|
||||
// eslint-disable-next-line react/jsx-no-bind
|
||||
onClick={() => openChat({ id: requester.userId })}
|
||||
>
|
||||
<PrivateChatInfo
|
||||
@ -155,6 +157,7 @@ const ManageInviteInfo: FC<OwnProps & StateProps> = ({
|
||||
<p>{lang('LinkCreatedeBy')}</p>
|
||||
<ListItem
|
||||
className="chat-item-clickable scroll-item small-icon"
|
||||
// eslint-disable-next-line react/jsx-no-bind
|
||||
onClick={() => openChat({ id: adminId })}
|
||||
>
|
||||
<PrivateChatInfo
|
||||
|
||||
@ -333,6 +333,7 @@ const ManageInvites: FC<OwnProps & StateProps> = ({
|
||||
icon="link"
|
||||
secondaryIcon="more"
|
||||
multiline
|
||||
// eslint-disable-next-line react/jsx-no-bind
|
||||
onClick={() => showInviteInfo(invite)}
|
||||
contextActions={prepareContextActions(invite)}
|
||||
key={invite.link}
|
||||
@ -361,6 +362,7 @@ const ManageInvites: FC<OwnProps & StateProps> = ({
|
||||
icon="link"
|
||||
secondaryIcon="more"
|
||||
multiline
|
||||
// eslint-disable-next-line react/jsx-no-bind
|
||||
onClick={() => showInviteInfo(invite)}
|
||||
contextActions={prepareContextActions(invite)}
|
||||
key={invite.link}
|
||||
|
||||
@ -71,7 +71,9 @@ const TestOrdered: FC<{}> = () => {
|
||||
teactOrderKey={itemValue}
|
||||
key={key}
|
||||
value={itemValue}
|
||||
// eslint-disable-next-line react/jsx-no-bind
|
||||
onChange={(newValue) => updateData(key, Number(newValue))}
|
||||
// eslint-disable-next-line react/jsx-no-bind
|
||||
onDelete={() => deleteData(key)}
|
||||
/>
|
||||
))}
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
import { ChangeEvent } from 'react';
|
||||
import React, {
|
||||
FC, useState, useEffect, memo,
|
||||
FC, useState, useEffect, memo, useCallback,
|
||||
} from '../../lib/teact/teact';
|
||||
|
||||
import buildClassName from '../../util/buildClassName';
|
||||
@ -40,7 +40,7 @@ const AvatarEditable: FC<OwnProps> = ({
|
||||
target.value = '';
|
||||
}
|
||||
|
||||
function handleAvatarCrop(croppedImg: File) {
|
||||
const handleAvatarCrop = useCallback((croppedImg: File) => {
|
||||
setSelectedFile(undefined);
|
||||
onChange(croppedImg);
|
||||
|
||||
@ -48,11 +48,11 @@ const AvatarEditable: FC<OwnProps> = ({
|
||||
URL.revokeObjectURL(croppedBlobUrl);
|
||||
}
|
||||
setCroppedBlobUrl(URL.createObjectURL(croppedImg));
|
||||
}
|
||||
}, [croppedBlobUrl, onChange]);
|
||||
|
||||
function handleModalClose() {
|
||||
const handleModalClose = useCallback(() => {
|
||||
setSelectedFile(undefined);
|
||||
}
|
||||
}, []);
|
||||
|
||||
const labelClassName = buildClassName(
|
||||
croppedBlobUrl && 'filled',
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
import React, {
|
||||
FC, useEffect, useState, memo,
|
||||
FC, useEffect, useState, memo, useCallback,
|
||||
} from '../../lib/teact/teact';
|
||||
|
||||
import { DEBUG } from '../../config';
|
||||
@ -93,7 +93,7 @@ const CropModal: FC<OwnProps> = ({ file, onChange, onClose }: OwnProps) => {
|
||||
|
||||
const lang = useLang();
|
||||
|
||||
async function handleCropClick() {
|
||||
const handleCropClick = useCallback(async () => {
|
||||
if (!cropper) {
|
||||
return;
|
||||
}
|
||||
@ -102,7 +102,7 @@ const CropModal: FC<OwnProps> = ({ file, onChange, onClose }: OwnProps) => {
|
||||
const croppedImg = typeof result === 'string' ? result : blobToFile(result, 'avatar.jpg');
|
||||
|
||||
onChange(croppedImg);
|
||||
}
|
||||
}, [onChange]);
|
||||
|
||||
return (
|
||||
<Modal
|
||||
|
||||
@ -1,4 +1,6 @@
|
||||
import React, { FC, useState, useRef } from '../../lib/teact/teact';
|
||||
import React, {
|
||||
FC, useState, useRef, useCallback,
|
||||
} from '../../lib/teact/teact';
|
||||
|
||||
import Menu from './Menu';
|
||||
|
||||
@ -55,10 +57,10 @@ const DropdownMenu: FC<OwnProps> = ({
|
||||
}
|
||||
};
|
||||
|
||||
const handleClose = () => {
|
||||
const handleClose = useCallback(() => {
|
||||
setIsOpen(false);
|
||||
if (onClose) onClose();
|
||||
};
|
||||
}, [onClose]);
|
||||
|
||||
return (
|
||||
<div
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user