diff --git a/src/main/kotlin/com/coder/gateway/views/steps/CoderLocateRemoteProjectStepView.kt b/src/main/kotlin/com/coder/gateway/views/steps/CoderLocateRemoteProjectStepView.kt index 936bc459..f5caa95e 100644 --- a/src/main/kotlin/com/coder/gateway/views/steps/CoderLocateRemoteProjectStepView.kt +++ b/src/main/kotlin/com/coder/gateway/views/steps/CoderLocateRemoteProjectStepView.kt @@ -14,6 +14,7 @@ import com.intellij.openapi.wm.impl.welcomeScreen.WelcomeScreenUIManager import com.intellij.remote.AuthType import com.intellij.remote.RemoteCredentialsHolder import com.intellij.ui.AnimatedIcon +import com.intellij.ui.ColoredListCellRenderer import com.intellij.ui.components.JBTextField import com.intellij.ui.dsl.builder.BottomGap import com.intellij.ui.dsl.builder.RowLayout @@ -21,7 +22,6 @@ import com.intellij.ui.dsl.builder.TopGap import com.intellij.ui.dsl.builder.panel import com.intellij.ui.dsl.gridLayout.HorizontalAlign import com.intellij.util.ui.JBFont -import com.intellij.util.ui.JBUI import com.intellij.util.ui.UIUtil import com.jetbrains.gateway.api.GatewayUI import com.jetbrains.gateway.ssh.CachingProductsJsonWrapper @@ -47,7 +47,6 @@ class CoderLocateRemoteProjectStepView : CoderWorkspacesWizardStep, Disposable { private val cs = CoroutineScope(Dispatchers.Main) private val coderClient: CoderRestClientService = ApplicationManager.getApplication().getService(CoderRestClientService::class.java) - private val spinner = JLabel("", AnimatedIcon.Default(), SwingConstants.LEFT) private var ideComboBoxModel = DefaultComboBoxModel() private lateinit var titleLabel: JLabel @@ -111,25 +110,35 @@ class CoderLocateRemoteProjectStepView : CoderWorkspacesWizardStep, Disposable { cs.launch { logger.info("Retrieving available IDE's for ${selectedWorkspace.name} workspace...") - val workspaceOS = withContext(Dispatchers.IO) { - RemoteCredentialsHolder().apply { - setHost("coder.${selectedWorkspace.name}") - userName = "coder" - authType = AuthType.OPEN_SSH - }.guessOs - } - logger.info("Resolved OS and Arch for ${selectedWorkspace.name} is: $workspaceOS") - val idesWithStatus = IntelliJPlatformProduct.values() - .filter { it.showInGateway } - .flatMap { CachingProductsJsonWrapper.getAvailableIdes(it, workspaceOS) } - .map { ide -> IdeWithStatus(ide.product, ide.buildNumber, IdeStatus.DOWNLOAD, ide.downloadLink, ide.presentableVersion) } - - if (idesWithStatus.isEmpty()) { - logger.warn("Could not resolve any IDE for workspace ${selectedWorkspace.name}, probably $workspaceOS is not supported by Gateway") - } else { - cbIDE.remove(spinner) - ideComboBoxModel.addAll(idesWithStatus) - cbIDE.selectedIndex = 0 + try { + val workspaceOS = withContext(Dispatchers.IO) { + RemoteCredentialsHolder().apply { + setHost("coder.${selectedWorkspace.name}") + userName = "coder" + authType = AuthType.OPEN_SSH + }.guessOs + } + logger.info("Resolved OS and Arch for ${selectedWorkspace.name} is: $workspaceOS") + val idesWithStatus = IntelliJPlatformProduct.values() + .filter { it.showInGateway } + .flatMap { CachingProductsJsonWrapper.getAvailableIdes(it, workspaceOS) } + .map { ide -> IdeWithStatus(ide.product, ide.buildNumber, IdeStatus.DOWNLOAD, ide.downloadLink, ide.presentableVersion) } + + if (idesWithStatus.isEmpty()) { + logger.warn("Could not resolve any IDE for workspace ${selectedWorkspace.name}, probably $workspaceOS is not supported by Gateway") + } else { + ideComboBoxModel.addAll(idesWithStatus) + cbIDE.selectedIndex = 0 + } + } catch (e: Exception) { + logger.error("Could not resolve any IDE for workspace ${selectedWorkspace.name}. Reason: $e") + cbIDE.renderer = object : ColoredListCellRenderer() { + override fun customizeCellRenderer(list: JList, value: IdeWithStatus?, index: Int, isSelected: Boolean, cellHasFocus: Boolean) { + background = UIUtil.getListBackground(isSelected, cellHasFocus) + icon = UIUtil.getBalloonErrorIcon() + append(CoderGatewayBundle.message("gateway.connector.view.coder.remoteproject.ide.error.text", selectedWorkspace.name)) + } + } } } } @@ -162,18 +171,28 @@ class CoderLocateRemoteProjectStepView : CoderWorkspacesWizardStep, Disposable { } private class IDEComboBox(model: ComboBoxModel) : ComboBox(model) { + + init { + putClientProperty(AnimatedIcon.ANIMATION_IN_RENDERER_ALLOWED, true) + } + override fun getSelectedItem(): IdeWithStatus? { return super.getSelectedItem() as IdeWithStatus? } } private class IDECellRenderer : ListCellRenderer { + private val loadingComponentRenderer: ListCellRenderer = object : ColoredListCellRenderer() { + override fun customizeCellRenderer(list: JList, value: IdeWithStatus?, index: Int, isSelected: Boolean, cellHasFocus: Boolean) { + background = UIUtil.getListBackground(isSelected, cellHasFocus) + icon = AnimatedIcon.Default.INSTANCE + append(CoderGatewayBundle.message("gateway.connector.view.coder.remoteproject.loading.text")) + } + } + override fun getListCellRendererComponent(list: JList?, ideWithStatus: IdeWithStatus?, index: Int, isSelected: Boolean, cellHasFocus: Boolean): Component { return if (ideWithStatus == null && index == -1) { - JPanel().apply { - layout = FlowLayout(FlowLayout.LEFT) - add(JLabel("Retrieving products...", AnimatedIcon.Default(), SwingConstants.LEFT)) - } + loadingComponentRenderer.getListCellRendererComponent(list, null, -1, isSelected, cellHasFocus) } else if (ideWithStatus != null) { JPanel().apply { layout = FlowLayout(FlowLayout.LEFT) @@ -181,10 +200,10 @@ class CoderLocateRemoteProjectStepView : CoderWorkspacesWizardStep, Disposable { add(JLabel("${ideWithStatus.product.productCode} ${ideWithStatus.presentableVersion} ${ideWithStatus.buildNumber} | ${ideWithStatus.status.name.toLowerCase()}").apply { foreground = UIUtil.getLabelDisabledForeground() }) - background = JBUI.CurrentTheme.List.background(isSelected, cellHasFocus) + background = UIUtil.getListBackground(isSelected, cellHasFocus) } } else { - JPanel() + panel { } } } } diff --git a/src/main/resources/messages/CoderGatewayBundle.properties b/src/main/resources/messages/CoderGatewayBundle.properties index 5fef708c..2fd16be0 100644 --- a/src/main/resources/messages/CoderGatewayBundle.properties +++ b/src/main/resources/messages/CoderGatewayBundle.properties @@ -10,8 +10,10 @@ gateway.connector.view.login.token.label=Session Token: gateway.connector.view.coder.auth.next.text=Connect gateway.connector.view.login.cli.downloader.dialog.title=Authenticate and setup coder gateway.connector.view.coder.workspaces.next.text=Select IDE and Project -gateway.connector.view.coder.remoteproject.next.text=Download and Start IDE gateway.connector.view.coder.workspaces.choose.text=Choose a workspace +gateway.connector.view.coder.remoteproject.loading.text=Retrieving products... +gateway.connector.view.coder.remoteproject.ide.error.text=Could not retrieve any IDE for workspace {0} because an error was encountered +gateway.connector.view.coder.remoteproject.next.text=Download and Start IDE gateway.connector.view.coder.remoteproject.choose.text=Choose IDE and project for workspace {0} gateway.connector.recentconnections.title=Recent Coder Workspaces gateway.connector.recentconnections.remove.button.tooltip=Remove from Recent Connections