* 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 codetags/v1.18.0-dev
// 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 | ||||
} | } | ||||
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": | ||||
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 | ||||
} | } |
<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"}} |
</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"}} |
</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"> | ||||
</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"> |
export function initGlobalEnterQuickSubmit() { | export function initGlobalEnterQuickSubmit() { | ||||
$('.js-quick-submit').on('keydown', function (e) { | |||||
if (((e.ctrlKey && !e.altKey) || e.metaKey) && (e.keyCode === 13 || e.keyCode === 10)) { | |||||
$(this).closest('form').trigger('submit'); | |||||
$(document).on('keydown', '.js-quick-submit', (e) => { | |||||
if (((e.ctrlKey && !e.altKey) || e.metaKey) && (e.key === 'Enter')) { | |||||
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 |
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} | ||||
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') { | ||||
$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()); | |||||
} |
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(); | ||||
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 () { | ||||
initCommentContent(); | initCommentContent(); | ||||
}); | }); | ||||
}); | }); | ||||
} else { | |||||
} else { // use existing form | |||||
$textarea = $segment.find('textarea'); | $textarea = $segment.find('textarea'); | ||||
easyMDE = getAttachedEasyMDE($textarea); | easyMDE = getAttachedEasyMDE($textarea); | ||||
} | } |
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; | ||||
] | ] | ||||
}); | }); | ||||
attachEasyMDEToElements(easyMDE); | |||||
easyMDE.codemirror.setOption('extraKeys', { | |||||
'Cmd-Enter': codeMirrorQuickSubmit, | |||||
'Ctrl-Enter': codeMirrorQuickSubmit, | |||||
}); | |||||
const $mdeInputField = $(easyMDE.codemirror.getInputField()); | |||||
$mdeInputField.addClass('js-quick-submit'); | |||||
attachEasyMDEToElements(easyMDE); | |||||
$form.on('submit', () => { | $form.on('submit', () => { | ||||
if (!validateTextareaNonEmpty($editArea)) { | if (!validateTextareaNonEmpty($editArea)) { |