mirror of
https://gitflic.ru/project/openide/openide.git
synced 2026-03-22 15:19:59 +07:00
[gitlab] ability to delete discussion notes
GitOrigin-RevId: c9f5a0351c9552742d0f7ded493cfb5bba1dd3e8
This commit is contained in:
committed by
intellij-monorepo-bot
parent
f962821f0d
commit
2bea8cb7e5
@@ -25,5 +25,6 @@ fragment note on Note {
|
||||
resolved
|
||||
userPermissions {
|
||||
resolveNote
|
||||
adminNote
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
mutation($noteId: NoteID!) {
|
||||
destroyNote(input: {id: $noteId}) {
|
||||
errors
|
||||
}
|
||||
}
|
||||
@@ -11,6 +11,7 @@ import com.intellij.collaboration.api.json.JsonHttpApiHelper
|
||||
import com.intellij.collaboration.api.json.loadJsonList
|
||||
import com.intellij.openapi.diagnostic.logger
|
||||
import com.intellij.util.io.HttpSecurityUtil
|
||||
import org.jetbrains.plugins.gitlab.api.dto.GitLabGraphQLMutationResultDTO
|
||||
import java.net.http.HttpResponse
|
||||
|
||||
class GitLabApi private constructor(httpHelper: HttpApiHelper)
|
||||
@@ -45,4 +46,13 @@ class GitLabApi private constructor(httpHelper: HttpApiHelper)
|
||||
requestConfigurer = requestConfigurer)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Throws(GitLabGraphQLMutationException::class)
|
||||
fun <R : GitLabGraphQLMutationResultDTO> HttpResponse<out R?>.getResultOrThrow(): R {
|
||||
val result = body()
|
||||
if (result == null) throw GitLabGraphQLMutationEmptyResultException()
|
||||
val errors = result.errors
|
||||
if (errors != null) throw GitLabGraphQLMutationErrorException(errors)
|
||||
return result
|
||||
}
|
||||
@@ -7,4 +7,5 @@ object GitLabGQLQueries {
|
||||
const val getProjectLabels = "graphql/query/getProjectLabels.graphql"
|
||||
const val getMergeRequestDiscussions = "graphql/query/getMergeRequestDiscussions.graphql"
|
||||
const val toggleMergeRequestDiscussionResolve = "graphql/query/toggleMergeRequestDiscussionResolve.graphql"
|
||||
const val destroyNote = "graphql/query/destroyNote.graphql"
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
// Copyright 2000-2023 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
|
||||
package org.jetbrains.plugins.gitlab.api
|
||||
|
||||
class GitLabGraphQLMutationEmptyResultException
|
||||
: GitLabGraphQLMutationException("Mutation returned empty result")
|
||||
@@ -0,0 +1,5 @@
|
||||
// Copyright 2000-2023 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
|
||||
package org.jetbrains.plugins.gitlab.api
|
||||
|
||||
class GitLabGraphQLMutationErrorException(val errors: List<String>)
|
||||
: GitLabGraphQLMutationException("Mutation execution returned errors: $errors")
|
||||
@@ -0,0 +1,4 @@
|
||||
// Copyright 2000-2023 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
|
||||
package org.jetbrains.plugins.gitlab.api
|
||||
|
||||
abstract class GitLabGraphQLMutationException(message: String?) : Exception(message)
|
||||
@@ -0,0 +1,6 @@
|
||||
// Copyright 2000-2023 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
|
||||
package org.jetbrains.plugins.gitlab.api.dto
|
||||
|
||||
open class GitLabGraphQLMutationResultDTO(
|
||||
val errors: List<String>?
|
||||
)
|
||||
@@ -34,6 +34,7 @@ data class GitLabNoteDTO(
|
||||
)
|
||||
|
||||
data class UserPermissions(
|
||||
val resolveNote: Boolean
|
||||
val resolveNote: Boolean,
|
||||
val adminNote: Boolean
|
||||
)
|
||||
}
|
||||
|
||||
@@ -10,6 +10,7 @@ import org.jetbrains.plugins.gitlab.api.GitLabApi
|
||||
import org.jetbrains.plugins.gitlab.api.GitLabGQLQueries
|
||||
import org.jetbrains.plugins.gitlab.api.GitLabProjectCoordinates
|
||||
import org.jetbrains.plugins.gitlab.api.dto.GitLabDiscussionDTO
|
||||
import org.jetbrains.plugins.gitlab.api.dto.GitLabGraphQLMutationResultDTO
|
||||
import org.jetbrains.plugins.gitlab.mergerequest.data.GitLabMergeRequestId
|
||||
import java.net.http.HttpResponse
|
||||
|
||||
@@ -39,4 +40,15 @@ suspend fun GitLabApi.changeMergeRequestDiscussionResolve(
|
||||
)
|
||||
val request = gqlQuery(project.serverPath.gqlApiUri, GitLabGQLQueries.toggleMergeRequestDiscussionResolve, parameters)
|
||||
return loadGQLResponse(request, GitLabDiscussionDTO::class.java, "discussionToggleResolve", "discussion")
|
||||
}
|
||||
|
||||
suspend fun GitLabApi.deleteNote(
|
||||
project: GitLabProjectCoordinates,
|
||||
noteId: String
|
||||
): HttpResponse<out GitLabGraphQLMutationResultDTO?> {
|
||||
val parameters = mapOf(
|
||||
"noteId" to noteId
|
||||
)
|
||||
val request = gqlQuery(project.serverPath.gqlApiUri, GitLabGQLQueries.destroyNote, parameters)
|
||||
return loadGQLResponse(request, GitLabGraphQLMutationResultDTO::class.java, "destroyNote")
|
||||
}
|
||||
@@ -3,19 +3,19 @@ package org.jetbrains.plugins.gitlab.mergerequest.data
|
||||
|
||||
import com.intellij.openapi.diagnostic.logger
|
||||
import com.intellij.util.childScope
|
||||
import kotlinx.coroutines.CoroutineExceptionHandler
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.*
|
||||
import kotlinx.coroutines.flow.*
|
||||
import kotlinx.coroutines.sync.Mutex
|
||||
import kotlinx.coroutines.sync.withLock
|
||||
import kotlinx.coroutines.withContext
|
||||
import org.jetbrains.plugins.gitlab.api.GitLabProjectConnection
|
||||
import org.jetbrains.plugins.gitlab.api.dto.GitLabDiscussionDTO
|
||||
import org.jetbrains.plugins.gitlab.api.dto.GitLabNoteDTO
|
||||
import org.jetbrains.plugins.gitlab.mergerequest.api.request.changeMergeRequestDiscussionResolve
|
||||
import java.util.*
|
||||
|
||||
interface GitLabDiscussion {
|
||||
val id: String
|
||||
|
||||
val createdAt: Date
|
||||
val notes: Flow<List<GitLabNote>>
|
||||
|
||||
@@ -25,15 +25,18 @@ interface GitLabDiscussion {
|
||||
suspend fun changeResolvedState()
|
||||
}
|
||||
|
||||
@OptIn(ExperimentalCoroutinesApi::class)
|
||||
class LoadedGitLabDiscussion(
|
||||
parentCs: CoroutineScope,
|
||||
private val connection: GitLabProjectConnection,
|
||||
private val eventSink: suspend (GitLabDiscussionEvent) -> Unit,
|
||||
private val discussion: GitLabDiscussionDTO
|
||||
) : GitLabDiscussion {
|
||||
init {
|
||||
require(discussion.notes.isNotEmpty()) { "Discussion with empty notes" }
|
||||
}
|
||||
|
||||
override val id: String = discussion.id
|
||||
override val createdAt: Date = discussion.createdAt
|
||||
|
||||
private val cs = parentCs.childScope(CoroutineExceptionHandler { _, e ->
|
||||
@@ -41,10 +44,38 @@ class LoadedGitLabDiscussion(
|
||||
})
|
||||
|
||||
private val operationsGuard = Mutex()
|
||||
private val events = MutableSharedFlow<GitLabNoteEvent>()
|
||||
|
||||
private val loadedNotes = MutableStateFlow(discussion.notes)
|
||||
override val notes: Flow<List<GitLabNote>> = loadedNotes.map { notes ->
|
||||
notes.map { LoadedGitLabNote(it) }
|
||||
private val loadedNotes = MutableSharedFlow<List<GitLabNoteDTO>>(1).apply {
|
||||
tryEmit(discussion.notes)
|
||||
}
|
||||
|
||||
override val notes: Flow<List<GitLabNote>> = loadedNotes.transformLatest<List<GitLabNoteDTO>, List<GitLabNote>> { loadedNotes ->
|
||||
coroutineScope {
|
||||
val notesCs = this
|
||||
val notes = Collections.synchronizedMap(LinkedHashMap<String, LoadedGitLabNote>())
|
||||
loadedNotes.associateByTo(notes, GitLabNoteDTO::id) { noteData ->
|
||||
LoadedGitLabNote(notesCs, connection, { events.emit(it) }, noteData)
|
||||
}
|
||||
emit(notes.values.toList())
|
||||
|
||||
events.collectLatest {
|
||||
when (it) {
|
||||
is GitLabNoteEvent.NoteDeleted -> {
|
||||
notes.remove(it.noteId)?.destroy()
|
||||
}
|
||||
}
|
||||
|
||||
emit(notes.values.toList())
|
||||
}
|
||||
awaitCancellation()
|
||||
}
|
||||
}.transform {
|
||||
if (it.isEmpty()) {
|
||||
eventSink(GitLabDiscussionEvent.DiscussionDeleted(id))
|
||||
currentCoroutineContext().cancel()
|
||||
}
|
||||
emit(it)
|
||||
}.shareIn(cs, SharingStarted.Lazily, 1)
|
||||
|
||||
private val firstNote = loadedNotes.map { it.first() }
|
||||
@@ -61,8 +92,16 @@ class LoadedGitLabDiscussion(
|
||||
connection.apiClient
|
||||
.changeMergeRequestDiscussionResolve(connection.repo.repository, discussion.id, !resolved).body()!!
|
||||
}
|
||||
loadedNotes.value = result.notes
|
||||
updateNotes(result.notes)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private suspend fun updateNotes(notes: List<GitLabNoteDTO>) {
|
||||
loadedNotes.emit(notes)
|
||||
}
|
||||
|
||||
suspend fun destroy() {
|
||||
cs.coroutineContext[Job]!!.cancelAndJoin()
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
// Copyright 2000-2023 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
|
||||
package org.jetbrains.plugins.gitlab.mergerequest.data
|
||||
|
||||
sealed interface GitLabDiscussionEvent {
|
||||
class DiscussionDeleted(val discussionId: String) : GitLabDiscussionEvent
|
||||
}
|
||||
@@ -3,20 +3,21 @@ package org.jetbrains.plugins.gitlab.mergerequest.data
|
||||
|
||||
import com.intellij.collaboration.api.page.ApiPageUtil
|
||||
import com.intellij.collaboration.api.page.foldToList
|
||||
import com.intellij.collaboration.async.mapScoped
|
||||
import com.intellij.util.childScope
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.*
|
||||
import kotlinx.coroutines.flow.*
|
||||
import org.jetbrains.plugins.gitlab.api.GitLabProjectConnection
|
||||
import org.jetbrains.plugins.gitlab.api.dto.GitLabDiscussionDTO
|
||||
import org.jetbrains.plugins.gitlab.mergerequest.api.request.loadMergeRequestDiscussions
|
||||
import java.util.concurrent.ConcurrentHashMap
|
||||
|
||||
//TODO: granualar collection changes notifications
|
||||
interface GitLabMergeRequestDiscussionsModel {
|
||||
val userDiscussions: Flow<List<GitLabDiscussion>>
|
||||
val systemDiscussions: Flow<List<GitLabDiscussionDTO>>
|
||||
val userDiscussions: Flow<Collection<GitLabDiscussion>>
|
||||
val systemDiscussions: Flow<Collection<GitLabDiscussionDTO>>
|
||||
}
|
||||
|
||||
@OptIn(ExperimentalCoroutinesApi::class)
|
||||
class GitLabMergeRequestDiscussionsModelImpl(
|
||||
parentCs: CoroutineScope,
|
||||
private val connection: GitLabProjectConnection,
|
||||
@@ -24,25 +25,43 @@ class GitLabMergeRequestDiscussionsModelImpl(
|
||||
) : GitLabMergeRequestDiscussionsModel {
|
||||
|
||||
private val cs = parentCs.childScope(Dispatchers.Default)
|
||||
private val events = MutableSharedFlow<GitLabDiscussionEvent>(extraBufferCapacity = 64)
|
||||
|
||||
private val nonEmptyDiscussionsData = flow {
|
||||
emit(loadNonEmptyDiscussions())
|
||||
}.shareIn(cs, SharingStarted.Lazily, 1)
|
||||
|
||||
override val userDiscussions: Flow<Collection<GitLabDiscussion>> = nonEmptyDiscussionsData.transformLatest { loadedDiscussions ->
|
||||
coroutineScope {
|
||||
val discussionsCs = this
|
||||
val discussions = ConcurrentHashMap<String, LoadedGitLabDiscussion>()
|
||||
loadedDiscussions.associateByTo(discussions, GitLabDiscussionDTO::id) { noteData ->
|
||||
LoadedGitLabDiscussion(discussionsCs, connection, { events.emit(it) }, noteData)
|
||||
}
|
||||
emit(discussions.values.toList())
|
||||
|
||||
events.collectLatest {
|
||||
when (it) {
|
||||
is GitLabDiscussionEvent.DiscussionDeleted -> {
|
||||
discussions.remove(it.discussionId)?.destroy()
|
||||
}
|
||||
}
|
||||
emit(discussions.values.toList())
|
||||
}
|
||||
|
||||
awaitCancellation()
|
||||
}
|
||||
}.shareIn(cs, SharingStarted.Lazily, 1)
|
||||
|
||||
override val systemDiscussions: Flow<List<GitLabDiscussionDTO>> =
|
||||
nonEmptyDiscussionsData.map { discussions ->
|
||||
discussions.filter { it.notes.first().system }
|
||||
}.shareIn(cs, SharingStarted.Lazily, 1)
|
||||
|
||||
private suspend fun loadNonEmptyDiscussions(): List<GitLabDiscussionDTO> =
|
||||
ApiPageUtil.createGQLPagesFlow {
|
||||
connection.apiClient.loadMergeRequestDiscussions(connection.repo.repository, mr, it)
|
||||
}.map { discussions ->
|
||||
discussions.filter { it.notes.isNotEmpty() }
|
||||
}.foldToList()
|
||||
.let { emit(it) }
|
||||
}.shareIn(cs, SharingStarted.Lazily, 1)
|
||||
|
||||
override val userDiscussions: Flow<List<GitLabDiscussion>> =
|
||||
nonEmptyDiscussionsData.mapScoped { discussions ->
|
||||
val cs = this
|
||||
discussions.filter { !it.notes.first().system }
|
||||
.map { LoadedGitLabDiscussion(cs, connection, it) }
|
||||
}
|
||||
|
||||
override val systemDiscussions: Flow<List<GitLabDiscussionDTO>> =
|
||||
nonEmptyDiscussionsData.map { discussions ->
|
||||
discussions.filter { it.notes.first().system }
|
||||
}
|
||||
}
|
||||
@@ -1,25 +1,63 @@
|
||||
// Copyright 2000-2023 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
|
||||
package org.jetbrains.plugins.gitlab.mergerequest.data
|
||||
|
||||
import com.intellij.openapi.diagnostic.logger
|
||||
import com.intellij.util.childScope
|
||||
import kotlinx.coroutines.*
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
import kotlinx.coroutines.flow.flowOf
|
||||
import kotlinx.coroutines.sync.Mutex
|
||||
import kotlinx.coroutines.sync.withLock
|
||||
import org.jetbrains.plugins.gitlab.api.GitLabProjectConnection
|
||||
import org.jetbrains.plugins.gitlab.api.dto.GitLabNoteDTO
|
||||
import org.jetbrains.plugins.gitlab.api.dto.GitLabUserDTO
|
||||
import org.jetbrains.plugins.gitlab.api.getResultOrThrow
|
||||
import org.jetbrains.plugins.gitlab.mergerequest.api.request.deleteNote
|
||||
import java.util.*
|
||||
|
||||
interface GitLabNote {
|
||||
val author: GitLabUserDTO
|
||||
val createdAt: Date
|
||||
val canAdmin: Boolean
|
||||
|
||||
val body: Flow<String>
|
||||
|
||||
suspend fun delete()
|
||||
}
|
||||
|
||||
class LoadedGitLabNote(
|
||||
note: GitLabNoteDTO
|
||||
parentCs: CoroutineScope,
|
||||
private val connection: GitLabProjectConnection,
|
||||
private val eventSink: suspend (GitLabNoteEvent) -> Unit,
|
||||
private val note: GitLabNoteDTO
|
||||
) : GitLabNote {
|
||||
|
||||
private val cs = parentCs.childScope(CoroutineExceptionHandler { _, e ->
|
||||
logger<GitLabDiscussion>().info(e.localizedMessage)
|
||||
})
|
||||
|
||||
private val operationsGuard = Mutex()
|
||||
|
||||
override val author: GitLabUserDTO = note.author
|
||||
override val createdAt: Date = note.createdAt
|
||||
override val canAdmin: Boolean = note.userPermissions.adminNote
|
||||
|
||||
override val body: Flow<String> = flowOf(note.body)
|
||||
|
||||
override suspend fun delete() {
|
||||
withContext(cs.coroutineContext) {
|
||||
operationsGuard.withLock {
|
||||
withContext(Dispatchers.IO) {
|
||||
connection.apiClient.deleteNote(connection.repo.repository, note.id).getResultOrThrow()
|
||||
}
|
||||
}
|
||||
withContext(NonCancellable) {
|
||||
eventSink(GitLabNoteEvent.NoteDeleted(note.id))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
suspend fun destroy() {
|
||||
cs.coroutineContext[Job]!!.cancelAndJoin()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,6 @@
|
||||
// Copyright 2000-2023 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
|
||||
package org.jetbrains.plugins.gitlab.mergerequest.data
|
||||
|
||||
sealed interface GitLabNoteEvent {
|
||||
class NoteDeleted(val noteId: String) : GitLabNoteEvent
|
||||
}
|
||||
@@ -0,0 +1,34 @@
|
||||
// Copyright 2000-2023 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
|
||||
package org.jetbrains.plugins.gitlab.mergerequest.ui.comment
|
||||
|
||||
import com.intellij.util.childScope
|
||||
import kotlinx.coroutines.CancellationException
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
import org.jetbrains.plugins.gitlab.mergerequest.data.GitLabNote
|
||||
import org.jetbrains.plugins.gitlab.util.SingleCoroutineLauncher
|
||||
|
||||
interface GitLabMergeRequestNoteActionsViewModel {
|
||||
val busy: Flow<Boolean>
|
||||
|
||||
fun delete()
|
||||
}
|
||||
|
||||
class GitLabMergeRequestNoteActionsViewModelImpl(parentCs: CoroutineScope, private val note: GitLabNote)
|
||||
: GitLabMergeRequestNoteActionsViewModel {
|
||||
|
||||
private val taskLauncher = SingleCoroutineLauncher(parentCs.childScope())
|
||||
override val busy: Flow<Boolean> = taskLauncher.busy
|
||||
|
||||
override fun delete() {
|
||||
taskLauncher.launch {
|
||||
try {
|
||||
note.delete()
|
||||
}
|
||||
catch (e: Exception) {
|
||||
if (e is CancellationException) throw e
|
||||
//TODO: handle???
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -18,6 +18,8 @@ interface GitLabMergeRequestNoteViewModel {
|
||||
val author: GitLabUserDTO
|
||||
val createdAt: Date
|
||||
|
||||
val actionsVm: GitLabMergeRequestNoteActionsViewModel?
|
||||
|
||||
val htmlBody: Flow<@Nls String>
|
||||
}
|
||||
|
||||
@@ -31,6 +33,9 @@ class GitLabMergeRequestNoteViewModelImpl(
|
||||
override val author: GitLabUserDTO = note.author
|
||||
override val createdAt: Date = note.createdAt
|
||||
|
||||
override val actionsVm: GitLabMergeRequestNoteActionsViewModel? =
|
||||
if (note.canAdmin) GitLabMergeRequestNoteActionsViewModelImpl(cs, note) else null
|
||||
|
||||
private val body: Flow<String> = note.body
|
||||
override val htmlBody: Flow<String> = body.map { GitLabUIUtil.convertToHtml(it) }
|
||||
.shareIn(cs, SharingStarted.Lazily, 1)
|
||||
|
||||
@@ -8,9 +8,13 @@ import com.intellij.collaboration.ui.SimpleHtmlPane
|
||||
import com.intellij.collaboration.ui.VerticalListPanel
|
||||
import com.intellij.collaboration.ui.codereview.CodeReviewChatItemUIUtil
|
||||
import com.intellij.collaboration.ui.codereview.CodeReviewTimelineUIUtil.Thread.Replies
|
||||
import com.intellij.collaboration.ui.codereview.comment.CodeReviewCommentUIUtil
|
||||
import com.intellij.collaboration.ui.icon.IconsProvider
|
||||
import com.intellij.collaboration.ui.icon.OverlaidOffsetIconsIcon
|
||||
import com.intellij.collaboration.ui.util.*
|
||||
import com.intellij.collaboration.ui.util.bindDisabled
|
||||
import com.intellij.collaboration.ui.util.bindIcon
|
||||
import com.intellij.collaboration.ui.util.bindText
|
||||
import com.intellij.collaboration.ui.util.bindVisibility
|
||||
import com.intellij.ui.components.labels.LinkLabel
|
||||
import com.intellij.ui.components.labels.LinkListener
|
||||
import com.intellij.util.containers.nullize
|
||||
@@ -44,6 +48,8 @@ object GitLabMergeRequestTimelineDiscussionComponentFactory {
|
||||
}
|
||||
}
|
||||
|
||||
val actionsPanel = createNoteActions(cs, item.mainNote)
|
||||
|
||||
val repliesPanel = VerticalListPanel().apply {
|
||||
cs.launch {
|
||||
item.replies.combine(item.repliesFolded) { replies, folded ->
|
||||
@@ -69,7 +75,7 @@ object GitLabMergeRequestTimelineDiscussionComponentFactory {
|
||||
return CodeReviewChatItemUIUtil.build(CodeReviewChatItemUIUtil.ComponentType.FULL,
|
||||
{ avatarIconsProvider.getIcon(constAuthor, it) },
|
||||
contentPanel) {
|
||||
withHeader(createTitleTextPane(cs, item.author, item.date))
|
||||
withHeader(createTitleTextPane(cs, item.author, item.date), actionsPanel)
|
||||
}.let {
|
||||
VerticalListPanel().apply {
|
||||
add(it)
|
||||
@@ -159,13 +165,30 @@ object GitLabMergeRequestTimelineDiscussionComponentFactory {
|
||||
avatarIconsProvider: IconsProvider<GitLabUserDTO>,
|
||||
vm: GitLabMergeRequestNoteViewModel): JComponent {
|
||||
val contentPanel = createNoteTextPanel(cs, vm.htmlBody)
|
||||
val actionsPanel = createNoteActions(cs, flowOf(vm))
|
||||
return CodeReviewChatItemUIUtil.build(CodeReviewChatItemUIUtil.ComponentType.FULL_SECONDARY,
|
||||
{ avatarIconsProvider.getIcon(vm.author, it) },
|
||||
contentPanel) {
|
||||
withHeader(createTitleTextPane(vm.author, vm.createdAt))
|
||||
withHeader(createTitleTextPane(vm.author, vm.createdAt), actionsPanel)
|
||||
}
|
||||
}
|
||||
|
||||
private fun createNoteActions(cs: CoroutineScope, note: Flow<GitLabMergeRequestNoteViewModel>): JComponent {
|
||||
val panel = HorizontalListPanel(CodeReviewCommentUIUtil.Actions.HORIZONTAL_GAP).apply {
|
||||
cs.launch {
|
||||
note.mapNotNull { it.actionsVm }.collect {
|
||||
removeAll()
|
||||
CodeReviewCommentUIUtil.createDeleteCommentIconButton { _ -> it.delete() }.apply {
|
||||
bindDisabled(cs, it.busy)
|
||||
}.also(::add)
|
||||
repaint()
|
||||
revalidate()
|
||||
}
|
||||
}
|
||||
}
|
||||
return panel
|
||||
}
|
||||
|
||||
private fun createNoteTextPanel(cs: CoroutineScope, textFlow: Flow<@Nls String>): JComponent =
|
||||
SimpleHtmlPane().apply {
|
||||
bindText(cs, textFlow)
|
||||
|
||||
Reference in New Issue
Block a user