mirror of
				https://github.com/go-gitea/gitea.git
				synced 2025-10-26 12:27:06 +00:00 
			
		
		
		
	Make Ctrl+Enter (quick submit) work for issue comment and wiki editor (#19729)
* Make Ctrl+Enter (quick submit) work for issue comment and wiki editor * Remove the required `SubmitReviewForm.Type`, empty type (triggered by quick submit) means "comment" * Merge duplicate code
This commit is contained in:
		| @@ -623,7 +623,7 @@ func (f *CodeCommentForm) Validate(req *http.Request, errs binding.Errors) bindi | |||||||
| // SubmitReviewForm for submitting a finished code review | // SubmitReviewForm for submitting a finished code review | ||||||
| type SubmitReviewForm struct { | type SubmitReviewForm struct { | ||||||
| 	Content  string | 	Content  string | ||||||
| 	Type     string `binding:"Required;In(approve,comment,reject)"` | 	Type     string | ||||||
| 	CommitID string | 	CommitID string | ||||||
| 	Files    []string | 	Files    []string | ||||||
| } | } | ||||||
| @@ -634,7 +634,7 @@ func (f *SubmitReviewForm) Validate(req *http.Request, errs binding.Errors) bind | |||||||
| 	return middleware.Validate(errs, ctx.Data, f, ctx.Locale) | 	return middleware.Validate(errs, ctx.Data, f, ctx.Locale) | ||||||
| } | } | ||||||
|  |  | ||||||
| // ReviewType will return the corresponding reviewtype for type | // ReviewType will return the corresponding ReviewType for type | ||||||
| func (f SubmitReviewForm) ReviewType() models.ReviewType { | func (f SubmitReviewForm) ReviewType() models.ReviewType { | ||||||
| 	switch f.Type { | 	switch f.Type { | ||||||
| 	case "approve": | 	case "approve": | ||||||
| @@ -643,6 +643,8 @@ func (f SubmitReviewForm) ReviewType() models.ReviewType { | |||||||
| 		return models.ReviewTypeComment | 		return models.ReviewTypeComment | ||||||
| 	case "reject": | 	case "reject": | ||||||
| 		return models.ReviewTypeReject | 		return models.ReviewTypeReject | ||||||
|  | 	case "": | ||||||
|  | 		return models.ReviewTypeComment // default to comment when doing quick-submit (Ctrl+Enter) on the review form | ||||||
| 	default: | 	default: | ||||||
| 		return models.ReviewTypeUnknown | 		return models.ReviewTypeUnknown | ||||||
| 	} | 	} | ||||||
|   | |||||||
| @@ -180,7 +180,7 @@ | |||||||
| 						<a class="preview item" data-url="{{$.Repository.HTMLURL}}/markdown" data-context="{{$.RepoLink}}">{{$.i18n.Tr "preview"}}</a> | 						<a class="preview item" data-url="{{$.Repository.HTMLURL}}/markdown" data-context="{{$.RepoLink}}">{{$.i18n.Tr "preview"}}</a> | ||||||
| 					</div> | 					</div> | ||||||
| 					<div class="ui bottom attached active write tab segment"> | 					<div class="ui bottom attached active write tab segment"> | ||||||
| 						<textarea class="review-textarea" tabindex="1" name="content"></textarea> | 						<textarea class="review-textarea js-quick-submit" tabindex="1" name="content"></textarea> | ||||||
| 					</div> | 					</div> | ||||||
| 					<div class="ui bottom attached tab preview segment markup"> | 					<div class="ui bottom attached tab preview segment markup"> | ||||||
| 					{{$.i18n.Tr "loading"}} | 					{{$.i18n.Tr "loading"}} | ||||||
|   | |||||||
| @@ -202,7 +202,7 @@ | |||||||
| 		</div> | 		</div> | ||||||
| 		<div class="field"> | 		<div class="field"> | ||||||
| 			<div class="ui bottom active tab write"> | 			<div class="ui bottom active tab write"> | ||||||
| 				<textarea tabindex="1" name="content"></textarea> | 				<textarea tabindex="1" name="content" class="js-quick-submit"></textarea> | ||||||
| 			</div> | 			</div> | ||||||
| 			<div class="ui bottom tab preview markup"> | 			<div class="ui bottom tab preview markup"> | ||||||
| 				{{$.i18n.Tr "loading"}} | 				{{$.i18n.Tr "loading"}} | ||||||
|   | |||||||
| @@ -20,7 +20,7 @@ | |||||||
| 			</div> | 			</div> | ||||||
| 			<div class="field {{if .Err_Content}}error{{end}}"> | 			<div class="field {{if .Err_Content}}error{{end}}"> | ||||||
| 				<label for="content">{{.i18n.Tr "settings.key_content"}}</label> | 				<label for="content">{{.i18n.Tr "settings.key_content"}}</label> | ||||||
| 				<textarea id="ssh-key-content" name="content" placeholder="{{.i18n.Tr "settings.key_content_ssh_placeholder"}}" required>{{.content}}</textarea> | 				<textarea id="ssh-key-content" name="content" class="js-quick-submit" placeholder="{{.i18n.Tr "settings.key_content_ssh_placeholder"}}" required>{{.content}}</textarea> | ||||||
| 			</div> | 			</div> | ||||||
| 			<input name="type" type="hidden" value="ssh"> | 			<input name="type" type="hidden" value="ssh"> | ||||||
| 			<button class="ui green button"> | 			<button class="ui green button"> | ||||||
| @@ -81,7 +81,7 @@ | |||||||
| 						</div> | 						</div> | ||||||
| 						<div class="field"> | 						<div class="field"> | ||||||
| 							<label for="signature">{{$.i18n.Tr "settings.ssh_token_signature"}}</label> | 							<label for="signature">{{$.i18n.Tr "settings.ssh_token_signature"}}</label> | ||||||
| 							<textarea id="ssh-key-signature" name="signature" placeholder="{{$.i18n.Tr "settings.key_signature_ssh_placeholder"}}" required>{{$.signature}}</textarea> | 							<textarea id="ssh-key-signature" name="signature" class="js-quick-submit" placeholder="{{$.i18n.Tr "settings.key_signature_ssh_placeholder"}}" required>{{$.signature}}</textarea> | ||||||
| 						</div> | 						</div> | ||||||
| 						<input name="type" type="hidden" value="verify_ssh"> | 						<input name="type" type="hidden" value="verify_ssh"> | ||||||
| 						<button class="ui green button"> | 						<button class="ui green button"> | ||||||
|   | |||||||
| @@ -44,13 +44,28 @@ export function initFootLanguageMenu() { | |||||||
|  |  | ||||||
|  |  | ||||||
| export function initGlobalEnterQuickSubmit() { | export function initGlobalEnterQuickSubmit() { | ||||||
|   $('.js-quick-submit').on('keydown', function (e) { |   $(document).on('keydown', '.js-quick-submit', (e) => { | ||||||
|     if (((e.ctrlKey && !e.altKey) || e.metaKey) && (e.keyCode === 13 || e.keyCode === 10)) { |     if (((e.ctrlKey && !e.altKey) || e.metaKey) && (e.key === 'Enter')) { | ||||||
|       $(this).closest('form').trigger('submit'); |       handleGlobalEnterQuickSubmit(e.target); | ||||||
|  |       return false; | ||||||
|     } |     } | ||||||
|   }); |   }); | ||||||
| } | } | ||||||
|  |  | ||||||
|  | export function handleGlobalEnterQuickSubmit(target) { | ||||||
|  |   const $target = $(target); | ||||||
|  |   const $form = $(target).closest('form'); | ||||||
|  |   if ($form.length) { | ||||||
|  |     // here use the event to trigger the submit event (instead of calling `submit()` method directly) | ||||||
|  |     // otherwise the `areYouSure` handler won't be executed, then there will be an annoying "confirm to leave" dialog | ||||||
|  |     $form.trigger('submit'); | ||||||
|  |   } else { | ||||||
|  |     // if no form, then the editor is for an AJAX request, dispatch an event to the target, let the target's event handler to do the AJAX request. | ||||||
|  |     // the 'ce-' prefix means this is a CustomEvent | ||||||
|  |     $target.trigger('ce-quick-submit'); | ||||||
|  |   } | ||||||
|  | } | ||||||
|  |  | ||||||
| export function initGlobalButtonClickOnEnter() { | export function initGlobalButtonClickOnEnter() { | ||||||
|   $(document).on('keypress', '.ui.button', (e) => { |   $(document).on('keypress', '.ui.button', (e) => { | ||||||
|     if (e.keyCode === 13 || e.keyCode === 32) { // enter key or space bar |     if (e.keyCode === 13 || e.keyCode === 32) { // enter key or space bar | ||||||
|   | |||||||
| @@ -1,5 +1,6 @@ | |||||||
| import $ from 'jquery'; | import $ from 'jquery'; | ||||||
| import attachTribute from '../tribute.js'; | import attachTribute from '../tribute.js'; | ||||||
|  | import {handleGlobalEnterQuickSubmit} from '../common-global.js'; | ||||||
|  |  | ||||||
| /** | /** | ||||||
|  * @returns {EasyMDE} |  * @returns {EasyMDE} | ||||||
| @@ -71,9 +72,12 @@ export async function createCommentEasyMDE(textarea, easyMDEOptions = {}) { | |||||||
|         title: 'Revert to simple textarea', |         title: 'Revert to simple textarea', | ||||||
|       }, |       }, | ||||||
|     ], ...easyMDEOptions}); |     ], ...easyMDEOptions}); | ||||||
|  |  | ||||||
|   const inputField = easyMDE.codemirror.getInputField(); |   const inputField = easyMDE.codemirror.getInputField(); | ||||||
|   inputField.classList.add('js-quick-submit'); |  | ||||||
|   easyMDE.codemirror.setOption('extraKeys', { |   easyMDE.codemirror.setOption('extraKeys', { | ||||||
|  |     'Cmd-Enter': codeMirrorQuickSubmit, | ||||||
|  |     'Ctrl-Enter': codeMirrorQuickSubmit, | ||||||
|     Enter: (cm) => { |     Enter: (cm) => { | ||||||
|       const tributeContainer = document.querySelector('.tribute-container'); |       const tributeContainer = document.querySelector('.tribute-container'); | ||||||
|       if (!tributeContainer || tributeContainer.style.display === 'none') { |       if (!tributeContainer || tributeContainer.style.display === 'none') { | ||||||
| @@ -149,3 +153,12 @@ export function validateTextareaNonEmpty($textarea) { | |||||||
|   $mdeInputField.prop('required', false); |   $mdeInputField.prop('required', false); | ||||||
|   return true; |   return true; | ||||||
| } | } | ||||||
|  |  | ||||||
|  | /** | ||||||
|  |  * there is no guarantee that the CodeMirror object is inside the same form as the textarea, | ||||||
|  |  * so can not call handleGlobalEnterQuickSubmit directly. | ||||||
|  |  * @param {CodeMirror.EditorFromTextArea} codeMirror | ||||||
|  |  */ | ||||||
|  | export function codeMirrorQuickSubmit(codeMirror) { | ||||||
|  |   handleGlobalEnterQuickSubmit(codeMirror.getTextArea()); | ||||||
|  | } | ||||||
|   | |||||||
| @@ -355,6 +355,11 @@ async function onEditContent(event) { | |||||||
|       initEasyMDEImagePaste(easyMDE, $dropzone[0], $dropzone.find('.files')); |       initEasyMDEImagePaste(easyMDE, $dropzone[0], $dropzone.find('.files')); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|  |     const $saveButton = $editContentZone.find('.save.button'); | ||||||
|  |     $textarea.on('ce-quick-submit', () => { | ||||||
|  |       $saveButton.trigger('click'); | ||||||
|  |     }); | ||||||
|  |  | ||||||
|     $editContentZone.find('.cancel.button').on('click', () => { |     $editContentZone.find('.cancel.button').on('click', () => { | ||||||
|       $renderContent.show(); |       $renderContent.show(); | ||||||
|       $editContentZone.hide(); |       $editContentZone.hide(); | ||||||
| @@ -362,7 +367,8 @@ async function onEditContent(event) { | |||||||
|         dz.emit('reload'); |         dz.emit('reload'); | ||||||
|       } |       } | ||||||
|     }); |     }); | ||||||
|     $editContentZone.find('.save.button').on('click', () => { |  | ||||||
|  |     $saveButton.on('click', () => { | ||||||
|       $renderContent.show(); |       $renderContent.show(); | ||||||
|       $editContentZone.hide(); |       $editContentZone.hide(); | ||||||
|       const $attachments = $dropzone.find('.files').find('[name=files]').map(function () { |       const $attachments = $dropzone.find('.files').find('[name=files]').map(function () { | ||||||
| @@ -400,7 +406,7 @@ async function onEditContent(event) { | |||||||
|         initCommentContent(); |         initCommentContent(); | ||||||
|       }); |       }); | ||||||
|     }); |     }); | ||||||
|   } else { |   } else { // use existing form | ||||||
|     $textarea = $segment.find('textarea'); |     $textarea = $segment.find('textarea'); | ||||||
|     easyMDE = getAttachedEasyMDE($textarea); |     easyMDE = getAttachedEasyMDE($textarea); | ||||||
|   } |   } | ||||||
|   | |||||||
| @@ -1,6 +1,6 @@ | |||||||
| import $ from 'jquery'; | import $ from 'jquery'; | ||||||
| import {initMarkupContent} from '../markup/content.js'; | import {initMarkupContent} from '../markup/content.js'; | ||||||
| import {attachEasyMDEToElements, importEasyMDE, validateTextareaNonEmpty} from './comp/EasyMDE.js'; | import {attachEasyMDEToElements, codeMirrorQuickSubmit, importEasyMDE, validateTextareaNonEmpty} from './comp/EasyMDE.js'; | ||||||
| import {initCompMarkupContentPreviewTab} from './comp/MarkupContentPreview.js'; | import {initCompMarkupContentPreviewTab} from './comp/MarkupContentPreview.js'; | ||||||
|  |  | ||||||
| const {csrfToken} = window.config; | const {csrfToken} = window.config; | ||||||
| @@ -122,10 +122,12 @@ async function initRepoWikiFormEditor() { | |||||||
|     ] |     ] | ||||||
|   }); |   }); | ||||||
|  |  | ||||||
|   attachEasyMDEToElements(easyMDE); |   easyMDE.codemirror.setOption('extraKeys', { | ||||||
|  |     'Cmd-Enter': codeMirrorQuickSubmit, | ||||||
|  |     'Ctrl-Enter': codeMirrorQuickSubmit, | ||||||
|  |   }); | ||||||
|  |  | ||||||
|   const $mdeInputField = $(easyMDE.codemirror.getInputField()); |   attachEasyMDEToElements(easyMDE); | ||||||
|   $mdeInputField.addClass('js-quick-submit'); |  | ||||||
|  |  | ||||||
|   $form.on('submit', () => { |   $form.on('submit', () => { | ||||||
|     if (!validateTextareaNonEmpty($editArea)) { |     if (!validateTextareaNonEmpty($editArea)) { | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user
	 wxiaoguang
					wxiaoguang