mirror of https://github.com/go-gitea/gitea.git
Merge branch 'main' into lunny/fix_move_column
This commit is contained in:
commit
ed67149046
|
@ -4,29 +4,36 @@
|
||||||
</div>
|
</div>
|
||||||
{{end}}
|
{{end}}
|
||||||
<div class="issue-title-header">
|
<div class="issue-title-header">
|
||||||
<div class="issue-title" id="issue-title-wrapper">
|
{{$canEditIssueTitle := and (or .HasIssuesOrPullsWritePermission .IsIssuePoster) (not .Repository.IsArchived)}}
|
||||||
|
<div class="issue-title" id="issue-title-display">
|
||||||
<h1 class="gt-word-break">
|
<h1 class="gt-word-break">
|
||||||
<span id="issue-title">{{RenderIssueTitle $.Context .Issue.Title ($.Repository.ComposeMetas ctx) | RenderCodeBlock}} <span class="index">#{{.Issue.Index}}</span>
|
{{RenderIssueTitle $.Context .Issue.Title ($.Repository.ComposeMetas ctx) | RenderCodeBlock}}
|
||||||
</span>
|
<span class="index">#{{.Issue.Index}}</span>
|
||||||
<div id="edit-title-input" class="ui input tw-flex-1 tw-hidden">
|
|
||||||
<input value="{{.Issue.Title}}" maxlength="255" autocomplete="off">
|
|
||||||
</div>
|
|
||||||
</h1>
|
</h1>
|
||||||
<div class="issue-title-buttons">
|
<div class="issue-title-buttons">
|
||||||
{{if and (or .HasIssuesOrPullsWritePermission .IsIssuePoster) (not .Repository.IsArchived)}}
|
{{if $canEditIssueTitle}}
|
||||||
<button id="edit-title" class="ui small basic button edit-button not-in-edit{{if .Issue.IsPull}} tw-mr-0{{end}}">{{ctx.Locale.Tr "repo.issues.edit"}}</button>
|
<button id="issue-title-edit-show" class="ui small basic button">{{ctx.Locale.Tr "repo.issues.edit"}}</button>
|
||||||
{{end}}
|
{{end}}
|
||||||
{{if not .Issue.IsPull}}
|
{{if not .Issue.IsPull}}
|
||||||
<a role="button" class="ui small primary button new-issue-button tw-mr-0" href="{{.RepoLink}}/issues/new{{if .NewIssueChooseTemplate}}/choose{{end}}">{{ctx.Locale.Tr "repo.issues.new"}}</a>
|
<a role="button" class="ui small primary button" href="{{.RepoLink}}/issues/new{{if .NewIssueChooseTemplate}}/choose{{end}}">{{ctx.Locale.Tr "repo.issues.new"}}</a>
|
||||||
{{end}}
|
{{end}}
|
||||||
</div>
|
</div>
|
||||||
{{if and (or .HasIssuesOrPullsWritePermission .IsIssuePoster) (not .Repository.IsArchived)}}
|
|
||||||
<div class="edit-buttons">
|
|
||||||
<button id="cancel-edit-title" class="ui small basic button in-edit tw-hidden">{{ctx.Locale.Tr "repo.issues.cancel"}}</button>
|
|
||||||
<button id="save-edit-title" class="ui small primary button in-edit tw-hidden tw-mr-0" data-update-url="{{$.RepoLink}}/issues/{{.Issue.Index}}/title" {{if .Issue.IsPull}}data-target-update-url="{{$.RepoLink}}/pull/{{.Issue.Index}}/target_branch"{{end}}>{{ctx.Locale.Tr "repo.issues.save"}}</button>
|
|
||||||
</div>
|
|
||||||
{{end}}
|
|
||||||
</div>
|
</div>
|
||||||
|
{{if $canEditIssueTitle}}
|
||||||
|
<div class="ui form issue-title tw-hidden" id="issue-title-editor">
|
||||||
|
<div class="ui input tw-flex-1">
|
||||||
|
<input value="{{.Issue.Title}}" data-old-title="{{.Issue.Title}}" maxlength="255" autocomplete="off">
|
||||||
|
</div>
|
||||||
|
<div class="issue-title-buttons">
|
||||||
|
<button class="ui small basic cancel button">{{ctx.Locale.Tr "repo.issues.cancel"}}</button>
|
||||||
|
<button class="ui small primary button"
|
||||||
|
data-update-url="{{$.RepoLink}}/issues/{{.Issue.Index}}/title"
|
||||||
|
{{if .Issue.IsPull}}data-target-update-url="{{$.RepoLink}}/pull/{{.Issue.Index}}/target_branch"{{end}}>
|
||||||
|
{{ctx.Locale.Tr "repo.issues.save"}}
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{{end}}
|
||||||
<div class="issue-title-meta">
|
<div class="issue-title-meta">
|
||||||
{{if .HasMerged}}
|
{{if .HasMerged}}
|
||||||
<div class="ui purple label issue-state-label">{{svg "octicon-git-merge" 16 "tw-mr-1"}} {{if eq .Issue.PullRequest.Status 3}}{{ctx.Locale.Tr "repo.pulls.manually_merged"}}{{else}}{{ctx.Locale.Tr "repo.pulls.merged"}}{{end}}</div>
|
<div class="ui purple label issue-state-label">{{svg "octicon-git-merge" 16 "tw-mr-1"}} {{if eq .Issue.PullRequest.Status 3}}{{ctx.Locale.Tr "repo.pulls.manually_merged"}}{{else}}{{ctx.Locale.Tr "repo.pulls.merged"}}{{end}}</div>
|
||||||
|
@ -63,14 +70,14 @@
|
||||||
{{end}}
|
{{end}}
|
||||||
{{else}}
|
{{else}}
|
||||||
{{if .Issue.OriginalAuthor}}
|
{{if .Issue.OriginalAuthor}}
|
||||||
<span id="pull-desc" class="pull-desc">{{.Issue.OriginalAuthor}} {{ctx.Locale.Tr "repo.pulls.title_desc" .NumCommits $headHref $baseHref}}</span>
|
<span id="pull-desc-display" class="pull-desc">{{.Issue.OriginalAuthor}} {{ctx.Locale.Tr "repo.pulls.title_desc" .NumCommits $headHref $baseHref}}</span>
|
||||||
{{else}}
|
{{else}}
|
||||||
<span id="pull-desc" class="pull-desc">
|
<span id="pull-desc-display" class="pull-desc">
|
||||||
<a {{if gt .Issue.Poster.ID 0}}href="{{.Issue.Poster.HomeLink}}"{{end}}>{{.Issue.Poster.GetDisplayName}}</a>
|
<a {{if gt .Issue.Poster.ID 0}}href="{{.Issue.Poster.HomeLink}}"{{end}}>{{.Issue.Poster.GetDisplayName}}</a>
|
||||||
{{ctx.Locale.Tr "repo.pulls.title_desc" .NumCommits $headHref $baseHref}}
|
{{ctx.Locale.Tr "repo.pulls.title_desc" .NumCommits $headHref $baseHref}}
|
||||||
</span>
|
</span>
|
||||||
{{end}}
|
{{end}}
|
||||||
<span id="pull-desc-edit" class="tw-hidden flex-text-block">
|
<span id="pull-desc-editor" class="tw-hidden flex-text-block">
|
||||||
<div class="ui floating filter dropdown">
|
<div class="ui floating filter dropdown">
|
||||||
<div class="ui basic small button tw-mr-0">
|
<div class="ui basic small button tw-mr-0">
|
||||||
<span class="text">{{ctx.Locale.Tr "repo.pulls.compare_compare"}}: {{$.HeadTarget}}</span>
|
<span class="text">{{ctx.Locale.Tr "repo.pulls.compare_compare"}}: {{$.HeadTarget}}</span>
|
||||||
|
|
|
@ -144,7 +144,7 @@ func testNewIssue(t *testing.T, session *TestSession, user, repo, title, content
|
||||||
resp = session.MakeRequest(t, req, http.StatusOK)
|
resp = session.MakeRequest(t, req, http.StatusOK)
|
||||||
|
|
||||||
htmlDoc = NewHTMLParser(t, resp.Body)
|
htmlDoc = NewHTMLParser(t, resp.Body)
|
||||||
val := htmlDoc.doc.Find("#issue-title").Text()
|
val := htmlDoc.doc.Find("#issue-title-display").Text()
|
||||||
assert.Contains(t, val, title)
|
assert.Contains(t, val, title)
|
||||||
val = htmlDoc.doc.Find(".comment .render-content p").First().Text()
|
val = htmlDoc.doc.Find(".comment .render-content p").First().Text()
|
||||||
assert.Equal(t, content, val)
|
assert.Equal(t, content, val)
|
||||||
|
|
|
@ -125,7 +125,7 @@ func TestPullCreate_TitleEscape(t *testing.T) {
|
||||||
req := NewRequest(t, "GET", url)
|
req := NewRequest(t, "GET", url)
|
||||||
resp = session.MakeRequest(t, req, http.StatusOK)
|
resp = session.MakeRequest(t, req, http.StatusOK)
|
||||||
htmlDoc := NewHTMLParser(t, resp.Body)
|
htmlDoc := NewHTMLParser(t, resp.Body)
|
||||||
editTestTitleURL, exists := htmlDoc.doc.Find("#save-edit-title").First().Attr("data-update-url")
|
editTestTitleURL, exists := htmlDoc.doc.Find(".issue-title-buttons button[data-update-url]").First().Attr("data-update-url")
|
||||||
assert.True(t, exists, "The template has changed")
|
assert.True(t, exists, "The template has changed")
|
||||||
|
|
||||||
req = NewRequestWithValues(t, "POST", editTestTitleURL, map[string]string{
|
req = NewRequestWithValues(t, "POST", editTestTitleURL, map[string]string{
|
||||||
|
|
|
@ -575,34 +575,7 @@ td .commit-summary {
|
||||||
display: inline-block;
|
display: inline-block;
|
||||||
}
|
}
|
||||||
|
|
||||||
.issue-title-header {
|
|
||||||
width: 100%;
|
|
||||||
padding-bottom: 4px;
|
|
||||||
margin-bottom: 1rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
.issue-title-meta {
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
}
|
|
||||||
|
|
||||||
.repository.view.issue .issue-title-buttons,
|
|
||||||
.repository.view.issue .edit-buttons {
|
|
||||||
display: flex;
|
|
||||||
}
|
|
||||||
|
|
||||||
@media (max-width: 767.98px) {
|
@media (max-width: 767.98px) {
|
||||||
.repository.view.issue .issue-title {
|
|
||||||
flex-direction: column;
|
|
||||||
}
|
|
||||||
.repository.view.issue .issue-title-buttons,
|
|
||||||
.repository.view.issue .edit-buttons {
|
|
||||||
width: 100%;
|
|
||||||
justify-content: space-between;
|
|
||||||
}
|
|
||||||
.repository.view.issue .edit-buttons {
|
|
||||||
margin-top: .5rem;
|
|
||||||
}
|
|
||||||
.comment.form .issue-content-left .avatar {
|
.comment.form .issue-content-left .avatar {
|
||||||
display: none;
|
display: none;
|
||||||
}
|
}
|
||||||
|
@ -617,15 +590,37 @@ td .commit-summary {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* issue title & meta & edit */
|
||||||
|
.issue-title-header {
|
||||||
|
width: 100%;
|
||||||
|
padding-bottom: 4px;
|
||||||
|
margin-bottom: 1rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.issue-title-meta {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.repository.view.issue .issue-title-buttons {
|
||||||
|
display: flex;
|
||||||
|
gap: 0.5em;
|
||||||
|
}
|
||||||
|
|
||||||
|
.repository.view.issue .issue-title-buttons > .ui.button {
|
||||||
|
margin: 0;
|
||||||
|
height: 35px;
|
||||||
|
}
|
||||||
|
|
||||||
.repository.view.issue .issue-title {
|
.repository.view.issue .issue-title {
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
|
gap: 0.5em;
|
||||||
margin-bottom: 8px;
|
margin-bottom: 8px;
|
||||||
|
min-height: 40px; /* avoid layout shift on edit */
|
||||||
}
|
}
|
||||||
|
|
||||||
.repository.view.issue .issue-title h1 {
|
.repository.view.issue .issue-title h1 {
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
flex: 1;
|
flex: 1;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
font-weight: var(--font-weight-normal);
|
font-weight: var(--font-weight-normal);
|
||||||
|
@ -633,14 +628,24 @@ td .commit-summary {
|
||||||
line-height: 40px;
|
line-height: 40px;
|
||||||
margin: 0;
|
margin: 0;
|
||||||
padding-right: 0.25rem;
|
padding-right: 0.25rem;
|
||||||
min-height: 41px; /* avoid layout shift on edit */
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.repository.view.issue .issue-title h1 .ui.input {
|
@media (max-width: 767.98px) {
|
||||||
font-size: 0.5em;
|
.repository.view.issue .issue-title {
|
||||||
|
flex-direction: column;
|
||||||
|
}
|
||||||
|
.repository.view.issue .issue-title-buttons {
|
||||||
|
width: 100%;
|
||||||
|
justify-content: space-between;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.repository.view.issue .issue-title h1 .ui.input input {
|
.repository.view.issue .issue-title .ui.input {
|
||||||
|
width: 100%;
|
||||||
|
height: 35px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.repository.view.issue .issue-title .ui.input input {
|
||||||
font-size: 1.5em;
|
font-size: 1.5em;
|
||||||
padding: 2px .5rem;
|
padding: 2px .5rem;
|
||||||
}
|
}
|
||||||
|
@ -653,10 +658,6 @@ td .commit-summary {
|
||||||
margin-right: 10px;
|
margin-right: 10px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.issue-title .edit-zone {
|
|
||||||
margin-top: 10px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.issue-state-label {
|
.issue-state-label {
|
||||||
display: flex !important;
|
display: flex !important;
|
||||||
align-items: center !important;
|
align-items: center !important;
|
||||||
|
|
|
@ -6,13 +6,20 @@
|
||||||
// This file must be imported before any lazy-loading is being attempted.
|
// This file must be imported before any lazy-loading is being attempted.
|
||||||
__webpack_public_path__ = `${window.config?.assetUrlPrefix ?? '/assets'}/`;
|
__webpack_public_path__ = `${window.config?.assetUrlPrefix ?? '/assets'}/`;
|
||||||
|
|
||||||
export function showGlobalErrorMessage(msg) {
|
function shouldIgnoreError(err) {
|
||||||
const pageContent = document.querySelector('.page-content');
|
const ignorePatterns = [
|
||||||
if (!pageContent) return;
|
'/assets/js/monaco.', // https://github.com/go-gitea/gitea/issues/30861 , https://github.com/microsoft/monaco-editor/issues/4496
|
||||||
|
];
|
||||||
|
for (const pattern of ignorePatterns) {
|
||||||
|
if (err.stack?.includes(pattern)) return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
// compact the message to a data attribute to avoid too many duplicated messages
|
export function showGlobalErrorMessage(msg) {
|
||||||
const msgCompact = msg.replace(/\W/g, '').trim();
|
const msgContainer = document.querySelector('.page-content') ?? document.body;
|
||||||
let msgDiv = pageContent.querySelector(`.js-global-error[data-global-error-msg-compact="${msgCompact}"]`);
|
const msgCompact = msg.replace(/\W/g, '').trim(); // compact the message to a data attribute to avoid too many duplicated messages
|
||||||
|
let msgDiv = msgContainer.querySelector(`.js-global-error[data-global-error-msg-compact="${msgCompact}"]`);
|
||||||
if (!msgDiv) {
|
if (!msgDiv) {
|
||||||
const el = document.createElement('div');
|
const el = document.createElement('div');
|
||||||
el.innerHTML = `<div class="ui container negative message center aligned js-global-error tw-mt-[15px] tw-whitespace-pre-line"></div>`;
|
el.innerHTML = `<div class="ui container negative message center aligned js-global-error tw-mt-[15px] tw-whitespace-pre-line"></div>`;
|
||||||
|
@ -23,7 +30,7 @@ export function showGlobalErrorMessage(msg) {
|
||||||
msgDiv.setAttribute(`data-global-error-msg-compact`, msgCompact);
|
msgDiv.setAttribute(`data-global-error-msg-compact`, msgCompact);
|
||||||
msgDiv.setAttribute(`data-global-error-msg-count`, msgCount.toString());
|
msgDiv.setAttribute(`data-global-error-msg-count`, msgCount.toString());
|
||||||
msgDiv.textContent = msg + (msgCount > 1 ? ` (${msgCount})` : '');
|
msgDiv.textContent = msg + (msgCount > 1 ? ` (${msgCount})` : '');
|
||||||
pageContent.prepend(msgDiv);
|
msgContainer.prepend(msgDiv);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -52,10 +59,12 @@ function processWindowErrorEvent({error, reason, message, type, filename, lineno
|
||||||
if (runModeIsProd) return;
|
if (runModeIsProd) return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// If the error stack trace does not include the base URL of our script assets, it likely came
|
if (err instanceof Error) {
|
||||||
// from a browser extension or inline script. Do not show such errors in production.
|
// If the error stack trace does not include the base URL of our script assets, it likely came
|
||||||
if (err instanceof Error && !err.stack?.includes(assetBaseUrl) && runModeIsProd) {
|
// from a browser extension or inline script. Do not show such errors in production.
|
||||||
return;
|
if (!err.stack?.includes(assetBaseUrl) && runModeIsProd) return;
|
||||||
|
// Ignore some known errors that are unable to fix
|
||||||
|
if (shouldIgnoreError(err)) return;
|
||||||
}
|
}
|
||||||
|
|
||||||
let msg = err?.message ?? message;
|
let msg = err?.message ?? message;
|
||||||
|
|
|
@ -67,7 +67,7 @@ export default {
|
||||||
const weekValues = Object.values(this.data);
|
const weekValues = Object.values(this.data);
|
||||||
const start = weekValues[0].week;
|
const start = weekValues[0].week;
|
||||||
const end = firstStartDateAfterDate(new Date());
|
const end = firstStartDateAfterDate(new Date());
|
||||||
const startDays = startDaysBetween(new Date(start), new Date(end));
|
const startDays = startDaysBetween(start, end);
|
||||||
this.data = fillEmptyStartDaysWithZeroes(startDays, this.data);
|
this.data = fillEmptyStartDaysWithZeroes(startDays, this.data);
|
||||||
this.errorText = '';
|
this.errorText = '';
|
||||||
} else {
|
} else {
|
||||||
|
|
|
@ -114,7 +114,7 @@ export default {
|
||||||
const weekValues = Object.values(total.weeks);
|
const weekValues = Object.values(total.weeks);
|
||||||
this.xAxisStart = weekValues[0].week;
|
this.xAxisStart = weekValues[0].week;
|
||||||
this.xAxisEnd = firstStartDateAfterDate(new Date());
|
this.xAxisEnd = firstStartDateAfterDate(new Date());
|
||||||
const startDays = startDaysBetween(new Date(this.xAxisStart), new Date(this.xAxisEnd));
|
const startDays = startDaysBetween(this.xAxisStart, this.xAxisEnd);
|
||||||
total.weeks = fillEmptyStartDaysWithZeroes(startDays, total.weeks);
|
total.weeks = fillEmptyStartDaysWithZeroes(startDays, total.weeks);
|
||||||
this.xAxisMin = this.xAxisStart;
|
this.xAxisMin = this.xAxisStart;
|
||||||
this.xAxisMax = this.xAxisEnd;
|
this.xAxisMax = this.xAxisEnd;
|
||||||
|
|
|
@ -62,7 +62,7 @@ export default {
|
||||||
const data = await response.json();
|
const data = await response.json();
|
||||||
const start = Object.values(data)[0].week;
|
const start = Object.values(data)[0].week;
|
||||||
const end = firstStartDateAfterDate(new Date());
|
const end = firstStartDateAfterDate(new Date());
|
||||||
const startDays = startDaysBetween(new Date(start), new Date(end));
|
const startDays = startDaysBetween(start, end);
|
||||||
this.data = fillEmptyStartDaysWithZeroes(startDays, data).slice(-52);
|
this.data = fillEmptyStartDaysWithZeroes(startDays, data).slice(-52);
|
||||||
this.errorText = '';
|
this.errorText = '';
|
||||||
} else {
|
} else {
|
||||||
|
|
|
@ -47,10 +47,18 @@ export function initFootLanguageMenu() {
|
||||||
|
|
||||||
export function initGlobalEnterQuickSubmit() {
|
export function initGlobalEnterQuickSubmit() {
|
||||||
document.addEventListener('keydown', (e) => {
|
document.addEventListener('keydown', (e) => {
|
||||||
const isQuickSubmitEnter = ((e.ctrlKey && !e.altKey) || e.metaKey) && (e.key === 'Enter');
|
if (e.key !== 'Enter') return;
|
||||||
if (isQuickSubmitEnter && e.target.matches('textarea')) {
|
const hasCtrlOrMeta = ((e.ctrlKey || e.metaKey) && !e.altKey);
|
||||||
e.preventDefault();
|
if (hasCtrlOrMeta && e.target.matches('textarea')) {
|
||||||
handleGlobalEnterQuickSubmit(e.target);
|
if (handleGlobalEnterQuickSubmit(e.target)) {
|
||||||
|
e.preventDefault();
|
||||||
|
}
|
||||||
|
} else if (e.target.matches('input') && !e.target.closest('form')) {
|
||||||
|
// input in a normal form could handle Enter key by default, so we only handle the input outside a form
|
||||||
|
// eslint-disable-next-line unicorn/no-lonely-if
|
||||||
|
if (handleGlobalEnterQuickSubmit(e.target)) {
|
||||||
|
e.preventDefault();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,16 +3,17 @@ export function handleGlobalEnterQuickSubmit(target) {
|
||||||
if (form) {
|
if (form) {
|
||||||
if (!form.checkValidity()) {
|
if (!form.checkValidity()) {
|
||||||
form.reportValidity();
|
form.reportValidity();
|
||||||
return;
|
} else {
|
||||||
|
// 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.dispatchEvent(new SubmitEvent('submit', {bubbles: true, cancelable: true}));
|
||||||
}
|
}
|
||||||
|
return true;
|
||||||
// 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.dispatchEvent(new SubmitEvent('submit', {bubbles: true, cancelable: true}));
|
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
form = target.closest('.ui.form');
|
form = target.closest('.ui.form');
|
||||||
if (form) {
|
if (form) {
|
||||||
form.querySelector('.ui.primary.button')?.click();
|
form.querySelector('.ui.primary.button')?.click();
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
|
|
|
@ -7,6 +7,7 @@ import {getComboMarkdownEditor, initComboMarkdownEditor} from './comp/ComboMarkd
|
||||||
import {toAbsoluteUrl} from '../utils.js';
|
import {toAbsoluteUrl} from '../utils.js';
|
||||||
import {initDropzone} from './common-global.js';
|
import {initDropzone} from './common-global.js';
|
||||||
import {POST, GET} from '../modules/fetch.js';
|
import {POST, GET} from '../modules/fetch.js';
|
||||||
|
import {showErrorToast} from '../modules/toast.js';
|
||||||
|
|
||||||
const {appSubUrl} = window.config;
|
const {appSubUrl} = window.config;
|
||||||
|
|
||||||
|
@ -602,85 +603,69 @@ export function initRepoIssueWipToggle() {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
async function pullrequest_targetbranch_change(update_url) {
|
|
||||||
const targetBranch = $('#pull-target-branch').data('branch');
|
|
||||||
const $branchTarget = $('#branch_target');
|
|
||||||
if (targetBranch === $branchTarget.text()) {
|
|
||||||
window.location.reload();
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
try {
|
|
||||||
await POST(update_url, {data: new URLSearchParams({target_branch: targetBranch})});
|
|
||||||
} catch (error) {
|
|
||||||
console.error(error);
|
|
||||||
} finally {
|
|
||||||
window.location.reload();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export function initRepoIssueTitleEdit() {
|
export function initRepoIssueTitleEdit() {
|
||||||
// Edit issue title
|
const issueTitleDisplay = document.querySelector('#issue-title-display');
|
||||||
const $issueTitle = $('#issue-title');
|
const issueTitleEditor = document.querySelector('#issue-title-editor');
|
||||||
const $editInput = $('#edit-title-input input');
|
if (!issueTitleEditor) return;
|
||||||
|
|
||||||
const editTitleToggle = function () {
|
const issueTitleInput = issueTitleEditor.querySelector('input');
|
||||||
toggleElem($issueTitle);
|
const oldTitle = issueTitleInput.getAttribute('data-old-title');
|
||||||
toggleElem('.not-in-edit');
|
issueTitleDisplay.querySelector('#issue-title-edit-show').addEventListener('click', () => {
|
||||||
toggleElem('#edit-title-input');
|
hideElem(issueTitleDisplay);
|
||||||
toggleElem('#pull-desc');
|
hideElem('#pull-desc-display');
|
||||||
toggleElem('#pull-desc-edit');
|
showElem(issueTitleEditor);
|
||||||
toggleElem('.in-edit');
|
showElem('#pull-desc-editor');
|
||||||
toggleElem('.new-issue-button');
|
if (!issueTitleInput.value.trim()) {
|
||||||
document.getElementById('issue-title-wrapper')?.classList.toggle('edit-active');
|
issueTitleInput.value = oldTitle;
|
||||||
$editInput[0].focus();
|
}
|
||||||
$editInput[0].select();
|
issueTitleInput.focus();
|
||||||
return false;
|
});
|
||||||
};
|
issueTitleEditor.querySelector('.ui.cancel.button').addEventListener('click', () => {
|
||||||
|
hideElem(issueTitleEditor);
|
||||||
$('#edit-title').on('click', editTitleToggle);
|
hideElem('#pull-desc-editor');
|
||||||
$('#cancel-edit-title').on('click', editTitleToggle);
|
showElem(issueTitleDisplay);
|
||||||
$('#save-edit-title').on('click', editTitleToggle).on('click', async function () {
|
showElem('#pull-desc-display');
|
||||||
const pullrequest_target_update_url = this.getAttribute('data-target-update-url');
|
});
|
||||||
if (!$editInput.val().length || $editInput.val() === $issueTitle.text()) {
|
const editSaveButton = issueTitleEditor.querySelector('.ui.primary.button');
|
||||||
$editInput.val($issueTitle.text());
|
editSaveButton.addEventListener('click', async () => {
|
||||||
await pullrequest_targetbranch_change(pullrequest_target_update_url);
|
const prTargetUpdateUrl = editSaveButton.getAttribute('data-target-update-url');
|
||||||
} else {
|
const newTitle = issueTitleInput.value.trim();
|
||||||
try {
|
try {
|
||||||
const params = new URLSearchParams();
|
if (newTitle && newTitle !== oldTitle) {
|
||||||
params.append('title', $editInput.val());
|
const resp = await POST(editSaveButton.getAttribute('data-update-url'), {data: new URLSearchParams({title: newTitle})});
|
||||||
const response = await POST(this.getAttribute('data-update-url'), {data: params});
|
if (!resp.ok) {
|
||||||
const data = await response.json();
|
throw new Error(`Failed to update issue title: ${resp.statusText}`);
|
||||||
$editInput.val(data.title);
|
}
|
||||||
$issueTitle.text(data.title);
|
}
|
||||||
if (pullrequest_target_update_url) {
|
if (prTargetUpdateUrl) {
|
||||||
await pullrequest_targetbranch_change(pullrequest_target_update_url); // it will reload the window
|
const newTargetBranch = document.querySelector('#pull-target-branch').getAttribute('data-branch');
|
||||||
} else {
|
const oldTargetBranch = document.querySelector('#branch_target').textContent;
|
||||||
window.location.reload();
|
if (newTargetBranch !== oldTargetBranch) {
|
||||||
}
|
const resp = await POST(prTargetUpdateUrl, {data: new URLSearchParams({target_branch: newTargetBranch})});
|
||||||
} catch (error) {
|
if (!resp.ok) {
|
||||||
console.error(error);
|
throw new Error(`Failed to update PR target branch: ${resp.statusText}`);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
window.location.reload();
|
||||||
|
} catch (error) {
|
||||||
|
console.error(error);
|
||||||
|
showErrorToast(error.message);
|
||||||
}
|
}
|
||||||
return false;
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
export function initRepoIssueBranchSelect() {
|
export function initRepoIssueBranchSelect() {
|
||||||
const changeBranchSelect = function () {
|
document.querySelector('#branch-select')?.addEventListener('click', (e) => {
|
||||||
const $selectionTextField = $('#pull-target-branch');
|
const el = e.target.closest('.item[data-branch]');
|
||||||
|
if (!el) return;
|
||||||
const baseName = $selectionTextField.data('basename');
|
const pullTargetBranch = document.querySelector('#pull-target-branch');
|
||||||
const branchNameNew = $(this).data('branch');
|
const baseName = pullTargetBranch.getAttribute('data-basename');
|
||||||
const branchNameOld = $selectionTextField.data('branch');
|
const branchNameNew = el.getAttribute('data-branch');
|
||||||
|
const branchNameOld = pullTargetBranch.getAttribute('data-branch');
|
||||||
// Replace branch name to keep translation from HTML template
|
pullTargetBranch.textContent = pullTargetBranch.textContent.replace(`${baseName}:${branchNameOld}`, `${baseName}:${branchNameNew}`);
|
||||||
$selectionTextField.html($selectionTextField.html().replace(
|
pullTargetBranch.setAttribute('data-branch', branchNameNew);
|
||||||
`${baseName}:${branchNameOld}`,
|
});
|
||||||
`${baseName}:${branchNameNew}`,
|
|
||||||
));
|
|
||||||
$selectionTextField.data('branch', branchNameNew); // update branch name in setting
|
|
||||||
};
|
|
||||||
$('#branch-select > .item').on('click', changeBranchSelect);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export function initSingleCommentEditor($commentForm) {
|
export function initSingleCommentEditor($commentForm) {
|
||||||
|
|
|
@ -1,25 +1,30 @@
|
||||||
import dayjs from 'dayjs';
|
import dayjs from 'dayjs';
|
||||||
|
import utc from 'dayjs/plugin/utc.js';
|
||||||
import {getCurrentLocale} from '../utils.js';
|
import {getCurrentLocale} from '../utils.js';
|
||||||
|
|
||||||
// Returns an array of millisecond-timestamps of start-of-week days (Sundays)
|
dayjs.extend(utc);
|
||||||
export function startDaysBetween(startDate, endDate) {
|
|
||||||
// Ensure the start date is a Sunday
|
|
||||||
while (startDate.getDay() !== 0) {
|
|
||||||
startDate.setDate(startDate.getDate() + 1);
|
|
||||||
}
|
|
||||||
|
|
||||||
const start = dayjs(startDate);
|
/**
|
||||||
const end = dayjs(endDate);
|
* Returns an array of millisecond-timestamps of start-of-week days (Sundays)
|
||||||
const startDays = [];
|
*
|
||||||
|
* @param startConfig The start date. Can take any type that `Date` accepts.
|
||||||
|
* @param endConfig The end date. Can take any type that `Date` accepts.
|
||||||
|
*/
|
||||||
|
export function startDaysBetween(startDate, endDate) {
|
||||||
|
const start = dayjs.utc(startDate);
|
||||||
|
const end = dayjs.utc(endDate);
|
||||||
|
|
||||||
let current = start;
|
let current = start;
|
||||||
|
|
||||||
|
// Ensure the start date is a Sunday
|
||||||
|
while (current.day() !== 0) {
|
||||||
|
current = current.add(1, 'day');
|
||||||
|
}
|
||||||
|
|
||||||
|
const startDays = [];
|
||||||
while (current.isBefore(end)) {
|
while (current.isBefore(end)) {
|
||||||
startDays.push(current.valueOf());
|
startDays.push(current.valueOf());
|
||||||
// we are adding 7 * 24 hours instead of 1 week because we don't want
|
current = current.add(1, 'week');
|
||||||
// date library to use local time zone to calculate 1 week from now.
|
|
||||||
// local time zone is problematic because of daylight saving time (dst)
|
|
||||||
// used on some countries
|
|
||||||
current = current.add(7 * 24, 'hour');
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return startDays;
|
return startDays;
|
||||||
|
@ -29,10 +34,10 @@ export function firstStartDateAfterDate(inputDate) {
|
||||||
if (!(inputDate instanceof Date)) {
|
if (!(inputDate instanceof Date)) {
|
||||||
throw new Error('Invalid date');
|
throw new Error('Invalid date');
|
||||||
}
|
}
|
||||||
const dayOfWeek = inputDate.getDay();
|
const dayOfWeek = inputDate.getUTCDay();
|
||||||
const daysUntilSunday = 7 - dayOfWeek;
|
const daysUntilSunday = 7 - dayOfWeek;
|
||||||
const resultDate = new Date(inputDate.getTime());
|
const resultDate = new Date(inputDate.getTime());
|
||||||
resultDate.setDate(resultDate.getDate() + daysUntilSunday);
|
resultDate.setUTCDate(resultDate.getUTCDate() + daysUntilSunday);
|
||||||
return resultDate.valueOf();
|
return resultDate.valueOf();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue