Skip to content

Fix file editor & preview #32706

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 2 commits into from
Dec 4, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
28 changes: 5 additions & 23 deletions templates/repo/editor/edit.tmpl
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,10 @@
{{template "repo/header" .}}
<div class="ui container">
{{template "base/alert" .}}
<form class="ui edit form" method="post">
<form class="ui edit form" method="post"
data-text-empty-confirm-header="{{ctx.Locale.Tr "repo.editor.commit_empty_file_header"}}"
data-text-empty-confirm-content="{{ctx.Locale.Tr "repo.editor.commit_empty_file_text"}}"
>
{{.CsrfTokenHtml}}
<input type="hidden" name="last_commit" value="{{.last_commit}}">
<input type="hidden" name="page_has_posted" value="{{.PageHasPosted}}">
Expand All @@ -29,7 +32,7 @@
<div class="ui top attached header">
<div class="ui compact small menu small-menu-items repo-editor-menu">
<a class="active item" data-tab="write">{{svg "octicon-code"}} {{if .IsNewFile}}{{ctx.Locale.Tr "repo.editor.new_file"}}{{else}}{{ctx.Locale.Tr "repo.editor.edit_file"}}{{end}}</a>
<a class="item" data-tab="preview" data-url="{{.Repository.Link}}/markup" data-context="{{.RepoLink}}/src/{{.BranchNameSubURL}}" data-markup-mode="file">{{svg "octicon-eye"}} {{ctx.Locale.Tr "preview"}}</a>
<a class="item" data-tab="preview" data-preview-url="{{.Repository.Link}}/markup" data-preview-context-ref="{{.RepoLink}}/src/{{.BranchNameSubURL}}">{{svg "octicon-eye"}} {{ctx.Locale.Tr "preview"}}</a>
{{if not .IsNewFile}}
<a class="item" data-tab="diff" hx-params="context,content" hx-vals='{"context":"{{.BranchLink}}"}' hx-include="#edit_area" hx-swap="innerHTML" hx-target=".tab[data-tab='diff']" hx-indicator=".tab[data-tab='diff']" hx-post="{{.RepoLink}}/_preview/{{.BranchName | PathEscapeSegments}}/{{.TreePath | PathEscapeSegments}}">{{svg "octicon-diff"}} {{ctx.Locale.Tr "repo.editor.preview_changes"}}</a>
{{end}}
Expand All @@ -38,8 +41,6 @@
<div class="ui bottom attached segment tw-p-0">
<div class="ui active tab tw-rounded-b" data-tab="write">
<textarea id="edit_area" name="content" class="tw-hidden" data-id="repo-{{.Repository.Name}}-{{.TreePath}}"
data-url="{{.Repository.Link}}/markup"
data-context="{{.RepoLink}}"
data-previewable-extensions="{{.PreviewableExtensions}}"
data-line-wrap-extensions="{{.LineWrapExtensions}}">{{.FileContent}}</textarea>
<div class="editor-loading is-loading"></div>
Expand All @@ -55,24 +56,5 @@
{{template "repo/editor/commit_form" .}}
</form>
</div>
<div class="ui g-modal-confirm modal" id="edit-empty-content-modal">
<div class="header">
{{svg "octicon-file"}}
{{ctx.Locale.Tr "repo.editor.commit_empty_file_header"}}
</div>
<div class="center content">
<p>{{ctx.Locale.Tr "repo.editor.commit_empty_file_text"}}</p>
</div>
<div class="actions">
<button class="ui cancel button">
{{svg "octicon-x"}}
{{ctx.Locale.Tr "repo.editor.cancel"}}
</button>
<button class="ui primary ok button">
{{svg "fontawesome-save"}}
{{ctx.Locale.Tr "repo.editor.commit_changes"}}
</button>
</div>
</div>
</div>
{{template "base/footer" .}}
25 changes: 4 additions & 21 deletions templates/repo/editor/patch.tmpl
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,10 @@
{{template "repo/header" .}}
<div class="ui container">
{{template "base/alert" .}}
<form class="ui edit form" method="post" action="{{.RepoLink}}/_diffpatch/{{.BranchName | PathEscapeSegments}}">
<form class="ui edit form" method="post" action="{{.RepoLink}}/_diffpatch/{{.BranchName | PathEscapeSegments}}"
data-text-empty-confirm-header="{{ctx.Locale.Tr "repo.editor.commit_empty_file_header"}}"
data-text-empty-confirm-content="{{ctx.Locale.Tr "repo.editor.commit_empty_file_text"}}"
>
{{.CsrfTokenHtml}}
<input type="hidden" name="last_commit" value="{{.last_commit}}">
<input type="hidden" name="page_has_posted" value="{{.PageHasPosted}}">
Expand Down Expand Up @@ -33,25 +36,5 @@
{{template "repo/editor/commit_form" .}}
</form>
</div>

<div class="ui g-modal-confirm modal" id="edit-empty-content-modal">
<div class="header">
{{svg "octicon-file"}}
{{ctx.Locale.Tr "repo.editor.commit_empty_file_header"}}
</div>
<div class="center content">
<p>{{ctx.Locale.Tr "repo.editor.commit_empty_file_text"}}</p>
</div>
<div class="actions">
<button class="ui cancel button">
{{svg "octicon-x"}}
{{ctx.Locale.Tr "repo.editor.cancel"}}
</button>
<button class="ui primary ok button">
{{svg "fontawesome-save"}}
{{ctx.Locale.Tr "repo.editor.commit_changes"}}
</button>
</div>
</div>
</div>
{{template "base/footer" .}}
6 changes: 2 additions & 4 deletions web_src/js/features/codeeditor.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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<HTMLElement>('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<HTMLElement>('a[data-tab="write"]');
writeTab.click();
}
}
Expand Down
2 changes: 1 addition & 1 deletion web_src/js/features/comp/ConfirmModal.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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<boolean> {
return new Promise((resolve) => {
const headerHtml = header ? `<div class="header">${htmlEscape(header)}</div>` : '';
const modal = createElementFromHTML(`
Expand Down
81 changes: 38 additions & 43 deletions web_src/js/features/repo-editor.ts
Original file line number Diff line number Diff line change
@@ -1,44 +1,37 @@
import $ from 'jquery';
import {htmlEscape} from 'escape-goat';
import {createCodeEditor} from './codeeditor.ts';
import {hideElem, queryElems, showElem, createElementFromHTML} from '../utils/dom.ts';
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<HTMLInputElement>('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<HTMLTextAreaElement>('.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() {
Expand Down Expand Up @@ -151,25 +144,25 @@ export function initRepoEditor() {
}
});

const $form = $('.repository.editor .edit.form');
initEditPreviewTab($form);
const elForm = document.querySelector<HTMLFormElement>('.repository.editor .edit.form');
initEditPreviewTab(elForm);

(async () => {
const editor = await createCodeEditor(editArea, filenameInput);

// Using events from https://github.com/codedance/jquery.AreYouSure#advanced-usage
// to enable or disable the commit button
const commitButton = document.querySelector<HTMLButtonElement>('#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<HTMLInputElement>('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)',
Expand All @@ -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();
}
}
});
})();
Expand Down
4 changes: 2 additions & 2 deletions web_src/js/vendor/jquery.are-you-sure.ts
Original file line number Diff line number Diff line change
Expand Up @@ -196,6 +196,6 @@ export function initAreYouSure($) {
};
}

export function applyAreYouSure(selector: string) {
$(selector).areYouSure();
export function applyAreYouSure(selectorOrEl: string|Element|$, opts = {}) {
$(selectorOrEl).areYouSure(opts);
}
Loading