From 838653d1dfa0fbc6313b0bba682075baba385a5e Mon Sep 17 00:00:00 2001 From: wxiaoguang Date: Wed, 4 Dec 2024 17:26:54 +0800 Subject: [PATCH] Fix file editor & preview (#32706) Fix a regression caused by jQuery removal (`renderPreviewPanelContent`) And simplify the file editor, it doesn't need to be that complex. And remove jQuery code. --- templates/repo/editor/edit.tmpl | 28 ++------ templates/repo/editor/patch.tmpl | 25 ++------ web_src/js/features/codeeditor.ts | 6 +- web_src/js/features/comp/ConfirmModal.ts | 2 +- web_src/js/features/repo-editor.ts | 81 +++++++++++------------- web_src/js/vendor/jquery.are-you-sure.ts | 4 +- 6 files changed, 52 insertions(+), 94 deletions(-) diff --git a/templates/repo/editor/edit.tmpl b/templates/repo/editor/edit.tmpl index c556826827..204a426970 100644 --- a/templates/repo/editor/edit.tmpl +++ b/templates/repo/editor/edit.tmpl @@ -3,7 +3,10 @@ {{template "repo/header" .}}
{{template "base/alert" .}} -
+ {{.CsrfTokenHtml}} @@ -29,7 +32,7 @@
{{template "base/footer" .}} diff --git a/web_src/js/features/codeeditor.ts b/web_src/js/features/codeeditor.ts index ee515e25e2..93b2042fa9 100644 --- a/web_src/js/features/codeeditor.ts +++ b/web_src/js/features/codeeditor.ts @@ -134,19 +134,17 @@ function getFileBasedOptions(filename: string, lineWrapExts: string[]) { } function togglePreviewDisplay(previewable: boolean) { - const previewTab = document.querySelector('a[data-tab="preview"]'); + const previewTab = document.querySelector('a[data-tab="preview"]'); if (!previewTab) return; if (previewable) { - const newUrl = (previewTab.getAttribute('data-url') || '').replace(/(.*)\/.*/, `$1/markup`); - previewTab.setAttribute('data-url', newUrl); previewTab.style.display = ''; } else { previewTab.style.display = 'none'; // If the "preview" tab was active, user changes the filename to a non-previewable one, // then the "preview" tab becomes inactive (hidden), so the "write" tab should become active if (previewTab.classList.contains('active')) { - const writeTab = document.querySelector('a[data-tab="write"]'); + const writeTab = document.querySelector('a[data-tab="write"]'); writeTab.click(); } } diff --git a/web_src/js/features/comp/ConfirmModal.ts b/web_src/js/features/comp/ConfirmModal.ts index bf645cdbdb..1ce490ec2e 100644 --- a/web_src/js/features/comp/ConfirmModal.ts +++ b/web_src/js/features/comp/ConfirmModal.ts @@ -5,7 +5,7 @@ import {fomanticQuery} from '../../modules/fomantic/base.ts'; const {i18n} = window.config; -export function confirmModal({header = '', content = '', confirmButtonColor = 'primary'} = {}) { +export function confirmModal({header = '', content = '', confirmButtonColor = 'primary'} = {}): Promise { return new Promise((resolve) => { const headerHtml = header ? `
${htmlEscape(header)}
` : ''; const modal = createElementFromHTML(` diff --git a/web_src/js/features/repo-editor.ts b/web_src/js/features/repo-editor.ts index adae55f25c..96b08250fb 100644 --- a/web_src/js/features/repo-editor.ts +++ b/web_src/js/features/repo-editor.ts @@ -1,4 +1,3 @@ -import $ from 'jquery'; import {htmlEscape} from 'escape-goat'; import {createCodeEditor} from './codeeditor.ts'; import {hideElem, queryElems, showElem, createElementFromHTML} from '../utils/dom.ts'; @@ -6,39 +5,33 @@ import {initMarkupContent} from '../markup/content.ts'; import {attachRefIssueContextPopup} from './contextpopup.ts'; import {POST} from '../modules/fetch.ts'; import {initDropzone} from './dropzone.ts'; +import {confirmModal} from './comp/ConfirmModal.ts'; +import {applyAreYouSure} from '../vendor/jquery.are-you-sure.ts'; +import {fomanticQuery} from '../modules/fomantic/base.ts'; -function initEditPreviewTab($form) { - const $tabMenu = $form.find('.repo-editor-menu'); - $tabMenu.find('.item').tab(); - const $previewTab = $tabMenu.find('a[data-tab="preview"]'); - if ($previewTab.length) { - $previewTab.on('click', async function () { - const $this = $(this); - let context = `${$this.data('context')}/`; - const mode = $this.data('markup-mode') || 'comment'; - const $treePathEl = $form.find('input#tree_path'); - if ($treePathEl.length > 0) { - context += $treePathEl.val(); - } - context = context.substring(0, context.lastIndexOf('/')); +function initEditPreviewTab(elForm: HTMLFormElement) { + const elTabMenu = elForm.querySelector('.repo-editor-menu'); + fomanticQuery(elTabMenu.querySelectorAll('.item')).tab(); - const formData = new FormData(); - formData.append('mode', mode); - formData.append('context', context); - formData.append('text', $form.find('.tab[data-tab="write"] textarea').val()); - formData.append('file_path', $treePathEl.val()); - try { - const response = await POST($this.data('url'), {data: formData}); - const data = await response.text(); - const $previewPanel = $form.find('.tab[data-tab="preview"]'); - if ($previewPanel.length) { - renderPreviewPanelContent($previewPanel, data); - } - } catch (error) { - console.error('Error:', error); - } - }); - } + const elPreviewTab = elTabMenu.querySelector('a[data-tab="preview"]'); + const elPreviewPanel = elForm.querySelector('.tab[data-tab="preview"]'); + if (!elPreviewTab || !elPreviewPanel) return; + + elPreviewTab.addEventListener('click', async () => { + const elTreePath = elForm.querySelector('input#tree_path'); + const previewUrl = elPreviewTab.getAttribute('data-preview-url'); + const previewContextRef = elPreviewTab.getAttribute('data-preview-context-ref'); + let previewContext = `${previewContextRef}/${elTreePath.value}`; + previewContext = previewContext.substring(0, previewContext.lastIndexOf('/')); + const formData = new FormData(); + formData.append('mode', 'file'); + formData.append('context', previewContext); + formData.append('text', elForm.querySelector('.tab[data-tab="write"] textarea').value); + formData.append('file_path', elTreePath.value); + const response = await POST(previewUrl, {data: formData}); + const data = await response.text(); + renderPreviewPanelContent(elPreviewPanel, data); + }); } export function initRepoEditor() { @@ -151,8 +144,8 @@ export function initRepoEditor() { } }); - const $form = $('.repository.editor .edit.form'); - initEditPreviewTab($form); + const elForm = document.querySelector('.repository.editor .edit.form'); + initEditPreviewTab(elForm); (async () => { const editor = await createCodeEditor(editArea, filenameInput); @@ -160,16 +153,16 @@ export function initRepoEditor() { // Using events from https://github.com/codedance/jquery.AreYouSure#advanced-usage // to enable or disable the commit button const commitButton = document.querySelector('#commit-button'); - const $editForm = $('.ui.edit.form'); const dirtyFileClass = 'dirty-file'; // Disabling the button at the start - if ($('input[name="page_has_posted"]').val() !== 'true') { + if (document.querySelector('input[name="page_has_posted"]').value !== 'true') { commitButton.disabled = true; } // Registering a custom listener for the file path and the file content - $editForm.areYouSure({ + // FIXME: it is not quite right here (old bug), it causes double-init, the global areYouSure "dirty" class will also be added + applyAreYouSure(elForm, { silent: true, dirtyClass: dirtyFileClass, fieldSelector: ':input:not(.commit-form-wrapper :input)', @@ -187,15 +180,17 @@ export function initRepoEditor() { editor.setValue(value); } - commitButton?.addEventListener('click', (e) => { + commitButton?.addEventListener('click', async (e) => { // A modal which asks if an empty file should be committed if (!editArea.value) { - $('#edit-empty-content-modal').modal({ - onApprove() { - $('.edit.form').trigger('submit'); - }, - }).modal('show'); e.preventDefault(); + if (await confirmModal({ + header: elForm.getAttribute('data-text-empty-confirm-header'), + content: elForm.getAttribute('data-text-empty-confirm-content'), + })) { + elForm.classList.remove('dirty'); + elForm.submit(); + } } }); })(); diff --git a/web_src/js/vendor/jquery.are-you-sure.ts b/web_src/js/vendor/jquery.are-you-sure.ts index bd621a145e..9efe783c54 100644 --- a/web_src/js/vendor/jquery.are-you-sure.ts +++ b/web_src/js/vendor/jquery.are-you-sure.ts @@ -196,6 +196,6 @@ export function initAreYouSure($) { }; } -export function applyAreYouSure(selector: string) { - $(selector).areYouSure(); +export function applyAreYouSure(selectorOrEl: string|Element|$, opts = {}) { + $(selectorOrEl).areYouSure(opts); }