[修复] 修复分组添加和编辑、拖动的功能

[修复] 修复关闭分组时直接消失的问题
[修复] 修复发现错误的字符串
[修复] 修复部分设置无法及时触发的问题
[修复] 修复下拉刷新时刷新全部书籍的问题
[修复] 修复不显示未读数量的问题
This commit is contained in:
HapeLee
2026-03-19 00:56:30 +08:00
parent 1f4a5fb01c
commit d28e9c8a0a
12 changed files with 240 additions and 158 deletions
@@ -0,0 +1,45 @@
package io.legado.app.data.repository
import androidx.lifecycle.asFlow
import io.legado.app.data.dao.BookGroupDao
import io.legado.app.data.entities.BookGroup
import kotlinx.coroutines.flow.Flow
class BookGroupRepository(private val bookGroupDao: BookGroupDao) {
fun flowAll(): Flow<List<BookGroup>> {
return bookGroupDao.flowAll()
}
fun flowShow(): Flow<List<BookGroup>> {
return bookGroupDao.show.asFlow()
}
suspend fun update(vararg bookGroup: BookGroup) {
bookGroupDao.update(*bookGroup)
}
suspend fun insert(vararg bookGroup: BookGroup) {
bookGroupDao.insert(*bookGroup)
}
suspend fun delete(vararg bookGroup: BookGroup) {
bookGroupDao.delete(*bookGroup)
}
suspend fun getUnusedId(): Long {
return bookGroupDao.getUnusedId()
}
fun getMaxOrder(): Int {
return bookGroupDao.maxOrder
}
suspend fun getByID(id: Long): BookGroup? {
return bookGroupDao.getByID(id)
}
suspend fun clearCover(groupId: Long) {
bookGroupDao.clearCover(groupId)
}
}
@@ -6,6 +6,7 @@ import coil.decode.GifDecoder
import coil.decode.ImageDecoderDecoder
import coil.decode.SvgDecoder
import io.legado.app.data.AppDatabase
import io.legado.app.data.repository.BookGroupRepository
import io.legado.app.data.repository.BookRepository
import io.legado.app.data.repository.DirectLinkUploadRepository
import io.legado.app.data.repository.ExploreRepository
@@ -50,9 +51,11 @@ val appModule = module {
single { get<AppDatabase>().readRecordDao }
single { get<AppDatabase>().bookDao }
single { get<AppDatabase>().bookChapterDao }
single { get<AppDatabase>().bookGroupDao }
singleOf(::ReadRecordRepository)
singleOf(::BookRepository)
singleOf(::BookGroupRepository)
singleOf(::SearchContentRepository)
singleOf(::RemoteBookRepository)
@@ -4,12 +4,16 @@ import android.app.Application
import io.legado.app.base.BaseViewModel
import io.legado.app.data.appDb
import io.legado.app.data.entities.BookGroup
import io.legado.app.data.repository.BookGroupRepository
class GroupViewModel(application: Application) : BaseViewModel(application) {
class GroupViewModel(
application: Application,
private val bookGroupRepository: BookGroupRepository
) : BaseViewModel(application) {
fun upGroup(vararg bookGroup: BookGroup, finally: (() -> Unit)? = null) {
execute {
appDb.bookGroupDao.update(*bookGroup)
bookGroupRepository.update(*bookGroup)
}.onFinally {
finally?.invoke()
}
@@ -23,17 +27,17 @@ class GroupViewModel(application: Application) : BaseViewModel(application) {
finally: () -> Unit
) {
execute {
val groupId = appDb.bookGroupDao.getUnusedId()
val groupId = bookGroupRepository.getUnusedId()
val bookGroup = BookGroup(
groupId = groupId,
groupName = groupName,
cover = cover,
bookSort = bookSort,
enableRefresh = enableRefresh,
order = appDb.bookGroupDao.maxOrder.plus(1)
order = bookGroupRepository.getMaxOrder().plus(1)
)
appDb.bookGroupDao.getByID(groupId) ?: appDb.bookDao.removeGroup(groupId)
appDb.bookGroupDao.insert(bookGroup)
bookGroupRepository.getByID(groupId) ?: appDb.bookDao.removeGroup(groupId)
bookGroupRepository.insert(bookGroup)
}.onFinally {
finally()
}
@@ -41,7 +45,7 @@ class GroupViewModel(application: Application) : BaseViewModel(application) {
fun delGroup(bookGroup: BookGroup, finally: () -> Unit) {
execute {
appDb.bookGroupDao.delete(bookGroup)
bookGroupRepository.delete(bookGroup)
appDb.bookDao.removeGroup(bookGroup.groupId)
}.onFinally {
finally()
@@ -50,10 +54,10 @@ class GroupViewModel(application: Application) : BaseViewModel(application) {
fun clearCover(bookGroup: BookGroup, finally: () -> Unit) {
execute {
appDb.bookGroupDao.clearCover(bookGroup.groupId)
bookGroupRepository.clearCover(bookGroup.groupId)
}.onFinally {
finally()
}
}
}
}
@@ -28,7 +28,7 @@ sealed class MainDestination(
object Explore : MainDestination(
"explore",
R.string.screen_find,
R.string.discovery,
Icons.Outlined.Explore,
Icons.Default.Explore
)
@@ -7,6 +7,7 @@ import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.WindowInsets
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.heightIn
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.pager.HorizontalPager
import androidx.compose.foundation.pager.rememberPagerState
@@ -20,10 +21,10 @@ import androidx.compose.material3.BottomAppBarDefaults
import androidx.compose.material3.ExperimentalMaterial3Api
import androidx.compose.material3.ExperimentalMaterial3ExpressiveApi
import androidx.compose.material3.ExtendedFloatingActionButton
import androidx.compose.material3.FlexibleBottomAppBar
import androidx.compose.material3.Icon
import androidx.compose.material3.IconButton
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.NavigationBar
import androidx.compose.material3.NavigationBarItem
import androidx.compose.material3.NavigationBarItemDefaults
import androidx.compose.material3.Scaffold
@@ -159,8 +160,10 @@ fun MainScreen(
modifier = Modifier.weight(1f),
bottomBar = {
if (!useRail && MainConfig.showBottomView) {
FlexibleBottomAppBar(
modifier = Modifier.regularHazeEffect(state = hazeState),
NavigationBar(
modifier = Modifier
.regularHazeEffect(state = hazeState)
.heightIn(max = 84.dp),
containerColor = GlassDefaults.glassColor(
noBlurColor = BottomAppBarDefaults.containerColor,
blurAlpha = GlassDefaults.DefaultBlurAlpha
@@ -180,7 +183,7 @@ fun MainScreen(
},
colors = NavigationBarItemDefaults.colors(
indicatorColor = GlassDefaults.glassColor(
noBlurColor = MaterialTheme.colorScheme.primaryContainer,
noBlurColor = MaterialTheme.colorScheme.secondaryContainer,
blurAlpha = GlassDefaults.ThickBlurAlpha
),
),
@@ -1,5 +1,6 @@
package io.legado.app.ui.main.bookshelf
import android.content.res.Configuration
import androidx.activity.compose.BackHandler
import androidx.activity.compose.rememberLauncherForActivityResult
import androidx.compose.foundation.ExperimentalFoundationApi
@@ -48,6 +49,7 @@ import androidx.compose.runtime.setValue
import androidx.compose.runtime.snapshotFlow
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.platform.LocalConfiguration
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.text.style.TextOverflow
@@ -150,6 +152,13 @@ fun BookshelfScreen(
}
}
val configuration = LocalConfiguration.current
val isLandscape = configuration.orientation == Configuration.ORIENTATION_LANDSCAPE
val bookshelfLayoutMode =
if (isLandscape) BookshelfConfig.bookshelfLayoutModeLandscape else BookshelfConfig.bookshelfLayoutModePortrait
val bookshelfLayoutGrid =
if (isLandscape) BookshelfConfig.bookshelfLayoutGridLandscape else BookshelfConfig.bookshelfLayoutGridPortrait
ListScaffold(
title = title,
state = uiState,
@@ -271,7 +280,7 @@ fun BookshelfScreen(
onRefresh = {
scope.launch {
isRefreshing = true
viewModel.upAllBookToc()
viewModel.upToc(uiState.items)
delay(1000)
isRefreshing = false
}
@@ -290,26 +299,26 @@ fun BookshelfScreen(
) {
if (bookGroupStyle == 2 && isInFolderRoot) {
LazyVerticalGrid(
columns = GridCells.Fixed(uiState.bookshelfLayoutGrid.coerceAtLeast(1)),
columns = GridCells.Fixed(bookshelfLayoutGrid.coerceAtLeast(1)),
modifier = Modifier.fillMaxSize(),
contentPadding = PaddingValues(
top = paddingValues.calculateTopPadding(),
bottom = 120.dp,
start = if (uiState.bookshelfLayoutMode != 0) 12.dp else 0.dp,
end = if (uiState.bookshelfLayoutMode != 0) 12.dp else 0.dp
start = if (bookshelfLayoutMode != 0) 12.dp else 0.dp,
end = if (bookshelfLayoutMode != 0) 12.dp else 0.dp
),
verticalArrangement = Arrangement.spacedBy(if (uiState.bookshelfLayoutMode != 0) 8.dp else 0.dp),
horizontalArrangement = Arrangement.spacedBy(if (uiState.bookshelfLayoutMode != 0) 8.dp else 0.dp)
verticalArrangement = Arrangement.spacedBy(if (bookshelfLayoutMode != 0) 8.dp else 0.dp),
horizontalArrangement = Arrangement.spacedBy(if (bookshelfLayoutMode != 0) 8.dp else 0.dp)
) {
items(uiState.groups) { group ->
if (uiState.bookshelfLayoutMode == 0) {
if (bookshelfLayoutMode == 0) {
BookGroupItemList(
group = group,
previewBooks = uiState.groupPreviews[group.groupId] ?: emptyList(),
isCompact = uiState.bookshelfLayoutCompact,
titleSmallFont = uiState.titleSmallFont,
titleCenter = uiState.titleCenter,
titleMaxLines = uiState.titleMaxLines,
isCompact = BookshelfConfig.bookshelfLayoutCompact,
titleSmallFont = BookshelfConfig.bookshelfTitleSmallFont,
titleCenter = BookshelfConfig.bookshelfTitleCenter,
titleMaxLines = BookshelfConfig.bookshelfTitleMaxLines,
onClick = {
val index = uiState.groups.indexOf(group)
if (index != -1) {
@@ -323,11 +332,11 @@ fun BookshelfScreen(
BookGroupItemGrid(
group = group,
previewBooks = uiState.groupPreviews[group.groupId] ?: emptyList(),
isCompact = uiState.bookshelfLayoutCompact,
titleSmallFont = uiState.titleSmallFont,
titleCenter = uiState.titleCenter,
titleMaxLines = uiState.titleMaxLines,
coverShadow = uiState.coverShadow,
isCompact = BookshelfConfig.bookshelfLayoutCompact,
titleSmallFont = BookshelfConfig.bookshelfTitleSmallFont,
titleCenter = BookshelfConfig.bookshelfTitleCenter,
titleMaxLines = BookshelfConfig.bookshelfTitleMaxLines,
coverShadow = BookshelfConfig.bookshelfCoverShadow,
onClick = {
val index = uiState.groups.indexOf(group)
if (index != -1) {
@@ -355,6 +364,8 @@ fun BookshelfScreen(
paddingValues = paddingValues,
books = books,
uiState = uiState,
bookshelfLayoutMode = bookshelfLayoutMode,
bookshelfLayoutGrid = bookshelfLayoutGrid,
onBookClick = onBookClick,
onBookLongClick = onBookLongClick
)
@@ -430,31 +441,33 @@ fun BookshelfPage(
paddingValues: PaddingValues,
books: List<Book>,
uiState: BookshelfUiState,
bookshelfLayoutMode: Int,
bookshelfLayoutGrid: Int,
onBookClick: (Book) -> Unit,
onBookLongClick: (Book) -> Unit
) {
LazyVerticalGrid(
columns = GridCells.Fixed(uiState.bookshelfLayoutGrid.coerceAtLeast(1)),
columns = GridCells.Fixed(bookshelfLayoutGrid.coerceAtLeast(1)),
modifier = Modifier.fillMaxSize(),
contentPadding = PaddingValues(
top = paddingValues.calculateTopPadding(),
bottom = 120.dp,
start = if (uiState.bookshelfLayoutMode != 0) 12.dp else 0.dp,
end = if (uiState.bookshelfLayoutMode != 0) 12.dp else 0.dp
start = if (bookshelfLayoutMode != 0) 12.dp else 0.dp,
end = if (bookshelfLayoutMode != 0) 12.dp else 0.dp
),
verticalArrangement = Arrangement.spacedBy(if (uiState.bookshelfLayoutMode != 0) 8.dp else 0.dp),
horizontalArrangement = Arrangement.spacedBy(if (uiState.bookshelfLayoutMode != 0) 8.dp else 0.dp)
verticalArrangement = Arrangement.spacedBy(if (bookshelfLayoutMode != 0) 8.dp else 0.dp),
horizontalArrangement = Arrangement.spacedBy(if (bookshelfLayoutMode != 0) 8.dp else 0.dp)
) {
items(books, key = { it.bookUrl }) { book ->
BookItem(
book = book,
layoutMode = uiState.bookshelfLayoutMode,
isCompact = uiState.bookshelfLayoutCompact,
layoutMode = bookshelfLayoutMode,
isCompact = BookshelfConfig.bookshelfLayoutCompact,
isUpdating = uiState.updatingBooks.contains(book.bookUrl),
titleSmallFont = uiState.titleSmallFont,
titleCenter = uiState.titleCenter,
titleMaxLines = uiState.titleMaxLines,
coverShadow = uiState.coverShadow,
titleSmallFont = BookshelfConfig.bookshelfTitleSmallFont,
titleCenter = BookshelfConfig.bookshelfTitleCenter,
titleMaxLines = BookshelfConfig.bookshelfTitleMaxLines,
coverShadow = BookshelfConfig.bookshelfCoverShadow,
onClick = { onBookClick(book) },
onLongClick = { onBookLongClick(book) }
)
@@ -475,7 +488,7 @@ fun BookItem(
onClick: () -> Unit,
onLongClick: () -> Unit
) {
val unreadCount = (book.totalChapterNum - book.durChapterIndex).coerceAtLeast(0)
val unreadCount = book.getUnreadChapterNum()
BookshelfItem(
isGrid = layoutMode != 0,
@@ -486,7 +499,9 @@ fun BookItem(
author = book.author,
path = book.getDisplayCover(),
isUpdating = isUpdating,
modifier = modifier
modifier = modifier,
badgeText = if (BookshelfConfig.showUnread && unreadCount > 0) unreadCount.toString() else null,
showBadgeDot = !BookshelfConfig.showUnread && BookshelfConfig.showUnreadNew && unreadCount > 0 && book.lastCheckCount > 0
)
},
title = book.name,
@@ -2,7 +2,6 @@ package io.legado.app.ui.main.bookshelf
import io.legado.app.data.entities.Book
import io.legado.app.data.entities.BookGroup
import io.legado.app.ui.config.bookshelfConfig.BookshelfConfig
import io.legado.app.ui.widget.components.list.ListUiState
data class BookshelfUiState(
@@ -15,12 +14,5 @@ data class BookshelfUiState(
val groupPreviews: Map<Long, List<Book>> = emptyMap(),
val selectedGroupIndex: Int = 0,
val loadingText: String? = null,
val bookshelfLayoutMode: Int = 1,
val bookshelfLayoutGrid: Int = 3,
val bookshelfLayoutCompact: Boolean = BookshelfConfig.bookshelfLayoutCompact,
val titleSmallFont: Boolean = BookshelfConfig.bookshelfTitleSmallFont,
val titleCenter: Boolean = BookshelfConfig.bookshelfTitleCenter,
val titleMaxLines: Int = BookshelfConfig.bookshelfTitleMaxLines,
val coverShadow: Boolean = BookshelfConfig.bookshelfCoverShadow,
val updatingBooks: Set<String> = emptySet()
) : ListUiState<Book>
@@ -1,8 +1,7 @@
package io.legado.app.ui.main.bookshelf
import android.app.Application
import android.content.res.Configuration
import androidx.lifecycle.asFlow
import androidx.compose.runtime.snapshotFlow
import androidx.lifecycle.viewModelScope
import com.google.gson.stream.JsonWriter
import io.legado.app.R
@@ -16,6 +15,7 @@ import io.legado.app.data.entities.Book
import io.legado.app.data.entities.BookGroup
import io.legado.app.data.entities.BookSource
import io.legado.app.data.entities.BookSourcePart
import io.legado.app.data.repository.BookGroupRepository
import io.legado.app.exception.NoStackTraceException
import io.legado.app.help.book.BookHelp
import io.legado.app.help.book.addType
@@ -83,7 +83,10 @@ import java.util.concurrent.Executors
import kotlin.math.max
import kotlin.math.min
class BookshelfViewModel(application: Application) : BaseViewModel(application) {
class BookshelfViewModel(
application: Application,
private val bookGroupRepository: BookGroupRepository
) : BaseViewModel(application) {
var addBookJob: Coroutine<*>? = null
private val groupIdFlow = MutableStateFlow(BookshelfConfig.saveTabPosition)
@@ -104,8 +107,10 @@ class BookshelfViewModel(application: Application) : BaseViewModel(application)
val scrollTrigger = MutableSharedFlow<Unit>(extraBufferCapacity = 1)
val groupsFlow: StateFlow<List<BookGroup>> = appDb.bookGroupDao.show.asFlow()
.map { it }
val groupsFlow: StateFlow<List<BookGroup>> = bookGroupRepository.flowShow()
.stateIn(viewModelScope, SharingStarted.WhileSubscribed(5000), emptyList())
val allGroupsFlow: StateFlow<List<BookGroup>> = bookGroupRepository.flowAll()
.stateIn(viewModelScope, SharingStarted.WhileSubscribed(5000), emptyList())
private val allBooksFlow = appDb.bookDao.flowAll().flowOn(Dispatchers.Default)
@@ -118,7 +123,8 @@ class BookshelfViewModel(application: Application) : BaseViewModel(application)
}
}.flowOn(Dispatchers.Default)
private val groupPreviewsFlow = combine(groupsFlow, allBooksFlow) { groups, allBooks ->
private val groupPreviewsFlow =
combine(groupsFlow, allBooksFlow, refreshTrigger) { groups, allBooks, _ ->
if (BookshelfConfig.bookGroupStyle in 2..3) {
groups.associate { group ->
val groupBooks = if (group.groupId == BookGroup.IdAll) {
@@ -165,8 +171,6 @@ class BookshelfViewModel(application: Application) : BaseViewModel(application)
) || it.author.contains(internal.searchKey, true)
}
}
val isLandscape =
context.resources.configuration.orientation == Configuration.ORIENTATION_LANDSCAPE
BookshelfUiState(
items = filteredBooks,
@@ -178,13 +182,6 @@ class BookshelfViewModel(application: Application) : BaseViewModel(application)
isSearch = internal.searchKey.isNotEmpty(),
isLoading = internal.loadingText != null,
loadingText = internal.loadingText,
bookshelfLayoutMode = if (isLandscape) BookshelfConfig.bookshelfLayoutModeLandscape else BookshelfConfig.bookshelfLayoutModePortrait,
bookshelfLayoutGrid = if (isLandscape) BookshelfConfig.bookshelfLayoutGridLandscape else BookshelfConfig.bookshelfLayoutGridPortrait,
bookshelfLayoutCompact = BookshelfConfig.bookshelfLayoutCompact,
titleSmallFont = BookshelfConfig.bookshelfTitleSmallFont,
titleCenter = BookshelfConfig.bookshelfTitleCenter,
titleMaxLines = BookshelfConfig.bookshelfTitleMaxLines,
coverShadow = BookshelfConfig.bookshelfCoverShadow,
updatingBooks = internal.updatingBooks
)
}.stateIn(viewModelScope, SharingStarted.WhileSubscribed(5000), BookshelfUiState())
@@ -201,6 +198,17 @@ class BookshelfViewModel(application: Application) : BaseViewModel(application)
}
}
// 监听排序配置变化,触发刷新
viewModelScope.launch {
snapshotFlow { BookshelfConfig.bookshelfSort }.collect { refresh() }
}
viewModelScope.launch {
snapshotFlow { BookshelfConfig.bookshelfSortOrder }.collect { refresh() }
}
viewModelScope.launch {
snapshotFlow { BookshelfConfig.bookGroupStyle }.collect { refresh() }
}
if (BookshelfConfig.autoRefreshBook) {
upAllBookToc()
}
@@ -212,8 +220,8 @@ class BookshelfViewModel(application: Application) : BaseViewModel(application)
}
private fun sortBooks(list: List<Book>, group: BookGroup?): List<Book> {
val bookSort = group?.getRealBookSort() ?: AppConfig.bookshelfSort
val isDescending = AppConfig.bookshelfSortOrder == 1
val bookSort = group?.getRealBookSort() ?: BookshelfConfig.bookshelfSort
val isDescending = BookshelfConfig.bookshelfSortOrder == 1
return when (bookSort) {
1 -> if (isDescending) list.sortedByDescending { it.latestChapterTime }
@@ -404,7 +412,8 @@ class BookshelfViewModel(application: Application) : BaseViewModel(application)
}
private fun postUpBooksCount() {
val count = if (AppConfig.showWaitUpCount) waitUpTocBooks.size + onUpTocBooks.size else 0
val count =
if (BookshelfConfig.showWaitUpCount) waitUpTocBooks.size + onUpTocBooks.size else 0
FlowEventBus.post(EventBus.UP_BOOKSHELF_COUNT, count)
}
@@ -1,9 +1,5 @@
package io.legado.app.ui.main.bookshelf
import androidx.compose.animation.AnimatedContent
import androidx.compose.animation.fadeIn
import androidx.compose.animation.fadeOut
import androidx.compose.animation.togetherWith
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Row
@@ -11,16 +7,16 @@ import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.lazy.items
import androidx.compose.foundation.lazy.rememberLazyListState
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.Add
import androidx.compose.material.icons.filled.Edit
import androidx.compose.material3.ExperimentalMaterial3Api
import androidx.compose.material3.Icon
import androidx.compose.material3.IconButton
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Switch
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.collectAsState
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
@@ -28,15 +24,18 @@ import androidx.compose.runtime.remember
import androidx.compose.runtime.setValue
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.unit.dp
import io.legado.app.R
import io.legado.app.data.entities.BookGroup
import io.legado.app.ui.book.group.GroupEditContent
import io.legado.app.ui.book.group.GroupViewModel
import io.legado.app.ui.widget.components.card.ReorderableSelectionItem
import io.legado.app.ui.widget.components.modalBottomSheet.GlassModalBottomSheet
import io.legado.app.ui.widget.components.settingItem.SettingItem
import io.legado.app.utils.move
import org.koin.androidx.compose.koinViewModel
import sh.calvin.reorderable.rememberReorderableLazyListState
@OptIn(ExperimentalMaterial3Api::class)
@Composable
@@ -45,95 +44,95 @@ fun GroupManageSheet(
viewModel: GroupViewModel = koinViewModel(),
bookshelfViewModel: BookshelfViewModel = koinViewModel()
) {
val groups by bookshelfViewModel.groupsFlow.collectAsState(initial = emptyList())
val context = LocalContext.current
val groups by bookshelfViewModel.allGroupsFlow.collectAsState()
var editingGroup by remember { mutableStateOf<BookGroup?>(null) }
var isEditing by remember { mutableStateOf(false) }
var listData by remember { mutableStateOf(groups) }
val listState = rememberLazyListState()
val reorderableState = rememberReorderableLazyListState(listState) { from, to ->
listData = listData.toMutableList().apply {
move(from.index, to.index)
}
}
LaunchedEffect(groups) {
if (!reorderableState.isAnyItemDragging) {
listData = groups
}
}
LaunchedEffect(reorderableState.isAnyItemDragging) {
if (!reorderableState.isAnyItemDragging) {
val updatedGroups = listData.mapIndexed { index, group ->
group.copy(order = index)
}
viewModel.upGroup(*updatedGroups.toTypedArray())
}
}
GlassModalBottomSheet(onDismissRequest = onDismissRequest) {
AnimatedContent(
targetState = isEditing,
transitionSpec = {
fadeIn().togetherWith(fadeOut())
},
label = "GroupManageTransition"
) { editing ->
if (editing) {
GroupEditContent(
group = editingGroup,
onDismissRequest = { isEditing = false },
viewModel = viewModel
)
} else {
Column(
if (isEditing) {
GroupEditContent(
group = editingGroup,
onDismissRequest = { isEditing = false },
viewModel = viewModel
)
} else {
Column(
modifier = Modifier
.fillMaxWidth()
.padding(bottom = 32.dp)
) {
Row(
modifier = Modifier
.fillMaxWidth()
.padding(horizontal = 16.dp)
.padding(bottom = 32.dp)
.padding(vertical = 8.dp),
horizontalArrangement = Arrangement.SpaceBetween,
verticalAlignment = Alignment.CenterVertically
) {
Row(
modifier = Modifier
.fillMaxWidth()
.padding(vertical = 8.dp),
horizontalArrangement = Arrangement.SpaceBetween,
verticalAlignment = Alignment.CenterVertically
) {
Text(
text = stringResource(R.string.group_manage),
style = MaterialTheme.typography.titleLarge
Text(
text = stringResource(R.string.group_manage),
style = MaterialTheme.typography.titleLarge
)
IconButton(onClick = {
editingGroup = null
isEditing = true
}) {
Icon(
Icons.Default.Add,
contentDescription = stringResource(R.string.add)
)
IconButton(onClick = {
editingGroup = null
isEditing = true
}) {
Icon(
Icons.Default.Add,
contentDescription = stringResource(R.string.add)
)
}
}
}
LazyColumn(
modifier = Modifier.fillMaxWidth(),
verticalArrangement = Arrangement.spacedBy(8.dp)
) {
items(groups, key = { it.groupId }) { group ->
GroupItem(
group = group,
onEdit = {
editingGroup = group
isEditing = true
},
onToggle = { isChecked ->
viewModel.upGroup(group.copy(show = isChecked))
}
)
}
LazyColumn(
state = listState,
modifier = Modifier.fillMaxWidth(),
verticalArrangement = Arrangement.spacedBy(8.dp)
) {
items(listData, key = { it.groupId }) { group ->
val manageNameInfo = remember(group) { group.getManageName(context) }
ReorderableSelectionItem(
state = reorderableState,
key = group.groupId,
title = group.groupName.ifBlank { manageNameInfo.suffix.orEmpty() },
subtitle = if (group.groupName.isNotBlank()) manageNameInfo.suffix else null,
isEnabled = group.show,
containerColor = MaterialTheme.colorScheme.surface,
onEnabledChange = { isChecked ->
viewModel.upGroup(group.copy(show = isChecked))
},
onClickEdit = {
editingGroup = group
isEditing = true
}
)
}
}
}
}
}
}
@Composable
private fun GroupItem(
group: BookGroup,
onEdit: () -> Unit,
onToggle: (Boolean) -> Unit
) {
SettingItem(
title = group.groupName,
onClick = onEdit,
trailingContent = {
Row(verticalAlignment = Alignment.CenterVertically) {
IconButton(onClick = onEdit) {
Icon(Icons.Default.Edit, contentDescription = stringResource(R.string.edit))
}
Switch(
checked = group.show,
onCheckedChange = onToggle
)
}
}
)
}
@@ -139,7 +139,7 @@ fun ExploreScreen(
}
ListScaffold(
title = stringResource(R.string.screen_find),
title = stringResource(R.string.discovery),
state = uiState,
subtitle = uiState.selectedGroup.ifEmpty { stringResource(R.string.all) },
onSearchQueryChange = { viewModel.search(it) },
@@ -60,15 +60,17 @@ fun SelectionItemCard(
onEnabledChange: ((Boolean) -> Unit)? = null,
onClickEdit: (() -> Unit)? = null,
trailingAction: @Composable (RowScope.() -> Unit)? = null,
dropdownContent: @Composable (ColumnScope.(onDismiss: () -> Unit) -> Unit)? = null
dropdownContent: @Composable (ColumnScope.(onDismiss: () -> Unit) -> Unit)? = null,
containerColor: Color? = null,
selectedContainerColor: Color? = null
) {
var showMenu by remember { mutableStateOf(false) }
val containerColor by animateColorAsState(
val animatedContainerColor by animateColorAsState(
targetValue = if (isSelected)
MaterialTheme.colorScheme.secondaryContainer
selectedContainerColor ?: MaterialTheme.colorScheme.secondaryContainer
else
MaterialTheme.colorScheme.surfaceContainerLow,
containerColor ?: MaterialTheme.colorScheme.surfaceContainerLow,
animationSpec = tween(durationMillis = 200, easing = FastOutSlowInEasing),
label = "CardColor"
)
@@ -79,7 +81,7 @@ fun SelectionItemCard(
.padding(horizontal = 16.dp)
.fillMaxWidth(),
shape = MaterialTheme.shapes.medium,
colors = CardDefaults.cardColors(containerColor = containerColor),
colors = CardDefaults.cardColors(containerColor = animatedContainerColor),
elevation = CardDefaults.cardElevation(defaultElevation = elevation)
) {
Row(
@@ -197,7 +199,9 @@ fun LazyItemScope.ReorderableSelectionItem(
onEnabledChange: ((Boolean) -> Unit)? = null,
onClickEdit: (() -> Unit)? = null,
trailingAction: @Composable (RowScope.() -> Unit)? = null,
dropdownContent: @Composable (ColumnScope.(onDismiss: () -> Unit) -> Unit)? = null
dropdownContent: @Composable (ColumnScope.(onDismiss: () -> Unit) -> Unit)? = null,
containerColor: Color? = null,
selectedContainerColor: Color? = null
) {
val hapticFeedback = LocalHapticFeedback.current
@@ -220,6 +224,8 @@ fun LazyItemScope.ReorderableSelectionItem(
onClickEdit = onClickEdit,
trailingAction = trailingAction,
dropdownContent = dropdownContent,
containerColor = containerColor,
selectedContainerColor = selectedContainerColor,
modifier = modifier
.zIndex(if (isDragging) 1f else 0f)
.then(
@@ -58,3 +58,9 @@ fun <T> MutableList<T>.removeLastElement(): T {
removeAt(lastIndex)
}
}
fun <T> MutableList<T>.move(fromIndex: Int, toIndex: Int) {
if (fromIndex == toIndex) return
val element = removeAt(fromIndex)
add(toIndex, element)
}