Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Commit abe0326

Browse files
Refactor link handler for Toolbox
In Toolbox, you get a ToolboxUi object that lets you show dialogs so we now have a dialog class for that which can be refactored to use ToolboxUi more easily. Also passed in the http client since we will need that in Toolbox to construct the client.
1 parent 9d040a8 commit abe0326

File tree

6 files changed

+345
-357
lines changed

6 files changed

+345
-357
lines changed

‎src/main/kotlin/com/coder/gateway/CoderGatewayConnectionProvider.kt‎

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,8 @@
33
package com.coder.gateway
44

55
import com.coder.gateway.services.CoderSettingsService
6-
import com.coder.gateway.util.handleLink
6+
import com.coder.gateway.util.DialogUi
7+
import com.coder.gateway.util.LinkHandler
78
import com.coder.gateway.util.isCoder
89
import com.intellij.openapi.components.service
910
import com.intellij.openapi.diagnostic.Logger
@@ -13,16 +14,16 @@ import com.jetbrains.gateway.api.GatewayConnectionProvider
1314

1415
// CoderGatewayConnectionProvider handles connecting via a Gateway link such as
1516
// jetbrains-gateway://connect#type=coder.
16-
class CoderGatewayConnectionProvider :GatewayConnectionProvider {
17-
privateval settings:CoderSettingsService=service<CoderSettingsService>()
18-
17+
class CoderGatewayConnectionProvider :
18+
LinkHandler(service<CoderSettingsService>(), null, DialogUi(service<CoderSettingsService>())),
19+
GatewayConnectionProvider {
1920
override suspend fun connect(
2021
parameters: Map<String, String>,
2122
requestor: ConnectionRequestor,
2223
): GatewayConnectionHandle? {
2324
CoderRemoteConnectionHandle().connect { indicator ->
2425
logger.debug("Launched Coder link handler", parameters)
25-
handleLink(parameters, settings) {
26+
handle(parameters) {
2627
indicator.text = it
2728
}
2829
}

‎src/main/kotlin/com/coder/gateway/CoderRemoteConnectionHandle.kt‎

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,8 +9,8 @@ import com.coder.gateway.models.toRawString
99
import com.coder.gateway.models.withWorkspaceProject
1010
import com.coder.gateway.services.CoderRecentWorkspaceConnectionsService
1111
import com.coder.gateway.services.CoderSettingsService
12+
import com.coder.gateway.util.DialogUi
1213
import com.coder.gateway.util.SemVer
13-
import com.coder.gateway.util.confirm
1414
import com.coder.gateway.util.humanizeDuration
1515
import com.coder.gateway.util.isCancellation
1616
import com.coder.gateway.util.isWorkerTimeout
@@ -63,6 +63,7 @@ class CoderRemoteConnectionHandle {
6363
private val settings = service<CoderSettingsService>()
6464

6565
private val localTimeFormatter = DateTimeFormatter.ofPattern("yyyy-MMM-dd HH:mm")
66+
private val dialogUi = DialogUi(settings)
6667

6768
fun connect(getParameters: (indicator: ProgressIndicator) -> WorkspaceProjectIDE) {
6869
val clientLifetime = LifetimeDefinition()
@@ -198,7 +199,7 @@ class CoderRemoteConnectionHandle {
198199
.minOfOrNull { it.toIdeWithStatus() }
199200
if (latest != null && SemVer.parse(latest.buildNumber) > SemVer.parse(workspace.ideBuildNumber)) {
200201
logger.info("Got newer version: ${latest.buildNumber} versus current ${workspace.ideBuildNumber}")
201-
if (confirm("Update IDE", "There is a new version of this IDE: ${latest.buildNumber}", "Would you like to update?")) {
202+
if (dialogUi.confirm("Update IDE", "There is a new version of this IDE: ${latest.buildNumber}. Would you like to update?")) {
202203
return latest
203204
}
204205
}

‎src/main/kotlin/com/coder/gateway/util/Dialogs.kt‎

Lines changed: 148 additions & 157 deletions
Original file line numberDiff line numberDiff line change
@@ -70,180 +70,171 @@ private class CoderWorkspaceStepDialog(
7070
}
7171
}
7272

73-
/**
74-
* Generic function to ask for consent.
75-
*/
76-
fun confirm(
77-
title: String,
78-
comment: String,
79-
details: String,
80-
): Boolean {
81-
var inputFromUser = false
82-
ApplicationManager.getApplication().invokeAndWait({
83-
val panel =
84-
panel {
85-
row {
86-
label(comment)
87-
}
88-
row {
89-
label(details)
90-
}
91-
}
92-
AppIcon.getInstance().requestAttention(null, true)
93-
if (!dialog(
94-
title = title,
95-
panel = panel,
96-
).showAndGet()
97-
) {
98-
return@invokeAndWait
99-
}
100-
inputFromUser = true
101-
}, ModalityState.defaultModalityState())
102-
return inputFromUser
73+
fun askIDE(
74+
name: String,
75+
agent: WorkspaceAgent,
76+
workspace: Workspace,
77+
cli: CoderCLIManager,
78+
client: CoderRestClient,
79+
workspaces: List<Workspace>,
80+
): WorkspaceProjectIDE? {
81+
var data: WorkspaceProjectIDE? = null
82+
ApplicationManager.getApplication().invokeAndWait {
83+
val dialog =
84+
CoderWorkspaceStepDialog(
85+
name,
86+
CoderWorkspacesStepSelection(agent, workspace, cli, client, workspaces),
87+
)
88+
data = dialog.showAndGetData()
89+
}
90+
return data
10391
}
10492

10593
/**
106-
* Generic function to ask for input.
94+
* Dialog implementation for standalone Gateway.
95+
*
96+
* This is meant to mimic ToolboxUi.
10797
*/
108-
fun ask(
109-
comment: String,
110-
isError: Boolean = false,
111-
link: Pair<String, String>? = null,
112-
default: String? = null,
113-
): String? {
114-
var inputFromUser: String? = null
115-
ApplicationManager.getApplication().invokeAndWait({
116-
lateinit var inputTextField: JBTextField
117-
val panel =
118-
panel {
119-
row {
120-
if (link != null) browserLink(link.first, link.second)
121-
inputTextField =
122-
textField()
123-
.applyToComponent {
124-
text = default ?: ""
125-
minimumSize = Dimension(520, -1)
126-
}.component
127-
}.layout(RowLayout.PARENT_GRID)
128-
row {
129-
cell() // To align with the text box.
130-
cell(
131-
ComponentPanelBuilder.createCommentComponent(comment, false, -1, true)
132-
.applyIf(isError) {
133-
apply {
134-
foreground = UIUtil.getErrorForeground()
135-
}
136-
},
137-
)
138-
}.layout(RowLayout.PARENT_GRID)
98+
class DialogUi(
99+
private val settings: CoderSettings,
100+
) {
101+
fun confirm(title: String, description: String): Boolean {
102+
var inputFromUser = false
103+
ApplicationManager.getApplication().invokeAndWait({
104+
AppIcon.getInstance().requestAttention(null, true)
105+
if (!dialog(
106+
title = title,
107+
panel = panel {
108+
row {
109+
label(description)
110+
}
111+
},
112+
).showAndGet()
113+
) {
114+
return@invokeAndWait
139115
}
140-
AppIcon.getInstance().requestAttention(null, true)
141-
if (!dialog(
142-
comment,
143-
panel = panel,
144-
focusedComponent = inputTextField,
145-
).showAndGet()
146-
) {
147-
return@invokeAndWait
148-
}
149-
inputFromUser = inputTextField.text
150-
}, ModalityState.any())
151-
return inputFromUser
152-
}
116+
inputFromUser = true
117+
}, ModalityState.defaultModalityState())
118+
return inputFromUser
119+
}
153120

154-
/**
155-
* Open a dialog for providing the token. Show any existing token so
156-
* the user can validate it if a previous connection failed.
157-
*
158-
* If we are not retrying and the user has not checked the existing
159-
* token box then also open a browser to the auth page.
160-
*
161-
* If the user has checked the existing token box then return the token
162-
* on disk immediately and skip the dialog (this will overwrite any
163-
* other existing token) unless this is a retry to avoid clobbering the
164-
* token that just failed.
165-
*/
166-
fun askToken(
167-
url: URL,
168-
token: Pair<String, Source>?,
169-
isRetry: Boolean,
170-
useExisting: Boolean,
171-
settings: CoderSettings,
172-
): Pair<String, Source>? {
173-
var (existingToken, tokenSource) = token ?: Pair("", Source.USER)
174-
val getTokenUrl = url.withPath("/login?redirect=%2Fcli-auth")
121+
fun ask(
122+
title: String,
123+
description: String,
124+
placeholder: String? = null,
125+
isError: Boolean = false,
126+
link: Pair<String, String>? = null,
127+
): String? {
128+
var inputFromUser: String? = null
129+
ApplicationManager.getApplication().invokeAndWait({
130+
lateinit var inputTextField: JBTextField
131+
AppIcon.getInstance().requestAttention(null, true)
132+
if (!dialog(
133+
title = title,
134+
panel = panel {
135+
row {
136+
if (link != null) browserLink(link.first, link.second)
137+
inputTextField =
138+
textField()
139+
.applyToComponent {
140+
this.text = placeholder
141+
minimumSize = Dimension(520, -1)
142+
}.component
143+
}.layout(RowLayout.PARENT_GRID)
144+
row {
145+
cell() // To align with the text box.
146+
cell(
147+
ComponentPanelBuilder.createCommentComponent(description, false, -1, true)
148+
.applyIf(isError) {
149+
apply {
150+
foreground = UIUtil.getErrorForeground()
151+
}
152+
},
153+
)
154+
}.layout(RowLayout.PARENT_GRID)
155+
},
156+
focusedComponent = inputTextField,
157+
).showAndGet()
158+
) {
159+
return@invokeAndWait
160+
}
161+
inputFromUser = inputTextField.text
162+
}, ModalityState.any())
163+
return inputFromUser
164+
}
165+
166+
private fun openUrl(url: URL) {
167+
BrowserUtil.browse(url)
168+
}
169+
170+
/**
171+
* Open a dialog for providing the token. Show any existing token so
172+
* the user can validate it if a previous connection failed.
173+
*
174+
* If we are not retrying and the user has not checked the existing
175+
* token box then also open a browser to the auth page.
176+
*
177+
* If the user has checked the existing token box then return the token
178+
* on disk immediately and skip the dialog (this will overwrite any
179+
* other existing token) unless this is a retry to avoid clobbering the
180+
* token that just failed.
181+
*/
182+
fun askToken(
183+
url: URL,
184+
token: Pair<String, Source>?,
185+
isRetry: Boolean,
186+
useExisting: Boolean,
187+
): Pair<String, Source>? {
188+
var (existingToken, tokenSource) = token ?: Pair("", Source.USER)
189+
val getTokenUrl = url.withPath("/login?redirect=%2Fcli-auth")
175190

176-
// On the first run either open a browser to generate a new token
177-
// or, if using an existing token, use the token on disk if it
178-
// exists otherwise assume the user already copied an existing
179-
// token and they will paste in.
180-
if (!isRetry) {
181-
if (!useExisting) {
182-
BrowserUtil.browse(getTokenUrl)
183-
} else {
184-
// Look on disk in case we already have a token, either in
185-
// the deployment's config or the global config.
186-
val tryToken = settings.token(url)
187-
if (tryToken != null && tryToken.first != existingToken) {
188-
return tryToken
191+
// On the first run either open a browser to generate a new token
192+
// or, if using an existing token, use the token on disk if it
193+
// exists otherwise assume the user already copied an existing
194+
// token and they will paste in.
195+
if (!isRetry) {
196+
if (!useExisting) {
197+
openUrl(getTokenUrl)
198+
} else {
199+
// Look on disk in case we already have a token, either in
200+
// the deployment's config or the global config.
201+
val tryToken = settings.token(url)
202+
if (tryToken != null && tryToken.first != existingToken) {
203+
return tryToken
204+
}
189205
}
190206
}
191-
}
192207

193-
// On subsequent tries or if not using an existing token, ask the user
194-
// for the token.
195-
val tokenFromUser =
196-
ask(
197-
CoderGatewayBundle.message(
198-
if (isRetry) {
199-
"gateway.connector.view.workspaces.token.rejected"
208+
// On subsequent tries or if not using an existing token, ask the user
209+
// for the token.
210+
val tokenFromUser =
211+
ask(
212+
title ="Session Token",
213+
description =if (isRetry) {
214+
"This token was rejected by ${url.host}."
200215
} else if (tokenSource == Source.CONFIG) {
201-
"gateway.connector.view.workspaces.token.injected-global"
216+
"This token was pulled from your global CLI config."
202217
} else if (tokenSource == Source.DEPLOYMENT_CONFIG) {
203-
"gateway.connector.view.workspaces.token.injected"
218+
"This token was pulled from your CLI config for ${url.host}."
204219
} else if (tokenSource == Source.LAST_USED) {
205-
"gateway.connector.view.workspaces.token.last-used"
220+
"This token was the lastused token for ${url.host}."
206221
} else if (tokenSource == Source.QUERY) {
207-
"gateway.connector.view.workspaces.token.query"
222+
"This token was pulled from the Gateway link from ${url.host}."
208223
} else if (existingToken.isNotBlank()) {
209-
"gateway.connector.view.workspaces.token.comment"
224+
"The last used token for ${url.host} is shown above."
210225
} else {
211-
"gateway.connector.view.workspaces.token.none"
226+
"No existing token for ${url.host} found."
212227
},
213-
url.host,
214-
),
215-
isRetry,
216-
Pair(
217-
CoderGatewayBundle.message("gateway.connector.view.login.token.label"),
218-
getTokenUrl.toString(),
219-
),
220-
existingToken,
221-
)
222-
if (tokenFromUser.isNullOrBlank()) {
223-
return null
224-
}
225-
if (tokenFromUser != existingToken) {
226-
tokenSource = Source.USER
227-
}
228-
return Pair(tokenFromUser, tokenSource)
229-
}
230-
231-
fun askIDE(
232-
name: String,
233-
agent: WorkspaceAgent,
234-
workspace: Workspace,
235-
cli: CoderCLIManager,
236-
client: CoderRestClient,
237-
workspaces: List<Workspace>,
238-
): WorkspaceProjectIDE? {
239-
var data: WorkspaceProjectIDE? = null
240-
ApplicationManager.getApplication().invokeAndWait {
241-
val dialog =
242-
CoderWorkspaceStepDialog(
243-
name,
244-
CoderWorkspacesStepSelection(agent, workspace, cli, client, workspaces),
228+
placeholder = existingToken,
229+
link = Pair("Session Token:", getTokenUrl.toString()),
230+
isError = isRetry,
245231
)
246-
data = dialog.showAndGetData()
232+
if (tokenFromUser.isNullOrBlank()) {
233+
return null
234+
}
235+
if (tokenFromUser != existingToken) {
236+
tokenSource = Source.USER
237+
}
238+
return Pair(tokenFromUser, tokenSource)
247239
}
248-
return data
249240
}

0 commit comments

Comments
(0)

AltStyle によって変換されたページ (->オリジナル) /