Movatterモバイル変換


[0]ホーム

URL:


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

impl: confirmation dialog for workspace deletion#179

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to ourterms of service andprivacy statement. We’ll occasionally send you account related emails.

Already on GitHub?Sign in to your account

Merged
fioan89 merged 5 commits intomainfromchore-improve-delete-confirmation-dialog
Sep 24, 2025
Merged
Show file tree
Hide file tree
Changes fromall commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletionsCHANGELOG.md
View file
Open in desktop
Original file line numberDiff line numberDiff line change
Expand Up@@ -2,6 +2,10 @@

## Unreleased

### Changed

- workspaces can no longer be removed by accident - users are now required to input the workspace name.

### Fixed

- relaxed SNI hostname resolution
Expand Down
78 changes: 45 additions & 33 deletionssrc/main/kotlin/com/coder/toolbox/CoderRemoteEnvironment.kt
View file
Open in desktop
Original file line numberDiff line numberDiff line change
Expand Up@@ -12,17 +12,18 @@ import com.coder.toolbox.sdk.v2.models.WorkspaceAgent
import com.coder.toolbox.util.waitForFalseWithTimeout
import com.coder.toolbox.util.withPath
import com.coder.toolbox.views.Action
import com.coder.toolbox.views.CoderDelimiter
import com.coder.toolbox.views.EnvironmentView
import com.jetbrains.toolbox.api.localization.LocalizableString
import com.jetbrains.toolbox.api.remoteDev.AfterDisconnectHook
import com.jetbrains.toolbox.api.remoteDev.BeforeConnectionHook
import com.jetbrains.toolbox.api.remoteDev.DeleteEnvironmentConfirmationParams
import com.jetbrains.toolbox.api.remoteDev.EnvironmentVisibilityState
import com.jetbrains.toolbox.api.remoteDev.RemoteProviderEnvironment
import com.jetbrains.toolbox.api.remoteDev.environments.EnvironmentContentsView
import com.jetbrains.toolbox.api.remoteDev.states.EnvironmentDescription
import com.jetbrains.toolbox.api.remoteDev.states.RemoteEnvironmentState
import com.jetbrains.toolbox.api.ui.actions.ActionDescription
import com.jetbrains.toolbox.api.ui.components.TextType
import com.squareup.moshi.Moshi
import kotlinx.coroutines.CoroutineName
import kotlinx.coroutines.Job
Expand DownExpand Up@@ -79,7 +80,7 @@ class CoderRemoteEnvironment(
fun asPairOfWorkspaceAndAgent(): Pair<Workspace, WorkspaceAgent> = Pair(workspace, agent)

private fun getAvailableActions(): List<ActionDescription> {
val actions = mutableListOf<Action>()
val actions = mutableListOf<ActionDescription>()
if (wsRawStatus.canStop()) {
actions.add(Action(context, "Open web terminal") {
context.desktop.browse(client.url.withPath("/${workspace.ownerName}/$name/terminal").toString()) {
Expand DownExpand Up@@ -137,6 +138,28 @@ class CoderRemoteEnvironment(
}
)
}
actions.add(CoderDelimiter(context.i18n.pnotr("")))
actions.add(Action(context, "Delete workspace", highlightInRed = true) {
context.cs.launch(CoroutineName("Delete Workspace Action")) {
var dialogText =
if (wsRawStatus.canStop()) "This will close the workspace and remove all its information, including files, unsaved changes, history, and usage data."
else "This will remove all information from the workspace, including files, unsaved changes, history, and usage data."
dialogText += "\n\nType \"${workspace.name}\" below to confirm:"

val confirmation = context.ui.showTextInputPopup(
if (wsRawStatus.canStop()) context.i18n.ptrl("Delete running workspace?") else context.i18n.ptrl("Delete workspace?"),
context.i18n.pnotr(dialogText),
context.i18n.ptrl("Workspace name"),
TextType.General,
context.i18n.ptrl("OK"),
context.i18n.ptrl("Cancel")
)
if (confirmation != workspace.name) {
return@launch
}
deleteWorkspace()
}
})
return actions
}

Expand DownExpand Up@@ -266,43 +289,32 @@ class CoderRemoteEnvironment(
return false
}

override fun getDeleteEnvironmentConfirmationParams(): DeleteEnvironmentConfirmationParams? {
return object : DeleteEnvironmentConfirmationParams {
override val cancelButtonText: String = "Cancel"
override val confirmButtonText: String = "Delete"
override val message: String =
if (wsRawStatus.canStop()) "Workspace will be closed and all the information will be lost, including all files, unsaved changes, historical info and usage data."
else "All the information in this workspace will be lost, including all files, unsaved changes, historical info and usage data."
override val title: String = if (wsRawStatus.canStop()) "Delete running workspace?" else "Delete workspace?"
}
}
override val deleteActionFlow: StateFlow<(() -> Unit)?> = MutableStateFlow(null)

override val deleteActionFlow: StateFlow<(() -> Unit)?> = MutableStateFlow {
context.cs.launch(CoroutineName("Delete Workspace Action")) {
try {
client.removeWorkspace(workspace)
// mark the env as deleting otherwise we will have to
// wait for the poller to update the status in the next 5 seconds
state.update {
WorkspaceAndAgentStatus.DELETING.toRemoteEnvironmentState(context)
}
suspend fun deleteWorkspace() {
try {
client.removeWorkspace(workspace)
// mark the env as deleting otherwise we will have to
// wait for the poller to update the status in the next 5 seconds
state.update {
WorkspaceAndAgentStatus.DELETING.toRemoteEnvironmentState(context)
}

context.cs.launch(CoroutineName("Workspace Deletion Poller")) {
withTimeout(5.minutes) {
var workspaceStillExists = true
while (context.cs.isActive && workspaceStillExists) {
if (wsRawStatus == WorkspaceAndAgentStatus.DELETING || wsRawStatus == WorkspaceAndAgentStatus.DELETED) {
workspaceStillExists = false
context.envPageManager.showPluginEnvironmentsPage()
} else {
delay(1.seconds)
}
context.cs.launch(CoroutineName("Workspace Deletion Poller")) {
withTimeout(5.minutes) {
var workspaceStillExists = true
while (context.cs.isActive && workspaceStillExists) {
if (wsRawStatus == WorkspaceAndAgentStatus.DELETING || wsRawStatus == WorkspaceAndAgentStatus.DELETED) {
workspaceStillExists = false
context.envPageManager.showPluginEnvironmentsPage()
} else {
delay(1.seconds)
}
}
}
} catch (e: APIResponseException) {
context.ui.showErrorInfoPopup(e)
}
} catch (e: APIResponseException) {
context.ui.showErrorInfoPopup(e)
}
}

Expand Down
6 changes: 2 additions & 4 deletionssrc/main/kotlin/com/coder/toolbox/CoderRemoteProvider.kt
View file
Open in desktop
Original file line numberDiff line numberDiff line change
Expand Up@@ -12,6 +12,7 @@ import com.coder.toolbox.util.waitForTrue
import com.coder.toolbox.util.withPath
import com.coder.toolbox.views.Action
import com.coder.toolbox.views.CoderCliSetupWizardPage
import com.coder.toolbox.views.CoderDelimiter
import com.coder.toolbox.views.CoderSettingsPage
import com.coder.toolbox.views.NewEnvironmentPage
import com.coder.toolbox.views.state.CoderCliSetupContext
Expand All@@ -23,7 +24,6 @@ import com.jetbrains.toolbox.api.core.util.LoadableState
import com.jetbrains.toolbox.api.localization.LocalizableString
import com.jetbrains.toolbox.api.remoteDev.ProviderVisibilityState
import com.jetbrains.toolbox.api.remoteDev.RemoteProvider
import com.jetbrains.toolbox.api.ui.actions.ActionDelimiter
import com.jetbrains.toolbox.api.ui.actions.ActionDescription
import com.jetbrains.toolbox.api.ui.components.UiPage
import kotlinx.coroutines.CoroutineName
Expand DownExpand Up@@ -428,6 +428,4 @@ class CoderRemoteProvider(
LoadableState.Loading
}
}
}

private class CoderDelimiter(override val label: LocalizableString) : ActionDelimiter
}
5 changes: 5 additions & 0 deletionssrc/main/kotlin/com/coder/toolbox/views/CoderPage.kt
View file
Open in desktop
Original file line numberDiff line numberDiff line change
Expand Up@@ -5,6 +5,7 @@ import com.coder.toolbox.sdk.ex.APIResponseException
import com.jetbrains.toolbox.api.core.ui.icons.SvgIcon
import com.jetbrains.toolbox.api.core.ui.icons.SvgIcon.IconType
import com.jetbrains.toolbox.api.localization.LocalizableString
import com.jetbrains.toolbox.api.ui.actions.ActionDelimiter
import com.jetbrains.toolbox.api.ui.actions.RunnableActionDescription
import com.jetbrains.toolbox.api.ui.components.UiPage
import kotlinx.coroutines.CoroutineName
Expand DownExpand Up@@ -55,12 +56,14 @@ class Action(
private val context: CoderToolboxContext,
private val description: String,
closesPage: Boolean = false,
highlightInRed: Boolean = false,
enabled: () -> Boolean = { true },
private val actionBlock: suspend () -> Unit,
) : RunnableActionDescription {
override val label: LocalizableString = context.i18n.ptrl(description)
override val shouldClosePage: Boolean = closesPage
override val isEnabled: Boolean = enabled()
override val isDangerous: Boolean = highlightInRed
override fun run() {
context.cs.launch(CoroutineName("$description Action")) {
try {
Expand All@@ -76,3 +79,5 @@ class Action(
}
}
}

class CoderDelimiter(override val label: LocalizableString) : ActionDelimiter
11 changes: 10 additions & 1 deletionsrc/main/resources/localization/defaultMessages.po
View file
Open in desktop
Original file line numberDiff line numberDiff line change
Expand Up@@ -179,4 +179,13 @@ msgid "Headers"
msgstr ""

msgid "Body"
msgstr ""
msgstr ""

msgid "Delete workspace"
msgstr ""

msgid "Delete running workspace?"
msgstr ""

msgid "Workspace name"
msgstr ""
Loading

[8]ページ先頭

©2009-2025 Movatter.jp