From c76f7f38d555f7b11cd8706a66d8b922ac2631db Mon Sep 17 00:00:00 2001 From: Acclorite <2ma0bhpk@anonaddy.com> Date: Fri, 7 Nov 2025 09:09:12 +0200 Subject: [PATCH 1/2] refactor: move parsers --- .../ua/acclorite/book_story/data/di/RepositoryModule.kt | 8 ++++---- .../data/parser/{ => document}/DocumentParser.kt | 2 +- .../data/parser/{ => document}/MarkdownParser.kt | 4 ++-- .../data/parser/{epub => file}/EpubFileParser.kt | 3 +-- .../book_story/data/parser/{fb2 => file}/Fb2FileParser.kt | 3 +-- .../book_story/data/parser/{ => file}/FileParser.kt | 2 +- .../book_story/data/parser/{ => file}/FileParserImpl.kt | 7 +------ .../data/parser/{html => file}/HtmlFileParser.kt | 3 +-- .../book_story/data/parser/{pdf => file}/PdfFileParser.kt | 3 +-- .../book_story/data/parser/{txt => file}/TxtFileParser.kt | 3 +-- .../data/parser/{epub => text}/EpubTextParser.kt | 5 ++--- .../data/parser/{html => text}/HtmlTextParser.kt | 5 ++--- .../book_story/data/parser/{pdf => text}/PdfTextParser.kt | 5 ++--- .../book_story/data/parser/{ => text}/TextParser.kt | 2 +- .../book_story/data/parser/{ => text}/TextParserImpl.kt | 7 +------ .../book_story/data/parser/{txt => text}/TxtTextParser.kt | 5 ++--- .../book_story/data/parser/{xml => text}/XmlTextParser.kt | 5 ++--- .../book_story/data/repository/BookRepositoryImpl.kt | 4 ++-- .../data/repository/FileSystemRepositoryImpl.kt | 2 +- 19 files changed, 29 insertions(+), 49 deletions(-) rename app/src/main/java/ua/acclorite/book_story/data/parser/{ => document}/DocumentParser.kt (99%) rename app/src/main/java/ua/acclorite/book_story/data/parser/{ => document}/MarkdownParser.kt (96%) rename app/src/main/java/ua/acclorite/book_story/data/parser/{epub => file}/EpubFileParser.kt (97%) rename app/src/main/java/ua/acclorite/book_story/data/parser/{fb2 => file}/Fb2FileParser.kt (95%) rename app/src/main/java/ua/acclorite/book_story/data/parser/{ => file}/FileParser.kt (88%) rename app/src/main/java/ua/acclorite/book_story/data/parser/{ => file}/FileParserImpl.kt (83%) rename app/src/main/java/ua/acclorite/book_story/data/parser/{html => file}/HtmlFileParser.kt (94%) rename app/src/main/java/ua/acclorite/book_story/data/parser/{pdf => file}/PdfFileParser.kt (95%) rename app/src/main/java/ua/acclorite/book_story/data/parser/{txt => file}/TxtFileParser.kt (92%) rename app/src/main/java/ua/acclorite/book_story/data/parser/{epub => text}/EpubTextParser.kt (98%) rename app/src/main/java/ua/acclorite/book_story/data/parser/{html => text}/HtmlTextParser.kt (90%) rename app/src/main/java/ua/acclorite/book_story/data/parser/{pdf => text}/PdfTextParser.kt (96%) rename app/src/main/java/ua/acclorite/book_story/data/parser/{ => text}/TextParser.kt (88%) rename app/src/main/java/ua/acclorite/book_story/data/parser/{ => text}/TextParserImpl.kt (85%) rename app/src/main/java/ua/acclorite/book_story/data/parser/{txt => text}/TxtTextParser.kt (94%) rename app/src/main/java/ua/acclorite/book_story/data/parser/{xml => text}/XmlTextParser.kt (90%) diff --git a/app/src/main/java/ua/acclorite/book_story/data/di/RepositoryModule.kt b/app/src/main/java/ua/acclorite/book_story/data/di/RepositoryModule.kt index c2f92e92..d14e5d66 100644 --- a/app/src/main/java/ua/acclorite/book_story/data/di/RepositoryModule.kt +++ b/app/src/main/java/ua/acclorite/book_story/data/di/RepositoryModule.kt @@ -22,10 +22,10 @@ import ua.acclorite.book_story.data.mapper.file.FileMapper import ua.acclorite.book_story.data.mapper.file.FileMapperImpl import ua.acclorite.book_story.data.mapper.history.HistoryMapper import ua.acclorite.book_story.data.mapper.history.HistoryMapperImpl -import ua.acclorite.book_story.data.parser.FileParser -import ua.acclorite.book_story.data.parser.FileParserImpl -import ua.acclorite.book_story.data.parser.TextParser -import ua.acclorite.book_story.data.parser.TextParserImpl +import ua.acclorite.book_story.data.parser.file.FileParser +import ua.acclorite.book_story.data.parser.file.FileParserImpl +import ua.acclorite.book_story.data.parser.text.TextParser +import ua.acclorite.book_story.data.parser.text.TextParserImpl import ua.acclorite.book_story.data.repository.BookRepositoryImpl import ua.acclorite.book_story.data.repository.CategoryRepositoryImpl import ua.acclorite.book_story.data.repository.ColorPresetRepositoryImpl diff --git a/app/src/main/java/ua/acclorite/book_story/data/parser/DocumentParser.kt b/app/src/main/java/ua/acclorite/book_story/data/parser/document/DocumentParser.kt similarity index 99% rename from app/src/main/java/ua/acclorite/book_story/data/parser/DocumentParser.kt rename to app/src/main/java/ua/acclorite/book_story/data/parser/document/DocumentParser.kt index 09f2bdfe..e0c25143 100644 --- a/app/src/main/java/ua/acclorite/book_story/data/parser/DocumentParser.kt +++ b/app/src/main/java/ua/acclorite/book_story/data/parser/document/DocumentParser.kt @@ -4,7 +4,7 @@ * SPDX-License-Identifier: GPL-3.0-only */ -package ua.acclorite.book_story.data.parser +package ua.acclorite.book_story.data.parser.document import android.graphics.Bitmap import android.graphics.BitmapFactory diff --git a/app/src/main/java/ua/acclorite/book_story/data/parser/MarkdownParser.kt b/app/src/main/java/ua/acclorite/book_story/data/parser/document/MarkdownParser.kt similarity index 96% rename from app/src/main/java/ua/acclorite/book_story/data/parser/MarkdownParser.kt rename to app/src/main/java/ua/acclorite/book_story/data/parser/document/MarkdownParser.kt index 47394f0a..74337a90 100644 --- a/app/src/main/java/ua/acclorite/book_story/data/parser/MarkdownParser.kt +++ b/app/src/main/java/ua/acclorite/book_story/data/parser/document/MarkdownParser.kt @@ -4,7 +4,7 @@ * SPDX-License-Identifier: GPL-3.0-only */ -package ua.acclorite.book_story.data.parser +package ua.acclorite.book_story.data.parser.document import androidx.compose.ui.text.AnnotatedString import androidx.compose.ui.text.LinkAnnotation @@ -30,7 +30,7 @@ class MarkdownParser @Inject constructor( private val commonmarkParser: Parser ) { /** - * Parses markdown text to [androidx.compose.ui.text.AnnotatedString]. + * Parses markdown text to [AnnotatedString]. * * @return Parsed annotated string. */ diff --git a/app/src/main/java/ua/acclorite/book_story/data/parser/epub/EpubFileParser.kt b/app/src/main/java/ua/acclorite/book_story/data/parser/file/EpubFileParser.kt similarity index 97% rename from app/src/main/java/ua/acclorite/book_story/data/parser/epub/EpubFileParser.kt rename to app/src/main/java/ua/acclorite/book_story/data/parser/file/EpubFileParser.kt index 1d741b76..4a2fafe4 100644 --- a/app/src/main/java/ua/acclorite/book_story/data/parser/epub/EpubFileParser.kt +++ b/app/src/main/java/ua/acclorite/book_story/data/parser/file/EpubFileParser.kt @@ -4,7 +4,7 @@ * SPDX-License-Identifier: GPL-3.0-only */ -package ua.acclorite.book_story.data.parser.epub +package ua.acclorite.book_story.data.parser.file import android.graphics.Bitmap import android.graphics.BitmapFactory @@ -16,7 +16,6 @@ import ua.acclorite.book_story.R import ua.acclorite.book_story.core.ui.UIText import ua.acclorite.book_story.data.model.common.BookWithCover import ua.acclorite.book_story.data.model.file.CachedFile -import ua.acclorite.book_story.data.parser.FileParser import ua.acclorite.book_story.domain.model.library.Book import java.io.File import java.net.URLDecoder diff --git a/app/src/main/java/ua/acclorite/book_story/data/parser/fb2/Fb2FileParser.kt b/app/src/main/java/ua/acclorite/book_story/data/parser/file/Fb2FileParser.kt similarity index 95% rename from app/src/main/java/ua/acclorite/book_story/data/parser/fb2/Fb2FileParser.kt rename to app/src/main/java/ua/acclorite/book_story/data/parser/file/Fb2FileParser.kt index 984d4594..7b9bf45c 100644 --- a/app/src/main/java/ua/acclorite/book_story/data/parser/fb2/Fb2FileParser.kt +++ b/app/src/main/java/ua/acclorite/book_story/data/parser/file/Fb2FileParser.kt @@ -4,7 +4,7 @@ * SPDX-License-Identifier: GPL-3.0-only */ -package ua.acclorite.book_story.data.parser.fb2 +package ua.acclorite.book_story.data.parser.file import org.jsoup.Jsoup import org.jsoup.parser.Parser @@ -12,7 +12,6 @@ import ua.acclorite.book_story.R import ua.acclorite.book_story.core.ui.UIText import ua.acclorite.book_story.data.model.common.BookWithCover import ua.acclorite.book_story.data.model.file.CachedFile -import ua.acclorite.book_story.data.parser.FileParser import ua.acclorite.book_story.domain.model.library.Book import javax.inject.Inject diff --git a/app/src/main/java/ua/acclorite/book_story/data/parser/FileParser.kt b/app/src/main/java/ua/acclorite/book_story/data/parser/file/FileParser.kt similarity index 88% rename from app/src/main/java/ua/acclorite/book_story/data/parser/FileParser.kt rename to app/src/main/java/ua/acclorite/book_story/data/parser/file/FileParser.kt index fb9bd1b9..0c327b03 100644 --- a/app/src/main/java/ua/acclorite/book_story/data/parser/FileParser.kt +++ b/app/src/main/java/ua/acclorite/book_story/data/parser/file/FileParser.kt @@ -4,7 +4,7 @@ * SPDX-License-Identifier: GPL-3.0-only */ -package ua.acclorite.book_story.data.parser +package ua.acclorite.book_story.data.parser.file import ua.acclorite.book_story.data.model.common.BookWithCover import ua.acclorite.book_story.data.model.file.CachedFile diff --git a/app/src/main/java/ua/acclorite/book_story/data/parser/FileParserImpl.kt b/app/src/main/java/ua/acclorite/book_story/data/parser/file/FileParserImpl.kt similarity index 83% rename from app/src/main/java/ua/acclorite/book_story/data/parser/FileParserImpl.kt rename to app/src/main/java/ua/acclorite/book_story/data/parser/file/FileParserImpl.kt index 36173cea..8503898b 100644 --- a/app/src/main/java/ua/acclorite/book_story/data/parser/FileParserImpl.kt +++ b/app/src/main/java/ua/acclorite/book_story/data/parser/file/FileParserImpl.kt @@ -4,16 +4,11 @@ * SPDX-License-Identifier: GPL-3.0-only */ -package ua.acclorite.book_story.data.parser +package ua.acclorite.book_story.data.parser.file import android.util.Log import ua.acclorite.book_story.data.model.common.BookWithCover import ua.acclorite.book_story.data.model.file.CachedFile -import ua.acclorite.book_story.data.parser.epub.EpubFileParser -import ua.acclorite.book_story.data.parser.fb2.Fb2FileParser -import ua.acclorite.book_story.data.parser.html.HtmlFileParser -import ua.acclorite.book_story.data.parser.pdf.PdfFileParser -import ua.acclorite.book_story.data.parser.txt.TxtFileParser import javax.inject.Inject private const val FILE_PARSER = "File Parser" diff --git a/app/src/main/java/ua/acclorite/book_story/data/parser/html/HtmlFileParser.kt b/app/src/main/java/ua/acclorite/book_story/data/parser/file/HtmlFileParser.kt similarity index 94% rename from app/src/main/java/ua/acclorite/book_story/data/parser/html/HtmlFileParser.kt rename to app/src/main/java/ua/acclorite/book_story/data/parser/file/HtmlFileParser.kt index 95d43a71..3ec75de1 100644 --- a/app/src/main/java/ua/acclorite/book_story/data/parser/html/HtmlFileParser.kt +++ b/app/src/main/java/ua/acclorite/book_story/data/parser/file/HtmlFileParser.kt @@ -4,7 +4,7 @@ * SPDX-License-Identifier: GPL-3.0-only */ -package ua.acclorite.book_story.data.parser.html +package ua.acclorite.book_story.data.parser.file import org.jsoup.Jsoup import org.jsoup.parser.Parser @@ -12,7 +12,6 @@ import ua.acclorite.book_story.R import ua.acclorite.book_story.core.ui.UIText import ua.acclorite.book_story.data.model.common.BookWithCover import ua.acclorite.book_story.data.model.file.CachedFile -import ua.acclorite.book_story.data.parser.FileParser import ua.acclorite.book_story.domain.model.library.Book import javax.inject.Inject diff --git a/app/src/main/java/ua/acclorite/book_story/data/parser/pdf/PdfFileParser.kt b/app/src/main/java/ua/acclorite/book_story/data/parser/file/PdfFileParser.kt similarity index 95% rename from app/src/main/java/ua/acclorite/book_story/data/parser/pdf/PdfFileParser.kt rename to app/src/main/java/ua/acclorite/book_story/data/parser/file/PdfFileParser.kt index 954dad66..8bbc2a77 100644 --- a/app/src/main/java/ua/acclorite/book_story/data/parser/pdf/PdfFileParser.kt +++ b/app/src/main/java/ua/acclorite/book_story/data/parser/file/PdfFileParser.kt @@ -4,7 +4,7 @@ * SPDX-License-Identifier: GPL-3.0-only */ -package ua.acclorite.book_story.data.parser.pdf +package ua.acclorite.book_story.data.parser.file import android.app.Application import com.tom_roush.pdfbox.android.PDFBoxResourceLoader @@ -13,7 +13,6 @@ import ua.acclorite.book_story.R import ua.acclorite.book_story.core.ui.UIText import ua.acclorite.book_story.data.model.common.BookWithCover import ua.acclorite.book_story.data.model.file.CachedFile -import ua.acclorite.book_story.data.parser.FileParser import ua.acclorite.book_story.domain.model.library.Book import javax.inject.Inject diff --git a/app/src/main/java/ua/acclorite/book_story/data/parser/txt/TxtFileParser.kt b/app/src/main/java/ua/acclorite/book_story/data/parser/file/TxtFileParser.kt similarity index 92% rename from app/src/main/java/ua/acclorite/book_story/data/parser/txt/TxtFileParser.kt rename to app/src/main/java/ua/acclorite/book_story/data/parser/file/TxtFileParser.kt index c304c515..0b4b56f1 100644 --- a/app/src/main/java/ua/acclorite/book_story/data/parser/txt/TxtFileParser.kt +++ b/app/src/main/java/ua/acclorite/book_story/data/parser/file/TxtFileParser.kt @@ -4,13 +4,12 @@ * SPDX-License-Identifier: GPL-3.0-only */ -package ua.acclorite.book_story.data.parser.txt +package ua.acclorite.book_story.data.parser.file import ua.acclorite.book_story.R import ua.acclorite.book_story.core.ui.UIText import ua.acclorite.book_story.data.model.common.BookWithCover import ua.acclorite.book_story.data.model.file.CachedFile -import ua.acclorite.book_story.data.parser.FileParser import ua.acclorite.book_story.domain.model.library.Book import javax.inject.Inject diff --git a/app/src/main/java/ua/acclorite/book_story/data/parser/epub/EpubTextParser.kt b/app/src/main/java/ua/acclorite/book_story/data/parser/text/EpubTextParser.kt similarity index 98% rename from app/src/main/java/ua/acclorite/book_story/data/parser/epub/EpubTextParser.kt rename to app/src/main/java/ua/acclorite/book_story/data/parser/text/EpubTextParser.kt index 93952d5a..2775b0e4 100644 --- a/app/src/main/java/ua/acclorite/book_story/data/parser/epub/EpubTextParser.kt +++ b/app/src/main/java/ua/acclorite/book_story/data/parser/text/EpubTextParser.kt @@ -6,7 +6,7 @@ @file:OptIn(ExperimentalCoroutinesApi::class) -package ua.acclorite.book_story.data.parser.epub +package ua.acclorite.book_story.data.parser.text import android.util.Log import androidx.core.net.toUri @@ -22,8 +22,7 @@ import ua.acclorite.book_story.core.data.ExtensionsData import ua.acclorite.book_story.core.helpers.addAll import ua.acclorite.book_story.core.helpers.containsVisibleText import ua.acclorite.book_story.data.model.file.CachedFile -import ua.acclorite.book_story.data.parser.DocumentParser -import ua.acclorite.book_story.data.parser.TextParser +import ua.acclorite.book_story.data.parser.document.DocumentParser import ua.acclorite.book_story.domain.model.reader.ReaderText import java.io.File import java.net.URLDecoder diff --git a/app/src/main/java/ua/acclorite/book_story/data/parser/html/HtmlTextParser.kt b/app/src/main/java/ua/acclorite/book_story/data/parser/text/HtmlTextParser.kt similarity index 90% rename from app/src/main/java/ua/acclorite/book_story/data/parser/html/HtmlTextParser.kt rename to app/src/main/java/ua/acclorite/book_story/data/parser/text/HtmlTextParser.kt index c8043c73..c80e5ced 100644 --- a/app/src/main/java/ua/acclorite/book_story/data/parser/html/HtmlTextParser.kt +++ b/app/src/main/java/ua/acclorite/book_story/data/parser/text/HtmlTextParser.kt @@ -4,15 +4,14 @@ * SPDX-License-Identifier: GPL-3.0-only */ -package ua.acclorite.book_story.data.parser.html +package ua.acclorite.book_story.data.parser.text import android.util.Log import kotlinx.coroutines.yield import org.jsoup.Jsoup import org.jsoup.parser.Parser import ua.acclorite.book_story.data.model.file.CachedFile -import ua.acclorite.book_story.data.parser.DocumentParser -import ua.acclorite.book_story.data.parser.TextParser +import ua.acclorite.book_story.data.parser.document.DocumentParser import ua.acclorite.book_story.domain.model.reader.ReaderText import javax.inject.Inject diff --git a/app/src/main/java/ua/acclorite/book_story/data/parser/pdf/PdfTextParser.kt b/app/src/main/java/ua/acclorite/book_story/data/parser/text/PdfTextParser.kt similarity index 96% rename from app/src/main/java/ua/acclorite/book_story/data/parser/pdf/PdfTextParser.kt rename to app/src/main/java/ua/acclorite/book_story/data/parser/text/PdfTextParser.kt index f5919651..898435fb 100644 --- a/app/src/main/java/ua/acclorite/book_story/data/parser/pdf/PdfTextParser.kt +++ b/app/src/main/java/ua/acclorite/book_story/data/parser/text/PdfTextParser.kt @@ -4,7 +4,7 @@ * SPDX-License-Identifier: GPL-3.0-only */ -package ua.acclorite.book_story.data.parser.pdf +package ua.acclorite.book_story.data.parser.text import android.app.Application import android.util.Log @@ -14,8 +14,7 @@ import com.tom_roush.pdfbox.text.PDFTextStripper import kotlinx.coroutines.yield import ua.acclorite.book_story.core.helpers.clearAllMarkdown import ua.acclorite.book_story.data.model.file.CachedFile -import ua.acclorite.book_story.data.parser.MarkdownParser -import ua.acclorite.book_story.data.parser.TextParser +import ua.acclorite.book_story.data.parser.document.MarkdownParser import ua.acclorite.book_story.domain.model.reader.ReaderText import javax.inject.Inject diff --git a/app/src/main/java/ua/acclorite/book_story/data/parser/TextParser.kt b/app/src/main/java/ua/acclorite/book_story/data/parser/text/TextParser.kt similarity index 88% rename from app/src/main/java/ua/acclorite/book_story/data/parser/TextParser.kt rename to app/src/main/java/ua/acclorite/book_story/data/parser/text/TextParser.kt index 7d70f502..451cd904 100644 --- a/app/src/main/java/ua/acclorite/book_story/data/parser/TextParser.kt +++ b/app/src/main/java/ua/acclorite/book_story/data/parser/text/TextParser.kt @@ -4,7 +4,7 @@ * SPDX-License-Identifier: GPL-3.0-only */ -package ua.acclorite.book_story.data.parser +package ua.acclorite.book_story.data.parser.text import ua.acclorite.book_story.data.model.file.CachedFile import ua.acclorite.book_story.domain.model.reader.ReaderText diff --git a/app/src/main/java/ua/acclorite/book_story/data/parser/TextParserImpl.kt b/app/src/main/java/ua/acclorite/book_story/data/parser/text/TextParserImpl.kt similarity index 85% rename from app/src/main/java/ua/acclorite/book_story/data/parser/TextParserImpl.kt rename to app/src/main/java/ua/acclorite/book_story/data/parser/text/TextParserImpl.kt index 9f6916cd..966cf5b0 100644 --- a/app/src/main/java/ua/acclorite/book_story/data/parser/TextParserImpl.kt +++ b/app/src/main/java/ua/acclorite/book_story/data/parser/text/TextParserImpl.kt @@ -4,17 +4,12 @@ * SPDX-License-Identifier: GPL-3.0-only */ -package ua.acclorite.book_story.data.parser +package ua.acclorite.book_story.data.parser.text import android.util.Log import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.withContext import ua.acclorite.book_story.data.model.file.CachedFile -import ua.acclorite.book_story.data.parser.epub.EpubTextParser -import ua.acclorite.book_story.data.parser.html.HtmlTextParser -import ua.acclorite.book_story.data.parser.pdf.PdfTextParser -import ua.acclorite.book_story.data.parser.txt.TxtTextParser -import ua.acclorite.book_story.data.parser.xml.XmlTextParser import ua.acclorite.book_story.domain.model.reader.ReaderText import javax.inject.Inject diff --git a/app/src/main/java/ua/acclorite/book_story/data/parser/txt/TxtTextParser.kt b/app/src/main/java/ua/acclorite/book_story/data/parser/text/TxtTextParser.kt similarity index 94% rename from app/src/main/java/ua/acclorite/book_story/data/parser/txt/TxtTextParser.kt rename to app/src/main/java/ua/acclorite/book_story/data/parser/text/TxtTextParser.kt index 8606d554..7e575244 100644 --- a/app/src/main/java/ua/acclorite/book_story/data/parser/txt/TxtTextParser.kt +++ b/app/src/main/java/ua/acclorite/book_story/data/parser/text/TxtTextParser.kt @@ -4,7 +4,7 @@ * SPDX-License-Identifier: GPL-3.0-only */ -package ua.acclorite.book_story.data.parser.txt +package ua.acclorite.book_story.data.parser.text import android.util.Log import kotlinx.coroutines.Dispatchers @@ -12,8 +12,7 @@ import kotlinx.coroutines.withContext import kotlinx.coroutines.yield import ua.acclorite.book_story.core.helpers.clearAllMarkdown import ua.acclorite.book_story.data.model.file.CachedFile -import ua.acclorite.book_story.data.parser.MarkdownParser -import ua.acclorite.book_story.data.parser.TextParser +import ua.acclorite.book_story.data.parser.document.MarkdownParser import ua.acclorite.book_story.domain.model.reader.ReaderText import javax.inject.Inject diff --git a/app/src/main/java/ua/acclorite/book_story/data/parser/xml/XmlTextParser.kt b/app/src/main/java/ua/acclorite/book_story/data/parser/text/XmlTextParser.kt similarity index 90% rename from app/src/main/java/ua/acclorite/book_story/data/parser/xml/XmlTextParser.kt rename to app/src/main/java/ua/acclorite/book_story/data/parser/text/XmlTextParser.kt index 57a0625f..a5222c08 100644 --- a/app/src/main/java/ua/acclorite/book_story/data/parser/xml/XmlTextParser.kt +++ b/app/src/main/java/ua/acclorite/book_story/data/parser/text/XmlTextParser.kt @@ -4,15 +4,14 @@ * SPDX-License-Identifier: GPL-3.0-only */ -package ua.acclorite.book_story.data.parser.xml +package ua.acclorite.book_story.data.parser.text import android.util.Log import kotlinx.coroutines.yield import org.jsoup.Jsoup import org.jsoup.parser.Parser import ua.acclorite.book_story.data.model.file.CachedFile -import ua.acclorite.book_story.data.parser.DocumentParser -import ua.acclorite.book_story.data.parser.TextParser +import ua.acclorite.book_story.data.parser.document.DocumentParser import ua.acclorite.book_story.domain.model.reader.ReaderText import javax.inject.Inject diff --git a/app/src/main/java/ua/acclorite/book_story/data/repository/BookRepositoryImpl.kt b/app/src/main/java/ua/acclorite/book_story/data/repository/BookRepositoryImpl.kt index 89930859..42d5c328 100644 --- a/app/src/main/java/ua/acclorite/book_story/data/repository/BookRepositoryImpl.kt +++ b/app/src/main/java/ua/acclorite/book_story/data/repository/BookRepositoryImpl.kt @@ -12,8 +12,8 @@ import ua.acclorite.book_story.core.CoverImage import ua.acclorite.book_story.data.local.room.BookDatabase import ua.acclorite.book_story.data.mapper.book.BookMapper import ua.acclorite.book_story.data.mapper.file.FileMapper -import ua.acclorite.book_story.data.parser.FileParser -import ua.acclorite.book_story.data.parser.TextParser +import ua.acclorite.book_story.data.parser.file.FileParser +import ua.acclorite.book_story.data.parser.text.TextParser import ua.acclorite.book_story.domain.model.file.File import ua.acclorite.book_story.domain.model.library.Book import ua.acclorite.book_story.domain.model.reader.ReaderText diff --git a/app/src/main/java/ua/acclorite/book_story/data/repository/FileSystemRepositoryImpl.kt b/app/src/main/java/ua/acclorite/book_story/data/repository/FileSystemRepositoryImpl.kt index 3c7b935d..8656286f 100644 --- a/app/src/main/java/ua/acclorite/book_story/data/repository/FileSystemRepositoryImpl.kt +++ b/app/src/main/java/ua/acclorite/book_story/data/repository/FileSystemRepositoryImpl.kt @@ -13,7 +13,7 @@ import ua.acclorite.book_story.data.local.room.BookDatabase import ua.acclorite.book_story.data.mapper.file.FileMapper import ua.acclorite.book_story.data.model.common.BookWithCover import ua.acclorite.book_story.data.model.file.CachedFile -import ua.acclorite.book_story.data.parser.FileParser +import ua.acclorite.book_story.data.parser.file.FileParser import ua.acclorite.book_story.domain.model.file.File import ua.acclorite.book_story.domain.repository.FileSystemRepository import ua.acclorite.book_story.domain.service.FileProvider From 9e17d86588bc65e08a1965f7294a601394f4d6b7 Mon Sep 17 00:00:00 2001 From: Acclorite <2ma0bhpk@anonaddy.com> Date: Wed, 12 Nov 2025 10:12:58 +0200 Subject: [PATCH 2/2] refactor: cover parser --- .../book_story/data/di/RepositoryModule.kt | 8 ++ .../data/model/common/BookWithCover.kt | 17 --- .../data/model/common/NullableBook.kt | 34 ----- .../data/parser/cover/CoverParser.kt | 14 ++ .../data/parser/cover/CoverParserImpl.kt | 64 +++++++++ .../data/parser/cover/EpubCoverParser.kt | 83 ++++++++++++ .../data/parser/cover/Fb2CoverParser.kt | 18 +++ .../data/parser/cover/HtmlCoverParser.kt | 18 +++ .../data/parser/cover/PdfCoverParser.kt | 18 +++ .../data/parser/cover/TxtCoverParser.kt | 18 +++ .../data/parser/file/EpubFileParser.kt | 74 ++--------- .../data/parser/file/Fb2FileParser.kt | 26 ++-- .../book_story/data/parser/file/FileParser.kt | 4 +- .../data/parser/file/FileParserImpl.kt | 4 +- .../data/parser/file/HtmlFileParser.kt | 26 ++-- .../data/parser/file/PdfFileParser.kt | 26 ++-- .../data/parser/file/TxtFileParser.kt | 26 ++-- .../data/repository/BookRepositoryImpl.kt | 9 +- .../repository/FileSystemRepositoryImpl.kt | 22 +++- .../domain/repository/FileSystemRepository.kt | 5 +- .../domain/use_case/book/AddBookUseCase.kt | 4 +- .../book/CanResetCoverImageUseCase.kt | 4 +- .../use_case/book/ResetCoverImageUseCase.kt | 4 +- .../file_system/GetBookFromFileUseCase.kt | 18 +-- .../presentation/browse/BrowseEvent.kt | 2 +- .../presentation/browse/BrowseModel.kt | 46 ++++--- .../presentation/browse/BrowseState.kt | 2 +- .../presentation/browse/model/NullableBook.kt | 25 ++++ .../model/SelectableNullableBook.kt | 3 +- .../book_story/ui/browse/BrowseAddDialog.kt | 32 ++--- .../ui/browse/BrowseAddDialogItem.kt | 124 +++++++++--------- .../book_story/ui/browse/BrowseContent.kt | 2 +- .../book_story/ui/browse/BrowseDialog.kt | 2 +- 33 files changed, 477 insertions(+), 305 deletions(-) delete mode 100644 app/src/main/java/ua/acclorite/book_story/data/model/common/BookWithCover.kt delete mode 100644 app/src/main/java/ua/acclorite/book_story/data/model/common/NullableBook.kt create mode 100644 app/src/main/java/ua/acclorite/book_story/data/parser/cover/CoverParser.kt create mode 100644 app/src/main/java/ua/acclorite/book_story/data/parser/cover/CoverParserImpl.kt create mode 100644 app/src/main/java/ua/acclorite/book_story/data/parser/cover/EpubCoverParser.kt create mode 100644 app/src/main/java/ua/acclorite/book_story/data/parser/cover/Fb2CoverParser.kt create mode 100644 app/src/main/java/ua/acclorite/book_story/data/parser/cover/HtmlCoverParser.kt create mode 100644 app/src/main/java/ua/acclorite/book_story/data/parser/cover/PdfCoverParser.kt create mode 100644 app/src/main/java/ua/acclorite/book_story/data/parser/cover/TxtCoverParser.kt create mode 100644 app/src/main/java/ua/acclorite/book_story/presentation/browse/model/NullableBook.kt rename app/src/main/java/ua/acclorite/book_story/presentation/{library => browse}/model/SelectableNullableBook.kt (71%) diff --git a/app/src/main/java/ua/acclorite/book_story/data/di/RepositoryModule.kt b/app/src/main/java/ua/acclorite/book_story/data/di/RepositoryModule.kt index d14e5d66..4a027397 100644 --- a/app/src/main/java/ua/acclorite/book_story/data/di/RepositoryModule.kt +++ b/app/src/main/java/ua/acclorite/book_story/data/di/RepositoryModule.kt @@ -22,6 +22,8 @@ import ua.acclorite.book_story.data.mapper.file.FileMapper import ua.acclorite.book_story.data.mapper.file.FileMapperImpl import ua.acclorite.book_story.data.mapper.history.HistoryMapper import ua.acclorite.book_story.data.mapper.history.HistoryMapperImpl +import ua.acclorite.book_story.data.parser.cover.CoverParser +import ua.acclorite.book_story.data.parser.cover.CoverParserImpl import ua.acclorite.book_story.data.parser.file.FileParser import ua.acclorite.book_story.data.parser.file.FileParserImpl import ua.acclorite.book_story.data.parser.text.TextParser @@ -121,6 +123,12 @@ abstract class RepositoryModule { fileParserImpl: FileParserImpl ): FileParser + @Binds + @Singleton + abstract fun bindCoverParser( + coverParserImpl: CoverParserImpl + ): CoverParser + @Binds @Singleton abstract fun bindTextParser( diff --git a/app/src/main/java/ua/acclorite/book_story/data/model/common/BookWithCover.kt b/app/src/main/java/ua/acclorite/book_story/data/model/common/BookWithCover.kt deleted file mode 100644 index 6d9fda06..00000000 --- a/app/src/main/java/ua/acclorite/book_story/data/model/common/BookWithCover.kt +++ /dev/null @@ -1,17 +0,0 @@ -/* - * 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.data.model.common - -import androidx.compose.runtime.Immutable -import ua.acclorite.book_story.core.CoverImage -import ua.acclorite.book_story.domain.model.library.Book - -@Immutable -data class BookWithCover( - val book: Book, - val coverImage: CoverImage? -) \ No newline at end of file diff --git a/app/src/main/java/ua/acclorite/book_story/data/model/common/NullableBook.kt b/app/src/main/java/ua/acclorite/book_story/data/model/common/NullableBook.kt deleted file mode 100644 index 157781e6..00000000 --- a/app/src/main/java/ua/acclorite/book_story/data/model/common/NullableBook.kt +++ /dev/null @@ -1,34 +0,0 @@ -/* - * 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.data.model.common - -import androidx.compose.runtime.Immutable -import ua.acclorite.book_story.core.ui.UIText - -@Immutable -sealed class NullableBook( - val bookWithCover: BookWithCover?, - val fileName: String?, - val message: UIText? -) { - class NotNull( - bookWithCover: BookWithCover - ) : NullableBook( - bookWithCover = bookWithCover, - fileName = null, - message = null - ) - - class Null( - fileName: String, - message: UIText? - ) : NullableBook( - bookWithCover = null, - fileName = fileName, - message = message - ) -} \ No newline at end of file diff --git a/app/src/main/java/ua/acclorite/book_story/data/parser/cover/CoverParser.kt b/app/src/main/java/ua/acclorite/book_story/data/parser/cover/CoverParser.kt new file mode 100644 index 00000000..08d687b1 --- /dev/null +++ b/app/src/main/java/ua/acclorite/book_story/data/parser/cover/CoverParser.kt @@ -0,0 +1,14 @@ +/* + * 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.data.parser.cover + +import ua.acclorite.book_story.core.CoverImage +import ua.acclorite.book_story.data.model.file.CachedFile + +interface CoverParser { + suspend fun parse(cachedFile: CachedFile): CoverImage? +} \ No newline at end of file diff --git a/app/src/main/java/ua/acclorite/book_story/data/parser/cover/CoverParserImpl.kt b/app/src/main/java/ua/acclorite/book_story/data/parser/cover/CoverParserImpl.kt new file mode 100644 index 00000000..2c874cd4 --- /dev/null +++ b/app/src/main/java/ua/acclorite/book_story/data/parser/cover/CoverParserImpl.kt @@ -0,0 +1,64 @@ +/* + * 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.data.parser.cover + +import ua.acclorite.book_story.core.CoverImage +import ua.acclorite.book_story.core.log.logE +import ua.acclorite.book_story.data.model.file.CachedFile +import javax.inject.Inject + +class CoverParserImpl @Inject constructor( + private val epubCoverParser: EpubCoverParser, + private val txtCoverParser: TxtCoverParser, + private val pdfCoverParser: PdfCoverParser, + private val fb2CoverParser: Fb2CoverParser, + private val htmlCoverParser: HtmlCoverParser +) : CoverParser { + + override suspend fun parse(cachedFile: CachedFile): CoverImage? { + if (!cachedFile.canAccess()) { + logE("File does not exist or no read access is granted.") + return null + } + + val fileFormat = ".${cachedFile.name.substringAfterLast(".")}".lowercase().trim() + return when (fileFormat) { + ".pdf" -> { + pdfCoverParser.parse(cachedFile) + } + + ".epub" -> { + epubCoverParser.parse(cachedFile) + } + + ".txt" -> { + txtCoverParser.parse(cachedFile) + } + + ".fb2" -> { + fb2CoverParser.parse(cachedFile) + } + + ".html" -> { + htmlCoverParser.parse(cachedFile) + } + + ".htm" -> { + htmlCoverParser.parse(cachedFile) + } + + ".md" -> { + txtCoverParser.parse(cachedFile) + } + + else -> { + logE("Wrong file format, could not find supported extension.") + null + } + } + } +} \ No newline at end of file diff --git a/app/src/main/java/ua/acclorite/book_story/data/parser/cover/EpubCoverParser.kt b/app/src/main/java/ua/acclorite/book_story/data/parser/cover/EpubCoverParser.kt new file mode 100644 index 00000000..facf3d86 --- /dev/null +++ b/app/src/main/java/ua/acclorite/book_story/data/parser/cover/EpubCoverParser.kt @@ -0,0 +1,83 @@ +/* + * 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.data.parser.cover + +import android.graphics.BitmapFactory +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.withContext +import org.jsoup.Jsoup +import org.jsoup.parser.Parser +import ua.acclorite.book_story.core.CoverImage +import ua.acclorite.book_story.core.log.logE +import ua.acclorite.book_story.data.model.file.CachedFile +import java.net.URLDecoder +import java.nio.charset.StandardCharsets +import java.util.zip.ZipFile +import javax.inject.Inject + +class EpubCoverParser @Inject constructor() : CoverParser { + + override suspend fun parse(cachedFile: CachedFile): CoverImage? { + return try { + var coverImage: CoverImage? = null + + val rawFile = cachedFile.rawFile + if (rawFile == null || !rawFile.exists() || !rawFile.canRead()) return null + + withContext(Dispatchers.IO) { + ZipFile(rawFile).use { zip -> + val opfEntry = zip.entries().asSequence().find { entry -> + entry.name.endsWith(".opf", ignoreCase = true) + } ?: return@withContext + + val opfContent = zip + .getInputStream(opfEntry) + .bufferedReader() + .use { it.readText() } + val document = Jsoup.parse(opfContent, Parser.xmlParser()) + + val coverImagePath = document + .select("metadata > meta[name=cover]") + .attr("content") + .let { coverId -> + if (coverId.isNotBlank()) { + document + .select("manifest > item[id=$coverId]") + .attr("href") + .also { src -> + if (src.isBlank()) return@also + return@let src + } + } + + document + .select("manifest > item[media-type*=image]") + .firstOrNull() + ?.attr("href") + ?.also { src -> if (src.isBlank()) return@let null } + } + ?.let { src -> URLDecoder.decode(src, StandardCharsets.UTF_8.name()) } + ?: return@use + + zip.entries().asSequence().first { entry -> + if (entry.name.endsWith(coverImagePath)) { + val imageBytes = zip.getInputStream(entry).readBytes() + coverImage = BitmapFactory.decodeByteArray( + imageBytes, 0, imageBytes.size + ) + return@first true + } else return@first false + } + } + } + coverImage + } catch (_: Exception) { + logE("Could not parse cover image.") + null + } + } +} \ No newline at end of file diff --git a/app/src/main/java/ua/acclorite/book_story/data/parser/cover/Fb2CoverParser.kt b/app/src/main/java/ua/acclorite/book_story/data/parser/cover/Fb2CoverParser.kt new file mode 100644 index 00000000..33440303 --- /dev/null +++ b/app/src/main/java/ua/acclorite/book_story/data/parser/cover/Fb2CoverParser.kt @@ -0,0 +1,18 @@ +/* + * 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.data.parser.cover + +import ua.acclorite.book_story.core.CoverImage +import ua.acclorite.book_story.data.model.file.CachedFile +import javax.inject.Inject + +class Fb2CoverParser @Inject constructor() : CoverParser { + + override suspend fun parse(cachedFile: CachedFile): CoverImage? { + return null + } +} \ No newline at end of file diff --git a/app/src/main/java/ua/acclorite/book_story/data/parser/cover/HtmlCoverParser.kt b/app/src/main/java/ua/acclorite/book_story/data/parser/cover/HtmlCoverParser.kt new file mode 100644 index 00000000..548a0c26 --- /dev/null +++ b/app/src/main/java/ua/acclorite/book_story/data/parser/cover/HtmlCoverParser.kt @@ -0,0 +1,18 @@ +/* + * 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.data.parser.cover + +import ua.acclorite.book_story.core.CoverImage +import ua.acclorite.book_story.data.model.file.CachedFile +import javax.inject.Inject + +class HtmlCoverParser @Inject constructor() : CoverParser { + + override suspend fun parse(cachedFile: CachedFile): CoverImage? { + return null + } +} \ No newline at end of file diff --git a/app/src/main/java/ua/acclorite/book_story/data/parser/cover/PdfCoverParser.kt b/app/src/main/java/ua/acclorite/book_story/data/parser/cover/PdfCoverParser.kt new file mode 100644 index 00000000..c4852116 --- /dev/null +++ b/app/src/main/java/ua/acclorite/book_story/data/parser/cover/PdfCoverParser.kt @@ -0,0 +1,18 @@ +/* + * 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.data.parser.cover + +import ua.acclorite.book_story.core.CoverImage +import ua.acclorite.book_story.data.model.file.CachedFile +import javax.inject.Inject + +class PdfCoverParser @Inject constructor() : CoverParser { + + override suspend fun parse(cachedFile: CachedFile): CoverImage? { + return null + } +} \ No newline at end of file diff --git a/app/src/main/java/ua/acclorite/book_story/data/parser/cover/TxtCoverParser.kt b/app/src/main/java/ua/acclorite/book_story/data/parser/cover/TxtCoverParser.kt new file mode 100644 index 00000000..31802ac3 --- /dev/null +++ b/app/src/main/java/ua/acclorite/book_story/data/parser/cover/TxtCoverParser.kt @@ -0,0 +1,18 @@ +/* + * 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.data.parser.cover + +import ua.acclorite.book_story.core.CoverImage +import ua.acclorite.book_story.data.model.file.CachedFile +import javax.inject.Inject + +class TxtCoverParser @Inject constructor() : CoverParser { + + override suspend fun parse(cachedFile: CachedFile): CoverImage? { + return null + } +} \ No newline at end of file diff --git a/app/src/main/java/ua/acclorite/book_story/data/parser/file/EpubFileParser.kt b/app/src/main/java/ua/acclorite/book_story/data/parser/file/EpubFileParser.kt index 4a2fafe4..bcd70fa9 100644 --- a/app/src/main/java/ua/acclorite/book_story/data/parser/file/EpubFileParser.kt +++ b/app/src/main/java/ua/acclorite/book_story/data/parser/file/EpubFileParser.kt @@ -6,28 +6,22 @@ package ua.acclorite.book_story.data.parser.file -import android.graphics.Bitmap -import android.graphics.BitmapFactory import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.withContext import org.jsoup.Jsoup import org.jsoup.parser.Parser import ua.acclorite.book_story.R import ua.acclorite.book_story.core.ui.UIText -import ua.acclorite.book_story.data.model.common.BookWithCover import ua.acclorite.book_story.data.model.file.CachedFile import ua.acclorite.book_story.domain.model.library.Book -import java.io.File -import java.net.URLDecoder -import java.nio.charset.StandardCharsets import java.util.zip.ZipFile import javax.inject.Inject class EpubFileParser @Inject constructor() : FileParser { - override suspend fun parse(cachedFile: CachedFile): BookWithCover? { + override suspend fun parse(cachedFile: CachedFile): Book? { return try { - var book: BookWithCover? = null + var book: Book? = null val rawFile = cachedFile.rawFile if (rawFile == null || !rawFile.exists() || !rawFile.canRead()) return null @@ -66,42 +60,17 @@ class EpubFileParser @Inject constructor() : FileParser { } } - val coverImage = document - .select("metadata > meta[name=cover]") - .attr("content") - .let { coverId -> - if (coverId.isNotBlank()) { - document - .select("manifest > item[id=$coverId]") - .attr("href") - .also { src -> - if (src.isBlank()) return@also - return@let src - } - } - - document - .select("manifest > item[media-type*=image]") - .firstOrNull() - ?.attr("href") - ?.also { src -> if (src.isBlank()) return@let null } - } - ?.let { src -> URLDecoder.decode(src, StandardCharsets.UTF_8.name()) } - - book = BookWithCover( - book = Book( - title = title, - author = author, - description = description, - scrollIndex = 0, - scrollOffset = 0, - progress = 0f, - filePath = cachedFile.path, - lastOpened = null, - categories = emptyList(), - coverImage = null - ), - coverImage = extractCoverImageBitmap(rawFile, coverImage) + book = Book( + title = title, + author = author, + description = description, + scrollIndex = 0, + scrollOffset = 0, + progress = 0f, + filePath = cachedFile.path, + lastOpened = null, + categories = emptyList(), + coverImage = null ) } } @@ -111,21 +80,4 @@ class EpubFileParser @Inject constructor() : FileParser { null } } - - private fun extractCoverImageBitmap(file: File, coverImagePath: String?): Bitmap? { - if (coverImagePath.isNullOrBlank()) { - return null - } - - ZipFile(file).use { zip -> - zip.entries().asSequence().forEach { entry -> - if (entry.name.endsWith(coverImagePath)) { - val imageBytes = zip.getInputStream(entry).readBytes() - return BitmapFactory.decodeByteArray(imageBytes, 0, imageBytes.size) - } - } - } - - return null - } } \ No newline at end of file diff --git a/app/src/main/java/ua/acclorite/book_story/data/parser/file/Fb2FileParser.kt b/app/src/main/java/ua/acclorite/book_story/data/parser/file/Fb2FileParser.kt index 7b9bf45c..23740b6a 100644 --- a/app/src/main/java/ua/acclorite/book_story/data/parser/file/Fb2FileParser.kt +++ b/app/src/main/java/ua/acclorite/book_story/data/parser/file/Fb2FileParser.kt @@ -10,14 +10,13 @@ import org.jsoup.Jsoup import org.jsoup.parser.Parser import ua.acclorite.book_story.R import ua.acclorite.book_story.core.ui.UIText -import ua.acclorite.book_story.data.model.common.BookWithCover import ua.acclorite.book_story.data.model.file.CachedFile import ua.acclorite.book_story.domain.model.library.Book import javax.inject.Inject class Fb2FileParser @Inject constructor() : FileParser { - override suspend fun parse(cachedFile: CachedFile): BookWithCover? { + override suspend fun parse(cachedFile: CachedFile): Book? { return try { val document = cachedFile.openInputStream()?.use { Jsoup.parse(it, null, "", Parser.xmlParser()) @@ -44,19 +43,16 @@ class Fb2FileParser @Inject constructor() : FileParser { this } - BookWithCover( - book = Book( - title = title, - author = author, - description = description, - scrollIndex = 0, - scrollOffset = 0, - progress = 0f, - filePath = cachedFile.path, - lastOpened = null, - categories = emptyList(), - coverImage = null - ), + Book( + title = title, + author = author, + description = description, + scrollIndex = 0, + scrollOffset = 0, + progress = 0f, + filePath = cachedFile.path, + lastOpened = null, + categories = emptyList(), coverImage = null ) } catch (e: Exception) { diff --git a/app/src/main/java/ua/acclorite/book_story/data/parser/file/FileParser.kt b/app/src/main/java/ua/acclorite/book_story/data/parser/file/FileParser.kt index 0c327b03..e4519aee 100644 --- a/app/src/main/java/ua/acclorite/book_story/data/parser/file/FileParser.kt +++ b/app/src/main/java/ua/acclorite/book_story/data/parser/file/FileParser.kt @@ -6,9 +6,9 @@ package ua.acclorite.book_story.data.parser.file -import ua.acclorite.book_story.data.model.common.BookWithCover import ua.acclorite.book_story.data.model.file.CachedFile +import ua.acclorite.book_story.domain.model.library.Book interface FileParser { - suspend fun parse(cachedFile: CachedFile): BookWithCover? + suspend fun parse(cachedFile: CachedFile): Book? } \ No newline at end of file diff --git a/app/src/main/java/ua/acclorite/book_story/data/parser/file/FileParserImpl.kt b/app/src/main/java/ua/acclorite/book_story/data/parser/file/FileParserImpl.kt index 8503898b..1b51764d 100644 --- a/app/src/main/java/ua/acclorite/book_story/data/parser/file/FileParserImpl.kt +++ b/app/src/main/java/ua/acclorite/book_story/data/parser/file/FileParserImpl.kt @@ -7,8 +7,8 @@ package ua.acclorite.book_story.data.parser.file import android.util.Log -import ua.acclorite.book_story.data.model.common.BookWithCover import ua.acclorite.book_story.data.model.file.CachedFile +import ua.acclorite.book_story.domain.model.library.Book import javax.inject.Inject private const val FILE_PARSER = "File Parser" @@ -21,7 +21,7 @@ class FileParserImpl @Inject constructor( private val htmlFileParser: HtmlFileParser, ) : FileParser { - override suspend fun parse(cachedFile: CachedFile): BookWithCover? { + override suspend fun parse(cachedFile: CachedFile): Book? { if (!cachedFile.canAccess()) { Log.e(FILE_PARSER, "File does not exist or no read access is granted.") return null diff --git a/app/src/main/java/ua/acclorite/book_story/data/parser/file/HtmlFileParser.kt b/app/src/main/java/ua/acclorite/book_story/data/parser/file/HtmlFileParser.kt index 3ec75de1..e4ddcc96 100644 --- a/app/src/main/java/ua/acclorite/book_story/data/parser/file/HtmlFileParser.kt +++ b/app/src/main/java/ua/acclorite/book_story/data/parser/file/HtmlFileParser.kt @@ -10,14 +10,13 @@ import org.jsoup.Jsoup import org.jsoup.parser.Parser import ua.acclorite.book_story.R import ua.acclorite.book_story.core.ui.UIText -import ua.acclorite.book_story.data.model.common.BookWithCover import ua.acclorite.book_story.data.model.file.CachedFile import ua.acclorite.book_story.domain.model.library.Book import javax.inject.Inject class HtmlFileParser @Inject constructor() : FileParser { - override suspend fun parse(cachedFile: CachedFile): BookWithCover? { + override suspend fun parse(cachedFile: CachedFile): Book? { return try { val document = cachedFile.openInputStream()?.use { Jsoup.parse(it, null, "", Parser.htmlParser()) @@ -30,19 +29,16 @@ class HtmlFileParser @Inject constructor() : FileParser { return@run this } - BookWithCover( - book = Book( - title = title, - author = UIText.StringResource(R.string.unknown_author), - description = null, - scrollIndex = 0, - scrollOffset = 0, - progress = 0f, - filePath = cachedFile.path, - lastOpened = null, - categories = emptyList(), - coverImage = null - ), + Book( + title = title, + author = UIText.StringResource(R.string.unknown_author), + description = null, + scrollIndex = 0, + scrollOffset = 0, + progress = 0f, + filePath = cachedFile.path, + lastOpened = null, + categories = emptyList(), coverImage = null ) } catch (e: Exception) { diff --git a/app/src/main/java/ua/acclorite/book_story/data/parser/file/PdfFileParser.kt b/app/src/main/java/ua/acclorite/book_story/data/parser/file/PdfFileParser.kt index 8bbc2a77..b36587ad 100644 --- a/app/src/main/java/ua/acclorite/book_story/data/parser/file/PdfFileParser.kt +++ b/app/src/main/java/ua/acclorite/book_story/data/parser/file/PdfFileParser.kt @@ -11,7 +11,6 @@ import com.tom_roush.pdfbox.android.PDFBoxResourceLoader import com.tom_roush.pdfbox.pdmodel.PDDocument import ua.acclorite.book_story.R import ua.acclorite.book_story.core.ui.UIText -import ua.acclorite.book_story.data.model.common.BookWithCover import ua.acclorite.book_story.data.model.file.CachedFile import ua.acclorite.book_story.domain.model.library.Book import javax.inject.Inject @@ -20,7 +19,7 @@ class PdfFileParser @Inject constructor( private val application: Application ) : FileParser { - override suspend fun parse(cachedFile: CachedFile): BookWithCover? { + override suspend fun parse(cachedFile: CachedFile): Book? { return try { PDFBoxResourceLoader.init(application) val document = PDDocument.load(cachedFile.openInputStream()) @@ -35,19 +34,16 @@ class PdfFileParser @Inject constructor( document.close() - BookWithCover( - book = Book( - title = title, - author = author, - description = description, - scrollIndex = 0, - scrollOffset = 0, - progress = 0f, - filePath = cachedFile.path, - lastOpened = null, - categories = emptyList(), - coverImage = null - ), + Book( + title = title, + author = author, + description = description, + scrollIndex = 0, + scrollOffset = 0, + progress = 0f, + filePath = cachedFile.path, + lastOpened = null, + categories = emptyList(), coverImage = null ) } catch (e: Exception) { diff --git a/app/src/main/java/ua/acclorite/book_story/data/parser/file/TxtFileParser.kt b/app/src/main/java/ua/acclorite/book_story/data/parser/file/TxtFileParser.kt index 0b4b56f1..c10d291c 100644 --- a/app/src/main/java/ua/acclorite/book_story/data/parser/file/TxtFileParser.kt +++ b/app/src/main/java/ua/acclorite/book_story/data/parser/file/TxtFileParser.kt @@ -8,31 +8,27 @@ package ua.acclorite.book_story.data.parser.file import ua.acclorite.book_story.R import ua.acclorite.book_story.core.ui.UIText -import ua.acclorite.book_story.data.model.common.BookWithCover import ua.acclorite.book_story.data.model.file.CachedFile import ua.acclorite.book_story.domain.model.library.Book import javax.inject.Inject class TxtFileParser @Inject constructor() : FileParser { - override suspend fun parse(cachedFile: CachedFile): BookWithCover? { + override suspend fun parse(cachedFile: CachedFile): Book? { return try { val title = cachedFile.name.substringBeforeLast(".").trim() val author = UIText.StringResource(R.string.unknown_author) - BookWithCover( - book = Book( - title = title, - author = author, - description = null, - scrollIndex = 0, - scrollOffset = 0, - progress = 0f, - filePath = cachedFile.path, - lastOpened = null, - categories = emptyList(), - coverImage = null - ), + Book( + title = title, + author = author, + description = null, + scrollIndex = 0, + scrollOffset = 0, + progress = 0f, + filePath = cachedFile.path, + lastOpened = null, + categories = emptyList(), coverImage = null ) } catch (e: Exception) { diff --git a/app/src/main/java/ua/acclorite/book_story/data/repository/BookRepositoryImpl.kt b/app/src/main/java/ua/acclorite/book_story/data/repository/BookRepositoryImpl.kt index 42d5c328..c66daf8d 100644 --- a/app/src/main/java/ua/acclorite/book_story/data/repository/BookRepositoryImpl.kt +++ b/app/src/main/java/ua/acclorite/book_story/data/repository/BookRepositoryImpl.kt @@ -12,7 +12,7 @@ import ua.acclorite.book_story.core.CoverImage import ua.acclorite.book_story.data.local.room.BookDatabase import ua.acclorite.book_story.data.mapper.book.BookMapper import ua.acclorite.book_story.data.mapper.file.FileMapper -import ua.acclorite.book_story.data.parser.file.FileParser +import ua.acclorite.book_story.data.parser.cover.CoverParser import ua.acclorite.book_story.data.parser.text.TextParser import ua.acclorite.book_story.domain.model.file.File import ua.acclorite.book_story.domain.model.library.Book @@ -27,7 +27,7 @@ class BookRepositoryImpl @Inject constructor( private val database: BookDatabase, private val bookMapper: BookMapper, private val fileMapper: FileMapper, - private val fileParser: FileParser, + private val coverParser: CoverParser, private val textParser: TextParser, private val fileProvider: FileProvider ) : BookRepository { @@ -87,8 +87,9 @@ class BookRepositoryImpl @Inject constructor( override suspend fun getDefaultCover(book: Book): Result = runCatching { return withContext(Dispatchers.IO) { - fileProvider.getFileFromBook(book) - .mapCatching { fileParser.parse(it)?.coverImage } + fileProvider.getFileFromBook(book).mapCatching { + coverParser.parse(it) + } } } } \ No newline at end of file diff --git a/app/src/main/java/ua/acclorite/book_story/data/repository/FileSystemRepositoryImpl.kt b/app/src/main/java/ua/acclorite/book_story/data/repository/FileSystemRepositoryImpl.kt index 8656286f..0fadaeb3 100644 --- a/app/src/main/java/ua/acclorite/book_story/data/repository/FileSystemRepositoryImpl.kt +++ b/app/src/main/java/ua/acclorite/book_story/data/repository/FileSystemRepositoryImpl.kt @@ -8,13 +8,15 @@ package ua.acclorite.book_story.data.repository import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.withContext +import ua.acclorite.book_story.core.CoverImage import ua.acclorite.book_story.core.data.ExtensionsData import ua.acclorite.book_story.data.local.room.BookDatabase import ua.acclorite.book_story.data.mapper.file.FileMapper -import ua.acclorite.book_story.data.model.common.BookWithCover import ua.acclorite.book_story.data.model.file.CachedFile +import ua.acclorite.book_story.data.parser.cover.CoverParser import ua.acclorite.book_story.data.parser.file.FileParser import ua.acclorite.book_story.domain.model.file.File +import ua.acclorite.book_story.domain.model.library.Book import ua.acclorite.book_story.domain.repository.FileSystemRepository import ua.acclorite.book_story.domain.service.FileProvider import javax.inject.Inject @@ -25,6 +27,7 @@ class FileSystemRepositoryImpl @Inject constructor( private val database: BookDatabase, private val fileMapper: FileMapper, private val fileParser: FileParser, + private val coverParser: CoverParser, private val fileProvider: FileProvider ) : FileSystemRepository { @@ -73,10 +76,17 @@ class FileSystemRepositoryImpl @Inject constructor( return files } - override suspend fun getBookFromFile(file: File): Result = runCatching { - withContext(Dispatchers.IO) { - fileParser.parse(fileMapper.toCachedFile(file)) - ?: throw Exception("Could not parse ${file.name}.") + override suspend fun getBookFromFile(file: File): Result> = + runCatching { + withContext(Dispatchers.IO) { + val cachedFile = fileMapper.toCachedFile(file) + + val book = fileParser.parse( + cachedFile = cachedFile + ) ?: throw Exception("Could not parse ${file.name}.") + val coverImage = coverParser.parse(cachedFile = cachedFile) + + return@withContext book to coverImage + } } - } } \ No newline at end of file diff --git a/app/src/main/java/ua/acclorite/book_story/domain/repository/FileSystemRepository.kt b/app/src/main/java/ua/acclorite/book_story/domain/repository/FileSystemRepository.kt index 48be7e21..223e14bc 100644 --- a/app/src/main/java/ua/acclorite/book_story/domain/repository/FileSystemRepository.kt +++ b/app/src/main/java/ua/acclorite/book_story/domain/repository/FileSystemRepository.kt @@ -6,8 +6,9 @@ package ua.acclorite.book_story.domain.repository -import ua.acclorite.book_story.data.model.common.BookWithCover +import ua.acclorite.book_story.core.CoverImage import ua.acclorite.book_story.domain.model.file.File +import ua.acclorite.book_story.domain.model.library.Book interface FileSystemRepository { suspend fun searchFiles( @@ -16,5 +17,5 @@ interface FileSystemRepository { suspend fun getBookFromFile( file: File - ): Result + ): Result> } \ No newline at end of file diff --git a/app/src/main/java/ua/acclorite/book_story/domain/use_case/book/AddBookUseCase.kt b/app/src/main/java/ua/acclorite/book_story/domain/use_case/book/AddBookUseCase.kt index 5e72915c..6bba8499 100644 --- a/app/src/main/java/ua/acclorite/book_story/domain/use_case/book/AddBookUseCase.kt +++ b/app/src/main/java/ua/acclorite/book_story/domain/use_case/book/AddBookUseCase.kt @@ -24,8 +24,8 @@ class AddBookUseCase @Inject constructor( suspend operator fun invoke(book: Book, coverImage: CoverImage?) { logI("Inserting [${book.title}].") - val coverImageUri = coverImage?.let { - coverImageHandler.saveCover(it).fold( + val coverImageUri = coverImage?.let { coverImage -> + coverImageHandler.saveCover(coverImage).fold( onSuccess = { logI("Successfully saved cover image of [${book.title}].") it.toUri() diff --git a/app/src/main/java/ua/acclorite/book_story/domain/use_case/book/CanResetCoverImageUseCase.kt b/app/src/main/java/ua/acclorite/book_story/domain/use_case/book/CanResetCoverImageUseCase.kt index 6853984d..2c6fcf89 100644 --- a/app/src/main/java/ua/acclorite/book_story/domain/use_case/book/CanResetCoverImageUseCase.kt +++ b/app/src/main/java/ua/acclorite/book_story/domain/use_case/book/CanResetCoverImageUseCase.kt @@ -26,9 +26,7 @@ class CanResetCoverImageUseCase @Inject constructor( bookRepository.getBook(bookId).mapCatching { book -> // Getting default cover image val defaultCoverImage = bookRepository.getDefaultCover(book).getOrThrow() - if (defaultCoverImage == null) { - return@mapCatching false - } + ?: return@mapCatching false // Return true if current cover is null (and default is not) if (book.coverImage == null) { diff --git a/app/src/main/java/ua/acclorite/book_story/domain/use_case/book/ResetCoverImageUseCase.kt b/app/src/main/java/ua/acclorite/book_story/domain/use_case/book/ResetCoverImageUseCase.kt index 969b163a..9678451c 100644 --- a/app/src/main/java/ua/acclorite/book_story/domain/use_case/book/ResetCoverImageUseCase.kt +++ b/app/src/main/java/ua/acclorite/book_story/domain/use_case/book/ResetCoverImageUseCase.kt @@ -25,9 +25,7 @@ class ResetCoverImageUseCase @Inject constructor( bookRepository.getBook(bookId).mapCatching { book -> // Getting default cover image val defaultCoverImage = bookRepository.getDefaultCover(book).getOrThrow() - if (defaultCoverImage == null) { - throw NoSuchElementException("Could not find default cover image") - } + ?: throw NoSuchElementException("Could not find default cover image") // Deleting old cover book.coverImage?.let { coverImageHandler.deleteCover(it) }?.onFailure { diff --git a/app/src/main/java/ua/acclorite/book_story/domain/use_case/file_system/GetBookFromFileUseCase.kt b/app/src/main/java/ua/acclorite/book_story/domain/use_case/file_system/GetBookFromFileUseCase.kt index 97f75e40..b6a8103c 100644 --- a/app/src/main/java/ua/acclorite/book_story/domain/use_case/file_system/GetBookFromFileUseCase.kt +++ b/app/src/main/java/ua/acclorite/book_story/domain/use_case/file_system/GetBookFromFileUseCase.kt @@ -6,14 +6,11 @@ package ua.acclorite.book_story.domain.use_case.file_system -import ua.acclorite.book_story.R +import ua.acclorite.book_story.core.CoverImage import ua.acclorite.book_story.core.log.logE import ua.acclorite.book_story.core.log.logI -import ua.acclorite.book_story.core.ui.UIText -import ua.acclorite.book_story.data.model.common.NullableBook -import ua.acclorite.book_story.data.model.common.NullableBook.NotNull -import ua.acclorite.book_story.data.model.common.NullableBook.Null import ua.acclorite.book_story.domain.model.file.File +import ua.acclorite.book_story.domain.model.library.Book import ua.acclorite.book_story.domain.repository.FileSystemRepository import javax.inject.Inject @@ -21,20 +18,17 @@ class GetBookFromFileUseCase @Inject constructor( private val fileSystemRepository: FileSystemRepository ) { - suspend operator fun invoke(file: File): NullableBook { + suspend operator fun invoke(file: File): Pair? { logI("Getting book from [${file.name}] file.") return fileSystemRepository.getBookFromFile(file).fold( onSuccess = { - logI("Successfully got [${it.book.title}] book from file.") - NotNull(it) + logI("Successfully got [${it.first.title}] book from file.") + it }, onFailure = { logE("Could not get book from file with error: ${it.message}") - Null( - file.name, - UIText.StringResource(R.string.error_something_went_wrong) - ) + null } ) } diff --git a/app/src/main/java/ua/acclorite/book_story/presentation/browse/BrowseEvent.kt b/app/src/main/java/ua/acclorite/book_story/presentation/browse/BrowseEvent.kt index 7a4f3fe1..7764038a 100644 --- a/app/src/main/java/ua/acclorite/book_story/presentation/browse/BrowseEvent.kt +++ b/app/src/main/java/ua/acclorite/book_story/presentation/browse/BrowseEvent.kt @@ -8,7 +8,7 @@ package ua.acclorite.book_story.presentation.browse import androidx.compose.runtime.Immutable import ua.acclorite.book_story.presentation.browse.model.SelectableFile -import ua.acclorite.book_story.presentation.library.model.SelectableNullableBook +import ua.acclorite.book_story.presentation.browse.model.SelectableNullableBook @Immutable sealed class BrowseEvent { diff --git a/app/src/main/java/ua/acclorite/book_story/presentation/browse/BrowseModel.kt b/app/src/main/java/ua/acclorite/book_story/presentation/browse/BrowseModel.kt index 95d6c6e3..2c53d40d 100644 --- a/app/src/main/java/ua/acclorite/book_story/presentation/browse/BrowseModel.kt +++ b/app/src/main/java/ua/acclorite/book_story/presentation/browse/BrowseModel.kt @@ -24,14 +24,16 @@ import kotlinx.coroutines.launch import kotlinx.coroutines.sync.Mutex import kotlinx.coroutines.sync.withLock import kotlinx.coroutines.withContext -import ua.acclorite.book_story.data.model.common.NullableBook +import ua.acclorite.book_story.R +import ua.acclorite.book_story.core.ui.UIText import ua.acclorite.book_story.domain.model.file.File import ua.acclorite.book_story.domain.use_case.book.AddBookUseCase import ua.acclorite.book_story.domain.use_case.file_system.GetBookFromFileUseCase import ua.acclorite.book_story.domain.use_case.file_system.GetFilesUseCase +import ua.acclorite.book_story.presentation.browse.model.NullableBook import ua.acclorite.book_story.presentation.browse.model.SelectableFile +import ua.acclorite.book_story.presentation.browse.model.SelectableNullableBook import ua.acclorite.book_story.presentation.library.LibraryScreen -import ua.acclorite.book_story.presentation.library.model.SelectableNullableBook import javax.inject.Inject import kotlin.coroutines.coroutineContext @@ -248,17 +250,29 @@ class BrowseModel @Inject constructor( val books = _state.value.files.mapNotNull { file -> ensureActive() if (!file.selected) return@mapNotNull null - SelectableNullableBook( - data = getBookFromFileUseCase( - File( - name = file.name, - uri = file.uri.toString(), - path = file.path, - size = file.size, - lastModified = file.lastModified, - isDirectory = file.isDirectory + + val nullableBook = getBookFromFileUseCase( + File( + name = file.name, + uri = file.uri.toString(), + path = file.path, + size = file.size, + lastModified = file.lastModified, + isDirectory = file.isDirectory + ) + ).let { + if (it != null) { + NullableBook.NotNull(it.first, it.second) + } else { + NullableBook.Null( + fileName = file.name, + UIText.StringResource(resId = R.string.error_something_went_wrong) ) - ), + } + } + + SelectableNullableBook( + data = nullableBook, selected = true ) } @@ -294,12 +308,12 @@ class BrowseModel @Inject constructor( is BrowseEvent.OnActionAddDialog -> { withContext(Dispatchers.Default) { _state.value.selectedBooksAddDialog.mapNotNull { - if (it.data is NullableBook.NotNull && it.selected) return@mapNotNull it.data.bookWithCover + if (it.data is NullableBook.NotNull && it.selected) return@mapNotNull it.data return@mapNotNull null - }.ifEmpty { return@withContext }.forEach { bookWithCover -> + }.ifEmpty { return@withContext }.forEach { nullableBook -> addBookUseCase( - bookWithCover.book, - bookWithCover.coverImage + nullableBook.book, + nullableBook.coverImage ) } diff --git a/app/src/main/java/ua/acclorite/book_story/presentation/browse/BrowseState.kt b/app/src/main/java/ua/acclorite/book_story/presentation/browse/BrowseState.kt index 30ae0233..951178d4 100644 --- a/app/src/main/java/ua/acclorite/book_story/presentation/browse/BrowseState.kt +++ b/app/src/main/java/ua/acclorite/book_story/presentation/browse/BrowseState.kt @@ -10,7 +10,7 @@ import androidx.compose.runtime.Immutable import ua.acclorite.book_story.core.BottomSheet import ua.acclorite.book_story.core.Dialog import ua.acclorite.book_story.presentation.browse.model.SelectableFile -import ua.acclorite.book_story.presentation.library.model.SelectableNullableBook +import ua.acclorite.book_story.presentation.browse.model.SelectableNullableBook @Immutable data class BrowseState( diff --git a/app/src/main/java/ua/acclorite/book_story/presentation/browse/model/NullableBook.kt b/app/src/main/java/ua/acclorite/book_story/presentation/browse/model/NullableBook.kt new file mode 100644 index 00000000..a1091b1b --- /dev/null +++ b/app/src/main/java/ua/acclorite/book_story/presentation/browse/model/NullableBook.kt @@ -0,0 +1,25 @@ +/* + * 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.browse.model + +import androidx.compose.runtime.Immutable +import ua.acclorite.book_story.core.CoverImage +import ua.acclorite.book_story.core.ui.UIText +import ua.acclorite.book_story.domain.model.library.Book + +@Immutable +sealed class NullableBook { + data class NotNull( + val book: Book, + val coverImage: CoverImage? + ) : NullableBook() + + data class Null( + val fileName: String, + val message: UIText? + ) : NullableBook() +} \ No newline at end of file diff --git a/app/src/main/java/ua/acclorite/book_story/presentation/library/model/SelectableNullableBook.kt b/app/src/main/java/ua/acclorite/book_story/presentation/browse/model/SelectableNullableBook.kt similarity index 71% rename from app/src/main/java/ua/acclorite/book_story/presentation/library/model/SelectableNullableBook.kt rename to app/src/main/java/ua/acclorite/book_story/presentation/browse/model/SelectableNullableBook.kt index 1c2b9f21..f2c5516f 100644 --- a/app/src/main/java/ua/acclorite/book_story/presentation/library/model/SelectableNullableBook.kt +++ b/app/src/main/java/ua/acclorite/book_story/presentation/browse/model/SelectableNullableBook.kt @@ -4,10 +4,9 @@ * SPDX-License-Identifier: GPL-3.0-only */ -package ua.acclorite.book_story.presentation.library.model +package ua.acclorite.book_story.presentation.browse.model import androidx.compose.runtime.Immutable -import ua.acclorite.book_story.data.model.common.NullableBook @Immutable data class SelectableNullableBook( diff --git a/app/src/main/java/ua/acclorite/book_story/ui/browse/BrowseAddDialog.kt b/app/src/main/java/ua/acclorite/book_story/ui/browse/BrowseAddDialog.kt index f7b1059a..771c781a 100644 --- a/app/src/main/java/ua/acclorite/book_story/ui/browse/BrowseAddDialog.kt +++ b/app/src/main/java/ua/acclorite/book_story/ui/browse/BrowseAddDialog.kt @@ -10,7 +10,7 @@ import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.size -import androidx.compose.foundation.lazy.items +import androidx.compose.foundation.lazy.itemsIndexed import androidx.compose.material.icons.Icons import androidx.compose.material.icons.filled.AddChart import androidx.compose.runtime.Composable @@ -20,13 +20,12 @@ import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.res.stringResource import androidx.compose.ui.unit.dp import ua.acclorite.book_story.R -import ua.acclorite.book_story.data.model.common.NullableBook import ua.acclorite.book_story.presentation.browse.BrowseEvent -import ua.acclorite.book_story.presentation.library.model.SelectableNullableBook +import ua.acclorite.book_story.presentation.browse.model.NullableBook +import ua.acclorite.book_story.presentation.browse.model.SelectableNullableBook import ua.acclorite.book_story.ui.common.components.dialog.Dialog import ua.acclorite.book_story.ui.common.components.progress_indicator.CircularProgressIndicator import ua.acclorite.book_story.ui.common.helpers.showToast -import java.util.UUID @Composable fun BrowseAddDialog( @@ -63,22 +62,25 @@ fun BrowseAddDialog( } } } else { - items( + itemsIndexed( selectedBooksAddDialog, - key = { it.data.fileName ?: UUID.randomUUID() } - ) { book -> + key = { index, _ -> index } + ) { _, book -> BrowseAddDialogItem( result = book ) { - if (it) { - selectAddDialog( - BrowseEvent.OnSelectAddDialog( - book = book + when (book.data) { + is NullableBook.NotNull -> { + selectAddDialog( + BrowseEvent.OnSelectAddDialog( + book = book + ) ) - ) - } else { - book.data.message?.asString(context) - ?.showToast(context = context) + } + + is NullableBook.Null -> { + book.data.message?.asString(context)?.showToast(context = context) + } } } } diff --git a/app/src/main/java/ua/acclorite/book_story/ui/browse/BrowseAddDialogItem.kt b/app/src/main/java/ua/acclorite/book_story/ui/browse/BrowseAddDialogItem.kt index d7920dc0..a297fce0 100644 --- a/app/src/main/java/ua/acclorite/book_story/ui/browse/BrowseAddDialogItem.kt +++ b/app/src/main/java/ua/acclorite/book_story/ui/browse/BrowseAddDialogItem.kt @@ -24,73 +24,77 @@ import androidx.compose.ui.Modifier import androidx.compose.ui.res.stringResource import androidx.compose.ui.unit.dp import ua.acclorite.book_story.R -import ua.acclorite.book_story.data.model.common.NullableBook -import ua.acclorite.book_story.presentation.library.model.SelectableNullableBook +import ua.acclorite.book_story.presentation.browse.model.NullableBook +import ua.acclorite.book_story.presentation.browse.model.SelectableNullableBook import ua.acclorite.book_story.ui.common.components.common.CircularCheckbox import ua.acclorite.book_story.ui.common.components.common.StyledText @Composable -fun BrowseAddDialogItem(result: SelectableNullableBook, onClick: (Boolean) -> Unit) { - if (result.data is NullableBook.NotNull) { - Row( - Modifier - .fillMaxWidth() - .clickable { - onClick(true) - } - .padding(vertical = 12.dp, horizontal = 24.dp), - verticalAlignment = Alignment.CenterVertically - ) { - Column( - Modifier.weight(1f) +fun BrowseAddDialogItem(result: SelectableNullableBook, onClick: () -> Unit) { + when (result.data) { + is NullableBook.NotNull -> { + Row( + Modifier + .fillMaxWidth() + .clickable { + onClick() + } + .padding(vertical = 12.dp, horizontal = 24.dp), + verticalAlignment = Alignment.CenterVertically ) { - StyledText( - text = result.data.bookWithCover!!.book.title, - style = MaterialTheme.typography.bodyLarge.copy( - color = MaterialTheme.colorScheme.onSurface - ), - maxLines = 1 - ) - StyledText( - text = result.data.bookWithCover.book.author.asString(), - style = MaterialTheme.typography.bodyMedium.copy( - color = MaterialTheme.colorScheme.onSurfaceVariant - ), - maxLines = 1 - ) - } - Row { - Spacer(modifier = Modifier.width(24.dp)) - CircularCheckbox( - selected = result.selected, - containerColor = MaterialTheme.colorScheme.surfaceContainerHigh - ) + Column( + Modifier.weight(1f) + ) { + StyledText( + text = result.data.book.title, + style = MaterialTheme.typography.bodyLarge.copy( + color = MaterialTheme.colorScheme.onSurface + ), + maxLines = 1 + ) + StyledText( + text = result.data.book.author.asString(), + style = MaterialTheme.typography.bodyMedium.copy( + color = MaterialTheme.colorScheme.onSurfaceVariant + ), + maxLines = 1 + ) + } + Row { + Spacer(modifier = Modifier.width(24.dp)) + CircularCheckbox( + selected = result.selected, + containerColor = MaterialTheme.colorScheme.surfaceContainerHigh + ) + } } } - } else { - Row( - Modifier - .fillMaxWidth() - .clickable { - onClick(false) - } - .padding(vertical = 12.dp, horizontal = 24.dp), - verticalAlignment = Alignment.CenterVertically - ) { - Icon( - imageVector = Icons.Default.Error, - contentDescription = stringResource(id = R.string.error_content_desc), - modifier = Modifier.size(26.dp), - tint = MaterialTheme.colorScheme.error - ) - Spacer(modifier = Modifier.width(8.dp)) - StyledText( - text = result.data.fileName!!, - style = MaterialTheme.typography.bodyLarge.copy( - color = MaterialTheme.colorScheme.error - ), - maxLines = 2 - ) + + is NullableBook.Null -> { + Row( + Modifier + .fillMaxWidth() + .clickable { + onClick() + } + .padding(vertical = 12.dp, horizontal = 24.dp), + verticalAlignment = Alignment.CenterVertically + ) { + Icon( + imageVector = Icons.Default.Error, + contentDescription = stringResource(id = R.string.error_content_desc), + modifier = Modifier.size(26.dp), + tint = MaterialTheme.colorScheme.error + ) + Spacer(modifier = Modifier.width(8.dp)) + StyledText( + text = result.data.fileName, + style = MaterialTheme.typography.bodyLarge.copy( + color = MaterialTheme.colorScheme.error + ), + maxLines = 2 + ) + } } } } \ No newline at end of file diff --git a/app/src/main/java/ua/acclorite/book_story/ui/browse/BrowseContent.kt b/app/src/main/java/ua/acclorite/book_story/ui/browse/BrowseContent.kt index 648fe2b3..4e9c7646 100644 --- a/app/src/main/java/ua/acclorite/book_story/ui/browse/BrowseContent.kt +++ b/app/src/main/java/ua/acclorite/book_story/ui/browse/BrowseContent.kt @@ -17,7 +17,7 @@ import ua.acclorite.book_story.core.Dialog import ua.acclorite.book_story.presentation.browse.BrowseEvent import ua.acclorite.book_story.presentation.browse.model.BrowseLayout import ua.acclorite.book_story.presentation.browse.model.SelectableFile -import ua.acclorite.book_story.presentation.library.model.SelectableNullableBook +import ua.acclorite.book_story.presentation.browse.model.SelectableNullableBook @OptIn(ExperimentalMaterialApi::class) @Composable diff --git a/app/src/main/java/ua/acclorite/book_story/ui/browse/BrowseDialog.kt b/app/src/main/java/ua/acclorite/book_story/ui/browse/BrowseDialog.kt index 77becb25..8652fc1f 100644 --- a/app/src/main/java/ua/acclorite/book_story/ui/browse/BrowseDialog.kt +++ b/app/src/main/java/ua/acclorite/book_story/ui/browse/BrowseDialog.kt @@ -10,7 +10,7 @@ import androidx.compose.runtime.Composable import ua.acclorite.book_story.core.Dialog import ua.acclorite.book_story.presentation.browse.BrowseEvent import ua.acclorite.book_story.presentation.browse.BrowseScreen -import ua.acclorite.book_story.presentation.library.model.SelectableNullableBook +import ua.acclorite.book_story.presentation.browse.model.SelectableNullableBook @Composable fun BrowseDialog(