@@ -21,24 +21,29 @@ import com.jetbrains.toolbox.api.core.PluginSecretStore
21
21
import com.jetbrains.toolbox.api.core.PluginSettingsStore
22
22
import com.jetbrains.toolbox.api.core.ServiceLocator
23
23
import com.jetbrains.toolbox.api.core.ui.icons.SvgIcon
24
+ import com.jetbrains.toolbox.api.core.util.LoadableState
25
+ import com.jetbrains.toolbox.api.localization.LocalizableStringFactory
24
26
import com.jetbrains.toolbox.api.remoteDev.ProviderVisibilityState
25
- import com.jetbrains.toolbox.api.remoteDev.RemoteEnvironmentConsumer
26
27
import com.jetbrains.toolbox.api.remoteDev.RemoteProvider
28
+ import com.jetbrains.toolbox.api.remoteDev.RemoteProviderEnvironment
27
29
import com.jetbrains.toolbox.api.remoteDev.ui.EnvironmentUiPageManager
28
30
import com.jetbrains.toolbox.api.ui.ToolboxUi
29
- import com.jetbrains.toolbox.api.ui.actions.RunnableActionDescription
30
- import com.jetbrains.toolbox.api.ui.components.AccountDropdownField
31
+ import com.jetbrains.toolbox.api.ui.actions.ActionDescription
31
32
import com.jetbrains.toolbox.api.ui.components.UiPage
32
33
import kotlinx.coroutines.CoroutineScope
33
34
import kotlinx.coroutines.Job
34
35
import kotlinx.coroutines.delay
36
+ import kotlinx.coroutines.flow.MutableStateFlow
37
+ import kotlinx.coroutines.flow.StateFlow
35
38
import kotlinx.coroutines.isActive
36
39
import kotlinx.coroutines.launch
37
40
import okhttp3.OkHttpClient
38
41
import java.net.URI
39
42
import java.net.URL
40
43
import kotlin.coroutines.cancellation.CancellationException
41
44
import kotlin.time.Duration.Companion.seconds
45
+ import com.jetbrains.toolbox.api.ui.components.AccountDropdownField as DropDownMenu
46
+ import com.jetbrains.toolbox.api.ui.components.AccountDropdownField as dropDownFactory
42
47
43
48
class CoderRemoteProvider (
44
49
private val serviceLocator : ServiceLocator ,
@@ -47,10 +52,10 @@ class CoderRemoteProvider(
47
52
private val logger = CoderLoggerFactory .getLogger(javaClass)
48
53
49
54
private val ui: ToolboxUi = serviceLocator.getService(ToolboxUi ::class .java)
50
- private val consumer: RemoteEnvironmentConsumer = serviceLocator.getService(RemoteEnvironmentConsumer ::class .java)
51
55
private val coroutineScope: CoroutineScope = serviceLocator.getService(CoroutineScope ::class .java)
52
56
private val settingsStore: PluginSettingsStore = serviceLocator.getService(PluginSettingsStore ::class .java)
53
57
private val secretsStore: PluginSecretStore = serviceLocator.getService(PluginSecretStore ::class .java)
58
+ private val i18n = serviceLocator.getService(LocalizableStringFactory ::class .java)
54
59
55
60
// Current polling job.
56
61
private var pollJob: Job ? = null
@@ -61,8 +66,8 @@ class CoderRemoteProvider(
61
66
private val settings: CoderSettings = CoderSettings (settingsService)
62
67
private val secrets: CoderSecretsService = CoderSecretsService (secretsStore)
63
68
private val settingsPage: CoderSettingsPage = CoderSettingsPage (settingsService)
64
- private val dialogUi = DialogUi (settings, ui )
65
- private val linkHandler = LinkHandler (settings, httpClient, dialogUi)
69
+ private val dialogUi = DialogUi (serviceLocator, settings )
70
+ private val linkHandler = LinkHandler (serviceLocator, settings, httpClient, dialogUi)
66
71
67
72
// The REST client, if we are signed in
68
73
private var client: CoderRestClient ? = null
@@ -75,6 +80,10 @@ class CoderRemoteProvider(
75
80
// On the first load, automatically log in if we can.
76
81
private var firstRun = true
77
82
83
+ override val environments: MutableStateFlow <LoadableState <List <RemoteProviderEnvironment >>> = MutableStateFlow (
84
+ LoadableState .Loading
85
+ )
86
+
78
87
/* *
79
88
* With the provided client, start polling for workspaces. Every time a new
80
89
* workspace is added, reconfigure SSH using the provided cli (including the
@@ -84,7 +93,7 @@ class CoderRemoteProvider(
84
93
while (isActive) {
85
94
try {
86
95
logger.debug(" Fetching workspace agents from {}" , client.url)
87
- val environments = client.workspaces().flatMap { ws ->
96
+ val resolvedEnvironments = client.workspaces().flatMap { ws ->
88
97
// Agents are not included in workspaces that are off
89
98
// so fetch them separately.
90
99
when (ws.latestBuild.status) {
@@ -117,16 +126,16 @@ class CoderRemoteProvider(
117
126
// Reconfigure if a new environment is found.
118
127
// TODO@JB: Should we use the add/remove listeners instead?
119
128
val newEnvironments = lastEnvironments
120
- ?.let { environments .subtract(it) }
121
- ? : environments
129
+ ?.let { resolvedEnvironments .subtract(it) }
130
+ ? : resolvedEnvironments
122
131
if (newEnvironments.isNotEmpty()) {
123
132
logger.info(" Found new environment(s), reconfiguring CLI: {}" , newEnvironments)
124
133
cli.configSsh(newEnvironments.map { it.name }.toSet())
125
134
}
126
135
127
- consumer.consumeEnvironments( environments, true )
136
+ environments.value = LoadableState . Value (resolvedEnvironments.toList() )
128
137
129
- lastEnvironments = environments
138
+ lastEnvironments = resolvedEnvironments
130
139
} catch (_: CancellationException ) {
131
140
logger.debug(" {} polling loop canceled" , client.url)
132
141
break
@@ -155,21 +164,20 @@ class CoderRemoteProvider(
155
164
/* *
156
165
* A dropdown that appears at the top of the environment list to the right.
157
166
*/
158
- override fun getAccountDropDown (): AccountDropdownField ? {
167
+ override fun getAccountDropDown (): DropDownMenu ? {
159
168
val username = client?.me?.username
160
169
if (username != null ) {
161
- return AccountDropdownField ( username, Runnable { logout() })
170
+ return dropDownFactory(i18n.pnotr( username), { logout() })
162
171
}
163
172
return null
164
173
}
165
174
166
- /* *
167
- * List of actions that appear next to the account.
168
- */
169
- override fun getAdditionalPluginActions (): List <RunnableActionDescription > = listOf (
170
- Action (" Settings" , closesPage = false ) {
171
- ui.showUiPage(settingsPage)
172
- },
175
+ override val additionalPluginActions: StateFlow <List <ActionDescription >> = MutableStateFlow (
176
+ listOf (
177
+ Action (i18n.ptrl(" Settings" )) {
178
+ ui.showUiPage(settingsPage)
179
+ },
180
+ )
173
181
)
174
182
175
183
/* *
@@ -182,7 +190,7 @@ class CoderRemoteProvider(
182
190
pollJob?.cancel()
183
191
client = null
184
192
lastEnvironments = null
185
- consumer.consumeEnvironments (emptyList(), true )
193
+ environments.value = LoadableState . Value (emptyList())
186
194
}
187
195
188
196
override val svgIcon: SvgIcon =
@@ -226,20 +234,10 @@ class CoderRemoteProvider(
226
234
*/
227
235
override fun setVisible (visibilityState : ProviderVisibilityState ) {}
228
236
229
- /* *
230
- * Ignored; unsure if we should use this over the consumer we get passed in.
231
- */
232
- override fun addEnvironmentsListener (listener : RemoteEnvironmentConsumer ) {}
233
-
234
- /* *
235
- * Ignored; unsure if we should use this over the consumer we get passed in.
236
- */
237
- override fun removeEnvironmentsListener (listener : RemoteEnvironmentConsumer ) {}
238
-
239
237
/* *
240
238
* Handle incoming links (like from the dashboard).
241
239
*/
242
- override fun handleUri (uri : URI ) {
240
+ override suspend fun handleUri (uri : URI ) {
243
241
val params = uri.toQueryParameters()
244
242
coroutineScope.launch {
245
243
val name = linkHandler.handle(params)
0 commit comments