@ -7,7 +7,6 @@ import {
queryElems ,
queryElems ,
showElem ,
showElem ,
toggleElem ,
toggleElem ,
type DOMEvent ,
} from '../utils/dom.ts' ;
} from '../utils/dom.ts' ;
import { setFileFolding } from './file-fold.ts' ;
import { setFileFolding } from './file-fold.ts' ;
import { ComboMarkdownEditor , getComboMarkdownEditor , initComboMarkdownEditor } from './comp/ComboMarkdownEditor.ts' ;
import { ComboMarkdownEditor , getComboMarkdownEditor , initComboMarkdownEditor } from './comp/ComboMarkdownEditor.ts' ;
@ -67,7 +66,7 @@ function initRepoIssueLabelFilter(elDropdown: HTMLElement) {
const excludeLabel = ( e : MouseEvent | KeyboardEvent , item : Element ) = > {
const excludeLabel = ( e : MouseEvent | KeyboardEvent , item : Element ) = > {
e . preventDefault ( ) ;
e . preventDefault ( ) ;
e . stopPropagation ( ) ;
e . stopPropagation ( ) ;
const labelId = item . getAttribute ( 'data-label-id' ) ;
const labelId = item . getAttribute ( 'data-label-id' ) ! ;
let labelIds : string [ ] = queryLabels ? queryLabels . split ( ',' ) : [ ] ;
let labelIds : string [ ] = queryLabels ? queryLabels . split ( ',' ) : [ ] ;
labelIds = labelIds . filter ( ( id ) = > Math . abs ( parseInt ( id ) ) !== Math . abs ( parseInt ( labelId ) ) ) ;
labelIds = labelIds . filter ( ( id ) = > Math . abs ( parseInt ( id ) ) !== Math . abs ( parseInt ( labelId ) ) ) ;
labelIds . push ( ` - ${ labelId } ` ) ;
labelIds . push ( ` - ${ labelId } ` ) ;
@ -89,14 +88,14 @@ function initRepoIssueLabelFilter(elDropdown: HTMLElement) {
}
}
} ) ;
} ) ;
// no "labels" query parameter means "all issues"
// no "labels" query parameter means "all issues"
elDropdown . querySelector ( '.label-filter-query-default' ) . classList . toggle ( 'selected' , queryLabels === '' ) ;
elDropdown . querySelector ( '.label-filter-query-default' ) ! . classList . toggle ( 'selected' , queryLabels === '' ) ;
// "labels=0" query parameter means "issues without label"
// "labels=0" query parameter means "issues without label"
elDropdown . querySelector ( '.label-filter-query-not-set' ) . classList . toggle ( 'selected' , queryLabels === '0' ) ;
elDropdown . querySelector ( '.label-filter-query-not-set' ) ! . classList . toggle ( 'selected' , queryLabels === '0' ) ;
// prepare to process "archived" labels
// prepare to process "archived" labels
const elShowArchivedLabel = elDropdown . querySelector ( '.label-filter-archived-toggle' ) ;
const elShowArchivedLabel = elDropdown . querySelector ( '.label-filter-archived-toggle' ) ;
if ( ! elShowArchivedLabel ) return ;
if ( ! elShowArchivedLabel ) return ;
const elShowArchivedInput = elShowArchivedLabel . querySelector < HTMLInputElement > ( 'input' ) ;
const elShowArchivedInput = elShowArchivedLabel . querySelector < HTMLInputElement > ( 'input' ) ! ;
elShowArchivedInput . checked = showArchivedLabels ;
elShowArchivedInput . checked = showArchivedLabels ;
const archivedLabels = elDropdown . querySelectorAll ( '.item[data-is-archived]' ) ;
const archivedLabels = elDropdown . querySelectorAll ( '.item[data-is-archived]' ) ;
// if no archived labels, hide the toggle and return
// if no archived labels, hide the toggle and return
@ -107,7 +106,7 @@ function initRepoIssueLabelFilter(elDropdown: HTMLElement) {
// show the archived labels if the toggle is checked or the label is selected
// show the archived labels if the toggle is checked or the label is selected
for ( const label of archivedLabels ) {
for ( const label of archivedLabels ) {
toggleElem ( label , showArchivedLabels || selectedLabelIds . has ( label . getAttribute ( 'data-label-id' ) ) ) ;
toggleElem ( label , showArchivedLabels || selectedLabelIds . has ( label . getAttribute ( 'data-label-id' ) ! ) ) ;
}
}
// update the url when the toggle is changed and reload
// update the url when the toggle is changed and reload
elShowArchivedInput . addEventListener ( 'input' , ( ) = > {
elShowArchivedInput . addEventListener ( 'input' , ( ) = > {
@ -127,14 +126,14 @@ export function initRepoIssueFilterItemLabel() {
export function initRepoIssueCommentDelete() {
export function initRepoIssueCommentDelete() {
// Delete comment
// Delete comment
document . addEventListener ( 'click' , async ( e : DOMEvent < MouseEvent > ) = > {
document . addEventListener ( 'click' , async ( e ) = > {
if ( ! e . target . matches ( '.delete-comment' ) ) return ;
if ( ! ( e . target as HTMLElement ) . matches ( '.delete-comment' ) ) return ;
e . preventDefault ( ) ;
e . preventDefault ( ) ;
const deleteButton = e . target ;
const deleteButton = e . target as HTMLElement ;
if ( window . confirm ( deleteButton . getAttribute ( 'data-locale' ) ) ) {
if ( window . confirm ( deleteButton . getAttribute ( 'data-locale' ) ! ) ) {
try {
try {
const response = await POST ( deleteButton . getAttribute ( 'data-url' ) ) ;
const response = await POST ( deleteButton . getAttribute ( 'data-url' ) ! ) ;
if ( ! response . ok ) throw new Error ( 'Failed to delete comment' ) ;
if ( ! response . ok ) throw new Error ( 'Failed to delete comment' ) ;
const conversationHolder = deleteButton . closest ( '.conversation-holder' ) ;
const conversationHolder = deleteButton . closest ( '.conversation-holder' ) ;
@ -143,8 +142,8 @@ export function initRepoIssueCommentDelete() {
// Check if this was a pending comment.
// Check if this was a pending comment.
if ( conversationHolder ? . querySelector ( '.pending-label' ) ) {
if ( conversationHolder ? . querySelector ( '.pending-label' ) ) {
const counter = document . querySelector ( '#review-box .review-comments-counter' ) ;
const counter = document . querySelector ( '#review-box .review-comments-counter' ) ! ;
let num = parseInt ( counter ? . getAttribute ( 'data-pending-comment-number' ) ) - 1 || 0 ;
let num = parseInt ( counter ? . getAttribute ( 'data-pending-comment-number' ) || '' ) - 1 || 0 ;
num = Math . max ( num , 0 ) ;
num = Math . max ( num , 0 ) ;
counter . setAttribute ( 'data-pending-comment-number' , String ( num ) ) ;
counter . setAttribute ( 'data-pending-comment-number' , String ( num ) ) ;
counter . textContent = String ( num ) ;
counter . textContent = String ( num ) ;
@ -162,9 +161,9 @@ export function initRepoIssueCommentDelete() {
// on the Conversation page, there is no parent "tr", so no need to do anything for "add-code-comment"
// on the Conversation page, there is no parent "tr", so no need to do anything for "add-code-comment"
if ( lineType ) {
if ( lineType ) {
if ( lineType === 'same' ) {
if ( lineType === 'same' ) {
document . querySelector ( ` [data-path=" ${ path } "] .add-code-comment[data-idx=" ${ idx } "] ` ) . classList . remove ( 'tw-invisible' ) ;
document . querySelector ( ` [data-path=" ${ path } "] .add-code-comment[data-idx=" ${ idx } "] ` ) ! . classList . remove ( 'tw-invisible' ) ;
} else {
} else {
document . querySelector ( ` [data-path=" ${ path } "] .add-code-comment[data-side=" ${ side } "][data-idx=" ${ idx } "] ` ) . classList . remove ( 'tw-invisible' ) ;
document . querySelector ( ` [data-path=" ${ path } "] .add-code-comment[data-side=" ${ side } "][data-idx=" ${ idx } "] ` ) ! . classList . remove ( 'tw-invisible' ) ;
}
}
}
}
conversationHolder . remove ( ) ;
conversationHolder . remove ( ) ;
@ -184,13 +183,13 @@ export function initRepoIssueCommentDelete() {
export function initRepoIssueCodeCommentCancel() {
export function initRepoIssueCodeCommentCancel() {
// Cancel inline code comment
// Cancel inline code comment
document . addEventListener ( 'click' , ( e : DOMEvent < MouseEvent > ) = > {
document . addEventListener ( 'click' , ( e ) = > {
if ( ! e . target . matches ( '.cancel-code-comment' ) ) return ;
if ( ! ( e . target as HTMLElement ) . matches ( '.cancel-code-comment' ) ) return ;
const form = e . target . closest ( 'form' ) ;
const form = ( e . target as HTMLElement ) . closest ( 'form' ) ! ;
if ( form ? . classList . contains ( 'comment-form' ) ) {
if ( form ? . classList . contains ( 'comment-form' ) ) {
hideElem ( form ) ;
hideElem ( form ) ;
showElem ( form . closest ( '.comment-code-cloud' ) ? . querySelectorAll ( 'button.comment-form-reply' ) ) ;
showElem ( form . closest ( '.comment-code-cloud' ) ! . querySelectorAll ( 'button.comment-form-reply' ) ) ;
} else {
} else {
form . closest ( '.comment-code-cloud' ) ? . remove ( ) ;
form . closest ( '.comment-code-cloud' ) ? . remove ( ) ;
}
}
@ -198,9 +197,9 @@ export function initRepoIssueCodeCommentCancel() {
}
}
export function initRepoPullRequestAllowMaintainerEdit() {
export function initRepoPullRequestAllowMaintainerEdit() {
const wrapper = document . querySelector ( '#allow-edits-from-maintainers' ) ;
const wrapper = document . querySelector ( '#allow-edits-from-maintainers' ) ! ;
if ( ! wrapper ) return ;
if ( ! wrapper ) return ;
const checkbox = wrapper . querySelector < HTMLInputElement > ( 'input[type="checkbox"]' ) ;
const checkbox = wrapper . querySelector < HTMLInputElement > ( 'input[type="checkbox"]' ) ! ;
checkbox . addEventListener ( 'input' , async ( ) = > {
checkbox . addEventListener ( 'input' , async ( ) = > {
const url = ` ${ wrapper . getAttribute ( 'data-url' ) } /set_allow_maintainer_edit ` ;
const url = ` ${ wrapper . getAttribute ( 'data-url' ) } /set_allow_maintainer_edit ` ;
wrapper . classList . add ( 'is-loading' ) ;
wrapper . classList . add ( 'is-loading' ) ;
@ -216,7 +215,7 @@ export function initRepoPullRequestAllowMaintainerEdit() {
} catch ( error ) {
} catch ( error ) {
checkbox . checked = ! checkbox . checked ;
checkbox . checked = ! checkbox . checked ;
console . error ( error ) ;
console . error ( error ) ;
showTemporaryTooltip ( wrapper , wrapper . getAttribute ( 'data-prompt-error' ) ) ;
showTemporaryTooltip ( wrapper , wrapper . getAttribute ( 'data-prompt-error' ) ! ) ;
} finally {
} finally {
wrapper . classList . remove ( 'is-loading' ) ;
wrapper . classList . remove ( 'is-loading' ) ;
}
}
@ -226,7 +225,7 @@ export function initRepoPullRequestAllowMaintainerEdit() {
export function initRepoIssueComments() {
export function initRepoIssueComments() {
if ( ! document . querySelector ( '.repository.view.issue .timeline' ) ) return ;
if ( ! document . querySelector ( '.repository.view.issue .timeline' ) ) return ;
document . addEventListener ( 'click' , ( e : DOM Event< MouseEvent > ) = > {
document . addEventListener ( 'click' , ( e : Event) = > {
const urlTarget = document . querySelector ( ':target' ) ;
const urlTarget = document . querySelector ( ':target' ) ;
if ( ! urlTarget ) return ;
if ( ! urlTarget ) return ;
@ -235,22 +234,22 @@ export function initRepoIssueComments() {
if ( ! /^(issue|pull)(comment)?-\d+$/ . test ( urlTargetId ) ) return ;
if ( ! /^(issue|pull)(comment)?-\d+$/ . test ( urlTargetId ) ) return ;
if ( ! e . target . closest ( ` # ${ urlTargetId } ` ) ) {
if ( ! ( e . target as HTMLElement ) . closest ( ` # ${ urlTargetId } ` ) ) {
// if the user clicks outside the comment, remove the hash from the url
// if the user clicks outside the comment, remove the hash from the url
// use empty hash and state to avoid scrolling
// use empty hash and state to avoid scrolling
window . location . hash = ' ' ;
window . location . hash = ' ' ;
window . history . pushState ( null , null , ' ' ) ;
window . history . pushState ( null , '' , ' ' ) ;
}
}
} ) ;
} ) ;
}
}
export async function handleReply ( el : HTMLElement ) {
export async function handleReply ( el : HTMLElement ) {
const form = el . closest ( '.comment-code-cloud' ) . querySelector ( '.comment-form' ) ;
const form = el . closest ( '.comment-code-cloud' ) ! . querySelector ( '.comment-form' ) ! ;
const textarea = form . querySelector ( 'textarea' ) ;
const textarea = form . querySelector ( 'textarea' ) ;
hideElem ( el ) ;
hideElem ( el ) ;
showElem ( form ) ;
showElem ( form ) ;
const editor = getComboMarkdownEditor ( textarea ) ? ? await initComboMarkdownEditor ( form . querySelector ( '.combo-markdown-editor' ) ) ;
const editor = getComboMarkdownEditor ( textarea ) ? ? await initComboMarkdownEditor ( form . querySelector ( '.combo-markdown-editor' ) ! ) ;
editor . focus ( ) ;
editor . focus ( ) ;
return editor ;
return editor ;
}
}
@ -269,7 +268,7 @@ export function initRepoPullRequestReview() {
showElem ( ` #code-comments- ${ id } , #code-preview- ${ id } , #hide-outdated- ${ id } ` ) ;
showElem ( ` #code-comments- ${ id } , #code-preview- ${ id } , #hide-outdated- ${ id } ` ) ;
// if the comment box is folded, expand it
// if the comment box is folded, expand it
if ( ancestorDiffBox ? . getAttribute ( 'data-folded' ) === 'true' ) {
if ( ancestorDiffBox ? . getAttribute ( 'data-folded' ) === 'true' ) {
setFileFolding ( ancestorDiffBox , ancestorDiffBox . querySelector ( '.fold-file' ) , false ) ;
setFileFolding ( ancestorDiffBox , ancestorDiffBox . querySelector ( '.fold-file' ) ! , false ) ;
}
}
}
}
// set scrollRestoration to 'manual' when there is a hash in url, so that the scroll position will not be remembered after refreshing
// set scrollRestoration to 'manual' when there is a hash in url, so that the scroll position will not be remembered after refreshing
@ -317,18 +316,18 @@ export function initRepoPullRequestReview() {
interactive : true ,
interactive : true ,
hideOnClick : true ,
hideOnClick : true ,
} ) ;
} ) ;
elReviewPanel . querySelector ( '.close' ) . addEventListener ( 'click' , ( ) = > tippy . hide ( ) ) ;
elReviewPanel . querySelector ( '.close' ) ! . addEventListener ( 'click' , ( ) = > tippy . hide ( ) ) ;
}
}
addDelegatedEventListener ( document , 'click' , '.add-code-comment' , async ( el , e ) = > {
addDelegatedEventListener ( document , 'click' , '.add-code-comment' , async ( el , e ) = > {
e . preventDefault ( ) ;
e . preventDefault ( ) ;
const isSplit = el . closest ( '.code-diff' ) ? . classList . contains ( 'code-diff-split' ) ;
const isSplit = el . closest ( '.code-diff' ) ? . classList . contains ( 'code-diff-split' ) ;
const side = el . getAttribute ( 'data-side' ) ;
const side = el . getAttribute ( 'data-side' ) ! ;
const idx = el . getAttribute ( 'data-idx' ) ;
const idx = el . getAttribute ( 'data-idx' ) ! ;
const path = el . closest ( '[data-path]' ) ? . getAttribute ( 'data-path' ) ;
const path = el . closest ( '[data-path]' ) ? . getAttribute ( 'data-path' ) ;
const tr = el . closest ( 'tr' ) ;
const tr = el . closest ( 'tr' ) ! ;
const lineType = tr . getAttribute ( 'data-line-type' ) ;
const lineType = tr . getAttribute ( 'data-line-type' ) ! ;
let ntr = tr . nextElementSibling ;
let ntr = tr . nextElementSibling ;
if ( ! ntr ? . classList . contains ( 'add-comment' ) ) {
if ( ! ntr ? . classList . contains ( 'add-comment' ) ) {
@ -343,15 +342,15 @@ export function initRepoPullRequestReview() {
< / tr > ` );
< / tr > ` );
tr . after ( ntr ) ;
tr . after ( ntr ) ;
}
}
const td = ntr . querySelector ( ` .add-comment- ${ side } ` ) ;
const td = ntr . querySelector ( ` .add-comment- ${ side } ` ) ! ;
const commentCloud = td . querySelector ( '.comment-code-cloud' ) ;
const commentCloud = td . querySelector ( '.comment-code-cloud' ) ;
if ( ! commentCloud && ! ntr . querySelector ( 'button[name="pending_review"]' ) ) {
if ( ! commentCloud && ! ntr . querySelector ( 'button[name="pending_review"]' ) ) {
const response = await GET ( el . closest ( '[data-new-comment-url]' ) ? . getAttribute ( 'data-new-comment-url' ) ) ;
const response = await GET ( el . closest ( '[data-new-comment-url]' ) ? . getAttribute ( 'data-new-comment-url' ) ? ? '' ) ;
td . innerHTML = await response . text ( ) ;
td . innerHTML = await response . text ( ) ;
td . querySelector < HTMLInputElement > ( "input[name='line']" ) . value = idx ;
td . querySelector < HTMLInputElement > ( "input[name='line']" ) ! . value = idx ;
td . querySelector < HTMLInputElement > ( "input[name='side']" ) . value = ( side === 'left' ? 'previous' : 'proposed' ) ;
td . querySelector < HTMLInputElement > ( "input[name='side']" ) ! . value = ( side === 'left' ? 'previous' : 'proposed' ) ;
td . querySelector < HTMLInputElement > ( "input[name='path']" ) . value = path ;
td . querySelector < HTMLInputElement > ( "input[name='path']" ) ! . value = String ( path ) ;
const editor = await initComboMarkdownEditor ( td . querySelector < HTMLElement > ( '.combo-markdown-editor' ) ) ;
const editor = await initComboMarkdownEditor ( td . querySelector < HTMLElement > ( '.combo-markdown-editor' ) ! ) ;
editor . focus ( ) ;
editor . focus ( ) ;
}
}
} ) ;
} ) ;
@ -360,7 +359,7 @@ export function initRepoPullRequestReview() {
export function initRepoIssueReferenceIssue() {
export function initRepoIssueReferenceIssue() {
const elDropdown = document . querySelector ( '.issue_reference_repository_search' ) ;
const elDropdown = document . querySelector ( '.issue_reference_repository_search' ) ;
if ( ! elDropdown ) return ;
if ( ! elDropdown ) return ;
const form = elDropdown . closest ( 'form' ) ;
const form = elDropdown . closest ( 'form' ) ! ;
fomanticQuery ( elDropdown ) . dropdown ( {
fomanticQuery ( elDropdown ) . dropdown ( {
fullTextSearch : true ,
fullTextSearch : true ,
apiSettings : {
apiSettings : {
@ -389,10 +388,10 @@ export function initRepoIssueReferenceIssue() {
const target = el . getAttribute ( 'data-target' ) ;
const target = el . getAttribute ( 'data-target' ) ;
const content = document . querySelector ( ` # ${ target } ` ) ? . textContent ? ? '' ;
const content = document . querySelector ( ` # ${ target } ` ) ? . textContent ? ? '' ;
const poster = el . getAttribute ( 'data-poster-username' ) ;
const poster = el . getAttribute ( 'data-poster-username' ) ;
const reference = toAbsoluteUrl ( el . getAttribute ( 'data-reference' ) ) ;
const reference = toAbsoluteUrl ( el . getAttribute ( 'data-reference' ) ! ) ;
const modalSelector = el . getAttribute ( 'data-modal' ) ;
const modalSelector = el . getAttribute ( 'data-modal' ) ! ;
const modal = document . querySelector ( modalSelector ) ;
const modal = document . querySelector ( modalSelector ) ! ;
const textarea = modal . querySelector < HTMLTextAreaElement > ( 'textarea[name="content"]' ) ;
const textarea = modal . querySelector < HTMLTextAreaElement > ( 'textarea[name="content"]' ) ! ;
textarea . value = ` ${ content } \ n \ n_Originally posted by @ ${ poster } in ${ reference } _ ` ;
textarea . value = ` ${ content } \ n \ n_Originally posted by @ ${ poster } in ${ reference } _ ` ;
fomanticQuery ( modal ) . modal ( 'show' ) ;
fomanticQuery ( modal ) . modal ( 'show' ) ;
} ) ;
} ) ;
@ -402,8 +401,8 @@ export function initRepoIssueWipNewTitle() {
// Toggle WIP for new PR
// Toggle WIP for new PR
queryElems ( document , '.title_wip_desc > a' , ( el ) = > el . addEventListener ( 'click' , ( e ) = > {
queryElems ( document , '.title_wip_desc > a' , ( el ) = > el . addEventListener ( 'click' , ( e ) = > {
e . preventDefault ( ) ;
e . preventDefault ( ) ;
const wipPrefixes = JSON . parse ( el . closest ( '.title_wip_desc' ) . getAttribute ( 'data-wip-prefixes' ) ) ;
const wipPrefixes = JSON . parse ( el . closest ( '.title_wip_desc' ) ! . getAttribute ( 'data-wip-prefixes' ) ! ) ;
const titleInput = document . querySelector < HTMLInputElement > ( '#issue_title' ) ;
const titleInput = document . querySelector < HTMLInputElement > ( '#issue_title' ) ! ;
const titleValue = titleInput . value ;
const titleValue = titleInput . value ;
for ( const prefix of wipPrefixes ) {
for ( const prefix of wipPrefixes ) {
if ( titleValue . startsWith ( prefix . toUpperCase ( ) ) ) {
if ( titleValue . startsWith ( prefix . toUpperCase ( ) ) ) {
@ -419,8 +418,8 @@ export function initRepoIssueWipToggle() {
registerGlobalInitFunc ( 'initPullRequestWipToggle' , ( toggleWip ) = > toggleWip . addEventListener ( 'click' , async ( e ) = > {
registerGlobalInitFunc ( 'initPullRequestWipToggle' , ( toggleWip ) = > toggleWip . addEventListener ( 'click' , async ( e ) = > {
e . preventDefault ( ) ;
e . preventDefault ( ) ;
const title = toggleWip . getAttribute ( 'data-title' ) ;
const title = toggleWip . getAttribute ( 'data-title' ) ;
const wipPrefix = toggleWip . getAttribute ( 'data-wip-prefix' ) ;
const wipPrefix = toggleWip . getAttribute ( 'data-wip-prefix' ) ! ;
const updateUrl = toggleWip . getAttribute ( 'data-update-url' ) ;
const updateUrl = toggleWip . getAttribute ( 'data-update-url' ) ! ;
const params = new URLSearchParams ( ) ;
const params = new URLSearchParams ( ) ;
params . append ( 'title' , title ? . startsWith ( wipPrefix ) ? title . slice ( wipPrefix . length ) . trim ( ) : ` ${ wipPrefix . trim ( ) } ${ title } ` ) ;
params . append ( 'title' , title ? . startsWith ( wipPrefix ) ? title . slice ( wipPrefix . length ) . trim ( ) : ` ${ wipPrefix . trim ( ) } ${ title } ` ) ;
@ -434,13 +433,13 @@ export function initRepoIssueWipToggle() {
}
}
export function initRepoIssueTitleEdit() {
export function initRepoIssueTitleEdit() {
const issueTitleDisplay = document . querySelector ( '#issue-title-display' ) ;
const issueTitleDisplay = document . querySelector ( '#issue-title-display' ) ! ;
const issueTitleEditor = document . querySelector < HTMLFormElement > ( '#issue-title-editor' ) ;
const issueTitleEditor = document . querySelector < HTMLFormElement > ( '#issue-title-editor' ) ;
if ( ! issueTitleEditor ) return ;
if ( ! issueTitleEditor ) return ;
const issueTitleInput = issueTitleEditor . querySelector ( 'input' ) ;
const issueTitleInput = issueTitleEditor . querySelector ( 'input' ) ! ;
const oldTitle = issueTitleInput . getAttribute ( 'data-old-title' ) ;
const oldTitle = issueTitleInput . getAttribute ( 'data-old-title' ) ! ;
issueTitleDisplay . querySelector ( '#issue-title-edit-show' ) . addEventListener ( 'click' , ( ) = > {
issueTitleDisplay . querySelector ( '#issue-title-edit-show' ) ! . addEventListener ( 'click' , ( ) = > {
hideElem ( issueTitleDisplay ) ;
hideElem ( issueTitleDisplay ) ;
hideElem ( '#pull-desc-display' ) ;
hideElem ( '#pull-desc-display' ) ;
showElem ( issueTitleEditor ) ;
showElem ( issueTitleEditor ) ;
@ -450,7 +449,7 @@ export function initRepoIssueTitleEdit() {
}
}
issueTitleInput . focus ( ) ;
issueTitleInput . focus ( ) ;
} ) ;
} ) ;
issueTitleEditor . querySelector ( '.ui.cancel.button' ) . addEventListener ( 'click' , ( ) = > {
issueTitleEditor . querySelector ( '.ui.cancel.button' ) ! . addEventListener ( 'click' , ( ) = > {
hideElem ( issueTitleEditor ) ;
hideElem ( issueTitleEditor ) ;
hideElem ( '#pull-desc-editor' ) ;
hideElem ( '#pull-desc-editor' ) ;
showElem ( issueTitleDisplay ) ;
showElem ( issueTitleDisplay ) ;
@ -460,22 +459,22 @@ export function initRepoIssueTitleEdit() {
const pullDescEditor = document . querySelector ( '#pull-desc-editor' ) ; // it may not exist for a merged PR
const pullDescEditor = document . querySelector ( '#pull-desc-editor' ) ; // it may not exist for a merged PR
const prTargetUpdateUrl = pullDescEditor ? . getAttribute ( 'data-target-update-url' ) ;
const prTargetUpdateUrl = pullDescEditor ? . getAttribute ( 'data-target-update-url' ) ;
const editSaveButton = issueTitleEditor . querySelector ( '.ui.primary.button' ) ;
const editSaveButton = issueTitleEditor . querySelector ( '.ui.primary.button' ) ! ;
issueTitleEditor . addEventListener ( 'submit' , async ( e ) = > {
issueTitleEditor . addEventListener ( 'submit' , async ( e ) = > {
e . preventDefault ( ) ;
e . preventDefault ( ) ;
const newTitle = issueTitleInput . value . trim ( ) ;
const newTitle = issueTitleInput . value . trim ( ) ;
try {
try {
if ( newTitle && newTitle !== oldTitle ) {
if ( newTitle && newTitle !== oldTitle ) {
const resp = await POST ( editSaveButton . getAttribute ( 'data-update-url' ) , { data : new URLSearchParams ( { title : newTitle } ) } ) ;
const resp = await POST ( editSaveButton . getAttribute ( 'data-update-url' ) ! , { data : new URLSearchParams ( { title : newTitle } ) } ) ;
if ( ! resp . ok ) {
if ( ! resp . ok ) {
throw new Error ( ` Failed to update issue title: ${ resp . statusText } ` ) ;
throw new Error ( ` Failed to update issue title: ${ resp . statusText } ` ) ;
}
}
}
}
if ( prTargetUpdateUrl ) {
if ( prTargetUpdateUrl ) {
const newTargetBranch = document . querySelector ( '#pull-target-branch' ) . getAttribute ( 'data-branch' ) ;
const newTargetBranch = document . querySelector ( '#pull-target-branch' ) ! . getAttribute ( 'data-branch' ) ;
const oldTargetBranch = document . querySelector ( '#branch_target' ) . textContent ;
const oldTargetBranch = document . querySelector ( '#branch_target' ) ! . textContent ;
if ( newTargetBranch !== oldTargetBranch ) {
if ( newTargetBranch !== oldTargetBranch ) {
const resp = await POST ( prTargetUpdateUrl , { data : new URLSearchParams ( { target_branch : newTargetBranch} ) } ) ;
const resp = await POST ( prTargetUpdateUrl , { data : new URLSearchParams ( { target_branch : String( newTargetBranch) } ) } ) ;
if ( ! resp . ok ) {
if ( ! resp . ok ) {
throw new Error ( ` Failed to update PR target branch: ${ resp . statusText } ` ) ;
throw new Error ( ` Failed to update PR target branch: ${ resp . statusText } ` ) ;
}
}
@ -491,12 +490,12 @@ export function initRepoIssueTitleEdit() {
}
}
export function initRepoIssueBranchSelect() {
export function initRepoIssueBranchSelect() {
document . querySelector < HTMLElement > ( '#branch-select' ) ? . addEventListener ( 'click' , ( e : DOM Event< MouseEvent > ) = > {
document . querySelector < HTMLElement > ( '#branch-select' ) ? . addEventListener ( 'click' , ( e : Event) = > {
const el = e . target . closest ( '.item[data-branch]' ) ;
const el = ( e . target as HTMLElement ) . closest ( '.item[data-branch]' ) ;
if ( ! el ) return ;
if ( ! el ) return ;
const pullTargetBranch = document . querySelector ( '#pull-target-branch' ) ;
const pullTargetBranch = document . querySelector ( '#pull-target-branch' ) ! ;
const baseName = pullTargetBranch . getAttribute ( 'data-basename' ) ;
const baseName = pullTargetBranch . getAttribute ( 'data-basename' ) ;
const branchNameNew = el . getAttribute ( 'data-branch' ) ;
const branchNameNew = el . getAttribute ( 'data-branch' ) ! ;
const branchNameOld = pullTargetBranch . getAttribute ( 'data-branch' ) ;
const branchNameOld = pullTargetBranch . getAttribute ( 'data-branch' ) ;
pullTargetBranch . textContent = pullTargetBranch . textContent . replace ( ` ${ baseName } : ${ branchNameOld } ` , ` ${ baseName } : ${ branchNameNew } ` ) ;
pullTargetBranch . textContent = pullTargetBranch . textContent . replace ( ` ${ baseName } : ${ branchNameOld } ` , ` ${ baseName } : ${ branchNameNew } ` ) ;
pullTargetBranch . setAttribute ( 'data-branch' , branchNameNew ) ;
pullTargetBranch . setAttribute ( 'data-branch' , branchNameNew ) ;
@ -507,7 +506,7 @@ async function initSingleCommentEditor(commentForm: HTMLFormElement) {
// pages:
// pages:
// * normal new issue/pr page: no status-button, no comment-button (there is only a normal submit button which can submit empty content)
// * normal new issue/pr page: no status-button, no comment-button (there is only a normal submit button which can submit empty content)
// * issue/pr view page: with comment form, has status-button and comment-button
// * issue/pr view page: with comment form, has status-button and comment-button
const editor = await initComboMarkdownEditor ( commentForm . querySelector ( '.combo-markdown-editor' ) ) ;
const editor = await initComboMarkdownEditor ( commentForm . querySelector ( '.combo-markdown-editor' ) ! ) ;
const statusButton = document . querySelector < HTMLButtonElement > ( '#status-button' ) ;
const statusButton = document . querySelector < HTMLButtonElement > ( '#status-button' ) ;
const commentButton = document . querySelector < HTMLButtonElement > ( '#comment-button' ) ;
const commentButton = document . querySelector < HTMLButtonElement > ( '#comment-button' ) ;
const syncUiState = ( ) = > {
const syncUiState = ( ) = > {
@ -531,9 +530,9 @@ function initIssueTemplateCommentEditors(commentForm: HTMLFormElement) {
const comboFields = commentForm . querySelectorAll < HTMLElement > ( '.combo-editor-dropzone' ) ;
const comboFields = commentForm . querySelectorAll < HTMLElement > ( '.combo-editor-dropzone' ) ;
const initCombo = async ( elCombo : HTMLElement ) = > {
const initCombo = async ( elCombo : HTMLElement ) = > {
const fieldTextarea = elCombo . querySelector < HTMLTextAreaElement > ( '.form-field-real' ) ;
const fieldTextarea = elCombo . querySelector < HTMLTextAreaElement > ( '.form-field-real' ) ! ;
const dropzoneContainer = elCombo . querySelector < HTMLElement > ( '.form-field-dropzone' ) ;
const dropzoneContainer = elCombo . querySelector < HTMLElement > ( '.form-field-dropzone' ) ! ;
const markdownEditor = elCombo . querySelector < HTMLElement > ( '.combo-markdown-editor' ) ;
const markdownEditor = elCombo . querySelector < HTMLElement > ( '.combo-markdown-editor' ) ! ;
const editor = await initComboMarkdownEditor ( markdownEditor ) ;
const editor = await initComboMarkdownEditor ( markdownEditor ) ;
editor . container . addEventListener ( ComboMarkdownEditor . EventEditorContentChanged , ( ) = > fieldTextarea . value = editor . value ( ) ) ;
editor . container . addEventListener ( ComboMarkdownEditor . EventEditorContentChanged , ( ) = > fieldTextarea . value = editor . value ( ) ) ;
@ -544,7 +543,7 @@ function initIssueTemplateCommentEditors(commentForm: HTMLFormElement) {
hideElem ( commentForm . querySelectorAll ( '.combo-editor-dropzone .combo-markdown-editor' ) ) ;
hideElem ( commentForm . querySelectorAll ( '.combo-editor-dropzone .combo-markdown-editor' ) ) ;
queryElems ( commentForm , '.combo-editor-dropzone .form-field-dropzone' , ( dropzoneContainer ) = > {
queryElems ( commentForm , '.combo-editor-dropzone .form-field-dropzone' , ( dropzoneContainer ) = > {
// if "form-field-dropzone" exists, then "dropzone" must also exist
// if "form-field-dropzone" exists, then "dropzone" must also exist
const dropzone = dropzoneContainer . querySelector < HTMLElement > ( '.dropzone' ) . dropzone ;
const dropzone = dropzoneContainer . querySelector < HTMLElement > ( '.dropzone' ) ! . dropzone ;
const hasUploadedFiles = dropzone . files . length !== 0 ;
const hasUploadedFiles = dropzone . files . length !== 0 ;
toggleElem ( dropzoneContainer , hasUploadedFiles ) ;
toggleElem ( dropzoneContainer , hasUploadedFiles ) ;
} ) ;
} ) ;