3cbed81cd25d88ffcdcb73e0c216133c1cfeadb4
[idea/community.git] / plugins / space / src / main / kotlin / com / intellij / space / vcs / review / details / SpaceReviewDetailsVm.kt
1 // Copyright 2000-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
2 package com.intellij.space.vcs.review.details
3
4 import circlet.client.api.ProjectKey
5 import circlet.client.api.TD_MemberProfile
6 import circlet.client.api.identifier
7 import circlet.code.api.*
8 import circlet.code.codeReview
9 import circlet.platform.api.Ref
10 import circlet.platform.api.TID
11 import circlet.platform.client.*
12 import circlet.workspaces.Workspace
13 import com.intellij.openapi.project.Project
14 import com.intellij.space.utils.SpaceUrls
15 import com.intellij.space.vcs.SpaceProjectInfo
16 import com.intellij.space.vcs.SpaceRepoInfo
17 import com.intellij.space.vcs.review.details.diff.SpaceDiffVm
18 import com.intellij.space.vcs.review.details.diff.SpaceDiffVmImpl
19 import com.intellij.space.vcs.review.details.diff.SpaceReviewDiffLoader
20 import com.intellij.space.vcs.review.details.process.SpaceReviewStateUpdater
21 import com.intellij.space.vcs.review.details.process.SpaceReviewStateUpdaterImpl
22 import libraries.coroutines.extra.Lifetime
23 import libraries.coroutines.extra.Lifetimed
24 import runtime.reactive.*
25 import runtime.reactive.property.seqCombineLatest
26
27 internal sealed class SpaceReviewDetailsVm<R : CodeReviewRecord>(
28   final override val lifetime: Lifetime,
29   val ideaProject: Project,
30   val spaceProjectInfo: SpaceProjectInfo,
31   spaceReposInfo: Set<SpaceRepoInfo>,
32   val reviewRef: Ref<R>,
33   val workspace: Workspace
34 ) : Lifetimed {
35   val client: KCircletClient = workspace.client
36
37   val review: Property<R> = reviewRef.property()
38
39   val projectKey: ProjectKey = review.value.project
40   val reviewKey: String? = review.value.key
41
42   val reviewUrl: String = SpaceUrls.review(projectKey, review.value.number)
43
44   val reviewId: TID = review.value.id
45
46   val title: Property<String> = cellProperty { review.live.title }
47
48   val state: Property<CodeReviewState> = cellProperty { review.live.state }
49
50   val createdAt: Property<Long> = cellProperty { review.live.createdAt }
51
52   val createdBy: Property<TD_MemberProfile> = cellProperty { review.live.createdBy!!.resolve() }
53
54   val turnBased: Property<Boolean?> = cellProperty { review.live.turnBased }
55
56   val reviewStateUpdater: SpaceReviewStateUpdater = SpaceReviewStateUpdaterImpl(workspace, review.value)
57
58   private val infoByRepos = spaceReposInfo.associateBy(SpaceRepoInfo::name)
59
60   private val participantsProperty: Property<LoadingValue<Ref<CodeReviewParticipants>>> = load {
61     client.arena.resolveRefsOrFetch {
62       reviewRef.extensionRef(CodeReviewParticipants::class)
63     }
64   }
65
66   private val participantsRef: Property<Ref<CodeReviewParticipants>?> = lastLoadedValueOrNull(participantsProperty)
67   private val pendingCounterRef: Property<Ref<CodeReviewPendingMessageCounter>?> = lastLoadedValueOrNull(pendingCounterAsync(client))
68
69   val participantsVm: Property<SpaceReviewParticipantsVm?> = seqCombineLatest(participantsRef,
70                                                                               pendingCounterRef) { participantsRef, pendingCounterRef ->
71     if (participantsRef != null && pendingCounterRef != null) {
72       SpaceReviewParticipantsVmImpl(lifetime, projectKey, reviewRef, participantsRef, pendingCounterRef, review.value.identifier, workspace)
73     }
74     else null
75   }
76
77   private val detailedInfo: Property<CodeReviewDetailedInfo?> = mapInit(review, null) {
78     client.codeReview.getReviewDetails(review.value.project.identifier, review.value.identifier)
79   }
80
81   val commits: Property<List<SpaceReviewCommitListItem>> = mapInit(detailedInfo, emptyList()) { detailedInfo ->
82     val revisions: List<RevisionsInReview> = detailedInfo?.commits ?: emptyList()
83
84     revisions.flatMap { revInReview ->
85       val repo = revInReview.repository
86       val repoInfo = infoByRepos[repo.name]
87       val commitsInRepository = revInReview.commits.size
88
89       revInReview.commits
90         .filterNot(GitCommitWithGraph::unreachable)
91         .mapIndexed { index, gitCommitWithGraph -> SpaceReviewCommitListItem(gitCommitWithGraph, repo, index, commitsInRepository, repoInfo) }
92     }
93   }
94
95   val selectedTab: MutableProperty<SelectedTab> = mutableProperty(SelectedTab.INFO)
96
97   val commitChangesVm: SpaceReviewChangesVm = SpaceReviewChangesVmImpl(
98     lifetime, client, projectKey, review.value.identifier,
99     reviewId, commits, participantsVm, infoByRepos
100   )
101
102   val allChangesVm: SpaceReviewChangesVmImpl = SpaceReviewChangesVmImpl(
103     lifetime, client, projectKey, review.value.identifier,
104     reviewId, commits, participantsVm, infoByRepos
105   )
106
107   val selectedChangesVm: Property<SpaceReviewChangesVm> = map(selectedTab) { tab ->
108     selectedOrAll(tab, commitChangesVm, allChangesVm)
109   }
110
111   val spaceDiffVm: SpaceDiffVm = SpaceDiffVmImpl(client,
112                                                  reviewId,
113                                                  reviewKey as String,
114                                                  projectKey,
115                                                  selectedChangesVm,
116                                                  SpaceReviewDiffLoader(lifetime, client),
117                                                  participantsVm)
118 }
119
120 private fun <T> selectedOrAll(tab: SelectedTab, selected: T, all: T): T = when (tab) {
121   SelectedTab.INFO -> all
122   SelectedTab.COMMITS -> selected
123 }
124
125 internal class MergeRequestDetailsVm(
126   lifetime: Lifetime,
127   ideaProject: Project,
128   spaceProjectInfo: SpaceProjectInfo,
129   spaceReposInfo: Set<SpaceRepoInfo>,
130   refMrRecord: Ref<MergeRequestRecord>,
131   workspace: Workspace
132 ) : SpaceReviewDetailsVm<MergeRequestRecord>(lifetime, ideaProject, spaceProjectInfo, spaceReposInfo, refMrRecord, workspace) {
133
134   private val branchPair: Property<MergeRequestBranchPair> = cellProperty { review.live.branchPair }
135
136   val repository: Property<String> = cellProperty { branchPair.live.repository }
137   val targetBranchInfo: Property<MergeRequestBranch?> = cellProperty { branchPair.live.targetBranchInfo }
138   val sourceBranchInfo: Property<MergeRequestBranch?> = cellProperty { branchPair.live.sourceBranchInfo }
139 }
140
141 internal class CommitSetReviewDetailsVm(
142   lifetime: Lifetime,
143   ideaProject: Project,
144   spaceProjectInfo: SpaceProjectInfo,
145   spaceReposInfo: Set<SpaceRepoInfo>,
146   refMrRecord: Ref<CommitSetReviewRecord>,
147   workspace: Workspace
148 ) : SpaceReviewDetailsVm<CommitSetReviewRecord>(lifetime, ideaProject, spaceProjectInfo, spaceReposInfo, refMrRecord, workspace)
149
150 internal fun createReviewDetailsVm(lifetime: Lifetime,
151                                    project: Project,
152                                    workspace: Workspace,
153                                    spaceProjectInfo: SpaceProjectInfo,
154                                    spaceReposInfo: Set<SpaceRepoInfo>,
155                                    codeReviewListItem: CodeReviewListItem): SpaceReviewDetailsVm<out CodeReviewRecord> {
156   val client = workspace.client
157   return when (val codeReviewRecord = codeReviewListItem.review.resolve()) {
158     is MergeRequestRecord -> MergeRequestDetailsVm(
159       lifetime,
160       project,
161       spaceProjectInfo,
162       spaceReposInfo,
163       codeReviewRecord.toRef(client.arena),
164       workspace
165     )
166     is CommitSetReviewRecord -> CommitSetReviewDetailsVm(
167       lifetime,
168       project,
169       spaceProjectInfo,
170       spaceReposInfo,
171       codeReviewRecord.toRef(client.arena),
172       workspace
173     )
174     else -> throw IllegalArgumentException("Unable to resolve CodeReviewRecord")
175   }
176 }
177
178 enum class SelectedTab {
179   INFO,
180   COMMITS
181 }
182
183 private fun SpaceReviewDetailsVm<*>.pendingCounterAsync(client: KCircletClient): LoadingProperty<Ref<CodeReviewPendingMessageCounter>> {
184   return load {
185     client.arena.resolveRefsOrFetch {
186       reviewRef.extensionRef(CodeReviewPendingMessageCounter::class)
187     }
188   }
189 }