From 7629396bd52b824101e0bdf56b434577796d5575 Mon Sep 17 00:00:00 2001 From: Terrance Date: Wed, 18 Mar 2026 20:09:31 +0000 Subject: [PATCH 1/6] Create record type to favourite comment repo --- .../data_local/di/DataLocalModule.kt | 7 ++ .../data_local/di/DataLocalModuleBinds.kt | 6 ++ .../RecordTypeToFavouriteCommentDBO.kt | 12 +++ .../RecordTypeToFavouriteCommentDao.kt | 35 +++++++ ...rdTypeToFavouriteCommentDataLocalMapper.kt | 28 ++++++ .../RecordTypeToFavouriteCommentRepoImpl.kt | 96 +++++++++++++++++++ .../RecordTypeToFavouriteCommentInteractor.kt | 34 +++++++ .../model/RecordTypeToFavouriteComment.kt | 6 ++ .../repo/RecordTypeToFavouriteCommentRepo.kt | 26 +++++ 9 files changed, 250 insertions(+) create mode 100644 data_local/src/main/java/com/example/util/simpletimetracker/data_local/favourite/RecordTypeToFavouriteCommentDBO.kt create mode 100644 data_local/src/main/java/com/example/util/simpletimetracker/data_local/favourite/RecordTypeToFavouriteCommentDao.kt create mode 100644 data_local/src/main/java/com/example/util/simpletimetracker/data_local/favourite/RecordTypeToFavouriteCommentDataLocalMapper.kt create mode 100644 data_local/src/main/java/com/example/util/simpletimetracker/data_local/favourite/RecordTypeToFavouriteCommentRepoImpl.kt create mode 100644 domain/src/main/java/com/example/util/simpletimetracker/domain/favourite/interactor/RecordTypeToFavouriteCommentInteractor.kt create mode 100644 domain/src/main/java/com/example/util/simpletimetracker/domain/favourite/model/RecordTypeToFavouriteComment.kt create mode 100644 domain/src/main/java/com/example/util/simpletimetracker/domain/favourite/repo/RecordTypeToFavouriteCommentRepo.kt diff --git a/data_local/src/main/java/com/example/util/simpletimetracker/data_local/di/DataLocalModule.kt b/data_local/src/main/java/com/example/util/simpletimetracker/data_local/di/DataLocalModule.kt index 8004c319e..1ac9aa40f 100644 --- a/data_local/src/main/java/com/example/util/simpletimetracker/data_local/di/DataLocalModule.kt +++ b/data_local/src/main/java/com/example/util/simpletimetracker/data_local/di/DataLocalModule.kt @@ -13,6 +13,7 @@ import com.example.util.simpletimetracker.data_local.complexRule.ComplexRulesDao import com.example.util.simpletimetracker.data_local.favourite.FavouriteColorDao import com.example.util.simpletimetracker.data_local.favourite.FavouriteCommentDao import com.example.util.simpletimetracker.data_local.favourite.FavouriteIconDao +import com.example.util.simpletimetracker.data_local.favourite.RecordTypeToFavouriteCommentDao import com.example.util.simpletimetracker.data_local.record.RecordDao import com.example.util.simpletimetracker.data_local.recordTag.RecordTagDao import com.example.util.simpletimetracker.data_local.recordTag.RecordToRecordTagDao @@ -148,6 +149,12 @@ class DataLocalModule { return database.favouriteCommentDao() } + @Provides + @Singleton + fun getRecordTypeToFavouriteCommentDao(database: AppDatabase): RecordTypeToFavouriteCommentDao { + return database.recordTypeToFavouriteCommentDao() + } + @Provides @Singleton fun getFavouriteColorDao(database: AppDatabase): FavouriteColorDao { diff --git a/data_local/src/main/java/com/example/util/simpletimetracker/data_local/di/DataLocalModuleBinds.kt b/data_local/src/main/java/com/example/util/simpletimetracker/data_local/di/DataLocalModuleBinds.kt index de65609f8..5c0b6aff8 100644 --- a/data_local/src/main/java/com/example/util/simpletimetracker/data_local/di/DataLocalModuleBinds.kt +++ b/data_local/src/main/java/com/example/util/simpletimetracker/data_local/di/DataLocalModuleBinds.kt @@ -8,6 +8,7 @@ import com.example.util.simpletimetracker.data_local.complexRule.ComplexRuleRepo import com.example.util.simpletimetracker.data_local.favourite.FavouriteColorRepoImpl import com.example.util.simpletimetracker.data_local.favourite.FavouriteCommentRepoImpl import com.example.util.simpletimetracker.data_local.favourite.FavouriteIconRepoImpl +import com.example.util.simpletimetracker.data_local.favourite.RecordTypeToFavouriteCommentRepoImpl import com.example.util.simpletimetracker.data_local.prefs.PrefsRepoImpl import com.example.util.simpletimetracker.data_local.record.RecordRepoImpl import com.example.util.simpletimetracker.data_local.record.RunningRecordRepoImpl @@ -34,6 +35,7 @@ import com.example.util.simpletimetracker.domain.complexRule.repo.ComplexRuleRep import com.example.util.simpletimetracker.domain.favourite.repo.FavouriteColorRepo import com.example.util.simpletimetracker.domain.favourite.repo.FavouriteCommentRepo import com.example.util.simpletimetracker.domain.favourite.repo.FavouriteIconRepo +import com.example.util.simpletimetracker.domain.favourite.repo.RecordTypeToFavouriteCommentRepo import com.example.util.simpletimetracker.domain.prefs.repo.PrefsRepo import com.example.util.simpletimetracker.domain.record.repo.RecordRepo import com.example.util.simpletimetracker.domain.recordTag.repo.RecordTagRepo @@ -144,6 +146,10 @@ interface DataLocalModuleBinds { @Singleton fun bindFavouriteCommentRepo(impl: FavouriteCommentRepoImpl): FavouriteCommentRepo + @Binds + @Singleton + fun bindRecordTagToFavouriteCommentRepo(impl: RecordTypeToFavouriteCommentRepoImpl): RecordTypeToFavouriteCommentRepo + @Binds @Singleton fun bindFavouriteColorRepo(impl: FavouriteColorRepoImpl): FavouriteColorRepo diff --git a/data_local/src/main/java/com/example/util/simpletimetracker/data_local/favourite/RecordTypeToFavouriteCommentDBO.kt b/data_local/src/main/java/com/example/util/simpletimetracker/data_local/favourite/RecordTypeToFavouriteCommentDBO.kt new file mode 100644 index 000000000..8e693831d --- /dev/null +++ b/data_local/src/main/java/com/example/util/simpletimetracker/data_local/favourite/RecordTypeToFavouriteCommentDBO.kt @@ -0,0 +1,12 @@ +package com.example.util.simpletimetracker.data_local.favourite + +import androidx.room.ColumnInfo +import androidx.room.Entity + +@Entity(tableName = "recordTypeToFavouriteComment", primaryKeys = ["record_type_id", "comment_id"]) +data class RecordTypeToFavouriteCommentDBO( + @ColumnInfo(name = "record_type_id") + val recordTypeId: Long, + @ColumnInfo(name = "comment_id") + val commentId: Long, +) \ No newline at end of file diff --git a/data_local/src/main/java/com/example/util/simpletimetracker/data_local/favourite/RecordTypeToFavouriteCommentDao.kt b/data_local/src/main/java/com/example/util/simpletimetracker/data_local/favourite/RecordTypeToFavouriteCommentDao.kt new file mode 100644 index 000000000..4b5b24e13 --- /dev/null +++ b/data_local/src/main/java/com/example/util/simpletimetracker/data_local/favourite/RecordTypeToFavouriteCommentDao.kt @@ -0,0 +1,35 @@ +package com.example.util.simpletimetracker.data_local.favourite + +import androidx.room.Dao +import androidx.room.Delete +import androidx.room.Insert +import androidx.room.OnConflictStrategy +import androidx.room.Query + +@Dao +interface RecordTypeToFavouriteCommentDao { + + @Query("SELECT * FROM recordTypeToFavouriteComment") + suspend fun getAll(): List + + @Query("SELECT comment_id FROM recordTypeToFavouriteComment WHERE record_type_id = :typeId") + suspend fun getCommentIdsByType(typeId: Long): List + + @Query("SELECT record_type_id FROM recordTypeToFavouriteComment WHERE comment_id = :commentId") + suspend fun getTypeIdsByComment(commentId: Long): List + + @Insert(onConflict = OnConflictStrategy.REPLACE) + suspend fun insert(recordTypeToFavouriteCommentDBO: List) + + @Delete + suspend fun delete(recordTypeToFavouriteCommentDBO: List) + + @Query("DELETE FROM recordTypeToFavouriteComment WHERE comment_id = :commentId") + suspend fun deleteAll(commentId: Long) + + @Query("DELETE FROM recordTypeToFavouriteComment WHERE record_type_id = :typeId") + suspend fun deleteAllByType(typeId: Long) + + @Query("DELETE FROM recordTypeToFavouriteComment") + suspend fun clear() +} \ No newline at end of file diff --git a/data_local/src/main/java/com/example/util/simpletimetracker/data_local/favourite/RecordTypeToFavouriteCommentDataLocalMapper.kt b/data_local/src/main/java/com/example/util/simpletimetracker/data_local/favourite/RecordTypeToFavouriteCommentDataLocalMapper.kt new file mode 100644 index 000000000..08f1ea8fd --- /dev/null +++ b/data_local/src/main/java/com/example/util/simpletimetracker/data_local/favourite/RecordTypeToFavouriteCommentDataLocalMapper.kt @@ -0,0 +1,28 @@ +package com.example.util.simpletimetracker.data_local.favourite + +import com.example.util.simpletimetracker.domain.favourite.model.RecordTypeToFavouriteComment +import javax.inject.Inject + +class RecordTypeToFavouriteCommentDataLocalMapper @Inject constructor() { + + fun map(dbo: RecordTypeToFavouriteCommentDBO): RecordTypeToFavouriteComment { + return RecordTypeToFavouriteComment( + recordTypeId = dbo.recordTypeId, + commentId = dbo.commentId, + ) + } + + fun map(typeId: Long, commentId: Long): RecordTypeToFavouriteCommentDBO { + return RecordTypeToFavouriteCommentDBO( + recordTypeId = typeId, + commentId = commentId, + ) + } + + fun map(domain: RecordTypeToFavouriteComment): RecordTypeToFavouriteCommentDBO { + return RecordTypeToFavouriteCommentDBO( + recordTypeId = domain.recordTypeId, + commentId = domain.commentId, + ) + } +} \ No newline at end of file diff --git a/data_local/src/main/java/com/example/util/simpletimetracker/data_local/favourite/RecordTypeToFavouriteCommentRepoImpl.kt b/data_local/src/main/java/com/example/util/simpletimetracker/data_local/favourite/RecordTypeToFavouriteCommentRepoImpl.kt new file mode 100644 index 000000000..21105f075 --- /dev/null +++ b/data_local/src/main/java/com/example/util/simpletimetracker/data_local/favourite/RecordTypeToFavouriteCommentRepoImpl.kt @@ -0,0 +1,96 @@ +package com.example.util.simpletimetracker.data_local.favourite + +import com.example.util.simpletimetracker.data_local.base.withLockedCache +import com.example.util.simpletimetracker.domain.extension.removeIf +import com.example.util.simpletimetracker.domain.favourite.model.RecordTypeToFavouriteComment +import com.example.util.simpletimetracker.domain.favourite.repo.RecordTypeToFavouriteCommentRepo +import kotlinx.coroutines.sync.Mutex +import javax.inject.Inject +import javax.inject.Singleton + +@Singleton +class RecordTypeToFavouriteCommentRepoImpl @Inject constructor( + private val dao: RecordTypeToFavouriteCommentDao, + private val mapper: RecordTypeToFavouriteCommentDataLocalMapper, +) : RecordTypeToFavouriteCommentRepo { + + private var cache: List? = null + private val mutex: Mutex = Mutex() + + override suspend fun getAll(): List = mutex.withLockedCache( + logMessage = "getAll", + accessCache = { cache }, + accessSource = { dao.getAll().map(mapper::map) }, + afterSourceAccess = { cache = it }, + ) + + override suspend fun getCommentIdsByType(typeId: Long): Set = mutex.withLockedCache( + logMessage = "getCommentIdsByType", + accessCache = { cache?.filter { it.recordTypeId == typeId }?.map { it.commentId }?.toSet() }, + accessSource = { dao.getCommentIdsByType(typeId).toSet() }, + ) + + override suspend fun getTypeIdsByComment(commentId: Long): Set = mutex.withLockedCache( + logMessage = "getTypeIdsByComment", + accessCache = { cache?.filter { it.commentId == commentId }?.map { it.recordTypeId }?.toSet() }, + accessSource = { dao.getTypeIdsByComment(commentId).toSet() }, + ) + + override suspend fun add(recordTypeToFavouriteComment: RecordTypeToFavouriteComment) = mutex.withLockedCache( + logMessage = "add", + accessSource = { + recordTypeToFavouriteComment + .let(mapper::map) + .let { dao.insert(listOf(it)) } + }, + afterSourceAccess = { cache = null }, + ) + + override suspend fun addTypes(commentId: Long, typeIds: List) = mutex.withLockedCache( + logMessage = "addTypes", + accessSource = { + typeIds.map { + mapper.map(typeId = it, commentId = commentId) + }.let { dao.insert(it) } + }, + afterSourceAccess = { cache = null }, + ) + + override suspend fun addComments(typeId: Long, commentIds: List) = mutex.withLockedCache( + logMessage = "addComments", + accessSource = { + commentIds.map { + mapper.map(typeId = typeId, commentId = it) + }.let { dao.insert(it) } + }, + afterSourceAccess = { cache = null }, + ) + + override suspend fun removeTypes(commentId: Long, typeIds: List) = mutex.withLockedCache( + logMessage = "removeTypes", + accessSource = { + typeIds.map { + mapper.map(typeId = it, commentId = commentId) + }.let { dao.delete(it) } + }, + afterSourceAccess = { cache = null }, + ) + + override suspend fun removeAll(commentId: Long) = mutex.withLockedCache( + logMessage = "removeAll", + accessSource = { dao.deleteAll(commentId) }, + afterSourceAccess = { cache = cache?.removeIf { it.commentId == commentId } }, + ) + + override suspend fun removeAllByType(typeId: Long) = mutex.withLockedCache( + logMessage = "removeAllByType", + accessSource = { dao.deleteAllByType(typeId) }, + afterSourceAccess = { cache = cache?.removeIf { it.recordTypeId == typeId } }, + ) + + override suspend fun clear() = mutex.withLockedCache( + logMessage = "clear", + accessSource = { dao.clear() }, + afterSourceAccess = { cache = null }, + ) +} \ No newline at end of file diff --git a/domain/src/main/java/com/example/util/simpletimetracker/domain/favourite/interactor/RecordTypeToFavouriteCommentInteractor.kt b/domain/src/main/java/com/example/util/simpletimetracker/domain/favourite/interactor/RecordTypeToFavouriteCommentInteractor.kt new file mode 100644 index 000000000..ad8f8557b --- /dev/null +++ b/domain/src/main/java/com/example/util/simpletimetracker/domain/favourite/interactor/RecordTypeToFavouriteCommentInteractor.kt @@ -0,0 +1,34 @@ +package com.example.util.simpletimetracker.domain.favourite.interactor + +import com.example.util.simpletimetracker.domain.favourite.model.RecordTypeToFavouriteComment +import com.example.util.simpletimetracker.domain.favourite.repo.RecordTypeToFavouriteCommentRepo +import javax.inject.Inject + +class RecordTypeToFavouriteCommentInteractor @Inject constructor( + private val repo: RecordTypeToFavouriteCommentRepo, +) { + + suspend fun getAll(): List { + return repo.getAll() + } + + suspend fun getComments(typeId: Long): Set { + return repo.getCommentIdsByType(typeId) + } + + suspend fun getTypes(commentId: Long): Set { + return repo.getTypeIdsByComment(commentId) + } + + suspend fun addTypes(commentId: Long, typeIds: List) { + repo.addTypes(commentId, typeIds) + } + + suspend fun addComments(typeId: Long, commentIds: List) { + repo.addComments(typeId, commentIds) + } + + suspend fun removeTypes(commentId: Long, typeIds: List) { + repo.removeTypes(commentId, typeIds) + } +} \ No newline at end of file diff --git a/domain/src/main/java/com/example/util/simpletimetracker/domain/favourite/model/RecordTypeToFavouriteComment.kt b/domain/src/main/java/com/example/util/simpletimetracker/domain/favourite/model/RecordTypeToFavouriteComment.kt new file mode 100644 index 000000000..5c9127f89 --- /dev/null +++ b/domain/src/main/java/com/example/util/simpletimetracker/domain/favourite/model/RecordTypeToFavouriteComment.kt @@ -0,0 +1,6 @@ +package com.example.util.simpletimetracker.domain.favourite.model + +data class RecordTypeToFavouriteComment( + val recordTypeId: Long, + val commentId: Long, +) \ No newline at end of file diff --git a/domain/src/main/java/com/example/util/simpletimetracker/domain/favourite/repo/RecordTypeToFavouriteCommentRepo.kt b/domain/src/main/java/com/example/util/simpletimetracker/domain/favourite/repo/RecordTypeToFavouriteCommentRepo.kt new file mode 100644 index 000000000..6222db970 --- /dev/null +++ b/domain/src/main/java/com/example/util/simpletimetracker/domain/favourite/repo/RecordTypeToFavouriteCommentRepo.kt @@ -0,0 +1,26 @@ +package com.example.util.simpletimetracker.domain.favourite.repo + +import com.example.util.simpletimetracker.domain.favourite.model.RecordTypeToFavouriteComment + +interface RecordTypeToFavouriteCommentRepo { + + suspend fun getAll(): List + + suspend fun getCommentIdsByType(typeId: Long): Set + + suspend fun getTypeIdsByComment(commentId: Long): Set + + suspend fun add(recordTypeToFavouriteComment: RecordTypeToFavouriteComment) + + suspend fun addTypes(commentId: Long, typeIds: List) + + suspend fun addComments(typeId: Long, commentIds: List) + + suspend fun removeTypes(commentId: Long, typeIds: List) + + suspend fun removeAll(commentId: Long) + + suspend fun removeAllByType(typeId: Long) + + suspend fun clear() +} \ No newline at end of file From 609a0f179c2d994b73a14487e1f4da0b132e31cc Mon Sep 17 00:00:00 2001 From: Terrance Date: Wed, 18 Mar 2026 20:11:40 +0000 Subject: [PATCH 2/6] Add type to comment database migration --- .../33.json | 997 ++++++++++++++++++ .../data_local/database/AppDatabase.kt | 7 +- .../database/AppDatabaseMigrations.kt | 9 + 3 files changed, 1012 insertions(+), 1 deletion(-) create mode 100644 data_local/schemas/com.example.util.simpletimetracker.data_local.database.AppDatabase/33.json diff --git a/data_local/schemas/com.example.util.simpletimetracker.data_local.database.AppDatabase/33.json b/data_local/schemas/com.example.util.simpletimetracker.data_local.database.AppDatabase/33.json new file mode 100644 index 000000000..a5b405c96 --- /dev/null +++ b/data_local/schemas/com.example.util.simpletimetracker.data_local.database.AppDatabase/33.json @@ -0,0 +1,997 @@ +{ + "formatVersion": 1, + "database": { + "version": 33, + "identityHash": "e1df3e7105f19d9115ffdfc7fd1d124a", + "entities": [ + { + "tableName": "records", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `type_id` INTEGER NOT NULL, `time_started` INTEGER NOT NULL, `time_ended` INTEGER NOT NULL, `comment` TEXT NOT NULL, `tag_id` INTEGER NOT NULL)", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "typeId", + "columnName": "type_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "timeStarted", + "columnName": "time_started", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "timeEnded", + "columnName": "time_ended", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "comment", + "columnName": "comment", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "tagId", + "columnName": "tag_id", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "autoGenerate": true, + "columnNames": [ + "id" + ] + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "recordTypes", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `name` TEXT NOT NULL, `icon` TEXT NOT NULL, `color` INTEGER NOT NULL, `color_int` TEXT NOT NULL, `hidden` INTEGER NOT NULL, `instant` INTEGER NOT NULL, `instantDuration` INTEGER NOT NULL, `note` TEXT NOT NULL)", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "name", + "columnName": "name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "icon", + "columnName": "icon", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "color", + "columnName": "color", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "colorInt", + "columnName": "color_int", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "hidden", + "columnName": "hidden", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "instant", + "columnName": "instant", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "defaultDuration", + "columnName": "instantDuration", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "note", + "columnName": "note", + "affinity": "TEXT", + "notNull": true + } + ], + "primaryKey": { + "autoGenerate": true, + "columnNames": [ + "id" + ] + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "runningRecords", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER NOT NULL, `time_started` INTEGER NOT NULL, `comment` TEXT NOT NULL, `tag_id` INTEGER NOT NULL, PRIMARY KEY(`id`))", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "timeStarted", + "columnName": "time_started", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "comment", + "columnName": "comment", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "tagId", + "columnName": "tag_id", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "autoGenerate": false, + "columnNames": [ + "id" + ] + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "categories", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `name` TEXT NOT NULL, `color` INTEGER NOT NULL, `color_int` TEXT NOT NULL, `note` TEXT NOT NULL)", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "name", + "columnName": "name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "color", + "columnName": "color", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "colorInt", + "columnName": "color_int", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "note", + "columnName": "note", + "affinity": "TEXT", + "notNull": true + } + ], + "primaryKey": { + "autoGenerate": true, + "columnNames": [ + "id" + ] + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "recordTypeCategory", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`record_type_id` INTEGER NOT NULL, `category_id` INTEGER NOT NULL, PRIMARY KEY(`record_type_id`, `category_id`))", + "fields": [ + { + "fieldPath": "recordTypeId", + "columnName": "record_type_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "categoryId", + "columnName": "category_id", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "autoGenerate": false, + "columnNames": [ + "record_type_id", + "category_id" + ] + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "recordTags", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `type_id` INTEGER NOT NULL, `name` TEXT NOT NULL, `icon` TEXT NOT NULL, `color` INTEGER NOT NULL, `color_int` TEXT NOT NULL, `icon_color_source` INTEGER NOT NULL, `archived` INTEGER NOT NULL, `note` TEXT NOT NULL, `value_type` INTEGER, `value_suffix` TEXT)", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "typeId", + "columnName": "type_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "name", + "columnName": "name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "icon", + "columnName": "icon", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "color", + "columnName": "color", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "colorInt", + "columnName": "color_int", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "iconColorSource", + "columnName": "icon_color_source", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "archived", + "columnName": "archived", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "note", + "columnName": "note", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "valueType", + "columnName": "value_type", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "valueSuffix", + "columnName": "value_suffix", + "affinity": "TEXT", + "notNull": false + } + ], + "primaryKey": { + "autoGenerate": true, + "columnNames": [ + "id" + ] + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "recordToRecordTag", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`record_id` INTEGER NOT NULL, `record_tag_id` INTEGER NOT NULL, `record_tag_numeric_value` REAL, PRIMARY KEY(`record_id`, `record_tag_id`))", + "fields": [ + { + "fieldPath": "recordId", + "columnName": "record_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "recordTagId", + "columnName": "record_tag_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "recordTagNumericValue", + "columnName": "record_tag_numeric_value", + "affinity": "REAL", + "notNull": false + } + ], + "primaryKey": { + "autoGenerate": false, + "columnNames": [ + "record_id", + "record_tag_id" + ] + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "runningRecordToRecordTag", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`running_record_id` INTEGER NOT NULL, `record_tag_id` INTEGER NOT NULL, `record_tag_numeric_value` REAL, PRIMARY KEY(`running_record_id`, `record_tag_id`))", + "fields": [ + { + "fieldPath": "runningRecordId", + "columnName": "running_record_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "recordTagId", + "columnName": "record_tag_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "recordTagNumericValue", + "columnName": "record_tag_numeric_value", + "affinity": "REAL", + "notNull": false + } + ], + "primaryKey": { + "autoGenerate": false, + "columnNames": [ + "running_record_id", + "record_tag_id" + ] + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "recordShortcutToRecordTag", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`shortcut_id` INTEGER NOT NULL, `record_tag_id` INTEGER NOT NULL, `record_tag_numeric_value` REAL, PRIMARY KEY(`shortcut_id`, `record_tag_id`))", + "fields": [ + { + "fieldPath": "shortcutId", + "columnName": "shortcut_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "recordTagId", + "columnName": "record_tag_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "recordTagNumericValue", + "columnName": "record_tag_numeric_value", + "affinity": "REAL", + "notNull": false + } + ], + "primaryKey": { + "autoGenerate": false, + "columnNames": [ + "shortcut_id", + "record_tag_id" + ] + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "recordTypeToTag", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`record_type_id` INTEGER NOT NULL, `record_tag_id` INTEGER NOT NULL, PRIMARY KEY(`record_type_id`, `record_tag_id`))", + "fields": [ + { + "fieldPath": "recordTypeId", + "columnName": "record_type_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "tagId", + "columnName": "record_tag_id", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "autoGenerate": false, + "columnNames": [ + "record_type_id", + "record_tag_id" + ] + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "recordTypeToDefaultTag", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`record_type_id` INTEGER NOT NULL, `record_tag_id` INTEGER NOT NULL, PRIMARY KEY(`record_type_id`, `record_tag_id`))", + "fields": [ + { + "fieldPath": "recordTypeId", + "columnName": "record_type_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "tagId", + "columnName": "record_tag_id", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "autoGenerate": false, + "columnNames": [ + "record_type_id", + "record_tag_id" + ] + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "activityFilters", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `selectedIds` TEXT NOT NULL, `type` INTEGER NOT NULL, `name` TEXT NOT NULL, `color` INTEGER NOT NULL, `color_int` TEXT NOT NULL, `selected` INTEGER NOT NULL)", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "selectedIds", + "columnName": "selectedIds", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "type", + "columnName": "type", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "name", + "columnName": "name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "color", + "columnName": "color", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "colorInt", + "columnName": "color_int", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "selected", + "columnName": "selected", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "autoGenerate": true, + "columnNames": [ + "id" + ] + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "favouriteComments", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `comment` TEXT NOT NULL)", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "comment", + "columnName": "comment", + "affinity": "TEXT", + "notNull": true + } + ], + "primaryKey": { + "autoGenerate": true, + "columnNames": [ + "id" + ] + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "recordTypeToFavouriteComment", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`record_type_id` INTEGER NOT NULL, `comment_id` INTEGER NOT NULL, PRIMARY KEY(`record_type_id`, `comment_id`))", + "fields": [ + { + "fieldPath": "recordTypeId", + "columnName": "record_type_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "commentId", + "columnName": "comment_id", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "autoGenerate": false, + "columnNames": [ + "record_type_id", + "comment_id" + ] + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "recordTypeGoals", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `owner_id` INTEGER NOT NULL, `owner_type` INTEGER NOT NULL, `range` INTEGER NOT NULL, `type` INTEGER NOT NULL, `goalType` INTEGER NOT NULL, `value` INTEGER NOT NULL, `days_of_week` TEXT NOT NULL)", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "ownerId", + "columnName": "owner_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "ownerType", + "columnName": "owner_type", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "range", + "columnName": "range", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "type", + "columnName": "type", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "subType", + "columnName": "goalType", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "value", + "columnName": "value", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "daysOfWeek", + "columnName": "days_of_week", + "affinity": "TEXT", + "notNull": true + } + ], + "primaryKey": { + "autoGenerate": true, + "columnNames": [ + "id" + ] + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "favouriteIcons", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `icon` TEXT NOT NULL)", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "icon", + "columnName": "icon", + "affinity": "TEXT", + "notNull": true + } + ], + "primaryKey": { + "autoGenerate": true, + "columnNames": [ + "id" + ] + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "complexRules", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `disabled` INTEGER NOT NULL, `actionType` INTEGER NOT NULL, `actionDisallowOnlyPrevious` INTEGER NOT NULL, `actionSetTagIds` TEXT NOT NULL, `actionSetTagValues` TEXT NOT NULL, `conditionStartingTypeIds` TEXT NOT NULL, `conditionCurrentTypeIds` TEXT NOT NULL, `conditionDaysOfWeek` TEXT NOT NULL)", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "disabled", + "columnName": "disabled", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "action", + "columnName": "actionType", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "actionDisallowOnlyPrevious", + "columnName": "actionDisallowOnlyPrevious", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "actionSetTagIds", + "columnName": "actionSetTagIds", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "actionSetTagValues", + "columnName": "actionSetTagValues", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "conditionStartingTypeIds", + "columnName": "conditionStartingTypeIds", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "conditionCurrentTypeIds", + "columnName": "conditionCurrentTypeIds", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "conditionDaysOfWeek", + "columnName": "conditionDaysOfWeek", + "affinity": "TEXT", + "notNull": true + } + ], + "primaryKey": { + "autoGenerate": true, + "columnNames": [ + "id" + ] + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "favouriteColors", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `color_int` TEXT NOT NULL)", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "colorInt", + "columnName": "color_int", + "affinity": "TEXT", + "notNull": true + } + ], + "primaryKey": { + "autoGenerate": true, + "columnNames": [ + "id" + ] + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "activitySuggestion", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `forTypeId` INTEGER NOT NULL, `suggestionIds` TEXT NOT NULL)", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "forTypeId", + "columnName": "forTypeId", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "suggestionIds", + "columnName": "suggestionIds", + "affinity": "TEXT", + "notNull": true + } + ], + "primaryKey": { + "autoGenerate": true, + "columnNames": [ + "id" + ] + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "durationSuggestions", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `valueSeconds` INTEGER NOT NULL)", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "valueSeconds", + "columnName": "valueSeconds", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "autoGenerate": true, + "columnNames": [ + "id" + ] + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "recordShortcuts", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `type_id` INTEGER NOT NULL, `comment` TEXT NOT NULL)", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "typeId", + "columnName": "type_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "comment", + "columnName": "comment", + "affinity": "TEXT", + "notNull": true + } + ], + "primaryKey": { + "autoGenerate": true, + "columnNames": [ + "id" + ] + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "favouriteRecordFilters", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL)", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "autoGenerate": true, + "columnNames": [ + "id" + ] + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "favouriteRecordFilter", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `owner_id` INTEGER NOT NULL, `type` INTEGER NOT NULL, `common_items_ids` TEXT, `comment_items_ids` TEXT, `comment_items_text` TEXT, `duplication_items_ids` TEXT, `manually_filtered_items_ids` TEXT, `daysOfWeek` TEXT, `range_time_started` INTEGER, `range_time_ended` INTEGER, `range_length_type` INTEGER, `range_length_last_days` INTEGER, `range_length_position` INTEGER, `range_length_custom_range_time_started` INTEGER, `range_length_custom_range_time_ended` INTEGER, FOREIGN KEY(`owner_id`) REFERENCES `favouriteRecordFilters`(`id`) ON UPDATE NO ACTION ON DELETE CASCADE )", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "ownerId", + "columnName": "owner_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "type", + "columnName": "type", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "commonItemsIds", + "columnName": "common_items_ids", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "commentItemsIds", + "columnName": "comment_items_ids", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "commentItemsText", + "columnName": "comment_items_text", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "duplicationItemsIds", + "columnName": "duplication_items_ids", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "manuallyFilteredItemsIds", + "columnName": "manually_filtered_items_ids", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "daysOfWeek", + "columnName": "daysOfWeek", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "range.rangeTimeStarted", + "columnName": "range_time_started", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "range.rangeTimeEnded", + "columnName": "range_time_ended", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "rangeLength.rangeType", + "columnName": "range_length_type", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "rangeLength.lastDays", + "columnName": "range_length_last_days", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "rangeLength.position", + "columnName": "range_length_position", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "rangeLength.customRange.rangeTimeStarted", + "columnName": "range_length_custom_range_time_started", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "rangeLength.customRange.rangeTimeEnded", + "columnName": "range_length_custom_range_time_ended", + "affinity": "INTEGER", + "notNull": false + } + ], + "primaryKey": { + "autoGenerate": true, + "columnNames": [ + "id" + ] + }, + "indices": [], + "foreignKeys": [ + { + "table": "favouriteRecordFilters", + "onDelete": "CASCADE", + "onUpdate": "NO ACTION", + "columns": [ + "owner_id" + ], + "referencedColumns": [ + "id" + ] + } + ] + } + ], + "views": [], + "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, 'e1df3e7105f19d9115ffdfc7fd1d124a')" + ] + } +} \ No newline at end of file diff --git a/data_local/src/main/java/com/example/util/simpletimetracker/data_local/database/AppDatabase.kt b/data_local/src/main/java/com/example/util/simpletimetracker/data_local/database/AppDatabase.kt index 504d19e7d..1394fd058 100644 --- a/data_local/src/main/java/com/example/util/simpletimetracker/data_local/database/AppDatabase.kt +++ b/data_local/src/main/java/com/example/util/simpletimetracker/data_local/database/AppDatabase.kt @@ -20,6 +20,8 @@ import com.example.util.simpletimetracker.data_local.favourite.FavouriteCommentD import com.example.util.simpletimetracker.data_local.favourite.FavouriteCommentDao import com.example.util.simpletimetracker.data_local.favourite.FavouriteIconDBO import com.example.util.simpletimetracker.data_local.favourite.FavouriteIconDao +import com.example.util.simpletimetracker.data_local.favourite.RecordTypeToFavouriteCommentDBO +import com.example.util.simpletimetracker.data_local.favourite.RecordTypeToFavouriteCommentDao import com.example.util.simpletimetracker.data_local.record.RecordDBO import com.example.util.simpletimetracker.data_local.record.RecordDao import com.example.util.simpletimetracker.data_local.record.RunningRecordDBO @@ -60,6 +62,7 @@ import com.example.util.simpletimetracker.data_local.recordsFilter.FavouriteReco RecordTypeToDefaultTagDBO::class, ActivityFilterDBO::class, FavouriteCommentDBO::class, + RecordTypeToFavouriteCommentDBO::class, RecordTypeGoalDBO::class, FavouriteIconDBO::class, ComplexRuleDBO::class, @@ -70,7 +73,7 @@ import com.example.util.simpletimetracker.data_local.recordsFilter.FavouriteReco FavouriteRecordsFilterDBO.MainDBO::class, FavouriteRecordsFilterDBO.FilterDBO::class, ], - version = 32, + version = 33, exportSchema = true, ) abstract class AppDatabase : RoomDatabase() { @@ -103,6 +106,8 @@ abstract class AppDatabase : RoomDatabase() { abstract fun favouriteCommentDao(): FavouriteCommentDao + abstract fun recordTypeToFavouriteCommentDao(): RecordTypeToFavouriteCommentDao + abstract fun recordTypeGoalDao(): RecordTypeGoalDao abstract fun favouriteIconDao(): FavouriteIconDao diff --git a/data_local/src/main/java/com/example/util/simpletimetracker/data_local/database/AppDatabaseMigrations.kt b/data_local/src/main/java/com/example/util/simpletimetracker/data_local/database/AppDatabaseMigrations.kt index 60e36363c..a81304330 100644 --- a/data_local/src/main/java/com/example/util/simpletimetracker/data_local/database/AppDatabaseMigrations.kt +++ b/data_local/src/main/java/com/example/util/simpletimetracker/data_local/database/AppDatabaseMigrations.kt @@ -40,6 +40,7 @@ class AppDatabaseMigrations { migration_29_30, migration_30_31, migration_31_32, + migration_32_33, ) private val migration_1_2 = object : Migration(1, 2) { @@ -407,5 +408,13 @@ class AppDatabaseMigrations { ) } } + + private val migration_32_33 = object : Migration(32, 33) { + override fun migrate(database: SupportSQLiteDatabase) { + database.execSQL( + "CREATE TABLE IF NOT EXISTS `recordTypeToFavouriteComment` (`record_type_id` INTEGER NOT NULL, `comment_id` INTEGER NOT NULL, PRIMARY KEY(`record_type_id`, `comment_id`))", + ) + } + } } } \ No newline at end of file From 5e502b2d0c12aff66c81d971ee281d31ad7650a0 Mon Sep 17 00:00:00 2001 From: Terrance Date: Wed, 18 Mar 2026 21:12:03 +0000 Subject: [PATCH 3/6] Handle activity-linked favourites in hints --- .../RecordCommentSearchViewDataInteractor.kt | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/core/src/main/java/com/example/util/simpletimetracker/core/interactor/RecordCommentSearchViewDataInteractor.kt b/core/src/main/java/com/example/util/simpletimetracker/core/interactor/RecordCommentSearchViewDataInteractor.kt index 44d884bf9..0f348465e 100644 --- a/core/src/main/java/com/example/util/simpletimetracker/core/interactor/RecordCommentSearchViewDataInteractor.kt +++ b/core/src/main/java/com/example/util/simpletimetracker/core/interactor/RecordCommentSearchViewDataInteractor.kt @@ -6,6 +6,7 @@ import com.example.util.simpletimetracker.core.repo.ResourceRepo import com.example.util.simpletimetracker.core.viewData.CommentFilterTypeViewData import com.example.util.simpletimetracker.domain.base.CommentFilterType import com.example.util.simpletimetracker.domain.favourite.interactor.FavouriteCommentInteractor +import com.example.util.simpletimetracker.domain.favourite.interactor.RecordTypeToFavouriteCommentInteractor import com.example.util.simpletimetracker.domain.prefs.interactor.PrefsInteractor import com.example.util.simpletimetracker.domain.record.interactor.RecordInteractor import com.example.util.simpletimetracker.domain.record.interactor.RunningRecordInteractor @@ -24,6 +25,7 @@ class RecordCommentSearchViewDataInteractor @Inject constructor( private val prefsInteractor: PrefsInteractor, private val recordInteractor: RecordInteractor, private val favouriteCommentInteractor: FavouriteCommentInteractor, + private val recordTypeToFavouriteCommentInteractor: RecordTypeToFavouriteCommentInteractor, private val runningRecordInteractor: RunningRecordInteractor, ) { @@ -35,7 +37,7 @@ class RecordCommentSearchViewDataInteractor @Inject constructor( val result = mutableListOf() val similar = getSimilarData(comment) - val favourite = getFavouriteData() + val favourite = getFavouriteData(typeId) val last = getLastCommentsData(typeId) val filters = getFilters( @@ -121,8 +123,15 @@ class RecordCommentSearchViewDataInteractor @Inject constructor( } } - private suspend fun getFavouriteData(): List { + private suspend fun getFavouriteData( + typeId: Long, + ): List { + val (included, excluded) = recordTypeToFavouriteCommentInteractor.getAll() + .partition { it.recordTypeId == typeId } + val includedComments = included.map { it.commentId } + val excludedComments = excluded.map { it.commentId } return favouriteCommentInteractor.getAll() + .filter { includedComments.contains(it.id) || !excludedComments.contains(it.id) } .map { RecordCommentViewData.Favourite(it.comment) } } From 28ecf1ce0e0b16454e432eb059224f2ca881bc8e Mon Sep 17 00:00:00 2001 From: Terrance Date: Wed, 18 Mar 2026 23:20:03 +0000 Subject: [PATCH 4/6] Toggle type to comment relation with favourite button --- ...ChangeRecordCommentFieldAdapterDelegate.kt | 3 +++ .../ChangeRecordViewDataInteractor.kt | 15 ++++++++++++- .../view/ChangeRecordCore.kt | 1 + .../viewModel/ChangeRecordBaseViewModel.kt | 22 +++++++++++++++++++ .../viewModel/ChangeRecordViewModel.kt | 3 +++ .../viewModel/ChangeRunningRecordViewModel.kt | 3 +++ 6 files changed, 46 insertions(+), 1 deletion(-) diff --git a/features/feature_change_record/src/main/java/com/example/util/simpletimetracker/feature_change_record/adapter/ChangeRecordCommentFieldAdapterDelegate.kt b/features/feature_change_record/src/main/java/com/example/util/simpletimetracker/feature_change_record/adapter/ChangeRecordCommentFieldAdapterDelegate.kt index 6d14e3319..950bca9b7 100644 --- a/features/feature_change_record/src/main/java/com/example/util/simpletimetracker/feature_change_record/adapter/ChangeRecordCommentFieldAdapterDelegate.kt +++ b/features/feature_change_record/src/main/java/com/example/util/simpletimetracker/feature_change_record/adapter/ChangeRecordCommentFieldAdapterDelegate.kt @@ -8,12 +8,14 @@ import androidx.core.widget.doAfterTextChanged import com.example.util.simpletimetracker.feature_base_adapter.ViewHolderType import com.example.util.simpletimetracker.feature_base_adapter.createRecyclerBindingAdapterDelegate import com.example.util.simpletimetracker.feature_views.extension.setOnClick +import com.example.util.simpletimetracker.feature_views.extension.setOnLongClick import com.example.util.simpletimetracker.feature_change_record.adapter.ChangeRecordCommentFieldViewData as ViewData import com.example.util.simpletimetracker.feature_change_record.databinding.ChangeRecordCommentFieldItemBinding as Binding fun createChangeRecordCommentFieldAdapterDelegate( afterTextChange: (String) -> Unit, onFavouriteClick: () -> Unit, + onFavouriteLongClick: () -> Unit, ) = createRecyclerBindingAdapterDelegate( Binding::inflate, ) { binding, item, _ -> @@ -32,6 +34,7 @@ fun createChangeRecordCommentFieldAdapterDelegate( ColorStateList.valueOf(item.iconColor), ) btnChangeRecordFavouriteComment.setOnClick { onFavouriteClick() } + btnChangeRecordFavouriteComment.setOnLongClick { onFavouriteLongClick() } etChangeRecordCommentField.removeTextChangedListener(textWatcher) textWatcher = etChangeRecordCommentField.doAfterTextChanged { afterTextChange(it.toString()) } diff --git a/features/feature_change_record/src/main/java/com/example/util/simpletimetracker/feature_change_record/interactor/ChangeRecordViewDataInteractor.kt b/features/feature_change_record/src/main/java/com/example/util/simpletimetracker/feature_change_record/interactor/ChangeRecordViewDataInteractor.kt index 1820e5a45..caf6519f2 100644 --- a/features/feature_change_record/src/main/java/com/example/util/simpletimetracker/feature_change_record/interactor/ChangeRecordViewDataInteractor.kt +++ b/features/feature_change_record/src/main/java/com/example/util/simpletimetracker/feature_change_record/interactor/ChangeRecordViewDataInteractor.kt @@ -6,6 +6,7 @@ import com.example.util.simpletimetracker.core.mapper.TimeMapper import com.example.util.simpletimetracker.core.repo.ResourceRepo import com.example.util.simpletimetracker.core.view.timeAdjustment.TimeAdjustmentView import com.example.util.simpletimetracker.domain.favourite.interactor.FavouriteCommentInteractor +import com.example.util.simpletimetracker.domain.favourite.interactor.RecordTypeToFavouriteCommentInteractor import com.example.util.simpletimetracker.domain.prefs.interactor.PrefsInteractor import com.example.util.simpletimetracker.domain.record.model.Record import com.example.util.simpletimetracker.domain.recordTag.interactor.RecordTagInteractor @@ -23,6 +24,7 @@ class ChangeRecordViewDataInteractor @Inject constructor( private val recordTypeInteractor: RecordTypeInteractor, private val recordTagInteractor: RecordTagInteractor, private val favouriteCommentInteractor: FavouriteCommentInteractor, + private val recordTypeToFavouriteCommentInteractor: RecordTypeToFavouriteCommentInteractor, private val changeRecordViewDataMapper: ChangeRecordViewDataMapper, private val resourceRepo: ResourceRepo, private val timeMapper: TimeMapper, @@ -62,7 +64,18 @@ class ChangeRecordViewDataInteractor @Inject constructor( ): List { val items = mutableListOf() val isDarkTheme = prefsInteractor.getDarkMode() - val isFavourite = favouriteCommentInteractor.get(comment) != null + + val favouriteComment = favouriteCommentInteractor.get(comment) + val isFavourite = if (favouriteComment == null) false else { + val (included, excluded) = recordTypeToFavouriteCommentInteractor.getAll() + .partition { it.recordTypeId == typeId } + val includedComments = included.map { it.commentId } + val excludedComments = excluded.map { it.commentId } + ( + includedComments.contains(favouriteComment.id) || + !excludedComments.contains(favouriteComment.id) + ) + } ChangeRecordCommentFieldViewData( // Only one at the time. diff --git a/features/feature_change_record/src/main/java/com/example/util/simpletimetracker/feature_change_record/view/ChangeRecordCore.kt b/features/feature_change_record/src/main/java/com/example/util/simpletimetracker/feature_change_record/view/ChangeRecordCore.kt index 2f8e80069..b67efc550 100644 --- a/features/feature_change_record/src/main/java/com/example/util/simpletimetracker/feature_change_record/view/ChangeRecordCore.kt +++ b/features/feature_change_record/src/main/java/com/example/util/simpletimetracker/feature_change_record/view/ChangeRecordCore.kt @@ -98,6 +98,7 @@ class ChangeRecordCore( createChangeRecordCommentFieldAdapterDelegate( afterTextChange = viewModel::onCommentChange, onFavouriteClick = viewModel::onFavouriteCommentClick, + onFavouriteLongClick = viewModel::onFavouriteCommentLongClick, ), createRecordCommentAdapterDelegate( onItemClick = viewModel::onCommentClick, diff --git a/features/feature_change_record/src/main/java/com/example/util/simpletimetracker/feature_change_record/viewModel/ChangeRecordBaseViewModel.kt b/features/feature_change_record/src/main/java/com/example/util/simpletimetracker/feature_change_record/viewModel/ChangeRecordBaseViewModel.kt index fc9901363..4ed13dca1 100644 --- a/features/feature_change_record/src/main/java/com/example/util/simpletimetracker/feature_change_record/viewModel/ChangeRecordBaseViewModel.kt +++ b/features/feature_change_record/src/main/java/com/example/util/simpletimetracker/feature_change_record/viewModel/ChangeRecordBaseViewModel.kt @@ -16,7 +16,9 @@ import com.example.util.simpletimetracker.domain.extension.addOrRemove import com.example.util.simpletimetracker.domain.extension.dropSeconds import com.example.util.simpletimetracker.domain.extension.orFalse import com.example.util.simpletimetracker.domain.favourite.interactor.FavouriteCommentInteractor +import com.example.util.simpletimetracker.domain.favourite.interactor.RecordTypeToFavouriteCommentInteractor import com.example.util.simpletimetracker.domain.favourite.model.FavouriteComment +import com.example.util.simpletimetracker.domain.favourite.model.RecordTypeToFavouriteComment import com.example.util.simpletimetracker.domain.prefs.interactor.PrefsInteractor import com.example.util.simpletimetracker.domain.record.interactor.RecordInteractor import com.example.util.simpletimetracker.domain.record.model.Record @@ -70,6 +72,7 @@ abstract class ChangeRecordBaseViewModel( private val recordTagInteractor: RecordTagInteractor, private val recordTypeToTagInteractor: RecordTypeToTagInteractor, private val favouriteCommentInteractor: FavouriteCommentInteractor, + private val recordTypeToFavouriteCommentInteractor: RecordTypeToFavouriteCommentInteractor, private val changeRecordActionsDelegate: ChangeRecordActionsDelegateImpl, private val needTagValueSelectionInteractor: NeedTagValueSelectionInteractor, private val recordCommentSearchViewDataInteractor: RecordCommentSearchViewDataInteractor, @@ -428,6 +431,25 @@ abstract class ChangeRecordBaseViewModel( } } + fun onFavouriteCommentLongClick() { + if (newComment.isEmpty()) return + + viewModelScope.launch { + val favouriteCommentId = favouriteCommentInteractor.get(newComment)?.id + ?: run { + val new = FavouriteComment(comment = newComment) + favouriteCommentInteractor.add(new) + } + val recordTypes = recordTypeToFavouriteCommentInteractor.getTypes(favouriteCommentId) + if (recordTypes.contains(newTypeId)) { + recordTypeToFavouriteCommentInteractor.removeTypes(favouriteCommentId, listOf(newTypeId)) + } else { + recordTypeToFavouriteCommentInteractor.addTypes(favouriteCommentId, listOf(newTypeId)) + } + updateCommentsViewData() + } + } + fun onDateTimeSet(timestamp: Long, tag: String?) { viewModelScope.launch { val coercedTimestamp = if (prefsInteractor.getShowSeconds()) { diff --git a/features/feature_change_record/src/main/java/com/example/util/simpletimetracker/feature_change_record/viewModel/ChangeRecordViewModel.kt b/features/feature_change_record/src/main/java/com/example/util/simpletimetracker/feature_change_record/viewModel/ChangeRecordViewModel.kt index e230f106f..2e693da09 100644 --- a/features/feature_change_record/src/main/java/com/example/util/simpletimetracker/feature_change_record/viewModel/ChangeRecordViewModel.kt +++ b/features/feature_change_record/src/main/java/com/example/util/simpletimetracker/feature_change_record/viewModel/ChangeRecordViewModel.kt @@ -15,6 +15,7 @@ import com.example.util.simpletimetracker.domain.base.UNTRACKED_ITEM_ID import com.example.util.simpletimetracker.domain.extension.orZero import com.example.util.simpletimetracker.domain.record.interactor.AddRecordMediator import com.example.util.simpletimetracker.domain.favourite.interactor.FavouriteCommentInteractor +import com.example.util.simpletimetracker.domain.favourite.interactor.RecordTypeToFavouriteCommentInteractor import com.example.util.simpletimetracker.domain.prefs.interactor.PrefsInteractor import com.example.util.simpletimetracker.domain.record.interactor.RecordInteractor import com.example.util.simpletimetracker.domain.recordTag.interactor.RecordTypeToTagInteractor @@ -47,6 +48,7 @@ class ChangeRecordViewModel @Inject constructor( recordTagInteractor: RecordTagInteractor, recordTypeToTagInteractor: RecordTypeToTagInteractor, favouriteCommentInteractor: FavouriteCommentInteractor, + recordTypeToFavouriteCommentInteractor: RecordTypeToFavouriteCommentInteractor, needTagValueSelectionInteractor: NeedTagValueSelectionInteractor, recordCommentSearchViewDataInteractor: RecordCommentSearchViewDataInteractor, private val prefsInteractor: PrefsInteractor, @@ -69,6 +71,7 @@ class ChangeRecordViewModel @Inject constructor( recordTagInteractor = recordTagInteractor, recordTypeToTagInteractor = recordTypeToTagInteractor, favouriteCommentInteractor = favouriteCommentInteractor, + recordTypeToFavouriteCommentInteractor = recordTypeToFavouriteCommentInteractor, changeRecordActionsDelegate = changeRecordActionsDelegate, needTagValueSelectionInteractor = needTagValueSelectionInteractor, recordCommentSearchViewDataInteractor = recordCommentSearchViewDataInteractor, diff --git a/features/feature_change_running_record/src/main/java/com/example/util/simpletimetracker/feature_change_running_record/viewModel/ChangeRunningRecordViewModel.kt b/features/feature_change_running_record/src/main/java/com/example/util/simpletimetracker/feature_change_running_record/viewModel/ChangeRunningRecordViewModel.kt index 75992498e..edfa70403 100644 --- a/features/feature_change_running_record/src/main/java/com/example/util/simpletimetracker/feature_change_running_record/viewModel/ChangeRunningRecordViewModel.kt +++ b/features/feature_change_running_record/src/main/java/com/example/util/simpletimetracker/feature_change_running_record/viewModel/ChangeRunningRecordViewModel.kt @@ -13,6 +13,7 @@ import com.example.util.simpletimetracker.core.repo.ResourceRepo import com.example.util.simpletimetracker.domain.extension.orZero import com.example.util.simpletimetracker.domain.record.interactor.AddRunningRecordMediator import com.example.util.simpletimetracker.domain.favourite.interactor.FavouriteCommentInteractor +import com.example.util.simpletimetracker.domain.favourite.interactor.RecordTypeToFavouriteCommentInteractor import com.example.util.simpletimetracker.domain.prefs.interactor.PrefsInteractor import com.example.util.simpletimetracker.domain.record.interactor.RecordInteractor import com.example.util.simpletimetracker.domain.recordTag.interactor.RecordTypeToTagInteractor @@ -57,6 +58,7 @@ class ChangeRunningRecordViewModel @Inject constructor( recordTagInteractor: RecordTagInteractor, recordTypeToTagInteractor: RecordTypeToTagInteractor, favouriteCommentInteractor: FavouriteCommentInteractor, + recordTypeToFavouriteCommentInteractor: RecordTypeToFavouriteCommentInteractor, snackBarMessageNavigationInteractor: SnackBarMessageNavigationInteractor, needTagValueSelectionInteractor: NeedTagValueSelectionInteractor, recordCommentSearchViewDataInteractor: RecordCommentSearchViewDataInteractor, @@ -81,6 +83,7 @@ class ChangeRunningRecordViewModel @Inject constructor( recordTagInteractor = recordTagInteractor, recordTypeToTagInteractor = recordTypeToTagInteractor, favouriteCommentInteractor = favouriteCommentInteractor, + recordTypeToFavouriteCommentInteractor = recordTypeToFavouriteCommentInteractor, changeRecordActionsDelegate = changeRecordActionsDelegate, needTagValueSelectionInteractor = needTagValueSelectionInteractor, recordCommentSearchViewDataInteractor = recordCommentSearchViewDataInteractor, From 53383835ea7f35f5d29a9aeba7d19ba7863bbad8 Mon Sep 17 00:00:00 2001 From: Terrance Date: Wed, 18 Mar 2026 23:44:34 +0000 Subject: [PATCH 5/6] Add type to comment to backup/restore --- .../backup/BackupPartialRepoImpl.kt | 35 ++++++++++++++++++- .../data_local/backup/BackupRepoImpl.kt | 30 ++++++++++++++++ .../backup/model/PartialBackupRestoreData.kt | 2 ++ .../viewModel/PartialRestoreViewModel.kt | 1 + 4 files changed, 67 insertions(+), 1 deletion(-) diff --git a/data_local/src/main/java/com/example/util/simpletimetracker/data_local/backup/BackupPartialRepoImpl.kt b/data_local/src/main/java/com/example/util/simpletimetracker/data_local/backup/BackupPartialRepoImpl.kt index 360e4ff1e..792459126 100644 --- a/data_local/src/main/java/com/example/util/simpletimetracker/data_local/backup/BackupPartialRepoImpl.kt +++ b/data_local/src/main/java/com/example/util/simpletimetracker/data_local/backup/BackupPartialRepoImpl.kt @@ -42,6 +42,8 @@ import com.example.util.simpletimetracker.domain.recordTag.repo.RecordTypeToTagR import com.example.util.simpletimetracker.domain.backup.repo.BackupPartialRepo import com.example.util.simpletimetracker.domain.backup.model.ResultCode import com.example.util.simpletimetracker.domain.category.model.Category +import com.example.util.simpletimetracker.domain.favourite.model.RecordTypeToFavouriteComment +import com.example.util.simpletimetracker.domain.favourite.repo.RecordTypeToFavouriteCommentRepo import com.example.util.simpletimetracker.domain.record.model.RecordBase import com.example.util.simpletimetracker.domain.recordShortcut.model.RecordShortcut import com.example.util.simpletimetracker.domain.recordShortcut.repo.RecordShortcutRepo @@ -65,6 +67,7 @@ class BackupPartialRepoImpl @Inject constructor( private val activityFilterRepo: ActivityFilterRepo, private val activitySuggestionRepo: ActivitySuggestionRepo, private val favouriteCommentRepo: FavouriteCommentRepo, + private val recordTypeToFavouriteCommentRepo: RecordTypeToFavouriteCommentRepo, private val favouriteColorRepo: FavouriteColorRepo, private val favouriteIconRepo: FavouriteIconRepo, private val recordTypeGoalRepo: RecordTypeGoalRepo, @@ -88,6 +91,8 @@ class BackupPartialRepoImpl @Inject constructor( .values.getExistingValues().associate { it.id to it.id }.toMutableMap() val originalRecordShortcutIdToAddedId: MutableMap = params.data.recordShortcuts .values.getExistingValues().associate { it.id to it.id }.toMutableMap() + val originalFavouriteCommentIdToAddedId: MutableMap = params.data.favouriteComments + .values.getExistingValues().associate { it.id to it.id }.toMutableMap() params.data.types.values.getNotExistingValues().forEach { type -> val originalId = type.id @@ -196,6 +201,16 @@ class BackupPartialRepoImpl @Inject constructor( id = 0, ).let { favouriteCommentRepo.add(it) } } + params.data.typeToFavouriteComment.getNotExistingValues().forEach { typeToComment -> + val newTypeId = originalTypeIdToAddedId[typeToComment.recordTypeId] + ?: return@forEach + val newCommentId = originalFavouriteCommentIdToAddedId[typeToComment.commentId] + ?: return@forEach + typeToComment.copy( + recordTypeId = newTypeId, + commentId = newCommentId, + ).let { recordTypeToFavouriteCommentRepo.add(it) } + } params.data.favouriteColors.values.getNotExistingValues().forEach { favColor -> favColor.copy( id = 0, @@ -295,6 +310,8 @@ class BackupPartialRepoImpl @Inject constructor( val activityFiltersCurrent: List = activityFilterRepo.getAll() val favouriteComments: MutableList = mutableListOf() val favouriteCommentsCurrent: List = favouriteCommentRepo.getAll() + val typeToFavouriteComment: MutableList = mutableListOf() + val typeToFavouriteCommentCurrent: List = recordTypeToFavouriteCommentRepo.getAll() val favouriteColors: MutableList = mutableListOf() val favouriteColorsCurrent: List = favouriteColorRepo.getAll() val favouriteIcon: MutableList = mutableListOf() @@ -334,6 +351,7 @@ class BackupPartialRepoImpl @Inject constructor( typeToDefaultTag = typeToDefaultTag::add, activityFilters = activityFilters::add, favouriteComments = favouriteComments::add, + typeToFavouriteComment = typeToFavouriteComment::add, favouriteColors = favouriteColors::add, favouriteIcon = favouriteIcon::add, goals = goals::add, @@ -459,8 +477,21 @@ class BackupPartialRepoImpl @Inject constructor( mapToHolder(it, activityFiltersCurrent) }.list - val newFavouriteComments = favouriteComments.let { + val (newFavouriteComments, originalFavouriteCommentIdToExistingId) = favouriteComments.let { mapToHolder(it, favouriteCommentsCurrent) + } + + val newTypeToFavouriteComment = typeToFavouriteComment.mapNotNull { item -> + val newTypeId = originalTypeIdToExistingId[item.recordTypeId] + ?: return@mapNotNull null + val newCommentId = originalFavouriteCommentIdToExistingId[item.commentId] + ?: return@mapNotNull null + item.copy( + recordTypeId = newTypeId, + commentId = newCommentId, + ) + }.let { + mapToHolder(it, typeToFavouriteCommentCurrent) }.list val newFavouriteColors = favouriteColors.let { @@ -566,6 +597,7 @@ class BackupPartialRepoImpl @Inject constructor( typeToDefaultTag = newTypeToDefaultTag, activityFilters = newActivityFilters.associateBy { it.data.id }, favouriteComments = newFavouriteComments.associateBy { it.data.id }, + typeToFavouriteComment = newTypeToFavouriteComment, favouriteColors = newFavouriteColors.associateBy { it.data.id }, favouriteIcon = newFavouriteIcon.associateBy { it.data.id }, goals = newGoals.associateBy { it.data.id }, @@ -602,6 +634,7 @@ class BackupPartialRepoImpl @Inject constructor( is RecordTypeToDefaultTag -> IdData({ this }, { 0 }) is ActivityFilter -> IdData({ copy(id = it) }, { id }) is FavouriteComment -> IdData({ copy(id = it) }, { id }) + is RecordTypeToFavouriteComment -> IdData({ this }, { 0 }) is FavouriteColor -> IdData({ copy(id = it) }, { id }) is FavouriteIcon -> IdData({ copy(id = it) }, { id }) is RecordTypeGoal -> IdData({ copy(id = it) }, { id }) diff --git a/data_local/src/main/java/com/example/util/simpletimetracker/data_local/backup/BackupRepoImpl.kt b/data_local/src/main/java/com/example/util/simpletimetracker/data_local/backup/BackupRepoImpl.kt index c116c3abc..c00b3c657 100644 --- a/data_local/src/main/java/com/example/util/simpletimetracker/data_local/backup/BackupRepoImpl.kt +++ b/data_local/src/main/java/com/example/util/simpletimetracker/data_local/backup/BackupRepoImpl.kt @@ -31,9 +31,11 @@ import com.example.util.simpletimetracker.domain.extension.orZero import com.example.util.simpletimetracker.domain.favourite.model.FavouriteColor import com.example.util.simpletimetracker.domain.favourite.model.FavouriteComment import com.example.util.simpletimetracker.domain.favourite.model.FavouriteIcon +import com.example.util.simpletimetracker.domain.favourite.model.RecordTypeToFavouriteComment import com.example.util.simpletimetracker.domain.favourite.repo.FavouriteColorRepo import com.example.util.simpletimetracker.domain.favourite.repo.FavouriteCommentRepo import com.example.util.simpletimetracker.domain.favourite.repo.FavouriteIconRepo +import com.example.util.simpletimetracker.domain.favourite.repo.RecordTypeToFavouriteCommentRepo import com.example.util.simpletimetracker.domain.prefs.interactor.PrefsInteractor import com.example.util.simpletimetracker.domain.record.model.Record import com.example.util.simpletimetracker.domain.record.model.RecordBase @@ -86,6 +88,7 @@ class BackupRepoImpl @Inject constructor( private val activityFilterRepo: ActivityFilterRepo, private val activitySuggestionRepo: ActivitySuggestionRepo, private val favouriteCommentRepo: FavouriteCommentRepo, + private val recordTypeToFavouriteCommentRepo: RecordTypeToFavouriteCommentRepo, private val favouriteColorRepo: FavouriteColorRepo, private val favouriteIconRepo: FavouriteIconRepo, private val recordTypeGoalRepo: RecordTypeGoalRepo, @@ -164,6 +167,9 @@ class BackupRepoImpl @Inject constructor( favouriteCommentRepo.getAll().forEach { fileOutputStream?.write(it.let(::toBackupString).toByteArray()) } + recordTypeToFavouriteCommentRepo.getAll().forEach { + fileOutputStream?.write(it.let(::toBackupString).toByteArray()) + } favouriteColorRepo.getAll().forEach { fileOutputStream?.write(it.let(::toBackupString).toByteArray()) } @@ -243,6 +249,7 @@ class BackupRepoImpl @Inject constructor( typeToDefaultTag = recordTypeToDefaultTagRepo::add, activityFilters = activityFilterRepo::add, favouriteComments = favouriteCommentRepo::add, + typeToFavouriteComment = recordTypeToFavouriteCommentRepo::add, favouriteColors = favouriteColorRepo::add, favouriteIcon = favouriteIconRepo::add, goals = recordTypeGoalRepo::add, @@ -374,6 +381,12 @@ class BackupRepoImpl @Inject constructor( } } + ROW_TYPE_TO_FAVOURITE_COMMENT -> { + typeToFavouriteCommentFromBackupString(parts).let { + dataHandler.typeToFavouriteComment.invoke(it) + } + } + ROW_FAVOURITE_COLOR -> { favouriteColorFromBackupString(parts)?.let { dataHandler.favouriteColors.invoke(it) @@ -575,6 +588,14 @@ class BackupRepoImpl @Inject constructor( ) } + private fun toBackupString(typeToTag: RecordTypeToFavouriteComment): String { + return String.format( + "$ROW_TYPE_TO_FAVOURITE_COMMENT\t%s\t%s\n", + typeToTag.recordTypeId.toString(), + typeToTag.commentId.toString(), + ) + } + private fun toBackupString(favouriteColor: FavouriteColor): String { return String.format( "$ROW_FAVOURITE_COLOR\t%s\t%s\n", @@ -894,6 +915,13 @@ class BackupRepoImpl @Inject constructor( ) } + private fun typeToFavouriteCommentFromBackupString(parts: List): RecordTypeToFavouriteComment { + return RecordTypeToFavouriteComment( + recordTypeId = parts.getOrNull(1)?.toLongOrNull().orZero(), + commentId = parts.getOrNull(2)?.toLongOrNull().orZero(), + ) + } + private fun favouriteColorFromBackupString(parts: List): FavouriteColor? { return FavouriteColor( id = parts.getOrNull(1)?.toLongOrNull().orZero(), @@ -1072,6 +1100,7 @@ class BackupRepoImpl @Inject constructor( val typeToDefaultTag: suspend (RecordTypeToDefaultTag) -> Unit, val activityFilters: suspend (ActivityFilter) -> Unit, val favouriteComments: suspend (FavouriteComment) -> Unit, + val typeToFavouriteComment: suspend (RecordTypeToFavouriteComment) -> Unit, val favouriteColors: suspend (FavouriteColor) -> Unit, val favouriteIcon: suspend (FavouriteIcon) -> Unit, val goals: suspend (RecordTypeGoal) -> Unit, @@ -1097,6 +1126,7 @@ class BackupRepoImpl @Inject constructor( private const val ROW_ACTIVITY_FILTER = "activityFilter" private const val ROW_ACTIVITY_SUGGESTION = "activitySuggestion" private const val ROW_FAVOURITE_COMMENT = "favouriteComment" + private const val ROW_TYPE_TO_FAVOURITE_COMMENT = "typeToFavouriteComment" private const val ROW_FAVOURITE_COLOR = "favouriteColor" private const val ROW_FAVOURITE_ICON = "favouriteIcon" private const val ROW_RECORD_TYPE_GOAL = "recordTypeGoal" diff --git a/domain/src/main/java/com/example/util/simpletimetracker/domain/backup/model/PartialBackupRestoreData.kt b/domain/src/main/java/com/example/util/simpletimetracker/domain/backup/model/PartialBackupRestoreData.kt index 1ded73767..8357d5124 100644 --- a/domain/src/main/java/com/example/util/simpletimetracker/domain/backup/model/PartialBackupRestoreData.kt +++ b/domain/src/main/java/com/example/util/simpletimetracker/domain/backup/model/PartialBackupRestoreData.kt @@ -8,6 +8,7 @@ import com.example.util.simpletimetracker.domain.complexRule.model.ComplexRule import com.example.util.simpletimetracker.domain.favourite.model.FavouriteColor import com.example.util.simpletimetracker.domain.favourite.model.FavouriteComment import com.example.util.simpletimetracker.domain.favourite.model.FavouriteIcon +import com.example.util.simpletimetracker.domain.favourite.model.RecordTypeToFavouriteComment import com.example.util.simpletimetracker.domain.record.model.Record import com.example.util.simpletimetracker.domain.recordShortcut.model.RecordShortcut import com.example.util.simpletimetracker.domain.recordTag.model.RecordShortcutToRecordTag @@ -32,6 +33,7 @@ data class PartialBackupRestoreData( val typeToDefaultTag: List>, val activityFilters: Map>, val favouriteComments: Map>, + val typeToFavouriteComment: List>, val favouriteColors: Map>, val favouriteIcon: Map>, val goals: Map>, diff --git a/features/feature_settings/src/main/java/com/example/util/simpletimetracker/feature_settings/partialRestore/viewModel/PartialRestoreViewModel.kt b/features/feature_settings/src/main/java/com/example/util/simpletimetracker/feature_settings/partialRestore/viewModel/PartialRestoreViewModel.kt index 4eeff3555..7f41206b5 100644 --- a/features/feature_settings/src/main/java/com/example/util/simpletimetracker/feature_settings/partialRestore/viewModel/PartialRestoreViewModel.kt +++ b/features/feature_settings/src/main/java/com/example/util/simpletimetracker/feature_settings/partialRestore/viewModel/PartialRestoreViewModel.kt @@ -234,6 +234,7 @@ class PartialRestoreViewModel @Inject constructor( typeToDefaultTag = typeToDefaultTag, activityFilters = activityFilters, favouriteComments = data.favouriteComments, + typeToFavouriteComment = data.typeToFavouriteComment, favouriteColors = data.favouriteColors, favouriteIcon = data.favouriteIcon, goals = goals, From 30cd13524126934b48ebdde4105cc41b5b368350 Mon Sep 17 00:00:00 2001 From: Terrance Date: Sun, 22 Mar 2026 10:27:12 +0000 Subject: [PATCH 6/6] Move shared favourite logic to interactor --- .../RecordCommentSearchViewDataInteractor.kt | 8 ++------ .../interactor/FavouriteCommentInteractor.kt | 3 +++ .../RecordTypeToFavouriteCommentInteractor.kt | 8 ++++++++ .../interactor/ChangeRecordViewDataInteractor.kt | 13 +++---------- 4 files changed, 16 insertions(+), 16 deletions(-) diff --git a/core/src/main/java/com/example/util/simpletimetracker/core/interactor/RecordCommentSearchViewDataInteractor.kt b/core/src/main/java/com/example/util/simpletimetracker/core/interactor/RecordCommentSearchViewDataInteractor.kt index 0f348465e..126a55e8f 100644 --- a/core/src/main/java/com/example/util/simpletimetracker/core/interactor/RecordCommentSearchViewDataInteractor.kt +++ b/core/src/main/java/com/example/util/simpletimetracker/core/interactor/RecordCommentSearchViewDataInteractor.kt @@ -126,12 +126,8 @@ class RecordCommentSearchViewDataInteractor @Inject constructor( private suspend fun getFavouriteData( typeId: Long, ): List { - val (included, excluded) = recordTypeToFavouriteCommentInteractor.getAll() - .partition { it.recordTypeId == typeId } - val includedComments = included.map { it.commentId } - val excludedComments = excluded.map { it.commentId } - return favouriteCommentInteractor.getAll() - .filter { includedComments.contains(it.id) || !excludedComments.contains(it.id) } + val comments = favouriteCommentInteractor.getAll() + return recordTypeToFavouriteCommentInteractor.filterFavourites(typeId, comments) .map { RecordCommentViewData.Favourite(it.comment) } } diff --git a/domain/src/main/java/com/example/util/simpletimetracker/domain/favourite/interactor/FavouriteCommentInteractor.kt b/domain/src/main/java/com/example/util/simpletimetracker/domain/favourite/interactor/FavouriteCommentInteractor.kt index a117e1039..1abe09f20 100644 --- a/domain/src/main/java/com/example/util/simpletimetracker/domain/favourite/interactor/FavouriteCommentInteractor.kt +++ b/domain/src/main/java/com/example/util/simpletimetracker/domain/favourite/interactor/FavouriteCommentInteractor.kt @@ -2,11 +2,13 @@ package com.example.util.simpletimetracker.domain.favourite.interactor import com.example.util.simpletimetracker.domain.favourite.repo.FavouriteCommentRepo import com.example.util.simpletimetracker.domain.favourite.model.FavouriteComment +import com.example.util.simpletimetracker.domain.favourite.repo.RecordTypeToFavouriteCommentRepo import java.util.Locale import javax.inject.Inject class FavouriteCommentInteractor @Inject constructor( private val repo: FavouriteCommentRepo, + private val recordTypeRepo: RecordTypeToFavouriteCommentRepo, ) { suspend fun getAll(): List { @@ -23,6 +25,7 @@ class FavouriteCommentInteractor @Inject constructor( suspend fun remove(id: Long) { repo.remove(id) + recordTypeRepo.removeAll(id) } fun sort( diff --git a/domain/src/main/java/com/example/util/simpletimetracker/domain/favourite/interactor/RecordTypeToFavouriteCommentInteractor.kt b/domain/src/main/java/com/example/util/simpletimetracker/domain/favourite/interactor/RecordTypeToFavouriteCommentInteractor.kt index ad8f8557b..bd9bbe118 100644 --- a/domain/src/main/java/com/example/util/simpletimetracker/domain/favourite/interactor/RecordTypeToFavouriteCommentInteractor.kt +++ b/domain/src/main/java/com/example/util/simpletimetracker/domain/favourite/interactor/RecordTypeToFavouriteCommentInteractor.kt @@ -1,5 +1,6 @@ package com.example.util.simpletimetracker.domain.favourite.interactor +import com.example.util.simpletimetracker.domain.favourite.model.FavouriteComment import com.example.util.simpletimetracker.domain.favourite.model.RecordTypeToFavouriteComment import com.example.util.simpletimetracker.domain.favourite.repo.RecordTypeToFavouriteCommentRepo import javax.inject.Inject @@ -31,4 +32,11 @@ class RecordTypeToFavouriteCommentInteractor @Inject constructor( suspend fun removeTypes(commentId: Long, typeIds: List) { repo.removeTypes(commentId, typeIds) } + + suspend fun filterFavourites(typeId: Long, comments: List): List { + val (included, excluded) = getAll().partition { it.recordTypeId == typeId } + val includedIds = included.map { it.commentId } + val excludedIds = excluded.map { it.commentId } + return comments.filter { includedIds.contains(it.id) || !excludedIds.contains(it.id) } + } } \ No newline at end of file diff --git a/features/feature_change_record/src/main/java/com/example/util/simpletimetracker/feature_change_record/interactor/ChangeRecordViewDataInteractor.kt b/features/feature_change_record/src/main/java/com/example/util/simpletimetracker/feature_change_record/interactor/ChangeRecordViewDataInteractor.kt index caf6519f2..777b603ad 100644 --- a/features/feature_change_record/src/main/java/com/example/util/simpletimetracker/feature_change_record/interactor/ChangeRecordViewDataInteractor.kt +++ b/features/feature_change_record/src/main/java/com/example/util/simpletimetracker/feature_change_record/interactor/ChangeRecordViewDataInteractor.kt @@ -66,16 +66,9 @@ class ChangeRecordViewDataInteractor @Inject constructor( val isDarkTheme = prefsInteractor.getDarkMode() val favouriteComment = favouriteCommentInteractor.get(comment) - val isFavourite = if (favouriteComment == null) false else { - val (included, excluded) = recordTypeToFavouriteCommentInteractor.getAll() - .partition { it.recordTypeId == typeId } - val includedComments = included.map { it.commentId } - val excludedComments = excluded.map { it.commentId } - ( - includedComments.contains(favouriteComment.id) || - !excludedComments.contains(favouriteComment.id) - ) - } + val isFavourite = if (favouriteComment == null) false else + recordTypeToFavouriteCommentInteractor + .filterFavourites(typeId, listOf(favouriteComment)).isNotEmpty() ChangeRecordCommentFieldViewData( // Only one at the time.