@@ -17,10 +17,8 @@ import com.coder.gateway.services.CoderRestClientService
17
17
import com.coder.gateway.services.CoderSettingsService
18
18
import com.coder.gateway.util.humanizeConnectionError
19
19
import com.coder.gateway.util.toURL
20
- import com.coder.gateway.util.withPath
21
20
import com.coder.gateway.util.withoutNull
22
21
import com.intellij.icons.AllIcons
23
- import com.intellij.ide.BrowserUtil
24
22
import com.intellij.openapi.Disposable
25
23
import com.intellij.openapi.actionSystem.AnActionEvent
26
24
import com.intellij.openapi.application.ModalityState
@@ -56,6 +54,7 @@ import kotlinx.coroutines.delay
56
54
import kotlinx.coroutines.isActive
57
55
import kotlinx.coroutines.launch
58
56
import kotlinx.coroutines.withContext
57
+ import java.awt.Color
59
58
import java.awt.Component
60
59
import java.awt.Dimension
61
60
import java.util.Locale
@@ -175,15 +174,21 @@ class CoderGatewayRecentWorkspaceConnectionsView(private val setContentCallback:
175
174
val workspaceWithAgent = deployment?.items?.firstOrNull { it.workspace.name == workspaceName }
176
175
val status =
177
176
if (deploymentError != null ) {
178
- Triple (UIUtil .getBalloonErrorIcon (), UIUtil .getErrorForeground(), deploymentError )
177
+ Triple (UIUtil .getErrorForeground (), deploymentError, UIUtil .getBalloonErrorIcon() )
179
178
} else if (workspaceWithAgent != null ) {
179
+ val inLoadingState = listOf (WorkspaceStatus .STARTING , WorkspaceStatus .CANCELING , WorkspaceStatus .DELETING , WorkspaceStatus .STOPPING ).contains(workspaceWithAgent?.workspace?.latestBuild?.status)
180
+
180
181
Triple (
181
- workspaceWithAgent.status.icon,
182
182
workspaceWithAgent.status.statusColor(),
183
183
workspaceWithAgent.status.description,
184
+ if (inLoadingState) {
185
+ AnimatedIcon .Default ()
186
+ } else {
187
+ null
188
+ },
184
189
)
185
190
} else {
186
- Triple (AnimatedIcon . Default . INSTANCE , UIUtil .getContextHelpForeground(), " Querying workspace status..." )
191
+ Triple (UIUtil .getContextHelpForeground(), " Querying workspace status..." , AnimatedIcon . Default () )
187
192
}
188
193
val gap =
189
194
if (top) {
@@ -193,11 +198,6 @@ class CoderGatewayRecentWorkspaceConnectionsView(private val setContentCallback:
193
198
TopGap .MEDIUM
194
199
}
195
200
row {
196
- icon(status.first).applyToComponent {
197
- foreground = status.second
198
- }.align(AlignX .LEFT ).gap(RightGap .SMALL ).applyToComponent {
199
- size = Dimension (JBUI .scale(16 ), JBUI .scale(16 ))
200
- }
201
201
label(workspaceName).applyToComponent {
202
202
font = JBFont .h3().asBold()
203
203
}.align(AlignX .LEFT ).gap(RightGap .SMALL )
@@ -206,94 +206,44 @@ class CoderGatewayRecentWorkspaceConnectionsView(private val setContentCallback:
206
206
font = ComponentPanelBuilder .getCommentFont(font)
207
207
}
208
208
label(" " ).resizableColumn().align(AlignX .FILL )
209
- actionButton(
210
- object : DumbAwareAction (
211
- CoderGatewayBundle .message(" gateway.connector.recent-connections.start.button.tooltip" ),
212
- " " ,
213
- CoderIcons .RUN ,
214
- ) {
215
- override fun actionPerformed (e : AnActionEvent ) {
216
- withoutNull(workspaceWithAgent?.workspace, deployment?.client) { workspace, client ->
217
- jobs[workspace.id]?.cancel()
218
- jobs[workspace.id] =
219
- cs.launch(ModalityState .current().asContextElement()) {
220
- withContext(Dispatchers .IO ) {
221
- try {
222
- client.startWorkspace(workspace)
223
- fetchWorkspaces()
224
- } catch (e: Exception ) {
225
- logger.error(" Could not start workspace ${workspace.name} " , e)
226
- }
227
- }
228
- }
229
- }
230
- }
231
- },
232
- ).applyToComponent {
233
- isEnabled =
234
- listOf (
235
- WorkspaceStatus .STOPPED ,
236
- WorkspaceStatus .FAILED ,
237
- ).contains(workspaceWithAgent?.workspace?.latestBuild?.status)
238
- }
239
- .gap(RightGap .SMALL )
240
- actionButton(
241
- object : DumbAwareAction (
242
- CoderGatewayBundle .message(" gateway.connector.recent-connections.stop.button.tooltip" ),
243
- " " ,
244
- CoderIcons .STOP ,
245
- ) {
246
- override fun actionPerformed (e : AnActionEvent ) {
247
- withoutNull(workspaceWithAgent?.workspace, deployment?.client) { workspace, client ->
248
- jobs[workspace.id]?.cancel()
249
- jobs[workspace.id] =
250
- cs.launch(ModalityState .current().asContextElement()) {
251
- withContext(Dispatchers .IO ) {
252
- try {
253
- client.stopWorkspace(workspace)
254
- fetchWorkspaces()
255
- } catch (e: Exception ) {
256
- logger.error(" Could not stop workspace ${workspace.name} " , e)
257
- }
258
- }
259
- }
260
- }
261
- }
262
- },
263
- ).applyToComponent { isEnabled = workspaceWithAgent?.workspace?.latestBuild?.status == WorkspaceStatus .RUNNING }
264
- .gap(RightGap .SMALL )
265
- actionButton(
266
- object : DumbAwareAction (
267
- CoderGatewayBundle .message(" gateway.connector.recent-connections.terminal.button.tooltip" ),
268
- " " ,
269
- CoderIcons .OPEN_TERMINAL ,
270
- ) {
271
- override fun actionPerformed (e : AnActionEvent ) {
272
- withoutNull(workspaceWithAgent, deployment?.client) { ws, client ->
273
- val link = client.url.withPath(" /me/${ws.name} /terminal" )
274
- BrowserUtil .browse(link.toString())
275
- }
276
- }
277
- },
278
- )
279
209
}.topGap(gap)
210
+
211
+ val enableLinks = listOf (WorkspaceStatus .STOPPED , WorkspaceStatus .CANCELED , WorkspaceStatus .FAILED , WorkspaceStatus .STARTING , WorkspaceStatus .RUNNING ).contains(workspaceWithAgent?.workspace?.latestBuild?.status)
212
+
213
+ // We only display an API error on the first workspace rather than duplicating it on each workspace.
280
214
if (deploymentError == null || showError) {
281
215
row {
282
- // There must be a way to make this properly wrap?
283
- label(" <html><body style='width:350px;'>" + status.third + " </html>" ).applyToComponent {
284
- foreground = status.second
216
+ status.third?.let {
217
+ icon(it)
218
+ }
219
+ label(" <html><body style='width:350px;'>" + status.second + " </html>" ).applyToComponent {
220
+ foreground = status.first
285
221
}
286
222
}
287
223
}
224
+
288
225
connections.forEach { workspaceProjectIDE ->
289
226
row {
290
227
icon(workspaceProjectIDE.ideProduct.icon)
291
- cell(
292
- ActionLink (workspaceProjectIDE.projectPathDisplay) {
293
- CoderRemoteConnectionHandle ().connect { workspaceProjectIDE }
294
- GatewayUI .getInstance().reset()
295
- },
296
- )
228
+ if (enableLinks) {
229
+ cell(
230
+ ActionLink (workspaceProjectIDE.projectPathDisplay) {
231
+ withoutNull(deployment?.client, workspaceWithAgent?.workspace) { client, workspace ->
232
+ CoderRemoteConnectionHandle ().connect {
233
+ if (listOf (WorkspaceStatus .STOPPED , WorkspaceStatus .CANCELED , WorkspaceStatus .FAILED ).contains(workspace.latestBuild.status)) {
234
+ client.startWorkspace(workspace)
235
+ }
236
+ workspaceProjectIDE
237
+ }
238
+ GatewayUI .getInstance().reset()
239
+ }
240
+ },
241
+ )
242
+ } else {
243
+ label(workspaceProjectIDE.projectPathDisplay).applyToComponent {
244
+ foreground = Color .GRAY
245
+ }
246
+ }
297
247
label(" " ).resizableColumn().align(AlignX .FILL )
298
248
label(workspaceProjectIDE.ideName).applyToComponent {
299
249
foreground = JBUI .CurrentTheme .ContextHelp .FOREGROUND
0 commit comments