mirror of
https://github.com/2dust/v2rayNG.git
synced 2026-10-29 06:31:54 +00:00
UI/UX: Add item names to deletion confirmations (#6202)
* Name the targets in deletion confirmations Extract the visible named-confirmation wording from the accessibility assembly onto current upstream. Cover main-list profiles, standard and custom profile editors, policy groups, proxy-chain members, subscriptions, routing-rule editors, and asset sources. Reuse the six localized named variants in all nine catalogs; preserve existing generic strings and the already-named asset-file confirmation. Carry display names alongside existing GUIDs in main/subscription list confirmation requests, and retain the main target across recreation with its two-string saver. Use saved names in editors rather than uncommitted renames. Do not import accessibility semantics, action menus, live regions, the separate routing-list Delete button, or the proxy-chain stable-key migration. Validation: 72 JVM tests passed, including two deletion-target saver regressions; Play Store debug compilation and x86_64 assembly passed. An API 33 emulator verified all 54 compiled localized resources with Unicode and percent-containing names, the main profile confirmation across rotation, and subscription confirmation/cancel. No deletion was confirmed. Other editors, double-column menus, keyboard/D-pad, and TalkBack were not exercised. Original APK and rotation settings were restored. * Standardize named delete confirmation layout Center delete confirmation messages while retaining a single Text node for the question and target. Separate each item name with a blank line in all nine locales, including asset files, which previously embedded the filename in the question. Keep the title-free dialog and existing Cancel/Delete actions, and leave ordinary confirmation messages start-aligned. Bulk-delete prompts retain their collective wording. Validation: 72 JVM tests, Kotlin compilation and Play Store debug assembly passed. All 63 compiled localized templates preserved an exact Unicode/percent target. Emulator checks covered profile and asset message nodes, two action targets, cancellation and profile dialog recreation after rotation. Actual spoken TalkBack output and the full theme/input matrix were not verified. * Resolve named proxy-chain confirmations by stable member key Upstream #6200 replaced pending member indices with stable keys. The textual rebase of #6202 retained an out-of-scope index reference in the confirmation text, breaking compilation. Look up the current member name through memberKeys, using the same pending identity as deletion. Reordering no longer separates the displayed target from the member removed on confirmation. * Reuse deletion questions instead of duplicating named translations Let DeleteConfirmDialog append an optional item name after a blank line while keeping the complete message in one centered Text. Reuse the existing localized questions at all named deletion sites and remove six duplicate resource keys from all nine locales. Asset-file questions use the same shared layout. Unnamed and bulk prompts remain unchanged. Retain stable-key proxy-chain target lookup and the corrected Russian policy-group wording in the reused question. Validation: existing ServerDeleteTargetTest, all 79 JVM tests, x86_64 Play Store debug assembly and a Pixel 9 Pro emulator check passed. The profile dialog exposes one question/name text node plus Cancel/Delete, and canceling preserves the lab profiles. TalkBack was enabled for the smoke check; actual speech and the full device/theme/locale matrix were not independently verified.
This commit is contained in:
1 parent
53fce9a8f5
commit
a1cfa2aa7a
25 files changed
+102
-29
No files matched your search
@@ -37,6 +37,7 @@ import androidx.compose.ui.res.painterResource
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.semantics.Role
|
||||
import androidx.compose.ui.text.input.VisualTransformation
|
||||
import androidx.compose.ui.text.style.TextAlign
|
||||
import androidx.compose.ui.unit.dp
|
||||
import com.v2ray.ang.R
|
||||
|
||||
@@ -47,6 +48,7 @@ fun ConfirmDialog(
|
||||
confirmText: String = stringResource(R.string.action_ok),
|
||||
dismissText: String? = stringResource(R.string.action_cancel),
|
||||
confirmIcon: @Composable (() -> Unit)? = null,
|
||||
messageTextAlign: TextAlign = TextAlign.Start,
|
||||
onConfirm: () -> Unit,
|
||||
onDismiss: () -> Unit
|
||||
) {
|
||||
@@ -61,7 +63,14 @@ fun ConfirmDialog(
|
||||
AlertDialog(
|
||||
onDismissRequest = onDismiss,
|
||||
title = title?.let { { Text(it) } },
|
||||
text = { Text(message, style = MaterialTheme.typography.bodyMedium) },
|
||||
text = {
|
||||
Text(
|
||||
text = message,
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
style = MaterialTheme.typography.bodyMedium,
|
||||
textAlign = messageTextAlign
|
||||
)
|
||||
},
|
||||
confirmButton = {
|
||||
TextButton(
|
||||
onClick = { onConfirm(); onDismiss() },
|
||||
@@ -91,9 +100,11 @@ fun DeleteConfirmDialog(
|
||||
message: String,
|
||||
onConfirm: () -> Unit,
|
||||
onDismiss: () -> Unit,
|
||||
itemName: String? = null,
|
||||
) {
|
||||
ConfirmDialog(
|
||||
message = message,
|
||||
message = if (itemName.isNullOrBlank()) message else "$message\n\n$itemName",
|
||||
messageTextAlign = TextAlign.Center,
|
||||
confirmText = stringResource(R.string.action_delete),
|
||||
confirmIcon = {
|
||||
Icon(
|
||||
|
||||
@@ -1,10 +1,23 @@
|
||||
package com.v2ray.ang.ui.main
|
||||
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.saveable.listSaver
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import com.v2ray.ang.R
|
||||
import com.v2ray.ang.ui.compose.DeleteConfirmDialog
|
||||
|
||||
data class ServerDeleteTarget(
|
||||
val guid: String,
|
||||
val profileName: String,
|
||||
) {
|
||||
companion object {
|
||||
val Saver = listSaver<ServerDeleteTarget?, String>(
|
||||
save = { target -> target?.let { listOf(it.guid, it.profileName) }.orEmpty() },
|
||||
restore = { ServerDeleteTarget(it[0], it[1]) },
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun MainDialogs(
|
||||
showDelAllConfirm: Boolean,
|
||||
@@ -16,7 +29,7 @@ fun MainDialogs(
|
||||
showDelInvalidConfirm: Boolean,
|
||||
onDismissDelInvalid: () -> Unit,
|
||||
onConfirmDelInvalid: () -> Unit,
|
||||
showRemoveConfirm: String?,
|
||||
showRemoveConfirm: ServerDeleteTarget?,
|
||||
onDismissRemove: () -> Unit,
|
||||
onConfirmRemove: (String) -> Unit,
|
||||
) {
|
||||
@@ -41,11 +54,11 @@ fun MainDialogs(
|
||||
onDismiss = onDismissDelInvalid
|
||||
)
|
||||
}
|
||||
if (showRemoveConfirm != null) {
|
||||
val guid = showRemoveConfirm
|
||||
showRemoveConfirm?.let { target ->
|
||||
DeleteConfirmDialog(
|
||||
message = stringResource(R.string.confirm_delete_profile),
|
||||
onConfirm = { onConfirmRemove(guid) },
|
||||
itemName = target.profileName,
|
||||
onConfirm = { onConfirmRemove(target.guid) },
|
||||
onDismiss = onDismissRemove
|
||||
)
|
||||
}
|
||||
|
||||
@@ -79,7 +79,7 @@ fun ShareMethodDialog(
|
||||
more: Boolean,
|
||||
onDismiss: () -> Unit,
|
||||
onAction: (MainAction) -> Unit,
|
||||
onRemove: (String) -> Unit,
|
||||
onRemove: (String, String) -> Unit,
|
||||
) {
|
||||
val menuActions = serverMenuActions(
|
||||
isComplexProfile = profile.configType.isComplexType(),
|
||||
@@ -95,7 +95,7 @@ fun ShareMethodDialog(
|
||||
ServerMenuAction.ShareClipboard -> onAction(MainAction.ShareClipboard(guid))
|
||||
ServerMenuAction.ShareFullContent -> onAction(MainAction.ShareFullContent(guid))
|
||||
ServerMenuAction.Edit -> onAction(MainAction.EditServer(guid, profile))
|
||||
ServerMenuAction.Delete -> onRemove(guid)
|
||||
ServerMenuAction.Delete -> onRemove(guid, profile.remarks)
|
||||
}
|
||||
},
|
||||
onDismiss = onDismiss
|
||||
|
||||
@@ -21,6 +21,7 @@ import androidx.compose.runtime.mutableStateOf
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.runtime.rememberCoroutineScope
|
||||
import androidx.compose.runtime.rememberUpdatedState
|
||||
import androidx.compose.runtime.saveable.rememberSaveable
|
||||
import androidx.compose.runtime.setValue
|
||||
import androidx.compose.runtime.snapshotFlow
|
||||
import androidx.compose.ui.Modifier
|
||||
@@ -57,11 +58,17 @@ fun MainScreen(
|
||||
var showDelAllConfirm by remember { mutableStateOf(false) }
|
||||
var showDelDuplicateConfirm by remember { mutableStateOf(false) }
|
||||
var showDelInvalidConfirm by remember { mutableStateOf(false) }
|
||||
var showRemoveConfirm by remember { mutableStateOf<String?>(null) }
|
||||
var showRemoveConfirm by rememberSaveable(stateSaver = ServerDeleteTarget.Saver) {
|
||||
mutableStateOf<ServerDeleteTarget?>(null)
|
||||
}
|
||||
|
||||
var shareTarget by remember { mutableStateOf<Triple<String, ProfileItem, Boolean>?>(null) }
|
||||
val removeServer: (String) -> Unit = { guid ->
|
||||
if (confirmRemove) showRemoveConfirm = guid else onAction(MainAction.RemoveServer(guid))
|
||||
val removeServer: (String, String) -> Unit = { guid, profileName ->
|
||||
if (confirmRemove) {
|
||||
showRemoveConfirm = ServerDeleteTarget(guid, profileName)
|
||||
} else {
|
||||
onAction(MainAction.RemoveServer(guid))
|
||||
}
|
||||
}
|
||||
|
||||
val pagerState = rememberPagerState(
|
||||
|
||||
@@ -75,7 +75,7 @@ fun GroupPagerPage(
|
||||
onEditServer: (String, ProfileItem) -> Unit,
|
||||
onShareServer: (String, ProfileItem) -> Unit,
|
||||
onMoreServer: (String, ProfileItem) -> Unit,
|
||||
onRemoveServer: (String) -> Unit,
|
||||
onRemoveServer: (String, String) -> Unit,
|
||||
contentPadding: PaddingValues
|
||||
) {
|
||||
val groupStateFlow = remember(groupId) {
|
||||
@@ -121,7 +121,7 @@ private class ServerRowActions(
|
||||
val edit: (String, ProfileItem) -> Unit,
|
||||
val share: (String, ProfileItem) -> Unit,
|
||||
val more: (String, ProfileItem) -> Unit,
|
||||
val remove: (String) -> Unit,
|
||||
val remove: (String, String) -> Unit,
|
||||
)
|
||||
|
||||
@Composable
|
||||
@@ -374,7 +374,7 @@ private fun ServerListItem(
|
||||
Modifier.size(24.dp)
|
||||
)
|
||||
}
|
||||
IconButton(onClick = { actions.remove(row.guid) }, Modifier.size(36.dp)) {
|
||||
IconButton(onClick = { actions.remove(row.guid, row.remarks) }, Modifier.size(36.dp)) {
|
||||
Icon(
|
||||
painterResource(R.drawable.ic_delete_24dp),
|
||||
stringResource(R.string.acc_delete),
|
||||
|
||||
@@ -309,6 +309,7 @@ fun RoutingEditScreen(
|
||||
if (showDeleteConfirm) {
|
||||
DeleteConfirmDialog(
|
||||
message = stringResource(R.string.confirm_delete_routing_rule),
|
||||
itemName = initial?.remarks.orEmpty(),
|
||||
onConfirm = onDelete,
|
||||
onDismiss = { showDeleteConfirm = false }
|
||||
)
|
||||
|
||||
@@ -491,6 +491,7 @@ abstract class BaseServerActivity : BaseComponentActivity() {
|
||||
if (showDeleteDialog) {
|
||||
DeleteConfirmDialog(
|
||||
message = stringResource(R.string.confirm_delete_profile),
|
||||
itemName = initialConfig.remarks,
|
||||
onConfirm = {
|
||||
showDeleteDialog = false
|
||||
deleteServer(editGuid)
|
||||
|
||||
@@ -519,6 +519,7 @@ fun ServerScreen(
|
||||
if (showDeleteDialog) {
|
||||
DeleteConfirmDialog(
|
||||
message = stringResource(R.string.confirm_delete_profile),
|
||||
itemName = initialConfig.remarks,
|
||||
onConfirm = { showDeleteDialog = false; onDelete() },
|
||||
onDismiss = { showDeleteDialog = false }
|
||||
)
|
||||
|
||||
@@ -486,6 +486,7 @@ fun ServerCustomConfigScreen(
|
||||
if (showDeleteConfirm) {
|
||||
DeleteConfirmDialog(
|
||||
message = stringResource(R.string.confirm_delete_profile),
|
||||
itemName = initialRemarks,
|
||||
onConfirm = {
|
||||
showDeleteConfirm = false
|
||||
onDelete()
|
||||
|
||||
@@ -306,6 +306,7 @@ fun ServerGroupScreen(
|
||||
if (showDeleteConfirm) {
|
||||
DeleteConfirmDialog(
|
||||
message = stringResource(R.string.confirm_delete_policy_group),
|
||||
itemName = initialRemarks,
|
||||
onConfirm = onDelete,
|
||||
onDismiss = { showDeleteConfirm = false }
|
||||
)
|
||||
|
||||
@@ -337,6 +337,7 @@ fun ProxyChainScreen(
|
||||
if (showProfileDeleteConfirm) {
|
||||
DeleteConfirmDialog(
|
||||
message = stringResource(R.string.confirm_delete_profile),
|
||||
itemName = initialRemarks,
|
||||
onConfirm = { showProfileDeleteConfirm = false; onDelete() },
|
||||
onDismiss = { showProfileDeleteConfirm = false }
|
||||
)
|
||||
@@ -344,6 +345,7 @@ fun ProxyChainScreen(
|
||||
memberToDeleteKey?.let { memberKey ->
|
||||
DeleteConfirmDialog(
|
||||
message = stringResource(R.string.confirm_delete_proxy_chain_member),
|
||||
itemName = members.getOrNull(memberKeys.indexOf(memberKey)).orEmpty(),
|
||||
onConfirm = {
|
||||
val (remainingMembers, remainingKeys) = withoutProxyChainMember(members, memberKeys, memberKey)
|
||||
members = remainingMembers
|
||||
|
||||
@@ -247,6 +247,7 @@ fun SubEditScreen(
|
||||
if (showDeleteConfirm) {
|
||||
DeleteConfirmDialog(
|
||||
message = stringResource(R.string.confirm_delete_subscription_group),
|
||||
itemName = initial.remarks,
|
||||
onConfirm = onDelete,
|
||||
onDismiss = { showDeleteConfirm = false }
|
||||
)
|
||||
|
||||
@@ -61,6 +61,8 @@ private enum class SubscriptionShareAction(@StringRes val labelRes: Int) {
|
||||
Clipboard(R.string.share_subscription_clipboard)
|
||||
}
|
||||
|
||||
private data class SubscriptionDeleteTarget(val guid: String, val name: String)
|
||||
|
||||
class SubSettingActivity : BaseComponentActivity() {
|
||||
private val viewModel: SubscriptionsViewModel by viewModels()
|
||||
|
||||
@@ -113,7 +115,7 @@ fun SubSettingScreen(
|
||||
) {
|
||||
val subscriptions by viewModel.subsFlow.collectAsStateWithLifecycle()
|
||||
var showUpdateDialog by remember { mutableStateOf(false) }
|
||||
var removeTarget by remember { mutableStateOf<String?>(null) }
|
||||
var removeTarget by remember { mutableStateOf<SubscriptionDeleteTarget?>(null) }
|
||||
val confirmRemove = MmkvManager.decodeSettingsBool(AppConfig.PREF_CONFIRM_REMOVE, false)
|
||||
|
||||
var shareTarget by remember { mutableStateOf<Pair<String, String>?>(null) }
|
||||
@@ -212,7 +214,7 @@ fun SubSettingScreen(
|
||||
)
|
||||
}
|
||||
IconButton(onClick = {
|
||||
if (confirmRemove) removeTarget = subCache.guid
|
||||
if (confirmRemove) removeTarget = SubscriptionDeleteTarget(subCache.guid, subCache.subscription.remarks)
|
||||
else onRemoveSub(subCache.guid)
|
||||
}) {
|
||||
Icon(
|
||||
@@ -268,12 +270,13 @@ fun SubSettingScreen(
|
||||
)
|
||||
}
|
||||
|
||||
if (removeTarget != null) {
|
||||
removeTarget?.let { target ->
|
||||
DeleteConfirmDialog(
|
||||
message = stringResource(R.string.confirm_delete_subscription_group),
|
||||
itemName = target.name,
|
||||
onConfirm = {
|
||||
onRemoveSub(removeTarget!!)
|
||||
removeTarget = null
|
||||
onRemoveSub(target.guid)
|
||||
},
|
||||
onDismiss = { removeTarget = null }
|
||||
)
|
||||
|
||||
@@ -345,7 +345,8 @@ internal fun UserAssetScreen(
|
||||
|
||||
deleteTarget?.let { asset ->
|
||||
DeleteConfirmDialog(
|
||||
message = stringResource(R.string.confirm_delete_asset_file, asset.name),
|
||||
message = stringResource(R.string.confirm_delete_asset_file),
|
||||
itemName = asset.name,
|
||||
onConfirm = {
|
||||
deleteTarget = null
|
||||
onRemoveAsset(asset.guid, asset.name)
|
||||
|
||||
@@ -197,6 +197,7 @@ fun UserAssetUrlScreen(
|
||||
if (showDeleteConfirm) {
|
||||
DeleteConfirmDialog(
|
||||
message = stringResource(R.string.confirm_delete_asset_source),
|
||||
itemName = initialRemarks,
|
||||
onConfirm = onDelete,
|
||||
onDismiss = { showDeleteConfirm = false }
|
||||
)
|
||||
|
||||
@@ -50,7 +50,7 @@
|
||||
<string name="confirm_delete_proxy_chain_member">هل أنت متأكد من أنك تريد حذف هذا العضو من سلسلة الوكلاء؟</string>
|
||||
<string name="confirm_delete_routing_rule">هل أنت متأكد من أنك تريد حذف قاعدة التوجيه هذه؟</string>
|
||||
<string name="confirm_delete_subscription_group">هل أنت متأكد من أنك تريد حذف مجموعة الاشتراك هذه؟</string>
|
||||
<string name="confirm_delete_asset_file">هل أنت متأكد من أنك تريد حذف ملف الأصول \"%1$s\"؟</string>
|
||||
<string name="confirm_delete_asset_file">هل أنت متأكد من أنك تريد حذف ملف الأصول هذا؟</string>
|
||||
<string name="confirm_delete_asset_source">هل أنت متأكد من أنك تريد حذف مصدر الأصول هذا؟</string>
|
||||
<string name="server_lab_remarks">ملاحظات</string>
|
||||
<string name="server_lab_address">العنوان</string>
|
||||
|
||||
@@ -50,7 +50,7 @@
|
||||
<string name="confirm_delete_proxy_chain_member">আপনি কি এই প্রক্সি চেইন সদস্যটিকে মুছে ফেলতে চান?</string>
|
||||
<string name="confirm_delete_routing_rule">আপনি কি এই রাউটিং নিয়মটি মুছে ফেলতে চান?</string>
|
||||
<string name="confirm_delete_subscription_group">আপনি কি এই সাবস্ক্রিপশন গ্রুপটি মুছে ফেলতে চান?</string>
|
||||
<string name="confirm_delete_asset_file">আপনি কি \"%1$s\" অ্যাসেট ফাইলটি মুছে ফেলতে চান?</string>
|
||||
<string name="confirm_delete_asset_file">আপনি কি এই অ্যাসেট ফাইলটি মুছে ফেলতে চান?</string>
|
||||
<string name="confirm_delete_asset_source">আপনি কি এই অ্যাসেট উৎসটি মুছে ফেলতে চান?</string>
|
||||
<string name="server_lab_remarks">মন্তব্য</string>
|
||||
<string name="server_lab_address">ঠিকানা</string>
|
||||
|
||||
@@ -50,7 +50,7 @@
|
||||
<string name="confirm_delete_proxy_chain_member">ای عوزو زنجیره پروکسی پاک ابۊ، هنی هم اخۊی پاکس کۊنی؟</string>
|
||||
<string name="confirm_delete_routing_rule">ای قانووݩ تور جوستن پاک ابۊ، هنی هم اخۊی پاکس کۊنی؟</string>
|
||||
<string name="confirm_delete_subscription_group">ای بونکۊ اشتراک پاک ابۊ، هنی هم اخۊی پاکس کۊنی؟</string>
|
||||
<string name="confirm_delete_asset_file">فایل بونچک جغرافیایی «%1$s» پاک ابۊ، هنی هم اخۊی پاکس کۊنی؟</string>
|
||||
<string name="confirm_delete_asset_file">ای فایل بونچک جغرافیایی پاک ابۊ، هنی هم اخۊی پاکس کۊنی؟</string>
|
||||
<string name="confirm_delete_asset_source">ای بونچک فایلا جغرافیایی پاک ابۊ، هنی هم اخۊی پاکس کۊنی؟</string>
|
||||
<string name="server_lab_remarks">نیشتنا</string>
|
||||
<string name="server_lab_address">نشۊوی</string>
|
||||
|
||||
@@ -50,7 +50,7 @@
|
||||
<string name="confirm_delete_proxy_chain_member">آیا مطمئن هستید که میخواهید این عضو زنجیره پراکسی را حذف کنید؟</string>
|
||||
<string name="confirm_delete_routing_rule">آیا مطمئن هستید که میخواهید این قانون مسیریابی را حذف کنید؟</string>
|
||||
<string name="confirm_delete_subscription_group">آیا مطمئن هستید که میخواهید این گروه اشتراک را حذف کنید؟</string>
|
||||
<string name="confirm_delete_asset_file">آیا مطمئن هستید که میخواهید فایل دارایی \"%1$s\" را حذف کنید؟</string>
|
||||
<string name="confirm_delete_asset_file">آیا مطمئن هستید که میخواهید این فایل دارایی را حذف کنید؟</string>
|
||||
<string name="confirm_delete_asset_source">آیا مطمئن هستید که میخواهید این منبع دارایی را حذف کنید؟</string>
|
||||
<string name="server_lab_remarks">ملاحظات</string>
|
||||
<string name="server_lab_address">نشانی</string>
|
||||
|
||||
@@ -46,11 +46,11 @@
|
||||
<string name="confirm_delete_duplicate_profiles">Удалить все отображаемые дубликаты профилей?</string>
|
||||
<string name="confirm_delete_invalid_profiles">Выполните проверку перед удалением! Удалить все отображаемые нерабочие профили?</string>
|
||||
<string name="confirm_delete_profile">Удалить этот профиль?</string>
|
||||
<string name="confirm_delete_policy_group">Удалить эту политику группы?</string>
|
||||
<string name="confirm_delete_policy_group">Удалить эту группу политик?</string>
|
||||
<string name="confirm_delete_proxy_chain_member">Удалить этого участника цепочки прокси?</string>
|
||||
<string name="confirm_delete_routing_rule">Удалить это правило маршрутизации?</string>
|
||||
<string name="confirm_delete_subscription_group">Удалить эту подписку?</string>
|
||||
<string name="confirm_delete_asset_file">Удалить файл ресурсов «%1$s»?</string>
|
||||
<string name="confirm_delete_asset_file">Удалить этот файл ресурсов?</string>
|
||||
<string name="confirm_delete_asset_source">Удалить этот источник ресурсов?</string>
|
||||
<string name="server_lab_remarks">Название</string>
|
||||
<string name="server_lab_address">Адрес</string>
|
||||
|
||||
@@ -50,7 +50,7 @@
|
||||
<string name="confirm_delete_proxy_chain_member">Bạn có chắc muốn xóa thành viên này khỏi chuỗi proxy không?</string>
|
||||
<string name="confirm_delete_routing_rule">Bạn có chắc muốn xóa quy tắc định tuyến này không?</string>
|
||||
<string name="confirm_delete_subscription_group">Bạn có chắc muốn xóa nhóm đăng ký này không?</string>
|
||||
<string name="confirm_delete_asset_file">Bạn có chắc muốn xóa tệp tài nguyên \"%1$s\" không?</string>
|
||||
<string name="confirm_delete_asset_file">Bạn có chắc muốn xóa tệp tài nguyên này không?</string>
|
||||
<string name="confirm_delete_asset_source">Bạn có chắc muốn xóa nguồn tài nguyên này không?</string>
|
||||
<string name="server_lab_remarks">Tên cấu hình</string>
|
||||
<string name="server_lab_address">Địa chỉ</string>
|
||||
|
||||
@@ -50,7 +50,7 @@
|
||||
<string name="confirm_delete_proxy_chain_member">确定要删除此代理链成员吗?</string>
|
||||
<string name="confirm_delete_routing_rule">确定要删除此路由规则吗?</string>
|
||||
<string name="confirm_delete_subscription_group">确定要删除此订阅组吗?</string>
|
||||
<string name="confirm_delete_asset_file">确定要删除资源文件“%1$s”吗?</string>
|
||||
<string name="confirm_delete_asset_file">确定要删除此资源文件吗?</string>
|
||||
<string name="confirm_delete_asset_source">确定要删除此资源来源吗?</string>
|
||||
<string name="server_lab_remarks">别名 (remarks)</string>
|
||||
<string name="server_lab_address">地址 (address)</string>
|
||||
|
||||
@@ -50,7 +50,7 @@
|
||||
<string name="confirm_delete_proxy_chain_member">確定要刪除此代理鏈成員嗎?</string>
|
||||
<string name="confirm_delete_routing_rule">確定要刪除此路由規則嗎?</string>
|
||||
<string name="confirm_delete_subscription_group">確定要刪除此訂閱群組嗎?</string>
|
||||
<string name="confirm_delete_asset_file">確定要刪除資源檔案「%1$s」嗎?</string>
|
||||
<string name="confirm_delete_asset_file">確定要刪除此資源檔案嗎?</string>
|
||||
<string name="confirm_delete_asset_source">確定要刪除此資源來源嗎?</string>
|
||||
<string name="server_lab_remarks">備註</string>
|
||||
<string name="server_lab_address">位址</string>
|
||||
|
||||
@@ -50,7 +50,7 @@
|
||||
<string name="confirm_delete_proxy_chain_member">Are you sure you want to delete this chain member?</string>
|
||||
<string name="confirm_delete_routing_rule">Are you sure you want to delete this routing rule?</string>
|
||||
<string name="confirm_delete_subscription_group">Are you sure you want to delete this subscription group?</string>
|
||||
<string name="confirm_delete_asset_file">Are you sure you want to delete the asset file \"%1$s\"?</string>
|
||||
<string name="confirm_delete_asset_file">Are you sure you want to delete this asset file?</string>
|
||||
<string name="confirm_delete_asset_source">Are you sure you want to delete this asset source?</string>
|
||||
<string name="server_lab_remarks">remarks</string>
|
||||
<string name="server_lab_address">address</string>
|
||||
|
||||
@@ -0,0 +1,29 @@
|
||||
package com.v2ray.ang.ui.main
|
||||
|
||||
import androidx.compose.runtime.saveable.SaverScope
|
||||
import org.junit.Assert.assertEquals
|
||||
import org.junit.Assert.assertNull
|
||||
import org.junit.Test
|
||||
|
||||
class ServerDeleteTargetTest {
|
||||
@Test
|
||||
fun pendingDeletionRestoresItsGuidAndDisplayName() {
|
||||
val scope = SaverScope { it is String }
|
||||
for (guid in listOf("first-guid", "second-guid")) {
|
||||
for (name in listOf("Same name", "", "Профиль\n服务器 100%")) {
|
||||
val target = ServerDeleteTarget(guid, name)
|
||||
val saved = with(ServerDeleteTarget.Saver) { scope.save(target) }
|
||||
|
||||
assertEquals(listOf(guid, name), saved)
|
||||
assertEquals(target, ServerDeleteTarget.Saver.restore(requireNotNull(saved)))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun noPendingDeletionHasNoSavedTarget() {
|
||||
val saved = with(ServerDeleteTarget.Saver) { SaverScope { it is String }.save(null) }
|
||||
|
||||
assertNull(saved)
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user