代码优化
This commit is contained in:
@@ -2,6 +2,7 @@ package io.legado.app.ui.main
|
||||
|
||||
import androidx.annotation.StringRes
|
||||
import io.legado.app.R
|
||||
import io.legado.app.ui.config.themeConfig.ThemeConfig
|
||||
import kotlinx.collections.immutable.persistentListOf
|
||||
|
||||
sealed class MainDestination(
|
||||
@@ -32,3 +33,11 @@ sealed class MainDestination(
|
||||
val mainDestinations = persistentListOf<MainDestination>(Bookshelf, Explore, Rss, My)
|
||||
}
|
||||
}
|
||||
|
||||
val MainDestination.customIconPath: String
|
||||
get() = when (this) {
|
||||
MainDestination.Bookshelf -> ThemeConfig.navIconBookshelf
|
||||
MainDestination.Explore -> ThemeConfig.navIconExplore
|
||||
MainDestination.Rss -> ThemeConfig.navIconRss
|
||||
MainDestination.My -> ThemeConfig.navIconMy
|
||||
}
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
package io.legado.app.ui.main
|
||||
|
||||
import android.content.Intent
|
||||
import android.net.Uri
|
||||
import android.os.Build
|
||||
import androidx.activity.ComponentActivity
|
||||
import androidx.appcompat.app.AppCompatActivity
|
||||
@@ -24,7 +23,6 @@ import androidx.compose.foundation.layout.fillMaxWidth
|
||||
import androidx.compose.foundation.layout.height
|
||||
import androidx.compose.foundation.layout.navigationBars
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.Image
|
||||
import androidx.compose.foundation.layout.size
|
||||
import androidx.compose.foundation.pager.HorizontalPager
|
||||
import androidx.compose.foundation.pager.rememberPagerState
|
||||
@@ -52,24 +50,22 @@ import androidx.compose.runtime.rememberCoroutineScope
|
||||
import androidx.compose.runtime.setValue
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.graphics.Color
|
||||
import androidx.compose.ui.hapticfeedback.HapticFeedbackType
|
||||
import androidx.compose.ui.platform.LocalContext
|
||||
import androidx.compose.ui.platform.LocalHapticFeedback
|
||||
import androidx.compose.ui.platform.testTag
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.semantics.contentDescription
|
||||
import androidx.compose.ui.semantics.semantics
|
||||
import androidx.compose.ui.semantics.testTagsAsResourceId
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.text.style.TextOverflow
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.compose.ui.graphics.Color
|
||||
import androidx.core.net.toUri
|
||||
import androidx.lifecycle.compose.collectAsStateWithLifecycle
|
||||
import coil.compose.AsyncImage
|
||||
import com.kyant.backdrop.backdrops.layerBackdrop
|
||||
import com.kyant.backdrop.backdrops.rememberLayerBackdrop
|
||||
import dev.chrisbanes.haze.HazeState
|
||||
import dev.chrisbanes.haze.hazeSource
|
||||
import androidx.compose.ui.graphics.asImageBitmap
|
||||
import androidx.compose.ui.graphics.painter.BitmapPainter
|
||||
import io.legado.app.R
|
||||
import io.legado.app.ui.config.themeConfig.ThemeConfig
|
||||
import io.legado.app.ui.main.bookshelf.BookshelfScreen
|
||||
@@ -78,7 +74,6 @@ import io.legado.app.ui.main.explore.ExploreScreen
|
||||
import io.legado.app.ui.main.my.MyScreen
|
||||
import io.legado.app.ui.main.my.PrefClickEvent
|
||||
import io.legado.app.ui.main.rss.RssScreen
|
||||
import io.legado.app.ui.theme.regularHazeEffect
|
||||
import io.legado.app.ui.widget.components.AppNavigationBar
|
||||
import io.legado.app.ui.widget.components.AppNavigationBarItem
|
||||
import io.legado.app.ui.widget.components.AppScaffold
|
||||
@@ -95,6 +90,7 @@ import io.legado.app.utils.sendToClip
|
||||
import io.legado.app.utils.showDialogFragment
|
||||
import io.legado.app.utils.startActivityForBook
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.flow.collectLatest
|
||||
import kotlinx.coroutines.launch
|
||||
import kotlinx.coroutines.withContext
|
||||
import org.koin.androidx.compose.koinViewModel
|
||||
@@ -124,20 +120,21 @@ fun MainScreen(
|
||||
val context = LocalContext.current
|
||||
val coroutineScope = rememberCoroutineScope()
|
||||
val mainUiState by viewModel.uiState.collectAsStateWithLifecycle()
|
||||
val defaultHelpTitle = stringResource(R.string.help)
|
||||
|
||||
LaunchedEffect(viewModel, context) {
|
||||
viewModel.effects.collect { effect ->
|
||||
viewModel.effects.collectLatest { effect ->
|
||||
when (effect) {
|
||||
is MainEffect.OpenUrl -> {
|
||||
context.startActivity(
|
||||
Intent(Intent.ACTION_VIEW, Uri.parse(effect.url))
|
||||
Intent(Intent.ACTION_VIEW, effect.url.toUri())
|
||||
)
|
||||
}
|
||||
|
||||
is MainEffect.CopyUrl -> context.sendToClip(effect.url)
|
||||
is MainEffect.ShowMarkdown -> {
|
||||
val activity = context as? AppCompatActivity ?: return@collect
|
||||
val title = effect.title.ifBlank { context.getString(R.string.help) }
|
||||
val activity = context as? AppCompatActivity ?: return@collectLatest
|
||||
val title = effect.title.ifBlank { defaultHelpTitle }
|
||||
val mdText = withContext(Dispatchers.IO) {
|
||||
context.assets
|
||||
.open("web/help/md/${effect.path}.md")
|
||||
@@ -288,12 +285,7 @@ fun MainScreen(
|
||||
}
|
||||
},
|
||||
label = if (labelVisibilityMode != "unlabeled") {
|
||||
val hasCustomIcon = when (destination) {
|
||||
MainDestination.Bookshelf -> ThemeConfig.navIconBookshelf.isNotEmpty()
|
||||
MainDestination.Explore -> ThemeConfig.navIconExplore.isNotEmpty()
|
||||
MainDestination.Rss -> ThemeConfig.navIconRss.isNotEmpty()
|
||||
MainDestination.My -> ThemeConfig.navIconMy.isNotEmpty()
|
||||
}
|
||||
val hasCustomIcon = destination.customIconPath.isNotEmpty()
|
||||
if (hasCustomIcon) null else {{ AppText(stringResource(destination.labelId)) }}
|
||||
} else null
|
||||
)
|
||||
@@ -308,12 +300,7 @@ fun MainScreen(
|
||||
AppNavigationBar() {
|
||||
destinations.forEachIndexed { index, destination ->
|
||||
val selected = pagerState.targetPage == index
|
||||
val customIconPath = when (destination) {
|
||||
MainDestination.Bookshelf -> ThemeConfig.navIconBookshelf
|
||||
MainDestination.Explore -> ThemeConfig.navIconExplore
|
||||
MainDestination.Rss -> ThemeConfig.navIconRss
|
||||
MainDestination.My -> ThemeConfig.navIconMy
|
||||
}
|
||||
val customIconPath = destination.customIconPath
|
||||
AppNavigationBarItem(
|
||||
modifier = Modifier.semantics(mergeDescendants = true) {
|
||||
contentDescription = "nav_${destination.route}"
|
||||
@@ -442,22 +429,12 @@ fun MainScreen(
|
||||
tabsCount = destinations.size,
|
||||
isBlurEnabled = useLiquidGlass,
|
||||
hasCustomIcons = destinations.any { dest ->
|
||||
when (dest) {
|
||||
MainDestination.Bookshelf -> ThemeConfig.navIconBookshelf.isNotEmpty()
|
||||
MainDestination.Explore -> ThemeConfig.navIconExplore.isNotEmpty()
|
||||
MainDestination.Rss -> ThemeConfig.navIconRss.isNotEmpty()
|
||||
MainDestination.My -> ThemeConfig.navIconMy.isNotEmpty()
|
||||
}
|
||||
dest.customIconPath.isNotEmpty()
|
||||
}
|
||||
) {
|
||||
destinations.forEachIndexed { index, destination ->
|
||||
val selected = pagerState.targetPage == index
|
||||
val hasCustomIcon = when (destination) {
|
||||
MainDestination.Bookshelf -> ThemeConfig.navIconBookshelf.isNotEmpty()
|
||||
MainDestination.Explore -> ThemeConfig.navIconExplore.isNotEmpty()
|
||||
MainDestination.Rss -> ThemeConfig.navIconRss.isNotEmpty()
|
||||
MainDestination.My -> ThemeConfig.navIconMy.isNotEmpty()
|
||||
}
|
||||
val hasCustomIcon = destination.customIconPath.isNotEmpty()
|
||||
FloatingBottomBarItem(
|
||||
onClick = {
|
||||
coroutineScope.launch {
|
||||
@@ -536,31 +513,13 @@ private fun NavigationIcon(
|
||||
selected: Boolean,
|
||||
modifier: Modifier = Modifier
|
||||
) {
|
||||
val customIconPath = when (destination) {
|
||||
MainDestination.Bookshelf -> ThemeConfig.navIconBookshelf
|
||||
MainDestination.Explore -> ThemeConfig.navIconExplore
|
||||
MainDestination.Rss -> ThemeConfig.navIconRss
|
||||
MainDestination.My -> ThemeConfig.navIconMy
|
||||
}
|
||||
val customIconPath = destination.customIconPath
|
||||
if (customIconPath.isNotEmpty()) {
|
||||
val context = LocalContext.current
|
||||
val bitmap = remember(customIconPath) {
|
||||
kotlin.runCatching {
|
||||
android.graphics.BitmapFactory.decodeFile(customIconPath)
|
||||
}.getOrNull()
|
||||
}
|
||||
if (bitmap != null) {
|
||||
Image(
|
||||
painter = remember(bitmap) {
|
||||
BitmapPainter(bitmap.asImageBitmap())
|
||||
},
|
||||
contentDescription = null,
|
||||
modifier = modifier.size(40.dp)
|
||||
)
|
||||
} else {
|
||||
val icon = AppIcons.mainDestination(destination, selected)
|
||||
AppIcon(icon, contentDescription = null, modifier = modifier)
|
||||
}
|
||||
AsyncImage(
|
||||
model = customIconPath,
|
||||
contentDescription = null,
|
||||
modifier = modifier.size(40.dp)
|
||||
)
|
||||
} else {
|
||||
val icon = AppIcons.mainDestination(destination, selected)
|
||||
AppIcon(icon, contentDescription = null, modifier = modifier)
|
||||
|
||||
@@ -2,9 +2,10 @@ package io.legado.app.ui.main
|
||||
|
||||
import android.app.Application
|
||||
import android.content.SharedPreferences
|
||||
import androidx.compose.runtime.Stable
|
||||
import io.legado.app.base.BaseViewModel
|
||||
import io.legado.app.constant.PreferKey
|
||||
import io.legado.app.constant.EventBus
|
||||
import io.legado.app.constant.PreferKey
|
||||
import io.legado.app.domain.usecase.AppStartupMaintenanceUseCase
|
||||
import io.legado.app.domain.usecase.WebDavBackupUseCase
|
||||
import io.legado.app.ui.config.themeConfig.ThemeConfig
|
||||
@@ -13,13 +14,13 @@ import io.legado.app.utils.defaultSharedPreferences
|
||||
import io.legado.app.utils.eventBus.FlowEventBus
|
||||
import io.legado.app.utils.getPrefBoolean
|
||||
import io.legado.app.utils.getPrefString
|
||||
import kotlinx.collections.immutable.ImmutableList
|
||||
import kotlinx.collections.immutable.toImmutableList
|
||||
import kotlinx.coroutines.flow.MutableSharedFlow
|
||||
import kotlinx.coroutines.flow.MutableStateFlow
|
||||
import kotlinx.coroutines.flow.asSharedFlow
|
||||
import kotlinx.coroutines.flow.asStateFlow
|
||||
import kotlinx.coroutines.flow.update
|
||||
import kotlinx.collections.immutable.ImmutableList
|
||||
import kotlinx.collections.immutable.toImmutableList
|
||||
|
||||
class MainViewModel(
|
||||
application: Application,
|
||||
@@ -133,6 +134,7 @@ sealed interface MainEffect {
|
||||
data object NavigateToReadRecord : MainEffect
|
||||
}
|
||||
|
||||
@Stable
|
||||
data class MainUiState(
|
||||
val destinations: ImmutableList<MainDestination> = MainDestination.mainDestinations,
|
||||
val defaultHomePage: String = "bookshelf",
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
package io.legado.app.ui.main.my
|
||||
|
||||
import android.app.Application
|
||||
import androidx.compose.runtime.Stable
|
||||
import androidx.lifecycle.viewModelScope
|
||||
import io.legado.app.base.BaseViewModel
|
||||
import io.legado.app.constant.EventBus
|
||||
@@ -12,6 +13,7 @@ import kotlinx.coroutines.flow.asStateFlow
|
||||
import kotlinx.coroutines.flow.update
|
||||
import kotlinx.coroutines.launch
|
||||
|
||||
@Stable
|
||||
data class MyUiState(
|
||||
val isWebServiceRun: Boolean = false,
|
||||
val webServiceAddress: String = ""
|
||||
|
||||
@@ -5,7 +5,6 @@ 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.PaddingValues
|
||||
import androidx.compose.foundation.layout.Spacer
|
||||
import androidx.compose.foundation.layout.fillMaxSize
|
||||
import androidx.compose.foundation.layout.fillMaxWidth
|
||||
@@ -18,27 +17,22 @@ import androidx.compose.foundation.lazy.grid.items
|
||||
import androidx.compose.foundation.shape.RoundedCornerShape
|
||||
import androidx.compose.material.icons.Icons
|
||||
import androidx.compose.material.icons.automirrored.filled.Login
|
||||
import androidx.compose.material.icons.automirrored.outlined.Label
|
||||
import androidx.compose.material.icons.filled.Close
|
||||
import androidx.compose.material.icons.filled.Delete
|
||||
import androidx.compose.material.icons.filled.Edit
|
||||
import androidx.compose.material.icons.filled.Group
|
||||
import androidx.compose.material.icons.filled.Settings
|
||||
import androidx.compose.material.icons.filled.Star
|
||||
import androidx.compose.material.icons.filled.Subscriptions
|
||||
import androidx.compose.material.icons.filled.VerticalAlignTop
|
||||
import androidx.compose.material3.AlertDialog
|
||||
import androidx.compose.material3.ExperimentalMaterial3Api
|
||||
import androidx.compose.material3.MaterialTheme
|
||||
import androidx.compose.material3.TextButton
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.LaunchedEffect
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.runtime.mutableStateOf
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.runtime.rememberUpdatedState
|
||||
import androidx.compose.runtime.setValue
|
||||
import androidx.compose.runtime.saveable.rememberSaveable
|
||||
import androidx.compose.runtime.setValue
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.draw.clip
|
||||
@@ -59,7 +53,6 @@ import io.legado.app.ui.theme.LegadoTheme
|
||||
import io.legado.app.ui.theme.adaptiveContentPadding
|
||||
import io.legado.app.ui.widget.components.SourceIcon
|
||||
import io.legado.app.ui.widget.components.alert.AppAlertDialog
|
||||
import io.legado.app.ui.widget.components.topbar.TopBarActionButton
|
||||
import io.legado.app.ui.widget.components.divider.PillDivider
|
||||
import io.legado.app.ui.widget.components.divider.PillHeaderDivider
|
||||
import io.legado.app.ui.widget.components.list.ListScaffold
|
||||
@@ -67,6 +60,7 @@ import io.legado.app.ui.widget.components.menuItem.MenuItemIcon
|
||||
import io.legado.app.ui.widget.components.menuItem.RoundDropdownMenu
|
||||
import io.legado.app.ui.widget.components.menuItem.RoundDropdownMenuItem
|
||||
import io.legado.app.ui.widget.components.text.AppText
|
||||
import io.legado.app.ui.widget.components.topbar.TopBarActionButton
|
||||
import io.legado.app.utils.openUrl
|
||||
import io.legado.app.utils.startActivity
|
||||
import kotlinx.coroutines.flow.collectLatest
|
||||
@@ -103,23 +97,35 @@ fun RssScreen(
|
||||
is RssEffect.OpenExternalUrl -> {
|
||||
currentContext.openUrl(effect.url)
|
||||
}
|
||||
|
||||
is RssEffect.OpenSourceEdit -> {
|
||||
currentContext.startActivity<RssSourceEditActivity> {
|
||||
putExtra("sourceUrl", effect.sourceUrl)
|
||||
}
|
||||
}
|
||||
|
||||
is RssEffect.Login -> {
|
||||
currentContext.startActivity<SourceLoginActivity> {
|
||||
putExtra("type", "rssSource")
|
||||
putExtra("key", effect.sourceUrl)
|
||||
}
|
||||
}
|
||||
|
||||
RssEffect.OpenRuleSub -> {
|
||||
currentContext.startActivity<RuleSubActivity>()
|
||||
}
|
||||
|
||||
RssEffect.OpenFavorites -> {
|
||||
currentContext.startActivity<RssFavoritesActivity>()
|
||||
}
|
||||
|
||||
RssEffect.OpenSourceManage -> {
|
||||
currentContext.startActivity<RssSourceActivity>()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
val edit: (RssSource) -> Unit = { rssSource ->
|
||||
context.startActivity<RssSourceEditActivity> {
|
||||
putExtra("sourceUrl", rssSource.sourceUrl)
|
||||
}
|
||||
}
|
||||
|
||||
val login: (RssSource) -> Unit = { rssSource ->
|
||||
context.startActivity<SourceLoginActivity> {
|
||||
putExtra("type", "rssSource")
|
||||
putExtra("key", rssSource.sourceUrl)
|
||||
}
|
||||
}
|
||||
|
||||
ListScaffold(
|
||||
title = stringResource(R.string.rss),
|
||||
state = uiState,
|
||||
@@ -130,12 +136,12 @@ fun RssScreen(
|
||||
searchPlaceholder = stringResource(R.string.search_rss_source),
|
||||
topBarActions = {
|
||||
TopBarActionButton(
|
||||
onClick = { context.startActivity<RuleSubActivity>() },
|
||||
onClick = { viewModel.openRuleSub() },
|
||||
imageVector = Icons.Default.Subscriptions,
|
||||
contentDescription = stringResource(R.string.rule_subscription)
|
||||
)
|
||||
TopBarActionButton(
|
||||
onClick = { context.startActivity<RssFavoritesActivity>() },
|
||||
onClick = { viewModel.openFavorites() },
|
||||
imageVector = Icons.Default.Star,
|
||||
contentDescription = stringResource(R.string.favorite)
|
||||
)
|
||||
@@ -143,7 +149,7 @@ fun RssScreen(
|
||||
dropDownMenuContent = { dismiss ->
|
||||
RoundDropdownMenuItem(
|
||||
onClick = {
|
||||
context.startActivity<RssSourceActivity>()
|
||||
viewModel.openSourceManage()
|
||||
dismiss()
|
||||
},
|
||||
text = stringResource(R.string.rss_feed_management),
|
||||
@@ -183,10 +189,10 @@ fun RssScreen(
|
||||
source = source,
|
||||
onClick = { viewModel.openSource(source) },
|
||||
onTop = { viewModel.topSource(source) },
|
||||
onEdit = { edit(source) },
|
||||
onEdit = { viewModel.openSourceEdit(source) },
|
||||
onDelete = { sourceToDeleteUrl = source.sourceUrl },
|
||||
onDisable = { viewModel.disable(source) },
|
||||
onLogin = { login(source) }
|
||||
onLogin = { viewModel.login(source) }
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
package io.legado.app.ui.main.rss
|
||||
|
||||
import androidx.compose.runtime.Stable
|
||||
import io.legado.app.data.entities.RssSource
|
||||
import io.legado.app.ui.widget.components.list.ListUiState
|
||||
import kotlinx.collections.immutable.ImmutableList
|
||||
@@ -7,6 +8,7 @@ import kotlinx.collections.immutable.ImmutableSet
|
||||
import kotlinx.collections.immutable.persistentListOf
|
||||
import kotlinx.collections.immutable.persistentSetOf
|
||||
|
||||
@Stable
|
||||
data class RssUiState(
|
||||
override val items: ImmutableList<RssSource> = persistentListOf(),
|
||||
override val selectedIds: ImmutableSet<String> = persistentSetOf(),
|
||||
|
||||
@@ -116,6 +116,26 @@ class RssViewModel(
|
||||
}
|
||||
}
|
||||
|
||||
fun openSourceEdit(rssSource: RssSource) {
|
||||
_effects.tryEmit(RssEffect.OpenSourceEdit(rssSource.sourceUrl))
|
||||
}
|
||||
|
||||
fun login(rssSource: RssSource) {
|
||||
_effects.tryEmit(RssEffect.Login(rssSource.sourceUrl))
|
||||
}
|
||||
|
||||
fun openRuleSub() {
|
||||
_effects.tryEmit(RssEffect.OpenRuleSub)
|
||||
}
|
||||
|
||||
fun openFavorites() {
|
||||
_effects.tryEmit(RssEffect.OpenFavorites)
|
||||
}
|
||||
|
||||
fun openSourceManage() {
|
||||
_effects.tryEmit(RssEffect.OpenSourceManage)
|
||||
}
|
||||
|
||||
fun openSource(rssSource: RssSource) {
|
||||
if (!rssSource.singleUrl) {
|
||||
_effects.tryEmit(RssEffect.OpenSort(rssSource.sourceUrl, null, null))
|
||||
@@ -186,4 +206,9 @@ sealed interface RssEffect {
|
||||
) : RssEffect
|
||||
|
||||
data class OpenExternalUrl(val url: String) : RssEffect
|
||||
data class OpenSourceEdit(val sourceUrl: String) : RssEffect
|
||||
data class Login(val sourceUrl: String) : RssEffect
|
||||
data object OpenRuleSub : RssEffect
|
||||
data object OpenFavorites : RssEffect
|
||||
data object OpenSourceManage : RssEffect
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user