fix: center top bar match details text and add error message component
This commit is contained in:
parent
c10a667b8d
commit
fc496e9eed
3 changed files with 123 additions and 29 deletions
|
@ -0,0 +1,66 @@
|
||||||
|
package xyz.leomurca.csgomatches.ui.components
|
||||||
|
|
||||||
|
import androidx.compose.foundation.layout.Arrangement
|
||||||
|
import androidx.compose.foundation.layout.Column
|
||||||
|
import androidx.compose.foundation.layout.Spacer
|
||||||
|
import androidx.compose.foundation.layout.fillMaxSize
|
||||||
|
import androidx.compose.foundation.layout.height
|
||||||
|
import androidx.compose.foundation.layout.padding
|
||||||
|
import androidx.compose.foundation.layout.size
|
||||||
|
import androidx.compose.foundation.rememberScrollState
|
||||||
|
import androidx.compose.foundation.verticalScroll
|
||||||
|
import androidx.compose.material.icons.Icons
|
||||||
|
import androidx.compose.material.icons.filled.Warning
|
||||||
|
import androidx.compose.material3.Button
|
||||||
|
import androidx.compose.material3.ButtonDefaults
|
||||||
|
import androidx.compose.material3.Icon
|
||||||
|
import androidx.compose.material3.MaterialTheme
|
||||||
|
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.TextAlign
|
||||||
|
import androidx.compose.ui.unit.dp
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
fun ErrorMessage(
|
||||||
|
message: String,
|
||||||
|
modifier: Modifier = Modifier,
|
||||||
|
onRetry: (() -> Unit)? = null
|
||||||
|
) {
|
||||||
|
Column(
|
||||||
|
modifier = modifier
|
||||||
|
.fillMaxSize()
|
||||||
|
.verticalScroll(rememberScrollState())
|
||||||
|
.padding(24.dp),
|
||||||
|
verticalArrangement = Arrangement.Center,
|
||||||
|
horizontalAlignment = Alignment.CenterHorizontally
|
||||||
|
) {
|
||||||
|
Icon(
|
||||||
|
imageVector = Icons.Filled.Warning,
|
||||||
|
contentDescription = null,
|
||||||
|
tint = MaterialTheme.colorScheme.error,
|
||||||
|
modifier = Modifier.size(64.dp)
|
||||||
|
)
|
||||||
|
|
||||||
|
Spacer(modifier = Modifier.height(16.dp))
|
||||||
|
|
||||||
|
Text(
|
||||||
|
text = message,
|
||||||
|
style = MaterialTheme.typography.bodyLarge,
|
||||||
|
color = MaterialTheme.colorScheme.error,
|
||||||
|
textAlign = TextAlign.Center
|
||||||
|
)
|
||||||
|
|
||||||
|
if (onRetry != null) {
|
||||||
|
Spacer(modifier = Modifier.height(24.dp))
|
||||||
|
|
||||||
|
Button(onClick = onRetry,
|
||||||
|
colors = ButtonDefaults.buttonColors()
|
||||||
|
.copy(containerColor = MaterialTheme.colorScheme.secondary)
|
||||||
|
) {
|
||||||
|
Text("Tentar novamente", style = MaterialTheme.typography.bodyMedium)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -47,7 +47,7 @@ import xyz.leomurca.csgomatches.R
|
||||||
import xyz.leomurca.csgomatches.domain.model.MatchStatus
|
import xyz.leomurca.csgomatches.domain.model.MatchStatus
|
||||||
import xyz.leomurca.csgomatches.domain.model.Player
|
import xyz.leomurca.csgomatches.domain.model.Player
|
||||||
import xyz.leomurca.csgomatches.domain.model.Team
|
import xyz.leomurca.csgomatches.domain.model.Team
|
||||||
import xyz.leomurca.csgomatches.ui.components.LoadingIndicator
|
import xyz.leomurca.csgomatches.ui.components.ErrorMessage
|
||||||
import xyz.leomurca.csgomatches.ui.navigation.MatchDetailsRoute
|
import xyz.leomurca.csgomatches.ui.navigation.MatchDetailsRoute
|
||||||
import xyz.leomurca.csgomatches.ui.theme.LiveRed
|
import xyz.leomurca.csgomatches.ui.theme.LiveRed
|
||||||
import xyz.leomurca.csgomatches.ui.theme.White
|
import xyz.leomurca.csgomatches.ui.theme.White
|
||||||
|
@ -62,16 +62,7 @@ fun MatchDetailsScreen(
|
||||||
val uiState = viewModel.uiState.collectAsState()
|
val uiState = viewModel.uiState.collectAsState()
|
||||||
|
|
||||||
LaunchedEffect(Unit) {
|
LaunchedEffect(Unit) {
|
||||||
if (matchDetails.leftOpponentId != -1L) {
|
loadTeams(matchDetails, viewModel)
|
||||||
viewModel.loadTeam(matchDetails.leftOpponentId.toString(), isLeft = true)
|
|
||||||
} else {
|
|
||||||
viewModel.applyPlaceholderTeamToSide(isLeft = true)
|
|
||||||
}
|
|
||||||
if (matchDetails.rightOpponentId != -1L) {
|
|
||||||
viewModel.loadTeam(matchDetails.rightOpponentId.toString(), isLeft = false)
|
|
||||||
} else {
|
|
||||||
viewModel.applyPlaceholderTeamToSide(isLeft = false)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Column(
|
Column(
|
||||||
|
@ -79,13 +70,15 @@ fun MatchDetailsScreen(
|
||||||
.fillMaxSize()
|
.fillMaxSize()
|
||||||
.background(MaterialTheme.colorScheme.background),
|
.background(MaterialTheme.colorScheme.background),
|
||||||
) {
|
) {
|
||||||
TopBar(matchDetails.leagueAndSerieName, onBackClick)
|
|
||||||
|
|
||||||
val value = uiState.value
|
val value = uiState.value
|
||||||
when {
|
when {
|
||||||
value.errorMessage != null -> Text(value.errorMessage)
|
value.errorMessage != null -> ErrorMessage(
|
||||||
value.isLoading -> LoadingIndicator()
|
message = value.errorMessage,
|
||||||
|
onRetry = { loadTeams(matchDetails, viewModel) }
|
||||||
|
)
|
||||||
|
|
||||||
value.leftTeam != null && value.rightTeam != null -> Column {
|
value.leftTeam != null && value.rightTeam != null -> Column {
|
||||||
|
TopBar(matchDetails.leagueAndSerieName, onBackClick)
|
||||||
MatchupRow(leftTeam = value.leftTeam, rightTeam = value.rightTeam)
|
MatchupRow(leftTeam = value.leftTeam, rightTeam = value.rightTeam)
|
||||||
ScheduleRow(matchDetails.scheduleString, matchDetails.matchStatus)
|
ScheduleRow(matchDetails.scheduleString, matchDetails.matchStatus)
|
||||||
DualPlayerLists(value.leftTeam.players, value.rightTeam.players)
|
DualPlayerLists(value.leftTeam.players, value.rightTeam.players)
|
||||||
|
@ -179,7 +172,9 @@ private fun PlayerAvatar(size: Dp, imageUrl: String?, nickName: String?) {
|
||||||
)
|
)
|
||||||
if (isLoading) {
|
if (isLoading) {
|
||||||
CircularProgressIndicator(
|
CircularProgressIndicator(
|
||||||
modifier = Modifier.size(16.dp).align(Alignment.Center),
|
modifier = Modifier
|
||||||
|
.size(16.dp)
|
||||||
|
.align(Alignment.Center),
|
||||||
strokeWidth = 2.dp,
|
strokeWidth = 2.dp,
|
||||||
color = MaterialTheme.colorScheme.background
|
color = MaterialTheme.colorScheme.background
|
||||||
)
|
)
|
||||||
|
@ -289,19 +284,30 @@ private fun MatchupRow(leftTeam: Team, rightTeam: Team) {
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
private fun TopBar(leagueAndSerieName: String, onBackClick: () -> Unit) {
|
private fun TopBar(leagueAndSerieName: String, onBackClick: () -> Unit) {
|
||||||
Row(
|
Box(
|
||||||
Modifier
|
modifier = Modifier
|
||||||
.fillMaxWidth()
|
.fillMaxWidth()
|
||||||
.background(MaterialTheme.colorScheme.background)
|
.background(MaterialTheme.colorScheme.background)
|
||||||
.padding(top = 52.dp, start = 24.dp, end = 24.dp),
|
.padding(top = 52.dp)
|
||||||
verticalAlignment = Alignment.CenterVertically,
|
|
||||||
) {
|
) {
|
||||||
IconButton(onBackClick, modifier = Modifier) {
|
IconButton(
|
||||||
Icon(Icons.AutoMirrored.Default.ArrowBack, contentDescription = "", tint = White)
|
onClick = onBackClick,
|
||||||
|
modifier = Modifier
|
||||||
|
.align(Alignment.CenterStart)
|
||||||
|
.padding(start = 18.dp)
|
||||||
|
) {
|
||||||
|
Icon(
|
||||||
|
imageVector = Icons.AutoMirrored.Default.ArrowBack,
|
||||||
|
contentDescription = "Back",
|
||||||
|
tint = White
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
Text(
|
Text(
|
||||||
leagueAndSerieName,
|
text = leagueAndSerieName,
|
||||||
|
modifier = Modifier
|
||||||
|
.align(Alignment.Center)
|
||||||
|
.requiredWidthIn(max = 250.dp),
|
||||||
textAlign = TextAlign.Center,
|
textAlign = TextAlign.Center,
|
||||||
style = MaterialTheme.typography.titleMedium,
|
style = MaterialTheme.typography.titleMedium,
|
||||||
color = White,
|
color = White,
|
||||||
|
@ -316,3 +322,20 @@ private fun playerFullNameOrEmpty(firstName: String?, lastName: String?): String
|
||||||
|
|
||||||
return if (fullName.isBlank()) "" else fullName
|
return if (fullName.isBlank()) "" else fullName
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun loadTeams(matchDetails: MatchDetailsRoute, viewModel: MatchDetailsViewModel) {
|
||||||
|
loadTeamOrPlaceholder(matchDetails.leftOpponentId, isLeft = true, viewModel)
|
||||||
|
loadTeamOrPlaceholder(matchDetails.rightOpponentId, isLeft = false, viewModel)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun loadTeamOrPlaceholder(
|
||||||
|
opponentId: Long,
|
||||||
|
isLeft: Boolean,
|
||||||
|
viewModel: MatchDetailsViewModel
|
||||||
|
) {
|
||||||
|
if (opponentId != -1L) {
|
||||||
|
viewModel.loadTeam(opponentId.toString(), isLeft)
|
||||||
|
} else {
|
||||||
|
viewModel.applyPlaceholderTeamToSide(isLeft)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -16,12 +16,12 @@ import androidx.compose.material3.TopAppBarDefaults
|
||||||
import androidx.compose.material3.pulltorefresh.PullToRefreshBox
|
import androidx.compose.material3.pulltorefresh.PullToRefreshBox
|
||||||
import androidx.compose.runtime.Composable
|
import androidx.compose.runtime.Composable
|
||||||
import androidx.compose.runtime.collectAsState
|
import androidx.compose.runtime.collectAsState
|
||||||
import androidx.compose.ui.Alignment
|
|
||||||
import androidx.compose.ui.Modifier
|
import androidx.compose.ui.Modifier
|
||||||
import androidx.compose.ui.input.nestedscroll.nestedScroll
|
import androidx.compose.ui.input.nestedscroll.nestedScroll
|
||||||
import androidx.compose.ui.unit.dp
|
import androidx.compose.ui.unit.dp
|
||||||
import androidx.hilt.navigation.compose.hiltViewModel
|
import androidx.hilt.navigation.compose.hiltViewModel
|
||||||
import xyz.leomurca.csgomatches.domain.model.Match
|
import xyz.leomurca.csgomatches.domain.model.Match
|
||||||
|
import xyz.leomurca.csgomatches.ui.components.ErrorMessage
|
||||||
import xyz.leomurca.csgomatches.ui.components.MatchCard
|
import xyz.leomurca.csgomatches.ui.components.MatchCard
|
||||||
import xyz.leomurca.csgomatches.ui.navigation.MatchDetailsRoute
|
import xyz.leomurca.csgomatches.ui.navigation.MatchDetailsRoute
|
||||||
import xyz.leomurca.csgomatches.ui.screens.matches.MatchesViewModel.MatchesUiState
|
import xyz.leomurca.csgomatches.ui.screens.matches.MatchesViewModel.MatchesUiState
|
||||||
|
@ -37,7 +37,8 @@ fun MatchesScreen(
|
||||||
val scrollBehavior = TopAppBarDefaults.enterAlwaysScrollBehavior()
|
val scrollBehavior = TopAppBarDefaults.enterAlwaysScrollBehavior()
|
||||||
|
|
||||||
Scaffold(
|
Scaffold(
|
||||||
modifier = Modifier.nestedScroll(scrollBehavior.nestedScrollConnection), topBar = {
|
modifier = Modifier.nestedScroll(scrollBehavior.nestedScrollConnection),
|
||||||
|
topBar = {
|
||||||
TopAppBar(
|
TopAppBar(
|
||||||
title = {
|
title = {
|
||||||
Text(
|
Text(
|
||||||
|
@ -53,7 +54,8 @@ fun MatchesScreen(
|
||||||
scrolledContainerColor = MaterialTheme.colorScheme.background
|
scrolledContainerColor = MaterialTheme.colorScheme.background
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
}) { innerPadding ->
|
}
|
||||||
|
) { innerPadding ->
|
||||||
PullToRefreshBox(
|
PullToRefreshBox(
|
||||||
isRefreshing = uiState.value is MatchesUiState.Loading,
|
isRefreshing = uiState.value is MatchesUiState.Loading,
|
||||||
onRefresh = { viewModel.loadUpcomingMatches() },
|
onRefresh = { viewModel.loadUpcomingMatches() },
|
||||||
|
@ -61,12 +63,15 @@ fun MatchesScreen(
|
||||||
.padding(innerPadding)
|
.padding(innerPadding)
|
||||||
.fillMaxSize()
|
.fillMaxSize()
|
||||||
.background(MaterialTheme.colorScheme.background),
|
.background(MaterialTheme.colorScheme.background),
|
||||||
contentAlignment = Alignment.Center
|
|
||||||
) {
|
) {
|
||||||
when (val value = uiState.value) {
|
when (val value = uiState.value) {
|
||||||
is MatchesUiState.Success -> MatchesList(value.matches, onTapCard)
|
is MatchesUiState.Success -> MatchesList(value.matches, onTapCard)
|
||||||
is MatchesUiState.Error -> Text(value.message)
|
is MatchesUiState.Error -> ErrorMessage(
|
||||||
else -> Unit // Do nothing
|
message = value.message,
|
||||||
|
onRetry = { viewModel.loadUpcomingMatches() }
|
||||||
|
)
|
||||||
|
|
||||||
|
else -> Unit
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Reference in a new issue