|
| 1 | +package com.alexdeww.reactiveviewmodel.core |
| 2 | + |
| 3 | +import androidx.lifecycle.SavedStateHandle |
| 4 | +import com.alexdeww.reactiveviewmodel.core.property.State |
| 5 | +import com.alexdeww.reactiveviewmodel.widget.BaseVisualControl |
| 6 | +import com.alexdeww.reactiveviewmodel.widget.FormatterAction |
| 7 | +import com.alexdeww.reactiveviewmodel.widget.InputControl |
| 8 | +import com.alexdeww.reactiveviewmodel.widget.RatingControl |
| 9 | +import kotlin.properties.ReadOnlyProperty |
| 10 | +import kotlin.properties.ReadWriteProperty |
| 11 | +import kotlin.reflect.KProperty |
| 12 | + |
| 13 | +fun <T> SavedStateHandle.delegate( |
| 14 | + initValue: (thisRef: ReactiveViewModel, stateHandle: SavedStateHandle, key: String) -> T |
| 15 | +): ReadWriteProperty<ReactiveViewModel, T> = SavedStateProperty(this, initValue) |
| 16 | + |
| 17 | +fun <T : Any> SavedStateHandle.value( |
| 18 | + initialValue: T? = null |
| 19 | +): ReadWriteProperty<ReactiveViewModel, T?> = delegate { _, stateHandle, key -> |
| 20 | + if (stateHandle.contains(key)) stateHandle[key] |
| 21 | + else initialValue |
| 22 | +} |
| 23 | + |
| 24 | +fun <T : Any> SavedStateHandle.valueNonNull( |
| 25 | + defaultValue: T |
| 26 | +): ReadWriteProperty<ReactiveViewModel, T> = delegate { _, stateHandle, key -> |
| 27 | + stateHandle[key] ?: defaultValue |
| 28 | +} |
| 29 | + |
| 30 | +fun <T : Any> SavedStateHandle.state( |
| 31 | + initialValue: T? = null, |
| 32 | + debounceInterval: Long? = null |
| 33 | +): ReadOnlyProperty<ReactiveViewModel, State<T>> = delegate { thisRef, stateHandle, key -> |
| 34 | + val state = State(stateHandle[key] ?: initialValue, debounceInterval) |
| 35 | + thisRef.run { state.viewFlowable.subscribe { stateHandle[key] = it }.disposeOnCleared() } |
| 36 | + state |
| 37 | +} |
| 38 | + |
| 39 | +fun SavedStateHandle.inputControl( |
| 40 | + initialText: String = "", |
| 41 | + hideErrorOnUserInput: Boolean = true, |
| 42 | + formatter: FormatterAction? = null, |
| 43 | + initialEnabled: Boolean = true, |
| 44 | + initialVisibility: BaseVisualControl.Visibility = BaseVisualControl.Visibility.VISIBLE |
| 45 | +): ReadOnlyProperty<ReactiveViewModel, InputControl> = delegate { thisRef, stateHandle, key -> |
| 46 | + val textKey = "$key.text" |
| 47 | + val errorKey = "$key.error" |
| 48 | + val enabledKey = "$key.enabled" |
| 49 | + val visibilityKey = "$key.visibility" |
| 50 | + val control = InputControl( |
| 51 | + initialText = stateHandle[textKey] ?: initialText, |
| 52 | + hideErrorOnUserInput = hideErrorOnUserInput, |
| 53 | + formatter = formatter, |
| 54 | + initialEnabled = stateHandle[enabledKey] ?: initialEnabled, |
| 55 | + initialVisibility = stateHandle[visibilityKey] ?: initialVisibility |
| 56 | + ) |
| 57 | + thisRef.run { |
| 58 | + control.value.viewFlowable |
| 59 | + .subscribe { stateHandle[textKey] = it } |
| 60 | + .disposeOnCleared() |
| 61 | + control.error.viewFlowable |
| 62 | + .subscribe { stateHandle[errorKey] = it } |
| 63 | + .disposeOnCleared() |
| 64 | + control.enabled.viewFlowable |
| 65 | + .subscribe { stateHandle[enabledKey] = it } |
| 66 | + .disposeOnCleared() |
| 67 | + control.visibility.viewFlowable |
| 68 | + .subscribe { stateHandle[visibilityKey] = it } |
| 69 | + .disposeOnCleared() |
| 70 | + } |
| 71 | + control |
| 72 | +} |
| 73 | + |
| 74 | +fun SavedStateHandle.ratingControl( |
| 75 | + initialValue: Float = 0f, |
| 76 | + initialEnabled: Boolean = true, |
| 77 | + initialVisibility: BaseVisualControl.Visibility = BaseVisualControl.Visibility.VISIBLE |
| 78 | +): ReadOnlyProperty<ReactiveViewModel, RatingControl> = delegate { thisRef, stateHandle, key -> |
| 79 | + val ratingKey = "$key.rating" |
| 80 | + val enabledKey = "$key.enabled" |
| 81 | + val visibilityKey = "$key.visibility" |
| 82 | + val control = RatingControl( |
| 83 | + initialValue = stateHandle[ratingKey] ?: initialValue, |
| 84 | + initialEnabled = stateHandle[enabledKey] ?: initialEnabled, |
| 85 | + initialVisibility = stateHandle[visibilityKey] ?: initialVisibility |
| 86 | + ) |
| 87 | + thisRef.run { |
| 88 | + control.value.viewFlowable |
| 89 | + .subscribe { stateHandle[ratingKey] = it } |
| 90 | + .disposeOnCleared() |
| 91 | + control.enabled.viewFlowable |
| 92 | + .subscribe { stateHandle[enabledKey] = it } |
| 93 | + .disposeOnCleared() |
| 94 | + control.visibility.viewFlowable |
| 95 | + .subscribe { stateHandle[visibilityKey] = it } |
| 96 | + .disposeOnCleared() |
| 97 | + } |
| 98 | + control |
| 99 | +} |
| 100 | + |
| 101 | +@PublishedApi |
| 102 | +internal class SavedStateProperty<T>( |
| 103 | + private val savedStateHandle: SavedStateHandle, |
| 104 | + private val initValue: (thisRef: ReactiveViewModel, stateHandle: SavedStateHandle, key: String) -> T |
| 105 | +) : ReadWriteProperty<ReactiveViewModel, T> { |
| 106 | + |
| 107 | + private object NoneValue |
| 108 | + |
| 109 | + private var value: Any? = NoneValue |
| 110 | + |
| 111 | + @Suppress("UNCHECKED_CAST") |
| 112 | + override fun getValue(thisRef: ReactiveViewModel, property: KProperty<*>): T { |
| 113 | + if (value === NoneValue) { |
| 114 | + value = initValue(thisRef, savedStateHandle, getStateKey(thisRef, property)) |
| 115 | + } |
| 116 | + return value as T |
| 117 | + } |
| 118 | + |
| 119 | + override fun setValue(thisRef: ReactiveViewModel, property: KProperty<*>, value: T) { |
| 120 | + this.value = value |
| 121 | + savedStateHandle[getStateKey(thisRef, property)] = value |
| 122 | + } |
| 123 | + |
| 124 | + private fun getStateKey(thisRef: ReactiveViewModel, property: KProperty<*>): String = |
| 125 | + "${thisRef::class.java.simpleName}.${property.name}" |
| 126 | + |
| 127 | +} |
0 commit comments