diff --git a/data/src/commonMain/kotlin/org/tasks/data/dao/CaldavDao.kt b/data/src/commonMain/kotlin/org/tasks/data/dao/CaldavDao.kt index d0dc2054d..eda7b5d65 100644 --- a/data/src/commonMain/kotlin/org/tasks/data/dao/CaldavDao.kt +++ b/data/src/commonMain/kotlin/org/tasks/data/dao/CaldavDao.kt @@ -4,13 +4,13 @@ import androidx.room.Dao import androidx.room.Delete import androidx.room.Insert import androidx.room.Query -import androidx.room.Transaction import androidx.room.Update import kotlinx.coroutines.flow.Flow import org.tasks.data.CaldavFilters import org.tasks.data.CaldavTaskContainer import org.tasks.data.NO_ORDER import org.tasks.data.TaskContainer +import org.tasks.data.db.Database import org.tasks.data.db.DbUtils.dbchunk import org.tasks.data.db.SuspendDbUtils.chunkedMap import org.tasks.data.entity.CaldavAccount @@ -22,12 +22,13 @@ import org.tasks.data.entity.CaldavAccount.Companion.TYPE_TASKS import org.tasks.data.entity.CaldavCalendar import org.tasks.data.entity.CaldavTask import org.tasks.data.entity.Task +import org.tasks.data.withTransaction import org.tasks.time.DateTimeUtils2.currentTimeMillis const val APPLE_EPOCH = 978307200000L // 1/1/2001 GMT @Dao -abstract class CaldavDao { +abstract class CaldavDao(private val database: Database) { @Query("SELECT COUNT(*) FROM caldav_lists WHERE cdl_account = :account") abstract suspend fun listCount(account: String): Int @@ -98,24 +99,24 @@ ORDER BY CASE cda_account_type @Update abstract suspend fun update(caldavCalendar: CaldavCalendar) - @Transaction - open suspend fun insert(task: Task, caldavTask: CaldavTask, addToTop: Boolean): Long { - if (task.order != null) { - return insert(caldavTask) - } - if (addToTop) { - task.order = findFirstTask(caldavTask.calendar!!, task.parent) - ?.takeIf { task.creationDate.toAppleEpoch() >= it } - ?.minus(1) - } else { - task.order = findLastTask(caldavTask.calendar!!, task.parent) - ?.takeIf { task.creationDate.toAppleEpoch() <= it } - ?.plus(1) + suspend fun insert(task: Task, caldavTask: CaldavTask, addToTop: Boolean): Long = + database.withTransaction { + if (task.order != null) { + return@withTransaction insert(caldavTask) + } + if (addToTop) { + task.order = findFirstTask(caldavTask.calendar!!, task.parent) + ?.takeIf { task.creationDate.toAppleEpoch() >= it } + ?.minus(1) + } else { + task.order = findLastTask(caldavTask.calendar!!, task.parent) + ?.takeIf { task.creationDate.toAppleEpoch() <= it } + ?.plus(1) + } + val id = insert(caldavTask) + update(task) + id } - val id = insert(caldavTask) - update(task) - return id - } @Query(""" SELECT MIN(IFNULL(`order`, (created - $APPLE_EPOCH) / 1000)) @@ -311,47 +312,49 @@ GROUP BY caldav_lists.cdl_uuid + "WHERE _id IN (SELECT _id FROM tasks INNER JOIN caldav_tasks ON _id = cd_task WHERE cd_deleted = 0 AND cd_calendar = :calendar)") abstract suspend fun updateParents(calendar: String) - @Transaction - open suspend fun move( + suspend fun move( task: TaskContainer, previousParent: Long, newParent: Long, newPosition: Long?, ) { - val previousPosition = task.caldavSortOrder - if (newPosition != null) { - if (newParent == previousParent && newPosition < previousPosition) { - shiftDown(task.caldav!!, newParent, newPosition, previousPosition) - } else { - val list = - newParent.takeIf { it > 0 }?.let { getTask(it)?.calendar } ?: task.caldav!! - shiftDown(list, newParent, newPosition) + database.withTransaction { + val previousPosition = task.caldavSortOrder + if (newPosition != null) { + if (newParent == previousParent && newPosition < previousPosition) { + shiftDown(task.caldav!!, newParent, newPosition, previousPosition) + } else { + val list = + newParent.takeIf { it > 0 }?.let { getTask(it)?.calendar } ?: task.caldav!! + shiftDown(list, newParent, newPosition) + } } + task.task.order = newPosition + setTaskOrder(task.id, newPosition) } - task.task.order = newPosition - setTaskOrder(task.id, newPosition) } - @Transaction - open suspend fun shiftDown(calendar: String, parent: Long, from: Long, to: Long? = null) { - val updated = ArrayList() - val tasks = getTasksToShift(calendar, parent, from, to) - for (i in tasks.indices) { - val task = tasks[i] - val current = from + i - if (task.sortOrder == current) { - val task = task.task - task.order = current + 1 - updated.add(task) - } else if (task.sortOrder > current) { - break + suspend fun shiftDown(calendar: String, parent: Long, from: Long, to: Long? = null) { + database.withTransaction { + val updated = ArrayList() + val tasks = getTasksToShift(calendar, parent, from, to) + for (i in tasks.indices) { + val task = tasks[i] + val current = from + i + if (task.sortOrder == current) { + val task = task.task + task.order = current + 1 + updated.add(task) + } else if (task.sortOrder > current) { + break + } } + updateTasks(updated) + updated + .map(Task::id) + .dbchunk() + .forEach { touchInternal(it) } } - updateTasks(updated) - updated - .map(Task::id) - .dbchunk() - .forEach { touchInternal(it) } } @Query("UPDATE tasks SET modified = :modificationTime WHERE _id in (:ids)") diff --git a/data/src/commonMain/kotlin/org/tasks/data/dao/DeletionDao.kt b/data/src/commonMain/kotlin/org/tasks/data/dao/DeletionDao.kt index 19f54cc0c..b50282c27 100644 --- a/data/src/commonMain/kotlin/org/tasks/data/dao/DeletionDao.kt +++ b/data/src/commonMain/kotlin/org/tasks/data/dao/DeletionDao.kt @@ -3,15 +3,16 @@ package org.tasks.data.dao import androidx.room.Dao import androidx.room.Delete import androidx.room.Query -import androidx.room.Transaction import org.tasks.data.dao.CaldavDao.Companion.LOCAL +import org.tasks.data.db.Database import org.tasks.data.db.SuspendDbUtils.chunkedMap import org.tasks.data.db.SuspendDbUtils.eachChunk import org.tasks.data.entity.CaldavAccount import org.tasks.data.entity.CaldavCalendar +import org.tasks.data.withTransaction @Dao -abstract class DeletionDao { +abstract class DeletionDao(private val database: Database) { @Query("DELETE FROM tasks WHERE _id IN(:ids)") internal abstract suspend fun deleteTasks(ids: List) @@ -58,13 +59,13 @@ WHERE recurring = 1 @Delete internal abstract suspend fun deleteCaldavCalendar(caldavCalendar: CaldavCalendar) - @Transaction - open suspend fun delete(caldavCalendar: CaldavCalendar): List { - val tasks = getActiveCaldavTasks(caldavCalendar.uuid!!) - delete(tasks) - deleteCaldavCalendar(caldavCalendar) - return tasks - } + suspend fun delete(caldavCalendar: CaldavCalendar): List = + database.withTransaction { + val tasks = getActiveCaldavTasks(caldavCalendar.uuid!!) + delete(tasks) + deleteCaldavCalendar(caldavCalendar) + tasks + } @Query("SELECT * FROM caldav_lists WHERE cdl_account = :account") abstract suspend fun getCalendars(account: String): List @@ -75,13 +76,13 @@ WHERE recurring = 1 @Query("DELETE FROM tasks WHERE _id IN (SELECT _id FROM tasks INNER JOIN caldav_tasks ON _id = cd_task INNER JOIN caldav_lists ON cdl_uuid = cd_calendar WHERE cdl_account = '$LOCAL' AND deleted > 0 AND cd_deleted = 0)") abstract suspend fun purgeDeleted() - @Transaction - open suspend fun delete(caldavAccount: CaldavAccount): List { - val deleted = ArrayList() - for (calendar in getCalendars(caldavAccount.uuid!!)) { - deleted.addAll(delete(calendar)) + suspend fun delete(caldavAccount: CaldavAccount): List = + database.withTransaction { + val deleted = ArrayList() + for (calendar in getCalendars(caldavAccount.uuid!!)) { + deleted.addAll(delete(calendar)) + } + deleteCaldavAccount(caldavAccount) + deleted } - deleteCaldavAccount(caldavAccount) - return deleted - } } \ No newline at end of file diff --git a/data/src/commonMain/kotlin/org/tasks/data/dao/GoogleTaskDao.kt b/data/src/commonMain/kotlin/org/tasks/data/dao/GoogleTaskDao.kt index 855bc87be..27a95de01 100644 --- a/data/src/commonMain/kotlin/org/tasks/data/dao/GoogleTaskDao.kt +++ b/data/src/commonMain/kotlin/org/tasks/data/dao/GoogleTaskDao.kt @@ -4,30 +4,32 @@ import androidx.room.Dao import androidx.room.Delete import androidx.room.Insert import androidx.room.Query -import androidx.room.Transaction import androidx.room.Update +import org.tasks.data.db.Database import org.tasks.data.entity.CaldavAccount.Companion.TYPE_GOOGLE_TASKS import org.tasks.data.entity.CaldavTask import org.tasks.data.entity.Task +import org.tasks.data.withTransaction @Dao -abstract class GoogleTaskDao { +abstract class GoogleTaskDao(private val database: Database) { @Insert abstract suspend fun insert(task: CaldavTask): Long @Insert abstract suspend fun insert(tasks: Iterable) - @Transaction - open suspend fun insertAndShift(task: Task, caldavTask: CaldavTask, top: Boolean) { - if (top) { - task.order = 0 - shiftDown(caldavTask.calendar!!, task.parent, 0) - } else { - task.order = getBottom(caldavTask.calendar!!, task.parent) + suspend fun insertAndShift(task: Task, caldavTask: CaldavTask, top: Boolean) { + database.withTransaction { + if (top) { + task.order = 0 + shiftDown(caldavTask.calendar!!, task.parent, 0) + } else { + task.order = getBottom(caldavTask.calendar!!, task.parent) + } + insert(caldavTask) + update(task) } - insert(caldavTask) - update(task) } @Query("UPDATE tasks SET `order` = `order` + 1 WHERE parent = :parent AND `order` >= :position AND _id IN (SELECT cd_task FROM caldav_tasks WHERE cd_calendar = :listId)") @@ -42,24 +44,25 @@ abstract class GoogleTaskDao { @Query("UPDATE tasks SET `order` = `order` - 1 WHERE parent = :parent AND `order` >= :position AND _id IN (SELECT cd_task FROM caldav_tasks WHERE cd_calendar = :listId)") internal abstract suspend fun shiftUp(listId: String, parent: Long, position: Long) - @Transaction - open suspend fun move(task: Task, list: String, newParent: Long, newPosition: Long) { - val previousParent = task.parent - val previousPosition = task.order!! - if (newParent == previousParent) { - if (previousPosition < newPosition) { - shiftUp(list, newParent, previousPosition, newPosition) + suspend fun move(task: Task, list: String, newParent: Long, newPosition: Long) { + database.withTransaction { + val previousParent = task.parent + val previousPosition = task.order!! + if (newParent == previousParent) { + if (previousPosition < newPosition) { + shiftUp(list, newParent, previousPosition, newPosition) + } else { + shiftDown(list, newParent, previousPosition, newPosition) + } } else { - shiftDown(list, newParent, previousPosition, newPosition) + shiftUp(list, previousParent, previousPosition) + shiftDown(list, newParent, newPosition) } - } else { - shiftUp(list, previousParent, previousPosition) - shiftDown(list, newParent, newPosition) + task.parent = newParent + task.order = newPosition + update(task) + setMoved(task.id, list) } - task.parent = newParent - task.order = newPosition - update(task) - setMoved(task.id, list) } @Query("UPDATE caldav_tasks SET gt_moved = 1 WHERE cd_task = :task and cd_calendar = :list") @@ -165,26 +168,27 @@ WHERE cd_remote_id = :id """) abstract suspend fun updatePosition(id: String, parent: String?, position: String) - @Transaction - open suspend fun reposition(caldavDao: CaldavDao, listId: String) { - caldavDao.updateParents(listId) - val orderedTasks = getByRemoteOrder(listId) - var subtasks = 0L - var parent = 0L - for (task in orderedTasks) { - if (task.parent > 0) { - if (task.order != subtasks) { - task.order = subtasks - update(task) + suspend fun reposition(caldavDao: CaldavDao, listId: String) { + database.withTransaction { + caldavDao.updateParents(listId) + val orderedTasks = getByRemoteOrder(listId) + var subtasks = 0L + var parent = 0L + for (task in orderedTasks) { + if (task.parent > 0) { + if (task.order != subtasks) { + task.order = subtasks + update(task) + } + subtasks++ + } else { + subtasks = 0 + if (task.order != parent) { + task.order = parent + update(task) + } + parent++ } - subtasks++ - } else { - subtasks = 0 - if (task.order != parent) { - task.order = parent - update(task) - } - parent++ } } } diff --git a/data/src/commonMain/kotlin/org/tasks/data/dao/PrincipalDao.kt b/data/src/commonMain/kotlin/org/tasks/data/dao/PrincipalDao.kt index a9cc790a5..6fc229383 100644 --- a/data/src/commonMain/kotlin/org/tasks/data/dao/PrincipalDao.kt +++ b/data/src/commonMain/kotlin/org/tasks/data/dao/PrincipalDao.kt @@ -4,39 +4,43 @@ import androidx.room.Dao import androidx.room.Delete import androidx.room.Insert import androidx.room.Query -import androidx.room.Transaction import androidx.room.Update import kotlinx.coroutines.flow.Flow import org.tasks.data.PrincipalWithAccess +import org.tasks.data.db.Database import org.tasks.data.entity.CaldavAccount import org.tasks.data.entity.CaldavCalendar import org.tasks.data.entity.Principal import org.tasks.data.entity.PrincipalAccess +import org.tasks.data.withTransaction @Dao -interface PrincipalDao { +abstract class PrincipalDao(private val database: Database) { @Insert - suspend fun insert(principal: Principal): Long + abstract suspend fun insert(principal: Principal): Long @Insert - suspend fun insert(access: PrincipalAccess): Long + abstract suspend fun insert(access: PrincipalAccess): Long @Update - suspend fun update(access: PrincipalAccess) + abstract suspend fun update(access: PrincipalAccess) @Query(""" DELETE FROM principal_access WHERE list = :list AND id NOT IN (:access)""") - suspend fun deleteRemoved(list: Long, access: List) + abstract suspend fun deleteRemoved(list: Long, access: List) @Delete - suspend fun delete(access: PrincipalAccess) + abstract suspend fun delete(access: PrincipalAccess) + + suspend fun getAll(): List = database.withTransaction { + getAllInternal() + } - @Transaction @Query("SELECT * FROM principal_access") - suspend fun getAll(): List + internal abstract suspend fun getAllInternal(): List suspend fun getOrCreatePrincipal(account: CaldavAccount, href: String, displayName: String? = null) = findPrincipal(account.id, href) @@ -65,12 +69,12 @@ WHERE list = :list ).apply { id = insert(this) } @Query("SELECT * FROM principals WHERE account = :account AND href = :href") - suspend fun findPrincipal(account: Long, href: String): Principal? + abstract suspend fun findPrincipal(account: Long, href: String): Principal? @Query("SELECT * FROM principal_access WHERE list = :list and principal = :principal") - suspend fun findAccess(list: Long, principal: Long): PrincipalAccess? + abstract suspend fun findAccess(list: Long, principal: Long): PrincipalAccess? - @Transaction @Query("SELECT * FROM principal_access WHERE list = :id") - fun getPrincipals(id: Long): Flow> -} + abstract fun getPrincipals(id: Long): Flow> + +} \ No newline at end of file diff --git a/data/src/commonMain/kotlin/org/tasks/data/dao/TagDao.kt b/data/src/commonMain/kotlin/org/tasks/data/dao/TagDao.kt index f9a5d5f0b..00bcd2e93 100644 --- a/data/src/commonMain/kotlin/org/tasks/data/dao/TagDao.kt +++ b/data/src/commonMain/kotlin/org/tasks/data/dao/TagDao.kt @@ -4,13 +4,14 @@ import androidx.room.Dao import androidx.room.Delete import androidx.room.Insert import androidx.room.Query -import androidx.room.Transaction +import org.tasks.data.db.Database import org.tasks.data.entity.Tag import org.tasks.data.entity.TagData import org.tasks.data.entity.Task +import org.tasks.data.withTransaction @Dao -abstract class TagDao { +abstract class TagDao(private val database: Database) { @Query("UPDATE tags SET name = :name WHERE tag_uid = :tagUid") abstract suspend fun rename(tagUid: String, name: String) @@ -35,15 +36,16 @@ abstract class TagDao { @Delete abstract suspend fun delete(tags: List) - @Transaction open suspend fun applyTags(task: Task, tagDataDao: TagDataDao, current: List) { - val taskId = task.id - val existing = HashSet(tagDataDao.getTagDataForTask(taskId)) - val selected = HashSet(current) - val added = selected subtract existing - val removed = existing subtract selected - deleteTags(taskId, removed.map { td -> td.remoteId!! }) - insert(task, added) + database.withTransaction { + val taskId = task.id + val existing = HashSet(tagDataDao.getTagDataForTask(taskId)) + val selected = HashSet(current) + val added = selected subtract existing + val removed = existing subtract selected + deleteTags(taskId, removed.map { td -> td.remoteId!! }) + insert(task, added) + } } suspend fun insert(task: Task, tags: Collection) { diff --git a/data/src/commonMain/kotlin/org/tasks/data/dao/TagDataDao.kt b/data/src/commonMain/kotlin/org/tasks/data/dao/TagDataDao.kt index 9aba9b161..2c4d0eb0f 100644 --- a/data/src/commonMain/kotlin/org/tasks/data/dao/TagDataDao.kt +++ b/data/src/commonMain/kotlin/org/tasks/data/dao/TagDataDao.kt @@ -4,19 +4,20 @@ import androidx.room.Dao import androidx.room.Delete import androidx.room.Insert import androidx.room.Query -import androidx.room.Transaction import androidx.room.Update import kotlinx.coroutines.flow.Flow import org.tasks.data.NO_ORDER import org.tasks.data.TagFilters +import org.tasks.data.db.Database import org.tasks.data.db.DbUtils import org.tasks.data.entity.Tag import org.tasks.data.entity.TagData import org.tasks.data.entity.Task +import org.tasks.data.withTransaction import org.tasks.time.DateTimeUtils2.currentTimeMillis @Dao -abstract class TagDataDao { +abstract class TagDataDao(private val database: Database) { @Query("SELECT * FROM tagdata") abstract fun subscribeToTags(): Flow> @@ -79,12 +80,11 @@ abstract class TagDataDao { + " GROUP BY tasks._id") internal abstract suspend fun getAllTags(tasks: List): List - @Transaction - open suspend fun applyTags( + suspend fun applyTags( tasks: List, partiallySelected: List, selected: List - ): List { + ): List = database.withTransaction { val modified = HashSet() val keep = partiallySelected.plus(selected).map { it.remoteId!! } for (sublist in tasks.chunked(DbUtils.MAX_SQLITE_ARGS - keep.size)) { @@ -108,13 +108,14 @@ abstract class TagDataDao { ) } } - return ArrayList(modified) + ArrayList(modified) } - @Transaction - open suspend fun delete(tagData: TagData) { - deleteTags(tagData.remoteId!!) - deleteTagData(tagData) + suspend fun delete(tagData: TagData) { + database.withTransaction { + deleteTags(tagData.remoteId!!) + deleteTagData(tagData) + } } @Delete