[优化] 修复和优化了一些问题
This commit is contained in:
13 files changed
+79
-99
No files matched your search
@@ -38,6 +38,5 @@ object EventBus {
|
|||||||
const val REFRESH_BOOK_CONTENT = "refreshBookContent"
|
const val REFRESH_BOOK_CONTENT = "refreshBookContent"
|
||||||
const val UP_TOC = "upToc"
|
const val UP_TOC = "upToc"
|
||||||
|
|
||||||
const val UP_BOOKSHELF_COUNT = "upBookshelfCount"
|
|
||||||
const val UP_ALL_BOOK_TOC = "upAllBookToc"
|
const val UP_ALL_BOOK_TOC = "upAllBookToc"
|
||||||
}
|
}
|
||||||
@@ -106,7 +106,7 @@ val appModule = module {
|
|||||||
ReplaceEditViewModel(
|
ReplaceEditViewModel(
|
||||||
app = get(),
|
app = get(),
|
||||||
replaceRuleDao = get(),
|
replaceRuleDao = get(),
|
||||||
savedStateHandle = get()
|
route = route
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -4,7 +4,6 @@ import androidx.compose.animation.AnimatedContent
|
|||||||
import androidx.compose.animation.AnimatedVisibility
|
import androidx.compose.animation.AnimatedVisibility
|
||||||
import androidx.compose.animation.core.animateFloatAsState
|
import androidx.compose.animation.core.animateFloatAsState
|
||||||
import androidx.compose.animation.core.tween
|
import androidx.compose.animation.core.tween
|
||||||
import androidx.compose.foundation.background
|
|
||||||
import androidx.compose.foundation.basicMarquee
|
import androidx.compose.foundation.basicMarquee
|
||||||
import androidx.compose.foundation.clickable
|
import androidx.compose.foundation.clickable
|
||||||
import androidx.compose.foundation.layout.Arrangement
|
import androidx.compose.foundation.layout.Arrangement
|
||||||
@@ -31,15 +30,12 @@ import androidx.compose.material.icons.filled.Merge
|
|||||||
import androidx.compose.material.icons.filled.Schedule
|
import androidx.compose.material.icons.filled.Schedule
|
||||||
import androidx.compose.material.icons.filled.Search
|
import androidx.compose.material.icons.filled.Search
|
||||||
import androidx.compose.material.icons.filled.Timeline
|
import androidx.compose.material.icons.filled.Timeline
|
||||||
import androidx.compose.material3.AlertDialog
|
|
||||||
import androidx.compose.material3.Checkbox
|
|
||||||
import androidx.compose.material3.ExperimentalMaterial3Api
|
import androidx.compose.material3.ExperimentalMaterial3Api
|
||||||
import androidx.compose.material3.ExperimentalMaterial3ExpressiveApi
|
import androidx.compose.material3.ExperimentalMaterial3ExpressiveApi
|
||||||
import androidx.compose.material3.MaterialTheme
|
import androidx.compose.material3.MaterialTheme
|
||||||
import androidx.compose.material3.SnackbarHost
|
import androidx.compose.material3.SnackbarHost
|
||||||
import androidx.compose.material3.SnackbarHostState
|
import androidx.compose.material3.SnackbarHostState
|
||||||
import androidx.compose.material3.Surface
|
import androidx.compose.material3.Surface
|
||||||
import androidx.compose.material3.TextButton
|
|
||||||
import androidx.compose.runtime.Composable
|
import androidx.compose.runtime.Composable
|
||||||
import androidx.compose.runtime.LaunchedEffect
|
import androidx.compose.runtime.LaunchedEffect
|
||||||
import androidx.compose.runtime.collectAsState
|
import androidx.compose.runtime.collectAsState
|
||||||
@@ -52,7 +48,6 @@ import androidx.compose.runtime.rememberCoroutineScope
|
|||||||
import androidx.compose.runtime.setValue
|
import androidx.compose.runtime.setValue
|
||||||
import androidx.compose.ui.Alignment
|
import androidx.compose.ui.Alignment
|
||||||
import androidx.compose.ui.Modifier
|
import androidx.compose.ui.Modifier
|
||||||
import androidx.compose.ui.draw.clip
|
|
||||||
import androidx.compose.ui.draw.drawBehind
|
import androidx.compose.ui.draw.drawBehind
|
||||||
import androidx.compose.ui.draw.drawWithContent
|
import androidx.compose.ui.draw.drawWithContent
|
||||||
import androidx.compose.ui.draw.rotate
|
import androidx.compose.ui.draw.rotate
|
||||||
@@ -80,7 +75,6 @@ import io.legado.app.ui.widget.components.AppScaffold
|
|||||||
import io.legado.app.ui.widget.components.EmptyMessageView
|
import io.legado.app.ui.widget.components.EmptyMessageView
|
||||||
import io.legado.app.ui.widget.components.SearchBarSection
|
import io.legado.app.ui.widget.components.SearchBarSection
|
||||||
import io.legado.app.ui.widget.components.alert.AppAlertDialog
|
import io.legado.app.ui.widget.components.alert.AppAlertDialog
|
||||||
import io.legado.app.ui.widget.components.button.AlertButton
|
|
||||||
import io.legado.app.ui.widget.components.button.AppIconButton
|
import io.legado.app.ui.widget.components.button.AppIconButton
|
||||||
import io.legado.app.ui.widget.components.button.TopBarNavigationButton
|
import io.legado.app.ui.widget.components.button.TopBarNavigationButton
|
||||||
import io.legado.app.ui.widget.components.card.GlassCard
|
import io.legado.app.ui.widget.components.card.GlassCard
|
||||||
@@ -100,7 +94,6 @@ import io.legado.app.ui.widget.components.icon.AppIcon
|
|||||||
import io.legado.app.ui.widget.components.modalBottomSheet.AppModalBottomSheet
|
import io.legado.app.ui.widget.components.modalBottomSheet.AppModalBottomSheet
|
||||||
import io.legado.app.ui.widget.components.swipe.SwipeAction
|
import io.legado.app.ui.widget.components.swipe.SwipeAction
|
||||||
import io.legado.app.ui.widget.components.swipe.SwipeActionContainer
|
import io.legado.app.ui.widget.components.swipe.SwipeActionContainer
|
||||||
import io.legado.app.ui.widget.components.text.AnimatedTextLine
|
|
||||||
import io.legado.app.ui.widget.components.text.AppText
|
import io.legado.app.ui.widget.components.text.AppText
|
||||||
import io.legado.app.ui.widget.components.topbar.GlassMediumFlexibleTopAppBar
|
import io.legado.app.ui.widget.components.topbar.GlassMediumFlexibleTopAppBar
|
||||||
import io.legado.app.ui.widget.components.topbar.GlassTopAppBarDefaults
|
import io.legado.app.ui.widget.components.topbar.GlassTopAppBarDefaults
|
||||||
@@ -152,15 +145,13 @@ fun ReadRecordScreen(
|
|||||||
Column {
|
Column {
|
||||||
GlassMediumFlexibleTopAppBar(
|
GlassMediumFlexibleTopAppBar(
|
||||||
title = "阅读记录",
|
title = "阅读记录",
|
||||||
subtitle = {
|
subtitle = run {
|
||||||
val subTitle = when (displayMode) {
|
val subTitle = when (displayMode) {
|
||||||
DisplayMode.AGGREGATE -> "汇总视图"
|
DisplayMode.AGGREGATE -> "汇总视图"
|
||||||
DisplayMode.TIMELINE -> "时间线视图"
|
DisplayMode.TIMELINE -> "时间线视图"
|
||||||
DisplayMode.LATEST -> "最后阅读"
|
DisplayMode.LATEST -> "最后阅读"
|
||||||
}
|
}
|
||||||
AnimatedTextLine(
|
subTitle
|
||||||
text = subTitle
|
|
||||||
)
|
|
||||||
},
|
},
|
||||||
navigationIcon = {
|
navigationIcon = {
|
||||||
TopBarNavigationButton(onClick = onBackClick)
|
TopBarNavigationButton(onClick = onBackClick)
|
||||||
|
|||||||
@@ -157,25 +157,37 @@ fun TocScreen(
|
|||||||
val useReplace = viewModel.useReplace
|
val useReplace = viewModel.useReplace
|
||||||
val showWordCount = viewModel.showWordCount
|
val showWordCount = viewModel.showWordCount
|
||||||
|
|
||||||
val subtitle = remember(
|
val topBarTitle = remember(
|
||||||
pagerState.currentPage,
|
pagerState.currentPage,
|
||||||
|
book?.name,
|
||||||
book?.durChapterTitle,
|
book?.durChapterTitle,
|
||||||
|
) {
|
||||||
|
when (pagerState.currentPage) {
|
||||||
|
0 -> {
|
||||||
|
book?.durChapterTitle?.takeIf { it.isNotBlank() } ?: (book?.name ?: "")
|
||||||
|
}
|
||||||
|
|
||||||
|
1 -> "书签管理"
|
||||||
|
else -> book?.name ?: ""
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
val topBarSubtitle = remember(
|
||||||
|
pagerState.currentPage,
|
||||||
book?.durChapterIndex,
|
book?.durChapterIndex,
|
||||||
book?.totalChapterNum
|
book?.totalChapterNum
|
||||||
) {
|
) {
|
||||||
when (pagerState.currentPage) {
|
when (pagerState.currentPage) {
|
||||||
0 -> {
|
0 -> {
|
||||||
val durTitle = book?.durChapterTitle ?: ""
|
val durIndex = (book?.durChapterIndex ?: -1) + 1
|
||||||
val durIndex = (book?.durChapterIndex ?: 0) + 1
|
|
||||||
val totalNum = book?.totalChapterNum ?: 0
|
val totalNum = book?.totalChapterNum ?: 0
|
||||||
if (totalNum > 0) {
|
if (durIndex > 0 && totalNum > 0) {
|
||||||
"$durTitle · $durIndex / $totalNum 章"
|
"$durIndex / $totalNum"
|
||||||
} else {
|
} else {
|
||||||
durTitle
|
null
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
1 -> "书签管理"
|
|
||||||
else -> null
|
else -> null
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -301,8 +313,8 @@ fun TocScreen(
|
|||||||
modifier = Modifier.nestedScroll(scrollBehavior.nestedScrollConnection),
|
modifier = Modifier.nestedScroll(scrollBehavior.nestedScrollConnection),
|
||||||
topBar = {
|
topBar = {
|
||||||
DynamicTopAppBar(
|
DynamicTopAppBar(
|
||||||
title = book?.name ?: "",
|
title = topBarTitle,
|
||||||
subtitle = subtitle,
|
subtitle = topBarSubtitle,
|
||||||
state = state,
|
state = state,
|
||||||
scrollBehavior = scrollBehavior,
|
scrollBehavior = scrollBehavior,
|
||||||
onBackClick = onBackClick,
|
onBackClick = onBackClick,
|
||||||
|
|||||||
@@ -25,15 +25,12 @@ import androidx.compose.material.icons.automirrored.filled.MenuOpen
|
|||||||
import androidx.compose.material.icons.filled.Check
|
import androidx.compose.material.icons.filled.Check
|
||||||
import androidx.compose.material.icons.filled.Menu
|
import androidx.compose.material.icons.filled.Menu
|
||||||
import androidx.compose.material.icons.filled.Search
|
import androidx.compose.material.icons.filled.Search
|
||||||
import androidx.compose.material3.Badge
|
|
||||||
import androidx.compose.material3.BadgedBox
|
|
||||||
import androidx.compose.material3.ExperimentalMaterial3Api
|
import androidx.compose.material3.ExperimentalMaterial3Api
|
||||||
import androidx.compose.material3.ExperimentalMaterial3ExpressiveApi
|
import androidx.compose.material3.ExperimentalMaterial3ExpressiveApi
|
||||||
import androidx.compose.material3.ExtendedFloatingActionButton
|
import androidx.compose.material3.ExtendedFloatingActionButton
|
||||||
import androidx.compose.material3.Icon
|
import androidx.compose.material3.Icon
|
||||||
import androidx.compose.material3.IconButton
|
import androidx.compose.material3.IconButton
|
||||||
import androidx.compose.material3.MaterialTheme
|
import androidx.compose.material3.MaterialTheme
|
||||||
import androidx.compose.material3.Text
|
|
||||||
import androidx.compose.material3.WideNavigationRail
|
import androidx.compose.material3.WideNavigationRail
|
||||||
import androidx.compose.material3.WideNavigationRailItem
|
import androidx.compose.material3.WideNavigationRailItem
|
||||||
import androidx.compose.material3.WideNavigationRailValue
|
import androidx.compose.material3.WideNavigationRailValue
|
||||||
@@ -54,7 +51,6 @@ import androidx.compose.ui.platform.LocalHapticFeedback
|
|||||||
import androidx.compose.ui.res.stringResource
|
import androidx.compose.ui.res.stringResource
|
||||||
import androidx.compose.ui.text.style.TextOverflow
|
import androidx.compose.ui.text.style.TextOverflow
|
||||||
import androidx.compose.ui.unit.dp
|
import androidx.compose.ui.unit.dp
|
||||||
import androidx.lifecycle.compose.collectAsStateWithLifecycle
|
|
||||||
import com.kyant.backdrop.backdrops.layerBackdrop
|
import com.kyant.backdrop.backdrops.layerBackdrop
|
||||||
import com.kyant.backdrop.backdrops.rememberLayerBackdrop
|
import com.kyant.backdrop.backdrops.rememberLayerBackdrop
|
||||||
import dev.chrisbanes.haze.HazeState
|
import dev.chrisbanes.haze.HazeState
|
||||||
@@ -68,7 +64,6 @@ import io.legado.app.ui.main.bookshelf.BookshelfViewModel
|
|||||||
import io.legado.app.ui.main.explore.ExploreScreen
|
import io.legado.app.ui.main.explore.ExploreScreen
|
||||||
import io.legado.app.ui.main.my.MyScreen
|
import io.legado.app.ui.main.my.MyScreen
|
||||||
import io.legado.app.ui.main.rss.RssScreen
|
import io.legado.app.ui.main.rss.RssScreen
|
||||||
import io.legado.app.ui.theme.LegadoTheme
|
|
||||||
import io.legado.app.ui.theme.regularHazeEffect
|
import io.legado.app.ui.theme.regularHazeEffect
|
||||||
import io.legado.app.ui.widget.components.AppNavigationBar
|
import io.legado.app.ui.widget.components.AppNavigationBar
|
||||||
import io.legado.app.ui.widget.components.AppNavigationBarItem
|
import io.legado.app.ui.widget.components.AppNavigationBarItem
|
||||||
@@ -97,7 +92,6 @@ fun MainScreen(
|
|||||||
onOpenSettings: () -> Unit
|
onOpenSettings: () -> Unit
|
||||||
) {
|
) {
|
||||||
val context = LocalContext.current
|
val context = LocalContext.current
|
||||||
val uiState by viewModel.state.collectAsStateWithLifecycle()
|
|
||||||
val coroutineScope = rememberCoroutineScope()
|
val coroutineScope = rememberCoroutineScope()
|
||||||
|
|
||||||
val bookshelfViewModel: BookshelfViewModel = koinViewModel()
|
val bookshelfViewModel: BookshelfViewModel = koinViewModel()
|
||||||
@@ -203,7 +197,6 @@ fun MainScreen(
|
|||||||
NavigationIcon(
|
NavigationIcon(
|
||||||
destination = destination,
|
destination = destination,
|
||||||
selected = selected,
|
selected = selected,
|
||||||
upBooksCount = uiState.upBooksCount,
|
|
||||||
modifier = if (destination == MainDestination.Bookshelf) {
|
modifier = if (destination == MainDestination.Bookshelf) {
|
||||||
Modifier.combinedClickable(
|
Modifier.combinedClickable(
|
||||||
onClick = {
|
onClick = {
|
||||||
@@ -280,7 +273,7 @@ fun MainScreen(
|
|||||||
.asPaddingValues()
|
.asPaddingValues()
|
||||||
.calculateBottomPadding()
|
.calculateBottomPadding()
|
||||||
),
|
),
|
||||||
selectedIndex = { pagerState.currentPage },
|
selectedIndex = { pagerState.targetPage },
|
||||||
onSelected = { index ->
|
onSelected = { index ->
|
||||||
coroutineScope.launch {
|
coroutineScope.launch {
|
||||||
pagerState.animateScrollToPage(index)
|
pagerState.animateScrollToPage(index)
|
||||||
@@ -291,7 +284,7 @@ fun MainScreen(
|
|||||||
isBlurEnabled = useLiquidGlass
|
isBlurEnabled = useLiquidGlass
|
||||||
) {
|
) {
|
||||||
destinations.forEachIndexed { index, destination ->
|
destinations.forEachIndexed { index, destination ->
|
||||||
val selected = pagerState.currentPage == index
|
val selected = pagerState.targetPage == index
|
||||||
FloatingBottomBarItem(
|
FloatingBottomBarItem(
|
||||||
onClick = {
|
onClick = {
|
||||||
coroutineScope.launch {
|
coroutineScope.launch {
|
||||||
@@ -302,18 +295,12 @@ fun MainScreen(
|
|||||||
) {
|
) {
|
||||||
NavigationIcon(
|
NavigationIcon(
|
||||||
destination = destination,
|
destination = destination,
|
||||||
selected = selected,
|
selected = selected
|
||||||
upBooksCount = uiState.upBooksCount
|
|
||||||
)
|
)
|
||||||
if (showLabel && (alwaysShowLabel || selected)) {
|
if (showLabel && (alwaysShowLabel || selected)) {
|
||||||
Text(
|
AppText(
|
||||||
text = stringResource(destination.labelId),
|
text = stringResource(destination.labelId),
|
||||||
style = MaterialTheme.typography.labelSmall,
|
style = MaterialTheme.typography.labelSmall,
|
||||||
color = if (selected) {
|
|
||||||
LegadoTheme.colorScheme.primary
|
|
||||||
} else {
|
|
||||||
LegadoTheme.colorScheme.onSurface
|
|
||||||
},
|
|
||||||
maxLines = 1,
|
maxLines = 1,
|
||||||
overflow = TextOverflow.Ellipsis
|
overflow = TextOverflow.Ellipsis
|
||||||
)
|
)
|
||||||
@@ -340,8 +327,7 @@ fun MainScreen(
|
|||||||
m3Icon = {
|
m3Icon = {
|
||||||
NavigationIcon(
|
NavigationIcon(
|
||||||
destination = destination,
|
destination = destination,
|
||||||
selected = selected,
|
selected = selected
|
||||||
upBooksCount = uiState.upBooksCount
|
|
||||||
)
|
)
|
||||||
},
|
},
|
||||||
m3IndicatorColor = GlassDefaults.glassColor(
|
m3IndicatorColor = GlassDefaults.glassColor(
|
||||||
@@ -410,17 +396,8 @@ fun MainScreen(
|
|||||||
private fun NavigationIcon(
|
private fun NavigationIcon(
|
||||||
destination: MainDestination,
|
destination: MainDestination,
|
||||||
selected: Boolean,
|
selected: Boolean,
|
||||||
upBooksCount: Int,
|
|
||||||
modifier: Modifier = Modifier
|
modifier: Modifier = Modifier
|
||||||
) {
|
) {
|
||||||
val icon = AppIcons.mainDestination(destination, selected)
|
val icon = AppIcons.mainDestination(destination, selected)
|
||||||
Box(modifier = modifier) {
|
AppIcon(icon, contentDescription = null, modifier = modifier)
|
||||||
if (destination == MainDestination.Bookshelf && upBooksCount > 0) {
|
|
||||||
BadgedBox(badge = { Badge { Text(upBooksCount.toString()) } }) {
|
|
||||||
AppIcon(icon, contentDescription = null)
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
AppIcon(icon, contentDescription = null)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
@@ -3,7 +3,6 @@ package io.legado.app.ui.main
|
|||||||
import android.app.Application
|
import android.app.Application
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
import android.content.Intent
|
import android.content.Intent
|
||||||
import androidx.lifecycle.viewModelScope
|
|
||||||
import io.legado.app.base.BaseViewModel
|
import io.legado.app.base.BaseViewModel
|
||||||
import io.legado.app.constant.EventBus
|
import io.legado.app.constant.EventBus
|
||||||
import io.legado.app.data.appDb
|
import io.legado.app.data.appDb
|
||||||
@@ -12,31 +11,10 @@ import io.legado.app.help.DefaultData
|
|||||||
import io.legado.app.ui.main.my.PrefClickEvent
|
import io.legado.app.ui.main.my.PrefClickEvent
|
||||||
import io.legado.app.utils.eventBus.FlowEventBus
|
import io.legado.app.utils.eventBus.FlowEventBus
|
||||||
import io.legado.app.utils.sendToClip
|
import io.legado.app.utils.sendToClip
|
||||||
import kotlinx.coroutines.flow.MutableStateFlow
|
|
||||||
import kotlinx.coroutines.flow.SharingStarted
|
|
||||||
import kotlinx.coroutines.flow.StateFlow
|
|
||||||
import kotlinx.coroutines.flow.map
|
|
||||||
import kotlinx.coroutines.flow.stateIn
|
|
||||||
import kotlinx.coroutines.launch
|
|
||||||
|
|
||||||
data class MainUiState(
|
|
||||||
val upBooksCount: Int = 0
|
|
||||||
)
|
|
||||||
|
|
||||||
class MainViewModel(application: Application) : BaseViewModel(application) {
|
class MainViewModel(application: Application) : BaseViewModel(application) {
|
||||||
|
|
||||||
private val upBooksCountFlow = MutableStateFlow(0)
|
|
||||||
|
|
||||||
val state: StateFlow<MainUiState> = upBooksCountFlow
|
|
||||||
.map { count -> MainUiState(count) }
|
|
||||||
.stateIn(viewModelScope, SharingStarted.WhileSubscribed(5000), MainUiState())
|
|
||||||
|
|
||||||
init {
|
init {
|
||||||
viewModelScope.launch {
|
|
||||||
FlowEventBus.with<Int>(EventBus.UP_BOOKSHELF_COUNT).collect {
|
|
||||||
upBooksCountFlow.value = it
|
|
||||||
}
|
|
||||||
}
|
|
||||||
deleteNotShelfBook()
|
deleteNotShelfBook()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -198,7 +198,7 @@ fun BookshelfScreen(
|
|||||||
val bookGroupStyle = BookshelfConfig.bookGroupStyle
|
val bookGroupStyle = BookshelfConfig.bookGroupStyle
|
||||||
// 控制是否处于“文件夹列表”根视图,还是“文件夹内部”书籍视图
|
// 控制是否处于“文件夹列表”根视图,还是“文件夹内部”书籍视图
|
||||||
var isInFolderRoot by remember(bookGroupStyle) { mutableStateOf(bookGroupStyle == 2) }
|
var isInFolderRoot by remember(bookGroupStyle) { mutableStateOf(bookGroupStyle == 2) }
|
||||||
val title = when {
|
val baseTitle = when {
|
||||||
bookGroupStyle == 1 -> {
|
bookGroupStyle == 1 -> {
|
||||||
uiState.groups.getOrNull(pagerState.currentPage)?.groupName
|
uiState.groups.getOrNull(pagerState.currentPage)?.groupName
|
||||||
?: stringResource(R.string.bookshelf)
|
?: stringResource(R.string.bookshelf)
|
||||||
@@ -212,6 +212,11 @@ fun BookshelfScreen(
|
|||||||
|
|
||||||
else -> stringResource(R.string.bookshelf)
|
else -> stringResource(R.string.bookshelf)
|
||||||
}
|
}
|
||||||
|
val title = if (uiState.upBooksCount > 0) {
|
||||||
|
"$baseTitle (${uiState.upBooksCount})"
|
||||||
|
} else {
|
||||||
|
baseTitle
|
||||||
|
}
|
||||||
|
|
||||||
if (bookGroupStyle == 2 && !isInFolderRoot) {
|
if (bookGroupStyle == 2 && !isInFolderRoot) {
|
||||||
BackHandler {
|
BackHandler {
|
||||||
|
|||||||
@@ -14,5 +14,6 @@ data class BookshelfUiState(
|
|||||||
val groupBookCounts: Map<Long, Int> = emptyMap(),
|
val groupBookCounts: Map<Long, Int> = emptyMap(),
|
||||||
val selectedGroupIndex: Int = 0,
|
val selectedGroupIndex: Int = 0,
|
||||||
val loadingText: String? = null,
|
val loadingText: String? = null,
|
||||||
|
val upBooksCount: Int = 0,
|
||||||
val updatingBooks: Set<String> = emptySet()
|
val updatingBooks: Set<String> = emptySet()
|
||||||
) : ListUiState<BookShelfItem>
|
) : ListUiState<BookShelfItem>
|
||||||
@@ -107,6 +107,7 @@ class BookshelfViewModel(
|
|||||||
private val waitUpTocBooks = LinkedList<String>()
|
private val waitUpTocBooks = LinkedList<String>()
|
||||||
private val onUpTocBooks = ConcurrentHashMap.newKeySet<String>()
|
private val onUpTocBooks = ConcurrentHashMap.newKeySet<String>()
|
||||||
private val updatingBooksFlow = MutableStateFlow<Set<String>>(emptySet())
|
private val updatingBooksFlow = MutableStateFlow<Set<String>>(emptySet())
|
||||||
|
private val upBooksCountFlow = MutableStateFlow(0)
|
||||||
private var upTocJob: Job? = null
|
private var upTocJob: Job? = null
|
||||||
private var cacheBookJob: Job? = null
|
private var cacheBookJob: Job? = null
|
||||||
private val eventListenerSource = ConcurrentHashMap<BookSource, Boolean>()
|
private val eventListenerSource = ConcurrentHashMap<BookSource, Boolean>()
|
||||||
@@ -210,16 +211,18 @@ class BookshelfViewModel(
|
|||||||
groupIdFlow,
|
groupIdFlow,
|
||||||
searchKeyFlow,
|
searchKeyFlow,
|
||||||
loadingTextFlow,
|
loadingTextFlow,
|
||||||
updatingBooksFlow
|
updatingBooksFlow,
|
||||||
) { groupId, searchKey, loadingText, updatingBooks ->
|
upBooksCountFlow
|
||||||
InternalState(groupId, searchKey, loadingText, updatingBooks)
|
) { groupId, searchKey, loadingText, updatingBooks, upBooksCount ->
|
||||||
|
InternalState(groupId, searchKey, loadingText, updatingBooks, upBooksCount)
|
||||||
}
|
}
|
||||||
|
|
||||||
data class InternalState(
|
data class InternalState(
|
||||||
val groupId: Long,
|
val groupId: Long,
|
||||||
val searchKey: String,
|
val searchKey: String,
|
||||||
val loadingText: String?,
|
val loadingText: String?,
|
||||||
val updatingBooks: Set<String>
|
val updatingBooks: Set<String>,
|
||||||
|
val upBooksCount: Int
|
||||||
)
|
)
|
||||||
|
|
||||||
val uiState: StateFlow<BookshelfUiState> = combine(
|
val uiState: StateFlow<BookshelfUiState> = combine(
|
||||||
@@ -250,6 +253,7 @@ class BookshelfViewModel(
|
|||||||
isSearch = internal.searchKey.isNotEmpty(),
|
isSearch = internal.searchKey.isNotEmpty(),
|
||||||
isLoading = internal.loadingText != null,
|
isLoading = internal.loadingText != null,
|
||||||
loadingText = internal.loadingText,
|
loadingText = internal.loadingText,
|
||||||
|
upBooksCount = internal.upBooksCount,
|
||||||
updatingBooks = internal.updatingBooks
|
updatingBooks = internal.updatingBooks
|
||||||
)
|
)
|
||||||
}.stateIn(viewModelScope, SharingStarted.WhileSubscribed(5000), BookshelfUiState())
|
}.stateIn(viewModelScope, SharingStarted.WhileSubscribed(5000), BookshelfUiState())
|
||||||
@@ -276,6 +280,9 @@ class BookshelfViewModel(
|
|||||||
viewModelScope.launch {
|
viewModelScope.launch {
|
||||||
snapshotFlow { BookshelfConfig.bookGroupStyle }.collect { refresh() }
|
snapshotFlow { BookshelfConfig.bookGroupStyle }.collect { refresh() }
|
||||||
}
|
}
|
||||||
|
viewModelScope.launch {
|
||||||
|
snapshotFlow { BookshelfConfig.showWaitUpCount }.collect { postUpBooksCount() }
|
||||||
|
}
|
||||||
|
|
||||||
if (BookshelfConfig.autoRefreshBook) {
|
if (BookshelfConfig.autoRefreshBook) {
|
||||||
upAllBookToc()
|
upAllBookToc()
|
||||||
@@ -382,6 +389,7 @@ class BookshelfViewModel(
|
|||||||
waitUpTocBooks.add(book.bookUrl)
|
waitUpTocBooks.add(book.bookUrl)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
postUpBooksCount()
|
||||||
if (upTocJob == null) {
|
if (upTocJob == null) {
|
||||||
startUpTocJob()
|
startUpTocJob()
|
||||||
}
|
}
|
||||||
@@ -480,7 +488,7 @@ class BookshelfViewModel(
|
|||||||
private fun postUpBooksCount() {
|
private fun postUpBooksCount() {
|
||||||
val count =
|
val count =
|
||||||
if (BookshelfConfig.showWaitUpCount) waitUpTocBooks.size + onUpTocBooks.size else 0
|
if (BookshelfConfig.showWaitUpCount) waitUpTocBooks.size + onUpTocBooks.size else 0
|
||||||
FlowEventBus.post(EventBus.UP_BOOKSHELF_COUNT, count)
|
upBooksCountFlow.value = count
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun addDownload(source: BookSource, book: Book) {
|
private fun addDownload(source: BookSource, book: Book) {
|
||||||
|
|||||||
@@ -1,10 +1,8 @@
|
|||||||
package io.legado.app.ui.replace.edit
|
package io.legado.app.ui.replace.edit
|
||||||
|
|
||||||
import android.app.Application
|
import android.app.Application
|
||||||
import androidx.lifecycle.SavedStateHandle
|
|
||||||
import androidx.lifecycle.ViewModel
|
import androidx.lifecycle.ViewModel
|
||||||
import androidx.lifecycle.viewModelScope
|
import androidx.lifecycle.viewModelScope
|
||||||
import androidx.navigation.toRoute
|
|
||||||
import io.legado.app.data.dao.ReplaceRuleDao
|
import io.legado.app.data.dao.ReplaceRuleDao
|
||||||
import io.legado.app.data.entities.ReplaceRule
|
import io.legado.app.data.entities.ReplaceRule
|
||||||
import io.legado.app.exception.NoStackTraceException
|
import io.legado.app.exception.NoStackTraceException
|
||||||
@@ -41,11 +39,9 @@ data class ReplaceEditUiState(
|
|||||||
class ReplaceEditViewModel(
|
class ReplaceEditViewModel(
|
||||||
private val app: Application,
|
private val app: Application,
|
||||||
private val replaceRuleDao: ReplaceRuleDao,
|
private val replaceRuleDao: ReplaceRuleDao,
|
||||||
private val savedStateHandle: SavedStateHandle
|
private val route: ReplaceEditRoute
|
||||||
) : ViewModel() {
|
) : ViewModel() {
|
||||||
|
|
||||||
private val route = savedStateHandle.toRoute<ReplaceEditRoute>()
|
|
||||||
|
|
||||||
private val _uiState = MutableStateFlow(ReplaceEditUiState())
|
private val _uiState = MutableStateFlow(ReplaceEditUiState())
|
||||||
val uiState = _uiState.asStateFlow()
|
val uiState = _uiState.asStateFlow()
|
||||||
|
|
||||||
@@ -59,7 +55,6 @@ class ReplaceEditViewModel(
|
|||||||
|
|
||||||
private fun initData() {
|
private fun initData() {
|
||||||
viewModelScope.launch {
|
viewModelScope.launch {
|
||||||
|
|
||||||
val id = route.id
|
val id = route.id
|
||||||
|
|
||||||
if (id > 0) {
|
if (id > 0) {
|
||||||
|
|||||||
@@ -67,7 +67,6 @@ import io.legado.app.ui.animation.DampedDragAnimation
|
|||||||
import io.legado.app.ui.animation.InteractiveHighlight
|
import io.legado.app.ui.animation.InteractiveHighlight
|
||||||
import io.legado.app.ui.theme.LegadoTheme
|
import io.legado.app.ui.theme.LegadoTheme
|
||||||
import kotlinx.coroutines.flow.collectLatest
|
import kotlinx.coroutines.flow.collectLatest
|
||||||
import kotlinx.coroutines.flow.drop
|
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
import kotlin.math.abs
|
import kotlin.math.abs
|
||||||
import kotlin.math.sign
|
import kotlin.math.sign
|
||||||
@@ -143,7 +142,7 @@ fun FloatingBottomBar(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
var currentIndex by remember(selectedIndex) { mutableIntStateOf(selectedIndex()) }
|
var currentIndex by remember { mutableIntStateOf(selectedIndex()) }
|
||||||
|
|
||||||
class DampedDragAnimationHolder {
|
class DampedDragAnimationHolder {
|
||||||
var instance: DampedDragAnimation? = null
|
var instance: DampedDragAnimation? = null
|
||||||
@@ -179,6 +178,9 @@ fun FloatingBottomBar(
|
|||||||
val targetIndex = targetValue.fastRoundToInt().fastCoerceIn(0, tabsCount - 1)
|
val targetIndex = targetValue.fastRoundToInt().fastCoerceIn(0, tabsCount - 1)
|
||||||
currentIndex = targetIndex
|
currentIndex = targetIndex
|
||||||
animateToValue(targetIndex.toFloat())
|
animateToValue(targetIndex.toFloat())
|
||||||
|
if (targetIndex != selectedIndex()) {
|
||||||
|
onSelected(targetIndex)
|
||||||
|
}
|
||||||
animationScope.launch {
|
animationScope.launch {
|
||||||
offsetAnimation.animateTo(0f, spring(1f, 300f, 0.5f))
|
offsetAnimation.animateTo(0f, spring(1f, 300f, 0.5f))
|
||||||
}
|
}
|
||||||
@@ -197,13 +199,10 @@ fun FloatingBottomBar(
|
|||||||
).also { holder.instance = it }
|
).also { holder.instance = it }
|
||||||
}
|
}
|
||||||
|
|
||||||
LaunchedEffect(selectedIndex) {
|
LaunchedEffect(selectedIndex, dampedDragAnimation) {
|
||||||
snapshotFlow { selectedIndex() }.collectLatest { currentIndex = it }
|
snapshotFlow { selectedIndex() }.collectLatest { index ->
|
||||||
}
|
currentIndex = index
|
||||||
LaunchedEffect(dampedDragAnimation) {
|
|
||||||
snapshotFlow { currentIndex }.drop(1).collectLatest { index ->
|
|
||||||
dampedDragAnimation.animateToValue(index.toFloat())
|
dampedDragAnimation.animateToValue(index.toFloat())
|
||||||
onSelected(index)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -27,7 +27,6 @@ import io.legado.app.ui.widget.components.button.TopBarNavigationButton
|
|||||||
import io.legado.app.ui.widget.components.icon.AppIcons
|
import io.legado.app.ui.widget.components.icon.AppIcons
|
||||||
import io.legado.app.ui.widget.components.list.ListUiState
|
import io.legado.app.ui.widget.components.list.ListUiState
|
||||||
import io.legado.app.ui.widget.components.menuItem.RoundDropdownMenu
|
import io.legado.app.ui.widget.components.menuItem.RoundDropdownMenu
|
||||||
import io.legado.app.ui.widget.components.text.AnimatedTextLine
|
|
||||||
|
|
||||||
|
|
||||||
@OptIn(ExperimentalMaterial3Api::class)
|
@OptIn(ExperimentalMaterial3Api::class)
|
||||||
@@ -62,9 +61,7 @@ fun <T> DynamicTopAppBar(
|
|||||||
else -> title
|
else -> title
|
||||||
},
|
},
|
||||||
useCharMode = isSelecting || state.isLoading,
|
useCharMode = isSelecting || state.isLoading,
|
||||||
subtitle = subtitle?.let {
|
subtitle = subtitle,
|
||||||
{ AnimatedTextLine(text = it) }
|
|
||||||
},
|
|
||||||
navigationIcon = {
|
navigationIcon = {
|
||||||
if (isSelecting || onBackClick != null) {
|
if (isSelecting || onBackClick != null) {
|
||||||
TopBarNavigationButton(
|
TopBarNavigationButton(
|
||||||
|
|||||||
+20
-2
@@ -23,6 +23,7 @@ import io.legado.app.ui.theme.ThemeResolver
|
|||||||
import io.legado.app.ui.theme.responsiveHazeEffect
|
import io.legado.app.ui.theme.responsiveHazeEffect
|
||||||
import io.legado.app.ui.widget.components.GlassDefaults
|
import io.legado.app.ui.widget.components.GlassDefaults
|
||||||
import io.legado.app.ui.widget.components.text.AdaptiveAnimatedText
|
import io.legado.app.ui.widget.components.text.AdaptiveAnimatedText
|
||||||
|
import io.legado.app.ui.widget.components.text.AnimatedTextLine
|
||||||
import top.yukonga.miuix.kmp.basic.MiuixScrollBehavior
|
import top.yukonga.miuix.kmp.basic.MiuixScrollBehavior
|
||||||
import top.yukonga.miuix.kmp.theme.MiuixTheme
|
import top.yukonga.miuix.kmp.theme.MiuixTheme
|
||||||
import top.yukonga.miuix.kmp.basic.TopAppBar as MiuixTopAppBar
|
import top.yukonga.miuix.kmp.basic.TopAppBar as MiuixTopAppBar
|
||||||
@@ -33,7 +34,7 @@ fun GlassMediumFlexibleTopAppBar(
|
|||||||
title: String,
|
title: String,
|
||||||
modifier: Modifier = Modifier,
|
modifier: Modifier = Modifier,
|
||||||
useCharMode: Boolean = false,
|
useCharMode: Boolean = false,
|
||||||
subtitle: (@Composable () -> Unit)? = null,
|
subtitle: String? = null,
|
||||||
scrollBehavior: GlassTopAppBarScrollBehavior? = null,
|
scrollBehavior: GlassTopAppBarScrollBehavior? = null,
|
||||||
navigationIcon: @Composable () -> Unit = {},
|
navigationIcon: @Composable () -> Unit = {},
|
||||||
actions: @Composable RowScope.() -> Unit = {},
|
actions: @Composable RowScope.() -> Unit = {},
|
||||||
@@ -73,6 +74,7 @@ fun GlassMediumFlexibleTopAppBar(
|
|||||||
containerColor = Color.Transparent,
|
containerColor = Color.Transparent,
|
||||||
scrolledContainerColor = Color.Transparent
|
scrolledContainerColor = Color.Transparent
|
||||||
)
|
)
|
||||||
|
val subtitleText = subtitle?.takeIf { it.isNotBlank() }
|
||||||
|
|
||||||
Column(
|
Column(
|
||||||
modifier = finalModifier
|
modifier = finalModifier
|
||||||
@@ -82,6 +84,7 @@ fun GlassMediumFlexibleTopAppBar(
|
|||||||
MiuixTopAppBar(
|
MiuixTopAppBar(
|
||||||
modifier = Modifier,
|
modifier = Modifier,
|
||||||
title = title,
|
title = title,
|
||||||
|
subtitle = subtitleText.orEmpty(),
|
||||||
navigationIcon = navigationIcon,
|
navigationIcon = navigationIcon,
|
||||||
actions = actions,
|
actions = actions,
|
||||||
color = Color.Transparent,
|
color = Color.Transparent,
|
||||||
@@ -101,7 +104,11 @@ fun GlassMediumFlexibleTopAppBar(
|
|||||||
overflow = TextOverflow.Ellipsis
|
overflow = TextOverflow.Ellipsis
|
||||||
)
|
)
|
||||||
},
|
},
|
||||||
subtitle = subtitle,
|
subtitle = subtitleText?.let { text ->
|
||||||
|
{
|
||||||
|
AnimatedTextLine(text = text)
|
||||||
|
}
|
||||||
|
},
|
||||||
navigationIcon = navigationIcon,
|
navigationIcon = navigationIcon,
|
||||||
actions = actions,
|
actions = actions,
|
||||||
scrollBehavior = (scrollBehavior as? M3GlassScrollBehavior)?.m3Behavior,
|
scrollBehavior = (scrollBehavior as? M3GlassScrollBehavior)?.m3Behavior,
|
||||||
@@ -111,12 +118,23 @@ fun GlassMediumFlexibleTopAppBar(
|
|||||||
TopAppBar(
|
TopAppBar(
|
||||||
modifier = Modifier,
|
modifier = Modifier,
|
||||||
title = {
|
title = {
|
||||||
|
Column {
|
||||||
AdaptiveAnimatedText(
|
AdaptiveAnimatedText(
|
||||||
text = title,
|
text = title,
|
||||||
useCharMode = useCharMode,
|
useCharMode = useCharMode,
|
||||||
maxLines = 1,
|
maxLines = 1,
|
||||||
overflow = TextOverflow.Ellipsis
|
overflow = TextOverflow.Ellipsis
|
||||||
)
|
)
|
||||||
|
subtitleText?.let { text ->
|
||||||
|
AnimatedTextLine(
|
||||||
|
text = text,
|
||||||
|
style = MaterialTheme.typography.labelSmall,
|
||||||
|
color = MaterialTheme.colorScheme.onSurfaceVariant,
|
||||||
|
maxLines = 1,
|
||||||
|
overflow = TextOverflow.Ellipsis
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
},
|
},
|
||||||
navigationIcon = navigationIcon,
|
navigationIcon = navigationIcon,
|
||||||
actions = actions,
|
actions = actions,
|
||||||
|
|||||||
Reference in new issue
Block a user