android: server delete confirmation as in the Final 2026 mockup

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
DenozordecandClaude Opus 5.5 committed 2026-10-12 10:31:32 +07:00
1 parent f193661e68
commit eb5a89623b
4 files changed
+100 -1

No files matched your search

@@ -97,6 +97,7 @@ import kotlinx.coroutines.delay
import org.strongswan.android.logic.connectivity.ConnectivityProbe
import org.strongswan.android.ui.compose.design.ShxButton
import org.strongswan.android.ui.compose.design.ShxButtonStyle
import org.strongswan.android.ui.compose.design.ShxDialog
import org.strongswan.android.ui.compose.design.ShxIcons
import org.strongswan.android.ui.compose.design.ShxPanel
import org.strongswan.android.ui.compose.design.ShxRow
@@ -200,6 +201,7 @@ private fun ProfilesList(
val snackbarHostState = remember { SnackbarHostState() }
val scope = rememberCoroutineScope()
var menuFor by remember { mutableStateOf<VpnProfile?>(null) }
var deleteFor by remember { mutableStateOf<VpnProfile?>(null) }
val activeUuid = state.activeProfile?.getUUID()
val connected = state.vpnState == State.CONNECTED
val profiles = state.profiles
@@ -237,6 +239,30 @@ private fun ProfilesList(
}
}
deleteFor?.let { profile ->
val current = profile.getUUID() == activeUuid && state.vpnState != State.DISABLED
ShxDialog(
title = stringResource(R.string.server_delete_title, profile.name ?: profile.gateway),
paragraphs = listOfNotNull(
if (current) stringResource(R.string.server_delete_current) else null,
stringResource(R.string.server_delete_restore),
),
confirm = stringResource(R.string.server_delete_confirm),
dismiss = stringResource(android.R.string.cancel),
onConfirm = {
deleteFor = null
if (current)
{
onDisconnect()
}
deleteWithUndo(profile)
},
onDismiss = { deleteFor = null },
icon = ShxIcons.Trash,
danger = true,
)
}
Box(Modifier.fillMaxSize().background(c.background))
{
Column(Modifier.fillMaxSize().statusBarsPadding().verticalScroll(rememberScrollState()))
@@ -327,7 +353,7 @@ private fun ProfilesList(
stringResource(R.string.delete_profile),
{
menuFor = null
deleteWithUndo(profile)
deleteFor = profile
},
style = ShxButtonStyle.DANGER,
modifier = Modifier.fillMaxWidth(),
@@ -737,3 +737,68 @@ fun ShxToggleRow(title: String, checked: Boolean, onChange: (Boolean) -> Unit, s
}
}
}
/**
* Confirmation dialog of the mockup (F_ServerDelete): 28dp card, a 48dp round
* icon, 22/800 title, 14sp paragraphs, a text «Cancel» and a filled action.
*/
@Composable
fun ShxDialog(
title: String,
paragraphs: List<String>,
confirm: String,
dismiss: String,
onConfirm: () -> Unit,
onDismiss: () -> Unit,
icon: ImageVector? = null,
danger: Boolean = false,
)
{
val c = LocalShxColors.current
androidx.compose.ui.window.Dialog(onDismissRequest = onDismiss)
{
Column(
verticalArrangement = Arrangement.spacedBy(14.dp),
modifier = Modifier.fillMaxWidth().clip(RoundedCornerShape(28.dp)).background(c.background).padding(24.dp),
)
{
if (icon != null)
{
Box(
contentAlignment = Alignment.Center,
modifier = Modifier.size(48.dp).clip(CircleShape).background(if (danger) c.errorContainer else c.accentContainer),
)
{
Icon(icon, contentDescription = null, tint = if (danger) c.error else c.onAccentContainer, modifier = Modifier.size(24.dp))
}
}
Text(title, style = ShxType.pageTitle, color = c.text)
paragraphs.forEach {
Text(it, style = ShxType.note.copy(fontSize = 14.sp, lineHeight = 21.sp, fontWeight = FontWeight.Normal), color = c.textSoft)
}
Row(
horizontalArrangement = Arrangement.spacedBy(8.dp, Alignment.End),
modifier = Modifier.fillMaxWidth().padding(top = 4.dp),
)
{
Box(
contentAlignment = Alignment.Center,
modifier = Modifier.height(44.dp).clip(RoundedCornerShape(22.dp))
.clickable(role = Role.Button, onClick = onDismiss).padding(horizontal = 16.dp),
)
{
Text(dismiss, style = ShxType.button.copy(fontWeight = FontWeight.Bold), color = c.accentText)
}
Box(
contentAlignment = Alignment.Center,
modifier = Modifier.height(44.dp).clip(RoundedCornerShape(22.dp))
.background(if (danger) c.error else c.accent)
.clickable(role = Role.Button, onClick = onConfirm).padding(horizontal = 20.dp),
)
{
Text(confirm, style = ShxType.button, color = if (danger) c.background else Color.White)
}
}
}
}
}
@@ -1026,4 +1026,8 @@
<string name="tile_no_answer">Сервер не отвечает</string>
<string name="tile_auth_error">Ошибка входа</string>
<string name="tile_problem">Проблема с подключением</string>
<string name="server_delete_title">Удалить «%1$s»?</string>
<string name="server_delete_current">Это текущий сервер — VPN отключится.</string>
<string name="server_delete_restore">Вернуть можно, снова добавив профиль по ссылке, файлу или QR.</string>
<string name="server_delete_confirm">Удалить</string>
</resources>
@@ -1012,4 +1012,8 @@
<string name="tile_auth_error">Sign-in failed</string>
<string name="tile_problem">Connection problem</string>
<string name="tile_label" translatable="false">SHX VPN</string>
<string name="server_delete_title">Delete «%1$s»?</string>
<string name="server_delete_current">This is the current server — the VPN will disconnect.</string>
<string name="server_delete_restore">You can get it back by adding the profile again from a link, file or QR.</string>
<string name="server_delete_confirm">Delete</string>
</resources>