Skip to content

Commit 1414a6c

Browse files
committed
Refactor wizard steps
The main goal was the ability to use steps separately. We may only need the IDE selection step when handling a link, for example. Each step now takes a specific type of data (if it needs some) then returns a specific type of data. The way to tell if a step is ready to return data is via a callback you pass into the step. I think more improvements will become apparent once we start using it in the link handler.
1 parent f872f69 commit 1414a6c

9 files changed

+406
-388
lines changed

src/main/kotlin/com/coder/gateway/models/CoderWorkspacesWizardModel.kt

Lines changed: 0 additions & 26 deletions
This file was deleted.
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
package com.coder.gateway.models
2+
3+
/**
4+
* Describes where a token came from.
5+
*/
6+
enum class TokenSource {
7+
CONFIG, // Pulled from the Coder CLI config.
8+
USER, // Input by the user.
9+
QUERY, // From the Gateway link as a query parameter.
10+
LAST_USED, // Last used token, either from storage or current run.
11+
}
12+

src/main/kotlin/com/coder/gateway/views/CoderGatewayConnectorWizardView.kt

Lines changed: 0 additions & 128 deletions
This file was deleted.
Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,17 @@
11
package com.coder.gateway.views
22

3+
import com.coder.gateway.CoderRemoteConnectionHandle
4+
import com.coder.gateway.views.steps.CoderWizardView
35
import com.intellij.ui.components.panels.Wrapper
46
import com.intellij.util.ui.JBUI
57
import com.jetbrains.gateway.api.GatewayConnectorView
68
import javax.swing.JComponent
79

810
class CoderGatewayConnectorWizardWrapperView : GatewayConnectorView {
911
override val component: JComponent
10-
get() = Wrapper(CoderGatewayConnectorWizardView()).apply { border = JBUI.Borders.empty() }
11-
}
12+
get() {
13+
return Wrapper(CoderWizardView { params ->
14+
CoderRemoteConnectionHandle().connect { params }
15+
}).apply { border = JBUI.Borders.empty() }
16+
}
17+
}
Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
package com.coder.gateway.views.steps
2+
3+
import com.intellij.openapi.Disposable
4+
import com.intellij.openapi.ui.DialogPanel
5+
6+
sealed interface CoderWizardStep : Disposable {
7+
val component: DialogPanel
8+
var nextActionText: String
9+
var previousActionText: String
10+
11+
/**
12+
* Stop any background processes. Data will still be available.
13+
*/
14+
fun stop()
15+
}
16+
17+
/**
18+
* Run block with provided arguments after checking they are all non-null. This
19+
* is to enforce non-null values and should be used to signify developer error.
20+
*/
21+
fun <A, Z> withoutNull(a: A?, block: (a: A) -> Z): Z {
22+
if (a == null) {
23+
throw Error("Unexpected null value")
24+
}
25+
return block(a)
26+
}
27+
28+
/**
29+
* Run block with provided arguments after checking they are all non-null. This
30+
* is to enforce non-null values and should be used to signify developer error.
31+
*/
32+
fun <A, B, Z> withoutNull(a: A?, b: B?, block: (a: A, b: B) -> Z): Z {
33+
if (a == null || b == null) {
34+
throw Error("Unexpected null value")
35+
}
36+
return block(a, b)
37+
}
38+
39+
/**
40+
* Run block with provided arguments after checking they are all non-null. This
41+
* is to enforce non-null values and should be used to signify developer error.
42+
*/
43+
fun <A, B, C, D, Z> withoutNull(a: A?, b: B?, c: C?, d: D?, block: (a: A, b: B, c: C, d: D) -> Z): Z {
44+
if (a == null || b == null || c == null || d == null) {
45+
throw Error("Unexpected null value")
46+
}
47+
return block(a, b, c, d)
48+
}
Lines changed: 108 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,108 @@
1+
package com.coder.gateway.views.steps
2+
3+
import com.intellij.openapi.Disposable
4+
import com.intellij.openapi.wm.impl.welcomeScreen.WelcomeScreenUIManager
5+
import com.intellij.ui.dsl.builder.AlignX
6+
import com.intellij.ui.dsl.builder.RightGap
7+
import com.intellij.ui.dsl.builder.panel
8+
import com.intellij.util.ui.JBUI
9+
import com.intellij.util.ui.components.BorderLayoutPanel
10+
import com.jetbrains.gateway.api.GatewayUI
11+
import javax.swing.JButton
12+
13+
/**
14+
* Wrapper around all wizard steps. This view takes you from configuring a URL
15+
* to connecting to a workspace.
16+
*/
17+
class CoderWizardView(
18+
private val onFinish: (data: Map<String, String>) -> Unit,
19+
) : BorderLayoutPanel(), Disposable {
20+
private lateinit var previousButton: JButton
21+
private lateinit var nextButton: JButton
22+
23+
// These are not in a list because the types of one step lead into the types
24+
// of the next and it would not be possible to do that with a generic type
25+
// on a list. It could possibly be refactored to have steps point to their
26+
// own next step which might be cleaner, but this works.
27+
private val step1 = CoderWorkspacesStepView { nextButton.isEnabled = it }
28+
private val step2 = CoderWorkspaceStepView { nextButton.isEnabled = it }
29+
private var current: CoderWizardStep? = null
30+
31+
private val buttons = panel {
32+
separator(background = WelcomeScreenUIManager.getSeparatorColor())
33+
row {
34+
label("").resizableColumn().align(AlignX.FILL).gap(RightGap.SMALL)
35+
previousButton = button("") { previous() }
36+
.align(AlignX.RIGHT).gap(RightGap.SMALL)
37+
.applyToComponent { background = WelcomeScreenUIManager.getMainAssociatedComponentBackground() }.component
38+
nextButton = button("") { next() }
39+
.align(AlignX.RIGHT)
40+
.applyToComponent { background = WelcomeScreenUIManager.getMainAssociatedComponentBackground() }.component
41+
}
42+
}.apply {
43+
background = WelcomeScreenUIManager.getMainAssociatedComponentBackground()
44+
border = JBUI.Borders.empty(0, 16)
45+
}
46+
47+
init {
48+
background = WelcomeScreenUIManager.getMainAssociatedComponentBackground()
49+
addToBottom(buttons)
50+
setStep(step1)
51+
step1.init()
52+
}
53+
54+
/**
55+
* Replace the current step with the new one.
56+
*/
57+
private fun setStep(step: CoderWizardStep) {
58+
current?.apply {
59+
remove(component)
60+
stop()
61+
}
62+
current = step
63+
step.apply {
64+
addToCenter(component.apply {
65+
background = WelcomeScreenUIManager.getMainAssociatedComponentBackground()
66+
border = JBUI.Borders.empty(0, 16)
67+
})
68+
nextButton.text = nextActionText
69+
previousButton.text = previousActionText
70+
nextButton.isEnabled = false
71+
updateUI()
72+
}
73+
}
74+
75+
private fun previous() {
76+
when(current) {
77+
is CoderWorkspacesStepView -> {
78+
GatewayUI.getInstance().reset()
79+
dispose()
80+
}
81+
is CoderWorkspaceStepView -> {
82+
setStep(step1)
83+
step1.init()
84+
}
85+
null -> throw Error("Unexpected null step")
86+
}
87+
}
88+
89+
private fun next() {
90+
when(current) {
91+
is CoderWorkspacesStepView -> {
92+
setStep(step2)
93+
step2.init(step1.data())
94+
}
95+
is CoderWorkspaceStepView -> {
96+
onFinish(step2.data())
97+
GatewayUI.getInstance().reset()
98+
dispose()
99+
}
100+
null -> throw Error("Unexpected null step")
101+
}
102+
}
103+
104+
override fun dispose() {
105+
step1.dispose()
106+
step2.dispose()
107+
}
108+
}

0 commit comments

Comments
 (0)