@@ -20,13 +20,16 @@ import com.jetbrains.toolbox.api.remoteDev.RemoteProvider
2020import com.jetbrains.toolbox.api.remoteDev.RemoteProviderEnvironment
2121import com.jetbrains.toolbox.api.ui.actions.ActionDescription
2222import com.jetbrains.toolbox.api.ui.components.UiPage
23+ import kotlinx.coroutines.ExperimentalCoroutinesApi
2324import kotlinx.coroutines.Job
24- import kotlinx.coroutines.delay
25+ import kotlinx.coroutines.channels.Channel
2526import kotlinx.coroutines.flow.MutableStateFlow
2627import kotlinx.coroutines.flow.StateFlow
2728import kotlinx.coroutines.flow.update
2829import kotlinx.coroutines.isActive
2930import kotlinx.coroutines.launch
31+ import kotlinx.coroutines.selects.onTimeout
32+ import kotlinx.coroutines.selects.select
3033import okhttp3.OkHttpClient
3134import java.net.URI
3235import java.net.URL
@@ -35,18 +38,20 @@ import kotlin.time.Duration.Companion.seconds
3538import com.jetbrains.toolbox.api.ui.components.AccountDropdownField as DropDownMenu
3639import com.jetbrains.toolbox.api.ui.components.AccountDropdownField as dropDownFactory
3740
41+ @OptIn(ExperimentalCoroutinesApi ::class )
3842class CoderRemoteProvider (
3943 private val context : CoderToolboxContext ,
4044 private val httpClient : OkHttpClient ,
4145) : RemoteProvider(" Coder" ) {
4246 // Current polling job.
4347 private var pollJob: Job ? = null
44- private var lastEnvironments: Set <CoderRemoteEnvironment >? = null
48+ private val lastEnvironments = mutableSetOf <CoderRemoteEnvironment >()
4549
46- private val cSettings = context.settingsStore.readOnly()
50+ private val settings = context.settingsStore.readOnly()
4751
4852 // Create our services from the Toolbox ones.
49- private val settingsPage: CoderSettingsPage = CoderSettingsPage (context)
53+ private val triggerSshConfig = Channel <Boolean >(Channel .CONFLATED )
54+ private val settingsPage: CoderSettingsPage = CoderSettingsPage (context, triggerSshConfig)
5055 private val dialogUi = DialogUi (context)
5156
5257 // The REST client, if we are signed in
@@ -92,7 +97,7 @@ class CoderRemoteProvider(
9297 }?.map { agent ->
9398 // If we have an environment already, update that.
9499 val env = CoderRemoteEnvironment (context, client, ws, agent)
95- lastEnvironments? .firstOrNull { it == env }?.let {
100+ lastEnvironments.firstOrNull { it == env }?.let {
96101 it.update(ws, agent)
97102 it
98103 } ? : env
@@ -107,9 +112,7 @@ class CoderRemoteProvider(
107112
108113 // Reconfigure if a new environment is found.
109114 // TODO@JB: Should we use the add/remove listeners instead?
110- val newEnvironments = lastEnvironments
111- ?.let { resolvedEnvironments.subtract(it) }
112- ? : resolvedEnvironments
115+ val newEnvironments = resolvedEnvironments.subtract(lastEnvironments)
113116 if (newEnvironments.isNotEmpty()) {
114117 context.logger.info(" Found new environment(s), reconfiguring CLI: $newEnvironments " )
115118 cli.configSsh(newEnvironments.map { it.name }.toSet())
@@ -124,8 +127,10 @@ class CoderRemoteProvider(
124127 true
125128 }
126129 }
127-
128- lastEnvironments = resolvedEnvironments
130+ lastEnvironments.apply {
131+ clear()
132+ addAll(resolvedEnvironments)
133+ }
129134 } catch (_: CancellationException ) {
130135 context.logger.debug(" ${client.url} polling loop canceled" )
131136 break
@@ -136,7 +141,17 @@ class CoderRemoteProvider(
136141 break
137142 }
138143 // TODO: Listening on a web socket might be better?
139- delay(5 .seconds)
144+ select<Unit > {
145+ onTimeout(5 .seconds) {
146+ context.logger.trace(" workspace poller waked up by the 5 seconds timeout" )
147+ }
148+ triggerSshConfig.onReceive { shouldTrigger ->
149+ if (shouldTrigger) {
150+ context.logger.trace(" workspace poller waked up because it should reconfigure the ssh configurations" )
151+ cli.configSsh(lastEnvironments.map { it.name }.toSet())
152+ }
153+ }
154+ }
140155 }
141156 }
142157
@@ -178,7 +193,7 @@ class CoderRemoteProvider(
178193 override fun close () {
179194 pollJob?.cancel()
180195 client?.close()
181- lastEnvironments = null
196+ lastEnvironments.clear()
182197 environments.value = LoadableState .Value (emptyList())
183198 isInitialized.update { false }
184199 }
@@ -270,7 +285,7 @@ class CoderRemoteProvider(
270285 var autologinEx: Exception ? = null
271286 context.secrets.lastToken.let { lastToken ->
272287 context.secrets.lastDeploymentURL.let { lastDeploymentURL ->
273- if (autologin && lastDeploymentURL.isNotBlank() && (lastToken.isNotBlank() || ! cSettings .requireTokenAuth)) {
288+ if (autologin && lastDeploymentURL.isNotBlank() && (lastToken.isNotBlank() || ! settings .requireTokenAuth)) {
274289 try {
275290 return createConnectPage(URL (lastDeploymentURL), lastToken)
276291 } catch (ex: Exception ) {
@@ -342,7 +357,7 @@ class CoderRemoteProvider(
342357 if (it.isNotBlank() && context.secrets.lastDeploymentURL == deploymentURL.toString()) {
343358 it to SettingSource .LAST_USED
344359 } else {
345- cSettings .token(deploymentURL)
360+ settings .token(deploymentURL)
346361 }
347362 }
348363
0 commit comments