[space] Upgrade space-idea-sdk to 1.1.63274
[idea/community.git] / plugins / space / src / main / kotlin / com / intellij / space / vcs / clone / SpaceCloneComponentViewModel.kt
1 // Copyright 2000-2021 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.clone
3
4 import circlet.client.api.*
5 import circlet.client.api.impl.vcsPasswords
6 import circlet.client.pr
7 import circlet.client.repoService
8 import circlet.client.ssh
9 import circlet.client.star
10 import circlet.platform.api.Ref
11 import circlet.platform.client.resolve
12 import circlet.platform.client.resolveRefsOrFetch
13 import circlet.platform.client.xTransformedPagedListOnFlux
14 import circlet.workspaces.Workspace
15 import com.intellij.space.settings.CloneType
16 import com.intellij.space.settings.CloneType.HTTP
17 import com.intellij.space.settings.SpaceSettings
18 import com.intellij.space.vcs.SpaceHttpPasswordState
19 import com.intellij.space.vcs.SpaceKeysState
20 import com.intellij.util.ui.cloneDialog.SearchableListItem
21 import libraries.coroutines.extra.Lifetime
22 import libraries.coroutines.extra.Lifetimed
23 import libraries.coroutines.extra.launch
24 import runtime.Ui
25 import runtime.UiDispatch
26 import runtime.dispatchInterval
27 import runtime.reactive.MutableProperty
28 import runtime.reactive.Property
29 import runtime.reactive.mutableProperty
30 import runtime.reactive.property.mapInit
31 import runtime.utils.mapToSet
32
33 internal class SpaceCloneComponentViewModel(
34   override val lifetime: Lifetime,
35   workspace: Workspace
36 ) : Lifetimed {
37   private val projectService: Projects = workspace.client.pr
38   private val repositoryService: RepositoryService = workspace.client.repoService
39   private val starService: Star = workspace.client.star
40   private val ssh: SshKeys = workspace.client.ssh
41
42   val isLoading: MutableProperty<Boolean> = Property.createMutable(false)
43
44   val me: MutableProperty<TD_MemberProfile> = workspace.me
45
46   val repos = xTransformedPagedListOnFlux<Ref<PR_Project>, List<SpaceCloneListItem>>(
47     client = workspace.client,
48     batchSize = 10,
49     keyFn = { it.id },
50     result = { allProjectRefs ->
51       val allProjectsWithRepos = workspace.client.arena.resolveRefsOrFetch {
52         allProjectRefs.map { it to it.extensionRef(ProjectReposRecord::class) }
53       }
54       val starredProjectIds = starService.starredProjects().mapToSet(Ref<PR_Project>::id)
55
56       allProjectsWithRepos.map { (projectRef, reposRef) ->
57         val project = projectRef.resolve()
58         val repos = reposRef.resolve().repos
59         val isStarred = projectRef.id in starredProjectIds
60         repos.map { repo ->
61           val detailsProperty = mutableProperty<RepoDetails?>(null)
62           val item = SpaceCloneListItem(project, isStarred, repo, detailsProperty)
63           item.visible.forEach(lifetime) { visible ->
64             launch(lifetime, Ui) {
65               if (visible) {
66                 detailsProperty.value = repositoryService.repositoryDetails(project.key, repo.name)
67               }
68             }
69           }
70           item
71         }
72       }
73     }
74   ) { batch ->
75     projectService.projectsBatch(batch, "", "")
76   }
77
78   val cloneType: MutableProperty<CloneType> = Property.createMutable(SpaceSettings.getInstance().cloneType)
79
80   val selectedUrl: MutableProperty<String?> = Property.createMutable(null)
81
82   val spaceHttpPasswordState: MutableProperty<SpaceHttpPasswordState> = lifetime.mapInit<CloneType, SpaceHttpPasswordState>(cloneType,
83                                                                                                                             SpaceHttpPasswordState.NotChecked) { cloneType ->
84     if (cloneType == CloneType.SSH) return@mapInit SpaceHttpPasswordState.NotChecked
85
86     workspace.client.api.vcsPasswords().getVcsPassword(me.value.identifier).let {
87       if (it == null) SpaceHttpPasswordState.NotSet else SpaceHttpPasswordState.Set(it)
88     }
89   } as MutableProperty<SpaceHttpPasswordState>
90
91   val circletKeysState: MutableProperty<SpaceKeysState> = run {
92     val property: MutableProperty<SpaceKeysState> = mutableProperty(SpaceKeysState.NotChecked)
93     UiDispatch.dispatchInterval(1000, lifetime) {
94       launch(lifetime, Ui) {
95         property.value = loadSshState(cloneType.value)
96       }
97     }
98     property
99   }
100
101   private suspend fun loadSshState(cloneType: CloneType): SpaceKeysState {
102     if (cloneType == HTTP) return SpaceKeysState.NotChecked
103
104     return ssh.sshKeys(me.value.identifier).let {
105       if (it.isNullOrEmpty()) SpaceKeysState.NotSet else SpaceKeysState.Set(it)
106     }
107   }
108
109   val readyToClone: Property<Boolean> = lifetime.mapInit(selectedUrl, spaceHttpPasswordState, circletKeysState, false) { url, http, ssh ->
110     url != null && (http is SpaceHttpPasswordState.Set || ssh is SpaceKeysState.Set)
111   }
112 }
113
114 data class SpaceCloneListItem(
115   val project: PR_Project,
116   val starred: Boolean,
117   val repoInfo: PR_RepositoryInfo,
118   val repoDetails: Property<RepoDetails?>
119 ) : SearchableListItem, Comparable<SpaceCloneListItem> {
120
121   val visible = mutableProperty(false)
122
123   override val stringToSearch: String
124     get() = repoInfo.name
125
126   override fun compareTo(other: SpaceCloneListItem): Int {
127     if (starred != other.starred) {
128       return other.starred.compareTo(starred)
129     }
130     if (project.name != other.project.name) {
131       return project.name.compareTo(other.project.name)
132     }
133     return repoInfo.name.compareTo(other.repoInfo.name)
134   }
135 }