From 496e657c73b7cbc42a24e2167ef5f6c03b69fa66 Mon Sep 17 00:00:00 2001 From: Daniel Frett Date: Fri, 5 Jan 2024 15:03:09 -0700 Subject: [PATCH 1/8] create a Filter wrapper object that will expose a tool count for each filter item --- .../cru/godtools/ui/dashboard/tools/ToolFilters.kt | 6 +++--- .../cru/godtools/ui/dashboard/tools/ToolsPresenter.kt | 8 ++++++-- .../cru/godtools/ui/dashboard/tools/ToolsScreen.kt | 6 ++++-- .../godtools/ui/dashboard/tools/ToolsPresenterTest.kt | 11 ++++++----- 4 files changed, 19 insertions(+), 12 deletions(-) diff --git a/app/src/main/kotlin/org/cru/godtools/ui/dashboard/tools/ToolFilters.kt b/app/src/main/kotlin/org/cru/godtools/ui/dashboard/tools/ToolFilters.kt index 32e0061872..4d1630b82e 100644 --- a/app/src/main/kotlin/org/cru/godtools/ui/dashboard/tools/ToolFilters.kt +++ b/app/src/main/kotlin/org/cru/godtools/ui/dashboard/tools/ToolFilters.kt @@ -102,11 +102,11 @@ private fun CategoryFilter(filters: ToolsScreen.Filters, modifier: Modifier = Mo expanded = false } ) - categories.forEach { + categories.forEach { (category) -> DropdownMenuItem( - text = { Text(getToolCategoryName(it, LocalContext.current)) }, + text = { Text(getToolCategoryName(category, LocalContext.current)) }, onClick = { - eventSink(ToolsScreen.FiltersEvent.SelectCategory(it)) + eventSink(ToolsScreen.FiltersEvent.SelectCategory(category)) expanded = false } ) diff --git a/app/src/main/kotlin/org/cru/godtools/ui/dashboard/tools/ToolsPresenter.kt b/app/src/main/kotlin/org/cru/godtools/ui/dashboard/tools/ToolsPresenter.kt index 4b3e527d1a..cf08f79a93 100644 --- a/app/src/main/kotlin/org/cru/godtools/ui/dashboard/tools/ToolsPresenter.kt +++ b/app/src/main/kotlin/org/cru/godtools/ui/dashboard/tools/ToolsPresenter.kt @@ -34,6 +34,7 @@ import org.cru.godtools.model.Language import org.cru.godtools.model.Language.Companion.filterByDisplayAndNativeName import org.cru.godtools.model.Tool import org.cru.godtools.ui.banner.BannerType +import org.cru.godtools.ui.dashboard.tools.ToolsScreen.Filters.Filter import org.cru.godtools.ui.tooldetails.ToolDetailsScreen import org.cru.godtools.ui.tools.ToolCard import org.cru.godtools.ui.tools.ToolCardPresenter @@ -114,11 +115,14 @@ class ToolsPresenter @AssistedInject constructor( } @Composable - private fun rememberFilterCategories(selectedLanguage: Locale?): List { + private fun rememberFilterCategories(selectedLanguage: Locale?): List> { val filteredToolsFlow = rememberFilteredToolsFlow(language = selectedLanguage) return remember(filteredToolsFlow) { - filteredToolsFlow.map { it.mapNotNull { it.category }.distinct() } + filteredToolsFlow.map { + it.groupBy { it.category } + .mapNotNull { (category, tools) -> category?.let { Filter(category, tools.size) } } + } }.collectAsState(emptyList()).value } diff --git a/app/src/main/kotlin/org/cru/godtools/ui/dashboard/tools/ToolsScreen.kt b/app/src/main/kotlin/org/cru/godtools/ui/dashboard/tools/ToolsScreen.kt index 26c056f5c2..bc2401e3c1 100644 --- a/app/src/main/kotlin/org/cru/godtools/ui/dashboard/tools/ToolsScreen.kt +++ b/app/src/main/kotlin/org/cru/godtools/ui/dashboard/tools/ToolsScreen.kt @@ -21,13 +21,15 @@ data object ToolsScreen : Screen { ) : CircuitUiState data class Filters( - val categories: List = emptyList(), + val categories: List> = emptyList(), val selectedCategory: String? = null, val languages: List = emptyList(), val languageQuery: String = "", val selectedLanguage: Language? = null, val eventSink: (FiltersEvent) -> Unit = {}, - ) : CircuitUiState + ) : CircuitUiState { + data class Filter(val item: T, val count: Int) + } sealed interface Event : CircuitUiEvent { data class OpenToolDetails(val tool: String, val source: String? = null) : Event diff --git a/app/src/testDebug/kotlin/org/cru/godtools/ui/dashboard/tools/ToolsPresenterTest.kt b/app/src/testDebug/kotlin/org/cru/godtools/ui/dashboard/tools/ToolsPresenterTest.kt index e1839f1db7..80aecc98ff 100644 --- a/app/src/testDebug/kotlin/org/cru/godtools/ui/dashboard/tools/ToolsPresenterTest.kt +++ b/app/src/testDebug/kotlin/org/cru/godtools/ui/dashboard/tools/ToolsPresenterTest.kt @@ -26,6 +26,7 @@ import org.cru.godtools.model.Language import org.cru.godtools.model.Tool import org.cru.godtools.model.randomTool import org.cru.godtools.ui.banner.BannerType +import org.cru.godtools.ui.dashboard.tools.ToolsScreen.Filters.Filter import org.cru.godtools.ui.tools.ToolCardPresenter import org.junit.runner.RunWith import org.robolectric.annotation.Config @@ -148,7 +149,7 @@ class ToolsPresenterTest { presenter.test { assertEquals( - listOf(Tool.CATEGORY_GOSPEL, Tool.CATEGORY_ARTICLES), + listOf(Filter(Tool.CATEGORY_GOSPEL, 1), Filter(Tool.CATEGORY_ARTICLES, 1)), expectMostRecentItem().filters.categories ) } @@ -162,7 +163,7 @@ class ToolsPresenterTest { ) presenter.test { - assertEquals(listOf(Tool.CATEGORY_GOSPEL), expectMostRecentItem().filters.categories) + assertEquals(listOf(Filter(Tool.CATEGORY_GOSPEL, 2)), expectMostRecentItem().filters.categories) } } @@ -175,7 +176,7 @@ class ToolsPresenterTest { presenter.test { assertEquals( - listOf(Tool.CATEGORY_ARTICLES, Tool.CATEGORY_GOSPEL), + listOf(Filter(Tool.CATEGORY_ARTICLES, 1), Filter(Tool.CATEGORY_GOSPEL, 1)), expectMostRecentItem().filters.categories ) } @@ -191,7 +192,7 @@ class ToolsPresenterTest { presenter.test { metatoolsFlow.value = listOf(meta) - assertEquals(listOf(Tool.CATEGORY_GOSPEL), expectMostRecentItem().filters.categories) + assertEquals(listOf(Filter(Tool.CATEGORY_GOSPEL, 1)), expectMostRecentItem().filters.categories) } } @@ -203,7 +204,7 @@ class ToolsPresenterTest { ) presenter.test { - assertEquals(listOf(Tool.CATEGORY_GOSPEL), expectMostRecentItem().filters.categories) + assertEquals(listOf(Filter(Tool.CATEGORY_GOSPEL, 1)), expectMostRecentItem().filters.categories) } } // endregion State.filters.categories From 2594a825fa0a3a9fb57c95a80bddaf07f7d601b9 Mon Sep 17 00:00:00 2001 From: Daniel Frett Date: Fri, 5 Jan 2024 16:45:24 -0700 Subject: [PATCH 2/8] display tool count for categories in the drop down menu --- .../ui/dashboard/tools/ToolFilters.kt | 31 ++++++++++++++++--- app/src/main/res/values/strings_dashboard.xml | 5 +++ 2 files changed, 31 insertions(+), 5 deletions(-) diff --git a/app/src/main/kotlin/org/cru/godtools/ui/dashboard/tools/ToolFilters.kt b/app/src/main/kotlin/org/cru/godtools/ui/dashboard/tools/ToolFilters.kt index 4d1630b82e..4ad74de57a 100644 --- a/app/src/main/kotlin/org/cru/godtools/ui/dashboard/tools/ToolFilters.kt +++ b/app/src/main/kotlin/org/cru/godtools/ui/dashboard/tools/ToolFilters.kt @@ -1,6 +1,7 @@ package org.cru.godtools.ui.dashboard.tools import androidx.compose.foundation.ExperimentalFoundationApi +import androidx.compose.foundation.clickable import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Row @@ -17,6 +18,7 @@ import androidx.compose.material3.ElevatedButton import androidx.compose.material3.ExperimentalMaterial3Api import androidx.compose.material3.ExposedDropdownMenuDefaults import androidx.compose.material3.Icon +import androidx.compose.material3.ListItem import androidx.compose.material3.MaterialTheme import androidx.compose.material3.SearchBar import androidx.compose.material3.Text @@ -29,6 +31,7 @@ import androidx.compose.runtime.setValue import androidx.compose.ui.Modifier import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.platform.testTag +import androidx.compose.ui.res.pluralStringResource import androidx.compose.ui.res.stringResource import androidx.compose.ui.text.style.TextOverflow import androidx.compose.ui.unit.dp @@ -95,16 +98,22 @@ private fun CategoryFilter(filters: ToolsScreen.Filters, modifier: Modifier = Mo onDismissRequest = { expanded = false }, modifier = Modifier.heightIn(max = DROPDOWN_MAX_HEIGHT), ) { - DropdownMenuItem( - text = { Text(stringResource(R.string.dashboard_tools_section_filter_category_any)) }, + FilterMenuItem( + label = stringResource(R.string.dashboard_tools_section_filter_category_any), + supportingText = stringResource(R.string.dashboard_tools_section_filter_available_tools_all), onClick = { eventSink(ToolsScreen.FiltersEvent.SelectCategory(null)) expanded = false } ) - categories.forEach { (category) -> - DropdownMenuItem( - text = { Text(getToolCategoryName(category, LocalContext.current)) }, + categories.forEach { (category, count) -> + FilterMenuItem( + label = getToolCategoryName(category, LocalContext.current), + supportingText = pluralStringResource( + R.plurals.dashboard_tools_section_filter_available_tools, + count, + count, + ), onClick = { eventSink(ToolsScreen.FiltersEvent.SelectCategory(category)) expanded = false @@ -185,3 +194,15 @@ internal fun LanguageFilter(filters: ToolsScreen.Filters, modifier: Modifier = M } } } + +@Composable +private fun FilterMenuItem( + label: String, + onClick: () -> Unit, + modifier: Modifier = Modifier, + supportingText: String? = null, +) = ListItem( + headlineContent = { Text(label) }, + supportingContent = supportingText?.let { { Text(it) } }, + modifier = modifier.clickable(onClick = onClick) +) diff --git a/app/src/main/res/values/strings_dashboard.xml b/app/src/main/res/values/strings_dashboard.xml index 8800628b07..0e8d16c80a 100644 --- a/app/src/main/res/values/strings_dashboard.xml +++ b/app/src/main/res/values/strings_dashboard.xml @@ -51,6 +51,11 @@ An online version can be found at https://knowgod.com/ Filter Any category Any language + + %1$d Tool available + %1$d Tools available + + All Tools available Categories All Tools Tool Spotlight From 00c4e512c62f52e4db7539da9aec0bf04a203ebc Mon Sep 17 00:00:00 2001 From: Daniel Frett Date: Thu, 11 Jan 2024 11:32:22 -0700 Subject: [PATCH 3/8] cleanup names for several TranslationsRepository methods --- .../db/repository/TranslationsRepository.kt | 6 +++--- .../cru/godtools/db/room/dao/TranslationsDao.kt | 4 ++-- .../room/repository/TranslationsRoomRepository.kt | 8 ++++---- .../db/repository/TranslationsRepositoryIT.kt | 10 +++++----- .../downloadmanager/GodToolsDownloadManager.kt | 2 +- .../GodToolsDownloadManagerDispatcherTest.kt | 14 +++++++------- .../article/aem/service/AemArticleManager.kt | 2 +- .../aem/service/AemArticleManagerDispatcherTest.kt | 2 +- 8 files changed, 24 insertions(+), 24 deletions(-) diff --git a/library/db/src/main/kotlin/org/cru/godtools/db/repository/TranslationsRepository.kt b/library/db/src/main/kotlin/org/cru/godtools/db/repository/TranslationsRepository.kt index 5ce7d3d84f..0098993ae1 100644 --- a/library/db/src/main/kotlin/org/cru/godtools/db/repository/TranslationsRepository.kt +++ b/library/db/src/main/kotlin/org/cru/godtools/db/repository/TranslationsRepository.kt @@ -14,9 +14,9 @@ interface TranslationsRepository { suspend fun getTranslationsForLanguages(languages: Collection): List fun getTranslationsFlow(): Flow> - fun getTranslationsFlowForTool(tool: String) = getTranslationsForToolsFlow(listOf(tool)) - fun getTranslationsForToolsFlow(tools: Collection): Flow> - fun getTranslationsForToolsAndLocalesFlow( + fun getTranslationsFlowForTool(tool: String) = getTranslationsFlowForTools(listOf(tool)) + fun getTranslationsFlowForTools(tools: Collection): Flow> + fun getTranslationsFlowForToolsAndLocales( tools: Collection, locales: Collection, ): Flow> diff --git a/library/db/src/main/kotlin/org/cru/godtools/db/room/dao/TranslationsDao.kt b/library/db/src/main/kotlin/org/cru/godtools/db/room/dao/TranslationsDao.kt index bd14b9d604..183f576db6 100644 --- a/library/db/src/main/kotlin/org/cru/godtools/db/room/dao/TranslationsDao.kt +++ b/library/db/src/main/kotlin/org/cru/godtools/db/room/dao/TranslationsDao.kt @@ -27,9 +27,9 @@ internal interface TranslationsDao { @Query("SELECT * FROM translations") fun getTranslationsFlow(): Flow> @Query("SELECT * FROM translations WHERE tool IN (:tools)") - fun getTranslationsForToolsFlow(tools: Collection): Flow> + fun getTranslationsFlowForTools(tools: Collection): Flow> @Query("SELECT * FROM translations WHERE tool IN (:tools) AND locale IN (:locales)") - fun getTranslationsForToolsAndLocalesFlow( + fun getTranslationsFlowForToolsAndLocales( tools: Collection, locales: Collection, ): Flow> diff --git a/library/db/src/main/kotlin/org/cru/godtools/db/room/repository/TranslationsRoomRepository.kt b/library/db/src/main/kotlin/org/cru/godtools/db/room/repository/TranslationsRoomRepository.kt index b9fc524e78..0b75dd5fb6 100644 --- a/library/db/src/main/kotlin/org/cru/godtools/db/room/repository/TranslationsRoomRepository.kt +++ b/library/db/src/main/kotlin/org/cru/godtools/db/room/repository/TranslationsRoomRepository.kt @@ -42,10 +42,10 @@ internal abstract class TranslationsRoomRepository(private val db: GodToolsRoomD dao.getTranslationsForLanguages(languages).map { it.toModel() } override fun getTranslationsFlow() = dao.getTranslationsFlow().map { it.map { it.toModel() } } - override fun getTranslationsForToolsFlow(tools: Collection) = - dao.getTranslationsForToolsFlow(tools).map { it.map { it.toModel() } } - override fun getTranslationsForToolsAndLocalesFlow(tools: Collection, locales: Collection) = - dao.getTranslationsForToolsAndLocalesFlow(tools, locales).map { it.map { it.toModel() } } + override fun getTranslationsFlowForTools(tools: Collection) = + dao.getTranslationsFlowForTools(tools).map { it.map { it.toModel() } } + override fun getTranslationsFlowForToolsAndLocales(tools: Collection, locales: Collection) = + dao.getTranslationsFlowForToolsAndLocales(tools, locales).map { it.map { it.toModel() } } override fun translationsChangeFlow(): Flow = db.changeFlow("translations") diff --git a/library/db/src/test/kotlin/org/cru/godtools/db/repository/TranslationsRepositoryIT.kt b/library/db/src/test/kotlin/org/cru/godtools/db/repository/TranslationsRepositoryIT.kt index 12abb34bec..416f740fe8 100644 --- a/library/db/src/test/kotlin/org/cru/godtools/db/repository/TranslationsRepositoryIT.kt +++ b/library/db/src/test/kotlin/org/cru/godtools/db/repository/TranslationsRepositoryIT.kt @@ -201,7 +201,7 @@ abstract class TranslationsRepositoryIT { } // endregion getTranslationsFlow() - // region getTranslationsForToolFlow() + // region getTranslationsFlowForTool() @Test fun `getTranslationsForToolFlow()`() = testScope.runTest { val trans1 = randomTranslation(TOOL, Locale.ENGLISH) @@ -229,16 +229,16 @@ abstract class TranslationsRepositoryIT { } } } - // endregion getTranslationsForToolFlow() + // endregion getTranslationsFlowForTool() - // region getTranslationsForToolsAndLocalesFlow() + // region getTranslationsFlowForToolsAndLocales() @Test fun `getTranslationsForToolsAndLocalesFlow()`() = testScope.runTest { val trans1 = randomTranslation(TOOL, Locale.ENGLISH) val trans2 = randomTranslation(TOOL, Locale.FRENCH) val trans3 = randomTranslation(TOOL, Locale.GERMAN) - repository.getTranslationsForToolsAndLocalesFlow(setOf(TOOL), setOf(Locale.ENGLISH, Locale.GERMAN)).test { + repository.getTranslationsFlowForToolsAndLocales(setOf(TOOL), setOf(Locale.ENGLISH, Locale.GERMAN)).test { repository.storeInitialTranslations(listOf(randomTranslation(TOOL2))) runCurrent() assertTrue(expectMostRecentItem().isEmpty()) @@ -268,7 +268,7 @@ abstract class TranslationsRepositoryIT { } } } - // endregion getTranslationsForToolsAndLocalesFlow() + // endregion getTranslationsFlowForToolsAndLocales() // region translationsChangeFlow() @Test diff --git a/library/download-manager/src/main/kotlin/org/cru/godtools/downloadmanager/GodToolsDownloadManager.kt b/library/download-manager/src/main/kotlin/org/cru/godtools/downloadmanager/GodToolsDownloadManager.kt index 236af4a7dc..934b4a1199 100644 --- a/library/download-manager/src/main/kotlin/org/cru/godtools/downloadmanager/GodToolsDownloadManager.kt +++ b/library/download-manager/src/main/kotlin/org/cru/godtools/downloadmanager/GodToolsDownloadManager.kt @@ -521,7 +521,7 @@ class GodToolsDownloadManager @VisibleForTesting internal constructor( .map { it.mapNotNullTo(mutableSetOf()) { it.code } } .distinctUntilChanged() .combineTransformLatest(languages) { t, l -> - emitAll(translationsRepository.getTranslationsForToolsAndLocalesFlow(t, l)) + emitAll(translationsRepository.getTranslationsFlowForToolsAndLocales(t, l)) } .map { it.filterNot { it.isDownloaded } diff --git a/library/download-manager/src/test/kotlin/org/cru/godtools/downloadmanager/GodToolsDownloadManagerDispatcherTest.kt b/library/download-manager/src/test/kotlin/org/cru/godtools/downloadmanager/GodToolsDownloadManagerDispatcherTest.kt index 8dc14b7c31..f453d71ea2 100644 --- a/library/download-manager/src/test/kotlin/org/cru/godtools/downloadmanager/GodToolsDownloadManagerDispatcherTest.kt +++ b/library/download-manager/src/test/kotlin/org/cru/godtools/downloadmanager/GodToolsDownloadManagerDispatcherTest.kt @@ -67,7 +67,7 @@ class GodToolsDownloadManagerDispatcherTest { } private val translationsRepository: TranslationsRepository by lazy { mockk { - every { getTranslationsForToolsAndLocalesFlow(any(), any()) } returns flowOf(emptyList()) + every { getTranslationsFlowForToolsAndLocales(any(), any()) } returns flowOf(emptyList()) } } private val testScope = TestScope() @@ -94,7 +94,7 @@ class GodToolsDownloadManagerDispatcherTest { val translationsFlow = MutableSharedFlow>(replay = 1) every { - translationsRepository.getTranslationsForToolsAndLocalesFlow( + translationsRepository.getTranslationsFlowForToolsAndLocales( tools = match { it.toSet() == setOf("tool1", "tool2") }, locales = match { it.toSet() == setOf(Locale.FRENCH) } ) @@ -105,7 +105,7 @@ class GodToolsDownloadManagerDispatcherTest { appLanguageFlow.emit(Locale.FRENCH) runCurrent() verifyAll { - translationsRepository.getTranslationsForToolsAndLocalesFlow( + translationsRepository.getTranslationsFlowForToolsAndLocales( tools = match { it.toSet() == setOf("tool1", "tool2") }, locales = match { it.toSet() == setOf(Locale.FRENCH) } ) @@ -125,7 +125,7 @@ class GodToolsDownloadManagerDispatcherTest { fun `Favorite Tools downloadLatestPublishedTranslation() - default language`() = testScope.runTest { val translationsFlow = MutableSharedFlow>(replay = 1) every { - translationsRepository.getTranslationsForToolsAndLocalesFlow( + translationsRepository.getTranslationsFlowForToolsAndLocales( tools = match { it.toSet() == setOf("tool1", "tool2") }, locales = match { it.toSet() == setOf(Settings.defaultLanguage) } ) @@ -135,7 +135,7 @@ class GodToolsDownloadManagerDispatcherTest { favoriteToolsFlow.emit(listOf(Tool("tool1"), Tool("tool2"))) runCurrent() verifyAll { - translationsRepository.getTranslationsForToolsAndLocalesFlow( + translationsRepository.getTranslationsFlowForToolsAndLocales( tools = match { it.toSet() == setOf("tool1", "tool2") }, locales = match { it.toSet() == setOf(Settings.defaultLanguage) } ) @@ -157,7 +157,7 @@ class GodToolsDownloadManagerDispatcherTest { val translationsFlow = MutableSharedFlow>(replay = 1) every { - translationsRepository.getTranslationsForToolsAndLocalesFlow( + translationsRepository.getTranslationsFlowForToolsAndLocales( tools = match { it.toSet() == setOf("tool1", "tool2") }, locales = match { it.toSet() == setOf(Locale.FRENCH, Locale.GERMAN) } ) @@ -168,7 +168,7 @@ class GodToolsDownloadManagerDispatcherTest { pinnedLanguagesFlow.emit(listOf(Language(Locale.FRENCH), Language(Locale.GERMAN))) runCurrent() verifyAll { - translationsRepository.getTranslationsForToolsAndLocalesFlow( + translationsRepository.getTranslationsFlowForToolsAndLocales( tools = match { it.toSet() == setOf("tool1", "tool2") }, locales = match { it.toSet() == setOf(Locale.FRENCH, Locale.GERMAN) } ) diff --git a/ui/article-aem-renderer/src/main/kotlin/org/cru/godtools/article/aem/service/AemArticleManager.kt b/ui/article-aem-renderer/src/main/kotlin/org/cru/godtools/article/aem/service/AemArticleManager.kt index 7b6ef4848b..8bc33a4153 100644 --- a/ui/article-aem-renderer/src/main/kotlin/org/cru/godtools/article/aem/service/AemArticleManager.kt +++ b/ui/article-aem-renderer/src/main/kotlin/org/cru/godtools/article/aem/service/AemArticleManager.kt @@ -236,7 +236,7 @@ class AemArticleManager @VisibleForTesting internal constructor( .toSet() } .distinctUntilChanged() - .flatMapLatest { translationsRepository.getTranslationsForToolsFlow(it) } + .flatMapLatest { translationsRepository.getTranslationsFlowForTools(it) } .map { it.filter { it.isDownloaded } } .conflate() .onEach { aemArticleManager.processDownloadedTranslations(it) } diff --git a/ui/article-aem-renderer/src/test/kotlin/org/cru/godtools/article/aem/service/AemArticleManagerDispatcherTest.kt b/ui/article-aem-renderer/src/test/kotlin/org/cru/godtools/article/aem/service/AemArticleManagerDispatcherTest.kt index fbab81218c..a61efdb19e 100644 --- a/ui/article-aem-renderer/src/test/kotlin/org/cru/godtools/article/aem/service/AemArticleManagerDispatcherTest.kt +++ b/ui/article-aem-renderer/src/test/kotlin/org/cru/godtools/article/aem/service/AemArticleManagerDispatcherTest.kt @@ -51,7 +51,7 @@ class AemArticleManagerDispatcherTest { every { getNormalToolsFlow() } returns flowOf(emptyList()) } private val translationsRepository: TranslationsRepository = mockk { - every { getTranslationsForToolsFlow(any()) } returns translationsFlow + every { getTranslationsFlowForTools(any()) } returns translationsFlow } @Before From a2e1eead9f0ca838820ddfcfbd683a646eedabff Mon Sep 17 00:00:00 2001 From: Daniel Frett Date: Wed, 10 Jan 2024 14:39:48 -0700 Subject: [PATCH 4/8] wrap filter languages in a Filter object to be able to expose the number of tools for a language --- .../cru/godtools/ui/dashboard/tools/ToolFilters.kt | 2 +- .../godtools/ui/dashboard/tools/ToolsPresenter.kt | 3 ++- .../cru/godtools/ui/dashboard/tools/ToolsScreen.kt | 2 +- .../godtools/ui/dashboard/tools/ToolFiltersTest.kt | 13 +++++++------ .../ui/dashboard/tools/ToolsPresenterTest.kt | 4 ++-- 5 files changed, 13 insertions(+), 11 deletions(-) diff --git a/app/src/main/kotlin/org/cru/godtools/ui/dashboard/tools/ToolFilters.kt b/app/src/main/kotlin/org/cru/godtools/ui/dashboard/tools/ToolFilters.kt index 4ad74de57a..4262db324a 100644 --- a/app/src/main/kotlin/org/cru/godtools/ui/dashboard/tools/ToolFilters.kt +++ b/app/src/main/kotlin/org/cru/godtools/ui/dashboard/tools/ToolFilters.kt @@ -181,7 +181,7 @@ internal fun LanguageFilter(filters: ToolsScreen.Filters, modifier: Modifier = M ) } - items(languages, key = { it.code }) { + items(languages, key = { (it) -> it.code }) { (it) -> DropdownMenuItem( text = { LanguageName(it) }, onClick = { diff --git a/app/src/main/kotlin/org/cru/godtools/ui/dashboard/tools/ToolsPresenter.kt b/app/src/main/kotlin/org/cru/godtools/ui/dashboard/tools/ToolsPresenter.kt index cf08f79a93..804b18bf70 100644 --- a/app/src/main/kotlin/org/cru/godtools/ui/dashboard/tools/ToolsPresenter.kt +++ b/app/src/main/kotlin/org/cru/godtools/ui/dashboard/tools/ToolsPresenter.kt @@ -128,7 +128,7 @@ class ToolsPresenter @AssistedInject constructor( @Composable @OptIn(ExperimentalCoroutinesApi::class) - private fun rememberFilterLanguages(category: String?, query: String): List { + private fun rememberFilterLanguages(category: String?, query: String): List> { val categoryFlow = remember { MutableStateFlow(category) }.apply { value = category } val queryFlow = remember { MutableStateFlow(query) }.apply { value = query } @@ -146,6 +146,7 @@ class ToolsPresenter @AssistedInject constructor( combine(languagesFlow, settings.appLanguageFlow, queryFlow) { languages, appLang, query -> languages.filterByDisplayAndNativeName(query, context, appLang) + .map { Filter(it, 0) } } }.collectAsState(emptyList()).value } diff --git a/app/src/main/kotlin/org/cru/godtools/ui/dashboard/tools/ToolsScreen.kt b/app/src/main/kotlin/org/cru/godtools/ui/dashboard/tools/ToolsScreen.kt index bc2401e3c1..bb48999973 100644 --- a/app/src/main/kotlin/org/cru/godtools/ui/dashboard/tools/ToolsScreen.kt +++ b/app/src/main/kotlin/org/cru/godtools/ui/dashboard/tools/ToolsScreen.kt @@ -23,7 +23,7 @@ data object ToolsScreen : Screen { data class Filters( val categories: List> = emptyList(), val selectedCategory: String? = null, - val languages: List = emptyList(), + val languages: List> = emptyList(), val languageQuery: String = "", val selectedLanguage: Language? = null, val eventSink: (FiltersEvent) -> Unit = {}, diff --git a/app/src/testDebug/kotlin/org/cru/godtools/ui/dashboard/tools/ToolFiltersTest.kt b/app/src/testDebug/kotlin/org/cru/godtools/ui/dashboard/tools/ToolFiltersTest.kt index 2a5989f44f..3a0039d26b 100644 --- a/app/src/testDebug/kotlin/org/cru/godtools/ui/dashboard/tools/ToolFiltersTest.kt +++ b/app/src/testDebug/kotlin/org/cru/godtools/ui/dashboard/tools/ToolFiltersTest.kt @@ -10,6 +10,7 @@ import androidx.test.ext.junit.runners.AndroidJUnit4 import com.slack.circuit.test.TestEventSink import java.util.Locale import org.cru.godtools.model.Language +import org.cru.godtools.ui.dashboard.tools.ToolsScreen.Filters.Filter import org.junit.Rule import org.junit.Test import org.junit.runner.RunWith @@ -75,8 +76,8 @@ class ToolFiltersTest { LanguageFilter( filters = ToolsScreen.Filters( languages = listOf( - Language(Locale.FRENCH), - Language(Locale.GERMAN), + Filter(Language(Locale.FRENCH), 1), + Filter(Language(Locale.GERMAN), 1), ), eventSink = events, ), @@ -97,8 +98,8 @@ class ToolFiltersTest { filters = ToolsScreen.Filters( selectedLanguage = Language(Locale.FRENCH), languages = listOf( - Language(Locale.FRENCH), - Language(Locale.GERMAN) + Filter(Language(Locale.FRENCH), 1), + Filter(Language(Locale.GERMAN), 1), ), eventSink = events, ), @@ -120,8 +121,8 @@ class ToolFiltersTest { LanguageFilter( filters = ToolsScreen.Filters( languages = listOf( - Language(Locale.FRENCH), - Language(Locale.GERMAN) + Filter(Language(Locale.FRENCH), 1), + Filter(Language(Locale.GERMAN), 1), ), eventSink = events, ), diff --git a/app/src/testDebug/kotlin/org/cru/godtools/ui/dashboard/tools/ToolsPresenterTest.kt b/app/src/testDebug/kotlin/org/cru/godtools/ui/dashboard/tools/ToolsPresenterTest.kt index 80aecc98ff..8621b6bca8 100644 --- a/app/src/testDebug/kotlin/org/cru/godtools/ui/dashboard/tools/ToolsPresenterTest.kt +++ b/app/src/testDebug/kotlin/org/cru/godtools/ui/dashboard/tools/ToolsPresenterTest.kt @@ -216,7 +216,7 @@ class ToolsPresenterTest { presenter.test { languagesFlow.value = languages - assertEquals(languages, expectMostRecentItem().filters.languages) + assertEquals(languages.map { Filter(it, 0) }, expectMostRecentItem().filters.languages) } verifyAll { @@ -232,7 +232,7 @@ class ToolsPresenterTest { awaitItem().filters.eventSink(ToolsScreen.FiltersEvent.SelectCategory(Tool.CATEGORY_GOSPEL)) gospelLanguagesFlow.value = languages - assertEquals(languages, expectMostRecentItem().filters.languages) + assertEquals(languages.map { Filter(it, 0) }, expectMostRecentItem().filters.languages) } } // endregion State.filters.languages From 6ea5f5e7d8098d1cf9d3f0baa96f1a2a04c6a7c8 Mon Sep 17 00:00:00 2001 From: Daniel Frett Date: Thu, 11 Jan 2024 15:30:29 -0700 Subject: [PATCH 5/8] generate a count of the number of tools for each language when filtering based on languages --- .../ui/dashboard/tools/ToolsPresenter.kt | 27 ++++++++++++++-- .../cru/godtools/ExternalSingletonsModule.kt | 6 +++- .../ui/dashboard/tools/ToolsPresenterTest.kt | 32 +++++++++++++++++++ 3 files changed, 61 insertions(+), 4 deletions(-) diff --git a/app/src/main/kotlin/org/cru/godtools/ui/dashboard/tools/ToolsPresenter.kt b/app/src/main/kotlin/org/cru/godtools/ui/dashboard/tools/ToolsPresenter.kt index 804b18bf70..e640f43801 100644 --- a/app/src/main/kotlin/org/cru/godtools/ui/dashboard/tools/ToolsPresenter.kt +++ b/app/src/main/kotlin/org/cru/godtools/ui/dashboard/tools/ToolsPresenter.kt @@ -21,15 +21,18 @@ import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.combine +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 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 import org.cru.godtools.base.Settings import org.cru.godtools.db.repository.LanguagesRepository import org.cru.godtools.db.repository.ToolsRepository +import org.cru.godtools.db.repository.TranslationsRepository import org.cru.godtools.model.Language import org.cru.godtools.model.Language.Companion.filterByDisplayAndNativeName import org.cru.godtools.model.Tool @@ -48,6 +51,7 @@ class ToolsPresenter @AssistedInject constructor( private val toolCardPresenter: ToolCardPresenter, private val languagesRepository: LanguagesRepository, private val toolsRepository: ToolsRepository, + private val translationsRepository: TranslationsRepository, @Assisted private val navigator: Navigator, ) : Presenter { @Composable @@ -131,6 +135,7 @@ class ToolsPresenter @AssistedInject constructor( private fun rememberFilterLanguages(category: String?, query: String): List> { val categoryFlow = remember { MutableStateFlow(category) }.apply { value = category } val queryFlow = remember { MutableStateFlow(query) }.apply { value = query } + val toolsFlow = rememberFilteredToolsFlow(category = category) return remember { val languagesFlow = categoryFlow @@ -144,9 +149,25 @@ class ToolsPresenter @AssistedInject constructor( languages.sortedWith(Language.displayNameComparator(context, appLang)) } - combine(languagesFlow, settings.appLanguageFlow, queryFlow) { languages, appLang, query -> - languages.filterByDisplayAndNativeName(query, context, appLang) - .map { Filter(it, 0) } + val toolCountsFlow = toolsFlow + .map { it.mapNotNullTo(mutableSetOf()) { it.code } } + .distinctUntilChanged() + .flatMapLatest { translationsRepository.getTranslationsFlowForTools(it) } + .map { translations -> + translations + .groupBy { it.languageCode } + .mapValues { it.value.distinctBy { it.toolCode }.count() } + } + + combine( + languagesFlow, + settings.appLanguageFlow, + queryFlow, + toolCountsFlow, + ) { languages, appLang, query, toolCounts -> + languages + .filterByDisplayAndNativeName(query, context, appLang) + .map { Filter(it, toolCounts[it.code] ?: 0) } } }.collectAsState(emptyList()).value } diff --git a/app/src/test/kotlin/org/cru/godtools/ExternalSingletonsModule.kt b/app/src/test/kotlin/org/cru/godtools/ExternalSingletonsModule.kt index bda7e0e6cb..4ca8b7e0c2 100644 --- a/app/src/test/kotlin/org/cru/godtools/ExternalSingletonsModule.kt +++ b/app/src/test/kotlin/org/cru/godtools/ExternalSingletonsModule.kt @@ -102,7 +102,11 @@ class ExternalSingletonsModule { @get:Provides val trainingTipsRepository: TrainingTipsRepository by lazy { mockk() } @get:Provides - val translationsRepository: TranslationsRepository by lazy { mockk() } + val translationsRepository: TranslationsRepository by lazy { + mockk { + every { getTranslationsFlowForTools(any()) } returns flowOf(emptyList()) + } + } @get:Provides val userRepository: UserRepository by lazy { mockk() } @get:Provides diff --git a/app/src/testDebug/kotlin/org/cru/godtools/ui/dashboard/tools/ToolsPresenterTest.kt b/app/src/testDebug/kotlin/org/cru/godtools/ui/dashboard/tools/ToolsPresenterTest.kt index 8621b6bca8..697172ae13 100644 --- a/app/src/testDebug/kotlin/org/cru/godtools/ui/dashboard/tools/ToolsPresenterTest.kt +++ b/app/src/testDebug/kotlin/org/cru/godtools/ui/dashboard/tools/ToolsPresenterTest.kt @@ -22,9 +22,12 @@ import org.cru.godtools.TestUtils.clearAndroidUiDispatcher import org.cru.godtools.base.Settings import org.cru.godtools.db.repository.LanguagesRepository import org.cru.godtools.db.repository.ToolsRepository +import org.cru.godtools.db.repository.TranslationsRepository import org.cru.godtools.model.Language import org.cru.godtools.model.Tool +import org.cru.godtools.model.Translation import org.cru.godtools.model.randomTool +import org.cru.godtools.model.randomTranslation import org.cru.godtools.ui.banner.BannerType import org.cru.godtools.ui.dashboard.tools.ToolsScreen.Filters.Filter import org.cru.godtools.ui.tools.ToolCardPresenter @@ -57,6 +60,9 @@ class ToolsPresenterTest { every { getNormalToolsFlowByLanguage(any()) } returns flowOf(emptyList()) every { getMetaToolsFlow() } returns metatoolsFlow } + private val translationsRepository: TranslationsRepository = mockk { + every { getTranslationsFlowForTools(any()) } returns flowOf(emptyList()) + } // TODO: figure out how to mock ToolCardPresenter private val toolCardPresenter = ToolCardPresenter( @@ -78,6 +84,7 @@ class ToolsPresenterTest { toolCardPresenter = toolCardPresenter, languagesRepository = languagesRepository, toolsRepository = toolsRepository, + translationsRepository = translationsRepository, navigator = navigator, ) } @@ -235,6 +242,31 @@ class ToolsPresenterTest { assertEquals(languages.map { Filter(it, 0) }, expectMostRecentItem().filters.languages) } } + + @Test + fun `State - filters - languages - include tool count`() = runTest { + val translationsFlow = MutableStateFlow(emptyList()) + every { translationsRepository.getTranslationsFlowForTools(setOf("tool1", "tool2")) } returns translationsFlow + + presenter.test { + toolsFlow.value = listOf( + randomTool("tool1", metatoolCode = null, isHidden = false), + randomTool("tool2", metatoolCode = null, isHidden = false), + ) + translationsFlow.value = listOf( + randomTranslation("tool1", Locale.ENGLISH), + randomTranslation("tool1", Locale.FRENCH), + randomTranslation("tool2", Locale.ENGLISH, version = 1), + randomTranslation("tool2", Locale.ENGLISH, version = 2), + ) + languagesFlow.value = listOf(Language(Locale.ENGLISH), Language(Locale.FRENCH)) + + assertEquals( + listOf(Filter(Language(Locale.ENGLISH), 2), Filter(Language(Locale.FRENCH), 1)), + expectMostRecentItem().filters.languages + ) + } + } // endregion State.filters.languages // region State.filters.selectedLanguage From 462c46f03221e4d1fb855c9618f570e90fae37e5 Mon Sep 17 00:00:00 2001 From: Daniel Frett Date: Thu, 11 Jan 2024 16:35:33 -0700 Subject: [PATCH 6/8] update the languages filter menu to display the number of tools available for a language --- .../ui/dashboard/tools/ToolFilters.kt | 32 +++++++++++++++---- 1 file changed, 25 insertions(+), 7 deletions(-) diff --git a/app/src/main/kotlin/org/cru/godtools/ui/dashboard/tools/ToolFilters.kt b/app/src/main/kotlin/org/cru/godtools/ui/dashboard/tools/ToolFilters.kt index 4262db324a..fcb6c06487 100644 --- a/app/src/main/kotlin/org/cru/godtools/ui/dashboard/tools/ToolFilters.kt +++ b/app/src/main/kotlin/org/cru/godtools/ui/dashboard/tools/ToolFilters.kt @@ -13,7 +13,6 @@ import androidx.compose.foundation.lazy.items import androidx.compose.material.icons.Icons import androidx.compose.material.icons.filled.Search import androidx.compose.material3.DropdownMenu -import androidx.compose.material3.DropdownMenuItem import androidx.compose.material3.ElevatedButton import androidx.compose.material3.ExperimentalMaterial3Api import androidx.compose.material3.ExposedDropdownMenuDefaults @@ -172,8 +171,9 @@ internal fun LanguageFilter(filters: ToolsScreen.Filters, modifier: Modifier = M content = {}, modifier = Modifier.padding(horizontal = 12.dp) ) - DropdownMenuItem( - text = { Text(stringResource(R.string.dashboard_tools_section_filter_language_any)) }, + FilterMenuItem( + label = stringResource(R.string.dashboard_tools_section_filter_language_any), + supportingText = stringResource(R.string.dashboard_tools_section_filter_available_tools_all), onClick = { eventSink(ToolsScreen.FiltersEvent.SelectLanguage(null)) expanded = false @@ -181,9 +181,14 @@ internal fun LanguageFilter(filters: ToolsScreen.Filters, modifier: Modifier = M ) } - items(languages, key = { (it) -> it.code }) { (it) -> - DropdownMenuItem( - text = { LanguageName(it) }, + items(languages, key = { (it) -> it.code }) { (it, count) -> + FilterMenuItem( + label = { LanguageName(it) }, + supportingText = pluralStringResource( + R.plurals.dashboard_tools_section_filter_available_tools, + count, + count, + ), onClick = { eventSink(ToolsScreen.FiltersEvent.SelectLanguage(it.code)) expanded = false @@ -201,8 +206,21 @@ private fun FilterMenuItem( onClick: () -> Unit, modifier: Modifier = Modifier, supportingText: String? = null, +) = FilterMenuItem( + label = { Text(label) }, + onClick = onClick, + modifier = modifier, + supportingText = supportingText, +) + +@Composable +private fun FilterMenuItem( + label: @Composable () -> Unit, + onClick: () -> Unit, + modifier: Modifier = Modifier, + supportingText: String? = null, ) = ListItem( - headlineContent = { Text(label) }, + headlineContent = label, supportingContent = supportingText?.let { { Text(it) } }, modifier = modifier.clickable(onClick = onClick) ) From aa7216f3357ab82270a67a4257e1034155e050d1 Mon Sep 17 00:00:00 2001 From: Daniel Frett Date: Thu, 11 Jan 2024 17:16:00 -0700 Subject: [PATCH 7/8] add unit tests for the CategoryFilter --- .../ui/dashboard/tools/ToolFilters.kt | 7 +- .../ui/dashboard/tools/ToolFiltersTest.kt | 127 ++++++++++++++++-- 2 files changed, 124 insertions(+), 10 deletions(-) diff --git a/app/src/main/kotlin/org/cru/godtools/ui/dashboard/tools/ToolFilters.kt b/app/src/main/kotlin/org/cru/godtools/ui/dashboard/tools/ToolFilters.kt index fcb6c06487..b563430127 100644 --- a/app/src/main/kotlin/org/cru/godtools/ui/dashboard/tools/ToolFilters.kt +++ b/app/src/main/kotlin/org/cru/godtools/ui/dashboard/tools/ToolFilters.kt @@ -71,8 +71,9 @@ internal fun ToolFilters( } @Composable +@VisibleForTesting @OptIn(ExperimentalMaterial3Api::class) -private fun CategoryFilter(filters: ToolsScreen.Filters, modifier: Modifier = Modifier) { +internal fun CategoryFilter(filters: ToolsScreen.Filters, modifier: Modifier = Modifier) { val categories by rememberUpdatedState(filters.categories) val selectedCategory by rememberUpdatedState(filters.selectedCategory) val eventSink by rememberUpdatedState(filters.eventSink) @@ -95,7 +96,9 @@ private fun CategoryFilter(filters: ToolsScreen.Filters, modifier: Modifier = Mo DropdownMenu( expanded = expanded, onDismissRequest = { expanded = false }, - modifier = Modifier.heightIn(max = DROPDOWN_MAX_HEIGHT), + modifier = Modifier + .heightIn(max = DROPDOWN_MAX_HEIGHT) + .testTag(TEST_TAG_FILTER_DROPDOWN) ) { FilterMenuItem( label = stringResource(R.string.dashboard_tools_section_filter_category_any), diff --git a/app/src/testDebug/kotlin/org/cru/godtools/ui/dashboard/tools/ToolFiltersTest.kt b/app/src/testDebug/kotlin/org/cru/godtools/ui/dashboard/tools/ToolFiltersTest.kt index 3a0039d26b..10b691192f 100644 --- a/app/src/testDebug/kotlin/org/cru/godtools/ui/dashboard/tools/ToolFiltersTest.kt +++ b/app/src/testDebug/kotlin/org/cru/godtools/ui/dashboard/tools/ToolFiltersTest.kt @@ -10,9 +10,11 @@ import androidx.test.ext.junit.runners.AndroidJUnit4 import com.slack.circuit.test.TestEventSink import java.util.Locale import org.cru.godtools.model.Language +import org.cru.godtools.model.Tool import org.cru.godtools.ui.dashboard.tools.ToolsScreen.Filters.Filter import org.junit.Rule import org.junit.Test +import org.junit.experimental.categories.Categories.CategoryFilter import org.junit.runner.RunWith import org.robolectric.annotation.Config @@ -24,9 +26,118 @@ class ToolFiltersTest { private val events = TestEventSink() - // region: LanguagesFilter + // region CategoriesFilter @Test - fun `LanguagesFilter() - Shows selectedLanguage`() { + fun `CategoryFilter() - Shows selected category`() { + composeTestRule.setContent { + CategoryFilter( + ToolsScreen.Filters( + selectedCategory = Tool.CATEGORY_GOSPEL, + eventSink = events, + ), + ) + } + + composeTestRule.onNodeWithText("Gospel", substring = true, ignoreCase = true).assertExists() + events.assertNoEvents() + } + + @Test + fun `CategoryFilter() - Shows Any Category when no category is specified`() { + composeTestRule.setContent { + CategoryFilter( + ToolsScreen.Filters( + selectedCategory = null, + eventSink = events, + ), + ) + } + + composeTestRule.onNodeWithText("Any category", substring = true, ignoreCase = true).assertExists() + events.assertNoEvents() + } + + @Test + fun `CategoryFilter() - Dropdown Menu - Show when button is clicked`() { + composeTestRule.setContent { + CategoryFilter(ToolsScreen.Filters(eventSink = events)) + } + + // dropdown menu not shown + composeTestRule.onNodeWithTag(TEST_TAG_FILTER_DROPDOWN).assertDoesNotExist() + + // click button to show dropdown + composeTestRule.onNode(hasClickAction()).performClick() + composeTestRule.onNodeWithTag(TEST_TAG_FILTER_DROPDOWN).assertExists() + events.assertNoEvents() + } + + @Test + fun `CategoryFilter() - Dropdown Menu - Show categories`() { + composeTestRule.setContent { + CategoryFilter( + filters = ToolsScreen.Filters( + categories = listOf( + Filter(Tool.CATEGORY_GOSPEL, 1), + Filter(Tool.CATEGORY_ARTICLES, 1) + ), + eventSink = events, + ), + ) + } + composeTestRule.onNode(hasClickAction()).performClick() + + composeTestRule.onNodeWithText("Growth", substring = true, ignoreCase = true).assertDoesNotExist() + composeTestRule.onNodeWithText("Articles", substring = true, ignoreCase = true).assertExists() + composeTestRule.onNodeWithText("Gospel", substring = true, ignoreCase = true).assertExists() + events.assertNoEvents() + } + + @Test + fun `CategoryFilter() - Dropdown Menu - Select 'Any category' option`() { + composeTestRule.setContent { + CategoryFilter( + filters = ToolsScreen.Filters( + selectedCategory = Tool.CATEGORY_GOSPEL, + categories = listOf( + Filter(Tool.CATEGORY_GOSPEL, 1), + Filter(Tool.CATEGORY_ARTICLES, 1) + ), + eventSink = events, + ), + ) + } + composeTestRule.onNode(hasClickAction()).performClick() + + composeTestRule.onNodeWithText("Any category", substring = true, ignoreCase = true).performClick() + composeTestRule.onNodeWithTag(TEST_TAG_FILTER_DROPDOWN).assertDoesNotExist() + events.assertEvent(ToolsScreen.FiltersEvent.SelectCategory(null)) + } + + @Test + fun `CategoryFilter() - Dropdown Menu - Select a category`() { + composeTestRule.setContent { + CategoryFilter( + filters = ToolsScreen.Filters( + categories = listOf( + Filter(Tool.CATEGORY_GOSPEL, 1), + Filter(Tool.CATEGORY_ARTICLES, 1) + ), + eventSink = events, + ), + ) + } + composeTestRule.onNode(hasClickAction()).performClick() + + composeTestRule.onNodeWithText("Gospel", substring = true, ignoreCase = true).performClick() + composeTestRule.onNodeWithTag(TEST_TAG_FILTER_DROPDOWN).assertDoesNotExist() + events.assertEvent(ToolsScreen.FiltersEvent.SelectCategory(Tool.CATEGORY_GOSPEL)) + } + // endregion CategoryFilter + + // region LanguageFilter + @Test + fun `LanguageFilter() - Shows selectedLanguage`() { composeTestRule.setContent { LanguageFilter( ToolsScreen.Filters( @@ -41,7 +152,7 @@ class ToolFiltersTest { } @Test - fun `LanguagesFilter() - Shows Any Language when no language is specified`() { + fun `LanguageFilter() - Shows Any Language when no language is specified`() { composeTestRule.setContent { LanguageFilter( ToolsScreen.Filters( @@ -56,7 +167,7 @@ class ToolFiltersTest { } @Test - fun `LanguagesFilter() - Dropdown Menu - Show when button is clicked`() { + fun `LanguageFilter() - Dropdown Menu - Show when button is clicked`() { composeTestRule.setContent { LanguageFilter(ToolsScreen.Filters(eventSink = events)) } @@ -71,7 +182,7 @@ class ToolFiltersTest { } @Test - fun `LanguagesFilter() - Dropdown Menu - Show languages`() { + fun `LanguageFilter() - Dropdown Menu - Show languages`() { composeTestRule.setContent { LanguageFilter( filters = ToolsScreen.Filters( @@ -92,7 +203,7 @@ class ToolFiltersTest { } @Test - fun `LanguagesFilter() - Dropdown Menu - Select "Any language" option`() { + fun `LanguageFilter() - Dropdown Menu - Select 'Any language' option`() { composeTestRule.setContent { LanguageFilter( filters = ToolsScreen.Filters( @@ -116,7 +227,7 @@ class ToolFiltersTest { } @Test - fun `LanguagesFilter() - Dropdown Menu - Select a language`() { + fun `LanguageFilter() - Dropdown Menu - Select a language`() { composeTestRule.setContent { LanguageFilter( filters = ToolsScreen.Filters( @@ -137,5 +248,5 @@ class ToolFiltersTest { ToolsScreen.FiltersEvent.SelectLanguage(Locale.FRENCH) ) } - // endregion: LanguagesFilter + // endregion LanguageFilter } From 9fe9ddbe00d783b26ec96d889e32b321943c16e4 Mon Sep 17 00:00:00 2001 From: Daniel Frett Date: Fri, 12 Jan 2024 08:26:01 -0700 Subject: [PATCH 8/8] make suportingText required and not optional --- .../org/cru/godtools/ui/dashboard/tools/ToolFilters.kt | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/app/src/main/kotlin/org/cru/godtools/ui/dashboard/tools/ToolFilters.kt b/app/src/main/kotlin/org/cru/godtools/ui/dashboard/tools/ToolFilters.kt index b563430127..8402dffa73 100644 --- a/app/src/main/kotlin/org/cru/godtools/ui/dashboard/tools/ToolFilters.kt +++ b/app/src/main/kotlin/org/cru/godtools/ui/dashboard/tools/ToolFilters.kt @@ -206,22 +206,22 @@ internal fun LanguageFilter(filters: ToolsScreen.Filters, modifier: Modifier = M @Composable private fun FilterMenuItem( label: String, + supportingText: String?, onClick: () -> Unit, modifier: Modifier = Modifier, - supportingText: String? = null, ) = FilterMenuItem( label = { Text(label) }, + supportingText = supportingText, onClick = onClick, modifier = modifier, - supportingText = supportingText, ) @Composable private fun FilterMenuItem( label: @Composable () -> Unit, + supportingText: String?, onClick: () -> Unit, modifier: Modifier = Modifier, - supportingText: String? = null, ) = ListItem( headlineContent = label, supportingContent = supportingText?.let { { Text(it) } },