feat: select all books button in library

* Added "Select all books" button to the Library which selects all books in the current category
This commit is contained in:
Acclorite 2025-09-28 17:48:48 +03:00
parent 14cf04a8ce
commit 30ee74cfff
No known key found for this signature in database
GPG key ID: 6E54C611F6EE8593
9 changed files with 54 additions and 10 deletions

View file

@ -8,6 +8,7 @@ package ua.acclorite.book_story.presentation.library
import androidx.compose.runtime.Immutable
import ua.acclorite.book_story.domain.model.library.Category
import ua.acclorite.book_story.presentation.library.model.SelectableBook
@Immutable
sealed class LibraryEvent {
@ -35,6 +36,10 @@ sealed class LibraryEvent {
val select: Boolean? = null
) : LibraryEvent()
data class OnSelectBooks(
val books: List<SelectableBook>
) : LibraryEvent()
data object OnShowMoveDialog : LibraryEvent()
data class OnActionMoveDialog(

View file

@ -191,6 +191,25 @@ class LibraryModel @Inject constructor(
}
}
is LibraryEvent.OnSelectBooks -> {
withContext(Dispatchers.Default) {
val editedList = _state.value.books.map { book ->
if (event.books.any { book.data.id == it.data.id }) book.copy(
selected = if (event.books.size > 1) true else !book.selected
)
else book
}
_state.update {
it.copy(
books = editedList,
selectedItemsCount = editedList.filter { book -> book.selected }.size,
hasSelectedItems = editedList.any { book -> book.selected }
)
}
}
}
is LibraryEvent.OnShowMoveDialog -> {
_state.update {
it.copy(

View file

@ -71,7 +71,6 @@ object LibraryScreen : Screen, Parcelable {
state.value.books.any { book ->
book.data.categories.none { category -> category in categoryIds }
} || categories.isEmpty() || settings.libraryShowDefaultTab.lastValue
}
}
@ -142,6 +141,7 @@ object LibraryScreen : Screen, Parcelable {
requestFocus = screenModel::onEvent,
searchQueryChange = screenModel::onEvent,
search = screenModel::onEvent,
selectBooks = screenModel::onEvent,
clearSelectedBooks = screenModel::onEvent,
showMoveDialog = screenModel::onEvent,
actionMoveDialog = screenModel::onEvent,

View file

@ -59,6 +59,7 @@ fun LibraryContent(
requestFocus: (LibraryEvent.OnRequestFocus) -> Unit,
searchQueryChange: (LibraryEvent.OnSearchQueryChange) -> Unit,
search: (LibraryEvent.OnSearch) -> Unit,
selectBooks: (LibraryEvent.OnSelectBooks) -> Unit,
clearSelectedBooks: (LibraryEvent.OnClearSelectedBooks) -> Unit,
showMoveDialog: (LibraryEvent.OnShowMoveDialog) -> Unit,
showDeleteDialog: (LibraryEvent.OnShowDeleteDialog) -> Unit,
@ -126,6 +127,7 @@ fun LibraryContent(
searchQueryChange = searchQueryChange,
search = search,
selectBook = selectBook,
selectBooks = selectBooks,
clearSelectedBooks = clearSelectedBooks,
showMoveDialog = showMoveDialog,
showDeleteDialog = showDeleteDialog,

View file

@ -57,6 +57,7 @@ fun LibraryScaffold(
requestFocus: (LibraryEvent.OnRequestFocus) -> Unit,
searchQueryChange: (LibraryEvent.OnSearchQueryChange) -> Unit,
search: (LibraryEvent.OnSearch) -> Unit,
selectBooks: (LibraryEvent.OnSelectBooks) -> Unit,
clearSelectedBooks: (LibraryEvent.OnClearSelectedBooks) -> Unit,
showMoveDialog: (LibraryEvent.OnShowMoveDialog) -> Unit,
showDeleteDialog: (LibraryEvent.OnShowDeleteDialog) -> Unit,
@ -90,6 +91,7 @@ fun LibraryScaffold(
requestFocus = requestFocus,
searchQueryChange = searchQueryChange,
search = search,
selectBooks = selectBooks,
clearSelectedBooks = clearSelectedBooks,
showMoveDialog = showMoveDialog,
showDeleteDialog = showDeleteDialog,

View file

@ -30,11 +30,12 @@ import androidx.compose.ui.graphics.Color
import androidx.compose.ui.unit.dp
import kotlinx.coroutines.launch
import ua.acclorite.book_story.domain.model.library.Category
import ua.acclorite.book_story.presentation.library.model.SelectableBook
import ua.acclorite.book_story.ui.common.components.common.StyledText
@Composable
fun LibraryTabs(
categoriesWithBookCount: List<Pair<Category, Int>>,
categoriesWithBooks: List<Pair<Category, List<SelectableBook>>>,
pagerState: PagerState,
itemCountBackgroundColor: Color,
showBookCount: Boolean
@ -70,7 +71,7 @@ fun LibraryTabs(
}
}
) {
categoriesWithBookCount.forEachIndexed { index, tabItem ->
categoriesWithBooks.forEachIndexed { index, tabItem ->
Tab(
selected = pagerState.currentPage == index,
onClick = {
@ -91,7 +92,7 @@ fun LibraryTabs(
if (showBookCount) {
Spacer(modifier = Modifier.width(6.dp))
StyledText(
text = tabItem.second.toString(),
text = tabItem.second.size.toString(),
modifier = Modifier
.background(
itemCountBackgroundColor,

View file

@ -23,6 +23,7 @@ import androidx.compose.material.icons.automirrored.filled.ArrowBack
import androidx.compose.material.icons.filled.Clear
import androidx.compose.material.icons.filled.FilterList
import androidx.compose.material.icons.filled.Search
import androidx.compose.material.icons.filled.SelectAll
import androidx.compose.material.icons.outlined.Delete
import androidx.compose.material.icons.outlined.MoveUp
import androidx.compose.material3.ExperimentalMaterial3Api
@ -72,13 +73,14 @@ fun LibraryTopBar(
requestFocus: (LibraryEvent.OnRequestFocus) -> Unit,
searchQueryChange: (LibraryEvent.OnSearchQueryChange) -> Unit,
search: (LibraryEvent.OnSearch) -> Unit,
selectBooks: (LibraryEvent.OnSelectBooks) -> Unit,
clearSelectedBooks: (LibraryEvent.OnClearSelectedBooks) -> Unit,
showMoveDialog: (LibraryEvent.OnShowMoveDialog) -> Unit,
showDeleteDialog: (LibraryEvent.OnShowDeleteDialog) -> Unit,
showFilterBottomSheet: (LibraryEvent.OnShowFilterBottomSheet) -> Unit
) {
val defaultCategory = stringResource(id = R.string.default_tab)
val categoriesWithBookCount = remember(
val categoriesWithBooks = remember(
books,
categories,
showDefaultCategory,
@ -86,7 +88,7 @@ fun LibraryTopBar(
) {
derivedStateOf {
categories.filterNot { it.id == -1 }.map { category ->
category to books.count { it.data.categories.any { it == category.id } }
category to books.filter { it.data.categories.any { it == category.id } }
}.toMutableList().apply {
if (showDefaultCategory) {
val categoryIds = categories.map { it.id }.toSet()
@ -95,7 +97,7 @@ fun LibraryTopBar(
Category(
id = -1,
title = defaultCategory
) to books.count { book ->
) to books.filter { book ->
book.data.categories.none { category -> category in categoryIds }
}
)
@ -103,9 +105,9 @@ fun LibraryTopBar(
}.toList()
}
}
val currentCategory = remember(pagerState.currentPage, categoriesWithBookCount) {
val currentCategory = remember(pagerState.currentPage, categoriesWithBooks) {
derivedStateOf {
categoriesWithBookCount.value[pagerState.currentPage]
categoriesWithBooks.value[pagerState.currentPage]
}
}
@ -226,6 +228,17 @@ fun LibraryTopBar(
)
},
contentActions = {
IconButton(
icon = Icons.Default.SelectAll,
contentDescription = R.string.select_all_books_content_desc,
disableOnClick = false,
) {
selectBooks(
LibraryEvent.OnSelectBooks(
books = currentCategory.value.second
)
)
}
IconButton(
icon = Icons.Outlined.MoveUp,
contentDescription = R.string.move_books_content_desc,
@ -252,7 +265,7 @@ fun LibraryTopBar(
exit = shrinkVertically()
) {
LibraryTabs(
categoriesWithBookCount = categoriesWithBookCount.value,
categoriesWithBooks = categoriesWithBooks.value,
pagerState = pagerState,
itemCountBackgroundColor = animatedItemCountBackgroundColor.value,
showBookCount = showBookCount

View file

@ -475,6 +475,7 @@
<string name="clear_selected_items_content_desc">Очистити обрані предмети</string>
<string name="add_files_content_desc">Додати файли</string>
<string name="select_all_files_content_desc">Вибрати всі файли</string>
<string name="select_all_books_content_desc">Вибрати всі книги</string>
<string name="exit_search_content_desc">Вийти з пошуку</string>
<string name="delete_whole_history_content_desc">Видалити всю історію</string>
<string name="move_books_content_desc">Перемістити обрані книги</string>

View file

@ -611,6 +611,7 @@
<string name="clear_selected_items_content_desc">Clear selected items</string>
<string name="add_files_content_desc">Add files</string>
<string name="select_all_files_content_desc">Select all files</string>
<string name="select_all_books_content_desc">Select all books</string>
<string name="exit_search_content_desc">Exit search</string>
<string name="delete_whole_history_content_desc">Delete whole history</string>
<string name="move_books_content_desc">Move selected books</string>