diff --git a/app/schemas/ua.acclorite.book_story.data.local.room.BookDatabase/16.json b/app/schemas/ua.acclorite.book_story.data.local.room.BookDatabase/16.json new file mode 100644 index 00000000..81c0bfb5 --- /dev/null +++ b/app/schemas/ua.acclorite.book_story.data.local.room.BookDatabase/16.json @@ -0,0 +1,205 @@ +{ + "formatVersion": 1, + "database": { + "version": 16, + "identityHash": "6e3e3d0d7b68361faad48f4fe9b8a0b2", + "entities": [ + { + "tableName": "BookEntity", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `title` TEXT NOT NULL, `author` TEXT NOT NULL, `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", + "notNull": true + }, + { + "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, `sortOrder` TEXT NOT NULL DEFAULT 'LAST_READ', `sortOrderDescending` INTEGER NOT NULL DEFAULT 1)", + "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 + }, + { + "fieldPath": "sortOrder", + "columnName": "sortOrder", + "affinity": "TEXT", + "notNull": true, + "defaultValue": "'LAST_READ'" + }, + { + "fieldPath": "sortOrderDescending", + "columnName": "sortOrderDescending", + "affinity": "INTEGER", + "notNull": true, + "defaultValue": "1" + } + ], + "primaryKey": { + "autoGenerate": true, + "columnNames": [ + "id" + ] + } + } + ], + "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, '6e3e3d0d7b68361faad48f4fe9b8a0b2')" + ] + } +} \ No newline at end of file diff --git a/app/src/main/java/ua/acclorite/book_story/data/di/AppModule.kt b/app/src/main/java/ua/acclorite/book_story/data/di/AppModule.kt index babcaeef..1a379a84 100644 --- a/app/src/main/java/ua/acclorite/book_story/data/di/AppModule.kt +++ b/app/src/main/java/ua/acclorite/book_story/data/di/AppModule.kt @@ -50,18 +50,19 @@ object AppModule { @Singleton fun provideBookDatabase(app: Application): BookDatabase { // Additional Migrations - DatabaseHelper.MIGRATION_7_8.removeBooksDir(app) + DatabaseHelper.AUTO_MIGRATION_7_8.removeBooksDir(app) return Room.databaseBuilder( app, BookDatabase::class.java, "book_db" ).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) - DatabaseHelper.MIGRATION_13_14, // remove nullability from ColorPresetEntity - DatabaseHelper.MIGRATION_14_15, // remove author nullability from BookEntity + DatabaseHelper.MANUAL_MIGRATION_2_3, // creates LanguageHistoryEntity table(if does not exist) + DatabaseHelper.MANUAL_MIGRATION_4_5, // creates ColorPresetEntity table(if does not exist) + DatabaseHelper.MANUAL_MIGRATION_5_6, // creates FavoriteDirectoryEntity table(if does not exist) + DatabaseHelper.MANUAL_MIGRATION_13_14, // remove nullability from ColorPresetEntity + DatabaseHelper.MANUAL_MIGRATION_14_15, // remove author nullability from BookEntity + DatabaseHelper.MANUAL_MIGRATION_15_16, // merge CategoryEntity and CategorySortEntity ).allowMainThreadQueries().build() } } \ No newline at end of file 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 0046edda..c2f92e92 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 @@ -16,8 +16,6 @@ import ua.acclorite.book_story.data.mapper.book.BookMapper import ua.acclorite.book_story.data.mapper.book.BookMapperImpl import ua.acclorite.book_story.data.mapper.category.CategoryMapper import ua.acclorite.book_story.data.mapper.category.CategoryMapperImpl -import ua.acclorite.book_story.data.mapper.category_sort.CategorySortMapper -import ua.acclorite.book_story.data.mapper.category_sort.CategorySortSortMapperImpl import ua.acclorite.book_story.data.mapper.color_preset.ColorPresetMapper import ua.acclorite.book_story.data.mapper.color_preset.ColorPresetMapperImpl import ua.acclorite.book_story.data.mapper.file.FileMapper @@ -111,12 +109,6 @@ abstract class RepositoryModule { categoryMapperImpl: CategoryMapperImpl ): CategoryMapper - @Binds - @Singleton - abstract fun bindCategorySortMapper( - categorySortSortMapperImpl: CategorySortSortMapperImpl - ): CategorySortMapper - @Binds @Singleton abstract fun bindFileMapper( diff --git a/app/src/main/java/ua/acclorite/book_story/data/local/dto/CategoryEntity.kt b/app/src/main/java/ua/acclorite/book_story/data/local/dto/CategoryEntity.kt index 76a79b50..d4662391 100644 --- a/app/src/main/java/ua/acclorite/book_story/data/local/dto/CategoryEntity.kt +++ b/app/src/main/java/ua/acclorite/book_story/data/local/dto/CategoryEntity.kt @@ -6,13 +6,16 @@ package ua.acclorite.book_story.data.local.dto +import androidx.room.ColumnInfo import androidx.room.Entity import androidx.room.PrimaryKey +import ua.acclorite.book_story.presentation.library.model.LibrarySortOrder @Entity data class CategoryEntity( - @PrimaryKey(true) - val id: Int = 0, + @PrimaryKey(true) val id: Int = 0, val title: String, - val order: Int + val order: Int, + @ColumnInfo(defaultValue = "LAST_READ") val sortOrder: LibrarySortOrder, + @ColumnInfo(defaultValue = "1") val sortOrderDescending: Boolean ) \ No newline at end of file diff --git a/app/src/main/java/ua/acclorite/book_story/data/local/dto/CategorySortEntity.kt b/app/src/main/java/ua/acclorite/book_story/data/local/dto/CategorySortEntity.kt deleted file mode 100644 index 44f53cf9..00000000 --- a/app/src/main/java/ua/acclorite/book_story/data/local/dto/CategorySortEntity.kt +++ /dev/null @@ -1,19 +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.local.dto - -import androidx.room.Entity -import androidx.room.PrimaryKey -import ua.acclorite.book_story.presentation.library.model.LibrarySortOrder - -@Entity -data class CategorySortEntity( - @PrimaryKey(false) - val categoryId: Int, - val sortOrder: LibrarySortOrder, - val sortOrderDescending: Boolean -) \ No newline at end of file diff --git a/app/src/main/java/ua/acclorite/book_story/data/local/room/BookDao.kt b/app/src/main/java/ua/acclorite/book_story/data/local/room/BookDao.kt index 94a5075f..8c074b3a 100644 --- a/app/src/main/java/ua/acclorite/book_story/data/local/room/BookDao.kt +++ b/app/src/main/java/ua/acclorite/book_story/data/local/room/BookDao.kt @@ -12,15 +12,10 @@ import androidx.room.Insert import androidx.room.OnConflictStrategy import androidx.room.Query import androidx.room.Update -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 @Dao interface BookDao { - - /* ------ BookEntity ------------------------ */ @Insert(onConflict = OnConflictStrategy.REPLACE) suspend fun insertBook( book: BookEntity @@ -42,48 +37,4 @@ interface BookDao { @Update suspend fun updateBook(book: BookEntity): Int - /* - - - - - - - - - - - - - - - - - - - - - - */ - - - /* ------ CategoryEntity ----------------- */ - @Insert(onConflict = OnConflictStrategy.REPLACE) - suspend fun insertCategory( - category: CategoryEntity - ) - - @Query("SELECT * FROM categoryentity") - suspend fun getCategories(): List - - @Query("SELECT COUNT(*) FROM categoryentity") - suspend fun getCategoriesCount(): Int - - @Query("UPDATE categoryentity SET title=:title WHERE id=:id") - suspend fun updateCategoryTitle( - id: Int, - title: String - ) - - @Query("UPDATE categoryentity SET `order`=:order WHERE id=:id") - suspend fun updateCategoryOrder( - id: Int, - order: Int - ) - - @Delete - suspend fun deleteCategory( - category: CategoryEntity - ) - /* - - - - - - - - - - - - - - - - - - - - - - */ - - - /* ------ CategorySortEntity ----------------- */ - @Upsert - suspend fun updateCategorySort(categorySort: CategorySortEntity) - - @Query("SELECT * FROM categorysortentity") - suspend fun getCategorySortEntities(): List - - @Query("DELETE FROM categorysortentity WHERE categoryId=:categoryId") - suspend fun deleteCategorySortEntity(categoryId: Int) - /* - - - - - - - - - - - - - - - - - - - - - - */ } \ No newline at end of file diff --git a/app/src/main/java/ua/acclorite/book_story/data/local/room/BookDatabase.kt b/app/src/main/java/ua/acclorite/book_story/data/local/room/BookDatabase.kt index 7b72e9fe..6f65f636 100644 --- a/app/src/main/java/ua/acclorite/book_story/data/local/room/BookDatabase.kt +++ b/app/src/main/java/ua/acclorite/book_story/data/local/room/BookDatabase.kt @@ -17,7 +17,6 @@ import androidx.room.migration.Migration import androidx.sqlite.db.SupportSQLiteDatabase 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 import java.io.File @@ -27,25 +26,25 @@ import java.io.File BookEntity::class, HistoryEntity::class, ColorPresetEntity::class, - CategoryEntity::class, - CategorySortEntity::class + CategoryEntity::class ], - version = 15, + version = 16, autoMigrations = [ AutoMigration(1, 2), AutoMigration(2, 3), - AutoMigration(3, 4, spec = DatabaseHelper.MIGRATION_3_4::class), + AutoMigration(3, 4, spec = DatabaseHelper.AUTO_MIGRATION_3_4::class), AutoMigration(4, 5), AutoMigration(5, 6), AutoMigration(6, 7), - AutoMigration(7, 8, spec = DatabaseHelper.MIGRATION_7_8::class), - AutoMigration(8, 9, spec = DatabaseHelper.MIGRATION_8_9::class), - AutoMigration(9, 10, spec = DatabaseHelper.MIGRATION_9_10::class), + AutoMigration(7, 8, spec = DatabaseHelper.AUTO_MIGRATION_7_8::class), + AutoMigration(8, 9, spec = DatabaseHelper.AUTO_MIGRATION_8_9::class), + AutoMigration(9, 10, spec = DatabaseHelper.AUTO_MIGRATION_9_10::class), AutoMigration(10, 11), AutoMigration(11, 12), AutoMigration(12, 13), AutoMigration(13, 14), AutoMigration(14, 15), + AutoMigration(15, 16, spec = DatabaseHelper.AUTO_MIGRATION_15_16::class), ], exportSchema = true ) @@ -53,12 +52,13 @@ abstract class BookDatabase : RoomDatabase() { abstract val bookDao: BookDao abstract val historyDao: HistoryDao abstract val colorPresetDao: ColorPresetDao + abstract val categoryDao: CategoryDao } @Suppress("ClassName") object DatabaseHelper { - val MIGRATION_2_3 = object : Migration(2, 3) { + val MANUAL_MIGRATION_2_3 = object : Migration(2, 3) { override fun migrate(db: SupportSQLiteDatabase) { db.execSQL( "CREATE TABLE IF NOT EXISTS `LanguageHistoryEntity` (" + @@ -76,9 +76,9 @@ object DatabaseHelper { @DeleteColumn("BookEntity", "doubleClickTranslation") @DeleteColumn("BookEntity", "translateWhenOpen") @DeleteTable("LanguageHistoryEntity") - class MIGRATION_3_4 : AutoMigrationSpec + class AUTO_MIGRATION_3_4 : AutoMigrationSpec - val MIGRATION_4_5 = object : Migration(4, 5) { + val MANUAL_MIGRATION_4_5 = object : Migration(4, 5) { override fun migrate(db: SupportSQLiteDatabase) { db.execSQL( "CREATE TABLE IF NOT EXISTS `ColorPresetEntity` (" + @@ -93,7 +93,7 @@ object DatabaseHelper { } } - val MIGRATION_5_6 = object : Migration(5, 6) { + val MANUAL_MIGRATION_5_6 = object : Migration(5, 6) { override fun migrate(db: SupportSQLiteDatabase) { db.execSQL( "CREATE TABLE IF NOT EXISTS `FavoriteDirectoryEntity` (" + @@ -106,7 +106,7 @@ object DatabaseHelper { @DeleteColumn("BookEntity", "textPath") @DeleteColumn("BookEntity", "chapters") - class MIGRATION_7_8 : AutoMigrationSpec { + class AUTO_MIGRATION_7_8 : AutoMigrationSpec { companion object { /** * Along with textPath deletion, @@ -124,12 +124,12 @@ object DatabaseHelper { } @DeleteTable("FavoriteDirectoryEntity") - class MIGRATION_8_9 : AutoMigrationSpec + class AUTO_MIGRATION_8_9 : AutoMigrationSpec @DeleteColumn("BookEntity", "category") - class MIGRATION_9_10 : AutoMigrationSpec + class AUTO_MIGRATION_9_10 : AutoMigrationSpec - val MIGRATION_13_14 = object : Migration(13, 14) { + val MANUAL_MIGRATION_13_14 = object : Migration(13, 14) { override fun migrate(database: SupportSQLiteDatabase) { database.execSQL( """ @@ -161,7 +161,7 @@ object DatabaseHelper { } } - val MIGRATION_14_15 = object : Migration(14, 15) { + val MANUAL_MIGRATION_14_15 = object : Migration(14, 15) { override fun migrate(database: SupportSQLiteDatabase) { database.execSQL( """ @@ -190,4 +190,46 @@ object DatabaseHelper { database.execSQL("ALTER TABLE BookEntity_new RENAME TO BookEntity") } } + + @DeleteTable("CategorySortEntity") + class AUTO_MIGRATION_15_16 : AutoMigrationSpec + + val MANUAL_MIGRATION_15_16 = object : Migration(15, 16) { + override fun migrate(database: SupportSQLiteDatabase) { + database.execSQL( + """ + CREATE TABLE IF NOT EXISTS CategoryEntity_new ( + id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, + title TEXT NOT NULL, + `order` INTEGER NOT NULL, + sortOrder TEXT NOT NULL DEFAULT 'LAST_READ', + sortOrderDescending INTEGER NOT NULL DEFAULT 1 + ) + """ + ) + database.execSQL( + """ + INSERT INTO CategoryEntity_new (id, title, `order`, sortOrder, sortOrderDescending) + SELECT + c.id, + c.title, + c.`order`, + COALESCE(cs.sortOrder, 'LAST_READ'), + COALESCE(cs.sortOrderDescending, 1) + FROM CategoryEntity c + LEFT JOIN CategorySortEntity cs + ON c.id = cs.categoryId + """ + ) + database.execSQL( + """ + INSERT INTO CategoryEntity_new (id, title, `order`, sortOrder, sortOrderDescending) + VALUES (-1, '', -1, 'LAST_READ', 1) + """ + ) + database.execSQL("DROP TABLE CategoryEntity") + database.execSQL("DROP TABLE CategorySortEntity") + database.execSQL("ALTER TABLE CategoryEntity_new RENAME TO CategoryEntity") + } + } } \ No newline at end of file diff --git a/app/src/main/java/ua/acclorite/book_story/data/local/room/CategoryDao.kt b/app/src/main/java/ua/acclorite/book_story/data/local/room/CategoryDao.kt new file mode 100644 index 00000000..f857aa06 --- /dev/null +++ b/app/src/main/java/ua/acclorite/book_story/data/local/room/CategoryDao.kt @@ -0,0 +1,39 @@ +/* + * 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.Insert +import androidx.room.OnConflictStrategy +import androidx.room.Query +import androidx.room.Update +import ua.acclorite.book_story.data.local.dto.CategoryEntity + +@Dao +interface CategoryDao { + @Insert(onConflict = OnConflictStrategy.REPLACE) + suspend fun insertCategory( + category: CategoryEntity + ) + + @Query("SELECT * FROM categoryentity ORDER BY `order` ASC") + suspend fun getCategories(): List + + @Query("SELECT COUNT(*) FROM categoryentity") + suspend fun getCategoriesCount(): Int + + @Update + suspend fun updateCategory( + category: CategoryEntity + ) + + @Delete + suspend fun deleteCategory( + category: CategoryEntity + ) +} \ No newline at end of file diff --git a/app/src/main/java/ua/acclorite/book_story/data/mapper/category/CategoryMapperImpl.kt b/app/src/main/java/ua/acclorite/book_story/data/mapper/category/CategoryMapperImpl.kt index fd8f4f71..c3493f84 100644 --- a/app/src/main/java/ua/acclorite/book_story/data/mapper/category/CategoryMapperImpl.kt +++ b/app/src/main/java/ua/acclorite/book_story/data/mapper/category/CategoryMapperImpl.kt @@ -15,7 +15,9 @@ class CategoryMapperImpl @Inject constructor() : CategoryMapper { return CategoryEntity( id = category.id, title = category.title, - order = category.order + order = category.order, + sortOrder = category.sortOrder, + sortOrderDescending = category.sortOrderDescending ) } @@ -23,7 +25,9 @@ class CategoryMapperImpl @Inject constructor() : CategoryMapper { return Category( id = categoryEntity.id, title = categoryEntity.title, - order = categoryEntity.order + order = categoryEntity.order, + sortOrder = categoryEntity.sortOrder, + sortOrderDescending = categoryEntity.sortOrderDescending ) } } \ No newline at end of file diff --git a/app/src/main/java/ua/acclorite/book_story/data/mapper/category_sort/CategorySortMapper.kt b/app/src/main/java/ua/acclorite/book_story/data/mapper/category_sort/CategorySortMapper.kt deleted file mode 100644 index 36a10ba9..00000000 --- a/app/src/main/java/ua/acclorite/book_story/data/mapper/category_sort/CategorySortMapper.kt +++ /dev/null @@ -1,15 +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.mapper.category_sort - -import ua.acclorite.book_story.data.local.dto.CategorySortEntity -import ua.acclorite.book_story.domain.model.library.CategorySort - -interface CategorySortMapper { - fun toCategorySortEntity(categorySort: CategorySort): CategorySortEntity - fun toCategorySort(categorySortEntity: CategorySortEntity): CategorySort -} \ No newline at end of file diff --git a/app/src/main/java/ua/acclorite/book_story/data/mapper/category_sort/CategorySortSortMapperImpl.kt b/app/src/main/java/ua/acclorite/book_story/data/mapper/category_sort/CategorySortSortMapperImpl.kt deleted file mode 100644 index 069b44a1..00000000 --- a/app/src/main/java/ua/acclorite/book_story/data/mapper/category_sort/CategorySortSortMapperImpl.kt +++ /dev/null @@ -1,29 +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.mapper.category_sort - -import ua.acclorite.book_story.data.local.dto.CategorySortEntity -import ua.acclorite.book_story.domain.model.library.CategorySort -import javax.inject.Inject - -class CategorySortSortMapperImpl @Inject constructor() : CategorySortMapper { - override fun toCategorySortEntity(categorySort: CategorySort): CategorySortEntity { - return CategorySortEntity( - categoryId = categorySort.categoryId, - sortOrder = categorySort.sortOrder, - sortOrderDescending = categorySort.sortOrderDescending - ) - } - - override fun toCategorySort(categorySortEntity: CategorySortEntity): CategorySort { - return CategorySort( - categoryId = categorySortEntity.categoryId, - sortOrder = categorySortEntity.sortOrder, - sortOrderDescending = categorySortEntity.sortOrderDescending - ) - } -} \ No newline at end of file diff --git a/app/src/main/java/ua/acclorite/book_story/data/repository/CategoryRepositoryImpl.kt b/app/src/main/java/ua/acclorite/book_story/data/repository/CategoryRepositoryImpl.kt index dfb43dbf..bfae909f 100644 --- a/app/src/main/java/ua/acclorite/book_story/data/repository/CategoryRepositoryImpl.kt +++ b/app/src/main/java/ua/acclorite/book_story/data/repository/CategoryRepositoryImpl.kt @@ -10,9 +10,7 @@ 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 -import ua.acclorite.book_story.domain.model.library.CategorySort import ua.acclorite.book_story.domain.repository.CategoryRepository import javax.inject.Inject import javax.inject.Singleton @@ -20,16 +18,15 @@ import javax.inject.Singleton @Singleton class CategoryRepositoryImpl @Inject constructor( private val database: BookDatabase, - private val categoryMapper: CategoryMapper, - private val categorySortMapper: CategorySortMapper, + private val categoryMapper: CategoryMapper ) : CategoryRepository { override suspend fun addCategory(category: Category): Result = runCatching { withContext(Dispatchers.IO) { - database.bookDao.insertCategory( + database.categoryDao.insertCategory( categoryMapper.toCategoryEntity( category.copy( - order = database.bookDao.getCategoriesCount() + order = database.categoryDao.getCategoriesCount() ) ) ) @@ -38,16 +35,19 @@ class CategoryRepositoryImpl @Inject constructor( override suspend fun getCategories(): Result> = runCatching { withContext(Dispatchers.IO) { - database.bookDao.getCategories().map { categoryMapper.toCategory(it) } - .sortedBy { it.order } + database.categoryDao.getCategories().map { categoryMapper.toCategory(it) } } } override suspend fun updateCategory(category: Category): Result = runCatching { withContext(Dispatchers.IO) { - database.bookDao.updateCategoryTitle( - id = category.id, - title = category.title + database.categoryDao.updateCategory( + category = categoryMapper.toCategoryEntity( + if (category.id != -1) category else category.copy( + title = "", + order = -1 + ) + ) ) updateOrder(getCategories().getOrThrow()) } @@ -56,35 +56,21 @@ class CategoryRepositoryImpl @Inject constructor( override suspend fun updateOrder(categories: List): Result = runCatching { withContext(Dispatchers.IO) { categories.forEachIndexed { index, category -> - database.bookDao.updateCategoryOrder(category.id, index) + if (category.id == -1) return@forEachIndexed + database.categoryDao.updateCategory( + categoryMapper.toCategoryEntity( + category.copy(order = index) + ) + ) } } } override suspend fun deleteCategory(category: Category): Result = runCatching { withContext(Dispatchers.IO) { - database.bookDao.deleteCategory(categoryMapper.toCategoryEntity(category)) - database.bookDao.deleteCategorySortEntity(category.id) - + if (category.id == -1) throw IllegalArgumentException("Id should not be -1.") + database.categoryDao.deleteCategory(categoryMapper.toCategoryEntity(category)) updateOrder(getCategories().getOrThrow()) } } - - override suspend fun updateCategorySorting( - categorySort: CategorySort - ): Result = runCatching { - withContext(Dispatchers.IO) { - database.bookDao.updateCategorySort( - categorySortMapper.toCategorySortEntity(categorySort) - ) - } - } - - override suspend fun getCategorySorting(): Result> = runCatching { - withContext(Dispatchers.IO) { - database.bookDao.getCategorySortEntities().map { - categorySortMapper.toCategorySort(it) - } - } - } } \ No newline at end of file diff --git a/app/src/main/java/ua/acclorite/book_story/domain/model/library/Category.kt b/app/src/main/java/ua/acclorite/book_story/domain/model/library/Category.kt index c8ab1cdb..55b8fdd6 100644 --- a/app/src/main/java/ua/acclorite/book_story/domain/model/library/Category.kt +++ b/app/src/main/java/ua/acclorite/book_story/domain/model/library/Category.kt @@ -7,10 +7,13 @@ package ua.acclorite.book_story.domain.model.library import androidx.compose.runtime.Immutable +import ua.acclorite.book_story.presentation.library.model.LibrarySortOrder @Immutable data class Category( val id: Int = 0, val title: String, - val order: Int = 0 + val order: Int = 0, + val sortOrder: LibrarySortOrder = LibrarySortOrder.LAST_READ, + val sortOrderDescending: Boolean = true ) \ No newline at end of file diff --git a/app/src/main/java/ua/acclorite/book_story/domain/model/library/CategorySort.kt b/app/src/main/java/ua/acclorite/book_story/domain/model/library/CategorySort.kt deleted file mode 100644 index bcb6d8e8..00000000 --- a/app/src/main/java/ua/acclorite/book_story/domain/model/library/CategorySort.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.domain.model.library - -import androidx.compose.runtime.Immutable -import ua.acclorite.book_story.presentation.library.model.LibrarySortOrder - -@Immutable -data class CategorySort( - val categoryId: Int, - val sortOrder: LibrarySortOrder, - val sortOrderDescending: Boolean -) \ No newline at end of file diff --git a/app/src/main/java/ua/acclorite/book_story/domain/repository/CategoryRepository.kt b/app/src/main/java/ua/acclorite/book_story/domain/repository/CategoryRepository.kt index 3634682e..26e9dc29 100644 --- a/app/src/main/java/ua/acclorite/book_story/domain/repository/CategoryRepository.kt +++ b/app/src/main/java/ua/acclorite/book_story/domain/repository/CategoryRepository.kt @@ -7,7 +7,6 @@ package ua.acclorite.book_story.domain.repository import ua.acclorite.book_story.domain.model.library.Category -import ua.acclorite.book_story.domain.model.library.CategorySort interface CategoryRepository { suspend fun addCategory( @@ -27,10 +26,4 @@ interface CategoryRepository { suspend fun deleteCategory( category: Category ): Result - - suspend fun updateCategorySorting( - categorySort: CategorySort - ): Result - - suspend fun getCategorySorting(): Result> } \ No newline at end of file diff --git a/app/src/main/java/ua/acclorite/book_story/domain/use_case/category/GetCategorySortingUseCase.kt b/app/src/main/java/ua/acclorite/book_story/domain/use_case/category/GetCategorySortingUseCase.kt deleted file mode 100644 index 1b7c203f..00000000 --- a/app/src/main/java/ua/acclorite/book_story/domain/use_case/category/GetCategorySortingUseCase.kt +++ /dev/null @@ -1,33 +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.domain.use_case.category - -import ua.acclorite.book_story.core.log.logE -import ua.acclorite.book_story.core.log.logI -import ua.acclorite.book_story.domain.model.library.CategorySort -import ua.acclorite.book_story.domain.repository.CategoryRepository -import javax.inject.Inject - -class GetCategorySortingUseCase @Inject constructor( - private val categoryRepository: CategoryRepository -) { - - suspend operator fun invoke(): List { - logI("Getting category sorting.") - - return categoryRepository.getCategorySorting().fold( - onSuccess = { - logI("Successfully got category sorting.") - it - }, - onFailure = { - logE("Could not get category sorting with error: ${it.message}") - emptyList() - } - ) - } -} \ No newline at end of file diff --git a/app/src/main/java/ua/acclorite/book_story/domain/use_case/category/UpdateCategorySortingUseCase.kt b/app/src/main/java/ua/acclorite/book_story/domain/use_case/category/UpdateCategorySortingUseCase.kt deleted file mode 100644 index d2b5b8f2..00000000 --- a/app/src/main/java/ua/acclorite/book_story/domain/use_case/category/UpdateCategorySortingUseCase.kt +++ /dev/null @@ -1,33 +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.domain.use_case.category - -import ua.acclorite.book_story.core.log.logE -import ua.acclorite.book_story.core.log.logI -import ua.acclorite.book_story.domain.model.library.CategorySort -import ua.acclorite.book_story.domain.repository.CategoryRepository -import javax.inject.Inject - -class UpdateCategorySortingUseCase @Inject constructor( - private val categoryRepository: CategoryRepository -) { - - suspend operator fun invoke(categorySort: CategorySort) { - logI("Updating category sorting of [${categorySort.categoryId}].") - - categoryRepository.updateCategorySorting(categorySort).fold( - onSuccess = { - logI("Successfully updated category sorting of [${categorySort.categoryId}].") - }, - onFailure = { - logE( - "Could not update category sorting of [${categorySort.categoryId}] with error: ${it.message}" - ) - } - ) - } -} \ No newline at end of file diff --git a/app/src/main/java/ua/acclorite/book_story/domain/use_case/category/UpdateCategoryUseCase.kt b/app/src/main/java/ua/acclorite/book_story/domain/use_case/category/UpdateCategoryUseCase.kt index a4271859..cb9e3dcf 100644 --- a/app/src/main/java/ua/acclorite/book_story/domain/use_case/category/UpdateCategoryUseCase.kt +++ b/app/src/main/java/ua/acclorite/book_story/domain/use_case/category/UpdateCategoryUseCase.kt @@ -15,15 +15,15 @@ class UpdateCategoryUseCase @Inject constructor( private val categoryRepository: CategoryRepository ) { - suspend operator fun invoke(categoryId: Int, newTitle: String) { - logI("Updating category [$categoryId].") + suspend operator fun invoke(category: Category) { + logI("Updating category [${category.id}].") - categoryRepository.updateCategory(Category(id = categoryId, title = newTitle)).fold( + categoryRepository.updateCategory(category).fold( onSuccess = { - logI("Successfully updated [$categoryId].") + logI("Successfully updated [${category.id}].") }, onFailure = { - logI("Could not update [$categoryId] with error: ${it.message}") + logI("Could not update [${category.id}] with error: ${it.message}") } ) } diff --git a/app/src/main/java/ua/acclorite/book_story/presentation/book_info/BookInfoScreen.kt b/app/src/main/java/ua/acclorite/book_story/presentation/book_info/BookInfoScreen.kt index f5218597..fa5857af 100644 --- a/app/src/main/java/ua/acclorite/book_story/presentation/book_info/BookInfoScreen.kt +++ b/app/src/main/java/ua/acclorite/book_story/presentation/book_info/BookInfoScreen.kt @@ -13,6 +13,7 @@ import androidx.compose.foundation.lazy.rememberLazyListState import androidx.compose.runtime.Composable import androidx.compose.runtime.DisposableEffect import androidx.compose.runtime.LaunchedEffect +import androidx.compose.runtime.remember import androidx.compose.ui.Modifier import androidx.hilt.navigation.compose.hiltViewModel import androidx.lifecycle.compose.collectAsStateWithLifecycle @@ -50,6 +51,10 @@ data class BookInfoScreen(val bookId: Int) : Screen, Parcelable { val state = screenModel.state.collectAsStateWithLifecycle() val listState = rememberLazyListState() + val categories = remember(settingsState.value.categories) { + settingsState.value.categories.filterNot { it.id == -1 } + } + LaunchedEffect(Unit) { screenModel.init( bookId = bookId, @@ -74,7 +79,7 @@ data class BookInfoScreen(val bookId: Int) : Screen, Parcelable { BookInfoContent( book = state.value.book, file = state.value.file, - categories = settingsState.value.categories, + categories = categories, bottomSheet = state.value.bottomSheet, dialog = state.value.dialog, listState = listState, diff --git a/app/src/main/java/ua/acclorite/book_story/presentation/library/LibraryScreen.kt b/app/src/main/java/ua/acclorite/book_story/presentation/library/LibraryScreen.kt index 97620ccb..955ee81c 100644 --- a/app/src/main/java/ua/acclorite/book_story/presentation/library/LibraryScreen.kt +++ b/app/src/main/java/ua/acclorite/book_story/presentation/library/LibraryScreen.kt @@ -89,7 +89,7 @@ object LibraryScreen : Screen, Parcelable { val pagerState = rememberPagerState( initialPage = initialPage ) { - settingsState.value.categories.count().plus(if (showDefaultCategory.value) 1 else 0) + settingsState.value.categories.count().minus(if (!showDefaultCategory.value) 1 else 0) } DisposableEffect(Unit) { onDispose { initialPage = pagerState.currentPage } } @@ -126,14 +126,13 @@ object LibraryScreen : Screen, Parcelable { autoGridSize = settings.libraryAutoGridSize.value, categories = settingsState.value.categories, showDefaultCategory = showDefaultCategory.value, - categoriesSort = settingsState.value.categoriesSort, sortOrder = settings.librarySortOrder.value, sortOrderDescending = settings.librarySortOrderDescending.value, perCategorySort = settings.libraryPerCategorySort.value, refreshState = refreshState, dialog = state.value.dialog, bottomSheet = state.value.bottomSheet, - updateCategorySort = settingsModel::onEvent, + updateCategory = settingsModel::onEvent, changeSortOrder = { settings.librarySortOrder.update(it) }, changeSortOrderDescending = { settings.librarySortOrderDescending.update(it) }, selectBook = screenModel::onEvent, diff --git a/app/src/main/java/ua/acclorite/book_story/presentation/settings/SettingsEvent.kt b/app/src/main/java/ua/acclorite/book_story/presentation/settings/SettingsEvent.kt index e476d81f..438dc45a 100644 --- a/app/src/main/java/ua/acclorite/book_story/presentation/settings/SettingsEvent.kt +++ b/app/src/main/java/ua/acclorite/book_story/presentation/settings/SettingsEvent.kt @@ -10,7 +10,6 @@ import androidx.compose.runtime.Immutable import androidx.compose.ui.graphics.Color import ua.acclorite.book_story.core.language.Language import ua.acclorite.book_story.domain.model.library.Category -import ua.acclorite.book_story.presentation.library.model.LibrarySortOrder @Immutable sealed class SettingsEvent { @@ -30,9 +29,8 @@ sealed class SettingsEvent { val title: String ) : SettingsEvent() - data class OnUpdateCategoryTitle( - val id: Int, - val title: String + data class OnUpdateCategory( + val category: Category ) : SettingsEvent() data class OnUpdateCategoryOrder( @@ -43,12 +41,6 @@ sealed class SettingsEvent { val category: Category ) : SettingsEvent() - data class OnUpdateCategorySort( - val categoryId: Int, - val sortOrder: LibrarySortOrder, - val sortOrderDescending: Boolean - ) : SettingsEvent() - data class OnSelectColorPreset( val id: Int, ) : SettingsEvent() diff --git a/app/src/main/java/ua/acclorite/book_story/presentation/settings/SettingsModel.kt b/app/src/main/java/ua/acclorite/book_story/presentation/settings/SettingsModel.kt index 4b7a5608..c0b2ace4 100644 --- a/app/src/main/java/ua/acclorite/book_story/presentation/settings/SettingsModel.kt +++ b/app/src/main/java/ua/acclorite/book_story/presentation/settings/SettingsModel.kt @@ -20,14 +20,11 @@ import kotlinx.coroutines.launch import kotlinx.coroutines.sync.Mutex import kotlinx.coroutines.sync.withLock import kotlinx.coroutines.withContext -import ua.acclorite.book_story.domain.model.library.CategorySort import ua.acclorite.book_story.domain.model.reader.ColorPreset import ua.acclorite.book_story.domain.use_case.category.AddCategoryUseCase import ua.acclorite.book_story.domain.use_case.category.DeleteCategoryUseCase import ua.acclorite.book_story.domain.use_case.category.GetCategoriesUseCase -import ua.acclorite.book_story.domain.use_case.category.GetCategorySortingUseCase import ua.acclorite.book_story.domain.use_case.category.UpdateCategoriesOrderUseCase -import ua.acclorite.book_story.domain.use_case.category.UpdateCategorySortingUseCase import ua.acclorite.book_story.domain.use_case.category.UpdateCategoryUseCase import ua.acclorite.book_story.domain.use_case.color_preset.DeleteColorPresetUseCase import ua.acclorite.book_story.domain.use_case.color_preset.GetColorPresetsUseCase @@ -55,9 +52,7 @@ class SettingsModel @Inject constructor( private val getCategoriesUseCase: GetCategoriesUseCase, private val updateCategoryUseCase: UpdateCategoryUseCase, private val updateCategoriesOrderUseCase: UpdateCategoriesOrderUseCase, - private val deleteCategoryUseCase: DeleteCategoryUseCase, - private val updateCategorySortingUseCase: UpdateCategorySortingUseCase, - private val getCategorySortingUseCase: GetCategorySortingUseCase + private val deleteCategoryUseCase: DeleteCategoryUseCase ) : ViewModel() { private val mutex = Mutex() @@ -98,8 +93,7 @@ class SettingsModel @Inject constructor( it.copy( selectedColorPreset = colorPresets.getSelectedColorPreset(), colorPresets = colorPresets, - categories = getCategoriesUseCase(), - categoriesSort = getCategorySortingUseCase() + categories = getCategoriesUseCase() ) } @@ -131,10 +125,9 @@ class SettingsModel @Inject constructor( } } - is SettingsEvent.OnUpdateCategoryTitle -> { + is SettingsEvent.OnUpdateCategory -> { updateCategoryUseCase( - categoryId = event.id, - newTitle = event.title + category = event.category ) _state.update { it.copy( @@ -165,21 +158,6 @@ class SettingsModel @Inject constructor( } } - is SettingsEvent.OnUpdateCategorySort -> { - updateCategorySortingUseCase( - categorySort = CategorySort( - categoryId = event.categoryId, - sortOrder = event.sortOrder, - sortOrderDescending = event.sortOrderDescending - ) - ) - _state.update { - it.copy( - categoriesSort = getCategorySortingUseCase() - ) - } - } - is SettingsEvent.OnSelectColorPreset -> { withContext(Dispatchers.IO) { colorPresetJob?.join() diff --git a/app/src/main/java/ua/acclorite/book_story/presentation/settings/SettingsState.kt b/app/src/main/java/ua/acclorite/book_story/presentation/settings/SettingsState.kt index 746502c3..3ba7b878 100644 --- a/app/src/main/java/ua/acclorite/book_story/presentation/settings/SettingsState.kt +++ b/app/src/main/java/ua/acclorite/book_story/presentation/settings/SettingsState.kt @@ -9,7 +9,6 @@ package ua.acclorite.book_story.presentation.settings import androidx.compose.foundation.lazy.LazyListState import androidx.compose.runtime.Immutable import ua.acclorite.book_story.domain.model.library.Category -import ua.acclorite.book_story.domain.model.library.CategorySort import ua.acclorite.book_story.domain.model.reader.ColorPreset @Immutable @@ -19,6 +18,5 @@ data class SettingsState( val animateColorPreset: Boolean = false, val colorPresetListState: LazyListState = LazyListState(), - val categories: List = emptyList(), - val categoriesSort: List = emptyList(), + val categories: List = emptyList() ) \ No newline at end of file diff --git a/app/src/main/java/ua/acclorite/book_story/ui/library/LibraryBottomSheet.kt b/app/src/main/java/ua/acclorite/book_story/ui/library/LibraryBottomSheet.kt index 083ee4a8..4c31aeae 100644 --- a/app/src/main/java/ua/acclorite/book_story/ui/library/LibraryBottomSheet.kt +++ b/app/src/main/java/ua/acclorite/book_story/ui/library/LibraryBottomSheet.kt @@ -10,7 +10,6 @@ import androidx.compose.foundation.pager.PagerState import androidx.compose.runtime.Composable import ua.acclorite.book_story.core.BottomSheet import ua.acclorite.book_story.domain.model.library.Category -import ua.acclorite.book_story.domain.model.library.CategorySort import ua.acclorite.book_story.presentation.library.LibraryEvent import ua.acclorite.book_story.presentation.library.LibraryScreen import ua.acclorite.book_story.presentation.library.model.LibrarySortOrder @@ -22,13 +21,12 @@ fun LibraryBottomSheet( categories: List, showDefaultCategory: Boolean, pagerState: PagerState, - categoriesSort: List, sortOrder: LibrarySortOrder, sortOrderDescending: Boolean, perCategorySort: Boolean, changeSortOrder: (LibrarySortOrder) -> Unit, changeSortOrderDescending: (Boolean) -> Unit, - updateCategorySort: (SettingsEvent.OnUpdateCategorySort) -> Unit, + updateCategory: (SettingsEvent.OnUpdateCategory) -> Unit, dismissBottomSheet: (LibraryEvent.OnDismissBottomSheet) -> Unit ) { when (bottomSheet) { @@ -39,11 +37,10 @@ fun LibraryBottomSheet( categoriesPagerState = pagerState, sortOrder = sortOrder, sortOrderDescending = sortOrderDescending, - categoriesSort = categoriesSort, perCategorySort = perCategorySort, changeSortOrder = changeSortOrder, changeSortOrderDescending = changeSortOrderDescending, - updateCategorySort = updateCategorySort, + updateCategory = updateCategory, dismissBottomSheet = dismissBottomSheet ) } diff --git a/app/src/main/java/ua/acclorite/book_story/ui/library/LibraryContent.kt b/app/src/main/java/ua/acclorite/book_story/ui/library/LibraryContent.kt index ca1678e6..1eb00027 100644 --- a/app/src/main/java/ua/acclorite/book_story/ui/library/LibraryContent.kt +++ b/app/src/main/java/ua/acclorite/book_story/ui/library/LibraryContent.kt @@ -14,7 +14,6 @@ import androidx.compose.ui.focus.FocusRequester import ua.acclorite.book_story.core.BottomSheet import ua.acclorite.book_story.core.Dialog import ua.acclorite.book_story.domain.model.library.Category -import ua.acclorite.book_story.domain.model.library.CategorySort import ua.acclorite.book_story.presentation.library.LibraryEvent import ua.acclorite.book_story.presentation.library.model.LibraryLayout import ua.acclorite.book_story.presentation.library.model.LibrarySortOrder @@ -44,7 +43,6 @@ fun LibraryContent( categories: List, showDefaultCategory: Boolean, perCategorySort: Boolean, - categoriesSort: List, sortOrder: LibrarySortOrder, sortOrderDescending: Boolean, layout: LibraryLayout, @@ -53,7 +51,7 @@ fun LibraryContent( refreshState: PullRefreshState, dialog: Dialog?, bottomSheet: BottomSheet?, - updateCategorySort: (SettingsEvent.OnUpdateCategorySort) -> Unit, + updateCategory: (SettingsEvent.OnUpdateCategory) -> Unit, changeSortOrder: (LibrarySortOrder) -> Unit, changeSortOrderDescending: (Boolean) -> Unit, selectBook: (LibraryEvent.OnSelectBook) -> Unit, @@ -90,13 +88,12 @@ fun LibraryContent( categories = categories, showDefaultCategory = showDefaultCategory, pagerState = pagerState, - categoriesSort = categoriesSort, sortOrder = sortOrder, sortOrderDescending = sortOrderDescending, perCategorySort = perCategorySort, changeSortOrder = changeSortOrder, changeSortOrderDescending = changeSortOrderDescending, - updateCategorySort = updateCategorySort, + updateCategory = updateCategory, dismissBottomSheet = dismissBottomSheet ) @@ -122,7 +119,6 @@ fun LibraryContent( categories = categories, showDefaultCategory = showDefaultCategory, perCategorySort = perCategorySort, - categoriesSort = categoriesSort, sortOrder = sortOrder, sortOrderDescending = sortOrderDescending, searchVisibility = searchVisibility, diff --git a/app/src/main/java/ua/acclorite/book_story/ui/library/LibraryFilterBottomSheet.kt b/app/src/main/java/ua/acclorite/book_story/ui/library/LibraryFilterBottomSheet.kt index fccb760b..5fe44a1a 100644 --- a/app/src/main/java/ua/acclorite/book_story/ui/library/LibraryFilterBottomSheet.kt +++ b/app/src/main/java/ua/acclorite/book_story/ui/library/LibraryFilterBottomSheet.kt @@ -23,7 +23,6 @@ import androidx.compose.ui.Modifier import androidx.compose.ui.unit.dp import kotlinx.coroutines.launch import ua.acclorite.book_story.domain.model.library.Category -import ua.acclorite.book_story.domain.model.library.CategorySort import ua.acclorite.book_story.presentation.library.LibraryEvent import ua.acclorite.book_story.presentation.library.model.LibrarySortOrder import ua.acclorite.book_story.presentation.settings.SettingsEvent @@ -42,11 +41,10 @@ fun LibraryFilterBottomSheet( categoriesPagerState: PagerState, sortOrder: LibrarySortOrder, sortOrderDescending: Boolean, - categoriesSort: List, perCategorySort: Boolean, changeSortOrder: (LibrarySortOrder) -> Unit, changeSortOrderDescending: (Boolean) -> Unit, - updateCategorySort: (SettingsEvent.OnUpdateCategorySort) -> Unit, + updateCategory: (SettingsEvent.OnUpdateCategory) -> Unit, dismissBottomSheet: (LibraryEvent.OnDismissBottomSheet) -> Unit ) { val currentCategory = remember( @@ -55,25 +53,12 @@ fun LibraryFilterBottomSheet( categoriesPagerState.currentPage ) { derivedStateOf { - categories.toMutableList().apply { - if (showDefaultCategory) { - add(0, Category(id = -1, title = "")) - } + categories.filterNot { + if (!showDefaultCategory) it.id == -1 else false }[categoriesPagerState.currentPage] } } - val currentCategorySort = remember(currentCategory, categoriesSort) { - derivedStateOf { - categoriesSort.firstOrNull { it.categoryId == currentCategory.value.id } - ?: CategorySort( - categoryId = currentCategory.value.id, - sortOrder = LibrarySortOrder.LAST_READ, - sortOrderDescending = true - ) - } - } - val scope = rememberCoroutineScope() val pagerState = rememberPagerState(initialPage) { 2 } DisposableEffect(Unit) { onDispose { initialPage = pagerState.currentPage } } @@ -106,19 +91,18 @@ fun LibraryFilterBottomSheet( contentPadding = PaddingValues(top = 18.dp) ) { LibrarySortOption( - sortOrder = if (perCategorySort) { - currentCategorySort.value.sortOrder - } else sortOrder, - sortOrderDescending = if (perCategorySort) { - currentCategorySort.value.sortOrderDescending - } else sortOrderDescending, + sortOrder = if (perCategorySort) currentCategory.value.sortOrder + else sortOrder, + sortOrderDescending = if (perCategorySort) currentCategory.value.sortOrderDescending + else sortOrderDescending, onChange = { sortOrder, sortOrderDescending -> if (perCategorySort) { - updateCategorySort( - SettingsEvent.OnUpdateCategorySort( - categoryId = currentCategorySort.value.categoryId, - sortOrder = sortOrder, - sortOrderDescending = sortOrderDescending + updateCategory( + SettingsEvent.OnUpdateCategory( + currentCategory.value.copy( + sortOrder = sortOrder, + sortOrderDescending = sortOrderDescending + ) ) ) } else { diff --git a/app/src/main/java/ua/acclorite/book_story/ui/library/LibraryMoveDialog.kt b/app/src/main/java/ua/acclorite/book_story/ui/library/LibraryMoveDialog.kt index 2a3f8e30..f933fec0 100644 --- a/app/src/main/java/ua/acclorite/book_story/ui/library/LibraryMoveDialog.kt +++ b/app/src/main/java/ua/acclorite/book_story/ui/library/LibraryMoveDialog.kt @@ -40,7 +40,7 @@ fun LibraryMoveDialog( val selectedCategories = remember { mutableStateListOf().apply { clear() - categories.forEach { category -> + categories.filterNot { it.id == -1 }.forEach { category -> if (selectedBooks.value.all { it.data.categories.any { it == category.id } }) { add(category) } @@ -69,7 +69,7 @@ fun LibraryMoveDialog( }, withContent = true, items = { - items(categories) { category -> + items(categories.filterNot { it.id == -1 }) { category -> val selected = remember( category, selectedCategories diff --git a/app/src/main/java/ua/acclorite/book_story/ui/library/LibraryPager.kt b/app/src/main/java/ua/acclorite/book_story/ui/library/LibraryPager.kt index f86408f6..18e4aa65 100644 --- a/app/src/main/java/ua/acclorite/book_story/ui/library/LibraryPager.kt +++ b/app/src/main/java/ua/acclorite/book_story/ui/library/LibraryPager.kt @@ -16,7 +16,6 @@ import androidx.compose.runtime.remember import androidx.compose.ui.Modifier import ua.acclorite.book_story.core.helpers.compareByWithOrder import ua.acclorite.book_story.domain.model.library.Category -import ua.acclorite.book_story.domain.model.library.CategorySort import ua.acclorite.book_story.presentation.library.LibraryEvent import ua.acclorite.book_story.presentation.library.model.LibraryLayout import ua.acclorite.book_story.presentation.library.model.LibrarySortOrder @@ -31,7 +30,6 @@ fun LibraryPager( categories: List, showDefaultCategory: Boolean, perCategorySort: Boolean, - categoriesSort: List, sortOrder: LibrarySortOrder, sortOrderDescending: Boolean, layout: LibraryLayout, @@ -51,7 +49,6 @@ fun LibraryPager( val categorizedBooks = remember( books, categories, - categoriesSort, perCategorySort, sortOrder, sortOrderDescending, @@ -76,61 +73,35 @@ fun LibraryPager( derivedStateOf { val categorizedBooks = mutableListOf>() - if (showDefaultCategory) { - val categoryIds = categories.map { it.id }.toSet() - categorizedBooks.add( - books.filter { book -> - book.data.categories.none { category -> category in categoryIds } - }.let { - if (perCategorySort) { - val categorySort = categoriesSort.firstOrNull { - it.categoryId == -1 - } ?: CategorySort( - categoryId = -1, - sortOrder = LibrarySortOrder.LAST_READ, - sortOrderDescending = true - ) - - return@let it.sortBooks( - categorySort.sortOrder, - categorySort.sortOrderDescending - ) - } - - return@let it - } - ) - } - - categories.sortedBy { it.order }.forEach { category -> - categorizedBooks.add( - books - .filter { it.data.categories.any { category.id == it } } - .let { + val categoryIds = categories.map { it.id }.toSet() + categories + .filterNot { if (!showDefaultCategory) it.id == -1 else false } + .sortedBy { it.order } + .forEach { category -> + categorizedBooks.add( + books.filter { book -> + if (category.id == -1) { + book.data.categories.none { it in categoryIds } + } else { + book.data.categories.any { it == category.id } + } + }.let { books -> if (perCategorySort) { - val categorySort = categoriesSort.firstOrNull { - it.categoryId == category.id - } ?: CategorySort( - categoryId = category.id, - sortOrder = LibrarySortOrder.LAST_READ, - sortOrderDescending = true - ) - - return@let it.sortBooks( - categorySort.sortOrder, - categorySort.sortOrderDescending + return@let books.sortBooks( + category.sortOrder, + category.sortOrderDescending ) } - return@let it + return@let books } - ) - } + ) + } return@derivedStateOf categorizedBooks.let { if (!perCategorySort) { - return@let it.map { - it.sortBooks( + return@let it.map { books -> + books.sortBooks( sortOrder, sortOrderDescending ) diff --git a/app/src/main/java/ua/acclorite/book_story/ui/library/LibraryScaffold.kt b/app/src/main/java/ua/acclorite/book_story/ui/library/LibraryScaffold.kt index ee8e16d3..63824699 100644 --- a/app/src/main/java/ua/acclorite/book_story/ui/library/LibraryScaffold.kt +++ b/app/src/main/java/ua/acclorite/book_story/ui/library/LibraryScaffold.kt @@ -19,7 +19,6 @@ import androidx.compose.runtime.Composable import androidx.compose.ui.Modifier import androidx.compose.ui.focus.FocusRequester import ua.acclorite.book_story.domain.model.library.Category -import ua.acclorite.book_story.domain.model.library.CategorySort import ua.acclorite.book_story.presentation.library.LibraryEvent import ua.acclorite.book_story.presentation.library.model.LibraryLayout import ua.acclorite.book_story.presentation.library.model.LibrarySortOrder @@ -51,7 +50,6 @@ fun LibraryScaffold( categories: List, showDefaultCategory: Boolean, perCategorySort: Boolean, - categoriesSort: List, sortOrder: LibrarySortOrder, sortOrderDescending: Boolean, searchVisibility: (LibraryEvent.OnSearchVisibility) -> Unit, @@ -110,7 +108,6 @@ fun LibraryScaffold( categories = categories, showDefaultCategory = showDefaultCategory, perCategorySort = perCategorySort, - categoriesSort = categoriesSort, sortOrder = sortOrder, sortOrderDescending = sortOrderDescending, layout = layout, diff --git a/app/src/main/java/ua/acclorite/book_story/ui/library/LibraryTopBar.kt b/app/src/main/java/ua/acclorite/book_story/ui/library/LibraryTopBar.kt index 096f55ac..79ee45c8 100644 --- a/app/src/main/java/ua/acclorite/book_story/ui/library/LibraryTopBar.kt +++ b/app/src/main/java/ua/acclorite/book_story/ui/library/LibraryTopBar.kt @@ -85,7 +85,7 @@ fun LibraryTopBar( defaultCategory ) { derivedStateOf { - categories.map { category -> + categories.filterNot { it.id == -1 }.map { category -> category to books.count { it.data.categories.any { it == category.id } } }.toMutableList().apply { if (showDefaultCategory) { diff --git a/app/src/main/java/ua/acclorite/book_story/ui/settings/library/categories/components/LibraryCategoriesOption.kt b/app/src/main/java/ua/acclorite/book_story/ui/settings/library/categories/components/LibraryCategoriesOption.kt index d54e4c72..cae650f2 100644 --- a/app/src/main/java/ua/acclorite/book_story/ui/settings/library/categories/components/LibraryCategoriesOption.kt +++ b/app/src/main/java/ua/acclorite/book_story/ui/settings/library/categories/components/LibraryCategoriesOption.kt @@ -68,6 +68,9 @@ fun LibraryCategoriesOption() { val settingsModel = hiltViewModel() val state = settingsModel.state.collectAsStateWithLifecycle() + val categories = remember(state.value.categories) { + state.value.categories.filterNot { it.id == -1 } + } val dialog = remember { mutableStateOf(null) } @@ -94,12 +97,15 @@ fun LibraryCategoriesOption() { initialValue = selectedCategory.value?.title ?: "", onDismiss = { dialog.value = null }, onAction = { title -> - settingsModel.onEvent( - SettingsEvent.OnUpdateCategoryTitle( - id = selectedCategory.value?.id ?: -1, - title = title + selectedCategory.value?.let { selectedCategory -> + settingsModel.onEvent( + SettingsEvent.OnUpdateCategory( + category = selectedCategory.copy( + title = title + ) + ) ) - ) + } } ) } @@ -108,10 +114,10 @@ fun LibraryCategoriesOption() { LibraryCategoriesRemoveDialog( onDismiss = { dialog.value = null }, onAction = { - if (selectedCategory.value != null) { + selectedCategory.value?.let { selectedCategory -> LibraryScreen.scrollToPageCompositionChannel.trySend(0) settingsModel.onEvent( - SettingsEvent.OnRemoveCategory(selectedCategory.value!!) + SettingsEvent.OnRemoveCategory(selectedCategory) ) } dialog.value = null @@ -124,12 +130,12 @@ fun LibraryCategoriesOption() { modifier = Modifier .fillMaxWidth() .animateContentSize(), - list = state.value.categories, + list = categories, onSettle = { from, to -> LibraryScreen.scrollToPageCompositionChannel.trySend(0) settingsModel.onEvent( SettingsEvent.OnUpdateCategoryOrder( - state.value.categories.toMutableList().apply { + categories.toMutableList().apply { add(to, removeAt(from)) } )