diff --git a/CHANGELOG.md b/CHANGELOG.md index 89524b285b4..a8b1a58daec 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,29 @@ # Changelog +## 0.5.10 + +* `Versions` + * `Core KTX`: `1.3.2` -> `1.5.0` + * `AndroidX Recycler`: `1.2.0` -> `1.2.1` + * `AppCompat`: `1.2.0` -> `1.3.0` +* `Android` + * `RecyclerView`: + * `data` of `RecyclerViewAdapter` became an abstract field + * New function `RecyclerViewAdapter` + * `Common`: + * New extension `View#changeVisibility` + * `Repos`: + * `Common`: + * `WriteOneToManyRepo` got new function `clearWithValue` + * `Android`: + * New extension `SQLiteDatabase#selectDistinct` + * Fixes in `OneToManyAndroidRepo` +* `Ktor` + * `Server` + * All elements in configurators became a `fun interface` +* `Pagination` + * New function `doForAllWithCurrentPaging` + ## 0.5.9 * `Repos` diff --git a/android/alerts/recyclerview/src/main/kotlin/dev/inmo/micro_utils/android/alerts/recyclerview/ActionsAlerts.kt b/android/alerts/recyclerview/src/main/kotlin/dev/inmo/micro_utils/android/alerts/recyclerview/ActionsAlerts.kt index d7a1de7ba48..2fab342c66c 100644 --- a/android/alerts/recyclerview/src/main/kotlin/dev/inmo/micro_utils/android/alerts/recyclerview/ActionsAlerts.kt +++ b/android/alerts/recyclerview/src/main/kotlin/dev/inmo/micro_utils/android/alerts/recyclerview/ActionsAlerts.kt @@ -35,9 +35,9 @@ class ActionViewHolder( } class ActionsRecyclerViewAdapter( - data: List, + override val data: List, private val dialogInterfaceGetter: () -> DialogInterface -) : RecyclerViewAdapter(data) { +) : RecyclerViewAdapter() { override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): AbstractViewHolder = ActionViewHolder( parent, dialogInterfaceGetter ) diff --git a/android/recyclerview/src/main/kotlin/dev/inmo/micro_utils/android/recyclerview/RecyclerViewAdapter.kt b/android/recyclerview/src/main/kotlin/dev/inmo/micro_utils/android/recyclerview/RecyclerViewAdapter.kt index 96d1020fff6..a5dc0e93450 100644 --- a/android/recyclerview/src/main/kotlin/dev/inmo/micro_utils/android/recyclerview/RecyclerViewAdapter.kt +++ b/android/recyclerview/src/main/kotlin/dev/inmo/micro_utils/android/recyclerview/RecyclerViewAdapter.kt @@ -1,12 +1,13 @@ package dev.inmo.micro_utils.android.recyclerview import android.view.View +import android.view.ViewGroup import androidx.recyclerview.widget.RecyclerView -abstract class RecyclerViewAdapter( - val data: List -): RecyclerView.Adapter>() { +abstract class RecyclerViewAdapter: RecyclerView.Adapter>() { + protected abstract val data: List + var emptyView: View? = null set(value) { field = value @@ -66,3 +67,11 @@ abstract class RecyclerViewAdapter( } } } + +fun RecyclerViewAdapter( + data: List, + onCreateViewHolder: (parent: ViewGroup, viewType: Int) -> AbstractViewHolder +) = object : RecyclerViewAdapter() { + override val data: List = data + override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): AbstractViewHolder = onCreateViewHolder(parent, viewType) +} diff --git a/common/src/main/kotlin/dev/inmo/micro_utils/common/ViewVisibility.kt b/common/src/main/kotlin/dev/inmo/micro_utils/common/ViewVisibility.kt index 3bdc1f3868a..5c842f98883 100644 --- a/common/src/main/kotlin/dev/inmo/micro_utils/common/ViewVisibility.kt +++ b/common/src/main/kotlin/dev/inmo/micro_utils/common/ViewVisibility.kt @@ -33,3 +33,15 @@ fun View.toggleVisibility(goneOnHide: Boolean = true) { show() } } + +fun View.changeVisibility(show: Boolean = !isShown, goneOnHide: Boolean = true) { + if (show) { + if (goneOnHide) { + gone() + } else { + hide() + } + } else { + show() + } +} diff --git a/gradle.properties b/gradle.properties index 19786d527a8..cb88500f55d 100644 --- a/gradle.properties +++ b/gradle.properties @@ -22,9 +22,9 @@ uuidVersion=0.3.0 # ANDROID -core_ktx_version=1.3.2 -androidx_recycler_version=1.2.0 -appcompat_version=1.2.0 +core_ktx_version=1.5.0 +androidx_recycler_version=1.2.1 +appcompat_version=1.3.0 android_minSdkVersion=19 android_compileSdkVersion=30 @@ -45,5 +45,5 @@ dokka_version=1.4.32 # Project data group=dev.inmo -version=0.5.9 -android_code_version=50 +version=0.5.10 +android_code_version=51 diff --git a/ktor/server/src/jvmMain/kotlin/dev/inmo/micro_utils/ktor/server/configurators/ApplicationCachingHeadersConfigurator.kt b/ktor/server/src/jvmMain/kotlin/dev/inmo/micro_utils/ktor/server/configurators/ApplicationCachingHeadersConfigurator.kt index 9b04dcd3910..eaf14f1f637 100644 --- a/ktor/server/src/jvmMain/kotlin/dev/inmo/micro_utils/ktor/server/configurators/ApplicationCachingHeadersConfigurator.kt +++ b/ktor/server/src/jvmMain/kotlin/dev/inmo/micro_utils/ktor/server/configurators/ApplicationCachingHeadersConfigurator.kt @@ -8,7 +8,7 @@ import kotlinx.serialization.Contextual data class ApplicationCachingHeadersConfigurator( private val elements: List<@Contextual Element> ) : KtorApplicationConfigurator { - interface Element { operator fun CachingHeaders.Configuration.invoke() } + fun interface Element { operator fun CachingHeaders.Configuration.invoke() } override fun Application.configure() { install(CachingHeaders) { diff --git a/ktor/server/src/jvmMain/kotlin/dev/inmo/micro_utils/ktor/server/configurators/ApplicationRoutingConfigurator.kt b/ktor/server/src/jvmMain/kotlin/dev/inmo/micro_utils/ktor/server/configurators/ApplicationRoutingConfigurator.kt index 8bb687a7744..2550dcb813b 100644 --- a/ktor/server/src/jvmMain/kotlin/dev/inmo/micro_utils/ktor/server/configurators/ApplicationRoutingConfigurator.kt +++ b/ktor/server/src/jvmMain/kotlin/dev/inmo/micro_utils/ktor/server/configurators/ApplicationRoutingConfigurator.kt @@ -10,17 +10,18 @@ import kotlinx.serialization.Serializable class ApplicationRoutingConfigurator( private val elements: List<@Contextual Element> ) : KtorApplicationConfigurator { - interface Element { operator fun Route.invoke() } + fun interface Element { operator fun Route.invoke() } + private val rootInstaller = Element { + elements.forEach { + it.apply { invoke() } + } + } override fun Application.configure() { - try { - feature(Routing) - } catch (e: IllegalStateException) { - install(Routing) { - elements.forEach { - it.apply { invoke() } - } - } + featureOrNull(Routing) ?.apply { + rootInstaller.apply { invoke() } + } ?: install(Routing) { + rootInstaller.apply { invoke() } } } } diff --git a/ktor/server/src/jvmMain/kotlin/dev/inmo/micro_utils/ktor/server/configurators/ApplicationSessionsConfigurator.kt b/ktor/server/src/jvmMain/kotlin/dev/inmo/micro_utils/ktor/server/configurators/ApplicationSessionsConfigurator.kt index 60513e24702..4ed2d16b009 100644 --- a/ktor/server/src/jvmMain/kotlin/dev/inmo/micro_utils/ktor/server/configurators/ApplicationSessionsConfigurator.kt +++ b/ktor/server/src/jvmMain/kotlin/dev/inmo/micro_utils/ktor/server/configurators/ApplicationSessionsConfigurator.kt @@ -8,7 +8,7 @@ import kotlinx.serialization.Contextual class ApplicationSessionsConfigurator( private val elements: List<@Contextual Element> ) : KtorApplicationConfigurator { - interface Element { operator fun Sessions.Configuration.invoke() } + fun interface Element { operator fun Sessions.Configuration.invoke() } override fun Application.configure() { install(Sessions) { diff --git a/ktor/server/src/jvmMain/kotlin/dev/inmo/micro_utils/ktor/server/configurators/StatusPagesConfigurator.kt b/ktor/server/src/jvmMain/kotlin/dev/inmo/micro_utils/ktor/server/configurators/StatusPagesConfigurator.kt index 7bb090df3c1..7e25ba1482b 100644 --- a/ktor/server/src/jvmMain/kotlin/dev/inmo/micro_utils/ktor/server/configurators/StatusPagesConfigurator.kt +++ b/ktor/server/src/jvmMain/kotlin/dev/inmo/micro_utils/ktor/server/configurators/StatusPagesConfigurator.kt @@ -8,7 +8,7 @@ import kotlinx.serialization.Contextual class StatusPagesConfigurator( private val elements: List<@Contextual Element> ) : KtorApplicationConfigurator { - interface Element { operator fun StatusPages.Configuration.invoke() } + fun interface Element { operator fun StatusPages.Configuration.invoke() } override fun Application.configure() { install(StatusPages) { diff --git a/pagination/common/src/commonMain/kotlin/dev/inmo/micro_utils/pagination/utils/DoForAll.kt b/pagination/common/src/commonMain/kotlin/dev/inmo/micro_utils/pagination/utils/DoForAll.kt index 761e3b63629..b77f3b59848 100644 --- a/pagination/common/src/commonMain/kotlin/dev/inmo/micro_utils/pagination/utils/DoForAll.kt +++ b/pagination/common/src/commonMain/kotlin/dev/inmo/micro_utils/pagination/utils/DoForAll.kt @@ -33,3 +33,8 @@ suspend fun doAllWithCurrentPaging( block ) } + +suspend fun doForAllWithCurrentPaging( + initialPagination: Pagination = FirstPagePagination(), + block: suspend (Pagination) -> PaginationResult +) = doAllWithCurrentPaging(initialPagination, block) diff --git a/repos/common/src/commonMain/kotlin/dev/inmo/micro_utils/repos/OneToManyKeyValueRepo.kt b/repos/common/src/commonMain/kotlin/dev/inmo/micro_utils/repos/OneToManyKeyValueRepo.kt index 001c618851b..df5bafa86d5 100644 --- a/repos/common/src/commonMain/kotlin/dev/inmo/micro_utils/repos/OneToManyKeyValueRepo.kt +++ b/repos/common/src/commonMain/kotlin/dev/inmo/micro_utils/repos/OneToManyKeyValueRepo.kt @@ -1,6 +1,7 @@ package dev.inmo.micro_utils.repos import dev.inmo.micro_utils.pagination.* +import dev.inmo.micro_utils.pagination.utils.doForAllWithCurrentPaging import dev.inmo.micro_utils.pagination.utils.getAllWithNextPaging import kotlinx.coroutines.flow.Flow @@ -47,6 +48,7 @@ interface WriteOneToManyKeyValueRepo : Repo { suspend fun remove(toRemove: Map>) suspend fun clear(k: Key) + suspend fun clearWithValue(v: Value) suspend fun set(toSet: Map>) { toSet.keys.forEach { key -> clear(key) } @@ -87,7 +89,19 @@ suspend inline fun WriteOneToManyKeyValueRepo.set( k: Key, vararg v: Value ) = set(k, v.toList()) -interface OneToManyKeyValueRepo : ReadOneToManyKeyValueRepo, WriteOneToManyKeyValueRepo +interface OneToManyKeyValueRepo : ReadOneToManyKeyValueRepo, WriteOneToManyKeyValueRepo { + override suspend fun clearWithValue(v: Value) { + doWithPagination { + val keysResult = keys(v, it) + + if (keysResult.results.isNotEmpty()) { + remove(keysResult.results.map { it to listOf(v) }) + } + + keysResult.currentPageIfNotEmpty() + } + } +} typealias KeyValuesRepo = OneToManyKeyValueRepo suspend inline fun WriteOneToManyKeyValueRepo.remove( diff --git a/repos/common/src/commonMain/kotlin/dev/inmo/micro_utils/repos/mappers/OneToManyKeyValueMappers.kt b/repos/common/src/commonMain/kotlin/dev/inmo/micro_utils/repos/mappers/OneToManyKeyValueMappers.kt index dd777d15088..97b872a7b1c 100644 --- a/repos/common/src/commonMain/kotlin/dev/inmo/micro_utils/repos/mappers/OneToManyKeyValueMappers.kt +++ b/repos/common/src/commonMain/kotlin/dev/inmo/micro_utils/repos/mappers/OneToManyKeyValueMappers.kt @@ -114,6 +114,7 @@ open class MapperWriteOneToManyKeyValueRepo( } override suspend fun clear(k: FromKey) = to.clear(k.toOutKey()) + override suspend fun clearWithValue(v: FromValue) = to.clearWithValue(v.toOutValue()) } @Suppress("NOTHING_TO_INLINE") diff --git a/repos/common/src/main/kotlin/dev/inmo/micro_utils/repos/DatabaseOperations.kt b/repos/common/src/main/kotlin/dev/inmo/micro_utils/repos/DatabaseOperations.kt index 35b04fa0c4b..d473afd0255 100644 --- a/repos/common/src/main/kotlin/dev/inmo/micro_utils/repos/DatabaseOperations.kt +++ b/repos/common/src/main/kotlin/dev/inmo/micro_utils/repos/DatabaseOperations.kt @@ -74,6 +74,19 @@ fun SQLiteDatabase.select( table, columns, selection, selectionArgs, groupBy, having, orderBy, limit ) +fun SQLiteDatabase.selectDistinct( + table: String, + columns: Array? = null, + selection: String? = null, + selectionArgs: Array? = null, + groupBy: String? = null, + having: String? = null, + orderBy: String? = null, + limit: String? = null +) = query( + true, table, columns, selection, selectionArgs, groupBy, having, orderBy, limit +) + fun makePlaceholders(count: Int): String { return (0 until count).joinToString { "?" } } diff --git a/repos/common/src/main/kotlin/dev/inmo/micro_utils/repos/onetomany/OneToManyAndroidRepo.kt b/repos/common/src/main/kotlin/dev/inmo/micro_utils/repos/onetomany/OneToManyAndroidRepo.kt index d8c4c7d248a..31c7be0ac99 100644 --- a/repos/common/src/main/kotlin/dev/inmo/micro_utils/repos/onetomany/OneToManyAndroidRepo.kt +++ b/repos/common/src/main/kotlin/dev/inmo/micro_utils/repos/onetomany/OneToManyAndroidRepo.kt @@ -3,10 +3,7 @@ package dev.inmo.micro_utils.repos.onetomany import android.database.sqlite.SQLiteOpenHelper import androidx.core.content.contentValuesOf import dev.inmo.micro_utils.common.mapNotNullA -import dev.inmo.micro_utils.pagination.FirstPagePagination -import dev.inmo.micro_utils.pagination.Pagination -import dev.inmo.micro_utils.pagination.PaginationResult -import dev.inmo.micro_utils.pagination.createPaginationResult +import dev.inmo.micro_utils.pagination.* import dev.inmo.micro_utils.pagination.utils.reverse import dev.inmo.micro_utils.repos.* import kotlinx.coroutines.flow.Flow @@ -38,7 +35,9 @@ class OneToManyAndroidRepo( override val onDataCleared: Flow = _onDataCleared.asSharedFlow() private val idColumnName = "id" + private val idColumnArray = arrayOf(idColumnName) private val valueColumnName = "value" + private val valueColumnArray = arrayOf(valueColumnName) init { helper.blockingWritableTransaction { @@ -108,7 +107,7 @@ class OneToManyAndroidRepo( } override suspend fun contains(k: Key): Boolean = helper.blockingReadableTransaction { - select(tableName, selection = "$idColumnName=?", selectionArgs = arrayOf(k.keyAsString()), limit = FirstPagePagination(1).limitClause()).use { + select(tableName, selection = "$idColumnName=?", selectionArgs = arrayOf(k.keyAsString()), limit = firstPageWithOneElementPagination.limitClause()).use { it.count > 0 } } @@ -124,7 +123,7 @@ class OneToManyAndroidRepo( } } - override suspend fun count(): Long =helper.blockingReadableTransaction { + override suspend fun count(): Long = helper.blockingReadableTransaction { select( tableName ).use { @@ -133,7 +132,7 @@ class OneToManyAndroidRepo( }.toLong() override suspend fun count(k: Key): Long = helper.blockingReadableTransaction { - select(tableName, selection = "$idColumnName=?", selectionArgs = arrayOf(k.keyAsString()), limit = FirstPagePagination(1).limitClause()).use { + selectDistinct(tableName, columns = valueColumnArray, selection = "$idColumnName=?", selectionArgs = arrayOf(k.keyAsString()), limit = FirstPagePagination(1).limitClause()).use { it.count } }.toLong() @@ -143,10 +142,17 @@ class OneToManyAndroidRepo( pagination: Pagination, reversed: Boolean ): PaginationResult = count(k).let { count -> + if (pagination.firstIndex >= count) { + return@let emptyList().createPaginationResult( + pagination, + count + ) + } val resultPagination = pagination.let { if (reversed) pagination.reverse(count) else pagination } helper.blockingReadableTransaction { select( tableName, + valueColumnArray, selection = "$idColumnName=?", selectionArgs = arrayOf(k.keyAsString()), limit = resultPagination.limitClause() @@ -169,10 +175,17 @@ class OneToManyAndroidRepo( pagination: Pagination, reversed: Boolean ): PaginationResult = count().let { count -> + if (pagination.firstIndex >= count) { + return@let emptyList().createPaginationResult( + pagination, + count + ) + } val resultPagination = pagination.let { if (reversed) pagination.reverse(count) else pagination } helper.blockingReadableTransaction { - select( + selectDistinct( tableName, + idColumnArray, limit = resultPagination.limitClause() ).use { c -> mutableListOf().also { @@ -196,8 +209,9 @@ class OneToManyAndroidRepo( ): PaginationResult = count().let { count -> val resultPagination = pagination.let { if (reversed) pagination.reverse(count) else pagination } helper.blockingReadableTransaction { - select( + selectDistinct( tableName, + idColumnArray, selection = "$valueColumnName=?", selectionArgs = arrayOf(v.valueAsString()), limit = resultPagination.limitClause() diff --git a/repos/inmemory/src/commonMain/kotlin/dev/inmo/micro_utils/repos/MapOneToManyKeyValueRepo.kt b/repos/inmemory/src/commonMain/kotlin/dev/inmo/micro_utils/repos/MapOneToManyKeyValueRepo.kt index 48bc9c697e0..08ebbb45f90 100644 --- a/repos/inmemory/src/commonMain/kotlin/dev/inmo/micro_utils/repos/MapOneToManyKeyValueRepo.kt +++ b/repos/inmemory/src/commonMain/kotlin/dev/inmo/micro_utils/repos/MapOneToManyKeyValueRepo.kt @@ -86,6 +86,12 @@ class MapWriteOneToManyKeyValueRepo( override suspend fun clear(k: Key) { map.remove(k) ?.also { _onDataCleared.emit(k) } } + + override suspend fun clearWithValue(v: Value) { + map.forEach { (k, values) -> + if (values.remove(v)) _onValueRemoved.emit(k to v) + } + } } class MapOneToManyKeyValueRepo( diff --git a/repos/ktor/client/src/commonMain/kotlin/dev/inmo/micro_utils/repos/ktor/client/one_to_many/KtorWriteOneToManyKeyValueRepo.kt b/repos/ktor/client/src/commonMain/kotlin/dev/inmo/micro_utils/repos/ktor/client/one_to_many/KtorWriteOneToManyKeyValueRepo.kt index 5263c53f83c..1a2122d25d7 100644 --- a/repos/ktor/client/src/commonMain/kotlin/dev/inmo/micro_utils/repos/ktor/client/one_to_many/KtorWriteOneToManyKeyValueRepo.kt +++ b/repos/ktor/client/src/commonMain/kotlin/dev/inmo/micro_utils/repos/ktor/client/one_to_many/KtorWriteOneToManyKeyValueRepo.kt @@ -67,6 +67,15 @@ class KtorWriteOneToManyKeyValueRepo ( Unit.serializer(), ) + override suspend fun clearWithValue(v: Value) = unifiedRequester.unipost( + buildStandardUrl( + baseUrl, + clearWithValueRoute, + ), + BodyPair(valueSerializer, v), + Unit.serializer(), + ) + override suspend fun set(toSet: Map>) = unifiedRequester.unipost( buildStandardUrl( baseUrl, @@ -75,4 +84,4 @@ class KtorWriteOneToManyKeyValueRepo ( BodyPair(keyValueMapSerializer, toSet), Unit.serializer(), ) -} \ No newline at end of file +} diff --git a/repos/ktor/common/src/commonMain/kotlin/dev/inmo/micro_utils/repos/ktor/common/one_to_many/OneToManyRoutes.kt b/repos/ktor/common/src/commonMain/kotlin/dev/inmo/micro_utils/repos/ktor/common/one_to_many/OneToManyRoutes.kt index c1a2df1eac1..b10a45476ff 100644 --- a/repos/ktor/common/src/commonMain/kotlin/dev/inmo/micro_utils/repos/ktor/common/one_to_many/OneToManyRoutes.kt +++ b/repos/ktor/common/src/commonMain/kotlin/dev/inmo/micro_utils/repos/ktor/common/one_to_many/OneToManyRoutes.kt @@ -14,4 +14,5 @@ const val onDataClearedRoute = "onDataCleared" const val addRoute = "add" const val removeRoute = "remove" const val clearRoute = "clear" -const val setRoute = "set" \ No newline at end of file +const val clearWithValueRoute = "clearWithValue" +const val setRoute = "set" diff --git a/repos/ktor/server/src/jvmMain/kotlin/dev/inmo/micro_utils/repos/ktor/server/one_to_many/ConfigureOneToManyWriteKeyValueRepoRoutes.kt b/repos/ktor/server/src/jvmMain/kotlin/dev/inmo/micro_utils/repos/ktor/server/one_to_many/ConfigureOneToManyWriteKeyValueRepoRoutes.kt index c9039bb87cb..2ef6ee6c450 100644 --- a/repos/ktor/server/src/jvmMain/kotlin/dev/inmo/micro_utils/repos/ktor/server/one_to_many/ConfigureOneToManyWriteKeyValueRepoRoutes.kt +++ b/repos/ktor/server/src/jvmMain/kotlin/dev/inmo/micro_utils/repos/ktor/server/one_to_many/ConfigureOneToManyWriteKeyValueRepoRoutes.kt @@ -72,6 +72,17 @@ fun Route.configureOneToManyWriteKeyValueRepoRoutes( } } + post(clearWithValueRoute) { + unifiedRouter.apply { + val v = uniload(valueSerializer) + + unianswer( + Unit.serializer(), + originalRepo.clearWithValue(v), + ) + } + } + post(setRoute) { unifiedRouter.apply { val obj = uniload(keyValueMapSerializer)