2022-01-29 00:00:11 +03:00
import $ from 'jquery' ;
2021-10-16 20:28:04 +03:00
import { initCompReactionSelector } from './comp/ReactionSelector.js' ;
2021-12-06 06:57:51 +03:00
import { initRepoIssueContentHistory } from './repo-issue-content.js' ;
2023-04-09 16:11:02 +03:00
import { initDiffFileTree } from './repo-diff-filetree.js' ;
2023-07-28 22:18:12 +03:00
import { initDiffCommitSelect } from './repo-diff-commitselect.js' ;
2023-04-03 13:06:57 +03:00
import { validateTextareaNonEmpty } from './comp/ComboMarkdownEditor.js' ;
2023-04-09 16:11:02 +03:00
import { initViewedCheckboxListenerFor , countAndUpdateViewedFiles , initExpandAndCollapseFilesButton } from './pull-view-file.js' ;
2023-07-04 11:36:27 +03:00
import { initImageDiff } from './imagediff.js' ;
2023-11-21 05:12:31 +03:00
import { showErrorToast } from '../modules/toast.js' ;
2024-03-29 21:51:44 +03:00
import { submitEventSubmitter , queryElemSiblings , hideElem , showElem } from '../utils/dom.js' ;
2024-03-15 01:21:14 +03:00
import { POST , GET } from '../modules/fetch.js' ;
2022-01-29 00:00:11 +03:00
2024-03-15 01:21:14 +03:00
const { pageData , i18n } = window . config ;
2021-10-16 20:28:04 +03:00
2023-04-09 16:11:02 +03:00
function initRepoDiffReviewButton ( ) {
2024-03-23 15:37:18 +03:00
const reviewBox = document . getElementById ( 'review-box' ) ;
if ( ! reviewBox ) return ;
const counter = reviewBox . querySelector ( '.review-comments-counter' ) ;
if ( ! counter ) return ;
2022-05-07 08:35:12 +03:00
2023-03-04 10:13:37 +03:00
$ ( document ) . on ( 'click' , 'button[name="pending_review"]' , ( e ) => {
2022-05-07 08:35:12 +03:00
const $form = $ ( e . target ) . closest ( 'form' ) ;
// Watch for the form's submit event.
$form . on ( 'submit' , ( ) => {
2024-03-23 15:37:18 +03:00
const num = parseInt ( counter . getAttribute ( 'data-pending-comment-number' ) ) + 1 || 1 ;
counter . setAttribute ( 'data-pending-comment-number' , num ) ;
counter . textContent = num ;
2024-03-29 21:51:44 +03:00
reviewBox . classList . remove ( 'pulse' ) ;
requestAnimationFrame ( ( ) => {
reviewBox . classList . add ( 'pulse' ) ;
} ) ;
2022-05-07 08:35:12 +03:00
} ) ;
2021-10-16 20:28:04 +03:00
} ) ;
}
2023-04-09 16:11:02 +03:00
function initRepoDiffFileViewToggle ( ) {
2021-10-16 20:28:04 +03:00
$ ( '.file-view-toggle' ) . on ( 'click' , function ( ) {
2024-03-29 21:51:44 +03:00
for ( const el of queryElemSiblings ( this ) ) {
el . classList . remove ( 'active' ) ;
}
this . classList . add ( 'active' ) ;
2021-10-16 20:28:04 +03:00
2024-03-29 21:51:44 +03:00
const target = document . querySelector ( this . getAttribute ( 'data-toggle-selector' ) ) ;
if ( ! target ) return ;
hideElem ( queryElemSiblings ( target ) ) ;
showElem ( target ) ;
2021-10-16 20:28:04 +03:00
} ) ;
}
2023-04-09 16:11:02 +03:00
function initRepoDiffConversationForm ( ) {
2021-10-16 23:30:31 +03:00
$ ( document ) . on ( 'submit' , '.conversation-holder form' , async ( e ) => {
2021-10-16 20:28:04 +03:00
e . preventDefault ( ) ;
2022-01-03 01:31:03 +03:00
2022-08-17 00:05:40 +03:00
const $form = $ ( e . target ) ;
2024-02-20 13:37:37 +03:00
const textArea = e . target . querySelector ( 'textarea' ) ;
if ( ! validateTextareaNonEmpty ( textArea ) ) {
2022-01-03 01:31:03 +03:00
return ;
}
2024-03-29 21:51:44 +03:00
if ( e . target . classList . contains ( 'is-loading' ) ) return ;
2023-11-21 05:12:31 +03:00
try {
2024-03-29 21:51:44 +03:00
e . target . classList . add ( 'is-loading' ) ;
2023-11-21 05:12:31 +03:00
const formData = new FormData ( $form [ 0 ] ) ;
2023-03-04 10:13:37 +03:00
2024-02-17 14:52:11 +03:00
// If the form is submitted by a button, append the button's name and value to the form data.
// originalEvent can be undefined, such as an event that's caused by Ctrl+Enter, in that case
// sent the event itself.
const submitter = submitEventSubmitter ( e . originalEvent ? ? e ) ;
2023-11-21 05:12:31 +03:00
const isSubmittedByButton = ( submitter ? . nodeName === 'BUTTON' ) || ( submitter ? . nodeName === 'INPUT' && submitter . type === 'submit' ) ;
if ( isSubmittedByButton && submitter . name ) {
formData . append ( submitter . name , submitter . value ) ;
}
2024-03-15 01:21:14 +03:00
2024-03-23 15:37:18 +03:00
const response = await POST ( e . target . getAttribute ( 'action' ) , { data : formData } ) ;
2024-03-15 01:21:14 +03:00
const $newConversationHolder = $ ( await response . text ( ) ) ;
2023-11-21 05:12:31 +03:00
const { path , side , idx } = $newConversationHolder . data ( ) ;
2021-10-16 20:28:04 +03:00
2023-11-21 05:12:31 +03:00
$form . closest ( '.conversation-holder' ) . replaceWith ( $newConversationHolder ) ;
2024-03-29 21:51:44 +03:00
let selector ;
2023-11-21 05:12:31 +03:00
if ( $form . closest ( 'tr' ) . data ( 'line-type' ) === 'same' ) {
2024-03-29 21:51:44 +03:00
selector = ` [data-path=" ${ path } "] .add-code-comment[data-idx=" ${ idx } "] ` ;
2023-11-21 05:12:31 +03:00
} else {
2024-03-29 21:51:44 +03:00
selector = ` [data-path=" ${ path } "] .add-code-comment[data-side=" ${ side } "][data-idx=" ${ idx } "] ` ;
}
for ( const el of document . querySelectorAll ( selector ) ) {
el . classList . add ( 'tw-invisible' ) ;
2023-11-21 05:12:31 +03:00
}
$newConversationHolder . find ( '.dropdown' ) . dropdown ( ) ;
initCompReactionSelector ( $newConversationHolder ) ;
} catch { // here the caught error might be a jQuery AJAX error (thrown by await $.post), which is not good to use for error message handling
2024-02-17 14:52:11 +03:00
console . error ( 'error when submitting conversation' , e ) ;
2023-11-21 05:12:31 +03:00
showErrorToast ( i18n . network _error ) ;
} finally {
2024-03-29 21:51:44 +03:00
e . target . classList . remove ( 'is-loading' ) ;
2021-10-16 20:28:04 +03:00
}
} ) ;
2022-01-18 20:28:38 +03:00
$ ( document ) . on ( 'click' , '.resolve-conversation' , async function ( e ) {
2021-10-16 20:28:04 +03:00
e . preventDefault ( ) ;
const comment _id = $ ( this ) . data ( 'comment-id' ) ;
const origin = $ ( this ) . data ( 'origin' ) ;
const action = $ ( this ) . data ( 'action' ) ;
const url = $ ( this ) . data ( 'update-url' ) ;
2024-03-15 01:21:14 +03:00
try {
const response = await POST ( url , { data : new URLSearchParams ( { origin , action , comment _id } ) } ) ;
const data = await response . text ( ) ;
if ( $ ( this ) . closest ( '.conversation-holder' ) . length ) {
2024-03-16 15:22:16 +03:00
const $conversation = $ ( data ) ;
$ ( this ) . closest ( '.conversation-holder' ) . replaceWith ( $conversation ) ;
$conversation . find ( '.dropdown' ) . dropdown ( ) ;
initCompReactionSelector ( $conversation ) ;
2024-03-15 01:21:14 +03:00
} else {
window . location . reload ( ) ;
}
} catch ( error ) {
console . error ( 'Error:' , error ) ;
2021-10-16 20:28:04 +03:00
}
} ) ;
}
export function initRepoDiffConversationNav ( ) {
// Previous/Next code review conversation
$ ( document ) . on ( 'click' , '.previous-conversation' , ( e ) => {
const $conversation = $ ( e . currentTarget ) . closest ( '.comment-code-cloud' ) ;
2024-03-24 21:23:38 +03:00
const $conversations = $ ( '.comment-code-cloud:not(.tw-hidden)' ) ;
2021-10-16 20:28:04 +03:00
const index = $conversations . index ( $conversation ) ;
const previousIndex = index > 0 ? index - 1 : $conversations . length - 1 ;
const $previousConversation = $conversations . eq ( previousIndex ) ;
2024-03-23 15:37:18 +03:00
const anchor = $previousConversation . find ( '.comment' ) . first ( ) [ 0 ] . getAttribute ( 'id' ) ;
2021-10-16 20:28:04 +03:00
window . location . href = ` # ${ anchor } ` ;
} ) ;
$ ( document ) . on ( 'click' , '.next-conversation' , ( e ) => {
const $conversation = $ ( e . currentTarget ) . closest ( '.comment-code-cloud' ) ;
2024-03-24 21:23:38 +03:00
const $conversations = $ ( '.comment-code-cloud:not(.tw-hidden)' ) ;
2021-10-16 20:28:04 +03:00
const index = $conversations . index ( $conversation ) ;
const nextIndex = index < $conversations . length - 1 ? index + 1 : 0 ;
const $nextConversation = $conversations . eq ( nextIndex ) ;
2024-03-23 15:37:18 +03:00
const anchor = $nextConversation . find ( '.comment' ) . first ( ) [ 0 ] . getAttribute ( 'id' ) ;
2021-10-16 20:28:04 +03:00
window . location . href = ` # ${ anchor } ` ;
} ) ;
}
2021-11-09 12:27:25 +03:00
2022-05-07 21:28:10 +03:00
// Will be called when the show more (files) button has been pressed
function onShowMoreFiles ( ) {
initRepoIssueContentHistory ( ) ;
initViewedCheckboxListenerFor ( ) ;
countAndUpdateViewedFiles ( ) ;
2023-07-04 11:36:27 +03:00
initImageDiff ( ) ;
2022-05-07 21:28:10 +03:00
}
2024-03-15 01:21:14 +03:00
export async function loadMoreFiles ( url ) {
2024-03-29 21:51:44 +03:00
const target = document . querySelector ( 'a#diff-show-more-files' ) ;
if ( target ? . classList . contains ( 'disabled' ) || pageData . diffFileInfo . isLoadingNewData ) {
2023-01-13 08:50:32 +03:00
return ;
}
2023-05-30 13:53:15 +03:00
pageData . diffFileInfo . isLoadingNewData = true ;
2024-03-29 21:51:44 +03:00
target ? . classList . add ( 'disabled' ) ;
2024-03-15 01:21:14 +03:00
try {
const response = await GET ( url ) ;
const resp = await response . text ( ) ;
2023-05-30 13:53:15 +03:00
const $resp = $ ( resp ) ;
// the response is a full HTML page, we need to extract the relevant contents:
// 1. append the newly loaded file list items to the existing list
$ ( '#diff-incomplete' ) . replaceWith ( $resp . find ( '#diff-file-boxes' ) . children ( ) ) ;
// 2. re-execute the script to append the newly loaded items to the JS variables to refresh the DiffFileTree
$ ( 'body' ) . append ( $resp . find ( 'script#diff-data-script' ) ) ;
2023-01-13 08:50:32 +03:00
onShowMoreFiles ( ) ;
2024-03-15 01:21:14 +03:00
} catch ( error ) {
console . error ( 'Error:' , error ) ;
showErrorToast ( 'An error occurred while loading more files.' ) ;
} finally {
2024-03-29 21:51:44 +03:00
target ? . classList . remove ( 'disabled' ) ;
2023-05-30 13:53:15 +03:00
pageData . diffFileInfo . isLoadingNewData = false ;
2024-03-15 01:21:14 +03:00
}
2022-09-27 08:22:19 +03:00
}
2023-04-09 16:11:02 +03:00
function initRepoDiffShowMore ( ) {
2023-01-13 08:50:32 +03:00
$ ( document ) . on ( 'click' , 'a#diff-show-more-files' , ( e ) => {
e . preventDefault ( ) ;
2024-03-23 15:37:18 +03:00
const linkLoadMore = e . target . getAttribute ( 'data-href' ) ;
2023-05-30 13:53:15 +03:00
loadMoreFiles ( linkLoadMore ) ;
2023-01-13 08:50:32 +03:00
} ) ;
2024-03-15 01:21:14 +03:00
$ ( document ) . on ( 'click' , 'a.diff-load-button' , async ( e ) => {
2021-11-21 19:51:08 +03:00
e . preventDefault ( ) ;
const $target = $ ( e . target ) ;
2024-03-29 21:51:44 +03:00
if ( e . target . classList . contains ( 'disabled' ) ) {
2021-11-21 19:51:08 +03:00
return ;
}
2024-03-29 21:51:44 +03:00
e . target . classList . add ( 'disabled' ) ;
2021-11-21 19:51:08 +03:00
const url = $target . data ( 'href' ) ;
2024-03-15 01:21:14 +03:00
try {
const response = await GET ( url ) ;
const resp = await response . text ( ) ;
2021-11-21 19:51:08 +03:00
if ( ! resp ) {
return ;
}
$target . parent ( ) . replaceWith ( $ ( resp ) . find ( '#diff-file-boxes .diff-file-body .file-body' ) . children ( ) ) ;
2022-05-07 21:28:10 +03:00
onShowMoreFiles ( ) ;
2024-03-15 01:21:14 +03:00
} catch ( error ) {
console . error ( 'Error:' , error ) ;
} finally {
2024-03-29 21:51:44 +03:00
e . target . classList . remove ( 'disabled' ) ;
2024-03-15 01:21:14 +03:00
}
2021-11-09 12:27:25 +03:00
} ) ;
}
2023-04-09 16:11:02 +03:00
export function initRepoDiffView ( ) {
2023-04-19 20:05:25 +03:00
initRepoDiffConversationForm ( ) ;
2024-03-25 21:37:55 +03:00
if ( ! $ ( '#diff-file-list' ) . length ) return ;
2023-04-09 16:11:02 +03:00
initDiffFileTree ( ) ;
2023-07-28 22:18:12 +03:00
initDiffCommitSelect ( ) ;
2023-04-09 16:11:02 +03:00
initRepoDiffShowMore ( ) ;
initRepoDiffReviewButton ( ) ;
initRepoDiffFileViewToggle ( ) ;
initViewedCheckboxListenerFor ( ) ;
initExpandAndCollapseFilesButton ( ) ;
}