diff options
author | GitLab Bot <gitlab-bot@gitlab.com> | 2022-01-27 00:16:58 +0300 |
---|---|---|
committer | GitLab Bot <gitlab-bot@gitlab.com> | 2022-01-27 00:16:58 +0300 |
commit | c9d1b778889bfb220bbde547a8633b507aa8a9db (patch) | |
tree | ac06708d6bf051195c4455a09af69e998916a47d /app/assets/javascripts/projects | |
parent | 733f1d8bb11ad5e17f95e31be14f745d13114bfd (diff) |
Add latest changes from gitlab-org/gitlab@master
Diffstat (limited to 'app/assets/javascripts/projects')
-rw-r--r-- | app/assets/javascripts/projects/project_new.js | 77 |
1 files changed, 54 insertions, 23 deletions
diff --git a/app/assets/javascripts/projects/project_new.js b/app/assets/javascripts/projects/project_new.js index 8d71a3dab68..55913394ca3 100644 --- a/app/assets/javascripts/projects/project_new.js +++ b/app/assets/javascripts/projects/project_new.js @@ -1,6 +1,7 @@ import $ from 'jquery'; import { debounce } from 'lodash'; import DEFAULT_PROJECT_TEMPLATES from 'ee_else_ce/projects/default_project_templates'; +import { DEFAULT_DEBOUNCE_AND_THROTTLE_MS } from '../lib/utils/constants'; import axios from '../lib/utils/axios_utils'; import { convertToTitleCase, @@ -13,20 +14,26 @@ let hasUserDefinedProjectPath = false; let hasUserDefinedProjectName = false; const invalidInputClass = 'gl-field-error-outline'; +const cancelSource = axios.CancelToken.source(); +const endpoint = `${gon.relative_url_root}/import/url/validate`; +let importCredentialsValidationPromise = null; const validateImportCredentials = (url, user, password) => { - const endpoint = `${gon.relative_url_root}/import/url/validate`; - return axios - .post(endpoint, { - url, - user, - password, - }) + cancelSource.cancel(); + importCredentialsValidationPromise = axios + .post(endpoint, { url, user, password }, { cancelToken: cancelSource.cancel() }) .then(({ data }) => data) - .catch(() => ({ - // intentionally reporting success in case of validation error - // we do not want to block users from trying import in case of validation exception - success: true, - })); + .catch((thrown) => + axios.isCancel(thrown) + ? { + cancelled: true, + } + : { + // intentionally reporting success in case of validation error + // we do not want to block users from trying import in case of validation exception + success: true, + }, + ); + return importCredentialsValidationPromise; }; const onProjectNameChange = ($projectNameInput, $projectPathInput) => { @@ -72,7 +79,7 @@ const deriveProjectPathFromUrl = ($projectImportUrl) => { .parents('.toggle-import-form') .find('#project_path'); - if (hasUserDefinedProjectPath) { + if (hasUserDefinedProjectPath || $currentProjectPath.length === 0) { return; } @@ -114,7 +121,7 @@ const bindEvents = () => { const $projectImportUrlUser = $('#project_import_url_user'); const $projectImportUrlPassword = $('#project_import_url_password'); const $projectImportUrlError = $('.js-import-url-error'); - const $projectImportForm = $('.project-import form'); + const $projectImportForm = $('form.js-project-import'); const $projectPath = $('.tab-pane.active #project_path'); const $useTemplateBtn = $('.template-button > input'); const $projectFieldsForm = $('.project-fields-form'); @@ -124,7 +131,7 @@ const bindEvents = () => { const $projectTemplateButtons = $('.project-templates-buttons'); const $projectName = $('.tab-pane.active #project_name'); - if ($newProjectForm.length !== 1) { + if ($newProjectForm.length !== 1 && $projectImportForm.length !== 1) { return; } @@ -168,20 +175,28 @@ const bindEvents = () => { $projectPath.val($projectPath.val().trim()); }); - const updateUrlPathWarningVisibility = debounce(async () => { - const { success: isUrlValid } = await validateImportCredentials( + const updateUrlPathWarningVisibility = async () => { + const { success: isUrlValid, cancelled } = await validateImportCredentials( $projectImportUrl.val(), $projectImportUrlUser.val(), $projectImportUrlPassword.val(), ); + if (cancelled) { + return; + } + $projectImportUrl.toggleClass(invalidInputClass, !isUrlValid); $projectImportUrlError.toggleClass('hide', isUrlValid); - }, 500); + }; + const debouncedUpdateUrlPathWarningVisibility = debounce( + updateUrlPathWarningVisibility, + DEFAULT_DEBOUNCE_AND_THROTTLE_MS, + ); let isProjectImportUrlDirty = false; $projectImportUrl.on('blur', () => { isProjectImportUrlDirty = true; - updateUrlPathWarningVisibility(); + debouncedUpdateUrlPathWarningVisibility(); }); $projectImportUrl.on('keyup', () => { deriveProjectPathFromUrl($projectImportUrl); @@ -190,17 +205,33 @@ const bindEvents = () => { [$projectImportUrl, $projectImportUrlUser, $projectImportUrlPassword].forEach(($f) => { $f.on('input', () => { if (isProjectImportUrlDirty) { - updateUrlPathWarningVisibility(); + debouncedUpdateUrlPathWarningVisibility(); } }); }); - $projectImportForm.on('submit', (e) => { + $projectImportForm.on('submit', async (e) => { + e.preventDefault(); + + if (importCredentialsValidationPromise === null) { + // we didn't validate credentials yet + debouncedUpdateUrlPathWarningVisibility.cancel(); + updateUrlPathWarningVisibility(); + } + + const submitBtn = $projectImportForm.find('input[type="submit"]'); + + submitBtn.disable(); + await importCredentialsValidationPromise; + submitBtn.enable(); + const $invalidFields = $projectImportForm.find(`.${invalidInputClass}`); if ($invalidFields.length > 0) { $invalidFields[0].focus(); - e.preventDefault(); - e.stopPropagation(); + } else { + // calling .submit() on HTMLFormElement does not trigger 'submit' event + // We are using this behavior to bypass this handler and avoid infinite loop + $projectImportForm[0].submit(); } }); |