From 74e36b381858350de97a6456aaac9c86c4f243c3 Mon Sep 17 00:00:00 2001 From: zachseidner1 Date: Wed, 26 Mar 2025 21:27:02 -0400 Subject: [PATCH 01/11] Bump material version in preparation for pull to refresh --- app/build.gradle.kts | 2 +- gradle/libs.versions.toml | 4 +++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/app/build.gradle.kts b/app/build.gradle.kts index 5642ff1..0dfecfb 100644 --- a/app/build.gradle.kts +++ b/app/build.gradle.kts @@ -69,7 +69,7 @@ dependencies { implementation("androidx.activity:activity-compose") implementation("androidx.lifecycle:lifecycle-viewmodel-compose") implementation("androidx.navigation:navigation-compose:2.8.2") - implementation("androidx.compose.material3:material3:1.0.0") + implementation(libs.material3) implementation("com.google.dagger:hilt-android:2.51.1") kapt("com.google.dagger:hilt-android-compiler:2.51.1") implementation("androidx.hilt:hilt-navigation-compose:1.0.0") diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index cfdf911..f72f8bd 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -7,12 +7,13 @@ junit = "4.13.2" junitVersion = "1.1.5" espressoCore = "3.5.1" appcompat = "1.6.1" -material = "1.10.0" +material = "1.12.0" activity = "1.8.0" constraintlayout = "2.1.4" runtimeAndroid = "1.7.2" apollo = "4.1.1" media3CommonKtx = "1.5.1" +material3 = "1.3.1" [libraries] androidx-core-ktx = { group = "androidx.core", name = "core-ktx", version.ref = "coreKtx" } @@ -22,6 +23,7 @@ androidx-junit = { group = "androidx.test.ext", name = "junit", version.ref = "j androidx-espresso-core = { group = "androidx.test.espresso", name = "espresso-core", version.ref = "espressoCore" } androidx-appcompat = { group = "androidx.appcompat", name = "appcompat", version.ref = "appcompat" } material = { group = "com.google.android.material", name = "material", version.ref = "material" } +material3 = { group = "androidx.compose.material3", name = "material3", version.ref = "material3" } androidx-activity = { group = "androidx.activity", name = "activity", version.ref = "activity" } androidx-constraintlayout = { group = "androidx.constraintlayout", name = "constraintlayout", version.ref = "constraintlayout" } androidx-runtime-android = { group = "androidx.compose.runtime", name = "runtime-android", version.ref = "runtimeAndroid" } From 7dba233e2a1b8f20c00906d317850b08ae7d02db Mon Sep 17 00:00:00 2001 From: zachseidner1 Date: Tue, 8 Apr 2025 14:04:53 -0400 Subject: [PATCH 02/11] Add pull to refresh box on home screen --- .../score/components/ScorePullToRefreshBox.kt | 29 +++++++++++++++++++ .../cornellappdev/score/screen/HomeScreen.kt | 29 +++++++++++++++++-- 2 files changed, 55 insertions(+), 3 deletions(-) create mode 100644 app/src/main/java/com/cornellappdev/score/components/ScorePullToRefreshBox.kt diff --git a/app/src/main/java/com/cornellappdev/score/components/ScorePullToRefreshBox.kt b/app/src/main/java/com/cornellappdev/score/components/ScorePullToRefreshBox.kt new file mode 100644 index 0000000..d24ec9e --- /dev/null +++ b/app/src/main/java/com/cornellappdev/score/components/ScorePullToRefreshBox.kt @@ -0,0 +1,29 @@ +package com.cornellappdev.score.components + +import androidx.compose.material3.ExperimentalMaterial3Api +import androidx.compose.material3.pulltorefresh.PullToRefreshBox +import androidx.compose.material3.pulltorefresh.PullToRefreshDefaults.Indicator +import androidx.compose.material3.pulltorefresh.rememberPullToRefreshState +import androidx.compose.runtime.Composable +import androidx.compose.ui.Modifier +import com.cornellappdev.score.theme.CrimsonPrimary + +@OptIn(ExperimentalMaterial3Api::class) +@Composable +fun ScorePullToRefreshBox( + isRefreshing: Boolean, + onRefresh: () -> Unit, + modifier: Modifier = Modifier, + content: @Composable () -> Unit +) { + val state = rememberPullToRefreshState() + + PullToRefreshBox( + isRefreshing, onRefresh, modifier, + indicator = { + Indicator(state, isRefreshing, color = CrimsonPrimary) + } + ) { + content() + } +} \ No newline at end of file diff --git a/app/src/main/java/com/cornellappdev/score/screen/HomeScreen.kt b/app/src/main/java/com/cornellappdev/score/screen/HomeScreen.kt index 86081be..df4f4f2 100644 --- a/app/src/main/java/com/cornellappdev/score/screen/HomeScreen.kt +++ b/app/src/main/java/com/cornellappdev/score/screen/HomeScreen.kt @@ -13,6 +13,7 @@ import androidx.compose.foundation.layout.height import androidx.compose.foundation.lazy.LazyColumn import androidx.compose.foundation.lazy.items import androidx.compose.material3.CircularProgressIndicator +import androidx.compose.material3.ExperimentalMaterial3Api import androidx.compose.material3.Text import androidx.compose.runtime.Composable import androidx.compose.ui.Alignment @@ -24,6 +25,7 @@ import androidx.compose.ui.unit.dp import androidx.hilt.navigation.compose.hiltViewModel import com.cornellappdev.score.components.GameCard import com.cornellappdev.score.components.GamesCarousel +import com.cornellappdev.score.components.ScorePullToRefreshBox import com.cornellappdev.score.components.SportSelectorHeader import com.cornellappdev.score.model.ApiResponse import com.cornellappdev.score.model.GamesCarouselVariant @@ -76,20 +78,35 @@ fun HomeScreen( uiState = uiState, onGenderSelected = { homeViewModel.onGenderSelected(it) }, onSportSelected = { homeViewModel.onSportSelected(it) }, - navigateToGameDetails = navigateToGameDetails + navigateToGameDetails = navigateToGameDetails, + onRefresh = { TODO() } ) } } } } -@OptIn(ExperimentalFoundationApi::class) +@OptIn(ExperimentalFoundationApi::class, ExperimentalMaterial3Api::class) @Composable private fun HomeContent( uiState: HomeUiState, onGenderSelected: (GenderDivision) -> Unit, onSportSelected: (SportSelection) -> Unit, + onRefresh: () -> Unit, navigateToGameDetails: (Boolean) -> Unit = {} +) { + ScorePullToRefreshBox(isRefreshing = uiState.loadedState == ApiResponse.Loading, onRefresh) { + HomeLazyColumn(uiState, onGenderSelected, onSportSelected, navigateToGameDetails) + } +} + +@Composable +@OptIn(ExperimentalFoundationApi::class) +private fun HomeLazyColumn( + uiState: HomeUiState, + onGenderSelected: (GenderDivision) -> Unit, + onSportSelected: (SportSelection) -> Unit, + navigateToGameDetails: (Boolean) -> Unit ) { LazyColumn(contentPadding = PaddingValues(top = 24.dp, start = 24.dp, end = 24.dp)) { item { @@ -135,6 +152,11 @@ private fun HomeContent( } } +@Composable +private fun HomeLazyColumn() { + +} + @Preview @Composable private fun HomeScreenPreview() { @@ -151,7 +173,8 @@ private fun HomeScreenPreview() { loadedState = ApiResponse.Success(gameList) ), onGenderSelected = {}, - onSportSelected = {} + onSportSelected = {}, + onRefresh = {}, ) } } From 002794f78d3b3a08c7ae02130ff92dd5bc7c6a59 Mon Sep 17 00:00:00 2001 From: amjiao Date: Sat, 22 Mar 2025 14:25:35 -0400 Subject: [PATCH 03/11] add onRefresh --- .../com/cornellappdev/score/viewmodel/HomeViewModel.kt | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/app/src/main/java/com/cornellappdev/score/viewmodel/HomeViewModel.kt b/app/src/main/java/com/cornellappdev/score/viewmodel/HomeViewModel.kt index 2121b70..f4f6778 100644 --- a/app/src/main/java/com/cornellappdev/score/viewmodel/HomeViewModel.kt +++ b/app/src/main/java/com/cornellappdev/score/viewmodel/HomeViewModel.kt @@ -60,6 +60,14 @@ class HomeViewModel @Inject constructor( } } + fun onRefresh() { + applyMutation { + copy(loadedState = ApiResponse.Loading) + } + + scoreRepository.fetchGames() + } + fun onGenderSelected(gender: GenderDivision) { applyMutation { copy( From 4b4e9698aec493f5b4e7c5c9a77f1852a03b7f5a Mon Sep 17 00:00:00 2001 From: zachseidner1 Date: Tue, 8 Apr 2025 14:14:07 -0400 Subject: [PATCH 04/11] Finalize pull to refresh on home screen --- .../score/components/ScorePullToRefreshBox.kt | 8 ++++++-- .../java/com/cornellappdev/score/screen/HomeScreen.kt | 2 +- gradle/libs.versions.toml | 4 +++- 3 files changed, 10 insertions(+), 4 deletions(-) diff --git a/app/src/main/java/com/cornellappdev/score/components/ScorePullToRefreshBox.kt b/app/src/main/java/com/cornellappdev/score/components/ScorePullToRefreshBox.kt index d24ec9e..65b020d 100644 --- a/app/src/main/java/com/cornellappdev/score/components/ScorePullToRefreshBox.kt +++ b/app/src/main/java/com/cornellappdev/score/components/ScorePullToRefreshBox.kt @@ -5,8 +5,10 @@ import androidx.compose.material3.pulltorefresh.PullToRefreshBox import androidx.compose.material3.pulltorefresh.PullToRefreshDefaults.Indicator import androidx.compose.material3.pulltorefresh.rememberPullToRefreshState import androidx.compose.runtime.Composable +import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import com.cornellappdev.score.theme.CrimsonPrimary +import com.cornellappdev.score.theme.White @OptIn(ExperimentalMaterial3Api::class) @Composable @@ -20,9 +22,11 @@ fun ScorePullToRefreshBox( PullToRefreshBox( isRefreshing, onRefresh, modifier, + state = state, indicator = { - Indicator(state, isRefreshing, color = CrimsonPrimary) - } + Indicator(state, isRefreshing, color = CrimsonPrimary, containerColor = White) + }, + contentAlignment = Alignment.TopCenter ) { content() } diff --git a/app/src/main/java/com/cornellappdev/score/screen/HomeScreen.kt b/app/src/main/java/com/cornellappdev/score/screen/HomeScreen.kt index df4f4f2..db84fe9 100644 --- a/app/src/main/java/com/cornellappdev/score/screen/HomeScreen.kt +++ b/app/src/main/java/com/cornellappdev/score/screen/HomeScreen.kt @@ -79,7 +79,7 @@ fun HomeScreen( onGenderSelected = { homeViewModel.onGenderSelected(it) }, onSportSelected = { homeViewModel.onSportSelected(it) }, navigateToGameDetails = navigateToGameDetails, - onRefresh = { TODO() } + onRefresh = { homeViewModel.onRefresh() } ) } } diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index f72f8bd..0fa6263 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -13,7 +13,9 @@ constraintlayout = "2.1.4" runtimeAndroid = "1.7.2" apollo = "4.1.1" media3CommonKtx = "1.5.1" -material3 = "1.3.1" +# Using alpha version due to bug with pull to refresh in the latest stabel version +# See https://stackoverflow.com/a/79126321 +material3 = "1.4.0-alpha11" [libraries] androidx-core-ktx = { group = "androidx.core", name = "core-ktx", version.ref = "coreKtx" } From 2841fd7d39c12cc044234609de8a3a8ef664d4cb Mon Sep 17 00:00:00 2001 From: zachseidner1 Date: Tue, 8 Apr 2025 14:14:31 -0400 Subject: [PATCH 05/11] Delete unused component --- .../main/java/com/cornellappdev/score/screen/HomeScreen.kt | 5 ----- 1 file changed, 5 deletions(-) diff --git a/app/src/main/java/com/cornellappdev/score/screen/HomeScreen.kt b/app/src/main/java/com/cornellappdev/score/screen/HomeScreen.kt index db84fe9..1a9f700 100644 --- a/app/src/main/java/com/cornellappdev/score/screen/HomeScreen.kt +++ b/app/src/main/java/com/cornellappdev/score/screen/HomeScreen.kt @@ -152,11 +152,6 @@ private fun HomeLazyColumn( } } -@Composable -private fun HomeLazyColumn() { - -} - @Preview @Composable private fun HomeScreenPreview() { From b145fdabf68efeb32203c0c5395efbcbd27aa138 Mon Sep 17 00:00:00 2001 From: zachseidner1 Date: Tue, 8 Apr 2025 14:24:10 -0400 Subject: [PATCH 06/11] Add pull to refresh for past games --- .../score/screen/PastGamesScreen.kt | 19 ++++++++++++++++++- .../score/viewmodel/PastGamesViewModel.kt | 6 ++++++ 2 files changed, 24 insertions(+), 1 deletion(-) diff --git a/app/src/main/java/com/cornellappdev/score/screen/PastGamesScreen.kt b/app/src/main/java/com/cornellappdev/score/screen/PastGamesScreen.kt index ded0052..0aa4a8c 100644 --- a/app/src/main/java/com/cornellappdev/score/screen/PastGamesScreen.kt +++ b/app/src/main/java/com/cornellappdev/score/screen/PastGamesScreen.kt @@ -23,6 +23,7 @@ import androidx.compose.ui.unit.dp import androidx.hilt.navigation.compose.hiltViewModel import com.cornellappdev.score.components.GamesCarousel import com.cornellappdev.score.components.PastGameCard +import com.cornellappdev.score.components.ScorePullToRefreshBox import com.cornellappdev.score.components.SportSelectorHeader import com.cornellappdev.score.model.ApiResponse import com.cornellappdev.score.model.GamesCarouselVariant @@ -75,7 +76,8 @@ fun PastGamesScreen( uiState = uiState, onGenderSelected = { pastGamesViewModel.onGenderSelected(it) }, onSportSelected = { pastGamesViewModel.onSportSelected(it) }, - navigateToGameDetails = navigateToGameDetails + navigateToGameDetails = navigateToGameDetails, + onRefresh = pastGamesViewModel::onRefresh ) } } @@ -88,7 +90,21 @@ private fun PastGamesContent( uiState: PastGamesUiState, onGenderSelected: (GenderDivision) -> Unit, onSportSelected: (SportSelection) -> Unit, + onRefresh: () -> Unit, navigateToGameDetails: (Boolean) -> Unit = {} +) { + ScorePullToRefreshBox(uiState.loadedState == ApiResponse.Loading, onRefresh = onRefresh) { + PastGamesLazyColumn(uiState, onGenderSelected, onSportSelected, navigateToGameDetails) + } +} + +@Composable +@OptIn(ExperimentalFoundationApi::class) +private fun PastGamesLazyColumn( + uiState: PastGamesUiState, + onGenderSelected: (GenderDivision) -> Unit, + onSportSelected: (SportSelection) -> Unit, + navigateToGameDetails: (Boolean) -> Unit ) { LazyColumn(contentPadding = PaddingValues(top = 24.dp, start = 24.dp, end = 24.dp)) { item { @@ -139,5 +155,6 @@ private fun PastGamesPreview() { ), onGenderSelected = {}, onSportSelected = {}, + onRefresh = {}, ) } \ No newline at end of file diff --git a/app/src/main/java/com/cornellappdev/score/viewmodel/PastGamesViewModel.kt b/app/src/main/java/com/cornellappdev/score/viewmodel/PastGamesViewModel.kt index 4df8393..cfc8963 100644 --- a/app/src/main/java/com/cornellappdev/score/viewmodel/PastGamesViewModel.kt +++ b/app/src/main/java/com/cornellappdev/score/viewmodel/PastGamesViewModel.kt @@ -74,4 +74,10 @@ class PastGamesViewModel @Inject constructor( ) } } + + fun onRefresh() { + applyMutation { copy(loadedState = ApiResponse.Loading) } + + scoreRepository.fetchGames() + } } \ No newline at end of file From 4b1c692aad630e5ec85fb340a959259def494dcc Mon Sep 17 00:00:00 2001 From: zachseidner1 Date: Mon, 14 Apr 2025 22:22:34 -0400 Subject: [PATCH 07/11] Fix small divider issue --- .../cornellappdev/score/screen/HomeScreen.kt | 15 ++++++----- .../score/screen/PastGamesScreen.kt | 26 ++++++++++++------- .../score/viewmodel/PastGamesViewModel.kt | 10 +------ 3 files changed, 26 insertions(+), 25 deletions(-) diff --git a/app/src/main/java/com/cornellappdev/score/screen/HomeScreen.kt b/app/src/main/java/com/cornellappdev/score/screen/HomeScreen.kt index b686404..6688183 100644 --- a/app/src/main/java/com/cornellappdev/score/screen/HomeScreen.kt +++ b/app/src/main/java/com/cornellappdev/score/screen/HomeScreen.kt @@ -3,6 +3,7 @@ package com.cornellappdev.score.screen import androidx.compose.foundation.ExperimentalFoundationApi import androidx.compose.foundation.background import androidx.compose.foundation.layout.Arrangement +import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.PaddingValues import androidx.compose.foundation.layout.Spacer @@ -12,7 +13,6 @@ import androidx.compose.foundation.layout.height import androidx.compose.foundation.layout.padding import androidx.compose.foundation.lazy.LazyColumn import androidx.compose.foundation.lazy.items -import androidx.compose.material3.CircularProgressIndicator import androidx.compose.material3.ExperimentalMaterial3Api import androidx.compose.material3.HorizontalDivider import androidx.compose.material3.Text @@ -98,7 +98,7 @@ private fun HomeLazyColumn( uiState: HomeUiState, onGenderSelected: (GenderDivision) -> Unit, onSportSelected: (SportSelection) -> Unit, - navigateToGameDetails: (Boolean) -> Unit + navigateToGameDetails: (String) -> Unit ) { LazyColumn(contentPadding = PaddingValues(top = 24.dp)) { item { @@ -139,12 +139,15 @@ private fun HomeLazyColumn( onSportSelected = onSportSelected, ) } + Box(modifier = Modifier.background(White)) { + HorizontalDivider( + modifier = Modifier.padding(top = 16.dp), + color = GrayStroke, + ) + } } item { - HorizontalDivider( - modifier = Modifier.padding(top = 16.dp, bottom = 24.dp), - color = GrayStroke, - ) + Spacer(modifier = Modifier.height(24.dp)) } items(uiState.filteredGames) { val game = it diff --git a/app/src/main/java/com/cornellappdev/score/screen/PastGamesScreen.kt b/app/src/main/java/com/cornellappdev/score/screen/PastGamesScreen.kt index 6e8875b..af87dcc 100644 --- a/app/src/main/java/com/cornellappdev/score/screen/PastGamesScreen.kt +++ b/app/src/main/java/com/cornellappdev/score/screen/PastGamesScreen.kt @@ -3,6 +3,7 @@ package com.cornellappdev.score.screen import androidx.compose.foundation.ExperimentalFoundationApi import androidx.compose.foundation.background import androidx.compose.foundation.layout.Arrangement +import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.PaddingValues import androidx.compose.foundation.layout.Spacer @@ -94,7 +95,7 @@ private fun PastGamesLazyColumn( uiState: PastGamesUiState, onGenderSelected: (GenderDivision) -> Unit, onSportSelected: (SportSelection) -> Unit, - navigateToGameDetails: (Boolean) -> Unit + navigateToGameDetails: (String) -> Unit ) { LazyColumn(contentPadding = PaddingValues(top = 24.dp)) { item { @@ -114,9 +115,11 @@ private fun PastGamesLazyColumn( GamesCarousel(uiState.pastGames, navigateToGameDetails) } stickyHeader { - Column(modifier = Modifier - .background(White) - .padding(horizontal = 24.dp)) { + Column( + modifier = Modifier + .background(White) + .padding(horizontal = 24.dp) + ) { Spacer(Modifier.height(24.dp)) Text( text = "All Scores", @@ -133,19 +136,22 @@ private fun PastGamesLazyColumn( onSportSelected = onSportSelected ) } + Box(modifier = Modifier.background(White)) { + HorizontalDivider( + modifier = Modifier.padding(top = 16.dp), + color = GrayStroke, + ) + } } item { - HorizontalDivider( - modifier = Modifier.padding(top = 16.dp, bottom = 24.dp), - color = GrayStroke, - ) + Spacer(modifier = Modifier.height(24.dp)) } items(uiState.filteredGames) { val game = it - Column (modifier = Modifier.padding(horizontal = 24.dp)) { + Column(modifier = Modifier.padding(horizontal = 24.dp)) { PastGameCard( data = game, - onClick = {navigateToGameDetails(game.id)} + onClick = { navigateToGameDetails(game.id) } ) Spacer(modifier = Modifier.height(16.dp)) } diff --git a/app/src/main/java/com/cornellappdev/score/viewmodel/PastGamesViewModel.kt b/app/src/main/java/com/cornellappdev/score/viewmodel/PastGamesViewModel.kt index 7d70e32..023b90b 100644 --- a/app/src/main/java/com/cornellappdev/score/viewmodel/PastGamesViewModel.kt +++ b/app/src/main/java/com/cornellappdev/score/viewmodel/PastGamesViewModel.kt @@ -58,15 +58,7 @@ class PastGamesViewModel @Inject constructor( } } } - - fun onRefresh() { - applyMutation { - copy(loadedState = ApiResponse.Loading) - } - - scoreRepository.fetchGames() - } - + fun onGenderSelected(gender: GenderDivision) { applyMutation { copy( From de22dcc330f7e16c302669eab47ffbac633c6744 Mon Sep 17 00:00:00 2001 From: zachseidner1 Date: Mon, 14 Apr 2025 22:40:07 -0400 Subject: [PATCH 08/11] pull to refresh game details --- .../score/screen/GameDetailsScreen.kt | 7 ++++ .../score/viewmodel/GameDetailsViewModel.kt | 32 ++++++++++--------- 2 files changed, 24 insertions(+), 15 deletions(-) diff --git a/app/src/main/java/com/cornellappdev/score/screen/GameDetailsScreen.kt b/app/src/main/java/com/cornellappdev/score/screen/GameDetailsScreen.kt index 554185d..7106047 100644 --- a/app/src/main/java/com/cornellappdev/score/screen/GameDetailsScreen.kt +++ b/app/src/main/java/com/cornellappdev/score/screen/GameDetailsScreen.kt @@ -13,6 +13,8 @@ import androidx.compose.foundation.layout.height import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.size import androidx.compose.foundation.layout.width +import androidx.compose.foundation.rememberScrollState +import androidx.compose.foundation.verticalScroll import androidx.compose.material3.CircularProgressIndicator import androidx.compose.material3.Icon import androidx.compose.material3.Text @@ -32,6 +34,7 @@ import com.cornellappdev.score.components.BoxScore import com.cornellappdev.score.components.ButtonPrimary import com.cornellappdev.score.components.GameScoreHeader import com.cornellappdev.score.components.NavigationHeader +import com.cornellappdev.score.components.ScorePullToRefreshBox import com.cornellappdev.score.components.TimeUntilStartCard import com.cornellappdev.score.model.ApiResponse import com.cornellappdev.score.model.DetailsCardData @@ -59,10 +62,14 @@ fun GameDetailsScreen( onBackArrow: () -> Unit = {} ) { val uiState = gameDetailsViewModel.collectUiStateValue() + ScorePullToRefreshBox( + uiState.loadedState == ApiResponse.Loading, + { gameDetailsViewModel.onRefresh() }) { } Column( modifier = Modifier .fillMaxSize() .background(White) + .verticalScroll(rememberScrollState()) ) { NavigationHeader( title = "Game Details", diff --git a/app/src/main/java/com/cornellappdev/score/viewmodel/GameDetailsViewModel.kt b/app/src/main/java/com/cornellappdev/score/viewmodel/GameDetailsViewModel.kt index 031f5d5..259d600 100644 --- a/app/src/main/java/com/cornellappdev/score/viewmodel/GameDetailsViewModel.kt +++ b/app/src/main/java/com/cornellappdev/score/viewmodel/GameDetailsViewModel.kt @@ -15,28 +15,30 @@ data class GameDetailsUiState( @HiltViewModel class GameDetailsViewModel @Inject constructor( - scoreRepository: ScoreRepository, - savedStateHandle: SavedStateHandle + private val scoreRepository: ScoreRepository, + savedStateHandle: SavedStateHandle, ) : BaseViewModel( initialUiState = GameDetailsUiState( loadedState = ApiResponse.Loading ) ) { + private val gameId: String = checkNotNull(savedStateHandle["gameId"]) + init { - val gameId: String? = savedStateHandle["gameId"] - gameId?.let { - scoreRepository.getGameById(it) - asyncCollect(scoreRepository.currentGamesFlow) { response -> - applyMutation { - copy( - loadedState = response.map { gameCard -> - gameCard.toGameCardData() - } - ) - } + asyncCollect(scoreRepository.currentGamesFlow) { response -> + applyMutation { + copy( + loadedState = response.map { gameCard -> + gameCard.toGameCardData() + } + ) } - } ?: applyMutation { - copy(loadedState = ApiResponse.Error) } + onRefresh() + } + + fun onRefresh() { + applyMutation { copy(loadedState = ApiResponse.Loading) } + scoreRepository.getGameById(gameId) } } \ No newline at end of file From 83abf8a075366a09fa5ea06dedbd123763c4937c Mon Sep 17 00:00:00 2001 From: zachseidner1 Date: Mon, 14 Apr 2025 22:45:44 -0400 Subject: [PATCH 09/11] start on game details screen maybe change stuff later oops --- .../score/screen/GameDetailsScreen.kt | 62 ++++++++++--------- 1 file changed, 32 insertions(+), 30 deletions(-) diff --git a/app/src/main/java/com/cornellappdev/score/screen/GameDetailsScreen.kt b/app/src/main/java/com/cornellappdev/score/screen/GameDetailsScreen.kt index 7106047..c72fe1b 100644 --- a/app/src/main/java/com/cornellappdev/score/screen/GameDetailsScreen.kt +++ b/app/src/main/java/com/cornellappdev/score/screen/GameDetailsScreen.kt @@ -64,43 +64,45 @@ fun GameDetailsScreen( val uiState = gameDetailsViewModel.collectUiStateValue() ScorePullToRefreshBox( uiState.loadedState == ApiResponse.Loading, - { gameDetailsViewModel.onRefresh() }) { } - Column( - modifier = Modifier - .fillMaxSize() - .background(White) - .verticalScroll(rememberScrollState()) - ) { - NavigationHeader( - title = "Game Details", - onBackPressed = onBackArrow - ) - when (val state = uiState.loadedState) { - is ApiResponse.Loading, ApiResponse.Loading -> { - Box( - modifier = Modifier.fillMaxSize(), - contentAlignment = Alignment.Center - ) { - CircularProgressIndicator(color = GrayPrimary) + { gameDetailsViewModel.onRefresh() }) { + Column( + modifier = Modifier + .fillMaxSize() + .background(White) + .verticalScroll(rememberScrollState()) + ) { + NavigationHeader( + title = "Game Details", + onBackPressed = onBackArrow + ) + when (val state = uiState.loadedState) { + is ApiResponse.Loading, ApiResponse.Loading -> { + Box( + modifier = Modifier.fillMaxSize(), + contentAlignment = Alignment.Center + ) { + CircularProgressIndicator(color = GrayPrimary) + } } - } - is ApiResponse.Error -> { - Box( - modifier = Modifier.fillMaxSize(), - contentAlignment = Alignment.Center - ) { - Text(text = "Failed to load game.") + is ApiResponse.Error -> { + Box( + modifier = Modifier.fillMaxSize(), + contentAlignment = Alignment.Center + ) { + Text(text = "Failed to load game.") + } } - } - is ApiResponse.Success -> { - GameDetailsContent( - gameCard = state.data - ) + is ApiResponse.Success -> { + GameDetailsContent( + gameCard = state.data + ) + } } } } + } @Composable From 3ae7eb9aadfad2ece6a14a1ef77d414586c9030f Mon Sep 17 00:00:00 2001 From: zachseidner1 Date: Tue, 15 Apr 2025 12:01:43 -0400 Subject: [PATCH 10/11] add loading screen --- .../com/cornellappdev/score/screen/GameDetailsScreen.kt | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/app/src/main/java/com/cornellappdev/score/screen/GameDetailsScreen.kt b/app/src/main/java/com/cornellappdev/score/screen/GameDetailsScreen.kt index c72fe1b..a096fc7 100644 --- a/app/src/main/java/com/cornellappdev/score/screen/GameDetailsScreen.kt +++ b/app/src/main/java/com/cornellappdev/score/screen/GameDetailsScreen.kt @@ -32,6 +32,7 @@ import androidx.hilt.navigation.compose.hiltViewModel import com.cornellappdev.score.R import com.cornellappdev.score.components.BoxScore import com.cornellappdev.score.components.ButtonPrimary +import com.cornellappdev.score.components.GameDetailsLoadingScreen import com.cornellappdev.score.components.GameScoreHeader import com.cornellappdev.score.components.NavigationHeader import com.cornellappdev.score.components.ScorePullToRefreshBox @@ -86,12 +87,7 @@ fun GameDetailsScreen( } is ApiResponse.Error -> { - Box( - modifier = Modifier.fillMaxSize(), - contentAlignment = Alignment.Center - ) { - Text(text = "Failed to load game.") - } + GameDetailsLoadingScreen() } is ApiResponse.Success -> { From aa518b6d65c987abf6d6fbbd1c9d7b41d067321f Mon Sep 17 00:00:00 2001 From: zachseidner1 Date: Tue, 15 Apr 2025 12:15:31 -0400 Subject: [PATCH 11/11] Add pull to refresh on game details --- .../score/components/ScoreSummary.kt | 20 ++++++++----------- .../score/screen/GameDetailsScreen.kt | 20 ++++++++----------- 2 files changed, 16 insertions(+), 24 deletions(-) diff --git a/app/src/main/java/com/cornellappdev/score/components/ScoreSummary.kt b/app/src/main/java/com/cornellappdev/score/components/ScoreSummary.kt index cca3322..68b339c 100644 --- a/app/src/main/java/com/cornellappdev/score/components/ScoreSummary.kt +++ b/app/src/main/java/com/cornellappdev/score/components/ScoreSummary.kt @@ -1,12 +1,11 @@ import androidx.compose.foundation.Image import androidx.compose.foundation.layout.Arrangement +import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Row import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.size -import androidx.compose.foundation.lazy.LazyColumn -import androidx.compose.foundation.lazy.items -import androidx.compose.material3.Divider +import androidx.compose.material3.HorizontalDivider import androidx.compose.material3.Text import androidx.compose.runtime.Composable import androidx.compose.ui.Alignment @@ -30,12 +29,10 @@ import com.cornellappdev.score.util.scoreEvents1 @Composable fun ScoringSummary(scoreEvents: List, modifier: Modifier = Modifier) { - LazyColumn( - modifier = modifier.fillMaxWidth() - ) { - items(scoreEvents) { event -> - ScoreEventItem(event) - Divider(color = Color.LightGray, thickness = 0.5.dp) + Column(modifier = modifier) { + scoreEvents.take(3).map { + ScoreEventItem(it) + HorizontalDivider(color = Color.LightGray, thickness = 0.5.dp) } } } @@ -48,7 +45,7 @@ fun ScoreEventItem(event: ScoreEvent) { .padding(vertical = 16.dp), verticalAlignment = Alignment.CenterVertically ) { - if (event.team.name == "COR"){ // TODO: Check if its "COR" for all queries. It is for baseball + if (event.team.name == "COR") { // TODO: Check if its "COR" for all queries. It is for baseball Image( painter = painterResource(R.drawable.cornell_logo), contentDescription = event.team.name, @@ -56,8 +53,7 @@ fun ScoreEventItem(event: ScoreEvent) { .size(40.dp) .padding(end = 12.dp) ) - } - else{ + } else { AsyncImage( model = event.team.logo, contentDescription = event.team.name, // Turn this into a if statement if i know the link for cornell logo diff --git a/app/src/main/java/com/cornellappdev/score/screen/GameDetailsScreen.kt b/app/src/main/java/com/cornellappdev/score/screen/GameDetailsScreen.kt index a096fc7..adf39c0 100644 --- a/app/src/main/java/com/cornellappdev/score/screen/GameDetailsScreen.kt +++ b/app/src/main/java/com/cornellappdev/score/screen/GameDetailsScreen.kt @@ -3,7 +3,6 @@ package com.cornellappdev.score.screen import ScoringSummary import androidx.compose.foundation.Image import androidx.compose.foundation.background -import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Row import androidx.compose.foundation.layout.Spacer @@ -15,7 +14,6 @@ import androidx.compose.foundation.layout.size import androidx.compose.foundation.layout.width import androidx.compose.foundation.rememberScrollState import androidx.compose.foundation.verticalScroll -import androidx.compose.material3.CircularProgressIndicator import androidx.compose.material3.Icon import androidx.compose.material3.Text import androidx.compose.runtime.Composable @@ -32,6 +30,7 @@ import androidx.hilt.navigation.compose.hiltViewModel import com.cornellappdev.score.R import com.cornellappdev.score.components.BoxScore import com.cornellappdev.score.components.ButtonPrimary +import com.cornellappdev.score.components.ErrorState import com.cornellappdev.score.components.GameDetailsLoadingScreen import com.cornellappdev.score.components.GameScoreHeader import com.cornellappdev.score.components.NavigationHeader @@ -64,8 +63,11 @@ fun GameDetailsScreen( ) { val uiState = gameDetailsViewModel.collectUiStateValue() ScorePullToRefreshBox( - uiState.loadedState == ApiResponse.Loading, - { gameDetailsViewModel.onRefresh() }) { + // We have a separate loading state for this screen so we don't want the refresh indicator + // to persist as the screen loads. + false, + gameDetailsViewModel::onRefresh + ) { Column( modifier = Modifier .fillMaxSize() @@ -78,16 +80,11 @@ fun GameDetailsScreen( ) when (val state = uiState.loadedState) { is ApiResponse.Loading, ApiResponse.Loading -> { - Box( - modifier = Modifier.fillMaxSize(), - contentAlignment = Alignment.Center - ) { - CircularProgressIndicator(color = GrayPrimary) - } + GameDetailsLoadingScreen() } is ApiResponse.Error -> { - GameDetailsLoadingScreen() + ErrorState(gameDetailsViewModel::onRefresh, "Failed to load game details") } is ApiResponse.Success -> { @@ -98,7 +95,6 @@ fun GameDetailsScreen( } } } - } @Composable