refactor: color preset entity
* I/O for repository calls * Removed nullability from ColorPresetEntity
This commit is contained in:
parent
b0c567bdc9
commit
a45078cae5
18 changed files with 468 additions and 151 deletions
|
|
@ -0,0 +1,220 @@
|
|||
{
|
||||
"formatVersion": 1,
|
||||
"database": {
|
||||
"version": 14,
|
||||
"identityHash": "cd99cc3f6288826f0ad96bc8fe7aca3a",
|
||||
"entities": [
|
||||
{
|
||||
"tableName": "BookEntity",
|
||||
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `title` TEXT NOT NULL, `author` TEXT, `description` TEXT, `filePath` TEXT NOT NULL, `scrollIndex` INTEGER NOT NULL, `scrollOffset` INTEGER NOT NULL, `progress` REAL NOT NULL, `image` TEXT, `categories` TEXT NOT NULL DEFAULT '[]')",
|
||||
"fields": [
|
||||
{
|
||||
"fieldPath": "id",
|
||||
"columnName": "id",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "title",
|
||||
"columnName": "title",
|
||||
"affinity": "TEXT",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "author",
|
||||
"columnName": "author",
|
||||
"affinity": "TEXT"
|
||||
},
|
||||
{
|
||||
"fieldPath": "description",
|
||||
"columnName": "description",
|
||||
"affinity": "TEXT"
|
||||
},
|
||||
{
|
||||
"fieldPath": "filePath",
|
||||
"columnName": "filePath",
|
||||
"affinity": "TEXT",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "scrollIndex",
|
||||
"columnName": "scrollIndex",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "scrollOffset",
|
||||
"columnName": "scrollOffset",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "progress",
|
||||
"columnName": "progress",
|
||||
"affinity": "REAL",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "image",
|
||||
"columnName": "image",
|
||||
"affinity": "TEXT"
|
||||
},
|
||||
{
|
||||
"fieldPath": "categories",
|
||||
"columnName": "categories",
|
||||
"affinity": "TEXT",
|
||||
"notNull": true,
|
||||
"defaultValue": "'[]'"
|
||||
}
|
||||
],
|
||||
"primaryKey": {
|
||||
"autoGenerate": true,
|
||||
"columnNames": [
|
||||
"id"
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"tableName": "HistoryEntity",
|
||||
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `bookId` INTEGER NOT NULL, `time` INTEGER NOT NULL)",
|
||||
"fields": [
|
||||
{
|
||||
"fieldPath": "id",
|
||||
"columnName": "id",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "bookId",
|
||||
"columnName": "bookId",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "time",
|
||||
"columnName": "time",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true
|
||||
}
|
||||
],
|
||||
"primaryKey": {
|
||||
"autoGenerate": true,
|
||||
"columnNames": [
|
||||
"id"
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"tableName": "ColorPresetEntity",
|
||||
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT, `name` TEXT NOT NULL, `backgroundColor` INTEGER NOT NULL, `fontColor` INTEGER NOT NULL, `isSelected` INTEGER NOT NULL, `order` INTEGER NOT NULL)",
|
||||
"fields": [
|
||||
{
|
||||
"fieldPath": "id",
|
||||
"columnName": "id",
|
||||
"affinity": "INTEGER"
|
||||
},
|
||||
{
|
||||
"fieldPath": "name",
|
||||
"columnName": "name",
|
||||
"affinity": "TEXT",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "backgroundColor",
|
||||
"columnName": "backgroundColor",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "fontColor",
|
||||
"columnName": "fontColor",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "isSelected",
|
||||
"columnName": "isSelected",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "order",
|
||||
"columnName": "order",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true
|
||||
}
|
||||
],
|
||||
"primaryKey": {
|
||||
"autoGenerate": true,
|
||||
"columnNames": [
|
||||
"id"
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"tableName": "CategoryEntity",
|
||||
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `title` TEXT NOT NULL, `order` INTEGER NOT NULL)",
|
||||
"fields": [
|
||||
{
|
||||
"fieldPath": "id",
|
||||
"columnName": "id",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "title",
|
||||
"columnName": "title",
|
||||
"affinity": "TEXT",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "order",
|
||||
"columnName": "order",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true
|
||||
}
|
||||
],
|
||||
"primaryKey": {
|
||||
"autoGenerate": true,
|
||||
"columnNames": [
|
||||
"id"
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"tableName": "CategorySortEntity",
|
||||
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`categoryId` INTEGER NOT NULL, `sortOrder` TEXT NOT NULL, `sortOrderDescending` INTEGER NOT NULL, PRIMARY KEY(`categoryId`))",
|
||||
"fields": [
|
||||
{
|
||||
"fieldPath": "categoryId",
|
||||
"columnName": "categoryId",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "sortOrder",
|
||||
"columnName": "sortOrder",
|
||||
"affinity": "TEXT",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "sortOrderDescending",
|
||||
"columnName": "sortOrderDescending",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true
|
||||
}
|
||||
],
|
||||
"primaryKey": {
|
||||
"autoGenerate": false,
|
||||
"columnNames": [
|
||||
"categoryId"
|
||||
]
|
||||
}
|
||||
}
|
||||
],
|
||||
"setupQueries": [
|
||||
"CREATE TABLE IF NOT EXISTS room_master_table (id INTEGER PRIMARY KEY,identity_hash TEXT)",
|
||||
"INSERT OR REPLACE INTO room_master_table (id,identity_hash) VALUES(42, 'cd99cc3f6288826f0ad96bc8fe7aca3a')"
|
||||
]
|
||||
}
|
||||
}
|
||||
|
|
@ -9,7 +9,6 @@ package ua.acclorite.book_story.core
|
|||
import android.graphics.Bitmap
|
||||
|
||||
typealias CoverImage = Bitmap
|
||||
typealias Selected = Boolean
|
||||
typealias BottomSheet = String
|
||||
typealias Dialog = String
|
||||
typealias Drawer = String
|
||||
|
|
@ -19,7 +19,6 @@ import org.commonmark.node.HtmlBlock
|
|||
import org.commonmark.node.IndentedCodeBlock
|
||||
import org.commonmark.node.ThematicBreak
|
||||
import org.commonmark.parser.Parser
|
||||
import ua.acclorite.book_story.data.local.room.BookDao
|
||||
import ua.acclorite.book_story.data.local.room.BookDatabase
|
||||
import ua.acclorite.book_story.data.local.room.DatabaseHelper
|
||||
import javax.inject.Singleton
|
||||
|
|
@ -49,7 +48,7 @@ object AppModule {
|
|||
|
||||
@Provides
|
||||
@Singleton
|
||||
fun provideBookDao(app: Application): BookDao {
|
||||
fun provideBookDatabase(app: Application): BookDatabase {
|
||||
// Additional Migrations
|
||||
DatabaseHelper.MIGRATION_7_8.removeBooksDir(app)
|
||||
|
||||
|
|
@ -57,14 +56,11 @@ object AppModule {
|
|||
app,
|
||||
BookDatabase::class.java,
|
||||
"book_db"
|
||||
)
|
||||
.addMigrations(
|
||||
).addMigrations(
|
||||
DatabaseHelper.MIGRATION_2_3, // creates LanguageHistoryEntity table(if does not exist)
|
||||
DatabaseHelper.MIGRATION_4_5, // creates ColorPresetEntity table(if does not exist)
|
||||
DatabaseHelper.MIGRATION_5_6, // creates FavoriteDirectoryEntity table(if does not exist)
|
||||
)
|
||||
.allowMainThreadQueries()
|
||||
.build()
|
||||
.dao
|
||||
DatabaseHelper.MIGRATION_13_14, // remove nullability from ColorPresetEntity
|
||||
).allowMainThreadQueries().build()
|
||||
}
|
||||
}
|
||||
|
|
@ -13,7 +13,7 @@ import androidx.room.PrimaryKey
|
|||
data class ColorPresetEntity(
|
||||
@PrimaryKey(true)
|
||||
val id: Int? = null,
|
||||
val name: String?,
|
||||
val name: String,
|
||||
val backgroundColor: Long,
|
||||
val fontColor: Long,
|
||||
val isSelected: Boolean,
|
||||
|
|
|
|||
|
|
@ -16,12 +16,8 @@ import androidx.room.Upsert
|
|||
import ua.acclorite.book_story.data.local.dto.BookEntity
|
||||
import ua.acclorite.book_story.data.local.dto.CategoryEntity
|
||||
import ua.acclorite.book_story.data.local.dto.CategorySortEntity
|
||||
import ua.acclorite.book_story.data.local.dto.ColorPresetEntity
|
||||
import ua.acclorite.book_story.data.local.dto.HistoryEntity
|
||||
|
||||
/**
|
||||
* Class to manipulate Room database.
|
||||
*/
|
||||
@Dao
|
||||
interface BookDao {
|
||||
|
||||
|
|
@ -73,27 +69,6 @@ interface BookDao {
|
|||
/* - - - - - - - - - - - - - - - - - - - - - - */
|
||||
|
||||
|
||||
/* ------ ColorPresetEntity ----------------- */
|
||||
@Upsert
|
||||
suspend fun updateColorPreset(colorPreset: ColorPresetEntity)
|
||||
|
||||
@Query("SELECT `order` FROM colorpresetentity WHERE :id=id")
|
||||
suspend fun getColorPresetOrder(id: Int): Int
|
||||
|
||||
@Query("SELECT COUNT(*) FROM colorpresetentity")
|
||||
suspend fun getColorPresetsSize(): Int
|
||||
|
||||
@Query("SELECT * FROM colorpresetentity")
|
||||
suspend fun getColorPresets(): List<ColorPresetEntity>
|
||||
|
||||
@Delete
|
||||
suspend fun deleteColorPreset(colorPreset: ColorPresetEntity)
|
||||
|
||||
@Query("DELETE FROM colorpresetentity")
|
||||
suspend fun deleteColorPresets()
|
||||
/* - - - - - - - - - - - - - - - - - - - - - - */
|
||||
|
||||
|
||||
/* ------ CategoryEntity ----------------- */
|
||||
@Insert(onConflict = OnConflictStrategy.REPLACE)
|
||||
suspend fun insertCategory(
|
||||
|
|
|
|||
|
|
@ -30,7 +30,7 @@ import java.io.File
|
|||
CategoryEntity::class,
|
||||
CategorySortEntity::class
|
||||
],
|
||||
version = 13,
|
||||
version = 14,
|
||||
autoMigrations = [
|
||||
AutoMigration(1, 2),
|
||||
AutoMigration(2, 3),
|
||||
|
|
@ -44,11 +44,13 @@ import java.io.File
|
|||
AutoMigration(10, 11),
|
||||
AutoMigration(11, 12),
|
||||
AutoMigration(12, 13),
|
||||
AutoMigration(13, 14),
|
||||
],
|
||||
exportSchema = true
|
||||
)
|
||||
abstract class BookDatabase : RoomDatabase() {
|
||||
abstract val dao: BookDao
|
||||
abstract val bookDao: BookDao
|
||||
abstract val colorPresetDao: ColorPresetDao
|
||||
}
|
||||
|
||||
@Suppress("ClassName")
|
||||
|
|
@ -124,4 +126,36 @@ object DatabaseHelper {
|
|||
|
||||
@DeleteColumn("BookEntity", "category")
|
||||
class MIGRATION_9_10 : AutoMigrationSpec
|
||||
|
||||
val MIGRATION_13_14 = object : Migration(13, 14) {
|
||||
override fun migrate(database: SupportSQLiteDatabase) {
|
||||
database.execSQL(
|
||||
"""
|
||||
CREATE TABLE IF NOT EXISTS ColorPresetEntity (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
name TEXT NOT NULL,
|
||||
backgroundColor INTEGER NOT NULL,
|
||||
fontColor INTEGER NOT NULL,
|
||||
isSelected INTEGER NOT NULL,
|
||||
`order` INTEGER NOT NULL
|
||||
)
|
||||
"""
|
||||
)
|
||||
database.execSQL(
|
||||
"""
|
||||
INSERT INTO ColorPresetEntity_new (id, name, backgroundColor, fontColor, isSelected, `order`)
|
||||
SELECT
|
||||
id,
|
||||
COALESCE(name, ''),
|
||||
backgroundColor,
|
||||
fontColor,
|
||||
isSelected,
|
||||
`order`
|
||||
FROM ColorPresetEntity
|
||||
"""
|
||||
)
|
||||
database.execSQL("DROP TABLE ColorPresetEntity")
|
||||
database.execSQL("ALTER TABLE ColorPresetEntity_new RENAME TO ColorPresetEntity")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,34 @@
|
|||
/*
|
||||
* 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.local.room
|
||||
|
||||
import androidx.room.Dao
|
||||
import androidx.room.Delete
|
||||
import androidx.room.Query
|
||||
import androidx.room.Upsert
|
||||
import ua.acclorite.book_story.data.local.dto.ColorPresetEntity
|
||||
|
||||
@Dao
|
||||
interface ColorPresetDao {
|
||||
@Upsert
|
||||
suspend fun updateColorPreset(colorPreset: ColorPresetEntity)
|
||||
|
||||
@Query("SELECT `order` FROM colorpresetentity WHERE id = :id")
|
||||
suspend fun getColorPresetOrder(id: Int): Int
|
||||
|
||||
@Query("SELECT COUNT(*) FROM colorpresetentity")
|
||||
suspend fun getColorPresetsSize(): Int
|
||||
|
||||
@Query("SELECT * FROM colorpresetentity ORDER BY `order` ASC")
|
||||
suspend fun getColorPresets(): List<ColorPresetEntity>
|
||||
|
||||
@Delete
|
||||
suspend fun deleteColorPreset(colorPreset: ColorPresetEntity)
|
||||
|
||||
@Query("DELETE FROM colorpresetentity")
|
||||
suspend fun deleteColorPresets()
|
||||
}
|
||||
|
|
@ -6,8 +6,10 @@
|
|||
|
||||
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.data.local.room.BookDao
|
||||
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
|
||||
|
|
@ -22,7 +24,7 @@ import javax.inject.Singleton
|
|||
|
||||
@Singleton
|
||||
class BookRepositoryImpl @Inject constructor(
|
||||
private val database: BookDao,
|
||||
private val database: BookDatabase,
|
||||
private val bookMapper: BookMapper,
|
||||
private val fileMapper: FileMapper,
|
||||
private val fileParser: FileParser,
|
||||
|
|
@ -31,46 +33,62 @@ class BookRepositoryImpl @Inject constructor(
|
|||
) : BookRepository {
|
||||
|
||||
override suspend fun searchBooks(query: String): Result<List<Book>> = runCatching {
|
||||
database.searchBooks(query).map { bookMapper.toBook(it) }
|
||||
withContext(Dispatchers.IO) {
|
||||
database.bookDao.searchBooks(query).map { bookMapper.toBook(it) }
|
||||
}
|
||||
}
|
||||
|
||||
override suspend fun getBook(bookId: Int): Result<Book> = runCatching {
|
||||
database.findBookById(bookId).let {
|
||||
withContext(Dispatchers.IO) {
|
||||
database.bookDao.findBookById(bookId).let {
|
||||
if (it == null) throw NoSuchElementException("Couldn't get book [$bookId].")
|
||||
else bookMapper.toBook(it)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override suspend fun getText(bookId: Int): Result<List<ReaderText>> {
|
||||
return getBook(bookId)
|
||||
return withContext(Dispatchers.IO) {
|
||||
getBook(bookId)
|
||||
.mapCatching { fileProvider.getFileFromBook(it).getOrThrow() }
|
||||
.mapCatching { textParser.parse(it) }
|
||||
}
|
||||
}
|
||||
|
||||
override suspend fun getFileFromBook(bookId: Int): Result<File> {
|
||||
return getBook(bookId)
|
||||
return withContext(Dispatchers.IO) {
|
||||
getBook(bookId)
|
||||
.mapCatching { fileProvider.getFileFromBook(it).getOrThrow() }
|
||||
.mapCatching { fileMapper.toFile(it) }
|
||||
}
|
||||
}
|
||||
|
||||
override suspend fun addBook(book: Book): Result<Unit> = runCatching {
|
||||
database.insertBook(bookMapper.toBookEntity(book))
|
||||
withContext(Dispatchers.IO) {
|
||||
database.bookDao.insertBook(bookMapper.toBookEntity(book))
|
||||
}
|
||||
}
|
||||
|
||||
override suspend fun updateBook(book: Book): Result<Unit> = runCatching {
|
||||
database.updateBook(bookMapper.toBookEntity(book)).also {
|
||||
withContext(Dispatchers.IO) {
|
||||
database.bookDao.updateBook(bookMapper.toBookEntity(book)).also {
|
||||
if (it == 0) throw Exception("Could not update book in database.")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override suspend fun deleteBook(book: Book): Result<Unit> = runCatching {
|
||||
database.deleteBook(bookMapper.toBookEntity(book)).also {
|
||||
withContext(Dispatchers.IO) {
|
||||
database.bookDao.deleteBook(bookMapper.toBookEntity(book)).also {
|
||||
if (it == 0) throw Exception("Could not delete book in database.")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override suspend fun getDefaultCover(book: Book): Result<CoverImage?> = runCatching {
|
||||
return fileProvider.getFileFromBook(book)
|
||||
return withContext(Dispatchers.IO) {
|
||||
fileProvider.getFileFromBook(book)
|
||||
.mapCatching { fileParser.parse(it)?.coverImage }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -6,7 +6,9 @@
|
|||
|
||||
package ua.acclorite.book_story.data.repository
|
||||
|
||||
import ua.acclorite.book_story.data.local.room.BookDao
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.withContext
|
||||
import ua.acclorite.book_story.data.local.room.BookDatabase
|
||||
import ua.acclorite.book_story.data.mapper.category.CategoryMapper
|
||||
import ua.acclorite.book_story.data.mapper.category_sort.CategorySortMapper
|
||||
import ua.acclorite.book_story.domain.model.library.Category
|
||||
|
|
@ -17,57 +19,72 @@ import javax.inject.Singleton
|
|||
|
||||
@Singleton
|
||||
class CategoryRepositoryImpl @Inject constructor(
|
||||
private val database: BookDao,
|
||||
private val database: BookDatabase,
|
||||
private val categoryMapper: CategoryMapper,
|
||||
private val categorySortMapper: CategorySortMapper,
|
||||
) : CategoryRepository {
|
||||
|
||||
override suspend fun addCategory(category: Category): Result<Unit> = runCatching {
|
||||
database.insertCategory(
|
||||
withContext(Dispatchers.IO) {
|
||||
database.bookDao.insertCategory(
|
||||
categoryMapper.toCategoryEntity(
|
||||
category.copy(
|
||||
order = database.getCategoriesCount()
|
||||
order = database.bookDao.getCategoriesCount()
|
||||
)
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
override suspend fun getCategories(): Result<List<Category>> = runCatching {
|
||||
database.getCategories().map { categoryMapper.toCategory(it) }.sortedBy { it.order }
|
||||
withContext(Dispatchers.IO) {
|
||||
database.bookDao.getCategories().map { categoryMapper.toCategory(it) }
|
||||
.sortedBy { it.order }
|
||||
}
|
||||
}
|
||||
|
||||
override suspend fun updateCategory(category: Category): Result<Unit> = runCatching {
|
||||
database.updateCategoryTitle(
|
||||
withContext(Dispatchers.IO) {
|
||||
database.bookDao.updateCategoryTitle(
|
||||
id = category.id,
|
||||
title = category.title
|
||||
)
|
||||
updateOrder(getCategories().getOrThrow())
|
||||
}
|
||||
}
|
||||
|
||||
override suspend fun updateOrder(categories: List<Category>): Result<Unit> = runCatching {
|
||||
withContext(Dispatchers.IO) {
|
||||
categories.forEachIndexed { index, category ->
|
||||
database.updateCategoryOrder(category.id, index)
|
||||
database.bookDao.updateCategoryOrder(category.id, index)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override suspend fun deleteCategory(category: Category): Result<Unit> = runCatching {
|
||||
database.deleteCategory(categoryMapper.toCategoryEntity(category))
|
||||
database.deleteCategorySortEntity(category.id)
|
||||
withContext(Dispatchers.IO) {
|
||||
database.bookDao.deleteCategory(categoryMapper.toCategoryEntity(category))
|
||||
database.bookDao.deleteCategorySortEntity(category.id)
|
||||
|
||||
updateOrder(getCategories().getOrThrow())
|
||||
}
|
||||
}
|
||||
|
||||
override suspend fun updateCategorySorting(
|
||||
categorySort: CategorySort
|
||||
): Result<Unit> = runCatching {
|
||||
database.updateCategorySort(
|
||||
withContext(Dispatchers.IO) {
|
||||
database.bookDao.updateCategorySort(
|
||||
categorySortMapper.toCategorySortEntity(categorySort)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
override suspend fun getCategorySorting(): Result<List<CategorySort>> = runCatching {
|
||||
database.getCategorySortEntities().map {
|
||||
withContext(Dispatchers.IO) {
|
||||
database.bookDao.getCategorySortEntities().map {
|
||||
categorySortMapper.toCategorySort(it)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -6,7 +6,9 @@
|
|||
|
||||
package ua.acclorite.book_story.data.repository
|
||||
|
||||
import ua.acclorite.book_story.data.local.room.BookDao
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.withContext
|
||||
import ua.acclorite.book_story.data.local.room.BookDatabase
|
||||
import ua.acclorite.book_story.data.mapper.color_preset.ColorPresetMapper
|
||||
import ua.acclorite.book_story.domain.model.reader.ColorPreset
|
||||
import ua.acclorite.book_story.domain.repository.ColorPresetRepository
|
||||
|
|
@ -15,56 +17,67 @@ import javax.inject.Singleton
|
|||
|
||||
@Singleton
|
||||
class ColorPresetRepositoryImpl @Inject constructor(
|
||||
private val database: BookDao,
|
||||
private val database: BookDatabase,
|
||||
private val colorPresetMapper: ColorPresetMapper
|
||||
) : ColorPresetRepository {
|
||||
|
||||
override suspend fun getColorPresets(): Result<List<ColorPreset>> = runCatching {
|
||||
database.getColorPresets()
|
||||
.sortedBy { it.order }
|
||||
.map { colorPresetMapper.toColorPreset(it) }
|
||||
withContext(Dispatchers.IO) {
|
||||
database.colorPresetDao.getColorPresets().map {
|
||||
colorPresetMapper.toColorPreset(it)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override suspend fun updateColorPreset(
|
||||
colorPreset: ColorPreset
|
||||
): Result<Unit> = runCatching {
|
||||
database.updateColorPreset(
|
||||
withContext(Dispatchers.IO) {
|
||||
database.colorPresetDao.updateColorPreset(
|
||||
colorPresetMapper.toColorPresetEntity(
|
||||
colorPreset = colorPreset,
|
||||
order = if (colorPreset.id != -1) database.getColorPresetOrder(colorPreset.id)
|
||||
else database.getColorPresetsSize()
|
||||
order = if (colorPreset.id != -1) {
|
||||
database.colorPresetDao.getColorPresetOrder(colorPreset.id)
|
||||
} else database.colorPresetDao.getColorPresetsSize()
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
override suspend fun selectColorPreset(
|
||||
colorPreset: ColorPreset
|
||||
): Result<Unit> = runCatching {
|
||||
database.getColorPresets().map {
|
||||
withContext(Dispatchers.IO) {
|
||||
database.colorPresetDao.getColorPresets().map {
|
||||
it.copy(isSelected = it.id == colorPreset.id)
|
||||
}.forEach {
|
||||
database.updateColorPreset(it)
|
||||
database.colorPresetDao.updateColorPreset(it)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override suspend fun reorderColorPresets(
|
||||
colorPresets: List<ColorPreset>
|
||||
): Result<Unit> = runCatching {
|
||||
database.deleteColorPresets()
|
||||
withContext(Dispatchers.IO) {
|
||||
database.colorPresetDao.deleteColorPresets()
|
||||
colorPresets.forEachIndexed { index, colorPreset ->
|
||||
database.updateColorPreset(
|
||||
database.colorPresetDao.updateColorPreset(
|
||||
colorPresetMapper.toColorPresetEntity(colorPreset, order = index)
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override suspend fun deleteColorPreset(
|
||||
colorPreset: ColorPreset
|
||||
): Result<Unit> = runCatching {
|
||||
database.deleteColorPreset(
|
||||
withContext(Dispatchers.IO) {
|
||||
database.colorPresetDao.deleteColorPreset(
|
||||
colorPresetMapper.toColorPresetEntity(
|
||||
colorPreset, -1
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -6,8 +6,10 @@
|
|||
|
||||
package ua.acclorite.book_story.data.repository
|
||||
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.withContext
|
||||
import ua.acclorite.book_story.core.data.ExtensionsData
|
||||
import ua.acclorite.book_story.data.local.room.BookDao
|
||||
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
|
||||
|
|
@ -18,21 +20,18 @@ import ua.acclorite.book_story.domain.service.FileProvider
|
|||
import javax.inject.Inject
|
||||
import javax.inject.Singleton
|
||||
|
||||
/**
|
||||
* File System repository.
|
||||
* Manages all File System related work.
|
||||
*/
|
||||
@Singleton
|
||||
class FileSystemRepositoryImpl @Inject constructor(
|
||||
private val database: BookDao,
|
||||
private val database: BookDatabase,
|
||||
private val fileMapper: FileMapper,
|
||||
private val fileParser: FileParser,
|
||||
private val fileProvider: FileProvider
|
||||
) : FileSystemRepository {
|
||||
|
||||
override suspend fun searchFiles(query: String): Result<List<File>> {
|
||||
return fileProvider.getStorageFiles().mapCatching { storages ->
|
||||
val existingFiles = database.searchBooks("").map { it.filePath }
|
||||
return withContext(Dispatchers.IO) {
|
||||
fileProvider.getStorageFiles().mapCatching { storages ->
|
||||
val existingFiles = database.bookDao.searchBooks("").map { it.filePath }
|
||||
|
||||
storages.map { storage ->
|
||||
storage.getFilesFromStorage(
|
||||
|
|
@ -42,6 +41,7 @@ class FileSystemRepositoryImpl @Inject constructor(
|
|||
}.flatten()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun CachedFile.isValid(
|
||||
query: String,
|
||||
|
|
@ -74,7 +74,9 @@ class FileSystemRepositoryImpl @Inject constructor(
|
|||
}
|
||||
|
||||
override suspend fun getBookFromFile(file: File): Result<BookWithCover> = runCatching {
|
||||
withContext(Dispatchers.IO) {
|
||||
fileParser.parse(fileMapper.toCachedFile(file))
|
||||
?: throw Exception("Could not parse ${file.name}.")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -6,7 +6,9 @@
|
|||
|
||||
package ua.acclorite.book_story.data.repository
|
||||
|
||||
import ua.acclorite.book_story.data.local.room.BookDao
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.withContext
|
||||
import ua.acclorite.book_story.data.local.room.BookDatabase
|
||||
import ua.acclorite.book_story.data.mapper.history.HistoryMapper
|
||||
import ua.acclorite.book_story.domain.model.history.History
|
||||
import ua.acclorite.book_story.domain.repository.HistoryRepository
|
||||
|
|
@ -15,40 +17,52 @@ import javax.inject.Singleton
|
|||
|
||||
@Singleton
|
||||
class HistoryRepositoryImpl @Inject constructor(
|
||||
private val database: BookDao,
|
||||
private val database: BookDatabase,
|
||||
private val historyMapper: HistoryMapper
|
||||
) : HistoryRepository {
|
||||
|
||||
override suspend fun getHistoryForBook(bookId: Int): Result<History> = runCatching {
|
||||
database.getHistoryForBook(bookId).let {
|
||||
withContext(Dispatchers.IO) {
|
||||
database.bookDao.getHistoryForBook(bookId).let {
|
||||
if (it == null) throw NoSuchElementException("Could not get history from [$bookId].")
|
||||
else historyMapper.toHistory(it)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override suspend fun addHistory(history: History): Result<Unit> = runCatching {
|
||||
database.insertHistory(historyMapper.toHistoryEntity(history))
|
||||
withContext(Dispatchers.IO) {
|
||||
database.bookDao.insertHistory(historyMapper.toHistoryEntity(history))
|
||||
}
|
||||
}
|
||||
|
||||
override suspend fun getHistory(): Result<List<History>> = runCatching {
|
||||
database.getHistory().map { historyMapper.toHistory(it) }
|
||||
withContext(Dispatchers.IO) {
|
||||
database.bookDao.getHistory().map { historyMapper.toHistory(it) }
|
||||
}
|
||||
}
|
||||
|
||||
override suspend fun deleteWholeHistory(): Result<Unit> = runCatching {
|
||||
database.deleteWholeHistory().also {
|
||||
withContext(Dispatchers.IO) {
|
||||
database.bookDao.deleteWholeHistory().also {
|
||||
if (it == 0) throw Exception("Could not delete whole history in database.")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override suspend fun deleteHistoryForBook(bookId: Int): Result<Unit> = runCatching {
|
||||
database.deleteHistoryForBook(bookId = bookId).also {
|
||||
withContext(Dispatchers.IO) {
|
||||
database.bookDao.deleteHistoryForBook(bookId = bookId).also {
|
||||
if (it == 0) throw Exception("Could not delete history for book [$bookId] in database.")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override suspend fun deleteHistory(history: History): Result<Unit> = runCatching {
|
||||
database.deleteHistory(historyMapper.toHistoryEntity(history)).also {
|
||||
withContext(Dispatchers.IO) {
|
||||
database.bookDao.deleteHistory(historyMapper.toHistoryEntity(history)).also {
|
||||
if (it == 0) throw Exception("Could not delete history in database.")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -8,20 +8,19 @@ package ua.acclorite.book_story.domain.model.reader
|
|||
|
||||
import androidx.compose.runtime.Immutable
|
||||
import androidx.compose.ui.graphics.Color
|
||||
import ua.acclorite.book_story.core.Selected
|
||||
|
||||
@Immutable
|
||||
data class ColorPreset(
|
||||
val id: Int,
|
||||
val name: String?,
|
||||
val name: String,
|
||||
val backgroundColor: Color,
|
||||
val fontColor: Color,
|
||||
val isSelected: Selected
|
||||
val isSelected: Boolean
|
||||
) {
|
||||
companion object {
|
||||
val default = ColorPreset(
|
||||
id = -1,
|
||||
name = null,
|
||||
name = "",
|
||||
backgroundColor = Color(0xFFFAF8FF), // Blue Light Surface (hardcoded)
|
||||
fontColor = Color(0xFF44464F), // Blue Light OnSurfaceVariant (hardcoded)
|
||||
isSelected = false
|
||||
|
|
|
|||
|
|
@ -7,11 +7,10 @@
|
|||
package ua.acclorite.book_story.presentation.library.model
|
||||
|
||||
import androidx.compose.runtime.Immutable
|
||||
import ua.acclorite.book_story.core.Selected
|
||||
import ua.acclorite.book_story.domain.model.library.Book
|
||||
|
||||
@Immutable
|
||||
data class SelectableBook(
|
||||
val data: Book,
|
||||
val selected: Selected
|
||||
val selected: Boolean
|
||||
)
|
||||
|
|
@ -7,11 +7,10 @@
|
|||
package ua.acclorite.book_story.presentation.library.model
|
||||
|
||||
import androidx.compose.runtime.Immutable
|
||||
import ua.acclorite.book_story.core.Selected
|
||||
import ua.acclorite.book_story.data.model.common.NullableBook
|
||||
|
||||
@Immutable
|
||||
data class SelectableNullableBook(
|
||||
val data: NullableBook,
|
||||
val selected: Selected
|
||||
val selected: Boolean
|
||||
)
|
||||
|
|
@ -23,7 +23,6 @@ import androidx.compose.ui.Modifier
|
|||
import androidx.compose.ui.draw.clip
|
||||
import androidx.compose.ui.graphics.Color
|
||||
import androidx.compose.ui.unit.dp
|
||||
import ua.acclorite.book_story.core.Selected
|
||||
|
||||
/**
|
||||
* Modal Drawer Selectable Item.
|
||||
|
|
@ -38,7 +37,7 @@ import ua.acclorite.book_story.core.Selected
|
|||
@Composable
|
||||
fun ModalDrawerSelectableItem(
|
||||
modifier: Modifier = Modifier,
|
||||
selected: Selected,
|
||||
selected: Boolean,
|
||||
enabled: Boolean = true,
|
||||
onClick: () -> Unit,
|
||||
content: @Composable RowScope.() -> Unit
|
||||
|
|
|
|||
|
|
@ -28,7 +28,7 @@ fun SettingsEffects(effects: SharedFlow<SettingsEffect>) {
|
|||
is SettingsEffect.OnSwitchedColorPreset -> {
|
||||
context.getString(
|
||||
R.string.color_preset_selected_query,
|
||||
if (effect.newColorPreset.name.isNullOrBlank()) {
|
||||
if (effect.newColorPreset.name.isBlank()) {
|
||||
context.getString(
|
||||
R.string.color_preset_query,
|
||||
effect.newColorPreset.id.toString()
|
||||
|
|
|
|||
|
|
@ -63,7 +63,6 @@ import sh.calvin.reorderable.ReorderableCollectionItemScope
|
|||
import sh.calvin.reorderable.ReorderableItem
|
||||
import sh.calvin.reorderable.rememberReorderableLazyListState
|
||||
import ua.acclorite.book_story.R
|
||||
import ua.acclorite.book_story.core.Selected
|
||||
import ua.acclorite.book_story.domain.model.reader.ColorPreset
|
||||
import ua.acclorite.book_story.presentation.settings.SettingsEvent
|
||||
import ua.acclorite.book_story.presentation.settings.SettingsModel
|
||||
|
|
@ -233,7 +232,7 @@ fun ColorPresetOption(backgroundColor: Color) {
|
|||
@Composable
|
||||
private fun ReorderableCollectionItemScope.ColorPresetOptionRowItem(
|
||||
colorPreset: ColorPreset,
|
||||
isSelected: Selected,
|
||||
isSelected: Boolean,
|
||||
canDrag: Boolean,
|
||||
enableAnimation: Boolean,
|
||||
onDragStopped: () -> Unit,
|
||||
|
|
@ -241,14 +240,14 @@ private fun ReorderableCollectionItemScope.ColorPresetOptionRowItem(
|
|||
) {
|
||||
val context = LocalContext.current
|
||||
val title = remember(colorPreset) {
|
||||
if ((colorPreset.name ?: "").isBlank()) {
|
||||
if (colorPreset.name.isBlank()) {
|
||||
return@remember context.getString(
|
||||
R.string.color_preset_query,
|
||||
colorPreset.id.toString()
|
||||
)
|
||||
}
|
||||
|
||||
colorPreset.name!!
|
||||
colorPreset.name
|
||||
}
|
||||
|
||||
val borderColor = remember(isSelected, colorPreset.fontColor) {
|
||||
|
|
@ -344,7 +343,7 @@ private fun ColorPresetOptionConfigurationItem(
|
|||
onAdd: () -> Unit
|
||||
) {
|
||||
val title = remember(selectedColorPreset.id) {
|
||||
mutableStateOf(selectedColorPreset.name ?: "")
|
||||
mutableStateOf(selectedColorPreset.name)
|
||||
}
|
||||
|
||||
LaunchedEffect(title) {
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue