Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Apply bookmark screen design #260

Merged
merged 29 commits into from
Jul 18, 2023
Merged
Show file tree
Hide file tree
Changes from 28 commits
Commits
Show all changes
29 commits
Select commit Hold shift + click to select a range
f56487e
Move to BookMarkScreen
mtkw0127 Jul 11, 2023
86d24dd
Fix lint error
mtkw0127 Jul 11, 2023
c290a0a
Fix the name of testTag
mtkw0127 Jul 11, 2023
7e04ea3
Add BookMarkScreenViewModel
mtkw0127 Jul 11, 2023
43886bb
Fix lint error
mtkw0127 Jul 11, 2023
68c1400
Use sealed interface for UiState
mtkw0127 Jul 11, 2023
d275816
Add Empty BookMarkScreen
mtkw0127 Jul 12, 2023
7657705
Add List BookMarkScreen Roughly
mtkw0127 Jul 13, 2023
f759753
Merge branch 'main' into feature/apply-bookmark-screen-design
mtkw0127 Jul 16, 2023
24978d8
Add Filter Feature
mtkw0127 Jul 16, 2023
b8cc144
Toggle bookmark
mtkw0127 Jul 16, 2023
e637c41
Add OnBackPress
mtkw0127 Jul 16, 2023
e2b91c8
Show Filter in EmptyView
mtkw0127 Jul 16, 2023
ae20277
Sort BookMarkItems
mtkw0127 Jul 16, 2023
b4e44bf
Fix TopBar
mtkw0127 Jul 16, 2023
f36dedc
Fix TopBar
mtkw0127 Jul 16, 2023
5e61dc1
Grouped ListItem By Time
mtkw0127 Jul 16, 2023
f557aad
Grouped ListItem By Time
mtkw0127 Jul 16, 2023
736fff7
Add checkNavigateToBookmarkShot
mtkw0127 Jul 17, 2023
b4fe687
Replace BookMark to Bookmark
mtkw0127 Jul 17, 2023
135e6c4
Add Preview and Fix border
mtkw0127 Jul 17, 2023
fd2b163
Merge branch 'main' into feature/apply-bookmark-screen-design
mtkw0127 Jul 17, 2023
d0b76b2
Fix ChipBorder
mtkw0127 Jul 17, 2023
341706c
Fix ChipStyle
mtkw0127 Jul 17, 2023
02986bf
Fix modifier of BookmarkSheet
mtkw0127 Jul 17, 2023
3191ac3
Define const string value in SessionsStrings
mtkw0127 Jul 17, 2023
4b3e70d
Define const string value in SessionsStrings
mtkw0127 Jul 17, 2023
87a42da
Add FIXME
takahirom Jul 18, 2023
7afc39e
Add FIXME
takahirom Jul 18, 2023
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ import io.github.droidkaigi.confsched2023.main.mainScreen
import io.github.droidkaigi.confsched2023.main.mainScreenRoute
import io.github.droidkaigi.confsched2023.sessions.navigateSearchScreen
import io.github.droidkaigi.confsched2023.sessions.navigateTimetableScreen
import io.github.droidkaigi.confsched2023.sessions.navigateToBookmarkScreen
import io.github.droidkaigi.confsched2023.sessions.navigateToTimetableItemDetailScreen
import io.github.droidkaigi.confsched2023.sessions.nestedSessionScreens
import io.github.droidkaigi.confsched2023.sessions.searchScreen
Expand Down Expand Up @@ -86,6 +87,9 @@ private fun NavGraphBuilder.mainScreen(navController: NavHostController) {
timetableitem.id,
)
},
onClickBookmarkIcon = {
navController.navigateToBookmarkScreen()
},
)
composable(contributorsScreenRoute) {
ContributorsScreen(hiltViewModel<ContributorsViewModel>())
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -73,4 +73,14 @@ class KaigiAppTest {
capture()
}
}

@Test
fun checkNavigateToBookmarkShot() {
kaigiAppRobot(robotTestRule) {
timetableScreenRobot(robotTestRule) {
clickBookmarkButton()
}
capture()
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import io.github.droidkaigi.confsched2023.designsystem.theme.KaigiTheme
import io.github.droidkaigi.confsched2023.sessions.TimetableScreen
import io.github.droidkaigi.confsched2023.sessions.TimetableScreenTestTag
import io.github.droidkaigi.confsched2023.sessions.component.SearchButtonTestTag
import io.github.droidkaigi.confsched2023.sessions.component.TimetableBookmarkIconTestTag
import io.github.droidkaigi.confsched2023.sessions.component.TimetableListItemTestTag
import io.github.droidkaigi.confsched2023.sessions.component.TimetableUiTypeChangeButtonTestTag
import io.github.droidkaigi.confsched2023.testing.RobotTestRule
Expand All @@ -39,6 +40,7 @@ class TimetableScreenRobot @Inject constructor(
TimetableScreen(
onSearchClick = { },
onTimetableItemClick = { },
onClickBookmarkIcon = { },
)
}
}
Expand Down Expand Up @@ -73,6 +75,12 @@ class TimetableScreenRobot @Inject constructor(
.performClick()
}

fun clickBookmarkButton() {
composeTestRule
.onNode(hasTestTag(TimetableBookmarkIconTestTag))
.performClick()
}

fun scrollTimetable() {
composeTestRule
.onNode(hasTestTag(TimetableScreenTestTag))
Expand Down
2 changes: 2 additions & 0 deletions feature/sessions/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,8 @@ dependencies {
implementation(libs.androidxLifecycleLifecycleRuntimeKtx)
implementation(libs.androidxActivityActivityCompose)
implementation(libs.composeCoil)
implementation(libs.composeConstraintLayout)
implementation(libs.composeMaterialIcon)
androidTestImplementation(libs.composeUiTestJunit4)
debugImplementation(libs.composeUiTooling)
debugImplementation(libs.composeUiTestManifest)
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,98 @@
package io.github.droidkaigi.confsched2023.sessions

import androidx.compose.foundation.layout.WindowInsets
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.lazy.rememberLazyListState
import androidx.compose.material3.Scaffold
import androidx.compose.runtime.Composable
import androidx.compose.runtime.collectAsState
import androidx.compose.runtime.getValue
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.platform.testTag
import androidx.compose.ui.unit.dp
import androidx.hilt.navigation.compose.hiltViewModel
import androidx.navigation.NavController
import io.github.droidkaigi.confsched2023.model.DroidKaigi2023Day
import io.github.droidkaigi.confsched2023.model.TimetableItem
import io.github.droidkaigi.confsched2023.model.TimetableItemId
import io.github.droidkaigi.confsched2023.sessions.component.BookmarkTopArea
import io.github.droidkaigi.confsched2023.sessions.section.BookmarkSheet
import kotlinx.collections.immutable.PersistentList
import kotlinx.collections.immutable.PersistentMap
import kotlinx.collections.immutable.PersistentSet

const val bookmarkScreenRoute = "bookmark"

fun NavController.navigateToBookmarkScreen() {
navigate(bookmarkScreenRoute)
}

sealed interface BookmarkScreenUiState {

val currentDayFilter: PersistentList<DroidKaigi2023Day>

data class Empty(
override val currentDayFilter: PersistentList<DroidKaigi2023Day>,
) : BookmarkScreenUiState

data class ListBookmark(
val bookmarkedTimetableItemIds: PersistentSet<TimetableItemId>,
val timetableItemMap: PersistentMap<String, List<TimetableItem>>,
override val currentDayFilter: PersistentList<DroidKaigi2023Day>,
) : BookmarkScreenUiState
}

@Composable
fun BookmarkScreen(
onClickBackPress: () -> Unit,
viewModel: BookmarkScreenViewModel = hiltViewModel<BookmarkScreenViewModel>(),
) {
val uiState by viewModel.uiState.collectAsState()
BookmarkScreen(
uiState = uiState,
onClickBackPress = onClickBackPress,
onClickBooMarkIcon = { viewModel.updateBookmark(it) },
onClickAllFilterChip = { viewModel.onClickAllFilterChip() },
onClickDayFirstChip = { viewModel.onClickDayFirstChip() },
onClickDaySecondChip = { viewModel.onClickDaySecondChip() },
onClickDayThirdChip = { viewModel.onClickDayThirdChip() },
)
}

const val BookmarkScreenTestTag = "BookmarkScreenTestTag"

@Composable
private fun BookmarkScreen(
uiState: BookmarkScreenUiState,
onClickBackPress: () -> Unit,
onClickBooMarkIcon: (TimetableItemId) -> Unit,
onClickAllFilterChip: () -> Unit,
onClickDayFirstChip: () -> Unit,
onClickDaySecondChip: () -> Unit,
onClickDayThirdChip: () -> Unit,
) {
val scrollState = rememberLazyListState()
Scaffold(
modifier = Modifier.testTag(BookmarkScreenTestTag),
topBar = {
BookmarkTopArea(
scrollState = scrollState,
onClickBackPress = onClickBackPress,
)
},
containerColor = Color(0xFFF8FAF6),
contentWindowInsets = WindowInsets(0.dp),
) { padding ->
BookmarkSheet(
modifier = Modifier.padding(padding),
scrollState = scrollState,
onClickBookmarkIcon = onClickBooMarkIcon,
onClickAllFilterChip = onClickAllFilterChip,
onClickDayFirstChip = onClickDayFirstChip,
onClickDaySecondChip = onClickDaySecondChip,
onClickDayThirdChip = onClickDayThirdChip,
uiState = uiState,
)
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,107 @@
package io.github.droidkaigi.confsched2023.sessions

import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
import dagger.hilt.android.lifecycle.HiltViewModel
import io.github.droidkaigi.confsched2023.designsystem.strings.AppStrings
import io.github.droidkaigi.confsched2023.model.DroidKaigi2023Day
import io.github.droidkaigi.confsched2023.model.Filters
import io.github.droidkaigi.confsched2023.model.SessionsRepository
import io.github.droidkaigi.confsched2023.model.Timetable
import io.github.droidkaigi.confsched2023.model.TimetableItemId
import io.github.droidkaigi.confsched2023.ui.UserMessageStateHolder
import io.github.droidkaigi.confsched2023.ui.buildUiState
import io.github.droidkaigi.confsched2023.ui.handleErrorAndRetry
import kotlinx.collections.immutable.toPersistentList
import kotlinx.collections.immutable.toPersistentMap
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.SharingStarted
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.stateIn
import kotlinx.coroutines.flow.update
import kotlinx.coroutines.launch
import javax.inject.Inject

@HiltViewModel
class BookmarkScreenViewModel @Inject constructor(
private val sessionsRepository: SessionsRepository,
userMessageStateHolder: UserMessageStateHolder,
) : ViewModel() {

private val sessionsStateFlow: StateFlow<Timetable> = sessionsRepository
.getTimetableStream()
.handleErrorAndRetry(
AppStrings.Retry,
userMessageStateHolder,
)
.stateIn(
scope = viewModelScope,
started = SharingStarted.WhileSubscribed(5_000),
initialValue = Timetable(),
)

private val currentDayFilter = MutableStateFlow(
DroidKaigi2023Day.values().map { it },
)

val uiState: StateFlow<BookmarkScreenUiState> =
buildUiState(
sessionsStateFlow,
currentDayFilter,
) { sessionsStateFlow, currentDayFilter ->
val sortAndGroupedBookmarkedTimetableItems = sessionsStateFlow.filtered(
Filters(
days = currentDayFilter,
filterFavorite = true,
),
).timetableItems.groupBy {
it.startsTimeString + it.endsTimeString
}.mapValues { entries ->
entries.value.sortedWith(
compareBy({ it.day?.name.orEmpty() }, { it.startsTimeString }),
)
}.toPersistentMap()

if (sortAndGroupedBookmarkedTimetableItems.isEmpty()) {
BookmarkScreenUiState.Empty(
currentDayFilter.toPersistentList(),
)
} else {
BookmarkScreenUiState.ListBookmark(
sessionsStateFlow.bookmarks,
sortAndGroupedBookmarkedTimetableItems,
currentDayFilter.toPersistentList(),
)
}
}

fun onClickAllFilterChip() {
currentDayFilter.update {
DroidKaigi2023Day.values().toList()
}
}

fun onClickDayFirstChip() {
currentDayFilter.update {
listOf(DroidKaigi2023Day.Day1)
}
}

fun onClickDaySecondChip() {
currentDayFilter.update {
listOf(DroidKaigi2023Day.Day2)
}
}

fun onClickDayThirdChip() {
currentDayFilter.update {
listOf(DroidKaigi2023Day.Day3)
}
}

fun updateBookmark(timetableItem: TimetableItemId) {
viewModelScope.launch {
sessionsRepository.toggleBookmark(timetableItem)
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,11 @@ fun NavGraphBuilder.sessionScreens(onNavigationIconClick: () -> Unit) {
onNavigationIconClick = onNavigationIconClick,
)
}
composable(bookmarkScreenRoute) {
BookmarkScreen(
onClickBackPress = onNavigationIconClick,
)
}
}

fun NavController.navigateToTimetableItemDetailScreen(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,11 +31,13 @@ const val timetableScreenRoute = "timetable"
fun NavGraphBuilder.nestedSessionScreens(
onSearchClick: () -> Unit,
onTimetableItemClick: (TimetableItem) -> Unit,
onClickBookmarkIcon: () -> Unit,
) {
composable(timetableScreenRoute) {
TimetableScreen(
onSearchClick = onSearchClick,
onTimetableItemClick = onTimetableItemClick,
onClickBookmarkIcon = onClickBookmarkIcon,
)
}
}
Expand All @@ -50,6 +52,7 @@ const val TimetableScreenTestTag = "TimetableScreen"
fun TimetableScreen(
onSearchClick: () -> Unit,
onTimetableItemClick: (TimetableItem) -> Unit,
onClickBookmarkIcon: () -> Unit,
viewModel: TimetableScreenViewModel = hiltViewModel<TimetableScreenViewModel>(),
) {
val uiState by viewModel.uiState.collectAsState()
Expand All @@ -64,6 +67,7 @@ fun TimetableScreen(
snackbarHostState = snackbarHostState,
onTimetableItemClick = onTimetableItemClick,
onBookmarkClick = viewModel::onBookmarkClick,
onClickBookmarkIcon = onClickBookmarkIcon,
onSearchClick = onSearchClick,
onTimetableUiChangeClick = viewModel::onUiTypeChange,
)
Expand All @@ -79,6 +83,7 @@ private fun TimetableScreen(
snackbarHostState: SnackbarHostState,
onTimetableItemClick: (TimetableItem) -> Unit,
onBookmarkClick: (TimetableItem) -> Unit,
onClickBookmarkIcon: () -> Unit,
onSearchClick: () -> Unit,
onTimetableUiChangeClick: () -> Unit,
) {
Expand All @@ -94,7 +99,12 @@ private fun TimetableScreen(
)
},
topBar = {
TimetableTopArea(state, onSearchClick, onTimetableUiChangeClick)
TimetableTopArea(
state,
onTimetableUiChangeClick,
onSearchClick,
onClickBookmarkIcon,
)
},
containerColor = MaterialTheme.colorScheme.surfaceVariant,
contentWindowInsets = WindowInsets(0.dp),
Expand Down
Loading
Loading