Skip to content

Commit

Permalink
feat: filter on ratings screen
Browse files Browse the repository at this point in the history
  • Loading branch information
makeevrserg committed Mar 15, 2024
1 parent 1f74534 commit c0863d5
Show file tree
Hide file tree
Showing 21 changed files with 303 additions and 232 deletions.
2 changes: 2 additions & 0 deletions modules/features/rating/impl/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,8 @@ kotlin {
implementation(libs.klibs.mikro.platform)
implementation(libs.klibs.kstorage)
implementation(libs.klibs.kdi)
// settings
implementation(libs.mppsettings)
// Decompose
implementation(libs.decompose.core)
implementation(libs.essenty)
Expand Down
Original file line number Diff line number Diff line change
@@ -1,14 +1,10 @@
package com.makeevrserg.empireprojekt.mobile.features.rating.users.data

import kotlinx.coroutines.flow.StateFlow
import com.makeevrserg.empireprojekt.mobile.features.rating.users.data.paging.RatingsPagingCollector
import ru.astrainteractive.empireapi.models.rating.RatingUserModel
import ru.astrainteractive.empireapi.models.rating.RatingsFilterModel
import ru.astrainteractive.klibs.paging.context.IntPageContext
import ru.astrainteractive.klibs.paging.state.PagingState

internal interface RatingUsersRepository {
fun updateRequest(request: RatingsFilterModel)
suspend fun loadNextPage()
suspend fun reset()
val state: StateFlow<PagingState<RatingUserModel, IntPageContext>>
val pagingCollector: RatingsPagingCollector<RatingUserModel>
suspend fun updateFilter(buildFilter: (RatingsFilterModel) -> RatingsFilterModel)
}
Original file line number Diff line number Diff line change
@@ -1,56 +1,62 @@
package com.makeevrserg.empireprojekt.mobile.features.rating.users.data

import com.makeevrserg.empireprojekt.mobile.api.empireapi.RatingApi
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.update
import com.makeevrserg.empireprojekt.mobile.features.rating.users.data.paging.RatingsPagingCollector
import com.makeevrserg.empireprojekt.mobile.features.rating.users.storage.RatingsFilterStorageValue
import com.russhwolf.settings.Settings
import kotlinx.coroutines.Job
import kotlinx.coroutines.cancelAndJoin
import kotlinx.coroutines.coroutineScope
import kotlinx.coroutines.job
import kotlinx.coroutines.withContext
import ru.astrainteractive.empireapi.models.rating.RatingUserModel
import ru.astrainteractive.empireapi.models.rating.RatingsFilterModel
import ru.astrainteractive.klibs.mikro.core.dispatchers.KotlinDispatchers
import ru.astrainteractive.klibs.paging.IntPagerCollector
import ru.astrainteractive.klibs.paging.context.IntPageContext
import ru.astrainteractive.klibs.paging.PagingCollectorExt.updatePageContext
import ru.astrainteractive.klibs.paging.data.LambdaPagedListDataSource
import ru.astrainteractive.klibs.paging.state.PagingState

internal class RatingUsersRepositoryImpl(
private val settings: Settings,
private val ratingApi: RatingApi,
private val dispatchers: KotlinDispatchers
) : RatingUsersRepository {
private val request = MutableStateFlow(RatingsFilterModel())

private val pagingCollector = IntPagerCollector(
initialPage = 0,
pager = LambdaPagedListDataSource {
loadPage(it.pageContext.page)
}
private val townsFilterStorageValue = RatingsFilterStorageValue(
settings = settings,
key = "ratings_filter_storage_key"
)

override val state: StateFlow<PagingState<RatingUserModel, IntPageContext>> = pagingCollector.state

private suspend fun loadPage(page: Int): Result<List<RatingUserModel>> {
return runCatching {
private var currentJob: Job? = null

private suspend fun loadPage(
page: Int,
filter: RatingsFilterModel
): Result<List<RatingUserModel>> = coroutineScope {
currentJob?.cancelAndJoin()
currentJob = this.coroutineContext.job
runCatching {
withContext(dispatchers.IO) {
ratingApi.users(
page = page,
size = 10,
body = request.value
body = filter
).data
}
}.onFailure(Throwable::printStackTrace)
}

override suspend fun reset() {
pagingCollector.reset()
}
override val pagingCollector = RatingsPagingCollector(
initialPage = 0,
pager = LambdaPagedListDataSource {
loadPage(it.pageContext.page, it.pageContext.filter)
},
initialFilter = townsFilterStorageValue.value
)

override suspend fun loadNextPage() {
override suspend fun updateFilter(buildFilter: (RatingsFilterModel) -> RatingsFilterModel) {
val newFilter = buildFilter.invoke(pagingCollector.state.value.pageContext.filter)
townsFilterStorageValue.save(newFilter)
currentJob?.cancelAndJoin()
pagingCollector.reset()
pagingCollector.updatePageContext { it.copy(filter = newFilter) }
pagingCollector.loadNextPage()
}

override fun updateRequest(request: RatingsFilterModel) {
this.request.update {
request
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
package com.makeevrserg.empireprojekt.mobile.features.rating.users.data.paging

import ru.astrainteractive.empireapi.models.rating.RatingsFilterModel
import ru.astrainteractive.klibs.paging.context.PageContext

internal data class RatingsPageContext(
val page: Int = 0,
val filter: RatingsFilterModel
) : PageContext {
object Factory : PageContext.Factory<RatingsPageContext> {
override fun next(pageContext: RatingsPageContext): RatingsPageContext {
return pageContext.copy(page = pageContext.page + 1)
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
package com.makeevrserg.empireprojekt.mobile.features.rating.users.data.paging

import ru.astrainteractive.empireapi.models.rating.RatingsFilterModel
import ru.astrainteractive.klibs.paging.DefaultPagingCollector
import ru.astrainteractive.klibs.paging.PagingCollector
import ru.astrainteractive.klibs.paging.data.PagedListDataSource
import ru.astrainteractive.klibs.paging.state.PagingState

internal class RatingsPagingCollector<T>(
private val initialPage: Int = 0,
private val pageSize: Int = 10,
private val pager: PagedListDataSource<T, RatingsPageContext>,
private val initialFilter: RatingsFilterModel
) : PagingCollector<T, RatingsPageContext> by DefaultPagingCollector(
initialPagingState = PagingState(
pageContext = RatingsPageContext(page = initialPage, filter = initialFilter),
pageSizeAtLeast = pageSize,
isLastPage = false,
isLoading = false,
isFailure = false,
items = emptyList()
),
pager = pager,
pageContextFactory = RatingsPageContext.Factory
)
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,8 @@ interface RatingUsersModule {
private val ratingUsersRepository: RatingUsersRepository by lazy {
RatingUsersRepositoryImpl(
ratingApi = apiEmpireApiModule.ratingApi,
dispatchers = coreModule.dispatchers
dispatchers = coreModule.dispatchers,
settings = coreModule.settings
)
}

Expand Down
Original file line number Diff line number Diff line change
@@ -1,48 +1,54 @@
package com.makeevrserg.empireprojekt.mobile.features.rating.users.presentation

import com.arkivanov.decompose.ComponentContext
import com.arkivanov.mvikotlin.core.instancekeeper.getStore
import com.arkivanov.mvikotlin.extensions.coroutines.stateFlow
import com.arkivanov.mvikotlin.main.store.DefaultStoreFactory
import com.arkivanov.essenty.instancekeeper.getOrCreate
import com.makeevrserg.empireprojekt.mobile.features.rating.users.di.RatingUsersDependencies
import com.makeevrserg.empireprojekt.mobile.features.rating.users.presentation.RatingUsersComponent.Model
import com.makeevrserg.empireprojekt.mobile.features.rating.users.store.RatingUsersStore
import com.makeevrserg.empireprojekt.mobile.features.rating.users.store.RatingUsersStoreFactory
import ru.astrainteractive.klibs.mikro.core.util.mapStateFlow
import kotlinx.coroutines.flow.StateFlow
import ru.astrainteractive.empireapi.models.towny.LocalSortOrder
import ru.astrainteractive.klibs.mikro.core.util.next

internal class DefaultRatingUsersComponent(
componentContext: ComponentContext,
private val dependencies: RatingUsersDependencies,
private val onShowUserRatingsClicked: (userId: Long, userName: String) -> Unit
) : RatingUsersComponent,
ComponentContext by componentContext {
private val ratingUsersFeature = instanceKeeper.getOrCreate {
RatingUsersFeature(dependencies = dependencies)
}
override val model: StateFlow<Model>
get() = ratingUsersFeature.model

private val store = instanceKeeper.getStore {
RatingUsersStoreFactory(
storeFactory = DefaultStoreFactory(),
dependencies = dependencies
).create()
private fun LocalSortOrder?.next(): LocalSortOrder {
return this?.next(LocalSortOrder.entries.toTypedArray()) ?: LocalSortOrder.entries.first()
}

override val model = store.stateFlow.mapStateFlow {
Model(
items = it.items,
request = it.request,
isLoading = it.isLoading,
isFailure = it.isFailure,
isLastPage = it.isLastPage
)
override fun reset() {
ratingUsersFeature.reset()
}

override fun showUserRatings(id: Long, userName: String) {
onShowUserRatingsClicked.invoke(id, userName)
override fun loadNextPage() {
ratingUsersFeature.loadNextPage()
}

override fun reset() {
RatingUsersStore.Intent.Reset.run(store::accept)
override fun updateQuery(query: String) {
ratingUsersFeature.updateFilter { it.copy(query = query) }
}

override fun loadNextPage() {
RatingUsersStore.Intent.LoadNextPage.run(store::accept)
override fun nextLastUpdateSort() {
ratingUsersFeature.updateFilter { it.copy(lastUpdatedSort = it.lastUpdatedSort.next()) }
}

override fun nextRatingSort() {
ratingUsersFeature.updateFilter { it.copy(ratingSort = it.ratingSort.next()) }
}

override fun nextNameSort() {
ratingUsersFeature.updateFilter { it.copy(nameSort = it.nameSort.next()) }
}

override fun showUserRatings(id: Long, userName: String) {
onShowUserRatingsClicked.invoke(id, userName)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -11,11 +11,19 @@ interface RatingUsersComponent {

fun loadNextPage()

fun updateQuery(query: String)

fun nextLastUpdateSort()

fun nextRatingSort()

fun nextNameSort()

fun showUserRatings(id: Long, userName: String)

data class Model(
val items: List<RatingUserModel> = emptyList(),
val request: RatingsFilterModel = RatingsFilterModel(),
val filter: RatingsFilterModel = RatingsFilterModel(),
val isLoading: Boolean = false,
val isFailure: Boolean = false,
val isLastPage: Boolean = false
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
package com.makeevrserg.empireprojekt.mobile.features.rating.users.presentation

import com.makeevrserg.empireprojekt.mobile.features.rating.users.di.RatingUsersDependencies
import kotlinx.coroutines.launch
import ru.astrainteractive.empireapi.models.rating.RatingsFilterModel
import ru.astrainteractive.klibs.mikro.core.util.mapStateFlow
import ru.astrainteractive.klibs.mikro.extensions.arkivanov.CoroutineFeature

internal class RatingUsersFeature(
dependencies: RatingUsersDependencies
) : CoroutineFeature by CoroutineFeature.Main(),
RatingUsersDependencies by dependencies {

fun loadNextPage() {
launch { ratingUsersRepository.pagingCollector.loadNextPage() }
}

fun reset() {
ratingUsersRepository.pagingCollector.reset()
}

fun updateFilter(block: (RatingsFilterModel) -> RatingsFilterModel) = launch {
ratingUsersRepository.updateFilter(block)
}

val model = ratingUsersRepository.pagingCollector.state.mapStateFlow {
RatingUsersComponent.Model(
items = it.items,
isLoading = it.isLoading,
isFailure = it.isFailure,
isLastPage = it.isLastPage,
filter = it.pageContext.filter
)
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
package com.makeevrserg.empireprojekt.mobile.features.rating.users.storage

import com.makeevrserg.empireprojekt.mobile.services.core.settings.SettingsExt.getEnumByOrdinalOrDefault
import com.makeevrserg.empireprojekt.mobile.services.core.settings.SettingsExt.putEnumByOrdinal
import com.russhwolf.settings.Settings
import ru.astrainteractive.empireapi.models.rating.RatingsFilterModel
import ru.astrainteractive.empireapi.models.towny.LocalSortOrder
import ru.astrainteractive.klibs.kstorage.MutableStorageValue
import ru.astrainteractive.klibs.kstorage.api.MutableStorageValue

private class Keys(key: String) {
val queryKey: String = "${key}queryKey"
val nameSortKey: String = "${key}nameSortKey"
val lastUpdatedSortKey: String = "${key}lastUpdatedSortKey"
val ratingSortKey: String = "${key}ratingSortKey"
}

internal class RatingsFilterStorageValue(
settings: Settings,
key: String,
default: RatingsFilterModel = RatingsFilterModel()
) : MutableStorageValue<RatingsFilterModel> by MutableStorageValue(
default = default,
loadSettingsValue = {
val keys = Keys(key)
RatingsFilterModel(
query = settings.getString(keys.queryKey, default.query),
nameSort = settings.getEnumByOrdinalOrDefault(
LocalSortOrder.entries,
keys.nameSortKey,
LocalSortOrder.NONE
),
lastUpdatedSort = settings.getEnumByOrdinalOrDefault(
LocalSortOrder.entries,
keys.lastUpdatedSortKey,
LocalSortOrder.NONE
),
ratingSort = settings.getEnumByOrdinalOrDefault(
LocalSortOrder.entries,
keys.ratingSortKey,
LocalSortOrder.NONE
),
)
},
saveSettingsValue = {
val keys = Keys(key)
settings.putString(keys.queryKey, it.query)
settings.putEnumByOrdinal(keys.nameSortKey, it.nameSort)
settings.putEnumByOrdinal(keys.lastUpdatedSortKey, it.lastUpdatedSort)
settings.putEnumByOrdinal(keys.ratingSortKey, it.ratingSort)
}
)

This file was deleted.

Loading

0 comments on commit c0863d5

Please sign in to comment.