@@ -17,10 +17,8 @@ import com.coder.gateway.services.CoderRestClientService
1717import com.coder.gateway.services.CoderSettingsService
1818import com.coder.gateway.util.humanizeConnectionError
1919import com.coder.gateway.util.toURL
20- import com.coder.gateway.util.withPath
2120import com.coder.gateway.util.withoutNull
2221import com.intellij.icons.AllIcons
23- import com.intellij.ide.BrowserUtil
2422import com.intellij.openapi.Disposable
2523import com.intellij.openapi.actionSystem.AnActionEvent
2624import com.intellij.openapi.application.ModalityState
@@ -56,6 +54,7 @@ import kotlinx.coroutines.delay
5654import kotlinx.coroutines.isActive
5755import kotlinx.coroutines.launch
5856import kotlinx.coroutines.withContext
57+ import java.awt.Color
5958import java.awt.Component
6059import java.awt.Dimension
6160import java.util.Locale
@@ -175,15 +174,21 @@ class CoderGatewayRecentWorkspaceConnectionsView(private val setContentCallback:
175174 val workspaceWithAgent = deployment?.items?.firstOrNull { it.workspace.name == workspaceName }
176175 val status =
177176 if (deploymentError != null ) {
178- Triple (UIUtil .getBalloonErrorIcon (), UIUtil .getErrorForeground(), deploymentError )
177+ Triple (UIUtil .getErrorForeground (), deploymentError, UIUtil .getBalloonErrorIcon() )
179178 } else if (workspaceWithAgent != null ) {
179+ val inLoadingState = listOf (WorkspaceStatus .STARTING , WorkspaceStatus .CANCELING , WorkspaceStatus .DELETING , WorkspaceStatus .STOPPING ).contains(workspaceWithAgent?.workspace?.latestBuild?.status)
180+
180181 Triple (
181- workspaceWithAgent.status.icon,
182182 workspaceWithAgent.status.statusColor(),
183183 workspaceWithAgent.status.description,
184+ if (inLoadingState) {
185+ AnimatedIcon .Default ()
186+ } else {
187+ null
188+ },
184189 )
185190 } else {
186- Triple (AnimatedIcon . Default . INSTANCE , UIUtil .getContextHelpForeground(), " Querying workspace status..." )
191+ Triple (UIUtil .getContextHelpForeground(), " Querying workspace status..." , AnimatedIcon . Default () )
187192 }
188193 val gap =
189194 if (top) {
@@ -193,11 +198,6 @@ class CoderGatewayRecentWorkspaceConnectionsView(private val setContentCallback:
193198 TopGap .MEDIUM
194199 }
195200 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- }
201201 label(workspaceName).applyToComponent {
202202 font = JBFont .h3().asBold()
203203 }.align(AlignX .LEFT ).gap(RightGap .SMALL )
@@ -206,94 +206,44 @@ class CoderGatewayRecentWorkspaceConnectionsView(private val setContentCallback:
206206 font = ComponentPanelBuilder .getCommentFont(font)
207207 }
208208 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- )
279209 }.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.
280214 if (deploymentError == null || showError) {
281215 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
285221 }
286222 }
287223 }
224+
288225 connections.forEach { workspaceProjectIDE ->
289226 row {
290227 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+ }
297247 label(" " ).resizableColumn().align(AlignX .FILL )
298248 label(workspaceProjectIDE.ideName).applyToComponent {
299249 foreground = JBUI .CurrentTheme .ContextHelp .FOREGROUND
0 commit comments