HapeLee 10 месяцев назад
Родитель
Сommit
bdb7c77442

+ 4 - 0
app/src/main/java/io/legado/app/service/WebService.kt

@@ -16,6 +16,7 @@ import io.legado.app.constant.NotificationId
 import io.legado.app.constant.PreferKey
 import io.legado.app.receiver.NetworkChangedListener
 import io.legado.app.utils.NetworkUtils
+import io.legado.app.utils.eventBus.FlowEventBus
 import io.legado.app.utils.getPrefBoolean
 import io.legado.app.utils.getPrefInt
 import io.legado.app.utils.postEvent
@@ -108,6 +109,7 @@ class WebService : BaseService() {
             }
             startForegroundNotification()
             postEvent(EventBus.WEB_SERVICE, hostAddress)
+            FlowEventBus.post(EventBus.WEB_SERVICE, hostAddress)
         }
     }
 
@@ -141,6 +143,7 @@ class WebService : BaseService() {
             webSocketServer?.stop()
         }
         postEvent(EventBus.WEB_SERVICE, "")
+        FlowEventBus.post(EventBus.WEB_SERVICE, "")
         upTile(false)
     }
 
@@ -170,6 +173,7 @@ class WebService : BaseService() {
                 hostAddress = notificationList.first()
                 isRun = true
                 postEvent(EventBus.WEB_SERVICE, hostAddress)
+                FlowEventBus.post(EventBus.WEB_SERVICE, hostAddress)
                 startForegroundNotification()
             } catch (e: IOException) {
                 toastOnUi(e.localizedMessage ?: "")

+ 18 - 32
app/src/main/java/io/legado/app/ui/main/my/MyScreen.kt

@@ -24,7 +24,6 @@ import androidx.compose.material.icons.filled.Settings
 import androidx.compose.material.icons.filled.Source
 import androidx.compose.material.icons.filled.Translate
 import androidx.compose.material.icons.filled.Web
-import androidx.compose.material3.DropdownMenu
 import androidx.compose.material3.DropdownMenuItem
 import androidx.compose.material3.ExperimentalMaterial3Api
 import androidx.compose.material3.Icon
@@ -37,9 +36,6 @@ import androidx.compose.material3.TopAppBarDefaults
 import androidx.compose.runtime.Composable
 import androidx.compose.runtime.collectAsState
 import androidx.compose.runtime.getValue
-import androidx.compose.runtime.mutableStateOf
-import androidx.compose.runtime.remember
-import androidx.compose.runtime.setValue
 import androidx.compose.ui.Modifier
 import androidx.compose.ui.input.nestedscroll.nestedScroll
 import androidx.compose.ui.res.stringResource
@@ -67,7 +63,6 @@ fun MyScreen(
 ) {
 
     val uiState by viewModel.uiState.collectAsState()
-    var showMenu by remember { mutableStateOf(false) }
     val scrollBehavior = TopAppBarDefaults.exitUntilCollapsedScrollBehavior()
 
     Scaffold(
@@ -126,35 +121,26 @@ fun MyScreen(
                             }
                         )
                     },
-                    onClick = {
-                        viewModel.onEvent(PrefClickEvent.ToggleWebService)
-                    },
-                    onLongClick = {
-                        if (uiState.isWebServiceRun) {
-                            showMenu = true
+                    dropdownMenu = if (uiState.isWebServiceRun) {
+                        { onDismiss ->
+                            DropdownMenuItem(
+                                text = { Text("复制地址") },
+                                onClick = {
+                                    onNavigate(PrefClickEvent.CopyUrl(uiState.webServiceAddress))
+                                    onDismiss()
+                                }
+                            )
+                            DropdownMenuItem(
+                                text = { Text("浏览器打开") },
+                                onClick = {
+                                    onNavigate(PrefClickEvent.OpenUrl(uiState.webServiceAddress))
+                                    onDismiss()
+                                }
+                            )
                         }
-                    }
+                    } else null,
+                    onClick = { }
                 )
-
-                DropdownMenu(
-                    expanded = showMenu,
-                    onDismissRequest = { showMenu = false }
-                ) {
-                    DropdownMenuItem(
-                        text = { Text("复制地址") },
-                        onClick = {
-                            onNavigate(PrefClickEvent.CopyUrl(uiState.webServiceAddress))
-                            showMenu = false
-                        }
-                    )
-                    DropdownMenuItem(
-                        text = { Text("浏览器打开") },
-                        onClick = {
-                            onNavigate(PrefClickEvent.OpenUrl(uiState.webServiceAddress))
-                            showMenu = false
-                        }
-                    )
-                }
             }
 
             SplicedColumnGroup(

+ 24 - 20
app/src/main/java/io/legado/app/ui/main/my/MyViewModel.kt

@@ -2,10 +2,15 @@ package io.legado.app.ui.main.my
 
 import android.app.Application
 import androidx.lifecycle.AndroidViewModel
+import androidx.lifecycle.viewModelScope
+import io.legado.app.constant.EventBus
 import io.legado.app.service.WebService
+import io.legado.app.utils.eventBus.FlowEventBus
 import kotlinx.coroutines.flow.MutableStateFlow
 import kotlinx.coroutines.flow.StateFlow
 import kotlinx.coroutines.flow.asStateFlow
+import kotlinx.coroutines.flow.update
+import kotlinx.coroutines.launch
 
 data class MyUiState(
     val isWebServiceRun: Boolean = false,
@@ -17,7 +22,6 @@ sealed class PrefClickEvent {
     data class CopyUrl(val url: String) : PrefClickEvent()
     data class ShowMd(val title: String, val path: String) : PrefClickEvent()
     data class StartActivity(val destination: Class<*>, val configTag: String? = null) : PrefClickEvent()
-    object ShowWebServiceMenu : PrefClickEvent()
     object ToggleWebService : PrefClickEvent()
     object ExitApp : PrefClickEvent()
 }
@@ -29,40 +33,40 @@ class MyViewModel(
     private val _uiState = MutableStateFlow(
         MyUiState(
             isWebServiceRun = WebService.isRun,
-            webServiceAddress = if (WebService.isRun) {
-                WebService.hostAddress
-            } else ""
+            webServiceAddress = WebService.hostAddress
         )
     )
     val uiState: StateFlow<MyUiState> = _uiState.asStateFlow()
 
+    init {
+        viewModelScope.launch {
+            FlowEventBus.with<String>(EventBus.WEB_SERVICE)
+                .collect { address ->
+                    _uiState.update { state ->
+                        state.copy(
+                            isWebServiceRun = address.isNotEmpty(),
+                            webServiceAddress = address
+                        )
+                    }
+                }
+        }
+    }
+
     fun onEvent(event: PrefClickEvent) {
         when (event) {
-
             PrefClickEvent.ToggleWebService -> {
-                val newRun = !_uiState.value.isWebServiceRun
-                if (newRun) {
+                val currentIsRun = _uiState.value.isWebServiceRun
+
+                if (!currentIsRun) {
                     WebService.start(getApplication())
                 } else {
                     WebService.stop(getApplication())
+                    _uiState.update { it.copy(isWebServiceRun = false, webServiceAddress = "") }
                 }
-                updateWebServiceState()
-            }
 
-            PrefClickEvent.ShowWebServiceMenu -> {
-                // UI 自己处理 showMenu
             }
-
             else -> Unit
         }
     }
 
-    private fun updateWebServiceState() {
-        _uiState.value = MyUiState(
-            isWebServiceRun = WebService.isRun,
-            webServiceAddress = if (WebService.isRun) {
-                WebService.hostAddress
-            } else ""
-        )
-    }
 }

+ 136 - 128
app/src/main/java/io/legado/app/ui/replace/ReplaceRuleScreen.kt

@@ -57,6 +57,7 @@ import io.legado.app.data.repository.UploadRepository
 import io.legado.app.ui.replace.edit.ReplaceEditActivity
 import io.legado.app.ui.widget.components.ActionItem
 import io.legado.app.ui.widget.components.AnimatedText
+import io.legado.app.ui.widget.components.EmptyMessageView
 import io.legado.app.ui.widget.components.lazylist.FastScrollLazyColumn
 import io.legado.app.ui.widget.components.SearchBarSection
 import io.legado.app.ui.widget.components.SelectionBottomBar
@@ -494,139 +495,146 @@ fun ReplaceRuleScreen(
                 .padding(padding)
                 .fillMaxSize()
         ) {
-            FastScrollLazyColumn(
-                state = listState,
-                modifier = Modifier
-                    .fillMaxSize(),
-                contentPadding = PaddingValues(
-                    top = 8.dp,
-                    bottom = 120.dp
-                ),
-                verticalArrangement = Arrangement.spacedBy(8.dp)
-            ) {
-                items(rules, key = { it.id }) { ui ->
-                    val isSelected = selectedRuleIds.contains(ui.id)
-                    ReorderableItem(
-                        state = reorderableState,
-                        key = ui.id
-                    ) { isDragging ->
-
-                        val elevation by animateDpAsState(if (isDragging) 8.dp else 0.dp)
-                        ReplaceRuleItem(
-                            modifier = Modifier
-                                .padding(horizontal = 12.dp)
-                                .zIndex(if (isDragging) 1f else 0f)
-                                .shadow(
-                                    elevation = elevation,
-                                    shape = MaterialTheme.shapes.medium,
-                                    clip = false
-                                )
-                                .then(
-                                    if (canReorder) {
-                                        Modifier.longPressDraggableHandle(
-                                            onDragStarted = {
-                                                hapticFeedback.performHapticFeedback(HapticFeedbackType.GestureThresholdActivate)
-                                            },
-                                            onDragStopped = {
-                                                hapticFeedback.performHapticFeedback(HapticFeedbackType.GestureEnd)
-                                            },
-                                            interactionSource = remember { MutableInteractionSource() }
-                                        )
-                                    } else {
-                                        Modifier
-                                    }
-                                )
-                                .animateItem(),
-                            name = ui.name,
-                            isEnabled = ui.isEnabled,
-                            isSelected = isSelected,
-                            inSelectionMode = inSelectionMode,
-                            onEnabledChange = { enabled ->
-                                viewModel.update(ui.rule.copy(isEnabled = enabled))
-                            },
-                            onDelete = { showDeleteRuleDialog = ui.rule },
-                            onToTop = { viewModel.toTop(ui.rule) },
-                            onToBottom = { viewModel.toBottom(ui.rule) },
-                            onToggleSelection = {
-                                viewModel.toggleSelection(ui.id)
-                            },
-                            onClickEdit = {
-                                context.startActivity(
-                                    ReplaceEditActivity.startIntent(context, ui.id)
-                                )
-                            }
-                        )
-                    }
-                }
-            }
-            if (inSelectionMode) {
-                DraggableSelectionHandler(
-                    listState = listState,
-                    rules = rules,
-                    selectedRuleIds = selectedRuleIds,
-                    onSelectionChange = viewModel::setSelection,
-                    haptic = haptic,
-                    modifier = Modifier
-                        .fillMaxHeight()
-                        .width(60.dp)
-                        .align(Alignment.TopStart)
+            if (rules.isEmpty()) {
+                EmptyMessageView(
+                    modifier = Modifier.fillMaxSize(),
+                    message = "没有替换规则!"
                 )
-            }
-            AnimatedVisibility(
-                visible = inSelectionMode,
-                modifier =
-                    Modifier.align(Alignment.BottomCenter).offset(y = -ScreenOffset).zIndex(1f),
-                enter = slideInVertically { it } + fadeIn(),
-                exit = slideOutVertically { it } + fadeOut()
-            ) {
-                SelectionBottomBar(
-                    onSelectAll = {
-                        viewModel.setSelection(rules.map { it.id }.toSet())
-                    },
-                    onSelectInvert = {
-                        val allIds = rules.map { it.id }.toSet()
-                        viewModel.setSelection(allIds - selectedRuleIds)
-                    },
-                    primaryAction = ActionItem(
-                        text = stringResource(R.string.delete),
-                        icon = { Icon(Icons.Default.Delete, null) },
-                        onClick = { showDeleteSelectedDialog = true }
+            } else {
+                FastScrollLazyColumn(
+                    state = listState,
+                    modifier = Modifier
+                        .fillMaxSize(),
+                    contentPadding = PaddingValues(
+                        top = 8.dp,
+                        bottom = 120.dp
                     ),
-                    secondaryActions = listOf(
-                        ActionItem(
-                            text = stringResource(R.string.enable),
-                            onClick = {
-                                viewModel.enableSelectionByIds(selectedRuleIds)
-                                viewModel.setSelection(emptySet())
-                            }
-                        ),
-                        ActionItem(
-                            text = stringResource(R.string.disable_selection),
-                            onClick = {
-                                viewModel.disableSelectionByIds(selectedRuleIds)
-                                viewModel.setSelection(emptySet())
-                            }
-                        ),
-                        ActionItem(
-                            text = stringResource(R.string.to_top),
-                            onClick = {
-                                viewModel.topSelectByIds(selectedRuleIds)
-                                viewModel.setSelection(emptySet())
-                            }
-                        ),
-                        ActionItem(
-                            text = stringResource(R.string.to_bottom),
-                            onClick = {
-                                viewModel.bottomSelectByIds(selectedRuleIds)
-                                viewModel.setSelection(emptySet())
-                            }
+                    verticalArrangement = Arrangement.spacedBy(8.dp)
+                ) {
+                    items(rules, key = { it.id }) { ui ->
+                        val isSelected = selectedRuleIds.contains(ui.id)
+                        ReorderableItem(
+                            state = reorderableState,
+                            key = ui.id
+                        ) { isDragging ->
+
+                            val elevation by animateDpAsState(if (isDragging) 8.dp else 0.dp)
+                            ReplaceRuleItem(
+                                modifier = Modifier
+                                    .padding(horizontal = 12.dp)
+                                    .zIndex(if (isDragging) 1f else 0f)
+                                    .shadow(
+                                        elevation = elevation,
+                                        shape = MaterialTheme.shapes.medium,
+                                        clip = false
+                                    )
+                                    .then(
+                                        if (canReorder) {
+                                            Modifier.longPressDraggableHandle(
+                                                onDragStarted = {
+                                                    hapticFeedback.performHapticFeedback(HapticFeedbackType.GestureThresholdActivate)
+                                                },
+                                                onDragStopped = {
+                                                    hapticFeedback.performHapticFeedback(HapticFeedbackType.GestureEnd)
+                                                },
+                                                interactionSource = remember { MutableInteractionSource() }
+                                            )
+                                        } else {
+                                            Modifier
+                                        }
+                                    )
+                                    .animateItem(),
+                                name = ui.name,
+                                isEnabled = ui.isEnabled,
+                                isSelected = isSelected,
+                                inSelectionMode = inSelectionMode,
+                                onEnabledChange = { enabled ->
+                                    viewModel.update(ui.rule.copy(isEnabled = enabled))
+                                },
+                                onDelete = { showDeleteRuleDialog = ui.rule },
+                                onToTop = { viewModel.toTop(ui.rule) },
+                                onToBottom = { viewModel.toBottom(ui.rule) },
+                                onToggleSelection = {
+                                    viewModel.toggleSelection(ui.id)
+                                },
+                                onClickEdit = {
+                                    context.startActivity(
+                                        ReplaceEditActivity.startIntent(context, ui.id)
+                                    )
+                                }
+                            )
+                        }
+                    }
+                }
+                if (inSelectionMode) {
+                    DraggableSelectionHandler(
+                        listState = listState,
+                        rules = rules,
+                        selectedRuleIds = selectedRuleIds,
+                        onSelectionChange = viewModel::setSelection,
+                        haptic = haptic,
+                        modifier = Modifier
+                            .fillMaxHeight()
+                            .width(60.dp)
+                            .align(Alignment.TopStart)
+                    )
+                }
+                AnimatedVisibility(
+                    visible = inSelectionMode,
+                    modifier =
+                        Modifier.align(Alignment.BottomCenter).offset(y = -ScreenOffset).zIndex(1f),
+                    enter = slideInVertically { it } + fadeIn(),
+                    exit = slideOutVertically { it } + fadeOut()
+                ) {
+                    SelectionBottomBar(
+                        onSelectAll = {
+                            viewModel.setSelection(rules.map { it.id }.toSet())
+                        },
+                        onSelectInvert = {
+                            val allIds = rules.map { it.id }.toSet()
+                            viewModel.setSelection(allIds - selectedRuleIds)
+                        },
+                        primaryAction = ActionItem(
+                            text = stringResource(R.string.delete),
+                            icon = { Icon(Icons.Default.Delete, null) },
+                            onClick = { showDeleteSelectedDialog = true }
                         ),
-                        ActionItem(
-                            text = stringResource(R.string.export),
-                            onClick = { showFilePickerSheet = true }
+                        secondaryActions = listOf(
+                            ActionItem(
+                                text = stringResource(R.string.enable),
+                                onClick = {
+                                    viewModel.enableSelectionByIds(selectedRuleIds)
+                                    viewModel.setSelection(emptySet())
+                                }
+                            ),
+                            ActionItem(
+                                text = stringResource(R.string.disable_selection),
+                                onClick = {
+                                    viewModel.disableSelectionByIds(selectedRuleIds)
+                                    viewModel.setSelection(emptySet())
+                                }
+                            ),
+                            ActionItem(
+                                text = stringResource(R.string.to_top),
+                                onClick = {
+                                    viewModel.topSelectByIds(selectedRuleIds)
+                                    viewModel.setSelection(emptySet())
+                                }
+                            ),
+                            ActionItem(
+                                text = stringResource(R.string.to_bottom),
+                                onClick = {
+                                    viewModel.bottomSelectByIds(selectedRuleIds)
+                                    viewModel.setSelection(emptySet())
+                                }
+                            ),
+                            ActionItem(
+                                text = stringResource(R.string.export),
+                                onClick = { showFilePickerSheet = true }
+                            )
                         )
                     )
-                )
+                }
             }
         }
     }

+ 68 - 55
app/src/main/java/io/legado/app/ui/widget/components/SettingItem.kt

@@ -1,34 +1,30 @@
 package io.legado.app.ui.widget.components
 
-import androidx.compose.foundation.background
-import androidx.compose.foundation.clickable
 import androidx.compose.foundation.combinedClickable
 import androidx.compose.foundation.layout.Arrangement
 import androidx.compose.foundation.layout.Box
 import androidx.compose.foundation.layout.Column
-import androidx.compose.foundation.layout.Row
 import androidx.compose.foundation.layout.fillMaxWidth
-import androidx.compose.foundation.layout.heightIn
 import androidx.compose.foundation.layout.padding
-import androidx.compose.foundation.layout.size
-import androidx.compose.foundation.layout.width
 import androidx.compose.foundation.shape.RoundedCornerShape
 import androidx.compose.material.icons.Icons
 import androidx.compose.material.icons.filled.ChevronRight
 import androidx.compose.material.icons.filled.Info
+import androidx.compose.material3.DropdownMenu
 import androidx.compose.material3.Icon
 import androidx.compose.material3.ListItem
 import androidx.compose.material3.ListItemDefaults
 import androidx.compose.material3.MaterialTheme
-import androidx.compose.material3.MaterialTheme.colorScheme
 import androidx.compose.material3.Text
 import androidx.compose.runtime.Composable
-import androidx.compose.ui.Alignment
+import androidx.compose.runtime.getValue
+import androidx.compose.runtime.mutableStateOf
+import androidx.compose.runtime.remember
+import androidx.compose.runtime.setValue
 import androidx.compose.ui.Modifier
 import androidx.compose.ui.draw.clip
 import androidx.compose.ui.graphics.painter.Painter
 import androidx.compose.ui.graphics.vector.ImageVector
-import androidx.compose.ui.text.font.FontWeight
 import androidx.compose.ui.text.style.TextOverflow
 import androidx.compose.ui.tooling.preview.Preview
 import androidx.compose.ui.unit.dp
@@ -101,65 +97,82 @@ fun SettingItem(
     description: String? = null,
     option: String? = null,
     trailingContent: @Composable (() -> Unit)? = null,
+    dropdownMenu: (@Composable (onDismiss: () -> Unit) -> Unit)? = null,
     onClick: () -> Unit,
     onLongClick: (() -> Unit)? = null,
 ) {
-    ListItem(
-        modifier = modifier
-            .clip(RoundedCornerShape(4.dp))
-            .combinedClickable(
-                onClick = onClick,
-                onLongClick = onLongClick
-            ),
-        leadingContent = {
-            when {
-                painter != null -> {
-                    Icon(
+    var showMenu by remember { mutableStateOf(false) }
+
+    Box ( modifier = Modifier.fillMaxWidth() ){
+        ListItem(
+            modifier = modifier
+                .clip(RoundedCornerShape(4.dp))
+                .combinedClickable(
+                    onClick = onClick,
+                    onLongClick = {
+                        if (dropdownMenu != null) {
+                            showMenu = true
+                            onLongClick?.invoke()
+                        }
+                    }
+                ),
+            leadingContent = {
+                when {
+                    painter != null -> Icon(
                         painter = painter,
                         contentDescription = null,
                         tint = MaterialTheme.colorScheme.onSurfaceVariant
                     )
-                }
-
-                imageVector != null -> {
-                    Icon(
+                    imageVector != null -> Icon(
                         imageVector = imageVector,
                         contentDescription = null,
                         tint = MaterialTheme.colorScheme.onSurfaceVariant
                     )
                 }
-            }
-        },
-        headlineContent = {
-            Text(
-                text = title,
-                style = MaterialTheme.typography.titleMedium
-            )
-        },
-        supportingContent = {
-            Column(verticalArrangement = Arrangement.spacedBy(2.dp)) {
-                description?.let {
-                    Text(
-                        text = it,
-                        style = MaterialTheme.typography.bodySmall,
-                        color = MaterialTheme.colorScheme.onSurfaceVariant
-                    )
+            },
+            headlineContent = {
+                Text(
+                    text = title,
+                    style = MaterialTheme.typography.titleMedium
+                )
+            },
+            supportingContent = {
+                Column(
+                    verticalArrangement = Arrangement.spacedBy(2.dp)
+                ) {
+                    description?.let {
+                        Text(
+                            text = it,
+                            style = MaterialTheme.typography.bodySmall,
+                            color = MaterialTheme.colorScheme.onSurfaceVariant
+                        )
+                    }
+                    option?.let {
+                        Text(
+                            text = it,
+                            style = MaterialTheme.typography.labelMedium,
+                            color = MaterialTheme.colorScheme.primary,
+                            maxLines = 3,
+                            overflow = TextOverflow.Ellipsis
+                        )
+                    }
                 }
+            },
+            trailingContent = trailingContent,
+            colors = ListItemDefaults.colors(
+                containerColor = MaterialTheme.colorScheme.surfaceContainerLow
+            )
+        )
 
-                option?.let {
-                    AnimatedTextLine(
-                        text = it,
-                        style = MaterialTheme.typography.labelMedium,
-                        color = MaterialTheme.colorScheme.primary,
-                        maxLines = 3,
-                        overflow = TextOverflow.Ellipsis
-                    )
+        if (dropdownMenu != null) {
+            DropdownMenu(
+                expanded = showMenu,
+                onDismissRequest = { showMenu = false }
+            ) {
+                dropdownMenu {
+                    showMenu = false
                 }
             }
-        },
-        trailingContent = trailingContent,
-        colors = ListItemDefaults.colors(
-            containerColor = MaterialTheme.colorScheme.surfaceContainerLow
-        )
-    )
-}
+        }
+    }
+}

+ 31 - 0
app/src/main/java/io/legado/app/utils/eventBus/FlowEventBus.kt

@@ -0,0 +1,31 @@
+package io.legado.app.utils.eventBus
+
+import kotlinx.coroutines.MainScope
+import kotlinx.coroutines.channels.BufferOverflow
+import kotlinx.coroutines.flow.MutableSharedFlow
+import kotlinx.coroutines.launch
+import java.util.concurrent.ConcurrentHashMap
+
+object FlowEventBus {
+    // 核心存储结构:Tag -> SharedFlow
+    private val events = ConcurrentHashMap<String, MutableSharedFlow<Any>>()
+
+    // 获取或创建对应 Tag 的 Flow
+    @Suppress("UNCHECKED_CAST")
+    fun <T> with(tag: String): MutableSharedFlow<T> {
+        return events.getOrPut(tag) {
+            MutableSharedFlow<Any>(
+                replay = 1, // 相当于 LiveData 的粘性,确保新订阅者能收到最后一次通知
+                extraBufferCapacity = 64,
+                onBufferOverflow = BufferOverflow.DROP_OLDEST
+            )
+        } as MutableSharedFlow<T>
+    }
+
+    // 快捷发送
+    fun post(tag: String, value: Any) {
+        MainScope().launch {
+            with<Any>(tag).emit(value)
+        }
+    }
+}

+ 22 - 0
app/src/main/java/io/legado/app/utils/eventBus/FlowEventBusExt.kt

@@ -0,0 +1,22 @@
+package io.legado.app.utils.eventBus
+
+import io.legado.app.utils.eventBus.FlowEventBus.with
+import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.Dispatchers
+import kotlinx.coroutines.launch
+
+class FlowEventBusExt {
+
+    // 在协程中直接调用
+    suspend fun <T> post(tag: String, value: T) {
+        with<T>(tag).emit(value)
+    }
+
+    // 在非协程环境调用
+    fun <T> postSync(tag: String, value: T) {
+        CoroutineScope(Dispatchers.Main).launch {
+            with<T>(tag).emit(value)
+        }
+    }
+
+}