[collab/gitlab/github] persist review changes grouping preferences

GitOrigin-RevId: 828169e0ac2190b734a35f14a5a62338d55a25ce
This commit is contained in:
Ivan Semenov
2024-04-02 14:23:53 +00:00
committed by intellij-monorepo-bot
parent 16d9271f23
commit 37aa331340
7 changed files with 89 additions and 31 deletions
@@ -1,6 +1,7 @@
// Copyright 2000-2023 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
package com.intellij.collaboration.ui.codereview.changes
import com.intellij.collaboration.async.launchNow
import com.intellij.collaboration.ui.codereview.CodeReviewProgressTreeModel
import com.intellij.collaboration.ui.codereview.details.model.CodeReviewChangeListViewModel
import com.intellij.collaboration.ui.codereview.setupCodeReviewProgressModel
@@ -11,7 +12,6 @@ import com.intellij.collaboration.util.fileStatus
import com.intellij.openapi.actionSystem.CommonDataKeys
import com.intellij.openapi.actionSystem.DataKey
import com.intellij.openapi.fileEditor.OpenFileDescriptor
import com.intellij.openapi.project.Project
import com.intellij.openapi.vcs.FilePath
import com.intellij.openapi.vcs.changes.ChangesUtil
import com.intellij.openapi.vcs.changes.ui.*
@@ -38,7 +38,7 @@ object CodeReviewChangeListComponentFactory {
progressModel: CodeReviewProgressTreeModel<*>?,
emptyTextText: @Nls String): AsyncChangesTree {
val treeModel = createTreeModel(vm)
val tree = createTree(vm.project, treeModel).apply {
val tree = cs.createTree(vm, treeModel).apply {
emptyText.text = emptyTextText
}.also { tree ->
ClientProperty.put(tree, ExpandableItemsHandler.IGNORE_ITEM_SELECTION, true)
@@ -89,8 +89,8 @@ object CodeReviewChangeListComponentFactory {
return tree
}
private fun createTree(project: Project, treeModel: AsyncChangesTreeModel) =
object : AsyncChangesTree(project, false, false) {
private fun CoroutineScope.createTree(vm: CodeReviewChangeListViewModel, treeModel: AsyncChangesTreeModel) =
object : AsyncChangesTree(vm.project, false, false) {
override val changesTreeModel: AsyncChangesTreeModel = treeModel
override fun getData(dataId: String): Any? {
@@ -108,6 +108,23 @@ object CodeReviewChangeListComponentFactory {
private fun getSelectedFiles(): List<VirtualFile> =
getSelectedChanges().mapNotNull { it.filePath.virtualFile }
override fun installGroupingSupport(): ChangesGroupingSupport =
if (vm is CodeReviewChangeListViewModel.WithGrouping) {
ChangesGroupingSupport(vm.project, this, false).also { gs ->
installGroupingSupport(this, gs, vm.grouping::value, vm::setGrouping)
launchNow {
vm.grouping.collect {
if(gs.groupingKeys != it) {
gs.setGroupingKeysOrSkip(it)
}
}
}
}
}
else {
super.installGroupingSupport()
}
}
}
@@ -12,6 +12,7 @@ import kotlinx.coroutines.flow.*
import kotlinx.coroutines.launch
import kotlinx.coroutines.sync.Mutex
import kotlinx.coroutines.sync.withLock
import org.jetbrains.annotations.ApiStatus
interface CodeReviewChangeListViewModel {
val project: Project
@@ -50,6 +51,16 @@ interface CodeReviewChangeListViewModel {
val detailsByChange: StateFlow<Map<RefComparisonChange, CodeReviewChangeDetails>>
}
@ApiStatus.Experimental
interface WithGrouping : CodeReviewChangeListViewModel {
/**
* A set of enabled grouping policies
*/
val grouping: StateFlow<Set<String>>
fun setGrouping(grouping: Collection<String>)
}
sealed interface SelectionRequest {
data object All : SelectionRequest
data class OneChange(val change: RefComparisonChange) : SelectionRequest
@@ -63,6 +74,7 @@ interface CodeReviewChangeListViewModel {
fun CodeReviewChangeListViewModel.WithDetails.isViewedStateForAllChanges(changes: Iterable<RefComparisonChange>, viewed: Boolean): Boolean =
changes.all { detailsByChange.value[it]?.isRead == viewed }
@ApiStatus.Internal
abstract class CodeReviewChangeListViewModelBase(
parentCs: CoroutineScope,
protected val changeList: CodeReviewChangeList
@@ -17,8 +17,8 @@ abstract class CollectableSerializablePersistentStateComponent<T : Any>(defaultS
}
protected fun updateStateAndEmit(updateFunction: (currentState: T) -> T): T {
protected fun updateStateAndEmit(updateFunction: (currentState: T) -> T) {
updateState(updateFunction)
stateFlow.update(updateFunction)
return updateState(updateFunction)
}
}
@@ -2,9 +2,10 @@
package org.jetbrains.plugins.github.pullrequest.config
import com.intellij.collaboration.async.mapState
import com.intellij.collaboration.util.CollectableSerializablePersistentStateComponent
import com.intellij.openapi.components.*
import com.intellij.openapi.project.Project
import kotlinx.coroutines.flow.MutableStateFlow
import com.intellij.openapi.vcs.changes.ui.ChangesGroupingSupport
import kotlinx.coroutines.flow.StateFlow
import kotlinx.serialization.Serializable
import org.jetbrains.plugins.github.api.GHRepositoryCoordinates
@@ -16,10 +17,8 @@ import org.jetbrains.plugins.github.authentication.accounts.GithubAccount
@Service(Service.Level.PROJECT)
@State(name = "GithubPullRequestsUISettings", storages = [Storage(StoragePathMacros.WORKSPACE_FILE)], reportStatistic = false)
class GithubPullRequestsProjectUISettings(private val project: Project)
: SerializablePersistentStateComponent<GithubPullRequestsProjectUISettings.SettingsState>(SettingsState()) {
private val stateOfState = MutableStateFlow(SettingsState())
internal class GithubPullRequestsProjectUISettings(private val project: Project)
: CollectableSerializablePersistentStateComponent<GithubPullRequestsProjectUISettings.SettingsState>(SettingsState()) {
@Serializable
data class SettingsState(
@@ -27,7 +26,8 @@ class GithubPullRequestsProjectUISettings(private val project: Project)
val recentNewPullRequestHead: RepoCoordinatesHolder? = null,
val reviewCommentPreferred: Boolean = true,
val editorReviewEnabled: Boolean = true,
val highlightDiffLinesInEditor: Boolean = false
val highlightDiffLinesInEditor: Boolean = false,
val changesGrouping: Set<String> = setOf(ChangesGroupingSupport.DIRECTORY_GROUPING, ChangesGroupingSupport.MODULE_GROUPING)
)
var selectedUrlAndAccount: Pair<String, GithubAccount>?
@@ -36,7 +36,7 @@ class GithubPullRequestsProjectUISettings(private val project: Project)
val account = GHAccountSerializer.deserialize(accountId) ?: return null
return url to account
}
set(value) = updateAndSetState {
set(value) = updateStateAndEmit {
it.copy(selectedUrlAndAccountId = value?.let { (repo, account) ->
UrlAndAccount(repo, GHAccountSerializer.serialize(account))
})
@@ -44,40 +44,40 @@ class GithubPullRequestsProjectUISettings(private val project: Project)
var recentNewPullRequestHead: GHRepositoryCoordinates?
get() = state.recentNewPullRequestHead?.let { GHRepositoryCoordinates(it.server, GHRepositoryPath(it.owner, it.repository)) }
set(value) = updateAndSetState {
set(value) = updateStateAndEmit {
it.copy(recentNewPullRequestHead = value?.let { RepoCoordinatesHolder(it) })
}
var reviewCommentsPreferred: Boolean
get() = state.reviewCommentPreferred
set(value) = updateAndSetState {
set(value) = updateStateAndEmit {
it.copy(reviewCommentPreferred = value)
}
var editorReviewEnabled: Boolean
get() = state.editorReviewEnabled
set(value) = updateAndSetState {
set(value) = updateStateAndEmit {
it.copy(editorReviewEnabled = value)
}
var highlightDiffLinesInEditor: Boolean
get() = state.highlightDiffLinesInEditor
set(value) = updateAndSetState {
set(value) = updateStateAndEmit {
it.copy(highlightDiffLinesInEditor = value)
}
val reviewCommentsPreferredState: StateFlow<Boolean> = stateOfState.mapState { it.reviewCommentPreferred }
val editorReviewEnabledState: StateFlow<Boolean> = stateOfState.mapState { it.editorReviewEnabled }
val highlightDiffLinesInEditorState: StateFlow<Boolean> = stateOfState.mapState { it.highlightDiffLinesInEditor }
var changesGrouping: Set<String>
get() = state.changesGrouping
set(value) {
updateStateAndEmit {
it.copy(changesGrouping = value)
}
}
val changesGroupingState: StateFlow<Set<String>> = stateFlow.mapState { it.changesGrouping }
override fun loadState(state: SettingsState) {
super.loadState(state)
stateOfState.value = state
}
private inline fun updateAndSetState(updateFunction: (currentState: SettingsState) -> SettingsState) {
stateOfState.value = updateState(updateFunction)
}
val reviewCommentsPreferredState: StateFlow<Boolean> = stateFlow.mapState { it.reviewCommentPreferred }
val editorReviewEnabledState: StateFlow<Boolean> = stateFlow.mapState { it.editorReviewEnabled }
val highlightDiffLinesInEditorState: StateFlow<Boolean> = stateFlow.mapState { it.highlightDiffLinesInEditor }
companion object {
@JvmStatic
@@ -14,13 +14,14 @@ import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.flow.*
import org.jetbrains.annotations.ApiStatus
import org.jetbrains.plugins.github.api.data.pullrequest.isViewed
import org.jetbrains.plugins.github.pullrequest.config.GithubPullRequestsProjectUISettings
import org.jetbrains.plugins.github.pullrequest.data.GHPRDataContext
import org.jetbrains.plugins.github.pullrequest.data.provider.GHPRDataProvider
import org.jetbrains.plugins.github.pullrequest.data.provider.createThreadsRequestsFlow
import org.jetbrains.plugins.github.pullrequest.data.provider.createViewedStateRequestsFlow
@ApiStatus.Experimental
interface GHPRChangeListViewModel : CodeReviewChangeListViewModel.WithDetails {
interface GHPRChangeListViewModel : CodeReviewChangeListViewModel.WithDetails, CodeReviewChangeListViewModel.WithGrouping {
val isUpdating: StateFlow<Boolean>
val isOnLatest: Boolean
@@ -40,6 +41,7 @@ internal class GHPRChangeListViewModelImpl(
changes: CodeReviewChangesContainer,
changeList: CodeReviewChangeList
) : GHPRChangeListViewModel, CodeReviewChangeListViewModelBase(parentCs, changeList) {
private val preferences = GithubPullRequestsProjectUISettings.getInstance(project)
private val repository: GitRepository get() = dataContext.repositoryDataService.remoteCoordinates.repository
private val _isUpdating = MutableStateFlow(false)
@@ -57,6 +59,8 @@ internal class GHPRChangeListViewModelImpl(
MutableStateFlow(emptyMap())
}
override val grouping: StateFlow<Set<String>> = preferences.changesGroupingState
fun setUpdating(updating: Boolean) {
_isUpdating.value = updating
}
@@ -85,6 +89,10 @@ internal class GHPRChangeListViewModelImpl(
}
}
override fun setGrouping(grouping: Collection<String>) {
preferences.changesGrouping = grouping.toSet()
}
private fun createDetailsByChangeFlow(): Flow<Map<RefComparisonChange, CodeReviewChangeDetails>> {
val threadsFlow = dataProvider.reviewData.createThreadsRequestsFlow().values()
val viewedStateFlow = viewedStateData.createViewedStateRequestsFlow().values()
@@ -5,6 +5,7 @@ import com.intellij.collaboration.async.mapState
import com.intellij.collaboration.util.CollectableSerializablePersistentStateComponent
import com.intellij.openapi.components.*
import com.intellij.openapi.project.Project
import com.intellij.openapi.vcs.changes.ui.ChangesGroupingSupport
import git4idea.remote.hosting.knownRepositories
import kotlinx.coroutines.flow.StateFlow
import kotlinx.serialization.Serializable
@@ -24,7 +25,8 @@ internal class GitLabMergeRequestsPreferences(private val project: Project)
val showEventsInTimeline: Boolean = true,
val highlightDiffLinesInEditor: Boolean = true,
val usedAsDraftSubmitActionLast: Boolean = true,
val editorReviewEnabled: Boolean = true
val editorReviewEnabled: Boolean = true,
val changesGrouping: Set<String> = setOf(ChangesGroupingSupport.DIRECTORY_GROUPING, ChangesGroupingSupport.MODULE_GROUPING)
)
var selectedRepoAndAccount: Pair<GitLabProjectMapping, GitLabAccount>?
@@ -78,4 +80,13 @@ internal class GitLabMergeRequestsPreferences(private val project: Project)
it.copy(editorReviewEnabled = value)
}
}
var changesGrouping: Set<String>
get() = state.changesGrouping
set(value) {
updateStateAndEmit {
it.copy(changesGrouping = value)
}
}
val changesGroupingState: StateFlow<Set<String>> = stateFlow.mapState { it.changesGrouping }
}
@@ -15,10 +15,13 @@ import git4idea.changes.GitBranchComparisonResult
import kotlinx.coroutines.*
import kotlinx.coroutines.flow.*
import org.jetbrains.plugins.gitlab.api.GitLabId
import org.jetbrains.plugins.gitlab.mergerequest.GitLabMergeRequestsPreferences
import org.jetbrains.plugins.gitlab.mergerequest.data.*
import java.util.concurrent.ConcurrentHashMap
interface GitLabMergeRequestChangeListViewModel : CodeReviewChangeListViewModel.WithDetails {
interface GitLabMergeRequestChangeListViewModel
: CodeReviewChangeListViewModel.WithDetails,
CodeReviewChangeListViewModel.WithGrouping {
val isOnLatest: Boolean
fun setViewedState(changes: Iterable<RefComparisonChange>, viewed: Boolean)
@@ -33,6 +36,7 @@ internal class GitLabMergeRequestChangeListViewModelImpl(
) : CodeReviewChangeListViewModelBase(parentCs, changeList),
GitLabMergeRequestChangeListViewModel {
private val persistentChangesViewedState = project.service<GitLabPersistentMergeRequestChangesViewedState>()
private val preferences = project.service<GitLabMergeRequestsPreferences>()
private val _showDiffRequests = MutableSharedFlow<Unit>()
val showDiffRequests: Flow<Unit> = _showDiffRequests.asSharedFlow()
@@ -67,6 +71,8 @@ internal class GitLabMergeRequestChangeListViewModelImpl(
}
}.stateIn(cs, SharingStarted.Eagerly, emptyMap())
override val grouping: StateFlow<Set<String>> = preferences.changesGroupingState
override fun showDiffPreview() {
cs.launch {
_showDiffRequests.emit(Unit)
@@ -90,6 +96,10 @@ internal class GitLabMergeRequestChangeListViewModelImpl(
viewed
)
}
override fun setGrouping(grouping: Collection<String>) {
preferences.changesGrouping = grouping.toSet()
}
}
@OptIn(ExperimentalCoroutinesApi::class)