Skip to content

Commit

Permalink
Merge pull request #3352 from CruGlobal/GT-2268-Persist-Filters
Browse files Browse the repository at this point in the history
GT-2268 Persist the dashboard tool filters
  • Loading branch information
frett authored Feb 5, 2024
2 parents a7509d1 + 91da100 commit aef921e
Show file tree
Hide file tree
Showing 6 changed files with 95 additions and 6 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import androidx.compose.runtime.collectAsState
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.rememberCoroutineScope
import androidx.compose.runtime.rememberUpdatedState
import androidx.compose.runtime.setValue
import com.slack.circuit.codegen.annotations.CircuitInject
Expand All @@ -25,7 +26,7 @@ import kotlinx.coroutines.flow.distinctUntilChanged
import kotlinx.coroutines.flow.flatMapLatest
import kotlinx.coroutines.flow.flowOf
import kotlinx.coroutines.flow.map
import kotlinx.coroutines.flow.mapNotNull
import kotlinx.coroutines.launch
import org.cru.godtools.analytics.model.OpenAnalyticsActionEvent
import org.cru.godtools.analytics.model.OpenAnalyticsActionEvent.Companion.ACTION_OPEN_TOOL_DETAILS
import org.cru.godtools.analytics.model.OpenAnalyticsActionEvent.Companion.SOURCE_SPOTLIGHT
Expand Down Expand Up @@ -90,19 +91,25 @@ class ToolsPresenter @AssistedInject constructor(

@Composable
private fun rememberFilters(): ToolsScreen.Filters {
val scope = rememberCoroutineScope()

// selected category
var selectedCategory: String? by remember { mutableStateOf(null) }
val selectedCategory by remember { settings.getDashboardFilterCategoryFlow() }.collectAsState(null)

// selected language
var selectedLocale: Locale? by remember { mutableStateOf(null) }
val selectedLocale by remember { settings.getDashboardFilterLocaleFlow() }.collectAsState(null)
val selectedLanguage = rememberLanguage(selectedLocale)
var languageQuery by remember { mutableStateOf("") }

val filtersEventSink: (ToolsScreen.FiltersEvent) -> Unit = remember {
{
when (it) {
is ToolsScreen.FiltersEvent.SelectCategory -> selectedCategory = it.category
is ToolsScreen.FiltersEvent.SelectLanguage -> selectedLocale = it.locale
is ToolsScreen.FiltersEvent.SelectCategory -> scope.launch {
settings.updateDashboardFilterCategory(it.category)
}
is ToolsScreen.FiltersEvent.SelectLanguage -> scope.launch {
settings.updateDashboardFilterLocale(it.locale)
}
is ToolsScreen.FiltersEvent.UpdateLanguageQuery -> languageQuery = it.query
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import androidx.test.core.app.ApplicationProvider
import androidx.test.ext.junit.runners.AndroidJUnit4
import com.slack.circuit.test.FakeNavigator
import com.slack.circuit.test.test
import io.mockk.coEvery
import io.mockk.every
import io.mockk.mockk
import io.mockk.verify
Expand Down Expand Up @@ -43,6 +44,8 @@ class ToolsPresenterTest {
private val toolsFlow = MutableStateFlow(emptyList<Tool>())
private val languagesFlow = MutableStateFlow(emptyList<Language>())
private val gospelLanguagesFlow = MutableStateFlow(emptyList<Language>())
private val selectedCategory = MutableStateFlow<String?>(null)
private val selectedLocale = MutableStateFlow<Locale?>(null)

private val languagesRepository: LanguagesRepository = mockk {
every { findLanguageFlow(any()) } returns flowOf(null)
Expand All @@ -54,6 +57,10 @@ class ToolsPresenterTest {
every { appLanguage } returns this@ToolsPresenterTest.appLanguage.value
every { appLanguageFlow } returns this@ToolsPresenterTest.appLanguage
every { isFeatureDiscoveredFlow(Settings.FEATURE_TOOL_FAVORITE) } returns isFavoritesFeatureDiscovered
every { getDashboardFilterCategoryFlow() } returns selectedCategory
every { getDashboardFilterLocaleFlow() } returns selectedLocale
coEvery { updateDashboardFilterCategory(any()) } answers { selectedCategory.value = firstArg() }
coEvery { updateDashboardFilterLocale(any()) } answers { selectedLocale.value = firstArg() }
}
private val toolsRepository: ToolsRepository = mockk {
every { getNormalToolsFlow() } returns toolsFlow
Expand Down
4 changes: 3 additions & 1 deletion gradle/libs.versions.toml
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ androidx-compose-compiler = "1.5.8"
androidx-compose-material = "1.5.4"
androidx-compose-ui = "1.5.4"
androidx-core = "1.12.0"
androidx-datastore = "1.0.0"
androidx-hilt = "1.1.0"
androidx-lifecycle = "2.7.0"
androidx-room = "2.6.1"
Expand Down Expand Up @@ -60,7 +61,8 @@ androidx-constraintlayout-compose = "androidx.constraintlayout:constraintlayout-
androidx-core = { module = "androidx.core:core", version.ref = "androidx-core" }
androidx-core-ktx = { module = "androidx.core:core-ktx", version.ref = "androidx-core" }
androidx-databinding-compiler = { module = "androidx.databinding:databinding-compiler", version.ref = "android-gradle-plugin" }
androidx-datastore = "androidx.datastore:datastore:1.0.0"
androidx-datastore = { module = "androidx.datastore:datastore", version.ref = "androidx-datastore" }
androidx-datastore-preferences = { module = "androidx.datastore:datastore-preferences", version.ref = "androidx-datastore" }
androidx-fragment-ktx = "androidx.fragment:fragment-ktx:1.6.2"
androidx-hilt-compiler = { module = "androidx.hilt:hilt-compiler", version.ref = "androidx-hilt" }
androidx-hilt-work = { module = "androidx.hilt:hilt-work", version.ref = "androidx-hilt" }
Expand Down
1 change: 1 addition & 0 deletions library/base/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ onesky {
dependencies {
implementation(libs.androidx.appcompat)
implementation(libs.androidx.core.ktx)
implementation(libs.androidx.datastore.preferences)
implementation(libs.androidx.lifecycle.livedata.ktx)

implementation(libs.gtoSupport.androidx.core)
Expand Down
40 changes: 40 additions & 0 deletions library/base/src/main/kotlin/org/cru/godtools/base/Settings.kt
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@ import androidx.appcompat.app.AppCompatDelegate
import androidx.core.content.edit
import androidx.core.content.pm.PackageInfoCompat
import androidx.core.os.LocaleListCompat
import androidx.datastore.preferences.core.stringPreferencesKey
import androidx.datastore.preferences.preferencesDataStore
import androidx.lifecycle.LiveData
import androidx.lifecycle.distinctUntilChanged
import dagger.hilt.android.qualifiers.ApplicationContext
Expand All @@ -18,6 +20,7 @@ import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.SharingStarted
import kotlinx.coroutines.flow.distinctUntilChanged
import kotlinx.coroutines.flow.map
import kotlinx.coroutines.flow.onStart
import kotlinx.coroutines.flow.shareIn
import org.ccci.gto.android.common.androidx.lifecycle.getBooleanLiveData
Expand All @@ -30,11 +33,14 @@ private const val PREF_LAUNCHES = "launches"
private const val PREF_VERSION_FIRST_LAUNCH = "version.firstLaunch"
private const val PREF_VERSION_LAST_LAUNCH = "version.lastLaunch"

private val Context.dataStorePreferences by preferencesDataStore("${PREFS_SETTINGS}Settings")

@Singleton
class Settings internal constructor(private val context: Context, coroutineScope: CoroutineScope) {
@Inject
internal constructor(@ApplicationContext context: Context) : this(context, CoroutineScope(Dispatchers.Default))

private val dataStorePreferences = context.dataStorePreferences
private val prefs: SharedPreferences = context.getSharedPreferences(PREFS_SETTINGS, Context.MODE_PRIVATE)

companion object {
Expand All @@ -53,6 +59,10 @@ class Settings internal constructor(private val context: Context, coroutineScope
const val FEATURE_TUTORIAL_FEATURES = "tutorialTraining"
const val FEATURE_TUTORIAL_LIVE_SHARE = "tutorialLiveShare."
const val FEATURE_TUTORIAL_TIPS = "tutorialTips."

// Dashboard Settings
private val KEY_DASHBOARD_FILTER_CATEGORY = stringPreferencesKey("dashboardFilterCategory")
private val KEY_DASHBOARD_FILTER_LOCALE = stringPreferencesKey("dashboardFilterLocale")
}

// region Language Settings
Expand Down Expand Up @@ -114,6 +124,36 @@ class Settings internal constructor(private val context: Context, coroutineScope
private fun isFeatureDiscoveredInt(feature: String) = prefs.getBoolean("$PREF_FEATURE_DISCOVERED$feature", false)
// endregion Feature Discovery Tracking

// region Dashboard Settings
fun getDashboardFilterCategoryFlow() = dataStorePreferences.data
.map { it[KEY_DASHBOARD_FILTER_CATEGORY] }
.distinctUntilChanged()
suspend fun updateDashboardFilterCategory(category: String?) {
dataStorePreferences.updateData {
it.toMutablePreferences().apply {
when (category) {
null -> remove(KEY_DASHBOARD_FILTER_CATEGORY)
else -> set(KEY_DASHBOARD_FILTER_CATEGORY, category)
}
}
}
}

fun getDashboardFilterLocaleFlow() = dataStorePreferences.data
.map { it[KEY_DASHBOARD_FILTER_LOCALE]?.let { Locale.forLanguageTag(it) } }
.distinctUntilChanged()
suspend fun updateDashboardFilterLocale(locale: Locale?) {
dataStorePreferences.updateData {
it.toMutablePreferences().apply {
when (locale) {
null -> remove(KEY_DASHBOARD_FILTER_LOCALE)
else -> set(KEY_DASHBOARD_FILTER_LOCALE, locale.toLanguageTag())
}
}
}
}
// endregion Dashboard Settings

// region Campaign Tracking
fun isAddedToCampaign(oktaId: String? = null, guid: String? = null) = when {
oktaId == null && guid == null -> true
Expand Down
32 changes: 32 additions & 0 deletions library/base/src/test/kotlin/org/cru/godtools/base/SettingsTest.kt
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,12 @@ package org.cru.godtools.base

import androidx.test.core.app.ApplicationProvider
import androidx.test.ext.junit.runners.AndroidJUnit4
import app.cash.turbine.test
import java.util.Locale
import kotlin.test.BeforeTest
import kotlin.test.Test
import kotlin.test.assertNull
import kotlinx.coroutines.test.runTest
import org.cru.godtools.base.Settings.Companion.FEATURE_TUTORIAL_ONBOARDING
import org.junit.Assert.assertEquals
import org.junit.Assert.assertFalse
Expand Down Expand Up @@ -37,4 +41,32 @@ class SettingsTest {
fun verifyFeatureDiscoveryTutorialOnboardingNewInstall() {
assertFalse(settings.isFeatureDiscovered(FEATURE_TUTORIAL_ONBOARDING))
}

// region Dashboard Settings
@Test
fun testDashboardFilterCategory() = runTest {
settings.getDashboardFilterCategoryFlow().test {
assertNull(awaitItem())

settings.updateDashboardFilterCategory("test")
assertEquals("test", awaitItem())

settings.updateDashboardFilterCategory(null)
assertNull(awaitItem())
}
}

@Test
fun testDashboardFilterLocale() = runTest {
settings.getDashboardFilterLocaleFlow().test {
assertNull(awaitItem())

settings.updateDashboardFilterLocale(Locale.ENGLISH)
assertEquals(Locale.ENGLISH, awaitItem())

settings.updateDashboardFilterLocale(null)
assertNull(awaitItem())
}
}
// endregion Dashboard Settings
}

0 comments on commit aef921e

Please sign in to comment.