Skip to content
This repository has been archived by the owner on Nov 5, 2024. It is now read-only.

Commit

Permalink
Implement a way for setting User Preferences in DataStore
Browse files Browse the repository at this point in the history
  • Loading branch information
ILIYANGERMANOV committed May 28, 2022
1 parent 741e740 commit 3f04d5a
Show file tree
Hide file tree
Showing 17 changed files with 297 additions and 34 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
package com.ivy.wallet.domain.action.settings.preference

import com.ivy.frp.action.FPAction
import com.ivy.frp.asParamTo
import com.ivy.wallet.domain.data.preference.Preference
import com.ivy.wallet.io.persistence.datastore.IvyDataStore
import javax.inject.Inject

class PreferenceAct<P : Preference<V>, V> @Inject constructor(
private val dataStore: IvyDataStore
) : FPAction<P, V?>() {
override suspend fun P.compose(): suspend () -> V? =
this.key asParamTo dataStore::get
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
package com.ivy.wallet.domain.action.settings.preference

import com.ivy.frp.action.FPAction
import com.ivy.frp.asParamTo
import com.ivy.wallet.domain.data.preference.Preference
import com.ivy.wallet.io.persistence.datastore.IvyDataStore
import javax.inject.Inject

class SetPreferenceAct<P : Preference<V>, V> @Inject constructor(
private val dataStore: IvyDataStore
) : FPAction<P, Unit>() {
override suspend fun P.compose(): suspend () -> Unit =
when (val newValue = value) {
newValue != null -> (key to newValue) asParamTo dataStore::insert
else -> key asParamTo dataStore::remove
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
package com.ivy.wallet.domain.data.preference

import androidx.datastore.preferences.core.Preferences

interface Preference<T> {
val key: Preferences.Key<T>
val value: T?
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
package com.ivy.wallet.domain.data.preference

import androidx.datastore.preferences.core.booleanPreferencesKey

data class SmallTrnsPref(
override val value: Boolean = false
) : Preference<Boolean> {
override val key = booleanPreferencesKey("exp_small_trns")
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
package com.ivy.wallet.io.persistence.datastore

import android.content.Context
import androidx.datastore.core.DataStore
import androidx.datastore.preferences.core.Preferences
import androidx.datastore.preferences.core.edit
import androidx.datastore.preferences.preferencesDataStore
import dagger.hilt.android.qualifiers.ApplicationContext
import kotlinx.coroutines.flow.firstOrNull
import kotlinx.coroutines.flow.map
import javax.inject.Inject
import javax.inject.Singleton

@Singleton
class IvyDataStore @Inject constructor(
@ApplicationContext private val appContext: Context
) {
private val Context.dataStore: DataStore<Preferences> by preferencesDataStore(name = "ivy_wallet")

suspend fun <T> insert(pair: Preferences.Pair<T>) {
appContext.dataStore.edit {
it.putAll(pair)
}
}

suspend fun <T> insert(
key: Preferences.Key<T>,
value: T
) {
appContext.dataStore.edit {
it[key] = value
}
}

suspend fun <T> remove(key: Preferences.Key<T>) {
appContext.dataStore.edit {
it.remove(key = key)
}
}

suspend fun <T> get(key: Preferences.Key<T>): T? = appContext.dataStore.data.map {
it[key]
}.firstOrNull()
}
2 changes: 2 additions & 0 deletions app/src/main/java/com/ivy/wallet/ui/RootActivity.kt
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,7 @@ import com.ivy.wallet.ui.planned.list.PlannedPaymentsScreen
import com.ivy.wallet.ui.reports.ReportScreen
import com.ivy.wallet.ui.search.SearchScreen
import com.ivy.wallet.ui.settings.SettingsScreen
import com.ivy.wallet.ui.settings.experimental.ExperimentalScreen
import com.ivy.wallet.ui.statistic.level1.PieChartStatisticScreen
import com.ivy.wallet.ui.statistic.level2.ItemStatisticScreen
import com.ivy.wallet.ui.test.TestScreen
Expand Down Expand Up @@ -210,6 +211,7 @@ class RootActivity : AppCompatActivity() {
is Search -> SearchScreen(screen = screen)
is IvyWebView -> WebViewScreen(screen = screen)
is ImagesScreen -> ImagesScreen(screen = screen)
is ExperimentalScreen -> ExperimentalScreen(screen = screen)
null -> {
}
}
Expand Down
29 changes: 0 additions & 29 deletions app/src/main/java/com/ivy/wallet/ui/architecture/UIArchitecture.kt

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -20,10 +20,10 @@ import com.ivy.design.l1_buildingBlocks.IvyText
import com.ivy.design.l1_buildingBlocks.SpacerVer
import com.ivy.design.l1_buildingBlocks.SpacerWeight
import com.ivy.design.l2_components.Button
import com.ivy.frp.view.FRP
import com.ivy.frp.view.navigation.Screen
import com.ivy.wallet.R
import com.ivy.wallet.ui.IvyWalletPreview
import com.ivy.wallet.ui.architecture.FRP

class ImagesScreen : Screen

Expand Down
2 changes: 1 addition & 1 deletion app/src/main/java/com/ivy/wallet/ui/home/HomeTab.kt
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp
import com.google.accompanist.insets.systemBarsPadding
import com.ivy.design.l0_system.Theme
import com.ivy.frp.view.FRP
import com.ivy.frp.view.navigation.navigation
import com.ivy.wallet.Constants
import com.ivy.wallet.R
Expand All @@ -29,7 +30,6 @@ import com.ivy.wallet.domain.deprecated.logic.model.CustomerJourneyCardData
import com.ivy.wallet.stringRes
import com.ivy.wallet.ui.IvyWalletPreview
import com.ivy.wallet.ui.Main
import com.ivy.wallet.ui.architecture.FRP
import com.ivy.wallet.ui.ivyWalletCtx
import com.ivy.wallet.ui.main.MainTab
import com.ivy.wallet.ui.onboarding.model.TimePeriod
Expand Down
15 changes: 15 additions & 0 deletions app/src/main/java/com/ivy/wallet/ui/settings/SettingsScreen.kt
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@ import com.ivy.wallet.domain.data.AuthProviderType
import com.ivy.wallet.domain.data.IvyCurrency
import com.ivy.wallet.domain.data.core.User
import com.ivy.wallet.ui.*
import com.ivy.wallet.ui.settings.experimental.ExperimentalScreen
import com.ivy.wallet.ui.theme.*
import com.ivy.wallet.ui.theme.components.IvyButton
import com.ivy.wallet.ui.theme.components.IvySwitch
Expand Down Expand Up @@ -305,6 +306,20 @@ private fun BoxWithConstraintsScope.UI(
}
}

item {
SettingsSectionDivider(text = "Experimental")

Spacer(Modifier.height(16.dp))

val nav = navigation()
SettingsDefaultButton(
icon = R.drawable.ic_custom_atom_m,
text = "Experimental Settings"
) {
nav.navigateTo(ExperimentalScreen)
}
}

item {
SettingsSectionDivider(text = stringResource(R.string.other))

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@ class SettingsViewModel @Inject constructor(
private val sharedPrefs: SharedPrefs,
private val exportZipLogic: ExportZipLogic,
private val startDayOfMonthAct: StartDayOfMonthAct,
private val updateStartDayOfMonthAct: UpdateStartDayOfMonthAct
private val updateStartDayOfMonthAct: UpdateStartDayOfMonthAct,
) : ViewModel() {

private val _user = MutableLiveData<User?>()
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
package com.ivy.wallet.ui.settings.experimental

sealed class ExpEvent {
object Load : ExpEvent()

data class SetSmallTrnsPref(val newValue: Boolean) : ExpEvent()
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
package com.ivy.wallet.ui.settings.experimental

sealed class ExpState {
object Initial : ExpState()

data class Loaded(
val smallTrnsPref: Boolean
) : ExpState()
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,125 @@
package com.ivy.wallet.ui.settings.experimental

import androidx.compose.foundation.background
import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.BoxWithConstraintsScope
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.runtime.Composable
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.clip
import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp
import com.ivy.design.l0_system.UI
import com.ivy.design.l0_system.style
import com.ivy.design.l1_buildingBlocks.*
import com.ivy.frp.forward
import com.ivy.frp.then2
import com.ivy.frp.view.FRP
import com.ivy.frp.view.navigation.Screen
import com.ivy.wallet.ui.IvyWalletPreview
import com.ivy.wallet.ui.theme.components.IvySwitch

object ExperimentalScreen : Screen

@Composable
fun BoxWithConstraintsScope.ExperimentalScreen(screen: ExperimentalScreen) {
FRP<ExpState, ExpEvent, ExperimentalViewModel>(
initialEvent = ExpEvent.Load
) { state, onEvent ->
UI(state, onEvent)
}
}

@Composable
private fun UI(
state: ExpState,

onEvent: (ExpEvent) -> Unit
) {
ColumnRoot(
modifier = Modifier.padding(horizontal = 24.dp)
) {
SpacerVer(height = 24.dp)

IvyText(text = "Experimental", typo = UI.typo.h2)

SpacerVer(height = 32.dp)

when (state) {
ExpState.Initial -> {}
is ExpState.Loaded -> LoadedState(state = state, onEvent = onEvent)
}
}
}

@Composable
private fun LoadedState(
state: ExpState.Loaded,
onEvent: (ExpEvent) -> Unit
) {
LazyColumn {
item {
BooleanPreference(
name = "Small transactions",
value = state.smallTrnsPref,
onValueChanged = forward<Boolean>() then2
{ ExpEvent.SetSmallTrnsPref(it) } then2 onEvent
)
}
}
}

@Composable
private fun BooleanPreference(
name: String,
value: Boolean,
onValueChanged: (Boolean) -> Unit
) {
Row(
modifier = Modifier
.fillMaxWidth()
.clip(UI.shapes.r2)
.background(UI.colors.medium, UI.shapes.r4)
.clickable {
onValueChanged(!value)
}
.padding(vertical = 20.dp),
verticalAlignment = Alignment.CenterVertically
) {
SpacerHor(width = 16.dp)

IvyText(
text = name,
typo = UI.typo.b2.style(
fontWeight = FontWeight.Bold
)
)

SpacerWeight(weight = 1f)

IvySwitch(
enabled = value,
onEnabledChange = forward<Boolean>() then2 onValueChanged
)

SpacerHor(width = 16.dp)
}
}

@Preview
@Composable
private fun Preview() {
IvyWalletPreview {
UI(
state = ExpState.Loaded(
smallTrnsPref = false
),
onEvent = {}
)
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
package com.ivy.wallet.ui.settings.experimental

import com.ivy.frp.asParamTo
import com.ivy.frp.then
import com.ivy.frp.thenInvokeAfter
import com.ivy.frp.viewmodel.FRPViewModel
import com.ivy.wallet.domain.action.settings.preference.PreferenceAct
import com.ivy.wallet.domain.action.settings.preference.SetPreferenceAct
import com.ivy.wallet.domain.data.preference.SmallTrnsPref
import dagger.hilt.android.lifecycle.HiltViewModel
import kotlinx.coroutines.flow.MutableStateFlow
import javax.inject.Inject

@HiltViewModel
class ExperimentalViewModel @Inject constructor(
private val smallTrnsPrefAct: PreferenceAct<SmallTrnsPref, Boolean>,
private val setSmallTrnsPrefAct: SetPreferenceAct<SmallTrnsPref, Boolean>
) : FRPViewModel<ExpState, ExpEvent>() {
override val _state: MutableStateFlow<ExpState> = MutableStateFlow(ExpState.Initial)

override suspend fun handleEvent(event: ExpEvent): suspend () -> ExpState = when (event) {
ExpEvent.Load -> load(Unit)
is ExpEvent.SetSmallTrnsPref -> setSmallTrnsPref(event)
}

private fun load(unit: Unit) = SmallTrnsPref() asParamTo smallTrnsPrefAct then { smallTrns ->
updateState {
ExpState.Loaded(
smallTrnsPref = smallTrns ?: false
)
}
}

private suspend fun setSmallTrnsPref(event: ExpEvent.SetSmallTrnsPref) =
SmallTrnsPref(value = event.newValue) asParamTo setSmallTrnsPrefAct thenInvokeAfter ::load
}
Loading

0 comments on commit 3f04d5a

Please sign in to comment.