@@ -41,11 +41,13 @@ import com.jetbrains.rd.util.ConcurrentHashMap
4141import com.jetbrains.rd.util.URI
4242import com.jetbrains.rd.util.lifetime.Lifetime
4343import com.jetbrains.rd.util.lifetime.LifetimeDefinition
44+ import com.jetbrains.rd.util.threading.coroutines.launch
4445import io.gitpod.gitpodprotocol.api.entities.WorkspaceInstance
4546import io.gitpod.jetbrains.gateway.common.GitpodConnectionHandleFactory
4647import io.gitpod.jetbrains.icons.GitpodIcons
4748import kotlinx.coroutines.*
4849import kotlinx.coroutines.future.await
50+ import java.awt.Component
4951import java.net.URL
5052import java.net.http.HttpClient
5153import java.net.http.HttpRequest
@@ -71,6 +73,17 @@ class GitpodConnectionProvider : GatewayConnectionProvider {
7173 private val jacksonMapper = jacksonObjectMapper()
7274 .configure(DeserializationFeature .FAIL_ON_UNKNOWN_PROPERTIES , false )
7375
76+
77+ private fun showTimedOutDialogDialog (workspaceId : String , detail : String? ) {
78+ val title = " Workspace Timed Out"
79+ val message = " Your workspace $workspaceId has timed out${if (detail.isNullOrBlank()) " " else " : $detail " } ."
80+ val okButton = Messages .getOkButton()
81+ val options = arrayOf(okButton)
82+ val defaultIndex = 0
83+ val icon = Messages .getInformationIcon()
84+ Messages .showDialog(message, title, options, defaultIndex, icon)
85+ }
86+
7487 override suspend fun connect (
7588 parameters : Map <String , String >,
7689 requestor : ConnectionRequestor
@@ -188,6 +201,7 @@ class GitpodConnectionProvider : GatewayConnectionProvider {
188201 var thinClientJob: Job ? = null
189202
190203 var lastUpdate: WorkspaceInstance ? = null
204+ var canceledByGitpod = false
191205 try {
192206 for (update in updates) {
193207 try {
@@ -255,8 +269,8 @@ class GitpodConnectionProvider : GatewayConnectionProvider {
255269 statusMessage.text = " "
256270 }
257271 }
258-
259272 if (update.status.phase == " stopping" || update.status.phase == " stopped" ) {
273+ canceledByGitpod = true
260274 thinClientJob?.cancel()
261275 thinClient?.close()
262276 }
@@ -295,9 +309,41 @@ class GitpodConnectionProvider : GatewayConnectionProvider {
295309 SshHostTunnelConnector (credentials),
296310 URI (joinLinkResp.joinLink)
297311 )
312+ var triggeredClientClosed = false
298313 clientHandle.clientClosed.advise(connectionLifetime) {
299- application.invokeLater {
300- connectionLifetime.terminate()
314+ // Been canceled by user
315+ if (! canceledByGitpod) {
316+ connectionLifetime.launch {
317+ // Delay for 5 seconds to see if thinClient could be terminated in time
318+ // Then we don't see error dialog from Gateway
319+ delay(5000 )
320+ application.invokeLater {
321+ connectionLifetime.terminate()
322+ }
323+ }
324+ return @advise
325+ }
326+ if (triggeredClientClosed) {
327+ return @advise
328+ }
329+ triggeredClientClosed = true
330+ // Wait until workspace is stopped
331+ suspend fun waitUntilStopped (): Boolean {
332+ while (lastUpdate.status.phase != " stopped" ) {
333+ delay(1000 )
334+ }
335+ return true
336+ }
337+ // Check if it's timed out, if so, show timed out dialog
338+ connectionLifetime.launch {
339+ val isInStoppedPhase = waitUntilStopped()
340+ val isTimedOut = isInStoppedPhase && phaseMessage.text == " Timed Out"
341+ application.invokeLater {
342+ if (isTimedOut) {
343+ showTimedOutDialogDialog(connectParams.resolvedWorkspaceId, lastUpdate.status.conditions.timeout)
344+ }
345+ connectionLifetime.terminate()
346+ }
301347 }
302348 }
303349 clientHandle.onClientPresenceChanged.advise(connectionLifetime) {
0 commit comments