Changed Reader text system. Now book doesn't hold any text, only ReaderViewModel does. (v1.0.1)

This commit is contained in:
acclorite 2024-04-28 19:52:22 +03:00
parent e0698d2c2f
commit 456d9fc3c9
25 changed files with 257 additions and 279 deletions

View file

@ -25,6 +25,9 @@ android {
}
buildTypes {
getByName("debug") {
applicationIdSuffix = ".debug"
}
getByName("release") {
isMinifyEnabled = true
isShrinkResources = true

View file

@ -5,7 +5,6 @@ import ua.acclorite.book_story.R
import ua.acclorite.book_story.data.local.dto.BookEntity
import ua.acclorite.book_story.domain.model.Book
import ua.acclorite.book_story.domain.util.UIText
import java.io.File
import javax.inject.Inject
class BookMapperImpl @Inject constructor() : BookMapper {
@ -26,8 +25,6 @@ class BookMapperImpl @Inject constructor() : BookMapper {
}
override suspend fun toBook(bookEntity: BookEntity): Book {
val file = File(bookEntity.filePath)
return Book(
id = bookEntity.id,
title = bookEntity.title,
@ -38,11 +35,7 @@ class BookMapperImpl @Inject constructor() : BookMapper {
scrollIndex = bookEntity.scrollIndex,
scrollOffset = bookEntity.scrollOffset,
progress = bookEntity.progress,
file = if (file.exists()) file else null,
textPath = bookEntity.textPath,
text = emptyList(),
letters = 0,
words = 0,
filePath = bookEntity.filePath,
lastOpened = null,
category = bookEntity.category,

View file

@ -16,7 +16,7 @@ import javax.inject.Inject
class EpubFileParser @Inject constructor() : FileParser {
override suspend fun parse(file: File): Pair<Book, CoverImage?>? {
if (!file.name.endsWith(".epub")) {
if (!file.name.endsWith(".epub") || !file.exists()) {
return null
}
@ -49,13 +49,9 @@ class EpubFileParser @Inject constructor() : FileParser {
author = author,
description = description?.toString(),
textPath = "",
text = emptyList(),
letters = 0,
words = 0,
scrollIndex = 0,
scrollOffset = 0,
progress = 0f,
file = file,
filePath = file.path,
lastOpened = null,
category = Category.entries[0],

View file

@ -15,7 +15,7 @@ import javax.inject.Inject
class PdfFileParser @Inject constructor(private val application: Application) : FileParser {
override suspend fun parse(file: File): Pair<Book, CoverImage?>? {
if (!file.name.endsWith(".pdf")) {
if (!file.name.endsWith(".pdf") || !file.exists()) {
return null
}
@ -37,13 +37,9 @@ class PdfFileParser @Inject constructor(private val application: Application) :
author = author,
description = description,
textPath = "",
text = emptyList(),
letters = 0,
words = 0,
scrollIndex = 0,
scrollOffset = 0,
progress = 0f,
file = file,
filePath = file.path,
lastOpened = null,
category = Category.entries[0],

View file

@ -12,7 +12,7 @@ import javax.inject.Inject
class TxtFileParser @Inject constructor() : FileParser {
override suspend fun parse(file: File): Pair<Book, CoverImage?>? {
if (!file.name.endsWith(".txt")) {
if (!file.name.endsWith(".txt") || !file.exists()) {
return null
}
@ -25,13 +25,9 @@ class TxtFileParser @Inject constructor() : FileParser {
author = author,
description = null,
textPath = "",
text = emptyList(),
letters = 0,
words = 0,
scrollIndex = 0,
scrollOffset = 0,
progress = 0f,
file = file,
filePath = file.path,
lastOpened = null,
category = Category.entries[0],

View file

@ -88,7 +88,7 @@ class BookRepositoryImpl @Inject constructor(
}
}
override suspend fun getBookTextById(textPath: String): List<StringWithId> {
override suspend fun getBookText(textPath: String): List<StringWithId> {
val textFile = File(textPath)
if (textPath.isBlank() || !textFile.exists()) {
@ -105,8 +105,10 @@ class BookRepositoryImpl @Inject constructor(
return text.data
}
override suspend fun insertBooks(
books: List<Pair<Book, CoverImage?>>
override suspend fun insertBook(
book: Book,
coverImage: CoverImage?,
text: List<StringWithId>
): Boolean {
val filesDir = application.filesDir
val coversDir = File(filesDir, "covers")
@ -119,21 +121,20 @@ class BookRepositoryImpl @Inject constructor(
booksDir.mkdirs()
}
val booksWithCoverAndText = books.map {
var coverUri = ""
val textUri: String
if (it.first.text.isEmpty()) {
if (text.isEmpty()) {
return false
}
try {
textUri = "${UUID.randomUUID()}.txt"
val text = File(booksDir, textUri)
val textPath = File(booksDir, textUri)
withContext(Dispatchers.IO) {
FileOutputStream(text).use { stream ->
it.first.text.forEach { line ->
FileOutputStream(textPath).use { stream ->
text.forEach { line ->
stream.write(line.line.toByteArray())
stream.write(System.lineSeparator().toByteArray())
}
@ -144,7 +145,7 @@ class BookRepositoryImpl @Inject constructor(
return false
}
if (it.second != null) {
if (coverImage != null) {
try {
coverUri = "${UUID.randomUUID()}.webp"
val cover = File(coversDir, coverUri)
@ -152,7 +153,7 @@ class BookRepositoryImpl @Inject constructor(
withContext(Dispatchers.IO) {
FileOutputStream(cover).use { stream ->
if (
!it.second!!.copy(Bitmap.Config.RGB_565, false).compress(
!coverImage.copy(Bitmap.Config.RGB_565, false).compress(
Bitmap.CompressFormat.WEBP,
20,
stream
@ -169,17 +170,15 @@ class BookRepositoryImpl @Inject constructor(
}
val book = it.first.copy(
val updatedBook = book.copy(
textPath = "$booksDir/$textUri",
coverImage = if (coverUri.isNotBlank()) {
Uri.fromFile(File("$coversDir/$coverUri"))
} else null
)
bookMapper.toBookEntity(book)
}
database.insertBooks(booksWithCoverAndText)
val bookToInsert = bookMapper.toBookEntity(updatedBook)
database.insertBooks(listOf(bookToInsert))
return true
}
@ -198,7 +197,7 @@ class BookRepositoryImpl @Inject constructor(
)
}
override suspend fun updateBooksWithText(books: List<Book>): Boolean {
override suspend fun updateBookWithText(book: Book, text: List<StringWithId>): Boolean {
// without cover image
val filesDir = application.filesDir
val booksDir = File(filesDir, "books")
@ -207,21 +206,20 @@ class BookRepositoryImpl @Inject constructor(
booksDir.mkdirs()
}
val booksWithText = books.map {
val textUri: String
val bookEntity = database.findBookById(it.id)
val bookEntity = database.findBookById(book.id)
if (it.text.isEmpty()) {
if (text.isEmpty()) {
return false
}
try {
textUri = "${UUID.randomUUID()}.txt"
val text = File(booksDir, textUri)
val textPath = File(booksDir, textUri)
withContext(Dispatchers.IO) {
FileOutputStream(text).use { stream ->
it.text.forEach { line ->
FileOutputStream(textPath).use { stream ->
text.forEach { line ->
stream.write(line.line.toByteArray())
stream.write(System.lineSeparator().toByteArray())
}
@ -232,10 +230,10 @@ class BookRepositoryImpl @Inject constructor(
return false
}
if (it.textPath.isNotBlank()) {
if (book.textPath.isNotBlank()) {
try {
val fileToDelete = File(
it.textPath
book.textPath
)
if (fileToDelete.exists()) {
@ -246,16 +244,15 @@ class BookRepositoryImpl @Inject constructor(
}
}
bookMapper.toBookEntity(
it.copy(
val updatedBook = bookMapper.toBookEntity(
book.copy(
textPath = "$booksDir/$textUri",
coverImage = if (bookEntity.image != null) Uri.parse(bookEntity.image) else null
)
)
}
database.updateBooks(
booksWithText
listOf(updatedBook)
)
return true
}
@ -560,10 +557,9 @@ class BookRepositoryImpl @Inject constructor(
books.add(
NullableBook.NotNull(
Pair(
parsedBook.first.copy(text = parsedText.data),
parsedBook.second
)
book = parsedBook.first,
coverImage = parsedBook.second,
text = parsedText.data
)
)
}
@ -613,7 +609,7 @@ class BookRepositoryImpl @Inject constructor(
try {
val result = githubAPI.getLatestRelease()
val version = result.tagName.substringAfterLast("v")
val version = result.tagName.substringAfter("v")
val currentVersion = application.getString(R.string.app_version)
if (version != currentVersion && postNotification) {

View file

@ -3,7 +3,6 @@ package ua.acclorite.book_story.domain.model
import android.net.Uri
import androidx.compose.runtime.Immutable
import ua.acclorite.book_story.domain.util.UIText
import java.io.File
@Immutable
data class Book(
@ -14,17 +13,13 @@ data class Book(
val description: String?,
val textPath: String,
val text: List<StringWithId> = emptyList(),
val letters: Int,
val words: Int,
val filePath: String,
val coverImage: Uri?,
val scrollIndex: Int,
val scrollOffset: Int,
val progress: Float,
val file: File?,
val filePath: String,
val lastOpened: Long?,
val category: Category,
val coverImage: Uri?
)

View file

@ -6,13 +6,26 @@ import ua.acclorite.book_story.domain.util.UIText
@Immutable
sealed class NullableBook(
val book: Pair<Book, CoverImage?>?,
val book: Book?,
val coverImage: CoverImage? = null,
val text: List<StringWithId> = emptyList(),
val fileName: String?,
val message: UIText?
) {
class NotNull(book: Pair<Book, CoverImage?>) : NullableBook(book, null, null)
class NotNull(
book: Book,
coverImage: CoverImage?,
text: List<StringWithId>
) : NullableBook(
book = book,
text = text,
coverImage = coverImage,
fileName = null,
message = null
)
class Null(
fileName: String,
message: UIText?
) : NullableBook(null, fileName, message)
) : NullableBook(null, text = emptyList(), fileName = fileName, message = message)
}

View file

@ -23,20 +23,23 @@ interface BookRepository {
ids: List<Int>
): List<Book>
suspend fun getBookTextById(
suspend fun getBookText(
textPath: String
): List<StringWithId>
suspend fun insertBooks(
books: List<Pair<Book, CoverImage?>>
suspend fun insertBook(
book: Book,
coverImage: CoverImage?,
text: List<StringWithId>
): Boolean
suspend fun updateBooks(
books: List<Book>
)
suspend fun updateBooksWithText(
books: List<Book>
suspend fun updateBookWithText(
book: Book,
text: List<StringWithId>
): Boolean
suspend fun updateCoverImageOfBook(

View file

@ -7,6 +7,6 @@ import javax.inject.Inject
class GetText @Inject constructor(private val repository: BookRepository) {
suspend fun execute(textPath: String): List<StringWithId> {
return repository.getBookTextById(textPath = textPath)
return repository.getBookText(textPath = textPath)
}
}

View file

@ -0,0 +1,13 @@
package ua.acclorite.book_story.domain.use_case
import ua.acclorite.book_story.domain.model.Book
import ua.acclorite.book_story.domain.model.StringWithId
import ua.acclorite.book_story.domain.repository.BookRepository
import ua.acclorite.book_story.domain.util.CoverImage
import javax.inject.Inject
class InsertBook @Inject constructor(private val repository: BookRepository) {
suspend fun execute(book: Book, coverImage: CoverImage?, text: List<StringWithId>): Boolean {
return repository.insertBook(book, coverImage, text)
}
}

View file

@ -1,12 +0,0 @@
package ua.acclorite.book_story.domain.use_case
import ua.acclorite.book_story.domain.model.Book
import ua.acclorite.book_story.domain.repository.BookRepository
import ua.acclorite.book_story.domain.util.CoverImage
import javax.inject.Inject
class InsertBooks @Inject constructor(private val repository: BookRepository) {
suspend fun execute(books: List<Pair<Book, CoverImage?>>): Boolean {
return repository.insertBooks(books)
}
}

View file

@ -0,0 +1,13 @@
package ua.acclorite.book_story.domain.use_case
import ua.acclorite.book_story.domain.model.Book
import ua.acclorite.book_story.domain.model.StringWithId
import ua.acclorite.book_story.domain.repository.BookRepository
import javax.inject.Inject
class UpdateBookWithText @Inject constructor(private val repository: BookRepository) {
suspend fun execute(book: Book, text: List<StringWithId>): Boolean {
return repository.updateBookWithText(book, text)
}
}

View file

@ -1,12 +0,0 @@
package ua.acclorite.book_story.domain.use_case
import ua.acclorite.book_story.domain.model.Book
import ua.acclorite.book_story.domain.repository.BookRepository
import javax.inject.Inject
class UpdateBooksWithText @Inject constructor(private val repository: BookRepository) {
suspend fun execute(books: List<Book>): Boolean {
return repository.updateBooksWithText(books)
}
}

View file

@ -244,21 +244,17 @@ object Constants {
)
val EMPTY_BOOK = Book(
-1,
"",
UIText.StringValue(""),
null,
"",
emptyList(),
0,
0,
0,
0,
0f,
null,
"",
null,
Category.READING,
null
id = -1,
title = "",
author = UIText.StringValue(""),
description = null,
textPath = "",
filePath = "",
coverImage = null,
scrollIndex = 0,
scrollOffset = 0,
progress = 0f,
lastOpened = null,
category = Category.READING
)
}

View file

@ -21,6 +21,7 @@ import androidx.compose.ui.unit.dp
import ua.acclorite.book_story.R
import ua.acclorite.book_story.presentation.screens.book_info.data.BookInfoEvent
import ua.acclorite.book_story.presentation.screens.book_info.data.BookInfoState
import java.io.File
import java.text.SimpleDateFormat
import java.util.Date
import java.util.Locale
@ -46,7 +47,10 @@ fun BookInfoDetailsBottomSheet(
}
val sizeBytes = remember {
state.value.book.file?.length() ?: 0
val file = File(state.value.book.filePath)
if (file.exists()) {
file.length()
} else 0
}
val fileSizeKB = remember {

View file

@ -7,6 +7,7 @@ import androidx.compose.runtime.Immutable
import androidx.compose.ui.focus.FocusRequester
import ua.acclorite.book_story.domain.model.Book
import ua.acclorite.book_story.domain.model.Category
import ua.acclorite.book_story.domain.model.StringWithId
import ua.acclorite.book_story.presentation.data.Navigator
@Immutable
@ -57,7 +58,7 @@ sealed class BookInfoEvent {
data object OnDismissConfirmUpdateDialog : BookInfoEvent()
data class OnShowConfirmUpdateDialog(
val updatedBook: Book,
val updatedBook: Pair<Book, List<StringWithId>>,
val authorUpdated: Boolean,
val descriptionUpdated: Boolean,
val textUpdated: Boolean,

View file

@ -3,6 +3,7 @@ package ua.acclorite.book_story.presentation.screens.book_info.data
import androidx.compose.runtime.Immutable
import ua.acclorite.book_story.domain.model.Book
import ua.acclorite.book_story.domain.model.Category
import ua.acclorite.book_story.domain.model.StringWithId
import ua.acclorite.book_story.domain.util.Constants
@Immutable
@ -12,7 +13,7 @@ data class BookInfoState(
val isLoadingUpdate: Boolean = false,
val isRefreshing: Boolean = false,
val showConfirmUpdateDialog: Boolean = false,
val updatedBook: Book? = null,
val updatedBook: Pair<Book, List<StringWithId>>? = null,
val authorChanged: Boolean = false,
val descriptionChanged: Boolean = false,
val textChanged: Boolean = false,

View file

@ -26,12 +26,13 @@ import ua.acclorite.book_story.domain.use_case.GetBookFromFile
import ua.acclorite.book_story.domain.use_case.GetBooksById
import ua.acclorite.book_story.domain.use_case.GetText
import ua.acclorite.book_story.domain.use_case.InsertHistory
import ua.acclorite.book_story.domain.use_case.UpdateBookWithText
import ua.acclorite.book_story.domain.use_case.UpdateBooks
import ua.acclorite.book_story.domain.use_case.UpdateBooksWithText
import ua.acclorite.book_story.domain.use_case.UpdateCoverImageOfBook
import ua.acclorite.book_story.presentation.data.Argument
import ua.acclorite.book_story.presentation.data.Navigator
import ua.acclorite.book_story.presentation.data.Screen
import java.io.File
import java.util.Date
import javax.inject.Inject
@ -39,7 +40,7 @@ import javax.inject.Inject
@HiltViewModel
class BookInfoViewModel @Inject constructor(
private val updateBooks: UpdateBooks,
private val updateBooksWithText: UpdateBooksWithText,
private val updateBookWithText: UpdateBookWithText,
private val updateCoverImageOfBook: UpdateCoverImageOfBook,
private val insertHistory: InsertHistory,
private val deleteBooks: DeleteBooks,
@ -302,7 +303,7 @@ class BookInfoViewModel @Inject constructor(
}
yield()
if (_state.value.book.file == null) {
if (!File(_state.value.book.filePath).exists()) {
onEvent(
BookInfoEvent.OnShowSnackbar(
text = event.context.getString(
@ -334,12 +335,13 @@ class BookInfoViewModel @Inject constructor(
}
yield()
val nullableBook = getBookFromFile.execute(_state.value.book.file!!)
val updatedBook = getBookFromFile.execute(File(_state.value.book.filePath))
yield()
if (nullableBook is NullableBook.Null) {
if (updatedBook is NullableBook.Null) {
onEvent(
BookInfoEvent.OnShowSnackbar(
text = nullableBook.message?.asString(event.context)
text = updatedBook.message?.asString(event.context)
?: event.context.getString(R.string.error_something_went_wrong_with_file),
action = event.context.getString(R.string.retry),
onAction = {
@ -365,7 +367,6 @@ class BookInfoViewModel @Inject constructor(
}
yield()
val updatedBook = nullableBook.book?.first ?: return@launch
val book = _state.value.book
var authorUpdated = false
@ -373,20 +374,19 @@ class BookInfoViewModel @Inject constructor(
var textUpdated = false
if (
updatedBook.author.asString(event.context) !=
updatedBook.book!!.author.asString(event.context) !=
book.author.asString(event.context)
) {
authorUpdated = true
}
if (updatedBook.description != book.description) {
if (updatedBook.book.description != book.description) {
descriptionUpdated = true
}
if (
updatedBook.text.map { it.line } !=
book.text.ifEmpty {
getText.execute(book.textPath)
}.map { it.line }
) {
val updatedText = updatedBook.text
val text = getText.execute(book.textPath)
if (updatedText.map { it.line } != text.map { it.line }) {
textUpdated = true
}
@ -412,7 +412,7 @@ class BookInfoViewModel @Inject constructor(
yield()
onEvent(
BookInfoEvent.OnShowConfirmUpdateDialog(
updatedBook = updatedBook,
updatedBook = updatedBook.book to updatedBook.text,
authorUpdated = authorUpdated,
descriptionUpdated = descriptionUpdated,
textUpdated = textUpdated
@ -492,36 +492,29 @@ class BookInfoViewModel @Inject constructor(
val updatedBook = _state.value.updatedBook ?: return@launch
val author = if (_state.value.authorChanged) {
updatedBook.author
updatedBook.first.author
} else {
book.author
}
val description = if (_state.value.descriptionChanged) {
updatedBook.description
updatedBook.first.description
} else {
book.description
}
val text = if (_state.value.textChanged) {
updatedBook.text
} else {
book.text
}
_state.update {
it.copy(
book = it.book.copy(
author = author,
description = description,
text = text
description = description
)
)
}
if (_state.value.textChanged) {
val isSuccess = updateBooksWithText.execute(
listOf(
_state.value.book
)
val isSuccess = updateBookWithText.execute(
book = _state.value.book,
text = updatedBook.second
)
if (!isSuccess) {

View file

@ -44,14 +44,14 @@ fun BrowseAddingDialogItem(result: Pair<NullableBook, Selected>, onClick: (Boole
Modifier.weight(0.85f)
) {
Text(
text = result.first.book!!.first.title,
text = result.first.book!!.title,
style = MaterialTheme.typography.bodyLarge,
color = MaterialTheme.colorScheme.onSurface,
maxLines = 1,
overflow = TextOverflow.Ellipsis
)
Text(
text = result.first.book!!.first.author.asString(),
text = result.first.book!!.author.asString(),
style = MaterialTheme.typography.bodyMedium,
color = MaterialTheme.colorScheme.onSurfaceVariant,
maxLines = 1,

View file

@ -23,7 +23,7 @@ import kotlinx.coroutines.yield
import ua.acclorite.book_story.domain.model.NullableBook
import ua.acclorite.book_story.domain.use_case.GetBooksFromFiles
import ua.acclorite.book_story.domain.use_case.GetFilesFromDevice
import ua.acclorite.book_story.domain.use_case.InsertBooks
import ua.acclorite.book_story.domain.use_case.InsertBook
import ua.acclorite.book_story.domain.util.Resource
import ua.acclorite.book_story.presentation.data.Screen
import javax.inject.Inject
@ -33,7 +33,7 @@ import javax.inject.Inject
class BrowseViewModel @Inject constructor(
private val getBooksFromFiles: GetBooksFromFiles,
private val getFilesFromDevice: GetFilesFromDevice,
private val insertBooks: InsertBooks
private val insertBook: InsertBook
) : ViewModel() {
private val _state = MutableStateFlow(BrowseState())
@ -369,13 +369,28 @@ class BrowseViewModel @Inject constructor(
val booksToInsert = _state.value.selectedBooks
.filter { it.first is NullableBook.NotNull }
.filter { it.second }
.map { it.first.book!! }
.map { it.first }
if (booksToInsert.isEmpty()) {
return@launch
}
if (!insertBooks.execute(booksToInsert)) {
val failed = booksToInsert.any {
!insertBook.execute(
it.book!!,
it.coverImage,
it.text
)
}
if (failed) {
_state.update {
it.copy(
showAddingDialog = false
)
}
onEvent(BrowseEvent.OnLoadList)
onEvent(BrowseEvent.OnClearSelectedFiles)
event.onFailed()
return@launch
}

View file

@ -201,14 +201,14 @@ private fun ReaderScreen(
snapshotFlow {
listState.firstVisibleItemIndex to listState.firstVisibleItemScrollOffset
}.debounce(500).collectLatest { items ->
if (!loading.value && state.value.book.text.isNotEmpty() && listState.layoutInfo.totalItemsCount > 0) {
if (!loading.value && state.value.text.isNotEmpty() && listState.layoutInfo.totalItemsCount > 0) {
val lastVisibleItemIndex = listState.layoutInfo.visibleItemsInfo.last().index
val progress = if (items.first > 0) {
if (lastVisibleItemIndex >= (listState.layoutInfo.totalItemsCount - 1)) {
1f
} else {
(items.first.toFloat() / (state.value.book.text.lastIndex).toFloat())
(items.first.toFloat() / (state.value.text.lastIndex).toFloat())
}
} else {
0f
@ -338,7 +338,7 @@ private fun ReaderScreen(
}
)
) {
if (state.value.book.text.isNotEmpty()) {
if (state.value.text.isNotEmpty()) {
item {
DisableSelection {
ReaderStartItem(state = state)
@ -347,9 +347,9 @@ private fun ReaderScreen(
}
customItemsIndexed(
state.value.book.text, key = { key -> key.id }
state.value.text, key = { key -> key.id }
) { index, line ->
val text = remember(mainState.value.paragraphIndentation) {
val text = remember(mainState.value.paragraphIndentation, line) {
"${if (mainState.value.paragraphIndentation!!) " " else ""}${line.line}"
}
@ -361,7 +361,7 @@ private fun ReaderScreen(
top = if (index == 0) 18.dp else 0.dp,
start = sidePadding,
end = sidePadding,
bottom = if (index == state.value.book.text.lastIndex) 18.dp
bottom = if (index == state.value.text.lastIndex) 18.dp
else paragraphHeight
)
) {
@ -377,7 +377,7 @@ private fun ReaderScreen(
}
}
if (state.value.book.text.isNotEmpty()) {
if (state.value.text.isNotEmpty()) {
item {
DisableSelection {
ReaderEndItem(

View file

@ -83,8 +83,8 @@ fun ReaderEndItem(
Text(
stringResource(
id = R.string.letters_and_words,
state.value.book.letters,
state.value.book.words
state.value.letters,
state.value.words
),
color = MaterialTheme.colorScheme.onSurfaceVariant,
style = MaterialTheme.typography.bodyLarge,

View file

@ -2,12 +2,17 @@ package ua.acclorite.book_story.presentation.screens.reader.data
import androidx.compose.runtime.Immutable
import ua.acclorite.book_story.domain.model.Book
import ua.acclorite.book_story.domain.model.StringWithId
import ua.acclorite.book_story.domain.util.Constants
import ua.acclorite.book_story.domain.util.UIText
@Immutable
data class ReaderState(
val book: Book = Constants.EMPTY_BOOK,
val text: List<StringWithId> = emptyList(),
val words: Int = 0,
val letters: Int = 0,
val errorMessage: UIText? = null,
val showMenu: Boolean = false,

View file

@ -57,11 +57,6 @@ class ReaderViewModel @Inject constructor(
is ReaderEvent.OnLoadText -> {
viewModelScope.launch(Dispatchers.IO) {
if (
_state.value.book.text.isEmpty() ||
_state.value.book.letters < 1 ||
_state.value.book.words < 1
) {
val text = getText.execute(_state.value.book.textPath)
if (text.isEmpty()) {
@ -85,13 +80,10 @@ class ReaderViewModel @Inject constructor(
_state.update {
it.copy(
book = it.book.copy(
text = text,
letters = letters,
words = words
)
)
}
}
val history = _state.value.book.id.let {
@ -111,13 +103,7 @@ class ReaderViewModel @Inject constructor(
updateBooks.execute(
listOf(_state.value.book)
)
event.refreshList(
_state.value.book.copy(
text = emptyList(),
letters = 0,
words = 0
)
)
event.refreshList(_state.value.book)
viewModelScope.launch {
snapshotFlow {
@ -126,7 +112,7 @@ class ReaderViewModel @Inject constructor(
val index = _state.value.book.scrollIndex
val offset = _state.value.book.scrollOffset
if (itemsCount >= _state.value.book.text.size) {
if (itemsCount >= _state.value.text.size) {
if (index > 0 || offset > 0) {
var loaded = false
for (i in 1..100) {
@ -213,7 +199,7 @@ class ReaderViewModel @Inject constructor(
if (lastVisibleItemIndex >= (event.listState.layoutInfo.totalItemsCount - 1)) {
1f
} else {
(firstVisibleItemIndex.toFloat() / (_state.value.book.text.lastIndex)
(firstVisibleItemIndex.toFloat() / (_state.value.text.lastIndex)
.toFloat())
}
} else {
@ -237,13 +223,7 @@ class ReaderViewModel @Inject constructor(
event.navigator.putArgument(
Argument("book", _state.value.book.id)
)
event.refreshList(
_state.value.book.copy(
text = emptyList(),
letters = 0,
words = 0
)
)
event.refreshList(_state.value.book)
insetsController.show(WindowInsetsCompat.Type.systemBars())
event.navigate(event.navigator)
@ -252,7 +232,7 @@ class ReaderViewModel @Inject constructor(
is ReaderEvent.OnScroll -> {
viewModelScope.launch {
val scrollTo = (_state.value.book.text.size * event.progress).roundToInt()
val scrollTo = (_state.value.text.size * event.progress).roundToInt()
event.listState.scrollToItem(
scrollTo
@ -281,13 +261,7 @@ class ReaderViewModel @Inject constructor(
_state.value.book.id
)
)
event.refreshList(
_state.value.book.copy(
text = emptyList(),
letters = 0,
words = 0
)
)
event.refreshList(_state.value.book)
}
}
@ -336,11 +310,7 @@ class ReaderViewModel @Inject constructor(
updateBooks.execute(listOf(_state.value.book))
event.onUpdateCategories(
_state.value.book.copy(
text = emptyList(),
letters = 0,
words = 0
)
_state.value.book.copy()
)
event.updatePage(
Category.entries.dropLastWhile {