refactor(main): unify UI interactions with MainAction dispatcher

Refactored MainActivity and its Compose UI components to use a single
onAction: (MainAction) -> Unit callback. This significantly simplifies
component signatures and centralizes interaction logic.

- Updated MainContract.kt to include all UI-triggered actions.
- Modified MainScreen and sub-components (TopBar, BottomBar, Drawer, etc.)
  to use the unified onAction dispatcher.
- Implemented a centralized action handler in MainActivity to dispatch
  between ViewModel and Activity-specific tasks.
- Removed redundant ImportConfigViaSub action.
- Moved QR code generation state into MainUiState for MVI consistency.

This change reduces parameter drilling and improves maintainability of
the main interface.
This commit is contained in:
2dust committed 2026-07-21 17:56:00 +08:00
1 parent ccadd5453a
commit 4b903cf5e0
9 files changed
+123 -161

No files matched your search

@@ -2,7 +2,6 @@ package com.v2ray.ang.ui.main
import android.app.Activity
import android.content.Intent
import android.graphics.Bitmap
import android.net.VpnService
import android.os.Build
import android.os.Bundle
@@ -103,40 +102,33 @@ class MainActivity : HelperBaseComponentActivity() {
override fun ScreenContent() {
MainScreen(
mainViewModel = mainViewModel,
onFabClick = ::handleFabAction,
onTestClick = ::handleLayoutTestClick,
onNavigate = ::navigateTo,
onImportManually = ::importManually,
onImportQRcode = ::importQRcode,
onImportClipboard = ::importClipboard,
onImportLocal = ::importConfigLocal,
onSubUpdate = ::importConfigViaSub,
onExportAll = { mainViewModel.onAction(MainAction.ExportAll) },
onRealPingAll = { mainViewModel.onAction(MainAction.TestAllServers) },
onRestartService = ::restartV2Ray,
onDelAllConfig = { mainViewModel.onAction(MainAction.RemoveAllServers) },
onDelDuplicateConfig = { mainViewModel.onAction(MainAction.RemoveDuplicateServers) },
onDelInvalidConfig = { mainViewModel.onAction(MainAction.RemoveInvalidServers) },
onSortByTestResults = { mainViewModel.onAction(MainAction.SortByTestResults) },
onEditServer = ::editServer,
onRemoveServer = { guid -> mainViewModel.onAction(MainAction.RemoveServer(guid)) },
onSelectServer = ::setSelectServer,
onShareQRCode = ::getShareQRCodeBitmap,
onShareClipboard = ::shareToClipboard,
onShareFullContent = ::shareFullContentAsync,
onSubscriptionIdChanged = { id -> mainViewModel.onAction(MainAction.SelectGroup(id)) },
onLocateSelectedServer = { mainViewModel.triggerLocateSelectedServer() },
onAction = { action ->
when (action) {
MainAction.ToggleService -> handleFabAction()
MainAction.TestCurrentServer -> handleLayoutTestClick()
MainAction.ImportQRcode -> importQRcode()
MainAction.ImportClipboard -> importClipboard()
MainAction.ImportConfigLocal -> importConfigLocal()
is MainAction.ImportManually -> importManually(action.type)
MainAction.RestartService -> restartV2Ray()
MainAction.LocateSelectedServer -> mainViewModel.triggerLocateSelectedServer()
is MainAction.SelectServer -> setSelectServer(action.guid)
is MainAction.EditServer -> editServer(action.guid, action.profile)
is MainAction.ShareClipboard -> shareToClipboard(action.guid)
is MainAction.ShareFullContent -> shareFullContentAsync(action.guid)
else -> mainViewModel.onAction(action)
}
},
onNavigate = { route -> navigateTo(route) },
shareMethodEntries = resources.getStringArray(R.array.share_method).toList(),
shareMethodMoreEntries = resources.getStringArray(R.array.share_method_more).toList()
)
}
fun getShareQRCodeBitmap(guid: String): Bitmap? = AngConfigManager.share2QRCode(guid)
fun shareToClipboard(guid: String): Boolean =
private fun shareToClipboard(guid: String): Boolean =
AngConfigManager.share2Clipboard(this, guid) == 0
fun shareFullContentAsync(guid: String) {
private fun shareFullContentAsync(guid: String) {
lifecycleScope.launch(Dispatchers.IO) {
val result = AngConfigManager.shareFullContent2Clipboard(this@MainActivity, guid)
withContext(Dispatchers.Main) {
@@ -258,10 +250,6 @@ class MainActivity : HelperBaseComponentActivity() {
}
}
private fun importConfigViaSub() {
mainViewModel.onAction(MainAction.ImportConfigViaSub)
}
private fun editServer(guid: String, profile: ProfileItem) {
val activityClass = when (profile.configType) {
EConfigType.CUSTOM -> ServerCustomConfigActivity::class.java
@@ -36,8 +36,7 @@ fun MainBottomBar(
displayText: String,
isRunning: Boolean,
isDarkTheme: Boolean,
onTestClick: () -> Unit,
onFabClick: () -> Unit
onAction: (MainAction) -> Unit
) {
Box(modifier = Modifier.fillMaxWidth()) {
Column(modifier = Modifier.fillMaxWidth()) {
@@ -47,7 +46,7 @@ fun MainBottomBar(
.fillMaxWidth()
.windowInsetsPadding(WindowInsets.navigationBars)
.height(64.dp)
.clickable(onClick = onTestClick),
.clickable(onClick = { onAction(MainAction.TestCurrentServer) }),
color = MaterialTheme.colorScheme.surface,
tonalElevation = 0.dp
) {
@@ -63,7 +62,7 @@ fun MainBottomBar(
}
}
FloatingActionButton(
onClick = onFabClick,
onClick = { onAction(MainAction.ToggleService) },
modifier = Modifier
.align(Alignment.TopEnd)
.padding(end = 24.dp)
@@ -15,7 +15,8 @@ data class MainUiState(
val statusText: String = "",
val locateTarget: LocateTarget? = null,
val confirmRemove: Boolean = false,
val doubleColumnDisplay: Boolean = false
val doubleColumnDisplay: Boolean = false,
val shareQRCodeBitmap: android.graphics.Bitmap? = null
)
/**
@@ -34,15 +35,27 @@ sealed interface MainAction {
data object SortByTestResults : MainAction
data object UpdateSubscriptions : MainAction
data object ExportAll : MainAction
data object ImportConfigViaSub : MainAction
data object ImportQRcode : MainAction
data object ImportClipboard : MainAction
data object ImportConfigLocal : MainAction
data class ImportManually(val type: Int) : MainAction
data object RestartService : MainAction
data object LocateSelectedServer : MainAction
data class SelectGroup(val groupId: String) : MainAction
data class SelectServer(val guid: String) : MainAction
data class RemoveServer(val guid: String) : MainAction
data class EditServer(val guid: String, val profile: com.v2ray.ang.dto.entities.ProfileItem) : MainAction
data class Search(val query: String) : MainAction
data class SwapServer(val fromIndex: Int, val toIndex: Int) : MainAction
data class ShareQRCode(val guid: String) : MainAction
data class ShareClipboard(val guid: String) : MainAction
data class ShareFullContent(val guid: String) : MainAction
data object DismissQRCodeDialog : MainAction
data class ImportBatchConfig(val configText: String) : MainAction
data class LocateHandled(val target: LocateTarget) : MainAction
data class LocateHandled(val target: com.v2ray.ang.dto.LocateTarget) : MainAction
}
@@ -55,6 +55,8 @@ interface MainDataSource : Closeable {
fun updateConfigViaSub(subscriptionCache: SubscriptionCache): SubscriptionUpdateResult
fun shareNonCustomConfigsToClipboard(guids: List<String>): Int
fun share2QRCode(guid: String): android.graphics.Bitmap?
fun share2Clipboard(guid: String): Boolean
fun sendMsg2Service(msgId: Int, content: String)
fun sendMsg2TestService(msg: TestServiceMessage)
@@ -14,80 +14,72 @@ import com.v2ray.ang.extension.isComplexType
@Composable
fun ImportMenuContent(
onImportQRcode: () -> Unit,
onImportClipboard: () -> Unit,
onImportLocal: () -> Unit,
onImportManually: (Int) -> Unit
onAction: (MainAction) -> Unit
) {
DropdownMenuItem(
text = { Text(stringResource(R.string.menu_item_import_config_qrcode)) },
onClick = onImportQRcode
onClick = { onAction(MainAction.ImportQRcode) }
)
DropdownMenuItem(
text = { Text(stringResource(R.string.menu_item_import_config_clipboard)) },
onClick = onImportClipboard
onClick = { onAction(MainAction.ImportClipboard) }
)
DropdownMenuItem(
text = { Text(stringResource(R.string.menu_item_import_config_local)) },
onClick = onImportLocal
onClick = { onAction(MainAction.ImportConfigLocal) }
)
DropdownMenuItem(
text = { Text(stringResource(R.string.menu_item_import_config_policy_group)) },
onClick = { onImportManually(EConfigType.POLICYGROUP.value) }
onClick = { onAction(MainAction.ImportManually(EConfigType.POLICYGROUP.value)) }
)
DropdownMenuItem(
text = { Text(stringResource(R.string.menu_item_import_config_proxy_chain)) },
onClick = { onImportManually(EConfigType.PROXYCHAIN.value) }
onClick = { onAction(MainAction.ImportManually(EConfigType.PROXYCHAIN.value)) }
)
DropdownMenuItem(
text = { Text(stringResource(R.string.menu_item_import_config_manually_vmess)) },
onClick = { onImportManually(EConfigType.VMESS.value) }
onClick = { onAction(MainAction.ImportManually(EConfigType.VMESS.value)) }
)
DropdownMenuItem(
text = { Text(stringResource(R.string.menu_item_import_config_manually_vless)) },
onClick = { onImportManually(EConfigType.VLESS.value) }
onClick = { onAction(MainAction.ImportManually(EConfigType.VLESS.value)) }
)
DropdownMenuItem(
text = { Text(stringResource(R.string.menu_item_import_config_manually_ss)) },
onClick = { onImportManually(EConfigType.SHADOWSOCKS.value) }
onClick = { onAction(MainAction.ImportManually(EConfigType.SHADOWSOCKS.value)) }
)
DropdownMenuItem(
text = { Text(stringResource(R.string.menu_item_import_config_manually_socks)) },
onClick = { onImportManually(EConfigType.SOCKS.value) }
onClick = { onAction(MainAction.ImportManually(EConfigType.SOCKS.value)) }
)
DropdownMenuItem(
text = { Text(stringResource(R.string.menu_item_import_config_manually_http)) },
onClick = { onImportManually(EConfigType.HTTP.value) }
onClick = { onAction(MainAction.ImportManually(EConfigType.HTTP.value)) }
)
DropdownMenuItem(
text = { Text(stringResource(R.string.menu_item_import_config_manually_trojan)) },
onClick = { onImportManually(EConfigType.TROJAN.value) }
onClick = { onAction(MainAction.ImportManually(EConfigType.TROJAN.value)) }
)
DropdownMenuItem(
text = { Text(stringResource(R.string.menu_item_import_config_manually_wireguard)) },
onClick = { onImportManually(EConfigType.WIREGUARD.value) }
onClick = { onAction(MainAction.ImportManually(EConfigType.WIREGUARD.value)) }
)
DropdownMenuItem(
text = { Text(stringResource(R.string.menu_item_import_config_manually_hysteria2)) },
onClick = { onImportManually(EConfigType.HYSTERIA2.value) }
onClick = { onAction(MainAction.ImportManually(EConfigType.HYSTERIA2.value)) }
)
}
@Composable
fun MoreMenuContent(
onRestartService: () -> Unit,
onAction: (MainAction) -> Unit,
onDelAllConfig: () -> Unit,
onDelDuplicateConfig: () -> Unit,
onDelInvalidConfig: () -> Unit,
onExportAll: () -> Unit,
onRealPingAll: () -> Unit,
onLocateSelectedServer: () -> Unit,
onSortByTestResults: () -> Unit,
onSubUpdate: () -> Unit
onDelInvalidConfig: () -> Unit
) {
DropdownMenuItem(
text = { Text(stringResource(R.string.title_service_restart)) },
onClick = onRestartService
onClick = { onAction(MainAction.RestartService) }
)
DropdownMenuItem(
text = { Text(stringResource(R.string.title_del_all_config)) },
@@ -103,23 +95,23 @@ fun MoreMenuContent(
)
DropdownMenuItem(
text = { Text(stringResource(R.string.title_export_all)) },
onClick = onExportAll
onClick = { onAction(MainAction.ExportAll) }
)
DropdownMenuItem(
text = { Text(stringResource(R.string.title_real_ping_all_server)) },
onClick = onRealPingAll
onClick = { onAction(MainAction.TestAllServers) }
)
DropdownMenuItem(
text = { Text(stringResource(R.string.title_locate_selected_config)) },
onClick = onLocateSelectedServer
onClick = { onAction(MainAction.LocateSelectedServer) }
)
DropdownMenuItem(
text = { Text(stringResource(R.string.title_sort_by_test_results)) },
onClick = onSortByTestResults
onClick = { onAction(MainAction.SortByTestResults) }
)
DropdownMenuItem(
text = { Text(stringResource(R.string.title_sub_update)) },
onClick = onSubUpdate
onClick = { onAction(MainAction.UpdateSubscriptions) }
)
}
@@ -131,12 +123,7 @@ fun ShareMethodDialog(
shareMethodEntries: List<String>,
shareMethodMoreEntries: List<String>,
onDismiss: () -> Unit,
onShareQRCode: (String) -> Bitmap?,
onShareClipboard: (String) -> Boolean,
onShareFullContent: (String) -> Unit,
onEditServer: (String, ProfileItem) -> Unit,
onRemoveServer: (String) -> Unit,
showQRCodeBitmap: (Bitmap?) -> Unit
onAction: (MainAction) -> Unit
) {
val isCustom = profile.configType.isComplexType()
val (shareOptions, skip) = if (more) {
@@ -151,11 +138,11 @@ fun ShareMethodDialog(
onSelected = { index, _ ->
onDismiss()
when (index + skip) {
0 -> showQRCodeBitmap(onShareQRCode(guid))
1 -> onShareClipboard(guid)
2 -> onShareFullContent(guid)
3 -> onEditServer(guid, profile)
4 -> onRemoveServer(guid)
0 -> onAction(MainAction.ShareQRCode(guid))
1 -> onAction(MainAction.ShareClipboard(guid))
2 -> onAction(MainAction.ShareFullContent(guid))
3 -> onAction(MainAction.EditServer(guid, profile))
4 -> onAction(MainAction.RemoveServer(guid))
}
},
onDismiss = onDismiss
@@ -201,6 +201,12 @@ class MainRepository(
override fun shareNonCustomConfigsToClipboard(guids: List<String>): Int =
AngConfigManager.shareNonCustomConfigsToClipboard(app, guids)
override fun share2QRCode(guid: String): android.graphics.Bitmap? =
AngConfigManager.share2QRCode(guid)
override fun share2Clipboard(guid: String): Boolean =
AngConfigManager.share2Clipboard(app, guid) == 0
override fun sendMsg2Service(msgId: Int, content: String) =
MessageUtil.sendMsg2Service(app, msgId, content)
@@ -1,6 +1,5 @@
package com.v2ray.ang.ui.main
import android.graphics.Bitmap
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.PaddingValues
import androidx.compose.foundation.layout.fillMaxSize
@@ -41,29 +40,8 @@ import kotlinx.coroutines.launch
@Composable
fun MainScreen(
mainViewModel: MainViewModel,
onFabClick: () -> Unit,
onTestClick: () -> Unit,
onAction: (MainAction) -> Unit,
onNavigate: (String) -> Unit,
onImportManually: (Int) -> Unit,
onImportQRcode: () -> Unit,
onImportClipboard: () -> Unit,
onImportLocal: () -> Unit,
onSubUpdate: () -> Unit,
onExportAll: () -> Unit,
onRealPingAll: () -> Unit,
onRestartService: () -> Unit,
onDelAllConfig: () -> Unit,
onDelDuplicateConfig: () -> Unit,
onDelInvalidConfig: () -> Unit,
onSortByTestResults: () -> Unit,
onEditServer: (String, ProfileItem) -> Unit,
onRemoveServer: (String) -> Unit,
onSelectServer: (String) -> Unit,
onShareQRCode: (String) -> Bitmap?,
onShareClipboard: (String) -> Boolean,
onShareFullContent: (String) -> Unit,
onSubscriptionIdChanged: (String) -> Unit,
onLocateSelectedServer: () -> Unit,
shareMethodEntries: List<String>,
shareMethodMoreEntries: List<String>
) {
@@ -75,6 +53,7 @@ fun MainScreen(
val selectedGuid = uiState.selectedGuid
val doubleColumnDisplay = uiState.doubleColumnDisplay
val confirmRemove = uiState.confirmRemove
val shareQRCodeBitmap = uiState.shareQRCodeBitmap
val isDarkTheme = LocalDarkTheme.current
val drawerState = rememberDrawerState(DrawerValue.Closed)
@@ -87,7 +66,6 @@ fun MainScreen(
var showRemoveConfirm by remember { mutableStateOf<String?>(null) }
var shareTarget by remember { mutableStateOf<Triple<String, ProfileItem, Boolean>?>(null) }
var showQRCodeBitmap by remember { mutableStateOf<Bitmap?>(null) }
val pagerState = rememberPagerState(
initialPage = 0,
@@ -125,7 +103,7 @@ fun MainScreen(
.collect { page ->
val currentGroups = latestGroups
if (!latestLocateInProgress && page in currentGroups.indices) {
onSubscriptionIdChanged(currentGroups[page].id)
onAction(MainAction.SelectGroup(currentGroups[page].id))
}
}
}
@@ -145,7 +123,7 @@ fun MainScreen(
animateAdjacentPage = false
)
}
onSubscriptionIdChanged(target.groupId)
onAction(MainAction.SelectGroup(target.groupId))
repeat(10) {
val ready = if (latestDoubleColumnDisplay) {
@@ -182,16 +160,16 @@ fun MainScreen(
MainDialogs(
showDelAllConfirm = showDelAllConfirm,
onDismissDelAll = { showDelAllConfirm = false },
onConfirmDelAll = { showDelAllConfirm = false; onDelAllConfig() },
onConfirmDelAll = { showDelAllConfirm = false; onAction(MainAction.RemoveAllServers) },
showDelDuplicateConfirm = showDelDuplicateConfirm,
onDismissDelDuplicate = { showDelDuplicateConfirm = false },
onConfirmDelDuplicate = { showDelDuplicateConfirm = false; onDelDuplicateConfig() },
onConfirmDelDuplicate = { showDelDuplicateConfirm = false; onAction(MainAction.RemoveDuplicateServers) },
showDelInvalidConfirm = showDelInvalidConfirm,
onDismissDelInvalid = { showDelInvalidConfirm = false },
onConfirmDelInvalid = { showDelInvalidConfirm = false; onDelInvalidConfig() },
onConfirmDelInvalid = { showDelInvalidConfirm = false; onAction(MainAction.RemoveInvalidServers) },
showRemoveConfirm = showRemoveConfirm,
onDismissRemove = { showRemoveConfirm = null },
onConfirmRemove = { guid -> showRemoveConfirm = null; onRemoveServer(guid) }
onConfirmRemove = { guid -> showRemoveConfirm = null; onAction(MainAction.RemoveServer(guid)) }
)
if (shareTarget != null) {
@@ -203,16 +181,11 @@ fun MainScreen(
shareMethodEntries = shareMethodEntries,
shareMethodMoreEntries = shareMethodMoreEntries,
onDismiss = { shareTarget = null },
onShareQRCode = onShareQRCode,
onShareClipboard = onShareClipboard,
onShareFullContent = onShareFullContent,
onEditServer = onEditServer,
onRemoveServer = onRemoveServer,
showQRCodeBitmap = { bitmap -> showQRCodeBitmap = bitmap }
onAction = onAction
)
}
if (showQRCodeBitmap != null) {
QRCodeDialog(bitmap = showQRCodeBitmap, onDismiss = { showQRCodeBitmap = null })
if (shareQRCodeBitmap != null) {
QRCodeDialog(bitmap = shareQRCodeBitmap, onDismiss = { onAction(MainAction.DismissQRCodeDialog) })
}
ModalNavigationDrawer(
@@ -235,28 +208,19 @@ fun MainScreen(
searchQuery = searchQuery,
onSearchQueryChange = { query: String ->
searchQuery = query
mainViewModel.filterConfig(query)
onAction(MainAction.Search(query))
},
onSearchClose = {
searchQuery = ""
mainViewModel.filterConfig("")
onAction(MainAction.Search(""))
showSearch = false
},
onSearchToggle = { show: Boolean -> showSearch = show },
onMenuClick = { scope.launch { drawerState.open() } },
onImportQRcode = onImportQRcode,
onImportClipboard = onImportClipboard,
onImportLocal = onImportLocal,
onImportManually = onImportManually,
onRestartService = onRestartService,
onAction = onAction,
onDelAllConfig = { showDelAllConfirm = true },
onDelDuplicateConfig = { showDelDuplicateConfirm = true },
onDelInvalidConfig = { showDelInvalidConfirm = true },
onExportAll = onExportAll,
onRealPingAll = onRealPingAll,
onLocateSelectedServer = onLocateSelectedServer,
onSortByTestResults = onSortByTestResults,
onSubUpdate = onSubUpdate
onDelInvalidConfig = { showDelInvalidConfirm = true }
)
},
bottomBar = {
@@ -264,8 +228,7 @@ fun MainScreen(
displayText = displayText,
isRunning = isRunning,
isDarkTheme = isDarkTheme,
onTestClick = onTestClick,
onFabClick = onFabClick
onAction = onAction
)
},
floatingActionButton = {},
@@ -312,8 +275,8 @@ fun MainScreen(
searchQuery = searchQuery,
lazyListStates = lazyListStates,
lazyGridStates = lazyGridStates,
onSelectServer = onSelectServer,
onEditServer = onEditServer,
onSelectServer = { guid -> onAction(MainAction.SelectServer(guid)) },
onEditServer = { guid, profile -> onAction(MainAction.EditServer(guid, profile)) },
onShareServer = { guid, profile ->
shareTarget = Triple(guid, profile, false)
},
@@ -322,7 +285,7 @@ fun MainScreen(
},
onRemoveServer = { guid ->
if (confirmRemove) showRemoveConfirm = guid
else onRemoveServer(guid)
else onAction(MainAction.RemoveServer(guid))
},
contentPadding = PaddingValues(
start = 0.dp,
@@ -39,19 +39,10 @@ fun MainTopBar(
onSearchClose: () -> Unit,
onSearchToggle: (Boolean) -> Unit,
onMenuClick: () -> Unit,
onImportQRcode: () -> Unit,
onImportClipboard: () -> Unit,
onImportLocal: () -> Unit,
onImportManually: (Int) -> Unit,
onRestartService: () -> Unit,
onAction: (MainAction) -> Unit,
onDelAllConfig: () -> Unit,
onDelDuplicateConfig: () -> Unit,
onDelInvalidConfig: () -> Unit,
onExportAll: () -> Unit,
onRealPingAll: () -> Unit,
onLocateSelectedServer: () -> Unit,
onSortByTestResults: () -> Unit,
onSubUpdate: () -> Unit
onDelInvalidConfig: () -> Unit
) {
var showImportMenu by remember { mutableStateOf(false) }
var showMenu by remember { mutableStateOf(false) }
@@ -101,10 +92,10 @@ fun MainTopBar(
.verticalScrollbar(importMenuScrollState)
) {
ImportMenuContent(
onImportQRcode = { showImportMenu = false; onImportQRcode() },
onImportClipboard = { showImportMenu = false; onImportClipboard() },
onImportLocal = { showImportMenu = false; onImportLocal() },
onImportManually = { type -> showImportMenu = false; onImportManually(type) }
onAction = { action ->
showImportMenu = false
onAction(action)
}
)
}
}
@@ -122,15 +113,13 @@ fun MainTopBar(
.verticalScrollbar(moreMenuScrollState)
) {
MoreMenuContent(
onRestartService = { showMenu = false; onRestartService() },
onAction = { action ->
showMenu = false
onAction(action)
},
onDelAllConfig = { showMenu = false; onDelAllConfig() },
onDelDuplicateConfig = { showMenu = false; onDelDuplicateConfig() },
onDelInvalidConfig = { showMenu = false; onDelInvalidConfig() },
onExportAll = { showMenu = false; onExportAll() },
onRealPingAll = { showMenu = false; onRealPingAll() },
onLocateSelectedServer = { showMenu = false; onLocateSelectedServer() },
onSortByTestResults = { showMenu = false; onSortByTestResults() },
onSubUpdate = { showMenu = false; onSubUpdate() }
onDelInvalidConfig = { showMenu = false; onDelInvalidConfig() }
)
}
}
@@ -173,7 +173,6 @@ class MainViewModel(
MainAction.SortByTestResults -> sortByTestResultsAsync()
MainAction.UpdateSubscriptions -> importConfigViaSub()
MainAction.ExportAll -> exportAllAsync()
MainAction.ImportConfigViaSub -> importConfigViaSub()
is MainAction.SelectGroup -> subscriptionIdChanged(action.groupId)
is MainAction.SelectServer -> updateSelectedGuid(action.guid)
is MainAction.RemoveServer -> removeServerAndRefresh(action.guid)
@@ -181,9 +180,25 @@ class MainViewModel(
is MainAction.SwapServer -> swapServer(action.fromIndex, action.toIndex)
is MainAction.ImportBatchConfig -> importBatchConfig(action.configText)
is MainAction.LocateHandled -> consumeLocateTarget(action.target)
is MainAction.ShareQRCode -> {
val bitmap = dataSource.share2QRCode(action.guid)
_uiState.update { it.copy(shareQRCodeBitmap = bitmap) }
}
MainAction.DismissQRCodeDialog -> {
_uiState.update { it.copy(shareQRCodeBitmap = null) }
}
MainAction.ToggleService,
MainAction.TestCurrentServer -> {
// Platform-specific, handled by Activity
MainAction.TestCurrentServer,
MainAction.ImportQRcode,
MainAction.ImportClipboard,
MainAction.ImportConfigLocal,
is MainAction.ImportManually,
MainAction.RestartService,
MainAction.LocateSelectedServer,
is MainAction.EditServer,
is MainAction.ShareClipboard,
is MainAction.ShareFullContent -> {
// Handled by Activity via its onAction lambda
}
}
}