mirror of
https://github.com/mattermost/mattermost.git
synced 2026-05-28 04:35:04 -04:00
Displayed error message for already used channel name (#24860)
* WIP * Displayed existing channel name error * refactoring
This commit is contained in:
parent
f18002a8d6
commit
06765b1f44
7 changed files with 72 additions and 8 deletions
|
|
@ -697,6 +697,9 @@ func (a *App) UpdateChannel(c request.CTX, channel *model.Channel) (*model.Chann
|
|||
var invErr *store.ErrInvalidInput
|
||||
switch {
|
||||
case errors.As(err, &invErr):
|
||||
if invErr.Entity == "Channel" && invErr.Field == "Name" {
|
||||
return nil, model.NewAppError("UpdateChannel", store.ChannelExistsError, nil, "", http.StatusBadRequest).Wrap(err)
|
||||
}
|
||||
return nil, model.NewAppError("UpdateChannel", "app.channel.update.bad_id", nil, "", http.StatusBadRequest).Wrap(err)
|
||||
case errors.As(err, &appErr):
|
||||
return nil, appErr
|
||||
|
|
|
|||
|
|
@ -26,6 +26,7 @@ export type Props = {
|
|||
autoFocus?: boolean;
|
||||
onErrorStateChange?: (isError: boolean) => void;
|
||||
team?: Team;
|
||||
urlError?: string;
|
||||
}
|
||||
|
||||
import './channel_name_form_field.scss';
|
||||
|
|
@ -139,7 +140,7 @@ const ChannelNameFormField = (props: Props): JSX.Element => {
|
|||
pathInfo={url}
|
||||
limit={Constants.MAX_CHANNELNAME_LENGTH}
|
||||
shortenLength={Constants.DEFAULT_CHANNELURL_SHORTEN_LENGTH}
|
||||
error={urlError}
|
||||
error={urlError || props.urlError}
|
||||
onChange={handleOnURLChange}
|
||||
/>
|
||||
</React.Fragment>
|
||||
|
|
|
|||
|
|
@ -175,4 +175,43 @@ describe('component/ConvertGmToChannelModal', () => {
|
|||
fireEvent.click(confirmButton!);
|
||||
});
|
||||
});
|
||||
|
||||
test('duplicate channel names should npt be allowed', async () => {
|
||||
TestHelper.initBasic(Client4);
|
||||
|
||||
nock(Client4.getBaseRoute()).
|
||||
get('/channels/channel_id_1/common_teams').
|
||||
reply(200, [
|
||||
{id: 'team_id_1', display_name: 'Team 1', name: 'team_1'},
|
||||
]);
|
||||
|
||||
baseProps.actions.convertGroupMessageToPrivateChannel.mockResolvedValueOnce({
|
||||
error: {
|
||||
server_error_id: 'store.sql_channel.save_channel.exists.app_error',
|
||||
},
|
||||
});
|
||||
|
||||
renderWithFullContext(
|
||||
<ConvertGmToChannelModal {...baseProps}/>,
|
||||
baseState,
|
||||
);
|
||||
|
||||
await waitFor(
|
||||
() => expect(screen.queryByText('Conversation history will be visible to any channel members')).toBeInTheDocument(),
|
||||
{timeout: 1500},
|
||||
);
|
||||
|
||||
const channelNameInput = screen.queryByPlaceholderText('Channel name');
|
||||
expect(channelNameInput).toBeInTheDocument();
|
||||
fireEvent.change(channelNameInput!, {target: {value: 'Channel'}});
|
||||
|
||||
const confirmButton = screen.queryByText('Convert to private channel');
|
||||
expect(channelNameInput).toBeInTheDocument();
|
||||
|
||||
await act(async () => {
|
||||
fireEvent.click(confirmButton!);
|
||||
});
|
||||
|
||||
expect(screen.queryByText('A channel with that URL already exists')).toBeInTheDocument();
|
||||
});
|
||||
});
|
||||
|
|
|
|||
|
|
@ -28,6 +28,10 @@ import TeamSelector from 'components/convert_gm_to_channel_modal/team_selector/t
|
|||
import WarningTextSection from 'components/convert_gm_to_channel_modal/warning_text_section/warning_text_section';
|
||||
import LoadingSpinner from 'components/widgets/loading/loading_spinner';
|
||||
|
||||
const enum ServerErrorId {
|
||||
CHANNEL_NAME_EXISTS = 'store.sql_channel.save_channel.exists.app_error',
|
||||
}
|
||||
|
||||
export type Props = {
|
||||
onExited: () => void;
|
||||
channel: Channel;
|
||||
|
|
@ -43,8 +47,11 @@ const ConvertGmToChannelModal = (props: Props) => {
|
|||
|
||||
const [channelName, setChannelName] = useState<string>('');
|
||||
const channelURL = useRef<string>('');
|
||||
|
||||
const [urlError, setURLError] = useState('');
|
||||
const handleChannelURLChange = useCallback((newURL: string) => {
|
||||
channelURL.current = newURL;
|
||||
setURLError('');
|
||||
}, []);
|
||||
|
||||
const [channelMemberNames, setChannelMemberNames] = useState<string[]>([]);
|
||||
|
|
@ -112,7 +119,17 @@ const ConvertGmToChannelModal = (props: Props) => {
|
|||
const {error} = await props.actions.convertGroupMessageToPrivateChannel(props.channel.id, selectedTeamId, channelName.trim(), channelURL.current.trim());
|
||||
|
||||
if (error) {
|
||||
setConversionError(error.message);
|
||||
if (error.server_error_id === ServerErrorId.CHANNEL_NAME_EXISTS) {
|
||||
setURLError(
|
||||
formatMessage({
|
||||
id: 'channel_modal.alreadyExist',
|
||||
defaultMessage: 'A channel with that URL already exists',
|
||||
}),
|
||||
);
|
||||
} else {
|
||||
setConversionError(error.message);
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
@ -122,7 +139,7 @@ const ConvertGmToChannelModal = (props: Props) => {
|
|||
}, [selectedTeamId, props.channel.id, channelName, channelURL.current, props.actions.moveChannelsInSidebar]);
|
||||
|
||||
const showLoader = !commonTeamsFetched || !loadingAnimationTimeout;
|
||||
const canCreate = selectedTeamId !== undefined && channelName !== '' && !nameError;
|
||||
const canCreate = selectedTeamId !== undefined && channelName !== '' && !nameError && !urlError;
|
||||
const modalProps: Partial<ComponentProps<typeof GenericModal>> = {};
|
||||
let modalBody;
|
||||
|
||||
|
|
@ -171,6 +188,7 @@ const ConvertGmToChannelModal = (props: Props) => {
|
|||
onURLChange={handleChannelURLChange}
|
||||
onErrorStateChange={setNameError}
|
||||
team={selectedTeamId ? commonTeamsById[selectedTeamId] : undefined}
|
||||
urlError={urlError}
|
||||
/>
|
||||
|
||||
{
|
||||
|
|
|
|||
|
|
@ -81,7 +81,3 @@
|
|||
font-weight: 500;
|
||||
}
|
||||
}
|
||||
|
||||
.new-channel-modal__url {
|
||||
margin-top: 4px;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -83,6 +83,11 @@ const NewChannelModal = () => {
|
|||
const [canCreateFromPluggable, setCanCreateFromPluggable] = useState(true);
|
||||
const [actionFromPluggable, setActionFromPluggable] = useState<((currentTeamId: string, channelId: string) => Promise<Board>) | undefined>(undefined);
|
||||
|
||||
const handleURLChange = useCallback((newURL: string) => {
|
||||
setURL(newURL);
|
||||
setURLError('');
|
||||
}, []);
|
||||
|
||||
const handleOnModalConfirm = async () => {
|
||||
if (!canCreate) {
|
||||
return;
|
||||
|
|
@ -265,8 +270,9 @@ const NewChannelModal = () => {
|
|||
name='new-channel-modal-name'
|
||||
placeholder={formatMessage({id: 'channel_modal.name.placeholder', defaultMessage: 'Enter a name for your new channel'})}
|
||||
onDisplayNameChange={setDisplayName}
|
||||
onURLChange={setURL}
|
||||
onURLChange={handleURLChange}
|
||||
onErrorStateChange={setChannelInputError}
|
||||
urlError={urlError}
|
||||
/>
|
||||
<PublicPrivateSelector
|
||||
className='new-channel-modal-type-selector'
|
||||
|
|
|
|||
|
|
@ -11,6 +11,7 @@
|
|||
display: flex;
|
||||
min-height: 32px;
|
||||
align-items: center;
|
||||
margin-top: 4px;
|
||||
|
||||
.url-input-label {
|
||||
flex: none;
|
||||
|
|
|
|||
Loading…
Reference in a new issue