Skip to content
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

RENTING-65: Filters Logic #35

Draft
wants to merge 2 commits into
base: master
Choose a base branch
from
Draft
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
Original file line number Diff line number Diff line change
Expand Up @@ -4,4 +4,17 @@ import com.arkivanov.decompose.ComponentContext

internal class DefaultFiltersComponent(
componentContext: ComponentContext,
) : FiltersComponent, ComponentContext by componentContext
) : FiltersComponent, ComponentContext by componentContext {

override fun onFilterClick(id: String) {
// TODO add store
}

override fun onRangeFilterChange(id: String, value: IntRange) {
// TODO add store
}

override fun onTextFilterChange(id: String, value: String) {
// TODO add store
}
}
Original file line number Diff line number Diff line change
@@ -1,3 +1,10 @@
package com.renting.app.feature.filters

interface FiltersComponent
interface FiltersComponent {

fun onFilterClick(id: String)

fun onRangeFilterChange(id: String, value: IntRange)

fun onTextFilterChange(id: String, value: String)
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
package com.renting.app.feature.filters

import com.arkivanov.mvikotlin.core.store.Store
import com.renting.app.feature.filters.FiltersStore.Intent
import com.renting.app.feature.filters.FiltersStore.State
import com.renting.app.feature.property.PropertyType

interface FiltersStore : Store<Intent, State, Nothing> {

sealed interface Intent {
data class SelectToggle(
val id: String,
val name: String,
) : Intent
data class ChangeRangeFilter(
val id: String,
val range: IntRange,
) : Intent

data class SelectFilterValue(
val id: String,
val value: String,
) : Intent
}

data class State(
val filterGroups: List<PropertyFilterGroup> = createFilters(),
)
}

private fun createFilters(): List<PropertyFilterGroup> = buildList {
val category = PropertyFilterGroup(
name = "Category",
filter = PropertyTypeFilter(
id = "PROPERTY_TYPE",
toggles = listOf(
SelectionPropertyFilter.Toggle(name = "House", value = PropertyType.FAMILY_HOUSE),
SelectionPropertyFilter.Toggle(name = "Apartment", value = PropertyType.APARTMENT),
SelectionPropertyFilter.Toggle(name = "Land", value = PropertyType.LAND),
),
)
)
add(category)

val price = PropertyFilterGroup(
name = "Price Range",
filter = PricePropertyFilter(
id = "PRICE"
),
)
add(price)

val location = PropertyFilterGroup(
name = "Location",
filter = PropertyLocationChooser(
id = "PROPERTY_LOCATION",
values = listOf(
"Moscow",
"Saint Petersburg",
"Yekaterinburg",
"Novosibirsk",
"Kazan",
),
selectedValue = "Moscow",
),
)
add(location)
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,106 @@
package com.renting.app.feature.filters

import com.arkivanov.mvikotlin.core.store.Reducer
import com.arkivanov.mvikotlin.core.store.Store
import com.arkivanov.mvikotlin.core.store.StoreFactory
import com.arkivanov.mvikotlin.core.utils.ExperimentalMviKotlinApi
import com.arkivanov.mvikotlin.extensions.coroutines.coroutineExecutorFactory
import com.renting.app.feature.filters.FiltersStore.Intent
import com.renting.app.feature.filters.FiltersStore.State

class FiltersStoreFactory(
private val storeFactory: StoreFactory,
) {

private fun createReducer() = Reducer<State, Msg> { msg ->
when (msg) {
is Msg.SelectToggle -> copy(
filterGroups = filterGroups.map { group ->
val updatedFilters = group.filters.map { filter ->
if (filter is PropertyTypeFilter && filter.id == msg.id) {
filter.setActiveToggle(msg.name)
} else {
filter
}
}
group.copy(filters = updatedFilters)
}
)
is Msg.ChangeRangeFilter -> copy(
filterGroups = filterGroups.map { group ->
val updatedFilters = group.filters.map { filter ->
if (filter is PricePropertyFilter && filter.id == msg.id) {
filter.copy(range = msg.range)
} else {
filter
}
}
group.copy(filters = updatedFilters)
},
)
is Msg.SelectFilterValue -> copy(
filterGroups = filterGroups.map { group ->
val updatedFilters = group.filters.map { filter ->
if (filter is PropertyLocationChooser && filter.id == msg.id) {
filter.copy(selectedValue = msg.value)
} else {
filter
}
}
group.copy(filters = updatedFilters)
},
)
}
}

@OptIn(ExperimentalMviKotlinApi::class)
fun create(): FiltersStore =
object : FiltersStore, Store<Intent, State, Nothing> by storeFactory.create(
name = "Filters",
initialState = State(),
executorFactory = coroutineExecutorFactory<Intent, Nothing, State, Msg, Nothing> {
onIntent<Intent.SelectToggle> { intent ->
dispatch(
Msg.SelectToggle(
id = intent.id,
name = intent.name,
)
)
}
onIntent<Intent.ChangeRangeFilter> { intent ->
dispatch(
Msg.ChangeRangeFilter(
id = intent.id,
range = intent.range,
)
)
}
onIntent<Intent.SelectFilterValue> { intent ->
dispatch(
Msg.SelectFilterValue(
id = intent.id,
value = intent.value,
)
)
}
},
reducer = createReducer(),
) {}

private sealed interface Msg {
data class SelectToggle(
val id: String,
val name: String,
) : Msg

data class ChangeRangeFilter(
val id: String,
val range: IntRange,
) : Msg

class SelectFilterValue(
val id: String,
val value: String,
) : Msg
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
package com.renting.app.feature.filters

import com.renting.app.feature.property.PropertySnippet
import com.renting.app.feature.property.PropertyType

interface PropertyFilter {
val id: String

fun isIncluded(snippet: PropertySnippet): Boolean
}

abstract class SelectionPropertyFilter<T>(
override val id: String,
open val toggles: List<Toggle<T>>,
open val activeToggle: Toggle<T>? = null,
) : PropertyFilter {

abstract val predicate: (T, snippet: PropertySnippet) -> Boolean

abstract fun setActiveToggle(name: String): SelectionPropertyFilter<T>

override fun isIncluded(snippet: PropertySnippet): Boolean {
val activeToggle = activeToggle
return if (activeToggle == null) {
true
} else {
predicate(activeToggle.value, snippet)
}
}

data class Toggle<T>(val name: String, val value: T)
}

data class PricePropertyFilter(
override val id: String,
val range: IntRange = DEFAULT_RANGE,
) : PropertyFilter {

override fun isIncluded(snippet: PropertySnippet): Boolean {
return snippet.price in range
}

companion object {
val DEFAULT_RANGE = 0..100
}
}

data class PropertyLocationChooser(
override val id: String,
val values: List<String>,
val selectedValue: String,
) : PropertyFilter {

override fun isIncluded(snippet: PropertySnippet): Boolean {
return selectedValue in snippet.location
}
}

data class PropertyTypeFilter(
override val id: String,
override val toggles: List<Toggle<PropertyType>>,
override val activeToggle: Toggle<PropertyType>? = null,
): SelectionPropertyFilter<PropertyType>(
id = id,
toggles = toggles,
activeToggle = activeToggle,
) {

override val predicate: (
PropertyType,
snippet: PropertySnippet,
) -> Boolean = { type, snippet -> type == snippet.type }

override fun setActiveToggle(name: String): PropertyTypeFilter {
return copy(
activeToggle = toggles.firstOrNull { it.name == name },
)
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
package com.renting.app.feature.filters

data class PropertyFilterGroup(
val name: String,
val filters: List<PropertyFilter>,
) {

constructor(name: String, filter: PropertyFilter) : this(name, listOf(filter))
}
Original file line number Diff line number Diff line change
@@ -1,3 +1,15 @@
package com.renting.app.feature.filters

class StubFiltersComponent : FiltersComponent
class StubFiltersComponent : FiltersComponent {
override fun onFilterClick(id: String) {
// Nothing to do
}

override fun onRangeFilterChange(id: String, value: IntRange) {
// Nothing to do
}

override fun onTextFilterChange(id: String, value: String) {
// Nothing to do
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
package com.renting.app.feature.filters

interface UiFilter {

val id: String
}

data class ToggleableFilter(
override val id: String,
val toggles: List<Toggle>,
val activeToggle: Toggle?,
) : UiFilter {

data class Toggle(
val name: String,
)
}

data class SlideFilter(
override val id: String,
val range: IntRange,
) : UiFilter

data class TextSelectorFilter(
override val id: String,
val availableValues: List<String>,
val value: String,
) : UiFilter
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
package com.renting.app.feature.filters

data class UiFilterGroup(
val name: String,
val filters: List<UiFilter>,
) {

constructor(name: String, filter: UiFilter) : this(name, listOf(filter))
}