diff --git a/.idea/androidTestResultsUserPreferences.xml b/.idea/androidTestResultsUserPreferences.xml index 4cdeb5e9..eff1f9a8 100644 --- a/.idea/androidTestResultsUserPreferences.xml +++ b/.idea/androidTestResultsUserPreferences.xml @@ -3,6 +3,45 @@ diff --git a/app/src/main/java/com/eshc/goonersapp/navigation/GnrNavHost.kt b/app/src/main/java/com/eshc/goonersapp/navigation/GnrNavHost.kt index 56cfee68..8360656d 100644 --- a/app/src/main/java/com/eshc/goonersapp/navigation/GnrNavHost.kt +++ b/app/src/main/java/com/eshc/goonersapp/navigation/GnrNavHost.kt @@ -19,7 +19,7 @@ import com.eshc.goonersapp.feature.team.navigation.clubDetailScreen import com.eshc.goonersapp.feature.match.navigation.navigateToMatchDetail import com.eshc.goonersapp.feature.team.navigation.navigateToPlayerDetail import com.eshc.goonersapp.feature.team.navigation.playerDetailScreen -import com.eshc.goonersapp.feature.team.navigation.teamHistoryScreen +import com.eshc.goonersapp.feature.team.navigation.teamSearchScreen import com.eshc.goonersapp.feature.team.navigation.teamScreen const val mainNavigationRoute = "main_route" @@ -71,35 +71,40 @@ fun GnrNavHost( } playerDetailScreen( + onBackIconClick = { navController.popBackStack() }, onShowSnackbar = onShowSnackbar ) matchDetailScreen( - onClickChat = { - navController.navigateToChatRoom(it) - }, + onClickChat = { navController.navigateToChatRoom(it) }, + onBackIconClick = { navController.popBackStack() }, onShowSnackbar = onShowSnackbar ) - teamHistoryScreen( - onPlayerClick = { - navController.navigateToPlayerDetail(it) - }, + + teamSearchScreen( + onPlayerClick = { navController.navigateToPlayerDetail(it) }, + onBackIconClick = { navController.popBackStack() }, onShowSnackbar = onShowSnackbar ) + chatRoomScreen( onShowSnackbar = onShowSnackbar ) + clubDetailScreen( + onBackIconClick = { navController.popBackStack() }, onShowSnackbar = onShowSnackbar ) + loginScreen( onShowSnackbar = onShowSnackbar, - onClickSignUp = { - navController.navigateToSignUp() - } + onClickSignUp = { navController.navigateToSignUp() }, + onBackIconClick = { navController.popBackStack() } ) + signUpScreen( - onShowSnackbar = onShowSnackbar + onShowSnackbar = onShowSnackbar, + onBackIconClick = { navController.popBackStack() } ) } } \ No newline at end of file diff --git a/app/src/main/java/com/eshc/goonersapp/navigation/TopLevelDestination.kt b/app/src/main/java/com/eshc/goonersapp/navigation/TopLevelDestination.kt index d86b8b66..bc0d921a 100644 --- a/app/src/main/java/com/eshc/goonersapp/navigation/TopLevelDestination.kt +++ b/app/src/main/java/com/eshc/goonersapp/navigation/TopLevelDestination.kt @@ -15,9 +15,9 @@ import com.eshc.goonersapp.feature.home.R as home import com.eshc.goonersapp.feature.match.R as match import com.eshc.goonersapp.feature.team.R as team -val topLevelDestinationSet = TopLevelDestination.entries.map { - it.route -}.toSet() +val topLevelDestinationSet = TopLevelDestination.entries + .map { destination -> destination.route } + .toSet() enum class TopLevelDestination( val selectedIcon: ImageVector, diff --git a/app/src/main/java/com/eshc/goonersapp/ui/GnrApp.kt b/app/src/main/java/com/eshc/goonersapp/ui/GnrApp.kt index e87d2356..0be01ef1 100644 --- a/app/src/main/java/com/eshc/goonersapp/ui/GnrApp.kt +++ b/app/src/main/java/com/eshc/goonersapp/ui/GnrApp.kt @@ -5,7 +5,6 @@ import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.heightIn import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.size -import androidx.compose.foundation.layout.widthIn import androidx.compose.material3.Icon import androidx.compose.material3.Scaffold import androidx.compose.material3.SnackbarDuration @@ -18,7 +17,6 @@ import androidx.compose.runtime.getValue import androidx.compose.runtime.remember import androidx.compose.runtime.rememberCoroutineScope import androidx.compose.ui.Modifier -import androidx.compose.ui.graphics.Color import androidx.compose.ui.res.stringResource import androidx.compose.ui.unit.dp import androidx.lifecycle.compose.collectAsStateWithLifecycle @@ -32,18 +30,21 @@ import com.eshc.goonersapp.core.data.util.NetworkConnectivityManager import com.eshc.goonersapp.core.designsystem.IconPack import com.eshc.goonersapp.core.designsystem.component.GnrNavigationBar import com.eshc.goonersapp.core.designsystem.component.GnrNavigationBarItem -import com.eshc.goonersapp.core.designsystem.component.TopLevelTopBar +import com.eshc.goonersapp.core.designsystem.component.GnrTopLevelTopBar import com.eshc.goonersapp.core.designsystem.iconpack.IcInfo +import com.eshc.goonersapp.core.designsystem.iconpack.IcNotification +import com.eshc.goonersapp.core.designsystem.iconpack.IcPeople import com.eshc.goonersapp.core.designsystem.iconpack.IcSearch -import com.eshc.goonersapp.core.designsystem.iconpack.IcUser import com.eshc.goonersapp.core.designsystem.theme.ColorFF181818 +import com.eshc.goonersapp.core.designsystem.theme.ColorFF777777 +import com.eshc.goonersapp.core.designsystem.theme.ColorFF9E9E9E import com.eshc.goonersapp.core.designsystem.theme.GnrTypography import com.eshc.goonersapp.feature.home.navigation.navigateToHome import com.eshc.goonersapp.feature.login.navigation.navigateToLogin import com.eshc.goonersapp.feature.match.navigation.navigateToMatch import com.eshc.goonersapp.feature.team.navigation.navigateToClubDetail import com.eshc.goonersapp.feature.team.navigation.navigateToTeam -import com.eshc.goonersapp.feature.team.navigation.navigateToTeamHistory +import com.eshc.goonersapp.feature.team.navigation.navigateToSearch import com.eshc.goonersapp.navigation.GnrNavHost import com.eshc.goonersapp.navigation.TopLevelDestination import kotlinx.coroutines.launch @@ -96,9 +97,8 @@ fun GnrApp( modifier = Modifier .padding(horizontal = 8.dp) .size(24.dp) - .clickable { - navController.navigateToClubDetail() - } + .clickable { navController.navigateToClubDetail() }, + tint = ColorFF777777 ) Icon( imageVector = IconPack.IcSearch, @@ -106,27 +106,49 @@ fun GnrApp( modifier = Modifier .padding(horizontal = 8.dp) .size(24.dp) - .clickable { - navController.navigateToTeamHistory() - } + .clickable { navController.navigateToSearch() }, + tint = ColorFF777777 ) } TopLevelDestination.HOME -> { Icon( - imageVector = IconPack.IcUser, + imageVector = IconPack.IcNotification, + contentDescription = null, + modifier = Modifier + .padding(horizontal = 8.dp) + .size(24.dp), + tint = ColorFF777777 + ) + Icon( + imageVector = IconPack.IcPeople, contentDescription = null, modifier = Modifier .padding(horizontal = 8.dp) .size(24.dp) - .clickable { - navController.navigateToLogin() - } + .clickable { navController.navigateToLogin() }, + tint = ColorFF777777 ) } else -> { - + Icon( + imageVector = IconPack.IcNotification, + contentDescription = null, + modifier = Modifier + .padding(horizontal = 8.dp) + .size(24.dp), + tint = ColorFF777777 + ) + Icon( + imageVector = IconPack.IcPeople, + contentDescription = null, + modifier = Modifier + .padding(horizontal = 8.dp) + .size(24.dp) + .clickable { navController.navigateToLogin() }, + tint = ColorFF777777 + ) } } } @@ -172,9 +194,9 @@ fun GnrTopLevelBar( topLevelDestination: TopLevelDestination, icons: @Composable () -> Unit ) { - TopLevelTopBar( + GnrTopLevelTopBar( modifier = Modifier.padding(horizontal = 8.dp), - title = topLevelDestination.name + title = stringResource(id = topLevelDestination.titleTextId) ) { icons() } @@ -200,15 +222,19 @@ fun GnrBottomBar( }, icon = { Icon( - modifier = Modifier.padding(bottom = 4.dp).heightIn(max = 18.dp), + modifier = Modifier + .padding(bottom = 4.dp) + .heightIn(max = 18.dp), imageVector = destination.unselectedIcon, - tint = Color(0xFF888888), + tint = ColorFF9E9E9E, contentDescription = null, ) }, selectedIcon = { Icon( - modifier = Modifier.padding(bottom = 4.dp).heightIn(max = 18.dp), + modifier = Modifier + .padding(bottom = 4.dp) + .heightIn(max = 18.dp), imageVector = destination.selectedIcon, contentDescription = null, ) @@ -217,7 +243,7 @@ fun GnrBottomBar( Text( text = stringResource(id = destination.iconTextId), style = GnrTypography.body2Regular.copy( - color = if(selected) ColorFF181818 else Color(0xFF888888) + color = if(selected) ColorFF181818 else ColorFF9E9E9E ) ) } diff --git a/core/common/src/main/java/com/eshc/goonersapp/core/common/util/DateUtil.kt b/core/common/src/main/java/com/eshc/goonersapp/core/common/util/DateUtil.kt index 35d01769..3140690c 100644 --- a/core/common/src/main/java/com/eshc/goonersapp/core/common/util/DateUtil.kt +++ b/core/common/src/main/java/com/eshc/goonersapp/core/common/util/DateUtil.kt @@ -3,41 +3,56 @@ package com.eshc.goonersapp.core.common.util import java.time.LocalDate import java.time.LocalDateTime import java.time.format.DateTimeFormatter +import java.util.Locale const val DefaultDatePattern = "yyyy-MM-dd'T'HH:mm:ss.SSS'Z'" object DateUtil { private val defaultDateFormat = DateTimeFormatter.ofPattern(DefaultDatePattern) - fun getYearAndMonthString(date : String ) : String { - if(date.isBlank()) return date - return LocalDate.parse(date, defaultDateFormat).format(DateTimeFormatter.ofPattern("yyyy.MM")) + fun getYearAndMonthString(date: String): String { + if (date.isBlank()) return date + return LocalDate.parse(date, defaultDateFormat) + .format(DateTimeFormatter.ofPattern("yyyy.MM")) + } + + fun getYearAndMonthAndDateString(date: String): String { + if (date.isBlank()) return date + return LocalDate.parse(date, defaultDateFormat) + .format(DateTimeFormatter.ofPattern("yyyy.MM.dd")) } - fun getYearAndMonthAndDateString(date : String ) : String { - if(date.isBlank()) return date - return LocalDate.parse(date, defaultDateFormat).format(DateTimeFormatter.ofPattern("yyyy.MM.dd")) + fun getYearAndMonthAndDateAndTimeString(date: String): String { + if (date.isBlank()) return date + return LocalDateTime.parse(date, defaultDateFormat) + .format(DateTimeFormatter.ofPattern("yyyy.MM.dd HH:mm")) } - fun getYearAndMonthAndDateAndTimeString(date : String ) : String { - if(date.isBlank()) return date - return LocalDateTime.parse(date, defaultDateFormat).format(DateTimeFormatter.ofPattern("yyyy.MM.dd HH:mm")) + fun getYearAndMonthAndDateAndDayAndTimeString(date: String): String { + if (date.isBlank()) return date + return LocalDateTime.parse(date, defaultDateFormat).format( + DateTimeFormatter.ofPattern( + "yyyy.MM.dd E HH:mm", + Locale.US + ) + ).uppercase() } - fun getYearAndMonthAndDateAndDayAndTimeString(date : String ) : String { - if(date.isBlank()) return date - return LocalDateTime.parse(date, defaultDateFormat).format(DateTimeFormatter.ofPattern("yyyy.MM.dd E HH:mm")).uppercase() + fun getMonthAndDateAndDayString(date: String): String { + if (date.isBlank()) return date + return LocalDateTime.parse(date, defaultDateFormat) + .format(DateTimeFormatter.ofPattern("MM.dd E", Locale.US)).uppercase() } - fun getYearAndMonthAndDateLocalDate(date : String ) : LocalDate { - if(date.isBlank()) return LocalDate.now() + fun getYearAndMonthAndDateLocalDate(date: String): LocalDate { + if (date.isBlank()) return LocalDate.now() return LocalDate.parse(date, defaultDateFormat) } - fun getTimeString(date: String) : String { - if(date.isBlank()) return date + fun getTimeString(date: String): String { + if (date.isBlank()) return date return LocalDateTime.parse(date, defaultDateFormat).let { - "${it.hour}:" + "${it.minute}".padStart(2,'0') + "${it.hour}:" + "${it.minute}".padStart(2, '0') } } } \ No newline at end of file diff --git a/core/data/src/main/java/com/eshc/goonersapp/core/data/di/DataModule.kt b/core/data/src/main/java/com/eshc/goonersapp/core/data/di/DataModule.kt index 3e7134dc..5370c28f 100644 --- a/core/data/src/main/java/com/eshc/goonersapp/core/data/di/DataModule.kt +++ b/core/data/src/main/java/com/eshc/goonersapp/core/data/di/DataModule.kt @@ -3,6 +3,7 @@ package com.eshc.goonersapp.core.data.di import com.eshc.goonersapp.core.data.repository.ChatRepositoryImpl import com.eshc.goonersapp.core.data.repository.MatchRepositoryImpl import com.eshc.goonersapp.core.data.repository.PlayerRepositoryImpl +import com.eshc.goonersapp.core.data.repository.SeasonRepositoryImpl import com.eshc.goonersapp.core.data.repository.TeamRepositoryImpl import com.eshc.goonersapp.core.data.repository.UserRepositoryImpl import com.eshc.goonersapp.core.data.util.NetworkConnectivityManager @@ -10,6 +11,7 @@ import com.eshc.goonersapp.core.data.util.NetworkConnectivityManagerImpl import com.eshc.goonersapp.core.domain.repository.ChatRepository import com.eshc.goonersapp.core.domain.repository.MatchRepository import com.eshc.goonersapp.core.domain.repository.PlayerRepository +import com.eshc.goonersapp.core.domain.repository.SeasonRepository import com.eshc.goonersapp.core.domain.repository.TeamRepository import com.eshc.goonersapp.core.domain.repository.UserRepository import dagger.Binds @@ -41,6 +43,11 @@ abstract class DataModule { teamRepository: TeamRepositoryImpl, ): TeamRepository + @Binds + abstract fun bindsSeasonRepository( + seasonRepository: SeasonRepositoryImpl + ): SeasonRepository + @Binds abstract fun bindsUserRepository( userRepository: UserRepositoryImpl, diff --git a/core/data/src/main/java/com/eshc/goonersapp/core/data/fake/FakeMatchRepositoryImpl.kt b/core/data/src/main/java/com/eshc/goonersapp/core/data/fake/FakeMatchRepositoryImpl.kt index 1f0930a1..7da05adb 100644 --- a/core/data/src/main/java/com/eshc/goonersapp/core/data/fake/FakeMatchRepositoryImpl.kt +++ b/core/data/src/main/java/com/eshc/goonersapp/core/data/fake/FakeMatchRepositoryImpl.kt @@ -6,6 +6,7 @@ import com.eshc.goonersapp.core.domain.model.DataResult import com.eshc.goonersapp.core.domain.model.match.Match import com.eshc.goonersapp.core.domain.model.match.MatchData import com.eshc.goonersapp.core.domain.model.match.MatchInformation +import com.eshc.goonersapp.core.domain.model.match.MatchLineup import com.eshc.goonersapp.core.domain.repository.MatchRepository import com.eshc.goonersapp.core.network.fake.FakeMatchDataSource import kotlinx.coroutines.flow.Flow @@ -63,4 +64,12 @@ class FakeMatchRepositoryImpl @Inject constructor( emit(result) } + override fun getMatchLineup(matchId: Int): Flow> = flow { + val result = fakeMatchDataSource + .getMatchLineup(matchId) + .toDataResult { remote -> remote.toModel() } + + emit(result) + } + } \ No newline at end of file diff --git a/core/data/src/main/java/com/eshc/goonersapp/core/data/fake/FakeSeasonRepositoryImpl.kt b/core/data/src/main/java/com/eshc/goonersapp/core/data/fake/FakeSeasonRepositoryImpl.kt new file mode 100644 index 00000000..b1b2df05 --- /dev/null +++ b/core/data/src/main/java/com/eshc/goonersapp/core/data/fake/FakeSeasonRepositoryImpl.kt @@ -0,0 +1,47 @@ +package com.eshc.goonersapp.core.data.fake + +import com.eshc.goonersapp.core.data.mapper.toDataResult +import com.eshc.goonersapp.core.data.mapper.toModel +import com.eshc.goonersapp.core.domain.model.DataResult +import com.eshc.goonersapp.core.domain.model.season.League +import com.eshc.goonersapp.core.domain.model.season.Rank +import com.eshc.goonersapp.core.domain.model.season.Season +import com.eshc.goonersapp.core.domain.repository.SeasonRepository +import com.eshc.goonersapp.core.network.fake.FakeSeasonDataSource +import kotlinx.coroutines.flow.Flow +import kotlinx.coroutines.flow.flow +import javax.inject.Inject + +class FakeSeasonRepositoryImpl @Inject constructor( + private val fakeSeasonDataSource: FakeSeasonDataSource +): SeasonRepository { + override fun getSeasonListByTeam(teamId: Int): Flow>> = flow { + val result = fakeSeasonDataSource + .getSeasonListByTeam(teamId) + .toDataResult { remote -> remote.map { it.toModel() } } + + emit(result) + } + + override fun getLeagueListAsCurrentSeasonByTeam( + teamId: Int + ): Flow>> = flow { + val result = fakeSeasonDataSource + .getLeagueListAsCurrentSeasonByTeam(teamId) + .toDataResult { remote -> remote.map { it.toModel() } } + + emit(result) + } + + override fun getPreviewRankListByTeamAndSeason( + teamId: Int, + seasonId: Int, + ): Flow>> = flow { + val result = fakeSeasonDataSource + .getPreviewRankListByTeamAndSeason(teamId, seasonId) + .toDataResult { remote -> remote.map { it.toModel() } } + + emit(result) + } + +} \ No newline at end of file diff --git a/core/data/src/main/java/com/eshc/goonersapp/core/data/mapper/MatchMapper.kt b/core/data/src/main/java/com/eshc/goonersapp/core/data/mapper/MatchMapper.kt index 39b141e3..9f4712d0 100644 --- a/core/data/src/main/java/com/eshc/goonersapp/core/data/mapper/MatchMapper.kt +++ b/core/data/src/main/java/com/eshc/goonersapp/core/data/mapper/MatchMapper.kt @@ -1,17 +1,22 @@ package com.eshc.goonersapp.core.data.mapper -import com.eshc.goonersapp.core.domain.model.match.LineUp +import com.eshc.goonersapp.core.domain.model.match.PlayerLineup import com.eshc.goonersapp.core.domain.model.match.Match import com.eshc.goonersapp.core.domain.model.match.MatchData import com.eshc.goonersapp.core.domain.model.match.MatchDetail import com.eshc.goonersapp.core.domain.model.match.MatchInformation +import com.eshc.goonersapp.core.domain.model.match.MatchLineup import com.eshc.goonersapp.core.domain.model.match.NotablePlayer import com.eshc.goonersapp.core.domain.model.match.Performance +import com.eshc.goonersapp.core.domain.model.match.TeamLineup import com.eshc.goonersapp.core.domain.model.match.toMatchDetailType import com.eshc.goonersapp.core.network.model.match.RemoteMatch import com.eshc.goonersapp.core.network.model.match.RemoteMatchData import com.eshc.goonersapp.core.network.model.match.RemoteMatchDetail import com.eshc.goonersapp.core.network.model.match.RemoteMatchInformation +import com.eshc.goonersapp.core.network.model.match.RemoteMatchLineup +import com.eshc.goonersapp.core.network.model.match.RemotePlayerLineup +import com.eshc.goonersapp.core.network.model.match.RemoteTeamLineup /** * [RemoteMatch] Mapper @@ -19,6 +24,7 @@ import com.eshc.goonersapp.core.network.model.match.RemoteMatchInformation */ fun RemoteMatch.toModel() = Match( id = matchId, + seasonId = seasonId, homeTeamId = homeTeamId, homeTeamName = homeTeamName, homeTeamImageUrl = homeTeamImage, @@ -46,27 +52,13 @@ fun RemoteMatchInformation.toModel() = MatchInformation( NotablePlayer( playerId = remote.playerId, playerName = remote.playerName, - playerHeight = remote.height, - playerWeight = remote.weight, - playerImageUrl = remote.playerImage, - playerPosition = remote.position, - playerPositionInitial = remote.positionInitial, - playerGoalCount = remote.goalCount - ) - }, - lineUp = lineUp.map { remote -> - LineUp( - lineUpId = remote.lineUpId, - matchId = remote.matchId, - playerId = remote.playerId, - teamId = remote.teamId, - playerName = remote.playerName, - playerBackNumber = remote.jerseyNumber, - formationField = remote.formationField, - formationPosition = remote.formationPosition, - positionId = remote.positionId, - positionCategory = remote.positionCategory, - positionInitial = remote.positionInitial + playerHeight = remote.height ?: 0, + playerWeight = remote.weight ?: 0, + playerImageUrl = remote.playerImage ?: "", + playerPosition = remote.position ?: "", + playerPositionInitial = remote.positionInitial ?: "", + playerGoalCount = remote.goalCount ?: 0, + playerParticipationCount = remote.participationCount ?: 0 ) }, performance = Performance( @@ -101,4 +93,34 @@ fun RemoteMatchDetail.toModel() = MatchDetail( fun RemoteMatchData.toModel() = MatchData( match = match.toModel(), matchDetail = matchDetail.map { remote -> remote.toModel() } -) \ No newline at end of file +) + +/** + * [RemoteMatchLineup] Mapper + * - Mapper [RemoteMatchLineup] to [MatchLineup] + */ +fun RemoteMatchLineup.toModel() = MatchLineup( + homeLineup = homeLineup.toModel(), + awayLineup = awayLineup.toModel() +) + +fun RemoteTeamLineup.toModel() = TeamLineup( + teamId = teamId.toInt(), + formation = formation, + playerLineup = players.map { remote -> remote.toModel() } +) + +fun RemotePlayerLineup.toModel() = PlayerLineup( + lineUpId = lineUpId, + matchId = matchId, + playerId = playerId, + teamId = teamId, + playerName = playerName, + playerImageUrl = playerImageUrl, + playerBackNumber = jerseyNumber, + formationField = formationField, + formationPosition = formationPosition, + positionId = positionId, + positionCategory = positionCategory, + positionInitial = positionInitial +) diff --git a/core/data/src/main/java/com/eshc/goonersapp/core/data/mapper/SeasonMapper.kt b/core/data/src/main/java/com/eshc/goonersapp/core/data/mapper/SeasonMapper.kt new file mode 100644 index 00000000..35587407 --- /dev/null +++ b/core/data/src/main/java/com/eshc/goonersapp/core/data/mapper/SeasonMapper.kt @@ -0,0 +1,33 @@ +package com.eshc.goonersapp.core.data.mapper + +import com.eshc.goonersapp.core.domain.model.season.League +import com.eshc.goonersapp.core.domain.model.season.Rank +import com.eshc.goonersapp.core.domain.model.season.Season +import com.eshc.goonersapp.core.network.model.season.RemoteLeague +import com.eshc.goonersapp.core.network.model.season.RemoteRank +import com.eshc.goonersapp.core.network.model.season.RemoteSeason + +fun RemoteLeague.toModel() = League( + leagueId = leagueId, + leagueName = leagueName, + shortCode = shortCode, + leagueImgUrl = leagueImageUrl, + seasonId = seasonId, + season = season, + teamId = teamId +) + +fun RemoteSeason.toModel() = Season(seasonId = seasonId, season = season) + +fun RemoteRank.toModel() = Rank( + standingId = standingId, + position = position, + points = points, + wins = win, + loss = loss, + draw = draw, + goalDifference = goalDifference, + teamId = teamId, + teamName = teamName, + shortCode = shortCode +) \ No newline at end of file diff --git a/core/data/src/main/java/com/eshc/goonersapp/core/data/repository/MatchRepositoryImpl.kt b/core/data/src/main/java/com/eshc/goonersapp/core/data/repository/MatchRepositoryImpl.kt index 6a1bc887..a867ea9b 100644 --- a/core/data/src/main/java/com/eshc/goonersapp/core/data/repository/MatchRepositoryImpl.kt +++ b/core/data/src/main/java/com/eshc/goonersapp/core/data/repository/MatchRepositoryImpl.kt @@ -6,6 +6,7 @@ import com.eshc.goonersapp.core.domain.model.DataResult import com.eshc.goonersapp.core.domain.model.match.Match import com.eshc.goonersapp.core.domain.model.match.MatchData import com.eshc.goonersapp.core.domain.model.match.MatchInformation +import com.eshc.goonersapp.core.domain.model.match.MatchLineup import com.eshc.goonersapp.core.domain.repository.MatchRepository import com.eshc.goonersapp.core.network.MatchNetworkDataSource import kotlinx.coroutines.flow.Flow @@ -59,4 +60,12 @@ class MatchRepositoryImpl @Inject constructor( emit(result) } + + override fun getMatchLineup(matchId: Int): Flow> = flow { + val result = matchNetworkDataSource + .getMatchLineup(matchId) + .toDataResult { remote -> remote.toModel() } + + emit(result) + } } \ No newline at end of file diff --git a/core/data/src/main/java/com/eshc/goonersapp/core/data/repository/SeasonRepositoryImpl.kt b/core/data/src/main/java/com/eshc/goonersapp/core/data/repository/SeasonRepositoryImpl.kt new file mode 100644 index 00000000..37173894 --- /dev/null +++ b/core/data/src/main/java/com/eshc/goonersapp/core/data/repository/SeasonRepositoryImpl.kt @@ -0,0 +1,47 @@ +package com.eshc.goonersapp.core.data.repository + +import com.eshc.goonersapp.core.data.mapper.toDataResult +import com.eshc.goonersapp.core.data.mapper.toModel +import com.eshc.goonersapp.core.domain.model.DataResult +import com.eshc.goonersapp.core.domain.model.season.League +import com.eshc.goonersapp.core.domain.model.season.Rank +import com.eshc.goonersapp.core.domain.model.season.Season +import com.eshc.goonersapp.core.domain.repository.SeasonRepository +import com.eshc.goonersapp.core.network.SeasonNetworkDataSource +import kotlinx.coroutines.flow.Flow +import kotlinx.coroutines.flow.flow +import javax.inject.Inject + +class SeasonRepositoryImpl @Inject constructor( + private val seasonNetworkDataSource: SeasonNetworkDataSource +): SeasonRepository { + override fun getSeasonListByTeam(teamId: Int): Flow>> = flow { + val result = seasonNetworkDataSource + .getSeasonListByTeam(teamId) + .toDataResult { remote -> remote.map { it.toModel() } } + + emit(result) + } + + override fun getLeagueListAsCurrentSeasonByTeam( + teamId: Int + ): Flow>> = flow { + val result = seasonNetworkDataSource + .getLeagueListAsCurrentSeasonByTeam(teamId) + .toDataResult { remote -> remote.map { it.toModel() } } + + emit(result) + } + + override fun getPreviewRankListByTeamAndSeason( + teamId: Int, + seasonId: Int, + ): Flow>> = flow { + val result = seasonNetworkDataSource + .getPreviewRankListByTeamAndSeason(teamId, seasonId) + .toDataResult { remote -> remote.map { it.toModel() } } + + emit(result) + } + +} \ No newline at end of file diff --git a/core/data/src/test/java/com/eshc/goonersapp/core/data/FakeMatchTest.kt b/core/data/src/test/java/com/eshc/goonersapp/core/data/FakeMatchRepositoryImplTest.kt similarity index 76% rename from core/data/src/test/java/com/eshc/goonersapp/core/data/FakeMatchTest.kt rename to core/data/src/test/java/com/eshc/goonersapp/core/data/FakeMatchRepositoryImplTest.kt index 8907f530..11720220 100644 --- a/core/data/src/test/java/com/eshc/goonersapp/core/data/FakeMatchTest.kt +++ b/core/data/src/test/java/com/eshc/goonersapp/core/data/FakeMatchRepositoryImplTest.kt @@ -5,7 +5,9 @@ import com.eshc.goonersapp.core.domain.model.DataResult import com.eshc.goonersapp.core.domain.model.match.Match import com.eshc.goonersapp.core.domain.model.match.MatchData import com.eshc.goonersapp.core.domain.model.match.MatchInformation +import com.eshc.goonersapp.core.domain.model.match.MatchLineup import com.eshc.goonersapp.core.domain.model.match.Performance +import com.eshc.goonersapp.core.domain.model.match.TeamLineup import com.eshc.goonersapp.core.domain.repository.MatchRepository import com.eshc.goonersapp.core.network.fake.FakeMatchDataSource import kotlinx.coroutines.runBlocking @@ -16,10 +18,10 @@ import org.junit.Test /** * Created By KanuKim97 * - * [FakeMatchTest] + * [FakeMatchRepositoryImplTest] * - FakeMatchRepository Test */ -class FakeMatchTest { +class FakeMatchRepositoryImplTest { private lateinit var fakeMatchDataSource: FakeMatchDataSource private lateinit var fakeMatchRepository: MatchRepository @@ -62,7 +64,6 @@ class FakeMatchTest { assertEquals( MatchInformation( notablePlayer = null, - lineUp = listOf(), performance = Performance(opponentImageUrl = "", win = 0, draw = 0, lose = 0) ), result.data @@ -110,4 +111,30 @@ class FakeMatchTest { } } } + + @Test + fun testMatchLineupWithFake() = runBlocking { + fakeMatchRepository.getMatchLineup(38).collect { result -> + when (result) { + is DataResult.Success -> { + assertEquals( + MatchLineup( + homeLineup = TeamLineup( + teamId = 0, + formation = "", + playerLineup = emptyList() + ), + awayLineup = TeamLineup( + teamId = 0, + formation = "", + playerLineup = emptyList() + ) + ), + result.data + ) + } + is DataResult.Failure -> { /* Nothing */ } + } + } + } } \ No newline at end of file diff --git a/core/data/src/test/java/com/eshc/goonersapp/core/data/FakeSeasonRepositoryImplTest.kt b/core/data/src/test/java/com/eshc/goonersapp/core/data/FakeSeasonRepositoryImplTest.kt new file mode 100644 index 00000000..33097b18 --- /dev/null +++ b/core/data/src/test/java/com/eshc/goonersapp/core/data/FakeSeasonRepositoryImplTest.kt @@ -0,0 +1,111 @@ +package com.eshc.goonersapp.core.data + +import com.eshc.goonersapp.core.data.fake.FakeSeasonRepositoryImpl +import com.eshc.goonersapp.core.domain.model.DataResult +import com.eshc.goonersapp.core.domain.model.season.Rank +import com.eshc.goonersapp.core.domain.repository.SeasonRepository +import com.eshc.goonersapp.core.network.fake.FakeSeasonDataSource +import kotlinx.coroutines.runBlocking +import org.junit.Assert.assertEquals +import org.junit.Assert.fail +import org.junit.Before +import org.junit.Test + +/** + * Created By KanuKim97 + * + * [FakeSeasonRepositoryImplTest] + * - FakeSeasonRepository Test + */ +class FakeSeasonRepositoryImplTest { + private lateinit var fakeSeasonDataSource: FakeSeasonDataSource + private lateinit var fakeSeasonRepository: SeasonRepository + + @Before + fun setUp() { + fakeSeasonDataSource = FakeSeasonDataSource() + fakeSeasonRepository = FakeSeasonRepositoryImpl(fakeSeasonDataSource) + } + + @Test + fun fake_get_season_list_by_team_flow_collect_as_success() = runBlocking { + fakeSeasonRepository.getSeasonListByTeam(teamId = 19).collect { result -> + when (result) { + is DataResult.Success -> { + assertEquals( + "2023/2024", + result.data.first().season + ) + } + is DataResult.Failure -> fail("Test should not be reached here") + } + } + } + + @Test + fun fake_get_season_list_by_team_flow_collect_as_fail() = runBlocking { + fakeSeasonRepository.getSeasonListByTeam(teamId = 20).collect { result -> + when (result) { + is DataResult.Success -> fail("Test should not be reached here") + is DataResult.Failure -> assertEquals(404, result.code) + } + } + } + + @Test + fun fake_get_league_list_as_current_season_by_team_as_success() = runBlocking { + fakeSeasonRepository.getLeagueListAsCurrentSeasonByTeam(19).collect { result -> + when(result) { + is DataResult.Success -> { + assertEquals( + 21366, + result.data.first().seasonId + ) + } + is DataResult.Failure -> fail("Test should not be reached here") + } + } + } + + + @Test + fun fake_get_league_list_as_current_season_by_team_as_fail() = runBlocking { + fakeSeasonRepository.getLeagueListAsCurrentSeasonByTeam(20).collect { result -> + when(result) { + is DataResult.Success -> fail("Test should not be reached here") + is DataResult.Failure -> assertEquals(404, result.code) + } + } + } + + @Test + fun fake_get_preview_rank_list_by_team_and_season_as_success() = runBlocking { + fakeSeasonRepository.getPreviewRankListByTeamAndSeason( + teamId = 19, + seasonId = 21366 + ).collect { result -> + when (result) { + is DataResult.Success -> { + assertEquals( + listOf(), + result.data + ) + } + is DataResult.Failure -> fail("Test should not be reached here") + } + } + } + + @Test + fun fake_get_preview_rank_list_by_team_and_season_as_fail() = runBlocking { + fakeSeasonRepository.getPreviewRankListByTeamAndSeason( + teamId = 20, + seasonId = 21366 + ).collect { result -> + when (result) { + is DataResult.Success -> fail("Test should not be reached here") + is DataResult.Failure -> assertEquals(404, result.code) + } + } + } +} \ 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 4c356951..bc53744b 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 @@ -57,7 +57,7 @@ fun MatchLeagueInfo( ) } ) - Spacer(modifier = Modifier.size(10.dp)) + Spacer(modifier = Modifier.size(7.dp)) Text( text = competitionName, color = ColorFF181818, diff --git a/core/designsystem/src/main/java/com/eshc/goonersapp/core/designsystem/component/TopBar.kt b/core/designsystem/src/main/java/com/eshc/goonersapp/core/designsystem/component/TopBar.kt index ab81bd0b..dd08b6ad 100644 --- a/core/designsystem/src/main/java/com/eshc/goonersapp/core/designsystem/component/TopBar.kt +++ b/core/designsystem/src/main/java/com/eshc/goonersapp/core/designsystem/component/TopBar.kt @@ -1,12 +1,11 @@ package com.eshc.goonersapp.core.designsystem.component +import androidx.compose.foundation.clickable 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.layout.wrapContentHeight -import androidx.compose.material.icons.Icons -import androidx.compose.material.icons.filled.ArrowBack import androidx.compose.material3.Icon import androidx.compose.material3.MaterialTheme import androidx.compose.material3.Text @@ -14,17 +13,15 @@ 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.font.FontWeight import androidx.compose.ui.unit.dp -import androidx.compose.ui.unit.sp import com.eshc.goonersapp.core.designsystem.IconPack -import com.eshc.goonersapp.core.designsystem.iconpack.IcFootballClub -import com.eshc.goonersapp.core.designsystem.theme.pretendard +import com.eshc.goonersapp.core.designsystem.iconpack.IcIosArrowBack +import com.eshc.goonersapp.core.designsystem.theme.GnrTypography @Composable -fun TopLevelTopBar( - modifier: Modifier = Modifier, +fun GnrTopLevelTopBar( title : String, + modifier: Modifier = Modifier, content : @Composable () -> Unit ){ Row( @@ -34,7 +31,7 @@ fun TopLevelTopBar( Text( modifier = Modifier.padding(vertical = 8.dp).wrapContentHeight().weight(1f), text = title, - style = MaterialTheme.typography.headlineLarge, + style = GnrTypography.heading2SemiBold, color = Color.Black, ) content() @@ -42,9 +39,10 @@ fun TopLevelTopBar( } @Composable -fun TopBar( - modifier: Modifier = Modifier, +fun GnrTopBar( title : String, + onBackIconClick: () -> Unit, + modifier: Modifier = Modifier, content : @Composable () -> Unit = {} ){ Row( @@ -52,9 +50,12 @@ fun TopBar( verticalAlignment = Alignment.CenterVertically ) { Icon( - imageVector = Icons.Default.ArrowBack, + imageVector = IconPack.IcIosArrowBack, contentDescription = null, - modifier= Modifier.padding(start = 8.dp,end = 8.dp).size(24.dp) + modifier= Modifier + .padding(start = 8.dp,end = 8.dp) + .size(24.dp) + .clickable(onClick = onBackIconClick) ) Text( modifier = Modifier.padding(vertical = 8.dp).wrapContentHeight().weight(1f), diff --git a/core/designsystem/src/main/java/com/eshc/goonersapp/core/designsystem/iconpack/IcIosArrowBack.kt b/core/designsystem/src/main/java/com/eshc/goonersapp/core/designsystem/iconpack/IcIosArrowBack.kt new file mode 100644 index 00000000..b81a5658 --- /dev/null +++ b/core/designsystem/src/main/java/com/eshc/goonersapp/core/designsystem/iconpack/IcIosArrowBack.kt @@ -0,0 +1,48 @@ +package com.eshc.goonersapp.core.designsystem.iconpack + +import androidx.compose.ui.graphics.Color +import androidx.compose.ui.graphics.PathFillType.Companion.NonZero +import androidx.compose.ui.graphics.SolidColor +import androidx.compose.ui.graphics.StrokeCap.Companion.Butt +import androidx.compose.ui.graphics.StrokeJoin.Companion.Miter +import androidx.compose.ui.graphics.vector.ImageVector +import androidx.compose.ui.graphics.vector.ImageVector.Builder +import androidx.compose.ui.graphics.vector.path +import androidx.compose.ui.unit.dp +import com.eshc.goonersapp.core.designsystem.IconPack + +public val IconPack.IcIosArrowBack: ImageVector + get() { + if (_iciosarrowback != null) { + return _iciosarrowback!! + } + _iciosarrowback = Builder(name = "IcIosArrowBack", defaultWidth = 10.0.dp, defaultHeight = + 18.0.dp, viewportWidth = 10.0f, viewportHeight = 18.0f).apply { + path(fill = SolidColor(Color(0xFF181818)), stroke = null, strokeLineWidth = 0.0f, + strokeLineCap = Butt, strokeLineJoin = Miter, strokeLineMiter = 4.0f, + pathFillType = NonZero) { + moveTo(1.4564f, 9.0014f) + lineTo(9.1754f, 16.7206f) + curveTo(9.323f, 16.868f, 9.3959f, 17.0444f, 9.394f, 17.2496f) + curveTo(9.392f, 17.4549f, 9.3173f, 17.6312f, 9.1697f, 17.7786f) + curveTo(9.0223f, 17.9262f, 8.846f, 18.0f, 8.6407f, 18.0f) + curveTo(8.4354f, 18.0f, 8.2591f, 17.9262f, 8.1117f, 17.7786f) + lineTo(0.4655f, 10.1384f) + curveTo(0.3039f, 9.9769f, 0.1859f, 9.7967f, 0.1116f, 9.5979f) + curveTo(0.0372f, 9.399f, 0.0f, 9.2002f, 0.0f, 9.0014f) + curveTo(0.0f, 8.8026f, 0.0372f, 8.6038f, 0.1116f, 8.4049f) + curveTo(0.1859f, 8.2061f, 0.3039f, 8.0259f, 0.4655f, 7.8643f) + lineTo(8.1117f, 0.2184f) + curveTo(8.2591f, 0.0708f, 8.4364f, -0.002f, 8.6437f, 0.0f) + curveTo(8.8508f, 0.0019f, 9.028f, 0.0766f, 9.1754f, 0.2241f) + curveTo(9.323f, 0.3716f, 9.3968f, 0.5479f, 9.3968f, 0.7532f) + curveTo(9.3968f, 0.9584f, 9.323f, 1.1348f, 9.1754f, 1.2822f) + lineTo(1.4564f, 9.0014f) + close() + } + } + .build() + return _iciosarrowback!! + } + +private var _iciosarrowback: ImageVector? = null diff --git a/core/designsystem/src/main/java/com/eshc/goonersapp/core/designsystem/iconpack/IcNotification.kt b/core/designsystem/src/main/java/com/eshc/goonersapp/core/designsystem/iconpack/IcNotification.kt new file mode 100644 index 00000000..4ece6a2d --- /dev/null +++ b/core/designsystem/src/main/java/com/eshc/goonersapp/core/designsystem/iconpack/IcNotification.kt @@ -0,0 +1,71 @@ +package com.eshc.goonersapp.core.designsystem.iconpack + +import androidx.compose.ui.graphics.Color +import androidx.compose.ui.graphics.PathFillType.Companion.NonZero +import androidx.compose.ui.graphics.SolidColor +import androidx.compose.ui.graphics.StrokeCap.Companion.Butt +import androidx.compose.ui.graphics.StrokeJoin.Companion.Miter +import androidx.compose.ui.graphics.vector.ImageVector +import androidx.compose.ui.graphics.vector.ImageVector.Builder +import androidx.compose.ui.graphics.vector.path +import androidx.compose.ui.unit.dp +import com.eshc.goonersapp.core.designsystem.IconPack + +public val IconPack.IcNotification: ImageVector + get() { + if (_notification != null) { + return _notification!! + } + _notification = Builder(name = "Notification", defaultWidth = 23.0.dp, defaultHeight = + 26.0.dp, viewportWidth = 23.0f, viewportHeight = 26.0f).apply { + path(fill = SolidColor(Color(0xFFBBBBBB)), stroke = null, strokeLineWidth = 0.0f, + strokeLineCap = Butt, strokeLineJoin = Miter, strokeLineMiter = 4.0f, + pathFillType = NonZero) { + moveTo(1.1197f, 22.1964f) + curveTo(0.8024f, 22.1964f, 0.5365f, 22.099f, 0.3219f, 21.9042f) + curveTo(0.1073f, 21.7094f, 0.0f, 21.468f, 0.0f, 21.18f) + curveTo(0.0f, 20.892f, 0.1073f, 20.6507f, 0.3219f, 20.4562f) + curveTo(0.5365f, 20.2617f, 0.8024f, 20.1644f, 1.1197f, 20.1644f) + horizontalLineTo(2.6987f) + verticalLineTo(10.0561f) + curveTo(2.6987f, 8.2342f, 3.3183f, 6.6233f, 4.5576f, 5.2234f) + curveTo(5.7969f, 3.8236f, 7.3878f, 2.9283f, 9.3305f, 2.5375f) + verticalLineTo(1.6934f) + curveTo(9.3305f, 1.223f, 9.5118f, 0.8232f, 9.8743f, 0.4939f) + curveTo(10.2368f, 0.1646f, 10.677f, 0.0f, 11.1949f, 0.0f) + curveTo(11.7128f, 0.0f, 12.1535f, 0.1646f, 12.5172f, 0.4939f) + curveTo(12.8808f, 0.8232f, 13.0626f, 1.223f, 13.0626f, 1.6934f) + verticalLineTo(2.5375f) + curveTo(15.0053f, 2.9283f, 16.5963f, 3.8236f, 17.8356f, 5.2234f) + curveTo(19.0748f, 6.6233f, 19.6945f, 8.2342f, 19.6945f, 10.0561f) + verticalLineTo(20.1644f) + horizontalLineTo(21.2735f) + curveTo(21.5907f, 20.1644f, 21.8566f, 20.2618f, 22.0712f, 20.4566f) + curveTo(22.2858f, 20.6514f, 22.3931f, 20.8929f, 22.3931f, 21.1808f) + curveTo(22.3931f, 21.4689f, 22.2858f, 21.7101f, 22.0712f, 21.9046f) + curveTo(21.8566f, 22.0991f, 21.5907f, 22.1964f, 21.2735f, 22.1964f) + horizontalLineTo(1.1197f) + close() + moveTo(11.194f, 26.0f) + curveTo(10.4512f, 26.0f, 9.8161f, 25.7602f, 9.2888f, 25.2806f) + curveTo(8.7616f, 24.8011f, 8.4979f, 24.2246f, 8.4979f, 23.5511f) + horizontalLineTo(13.8952f) + curveTo(13.8952f, 24.2267f, 13.6307f, 24.8038f, 13.1017f, 25.2823f) + curveTo(12.5728f, 25.7608f, 11.9369f, 26.0f, 11.194f, 26.0f) + close() + moveTo(4.9379f, 20.1644f) + horizontalLineTo(17.4552f) + verticalLineTo(10.0561f) + curveTo(17.4552f, 8.4878f, 16.8442f, 7.1491f, 15.6221f, 6.0402f) + curveTo(14.4001f, 4.9312f, 12.9249f, 4.3767f, 11.1966f, 4.3767f) + curveTo(9.4683f, 4.3767f, 7.9931f, 4.9312f, 6.771f, 6.0402f) + curveTo(5.549f, 7.1491f, 4.9379f, 8.4878f, 4.9379f, 10.0561f) + verticalLineTo(20.1644f) + close() + } + } + .build() + return _notification!! + } + +private var _notification: ImageVector? = null diff --git a/core/designsystem/src/main/java/com/eshc/goonersapp/core/designsystem/iconpack/IcPeople.kt b/core/designsystem/src/main/java/com/eshc/goonersapp/core/designsystem/iconpack/IcPeople.kt new file mode 100644 index 00000000..4a6347a5 --- /dev/null +++ b/core/designsystem/src/main/java/com/eshc/goonersapp/core/designsystem/iconpack/IcPeople.kt @@ -0,0 +1,80 @@ +package com.eshc.goonersapp.core.designsystem.iconpack + +import androidx.compose.ui.graphics.Color +import androidx.compose.ui.graphics.PathFillType.Companion.NonZero +import androidx.compose.ui.graphics.SolidColor +import androidx.compose.ui.graphics.StrokeCap.Companion.Butt +import androidx.compose.ui.graphics.StrokeJoin.Companion.Miter +import androidx.compose.ui.graphics.vector.ImageVector +import androidx.compose.ui.graphics.vector.ImageVector.Builder +import androidx.compose.ui.graphics.vector.path +import androidx.compose.ui.unit.dp +import com.eshc.goonersapp.core.designsystem.IconPack + +public val IconPack.IcPeople: ImageVector + get() { + if (_icpeople != null) { + return _icpeople!! + } + _icpeople = Builder(name = "IcPeople", defaultWidth = 18.0.dp, defaultHeight = 20.0.dp, + viewportWidth = 18.0f, viewportHeight = 20.0f).apply { + path(fill = SolidColor(Color(0xFF777777)), stroke = null, strokeLineWidth = 0.0f, + strokeLineCap = Butt, strokeLineJoin = Miter, strokeLineMiter = 4.0f, + pathFillType = NonZero) { + moveTo(9.0f, 9.2574f) + curveTo(7.9393f, 9.2574f, 7.0313f, 8.8416f, 6.2759f, 8.0101f) + curveTo(5.5205f, 7.1787f, 5.1429f, 6.1791f, 5.1429f, 5.0115f) + curveTo(5.1429f, 3.8439f, 5.5205f, 2.8443f, 6.2759f, 2.0128f) + curveTo(7.0313f, 1.1814f, 7.9393f, 0.7656f, 9.0f, 0.7656f) + curveTo(10.0607f, 0.7656f, 10.9688f, 1.1814f, 11.7241f, 2.0128f) + curveTo(12.4795f, 2.8443f, 12.8571f, 3.8439f, 12.8571f, 5.0115f) + curveTo(12.8571f, 6.1791f, 12.4795f, 7.1787f, 11.7241f, 8.0101f) + curveTo(10.9688f, 8.8416f, 10.0607f, 9.2574f, 9.0f, 9.2574f) + close() + moveTo(0.0f, 18.0212f) + verticalLineTo(17.1612f) + curveTo(0.0f, 16.5769f, 0.1545f, 16.0303f, 0.4636f, 15.5213f) + curveTo(0.7727f, 15.0124f, 1.1885f, 14.6173f, 1.711f, 14.336f) + curveTo(2.9242f, 13.6955f, 4.1382f, 13.2151f, 5.353f, 12.8949f) + curveTo(6.5679f, 12.5746f, 7.7835f, 12.4145f, 9.0f, 12.4145f) + curveTo(10.2165f, 12.4145f, 11.4321f, 12.5746f, 12.647f, 12.8949f) + curveTo(13.8618f, 13.2151f, 15.0758f, 13.6955f, 16.289f, 14.336f) + curveTo(16.8115f, 14.6173f, 17.2273f, 15.0124f, 17.5364f, 15.5213f) + curveTo(17.8455f, 16.0303f, 18.0f, 16.5769f, 18.0f, 17.1612f) + verticalLineTo(18.0212f) + curveTo(18.0f, 18.4368f, 17.8718f, 18.7856f, 17.6155f, 19.0677f) + curveTo(17.3592f, 19.3499f, 17.0423f, 19.491f, 16.6649f, 19.491f) + horizontalLineTo(1.3352f) + curveTo(0.9577f, 19.491f, 0.6408f, 19.3499f, 0.3845f, 19.0677f) + curveTo(0.1282f, 18.7856f, 0.0f, 18.4368f, 0.0f, 18.0212f) + close() + moveTo(1.2857f, 18.0756f) + horizontalLineTo(16.7143f) + verticalLineTo(17.1612f) + curveTo(16.7143f, 16.8473f, 16.6224f, 16.5524f, 16.4386f, 16.2766f) + curveTo(16.2548f, 16.0008f, 16.0005f, 15.7677f, 15.6758f, 15.5771f) + curveTo(14.6176f, 15.0128f, 13.5265f, 14.5805f, 12.4025f, 14.2802f) + curveTo(11.2786f, 13.9799f, 10.1444f, 13.8298f, 9.0f, 13.8298f) + curveTo(7.8556f, 13.8298f, 6.7214f, 13.9799f, 5.5975f, 14.2802f) + curveTo(4.4735f, 14.5805f, 3.3824f, 15.0128f, 2.3242f, 15.5771f) + curveTo(1.9995f, 15.7677f, 1.7452f, 16.0008f, 1.5614f, 16.2766f) + curveTo(1.3776f, 16.5524f, 1.2857f, 16.8473f, 1.2857f, 17.1612f) + verticalLineTo(18.0756f) + close() + moveTo(9.0f, 7.8421f) + curveTo(9.7071f, 7.8421f, 10.3125f, 7.5649f, 10.8161f, 7.0106f) + curveTo(11.3196f, 6.4563f, 11.5714f, 5.7899f, 11.5714f, 5.0115f) + curveTo(11.5714f, 4.2331f, 11.3196f, 3.5667f, 10.8161f, 3.0124f) + curveTo(10.3125f, 2.4581f, 9.7071f, 2.1809f, 9.0f, 2.1809f) + curveTo(8.2929f, 2.1809f, 7.6875f, 2.4581f, 7.1839f, 3.0124f) + curveTo(6.6804f, 3.5667f, 6.4286f, 4.2331f, 6.4286f, 5.0115f) + curveTo(6.4286f, 5.7899f, 6.6804f, 6.4563f, 7.1839f, 7.0106f) + curveTo(7.6875f, 7.5649f, 8.2929f, 7.8421f, 9.0f, 7.8421f) + close() + } + } + .build() + return _icpeople!! + } + +private var _icpeople: ImageVector? = null diff --git a/core/designsystem/src/main/java/com/eshc/goonersapp/core/designsystem/theme/Color.kt b/core/designsystem/src/main/java/com/eshc/goonersapp/core/designsystem/theme/Color.kt index bbd63f34..914f4d46 100644 --- a/core/designsystem/src/main/java/com/eshc/goonersapp/core/designsystem/theme/Color.kt +++ b/core/designsystem/src/main/java/com/eshc/goonersapp/core/designsystem/theme/Color.kt @@ -36,9 +36,17 @@ val Color88FFFFFF = Color(0x88FFFFFF) val ColorFFFECD44 = Color(0xFFFECD44) val ColorFFE9343C = Color(0xFFE9343C) +val ColorFFE6EDFC = Color(0xFFE6EDFC) + val BrushMainGradient = Brush.verticalGradient( listOf( ColorFF10358A, ColorFF072872 ) -) \ No newline at end of file +) + +/** + * Player Detail Color + * */ +val ColorFFC10006 = Color(0xFFC10006) +val ColorFF720509 = Color(0xFF720509) \ No newline at end of file diff --git a/core/designsystem/src/main/res/drawable/ic_ball.png b/core/designsystem/src/main/res/drawable/ic_ball.png new file mode 100644 index 00000000..82462abc Binary files /dev/null and b/core/designsystem/src/main/res/drawable/ic_ball.png differ diff --git a/core/domain/src/main/java/com/eshc/goonersapp/core/domain/model/match/Match.kt b/core/domain/src/main/java/com/eshc/goonersapp/core/domain/model/match/Match.kt index 6e785e70..0fbfa6d1 100644 --- a/core/domain/src/main/java/com/eshc/goonersapp/core/domain/model/match/Match.kt +++ b/core/domain/src/main/java/com/eshc/goonersapp/core/domain/model/match/Match.kt @@ -2,6 +2,7 @@ package com.eshc.goonersapp.core.domain.model.match data class Match( val id :Int = 0, + val seasonId : Int = 0, val homeTeamId : Int = 0, val homeTeamName : String = "", val homeTeamImageUrl : String = "", diff --git a/core/domain/src/main/java/com/eshc/goonersapp/core/domain/model/match/MatchInformation.kt b/core/domain/src/main/java/com/eshc/goonersapp/core/domain/model/match/MatchInformation.kt index 271a29ec..95c8d2fc 100644 --- a/core/domain/src/main/java/com/eshc/goonersapp/core/domain/model/match/MatchInformation.kt +++ b/core/domain/src/main/java/com/eshc/goonersapp/core/domain/model/match/MatchInformation.kt @@ -2,7 +2,6 @@ package com.eshc.goonersapp.core.domain.model.match data class MatchInformation( val notablePlayer: NotablePlayer? = null, - val lineUp: List, val performance: Performance ) @@ -14,21 +13,8 @@ data class NotablePlayer( val playerImageUrl: String, val playerPosition: String, val playerPositionInitial: String, - val playerGoalCount: Int -) - -data class LineUp( - val lineUpId: Double, - val matchId: Int, - val playerId: Int, - val teamId: Int, - val playerName: String, - val playerBackNumber: Int, - val formationField: String?, - val formationPosition: Int?, - val positionId: Int, - val positionCategory: String, - val positionInitial: String + val playerGoalCount: Int, + val playerParticipationCount : Int ) data class Performance( diff --git a/core/domain/src/main/java/com/eshc/goonersapp/core/domain/model/match/MatchLineup.kt b/core/domain/src/main/java/com/eshc/goonersapp/core/domain/model/match/MatchLineup.kt new file mode 100644 index 00000000..91ba5773 --- /dev/null +++ b/core/domain/src/main/java/com/eshc/goonersapp/core/domain/model/match/MatchLineup.kt @@ -0,0 +1,49 @@ +package com.eshc.goonersapp.core.domain.model.match + +import com.eshc.goonersapp.core.domain.model.player.Player + +data class MatchLineup( + val homeLineup : TeamLineup, + val awayLineup : TeamLineup +){ + fun getMyTeamLineup(myTeamId : Int) : TeamLineup { + return if(homeLineup.teamId == myTeamId) homeLineup else awayLineup + } +} + +data class TeamLineup( + val teamId : Int, + val formation : String, + val playerLineup : List +){ + private fun getFormationAsList() : List { + return listOf(1) + formation.split("-").map { it.toIntOrNull() ?: 0 } + } + + + fun groupStartingPlayersByPosition(): List> { + val actualPositionList = getFormationAsList() + val sortedPlayers = playerLineup.filter { it.formationField != "null" }.sortedBy { it.formationPosition } + + return actualPositionList.mapIndexed { index, i -> + val p = i - 1 + val s = actualPositionList.subList(0, index).sum() + sortedPlayers.slice(s..s + p) + } + } +} + +data class PlayerLineup( + val lineUpId: Long, + val matchId: Int, + val playerId: Int, + val teamId: Int, + val playerName: String, + val playerImageUrl : String, + val playerBackNumber: Int, + val formationField: String?, + val formationPosition: Int?, + val positionId: Int, + val positionCategory: String, + val positionInitial: String +) \ No newline at end of file diff --git a/core/domain/src/main/java/com/eshc/goonersapp/core/domain/model/season/League.kt b/core/domain/src/main/java/com/eshc/goonersapp/core/domain/model/season/League.kt new file mode 100644 index 00000000..1b8a2f68 --- /dev/null +++ b/core/domain/src/main/java/com/eshc/goonersapp/core/domain/model/season/League.kt @@ -0,0 +1,11 @@ +package com.eshc.goonersapp.core.domain.model.season + +data class League( + val leagueId: Int, + val leagueName: String, + val shortCode: String, + val leagueImgUrl: String, + val seasonId: Int, + val season: String, + val teamId: Int +) \ 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 new file mode 100644 index 00000000..d6f8a8d1 --- /dev/null +++ b/core/domain/src/main/java/com/eshc/goonersapp/core/domain/model/season/Rank.kt @@ -0,0 +1,14 @@ +package com.eshc.goonersapp.core.domain.model.season + +data class Rank( + val standingId: Int, + val position: Int, + val points: Int, + val wins: Int, + val loss: Int, + val draw: Int, + val goalDifference: Int, + val teamId: Int, + val teamName: String, + val shortCode: String +) \ No newline at end of file diff --git a/core/domain/src/main/java/com/eshc/goonersapp/core/domain/model/season/Season.kt b/core/domain/src/main/java/com/eshc/goonersapp/core/domain/model/season/Season.kt new file mode 100644 index 00000000..5fd1c252 --- /dev/null +++ b/core/domain/src/main/java/com/eshc/goonersapp/core/domain/model/season/Season.kt @@ -0,0 +1,3 @@ +package com.eshc.goonersapp.core.domain.model.season + +data class Season(val seasonId: Int, val season: String) \ No newline at end of file diff --git a/core/domain/src/main/java/com/eshc/goonersapp/core/domain/repository/MatchRepository.kt b/core/domain/src/main/java/com/eshc/goonersapp/core/domain/repository/MatchRepository.kt index bb41a976..e624246b 100644 --- a/core/domain/src/main/java/com/eshc/goonersapp/core/domain/repository/MatchRepository.kt +++ b/core/domain/src/main/java/com/eshc/goonersapp/core/domain/repository/MatchRepository.kt @@ -4,6 +4,7 @@ import com.eshc.goonersapp.core.domain.model.DataResult import com.eshc.goonersapp.core.domain.model.match.Match import com.eshc.goonersapp.core.domain.model.match.MatchData import com.eshc.goonersapp.core.domain.model.match.MatchInformation +import com.eshc.goonersapp.core.domain.model.match.MatchLineup import kotlinx.coroutines.flow.Flow interface MatchRepository { @@ -18,4 +19,5 @@ interface MatchRepository { fun getRecentlyMatch() : Flow> + fun getMatchLineup(matchId: Int) : Flow> } \ No newline at end of file diff --git a/core/domain/src/main/java/com/eshc/goonersapp/core/domain/repository/SeasonRepository.kt b/core/domain/src/main/java/com/eshc/goonersapp/core/domain/repository/SeasonRepository.kt new file mode 100644 index 00000000..e94f202c --- /dev/null +++ b/core/domain/src/main/java/com/eshc/goonersapp/core/domain/repository/SeasonRepository.kt @@ -0,0 +1,19 @@ +package com.eshc.goonersapp.core.domain.repository + +import com.eshc.goonersapp.core.domain.model.DataResult +import com.eshc.goonersapp.core.domain.model.season.League +import com.eshc.goonersapp.core.domain.model.season.Rank +import com.eshc.goonersapp.core.domain.model.season.Season +import kotlinx.coroutines.flow.Flow + +interface SeasonRepository { + + fun getSeasonListByTeam(teamId: Int): Flow>> + + fun getLeagueListAsCurrentSeasonByTeam(teamId: Int): Flow>> + + fun getPreviewRankListByTeamAndSeason( + teamId: Int, + seasonId: Int + ): Flow>> +} \ No newline at end of file diff --git a/core/domain/src/main/java/com/eshc/goonersapp/core/domain/usecase/match/GetMatchLineupUseCase.kt b/core/domain/src/main/java/com/eshc/goonersapp/core/domain/usecase/match/GetMatchLineupUseCase.kt new file mode 100644 index 00000000..75f7adf4 --- /dev/null +++ b/core/domain/src/main/java/com/eshc/goonersapp/core/domain/usecase/match/GetMatchLineupUseCase.kt @@ -0,0 +1,17 @@ +package com.eshc.goonersapp.core.domain.usecase.match + +import com.eshc.goonersapp.core.domain.model.DataResult +import com.eshc.goonersapp.core.domain.model.match.MatchLineup +import com.eshc.goonersapp.core.domain.repository.MatchRepository +import kotlinx.coroutines.flow.Flow +import javax.inject.Inject + +class GetMatchLineupUseCase @Inject constructor( + private val matchRepository: MatchRepository +) { + operator fun invoke( + matchId: Int, + ): Flow> = matchRepository.getMatchLineup( + matchId + ) +} \ No newline at end of file diff --git a/core/domain/src/main/java/com/eshc/goonersapp/core/domain/usecase/season/GetLeagueListAsCurrentSeasonByTeamUseCase.kt b/core/domain/src/main/java/com/eshc/goonersapp/core/domain/usecase/season/GetLeagueListAsCurrentSeasonByTeamUseCase.kt new file mode 100644 index 00000000..eac9ce27 --- /dev/null +++ b/core/domain/src/main/java/com/eshc/goonersapp/core/domain/usecase/season/GetLeagueListAsCurrentSeasonByTeamUseCase.kt @@ -0,0 +1,15 @@ +package com.eshc.goonersapp.core.domain.usecase.season + +import com.eshc.goonersapp.core.domain.model.DataResult +import com.eshc.goonersapp.core.domain.model.season.League +import com.eshc.goonersapp.core.domain.repository.SeasonRepository +import kotlinx.coroutines.flow.Flow +import javax.inject.Inject + +class GetLeagueListAsCurrentSeasonByTeamUseCase @Inject constructor( + private val seasonRepository: SeasonRepository +) { + operator fun invoke( + teamId: Int + ): Flow>> = seasonRepository.getLeagueListAsCurrentSeasonByTeam(teamId) +} \ 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 new file mode 100644 index 00000000..2851aad5 --- /dev/null +++ b/core/domain/src/main/java/com/eshc/goonersapp/core/domain/usecase/season/GetPreviewRankListByTeamAndSeasonUseCase.kt @@ -0,0 +1,19 @@ +package com.eshc.goonersapp.core.domain.usecase.season + +import com.eshc.goonersapp.core.domain.model.DataResult +import com.eshc.goonersapp.core.domain.model.season.Rank +import com.eshc.goonersapp.core.domain.repository.SeasonRepository +import kotlinx.coroutines.flow.Flow +import javax.inject.Inject + +class GetPreviewRankListByTeamAndSeasonUseCase @Inject constructor( + private val seasonRepository: SeasonRepository +) { + operator fun invoke( + teamId: Int, + seasonId: Int + ): Flow>> = seasonRepository.getPreviewRankListByTeamAndSeason( + teamId = teamId, + seasonId = seasonId + ) +} \ No newline at end of file diff --git a/core/domain/src/main/java/com/eshc/goonersapp/core/domain/usecase/season/GetSeasonListByTeamUseCase.kt b/core/domain/src/main/java/com/eshc/goonersapp/core/domain/usecase/season/GetSeasonListByTeamUseCase.kt new file mode 100644 index 00000000..385f9023 --- /dev/null +++ b/core/domain/src/main/java/com/eshc/goonersapp/core/domain/usecase/season/GetSeasonListByTeamUseCase.kt @@ -0,0 +1,15 @@ +package com.eshc.goonersapp.core.domain.usecase.season + +import com.eshc.goonersapp.core.domain.model.DataResult +import com.eshc.goonersapp.core.domain.model.season.Season +import com.eshc.goonersapp.core.domain.repository.SeasonRepository +import kotlinx.coroutines.flow.Flow +import javax.inject.Inject + +class GetSeasonListByTeamUseCase @Inject constructor( + private val seasonRepository: SeasonRepository +) { + operator fun invoke( + teamId: Int + ): Flow>> = seasonRepository.getSeasonListByTeam(teamId) +} \ No newline at end of file diff --git a/core/network/src/main/java/com/eshc/goonersapp/core/network/MatchNetworkDataSource.kt b/core/network/src/main/java/com/eshc/goonersapp/core/network/MatchNetworkDataSource.kt index 2ccbb9df..c189d6f0 100644 --- a/core/network/src/main/java/com/eshc/goonersapp/core/network/MatchNetworkDataSource.kt +++ b/core/network/src/main/java/com/eshc/goonersapp/core/network/MatchNetworkDataSource.kt @@ -4,6 +4,7 @@ import com.eshc.goonersapp.core.network.model.NetworkResult import com.eshc.goonersapp.core.network.model.match.RemoteMatch import com.eshc.goonersapp.core.network.model.match.RemoteMatchData import com.eshc.goonersapp.core.network.model.match.RemoteMatchInformation +import com.eshc.goonersapp.core.network.model.match.RemoteMatchLineup interface MatchNetworkDataSource { @@ -20,4 +21,6 @@ interface MatchNetworkDataSource { suspend fun getUpcomingMatches(): NetworkResult> suspend fun getRecentlyMatch(): NetworkResult + + suspend fun getMatchLineup(matchId: Int) : NetworkResult } \ No newline at end of file diff --git a/core/network/src/main/java/com/eshc/goonersapp/core/network/api/MatchNetworkService.kt b/core/network/src/main/java/com/eshc/goonersapp/core/network/api/MatchNetworkService.kt index 5ff24821..b93ee4af 100644 --- a/core/network/src/main/java/com/eshc/goonersapp/core/network/api/MatchNetworkService.kt +++ b/core/network/src/main/java/com/eshc/goonersapp/core/network/api/MatchNetworkService.kt @@ -4,6 +4,7 @@ import com.eshc.goonersapp.core.network.model.BaseResponse import com.eshc.goonersapp.core.network.model.match.RemoteMatch import com.eshc.goonersapp.core.network.model.match.RemoteMatchData import com.eshc.goonersapp.core.network.model.match.RemoteMatchInformation +import com.eshc.goonersapp.core.network.model.match.RemoteMatchLineup import retrofit2.Response import retrofit2.http.GET import retrofit2.http.Path @@ -40,4 +41,9 @@ interface MatchNetworkService { suspend fun getRecentlyMatch( @Query("teamId") teamId : Int = 19 ) : Response> + + @GET(value = "$MATCH_BASE_URL/lineup") + suspend fun getMatchLineup( + @Query("matchId") matchId: Int + ) : Response> } \ No newline at end of file diff --git a/core/network/src/main/java/com/eshc/goonersapp/core/network/fake/FakeMatchDataSource.kt b/core/network/src/main/java/com/eshc/goonersapp/core/network/fake/FakeMatchDataSource.kt index 1ff4a05c..29a88cbd 100644 --- a/core/network/src/main/java/com/eshc/goonersapp/core/network/fake/FakeMatchDataSource.kt +++ b/core/network/src/main/java/com/eshc/goonersapp/core/network/fake/FakeMatchDataSource.kt @@ -6,6 +6,8 @@ import com.eshc.goonersapp.core.network.model.match.Performance import com.eshc.goonersapp.core.network.model.match.RemoteMatch import com.eshc.goonersapp.core.network.model.match.RemoteMatchData import com.eshc.goonersapp.core.network.model.match.RemoteMatchInformation +import com.eshc.goonersapp.core.network.model.match.RemoteMatchLineup +import com.eshc.goonersapp.core.network.model.match.RemoteTeamLineup import javax.inject.Inject /** @@ -31,6 +33,10 @@ import javax.inject.Inject * * getRecentlyMatch() * - return [NetworkResult.Success] + * + * getMatchLineup(matchId: Int) + * - if matchId is over 39 then return [NetworkResult.Error] + * - else return [NetworkResult.Success] */ class FakeMatchDataSource @Inject constructor(): MatchNetworkDataSource { override suspend fun getMatch(matchId: Int): NetworkResult { @@ -57,7 +63,6 @@ class FakeMatchDataSource @Inject constructor(): MatchNetworkDataSource { NetworkResult.Success( RemoteMatchInformation( notablePlayer = null, - lineUp = listOf(), performance = Performance() ) ) @@ -89,4 +94,16 @@ class FakeMatchDataSource @Inject constructor(): MatchNetworkDataSource { ) } + override suspend fun getMatchLineup(matchId: Int): NetworkResult { + return if (matchId < 39) { + NetworkResult.Success( + RemoteMatchLineup( + homeLineup = RemoteTeamLineup(), + awayLineup = RemoteTeamLineup() + ) + ) + } else { + NetworkResult.Error(code = 404, message = "Not Found") + } + } } \ No newline at end of file diff --git a/core/network/src/main/java/com/eshc/goonersapp/core/network/fake/FakeSeasonDataSource.kt b/core/network/src/main/java/com/eshc/goonersapp/core/network/fake/FakeSeasonDataSource.kt new file mode 100644 index 00000000..9e406018 --- /dev/null +++ b/core/network/src/main/java/com/eshc/goonersapp/core/network/fake/FakeSeasonDataSource.kt @@ -0,0 +1,68 @@ +package com.eshc.goonersapp.core.network.fake + +import com.eshc.goonersapp.core.network.SeasonNetworkDataSource +import com.eshc.goonersapp.core.network.model.NetworkResult +import com.eshc.goonersapp.core.network.model.season.RemoteLeague +import com.eshc.goonersapp.core.network.model.season.RemoteRank +import com.eshc.goonersapp.core.network.model.season.RemoteSeason +import javax.inject.Inject + +/** + * Created By KanuKim97 + * + * [FakeSeasonDataSource] + * - fakeSeasonDataSource used in data Layer Test and network Layer Unit Test + */ +class FakeSeasonDataSource @Inject constructor(): SeasonNetworkDataSource { + override suspend fun getSeasonListByTeam(teamId: Int): NetworkResult> { + return if (teamId == 19) { + NetworkResult.Success( + listOf( + RemoteSeason( + seasonId = 21366, + season = "2023/2024" + ), + RemoteSeason( + seasonId = 21365, + season = "2022/2023" + ) + ) + ) + } else { + NetworkResult.Error(code = 404, message = "Not Found") + } + } + + override suspend fun getLeagueListAsCurrentSeasonByTeam( + teamId: Int + ): NetworkResult> { + return if (teamId == 19) { + NetworkResult.Success( + listOf( + RemoteLeague( + leagueId = 1, + leagueName = "Premier League", + shortCode = "PL", + leagueImageUrl = "", + seasonId = 21366, + season = "2023/2024", + teamId = teamId + ) + ) + ) + } else { + NetworkResult.Error(code = 404, message = "Not Found") + } + } + + override suspend fun getPreviewRankListByTeamAndSeason( + teamId: Int, + seasonId: Int, + ): NetworkResult> { + return if (teamId == 19 && seasonId < 21367) { + NetworkResult.Success(listOf()) + } else { + NetworkResult.Error(404, "Not Found") + } + } +} \ No newline at end of file diff --git a/core/network/src/main/java/com/eshc/goonersapp/core/network/model/match/RemoteMatchInformation.kt b/core/network/src/main/java/com/eshc/goonersapp/core/network/model/match/RemoteMatchInformation.kt index 8da92a8d..0ff42d67 100644 --- a/core/network/src/main/java/com/eshc/goonersapp/core/network/model/match/RemoteMatchInformation.kt +++ b/core/network/src/main/java/com/eshc/goonersapp/core/network/model/match/RemoteMatchInformation.kt @@ -6,7 +6,6 @@ import kotlinx.serialization.Serializable @Serializable data class RemoteMatchInformation( @SerialName("notablePlayer") val notablePlayer: NotablePlayer? = null, - @SerialName("lineUp") val lineUp: List, @SerialName("performance") val performance: Performance ) @@ -14,28 +13,13 @@ data class RemoteMatchInformation( data class NotablePlayer( @SerialName("player_id") val playerId: Int, @SerialName("player_name") val playerName: String, - @SerialName("height") val height: Int, - @SerialName("weight") val weight: Int, - @SerialName("player_image_url") val playerImage: String, - @SerialName("position") val position: String, - @SerialName("position_initial") val positionInitial: String, - @SerialName("goal_count") val goalCount: Int -) - -@Serializable -data class LineUp( - @SerialName("lineup_id") val lineUpId: Double = 0.0, - @SerialName("match_id") val matchId: Int = 0, - @SerialName("player_id") val playerId: Int = 0, - @SerialName("team_id") val teamId: Int = 0, - @SerialName("player_name") val playerName: String = "", - @SerialName("player_image_url") val playerImageUrl: String = "", - @SerialName("jersey_number") val jerseyNumber: Int = 0, - @SerialName("formation_field") val formationField: String? = "", - @SerialName("formation_position") val formationPosition: Int? = 0, - @SerialName("position_id") val positionId: Int = 0, - @SerialName("position_category") val positionCategory: String = "", - @SerialName("position_initial") val positionInitial: String = "" + @SerialName("height") val height: Int?, + @SerialName("weight") val weight: Int?, + @SerialName("player_image_url") val playerImage: String?, + @SerialName("position") val position: String?, + @SerialName("position_initial") val positionInitial: String?, + @SerialName("goal_count") val goalCount: Int?, + @SerialName("participation_count") val participationCount: Int? ) @Serializable diff --git a/core/network/src/main/java/com/eshc/goonersapp/core/network/model/match/RemoteMatchLineup.kt b/core/network/src/main/java/com/eshc/goonersapp/core/network/model/match/RemoteMatchLineup.kt new file mode 100644 index 00000000..40cf2f0d --- /dev/null +++ b/core/network/src/main/java/com/eshc/goonersapp/core/network/model/match/RemoteMatchLineup.kt @@ -0,0 +1,33 @@ +package com.eshc.goonersapp.core.network.model.match + +import kotlinx.serialization.SerialName +import kotlinx.serialization.Serializable + +@Serializable +data class RemoteMatchLineup( + @SerialName("home_lineup") val homeLineup : RemoteTeamLineup = RemoteTeamLineup(), + @SerialName("away_lineup") val awayLineup : RemoteTeamLineup = RemoteTeamLineup(), +) + +@Serializable +data class RemoteTeamLineup( + @SerialName("team_id") val teamId : Long = 0L, + @SerialName("formaition") val formation : String = "", + @SerialName("players") val players : List = emptyList() +) + +@Serializable +data class RemotePlayerLineup( + @SerialName("lineup_id") val lineUpId: Long = 0L, + @SerialName("match_id") val matchId: Int = 0, + @SerialName("player_id") val playerId: Int = 0, + @SerialName("team_id") val teamId: Int = 0, + @SerialName("player_name") val playerName: String = "", + @SerialName("player_image_url") val playerImageUrl: String = "", + @SerialName("jersey_number") val jerseyNumber: Int = 0, + @SerialName("formation_field") val formationField: String? = "", + @SerialName("formation_position") val formationPosition: Int? = 0, + @SerialName("position_id") val positionId: Int = 0, + @SerialName("position_category") val positionCategory: String = "", + @SerialName("position_initial") val positionInitial: String = "" +) \ No newline at end of file diff --git a/core/network/src/main/java/com/eshc/goonersapp/core/network/remote/MatchNetworkDataSourceImpl.kt b/core/network/src/main/java/com/eshc/goonersapp/core/network/remote/MatchNetworkDataSourceImpl.kt index 56adf276..18897080 100644 --- a/core/network/src/main/java/com/eshc/goonersapp/core/network/remote/MatchNetworkDataSourceImpl.kt +++ b/core/network/src/main/java/com/eshc/goonersapp/core/network/remote/MatchNetworkDataSourceImpl.kt @@ -7,6 +7,7 @@ import com.eshc.goonersapp.core.network.model.handleApi import com.eshc.goonersapp.core.network.model.match.RemoteMatch import com.eshc.goonersapp.core.network.model.match.RemoteMatchData import com.eshc.goonersapp.core.network.model.match.RemoteMatchInformation +import com.eshc.goonersapp.core.network.model.match.RemoteMatchLineup import javax.inject.Inject import javax.inject.Singleton @@ -51,4 +52,10 @@ class MatchNetworkDataSourceImpl @Inject constructor( matchNetworkService.getRecentlyMatch() } } + + override suspend fun getMatchLineup(matchId: Int): NetworkResult { + return handleApi { + matchNetworkService.getMatchLineup(matchId) + } + } } diff --git a/core/network/src/main/java/com/eshc/goonersapp/core/network/remote/SeasonNetworkDataSourceImpl.kt b/core/network/src/main/java/com/eshc/goonersapp/core/network/remote/SeasonNetworkDataSourceImpl.kt index 46359a2f..7425ffdc 100644 --- a/core/network/src/main/java/com/eshc/goonersapp/core/network/remote/SeasonNetworkDataSourceImpl.kt +++ b/core/network/src/main/java/com/eshc/goonersapp/core/network/remote/SeasonNetworkDataSourceImpl.kt @@ -1,12 +1,9 @@ package com.eshc.goonersapp.core.network.remote -import com.eshc.goonersapp.core.network.PlayerNetworkDataSource import com.eshc.goonersapp.core.network.SeasonNetworkDataSource -import com.eshc.goonersapp.core.network.api.PlayerNetworkService import com.eshc.goonersapp.core.network.api.SeasonNetworkService import com.eshc.goonersapp.core.network.model.NetworkResult import com.eshc.goonersapp.core.network.model.handleApi -import com.eshc.goonersapp.core.network.model.player.RemotePlayer import com.eshc.goonersapp.core.network.model.season.RemoteLeague import com.eshc.goonersapp.core.network.model.season.RemoteRank import com.eshc.goonersapp.core.network.model.season.RemoteSeason diff --git a/core/network/src/test/java/com/eshc/goonersapp/core/network/FakeMatchDataSourceTest.kt b/core/network/src/test/java/com/eshc/goonersapp/core/network/FakeMatchDataSourceTest.kt index f1c878b5..0d66131a 100644 --- a/core/network/src/test/java/com/eshc/goonersapp/core/network/FakeMatchDataSourceTest.kt +++ b/core/network/src/test/java/com/eshc/goonersapp/core/network/FakeMatchDataSourceTest.kt @@ -2,9 +2,10 @@ package com.eshc.goonersapp.core.network import com.eshc.goonersapp.core.network.fake.FakeMatchDataSource import com.eshc.goonersapp.core.network.model.NetworkResult -import com.eshc.goonersapp.core.network.model.match.LineUp -import com.eshc.goonersapp.core.network.model.match.RemoteMatchDetail +import com.eshc.goonersapp.core.network.model.match.Performance import com.eshc.goonersapp.core.network.model.match.RemoteMatch +import com.eshc.goonersapp.core.network.model.match.RemoteMatchDetail +import com.eshc.goonersapp.core.network.model.match.RemoteMatchLineup import kotlinx.coroutines.runBlocking import org.junit.Assert.assertEquals import org.junit.Before @@ -52,8 +53,8 @@ class FakeMatchDataSourceTest { when (result) { is NetworkResult.Success -> { assertEquals( - listOf(), - result.data.lineUp + Performance(), + result.data.performance ) } is NetworkResult.Error -> { @@ -113,4 +114,19 @@ class FakeMatchDataSourceTest { } } + + @Test + fun fakeGetMatchLineup() = runBlocking { + when (val result = fakeMatchDataSource.getMatchLineup(38)) { + is NetworkResult.Success -> { + assertEquals( + RemoteMatchLineup(), + result.data + ) + } + is NetworkResult.Error -> { /* Nothing */ } + is NetworkResult.Exception -> { /* Nothing */ } + } + + } } \ No newline at end of file diff --git a/core/network/src/test/java/com/eshc/goonersapp/core/network/FakeSeasonDataSourceTest.kt b/core/network/src/test/java/com/eshc/goonersapp/core/network/FakeSeasonDataSourceTest.kt new file mode 100644 index 00000000..71d7d8dc --- /dev/null +++ b/core/network/src/test/java/com/eshc/goonersapp/core/network/FakeSeasonDataSourceTest.kt @@ -0,0 +1,132 @@ +package com.eshc.goonersapp.core.network + +import com.eshc.goonersapp.core.network.fake.FakeSeasonDataSource +import com.eshc.goonersapp.core.network.model.NetworkResult +import com.eshc.goonersapp.core.network.model.season.RemoteRank +import kotlinx.coroutines.runBlocking +import org.junit.Assert.assertEquals +import org.junit.Assert.fail +import org.junit.Before +import org.junit.Test + +/** + * Created By KanuKim97 + * + * [FakeSeasonDataSourceTest] + * - [FakeSeasonDataSource] test codes + */ +class FakeSeasonDataSourceTest { + private lateinit var fakeSeasonDataSource: FakeSeasonDataSource + + @Before + fun setUp() { + fakeSeasonDataSource = FakeSeasonDataSource() + } + + @Test + fun fake_get_season_list_by_team_as_success() = runBlocking { + when (val result = fakeSeasonDataSource.getSeasonListByTeam(19)) { + is NetworkResult.Success -> { + assertEquals( + "2023/2024", + result.data.first().season + ) + } + is NetworkResult.Error -> { + fail("Test should not be reached here, excepted Network Result.Success") + } + is NetworkResult.Exception -> { + fail("Test should not be reached here, excepted Network Result.Success") + } + } + } + + @Test + fun fake_get_season_list_by_team_as_error() = runBlocking { + when (val result = fakeSeasonDataSource.getSeasonListByTeam(20)) { + is NetworkResult.Error -> { + assertEquals(404, result.code) + } + is NetworkResult.Success -> { + fail("Test should not be reached here, excepted Network Result.Error") + } + is NetworkResult.Exception -> { + fail("Test should not be reached here, excepted Network Result.Error") + } + } + } + + @Test + fun fake_get_league_list_as_current_season_by_team_as_success() = runBlocking { + when (val result = fakeSeasonDataSource.getLeagueListAsCurrentSeasonByTeam(19)) { + is NetworkResult.Success -> { + assertEquals( + 21366, + result.data.first().seasonId + ) + } + is NetworkResult.Error -> { + fail("Test should not be reached here, excepted Network Result.Success") + } + is NetworkResult.Exception -> { + fail("Test should not be reached here, excepted Network Result.Success") + } + } + } + + @Test + fun fake_get_league_list_as_current_season_by_team_as_error() = runBlocking { + when (val result = fakeSeasonDataSource.getLeagueListAsCurrentSeasonByTeam(20)) { + is NetworkResult.Error -> { + assertEquals(404, result.code) + } + is NetworkResult.Success -> { + fail("Test should not be reached here, excepted Network Result.Error") + } + is NetworkResult.Exception -> { + fail("Test should not be reached here, excepted Network Result.Error") + } + } + } + + @Test + fun fake_get_preview_rank_list_by_team_and_season_as_success() = runBlocking { + val result = fakeSeasonDataSource.getPreviewRankListByTeamAndSeason( + teamId = 19, + seasonId = 21366 + ) + + when (result) { + is NetworkResult.Success -> { + assertEquals(listOf(), result.data) + } + is NetworkResult.Error -> { + fail("Test should not be reached here, excepted Network Result.Success") + } + is NetworkResult.Exception -> { + fail("Test should not be reached here, excepted Network Result.Success") + } + } + } + + + @Test + fun fake_get_preview_rank_list_by_team_and_season_as_error() = runBlocking { + val result = fakeSeasonDataSource.getPreviewRankListByTeamAndSeason( + teamId = 20, + seasonId = 21366 + ) + + when (result) { + is NetworkResult.Error -> { + assertEquals(404, result.code) + } + is NetworkResult.Success -> { + fail("Test should not be reached here, excepted Network Result.Error") + } + is NetworkResult.Exception -> { + fail("Test should not be reached here, excepted Network Result.Error") + } + } + } +} \ No newline at end of file diff --git a/core/network/src/test/java/com/eshc/goonersapp/core/network/RemoteMatchApiTest.kt b/core/network/src/test/java/com/eshc/goonersapp/core/network/RemoteMatchApiTest.kt index c525ad49..24d0ca50 100644 --- a/core/network/src/test/java/com/eshc/goonersapp/core/network/RemoteMatchApiTest.kt +++ b/core/network/src/test/java/com/eshc/goonersapp/core/network/RemoteMatchApiTest.kt @@ -117,4 +117,16 @@ class RemoteMatchApiTest { val response = matchApi.getRecentlyMatch() assertEquals(responseOkCode, response.code()) } + + /** + * (Test Passed) + * + * Match Lineup network communication test + * - when response code is 200, Test Passed + */ + @Test + fun test_GET_LINEUP_STATUS_IS_OK() = runBlocking { + val response = matchApi.getMatchLineup(18138603) + assertEquals(responseOkCode, response.code()) + } } \ No newline at end of file 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 d1eec783..2a51ebe7 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,7 +3,6 @@ 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.Spacer import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.height import androidx.compose.foundation.layout.padding @@ -23,6 +22,7 @@ import androidx.compose.ui.unit.dp import androidx.hilt.navigation.compose.hiltViewModel import androidx.lifecycle.compose.collectAsStateWithLifecycle import com.eshc.goonersapp.core.common.util.DateUtil +import com.eshc.goonersapp.core.designsystem.theme.ColorFF181818 import com.eshc.goonersapp.core.designsystem.theme.GnrTypography import com.eshc.goonersapp.feature.home.component.DashboardCard import com.eshc.goonersapp.feature.home.component.RecentlyMatchCard @@ -41,12 +41,8 @@ fun HomeRoute( val recentlyResultUiState by viewModel.recentlyResultUiStateFlow.collectAsStateWithLifecycle() Scaffold( - topBar = { - topBar() - }, - bottomBar = { - bottomBar() - } + topBar = { topBar() }, + bottomBar = { bottomBar() } ) { padding -> HomeScreen( modifier = Modifier.padding(padding), @@ -64,13 +60,13 @@ fun HomeScreen( ) { LazyColumn( modifier = modifier, - contentPadding = PaddingValues(top = 12.dp) + contentPadding = PaddingValues(top = 30.dp) ) { item { - Spacer(modifier = Modifier.height(12.dp)) Text( - modifier = Modifier.padding(start = 8.dp), text = "Team Dashboard", + modifier = Modifier.padding(start = 8.dp), + color = ColorFF181818, style = GnrTypography.subtitleMedium ) DashboardCard() @@ -78,10 +74,10 @@ fun HomeScreen( item { Text( - modifier = Modifier.padding(start = 8.dp), text = "Upcoming Matches", + modifier = Modifier.padding(start = 8.dp), + color = ColorFF181818, style = GnrTypography.subtitleMedium, - color = Color.Black, ) when (upcomingMatchesUiState) { is UpcomingMatchesUiState.Loading -> { 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 index 3c5c4ea3..f50b7af3 100644 --- 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 @@ -26,6 +26,7 @@ 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 @@ -39,6 +40,7 @@ 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 @@ -84,7 +86,7 @@ fun RecentlyMatchCard( horizontalAlignment = Alignment.CenterHorizontally ) { Row( - modifier = modifier.fillMaxWidth(), + modifier = modifier.fillMaxWidth().padding(bottom = 15.dp), verticalAlignment = Alignment.CenterVertically, horizontalArrangement = Arrangement.SpaceBetween ) { @@ -95,7 +97,9 @@ fun RecentlyMatchCard( competitionName = competitionName, verticalAlignment = Alignment.CenterVertically, ) - Row { + Column( + horizontalAlignment = Alignment.End + ) { Text( text = time, color = ColorFF4C68A7, @@ -104,8 +108,14 @@ fun RecentlyMatchCard( Spacer(modifier = modifier.size(5.dp)) Text( text = location, - color = ColorFF4C68A7, - style = GnrTypography.descriptionMedium + color = ColorFF9E9E9E, + style = GnrTypography.descriptionMedium.copy( + lineBreak = LineBreak( + strategy = LineBreak.Strategy.Balanced, + strictness = LineBreak.Strictness.Strict, + wordBreak = LineBreak.WordBreak.Default + ) + ) ) } } @@ -303,9 +313,10 @@ fun UpcomingHomeTeamInfo( modifier = modifier.size(30.dp), contentDescription = "Home Team Logo" ) - Spacer(modifier = modifier.size(10.dp)) + Spacer(modifier = modifier.size(7.dp)) Text( text = homeShortName, + color = ColorFF181818, style = GnrTypography.subtitleMedium ) } @@ -325,7 +336,7 @@ fun UpcomingAwayTeamInfo( text = awayShortName, style = GnrTypography.subtitleMedium ) - Spacer(modifier = modifier.size(10.dp)) + Spacer(modifier = modifier.size(7.dp)) AsyncImage( model = awayUrl, modifier = modifier.size(30.dp), @@ -350,7 +361,7 @@ fun UpcomingMatchDateInfo( ), border = BorderStroke( width = 0.5.dp, - color = ColorFFC3CDE2 + color = ColorFFE6EDFC ) ) { Row( @@ -360,6 +371,7 @@ fun UpcomingMatchDateInfo( content = { Text( text = time, + color = ColorFF4C68A7, overflow = TextOverflow.Ellipsis, maxLines = 1, style = GnrTypography.body2Medium @@ -367,6 +379,7 @@ fun UpcomingMatchDateInfo( Spacer(modifier = Modifier.size(5.dp)) Text( text = location, + color = ColorFF4C68A7, overflow = TextOverflow.Ellipsis, maxLines = 1, style = GnrTypography.body2Medium diff --git a/feature/login/src/main/java/com/eshc/goonersapp/feature/login/LoginScreen.kt b/feature/login/src/main/java/com/eshc/goonersapp/feature/login/LoginScreen.kt index 3f8ab2d4..22303d9d 100644 --- a/feature/login/src/main/java/com/eshc/goonersapp/feature/login/LoginScreen.kt +++ b/feature/login/src/main/java/com/eshc/goonersapp/feature/login/LoginScreen.kt @@ -16,23 +16,23 @@ import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.graphics.Color import androidx.compose.ui.res.painterResource -import androidx.compose.ui.text.font.FontWeight import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.unit.dp import com.eshc.goonersapp.core.designsystem.component.GnrTextFiled -import com.eshc.goonersapp.core.designsystem.component.TopBar -import com.eshc.goonersapp.core.designsystem.theme.pretendard +import com.eshc.goonersapp.core.designsystem.component.GnrTopBar @Composable fun LoginScreen( onShowSnackbar: (String) -> Unit, - onClickSignUp : () -> Unit + onClickSignUp : () -> Unit, + onBackIconClick: () -> Unit ) { Column( modifier = Modifier.fillMaxSize() ) { - TopBar( - title = "LOGIN" + GnrTopBar( + title = "LOGIN", + onBackIconClick = onBackIconClick ) Image( @@ -113,6 +113,7 @@ fun LoginScreen( fun PreviewLoginScreen() { LoginScreen( onShowSnackbar = {}, - onClickSignUp = {} + onClickSignUp = {}, + onBackIconClick = {} ) } \ No newline at end of file diff --git a/feature/login/src/main/java/com/eshc/goonersapp/feature/login/SignUpScreen.kt b/feature/login/src/main/java/com/eshc/goonersapp/feature/login/SignUpScreen.kt index 5d630db7..6e6e8677 100644 --- a/feature/login/src/main/java/com/eshc/goonersapp/feature/login/SignUpScreen.kt +++ b/feature/login/src/main/java/com/eshc/goonersapp/feature/login/SignUpScreen.kt @@ -24,13 +24,14 @@ import androidx.compose.ui.unit.dp import androidx.hilt.navigation.compose.hiltViewModel import androidx.lifecycle.compose.collectAsStateWithLifecycle import com.eshc.goonersapp.core.designsystem.component.GnrTextFiled -import com.eshc.goonersapp.core.designsystem.component.TopBar +import com.eshc.goonersapp.core.designsystem.component.GnrTopBar import com.eshc.goonersapp.core.designsystem.theme.pretendard @OptIn(ExperimentalFoundationApi::class) @Composable fun SignUpScreen( onShowSnackbar: (String) -> Unit, + onBackIconClick: () -> Unit, viewModel: SignUpViewModel = hiltViewModel() ) { val inputTypeList by viewModel.signUpInputTypeList.collectAsStateWithLifecycle() @@ -41,8 +42,9 @@ fun SignUpScreen( Column( modifier = Modifier.fillMaxSize() ) { - TopBar( - title = "SIGN UP" + GnrTopBar( + title = "SIGN UP", + onBackIconClick = onBackIconClick ) LazyColumn( modifier = Modifier @@ -125,5 +127,8 @@ fun SignUpScreen( @Preview @Composable fun PreviewSignUpScreen() { - SignUpScreen(onShowSnackbar = {}) + SignUpScreen( + onShowSnackbar = {}, + onBackIconClick = {} + ) } \ No newline at end of file diff --git a/feature/login/src/main/java/com/eshc/goonersapp/feature/login/navigation/LoginNavigation.kt b/feature/login/src/main/java/com/eshc/goonersapp/feature/login/navigation/LoginNavigation.kt index 660763a4..c7a12b67 100644 --- a/feature/login/src/main/java/com/eshc/goonersapp/feature/login/navigation/LoginNavigation.kt +++ b/feature/login/src/main/java/com/eshc/goonersapp/feature/login/navigation/LoginNavigation.kt @@ -20,26 +20,30 @@ fun NavController.navigateToSignUp(navOptions: NavOptions? = null) { fun NavGraphBuilder.loginScreen( onShowSnackbar : (String) -> Unit, - onClickSignUp : () -> Unit + onClickSignUp : () -> Unit, + onBackIconClick: () -> Unit ) { composable( route = loginNavigationRoute ) { LoginScreen( onShowSnackbar = onShowSnackbar, - onClickSignUp = onClickSignUp + onClickSignUp = onClickSignUp, + onBackIconClick = onBackIconClick ) } } fun NavGraphBuilder.signUpScreen( - onShowSnackbar : (String) -> Unit + onShowSnackbar : (String) -> Unit, + onBackIconClick: () -> Unit ) { composable( route = signUpNavigationRoute ) { SignUpScreen( - onShowSnackbar = onShowSnackbar + onShowSnackbar = onShowSnackbar, + onBackIconClick = onBackIconClick ) } } diff --git a/feature/match/src/main/java/com/eshc/goonersapp/feature/match/component/formation/FootballFieldBox.kt b/feature/match/src/main/java/com/eshc/goonersapp/feature/match/component/formation/FootballFieldBox.kt new file mode 100644 index 00000000..49972f3e --- /dev/null +++ b/feature/match/src/main/java/com/eshc/goonersapp/feature/match/component/formation/FootballFieldBox.kt @@ -0,0 +1,130 @@ +package com.eshc.goonersapp.feature.match.component.formation + +import androidx.compose.foundation.Canvas +import androidx.compose.foundation.background +import androidx.compose.foundation.gestures.detectDragGestures +import androidx.compose.foundation.layout.Box +import androidx.compose.foundation.layout.Spacer +import androidx.compose.foundation.layout.fillMaxSize +import androidx.compose.foundation.layout.height +import androidx.compose.foundation.layout.offset +import androidx.compose.foundation.layout.size +import androidx.compose.foundation.layout.width +import androidx.compose.foundation.shape.CircleShape +import androidx.compose.foundation.shape.RoundedCornerShape +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.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.draw.clip +import androidx.compose.ui.draw.rotate +import androidx.compose.ui.geometry.Offset +import androidx.compose.ui.geometry.Size +import androidx.compose.ui.graphics.Color +import androidx.compose.ui.graphics.drawscope.Stroke +import androidx.compose.ui.input.pointer.pointerInput +import androidx.compose.ui.layout.onGloballyPositioned +import androidx.compose.ui.tooling.preview.Preview +import androidx.compose.ui.unit.dp +import com.eshc.goonersapp.core.designsystem.theme.BrushMainGradient +import com.eshc.goonersapp.core.designsystem.theme.Color88FFFFFF + +@Composable +fun FootballFieldBox( + modifier: Modifier = Modifier, + rotateDegree : Float = 0f +) { + Box( + modifier = modifier + .fillMaxSize() + .clip(RoundedCornerShape(10.dp)) + .background(BrushMainGradient) + .rotate(rotateDegree), + contentAlignment = Alignment.Center + ) { + Canvas( + modifier = modifier + ) { + drawArc( + color = Color88FFFFFF, + startAngle = 0f, + sweepAngle = 180f, + useCenter = false, + style = Stroke(width = 3f), + size = Size(width = size.width * 0.3f, height = size.height * 0.3f), + topLeft = Offset(size.width * 0.35f, (size.height * 0.15f) * -1f) + ) + drawLine( + color = Color88FFFFFF, + start = Offset(0f, 0f), + end = Offset(0f, size.height), + strokeWidth = 3f + ) + drawLine( + color = Color88FFFFFF, + start = Offset(0f, size.height), + end = Offset(size.width, size.height), + strokeWidth = 3f + ) + drawLine( + color = Color88FFFFFF, + start = Offset(size.width, size.height), + end = Offset(size.width, 0f), + strokeWidth = 3f + ) + drawLine( + color = Color88FFFFFF, + start = Offset(size.width, 0f), + end = Offset(0f, 0f), + strokeWidth = 3f + ) + drawLine( + color = Color88FFFFFF, + start = Offset(size.width * 0.3f, size.height), + end = Offset(size.width * 0.3f, size.height * 0.84f), + strokeWidth = 3f + ) + drawLine( + color = Color88FFFFFF, + start = Offset(size.width * 0.3f, size.height * 0.84f), + end = Offset(size.width * 0.7f, size.height * 0.84f), + strokeWidth = 3f + ) + drawLine( + color = Color88FFFFFF, + start = Offset(size.width * 0.7f, size.height * 0.84f), + end = Offset(size.width * 0.7f, size.height), + strokeWidth = 3f + ) + drawLine( + color = Color88FFFFFF, + start = Offset(size.width * 0.4f, size.height), + end = Offset(size.width * 0.4f, size.height * 0.9f), + strokeWidth = 3f + ) + drawLine( + color = Color88FFFFFF, + start = Offset(size.width * 0.4f, size.height * 0.9f), + end = Offset(size.width * 0.6f, size.height * 0.9f), + strokeWidth = 3f + ) + drawLine( + color = Color88FFFFFF, + start = Offset(size.width * 0.6f, size.height * 0.9f), + end = Offset(size.width * 0.6f, size.height), + strokeWidth = 3f + ) + } + + } + +} + +@Preview +@Composable +fun PreviewFormationBox(){ + FootballFieldBox() +} \ No newline at end of file diff --git a/feature/match/src/main/java/com/eshc/goonersapp/feature/match/component/formation/LineUpPlayerCard.kt b/feature/match/src/main/java/com/eshc/goonersapp/feature/match/component/formation/LineUpPlayerCard.kt new file mode 100644 index 00000000..17c1273c --- /dev/null +++ b/feature/match/src/main/java/com/eshc/goonersapp/feature/match/component/formation/LineUpPlayerCard.kt @@ -0,0 +1,59 @@ +package com.eshc.goonersapp.feature.match.component.formation + +import androidx.compose.foundation.background +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.RowScope +import androidx.compose.foundation.layout.height +import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.layout.size +import androidx.compose.foundation.layout.wrapContentHeight +import androidx.compose.foundation.shape.CircleShape +import androidx.compose.material3.Text +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.layout.ContentScale +import androidx.compose.ui.text.style.TextAlign +import androidx.compose.ui.unit.dp +import androidx.compose.ui.unit.sp +import coil.compose.AsyncImage +import com.eshc.goonersapp.core.designsystem.theme.ColorFFDCDCDC +import com.eshc.goonersapp.core.designsystem.theme.GnrTypography +import com.eshc.goonersapp.core.domain.model.match.PlayerLineup + +@Composable +fun RowScope.LineUpPlayerCard( + player: PlayerLineup, + modifier: Modifier = Modifier +) { + Column( + modifier = modifier + .wrapContentHeight() + .weight(1f), + horizontalAlignment = Alignment.CenterHorizontally + ) { + AsyncImage( + modifier = Modifier + .size(46.dp) + .clip(CircleShape) + .background(ColorFFDCDCDC), + contentScale = ContentScale.Crop, + model = player.playerImageUrl, + contentDescription = null + ) + Text( + modifier = Modifier + .padding(top = 2.dp) + .height(24.dp), + text = player.playerName, + style = GnrTypography.descriptionMedium.copy( + lineHeight = 11.sp + ), + maxLines = 2, + textAlign = TextAlign.Center, + color = ColorFFDCDCDC + + ) + } +} diff --git a/feature/match/src/main/java/com/eshc/goonersapp/feature/match/detail/MatchDetailScreen.kt b/feature/match/src/main/java/com/eshc/goonersapp/feature/match/detail/MatchDetailScreen.kt index 8b3c36f2..309e4470 100644 --- a/feature/match/src/main/java/com/eshc/goonersapp/feature/match/detail/MatchDetailScreen.kt +++ b/feature/match/src/main/java/com/eshc/goonersapp/feature/match/detail/MatchDetailScreen.kt @@ -4,24 +4,20 @@ 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.IntrinsicSize import androidx.compose.foundation.layout.Row import androidx.compose.foundation.layout.RowScope -import androidx.compose.foundation.layout.fillMaxHeight 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.wrapContentHeight import androidx.compose.foundation.lazy.LazyColumn -import androidx.compose.foundation.shape.CircleShape import androidx.compose.material3.CircularProgressIndicator import androidx.compose.material3.FloatingActionButton import androidx.compose.material3.HorizontalDivider import androidx.compose.material3.Icon +import androidx.compose.material3.Scaffold import androidx.compose.material3.Text -import androidx.compose.material3.VerticalDivider import androidx.compose.runtime.Composable import androidx.compose.runtime.getValue import androidx.compose.runtime.mutableStateOf @@ -29,57 +25,77 @@ import androidx.compose.runtime.remember import androidx.compose.runtime.setValue import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier -import androidx.compose.ui.draw.clip -import androidx.compose.ui.draw.shadow import androidx.compose.ui.graphics.Color -import androidx.compose.ui.layout.ContentScale -import androidx.compose.ui.text.SpanStyle -import androidx.compose.ui.text.buildAnnotatedString -import androidx.compose.ui.text.withStyle import androidx.compose.ui.unit.dp import androidx.hilt.navigation.compose.hiltViewModel import androidx.lifecycle.compose.collectAsStateWithLifecycle -import coil.compose.AsyncImage import com.eshc.goonersapp.core.common.state.UiState import com.eshc.goonersapp.core.common.util.DateUtil import com.eshc.goonersapp.core.designsystem.IconPack import com.eshc.goonersapp.core.designsystem.component.GnrTabItem +import com.eshc.goonersapp.core.designsystem.component.GnrTopBar import com.eshc.goonersapp.core.designsystem.component.MatchLeagueInfo import com.eshc.goonersapp.core.designsystem.iconpack.IcTalk import com.eshc.goonersapp.core.designsystem.theme.ColorFF10358A import com.eshc.goonersapp.core.designsystem.theme.ColorFF777777 -import com.eshc.goonersapp.core.designsystem.theme.ColorFFC3CDE2 -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.match.MatchDetail +import com.eshc.goonersapp.core.domain.model.match.MatchInformation +import com.eshc.goonersapp.core.domain.model.match.MatchLineup import com.eshc.goonersapp.core.domain.model.match.getScoreHistoryList import com.eshc.goonersapp.feature.match.model.MatchUiModel import com.eshc.goonersapp.feature.match.state.MatchDetailUiState @Composable fun MatchDetailRootScreen( - viewModel: MatchDetailViewModel = hiltViewModel(), + onBackIconClick: () -> Unit, + onShowSnackbar: (String) -> Unit, onClickChat: (MatchUiModel) -> Unit, - onShowSnackbar: (String) -> Unit + viewModel: MatchDetailViewModel = hiltViewModel(), ) { val matchData by viewModel.matchDetailUiState.collectAsStateWithLifecycle() - MatchDetailScreen( - matchDetailUiState = matchData, - onClickChat = onClickChat - ) + val lineup by viewModel.lineupUiState.collectAsStateWithLifecycle() + val matchInformation by viewModel.matchInformationState.collectAsStateWithLifecycle() + + Scaffold( + topBar = { + GnrTopBar( + title = "", + onBackIconClick = onBackIconClick, + content = { + MatchDetailTopBar( + match = matchData.match + ) + } + ) + } + ) { paddingValues -> + MatchDetailScreen( + matchDetailUiState = matchData, + lineupUiState = lineup, + matchInformationState = matchInformation, + onClickChat = onClickChat, + modifier = Modifier + .fillMaxSize() + .padding(paddingValues) + ) + } } @Composable fun MatchDetailScreen( matchDetailUiState: MatchDetailUiState, - onClickChat: (MatchUiModel) -> Unit + lineupUiState: UiState, + matchInformationState : UiState, + onClickChat: (MatchUiModel) -> Unit, + modifier: Modifier = Modifier ) { var selectedTab by remember { mutableStateOf(DetailTab.SUMMARY) } val match = matchDetailUiState.match val matchDetail = matchDetailUiState.matchDetailState + Box( - modifier = Modifier + modifier = modifier .fillMaxSize() .background(Color.White) ) { @@ -88,113 +104,37 @@ fun MatchDetailScreen( .fillMaxSize() ) { item { - Row( - modifier = Modifier - .fillMaxWidth() - .padding(horizontal = 12.dp, vertical = 20.dp), - verticalAlignment = Alignment.CenterVertically - ) { - MatchLeagueInfo( - logoSize = 25.dp, - logoPadding = 4.dp, - competitionUrl = match.leagueImageUrl, - competitionName = "Premier league", - verticalAlignment = Alignment.CenterVertically, - ) - - Column( - modifier = Modifier - .wrapContentHeight() - .weight(1f), - horizontalAlignment = Alignment.End - ) { - Text( - text = DateUtil.getYearAndMonthAndDateAndDayAndTimeString(match.matchDate), - style = GnrTypography.body1SemiBold, - color = ColorFF10358A, - ) - Text( - modifier = Modifier.padding(top = 4.dp), - text = match.stadiumName, - style = GnrTypography.body2Regular, - color = ColorFF777777, - ) - } - } - - val annotatedScore = if (match.isFinished) buildAnnotatedString { - withStyle(style = SpanStyle(ColorFF10358A)) { append("${match.homeScore} ") } - withStyle(style = SpanStyle(ColorFFC3CDE2)) { append(":") } - withStyle(style = SpanStyle(ColorFF10358A)) { append(" ${match.awayScore}") } - } else buildAnnotatedString { - withStyle(style = SpanStyle(ColorFF10358A)) { append(" vs ") } - } - Row( - modifier = Modifier - .fillMaxWidth() - .wrapContentHeight() - .padding(horizontal = 24.dp), - verticalAlignment = Alignment.CenterVertically, - horizontalArrangement = Arrangement.SpaceAround - ) { - TeamLogoImageBox( - teamLogoImageUrl = match.homeTeamImageUrl - ) - - Text( - modifier = Modifier.padding(horizontal = 8.dp), - text = annotatedScore, - style = GnrTypography.heading1Bold, - ) - - TeamLogoImageBox( - teamLogoImageUrl = match.awayTeamImageUrl - ) - } - + MatchInfoBoard( + match + ) } + item { - when(matchDetail){ - is UiState.Success -> { - Row( - modifier = Modifier - .fillMaxWidth() - .padding( - top = 20.dp - ) - .height(IntrinsicSize.Min), - horizontalArrangement = Arrangement.Center, - content = { - MatchScoredHistory( - modifier = Modifier.weight(1f), - horizontalAlignment = Alignment.End, - matchScoreHistoryList = matchDetail.data.getScoreHistoryList(match.homeTeamId) - ) - VerticalDivider( - modifier = Modifier - .fillMaxHeight() - .padding(horizontal = 12.dp) - ) - MatchScoredHistory( - modifier = Modifier.weight(1f), - horizontalAlignment = Alignment.Start, - matchScoreHistoryList = matchDetail.data.getScoreHistoryList(match.awayTeamId) - ) - } - ) - } - is UiState.Loading -> { - Box( - modifier = Modifier.fillMaxWidth().padding(vertical = 8.dp), - contentAlignment = Alignment.Center - ){ - CircularProgressIndicator() + if(match.isFinished){ + when (matchDetail) { + is UiState.Success -> { + MatchScoreBoard( + match = match, + matchDetailList = matchDetail.data + ) } - } - else -> { + is UiState.Loading -> { + Box( + modifier = Modifier + .fillMaxWidth() + .padding(vertical = 8.dp), + contentAlignment = Alignment.Center + ) { + CircularProgressIndicator() + } + + } + else -> { + //TODOx + } } } @@ -227,12 +167,13 @@ fun MatchDetailScreen( item { when (selectedTab) { DetailTab.SUMMARY -> { - - } - - DetailTab.COMMENT -> { - + SummaryScreen( + match = match, + lineupUiState = lineupUiState, + matchInformationState = matchInformationState + ) } + DetailTab.COMMENT -> { /* TODO("Not yet implemented") */ } } } } @@ -270,49 +211,13 @@ fun RowScope.MatchScoredHistory( matchScoreHistoryList.forEach { history -> Text( text = history.scoringRecordText, - color = ColorFF10358A, + color = if(history.teamId == 19) ColorFF10358A else ColorFF777777, style = GnrTypography.descriptionMedium ) } } } -@Composable -fun TeamLogoImageBox( - teamLogoImageUrl: String, - modifier: Modifier = Modifier -) { - Box( - modifier = modifier - .size(88.dp) - .clip(CircleShape) - .background( - ColorFFF5F5F5 - ), - contentAlignment = Alignment.Center - ) { - Box( - modifier = Modifier - .shadow( - elevation = 3.dp, - shape = CircleShape - ) - .size(72.dp) - .clip(CircleShape) - .background( - ColorFFFFFFFF - ), - contentAlignment = Alignment.Center - ) { - AsyncImage( - modifier = Modifier.fillMaxWidth(0.65f), - model = teamLogoImageUrl, - contentDescription = null, - contentScale = ContentScale.FillWidth - ) - } - } -} enum class DetailTab { SUMMARY, COMMENT diff --git a/feature/match/src/main/java/com/eshc/goonersapp/feature/match/detail/MatchDetailTopBar.kt b/feature/match/src/main/java/com/eshc/goonersapp/feature/match/detail/MatchDetailTopBar.kt new file mode 100644 index 00000000..8ceb5d8b --- /dev/null +++ b/feature/match/src/main/java/com/eshc/goonersapp/feature/match/detail/MatchDetailTopBar.kt @@ -0,0 +1,41 @@ +package com.eshc.goonersapp.feature.match.detail + +import androidx.compose.foundation.layout.Arrangement +import androidx.compose.foundation.layout.Row +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.size +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.unit.dp +import coil.compose.AsyncImage +import com.eshc.goonersapp.core.designsystem.theme.ColorFF181818 +import com.eshc.goonersapp.core.designsystem.theme.GnrTypography +import com.eshc.goonersapp.feature.match.model.MatchUiModel + +@Composable +fun MatchDetailTopBar( + match : MatchUiModel +){ + Row( + modifier = Modifier + .fillMaxWidth(), + verticalAlignment = Alignment.CenterVertically, + horizontalArrangement = Arrangement.spacedBy(7.dp, alignment = Alignment.Start) + ) { + AsyncImage( + modifier = Modifier.size(25.dp), + model = match.leagueImageUrl, + contentDescription = "League Logo" + ) + Text( + text = match.getMatchTitle(), + style = GnrTypography.subtitleSemiBold, + color = ColorFF181818, + maxLines = 1, + overflow = TextOverflow.Ellipsis + ) + } +} \ No newline at end of file diff --git a/feature/match/src/main/java/com/eshc/goonersapp/feature/match/detail/MatchDetailViewModel.kt b/feature/match/src/main/java/com/eshc/goonersapp/feature/match/detail/MatchDetailViewModel.kt index 09ecc92d..e8eff62f 100644 --- a/feature/match/src/main/java/com/eshc/goonersapp/feature/match/detail/MatchDetailViewModel.kt +++ b/feature/match/src/main/java/com/eshc/goonersapp/feature/match/detail/MatchDetailViewModel.kt @@ -5,7 +5,11 @@ import androidx.lifecycle.ViewModel import androidx.lifecycle.viewModelScope import com.eshc.goonersapp.core.common.state.UiState import com.eshc.goonersapp.core.domain.model.DataResult +import com.eshc.goonersapp.core.domain.model.match.MatchInformation +import com.eshc.goonersapp.core.domain.model.match.MatchLineup import com.eshc.goonersapp.core.domain.usecase.match.GetMatchDetailUseCase +import com.eshc.goonersapp.core.domain.usecase.match.GetMatchInformationUseCase +import com.eshc.goonersapp.core.domain.usecase.match.GetMatchLineupUseCase import com.eshc.goonersapp.feature.match.model.MatchUiModel import com.eshc.goonersapp.feature.match.model.toUiModel import com.eshc.goonersapp.feature.match.state.MatchDetailUiState @@ -19,6 +23,8 @@ import javax.inject.Inject @HiltViewModel class MatchDetailViewModel @Inject constructor( getMatchDetailUseCase : GetMatchDetailUseCase, + getMatchLineupUseCase: GetMatchLineupUseCase, + getMatchInformationUseCase: GetMatchInformationUseCase, savedStateHandle: SavedStateHandle, ) : ViewModel() { private val _match = savedStateHandle.getStateFlow(MATCH_SAVED_STATE_KEY,MatchUiModel(0)) @@ -49,6 +55,46 @@ class MatchDetailViewModel @Inject constructor( started = SharingStarted.Eagerly ) + val lineupUiState : StateFlow> = getMatchLineupUseCase( + matchId = _match.value.id + ).map { + when(it){ + is DataResult.Success -> { + UiState.Success( + it.data + ) + } + is DataResult.Failure -> { + UiState.Error + } + } + }.stateIn( + scope = viewModelScope, + initialValue = UiState.Loading, + started = SharingStarted.Eagerly + ) + + val matchInformationState : StateFlow> = getMatchInformationUseCase( + match = _match.value.id, + opponent = _match.value.getOpponentTeamId(19), + season = _match.value.seasonId + ).map { + when(it){ + is DataResult.Success -> { + UiState.Success( + it.data + ) + } + is DataResult.Failure -> { + UiState.Error + } + } + }.stateIn( + scope = viewModelScope, + initialValue = UiState.Loading, + started = SharingStarted.Eagerly + ) + companion object { private const val MATCH_SAVED_STATE_KEY = "match" } diff --git a/feature/match/src/main/java/com/eshc/goonersapp/feature/match/detail/MatchInfoBoard.kt b/feature/match/src/main/java/com/eshc/goonersapp/feature/match/detail/MatchInfoBoard.kt new file mode 100644 index 00000000..c4f2c011 --- /dev/null +++ b/feature/match/src/main/java/com/eshc/goonersapp/feature/match/detail/MatchInfoBoard.kt @@ -0,0 +1,229 @@ +package com.eshc.goonersapp.feature.match.detail + +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.IntrinsicSize +import androidx.compose.foundation.layout.Row +import androidx.compose.foundation.layout.RowScope +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.widthIn +import androidx.compose.foundation.layout.wrapContentHeight +import androidx.compose.foundation.lazy.LazyItemScope +import androidx.compose.foundation.shape.CircleShape +import androidx.compose.material3.Text +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.draw.shadow +import androidx.compose.ui.layout.ContentScale +import androidx.compose.ui.text.SpanStyle +import androidx.compose.ui.text.buildAnnotatedString +import androidx.compose.ui.text.style.TextAlign +import androidx.compose.ui.text.withStyle +import androidx.compose.ui.unit.dp +import androidx.compose.ui.unit.sp +import coil.compose.AsyncImage +import com.eshc.goonersapp.core.common.util.DateUtil +import com.eshc.goonersapp.core.designsystem.theme.ColorFF10358A +import com.eshc.goonersapp.core.designsystem.theme.ColorFF181818 +import com.eshc.goonersapp.core.designsystem.theme.ColorFF777777 +import com.eshc.goonersapp.core.designsystem.theme.ColorFF889AC4 +import com.eshc.goonersapp.core.designsystem.theme.ColorFFC3CDE2 +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.feature.match.model.MatchUiModel + +@Composable +fun LazyItemScope.MatchInfoBoard( + match: MatchUiModel +) { + + Row( + modifier = Modifier + .fillMaxWidth() + .wrapContentHeight() + .padding(horizontal = 24.dp), + horizontalArrangement = Arrangement.SpaceAround + ) { + TeamLogoImageBox( + modifier = Modifier + .wrapContentHeight() + .weight(1f), + teamLogoImageUrl = match.homeTeamImageUrl, + teamName = match.homeTeamName + ) + + if (match.isFinished) { + MatchResultBoard( + match + ) + } else { + MatchFixtureBoard( + match + ) + } + + TeamLogoImageBox( + modifier = Modifier + .wrapContentHeight() + .weight(1f), + teamLogoImageUrl = match.awayTeamImageUrl, + teamName = match.awayTeamName + ) + } +} + + +@Composable +fun RowScope.MatchFixtureBoard( + match: MatchUiModel, + modifier: Modifier = Modifier +) { + Column( + modifier = modifier.padding(horizontal = 8.dp), + horizontalAlignment = Alignment.CenterHorizontally + + ) { + Column( + modifier = Modifier + .height(78.dp), + verticalArrangement = Arrangement.Center, + horizontalAlignment = Alignment.CenterHorizontally + ) { + Text( + text = DateUtil.getMonthAndDateAndDayString(match.matchDate), + style = GnrTypography.heading2SemiBold, + color = ColorFF889AC4 + ) + Text( + text = DateUtil.getTimeString(match.matchDate), + style = GnrTypography.heading2SemiBold, + color = ColorFF10358A + ) + + } + Text( + modifier = Modifier + .padding(top = 3.dp) + .widthIn(max = 140.dp), + text = match.stadiumName, + textAlign = TextAlign.Center, + style = GnrTypography.body2Regular, + color = ColorFF777777 + ) + } +} + +@Composable +fun RowScope.MatchResultBoard( + match: MatchUiModel, + modifier: Modifier = Modifier +) { + val annotatedScore = if (match.isFinished) buildAnnotatedString { + withStyle(style = SpanStyle(ColorFF10358A)) { append("${match.homeScore} ") } + withStyle(style = SpanStyle(ColorFFC3CDE2)) { append(":") } + withStyle(style = SpanStyle(ColorFF10358A)) { append(" ${match.awayScore}") } + } else buildAnnotatedString { + withStyle(style = SpanStyle(ColorFF10358A)) { append(" vs ") } + } + Column( + modifier = modifier + .width(IntrinsicSize.Min) + .padding(horizontal = 8.dp), + horizontalAlignment = Alignment.CenterHorizontally + + ) { + Column( + modifier = Modifier + .width(IntrinsicSize.Max) + .height(78.dp), + verticalArrangement = Arrangement.spacedBy(6.dp, Alignment.CenterVertically), + horizontalAlignment = Alignment.CenterHorizontally + ) { + Text( + text = annotatedScore, + style = GnrTypography.heading1Bold.copy( + fontSize = 28.sp + ), + ) + Text( + text = DateUtil.getYearAndMonthAndDateAndDayAndTimeString(match.matchDate), + style = GnrTypography.body1Medium, + color = ColorFF777777 + ) + + } + Text( + modifier = Modifier + .fillMaxWidth() + .padding(top = 3.dp), + text = match.stadiumName, + textAlign = TextAlign.Center, + style = GnrTypography.body2Regular, + color = ColorFF777777 + ) + } + + +} + + +@Composable +fun TeamLogoImageBox( + teamName: String, + teamLogoImageUrl: String, + modifier: Modifier = Modifier +) { + Column( + modifier = modifier, + horizontalAlignment = Alignment.CenterHorizontally + ) { + Box( + modifier = Modifier + .size(78.dp) + .clip(CircleShape) + .background( + ColorFFF5F5F5 + ), + contentAlignment = Alignment.Center + ) { + Box( + modifier = Modifier + .shadow( + elevation = 3.dp, + shape = CircleShape + ) + .size(64.dp) + .clip(CircleShape) + .background( + ColorFFFFFFFF + ), + contentAlignment = Alignment.Center + ) { + AsyncImage( + modifier = Modifier.fillMaxWidth(0.65f), + model = teamLogoImageUrl, + contentDescription = null, + contentScale = ContentScale.FillWidth + ) + } + } + + Text( + modifier = Modifier.padding(top = 3.dp), + text = teamName, + textAlign = TextAlign.Center, + color = ColorFF181818, + style = GnrTypography.body2SemiBold + ) + } + +} diff --git a/feature/match/src/main/java/com/eshc/goonersapp/feature/match/detail/MatchScoreBoard.kt b/feature/match/src/main/java/com/eshc/goonersapp/feature/match/detail/MatchScoreBoard.kt new file mode 100644 index 00000000..f5921cd1 --- /dev/null +++ b/feature/match/src/main/java/com/eshc/goonersapp/feature/match/detail/MatchScoreBoard.kt @@ -0,0 +1,58 @@ +package com.eshc.goonersapp.feature.match.detail + +import androidx.compose.foundation.layout.Arrangement +import androidx.compose.foundation.layout.IntrinsicSize +import androidx.compose.foundation.layout.Row +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.lazy.LazyItemScope +import androidx.compose.material3.Icon +import androidx.compose.runtime.Composable +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.res.painterResource +import androidx.compose.ui.unit.dp +import com.eshc.goonersapp.core.domain.model.match.MatchDetail +import com.eshc.goonersapp.core.domain.model.match.getScoreHistoryList +import com.eshc.goonersapp.feature.match.model.MatchUiModel + +@Composable +fun LazyItemScope.MatchScoreBoard( + match: MatchUiModel, + matchDetailList: List +) { + if(match.isScorelessMatch) return + + Row( + modifier = Modifier + .fillMaxWidth() + .padding( + top = 20.dp + ) + .height(IntrinsicSize.Min), + horizontalArrangement = Arrangement.spacedBy(15.dp, Alignment.CenterHorizontally), + content = { + MatchScoredHistory( + modifier = Modifier.weight(1f), + horizontalAlignment = Alignment.End, + matchScoreHistoryList = matchDetailList.getScoreHistoryList( + match.homeTeamId + ) + ) + Icon( + modifier = Modifier.size(12.dp), + painter = painterResource(id = com.eshc.goonersapp.core.designsystem.R.drawable.ic_ball), + contentDescription = "ball" + ) + MatchScoredHistory( + modifier = Modifier.weight(1f), + horizontalAlignment = Alignment.Start, + matchScoreHistoryList = matchDetailList.getScoreHistoryList( + match.awayTeamId + ) + ) + } + ) +} \ No newline at end of file diff --git a/feature/match/src/main/java/com/eshc/goonersapp/feature/match/detail/SummaryScreen.kt b/feature/match/src/main/java/com/eshc/goonersapp/feature/match/detail/SummaryScreen.kt new file mode 100644 index 00000000..4f6877c0 --- /dev/null +++ b/feature/match/src/main/java/com/eshc/goonersapp/feature/match/detail/SummaryScreen.kt @@ -0,0 +1,398 @@ +package com.eshc.goonersapp.feature.match.detail + +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.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.widthIn +import androidx.compose.foundation.layout.wrapContentHeight +import androidx.compose.foundation.shape.RoundedCornerShape +import androidx.compose.material3.CardDefaults +import androidx.compose.material3.CircularProgressIndicator +import androidx.compose.material3.Text +import androidx.compose.material3.VerticalDivider +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.layout.ContentScale +import androidx.compose.ui.text.style.TextAlign +import androidx.compose.ui.unit.dp +import coil.compose.AsyncImage +import com.eshc.goonersapp.core.common.state.UiState +import com.eshc.goonersapp.core.designsystem.component.GnrElevatedCard +import com.eshc.goonersapp.core.designsystem.ext.gnrElevatedCardBorder +import com.eshc.goonersapp.core.designsystem.theme.Color88FFFFFF +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.ColorFF889AC4 +import com.eshc.goonersapp.core.designsystem.theme.ColorFFDCDCDC +import com.eshc.goonersapp.core.designsystem.theme.ColorFFE6EDFC +import com.eshc.goonersapp.core.designsystem.theme.ColorFFFFFFFF +import com.eshc.goonersapp.core.designsystem.theme.GnrTypography +import com.eshc.goonersapp.core.domain.model.match.MatchInformation +import com.eshc.goonersapp.core.domain.model.match.MatchLineup +import com.eshc.goonersapp.core.domain.model.match.NotablePlayer +import com.eshc.goonersapp.core.domain.model.match.Performance +import com.eshc.goonersapp.feature.match.component.formation.FootballFieldBox +import com.eshc.goonersapp.feature.match.component.formation.LineUpPlayerCard +import com.eshc.goonersapp.feature.match.model.MatchUiModel + +@Composable +fun SummaryScreen( + match : MatchUiModel, + lineupUiState: UiState, + matchInformationState: UiState, +) { + Column( + modifier = Modifier + .padding(start = 14.dp, end = 14.dp, top = 22.dp, bottom = 10.dp) + ) { + when (lineupUiState) { + is UiState.Success -> { + Text( + text = "Line-Ups", + modifier = Modifier.padding(bottom = 16.dp), + style = GnrTypography.subtitleMedium, + color = ColorFF181818 + ) + + LineUpBox( + matchLineup = lineupUiState.data, + modifier = Modifier.padding(bottom = 70.dp) + ) + } + + is UiState.Loading -> { + Box( + modifier = Modifier + .fillMaxWidth() + .height(360.dp), + contentAlignment = Alignment.Center + ) { + CircularProgressIndicator() + } + } + + else -> { + + } + } + when (matchInformationState) { + is UiState.Success -> { + matchInformationState.data.notablePlayer?.let { + Text( + text = "Player To Watch For", + modifier = Modifier.padding(bottom = 16.dp), + style = GnrTypography.subtitleMedium, + color = ColorFF181818 + ) + PlayerToWatchForBox( + player = it, + modifier = Modifier + .padding(bottom = 70.dp) + .fillMaxWidth() + ) + } + + Text( + text = "Head To Head", + modifier = Modifier.padding(bottom = 16.dp), + style = GnrTypography.subtitleMedium, + color = ColorFF181818 + ) + HeadToHeadBox( + opponentTeamName = match.getOpponentTeamName(19), + performance = matchInformationState.data.performance, + modifier = Modifier.padding(bottom = 70.dp) + ) + + } + + is UiState.Loading -> { + Box( + modifier = Modifier + .fillMaxWidth() + .height(40.dp), + contentAlignment = Alignment.Center + ) { + CircularProgressIndicator() + } + } + + else -> { + + } + } + } +} + +@Composable +fun LineUpBox( + matchLineup: MatchLineup, + modifier: Modifier = Modifier +) { + Box( + modifier = modifier + .fillMaxWidth() + .height(IntrinsicSize.Max) + ) { + FootballFieldBox( + modifier = Modifier.fillMaxSize(), + rotateDegree = 180f + ) + PlayerLineUpBox( + matchLineup = matchLineup + ) + } +} + + +@Composable +fun PlayerToWatchForBox( + player: NotablePlayer, + modifier: Modifier = Modifier +) { + Box( + modifier = modifier.background( + color = ColorFFE6EDFC, + shape = RoundedCornerShape(10.dp) + ) + ) { + Column( + modifier = Modifier.fillMaxWidth() + ) { + Row( + modifier = Modifier + .padding(top = 10.dp, bottom = 8.dp, end = 12.dp) + ) { + Text( + modifier = Modifier + .wrapContentHeight() + .weight(0.45f) + .padding(end = 18.dp), + text = "", + style = GnrTypography.heading2SemiBold, + color = ColorFF889AC4, + textAlign = TextAlign.End + ) + Column( + modifier = Modifier + .wrapContentHeight() + .weight(0.55f) + ) { + Text( + text = player.playerName, + style = GnrTypography.body1Medium, + color = ColorFF10358A + ) + Text( + text = player.playerPosition, + style = GnrTypography.descriptionRegular, + color = ColorFF10358A + ) + } + } + PlayerStatToWatchForRow( + title = "Games", + stat = "${player.playerParticipationCount}", + modifier = Modifier.padding(bottom = 3.dp) + ) + PlayerStatToWatchForRow( + title = "Goals", + stat = "${player.playerGoalCount}", + modifier = Modifier + ) + } + Box( + modifier = Modifier.fillMaxWidth(0.4f), + contentAlignment = Alignment.BottomCenter + ) { + AsyncImage( + modifier = Modifier + .fillMaxWidth(0.85f), + model = player.playerImageUrl, + contentDescription = "player" + ) + } + + } +} + +@Composable +fun PlayerStatToWatchForRow( + title: String, + stat: String, + modifier: Modifier = Modifier +) { + Row( + modifier = modifier + .background(color = Color88FFFFFF) + .padding(top = 10.dp, bottom = 8.dp, end = 12.dp) + ) { + Spacer( + modifier = Modifier + .wrapContentHeight() + .weight(0.45f) + ) + Text( + modifier = Modifier + .wrapContentHeight() + .weight(0.45f), + text = title, + style = GnrTypography.body1Medium, + color = ColorFF181818 + ) + Text( + modifier = Modifier + .wrapContentHeight() + .weight(0.1f), + text = stat, + maxLines = 1, + textAlign = TextAlign.Center, + style = GnrTypography.body1Medium, + color = ColorFF10358A + ) + } +} + +@Composable +fun PlayerLineUpBox( + matchLineup: MatchLineup +) { + Column( + modifier = Modifier + .fillMaxSize() + .padding(start = 12.dp, end = 12.dp, top = 16.dp, bottom = 16.dp), + verticalArrangement = Arrangement.spacedBy(26.dp) + ) { + matchLineup.getMyTeamLineup(19).groupStartingPlayersByPosition().forEach { players -> + Row( + modifier = Modifier + .fillMaxWidth() + .weight(1f), + horizontalArrangement = Arrangement.SpaceAround + ) { + players.forEach { player -> + LineUpPlayerCard( + player = player + ) + } + + } + } + } +} + +@Composable +fun HeadToHeadBox( + opponentTeamName: String, + performance: Performance, + modifier: Modifier = Modifier +) { + GnrElevatedCard( + colors = CardDefaults.cardColors(containerColor = ColorFFFFFFFF), + radius = 10.dp, + modifier = modifier + .fillMaxWidth() + .height(IntrinsicSize.Max) + .gnrElevatedCardBorder(10.dp) + ) { + Column( + modifier = Modifier.padding(top = 20.dp, bottom = 13.dp), + horizontalAlignment = Alignment.CenterHorizontally, + verticalArrangement = Arrangement.spacedBy(13.dp) + ) { + Row( + modifier = Modifier.fillMaxWidth(), + horizontalArrangement = Arrangement.Center, + verticalAlignment = Alignment.CenterVertically + ) { + Text( + modifier = Modifier.padding(end = 10.dp), + text = "VS", + style = GnrTypography.subtitleMedium, + color = ColorFF4C68A7 + ) + AsyncImage( + modifier = Modifier.size(34.dp), + model = performance.opponentImageUrl, + contentDescription = "teamLogo", + contentScale = ContentScale.Crop + ) + Text( + modifier = Modifier + .padding(start = 6.dp) + .widthIn(max = 120.dp), + text = opponentTeamName, + style = GnrTypography.body2Medium, + color = ColorFF181818, textAlign = TextAlign.Center + ) + } + Row( + modifier = Modifier.fillMaxWidth().padding(), + horizontalArrangement = Arrangement.spacedBy(20.dp,Alignment.CenterHorizontally), + verticalAlignment = Alignment.CenterVertically + ) { + MatchRecordText( + resultType = "Win", + resultCount = performance.win, + color = ColorFF10358A + ) + VerticalDivider( + modifier = Modifier.height(25.dp), + thickness = 1.dp, + color = ColorFFDCDCDC + ) + MatchRecordText( + resultType = "Draw", + resultCount = performance.draw, + color = ColorFF181818 + ) + VerticalDivider( + modifier = Modifier.height(25.dp), + thickness = 1.dp, + color = ColorFFDCDCDC + ) + MatchRecordText( + resultType = "Loss", + resultCount = performance.lose, + color = ColorFF181818 + ) + } + } + } +} + +@Composable +fun MatchRecordText( + resultType : String, + resultCount : Int, + color : Color +){ + Row( + verticalAlignment = Alignment.CenterVertically + ){ + Text( + modifier = Modifier + .padding(end = 6.dp), + text = resultType, + style = GnrTypography.body1Medium, + color = color + ) + Text( + text = "$resultCount", + style = GnrTypography.heading1SemiBold, + color = color + ) + } +} \ No newline at end of file diff --git a/feature/match/src/main/java/com/eshc/goonersapp/feature/match/model/MatchUiModel.kt b/feature/match/src/main/java/com/eshc/goonersapp/feature/match/model/MatchUiModel.kt index c761cff5..2539c6b1 100644 --- a/feature/match/src/main/java/com/eshc/goonersapp/feature/match/model/MatchUiModel.kt +++ b/feature/match/src/main/java/com/eshc/goonersapp/feature/match/model/MatchUiModel.kt @@ -12,33 +12,64 @@ import java.nio.charset.StandardCharsets @Serializable @Parcelize data class MatchUiModel( - val id :Int, - val homeTeamName : String = "", - val homeTeamImageUrl : String = "", - val awayTeamName : String = "", - val awayTeamImageUrl : String = "", - val matchDate : String = "", - val homeScore :Int = 0, - val awayScore :Int = 0, - val homeTeamId : Int = 0, - val awayTeamId : Int = 0, - val round :Int = 0, - val isFinished :Boolean = false, - val stadiumName : String = "", - val leagueImageUrl : String = "", + val id: Int, + val seasonId: Int = 0, + val homeTeamName: String = "", + val homeTeamNickname: String = "", + val homeTeamImageUrl: String = "", + val awayTeamName: String = "", + val awayTeamNickname: String = "", + val awayTeamImageUrl: String = "", + val matchDate: String = "", + val homeScore: Int = 0, + val awayScore: Int = 0, + val homeTeamId: Int = 0, + val awayTeamId: Int = 0, + val round: Int = 0, + val isFinished: Boolean = false, + val stadiumName: String = "", + val leagueImageUrl: String = "", ) : Parcelable { + val isScorelessMatch: Boolean + get() = homeScore == 0 && awayScore == 0 + override fun toString(): String { return URLEncoder.encode( Json.encodeToString(this), StandardCharsets.UTF_8.toString() ) } + + fun getMatchTitle(): String { + val homeTeamNameTitle = + if (homeTeamNickname.isBlank() && homeTeamName.length >= 2) homeTeamName.substring(0..2) + .uppercase() + else homeTeamNickname + + val awayTeamNameTitle = + if (awayTeamNickname.isBlank() && awayTeamName.length >= 2) awayTeamName.substring(0..2) + .uppercase() + else awayTeamNickname + + return "$homeTeamNameTitle vs $awayTeamNameTitle" + } + + fun getOpponentTeamId(myTeamId: Int): Int { + return if (homeTeamId == myTeamId) awayTeamId + else homeTeamId + } + + fun getOpponentTeamName(myTeamId: Int): String { + return if (homeTeamId == myTeamId) awayTeamName + else homeTeamName + } } fun Match.toUiModel() = MatchUiModel( id = id, + seasonId = seasonId, homeTeamName = homeTeamName, - homeTeamImageUrl= homeTeamImageUrl, + homeTeamImageUrl = homeTeamImageUrl, homeTeamId = homeTeamId, awayTeamName = awayTeamName, awayTeamImageUrl = awayTeamImageUrl, diff --git a/feature/match/src/main/java/com/eshc/goonersapp/feature/match/navigation/MatchNavigation.kt b/feature/match/src/main/java/com/eshc/goonersapp/feature/match/navigation/MatchNavigation.kt index 62dd8bac..b1a3615e 100644 --- a/feature/match/src/main/java/com/eshc/goonersapp/feature/match/navigation/MatchNavigation.kt +++ b/feature/match/src/main/java/com/eshc/goonersapp/feature/match/navigation/MatchNavigation.kt @@ -45,17 +45,17 @@ fun NavGraphBuilder.matchScreen( fun NavGraphBuilder.matchDetailScreen( + onBackIconClick: () -> Unit, onClickChat : (MatchUiModel) -> Unit, onShowSnackbar : (String) -> Unit ) { composable( route = "$matchDetailNavigationRoute?$matchArg={$matchArg}", - arguments = listOf( - navArgument(matchArg) { type = MatchType }, - ), + arguments = listOf(navArgument(matchArg) { type = MatchType }) ) { MatchDetailRootScreen( onClickChat = onClickChat, + onBackIconClick = onBackIconClick, onShowSnackbar = onShowSnackbar ) } diff --git a/feature/team/src/main/java/com/eshc/goonersapp/feature/team/club/ClubDetailScreen.kt b/feature/team/src/main/java/com/eshc/goonersapp/feature/team/club/ClubDetailScreen.kt index 26649663..48910824 100644 --- a/feature/team/src/main/java/com/eshc/goonersapp/feature/team/club/ClubDetailScreen.kt +++ b/feature/team/src/main/java/com/eshc/goonersapp/feature/team/club/ClubDetailScreen.kt @@ -32,16 +32,16 @@ import androidx.compose.ui.unit.sp import androidx.hilt.navigation.compose.hiltViewModel import androidx.lifecycle.compose.collectAsStateWithLifecycle import coil.compose.AsyncImage -import com.eshc.goonersapp.core.common.util.DateUtil -import com.eshc.goonersapp.core.designsystem.component.TopBar +import com.eshc.goonersapp.core.designsystem.component.GnrTopBar import com.eshc.goonersapp.core.designsystem.theme.pretendard import com.eshc.goonersapp.core.domain.model.match.Match import com.eshc.goonersapp.feature.team.state.ClubDetailUiState @Composable fun ClubDetailRoute( - clubDetailViewModel: ClubDetailViewModel = hiltViewModel(), - onShowSnackbar : (String) -> Unit + onBackIconClick: () -> Unit, + onShowSnackbar : (String) -> Unit, + clubDetailViewModel: ClubDetailViewModel = hiltViewModel() ) { val clubDetailUiState by clubDetailViewModel.clubDetail.collectAsStateWithLifecycle() @@ -52,19 +52,22 @@ fun ClubDetailRoute( } ClubDetailScreen( - clubDetailUiState + onBackIconClick = onBackIconClick, + clubDetailUiState = clubDetailUiState ) } @Composable fun ClubDetailScreen( + onBackIconClick: () -> Unit, clubDetailUiState: ClubDetailUiState ) { Column( modifier = Modifier.fillMaxSize() ) { - TopBar( - title = "CLUB" + GnrTopBar( + title = "CLUB", + onBackIconClick = onBackIconClick ) when(clubDetailUiState){ is ClubDetailUiState.Success -> { diff --git a/feature/team/src/main/java/com/eshc/goonersapp/feature/team/detail/PlayerDetailScreen.kt b/feature/team/src/main/java/com/eshc/goonersapp/feature/team/detail/PlayerDetailScreen.kt index 757e8bf2..1f0e6174 100644 --- a/feature/team/src/main/java/com/eshc/goonersapp/feature/team/detail/PlayerDetailScreen.kt +++ b/feature/team/src/main/java/com/eshc/goonersapp/feature/team/detail/PlayerDetailScreen.kt @@ -1,6 +1,8 @@ package com.eshc.goonersapp.feature.team.detail +import android.annotation.SuppressLint import androidx.compose.foundation.background +import androidx.compose.foundation.clickable import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.Column @@ -19,9 +21,14 @@ import androidx.compose.foundation.shape.CircleShape import androidx.compose.foundation.shape.RoundedCornerShape import androidx.compose.material3.CardDefaults import androidx.compose.material3.CircularProgressIndicator +import androidx.compose.material3.ExperimentalMaterial3Api import androidx.compose.material3.HorizontalDivider +import androidx.compose.material3.Icon +import androidx.compose.material3.Scaffold import androidx.compose.material3.Surface import androidx.compose.material3.Text +import androidx.compose.material3.TopAppBar +import androidx.compose.material3.TopAppBarDefaults import androidx.compose.runtime.Composable import androidx.compose.runtime.getValue import androidx.compose.runtime.mutableStateOf @@ -38,33 +45,72 @@ import androidx.compose.ui.unit.sp import androidx.hilt.navigation.compose.hiltViewModel import androidx.lifecycle.compose.collectAsStateWithLifecycle import coil.compose.AsyncImage +import com.eshc.goonersapp.core.designsystem.IconPack import com.eshc.goonersapp.core.designsystem.component.GnrElevatedCard import com.eshc.goonersapp.core.designsystem.component.GnrTabItem import com.eshc.goonersapp.core.designsystem.ext.gnrElevatedCardBorder +import com.eshc.goonersapp.core.designsystem.iconpack.IcIosArrowBack +import com.eshc.goonersapp.core.designsystem.iconpack.IcNotification import com.eshc.goonersapp.core.designsystem.theme.ColorFF10358A import com.eshc.goonersapp.core.designsystem.theme.ColorFF181818 +import com.eshc.goonersapp.core.designsystem.theme.ColorFF720509 +import com.eshc.goonersapp.core.designsystem.theme.ColorFFC10006 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.player.Player import com.eshc.goonersapp.feature.team.state.PlayerDetailUiState +@SuppressLint("UnusedMaterial3ScaffoldPaddingParameter") +@OptIn(ExperimentalMaterial3Api::class) @Composable fun PlayerDetailRootScreen( + onBackIconClick: () -> Unit, onShowSnackbar: (String) -> Unit, viewModel : PlayerDetailViewModel = hiltViewModel() ) { val playerDetailUiState by viewModel.playerDetailUiState.collectAsStateWithLifecycle() var selectedTab by remember { mutableStateOf(DetailTab.PROFILE) } - PlayerDetailScreen( - playerDetailUiState = playerDetailUiState, - selectedTab = selectedTab, - onShowSnackbar = onShowSnackbar, - onUpdateTab = { - selectedTab = it + Scaffold( + topBar = { + TopAppBar( + title = { /*TODO*/ }, + modifier = Modifier.fillMaxWidth(), + navigationIcon = { + Icon( + imageVector = IconPack.IcIosArrowBack, + contentDescription = null, + modifier = Modifier + .padding(horizontal = 8.dp) + .size(24.dp) + .clickable(onClick = onBackIconClick), + tint = ColorFFFFFFFF + ) + }, + actions = { + Icon( + imageVector = IconPack.IcNotification, + contentDescription = null, + modifier = Modifier + .padding(horizontal = 8.dp) + .size(24.dp) + .clickable { /* TODO("Not yet implemented") */ }, + tint = ColorFFFFFFFF + ) + }, + colors = TopAppBarDefaults.topAppBarColors(containerColor = Color.Transparent) + ) } - ) + ) { _ -> + PlayerDetailScreen( + playerDetailUiState = playerDetailUiState, + selectedTab = selectedTab, + onShowSnackbar = onShowSnackbar, + onUpdateTab = { tab -> selectedTab = tab }, + modifier = Modifier + ) + } } @@ -73,9 +119,11 @@ fun PlayerDetailScreen( playerDetailUiState : PlayerDetailUiState, selectedTab : DetailTab, onShowSnackbar: (String) -> Unit, - onUpdateTab : (DetailTab) -> Unit + onUpdateTab : (DetailTab) -> Unit, + modifier: Modifier = Modifier ) { Surface( + modifier = modifier, color = ColorFFFFFFFF ) { when (playerDetailUiState) { @@ -183,8 +231,8 @@ fun PlayerDetailImage( .background( Brush.verticalGradient( listOf( - Color(0xFFC10006), - Color(0xFF720509) + ColorFFC10006, + ColorFF720509 ) ) ), @@ -218,7 +266,7 @@ fun PlayerDetailImage( ) PlayerDetailBackNumberChip( backNumber = player.backNumber, - backgroundColor = Color(0xFFC10006), + backgroundColor = ColorFFC10006 ) } Text( diff --git a/feature/team/src/main/java/com/eshc/goonersapp/feature/team/history/TeamHistoryScreen.kt b/feature/team/src/main/java/com/eshc/goonersapp/feature/team/history/TeamHistoryScreen.kt index 03faf120..52fb4a54 100644 --- a/feature/team/src/main/java/com/eshc/goonersapp/feature/team/history/TeamHistoryScreen.kt +++ b/feature/team/src/main/java/com/eshc/goonersapp/feature/team/history/TeamHistoryScreen.kt @@ -2,21 +2,41 @@ package com.eshc.goonersapp.feature.team.history import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.fillMaxSize +import androidx.compose.foundation.layout.padding +import androidx.compose.material3.Scaffold import androidx.compose.runtime.Composable import androidx.compose.ui.Modifier -import com.eshc.goonersapp.core.designsystem.component.TopBar +import com.eshc.goonersapp.core.designsystem.component.GnrTopBar @Composable -fun TeamHistoryScreen( +fun TeamSearchRootScreen( + onClick: (String) -> Unit, + onBackIconClick: () -> Unit, + onShowSnackbar: (String) -> Unit +) { + Scaffold( + topBar = { + GnrTopBar( + title = "Search", + onBackIconClick = onBackIconClick + ) + } + ) { paddingValues -> + TeamSearchScreen( + onClick = onClick, + modifier = Modifier.padding(paddingValues) + ) + } +} + +@Composable +fun TeamSearchScreen( onClick : (String) -> Unit, - onShowSnackbar : (String) -> Unit + modifier: Modifier = Modifier, ) { Column( - modifier = Modifier.fillMaxSize() + modifier = modifier.fillMaxSize() ) { - TopBar( - title = "HISTORY" - ) - + /* TODO("Not yet Implemented") */ } } \ No newline at end of file diff --git a/feature/team/src/main/java/com/eshc/goonersapp/feature/team/navigation/TeamNavigation.kt b/feature/team/src/main/java/com/eshc/goonersapp/feature/team/navigation/TeamNavigation.kt index c6425cf5..a4292c93 100644 --- a/feature/team/src/main/java/com/eshc/goonersapp/feature/team/navigation/TeamNavigation.kt +++ b/feature/team/src/main/java/com/eshc/goonersapp/feature/team/navigation/TeamNavigation.kt @@ -10,11 +10,11 @@ import androidx.navigation.navArgument import com.eshc.goonersapp.feature.team.TeamRootScreen import com.eshc.goonersapp.feature.team.club.ClubDetailRoute import com.eshc.goonersapp.feature.team.detail.PlayerDetailRootScreen -import com.eshc.goonersapp.feature.team.history.TeamHistoryScreen +import com.eshc.goonersapp.feature.team.history.TeamSearchRootScreen const val teamNavigationRoute = "team_route" const val playerDetailNavigationRoute = "player_route" -const val teamHistoryNavigationRoute = "team_history_route" +const val teamSearchNavigationRoute = "team_search_route" const val clubDetailNavigationRoute = "club_detail_route" @@ -28,8 +28,8 @@ fun NavController.navigateToPlayerDetail(playerId: String, navOptions: NavOption this.navigate(playerDetailNavigationRoute + "/${playerId}", navOptions) } -fun NavController.navigateToTeamHistory(navOptions: NavOptions? = null) { - this.navigate(teamHistoryNavigationRoute, navOptions) +fun NavController.navigateToSearch(navOptions: NavOptions? = null) { + this.navigate(teamSearchNavigationRoute, navOptions) } fun NavController.navigateToClubDetail(navOptions: NavOptions? = null) { @@ -55,6 +55,7 @@ fun NavGraphBuilder.teamScreen( } fun NavGraphBuilder.playerDetailScreen( + onBackIconClick: () -> Unit, onShowSnackbar: (String) -> Unit ) { composable( @@ -64,32 +65,37 @@ fun NavGraphBuilder.playerDetailScreen( ), ) { PlayerDetailRootScreen( + onBackIconClick = onBackIconClick, onShowSnackbar = onShowSnackbar ) } } -fun NavGraphBuilder.teamHistoryScreen( +fun NavGraphBuilder.teamSearchScreen( onPlayerClick: (String) -> Unit, + onBackIconClick: () -> Unit, onShowSnackbar: (String) -> Unit ) { composable( - route = teamHistoryNavigationRoute + route = teamSearchNavigationRoute ) { - TeamHistoryScreen( - onPlayerClick, - onShowSnackbar + TeamSearchRootScreen( + onClick = onPlayerClick, + onBackIconClick = onBackIconClick, + onShowSnackbar = onShowSnackbar ) } } fun NavGraphBuilder.clubDetailScreen( + onBackIconClick: () -> Unit, onShowSnackbar: (String) -> Unit ) { composable( route = clubDetailNavigationRoute ) { ClubDetailRoute( + onBackIconClick = onBackIconClick, onShowSnackbar = onShowSnackbar ) }