Skip to content

Fix duplicate ids crashes #90

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Jun 29, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions app/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -27,8 +27,8 @@ android {
multiDexEnabled = true
applicationId = "com.lolo.io.onelist"
// version code elvis right operand should be incremented too when publishing a new release, for fDroid build.
versionCode = versionCodeCI ?: 20
versionName = "1.5.0"
versionCode = versionCodeCI ?: 21
versionName = "1.5.1"
vectorDrawables.useSupportLibrary = true
testBuildType = "instrumented"

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,11 +7,11 @@ import com.lolo.io.onelist.core.data.file_access.FileAccess
import com.lolo.io.onelist.core.data.datamodel.ListsWithErrors
import com.lolo.io.onelist.core.data.datamodel.ErrorLoadingList
import com.lolo.io.onelist.core.data.shared_preferences.SharedPreferencesHelper
import com.lolo.io.onelist.core.data.utils.ensureAllItemsIdsAreUnique
import com.lolo.io.onelist.core.data.utils.toItemListEntity
import com.lolo.io.onelist.core.data.utils.updateOneIf
import com.lolo.io.onelist.core.database.dao.ItemListDao
import com.lolo.io.onelist.core.database.util.toItemListModel
import com.lolo.io.onelist.core.database.util.toItemListModels
import com.lolo.io.onelist.core.model.ItemList
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
Expand Down Expand Up @@ -45,10 +45,10 @@ class OneListRepositoryImpl(
preferences.backupUri != null
) {
allListsFromDb.map {
val list = it.toItemListModel()
val list = ensureAllItemsIdsAreUnique(it.toItemListModel())
try {
supervisorScope {
fileAccess.getListFromLocalFile(list)
ensureAllItemsIdsAreUnique(fileAccess.getListFromLocalFile(list))
}
} catch (e: SecurityException) {
errors += ErrorLoadingList.PermissionDeniedError
Expand All @@ -72,7 +72,9 @@ class OneListRepositoryImpl(
}
}
} else {
allListsFromDb.toItemListModels()
allListsFromDb.map {
ensureAllItemsIdsAreUnique(it.toItemListModel())
}
}

_allListsWithErrors.value = ListsWithErrors(
Expand All @@ -97,7 +99,9 @@ class OneListRepositoryImpl(
// does upsert in dao, and if has backup uri -> save list file; can create a file
// and also update allLists flow
override suspend fun saveList(itemList: ItemList) {
upsertList(itemList).let {
upsertList(
ensureAllItemsIdsAreUnique(itemList)
).let {
_allListsWithErrors.value = ListsWithErrors(
_allListsWithErrors.value.lists.updateOneIf(itemList) { it.id == itemList.id })
}
Expand All @@ -115,19 +119,21 @@ class OneListRepositoryImpl(
}
}

val listToSave = ensureAllItemsIdsAreUnique(list)

return withContext(Dispatchers.IO) {
upsertInDao(list)
upsertInDao(listToSave)
if (preferences.backupUri != null) {
fileAccess.saveListFile(
preferences.backupUri,
list,
listToSave,
onNewFileCreated = { list, uri ->
list.uri = uri
upsertInDao(list)
_allListsWithErrors.value = ListsWithErrors(
_allListsWithErrors.value.lists.updateOneIf(list) { it.id == list.id })
})
} else list
} else listToSave
}
}

Expand Down Expand Up @@ -157,9 +163,11 @@ class OneListRepositoryImpl(
val list = fileAccess.createListFromUri(uri,
onListCreated = {
saveList(
it.copy(
id = 0L,
position = _allListsWithErrors.value.lists.size
ensureAllItemsIdsAreUnique(
it.copy(
id = 0L,
position = _allListsWithErrors.value.lists.size
)
)
)
})
Expand Down
Original file line number Diff line number Diff line change
@@ -1,9 +1,22 @@
package com.lolo.io.onelist.core.data.utils

import com.lolo.io.onelist.core.model.ItemList

fun <T> List<T>.updateOneIf(newItem: T, condition: (T) -> Boolean): List<T> {
return this.map {
if (condition(it)) {
newItem
} else it
}
}

inline fun <T, R> Iterable<T>.allUniqueBy(transform: (T) -> R): Boolean {
val hashset = hashSetOf<R>()
return this.all { hashset.add(transform(it)) }
}

fun ensureAllItemsIdsAreUnique(itemList: ItemList): ItemList {
return if(!itemList.items.allUniqueBy { it.id }) {
itemList.copy(items = itemList.items.distinctBy { it.id })
} else itemList
}
Original file line number Diff line number Diff line change
Expand Up @@ -10,17 +10,17 @@ import com.lolo.io.onelist.core.model.Item
import com.lolo.io.onelist.core.model.ItemList

class Converters {
@TypeConverter
fun toItemEntityList(value: String?): List<ItemEntity> {
val listType = object : TypeToken<MutableList<ItemEntity?>?>() {}.type
return Gson().fromJson(value, listType)
}
@TypeConverter
fun toItemEntityList(value: String?): List<ItemEntity> {
val listType = object : TypeToken<MutableList<ItemEntity?>?>() {}.type
return Gson().fromJson(value, listType)
}

@TypeConverter
fun fromItemEntityList(list: List<ItemEntity?>?): String {
val gson = Gson()
return gson.toJson(list)
}
@TypeConverter
fun fromItemEntityList(list: List<ItemEntity?>?): String {
val gson = Gson()
return gson.toJson(list)
}


@TypeConverter
Expand All @@ -34,17 +34,17 @@ class Converters {
}
}

fun ItemListEntity.toItemListModel() = com.lolo.io.onelist.core.model.ItemList(
fun ItemListEntity.toItemListModel() = ItemList(
id = this.id,
items = this.items.toItemModels().toMutableList(),
uri = this.uri,
position = this.position,
title = this.title
)

fun List<ItemListEntity>.toItemListModels() = map { it.toItemListModel() }

fun ItemEntity.toItemModel() = com.lolo.io.onelist.core.model.Item(

fun ItemEntity.toItemModel() = Item(
id = id,
title = title,
comment = comment,
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
package com.lolo.io.onelist.core.domain.use_cases

import com.lolo.io.onelist.core.data.shared_preferences.SharedPreferencesHelper
import isNotNullOrEmpty
import com.lolo.io.onelist.core.domain.utils.isNotNullOrEmpty

class ShouldShowWhatsNew(private val persistenceHelper: SharedPreferencesHelper) {
operator fun invoke(currentVersionName: String): Boolean {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,3 +1,7 @@
package com.lolo.io.onelist.core.domain.utils

import com.lolo.io.onelist.core.model.ItemList

fun String?.isNotNullOrEmpty(): Boolean {
return this?.isNotEmpty() == true
}
Expand All @@ -8,7 +12,7 @@ fun <T> List<T>.ifNotEmpty(block: (List<T>) -> Unit): List<T> {
return this
}

fun <T>List<T>.insertAtIndex(item1: T, index: Int): List<T> {
private fun <T>List<T>.insertAtIndex(item1: T, index: Int): List<T> {
var listCopy = this.toList()
listCopy -= item1
return (listCopy.subList(
Expand Down Expand Up @@ -38,4 +42,6 @@ fun <T>List<T>.moveItemToLeftOf(
listCopy -= item
val toItemIndex = listCopy.indexOf(toItem)
return listCopy.insertAtIndex(item, toItemIndex)
}
}


Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ import com.lolo.io.onelist.core.model.Item
import com.lolo.io.onelist.core.model.ItemList
import com.lolo.io.onelist.core.model.previewMany
import com.lolo.io.onelist.core.designsystem.preview.ThemedPreview
import com.lolo.io.onelist.core.domain.utils.ifNotEmpty
import com.lolo.io.onelist.feature.lists.components.add_item_input.AddItemInput
import com.lolo.io.onelist.feature.lists.components.dialogs.CreateListDialog
import com.lolo.io.onelist.feature.lists.components.dialogs.DeleteListDialog
Expand All @@ -50,7 +51,6 @@ import com.lolo.io.onelist.feature.lists.components.items_lists.ReorderableAndSw
import com.lolo.io.onelist.feature.lists.components.items_lists.rememberSwipeableLazyListState
import com.lolo.io.onelist.feature.lists.components.list_chips.ListsFlowRow
import com.lolo.io.onelist.feature.lists.utils.shareList
import ifNotEmpty
import kotlinx.coroutines.CancellationException
import kotlinx.coroutines.Job
import kotlinx.coroutines.delay
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,11 +25,11 @@ import androidx.compose.ui.layout.onPlaced
import androidx.compose.ui.layout.positionInParent
import androidx.compose.ui.unit.IntOffset
import androidx.compose.ui.unit.round
import com.lolo.io.onelist.core.domain.utils.moveItemToLeftOf
import com.lolo.io.onelist.core.domain.utils.moveItemToRightOf
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
import moveItemToLeftOf
import moveItemToRightOf
import kotlin.math.roundToInt

class ReorderableFlowRowItem<T>(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,9 +23,9 @@ import com.anggrayudi.storage.file.DocumentFileCompat
import com.anggrayudi.storage.file.getAbsolutePath
import com.anggrayudi.storage.file.toTreeDocumentFile
import com.lolo.io.onelist.core.data.shared_preferences.SharedPreferencesHelper
import com.lolo.io.onelist.core.domain.utils.isNotNullOrEmpty
import com.lolo.io.onelist.feature.settings.R
import com.lolo.io.onelist.feature.settings.databinding.FragmentSettingsBinding
import isNotNullOrEmpty
import kotlinx.coroutines.launch
import org.koin.androidx.viewmodel.ext.android.getViewModel

Expand Down