refactor: library viewmodel

This commit is contained in:
Acclorite 2025-08-16 22:41:02 +03:00
parent d50b1a5f9a
commit 3817cc1980
No known key found for this signature in database
GPG key ID: 6E54C611F6EE8593
13 changed files with 283 additions and 223 deletions

View file

@ -0,0 +1,20 @@
/*
* Book's Story free and open-source Material You eBook reader.
* Copyright (C) 2024-2025 Acclorite
* SPDX-License-Identifier: GPL-3.0-only
*/
package ua.acclorite.book_story.presentation.library
import androidx.compose.runtime.Immutable
@Immutable
sealed class LibraryEffect {
data object OnRequestFocus : LibraryEffect()
data object OnBooksMoved : LibraryEffect()
data object OnBooksDeleted : LibraryEffect()
data object OnNavigateToLibrarySettings : LibraryEffect()
data object OnNavigateToBrowse : LibraryEffect()
data class OnNavigateToBookInfo(val id: Int) : LibraryEffect()
data class OnNavigateToReader(val id: Int) : LibraryEffect()
}

View file

@ -6,9 +6,7 @@
package ua.acclorite.book_story.presentation.library
import android.content.Context
import androidx.compose.runtime.Immutable
import androidx.compose.ui.focus.FocusRequester
import ua.acclorite.book_story.domain.model.library.Category
@Immutable
@ -28,9 +26,7 @@ sealed class LibraryEvent {
data object OnSearch : LibraryEvent()
data class OnRequestFocus(
val focusRequester: FocusRequester
) : LibraryEvent()
data object OnRequestFocus : LibraryEvent()
data object OnClearSelectedBooks : LibraryEvent()
@ -42,19 +38,28 @@ sealed class LibraryEvent {
data object OnShowMoveDialog : LibraryEvent()
data class OnActionMoveDialog(
val selectedCategories: List<Category>,
val context: Context
val selectedCategories: List<Category>
) : LibraryEvent()
data object OnShowDeleteDialog : LibraryEvent()
data class OnActionDeleteDialog(
val context: Context
) : LibraryEvent()
data object OnActionDeleteDialog : LibraryEvent()
data object OnDismissDialog : LibraryEvent()
data object OnShowFilterBottomSheet : LibraryEvent()
data object OnDismissBottomSheet : LibraryEvent()
data object OnNavigateToLibrarySettings : LibraryEvent()
data object OnNavigateToBrowse : LibraryEvent()
data class OnNavigateToBookInfo(
val id: Int
) : LibraryEvent()
data class OnNavigateToReader(
val id: Int
) : LibraryEvent()
}

View file

@ -12,7 +12,10 @@ import dagger.hilt.android.lifecycle.HiltViewModel
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.Job
import kotlinx.coroutines.delay
import kotlinx.coroutines.ensureActive
import kotlinx.coroutines.flow.MutableSharedFlow
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.asSharedFlow
import kotlinx.coroutines.flow.asStateFlow
import kotlinx.coroutines.flow.collectLatest
import kotlinx.coroutines.flow.receiveAsFlow
@ -20,17 +23,14 @@ import kotlinx.coroutines.launch
import kotlinx.coroutines.sync.Mutex
import kotlinx.coroutines.sync.withLock
import kotlinx.coroutines.withContext
import kotlinx.coroutines.yield
import ua.acclorite.book_story.R
import ua.acclorite.book_story.domain.model.library.Book
import ua.acclorite.book_story.domain.use_case.book.DeleteBookUseCase
import ua.acclorite.book_story.domain.use_case.book.SearchBooksUseCase
import ua.acclorite.book_story.domain.use_case.book.UpdateBookUseCase
import ua.acclorite.book_story.presentation.browse.BrowseScreen
import ua.acclorite.book_story.presentation.history.HistoryScreen
import ua.acclorite.book_story.presentation.library.model.SelectableBook
import ua.acclorite.book_story.ui.common.helpers.showToast
import javax.inject.Inject
import kotlin.coroutines.coroutineContext
@HiltViewModel
class LibraryModel @Inject constructor(
@ -44,22 +44,21 @@ class LibraryModel @Inject constructor(
private val _state = MutableStateFlow(LibraryState())
val state = _state.asStateFlow()
private val _effects = MutableSharedFlow<LibraryEffect>()
val effects = _effects.asSharedFlow()
init {
viewModelScope.launch(Dispatchers.IO) {
onEvent(
LibraryEvent.OnRefreshList(
loading = true,
hideSearch = true
)
onEvent(
LibraryEvent.OnRefreshList(
loading = true,
hideSearch = true
)
}
)
/* Observe channel - - - - - - - - - - - */
viewModelScope.launch(Dispatchers.IO) {
viewModelScope.launch {
LibraryScreen.refreshListChannel.receiveAsFlow().collectLatest {
delay(it)
yield()
onEvent(
LibraryEvent.OnRefreshList(
loading = false,
@ -75,33 +74,42 @@ class LibraryModel @Inject constructor(
private var searchQueryChange: Job? = null
fun onEvent(event: LibraryEvent) {
when (event) {
is LibraryEvent.OnRefreshList -> {
refreshJob?.cancel()
refreshJob = viewModelScope.launch(Dispatchers.IO) {
_state.update {
it.copy(
isRefreshing = true,
isLoading = event.loading,
showSearch = if (event.hideSearch) false else it.showSearch
)
}
viewModelScope.launch {
when (event) {
is LibraryEvent.OnRefreshList -> {
refreshJob?.cancel()
refreshJob = viewModelScope.launch(Dispatchers.Default) {
_state.update {
it.copy(
isRefreshing = true,
isLoading = event.loading,
showSearch = if (event.hideSearch) false else it.showSearch
)
}
yield()
getBooksFromDatabase()
ensureActive()
val books = searchBooksUseCase(
if (_state.value.showSearch) _state.value.searchQuery
else ""
).map { book -> SelectableBook(book, false) }
_state.update {
it.copy(
books = books,
hasSelectedItems = false,
isLoading = false
)
}
delay(500)
_state.update {
it.copy(
isRefreshing = false,
isLoading = false
)
delay(500) // Delay for UI smoothness
_state.update {
it.copy(
isRefreshing = false
)
}
}
}
}
is LibraryEvent.OnSearchVisibility -> {
viewModelScope.launch(Dispatchers.IO) {
is LibraryEvent.OnSearchVisibility -> {
if (!event.show) {
onEvent(
LibraryEvent.OnRefreshList(
@ -109,41 +117,31 @@ class LibraryModel @Inject constructor(
hideSearch = true
)
)
} else {
_state.update {
it.copy(
searchQuery = "",
hasFocused = false
)
}
}
_state.update {
it.copy(
showSearch = event.show
showSearch = event.show,
searchQuery = if (event.show) "" else it.searchQuery,
hasFocused = if (event.show) false else it.hasFocused
)
}
}
}
is LibraryEvent.OnSearchQueryChange -> {
viewModelScope.launch {
is LibraryEvent.OnSearchQueryChange -> {
_state.update {
it.copy(
searchQuery = event.query
)
}
searchQueryChange?.cancel()
searchQueryChange = launch(Dispatchers.IO) {
searchQueryChange = viewModelScope.launch(Dispatchers.IO) {
delay(500)
yield()
onEvent(LibraryEvent.OnSearch)
}
}
}
is LibraryEvent.OnSearch -> {
viewModelScope.launch(Dispatchers.IO) {
is LibraryEvent.OnSearch -> {
onEvent(
LibraryEvent.OnRefreshList(
loading = false,
@ -151,12 +149,10 @@ class LibraryModel @Inject constructor(
)
)
}
}
is LibraryEvent.OnRequestFocus -> {
viewModelScope.launch(Dispatchers.Main) {
is LibraryEvent.OnRequestFocus -> {
if (!_state.value.hasFocused) {
event.focusRequester.requestFocus()
_effects.emit(LibraryEffect.OnRequestFocus)
_state.update {
it.copy(
hasFocused = true
@ -164,170 +160,152 @@ class LibraryModel @Inject constructor(
}
}
}
}
is LibraryEvent.OnClearSelectedBooks -> {
viewModelScope.launch(Dispatchers.IO) {
_state.update {
it.copy(
books = it.books.map { book -> book.copy(selected = false) },
hasSelectedItems = false
)
is LibraryEvent.OnClearSelectedBooks -> {
withContext(Dispatchers.Default) {
_state.update {
it.copy(
books = it.books.map { book -> book.copy(selected = false) },
hasSelectedItems = false
)
}
}
}
}
is LibraryEvent.OnSelectBook -> {
viewModelScope.launch(Dispatchers.IO) {
val editedList = _state.value.books.map {
if (it.data.id == event.id) it.copy(selected = event.select ?: !it.selected)
else it
}
is LibraryEvent.OnSelectBook -> {
withContext(Dispatchers.Default) {
val editedList = _state.value.books.map {
if (it.data.id == event.id) it.copy(
selected = event.select ?: !it.selected
)
else it
}
_state.update {
it.copy(
books = editedList,
selectedItemsCount = editedList.filter { book -> book.selected }.size,
hasSelectedItems = editedList.any { book -> book.selected }
)
_state.update {
it.copy(
books = editedList,
selectedItemsCount = editedList.filter { book -> book.selected }.size,
hasSelectedItems = editedList.any { book -> book.selected }
)
}
}
}
}
is LibraryEvent.OnShowMoveDialog -> {
viewModelScope.launch {
is LibraryEvent.OnShowMoveDialog -> {
_state.update {
it.copy(
dialog = LibraryScreen.MOVE_DIALOG
)
}
}
}
is LibraryEvent.OnActionMoveDialog -> {
viewModelScope.launch {
_state.value.books.forEach { book ->
if (!book.selected) return@forEach
updateBookUseCase(
book.data.copy(
categories = event.selectedCategories.map { it.id }
)
)
}
_state.update {
it.copy(
books = it.books.map { book ->
if (!book.selected) return@map book
book.copy(
data = book.data.copy(
categories = event.selectedCategories.map { it.id }
),
selected = false
is LibraryEvent.OnActionMoveDialog -> {
withContext(Dispatchers.Default) {
_state.value.books.forEach { book ->
if (!book.selected) return@forEach
updateBookUseCase(
book.data.copy(
categories = event.selectedCategories.map { it.id }
)
},
hasSelectedItems = false,
dialog = null
)
}
)
}
HistoryScreen.refreshListChannel.trySend(0)
_state.update {
it.copy(
books = it.books.map { book ->
if (!book.selected) return@map book
book.copy(
data = book.data.copy(
categories = event.selectedCategories.map { it.id }
),
selected = false
)
},
hasSelectedItems = false,
dialog = null
)
}
withContext(Dispatchers.Main) {
event.context
.getString(R.string.books_moved)
.showToast(context = event.context)
HistoryScreen.refreshListChannel.trySend(0)
_effects.emit(LibraryEffect.OnBooksMoved)
}
}
}
is LibraryEvent.OnShowDeleteDialog -> {
viewModelScope.launch {
is LibraryEvent.OnShowDeleteDialog -> {
_state.update {
it.copy(
dialog = LibraryScreen.DELETE_DIALOG
)
}
}
}
is LibraryEvent.OnActionDeleteDialog -> {
viewModelScope.launch {
_state.value.books.forEach {
if (!it.selected) return@forEach
deleteBookUseCase(it.data)
}
is LibraryEvent.OnActionDeleteDialog -> {
withContext(Dispatchers.Default) {
_state.value.books.forEach {
if (!it.selected) return@forEach
deleteBookUseCase(it.data)
}
_state.update {
it.copy(
books = it.books.filter { book -> !book.selected },
hasSelectedItems = false,
dialog = null
)
}
_state.update {
it.copy(
books = it.books.filter { book -> !book.selected },
hasSelectedItems = false,
dialog = null
)
}
HistoryScreen.refreshListChannel.trySend(0)
BrowseScreen.refreshListChannel.trySend(Unit)
withContext(Dispatchers.Main) {
event.context
.getString(R.string.books_deleted)
.showToast(context = event.context)
HistoryScreen.refreshListChannel.trySend(0)
BrowseScreen.refreshListChannel.trySend(Unit)
_effects.emit(LibraryEffect.OnBooksDeleted)
}
}
}
is LibraryEvent.OnDismissDialog -> {
viewModelScope.launch {
is LibraryEvent.OnDismissDialog -> {
_state.update {
it.copy(
dialog = null
)
}
}
}
is LibraryEvent.OnShowFilterBottomSheet -> {
viewModelScope.launch {
is LibraryEvent.OnShowFilterBottomSheet -> {
_state.update {
it.copy(
bottomSheet = LibraryScreen.FILTER_BOTTOM_SHEET
)
}
}
}
is LibraryEvent.OnDismissBottomSheet -> {
viewModelScope.launch {
is LibraryEvent.OnDismissBottomSheet -> {
_state.update {
it.copy(
bottomSheet = null
)
}
}
is LibraryEvent.OnNavigateToLibrarySettings -> {
_effects.emit(LibraryEffect.OnNavigateToLibrarySettings)
}
is LibraryEvent.OnNavigateToBrowse -> {
_effects.emit(LibraryEffect.OnNavigateToBrowse)
}
is LibraryEvent.OnNavigateToBookInfo -> {
_effects.emit(LibraryEffect.OnNavigateToBookInfo(event.id))
}
is LibraryEvent.OnNavigateToReader -> {
_effects.emit(LibraryEffect.OnNavigateToReader(event.id))
}
}
}
}
private suspend fun getBooksFromDatabase(
query: String = if (_state.value.showSearch) _state.value.searchQuery else ""
) {
val books = searchBooksUseCase(query)
.sortedWith(compareByDescending<Book> { it.lastOpened }.thenBy { it.title })
.map { book -> SelectableBook(book, false) }
_state.update {
it.copy(
books = books,
hasSelectedItems = false,
isLoading = false
)
}
}
private suspend inline fun <T> MutableStateFlow<T>.update(function: (T) -> T) {
mutex.withLock {
yield()
coroutineContext.ensureActive()
this.value = function(this.value)
}
}

View file

@ -23,16 +23,11 @@ import kotlinx.coroutines.flow.collectLatest
import kotlinx.coroutines.flow.receiveAsFlow
import kotlinx.parcelize.IgnoredOnParcel
import kotlinx.parcelize.Parcelize
import ua.acclorite.book_story.presentation.book_info.BookInfoScreen
import ua.acclorite.book_story.presentation.browse.BrowseScreen
import ua.acclorite.book_story.presentation.history.HistoryScreen
import ua.acclorite.book_story.presentation.navigator.Screen
import ua.acclorite.book_story.presentation.reader.ReaderScreen
import ua.acclorite.book_story.presentation.settings.LibrarySettingsScreen
import ua.acclorite.book_story.presentation.settings.SettingsModel
import ua.acclorite.book_story.ui.common.helpers.LocalSettings
import ua.acclorite.book_story.ui.library.LibraryContent
import ua.acclorite.book_story.ui.navigator.LocalNavigator
import ua.acclorite.book_story.ui.library.LibraryEffects
@Parcelize
object LibraryScreen : Screen, Parcelable {
@ -58,7 +53,6 @@ object LibraryScreen : Screen, Parcelable {
@OptIn(ExperimentalMaterialApi::class)
@Composable
override fun Content() {
val navigator = LocalNavigator.current
val screenModel = hiltViewModel<LibraryModel>()
val settingsModel = hiltViewModel<SettingsModel>()
val settings = LocalSettings.current
@ -105,6 +99,11 @@ object LibraryScreen : Screen, Parcelable {
}
}
LibraryEffects(
effects = screenModel.effects,
focusRequester = focusRequester
)
LibraryContent(
books = state.value.books,
selectedItemsCount = state.value.selectedItemsCount,
@ -150,19 +149,10 @@ object LibraryScreen : Screen, Parcelable {
showFilterBottomSheet = screenModel::onEvent,
dismissBottomSheet = screenModel::onEvent,
dismissDialog = screenModel::onEvent,
navigateToBrowse = {
navigator.push(BrowseScreen)
},
navigateToReader = {
HistoryScreen.insertHistoryChannel.trySend(it)
navigator.push(ReaderScreen(it))
},
navigateToBookInfo = {
navigator.push(BookInfoScreen(bookId = it))
},
navigateToLibrarySettings = {
navigator.push(LibrarySettingsScreen)
}
navigateToBrowse = screenModel::onEvent,
navigateToReader = screenModel::onEvent,
navigateToBookInfo = screenModel::onEvent,
navigateToLibrarySettings = screenModel::onEvent
)
}
}

View file

@ -69,10 +69,10 @@ fun LibraryContent(
dismissDialog: (LibraryEvent.OnDismissDialog) -> Unit,
showFilterBottomSheet: (LibraryEvent.OnShowFilterBottomSheet) -> Unit,
dismissBottomSheet: (LibraryEvent.OnDismissBottomSheet) -> Unit,
navigateToBrowse: () -> Unit,
navigateToBookInfo: (id: Int) -> Unit,
navigateToReader: (id: Int) -> Unit,
navigateToLibrarySettings: () -> Unit
navigateToBrowse: (LibraryEvent.OnNavigateToBrowse) -> Unit,
navigateToBookInfo: (LibraryEvent.OnNavigateToBookInfo) -> Unit,
navigateToReader: (LibraryEvent.OnNavigateToReader) -> Unit,
navigateToLibrarySettings: (LibraryEvent.OnNavigateToLibrarySettings) -> Unit
) {
LibraryDialog(
dialog = dialog,
@ -146,6 +146,6 @@ fun LibraryContent(
pagerState = pagerState,
doublePressExit = doublePressExit,
clearSelectedBooks = clearSelectedBooks,
searchVisibility = searchVisibility,
searchVisibility = searchVisibility
)
}

View file

@ -9,7 +9,6 @@ package ua.acclorite.book_story.ui.library
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.outlined.DeleteOutline
import androidx.compose.runtime.Composable
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.res.stringResource
import ua.acclorite.book_story.R
import ua.acclorite.book_story.presentation.library.LibraryEvent
@ -21,8 +20,6 @@ fun LibraryDeleteDialog(
actionDeleteDialog: (LibraryEvent.OnActionDeleteDialog) -> Unit,
dismissDialog: (LibraryEvent.OnDismissDialog) -> Unit
) {
val context = LocalContext.current
Dialog(
title = stringResource(id = R.string.delete_books),
icon = Icons.Outlined.DeleteOutline,
@ -35,11 +32,7 @@ fun LibraryDeleteDialog(
dismissDialog(LibraryEvent.OnDismissDialog)
},
onAction = {
actionDeleteDialog(
LibraryEvent.OnActionDeleteDialog(
context = context
)
)
actionDeleteDialog(LibraryEvent.OnActionDeleteDialog)
},
withContent = false
)

View file

@ -22,7 +22,7 @@ fun LibraryDialog(
actionMoveDialog: (LibraryEvent.OnActionMoveDialog) -> Unit,
actionDeleteDialog: (LibraryEvent.OnActionDeleteDialog) -> Unit,
dismissDialog: (LibraryEvent.OnDismissDialog) -> Unit,
navigateToLibrarySettings: () -> Unit
navigateToLibrarySettings: (LibraryEvent.OnNavigateToLibrarySettings) -> Unit
) {
when (dialog) {
LibraryScreen.MOVE_DIALOG -> {

View file

@ -0,0 +1,65 @@
/*
* Book's Story free and open-source Material You eBook reader.
* Copyright (C) 2024-2025 Acclorite
* SPDX-License-Identifier: GPL-3.0-only
*/
package ua.acclorite.book_story.ui.library
import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.ui.focus.FocusRequester
import androidx.compose.ui.platform.LocalContext
import kotlinx.coroutines.flow.SharedFlow
import ua.acclorite.book_story.R
import ua.acclorite.book_story.presentation.book_info.BookInfoScreen
import ua.acclorite.book_story.presentation.browse.BrowseScreen
import ua.acclorite.book_story.presentation.history.HistoryScreen
import ua.acclorite.book_story.presentation.library.LibraryEffect
import ua.acclorite.book_story.presentation.reader.ReaderScreen
import ua.acclorite.book_story.presentation.settings.LibrarySettingsScreen
import ua.acclorite.book_story.ui.common.helpers.showToast
import ua.acclorite.book_story.ui.navigator.LocalNavigator
@Composable
fun LibraryEffects(effects: SharedFlow<LibraryEffect>, focusRequester: FocusRequester) {
val context = LocalContext.current
val navigator = LocalNavigator.current
LaunchedEffect(effects, focusRequester, context, navigator) {
effects.collect { effect ->
when (effect) {
is LibraryEffect.OnRequestFocus -> {
focusRequester.requestFocus()
}
is LibraryEffect.OnBooksMoved -> {
context.getString(R.string.books_moved)
.showToast(context = context)
}
is LibraryEffect.OnBooksDeleted -> {
context.getString(R.string.books_deleted)
.showToast(context = context)
}
is LibraryEffect.OnNavigateToLibrarySettings -> {
navigator.push(LibrarySettingsScreen)
}
is LibraryEffect.OnNavigateToBrowse -> {
navigator.push(BrowseScreen)
}
is LibraryEffect.OnNavigateToBookInfo -> {
navigator.push(BookInfoScreen(bookId = effect.id))
}
is LibraryEffect.OnNavigateToReader -> {
HistoryScreen.insertHistoryChannel.trySend(effect.id)
navigator.push(ReaderScreen(effect.id))
}
}
}
}
}

View file

@ -16,6 +16,7 @@ import androidx.compose.ui.Modifier
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.res.stringResource
import ua.acclorite.book_story.R
import ua.acclorite.book_story.presentation.library.LibraryEvent
import ua.acclorite.book_story.ui.common.components.placeholder.EmptyPlaceholder
import ua.acclorite.book_story.ui.theme.Transitions
@ -24,7 +25,7 @@ fun BoxScope.LibraryEmptyPlaceholder(
isLoading: Boolean,
isRefreshing: Boolean,
isBooksEmpty: Boolean,
navigateToBrowse: () -> Unit
navigateToBrowse: (LibraryEvent.OnNavigateToBrowse) -> Unit
) {
AnimatedVisibility(
visible = !isLoading
@ -40,7 +41,7 @@ fun BoxScope.LibraryEmptyPlaceholder(
modifier = Modifier.align(Alignment.Center),
actionTitle = stringResource(id = R.string.add_book)
) {
navigateToBrowse()
navigateToBrowse(LibraryEvent.OnNavigateToBrowse)
}
}
}

View file

@ -14,7 +14,6 @@ import androidx.compose.runtime.derivedStateOf
import androidx.compose.runtime.mutableStateListOf
import androidx.compose.runtime.remember
import androidx.compose.ui.Modifier
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.unit.dp
import ua.acclorite.book_story.R
@ -31,10 +30,8 @@ fun LibraryMoveDialog(
categories: List<Category>,
actionMoveDialog: (LibraryEvent.OnActionMoveDialog) -> Unit,
dismissDialog: (LibraryEvent.OnDismissDialog) -> Unit,
navigateToLibrarySettings: () -> Unit
navigateToLibrarySettings: (LibraryEvent.OnNavigateToLibrarySettings) -> Unit
) {
val context = LocalContext.current
val selectedBooks = remember {
derivedStateOf {
books.filter { it.selected }
@ -61,14 +58,13 @@ fun LibraryMoveDialog(
onAction = {
actionMoveDialog(
LibraryEvent.OnActionMoveDialog(
selectedCategories = selectedCategories,
context = context
selectedCategories = selectedCategories
)
)
},
secondaryAction = stringResource(id = R.string.edit),
onSecondaryAction = {
navigateToLibrarySettings()
navigateToLibrarySettings(LibraryEvent.OnNavigateToLibrarySettings)
dismissDialog(LibraryEvent.OnDismissDialog)
},
withContent = true,

View file

@ -44,9 +44,9 @@ fun LibraryPager(
isLoading: Boolean,
isRefreshing: Boolean,
selectBook: (LibraryEvent.OnSelectBook) -> Unit,
navigateToBrowse: () -> Unit,
navigateToBookInfo: (id: Int) -> Unit,
navigateToReader: (id: Int) -> Unit,
navigateToBrowse: (LibraryEvent.OnNavigateToBrowse) -> Unit,
navigateToBookInfo: (LibraryEvent.OnNavigateToBookInfo) -> Unit,
navigateToReader: (LibraryEvent.OnNavigateToReader) -> Unit,
) {
val categorizedBooks = remember(
books,
@ -172,8 +172,20 @@ fun LibraryPager(
)
)
},
navigateToBookInfo = { navigateToBookInfo(book.data.id) },
navigateToReader = { navigateToReader(book.data.id) },
navigateToBookInfo = {
navigateToBookInfo(
LibraryEvent.OnNavigateToBookInfo(
book.data.id
)
)
},
navigateToReader = {
navigateToReader(
LibraryEvent.OnNavigateToReader(
book.data.id
)
)
}
)
}
}

View file

@ -63,9 +63,9 @@ fun LibraryScaffold(
showMoveDialog: (LibraryEvent.OnShowMoveDialog) -> Unit,
showDeleteDialog: (LibraryEvent.OnShowDeleteDialog) -> Unit,
showFilterBottomSheet: (LibraryEvent.OnShowFilterBottomSheet) -> Unit,
navigateToBrowse: () -> Unit,
navigateToBookInfo: (id: Int) -> Unit,
navigateToReader: (id: Int) -> Unit,
navigateToBrowse: (LibraryEvent.OnNavigateToBrowse) -> Unit,
navigateToBookInfo: (LibraryEvent.OnNavigateToBookInfo) -> Unit,
navigateToReader: (LibraryEvent.OnNavigateToReader) -> Unit,
) {
Scaffold(
Modifier

View file

@ -189,7 +189,7 @@ fun LibraryTopBar(
modifier = Modifier
.focusRequester(focusRequester)
.onGloballyPositioned {
requestFocus(LibraryEvent.OnRequestFocus(focusRequester))
requestFocus(LibraryEvent.OnRequestFocus)
},
initialQuery = searchQuery,
onQueryChange = {