Skip to content

Commit e63fd07

Browse files
committed
impl: confirmation dialog for workspace deletion
Users are now required to confirm the workspace name if they want to delete a workspace. This is in order to avoid any accidental removals.
1 parent 05be524 commit e63fd07

File tree

5 files changed

+63
-39
lines changed

5 files changed

+63
-39
lines changed

CHANGELOG.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
### Changed
66

77
- workspaces status is now refresh every time Coder Toolbox becomes visible
8-
- improved workspace delete confirmation dialog
8+
- workspaces can no longer be removed by accident - users are now required to input the workspace name.
99

1010
### Fixed
1111

src/main/kotlin/com/coder/toolbox/CoderRemoteEnvironment.kt

Lines changed: 41 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -12,17 +12,18 @@ import com.coder.toolbox.sdk.v2.models.WorkspaceAgent
1212
import com.coder.toolbox.util.waitForFalseWithTimeout
1313
import com.coder.toolbox.util.withPath
1414
import com.coder.toolbox.views.Action
15+
import com.coder.toolbox.views.CoderDelimiter
1516
import com.coder.toolbox.views.EnvironmentView
1617
import com.jetbrains.toolbox.api.localization.LocalizableString
1718
import com.jetbrains.toolbox.api.remoteDev.AfterDisconnectHook
1819
import com.jetbrains.toolbox.api.remoteDev.BeforeConnectionHook
19-
import com.jetbrains.toolbox.api.remoteDev.DeleteEnvironmentConfirmationParams
2020
import com.jetbrains.toolbox.api.remoteDev.EnvironmentVisibilityState
2121
import com.jetbrains.toolbox.api.remoteDev.RemoteProviderEnvironment
2222
import com.jetbrains.toolbox.api.remoteDev.environments.EnvironmentContentsView
2323
import com.jetbrains.toolbox.api.remoteDev.states.EnvironmentDescription
2424
import com.jetbrains.toolbox.api.remoteDev.states.RemoteEnvironmentState
2525
import com.jetbrains.toolbox.api.ui.actions.ActionDescription
26+
import com.jetbrains.toolbox.api.ui.components.TextType
2627
import com.squareup.moshi.Moshi
2728
import kotlinx.coroutines.Job
2829
import kotlinx.coroutines.delay
@@ -78,7 +79,7 @@ class CoderRemoteEnvironment(
7879
fun asPairOfWorkspaceAndAgent(): Pair<Workspace, WorkspaceAgent> = Pair(workspace, agent)
7980

8081
private fun getAvailableActions(): List<ActionDescription> {
81-
val actions = mutableListOf<Action>()
82+
val actions = mutableListOf<ActionDescription>()
8283
if (wsRawStatus.canStop()) {
8384
actions.add(Action(context.i18n.ptrl("Open web terminal")) {
8485
context.cs.launch {
@@ -143,6 +144,24 @@ class CoderRemoteEnvironment(
143144
}
144145
})
145146
}
147+
actions.add(CoderDelimiter(context.i18n.pnotr("")))
148+
actions.add(Action(context.i18n.ptrl("Delete workspace")) {
149+
context.cs.launch {
150+
val confirmation = context.ui.showTextInputPopup(
151+
if (wsRawStatus.canStop()) context.i18n.ptrl("Delete running workspace?") else context.i18n.ptrl("Delete workspace?"),
152+
if (wsRawStatus.canStop()) context.i18n.ptrl("This will close the workspace and remove all its information, including files, unsaved changes, history, and usage data.")
153+
else context.i18n.ptrl("This will remove all information from the workspace, including files, unsaved changes, history, and usage data."),
154+
context.i18n.ptrl("Workspace name"),
155+
TextType.General,
156+
context.i18n.ptrl("OK"),
157+
context.i18n.ptrl("Cancel")
158+
)
159+
if (confirmation != workspace.name) {
160+
return@launch
161+
}
162+
deleteWorkspace()
163+
}
164+
})
146165
return actions
147166
}
148167

@@ -272,43 +291,32 @@ class CoderRemoteEnvironment(
272291
return false
273292
}
274293

275-
override fun getDeleteEnvironmentConfirmationParams(): DeleteEnvironmentConfirmationParams? {
276-
return object : DeleteEnvironmentConfirmationParams {
277-
override val cancelButtonText: String = "Cancel"
278-
override val confirmButtonText: String = "Delete"
279-
override val message: String =
280-
if (wsRawStatus.canStop()) "This will close the workspace and remove all its information, including files, unsaved changes, history, and usage data."
281-
else "This will remove all information from the workspace, including files, unsaved changes, history, and usage data."
282-
override val title: String = if (wsRawStatus.canStop()) "Delete running workspace?" else "Delete workspace?"
283-
}
284-
}
294+
override val deleteActionFlow: StateFlow<(() -> Unit)?> = MutableStateFlow(null)
285295

286-
override val deleteActionFlow: StateFlow<(() -> Unit)?> = MutableStateFlow {
287-
context.cs.launch {
288-
try {
289-
client.removeWorkspace(workspace)
290-
// mark the env as deleting otherwise we will have to
291-
// wait for the poller to update the status in the next 5 seconds
292-
state.update {
293-
WorkspaceAndAgentStatus.DELETING.toRemoteEnvironmentState(context)
294-
}
296+
suspend fun deleteWorkspace() {
297+
try {
298+
client.removeWorkspace(workspace)
299+
// mark the env as deleting otherwise we will have to
300+
// wait for the poller to update the status in the next 5 seconds
301+
state.update {
302+
WorkspaceAndAgentStatus.DELETING.toRemoteEnvironmentState(context)
303+
}
295304

296-
context.cs.launch {
297-
withTimeout(5.minutes) {
298-
var workspaceStillExists = true
299-
while (context.cs.isActive && workspaceStillExists) {
300-
if (wsRawStatus == WorkspaceAndAgentStatus.DELETING || wsRawStatus == WorkspaceAndAgentStatus.DELETED) {
301-
workspaceStillExists = false
302-
context.envPageManager.showPluginEnvironmentsPage()
303-
} else {
304-
delay(1.seconds)
305-
}
305+
context.cs.launch {
306+
withTimeout(5.minutes) {
307+
var workspaceStillExists = true
308+
while (context.cs.isActive && workspaceStillExists) {
309+
if (wsRawStatus == WorkspaceAndAgentStatus.DELETING || wsRawStatus == WorkspaceAndAgentStatus.DELETED) {
310+
workspaceStillExists = false
311+
context.envPageManager.showPluginEnvironmentsPage()
312+
} else {
313+
delay(1.seconds)
306314
}
307315
}
308316
}
309-
} catch (e: APIResponseException) {
310-
context.ui.showErrorInfoPopup(e)
311317
}
318+
} catch (e: APIResponseException) {
319+
context.ui.showErrorInfoPopup(e)
312320
}
313321
}
314322

src/main/kotlin/com/coder/toolbox/CoderRemoteProvider.kt

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ import com.coder.toolbox.util.waitForTrue
1111
import com.coder.toolbox.util.withPath
1212
import com.coder.toolbox.views.Action
1313
import com.coder.toolbox.views.CoderCliSetupWizardPage
14+
import com.coder.toolbox.views.CoderDelimiter
1415
import com.coder.toolbox.views.CoderSettingsPage
1516
import com.coder.toolbox.views.NewEnvironmentPage
1617
import com.coder.toolbox.views.state.CoderCliSetupWizardState
@@ -21,7 +22,6 @@ import com.jetbrains.toolbox.api.core.util.LoadableState
2122
import com.jetbrains.toolbox.api.localization.LocalizableString
2223
import com.jetbrains.toolbox.api.remoteDev.ProviderVisibilityState
2324
import com.jetbrains.toolbox.api.remoteDev.RemoteProvider
24-
import com.jetbrains.toolbox.api.ui.actions.ActionDelimiter
2525
import com.jetbrains.toolbox.api.ui.actions.ActionDescription
2626
import com.jetbrains.toolbox.api.ui.components.UiPage
2727
import kotlinx.coroutines.ExperimentalCoroutinesApi
@@ -416,6 +416,4 @@ class CoderRemoteProvider(
416416
LoadableState.Loading
417417
}
418418
}
419-
}
420-
421-
private class CoderDelimiter(override val label: LocalizableString) : ActionDelimiter
419+
}

src/main/kotlin/com/coder/toolbox/views/CoderPage.kt

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import com.coder.toolbox.CoderToolboxContext
44
import com.jetbrains.toolbox.api.core.ui.icons.SvgIcon
55
import com.jetbrains.toolbox.api.core.ui.icons.SvgIcon.IconType
66
import com.jetbrains.toolbox.api.localization.LocalizableString
7+
import com.jetbrains.toolbox.api.ui.actions.ActionDelimiter
78
import com.jetbrains.toolbox.api.ui.actions.RunnableActionDescription
89
import com.jetbrains.toolbox.api.ui.components.UiPage
910
import kotlinx.coroutines.flow.MutableStateFlow
@@ -67,3 +68,5 @@ class Action(
6768
actionBlock()
6869
}
6970
}
71+
72+
class CoderDelimiter(override val label: LocalizableString) : ActionDelimiter

src/main/resources/localization/defaultMessages.po

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -179,4 +179,19 @@ msgid "Headers"
179179
msgstr ""
180180

181181
msgid "Body"
182-
msgstr ""
182+
msgstr ""
183+
184+
msgid "Delete workspace"
185+
msgstr ""
186+
187+
msgid "Delete running workspace?"
188+
msgstr ""
189+
190+
msgid "This will close the workspace and remove all its information, including files, unsaved changes, history, and usage data."
191+
msgstr ""
192+
193+
msgid "This will remove all information from the workspace, including files, unsaved changes, history, and usage data."
194+
msgstr ""
195+
196+
msgid "Workspace name"
197+
msgstr ""

0 commit comments

Comments
 (0)