Enable import order (#24091)
* Enable import order * Add new order groups and add consistent-type-imports * Remove types group, move react to top and fix issues * Add store and reducers to the redux group and move imports from types folder to the end * Fix tsc * Remove react rule * Fix test * Fix eslint disable
Этот коммит содержится в:
коммит произвёл
GitHub
родитель
b2f36c7cdf
Коммит
d13429aa92
@@ -1,10 +1,11 @@
|
||||
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||||
// See LICENSE.txt for license information.
|
||||
|
||||
import {ActionTypes} from 'utils/constants';
|
||||
import {hideRHSPlugin as hideRHSPluginAction} from 'actions/views/rhs';
|
||||
import {getPluggableId} from 'selectors/rhs';
|
||||
|
||||
import {ActionTypes} from 'utils/constants';
|
||||
|
||||
export const removeWebappPlugin = (manifest) => {
|
||||
return (dispatch) => {
|
||||
dispatch(hideRHSPlugin(manifest.id));
|
||||
|
||||
@@ -1,19 +1,21 @@
|
||||
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||||
// See LICENSE.txt for license information.
|
||||
|
||||
import React, {CSSProperties, useState, useEffect, useRef} from 'react';
|
||||
import {useIntl} from 'react-intl';
|
||||
import classNames from 'classnames';
|
||||
import React, {useState, useEffect, useRef} from 'react';
|
||||
import type {CSSProperties} from 'react';
|
||||
import {useIntl} from 'react-intl';
|
||||
|
||||
import PhoneOutlineIcon from '@mattermost/compass-icons/components/phone-outline';
|
||||
import ChevronDownIcon from '@mattermost/compass-icons/components/chevron-down';
|
||||
import PhoneOutlineIcon from '@mattermost/compass-icons/components/phone-outline';
|
||||
import type {Channel, ChannelMembership} from '@mattermost/types/channels';
|
||||
|
||||
import MenuWrapper from 'components/widgets/menu/menu_wrapper';
|
||||
import Menu from 'components/widgets/menu/menu';
|
||||
import MenuWrapper from 'components/widgets/menu/menu_wrapper';
|
||||
|
||||
import {Constants} from 'utils/constants';
|
||||
|
||||
import {Channel, ChannelMembership} from '@mattermost/types/channels';
|
||||
import {PluginComponent} from 'types/store/plugins';
|
||||
import type {PluginComponent} from 'types/store/plugins';
|
||||
|
||||
import './call_button.scss';
|
||||
|
||||
|
||||
@@ -5,7 +5,7 @@ import {connect} from 'react-redux';
|
||||
|
||||
import {getCurrentChannel, getMyCurrentChannelMembership} from 'mattermost-redux/selectors/entities/channels';
|
||||
|
||||
import {GlobalState} from 'types/store/index';
|
||||
import type {GlobalState} from 'types/store/index';
|
||||
|
||||
import CallButton from './call_button';
|
||||
|
||||
|
||||
@@ -3,12 +3,14 @@
|
||||
|
||||
import React from 'react';
|
||||
|
||||
import {Channel, ChannelMembership} from '@mattermost/types/channels';
|
||||
import {Theme} from 'mattermost-redux/selectors/entities/preferences';
|
||||
import type {Channel, ChannelMembership} from '@mattermost/types/channels';
|
||||
|
||||
import type {Theme} from 'mattermost-redux/selectors/entities/preferences';
|
||||
|
||||
import ChannelHeaderPlug from 'plugins/channel_header_plug/channel_header_plug';
|
||||
import {mountWithIntl} from '../../tests/helpers/intl-test-helper';
|
||||
import {PluginComponent} from 'types/store/plugins';
|
||||
import {mountWithIntl} from 'tests/helpers/intl-test-helper';
|
||||
|
||||
import type {PluginComponent} from 'types/store/plugins';
|
||||
|
||||
describe('plugins/ChannelHeaderPlug', () => {
|
||||
const testPlug: PluginComponent = {
|
||||
|
||||
@@ -5,25 +5,26 @@
|
||||
|
||||
import React from 'react';
|
||||
import {Dropdown, Tooltip} from 'react-bootstrap';
|
||||
import {FormattedMessage, injectIntl} from 'react-intl';
|
||||
import type {IntlShape} from 'react-intl';
|
||||
import {RootCloseWrapper} from 'react-overlays';
|
||||
import {FormattedMessage, injectIntl, IntlShape} from 'react-intl';
|
||||
|
||||
import {Channel, ChannelMembership} from '@mattermost/types/channels';
|
||||
import {Theme} from 'mattermost-redux/selectors/entities/preferences';
|
||||
import {AppBinding} from '@mattermost/types/apps';
|
||||
import type {AppBinding} from '@mattermost/types/apps';
|
||||
import type {Channel, ChannelMembership} from '@mattermost/types/channels';
|
||||
|
||||
import {AppCallResponseTypes} from 'mattermost-redux/constants/apps';
|
||||
|
||||
import {HandleBindingClick, OpenAppsModal, PostEphemeralCallResponseForChannel} from 'types/apps';
|
||||
import type {Theme} from 'mattermost-redux/selectors/entities/preferences';
|
||||
|
||||
import HeaderIconWrapper from 'components/channel_header/components/header_icon_wrapper';
|
||||
import PluginChannelHeaderIcon from 'components/widgets/icons/plugin_channel_header_icon';
|
||||
import OverlayTrigger from 'components/overlay_trigger';
|
||||
|
||||
import {PluginComponent} from 'types/store/plugins';
|
||||
import PluginChannelHeaderIcon from 'components/widgets/icons/plugin_channel_header_icon';
|
||||
|
||||
import {createCallContext} from 'utils/apps';
|
||||
import {Constants} from 'utils/constants';
|
||||
|
||||
import type {HandleBindingClick, OpenAppsModal, PostEphemeralCallResponseForChannel} from 'types/apps';
|
||||
import type {PluginComponent} from 'types/store/plugins';
|
||||
|
||||
type CustomMenuProps = {
|
||||
open?: boolean;
|
||||
children?: React.ReactNode;
|
||||
|
||||
@@ -2,19 +2,19 @@
|
||||
// See LICENSE.txt for license information.
|
||||
|
||||
import {connect} from 'react-redux';
|
||||
import {ActionCreatorsMapObject, bindActionCreators, Dispatch} from 'redux';
|
||||
import {bindActionCreators} from 'redux';
|
||||
import type {ActionCreatorsMapObject, Dispatch} from 'redux';
|
||||
|
||||
import {getTheme} from 'mattermost-redux/selectors/entities/preferences';
|
||||
import {appBarEnabled, appsEnabled, getChannelHeaderAppBindings} from 'mattermost-redux/selectors/entities/apps';
|
||||
import {GenericAction} from 'mattermost-redux/types/actions';
|
||||
|
||||
import {HandleBindingClick, OpenAppsModal, PostEphemeralCallResponseForChannel} from 'types/apps';
|
||||
import {getTheme} from 'mattermost-redux/selectors/entities/preferences';
|
||||
import type {GenericAction} from 'mattermost-redux/types/actions';
|
||||
|
||||
import {handleBindingClick, openAppsModal, postEphemeralCallResponseForChannel} from 'actions/apps';
|
||||
import {GlobalState} from 'types/store';
|
||||
|
||||
import {getChannelHeaderPluginComponents, shouldShowAppBar} from 'selectors/plugins';
|
||||
|
||||
import type {HandleBindingClick, OpenAppsModal, PostEphemeralCallResponseForChannel} from 'types/apps';
|
||||
import type {GlobalState} from 'types/store';
|
||||
|
||||
import ChannelHeaderPlug from './channel_header_plug';
|
||||
|
||||
function mapStateToProps(state: GlobalState) {
|
||||
|
||||
@@ -1,37 +1,32 @@
|
||||
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||||
// See LICENSE.txt for license information.
|
||||
|
||||
import {closeRightHandSide, selectPostById} from 'actions/views/rhs';
|
||||
import {notifyMe} from 'actions/notification_actions';
|
||||
import {getSelectedPostId, getIsRhsOpen} from 'selectors/rhs';
|
||||
|
||||
import BotTag from 'components/widgets/tag/bot_tag';
|
||||
|
||||
import messageHtmlToComponent from 'utils/message_html_to_component';
|
||||
import * as NotificationSounds from 'utils/notification_sounds';
|
||||
import {formatText} from 'utils/text_formatting';
|
||||
import {getHistory} from 'utils/browser_history';
|
||||
|
||||
import {openModal} from 'actions/views/modals';
|
||||
import {ModalIdentifiers} from 'utils/constants';
|
||||
import {useWebSocket, useWebSocketClient, WebSocketContext} from 'utils/use_websocket';
|
||||
import {imageURLForUser} from 'utils/utils';
|
||||
import {closeRightHandSide, selectPostById} from 'actions/views/rhs';
|
||||
import {getSelectedPostId, getIsRhsOpen} from 'selectors/rhs';
|
||||
|
||||
import ChannelInviteModal from 'components/channel_invite_modal';
|
||||
import ChannelMembersModal from 'components/channel_members_modal';
|
||||
import PurchaseModal from 'components/purchase_modal';
|
||||
import {openPricingModal} from 'components/global_header/right_controls/plan_upgrade_button';
|
||||
import {useNotifyAdmin} from 'components/notify_admin_cta/notify_admin_cta';
|
||||
import Timestamp from 'components/timestamp';
|
||||
import Avatar from 'components/widgets/users/avatar';
|
||||
import PurchaseModal from 'components/purchase_modal';
|
||||
import StartTrialFormModal from 'components/start_trial_form_modal';
|
||||
import Timestamp from 'components/timestamp';
|
||||
import BotTag from 'components/widgets/tag/bot_tag';
|
||||
import Avatar from 'components/widgets/users/avatar';
|
||||
|
||||
import {openPricingModal} from '../components/global_header/right_controls/plan_upgrade_button';
|
||||
import {getHistory} from 'utils/browser_history';
|
||||
import {ModalIdentifiers} from 'utils/constants';
|
||||
import messageHtmlToComponent from 'utils/message_html_to_component';
|
||||
import * as NotificationSounds from 'utils/notification_sounds';
|
||||
import {formatText} from 'utils/text_formatting';
|
||||
import {useWebSocket, useWebSocketClient, WebSocketContext} from 'utils/use_websocket';
|
||||
import {imageURLForUser} from 'utils/utils';
|
||||
|
||||
import {openInteractiveDialog} from './interactive_dialog'; // This import has intentional side effects. Do not remove without research.
|
||||
import Textbox from './textbox';
|
||||
|
||||
// The following import has intentional side effects. Do not remove without research.
|
||||
import {openInteractiveDialog} from './interactive_dialog';
|
||||
|
||||
// Common libraries exposed on window for plugins to use as Webpack externals.
|
||||
window.React = require('react');
|
||||
window.ReactDOM = require('react-dom');
|
||||
|
||||
@@ -4,20 +4,19 @@
|
||||
import regeneratorRuntime from 'regenerator-runtime';
|
||||
|
||||
import {Client4} from 'mattermost-redux/client';
|
||||
|
||||
import {Preferences} from 'mattermost-redux/constants';
|
||||
|
||||
import {getConfig, isPerformanceDebuggingEnabled} from 'mattermost-redux/selectors/entities/general';
|
||||
import {getBool} from 'mattermost-redux/selectors/entities/preferences';
|
||||
|
||||
import store from 'stores/redux_store.jsx';
|
||||
import {ActionTypes} from 'utils/constants';
|
||||
import {getSiteURL} from 'utils/url';
|
||||
import PluginRegistry from 'plugins/registry';
|
||||
import {unregisterAllPluginWebSocketEvents, unregisterPluginReconnectHandler} from 'actions/websocket_actions.jsx';
|
||||
import {unregisterPluginTranslationsSource} from 'actions/views/root';
|
||||
import {unregisterAdminConsolePlugin} from 'actions/admin_actions';
|
||||
import {trackPluginInitialization} from 'actions/telemetry_actions';
|
||||
import {unregisterPluginTranslationsSource} from 'actions/views/root';
|
||||
import {unregisterAllPluginWebSocketEvents, unregisterPluginReconnectHandler} from 'actions/websocket_actions.jsx';
|
||||
import store from 'stores/redux_store.jsx';
|
||||
|
||||
import PluginRegistry from 'plugins/registry';
|
||||
import {ActionTypes} from 'utils/constants';
|
||||
import {getSiteURL} from 'utils/url';
|
||||
|
||||
import {removeWebappPlugin} from './actions';
|
||||
|
||||
|
||||
@@ -6,11 +6,11 @@ import {
|
||||
} from 'mattermost-redux/action_types';
|
||||
|
||||
import {openModal} from 'actions/views/modals';
|
||||
|
||||
import {ModalIdentifiers} from 'utils/constants';
|
||||
import store from 'stores/redux_store';
|
||||
|
||||
import InteractiveDialog from 'components/interactive_dialog';
|
||||
import store from '../stores/redux_store';
|
||||
|
||||
import {ModalIdentifiers} from 'utils/constants';
|
||||
|
||||
export function openInteractiveDialog(dialog) {
|
||||
store.dispatch({type: IntegrationTypes.RECEIVED_DIALOG, data: dialog});
|
||||
|
||||
@@ -2,18 +2,19 @@
|
||||
// See LICENSE.txt for license information.
|
||||
|
||||
import {connect} from 'react-redux';
|
||||
import {ActionCreatorsMapObject, bindActionCreators, Dispatch} from 'redux';
|
||||
import {bindActionCreators} from 'redux';
|
||||
import type {ActionCreatorsMapObject, Dispatch} from 'redux';
|
||||
|
||||
import {getTheme} from 'mattermost-redux/selectors/entities/preferences';
|
||||
import {getMyCurrentChannelMembership} from 'mattermost-redux/selectors/entities/channels';
|
||||
|
||||
import {appsEnabled, makeAppBindingsSelector} from 'mattermost-redux/selectors/entities/apps';
|
||||
import {AppBindingLocations} from 'mattermost-redux/constants/apps';
|
||||
import {GlobalState} from 'types/store';
|
||||
import {GenericAction} from 'mattermost-redux/types/actions';
|
||||
import {appsEnabled, makeAppBindingsSelector} from 'mattermost-redux/selectors/entities/apps';
|
||||
import {getMyCurrentChannelMembership} from 'mattermost-redux/selectors/entities/channels';
|
||||
import {getTheme} from 'mattermost-redux/selectors/entities/preferences';
|
||||
import type {GenericAction} from 'mattermost-redux/types/actions';
|
||||
|
||||
import {handleBindingClick, openAppsModal, postEphemeralCallResponseForChannel} from 'actions/apps';
|
||||
|
||||
import {HandleBindingClick, OpenAppsModal, PostEphemeralCallResponseForChannel} from 'types/apps';
|
||||
import type {HandleBindingClick, OpenAppsModal, PostEphemeralCallResponseForChannel} from 'types/apps';
|
||||
import type {GlobalState} from 'types/store';
|
||||
|
||||
import MobileChannelHeaderPlug from './mobile_channel_header_plug';
|
||||
|
||||
|
||||
@@ -1,16 +1,17 @@
|
||||
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||||
// See LICENSE.txt for license information.
|
||||
|
||||
import {mount} from 'enzyme';
|
||||
import React from 'react';
|
||||
|
||||
import {mount} from 'enzyme';
|
||||
import type {Channel, ChannelMembership} from '@mattermost/types/channels';
|
||||
|
||||
import {AppCallResponseTypes} from 'mattermost-redux/constants/apps';
|
||||
import type {Theme} from 'mattermost-redux/selectors/entities/preferences';
|
||||
|
||||
import MobileChannelHeaderPlug, {RawMobileChannelHeaderPlug} from 'plugins/mobile_channel_header_plug/mobile_channel_header_plug';
|
||||
import {mountWithIntl} from 'tests/helpers/intl-test-helper';
|
||||
import {createCallContext} from 'utils/apps';
|
||||
import {AppCallResponseTypes} from 'mattermost-redux/constants/apps';
|
||||
import {Channel, ChannelMembership} from '@mattermost/types/channels';
|
||||
import {Theme} from 'mattermost-redux/selectors/entities/preferences';
|
||||
|
||||
describe('plugins/MobileChannelHeaderPlug', () => {
|
||||
const testPlug = {
|
||||
|
||||
@@ -2,17 +2,19 @@
|
||||
// See LICENSE.txt for license information.
|
||||
|
||||
import React from 'react';
|
||||
import {injectIntl} from 'react-intl';
|
||||
import type {IntlShape} from 'react-intl';
|
||||
|
||||
import {injectIntl, IntlShape} from 'react-intl';
|
||||
import type {AppBinding} from '@mattermost/types/apps';
|
||||
import type {Channel, ChannelMembership} from '@mattermost/types/channels';
|
||||
|
||||
import {AppCallResponseTypes} from 'mattermost-redux/constants/apps';
|
||||
import {AppBinding} from '@mattermost/types/apps';
|
||||
import {Channel, ChannelMembership} from '@mattermost/types/channels';
|
||||
import {Theme} from 'mattermost-redux/selectors/entities/preferences';
|
||||
import type {Theme} from 'mattermost-redux/selectors/entities/preferences';
|
||||
|
||||
import {PluginComponent} from 'types/store/plugins';
|
||||
import {createCallContext} from 'utils/apps';
|
||||
import {HandleBindingClick, OpenAppsModal, PostEphemeralCallResponseForChannel} from 'types/apps';
|
||||
|
||||
import type {HandleBindingClick, OpenAppsModal, PostEphemeralCallResponseForChannel} from 'types/apps';
|
||||
import type {PluginComponent} from 'types/store/plugins';
|
||||
|
||||
type Props = {
|
||||
|
||||
|
||||
@@ -5,7 +5,7 @@ import {connect} from 'react-redux';
|
||||
|
||||
import {getTheme} from 'mattermost-redux/selectors/entities/preferences';
|
||||
|
||||
import {GlobalState} from 'types/store';
|
||||
import type {GlobalState} from 'types/store';
|
||||
|
||||
import Pluggable from './pluggable';
|
||||
|
||||
|
||||
@@ -3,15 +3,15 @@
|
||||
|
||||
import React from 'react';
|
||||
|
||||
import {WebSocketClient} from '@mattermost/client';
|
||||
import type {WebSocketClient} from '@mattermost/client';
|
||||
|
||||
import {Theme} from 'mattermost-redux/selectors/entities/preferences';
|
||||
|
||||
import {ProductComponent} from 'types/store/plugins';
|
||||
import {GlobalState} from 'types/store';
|
||||
import type {Theme} from 'mattermost-redux/selectors/entities/preferences';
|
||||
|
||||
import webSocketClient from 'client/web_websocket_client';
|
||||
|
||||
import type {GlobalState} from 'types/store';
|
||||
import type {ProductComponent} from 'types/store/plugins';
|
||||
|
||||
import PluggableErrorBoundary from './error_boundary';
|
||||
|
||||
type Props = {
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||||
// See LICENSE.txt for license information.
|
||||
|
||||
import {Store} from 'redux';
|
||||
import type {Store} from 'redux';
|
||||
|
||||
import {DispatchFunc, GetStateFunc} from 'mattermost-redux/types/actions';
|
||||
import type {DispatchFunc, GetStateFunc} from 'mattermost-redux/types/actions';
|
||||
|
||||
import store from 'stores/redux_store';
|
||||
|
||||
|
||||
@@ -3,40 +3,39 @@
|
||||
|
||||
import React from 'react';
|
||||
import {isValidElementType} from 'react-is';
|
||||
import {Reducer} from 'redux';
|
||||
import type {Reducer} from 'redux';
|
||||
|
||||
import {ProductScope} from '@mattermost/types/products';
|
||||
import type {Channel, ChannelMembership} from '@mattermost/types/channels';
|
||||
import type {FileInfo} from '@mattermost/types/files';
|
||||
import type {ProductScope} from '@mattermost/types/products';
|
||||
|
||||
import reducerRegistry from 'mattermost-redux/store/reducer_registry';
|
||||
|
||||
import {
|
||||
registerPluginWebSocketEvent,
|
||||
unregisterPluginWebSocketEvent,
|
||||
registerPluginReconnectHandler,
|
||||
unregisterPluginReconnectHandler,
|
||||
} from 'actions/websocket_actions.jsx';
|
||||
|
||||
import {showRHSPlugin, hideRHSPlugin, toggleRHSPlugin} from 'actions/views/rhs';
|
||||
|
||||
import {
|
||||
registerPluginTranslationsSource,
|
||||
TranslationPluginFunction,
|
||||
} from 'actions/views/root';
|
||||
|
||||
import {
|
||||
registerAdminConsolePlugin,
|
||||
unregisterAdminConsolePlugin,
|
||||
registerAdminConsoleCustomSetting,
|
||||
} from 'actions/admin_actions';
|
||||
|
||||
import {showRHSPlugin, hideRHSPlugin, toggleRHSPlugin} from 'actions/views/rhs';
|
||||
import {
|
||||
registerPluginTranslationsSource,
|
||||
} from 'actions/views/root';
|
||||
import type {
|
||||
TranslationPluginFunction} from 'actions/views/root';
|
||||
import {
|
||||
registerPluginWebSocketEvent,
|
||||
unregisterPluginWebSocketEvent,
|
||||
registerPluginReconnectHandler,
|
||||
unregisterPluginReconnectHandler,
|
||||
} from 'actions/websocket_actions.jsx';
|
||||
import store from 'stores/redux_store.jsx';
|
||||
|
||||
import {ActionTypes} from 'utils/constants';
|
||||
import {generateId} from 'utils/utils';
|
||||
import {PluginComponent, PluginsState, ProductComponent, NeedsTeamComponent} from 'types/store/plugins';
|
||||
import {GlobalState} from 'types/store';
|
||||
import {FileInfo} from '@mattermost/types/files';
|
||||
import {Channel, ChannelMembership} from '@mattermost/types/channels';
|
||||
import {reArg} from 'utils/func';
|
||||
import {generateId} from 'utils/utils';
|
||||
|
||||
import type {GlobalState} from 'types/store';
|
||||
import type {PluginComponent, PluginsState, ProductComponent, NeedsTeamComponent} from 'types/store/plugins';
|
||||
|
||||
const defaultShouldRender = () => true;
|
||||
|
||||
|
||||
@@ -3,11 +3,11 @@
|
||||
|
||||
import {connect} from 'react-redux';
|
||||
|
||||
import {GlobalState} from 'types/store';
|
||||
import {PluginComponent} from 'types/store/plugins';
|
||||
|
||||
import {getPluggableId} from 'selectors/rhs';
|
||||
|
||||
import type {GlobalState} from 'types/store';
|
||||
import type {PluginComponent} from 'types/store/plugins';
|
||||
|
||||
import RHSPlugin from './rhs_plugin';
|
||||
|
||||
function mapStateToProps(state: GlobalState) {
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||||
// See LICENSE.txt for license information.
|
||||
|
||||
import React from 'react';
|
||||
import {shallow, mount} from 'enzyme';
|
||||
import React from 'react';
|
||||
|
||||
import PostMessageView from 'components/post_view/post_message_view/post_message_view';
|
||||
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
import React from 'react';
|
||||
|
||||
import Textbox from 'components/textbox';
|
||||
import BaseTextbox from 'components/textbox/textbox';
|
||||
import type BaseTextbox from 'components/textbox/textbox';
|
||||
|
||||
type Props = Omit<React.ComponentPropsWithRef<typeof Textbox>, 'suggestionListPosition'> & {
|
||||
suggestionListStyle?: React.ComponentPropsWithRef<typeof Textbox>['suggestionListPosition'];
|
||||
|
||||
@@ -3,10 +3,10 @@
|
||||
|
||||
import {useSelector} from 'react-redux';
|
||||
|
||||
import {suitePluginIds} from 'utils/constants';
|
||||
import {useProducts} from 'utils/products';
|
||||
|
||||
import {GlobalState} from 'types/store';
|
||||
import {suitePluginIds} from 'utils/constants';
|
||||
import type {GlobalState} from 'types/store';
|
||||
|
||||
export const useGetPluginsActivationState = () => {
|
||||
const pluginsList = useSelector((state: GlobalState) => state.plugins.plugins);
|
||||
|
||||
Ссылка в новой задаче
Block a user