From 560d710c23845ba0567570dfcee1a0e236a0becd Mon Sep 17 00:00:00 2001 From: shxn6934 <23094546+shxun6934@users.noreply.github.com> Date: Sun, 20 Aug 2023 13:33:47 +0900 Subject: [PATCH 01/20] add FilterChipWithDropdownMenu composable --- .../component/FilterChipWithDropdownMenu.kt | 139 ++++++++++++++++++ 1 file changed, 139 insertions(+) create mode 100644 feature/sessions/src/main/java/io/github/droidkaigi/confsched2023/sessions/component/FilterChipWithDropdownMenu.kt diff --git a/feature/sessions/src/main/java/io/github/droidkaigi/confsched2023/sessions/component/FilterChipWithDropdownMenu.kt b/feature/sessions/src/main/java/io/github/droidkaigi/confsched2023/sessions/component/FilterChipWithDropdownMenu.kt new file mode 100644 index 000000000..4c45bb027 --- /dev/null +++ b/feature/sessions/src/main/java/io/github/droidkaigi/confsched2023/sessions/component/FilterChipWithDropdownMenu.kt @@ -0,0 +1,139 @@ +package io.github.droidkaigi.confsched2023.sessions.component + +import androidx.compose.foundation.layout.Box +import androidx.compose.foundation.layout.size +import androidx.compose.material.icons.Icons +import androidx.compose.material.icons.filled.ArrowDropDown +import androidx.compose.material.icons.filled.Check +import androidx.compose.material3.DropdownMenu +import androidx.compose.material3.DropdownMenuItem +import androidx.compose.material3.ExperimentalMaterial3Api +import androidx.compose.material3.FilterChip +import androidx.compose.material3.Icon +import androidx.compose.material3.MaterialTheme +import androidx.compose.material3.Text +import androidx.compose.runtime.Composable +import androidx.compose.runtime.getValue +import androidx.compose.runtime.mutableStateOf +import androidx.compose.runtime.remember +import androidx.compose.runtime.rememberUpdatedState +import androidx.compose.runtime.setValue +import androidx.compose.ui.Modifier +import androidx.compose.ui.unit.dp + +data class SearchFilterItemUiState( + val selectedItems: List, + val items: List, + val isSelected: Boolean = false, + val selectedValues: String = "", +) + +@OptIn(ExperimentalMaterial3Api::class) +@Composable +fun FilterChipWithDropdownMenu( + searchFilterItemUiState: SearchFilterItemUiState, + onSelected: (T, Boolean) -> Unit, + filterChipLabel: @Composable () -> Unit, + dropdownMenuItemText: @Composable (T) -> Unit, + modifier: Modifier = Modifier, + filterChipLeadingIcon: @Composable (() -> Unit)? = null, + filterChipTrailingIcon: @Composable (() -> Unit)? = null, + onFilterChipClick: (() -> Unit)? = null, + dropdownMenuItemLeadingIcon: @Composable ((T) -> Unit)? = null, + dropdownMenuItemTrailingIcon: @Composable ((T) -> Unit)? = null, +) { + var expanded by remember { mutableStateOf(false) } + val onSelectedUpdated by rememberUpdatedState(newValue = onSelected) + + val expandMenu = { expanded = true } + val shrinkMenu = { expanded = false } + + val selectedItems = searchFilterItemUiState.selectedItems + + Box( + modifier = modifier, + ) { + FilterChip( + selected = searchFilterItemUiState.isSelected, + onClick = { + onFilterChipClick?.invoke() + expandMenu() + }, + label = filterChipLabel, + leadingIcon = filterChipLeadingIcon, + trailingIcon = filterChipTrailingIcon, + ) + DropdownMenu( + expanded = expanded, + onDismissRequest = shrinkMenu, + ) { + searchFilterItemUiState.items.forEach { item -> + DropdownMenuItem( + text = { + dropdownMenuItemText(item) + }, + leadingIcon = dropdownMenuItemLeadingIcon?.let { icon -> + { icon(item) } + }, + trailingIcon = dropdownMenuItemTrailingIcon?.let { icon -> + { icon(item) } + }, + onClick = { + onSelectedUpdated( + item, + selectedItems.contains(item).not(), + ) + shrinkMenu() + }, + ) + } + } + } +} + +@Composable +fun FilterChipWithDropdownMenu( + searchFilterItemUiState: SearchFilterItemUiState, + onSelected: (T, Boolean) -> Unit, + filterChipLabelDefaultText: String, + dropdownMenuItemText: (T) -> String, + modifier: Modifier = Modifier, + onFilterChipClick: (() -> Unit)? = null, +) { + FilterChipWithDropdownMenu( + modifier = modifier, + searchFilterItemUiState = searchFilterItemUiState, + onSelected = onSelected, + filterChipLabel = { + Text( + text = searchFilterItemUiState.selectedValues.ifEmpty { + filterChipLabelDefaultText + }, + ) + }, + filterChipTrailingIcon = { + Icon( + imageVector = Icons.Default.ArrowDropDown, + contentDescription = null, + ) + }, + onFilterChipClick = onFilterChipClick, + dropdownMenuItemText = { item -> + Text( + text = dropdownMenuItemText(item), + style = MaterialTheme.typography.bodyLarge, + color = MaterialTheme.colorScheme.onSurfaceVariant, + ) + }, + dropdownMenuItemLeadingIcon = { item -> + if (searchFilterItemUiState.selectedItems.contains(item)) { + Icon( + imageVector = Icons.Default.Check, + contentDescription = null, + modifier = Modifier.size(24.dp), + tint = MaterialTheme.colorScheme.onSurfaceVariant, + ) + } + }, + ) +} From 9e9f3c85f010d501493cc71d7c61575a1751b301 Mon Sep 17 00:00:00 2001 From: shxn6934 <23094546+shxun6934@users.noreply.github.com> Date: Sun, 20 Aug 2023 13:34:32 +0900 Subject: [PATCH 02/20] apply to each FilterChip composable --- .../confsched2023/model/TimetableCategory.kt | 30 ++++- .../sessions/component/FilterCategoryChip.kt | 116 +++++++----------- .../sessions/component/FilterDayChip.kt | 111 +++++++---------- .../sessions/component/FilterLanguageChip.kt | 115 +++++++---------- .../component/FilterSessionTypeChip.kt | 115 +++++++---------- .../sessions/component/SearchFilter.kt | 40 +++--- 6 files changed, 237 insertions(+), 290 deletions(-) diff --git a/core/model/src/commonMain/kotlin/io/github/droidkaigi/confsched2023/model/TimetableCategory.kt b/core/model/src/commonMain/kotlin/io/github/droidkaigi/confsched2023/model/TimetableCategory.kt index df1ac5713..2607d8f92 100644 --- a/core/model/src/commonMain/kotlin/io/github/droidkaigi/confsched2023/model/TimetableCategory.kt +++ b/core/model/src/commonMain/kotlin/io/github/droidkaigi/confsched2023/model/TimetableCategory.kt @@ -3,4 +3,32 @@ package io.github.droidkaigi.confsched2023.model public data class TimetableCategory( val id: Int, val title: MultiLangText, -) +) { + public companion object +} + +fun TimetableCategory.Companion.fakes(): List { + return listOf( + TimetableCategory( + id = 1, + title = MultiLangText( + jaTitle = "Kotlin", + enTitle = "Kotlin", + ), + ), + TimetableCategory( + id = 2, + title = MultiLangText( + jaTitle = "Security / Identity / Privacy", + enTitle = "Security / Identity / Privacy", + ), + ), + TimetableCategory( + id = 3, + title = MultiLangText( + jaTitle = "UI・UX・デザイン", + enTitle = "UI・UX・Design", + ), + ), + ) +} diff --git a/feature/sessions/src/main/java/io/github/droidkaigi/confsched2023/sessions/component/FilterCategoryChip.kt b/feature/sessions/src/main/java/io/github/droidkaigi/confsched2023/sessions/component/FilterCategoryChip.kt index f59686832..7d5153ba7 100644 --- a/feature/sessions/src/main/java/io/github/droidkaigi/confsched2023/sessions/component/FilterCategoryChip.kt +++ b/feature/sessions/src/main/java/io/github/droidkaigi/confsched2023/sessions/component/FilterCategoryChip.kt @@ -1,93 +1,71 @@ package io.github.droidkaigi.confsched2023.sessions.component -import androidx.compose.foundation.layout.Box -import androidx.compose.foundation.layout.size -import androidx.compose.material.icons.Icons -import androidx.compose.material.icons.filled.ArrowDropDown -import androidx.compose.material.icons.filled.Check -import androidx.compose.material3.DropdownMenu -import androidx.compose.material3.DropdownMenuItem -import androidx.compose.material3.ExperimentalMaterial3Api -import androidx.compose.material3.FilterChip -import androidx.compose.material3.Icon -import androidx.compose.material3.MaterialTheme -import androidx.compose.material3.Text +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.height import androidx.compose.runtime.Composable import androidx.compose.runtime.getValue import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.remember -import androidx.compose.runtime.rememberUpdatedState import androidx.compose.runtime.setValue import androidx.compose.ui.Modifier import androidx.compose.ui.unit.dp +import io.github.droidkaigi.confsched2023.designsystem.preview.MultiThemePreviews +import io.github.droidkaigi.confsched2023.designsystem.theme.KaigiTheme import io.github.droidkaigi.confsched2023.model.TimetableCategory +import io.github.droidkaigi.confsched2023.model.fakes import io.github.droidkaigi.confsched2023.sessions.SessionsStrings -@OptIn(ExperimentalMaterial3Api::class) @Composable fun FilterCategoryChip( - selectedCategories: List, - categories: List, + searchFilterItemUiState: SearchFilterItemUiState, onCategoriesSelected: (TimetableCategory, Boolean) -> Unit, modifier: Modifier = Modifier, - isSelected: Boolean = false, - selectedCategoriesValues: String = "", onFilterCategoryChipClicked: () -> Unit, ) { - var expanded by remember { mutableStateOf(false) } - val onCategoriesSelectedUpdated by rememberUpdatedState(newValue = onCategoriesSelected) - - Box( + FilterChipWithDropdownMenu( + searchFilterItemUiState = searchFilterItemUiState, + onSelected = onCategoriesSelected, + filterChipLabelDefaultText = SessionsStrings.Category.asString(), + onFilterChipClick = onFilterCategoryChipClicked, + dropdownMenuItemText = { category -> + category.title.currentLangTitle + }, modifier = modifier, - ) { - FilterChip( - selected = isSelected, - onClick = { - onFilterCategoryChipClicked() - expanded = true - }, - label = { Text(text = selectedCategoriesValues.ifEmpty { SessionsStrings.Category.asString() }) }, - trailingIcon = { - Icon( - imageVector = Icons.Default.ArrowDropDown, - contentDescription = null, + ) +} + +@MultiThemePreviews +@Composable +fun PreviewFilterCategoryChip() { + var uiState by remember { + mutableStateOf( + SearchFilterItemUiState( + selectedItems = emptyList(), + items = TimetableCategory.fakes(), + ), + ) + } + + KaigiTheme { + FilterCategoryChip( + searchFilterItemUiState = uiState, + onCategoriesSelected = { category, isSelected -> + val selectedCategories = uiState.selectedItems.toMutableList() + val newSelectedCategories = selectedCategories.apply { + if (isSelected) { + add(category) + } else { + remove(category) + } + } + uiState = uiState.copy( + selectedItems = newSelectedCategories, + isSelected = newSelectedCategories.isNotEmpty(), + selectedValues = newSelectedCategories.joinToString { it.title.currentLangTitle }, ) }, + onFilterCategoryChipClicked = {}, + modifier = Modifier.fillMaxWidth().height(300.dp), ) - DropdownMenu( - expanded = expanded, - onDismissRequest = { expanded = false }, - ) { - categories.forEach { category -> - DropdownMenuItem( - text = { - Text( - text = category.title.currentLangTitle, - style = MaterialTheme.typography.bodyLarge, - color = MaterialTheme.colorScheme.onSurfaceVariant, - ) - }, - leadingIcon = { - if (selectedCategories.contains(category)) { - Icon( - imageVector = Icons.Default.Check, - contentDescription = null, - modifier = Modifier.size(24.dp), - tint = MaterialTheme.colorScheme.onSurfaceVariant, - ) - } - }, - onClick = { - onCategoriesSelectedUpdated( - category, - selectedCategories - .contains(category) - .not(), - ) - expanded = false - }, - ) - } - } } } diff --git a/feature/sessions/src/main/java/io/github/droidkaigi/confsched2023/sessions/component/FilterDayChip.kt b/feature/sessions/src/main/java/io/github/droidkaigi/confsched2023/sessions/component/FilterDayChip.kt index d4943449c..03b0c4f03 100644 --- a/feature/sessions/src/main/java/io/github/droidkaigi/confsched2023/sessions/component/FilterDayChip.kt +++ b/feature/sessions/src/main/java/io/github/droidkaigi/confsched2023/sessions/component/FilterDayChip.kt @@ -1,90 +1,69 @@ package io.github.droidkaigi.confsched2023.sessions.component -import androidx.compose.foundation.layout.Box -import androidx.compose.foundation.layout.size -import androidx.compose.material.icons.Icons -import androidx.compose.material.icons.filled.ArrowDropDown -import androidx.compose.material.icons.filled.Check -import androidx.compose.material3.DropdownMenu -import androidx.compose.material3.DropdownMenuItem -import androidx.compose.material3.ExperimentalMaterial3Api -import androidx.compose.material3.FilterChip -import androidx.compose.material3.Icon -import androidx.compose.material3.MaterialTheme -import androidx.compose.material3.Text +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.height import androidx.compose.runtime.Composable import androidx.compose.runtime.getValue import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.remember -import androidx.compose.runtime.rememberUpdatedState import androidx.compose.runtime.setValue import androidx.compose.ui.Modifier +import androidx.compose.ui.platform.testTag import androidx.compose.ui.unit.dp +import io.github.droidkaigi.confsched2023.designsystem.preview.MultiThemePreviews +import io.github.droidkaigi.confsched2023.designsystem.theme.KaigiTheme import io.github.droidkaigi.confsched2023.model.DroidKaigi2023Day import io.github.droidkaigi.confsched2023.sessions.SessionsStrings import java.util.Locale -@OptIn(ExperimentalMaterial3Api::class) @Composable fun FilterDayChip( - selectedDays: List, - kaigiDays: List, + searchFilterItemUiState: SearchFilterItemUiState, onDaySelected: (DroidKaigi2023Day, Boolean) -> Unit, modifier: Modifier = Modifier, - isSelected: Boolean = false, - selectedDaysValues: String = "", ) { - var expanded by remember { mutableStateOf(false) } - val onDaySelectedUpdated by rememberUpdatedState(newValue = onDaySelected) + FilterChipWithDropdownMenu( + searchFilterItemUiState = searchFilterItemUiState, + onSelected = onDaySelected, + filterChipLabelDefaultText = SessionsStrings.EventDay.asString(), + dropdownMenuItemText = { kaigiDay -> + kaigiDay.getDropDownText(Locale.getDefault().language) + }, + modifier = modifier.testTag(""), + ) +} + +@MultiThemePreviews +@Composable +fun PreviewFilterDayChip() { + var uiState by remember { + mutableStateOf( + SearchFilterItemUiState( + selectedItems = emptyList(), + items = DroidKaigi2023Day.entries.toList(), + ), + ) + } - Box( - modifier = modifier, - ) { - FilterChip( - selected = isSelected, - onClick = { expanded = true }, - label = { Text(text = selectedDaysValues.ifEmpty { SessionsStrings.EventDay.asString() }) }, - trailingIcon = { - Icon( - imageVector = Icons.Default.ArrowDropDown, - contentDescription = null, + KaigiTheme { + FilterDayChip( + searchFilterItemUiState = uiState, + onDaySelected = { kaigiDay, isSelected -> + val selectedDays = uiState.selectedItems.toMutableList() + val newSelectedDays = selectedDays.apply { + if (isSelected) { + add(kaigiDay) + } else { + remove(kaigiDay) + } + }.sortedBy(DroidKaigi2023Day::start) + uiState = uiState.copy( + selectedItems = newSelectedDays, + isSelected = newSelectedDays.isNotEmpty(), + selectedValues = newSelectedDays.joinToString { it.name }, ) }, + modifier = Modifier.fillMaxWidth().height(300.dp), ) - DropdownMenu( - expanded = expanded, - onDismissRequest = { expanded = false }, - ) { - kaigiDays.forEach { kaigiDay -> - DropdownMenuItem( - text = { - Text( - text = kaigiDay.getDropDownText(Locale.getDefault().language), - style = MaterialTheme.typography.bodyLarge, - color = MaterialTheme.colorScheme.onSurfaceVariant, - ) - }, - leadingIcon = { - if (selectedDays.contains(kaigiDay)) { - Icon( - imageVector = Icons.Default.Check, - contentDescription = null, - modifier = Modifier.size(24.dp), - tint = MaterialTheme.colorScheme.onSurfaceVariant, - ) - } - }, - onClick = { - onDaySelectedUpdated( - kaigiDay, - selectedDays - .contains(kaigiDay) - .not(), - ) - expanded = false - }, - ) - } - } } } diff --git a/feature/sessions/src/main/java/io/github/droidkaigi/confsched2023/sessions/component/FilterLanguageChip.kt b/feature/sessions/src/main/java/io/github/droidkaigi/confsched2023/sessions/component/FilterLanguageChip.kt index ed3ea7354..07597401e 100644 --- a/feature/sessions/src/main/java/io/github/droidkaigi/confsched2023/sessions/component/FilterLanguageChip.kt +++ b/feature/sessions/src/main/java/io/github/droidkaigi/confsched2023/sessions/component/FilterLanguageChip.kt @@ -1,93 +1,70 @@ package io.github.droidkaigi.confsched2023.sessions.component -import androidx.compose.foundation.layout.Box -import androidx.compose.foundation.layout.size -import androidx.compose.material.icons.Icons -import androidx.compose.material.icons.filled.ArrowDropDown -import androidx.compose.material.icons.filled.Check -import androidx.compose.material3.DropdownMenu -import androidx.compose.material3.DropdownMenuItem -import androidx.compose.material3.ExperimentalMaterial3Api -import androidx.compose.material3.FilterChip -import androidx.compose.material3.Icon -import androidx.compose.material3.MaterialTheme -import androidx.compose.material3.Text +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.height import androidx.compose.runtime.Composable import androidx.compose.runtime.getValue import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.remember -import androidx.compose.runtime.rememberUpdatedState import androidx.compose.runtime.setValue import androidx.compose.ui.Modifier import androidx.compose.ui.unit.dp +import io.github.droidkaigi.confsched2023.designsystem.preview.MultiThemePreviews +import io.github.droidkaigi.confsched2023.designsystem.theme.KaigiTheme import io.github.droidkaigi.confsched2023.model.Lang import io.github.droidkaigi.confsched2023.sessions.SessionsStrings.SupportedLanguages -@OptIn(ExperimentalMaterial3Api::class) @Composable fun FilterLanguageChip( - selectedLanguages: List, - languages: List, + searchFilterItemUiState: SearchFilterItemUiState, onLanguagesSelected: (Lang, Boolean) -> Unit, modifier: Modifier = Modifier, - isSelected: Boolean = false, - selectedLanguagesValues: String = "", onFilterLanguageChipClicked: () -> Unit, ) { - var expanded by remember { mutableStateOf(false) } - val onLanguagesSelectedUpdated by rememberUpdatedState(newValue = onLanguagesSelected) - - Box( + FilterChipWithDropdownMenu( + searchFilterItemUiState = searchFilterItemUiState, + onSelected = onLanguagesSelected, + filterChipLabelDefaultText = SupportedLanguages.asString(), + onFilterChipClick = onFilterLanguageChipClicked, + dropdownMenuItemText = { language -> + language.tagName + }, modifier = modifier, - ) { - FilterChip( - selected = isSelected, - onClick = { - onFilterLanguageChipClicked() - expanded = true - }, - label = { Text(text = selectedLanguagesValues.ifEmpty { SupportedLanguages.asString() }) }, - trailingIcon = { - Icon( - imageVector = Icons.Default.ArrowDropDown, - contentDescription = null, + ) +} + +@MultiThemePreviews +@Composable +fun PreviewFilterLanguageChip() { + var uiState by remember { + mutableStateOf( + SearchFilterItemUiState( + selectedItems = emptyList(), + items = listOf(Lang.JAPANESE, Lang.ENGLISH), + ), + ) + } + + KaigiTheme { + FilterLanguageChip( + searchFilterItemUiState = uiState, + onLanguagesSelected = { language, isSelected -> + val selectedLanguages = uiState.selectedItems.toMutableList() + val newSelectedLanguages = selectedLanguages.apply { + if (isSelected) { + add(language) + } else { + remove(language) + } + } + uiState = uiState.copy( + selectedItems = newSelectedLanguages, + isSelected = newSelectedLanguages.isNotEmpty(), + selectedValues = newSelectedLanguages.joinToString { it.tagName }, ) }, + onFilterLanguageChipClicked = {}, + modifier = Modifier.fillMaxWidth().height(300.dp), ) - DropdownMenu( - expanded = expanded, - onDismissRequest = { expanded = false }, - ) { - languages.forEach { language -> - DropdownMenuItem( - text = { - Text( - text = language.tagName, - style = MaterialTheme.typography.bodyLarge, - color = MaterialTheme.colorScheme.onSurfaceVariant, - ) - }, - leadingIcon = { - if (selectedLanguages.contains(language)) { - Icon( - imageVector = Icons.Default.Check, - contentDescription = null, - modifier = Modifier.size(24.dp), - tint = MaterialTheme.colorScheme.onSurfaceVariant, - ) - } - }, - onClick = { - onLanguagesSelectedUpdated( - language, - selectedLanguages - .contains(language) - .not(), - ) - expanded = false - }, - ) - } - } } } diff --git a/feature/sessions/src/main/java/io/github/droidkaigi/confsched2023/sessions/component/FilterSessionTypeChip.kt b/feature/sessions/src/main/java/io/github/droidkaigi/confsched2023/sessions/component/FilterSessionTypeChip.kt index 021ba7b58..4fb397974 100644 --- a/feature/sessions/src/main/java/io/github/droidkaigi/confsched2023/sessions/component/FilterSessionTypeChip.kt +++ b/feature/sessions/src/main/java/io/github/droidkaigi/confsched2023/sessions/component/FilterSessionTypeChip.kt @@ -1,93 +1,70 @@ package io.github.droidkaigi.confsched2023.sessions.component -import androidx.compose.foundation.layout.Box -import androidx.compose.foundation.layout.size -import androidx.compose.material.icons.Icons -import androidx.compose.material.icons.filled.ArrowDropDown -import androidx.compose.material.icons.filled.Check -import androidx.compose.material3.DropdownMenu -import androidx.compose.material3.DropdownMenuItem -import androidx.compose.material3.ExperimentalMaterial3Api -import androidx.compose.material3.FilterChip -import androidx.compose.material3.Icon -import androidx.compose.material3.MaterialTheme -import androidx.compose.material3.Text +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.height import androidx.compose.runtime.Composable import androidx.compose.runtime.getValue import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.remember -import androidx.compose.runtime.rememberUpdatedState import androidx.compose.runtime.setValue import androidx.compose.ui.Modifier import androidx.compose.ui.unit.dp +import io.github.droidkaigi.confsched2023.designsystem.preview.MultiThemePreviews +import io.github.droidkaigi.confsched2023.designsystem.theme.KaigiTheme import io.github.droidkaigi.confsched2023.model.TimetableSessionType import io.github.droidkaigi.confsched2023.sessions.SessionsStrings.SessionType -@OptIn(ExperimentalMaterial3Api::class) @Composable fun FilterSessionTypeChip( - selectedSessionTypes: List, - sessionTypes: List, + searchFilterItemUiState: SearchFilterItemUiState, onSessionTypeSelected: (TimetableSessionType, Boolean) -> Unit, modifier: Modifier = Modifier, - isSelected: Boolean = false, - selectedSessionTypesValues: String = "", onFilterSessionTypeChipClicked: () -> Unit, ) { - var expanded by remember { mutableStateOf(false) } - val onLanguagesSelectedUpdated by rememberUpdatedState(newValue = onSessionTypeSelected) - - Box( + FilterChipWithDropdownMenu( + searchFilterItemUiState = searchFilterItemUiState, + onSelected = onSessionTypeSelected, + filterChipLabelDefaultText = SessionType.asString(), + onFilterChipClick = onFilterSessionTypeChipClicked, + dropdownMenuItemText = { sessionType -> + sessionType.label.currentLangTitle + }, modifier = modifier, - ) { - FilterChip( - selected = isSelected, - onClick = { - onFilterSessionTypeChipClicked() - expanded = true - }, - label = { Text(text = selectedSessionTypesValues.ifEmpty { SessionType.asString() }) }, - trailingIcon = { - Icon( - imageVector = Icons.Default.ArrowDropDown, - contentDescription = null, + ) +} + +@MultiThemePreviews +@Composable +fun PreviewFilterSessionTypeChip() { + var uiState by remember { + mutableStateOf( + SearchFilterItemUiState( + selectedItems = emptyList(), + items = TimetableSessionType.entries.toList(), + ), + ) + } + + KaigiTheme { + FilterSessionTypeChip( + searchFilterItemUiState = uiState, + onSessionTypeSelected = { sessionType, isSelected -> + val selectedSessionTypes = uiState.selectedItems.toMutableList() + val newSelectedSessionTypes = selectedSessionTypes.apply { + if (isSelected) { + add(sessionType) + } else { + remove(sessionType) + } + } + uiState = uiState.copy( + selectedItems = newSelectedSessionTypes, + isSelected = newSelectedSessionTypes.isNotEmpty(), + selectedValues = newSelectedSessionTypes.joinToString { it.label.currentLangTitle }, ) }, + onFilterSessionTypeChipClicked = {}, + modifier = Modifier.fillMaxWidth().height(320.dp), ) - DropdownMenu( - expanded = expanded, - onDismissRequest = { expanded = false }, - ) { - sessionTypes.forEach { sessionType -> - DropdownMenuItem( - text = { - Text( - text = sessionType.label.currentLangTitle, - style = MaterialTheme.typography.bodyLarge, - color = MaterialTheme.colorScheme.onSurfaceVariant, - ) - }, - leadingIcon = { - if (selectedSessionTypes.contains(sessionType)) { - Icon( - imageVector = Icons.Default.Check, - contentDescription = null, - modifier = Modifier.size(24.dp), - tint = MaterialTheme.colorScheme.onSurfaceVariant, - ) - } - }, - onClick = { - onLanguagesSelectedUpdated( - sessionType, - selectedSessionTypes - .contains(sessionType) - .not(), - ) - expanded = false - }, - ) - } - } } } diff --git a/feature/sessions/src/main/java/io/github/droidkaigi/confsched2023/sessions/component/SearchFilter.kt b/feature/sessions/src/main/java/io/github/droidkaigi/confsched2023/sessions/component/SearchFilter.kt index fa031f425..38797e320 100644 --- a/feature/sessions/src/main/java/io/github/droidkaigi/confsched2023/sessions/component/SearchFilter.kt +++ b/feature/sessions/src/main/java/io/github/droidkaigi/confsched2023/sessions/component/SearchFilter.kt @@ -66,39 +66,47 @@ fun SearchFilter( ) { item { FilterDayChip( - isSelected = searchFilterUiState.isDaySelected, - selectedDays = searchFilterUiState.selectedDays, - selectedDaysValues = searchFilterUiState.selectedDaysValues, - kaigiDays = DroidKaigi2023Day.entries.toList(), + searchFilterItemUiState = SearchFilterItemUiState( + selectedItems = searchFilterUiState.selectedDays, + items = DroidKaigi2023Day.entries.toList(), + isSelected = searchFilterUiState.isDaySelected, + selectedValues = searchFilterUiState.selectedDaysValues, + ), onDaySelected = onDaySelected, ) } item { FilterCategoryChip( - isSelected = searchFilterUiState.isCategoriesSelected, - selectedCategories = searchFilterUiState.selectedCategories, - selectedCategoriesValues = searchFilterUiState.selectedCategoriesValue, - categories = searchFilterUiState.categories, + searchFilterItemUiState = SearchFilterItemUiState( + selectedItems = searchFilterUiState.selectedCategories, + items = searchFilterUiState.categories, + isSelected = searchFilterUiState.isCategoriesSelected, + selectedValues = searchFilterUiState.selectedCategoriesValue, + ), onCategoriesSelected = onCategoriesSelected, onFilterCategoryChipClicked = { keyboardController?.hide() }, ) } item { FilterSessionTypeChip( - isSelected = searchFilterUiState.isSessionTypeSelected, - selectedSessionTypes = searchFilterUiState.selectedSessionTypes, - selectedSessionTypesValues = searchFilterUiState.selectedSessionTypesValue, - sessionTypes = searchFilterUiState.sessionTypes, + searchFilterItemUiState = SearchFilterItemUiState( + selectedItems = searchFilterUiState.selectedSessionTypes, + items = searchFilterUiState.sessionTypes, + isSelected = searchFilterUiState.isSessionTypeSelected, + selectedValues = searchFilterUiState.selectedSessionTypesValue, + ), onSessionTypeSelected = onSessionTypesSelected, onFilterSessionTypeChipClicked = { keyboardController?.hide() }, ) } item { FilterLanguageChip( - isSelected = searchFilterUiState.isLanguagesSelected, - selectedLanguages = searchFilterUiState.selectedLanguages, - selectedLanguagesValues = searchFilterUiState.selectedLanguagesValue, - languages = listOf(Lang.JAPANESE, Lang.ENGLISH), + searchFilterItemUiState = SearchFilterItemUiState( + selectedItems = searchFilterUiState.selectedLanguages, + items = listOf(Lang.JAPANESE, Lang.ENGLISH), + isSelected = searchFilterUiState.isLanguagesSelected, + selectedValues = searchFilterUiState.selectedLanguagesValue, + ), onLanguagesSelected = onLanguagesSelected, onFilterLanguageChipClicked = { keyboardController?.hide() }, ) From 2ed5b78376309b7cd3c64bebdf18dc0e7bd93e27 Mon Sep 17 00:00:00 2001 From: shxn6934 <23094546+shxun6934@users.noreply.github.com> Date: Sun, 20 Aug 2023 13:46:56 +0900 Subject: [PATCH 03/20] add wrapContentSize to FilterChipWithDropdownMenu modifier --- .../sessions/component/FilterChipWithDropdownMenu.kt | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/feature/sessions/src/main/java/io/github/droidkaigi/confsched2023/sessions/component/FilterChipWithDropdownMenu.kt b/feature/sessions/src/main/java/io/github/droidkaigi/confsched2023/sessions/component/FilterChipWithDropdownMenu.kt index 4c45bb027..f1b8f0687 100644 --- a/feature/sessions/src/main/java/io/github/droidkaigi/confsched2023/sessions/component/FilterChipWithDropdownMenu.kt +++ b/feature/sessions/src/main/java/io/github/droidkaigi/confsched2023/sessions/component/FilterChipWithDropdownMenu.kt @@ -2,6 +2,7 @@ package io.github.droidkaigi.confsched2023.sessions.component import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.size +import androidx.compose.foundation.layout.wrapContentSize import androidx.compose.material.icons.Icons import androidx.compose.material.icons.filled.ArrowDropDown import androidx.compose.material.icons.filled.Check @@ -18,6 +19,7 @@ import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.remember import androidx.compose.runtime.rememberUpdatedState import androidx.compose.runtime.setValue +import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.unit.dp @@ -28,6 +30,7 @@ data class SearchFilterItemUiState( val selectedValues: String = "", ) + @OptIn(ExperimentalMaterial3Api::class) @Composable fun FilterChipWithDropdownMenu( @@ -51,7 +54,7 @@ fun FilterChipWithDropdownMenu( val selectedItems = searchFilterItemUiState.selectedItems Box( - modifier = modifier, + modifier = modifier.wrapContentSize(Alignment.TopStart), ) { FilterChip( selected = searchFilterItemUiState.isSelected, From 0dbf919ebc7aeb68b1f9b970182ff343222135bf Mon Sep 17 00:00:00 2001 From: shxn6934 <23094546+shxun6934@users.noreply.github.com> Date: Sun, 20 Aug 2023 13:47:49 +0900 Subject: [PATCH 04/20] remove space --- .../sessions/component/FilterChipWithDropdownMenu.kt | 1 - 1 file changed, 1 deletion(-) diff --git a/feature/sessions/src/main/java/io/github/droidkaigi/confsched2023/sessions/component/FilterChipWithDropdownMenu.kt b/feature/sessions/src/main/java/io/github/droidkaigi/confsched2023/sessions/component/FilterChipWithDropdownMenu.kt index f1b8f0687..334bda662 100644 --- a/feature/sessions/src/main/java/io/github/droidkaigi/confsched2023/sessions/component/FilterChipWithDropdownMenu.kt +++ b/feature/sessions/src/main/java/io/github/droidkaigi/confsched2023/sessions/component/FilterChipWithDropdownMenu.kt @@ -30,7 +30,6 @@ data class SearchFilterItemUiState( val selectedValues: String = "", ) - @OptIn(ExperimentalMaterial3Api::class) @Composable fun FilterChipWithDropdownMenu( From fd209e4dd7a5ae3220fa10ab3263f5108602211d Mon Sep 17 00:00:00 2001 From: shxn6934 <23094546+shxun6934@users.noreply.github.com> Date: Sun, 20 Aug 2023 14:28:39 +0900 Subject: [PATCH 05/20] fix preview filter chip height --- .../confsched2023/sessions/component/FilterCategoryChip.kt | 4 ---- .../confsched2023/sessions/component/FilterDayChip.kt | 7 +------ .../confsched2023/sessions/component/FilterLanguageChip.kt | 4 ---- .../sessions/component/FilterSessionTypeChip.kt | 4 ---- 4 files changed, 1 insertion(+), 18 deletions(-) diff --git a/feature/sessions/src/main/java/io/github/droidkaigi/confsched2023/sessions/component/FilterCategoryChip.kt b/feature/sessions/src/main/java/io/github/droidkaigi/confsched2023/sessions/component/FilterCategoryChip.kt index 7d5153ba7..ec64c820e 100644 --- a/feature/sessions/src/main/java/io/github/droidkaigi/confsched2023/sessions/component/FilterCategoryChip.kt +++ b/feature/sessions/src/main/java/io/github/droidkaigi/confsched2023/sessions/component/FilterCategoryChip.kt @@ -1,14 +1,11 @@ package io.github.droidkaigi.confsched2023.sessions.component -import androidx.compose.foundation.layout.fillMaxWidth -import androidx.compose.foundation.layout.height import androidx.compose.runtime.Composable import androidx.compose.runtime.getValue import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.remember import androidx.compose.runtime.setValue import androidx.compose.ui.Modifier -import androidx.compose.ui.unit.dp import io.github.droidkaigi.confsched2023.designsystem.preview.MultiThemePreviews import io.github.droidkaigi.confsched2023.designsystem.theme.KaigiTheme import io.github.droidkaigi.confsched2023.model.TimetableCategory @@ -65,7 +62,6 @@ fun PreviewFilterCategoryChip() { ) }, onFilterCategoryChipClicked = {}, - modifier = Modifier.fillMaxWidth().height(300.dp), ) } } diff --git a/feature/sessions/src/main/java/io/github/droidkaigi/confsched2023/sessions/component/FilterDayChip.kt b/feature/sessions/src/main/java/io/github/droidkaigi/confsched2023/sessions/component/FilterDayChip.kt index 03b0c4f03..8028f0140 100644 --- a/feature/sessions/src/main/java/io/github/droidkaigi/confsched2023/sessions/component/FilterDayChip.kt +++ b/feature/sessions/src/main/java/io/github/droidkaigi/confsched2023/sessions/component/FilterDayChip.kt @@ -1,15 +1,11 @@ package io.github.droidkaigi.confsched2023.sessions.component -import androidx.compose.foundation.layout.fillMaxWidth -import androidx.compose.foundation.layout.height import androidx.compose.runtime.Composable import androidx.compose.runtime.getValue import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.remember import androidx.compose.runtime.setValue import androidx.compose.ui.Modifier -import androidx.compose.ui.platform.testTag -import androidx.compose.ui.unit.dp import io.github.droidkaigi.confsched2023.designsystem.preview.MultiThemePreviews import io.github.droidkaigi.confsched2023.designsystem.theme.KaigiTheme import io.github.droidkaigi.confsched2023.model.DroidKaigi2023Day @@ -29,7 +25,7 @@ fun FilterDayChip( dropdownMenuItemText = { kaigiDay -> kaigiDay.getDropDownText(Locale.getDefault().language) }, - modifier = modifier.testTag(""), + modifier = modifier, ) } @@ -63,7 +59,6 @@ fun PreviewFilterDayChip() { selectedValues = newSelectedDays.joinToString { it.name }, ) }, - modifier = Modifier.fillMaxWidth().height(300.dp), ) } } diff --git a/feature/sessions/src/main/java/io/github/droidkaigi/confsched2023/sessions/component/FilterLanguageChip.kt b/feature/sessions/src/main/java/io/github/droidkaigi/confsched2023/sessions/component/FilterLanguageChip.kt index 07597401e..8038e3c11 100644 --- a/feature/sessions/src/main/java/io/github/droidkaigi/confsched2023/sessions/component/FilterLanguageChip.kt +++ b/feature/sessions/src/main/java/io/github/droidkaigi/confsched2023/sessions/component/FilterLanguageChip.kt @@ -1,14 +1,11 @@ package io.github.droidkaigi.confsched2023.sessions.component -import androidx.compose.foundation.layout.fillMaxWidth -import androidx.compose.foundation.layout.height import androidx.compose.runtime.Composable import androidx.compose.runtime.getValue import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.remember import androidx.compose.runtime.setValue import androidx.compose.ui.Modifier -import androidx.compose.ui.unit.dp import io.github.droidkaigi.confsched2023.designsystem.preview.MultiThemePreviews import io.github.droidkaigi.confsched2023.designsystem.theme.KaigiTheme import io.github.droidkaigi.confsched2023.model.Lang @@ -64,7 +61,6 @@ fun PreviewFilterLanguageChip() { ) }, onFilterLanguageChipClicked = {}, - modifier = Modifier.fillMaxWidth().height(300.dp), ) } } diff --git a/feature/sessions/src/main/java/io/github/droidkaigi/confsched2023/sessions/component/FilterSessionTypeChip.kt b/feature/sessions/src/main/java/io/github/droidkaigi/confsched2023/sessions/component/FilterSessionTypeChip.kt index 4fb397974..0a82178f2 100644 --- a/feature/sessions/src/main/java/io/github/droidkaigi/confsched2023/sessions/component/FilterSessionTypeChip.kt +++ b/feature/sessions/src/main/java/io/github/droidkaigi/confsched2023/sessions/component/FilterSessionTypeChip.kt @@ -1,14 +1,11 @@ package io.github.droidkaigi.confsched2023.sessions.component -import androidx.compose.foundation.layout.fillMaxWidth -import androidx.compose.foundation.layout.height import androidx.compose.runtime.Composable import androidx.compose.runtime.getValue import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.remember import androidx.compose.runtime.setValue import androidx.compose.ui.Modifier -import androidx.compose.ui.unit.dp import io.github.droidkaigi.confsched2023.designsystem.preview.MultiThemePreviews import io.github.droidkaigi.confsched2023.designsystem.theme.KaigiTheme import io.github.droidkaigi.confsched2023.model.TimetableSessionType @@ -64,7 +61,6 @@ fun PreviewFilterSessionTypeChip() { ) }, onFilterSessionTypeChipClicked = {}, - modifier = Modifier.fillMaxWidth().height(320.dp), ) } } From 0a7a1e2c785e3822951df09fcd4f12833f64b12b Mon Sep 17 00:00:00 2001 From: shxn6934 <23094546+shxun6934@users.noreply.github.com> Date: Sun, 20 Aug 2023 15:46:11 +0900 Subject: [PATCH 06/20] add FilterChip UiTest for SearchScreenTest --- .../testing/robot/SearchScreenRobot.kt | 101 +++++++++++++++++ .../sessions/component/FilterCategoryChip.kt | 5 +- .../component/FilterChipWithDropdownMenu.kt | 4 + .../sessions/component/FilterDayChip.kt | 5 +- .../sessions/component/FilterLanguageChip.kt | 5 +- .../component/FilterSessionTypeChip.kt | 5 +- .../sessions/component/SearchFilter.kt | 5 +- .../sessions/SearchScreenTest.kt | 106 ++++++++++++++++++ 8 files changed, 231 insertions(+), 5 deletions(-) create mode 100644 core/testing/src/main/java/io/github/droidkaigi/confsched2023/testing/robot/SearchScreenRobot.kt create mode 100644 feature/sessions/src/test/java/io/github/droidkaigi/confsched2023/sessions/SearchScreenTest.kt diff --git a/core/testing/src/main/java/io/github/droidkaigi/confsched2023/testing/robot/SearchScreenRobot.kt b/core/testing/src/main/java/io/github/droidkaigi/confsched2023/testing/robot/SearchScreenRobot.kt new file mode 100644 index 000000000..de2b4fc62 --- /dev/null +++ b/core/testing/src/main/java/io/github/droidkaigi/confsched2023/testing/robot/SearchScreenRobot.kt @@ -0,0 +1,101 @@ +package io.github.droidkaigi.confsched2023.testing.robot + +import androidx.compose.ui.test.hasTestTag +import androidx.compose.ui.test.isRoot +import androidx.compose.ui.test.junit4.AndroidComposeTestRule +import androidx.compose.ui.test.onFirst +import androidx.compose.ui.test.onLast +import androidx.compose.ui.test.performClick +import androidx.compose.ui.test.performTouchInput +import androidx.compose.ui.test.swipeLeft +import com.github.takahirom.roborazzi.captureRoboImage +import io.github.droidkaigi.confsched2023.designsystem.theme.KaigiTheme +import io.github.droidkaigi.confsched2023.sessions.SearchScreen +import io.github.droidkaigi.confsched2023.sessions.SearchScreenTestTag +import io.github.droidkaigi.confsched2023.sessions.component.FilterChipWithDropdownMenuItemTestTag +import io.github.droidkaigi.confsched2023.sessions.component.SearchFilterTestTag +import io.github.droidkaigi.confsched2023.testing.RobotTestRule +import io.github.droidkaigi.confsched2023.testing.coroutines.runTestWithLogging +import kotlinx.coroutines.test.TestDispatcher +import javax.inject.Inject +import kotlin.time.Duration.Companion.seconds + +class SearchScreenRobot @Inject constructor( + private val testDispatcher: TestDispatcher, +) { + @Inject lateinit var robotTestRule: RobotTestRule + + private lateinit var composeTestRule: AndroidComposeTestRule<*, *> + + operator fun invoke( + block: SearchScreenRobot.() -> Unit, + ) { + runTestWithLogging(timeout = 30.seconds) { + this@SearchScreenRobot.composeTestRule = robotTestRule.composeTestRule + block() + } + } + + fun setupSearchScreenContent() { + composeTestRule.setContent { + KaigiTheme { + SearchScreen( + onBackClick = {}, + onTimetableItemClick = {}, + ) + } + } + waitUntilIdle() + } + + fun clickFilterChip(filterChipTestTag: String) { + composeTestRule + .onNode(hasTestTag(filterChipTestTag)) + .performClick() + waitUntilIdle() + } + + fun scrollSearchFilterToLeft() { + composeTestRule + .onNode(hasTestTag(SearchFilterTestTag)) + .performTouchInput { + swipeLeft( + startX = visibleSize.width.toFloat(), + endX = visibleSize.width / 2f, + ) + } + } + + fun clickFirstDropdownMenuItem() { + composeTestRule + .onAllNodes(hasTestTag(FilterChipWithDropdownMenuItemTestTag)) + .onFirst() + .performClick() + waitUntilIdle() + } + + fun clickLastDropdownMenuItem() { + composeTestRule + .onAllNodes(hasTestTag(FilterChipWithDropdownMenuItemTestTag)) + .onLast() + .performClick() + waitUntilIdle() + } + + fun checkScreenCapture() { + composeTestRule + .onNode(isRoot()) + .captureRoboImage() + } + + fun checkSearchScreenCapture() { + composeTestRule + .onNode(hasTestTag(SearchScreenTestTag)) + .captureRoboImage() + } + + fun waitUntilIdle() { + composeTestRule.waitForIdle() + testDispatcher.scheduler.advanceUntilIdle() + } +} diff --git a/feature/sessions/src/main/java/io/github/droidkaigi/confsched2023/sessions/component/FilterCategoryChip.kt b/feature/sessions/src/main/java/io/github/droidkaigi/confsched2023/sessions/component/FilterCategoryChip.kt index ec64c820e..79c4a82bf 100644 --- a/feature/sessions/src/main/java/io/github/droidkaigi/confsched2023/sessions/component/FilterCategoryChip.kt +++ b/feature/sessions/src/main/java/io/github/droidkaigi/confsched2023/sessions/component/FilterCategoryChip.kt @@ -6,12 +6,15 @@ import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.remember import androidx.compose.runtime.setValue import androidx.compose.ui.Modifier +import androidx.compose.ui.platform.testTag import io.github.droidkaigi.confsched2023.designsystem.preview.MultiThemePreviews import io.github.droidkaigi.confsched2023.designsystem.theme.KaigiTheme import io.github.droidkaigi.confsched2023.model.TimetableCategory import io.github.droidkaigi.confsched2023.model.fakes import io.github.droidkaigi.confsched2023.sessions.SessionsStrings +const val FilterCategoryChipTestTag = "FilterCategoryChip" + @Composable fun FilterCategoryChip( searchFilterItemUiState: SearchFilterItemUiState, @@ -27,7 +30,7 @@ fun FilterCategoryChip( dropdownMenuItemText = { category -> category.title.currentLangTitle }, - modifier = modifier, + modifier = modifier.testTag(FilterCategoryChipTestTag), ) } diff --git a/feature/sessions/src/main/java/io/github/droidkaigi/confsched2023/sessions/component/FilterChipWithDropdownMenu.kt b/feature/sessions/src/main/java/io/github/droidkaigi/confsched2023/sessions/component/FilterChipWithDropdownMenu.kt index 334bda662..2bd40712c 100644 --- a/feature/sessions/src/main/java/io/github/droidkaigi/confsched2023/sessions/component/FilterChipWithDropdownMenu.kt +++ b/feature/sessions/src/main/java/io/github/droidkaigi/confsched2023/sessions/component/FilterChipWithDropdownMenu.kt @@ -21,6 +21,7 @@ import androidx.compose.runtime.rememberUpdatedState import androidx.compose.runtime.setValue import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier +import androidx.compose.ui.platform.testTag import androidx.compose.ui.unit.dp data class SearchFilterItemUiState( @@ -30,6 +31,8 @@ data class SearchFilterItemUiState( val selectedValues: String = "", ) +const val FilterChipWithDropdownMenuItemTestTag = "FilterChipWithDropdownMenuItem" + @OptIn(ExperimentalMaterial3Api::class) @Composable fun FilterChipWithDropdownMenu( @@ -87,6 +90,7 @@ fun FilterChipWithDropdownMenu( ) shrinkMenu() }, + modifier = Modifier.testTag(FilterChipWithDropdownMenuItemTestTag), ) } } diff --git a/feature/sessions/src/main/java/io/github/droidkaigi/confsched2023/sessions/component/FilterDayChip.kt b/feature/sessions/src/main/java/io/github/droidkaigi/confsched2023/sessions/component/FilterDayChip.kt index 8028f0140..0eff32361 100644 --- a/feature/sessions/src/main/java/io/github/droidkaigi/confsched2023/sessions/component/FilterDayChip.kt +++ b/feature/sessions/src/main/java/io/github/droidkaigi/confsched2023/sessions/component/FilterDayChip.kt @@ -6,12 +6,15 @@ import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.remember import androidx.compose.runtime.setValue import androidx.compose.ui.Modifier +import androidx.compose.ui.platform.testTag import io.github.droidkaigi.confsched2023.designsystem.preview.MultiThemePreviews import io.github.droidkaigi.confsched2023.designsystem.theme.KaigiTheme import io.github.droidkaigi.confsched2023.model.DroidKaigi2023Day import io.github.droidkaigi.confsched2023.sessions.SessionsStrings import java.util.Locale +const val FilterDayChipTestTag = "FilterDayChip" + @Composable fun FilterDayChip( searchFilterItemUiState: SearchFilterItemUiState, @@ -25,7 +28,7 @@ fun FilterDayChip( dropdownMenuItemText = { kaigiDay -> kaigiDay.getDropDownText(Locale.getDefault().language) }, - modifier = modifier, + modifier = modifier.testTag(FilterDayChipTestTag), ) } diff --git a/feature/sessions/src/main/java/io/github/droidkaigi/confsched2023/sessions/component/FilterLanguageChip.kt b/feature/sessions/src/main/java/io/github/droidkaigi/confsched2023/sessions/component/FilterLanguageChip.kt index 8038e3c11..a140f421b 100644 --- a/feature/sessions/src/main/java/io/github/droidkaigi/confsched2023/sessions/component/FilterLanguageChip.kt +++ b/feature/sessions/src/main/java/io/github/droidkaigi/confsched2023/sessions/component/FilterLanguageChip.kt @@ -6,11 +6,14 @@ import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.remember import androidx.compose.runtime.setValue import androidx.compose.ui.Modifier +import androidx.compose.ui.platform.testTag import io.github.droidkaigi.confsched2023.designsystem.preview.MultiThemePreviews import io.github.droidkaigi.confsched2023.designsystem.theme.KaigiTheme import io.github.droidkaigi.confsched2023.model.Lang import io.github.droidkaigi.confsched2023.sessions.SessionsStrings.SupportedLanguages +const val FilterLanguageChipTestTag = "FilterLanguageChip" + @Composable fun FilterLanguageChip( searchFilterItemUiState: SearchFilterItemUiState, @@ -26,7 +29,7 @@ fun FilterLanguageChip( dropdownMenuItemText = { language -> language.tagName }, - modifier = modifier, + modifier = modifier.testTag(FilterLanguageChipTestTag), ) } diff --git a/feature/sessions/src/main/java/io/github/droidkaigi/confsched2023/sessions/component/FilterSessionTypeChip.kt b/feature/sessions/src/main/java/io/github/droidkaigi/confsched2023/sessions/component/FilterSessionTypeChip.kt index 0a82178f2..23df376c4 100644 --- a/feature/sessions/src/main/java/io/github/droidkaigi/confsched2023/sessions/component/FilterSessionTypeChip.kt +++ b/feature/sessions/src/main/java/io/github/droidkaigi/confsched2023/sessions/component/FilterSessionTypeChip.kt @@ -6,11 +6,14 @@ import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.remember import androidx.compose.runtime.setValue import androidx.compose.ui.Modifier +import androidx.compose.ui.platform.testTag import io.github.droidkaigi.confsched2023.designsystem.preview.MultiThemePreviews import io.github.droidkaigi.confsched2023.designsystem.theme.KaigiTheme import io.github.droidkaigi.confsched2023.model.TimetableSessionType import io.github.droidkaigi.confsched2023.sessions.SessionsStrings.SessionType +const val FilterSessionTypeChipTestTag = "FilterSessionTypeChip" + @Composable fun FilterSessionTypeChip( searchFilterItemUiState: SearchFilterItemUiState, @@ -26,7 +29,7 @@ fun FilterSessionTypeChip( dropdownMenuItemText = { sessionType -> sessionType.label.currentLangTitle }, - modifier = modifier, + modifier = modifier.testTag(FilterSessionTypeChipTestTag), ) } diff --git a/feature/sessions/src/main/java/io/github/droidkaigi/confsched2023/sessions/component/SearchFilter.kt b/feature/sessions/src/main/java/io/github/droidkaigi/confsched2023/sessions/component/SearchFilter.kt index 38797e320..125160f66 100644 --- a/feature/sessions/src/main/java/io/github/droidkaigi/confsched2023/sessions/component/SearchFilter.kt +++ b/feature/sessions/src/main/java/io/github/droidkaigi/confsched2023/sessions/component/SearchFilter.kt @@ -7,6 +7,7 @@ import androidx.compose.runtime.Composable import androidx.compose.ui.ExperimentalComposeUiApi import androidx.compose.ui.Modifier import androidx.compose.ui.platform.LocalSoftwareKeyboardController +import androidx.compose.ui.platform.testTag import androidx.compose.ui.unit.dp import io.github.droidkaigi.confsched2023.model.DroidKaigi2023Day import io.github.droidkaigi.confsched2023.model.Lang @@ -47,6 +48,8 @@ data class SearchFilterUiState( get() = selectedSessionTypes.isNotEmpty() } +const val SearchFilterTestTag = "SearchFilter" + @OptIn(ExperimentalComposeUiApi::class) @Composable fun SearchFilter( @@ -60,7 +63,7 @@ fun SearchFilter( val keyboardController = LocalSoftwareKeyboardController.current LazyRow( - modifier = modifier, + modifier = modifier.testTag(SearchFilterTestTag), horizontalArrangement = Arrangement.spacedBy(8.dp), contentPadding = PaddingValues(horizontal = 16.dp), ) { diff --git a/feature/sessions/src/test/java/io/github/droidkaigi/confsched2023/sessions/SearchScreenTest.kt b/feature/sessions/src/test/java/io/github/droidkaigi/confsched2023/sessions/SearchScreenTest.kt new file mode 100644 index 000000000..5d3e971db --- /dev/null +++ b/feature/sessions/src/test/java/io/github/droidkaigi/confsched2023/sessions/SearchScreenTest.kt @@ -0,0 +1,106 @@ +package io.github.droidkaigi.confsched2023.sessions + +import androidx.test.ext.junit.runners.AndroidJUnit4 +import com.github.takahirom.roborazzi.RobolectricDeviceQualifiers +import dagger.hilt.android.testing.BindValue +import dagger.hilt.android.testing.HiltAndroidTest +import io.github.droidkaigi.confsched2023.sessions.component.FilterCategoryChipTestTag +import io.github.droidkaigi.confsched2023.sessions.component.FilterDayChipTestTag +import io.github.droidkaigi.confsched2023.sessions.component.FilterLanguageChipTestTag +import io.github.droidkaigi.confsched2023.sessions.component.FilterSessionTypeChipTestTag +import io.github.droidkaigi.confsched2023.testing.HiltTestActivity +import io.github.droidkaigi.confsched2023.testing.RobotTestRule +import io.github.droidkaigi.confsched2023.testing.category.ScreenshotTests +import io.github.droidkaigi.confsched2023.testing.robot.SearchScreenRobot +import org.junit.Rule +import org.junit.Test +import org.junit.experimental.categories.Category +import org.junit.runner.RunWith +import org.robolectric.annotation.Config +import org.robolectric.annotation.GraphicsMode +import javax.inject.Inject + +@RunWith(AndroidJUnit4::class) +@GraphicsMode(GraphicsMode.Mode.NATIVE) +@HiltAndroidTest +@Config( + qualifiers = RobolectricDeviceQualifiers.NexusOne, +) +class SearchScreenTest { + + @get:Rule + @BindValue val robotTestRule: RobotTestRule = RobotTestRule(this) + + @Inject + lateinit var searchScreenRobot: SearchScreenRobot + + @Test + @Category(ScreenshotTests::class) + fun checkLaunchShot() { + searchScreenRobot { + setupSearchScreenContent() + checkScreenCapture() + } + } + + @Test + @Category(ScreenshotTests::class) + fun checkFilterDayChipShot() { + searchScreenRobot { + checkFilterChipShot(FilterDayChipTestTag) + } + } + + @Test + @Category(ScreenshotTests::class) + fun checkFilterCategoryChipShot() { + searchScreenRobot { + checkFilterChipShot(FilterCategoryChipTestTag) + } + } + + @Test + @Category(ScreenshotTests::class) + fun checkFilterSessionTypeChipShot() { + searchScreenRobot { + checkFilterChipShot(FilterSessionTypeChipTestTag) { + scrollSearchFilterToLeft() + } + } + } + + @Test + @Category(ScreenshotTests::class) + fun checkFilterLanguageChipShot() { + searchScreenRobot { + checkFilterChipShot(FilterLanguageChipTestTag) { + scrollSearchFilterToLeft() + } + } + } + + private fun SearchScreenRobot.checkFilterChipShot( + filterChipTestTag: String, + scrollToLeft: (() -> Unit)? = null, + ) { + setupSearchScreenContent() + scrollToLeft?.invoke() + + // select item + clickFilterChip(filterChipTestTag) + clickFirstDropdownMenuItem() + checkSearchScreenCapture() + + // select other item + clickFilterChip(filterChipTestTag) + clickLastDropdownMenuItem() + checkSearchScreenCapture() + + // remove all items + clickFilterChip(filterChipTestTag) + clickFirstDropdownMenuItem() + clickFilterChip(filterChipTestTag) + clickLastDropdownMenuItem() + checkSearchScreenCapture() + } +} From 842adb92d9aa6a97b7f414751f68e3954f8fad89 Mon Sep 17 00:00:00 2001 From: shxun6934 <23094546+shxun6934@users.noreply.github.com> Date: Mon, 21 Aug 2023 20:09:41 +0900 Subject: [PATCH 07/20] rename FilterChipWithDropdownMenu to DropdownFilterChip --- .../confsched2023/testing/robot/SearchScreenRobot.kt | 6 +++--- ...erChipWithDropdownMenu.kt => DropdownFilterChip.kt} | 10 +++++----- .../sessions/component/FilterCategoryChip.kt | 2 +- .../confsched2023/sessions/component/FilterDayChip.kt | 2 +- .../sessions/component/FilterLanguageChip.kt | 2 +- .../sessions/component/FilterSessionTypeChip.kt | 2 +- 6 files changed, 12 insertions(+), 12 deletions(-) rename feature/sessions/src/main/java/io/github/droidkaigi/confsched2023/sessions/component/{FilterChipWithDropdownMenu.kt => DropdownFilterChip.kt} (94%) diff --git a/core/testing/src/main/java/io/github/droidkaigi/confsched2023/testing/robot/SearchScreenRobot.kt b/core/testing/src/main/java/io/github/droidkaigi/confsched2023/testing/robot/SearchScreenRobot.kt index de2b4fc62..d042aa360 100644 --- a/core/testing/src/main/java/io/github/droidkaigi/confsched2023/testing/robot/SearchScreenRobot.kt +++ b/core/testing/src/main/java/io/github/droidkaigi/confsched2023/testing/robot/SearchScreenRobot.kt @@ -12,7 +12,7 @@ import com.github.takahirom.roborazzi.captureRoboImage import io.github.droidkaigi.confsched2023.designsystem.theme.KaigiTheme import io.github.droidkaigi.confsched2023.sessions.SearchScreen import io.github.droidkaigi.confsched2023.sessions.SearchScreenTestTag -import io.github.droidkaigi.confsched2023.sessions.component.FilterChipWithDropdownMenuItemTestTag +import io.github.droidkaigi.confsched2023.sessions.component.DropdownFilterChipItemTestTag import io.github.droidkaigi.confsched2023.sessions.component.SearchFilterTestTag import io.github.droidkaigi.confsched2023.testing.RobotTestRule import io.github.droidkaigi.confsched2023.testing.coroutines.runTestWithLogging @@ -68,7 +68,7 @@ class SearchScreenRobot @Inject constructor( fun clickFirstDropdownMenuItem() { composeTestRule - .onAllNodes(hasTestTag(FilterChipWithDropdownMenuItemTestTag)) + .onAllNodes(hasTestTag(DropdownFilterChipItemTestTag)) .onFirst() .performClick() waitUntilIdle() @@ -76,7 +76,7 @@ class SearchScreenRobot @Inject constructor( fun clickLastDropdownMenuItem() { composeTestRule - .onAllNodes(hasTestTag(FilterChipWithDropdownMenuItemTestTag)) + .onAllNodes(hasTestTag(DropdownFilterChipItemTestTag)) .onLast() .performClick() waitUntilIdle() diff --git a/feature/sessions/src/main/java/io/github/droidkaigi/confsched2023/sessions/component/FilterChipWithDropdownMenu.kt b/feature/sessions/src/main/java/io/github/droidkaigi/confsched2023/sessions/component/DropdownFilterChip.kt similarity index 94% rename from feature/sessions/src/main/java/io/github/droidkaigi/confsched2023/sessions/component/FilterChipWithDropdownMenu.kt rename to feature/sessions/src/main/java/io/github/droidkaigi/confsched2023/sessions/component/DropdownFilterChip.kt index 2bd40712c..f09f12635 100644 --- a/feature/sessions/src/main/java/io/github/droidkaigi/confsched2023/sessions/component/FilterChipWithDropdownMenu.kt +++ b/feature/sessions/src/main/java/io/github/droidkaigi/confsched2023/sessions/component/DropdownFilterChip.kt @@ -31,11 +31,11 @@ data class SearchFilterItemUiState( val selectedValues: String = "", ) -const val FilterChipWithDropdownMenuItemTestTag = "FilterChipWithDropdownMenuItem" +const val DropdownFilterChipItemTestTag = "DropdownFilterChipItem" @OptIn(ExperimentalMaterial3Api::class) @Composable -fun FilterChipWithDropdownMenu( +fun DropdownFilterChip( searchFilterItemUiState: SearchFilterItemUiState, onSelected: (T, Boolean) -> Unit, filterChipLabel: @Composable () -> Unit, @@ -90,7 +90,7 @@ fun FilterChipWithDropdownMenu( ) shrinkMenu() }, - modifier = Modifier.testTag(FilterChipWithDropdownMenuItemTestTag), + modifier = Modifier.testTag(DropdownFilterChipItemTestTag), ) } } @@ -98,7 +98,7 @@ fun FilterChipWithDropdownMenu( } @Composable -fun FilterChipWithDropdownMenu( +fun DropdownFilterChip( searchFilterItemUiState: SearchFilterItemUiState, onSelected: (T, Boolean) -> Unit, filterChipLabelDefaultText: String, @@ -106,7 +106,7 @@ fun FilterChipWithDropdownMenu( modifier: Modifier = Modifier, onFilterChipClick: (() -> Unit)? = null, ) { - FilterChipWithDropdownMenu( + DropdownFilterChip( modifier = modifier, searchFilterItemUiState = searchFilterItemUiState, onSelected = onSelected, diff --git a/feature/sessions/src/main/java/io/github/droidkaigi/confsched2023/sessions/component/FilterCategoryChip.kt b/feature/sessions/src/main/java/io/github/droidkaigi/confsched2023/sessions/component/FilterCategoryChip.kt index 79c4a82bf..095296067 100644 --- a/feature/sessions/src/main/java/io/github/droidkaigi/confsched2023/sessions/component/FilterCategoryChip.kt +++ b/feature/sessions/src/main/java/io/github/droidkaigi/confsched2023/sessions/component/FilterCategoryChip.kt @@ -22,7 +22,7 @@ fun FilterCategoryChip( modifier: Modifier = Modifier, onFilterCategoryChipClicked: () -> Unit, ) { - FilterChipWithDropdownMenu( + DropdownFilterChip( searchFilterItemUiState = searchFilterItemUiState, onSelected = onCategoriesSelected, filterChipLabelDefaultText = SessionsStrings.Category.asString(), diff --git a/feature/sessions/src/main/java/io/github/droidkaigi/confsched2023/sessions/component/FilterDayChip.kt b/feature/sessions/src/main/java/io/github/droidkaigi/confsched2023/sessions/component/FilterDayChip.kt index 0eff32361..b66090b13 100644 --- a/feature/sessions/src/main/java/io/github/droidkaigi/confsched2023/sessions/component/FilterDayChip.kt +++ b/feature/sessions/src/main/java/io/github/droidkaigi/confsched2023/sessions/component/FilterDayChip.kt @@ -21,7 +21,7 @@ fun FilterDayChip( onDaySelected: (DroidKaigi2023Day, Boolean) -> Unit, modifier: Modifier = Modifier, ) { - FilterChipWithDropdownMenu( + DropdownFilterChip( searchFilterItemUiState = searchFilterItemUiState, onSelected = onDaySelected, filterChipLabelDefaultText = SessionsStrings.EventDay.asString(), diff --git a/feature/sessions/src/main/java/io/github/droidkaigi/confsched2023/sessions/component/FilterLanguageChip.kt b/feature/sessions/src/main/java/io/github/droidkaigi/confsched2023/sessions/component/FilterLanguageChip.kt index a140f421b..194827a6a 100644 --- a/feature/sessions/src/main/java/io/github/droidkaigi/confsched2023/sessions/component/FilterLanguageChip.kt +++ b/feature/sessions/src/main/java/io/github/droidkaigi/confsched2023/sessions/component/FilterLanguageChip.kt @@ -21,7 +21,7 @@ fun FilterLanguageChip( modifier: Modifier = Modifier, onFilterLanguageChipClicked: () -> Unit, ) { - FilterChipWithDropdownMenu( + DropdownFilterChip( searchFilterItemUiState = searchFilterItemUiState, onSelected = onLanguagesSelected, filterChipLabelDefaultText = SupportedLanguages.asString(), diff --git a/feature/sessions/src/main/java/io/github/droidkaigi/confsched2023/sessions/component/FilterSessionTypeChip.kt b/feature/sessions/src/main/java/io/github/droidkaigi/confsched2023/sessions/component/FilterSessionTypeChip.kt index 23df376c4..65ef2b5b0 100644 --- a/feature/sessions/src/main/java/io/github/droidkaigi/confsched2023/sessions/component/FilterSessionTypeChip.kt +++ b/feature/sessions/src/main/java/io/github/droidkaigi/confsched2023/sessions/component/FilterSessionTypeChip.kt @@ -21,7 +21,7 @@ fun FilterSessionTypeChip( modifier: Modifier = Modifier, onFilterSessionTypeChipClicked: () -> Unit, ) { - FilterChipWithDropdownMenu( + DropdownFilterChip( searchFilterItemUiState = searchFilterItemUiState, onSelected = onSessionTypeSelected, filterChipLabelDefaultText = SessionType.asString(), From f218e3634b076944368bcd83cf836eb912119ccb Mon Sep 17 00:00:00 2001 From: shxun6934 <23094546+shxun6934@users.noreply.github.com> Date: Mon, 21 Aug 2023 21:13:37 +0900 Subject: [PATCH 08/20] rename SearchFilterItemUiState to DropdownFilterChipUiState --- .../sessions/component/DropdownFilterChip.kt | 18 +++++++++--------- .../sessions/component/FilterCategoryChip.kt | 8 ++++---- .../sessions/component/FilterDayChip.kt | 8 ++++---- .../sessions/component/FilterLanguageChip.kt | 8 ++++---- .../component/FilterSessionTypeChip.kt | 8 ++++---- .../sessions/component/SearchFilter.kt | 8 ++++---- 6 files changed, 29 insertions(+), 29 deletions(-) diff --git a/feature/sessions/src/main/java/io/github/droidkaigi/confsched2023/sessions/component/DropdownFilterChip.kt b/feature/sessions/src/main/java/io/github/droidkaigi/confsched2023/sessions/component/DropdownFilterChip.kt index f09f12635..b6f220b5f 100644 --- a/feature/sessions/src/main/java/io/github/droidkaigi/confsched2023/sessions/component/DropdownFilterChip.kt +++ b/feature/sessions/src/main/java/io/github/droidkaigi/confsched2023/sessions/component/DropdownFilterChip.kt @@ -24,7 +24,7 @@ import androidx.compose.ui.Modifier import androidx.compose.ui.platform.testTag import androidx.compose.ui.unit.dp -data class SearchFilterItemUiState( +data class DropdownFilterChipUiState( val selectedItems: List, val items: List, val isSelected: Boolean = false, @@ -36,7 +36,7 @@ const val DropdownFilterChipItemTestTag = "DropdownFilterChipItem" @OptIn(ExperimentalMaterial3Api::class) @Composable fun DropdownFilterChip( - searchFilterItemUiState: SearchFilterItemUiState, + dropdownFilterChipUiState: DropdownFilterChipUiState, onSelected: (T, Boolean) -> Unit, filterChipLabel: @Composable () -> Unit, dropdownMenuItemText: @Composable (T) -> Unit, @@ -53,13 +53,13 @@ fun DropdownFilterChip( val expandMenu = { expanded = true } val shrinkMenu = { expanded = false } - val selectedItems = searchFilterItemUiState.selectedItems + val selectedItems = dropdownFilterChipUiState.selectedItems Box( modifier = modifier.wrapContentSize(Alignment.TopStart), ) { FilterChip( - selected = searchFilterItemUiState.isSelected, + selected = dropdownFilterChipUiState.isSelected, onClick = { onFilterChipClick?.invoke() expandMenu() @@ -72,7 +72,7 @@ fun DropdownFilterChip( expanded = expanded, onDismissRequest = shrinkMenu, ) { - searchFilterItemUiState.items.forEach { item -> + dropdownFilterChipUiState.items.forEach { item -> DropdownMenuItem( text = { dropdownMenuItemText(item) @@ -99,7 +99,7 @@ fun DropdownFilterChip( @Composable fun DropdownFilterChip( - searchFilterItemUiState: SearchFilterItemUiState, + dropdownFilterChipUiState: DropdownFilterChipUiState, onSelected: (T, Boolean) -> Unit, filterChipLabelDefaultText: String, dropdownMenuItemText: (T) -> String, @@ -108,11 +108,11 @@ fun DropdownFilterChip( ) { DropdownFilterChip( modifier = modifier, - searchFilterItemUiState = searchFilterItemUiState, + dropdownFilterChipUiState = dropdownFilterChipUiState, onSelected = onSelected, filterChipLabel = { Text( - text = searchFilterItemUiState.selectedValues.ifEmpty { + text = dropdownFilterChipUiState.selectedValues.ifEmpty { filterChipLabelDefaultText }, ) @@ -132,7 +132,7 @@ fun DropdownFilterChip( ) }, dropdownMenuItemLeadingIcon = { item -> - if (searchFilterItemUiState.selectedItems.contains(item)) { + if (dropdownFilterChipUiState.selectedItems.contains(item)) { Icon( imageVector = Icons.Default.Check, contentDescription = null, diff --git a/feature/sessions/src/main/java/io/github/droidkaigi/confsched2023/sessions/component/FilterCategoryChip.kt b/feature/sessions/src/main/java/io/github/droidkaigi/confsched2023/sessions/component/FilterCategoryChip.kt index 095296067..89486c08e 100644 --- a/feature/sessions/src/main/java/io/github/droidkaigi/confsched2023/sessions/component/FilterCategoryChip.kt +++ b/feature/sessions/src/main/java/io/github/droidkaigi/confsched2023/sessions/component/FilterCategoryChip.kt @@ -17,13 +17,13 @@ const val FilterCategoryChipTestTag = "FilterCategoryChip" @Composable fun FilterCategoryChip( - searchFilterItemUiState: SearchFilterItemUiState, + dropdownFilterChipUiState: DropdownFilterChipUiState, onCategoriesSelected: (TimetableCategory, Boolean) -> Unit, modifier: Modifier = Modifier, onFilterCategoryChipClicked: () -> Unit, ) { DropdownFilterChip( - searchFilterItemUiState = searchFilterItemUiState, + dropdownFilterChipUiState = dropdownFilterChipUiState, onSelected = onCategoriesSelected, filterChipLabelDefaultText = SessionsStrings.Category.asString(), onFilterChipClick = onFilterCategoryChipClicked, @@ -39,7 +39,7 @@ fun FilterCategoryChip( fun PreviewFilterCategoryChip() { var uiState by remember { mutableStateOf( - SearchFilterItemUiState( + DropdownFilterChipUiState( selectedItems = emptyList(), items = TimetableCategory.fakes(), ), @@ -48,7 +48,7 @@ fun PreviewFilterCategoryChip() { KaigiTheme { FilterCategoryChip( - searchFilterItemUiState = uiState, + dropdownFilterChipUiState = uiState, onCategoriesSelected = { category, isSelected -> val selectedCategories = uiState.selectedItems.toMutableList() val newSelectedCategories = selectedCategories.apply { diff --git a/feature/sessions/src/main/java/io/github/droidkaigi/confsched2023/sessions/component/FilterDayChip.kt b/feature/sessions/src/main/java/io/github/droidkaigi/confsched2023/sessions/component/FilterDayChip.kt index b66090b13..b4e811cbc 100644 --- a/feature/sessions/src/main/java/io/github/droidkaigi/confsched2023/sessions/component/FilterDayChip.kt +++ b/feature/sessions/src/main/java/io/github/droidkaigi/confsched2023/sessions/component/FilterDayChip.kt @@ -17,12 +17,12 @@ const val FilterDayChipTestTag = "FilterDayChip" @Composable fun FilterDayChip( - searchFilterItemUiState: SearchFilterItemUiState, + dropdownFilterChipUiState: DropdownFilterChipUiState, onDaySelected: (DroidKaigi2023Day, Boolean) -> Unit, modifier: Modifier = Modifier, ) { DropdownFilterChip( - searchFilterItemUiState = searchFilterItemUiState, + dropdownFilterChipUiState = dropdownFilterChipUiState, onSelected = onDaySelected, filterChipLabelDefaultText = SessionsStrings.EventDay.asString(), dropdownMenuItemText = { kaigiDay -> @@ -37,7 +37,7 @@ fun FilterDayChip( fun PreviewFilterDayChip() { var uiState by remember { mutableStateOf( - SearchFilterItemUiState( + DropdownFilterChipUiState( selectedItems = emptyList(), items = DroidKaigi2023Day.entries.toList(), ), @@ -46,7 +46,7 @@ fun PreviewFilterDayChip() { KaigiTheme { FilterDayChip( - searchFilterItemUiState = uiState, + dropdownFilterChipUiState = uiState, onDaySelected = { kaigiDay, isSelected -> val selectedDays = uiState.selectedItems.toMutableList() val newSelectedDays = selectedDays.apply { diff --git a/feature/sessions/src/main/java/io/github/droidkaigi/confsched2023/sessions/component/FilterLanguageChip.kt b/feature/sessions/src/main/java/io/github/droidkaigi/confsched2023/sessions/component/FilterLanguageChip.kt index 194827a6a..65b08b2d8 100644 --- a/feature/sessions/src/main/java/io/github/droidkaigi/confsched2023/sessions/component/FilterLanguageChip.kt +++ b/feature/sessions/src/main/java/io/github/droidkaigi/confsched2023/sessions/component/FilterLanguageChip.kt @@ -16,13 +16,13 @@ const val FilterLanguageChipTestTag = "FilterLanguageChip" @Composable fun FilterLanguageChip( - searchFilterItemUiState: SearchFilterItemUiState, + dropdownFilterChipUiState: DropdownFilterChipUiState, onLanguagesSelected: (Lang, Boolean) -> Unit, modifier: Modifier = Modifier, onFilterLanguageChipClicked: () -> Unit, ) { DropdownFilterChip( - searchFilterItemUiState = searchFilterItemUiState, + dropdownFilterChipUiState = dropdownFilterChipUiState, onSelected = onLanguagesSelected, filterChipLabelDefaultText = SupportedLanguages.asString(), onFilterChipClick = onFilterLanguageChipClicked, @@ -38,7 +38,7 @@ fun FilterLanguageChip( fun PreviewFilterLanguageChip() { var uiState by remember { mutableStateOf( - SearchFilterItemUiState( + DropdownFilterChipUiState( selectedItems = emptyList(), items = listOf(Lang.JAPANESE, Lang.ENGLISH), ), @@ -47,7 +47,7 @@ fun PreviewFilterLanguageChip() { KaigiTheme { FilterLanguageChip( - searchFilterItemUiState = uiState, + dropdownFilterChipUiState = uiState, onLanguagesSelected = { language, isSelected -> val selectedLanguages = uiState.selectedItems.toMutableList() val newSelectedLanguages = selectedLanguages.apply { diff --git a/feature/sessions/src/main/java/io/github/droidkaigi/confsched2023/sessions/component/FilterSessionTypeChip.kt b/feature/sessions/src/main/java/io/github/droidkaigi/confsched2023/sessions/component/FilterSessionTypeChip.kt index 65ef2b5b0..4d574a550 100644 --- a/feature/sessions/src/main/java/io/github/droidkaigi/confsched2023/sessions/component/FilterSessionTypeChip.kt +++ b/feature/sessions/src/main/java/io/github/droidkaigi/confsched2023/sessions/component/FilterSessionTypeChip.kt @@ -16,13 +16,13 @@ const val FilterSessionTypeChipTestTag = "FilterSessionTypeChip" @Composable fun FilterSessionTypeChip( - searchFilterItemUiState: SearchFilterItemUiState, + dropdownFilterChipUiState: DropdownFilterChipUiState, onSessionTypeSelected: (TimetableSessionType, Boolean) -> Unit, modifier: Modifier = Modifier, onFilterSessionTypeChipClicked: () -> Unit, ) { DropdownFilterChip( - searchFilterItemUiState = searchFilterItemUiState, + dropdownFilterChipUiState = dropdownFilterChipUiState, onSelected = onSessionTypeSelected, filterChipLabelDefaultText = SessionType.asString(), onFilterChipClick = onFilterSessionTypeChipClicked, @@ -38,7 +38,7 @@ fun FilterSessionTypeChip( fun PreviewFilterSessionTypeChip() { var uiState by remember { mutableStateOf( - SearchFilterItemUiState( + DropdownFilterChipUiState( selectedItems = emptyList(), items = TimetableSessionType.entries.toList(), ), @@ -47,7 +47,7 @@ fun PreviewFilterSessionTypeChip() { KaigiTheme { FilterSessionTypeChip( - searchFilterItemUiState = uiState, + dropdownFilterChipUiState = uiState, onSessionTypeSelected = { sessionType, isSelected -> val selectedSessionTypes = uiState.selectedItems.toMutableList() val newSelectedSessionTypes = selectedSessionTypes.apply { diff --git a/feature/sessions/src/main/java/io/github/droidkaigi/confsched2023/sessions/component/SearchFilter.kt b/feature/sessions/src/main/java/io/github/droidkaigi/confsched2023/sessions/component/SearchFilter.kt index 125160f66..3773ab935 100644 --- a/feature/sessions/src/main/java/io/github/droidkaigi/confsched2023/sessions/component/SearchFilter.kt +++ b/feature/sessions/src/main/java/io/github/droidkaigi/confsched2023/sessions/component/SearchFilter.kt @@ -69,7 +69,7 @@ fun SearchFilter( ) { item { FilterDayChip( - searchFilterItemUiState = SearchFilterItemUiState( + dropdownFilterChipUiState = DropdownFilterChipUiState( selectedItems = searchFilterUiState.selectedDays, items = DroidKaigi2023Day.entries.toList(), isSelected = searchFilterUiState.isDaySelected, @@ -80,7 +80,7 @@ fun SearchFilter( } item { FilterCategoryChip( - searchFilterItemUiState = SearchFilterItemUiState( + dropdownFilterChipUiState = DropdownFilterChipUiState( selectedItems = searchFilterUiState.selectedCategories, items = searchFilterUiState.categories, isSelected = searchFilterUiState.isCategoriesSelected, @@ -92,7 +92,7 @@ fun SearchFilter( } item { FilterSessionTypeChip( - searchFilterItemUiState = SearchFilterItemUiState( + dropdownFilterChipUiState = DropdownFilterChipUiState( selectedItems = searchFilterUiState.selectedSessionTypes, items = searchFilterUiState.sessionTypes, isSelected = searchFilterUiState.isSessionTypeSelected, @@ -104,7 +104,7 @@ fun SearchFilter( } item { FilterLanguageChip( - searchFilterItemUiState = SearchFilterItemUiState( + dropdownFilterChipUiState = DropdownFilterChipUiState( selectedItems = searchFilterUiState.selectedLanguages, items = listOf(Lang.JAPANESE, Lang.ENGLISH), isSelected = searchFilterUiState.isLanguagesSelected, From f06d51d20e09776fa00885875564c22c68cdc13d Mon Sep 17 00:00:00 2001 From: shxun6934 <23094546+shxun6934@users.noreply.github.com> Date: Mon, 21 Aug 2023 21:42:39 +0900 Subject: [PATCH 09/20] apply generics to SearchFilterUiState --- .../testing/robot/SearchScreenRobot.kt | 2 +- .../confsched2023/sessions/SearchScreen.kt | 24 +++- .../sessions/SearchScreenViewModel.kt | 72 ++++++++--- .../sessions/component/DropdownFilterChip.kt | 24 ++-- .../sessions/component/FilterCategoryChip.kt | 9 +- .../sessions/component/FilterDayChip.kt | 9 +- .../sessions/component/FilterLanguageChip.kt | 13 +- .../component/FilterSessionTypeChip.kt | 9 +- .../sessions/component/SearchFilter.kt | 118 ------------------ .../sessions/section/SearchFilter.kt | 78 ++++++++++++ 10 files changed, 186 insertions(+), 172 deletions(-) delete mode 100644 feature/sessions/src/main/java/io/github/droidkaigi/confsched2023/sessions/component/SearchFilter.kt create mode 100644 feature/sessions/src/main/java/io/github/droidkaigi/confsched2023/sessions/section/SearchFilter.kt diff --git a/core/testing/src/main/java/io/github/droidkaigi/confsched2023/testing/robot/SearchScreenRobot.kt b/core/testing/src/main/java/io/github/droidkaigi/confsched2023/testing/robot/SearchScreenRobot.kt index d042aa360..e99834223 100644 --- a/core/testing/src/main/java/io/github/droidkaigi/confsched2023/testing/robot/SearchScreenRobot.kt +++ b/core/testing/src/main/java/io/github/droidkaigi/confsched2023/testing/robot/SearchScreenRobot.kt @@ -13,7 +13,7 @@ import io.github.droidkaigi.confsched2023.designsystem.theme.KaigiTheme import io.github.droidkaigi.confsched2023.sessions.SearchScreen import io.github.droidkaigi.confsched2023.sessions.SearchScreenTestTag import io.github.droidkaigi.confsched2023.sessions.component.DropdownFilterChipItemTestTag -import io.github.droidkaigi.confsched2023.sessions.component.SearchFilterTestTag +import io.github.droidkaigi.confsched2023.sessions.section.SearchFilterTestTag import io.github.droidkaigi.confsched2023.testing.RobotTestRule import io.github.droidkaigi.confsched2023.testing.coroutines.runTestWithLogging import kotlinx.coroutines.test.TestDispatcher diff --git a/feature/sessions/src/main/java/io/github/droidkaigi/confsched2023/sessions/SearchScreen.kt b/feature/sessions/src/main/java/io/github/droidkaigi/confsched2023/sessions/SearchScreen.kt index 40d4c18e7..422b1de36 100644 --- a/feature/sessions/src/main/java/io/github/droidkaigi/confsched2023/sessions/SearchScreen.kt +++ b/feature/sessions/src/main/java/io/github/droidkaigi/confsched2023/sessions/SearchScreen.kt @@ -25,9 +25,9 @@ import io.github.droidkaigi.confsched2023.model.TimetableSessionType import io.github.droidkaigi.confsched2023.sessions.SearchScreenUiState.Empty import io.github.droidkaigi.confsched2023.sessions.SearchScreenUiState.SearchList import io.github.droidkaigi.confsched2023.sessions.component.EmptySearchResultBody -import io.github.droidkaigi.confsched2023.sessions.component.SearchFilter -import io.github.droidkaigi.confsched2023.sessions.component.SearchFilterUiState import io.github.droidkaigi.confsched2023.sessions.component.SearchTextFieldAppBar +import io.github.droidkaigi.confsched2023.sessions.section.SearchFilter +import io.github.droidkaigi.confsched2023.sessions.section.SearchFilterUiState import io.github.droidkaigi.confsched2023.sessions.section.SearchList import io.github.droidkaigi.confsched2023.sessions.section.SearchListUiState @@ -36,16 +36,25 @@ const val SearchScreenTestTag = "SearchScreen" sealed interface SearchScreenUiState { val searchQuery: String - val searchFilterUiState: SearchFilterUiState + val searchFilterDayUiState: SearchFilterUiState + val searchFilterCategoryUiState: SearchFilterUiState + val searchFilterSessionTypeUiState: SearchFilterUiState + val searchFilterLanguageUiState: SearchFilterUiState data class Empty( override val searchQuery: String, - override val searchFilterUiState: SearchFilterUiState, + override val searchFilterDayUiState: SearchFilterUiState, + override val searchFilterCategoryUiState: SearchFilterUiState, + override val searchFilterSessionTypeUiState: SearchFilterUiState, + override val searchFilterLanguageUiState: SearchFilterUiState, ) : SearchScreenUiState data class SearchList( override val searchQuery: String, - override val searchFilterUiState: SearchFilterUiState, + override val searchFilterDayUiState: SearchFilterUiState, + override val searchFilterCategoryUiState: SearchFilterUiState, + override val searchFilterSessionTypeUiState: SearchFilterUiState, + override val searchFilterLanguageUiState: SearchFilterUiState, val searchListUiState: SearchListUiState, ) : SearchScreenUiState } @@ -121,7 +130,10 @@ private fun SearchScreen( color = MaterialTheme.colorScheme.outline, ) SearchFilter( - searchFilterUiState = uiState.searchFilterUiState, + searchFilterDayUiState = uiState.searchFilterDayUiState, + searchFilterCategoryUiState = uiState.searchFilterCategoryUiState, + searchFilterSessionTypeUiState = uiState.searchFilterSessionTypeUiState, + searchFilterLanguageUiState = uiState.searchFilterLanguageUiState, onDaySelected = onDaySelected, onCategoriesSelected = onCategoriesSelected, onSessionTypesSelected = onSessionTypesSelected, diff --git a/feature/sessions/src/main/java/io/github/droidkaigi/confsched2023/sessions/SearchScreenViewModel.kt b/feature/sessions/src/main/java/io/github/droidkaigi/confsched2023/sessions/SearchScreenViewModel.kt index 075058954..71f33d551 100644 --- a/feature/sessions/src/main/java/io/github/droidkaigi/confsched2023/sessions/SearchScreenViewModel.kt +++ b/feature/sessions/src/main/java/io/github/droidkaigi/confsched2023/sessions/SearchScreenViewModel.kt @@ -13,7 +13,7 @@ import io.github.droidkaigi.confsched2023.model.Timetable import io.github.droidkaigi.confsched2023.model.TimetableCategory import io.github.droidkaigi.confsched2023.model.TimetableItem import io.github.droidkaigi.confsched2023.model.TimetableSessionType -import io.github.droidkaigi.confsched2023.sessions.component.SearchFilterUiState +import io.github.droidkaigi.confsched2023.sessions.section.SearchFilterUiState import io.github.droidkaigi.confsched2023.sessions.section.SearchListUiState import io.github.droidkaigi.confsched2023.ui.UserMessageStateHolder import io.github.droidkaigi.confsched2023.ui.buildUiState @@ -68,24 +68,20 @@ class SearchScreenViewModel @Inject constructor( if (searchedSessions.isEmpty()) { SearchScreenUiState.Empty( searchQuery = searchQuery, - searchFilterUiState = SearchFilterUiState( - selectedDays = filters.days, - selectedCategories = filters.categories, - selectedSessionTypes = filters.sessionTypes, - selectedLanguages = filters.languages, - ), + searchFilterDayUiState = searchFilterDayUiState(filters.days), + searchFilterCategoryUiState = searchFilterCategoryUiState(filters.categories), + searchFilterSessionTypeUiState = searchFilterSessionTypeUiState(filters.sessionTypes), + searchFilterLanguageUiState = searchFilterLanguageUiState(filters.languages), ) } else { SearchScreenUiState.SearchList( searchQuery = searchQuery, - searchFilterUiState = SearchFilterUiState( - categories = sessions.categories, - sessionTypes = sessions.sessionTypes, - selectedDays = filters.days, - selectedCategories = filters.categories, - selectedSessionTypes = filters.sessionTypes, - selectedLanguages = filters.languages, - ), + searchFilterDayUiState = searchFilterDayUiState(filters.days), + searchFilterCategoryUiState = + searchFilterCategoryUiState(filters.categories, sessions.categories), + searchFilterSessionTypeUiState = + searchFilterSessionTypeUiState(filters.sessionTypes, sessions.sessionTypes), + searchFilterLanguageUiState = searchFilterLanguageUiState(filters.languages), searchListUiState = SearchListUiState( bookmarkedTimetableItemIds = sessions.bookmarks, timetableItems = searchedSessions.timetableItems, @@ -94,6 +90,52 @@ class SearchScreenViewModel @Inject constructor( } } + private fun searchFilterDayUiState( + selectedDays: List, + ): SearchFilterUiState { + return SearchFilterUiState( + selectedItems = selectedDays, + items = DroidKaigi2023Day.entries.toList(), + isSelected = selectedDays.isNotEmpty(), + selectedValues = selectedDays.joinToString { it.name }, + ) + } + + private fun searchFilterCategoryUiState( + selectedCategories: List, + categories: List? = null, + ): SearchFilterUiState { + return SearchFilterUiState( + selectedItems = selectedCategories, + items = categories.orEmpty(), + isSelected = selectedCategories.isNotEmpty(), + selectedValues = selectedCategories.joinToString { it.title.currentLangTitle }, + ) + } + + private fun searchFilterSessionTypeUiState( + selectedSessionTypes: List, + sessionTypes: List? = null, + ): SearchFilterUiState { + return SearchFilterUiState( + selectedItems = selectedSessionTypes, + items = sessionTypes.orEmpty(), + isSelected = selectedSessionTypes.isNotEmpty(), + selectedValues = selectedSessionTypes.joinToString { it.label.currentLangTitle }, + ) + } + + private fun searchFilterLanguageUiState( + selectedLanguages: List, + ): SearchFilterUiState { + return SearchFilterUiState( + selectedItems = selectedLanguages, + items = listOf(Lang.JAPANESE, Lang.ENGLISH), + isSelected = selectedLanguages.isNotEmpty(), + selectedValues = selectedLanguages.joinToString { it.tagName }, + ) + } + fun onSearchQueryChanged(searchQuery: String) { savedStateHandle[SEARCH_QUERY] = searchQuery } diff --git a/feature/sessions/src/main/java/io/github/droidkaigi/confsched2023/sessions/component/DropdownFilterChip.kt b/feature/sessions/src/main/java/io/github/droidkaigi/confsched2023/sessions/component/DropdownFilterChip.kt index b6f220b5f..33d1bebe0 100644 --- a/feature/sessions/src/main/java/io/github/droidkaigi/confsched2023/sessions/component/DropdownFilterChip.kt +++ b/feature/sessions/src/main/java/io/github/droidkaigi/confsched2023/sessions/component/DropdownFilterChip.kt @@ -23,20 +23,14 @@ import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.platform.testTag import androidx.compose.ui.unit.dp - -data class DropdownFilterChipUiState( - val selectedItems: List, - val items: List, - val isSelected: Boolean = false, - val selectedValues: String = "", -) +import io.github.droidkaigi.confsched2023.sessions.section.SearchFilterUiState const val DropdownFilterChipItemTestTag = "DropdownFilterChipItem" @OptIn(ExperimentalMaterial3Api::class) @Composable fun DropdownFilterChip( - dropdownFilterChipUiState: DropdownFilterChipUiState, + searchFilterUiState: SearchFilterUiState, onSelected: (T, Boolean) -> Unit, filterChipLabel: @Composable () -> Unit, dropdownMenuItemText: @Composable (T) -> Unit, @@ -53,13 +47,13 @@ fun DropdownFilterChip( val expandMenu = { expanded = true } val shrinkMenu = { expanded = false } - val selectedItems = dropdownFilterChipUiState.selectedItems + val selectedItems = searchFilterUiState.selectedItems Box( modifier = modifier.wrapContentSize(Alignment.TopStart), ) { FilterChip( - selected = dropdownFilterChipUiState.isSelected, + selected = searchFilterUiState.isSelected, onClick = { onFilterChipClick?.invoke() expandMenu() @@ -72,7 +66,7 @@ fun DropdownFilterChip( expanded = expanded, onDismissRequest = shrinkMenu, ) { - dropdownFilterChipUiState.items.forEach { item -> + searchFilterUiState.items.forEach { item -> DropdownMenuItem( text = { dropdownMenuItemText(item) @@ -99,7 +93,7 @@ fun DropdownFilterChip( @Composable fun DropdownFilterChip( - dropdownFilterChipUiState: DropdownFilterChipUiState, + searchFilterUiState: SearchFilterUiState, onSelected: (T, Boolean) -> Unit, filterChipLabelDefaultText: String, dropdownMenuItemText: (T) -> String, @@ -108,11 +102,11 @@ fun DropdownFilterChip( ) { DropdownFilterChip( modifier = modifier, - dropdownFilterChipUiState = dropdownFilterChipUiState, + searchFilterUiState = searchFilterUiState, onSelected = onSelected, filterChipLabel = { Text( - text = dropdownFilterChipUiState.selectedValues.ifEmpty { + text = searchFilterUiState.selectedValues.ifEmpty { filterChipLabelDefaultText }, ) @@ -132,7 +126,7 @@ fun DropdownFilterChip( ) }, dropdownMenuItemLeadingIcon = { item -> - if (dropdownFilterChipUiState.selectedItems.contains(item)) { + if (searchFilterUiState.selectedItems.contains(item)) { Icon( imageVector = Icons.Default.Check, contentDescription = null, diff --git a/feature/sessions/src/main/java/io/github/droidkaigi/confsched2023/sessions/component/FilterCategoryChip.kt b/feature/sessions/src/main/java/io/github/droidkaigi/confsched2023/sessions/component/FilterCategoryChip.kt index 89486c08e..958a95664 100644 --- a/feature/sessions/src/main/java/io/github/droidkaigi/confsched2023/sessions/component/FilterCategoryChip.kt +++ b/feature/sessions/src/main/java/io/github/droidkaigi/confsched2023/sessions/component/FilterCategoryChip.kt @@ -12,18 +12,19 @@ import io.github.droidkaigi.confsched2023.designsystem.theme.KaigiTheme import io.github.droidkaigi.confsched2023.model.TimetableCategory import io.github.droidkaigi.confsched2023.model.fakes import io.github.droidkaigi.confsched2023.sessions.SessionsStrings +import io.github.droidkaigi.confsched2023.sessions.section.SearchFilterUiState const val FilterCategoryChipTestTag = "FilterCategoryChip" @Composable fun FilterCategoryChip( - dropdownFilterChipUiState: DropdownFilterChipUiState, + searchFilterUiState: SearchFilterUiState, onCategoriesSelected: (TimetableCategory, Boolean) -> Unit, modifier: Modifier = Modifier, onFilterCategoryChipClicked: () -> Unit, ) { DropdownFilterChip( - dropdownFilterChipUiState = dropdownFilterChipUiState, + searchFilterUiState = searchFilterUiState, onSelected = onCategoriesSelected, filterChipLabelDefaultText = SessionsStrings.Category.asString(), onFilterChipClick = onFilterCategoryChipClicked, @@ -39,7 +40,7 @@ fun FilterCategoryChip( fun PreviewFilterCategoryChip() { var uiState by remember { mutableStateOf( - DropdownFilterChipUiState( + SearchFilterUiState( selectedItems = emptyList(), items = TimetableCategory.fakes(), ), @@ -48,7 +49,7 @@ fun PreviewFilterCategoryChip() { KaigiTheme { FilterCategoryChip( - dropdownFilterChipUiState = uiState, + searchFilterUiState = uiState, onCategoriesSelected = { category, isSelected -> val selectedCategories = uiState.selectedItems.toMutableList() val newSelectedCategories = selectedCategories.apply { diff --git a/feature/sessions/src/main/java/io/github/droidkaigi/confsched2023/sessions/component/FilterDayChip.kt b/feature/sessions/src/main/java/io/github/droidkaigi/confsched2023/sessions/component/FilterDayChip.kt index b4e811cbc..1fe05b9a9 100644 --- a/feature/sessions/src/main/java/io/github/droidkaigi/confsched2023/sessions/component/FilterDayChip.kt +++ b/feature/sessions/src/main/java/io/github/droidkaigi/confsched2023/sessions/component/FilterDayChip.kt @@ -11,18 +11,19 @@ import io.github.droidkaigi.confsched2023.designsystem.preview.MultiThemePreview import io.github.droidkaigi.confsched2023.designsystem.theme.KaigiTheme import io.github.droidkaigi.confsched2023.model.DroidKaigi2023Day import io.github.droidkaigi.confsched2023.sessions.SessionsStrings +import io.github.droidkaigi.confsched2023.sessions.section.SearchFilterUiState import java.util.Locale const val FilterDayChipTestTag = "FilterDayChip" @Composable fun FilterDayChip( - dropdownFilterChipUiState: DropdownFilterChipUiState, + searchFilterUiState: SearchFilterUiState, onDaySelected: (DroidKaigi2023Day, Boolean) -> Unit, modifier: Modifier = Modifier, ) { DropdownFilterChip( - dropdownFilterChipUiState = dropdownFilterChipUiState, + searchFilterUiState = searchFilterUiState, onSelected = onDaySelected, filterChipLabelDefaultText = SessionsStrings.EventDay.asString(), dropdownMenuItemText = { kaigiDay -> @@ -37,7 +38,7 @@ fun FilterDayChip( fun PreviewFilterDayChip() { var uiState by remember { mutableStateOf( - DropdownFilterChipUiState( + SearchFilterUiState( selectedItems = emptyList(), items = DroidKaigi2023Day.entries.toList(), ), @@ -46,7 +47,7 @@ fun PreviewFilterDayChip() { KaigiTheme { FilterDayChip( - dropdownFilterChipUiState = uiState, + searchFilterUiState = uiState, onDaySelected = { kaigiDay, isSelected -> val selectedDays = uiState.selectedItems.toMutableList() val newSelectedDays = selectedDays.apply { diff --git a/feature/sessions/src/main/java/io/github/droidkaigi/confsched2023/sessions/component/FilterLanguageChip.kt b/feature/sessions/src/main/java/io/github/droidkaigi/confsched2023/sessions/component/FilterLanguageChip.kt index 65b08b2d8..75330a9cf 100644 --- a/feature/sessions/src/main/java/io/github/droidkaigi/confsched2023/sessions/component/FilterLanguageChip.kt +++ b/feature/sessions/src/main/java/io/github/droidkaigi/confsched2023/sessions/component/FilterLanguageChip.kt @@ -10,19 +10,22 @@ import androidx.compose.ui.platform.testTag import io.github.droidkaigi.confsched2023.designsystem.preview.MultiThemePreviews import io.github.droidkaigi.confsched2023.designsystem.theme.KaigiTheme import io.github.droidkaigi.confsched2023.model.Lang +import io.github.droidkaigi.confsched2023.model.Lang.ENGLISH +import io.github.droidkaigi.confsched2023.model.Lang.JAPANESE import io.github.droidkaigi.confsched2023.sessions.SessionsStrings.SupportedLanguages +import io.github.droidkaigi.confsched2023.sessions.section.SearchFilterUiState const val FilterLanguageChipTestTag = "FilterLanguageChip" @Composable fun FilterLanguageChip( - dropdownFilterChipUiState: DropdownFilterChipUiState, + searchFilterUiState: SearchFilterUiState, onLanguagesSelected: (Lang, Boolean) -> Unit, modifier: Modifier = Modifier, onFilterLanguageChipClicked: () -> Unit, ) { DropdownFilterChip( - dropdownFilterChipUiState = dropdownFilterChipUiState, + searchFilterUiState = searchFilterUiState, onSelected = onLanguagesSelected, filterChipLabelDefaultText = SupportedLanguages.asString(), onFilterChipClick = onFilterLanguageChipClicked, @@ -38,16 +41,16 @@ fun FilterLanguageChip( fun PreviewFilterLanguageChip() { var uiState by remember { mutableStateOf( - DropdownFilterChipUiState( + SearchFilterUiState( selectedItems = emptyList(), - items = listOf(Lang.JAPANESE, Lang.ENGLISH), + items = listOf(JAPANESE, ENGLISH), ), ) } KaigiTheme { FilterLanguageChip( - dropdownFilterChipUiState = uiState, + searchFilterUiState = uiState, onLanguagesSelected = { language, isSelected -> val selectedLanguages = uiState.selectedItems.toMutableList() val newSelectedLanguages = selectedLanguages.apply { diff --git a/feature/sessions/src/main/java/io/github/droidkaigi/confsched2023/sessions/component/FilterSessionTypeChip.kt b/feature/sessions/src/main/java/io/github/droidkaigi/confsched2023/sessions/component/FilterSessionTypeChip.kt index 4d574a550..e5162a11e 100644 --- a/feature/sessions/src/main/java/io/github/droidkaigi/confsched2023/sessions/component/FilterSessionTypeChip.kt +++ b/feature/sessions/src/main/java/io/github/droidkaigi/confsched2023/sessions/component/FilterSessionTypeChip.kt @@ -11,18 +11,19 @@ import io.github.droidkaigi.confsched2023.designsystem.preview.MultiThemePreview import io.github.droidkaigi.confsched2023.designsystem.theme.KaigiTheme import io.github.droidkaigi.confsched2023.model.TimetableSessionType import io.github.droidkaigi.confsched2023.sessions.SessionsStrings.SessionType +import io.github.droidkaigi.confsched2023.sessions.section.SearchFilterUiState const val FilterSessionTypeChipTestTag = "FilterSessionTypeChip" @Composable fun FilterSessionTypeChip( - dropdownFilterChipUiState: DropdownFilterChipUiState, + searchFilterUiState: SearchFilterUiState, onSessionTypeSelected: (TimetableSessionType, Boolean) -> Unit, modifier: Modifier = Modifier, onFilterSessionTypeChipClicked: () -> Unit, ) { DropdownFilterChip( - dropdownFilterChipUiState = dropdownFilterChipUiState, + searchFilterUiState = searchFilterUiState, onSelected = onSessionTypeSelected, filterChipLabelDefaultText = SessionType.asString(), onFilterChipClick = onFilterSessionTypeChipClicked, @@ -38,7 +39,7 @@ fun FilterSessionTypeChip( fun PreviewFilterSessionTypeChip() { var uiState by remember { mutableStateOf( - DropdownFilterChipUiState( + SearchFilterUiState( selectedItems = emptyList(), items = TimetableSessionType.entries.toList(), ), @@ -47,7 +48,7 @@ fun PreviewFilterSessionTypeChip() { KaigiTheme { FilterSessionTypeChip( - dropdownFilterChipUiState = uiState, + searchFilterUiState = uiState, onSessionTypeSelected = { sessionType, isSelected -> val selectedSessionTypes = uiState.selectedItems.toMutableList() val newSelectedSessionTypes = selectedSessionTypes.apply { diff --git a/feature/sessions/src/main/java/io/github/droidkaigi/confsched2023/sessions/component/SearchFilter.kt b/feature/sessions/src/main/java/io/github/droidkaigi/confsched2023/sessions/component/SearchFilter.kt deleted file mode 100644 index 3773ab935..000000000 --- a/feature/sessions/src/main/java/io/github/droidkaigi/confsched2023/sessions/component/SearchFilter.kt +++ /dev/null @@ -1,118 +0,0 @@ -package io.github.droidkaigi.confsched2023.sessions.component - -import androidx.compose.foundation.layout.Arrangement -import androidx.compose.foundation.layout.PaddingValues -import androidx.compose.foundation.lazy.LazyRow -import androidx.compose.runtime.Composable -import androidx.compose.ui.ExperimentalComposeUiApi -import androidx.compose.ui.Modifier -import androidx.compose.ui.platform.LocalSoftwareKeyboardController -import androidx.compose.ui.platform.testTag -import androidx.compose.ui.unit.dp -import io.github.droidkaigi.confsched2023.model.DroidKaigi2023Day -import io.github.droidkaigi.confsched2023.model.Lang -import io.github.droidkaigi.confsched2023.model.TimetableCategory -import io.github.droidkaigi.confsched2023.model.TimetableSessionType - -data class SearchFilterUiState( - val categories: List = emptyList(), - val sessionTypes: List = emptyList(), - val selectedDays: List = emptyList(), - val selectedCategories: List = emptyList(), - val selectedSessionTypes: List = emptyList(), - val selectedLanguages: List = emptyList(), - val isFavoritesOn: Boolean = false, -) { - val selectedDaysValues: String - get() = selectedDays.joinToString { it.name } - - val isDaySelected: Boolean - get() = selectedDays.isNotEmpty() - - val selectedCategoriesValue: String - get() = selectedCategories.joinToString { it.title.currentLangTitle } - - val selectedSessionTypesValue: String - get() = selectedSessionTypes.joinToString { it.label.currentLangTitle } - - val selectedLanguagesValue: String - get() = selectedLanguages.joinToString { it.tagName } - - val isCategoriesSelected: Boolean - get() = selectedCategories.isNotEmpty() - - val isLanguagesSelected: Boolean - get() = selectedLanguages.isNotEmpty() - - val isSessionTypeSelected: Boolean - get() = selectedSessionTypes.isNotEmpty() -} - -const val SearchFilterTestTag = "SearchFilter" - -@OptIn(ExperimentalComposeUiApi::class) -@Composable -fun SearchFilter( - searchFilterUiState: SearchFilterUiState, - modifier: Modifier = Modifier, - onDaySelected: (DroidKaigi2023Day, Boolean) -> Unit = { _, _ -> }, - onCategoriesSelected: (TimetableCategory, Boolean) -> Unit = { _, _ -> }, - onSessionTypesSelected: (TimetableSessionType, Boolean) -> Unit = { _, _ -> }, - onLanguagesSelected: (Lang, Boolean) -> Unit = { _, _ -> }, -) { - val keyboardController = LocalSoftwareKeyboardController.current - - LazyRow( - modifier = modifier.testTag(SearchFilterTestTag), - horizontalArrangement = Arrangement.spacedBy(8.dp), - contentPadding = PaddingValues(horizontal = 16.dp), - ) { - item { - FilterDayChip( - dropdownFilterChipUiState = DropdownFilterChipUiState( - selectedItems = searchFilterUiState.selectedDays, - items = DroidKaigi2023Day.entries.toList(), - isSelected = searchFilterUiState.isDaySelected, - selectedValues = searchFilterUiState.selectedDaysValues, - ), - onDaySelected = onDaySelected, - ) - } - item { - FilterCategoryChip( - dropdownFilterChipUiState = DropdownFilterChipUiState( - selectedItems = searchFilterUiState.selectedCategories, - items = searchFilterUiState.categories, - isSelected = searchFilterUiState.isCategoriesSelected, - selectedValues = searchFilterUiState.selectedCategoriesValue, - ), - onCategoriesSelected = onCategoriesSelected, - onFilterCategoryChipClicked = { keyboardController?.hide() }, - ) - } - item { - FilterSessionTypeChip( - dropdownFilterChipUiState = DropdownFilterChipUiState( - selectedItems = searchFilterUiState.selectedSessionTypes, - items = searchFilterUiState.sessionTypes, - isSelected = searchFilterUiState.isSessionTypeSelected, - selectedValues = searchFilterUiState.selectedSessionTypesValue, - ), - onSessionTypeSelected = onSessionTypesSelected, - onFilterSessionTypeChipClicked = { keyboardController?.hide() }, - ) - } - item { - FilterLanguageChip( - dropdownFilterChipUiState = DropdownFilterChipUiState( - selectedItems = searchFilterUiState.selectedLanguages, - items = listOf(Lang.JAPANESE, Lang.ENGLISH), - isSelected = searchFilterUiState.isLanguagesSelected, - selectedValues = searchFilterUiState.selectedLanguagesValue, - ), - onLanguagesSelected = onLanguagesSelected, - onFilterLanguageChipClicked = { keyboardController?.hide() }, - ) - } - } -} diff --git a/feature/sessions/src/main/java/io/github/droidkaigi/confsched2023/sessions/section/SearchFilter.kt b/feature/sessions/src/main/java/io/github/droidkaigi/confsched2023/sessions/section/SearchFilter.kt new file mode 100644 index 000000000..e11080731 --- /dev/null +++ b/feature/sessions/src/main/java/io/github/droidkaigi/confsched2023/sessions/section/SearchFilter.kt @@ -0,0 +1,78 @@ +package io.github.droidkaigi.confsched2023.sessions.section + +import androidx.compose.foundation.layout.Arrangement +import androidx.compose.foundation.layout.PaddingValues +import androidx.compose.foundation.lazy.LazyRow +import androidx.compose.runtime.Composable +import androidx.compose.ui.ExperimentalComposeUiApi +import androidx.compose.ui.Modifier +import androidx.compose.ui.platform.LocalSoftwareKeyboardController +import androidx.compose.ui.platform.testTag +import androidx.compose.ui.unit.dp +import io.github.droidkaigi.confsched2023.model.DroidKaigi2023Day +import io.github.droidkaigi.confsched2023.model.Lang +import io.github.droidkaigi.confsched2023.model.TimetableCategory +import io.github.droidkaigi.confsched2023.model.TimetableSessionType +import io.github.droidkaigi.confsched2023.sessions.component.FilterCategoryChip +import io.github.droidkaigi.confsched2023.sessions.component.FilterDayChip +import io.github.droidkaigi.confsched2023.sessions.component.FilterLanguageChip +import io.github.droidkaigi.confsched2023.sessions.component.FilterSessionTypeChip + +data class SearchFilterUiState( + val selectedItems: List, + val items: List, + val isSelected: Boolean = false, + val selectedValues: String = "", +) + +const val SearchFilterTestTag = "SearchFilter" + +@OptIn(ExperimentalComposeUiApi::class) +@Composable +fun SearchFilter( + searchFilterDayUiState: SearchFilterUiState, + searchFilterCategoryUiState: SearchFilterUiState, + searchFilterSessionTypeUiState: SearchFilterUiState, + searchFilterLanguageUiState: SearchFilterUiState, + modifier: Modifier = Modifier, + onDaySelected: (DroidKaigi2023Day, Boolean) -> Unit = { _, _ -> }, + onCategoriesSelected: (TimetableCategory, Boolean) -> Unit = { _, _ -> }, + onSessionTypesSelected: (TimetableSessionType, Boolean) -> Unit = { _, _ -> }, + onLanguagesSelected: (Lang, Boolean) -> Unit = { _, _ -> }, +) { + val keyboardController = LocalSoftwareKeyboardController.current + + LazyRow( + modifier = modifier.testTag(SearchFilterTestTag), + horizontalArrangement = Arrangement.spacedBy(8.dp), + contentPadding = PaddingValues(horizontal = 16.dp), + ) { + item { + FilterDayChip( + searchFilterUiState = searchFilterDayUiState, + onDaySelected = onDaySelected, + ) + } + item { + FilterCategoryChip( + searchFilterUiState = searchFilterCategoryUiState, + onCategoriesSelected = onCategoriesSelected, + onFilterCategoryChipClicked = { keyboardController?.hide() }, + ) + } + item { + FilterSessionTypeChip( + searchFilterUiState = searchFilterSessionTypeUiState, + onSessionTypeSelected = onSessionTypesSelected, + onFilterSessionTypeChipClicked = { keyboardController?.hide() }, + ) + } + item { + FilterLanguageChip( + searchFilterUiState = searchFilterLanguageUiState, + onLanguagesSelected = onLanguagesSelected, + onFilterLanguageChipClicked = { keyboardController?.hide() }, + ) + } + } +} From 0a60dc81d3f3729a492473ba86716047ed8cf1da Mon Sep 17 00:00:00 2001 From: Ben Hachimori Date: Mon, 21 Aug 2023 16:11:15 +0200 Subject: [PATCH 10/20] Issue 823 - Make filters in bookmark scrollable --- .../confsched2023/sessions/component/BookmarkFilters.kt | 7 ++++++- .../confsched2023/sessions/section/BookmarkSheet.kt | 3 +-- 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/feature/sessions/src/main/java/io/github/droidkaigi/confsched2023/sessions/component/BookmarkFilters.kt b/feature/sessions/src/main/java/io/github/droidkaigi/confsched2023/sessions/component/BookmarkFilters.kt index 624c0f971..29316f3f5 100644 --- a/feature/sessions/src/main/java/io/github/droidkaigi/confsched2023/sessions/component/BookmarkFilters.kt +++ b/feature/sessions/src/main/java/io/github/droidkaigi/confsched2023/sessions/component/BookmarkFilters.kt @@ -1,8 +1,11 @@ package io.github.droidkaigi.confsched2023.sessions.component +import androidx.compose.foundation.horizontalScroll import androidx.compose.foundation.layout.Row import androidx.compose.foundation.layout.Spacer +import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.size +import androidx.compose.foundation.rememberScrollState import androidx.compose.material.icons.Icons import androidx.compose.material.icons.filled.Check import androidx.compose.material3.ExperimentalMaterial3Api @@ -34,7 +37,9 @@ fun BookmarkFilters( onDayThirdChipClick: () -> Unit, modifier: Modifier = Modifier, ) { - Row(modifier) { + Row(modifier + .horizontalScroll(rememberScrollState()) + .padding(horizontal = 16.dp)) { BookmarkFilterChip( labelText = SessionsStrings.BookmarkFilterAllChip.asString(), isSelected = isAll, diff --git a/feature/sessions/src/main/java/io/github/droidkaigi/confsched2023/sessions/section/BookmarkSheet.kt b/feature/sessions/src/main/java/io/github/droidkaigi/confsched2023/sessions/section/BookmarkSheet.kt index 75073b7c1..357123266 100644 --- a/feature/sessions/src/main/java/io/github/droidkaigi/confsched2023/sessions/section/BookmarkSheet.kt +++ b/feature/sessions/src/main/java/io/github/droidkaigi/confsched2023/sessions/section/BookmarkSheet.kt @@ -7,7 +7,6 @@ import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Spacer import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.layout.height -import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.size import androidx.compose.foundation.lazy.LazyListState import androidx.compose.foundation.shape.RoundedCornerShape @@ -84,7 +83,7 @@ fun BookmarkSheet( onDayFirstChipClick = onDayFirstChipClick, onDaySecondChipClick = onDaySecondChipClick, onDayThirdChipClick = onDayThirdChipClick, - modifier = Modifier.padding(start = 16.dp), + modifier = Modifier, ) when (uiState) { is Empty -> { From 5788759a5af1a9db8ff3c0e842cd531128d9a916 Mon Sep 17 00:00:00 2001 From: matsuurayuki1219 Date: Mon, 21 Aug 2023 23:23:56 +0900 Subject: [PATCH 11/20] feat: add license description on about screen. --- .../confsched2023/about/AboutStrings.kt | 3 +++ .../about/component/AboutFooterLinks.kt | 15 +++++++++++++++ 2 files changed, 18 insertions(+) diff --git a/feature/about/src/main/java/io/github/droidkaigi/confsched2023/about/AboutStrings.kt b/feature/about/src/main/java/io/github/droidkaigi/confsched2023/about/AboutStrings.kt index e683de3ed..73b19076b 100644 --- a/feature/about/src/main/java/io/github/droidkaigi/confsched2023/about/AboutStrings.kt +++ b/feature/about/src/main/java/io/github/droidkaigi/confsched2023/about/AboutStrings.kt @@ -23,6 +23,7 @@ sealed class AboutStrings : Strings(Bindings) { object License : AboutStrings() object PrivacyPolicy : AboutStrings() object AppVersion : AboutStrings() + object LicenceDescription : AboutStrings() private object Bindings : StringsBindings( Lang.Japanese to { item, _ -> @@ -43,6 +44,7 @@ sealed class AboutStrings : Strings(Bindings) { License -> "ライセンス" PrivacyPolicy -> "プライバシーポリシー" AppVersion -> "アプリバージョン" + LicenceDescription -> "The Android robot is reproduced or modified from work created and shared by Google and used according to terms described in the Creative Commons 3.0 Attribution License." } }, Lang.English to { item, bindings -> @@ -63,6 +65,7 @@ sealed class AboutStrings : Strings(Bindings) { License -> "License" PrivacyPolicy -> "Privacy Policy" AppVersion -> "App Version" + LicenceDescription -> bindings.defaultBinding(item, bindings) } }, default = Lang.Japanese, diff --git a/feature/about/src/main/java/io/github/droidkaigi/confsched2023/about/component/AboutFooterLinks.kt b/feature/about/src/main/java/io/github/droidkaigi/confsched2023/about/component/AboutFooterLinks.kt index 0d02a5616..2c7ec580b 100644 --- a/feature/about/src/main/java/io/github/droidkaigi/confsched2023/about/component/AboutFooterLinks.kt +++ b/feature/about/src/main/java/io/github/droidkaigi/confsched2023/about/component/AboutFooterLinks.kt @@ -1,5 +1,6 @@ package io.github.droidkaigi.confsched2023.about.component +import androidx.compose.foundation.isSystemInDarkTheme import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Row @@ -13,6 +14,8 @@ import androidx.compose.material3.Text import androidx.compose.runtime.Composable import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier +import androidx.compose.ui.graphics.Color +import androidx.compose.ui.text.style.TextAlign import androidx.compose.ui.unit.dp import io.github.droidkaigi.confsched2023.about.AboutStrings import io.github.droidkaigi.confsched2023.designsystem.preview.MultiLanguagePreviews @@ -24,6 +27,9 @@ const val AboutFooterLinksYouTubeItemTestTag = "AboutFooterLinksYouTubeItem" const val AboutFooterLinksXItemTestTag = "AboutFooterLinksXItem" const val AboutFooterLinksMediumItemTestTag = "AboutFooterLinksMediumItem" +private val licenseDescriptionLight = Color(0xFF6D7256) +private val licenseDescriptionDark = Color(0xFFFFFFFF) + @Composable fun AboutFooterLinks( versionName: String?, @@ -71,6 +77,15 @@ fun AboutFooterLinks( style = MaterialTheme.typography.labelLarge, ) } + Spacer(modifier = Modifier.height(8.dp)) + Text( + modifier = Modifier.padding(horizontal = 12.dp), + text = AboutStrings.LicenceDescription.asString(), + style = MaterialTheme.typography.labelSmall, + textAlign = TextAlign.Center, + color = if (isSystemInDarkTheme()) licenseDescriptionDark else licenseDescriptionLight, + ) + Spacer(modifier = Modifier.height(8.dp)) } } From 3de8d199f3a702598c729a12134f275f255caa51 Mon Sep 17 00:00:00 2001 From: Ben Hachimori Date: Mon, 21 Aug 2023 16:30:55 +0200 Subject: [PATCH 12/20] Fixed format --- .../confsched2023/sessions/component/BookmarkFilters.kt | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/feature/sessions/src/main/java/io/github/droidkaigi/confsched2023/sessions/component/BookmarkFilters.kt b/feature/sessions/src/main/java/io/github/droidkaigi/confsched2023/sessions/component/BookmarkFilters.kt index 29316f3f5..51821248b 100644 --- a/feature/sessions/src/main/java/io/github/droidkaigi/confsched2023/sessions/component/BookmarkFilters.kt +++ b/feature/sessions/src/main/java/io/github/droidkaigi/confsched2023/sessions/component/BookmarkFilters.kt @@ -37,9 +37,11 @@ fun BookmarkFilters( onDayThirdChipClick: () -> Unit, modifier: Modifier = Modifier, ) { - Row(modifier + Row( + modifier .horizontalScroll(rememberScrollState()) - .padding(horizontal = 16.dp)) { + .padding(horizontal = 16.dp), + ) { BookmarkFilterChip( labelText = SessionsStrings.BookmarkFilterAllChip.asString(), isSelected = isAll, From 93f3cf72361bf2ad63a23c7df570e50d61503b7c Mon Sep 17 00:00:00 2001 From: ked4ma Date: Tue, 22 Aug 2023 01:44:43 +0900 Subject: [PATCH 13/20] enable to show multi speakers at TimeTableGrid --- .../PreviewTimeTableItemRoomProvider.kt | 1 + .../sessions/component/TimetableGridItem.kt | 112 ++++++++++++++---- 2 files changed, 88 insertions(+), 25 deletions(-) diff --git a/feature/sessions/src/main/java/io/github/droidkaigi/confsched2023/sessions/component/PreviewTimeTableItemRoomProvider.kt b/feature/sessions/src/main/java/io/github/droidkaigi/confsched2023/sessions/component/PreviewTimeTableItemRoomProvider.kt index 605ae947d..500b58ad8 100644 --- a/feature/sessions/src/main/java/io/github/droidkaigi/confsched2023/sessions/component/PreviewTimeTableItemRoomProvider.kt +++ b/feature/sessions/src/main/java/io/github/droidkaigi/confsched2023/sessions/component/PreviewTimeTableItemRoomProvider.kt @@ -18,6 +18,7 @@ class PreviewTimeTableItemRoomProvider : PreviewParameterProvider Session.fake().copy(room = Session.fake().room.copy(type = RoomA)), Session.fake().copy(room = Session.fake().room.copy(type = RoomB)), Session.fake().copy(room = Session.fake().room.copy(type = RoomD)), + Session.fake().copy(speakers = persistentListOf(Session.fake().speakers.first())), Session.fake().copy(speakers = persistentListOf()), ) } diff --git a/feature/sessions/src/main/java/io/github/droidkaigi/confsched2023/sessions/component/TimetableGridItem.kt b/feature/sessions/src/main/java/io/github/droidkaigi/confsched2023/sessions/component/TimetableGridItem.kt index 2a8f77864..0e51fb1af 100644 --- a/feature/sessions/src/main/java/io/github/droidkaigi/confsched2023/sessions/component/TimetableGridItem.kt +++ b/feature/sessions/src/main/java/io/github/droidkaigi/confsched2023/sessions/component/TimetableGridItem.kt @@ -1,8 +1,11 @@ package io.github.droidkaigi.confsched2023.sessions.component +import androidx.compose.foundation.BorderStroke import androidx.compose.foundation.Image import androidx.compose.foundation.background +import androidx.compose.foundation.border import androidx.compose.foundation.clickable +import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Row @@ -23,6 +26,7 @@ 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.graphics.Color import androidx.compose.ui.graphics.vector.rememberVectorPainter import androidx.compose.ui.platform.LocalDensity import androidx.compose.ui.text.TextStyle @@ -35,6 +39,7 @@ import io.github.droidkaigi.confsched2023.designsystem.preview.MultiLanguagePrev import io.github.droidkaigi.confsched2023.designsystem.preview.MultiThemePreviews import io.github.droidkaigi.confsched2023.designsystem.theme.KaigiTheme import io.github.droidkaigi.confsched2023.designsystem.theme.hallColors +import io.github.droidkaigi.confsched2023.designsystem.theme.md_theme_light_outline import io.github.droidkaigi.confsched2023.model.TimetableItem import io.github.droidkaigi.confsched2023.model.TimetableItem.Session import io.github.droidkaigi.confsched2023.model.TimetableSpeaker @@ -44,6 +49,8 @@ import io.github.droidkaigi.confsched2023.sessions.SessionsStrings.UserIcon import io.github.droidkaigi.confsched2023.sessions.section.TimetableSizes import io.github.droidkaigi.confsched2023.ui.previewOverride import io.github.droidkaigi.confsched2023.ui.rememberAsyncImagePainter +import kotlinx.collections.immutable.PersistentList +import kotlinx.collections.immutable.persistentListOf import kotlinx.datetime.DateTimeUnit import kotlinx.datetime.minus import kotlin.math.ceil @@ -61,6 +68,7 @@ fun TimetableGridItem( val localDensity = LocalDensity.current val speaker = timetableItem.speakers.firstOrNull() + val speakers = timetableItem.speakers val hallColor = hallColors() val backgroundColor = timetableItem.room.color @@ -86,10 +94,10 @@ fun TimetableGridItem( Box( modifier = Modifier .background( - color = if (speaker != null) { - backgroundColor - } else { + color = if (speakers.isEmpty()) { MaterialTheme.colorScheme.surfaceVariant + } else { + backgroundColor }, shape = RoundedCornerShape(4.dp), ) @@ -130,31 +138,70 @@ fun TimetableGridItem( .defaultMinSize(minHeight = 8.dp), ) - // TODO: Dealing with more than one speaker - if (speaker != null) { - Row( - modifier = Modifier.height(TimetableGridItemSizes.speakerHeight), - verticalAlignment = Alignment.CenterVertically, - ) { - Image( - painter = previewOverride(previewPainter = { rememberVectorPainter(image = Icons.Default.Person) }) { - rememberAsyncImagePainter(speaker.iconUrl) - }, - contentDescription = UserIcon.asString(), - modifier = Modifier.clip(RoundedCornerShape(8.dp)), - ) - Spacer(modifier = Modifier.width(8.dp)) - Text( - text = speaker.name, - style = MaterialTheme.typography.labelMedium, - color = textColor, - ) - } + when (speakers.size) { + 0 -> Unit + 1 -> SingleSpeaker(speaker = speakers.first(), textColor = textColor) + else -> MultiSpeakers(speakers = speakers) } } } } +@Composable +private fun SingleSpeaker( + speaker: TimetableSpeaker, + textColor: Color, + modifier: Modifier = Modifier, +) { + Row( + modifier = modifier.height(TimetableGridItemSizes.speakerHeight), + verticalAlignment = Alignment.CenterVertically, + ) { + SpeakerIcon(iconUrl = speaker.iconUrl) + Spacer(modifier = Modifier.width(8.dp)) + Text( + text = speaker.name, + style = MaterialTheme.typography.labelMedium, + color = textColor, + ) + } +} + +@Composable +private fun MultiSpeakers( + speakers: PersistentList, + modifier: Modifier = Modifier, +) { + Row( + modifier = modifier.height(TimetableGridItemSizes.speakerHeight), + verticalAlignment = Alignment.CenterVertically, + horizontalArrangement = Arrangement.spacedBy(4.dp) + ) { + speakers.forEach { speaker -> + SpeakerIcon(speaker.iconUrl) + } + } +} + +@Composable +private fun SpeakerIcon( + iconUrl: String, + modifier: Modifier = Modifier, +) { + Image( + painter = previewOverride(previewPainter = { rememberVectorPainter(image = Icons.Default.Person) }) { + rememberAsyncImagePainter(iconUrl) + }, + contentDescription = UserIcon.asString(), + modifier = modifier + .clip(RoundedCornerShape(8.dp)) + .border( + BorderStroke(1.dp, md_theme_light_outline), + RoundedCornerShape(8.dp), + ), + ) +} + /** * * Calculate the font size and line height of the title by the height of the session grid item. @@ -301,7 +348,8 @@ fun PreviewTimetableGridItem() { KaigiTheme { Surface { TimetableGridItem( - timetableItem = Session.fake(), + timetableItem = Session.fake() + .copy(speakers = persistentListOf(Session.fake().speakers.first())), onTimetableItemClick = {}, gridItemHeightPx = 350, ) @@ -329,7 +377,7 @@ fun PreviewTimetableGridLongTitleItem() { jaTitle = it.title.jaTitle.repeat(2), enTitle = it.title.enTitle.repeat(2), ) - it.copy(title = longTitle) + it.copy(title = longTitle, speakers = persistentListOf(it.speakers.first())) }, onTimetableItemClick = {}, gridItemHeightPx = height, @@ -338,6 +386,20 @@ fun PreviewTimetableGridLongTitleItem() { } } +@MultiThemePreviews +@Composable +fun PreviewTimetableGridMultiSpeakersItem() { + KaigiTheme { + Surface { + TimetableGridItem( + timetableItem = Session.fake(), + onTimetableItemClick = {}, + gridItemHeightPx = 350, + ) + } + } +} + @MultiThemePreviews @Composable internal fun PreviewTimetableGridItem( From 14f19a6e70ce54f98dd17f2dfe748087450828b0 Mon Sep 17 00:00:00 2001 From: ked4ma Date: Tue, 22 Aug 2023 01:53:37 +0900 Subject: [PATCH 14/20] fix lint (add trailing comma) --- .../confsched2023/sessions/component/TimetableGridItem.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/feature/sessions/src/main/java/io/github/droidkaigi/confsched2023/sessions/component/TimetableGridItem.kt b/feature/sessions/src/main/java/io/github/droidkaigi/confsched2023/sessions/component/TimetableGridItem.kt index 0e51fb1af..041668690 100644 --- a/feature/sessions/src/main/java/io/github/droidkaigi/confsched2023/sessions/component/TimetableGridItem.kt +++ b/feature/sessions/src/main/java/io/github/droidkaigi/confsched2023/sessions/component/TimetableGridItem.kt @@ -175,7 +175,7 @@ private fun MultiSpeakers( Row( modifier = modifier.height(TimetableGridItemSizes.speakerHeight), verticalAlignment = Alignment.CenterVertically, - horizontalArrangement = Arrangement.spacedBy(4.dp) + horizontalArrangement = Arrangement.spacedBy(4.dp), ) { speakers.forEach { speaker -> SpeakerIcon(speaker.iconUrl) From dce8b37d07d3aed27c621d8a21e787e48bb4aa15 Mon Sep 17 00:00:00 2001 From: ked4ma Date: Tue, 22 Aug 2023 02:15:22 +0900 Subject: [PATCH 15/20] introduce PreviewParameter to FloorLevelSwitcher --- .../floormap/component/FloorLevelSwitcher.kt | 22 +++++-------------- ...eviewFloorMapSwitcherFloorLevelProvider.kt | 9 ++++++++ 2 files changed, 14 insertions(+), 17 deletions(-) create mode 100644 feature/floor-map/src/main/java/io/github/droidkaigi/confsched2023/floormap/component/PreviewFloorMapSwitcherFloorLevelProvider.kt diff --git a/feature/floor-map/src/main/java/io/github/droidkaigi/confsched2023/floormap/component/FloorLevelSwitcher.kt b/feature/floor-map/src/main/java/io/github/droidkaigi/confsched2023/floormap/component/FloorLevelSwitcher.kt index 3a5620601..839cb5475 100644 --- a/feature/floor-map/src/main/java/io/github/droidkaigi/confsched2023/floormap/component/FloorLevelSwitcher.kt +++ b/feature/floor-map/src/main/java/io/github/droidkaigi/confsched2023/floormap/component/FloorLevelSwitcher.kt @@ -22,6 +22,7 @@ import androidx.compose.ui.platform.testTag import androidx.compose.ui.text.TextStyle import androidx.compose.ui.text.font.FontWeight import androidx.compose.ui.text.style.TextAlign +import androidx.compose.ui.tooling.preview.PreviewParameter import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.sp import io.github.droidkaigi.confsched2023.designsystem.preview.MultiThemePreviews @@ -108,28 +109,15 @@ private fun FloorLevelSwitcherButton( } } -// TODO Use PreviewParameterProvider to display the Preview once the Linter issue is resolved. -// https://github.com/DroidKaigi/conference-app-2023/pull/557#discussion_r1295780974 @MultiThemePreviews @Composable -fun FloorLevelSwitcherGroundPreview() { - KaigiTheme { - Surface { - FloorLevelSwitcher( - selectingFloorLevel = FloorLevel.Ground, - onClickFloorLevelSwitcher = {}, - ) - } - } -} - -@MultiThemePreviews -@Composable -fun FloorLevelSwitcherBasementPreview() { +internal fun FloorLevelSwitcherPreview( + @PreviewParameter(PreviewFloorMapSwitcherFloorLevelProvider::class) floorLevel: FloorLevel, +) { KaigiTheme { Surface { FloorLevelSwitcher( - selectingFloorLevel = FloorLevel.Basement, + selectingFloorLevel = floorLevel, onClickFloorLevelSwitcher = {}, ) } diff --git a/feature/floor-map/src/main/java/io/github/droidkaigi/confsched2023/floormap/component/PreviewFloorMapSwitcherFloorLevelProvider.kt b/feature/floor-map/src/main/java/io/github/droidkaigi/confsched2023/floormap/component/PreviewFloorMapSwitcherFloorLevelProvider.kt new file mode 100644 index 000000000..a2a3121f7 --- /dev/null +++ b/feature/floor-map/src/main/java/io/github/droidkaigi/confsched2023/floormap/component/PreviewFloorMapSwitcherFloorLevelProvider.kt @@ -0,0 +1,9 @@ +package io.github.droidkaigi.confsched2023.floormap.component + +import androidx.compose.ui.tooling.preview.PreviewParameterProvider +import io.github.droidkaigi.confsched2023.model.FloorLevel + +class PreviewFloorMapSwitcherFloorLevelProvider : PreviewParameterProvider { + override val values: Sequence + get() = FloorLevel.values().asSequence() +} From 6fa71f1bd62ab7c2c1c0f67c3e392628e5b648d1 Mon Sep 17 00:00:00 2001 From: "naotama (Naoki KOBAYASHI)" Date: Tue, 22 Aug 2023 01:58:21 +0900 Subject: [PATCH 16/20] Toggle icons when switching timetable style --- .../confsched2023/sessions/TimetableScreen.kt | 4 ++++ .../sessions/TimetableScreenViewModel.kt | 1 + .../sessions/component/TimetableTopArea.kt | 11 +++++++++-- 3 files changed, 14 insertions(+), 2 deletions(-) diff --git a/feature/sessions/src/main/java/io/github/droidkaigi/confsched2023/sessions/TimetableScreen.kt b/feature/sessions/src/main/java/io/github/droidkaigi/confsched2023/sessions/TimetableScreen.kt index 2ce03dfc9..6f5b6ef58 100644 --- a/feature/sessions/src/main/java/io/github/droidkaigi/confsched2023/sessions/TimetableScreen.kt +++ b/feature/sessions/src/main/java/io/github/droidkaigi/confsched2023/sessions/TimetableScreen.kt @@ -37,6 +37,7 @@ import io.github.droidkaigi.confsched2023.designsystem.theme.KaigiTheme import io.github.droidkaigi.confsched2023.model.DroidKaigi2023Day import io.github.droidkaigi.confsched2023.model.Timetable import io.github.droidkaigi.confsched2023.model.TimetableItem +import io.github.droidkaigi.confsched2023.model.TimetableUiType import io.github.droidkaigi.confsched2023.sessions.component.TimetableTopArea import io.github.droidkaigi.confsched2023.sessions.component.rememberTimetableScreenScrollState import io.github.droidkaigi.confsched2023.sessions.section.TimetableHeader @@ -102,6 +103,7 @@ fun TimetableScreen( data class TimetableScreenUiState( val contentUiState: TimetableSheetUiState, + val timetableUiType: TimetableUiType, ) private val timetableTopBackgroundLight = Color(0xFFF6FFD3) @@ -165,6 +167,7 @@ private fun TimetableScreen( }, topBar = { TimetableTopArea( + timetableUiType = uiState.timetableUiType, onTimetableUiChangeClick, onSearchClick, onBookmarkIconClick, @@ -223,6 +226,7 @@ fun PreviewTimetableScreenDark() { ), ), ), + TimetableUiType.Grid, ), SnackbarHostState(), {}, diff --git a/feature/sessions/src/main/java/io/github/droidkaigi/confsched2023/sessions/TimetableScreenViewModel.kt b/feature/sessions/src/main/java/io/github/droidkaigi/confsched2023/sessions/TimetableScreenViewModel.kt index 6c4da2871..00a6e59cb 100644 --- a/feature/sessions/src/main/java/io/github/droidkaigi/confsched2023/sessions/TimetableScreenViewModel.kt +++ b/feature/sessions/src/main/java/io/github/droidkaigi/confsched2023/sessions/TimetableScreenViewModel.kt @@ -87,6 +87,7 @@ class TimetableScreenViewModel @Inject constructor( ) { sessionListUiState -> TimetableScreenUiState( contentUiState = sessionListUiState, + timetableUiType = timetableUiTypeStateFlow.value, ) } diff --git a/feature/sessions/src/main/java/io/github/droidkaigi/confsched2023/sessions/component/TimetableTopArea.kt b/feature/sessions/src/main/java/io/github/droidkaigi/confsched2023/sessions/component/TimetableTopArea.kt index d20be8eeb..0f231428c 100644 --- a/feature/sessions/src/main/java/io/github/droidkaigi/confsched2023/sessions/component/TimetableTopArea.kt +++ b/feature/sessions/src/main/java/io/github/droidkaigi/confsched2023/sessions/component/TimetableTopArea.kt @@ -1,9 +1,10 @@ package io.github.droidkaigi.confsched2023.sessions.component import androidx.compose.material.icons.Icons -import androidx.compose.material.icons.filled.GridView import androidx.compose.material.icons.filled.Search import androidx.compose.material.icons.outlined.Bookmarks +import androidx.compose.material.icons.outlined.GridView +import androidx.compose.material.icons.outlined.ViewTimeline import androidx.compose.material3.ExperimentalMaterial3Api import androidx.compose.material3.Icon import androidx.compose.material3.IconButton @@ -15,6 +16,7 @@ import androidx.compose.ui.graphics.Color import androidx.compose.ui.platform.testTag import androidx.compose.ui.res.painterResource import io.github.droidkaigi.confsched2023.feature.sessions.R +import io.github.droidkaigi.confsched2023.model.TimetableUiType import io.github.droidkaigi.confsched2023.sessions.SessionsStrings.Bookmark import io.github.droidkaigi.confsched2023.sessions.SessionsStrings.Search import io.github.droidkaigi.confsched2023.sessions.SessionsStrings.Timetable @@ -26,6 +28,7 @@ const val TimetableBookmarksIconTestTag = "TimetableBookmarksIconTestTag" @Composable @OptIn(ExperimentalMaterial3Api::class) fun TimetableTopArea( + timetableUiType: TimetableUiType, onTimetableUiChangeClick: () -> Unit, onSearchClick: () -> Unit, onTopAreaBookmarkIconClick: () -> Unit, @@ -63,7 +66,11 @@ fun TimetableTopArea( onClick = { onTimetableUiChangeClick() }, ) { Icon( - imageVector = Icons.Default.GridView, + imageVector = if (timetableUiType != TimetableUiType.Grid) { + Icons.Outlined.GridView + } else { + Icons.Outlined.ViewTimeline + }, contentDescription = Timetable.asString(), ) } From 047184ec182aec72caaf6cbb3ab0970eed59534b Mon Sep 17 00:00:00 2001 From: shxun6934 <23094546+shxun6934@users.noreply.github.com> Date: Tue, 22 Aug 2023 11:02:08 +0900 Subject: [PATCH 17/20] fix DropdownFilterChip code format --- .../sessions/component/DropdownFilterChip.kt | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/feature/sessions/src/main/java/io/github/droidkaigi/confsched2023/sessions/component/DropdownFilterChip.kt b/feature/sessions/src/main/java/io/github/droidkaigi/confsched2023/sessions/component/DropdownFilterChip.kt index 33d1bebe0..1179d1303 100644 --- a/feature/sessions/src/main/java/io/github/droidkaigi/confsched2023/sessions/component/DropdownFilterChip.kt +++ b/feature/sessions/src/main/java/io/github/droidkaigi/confsched2023/sessions/component/DropdownFilterChip.kt @@ -72,10 +72,14 @@ fun DropdownFilterChip( dropdownMenuItemText(item) }, leadingIcon = dropdownMenuItemLeadingIcon?.let { icon -> - { icon(item) } + { + icon(item) + } }, trailingIcon = dropdownMenuItemTrailingIcon?.let { icon -> - { icon(item) } + { + icon(item) + } }, onClick = { onSelectedUpdated( From 7875a5f26b86b8f7d7882d52227a575679c8d33c Mon Sep 17 00:00:00 2001 From: Tatsuya Fujisaki <1838962+tatsuyafujisaki@users.noreply.github.com> Date: Tue, 22 Aug 2023 11:26:35 +0900 Subject: [PATCH 18/20] Use gradlePluginPortal() --- build-logic/build.gradle.kts | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/build-logic/build.gradle.kts b/build-logic/build.gradle.kts index 6befe1230..3990d7375 100644 --- a/build-logic/build.gradle.kts +++ b/build-logic/build.gradle.kts @@ -9,9 +9,7 @@ group = "io.github.droidkaigi.confsched2023.buildlogic" repositories { google() mavenCentral() - maven { - url = uri("https://plugins.gradle.org/m2/") - } + gradlePluginPortal() } // If we use jvmToolchain, we need to install JDK 11 From 6c0d3e646ea527d77f8b3fb7f864bdfcdd479c17 Mon Sep 17 00:00:00 2001 From: Takahiro Menju Date: Tue, 22 Aug 2023 11:57:36 +0900 Subject: [PATCH 19/20] Use spotless for now --- .github/workflows/WorkflowHook.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/WorkflowHook.yml b/.github/workflows/WorkflowHook.yml index 4a7e906df..28d08bbea 100644 --- a/.github/workflows/WorkflowHook.yml +++ b/.github/workflows/WorkflowHook.yml @@ -100,7 +100,7 @@ jobs: recreate: true message: > Hi @${{ github.event.workflow_run.actor.login }}! - Codes seem to be unformatted. To resolve this issue, please run `./gradlew detekt --auto-correct` and fix the results of ./gradlew lintDebug.. + Codes seem to be unformatted. To resolve this issue, please run `./gradlew spotlessKotlinApply` and fix the results of ./gradlew lintDebug.. Thank you for your contribution. From 2933229eedbae72020f9f4c246bca79df1ce2ae2 Mon Sep 17 00:00:00 2001 From: Takahiro Menju Date: Tue, 22 Aug 2023 12:59:19 +0900 Subject: [PATCH 20/20] Revert "Use spotless for workflow for now" --- .github/workflows/WorkflowHook.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/WorkflowHook.yml b/.github/workflows/WorkflowHook.yml index 28d08bbea..4a7e906df 100644 --- a/.github/workflows/WorkflowHook.yml +++ b/.github/workflows/WorkflowHook.yml @@ -100,7 +100,7 @@ jobs: recreate: true message: > Hi @${{ github.event.workflow_run.actor.login }}! - Codes seem to be unformatted. To resolve this issue, please run `./gradlew spotlessKotlinApply` and fix the results of ./gradlew lintDebug.. + Codes seem to be unformatted. To resolve this issue, please run `./gradlew detekt --auto-correct` and fix the results of ./gradlew lintDebug.. Thank you for your contribution.