diff --git a/core/designsystem/src/main/java/com/eshc/goonersapp/core/designsystem/component/ElevatedCard.kt b/core/designsystem/src/main/java/com/eshc/goonersapp/core/designsystem/component/ElevatedCard.kt index 7e249675..81de09bb 100644 --- a/core/designsystem/src/main/java/com/eshc/goonersapp/core/designsystem/component/ElevatedCard.kt +++ b/core/designsystem/src/main/java/com/eshc/goonersapp/core/designsystem/component/ElevatedCard.kt @@ -42,4 +42,21 @@ fun GnrElevatedCard( modifier = modifier, content = content ) +} + +@Composable +fun GnrElevatedCard( + colors: CardColors, + radius: Dp, + elevation: Dp, + modifier: Modifier = Modifier, + content: @Composable ColumnScope.() -> Unit +) { + GnrElevatedCard( + shape = RoundedCornerShape(radius), + colors = colors, + elevation = CardDefaults.elevatedCardElevation(elevation), + modifier = modifier, + content = content + ) } \ No newline at end of file diff --git a/core/designsystem/src/main/java/com/eshc/goonersapp/core/designsystem/component/MatchLeagueInfo.kt b/core/designsystem/src/main/java/com/eshc/goonersapp/core/designsystem/component/MatchLeagueInfo.kt index bc53744b..8a700373 100644 --- a/core/designsystem/src/main/java/com/eshc/goonersapp/core/designsystem/component/MatchLeagueInfo.kt +++ b/core/designsystem/src/main/java/com/eshc/goonersapp/core/designsystem/component/MatchLeagueInfo.kt @@ -61,7 +61,7 @@ fun MatchLeagueInfo( Text( text = competitionName, color = ColorFF181818, - style = GnrTypography.descriptionSemiBold + style = GnrTypography.descriptionMedium ) } } \ No newline at end of file diff --git a/core/domain/src/main/java/com/eshc/goonersapp/core/domain/model/season/Rank.kt b/core/domain/src/main/java/com/eshc/goonersapp/core/domain/model/season/Rank.kt index d6f8a8d1..c4e414f5 100644 --- a/core/domain/src/main/java/com/eshc/goonersapp/core/domain/model/season/Rank.kt +++ b/core/domain/src/main/java/com/eshc/goonersapp/core/domain/model/season/Rank.kt @@ -11,4 +11,6 @@ data class Rank( val teamId: Int, val teamName: String, val shortCode: String -) \ No newline at end of file +) { + fun getTotalGames(): Int = wins + draw + loss +} \ No newline at end of file diff --git a/core/domain/src/main/java/com/eshc/goonersapp/core/domain/usecase/season/GetPreviewRankListByTeamAndSeasonUseCase.kt b/core/domain/src/main/java/com/eshc/goonersapp/core/domain/usecase/season/GetPreviewRankListByTeamAndSeasonUseCase.kt index 2851aad5..69c4bc6c 100644 --- a/core/domain/src/main/java/com/eshc/goonersapp/core/domain/usecase/season/GetPreviewRankListByTeamAndSeasonUseCase.kt +++ b/core/domain/src/main/java/com/eshc/goonersapp/core/domain/usecase/season/GetPreviewRankListByTeamAndSeasonUseCase.kt @@ -10,8 +10,8 @@ class GetPreviewRankListByTeamAndSeasonUseCase @Inject constructor( private val seasonRepository: SeasonRepository ) { operator fun invoke( - teamId: Int, - seasonId: Int + seasonId: Int, + teamId: Int = 19 ): Flow>> = seasonRepository.getPreviewRankListByTeamAndSeason( teamId = teamId, seasonId = seasonId diff --git a/core/network/src/main/java/com/eshc/goonersapp/core/network/di/NetworkDataSourceModule.kt b/core/network/src/main/java/com/eshc/goonersapp/core/network/di/NetworkDataSourceModule.kt index 02da3b54..d1c00f92 100644 --- a/core/network/src/main/java/com/eshc/goonersapp/core/network/di/NetworkDataSourceModule.kt +++ b/core/network/src/main/java/com/eshc/goonersapp/core/network/di/NetworkDataSourceModule.kt @@ -3,11 +3,13 @@ package com.eshc.goonersapp.core.network.di import com.eshc.goonersapp.core.network.ChatNetworkDataSource import com.eshc.goonersapp.core.network.MatchNetworkDataSource import com.eshc.goonersapp.core.network.PlayerNetworkDataSource +import com.eshc.goonersapp.core.network.SeasonNetworkDataSource import com.eshc.goonersapp.core.network.TeamNetworkDataSource import com.eshc.goonersapp.core.network.UserNetworkDataSource import com.eshc.goonersapp.core.network.remote.ChatNetworkDataSourceImpl import com.eshc.goonersapp.core.network.remote.MatchNetworkDataSourceImpl import com.eshc.goonersapp.core.network.remote.PlayerNetworkDataSourceImpl +import com.eshc.goonersapp.core.network.remote.SeasonNetworkDataSourceImpl import com.eshc.goonersapp.core.network.remote.TeamNetworkDataSourceImpl import com.eshc.goonersapp.core.network.remote.UserNetworkDataSourceImpl import dagger.Binds @@ -43,4 +45,9 @@ abstract class NetworkDataSourceModule { abstract fun bindUserNetworkDataSource( gnrRemoteDataSource: UserNetworkDataSourceImpl ): UserNetworkDataSource + + @Binds + abstract fun bindSeasonNetworkDataSource( + gnrRemoteSeason: SeasonNetworkDataSourceImpl + ): SeasonNetworkDataSource } \ No newline at end of file diff --git a/core/network/src/main/java/com/eshc/goonersapp/core/network/di/NetworkModule.kt b/core/network/src/main/java/com/eshc/goonersapp/core/network/di/NetworkModule.kt index 408bf8df..4803a30d 100644 --- a/core/network/src/main/java/com/eshc/goonersapp/core/network/di/NetworkModule.kt +++ b/core/network/src/main/java/com/eshc/goonersapp/core/network/di/NetworkModule.kt @@ -3,6 +3,7 @@ package com.eshc.goonersapp.core.network.di import com.eshc.goonersapp.core.network.BuildConfig import com.eshc.goonersapp.core.network.api.MatchNetworkService import com.eshc.goonersapp.core.network.api.PlayerNetworkService +import com.eshc.goonersapp.core.network.api.SeasonNetworkService import com.eshc.goonersapp.core.network.api.TeamNetworkService import com.eshc.goonersapp.core.network.api.UserNetworkService import com.jakewharton.retrofit2.converter.kotlinx.serialization.asConverterFactory @@ -82,6 +83,13 @@ object NetworkModule { UserNetworkService::class.java ) + @Singleton + @Provides + fun provideSeasonNetworkService(retrofit: Retrofit): SeasonNetworkService = + retrofit.create( + SeasonNetworkService::class.java + ) + @Singleton @Provides fun provideChatSocket(): Socket = IO.socket( diff --git a/feature/home/src/main/java/com/eshc/goonersapp/feature/home/HomeScreen.kt b/feature/home/src/main/java/com/eshc/goonersapp/feature/home/HomeScreen.kt index 2a51ebe7..ecf5442f 100644 --- a/feature/home/src/main/java/com/eshc/goonersapp/feature/home/HomeScreen.kt +++ b/feature/home/src/main/java/com/eshc/goonersapp/feature/home/HomeScreen.kt @@ -3,6 +3,7 @@ package com.eshc.goonersapp.feature.home import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.PaddingValues +import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.height import androidx.compose.foundation.layout.padding @@ -27,6 +28,7 @@ import com.eshc.goonersapp.core.designsystem.theme.GnrTypography import com.eshc.goonersapp.feature.home.component.DashboardCard import com.eshc.goonersapp.feature.home.component.RecentlyMatchCard import com.eshc.goonersapp.feature.home.component.UpcomingMatchCard +import com.eshc.goonersapp.feature.home.state.DashBoardUiState import com.eshc.goonersapp.feature.home.state.RecentlyResultUiState import com.eshc.goonersapp.feature.home.state.UpcomingMatchesUiState @@ -37,6 +39,7 @@ fun HomeRoute( viewModel: HomeViewModel = hiltViewModel(), onShowSnackbar : (String) -> Unit ) { + val dashBoardUiState by viewModel.teamDashBoardUiStateFlow.collectAsStateWithLifecycle() val upcomingMatchesUiState by viewModel.upcomingMatchesUiStateFlow.collectAsStateWithLifecycle() val recentlyResultUiState by viewModel.recentlyResultUiStateFlow.collectAsStateWithLifecycle() @@ -46,6 +49,7 @@ fun HomeRoute( ) { padding -> HomeScreen( modifier = Modifier.padding(padding), + dashBoardUiState = dashBoardUiState, upcomingMatchesUiState = upcomingMatchesUiState, recentlyResultUiState = recentlyResultUiState ) @@ -54,28 +58,71 @@ fun HomeRoute( @Composable fun HomeScreen( - modifier: Modifier = Modifier, + dashBoardUiState: DashBoardUiState, upcomingMatchesUiState: UpcomingMatchesUiState, - recentlyResultUiState: RecentlyResultUiState + recentlyResultUiState: RecentlyResultUiState, + modifier: Modifier = Modifier, ) { LazyColumn( - modifier = modifier, + modifier = modifier.fillMaxSize(), contentPadding = PaddingValues(top = 30.dp) ) { item { Text( text = "Team Dashboard", - modifier = Modifier.padding(start = 8.dp), + modifier = Modifier.padding(start = 15.dp), color = ColorFF181818, style = GnrTypography.subtitleMedium ) - DashboardCard() + when (dashBoardUiState) { + is DashBoardUiState.Loading -> { + Box( + modifier = Modifier + .fillMaxWidth() + .height(208.dp), + contentAlignment = Alignment.Center + ) { + CircularProgressIndicator() + } + } + is DashBoardUiState.Success -> { + DashboardCard(dashBoardUiState.data) + } + is DashBoardUiState.Failed -> { + Box( + modifier = Modifier + .fillMaxWidth() + .height(208.dp), + contentAlignment = Alignment.Center + ) { + Text( + text = "Rank List cannot be loaded.", + style = MaterialTheme.typography.titleSmall, + color = Color.LightGray, + ) + } + } + is DashBoardUiState.Error -> { + Box( + modifier = Modifier + .fillMaxWidth() + .height(208.dp), + contentAlignment = Alignment.Center + ) { + Text( + text = "Error! ${dashBoardUiState.throwable}", + style = MaterialTheme.typography.titleSmall, + color = Color.LightGray, + ) + } + } + } } item { Text( text = "Upcoming Matches", - modifier = Modifier.padding(start = 8.dp), + modifier = Modifier.padding(start = 15.dp, top = 55.dp), color = ColorFF181818, style = GnrTypography.subtitleMedium, ) @@ -92,7 +139,7 @@ fun HomeScreen( } is UpcomingMatchesUiState.Success -> { LazyRow( - contentPadding = PaddingValues(horizontal = 8.dp, vertical = 12.dp), + contentPadding = PaddingValues(horizontal = 15.dp, vertical = 15.dp), horizontalArrangement = Arrangement.spacedBy(12.dp) ) { items( @@ -104,7 +151,7 @@ fun HomeScreen( homeShortName = matches.homeTeamNickname, awayUrl = matches.awayTeamImageUrl, awayShortName = matches.awayTeamNickname, - time = DateUtil.getYearAndMonthAndDateAndTimeString(matches.matchDate), + time = DateUtil.getYearAndMonthAndDateAndDayAndTimeString(matches.matchDate), location = if (matches.stadiumName == "null") "" else matches.stadiumName, competitionUrl = matches.leagueImageUrl, competitionName = "Premier League" @@ -145,7 +192,7 @@ fun HomeScreen( item { Text( - modifier = Modifier.padding(start = 8.dp), + modifier = Modifier.padding(start = 15.dp, top = 55.dp), text = "Recently Result", style = GnrTypography.subtitleMedium, color = Color.Black, @@ -166,7 +213,7 @@ fun HomeScreen( RecentlyMatchCard( competitionUrl = match.match.leagueImageUrl, competitionName = "Premier league", - time = DateUtil.getYearAndMonthAndDateAndTimeString(match.match.matchDate), + time = DateUtil.getYearAndMonthAndDateAndDayAndTimeString(match.match.matchDate), location = match.match.stadiumName, homeId = match.match.homeTeamId, homeUrl = match.match.homeTeamImageUrl, diff --git a/feature/home/src/main/java/com/eshc/goonersapp/feature/home/HomeViewModel.kt b/feature/home/src/main/java/com/eshc/goonersapp/feature/home/HomeViewModel.kt index dae9b302..dba0785e 100644 --- a/feature/home/src/main/java/com/eshc/goonersapp/feature/home/HomeViewModel.kt +++ b/feature/home/src/main/java/com/eshc/goonersapp/feature/home/HomeViewModel.kt @@ -5,6 +5,8 @@ import androidx.lifecycle.viewModelScope import com.eshc.goonersapp.core.domain.model.DataResult import com.eshc.goonersapp.core.domain.usecase.match.GetRecentlyMatchUseCase import com.eshc.goonersapp.core.domain.usecase.match.GetUpcomingMatchesUseCase +import com.eshc.goonersapp.core.domain.usecase.season.GetPreviewRankListByTeamAndSeasonUseCase +import com.eshc.goonersapp.feature.home.state.DashBoardUiState import com.eshc.goonersapp.feature.home.state.RecentlyResultUiState import com.eshc.goonersapp.feature.home.state.UpcomingMatchesUiState import dagger.hilt.android.lifecycle.HiltViewModel @@ -17,9 +19,27 @@ import javax.inject.Inject @HiltViewModel class HomeViewModel @Inject constructor( + getPreviewRankListByTeamAndSeasonUseCase: GetPreviewRankListByTeamAndSeasonUseCase, getUpcomingMatchesUseCase: GetUpcomingMatchesUseCase, getRecentlyMatchUseCase: GetRecentlyMatchUseCase ) : ViewModel() { + val teamDashBoardUiStateFlow: StateFlow = + getPreviewRankListByTeamAndSeasonUseCase( + seasonId = 21646 + ).catch { exception -> + DashBoardUiState.Error(exception.message) + }.map { result -> + when (result) { + is DataResult.Success -> DashBoardUiState.Success(result.data) + is DataResult.Failure -> DashBoardUiState.Failed(result.message) + } + }.stateIn( + scope = viewModelScope, + initialValue = DashBoardUiState.Loading, + started = SharingStarted.Eagerly + ) + + val upcomingMatchesUiStateFlow: StateFlow = getUpcomingMatchesUseCase() .catch { exception -> diff --git a/feature/home/src/main/java/com/eshc/goonersapp/feature/home/component/DashboardCard.kt b/feature/home/src/main/java/com/eshc/goonersapp/feature/home/component/DashboardCard.kt index 0ea9f5ba..4d78fc39 100644 --- a/feature/home/src/main/java/com/eshc/goonersapp/feature/home/component/DashboardCard.kt +++ b/feature/home/src/main/java/com/eshc/goonersapp/feature/home/component/DashboardCard.kt @@ -19,6 +19,7 @@ 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 coil.compose.AsyncImage import com.eshc.goonersapp.core.designsystem.IconPack @@ -32,25 +33,30 @@ import com.eshc.goonersapp.core.designsystem.theme.ColorFFDCDCDC import com.eshc.goonersapp.core.designsystem.theme.ColorFFF5F5F5 import com.eshc.goonersapp.core.designsystem.theme.ColorFFFFFFFF import com.eshc.goonersapp.core.designsystem.theme.GnrTypography +import com.eshc.goonersapp.core.domain.model.season.Rank @Composable -fun DashboardCard(modifier: Modifier = Modifier) { +fun DashboardCard( + previewRankList: List, + modifier: Modifier = Modifier +) { GnrElevatedCard( colors = CardDefaults.cardColors(containerColor = ColorFFFFFFFF), radius = 10.dp, modifier = modifier - .padding(horizontal = 8.dp, vertical = 12.dp) + .padding(horizontal = 15.dp, vertical = 15.dp) .fillMaxWidth() - .height(IntrinsicSize.Max) + .height(IntrinsicSize.Max), + elevation = 6.dp ) { Column( modifier = Modifier .fillMaxWidth() .wrapContentHeight() - .padding(top = 12.dp, start = 12.dp, end = 12.dp, bottom = 5.dp) + .padding(top = 20.dp, start = 20.dp, end = 20.dp, bottom = 7.dp) ) { LeagueDashboardTitle() - Spacer(modifier = Modifier.size(5.dp)) + Spacer(modifier = Modifier.size(7.dp)) HorizontalDivider( modifier = modifier.fillMaxWidth(), color = ColorFFDCDCDC, @@ -63,56 +69,22 @@ fun DashboardCard(modifier: Modifier = Modifier) { modifier = Modifier .fillMaxWidth() .wrapContentHeight() - .padding(start = 12.dp, end = 12.dp, top = 4.dp, bottom = 12.dp) + .padding(start = 20.dp, end = 20.dp, bottom = 20.dp) ) { - LeagueDashboardItem( - rank = 1, - teamId = 20, - teamImgUrl = "", - teamShortName = "MCI", - totalGames = 31, - wins = 22, - draws = 5, - losses = 4, - goalDiff = 51, - points = 71 - ) - LeagueDashboardItem( - rank = 2, - teamId = 19, - teamImgUrl = "", - teamShortName = "ARS", - totalGames = 31, - wins = 21, - draws = 8, - losses = 2, - goalDiff = 42, - points = 71 - ) - LeagueDashboardItem( - rank = 3, - teamId = 100, - teamImgUrl = "", - teamShortName = "CHE", - totalGames = 30, - wins = 18, - draws = 7, - losses = 3, - goalDiff = 40, - points = 70 - ) - LeagueDashboardItem( - rank = 4, - teamId = 201, - teamImgUrl = "", - teamShortName = "TOT", - totalGames = 32, - wins = 18, - draws = 6, - losses = 7, - goalDiff = 20, - points = 60 - ) + previewRankList.forEach { rank -> + LeagueDashboardItem( + rank = rank.position, + teamId = rank.teamId, + teamImgUrl = "", + teamShortName = rank.shortCode, + totalGames = rank.getTotalGames(), + wins = rank.wins, + draws = rank.draw, + losses = rank.loss, + goalDiff = rank.goalDifference, + points = rank.points + ) + } } } } @@ -153,11 +125,14 @@ fun LeagueDashboardTitle(modifier: Modifier = Modifier) { @Composable fun LeagueDashboardRow(modifier: Modifier = Modifier) { Row( - modifier = modifier.fillMaxWidth().padding(horizontal = 15.dp), + modifier = modifier + .fillMaxWidth() + .padding(start = 10.dp, end = 17.dp), verticalAlignment = Alignment.CenterVertically ) { Text( text = "Pos", + textAlign = TextAlign.Center, modifier = modifier.weight(1f), color = ColorFF9E9E9E, style = GnrTypography.descriptionMedium @@ -232,11 +207,12 @@ fun LeagueDashboardItem( Row( modifier = modifier .fillMaxWidth() - .padding(horizontal = 15.dp, vertical = 2.dp), + .padding(top = 2.dp, bottom = 2.dp, end = 17.dp, start = 10.dp), verticalAlignment = Alignment.CenterVertically ) { Text( text = "$rank", + textAlign = TextAlign.Center, modifier = modifier.weight(1f), color = ColorFF9E9E9E, style = GnrTypography.body2Medium @@ -317,4 +293,5 @@ fun LeagueDashBoardClubInfo( style = GnrTypography.body2Medium ) } -} \ No newline at end of file +} + diff --git a/feature/home/src/main/java/com/eshc/goonersapp/feature/home/component/MatchCard.kt b/feature/home/src/main/java/com/eshc/goonersapp/feature/home/component/MatchCard.kt deleted file mode 100644 index f50b7af3..00000000 --- a/feature/home/src/main/java/com/eshc/goonersapp/feature/home/component/MatchCard.kt +++ /dev/null @@ -1,390 +0,0 @@ -package com.eshc.goonersapp.feature.home.component - -import androidx.compose.foundation.BorderStroke -import androidx.compose.foundation.layout.Arrangement -import androidx.compose.foundation.layout.Column -import androidx.compose.foundation.layout.IntrinsicSize -import androidx.compose.foundation.layout.Row -import androidx.compose.foundation.layout.Spacer -import androidx.compose.foundation.layout.fillMaxSize -import androidx.compose.foundation.layout.fillMaxWidth -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.layout.wrapContentHeight -import androidx.compose.foundation.layout.wrapContentSize -import androidx.compose.foundation.layout.wrapContentWidth -import androidx.compose.foundation.shape.RoundedCornerShape -import androidx.compose.material3.Card -import androidx.compose.material3.CardDefaults -import androidx.compose.material3.HorizontalDivider -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.SpanStyle -import androidx.compose.ui.text.buildAnnotatedString -import androidx.compose.ui.text.style.LineBreak -import androidx.compose.ui.text.style.TextOverflow -import androidx.compose.ui.text.withStyle -import androidx.compose.ui.unit.dp -import coil.compose.AsyncImage -import com.eshc.goonersapp.core.designsystem.component.GnrElevatedCard -import com.eshc.goonersapp.core.designsystem.component.MatchLeagueInfo -import com.eshc.goonersapp.core.designsystem.ext.gnrElevatedCardBorder -import com.eshc.goonersapp.core.designsystem.theme.ColorFF10358A -import com.eshc.goonersapp.core.designsystem.theme.ColorFF181818 -import com.eshc.goonersapp.core.designsystem.theme.ColorFF4C68A7 -import com.eshc.goonersapp.core.designsystem.theme.ColorFF9E9E9E -import com.eshc.goonersapp.core.designsystem.theme.ColorFFC3CDE2 -import com.eshc.goonersapp.core.designsystem.theme.ColorFFDCDCDC -import com.eshc.goonersapp.core.designsystem.theme.ColorFFE6EDFC -import com.eshc.goonersapp.core.designsystem.theme.ColorFFF7F9FF -import com.eshc.goonersapp.core.designsystem.theme.GnrTypography -import com.eshc.goonersapp.core.domain.model.match.MatchDetail -import com.eshc.goonersapp.core.domain.model.match.getScoreHistoryList - -@Composable -fun RecentlyMatchCard( - competitionUrl: String, - competitionName: String, - time: String, - location: String, - homeId: Int, - homeUrl: String, - homeShortName: String, - homeScore: String, - awayId: Int, - awayUrl: String, - awayShortName: String, - awayScore: String, - matchHistory: List, - modifier: Modifier = Modifier -) { - val annotatedScore = buildAnnotatedString { - withStyle(style = SpanStyle(ColorFF10358A)) { append("$homeScore ") } - withStyle(style = SpanStyle(ColorFFC3CDE2)) { append(":") } - withStyle(style = SpanStyle(ColorFF10358A)) { append(" $awayScore") } - } - - GnrElevatedCard( - modifier = modifier - .padding(horizontal = 8.dp, vertical = 12.dp) - .fillMaxWidth() - .height(IntrinsicSize.Max) - .gnrElevatedCardBorder(10.dp), - colors = CardDefaults.cardColors(containerColor = Color.White), - radius = 10.dp - ) { - Column( - modifier = modifier - .fillMaxWidth() - .weight(1f) - .padding(10.dp), - horizontalAlignment = Alignment.CenterHorizontally - ) { - Row( - modifier = modifier.fillMaxWidth().padding(bottom = 15.dp), - verticalAlignment = Alignment.CenterVertically, - horizontalArrangement = Arrangement.SpaceBetween - ) { - MatchLeagueInfo( - logoSize = 25.dp, - logoPadding = 4.dp, - competitionUrl = competitionUrl, - competitionName = competitionName, - verticalAlignment = Alignment.CenterVertically, - ) - Column( - horizontalAlignment = Alignment.End - ) { - Text( - text = time, - color = ColorFF4C68A7, - style = GnrTypography.descriptionRegular - ) - Spacer(modifier = modifier.size(5.dp)) - Text( - text = location, - color = ColorFF9E9E9E, - style = GnrTypography.descriptionMedium.copy( - lineBreak = LineBreak( - strategy = LineBreak.Strategy.Balanced, - strictness = LineBreak.Strictness.Strict, - wordBreak = LineBreak.WordBreak.Default - ) - ) - ) - } - } - Row( - modifier = modifier.fillMaxWidth(), - horizontalArrangement = Arrangement.SpaceEvenly, - verticalAlignment = Alignment.CenterVertically, - content = { - RecentlyTeamInfo( - teamImgUrl = homeUrl, - teamShortName = homeShortName, - teamSide = "Home" - ) - Text( - text = annotatedScore, - style = GnrTypography.heading1Bold - ) - RecentlyTeamInfo( - teamImgUrl = awayUrl, - teamShortName = awayShortName, - teamSide = "Away" - ) - } - ) - Spacer(modifier = modifier.size(20.dp)) - Row( - modifier = modifier.fillMaxWidth(), - horizontalArrangement = Arrangement.SpaceBetween, - content = { - RecentlyGoalHistory( - teamId = homeId, - matchHistory = matchHistory, - modifier = modifier - .wrapContentHeight() - .weight(1f) - ) - RecentlyGoalHistory( - teamId = awayId, - matchHistory = matchHistory, - modifier = modifier - .wrapContentHeight() - .weight(1f) - ) - } - ) - } - } -} - -@Composable -fun UpcomingMatchCard( - modifier: Modifier = Modifier, - homeUrl: String, - homeShortName: String, - awayUrl: String, - awayShortName: String, - time: String, - location: String, - competitionUrl: String, - competitionName: String -) { - GnrElevatedCard( - colors = CardDefaults.cardColors(containerColor = Color.White), - radius = 10.dp, - modifier = modifier - .width(263.dp) - .height(131.dp) - .gnrElevatedCardBorder(10.dp) - ) { - Column( - modifier = modifier - .fillMaxSize() - .padding(10.dp), - horizontalAlignment = Alignment.CenterHorizontally - ) { - MatchLeagueInfo( - logoSize = 22.dp, - logoPadding = 4.dp, - competitionUrl = competitionUrl, - competitionName = competitionName, - modifier = modifier.fillMaxWidth() - ) - UpcomingMatchInfo( - homeUrl = homeUrl, - homeShortName = homeShortName, - awayUrl = awayUrl, - awayShortName = awayShortName, - modifier = modifier - .fillMaxWidth() - .weight(1f) - ) - UpcomingMatchDateInfo( - time = time, - location = location, - modifier = modifier.fillMaxWidth() - ) - } - } -} - -@Composable -fun RecentlyTeamInfo( - teamImgUrl: String, - teamShortName: String, - teamSide: String, - modifier: Modifier = Modifier -) { - Column( - modifier = modifier.wrapContentSize(), - horizontalAlignment = Alignment.CenterHorizontally - ) { - AsyncImage( - model = teamImgUrl, - contentDescription = "Home Team Logo", - modifier = modifier.size(50.dp) - ) - Text( - text = teamShortName, - color = ColorFF181818, - style = GnrTypography.body1SemiBold - ) - Text( - text = teamSide, - color = ColorFF9E9E9E, - style = GnrTypography.descriptionRegular - ) - } -} - -@Composable -fun RecentlyGoalHistory( - teamId: Int, - matchHistory: List, - modifier: Modifier = Modifier -) { - val goalHistory = matchHistory.getScoreHistoryList(teamId) - - Column( - modifier = modifier - .wrapContentSize() - .padding(horizontal = 10.dp), - horizontalAlignment = Alignment.Start, - content = { - goalHistory.forEach { history -> - Spacer(modifier = Modifier.size(3.dp)) - Text( - text = history.scoringRecordText, - modifier = Modifier.padding(horizontal = 10.dp), - overflow = TextOverflow.Ellipsis, - maxLines = 1, - style = GnrTypography.descriptionMedium - ) - Spacer(modifier = Modifier.size(1.5.dp)) - HorizontalDivider( - modifier = Modifier.wrapContentWidth(), - thickness = 0.5.dp, - color = ColorFFDCDCDC - ) - } - } - ) -} - -@Composable -fun UpcomingMatchInfo( - homeUrl: String, - homeShortName: String, - awayUrl: String, - awayShortName: String, - modifier: Modifier = Modifier -) { - Row( - modifier = modifier, - horizontalArrangement = Arrangement.SpaceBetween, - verticalAlignment = Alignment.CenterVertically, - content = { - UpcomingHomeTeamInfo(homeUrl = homeUrl, homeShortName = homeShortName) - Text(text = "VS", color = ColorFF10358A, style = GnrTypography.subtitleMedium) - UpcomingAwayTeamInfo(awayUrl = awayUrl, awayShortName = awayShortName) - } - ) -} - -@Composable -fun UpcomingHomeTeamInfo( - homeUrl: String, - homeShortName: String, - modifier: Modifier = Modifier -) { - Row( - verticalAlignment = Alignment.CenterVertically, - content = { - AsyncImage( - model = homeUrl, - modifier = modifier.size(30.dp), - contentDescription = "Home Team Logo" - ) - Spacer(modifier = modifier.size(7.dp)) - Text( - text = homeShortName, - color = ColorFF181818, - style = GnrTypography.subtitleMedium - ) - } - ) -} - -@Composable -fun UpcomingAwayTeamInfo( - awayUrl: String, - awayShortName: String, - modifier: Modifier = Modifier -) { - Row( - verticalAlignment = Alignment.CenterVertically, - content = { - Text( - text = awayShortName, - style = GnrTypography.subtitleMedium - ) - Spacer(modifier = modifier.size(7.dp)) - AsyncImage( - model = awayUrl, - modifier = modifier.size(30.dp), - contentDescription = "Away Team Logo" - ) - } - ) -} - -@Composable -fun UpcomingMatchDateInfo( - time: String, - location: String, - modifier: Modifier = Modifier -) { - Card( - modifier = modifier.wrapContentHeight(), - shape = RoundedCornerShape(50.dp), - colors = CardDefaults.cardColors( - containerColor = ColorFFF7F9FF, - contentColor = ColorFF4C68A7 - ), - border = BorderStroke( - width = 0.5.dp, - color = ColorFFE6EDFC - ) - ) { - Row( - modifier = modifier.padding(vertical = 3.dp, horizontal = 15.dp), - verticalAlignment = Alignment.CenterVertically, - horizontalArrangement = Arrangement.Center, - content = { - Text( - text = time, - color = ColorFF4C68A7, - overflow = TextOverflow.Ellipsis, - maxLines = 1, - style = GnrTypography.body2Medium - ) - Spacer(modifier = Modifier.size(5.dp)) - Text( - text = location, - color = ColorFF4C68A7, - overflow = TextOverflow.Ellipsis, - maxLines = 1, - style = GnrTypography.body2Medium - ) - } - ) - } -} \ No newline at end of file diff --git a/feature/home/src/main/java/com/eshc/goonersapp/feature/home/component/RecentlyMatchCard.kt b/feature/home/src/main/java/com/eshc/goonersapp/feature/home/component/RecentlyMatchCard.kt new file mode 100644 index 00000000..1a3661a6 --- /dev/null +++ b/feature/home/src/main/java/com/eshc/goonersapp/feature/home/component/RecentlyMatchCard.kt @@ -0,0 +1,290 @@ +package com.eshc.goonersapp.feature.home.component + +import androidx.compose.foundation.layout.Arrangement +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.IntrinsicSize +import androidx.compose.foundation.layout.Row +import androidx.compose.foundation.layout.Spacer +import androidx.compose.foundation.layout.fillMaxWidth +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.layout.wrapContentHeight +import androidx.compose.foundation.layout.wrapContentSize +import androidx.compose.foundation.layout.wrapContentWidth +import androidx.compose.material3.CardDefaults +import androidx.compose.material3.HorizontalDivider +import androidx.compose.material3.Text +import androidx.compose.runtime.Composable +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.text.SpanStyle +import androidx.compose.ui.text.buildAnnotatedString +import androidx.compose.ui.text.style.LineBreak +import androidx.compose.ui.text.style.TextAlign +import androidx.compose.ui.text.style.TextOverflow +import androidx.compose.ui.text.withStyle +import androidx.compose.ui.tooling.preview.Preview +import androidx.compose.ui.unit.dp +import coil.compose.AsyncImage +import com.eshc.goonersapp.core.common.util.DateUtil +import com.eshc.goonersapp.core.designsystem.component.GnrElevatedCard +import com.eshc.goonersapp.core.designsystem.component.MatchLeagueInfo +import com.eshc.goonersapp.core.designsystem.ext.gnrElevatedCardBorder +import com.eshc.goonersapp.core.designsystem.theme.ColorFF10358A +import com.eshc.goonersapp.core.designsystem.theme.ColorFF181818 +import com.eshc.goonersapp.core.designsystem.theme.ColorFF4C68A7 +import com.eshc.goonersapp.core.designsystem.theme.ColorFF9E9E9E +import com.eshc.goonersapp.core.designsystem.theme.ColorFFC3CDE2 +import com.eshc.goonersapp.core.designsystem.theme.ColorFFDCDCDC +import com.eshc.goonersapp.core.designsystem.theme.ColorFFFFFFFF +import com.eshc.goonersapp.core.designsystem.theme.GnrTypography +import com.eshc.goonersapp.core.domain.model.match.MatchDetail +import com.eshc.goonersapp.core.domain.model.match.MatchDetailType +import com.eshc.goonersapp.core.domain.model.match.getScoreHistoryList + +@Composable +fun RecentlyMatchCard( + competitionUrl: String, + competitionName: String, + time: String, + location: String, + homeId: Int, + homeUrl: String, + homeShortName: String, + homeScore: String, + awayId: Int, + awayUrl: String, + awayShortName: String, + awayScore: String, + matchHistory: List, + modifier: Modifier = Modifier +) { + val annotatedScore = buildAnnotatedString { + withStyle(style = SpanStyle(ColorFF10358A)) { append("$homeScore ") } + withStyle(style = SpanStyle(ColorFFC3CDE2)) { append(":") } + withStyle(style = SpanStyle(ColorFF10358A)) { append(" $awayScore") } + } + + GnrElevatedCard( + modifier = modifier + .padding(horizontal = 15.dp, vertical = 15.dp) + .fillMaxWidth() + .height(IntrinsicSize.Max) + .gnrElevatedCardBorder(10.dp), + colors = CardDefaults.cardColors(containerColor = ColorFFFFFFFF), + elevation = 6.dp, + radius = 10.dp + ) { + Column( + modifier = modifier + .fillMaxWidth() + .weight(1f) + .padding(20.dp), + horizontalAlignment = Alignment.CenterHorizontally + ) { + Row( + modifier = modifier + .fillMaxWidth() + .padding(bottom = 15.dp), + verticalAlignment = Alignment.Top, + horizontalArrangement = Arrangement.SpaceBetween + ) { + MatchLeagueInfo( + logoSize = 25.dp, + logoPadding = 4.dp, + competitionUrl = competitionUrl, + competitionName = competitionName, + verticalAlignment = Alignment.CenterVertically, + ) + RecentlyMatchDateStadiumInfo( + matchDate = time, + stadiumName = location + ) + } + Row( + modifier = modifier.fillMaxWidth(), + horizontalArrangement = Arrangement.SpaceEvenly, + verticalAlignment = Alignment.CenterVertically + ) { + RecentlyTeamInfo( + teamImgUrl = homeUrl, + teamShortName = homeShortName, + teamSide = "Home" + ) + Text( + text = annotatedScore, + style = GnrTypography.heading1Bold + ) + RecentlyTeamInfo( + teamImgUrl = awayUrl, + teamShortName = awayShortName, + teamSide = "Away" + ) + } + Spacer(modifier = modifier.size(20.dp)) + Row( + modifier = modifier.fillMaxWidth(), + horizontalArrangement = Arrangement.SpaceBetween + ) { + RecentlyGoalHistory( + teamId = homeId, + matchHistory = matchHistory, + modifier = modifier + .wrapContentHeight() + .weight(1f), + alignment = Alignment.Start + ) + Spacer(modifier = Modifier.width(20.dp)) + RecentlyGoalHistory( + teamId = awayId, + matchHistory = matchHistory, + modifier = modifier + .wrapContentHeight() + .weight(1f), + alignment = Alignment.Start + ) + } + } + } +} + + + +@Composable +fun RecentlyTeamInfo( + teamImgUrl: String, + teamShortName: String, + teamSide: String, + modifier: Modifier = Modifier +) { + Column( + modifier = modifier.wrapContentSize(), + horizontalAlignment = Alignment.CenterHorizontally + ) { + AsyncImage( + model = teamImgUrl, + contentDescription = "Home Team Logo", + modifier = modifier.size(50.dp) + ) + Text( + text = teamShortName, + color = ColorFF181818, + style = GnrTypography.body1SemiBold + ) + Text( + text = teamSide, + color = ColorFF9E9E9E, + style = GnrTypography.descriptionRegular + ) + } +} + +@Composable +fun RecentlyMatchDateStadiumInfo( + matchDate: String, + stadiumName: String, + modifier: Modifier = Modifier +) { + Column(horizontalAlignment = Alignment.End) { + Text( + text = matchDate, + color = ColorFF4C68A7, + style = GnrTypography.body2Medium + ) + Spacer(modifier = modifier.size(2.dp)) + Text( + text = stadiumName, + color = ColorFF9E9E9E, + textAlign = TextAlign.End, + style = GnrTypography.descriptionRegular.copy( + lineBreak = LineBreak( + strategy = LineBreak.Strategy.Balanced, + strictness = LineBreak.Strictness.Strict, + wordBreak = LineBreak.WordBreak.Default + ) + ) + ) + } +} + +@Composable +fun RecentlyGoalHistory( + teamId: Int, + matchHistory: List, + alignment: Alignment.Horizontal, + modifier: Modifier = Modifier +) { + val goalHistory = matchHistory.getScoreHistoryList(teamId) + + Column( + modifier = modifier.wrapContentSize(), + horizontalAlignment = alignment, + content = { + goalHistory.forEach { history -> + Text( + text = history.scoringRecordText, + modifier = Modifier.padding(horizontal = 10.dp, vertical = 4.dp), + overflow = TextOverflow.Ellipsis, + maxLines = 1, + style = GnrTypography.descriptionMedium + ) + HorizontalDivider( + modifier = Modifier.wrapContentWidth(), + thickness = 0.5.dp, + color = ColorFFDCDCDC + ) + } + } + ) +} + +@Preview +@Composable +fun RecentlyCard() { + RecentlyMatchCard( + competitionUrl = "", + competitionName = "Premier League", + time = DateUtil.getYearAndMonthAndDateAndDayAndTimeString("2024-04-06T16:30:00.000Z"), + location = "The American Express Community Stadium", + homeId = 19, + homeUrl = "", + homeShortName = "ARS", + homeScore = "3", + awayId = 236, + awayUrl = "", + awayShortName = "BHA", + awayScore = "1", + matchHistory = listOf( + MatchDetail( + matchId = 222, + matchDetailId = 222, + teamId = 19, + playerId = 2222222, + playerName = "Antony", + minute = 90, + type = MatchDetailType.GOAL, + ), + MatchDetail( + matchId = 222, + matchDetailId = 222, + teamId = 236, + playerId = 2222222, + playerName = "Gabriel Martinelli", + minute = 9, + type = MatchDetailType.OWNGOAL, + ), + MatchDetail( + matchId = 222, + matchDetailId = 222, + teamId = 19, + playerId = 2222222, + playerName = "Antony", + minute = 90, + extraMinute = 5, + type = MatchDetailType.GOAL, + ) + ) + ) +} \ No newline at end of file diff --git a/feature/home/src/main/java/com/eshc/goonersapp/feature/home/component/UpcomingMatchCard.kt b/feature/home/src/main/java/com/eshc/goonersapp/feature/home/component/UpcomingMatchCard.kt new file mode 100644 index 00000000..0e234ee5 --- /dev/null +++ b/feature/home/src/main/java/com/eshc/goonersapp/feature/home/component/UpcomingMatchCard.kt @@ -0,0 +1,194 @@ +package com.eshc.goonersapp.feature.home.component + +import androidx.compose.foundation.layout.Arrangement +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.IntrinsicSize +import androidx.compose.foundation.layout.Row +import androidx.compose.foundation.layout.Spacer +import androidx.compose.foundation.layout.fillMaxSize +import androidx.compose.foundation.layout.fillMaxWidth +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.material3.CardDefaults +import androidx.compose.material3.Text +import androidx.compose.runtime.Composable +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.text.style.TextOverflow +import androidx.compose.ui.tooling.preview.Preview +import androidx.compose.ui.unit.dp +import coil.compose.AsyncImage +import com.eshc.goonersapp.core.designsystem.component.GnrCard +import com.eshc.goonersapp.core.designsystem.component.GnrElevatedCard +import com.eshc.goonersapp.core.designsystem.component.MatchLeagueInfo +import com.eshc.goonersapp.core.designsystem.ext.gnrElevatedCardBorder +import com.eshc.goonersapp.core.designsystem.theme.ColorFF10358A +import com.eshc.goonersapp.core.designsystem.theme.ColorFF181818 +import com.eshc.goonersapp.core.designsystem.theme.ColorFF4C68A7 +import com.eshc.goonersapp.core.designsystem.theme.ColorFFE6EDFC +import com.eshc.goonersapp.core.designsystem.theme.ColorFFFFFFFF +import com.eshc.goonersapp.core.designsystem.theme.GnrTypography + +@Composable +fun UpcomingMatchCard( + homeUrl: String, + homeShortName: String, + awayUrl: String, + awayShortName: String, + time: String, + location: String, + competitionUrl: String, + competitionName: String, + modifier: Modifier = Modifier +) { + GnrElevatedCard( + modifier = modifier + .width(263.dp) + .height(IntrinsicSize.Max) + .gnrElevatedCardBorder(10.dp), + colors = CardDefaults.cardColors(containerColor = ColorFFFFFFFF), + elevation = 6.dp, + radius = 10.dp + ) { + Column( + modifier = modifier + .fillMaxSize() + .padding(15.dp), + horizontalAlignment = Alignment.CenterHorizontally, + ) { + MatchLeagueInfo( + logoSize = 22.dp, + logoPadding = 5.dp, + competitionUrl = competitionUrl, + competitionName = competitionName, + modifier = modifier.fillMaxWidth(), + verticalAlignment = Alignment.CenterVertically + ) + UpcomingMatchInfo( + homeUrl = homeUrl, + homeShortName = homeShortName, + awayUrl = awayUrl, + awayShortName = awayShortName, + modifier = modifier.width(IntrinsicSize.Max).padding(top = 6.dp) + ) + Text( + text = location, + modifier = modifier.padding(top = 12.dp, bottom = 6.dp), + color = ColorFF4C68A7, + style = GnrTypography.descriptionMedium, + maxLines = 1, + overflow = TextOverflow.Ellipsis + ) + UpcomingDateCard(matchDate = time) + } + } +} + +@Composable +fun UpcomingMatchInfo( + homeUrl: String, + homeShortName: String, + awayUrl: String, + awayShortName: String, + modifier: Modifier = Modifier +) { + Row( + modifier = modifier, + horizontalArrangement = Arrangement.SpaceBetween, + verticalAlignment = Alignment.CenterVertically, + content = { + UpcomingHomeTeamInfo(homeUrl = homeUrl, homeShortName = homeShortName) + Text( + text = "VS", + modifier = Modifier.padding(horizontal = 10.dp), + color = ColorFF10358A, + style = GnrTypography.subtitleMedium + ) + UpcomingAwayTeamInfo(awayUrl = awayUrl, awayShortName = awayShortName) + } + ) +} + +@Composable +fun UpcomingHomeTeamInfo( + homeUrl: String, + homeShortName: String, + modifier: Modifier = Modifier +) { + Row( + verticalAlignment = Alignment.CenterVertically, + content = { + Text( + text = homeShortName, + color = ColorFF181818, + style = GnrTypography.subtitleMedium + ) + Spacer(modifier = modifier.size(7.dp)) + AsyncImage( + model = homeUrl, + modifier = modifier.size(25.dp), + contentDescription = "Home Team Logo" + ) + } + ) +} + +@Composable +fun UpcomingAwayTeamInfo( + awayUrl: String, + awayShortName: String, + modifier: Modifier = Modifier +) { + Row( + verticalAlignment = Alignment.CenterVertically, + content = { + AsyncImage( + model = awayUrl, + modifier = modifier.size(25.dp), + contentDescription = "Away Team Logo" + ) + Spacer(modifier = modifier.size(7.dp)) + Text( + text = awayShortName, + style = GnrTypography.subtitleMedium + ) + } + ) +} + +@Composable +fun UpcomingDateCard( + matchDate: String, + modifier: Modifier = Modifier +) { + GnrCard( + shapeRadius = 50.dp, + containerColor = ColorFFE6EDFC + ) { + Text( + text = matchDate, + modifier = modifier.padding(horizontal = 12.dp, vertical = 5.dp), + color = ColorFF10358A, + overflow = TextOverflow.Ellipsis, + maxLines = 1, + style = GnrTypography.body2Medium + ) + } +} + +@Preview +@Composable +fun PreviewUpcomingCard() { + UpcomingMatchCard( + homeUrl = "", + homeShortName = "ARS", + awayUrl = "", + awayShortName = "MCI", + time = "24.05.28 SUN 00:30", + location = "The American Express Community Stadium", + competitionUrl = "", + competitionName = "premier League" + ) +} \ No newline at end of file diff --git a/feature/home/src/main/java/com/eshc/goonersapp/feature/home/state/DashBoardUiState.kt b/feature/home/src/main/java/com/eshc/goonersapp/feature/home/state/DashBoardUiState.kt new file mode 100644 index 00000000..bfb13f45 --- /dev/null +++ b/feature/home/src/main/java/com/eshc/goonersapp/feature/home/state/DashBoardUiState.kt @@ -0,0 +1,13 @@ +package com.eshc.goonersapp.feature.home.state + +import com.eshc.goonersapp.core.domain.model.season.Rank + +interface DashBoardUiState { + data object Loading: DashBoardUiState + + data class Success(val data: List): DashBoardUiState + + data class Failed(val message: String? = null): DashBoardUiState + + data class Error(val throwable: String? = null): DashBoardUiState +} \ No newline at end of file