Add bet confirmation screen
continuous-integration/drone/push Build is passing Details

pull/4/head
Arthur VALIN 1 year ago
parent 8c073bdec0
commit 58d788f58f

@ -1,49 +1,48 @@
package fr.iut.alldev.allin.test.mock
import fr.iut.alldev.allin.data.model.bet.BetStatus
import fr.iut.alldev.allin.data.model.bet.CustomBet
import fr.iut.alldev.allin.data.model.bet.MatchBet
import fr.iut.alldev.allin.data.model.bet.YesNoBet
import java.time.ZonedDateTime
import fr.iut.alldev.allin.data.model.bet.vo.BetDetail
object Bets {
val bets by lazy {
listOf(
YesNoBet(
theme = "Theme",
phrase = "Phrase",
endRegisterDate = ZonedDateTime.now(),
endBetDate = ZonedDateTime.now(),
isPublic = true,
betStatus = BetStatus.InProgress,
creator = "creator",
),
MatchBet(
theme = "Theme",
phrase = "Phrase",
endRegisterDate = ZonedDateTime.now(),
endBetDate = ZonedDateTime.now(),
isPublic = true,
betStatus = BetStatus.InProgress,
nameTeam1 = "Team_1",
nameTeam2 = "Team_2",
creator = "creator"
),
CustomBet(
theme = "Theme",
phrase = "Phrase",
endRegisterDate = ZonedDateTime.now(),
endBetDate = ZonedDateTime.now(),
isPublic = true,
betStatus = BetStatus.InProgress,
creator = "creator",
possibleAnswers = listOf(
"Answer 1",
"Answer 2",
"Answer 3",
"Answer 4"
)
),
listOf<BetDetail>(
/* YesNoBet(
theme = "Theme",
phrase = "Phrase",
endRegisterDate = ZonedDateTime.now(),
endBetDate = ZonedDateTime.now(),
isPublic = true,
betStatus = BetStatus.InProgress,
creator = "creator",
id = ""
),
MatchBet(
theme = "Theme",
phrase = "Phrase",
endRegisterDate = ZonedDateTime.now(),
endBetDate = ZonedDateTime.now(),
isPublic = true,
betStatus = BetStatus.InProgress,
nameTeam1 = "Team_1",
nameTeam2 = "Team_2",
creator = "creator",
id = ""
),
CustomBet(
theme = "Theme",
phrase = "Phrase",
endRegisterDate = ZonedDateTime.now(),
endBetDate = ZonedDateTime.now(),
isPublic = true,
betStatus = BetStatus.InProgress,
creator = "creator",
possibleAnswers = listOf(
"Answer 1",
"Answer 2",
"Answer 3",
"Answer 4"
),
id = ""
)*/
)
}
}

@ -4,25 +4,23 @@ import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.compose.ui.platform.testTag
import fr.iut.alldev.allin.data.model.bet.CustomBet
import fr.iut.alldev.allin.data.model.bet.MatchBet
import fr.iut.alldev.allin.data.model.bet.YesNoBet
import fr.iut.alldev.allin.data.model.bet.vo.BetDetail
import fr.iut.alldev.allin.test.TestTags
import fr.iut.alldev.allin.vo.bet.BetDisplayer
class BetTestDisplayer : BetDisplayer {
@Composable
override fun DisplayYesNoBet(bet: YesNoBet) {
override fun DisplayYesNoBet(betDetail: BetDetail) {
Text("This is a YesNo Bet", Modifier.testTag(TestTags.YES_NO_BET.tag))
}
@Composable
override fun DisplayMatchBet(bet: MatchBet) {
override fun DisplayMatchBet(betDetail: BetDetail) {
Text("This is a Match Bet", Modifier.testTag(TestTags.MATCH_BET.tag))
}
@Composable
override fun DisplayCustomBet(bet: CustomBet) {
override fun DisplayCustomBet(betDetail: BetDetail) {
Text("This is a Custom Bet", Modifier.testTag(TestTags.CUSTOM_BET.tag))
}
}

@ -14,6 +14,9 @@ sealed class FieldErrorState(
) {
data object NoError : FieldErrorState()
data object Mandatory : FieldErrorState(R.string.FieldError_Mandatory)
data class TooShort(val fieldName: String, val minChar: Int) :
FieldErrorState(R.string.FieldError_TooShort, fieldName, minChar)

@ -5,17 +5,13 @@ import androidx.lifecycle.viewModelScope
import dagger.hilt.android.lifecycle.HiltViewModel
import fr.iut.alldev.allin.data.model.bet.Bet
import fr.iut.alldev.allin.data.repository.BetRepository
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.SharingStarted
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.asStateFlow
import kotlinx.coroutines.flow.emitAll
import kotlinx.coroutines.flow.filterNotNull
import kotlinx.coroutines.flow.flow
import kotlinx.coroutines.flow.stateIn
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
import javax.inject.Inject
@HiltViewModel
@ -23,14 +19,16 @@ class BetViewModel @Inject constructor(
private val betRepository: BetRepository
) : ViewModel() {
private val _isRefreshing = MutableStateFlow(false)
private val _isRefreshing by lazy { MutableStateFlow(false) }
val isRefreshing: StateFlow<Boolean>
get() = _isRefreshing.asStateFlow()
private val _bets: MutableStateFlow<List<Bet>> by lazy {
MutableStateFlow(emptyList())
}
val bets: StateFlow<List<Bet>> by lazy {
flow {
kotlin.runCatching { emitAll(betRepository.getAllBets()) }
}
_bets.asStateFlow()
.filterNotNull()
.stateIn(
viewModelScope,
@ -39,16 +37,22 @@ class BetViewModel @Inject constructor(
)
}
private fun refreshData() {
Thread.sleep(1000)
init {
viewModelScope.launch {
refreshData()
}
}
private suspend fun refreshData() {
runCatching {
_bets.emit(betRepository.getAllBets())
}
}
fun refresh() {
viewModelScope.launch {
_isRefreshing.emit(true)
withContext(Dispatchers.IO) {
refreshData()
}
refreshData()
_isRefreshing.emit(false)
}
}

@ -0,0 +1,388 @@
package fr.iut.alldev.allin.ui.betConfirmation
import androidx.compose.animation.core.animateFloatAsState
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.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.lazy.LazyColumn
import androidx.compose.foundation.lazy.items
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.Close
import androidx.compose.material3.Icon
import androidx.compose.material3.IconButton
import androidx.compose.material3.SheetState
import androidx.compose.material3.Text
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.alpha
import androidx.compose.ui.draw.scale
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.platform.LocalConfiguration
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.text.style.TextAlign
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp
import androidx.core.os.ConfigurationCompat
import fr.iut.alldev.allin.R
import fr.iut.alldev.allin.data.ext.formatToMediumDateNoYear
import fr.iut.alldev.allin.data.ext.formatToTime
import fr.iut.alldev.allin.data.model.bet.BetFinishedStatus
import fr.iut.alldev.allin.data.model.bet.BetStatus
import fr.iut.alldev.allin.data.model.bet.CustomBet
import fr.iut.alldev.allin.data.model.bet.MatchBet
import fr.iut.alldev.allin.data.model.bet.NO_VALUE
import fr.iut.alldev.allin.data.model.bet.YES_VALUE
import fr.iut.alldev.allin.data.model.bet.YesNoBet
import fr.iut.alldev.allin.data.model.bet.vo.BetAnswerDetail
import fr.iut.alldev.allin.data.model.bet.vo.BetDetail
import fr.iut.alldev.allin.ext.formatToSimple
import fr.iut.alldev.allin.theme.AllInTheme
import fr.iut.alldev.allin.ui.core.AllInBottomSheet
import fr.iut.alldev.allin.ui.core.AllInButton
import fr.iut.alldev.allin.ui.core.AllInCard
import fr.iut.alldev.allin.ui.core.AllInMarqueeBox
import fr.iut.alldev.allin.ui.core.bet.BetCard
import java.time.ZonedDateTime
import java.util.Locale
@Composable
fun BetConfirmationBottomSheet(
state: SheetState,
sheetVisibility: Boolean,
betDetail: BetDetail,
onDismiss: () -> Unit,
onConfirm: (selectedAnswer: String) -> Unit
) {
AllInBottomSheet(
sheetVisibility = sheetVisibility,
onDismiss = onDismiss,
state = state,
dragHandle = null
) {
BetConfirmationBottomSheetContent(
betDetail = betDetail,
onConfirm = {
onConfirm(it)
onDismiss()
},
onClose = onDismiss
)
}
}
@Composable
fun BetConfirmationBottomSheetAnswer(
text: String,
odds: Float,
modifier: Modifier = Modifier,
color: Color = AllInTheme.colors.allInBlue,
isSelected: Boolean,
locale: Locale,
onClick: () -> Unit
) {
val backColor = if (isSelected) AllInTheme.colors.allInPurple else AllInTheme.colors.white
val contentColor = if (isSelected) AllInTheme.colors.white else null
AllInCard(
backgroundColor = backColor,
onClick = onClick,
modifier = modifier
) {
Box(
modifier = Modifier
.fillMaxWidth()
.padding(horizontal = 12.dp, vertical = 18.dp),
) {
Text(
text = text.uppercase(),
color = contentColor ?: color,
style = AllInTheme.typography.h1,
fontSize = 40.sp,
modifier = Modifier.align(Alignment.Center)
)
AllInCard(
radius = 50.dp,
backgroundColor = contentColor ?: AllInTheme.colors.allInPurple,
modifier = Modifier.align(Alignment.CenterEnd)
) {
Box(Modifier.padding(vertical = 4.dp, horizontal = 8.dp)) {
Text(
text = "x${odds.formatToSimple(locale)}",
color = backColor,
style = AllInTheme.typography.h2
)
}
}
}
}
}
@Composable
fun ConfirmationAnswers(
betDetail: BetDetail,
selectedAnswer: String?,
onClick: (String) -> Unit
) {
val configuration = LocalConfiguration.current
val locale = remember { ConfigurationCompat.getLocales(configuration).get(0) ?: Locale.getDefault() }
LazyColumn(
verticalArrangement = Arrangement.spacedBy(8.dp)
) {
when (betDetail.bet) {
is CustomBet -> items((betDetail.bet as CustomBet).possibleAnswers) {
betDetail.getAnswerOfResponse(it)?.let {
val opacity by animateFloatAsState(targetValue = if (selectedAnswer != null && selectedAnswer != it.response) .5f else 1f, label = "")
BetConfirmationBottomSheetAnswer(
text = it.response,
odds = it.odds,
locale = locale,
onClick = { onClick(it.response) },
isSelected = selectedAnswer == it.response,
modifier = Modifier.alpha(opacity)
)
}
}
is MatchBet -> {
val bet = (betDetail.bet as MatchBet)
item {
betDetail.getAnswerOfResponse(bet.nameTeam1)?.let {
val opacity by animateFloatAsState(targetValue = if (selectedAnswer != null && selectedAnswer != it.response) .5f else 1f, label = "")
BetConfirmationBottomSheetAnswer(
text = it.response,
odds = it.odds,
locale = locale,
onClick = { onClick(it.response) },
isSelected = selectedAnswer == it.response,
modifier = Modifier.alpha(opacity)
)
}
}
item {
betDetail.getAnswerOfResponse(bet.nameTeam2)?.let {
val opacity by animateFloatAsState(targetValue = if (selectedAnswer != null && selectedAnswer != it.response) .5f else 1f, label = "")
BetConfirmationBottomSheetAnswer(
text = it.response,
color = AllInTheme.colors.allInBarPink,
odds = it.odds,
locale = locale,
onClick = { onClick(it.response) },
isSelected = selectedAnswer == it.response,
modifier = Modifier
.alpha(opacity)
)
}
}
}
is YesNoBet -> {
item {
betDetail.getAnswerOfResponse(YES_VALUE)?.let {
val opacity by animateFloatAsState(targetValue = if (selectedAnswer != null && selectedAnswer != it.response) .5f else 1f, label = "")
val scale by animateFloatAsState(
targetValue = if (selectedAnswer == null) 1f
else if (selectedAnswer != it.response) .95f else 1.05f,
label = ""
)
BetConfirmationBottomSheetAnswer(
text = it.response,
odds = it.odds,
locale = locale,
onClick = { onClick(it.response) },
isSelected = selectedAnswer == it.response,
modifier = Modifier
.alpha(opacity)
.scale(scale)
)
}
}
item {
betDetail.getAnswerOfResponse(NO_VALUE)?.let {
val opacity by animateFloatAsState(targetValue = if (selectedAnswer != null && selectedAnswer != it.response) .5f else 1f, label = "")
val scale by animateFloatAsState(
targetValue = if (selectedAnswer == null) 1f
else if (selectedAnswer != it.response) .95f else 1.05f,
label = ""
)
BetConfirmationBottomSheetAnswer(
text = it.response,
color = AllInTheme.colors.allInBarPink,
odds = it.odds,
locale = locale,
onClick = { onClick(it.response) },
isSelected = selectedAnswer == it.response,
modifier = Modifier
.alpha(opacity)
.scale(scale)
)
}
}
}
}
}
}
@Composable
fun BetConfirmationBottomSheetContent(
betDetail: BetDetail,
onConfirm: (String) -> Unit,
onClose: () -> Unit
) {
var selectedAnswer by remember { mutableStateOf<String?>(null) }
AllInMarqueeBox(backgroundColor = AllInTheme.colors.allInDarkGrey300) {
Box(
modifier = Modifier
.fillMaxSize()
.padding(16.dp)
) {
IconButton(
onClick = onClose,
modifier = Modifier
.size(24.dp)
.align(Alignment.TopStart)
) {
Icon(
imageVector = Icons.Default.Close,
tint = AllInTheme.colors.white,
contentDescription = null,
modifier = Modifier.size(24.dp)
)
}
Icon(
painter = painterResource(R.drawable.allin),
contentDescription = null,
tint = AllInTheme.colors.white,
modifier = Modifier
.size(40.dp)
.align(Alignment.TopCenter)
)
Column(
modifier = Modifier.align(Alignment.Center),
verticalArrangement = Arrangement.spacedBy(22.dp),
horizontalAlignment = Alignment.CenterHorizontally
) {
BetCard(
title = betDetail.bet.phrase,
creator = betDetail.bet.creator,
category = betDetail.bet.theme,
date = betDetail.bet.endBetDate.formatToMediumDateNoYear(),
time = betDetail.bet.endBetDate.formatToTime(),
status = betDetail.bet.betStatus
) {
Row(
modifier = Modifier
.fillMaxWidth()
.background(AllInTheme.colors.allInMainGradient)
.padding(16.dp),
horizontalArrangement = Arrangement.Center,
verticalAlignment = Alignment.CenterVertically
) {
Text(
text = stringResource(id = R.string.Finished),
color = AllInTheme.colors.white,
style = AllInTheme.typography.h1,
fontSize = 24.sp
)
}
}
Text(
text = "Ce bet est arrivé à la date de fin. Vous devez à présent distribuer les gains en validant le pari gagnant.",
color = AllInTheme.colors.allInLightGrey200,
style = AllInTheme.typography.p2,
textAlign = TextAlign.Center
)
Spacer(modifier = Modifier.height(16.dp))
Text(
text = "Veuillez choisir la réponse finale :",
fontSize = 20.sp,
color = AllInTheme.colors.white,
style = AllInTheme.typography.h1,
modifier = Modifier.fillMaxWidth()
)
ConfirmationAnswers(
betDetail = betDetail,
selectedAnswer = selectedAnswer
) { selectedAnswer = if (selectedAnswer == it) null else it }
}
if (selectedAnswer != null) {
AllInButton(
color = AllInTheme.colors.allInPurple,
text = stringResource(id = R.string.Validate),
textColor = AllInTheme.colors.white,
radius = 5.dp,
onClick = { selectedAnswer?.let(onConfirm) },
modifier = Modifier.align(Alignment.BottomCenter)
)
}
}
}
}
@Preview
@Composable
private fun BetConfirmationBottomSheetContentPreview() {
AllInTheme {
BetConfirmationBottomSheetContent(
betDetail = BetDetail(
bet = YesNoBet(
id = "1",
theme = "Theme",
phrase = "Phrase",
endRegisterDate = ZonedDateTime.now(),
endBetDate = ZonedDateTime.now(),
isPublic = true,
betStatus = BetStatus.Finished(BetFinishedStatus.WON),
creator = "creator",
),
answers = listOf(
BetAnswerDetail(
response = YES_VALUE,
totalStakes = 300,
totalParticipants = 2,
highestStake = 200,
odds = 1.0f
),
BetAnswerDetail(
response = NO_VALUE,
totalStakes = 150,
totalParticipants = 1,
highestStake = 150,
odds = 2.0f
)
),
participations = emptyList(),
userParticipation = null
),
onConfirm = { }
) {
}
}
}

@ -64,8 +64,6 @@ fun BetCreationScreen(
val themeFieldName = stringResource(id = R.string.Theme)
val phraseFieldName = stringResource(id = R.string.Bet_Phrase)
val registerDateFieldName = stringResource(id = R.string.End_registration_date)
val betDateFieldName = stringResource(id = R.string.End_bet_date)
LaunchedEffect(key1 = betTypes) {
selectionElements = betTypes.map {
@ -152,8 +150,6 @@ fun BetCreationScreen(
viewModel.createBet(
themeFieldName = themeFieldName,
phraseFieldName = phraseFieldName,
registerDateFieldName = registerDateFieldName,
betDateFieldName = betDateFieldName,
setLoading = setLoading,
onError = { hasError = true },
onSuccess = {

@ -17,9 +17,6 @@ import timber.log.Timber
import java.time.ZonedDateTime
import javax.inject.Inject
const val THEME_MIN_SIZE = 3
const val PHRASE_MIN_SIZE = 5
@HiltViewModel
class BetCreationViewModel @Inject constructor(
@AllInCurrentUser val currentUser: User,
@ -27,7 +24,7 @@ class BetCreationViewModel @Inject constructor(
private val keystoreManager: AllInKeystoreManager
) : ViewModel() {
var hasError = mutableStateOf(false)
private var hasError = mutableStateOf(false)
var theme = mutableStateOf("")
var phrase = mutableStateOf("")
val registerDate = mutableStateOf(ZonedDateTime.now())
@ -49,20 +46,16 @@ class BetCreationViewModel @Inject constructor(
}
private fun verifyField(
themeFieldName: String,
phraseFieldName: String,
registerDateFieldName: String,
betDateFieldName: String,
) {
if (theme.value.length < THEME_MIN_SIZE) {
themeError.value =
FieldErrorState.TooShort(themeFieldName.lowercase(), THEME_MIN_SIZE)
if (theme.value.isBlank()) {
themeError.value = FieldErrorState.Mandatory
hasError.value = true
}
if (phrase.value.length < PHRASE_MIN_SIZE) {
phraseError.value =
FieldErrorState.TooShort(phraseFieldName.lowercase(), PHRASE_MIN_SIZE)
if (phrase.value.isBlank()) {
phraseError.value = FieldErrorState.Mandatory
hasError.value = true
}
@ -90,8 +83,6 @@ class BetCreationViewModel @Inject constructor(
fun createBet(
themeFieldName: String,
phraseFieldName: String,
registerDateFieldName: String,
betDateFieldName: String,
onError: () -> Unit,
setLoading: (Boolean) -> Unit,
onSuccess: () -> Unit
@ -103,8 +94,6 @@ class BetCreationViewModel @Inject constructor(
verifyField(
themeFieldName,
phraseFieldName,
registerDateFieldName,
betDateFieldName,
)
if (!hasError.value) {

@ -1,13 +1,9 @@
package fr.iut.alldev.allin.ui.betResult
import androidx.compose.foundation.MarqueeSpacing
import androidx.compose.foundation.background
import androidx.compose.foundation.basicMarquee
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.offset
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.size
import androidx.compose.material.icons.Icons
@ -18,8 +14,6 @@ import androidx.compose.material3.SheetState
import androidx.compose.runtime.Composable
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.rotate
import androidx.compose.ui.draw.scale
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp
@ -34,6 +28,7 @@ import fr.iut.alldev.allin.ui.betResult.components.BetResultBottomSheetBetCard
import fr.iut.alldev.allin.ui.betResult.components.BetResultBottomSheetContentCoinAmount
import fr.iut.alldev.allin.ui.betResult.components.BetResultBottomSheetContentCongratulations
import fr.iut.alldev.allin.ui.core.AllInBottomSheet
import fr.iut.alldev.allin.ui.core.AllInMarqueeBox
import java.time.ZonedDateTime
@Composable
@ -76,22 +71,7 @@ fun BetResultBottomSheetContent(
odds: Float,
onClose: () -> Unit
) {
Box(
modifier = Modifier
.fillMaxSize()
.background(AllInTheme.colors.allInMainGradientReverse),
) {
Icon(
painter = painterResource(id = R.drawable.allin_marquee),
contentDescription = null,
modifier = Modifier
.fillMaxSize()
.rotate(11f)
.scale(1.2f)
.offset(x = (-24).dp)
.basicMarquee(spacing = MarqueeSpacing(0.dp)),
tint = AllInTheme.colors.white.copy(alpha = .05f)
)
AllInMarqueeBox(backgroundBrush = AllInTheme.colors.allInMainGradientReverse) {
Box(
modifier = Modifier
.fillMaxSize()
@ -146,7 +126,6 @@ fun BetResultBottomSheetContent(
}
@Preview
@Preview(widthDp = 800, heightDp = 1280)
@Composable
private fun BetResultBottomSheetContentPreview() {
AllInTheme {

@ -7,42 +7,11 @@ import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.painter.Painter
import androidx.compose.ui.graphics.vector.ImageVector
import androidx.compose.ui.graphics.vector.rememberVectorPainter
import androidx.compose.ui.tooling.preview.Preview
import fr.iut.alldev.allin.theme.AllInTheme
import fr.iut.alldev.allin.ui.core.AllInTextIcon
import fr.iut.alldev.allin.ui.core.IconPosition
@Composable
fun YesNoDetailsLine(
icon: ImageVector,
yesText: String,
noText: String,
) {
Row(
modifier = Modifier.fillMaxWidth(),
horizontalArrangement = Arrangement.SpaceBetween
) {
AllInTextIcon(
text = yesText,
color = AllInTheme.colors.allInBlue,
icon = rememberVectorPainter(image = icon),
position = IconPosition.LEADING,
size = 10,
iconSize = 15
)
AllInTextIcon(
text = noText,
color = AllInTheme.colors.allInBarPink,
icon = rememberVectorPainter(image = icon),
position = IconPosition.TRAILING,
size = 10,
iconSize = 15
)
}
}
@Composable
fun YesNoDetailsLine(
icon: Painter,
@ -58,7 +27,7 @@ fun YesNoDetailsLine(
color = AllInTheme.colors.allInBlue,
icon = icon,
position = IconPosition.LEADING,
size = 10,
size = 15,
iconSize = 15
)
AllInTextIcon(
@ -66,7 +35,7 @@ fun YesNoDetailsLine(
color = AllInTheme.colors.allInBarPink,
icon = icon,
position = IconPosition.TRAILING,
size = 10,
size = 15,
iconSize = 15
)
}

@ -15,6 +15,7 @@ import androidx.compose.material3.Text
import androidx.compose.runtime.*
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.vector.rememberVectorPainter
import androidx.compose.ui.platform.LocalConfiguration
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.text.font.FontWeight
@ -131,17 +132,17 @@ class BetStatusBottomSheetBetDisplayer(
noText = response2Answer?.totalStakes?.toString() ?: "0"
)
YesNoDetailsLine(
icon = Icons.Filled.People,
icon = rememberVectorPainter(image = Icons.Filled.People),
yesText = response1Answer?.totalParticipants?.toString() ?: "0",
noText = response2Answer?.totalParticipants?.toString() ?: "0"
)
YesNoDetailsLine(
icon = Icons.Filled.WorkspacePremium,
icon = rememberVectorPainter(image = Icons.Filled.WorkspacePremium),
yesText = response1Answer?.highestStake?.toString() ?: "0",
noText = response2Answer?.highestStake?.toString() ?: "0"
)
YesNoDetailsLine(
icon = Icons.Filled.EmojiEvents,
icon = rememberVectorPainter(image = Icons.Filled.EmojiEvents),
yesText = "x${response1Answer?.odds?.formatToSimple(locale) ?: "1.00"}",
noText = "x${response2Answer?.odds?.formatToSimple(locale) ?: "1.00"}"
)

@ -0,0 +1,65 @@
package fr.iut.alldev.allin.ui.core
import androidx.compose.foundation.MarqueeSpacing
import androidx.compose.foundation.background
import androidx.compose.foundation.basicMarquee
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.BoxScope
import androidx.compose.foundation.layout.aspectRatio
import androidx.compose.foundation.layout.fillMaxHeight
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.rotate
import androidx.compose.ui.draw.scale
import androidx.compose.ui.graphics.Brush
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp
import fr.iut.alldev.allin.R
import fr.iut.alldev.allin.theme.AllInTheme
@Composable
fun AllInMarqueeBox(
backgroundColor: Color = AllInTheme.themeColors.mainSurface,
backgroundBrush: Brush? = null,
content: @Composable BoxScope.() -> Unit
) {
Box(
modifier = Modifier
.fillMaxSize().let { itModifier ->
backgroundBrush?.let {
itModifier.background(it)
} ?: itModifier.background(backgroundColor)
}
) {
Icon(
painter = painterResource(id = R.drawable.allin_marquee),
contentDescription = null,
modifier = Modifier
.fillMaxHeight()
.aspectRatio(1f, true)
.scale(1.2f)
.rotate(11f)
.basicMarquee(spacing = MarqueeSpacing(0.dp)),
tint = AllInTheme.colors.white.copy(alpha = .05f)
)
content()
}
}
@Preview
@Preview(widthDp = 800, heightDp = 1280)
@Composable
private fun AllInMarqueeBoxPreview() {
AllInTheme {
AllInMarqueeBox(
backgroundBrush = AllInTheme.colors.allInMainGradient,
) {
Text("CONTENT")
}
}
}

@ -11,6 +11,7 @@ import androidx.hilt.navigation.compose.hiltViewModel
import androidx.navigation.NavHostController
import androidx.navigation.compose.rememberNavController
import fr.iut.alldev.allin.theme.AllInTheme
import fr.iut.alldev.allin.ui.betConfirmation.BetConfirmationBottomSheet
import fr.iut.alldev.allin.ui.betResult.BetResultBottomSheet
import fr.iut.alldev.allin.ui.betStatus.BetStatusBottomSheet
import fr.iut.alldev.allin.ui.betStatus.vo.BetStatusBottomSheetBetDisplayer
@ -68,11 +69,10 @@ fun MainScreen(
var loading by remember { mainViewModel.loading }
val currentUser = remember { mainViewModel.currentUserState }
val selectedBet by remember { mainViewModel.selectedBet }
val (wonBet, setWonBet) = remember { mainViewModel.wonBet }
val wonBet by remember { mainViewModel.wonBet }
val toConfirm by remember { mainViewModel.toConfirmBet }
val (statusVisibility, sheetBackVisibility, setStatusVisibility) = rememberBetStatusVisibilities()
val (participateSheetVisibility, setParticipateSheetVisibility) = remember {
mutableStateOf(false)
}
val (participateSheetVisibility, setParticipateSheetVisibility) = remember { mutableStateOf(false) }
val (displayResult, setDisplayResult) = remember { mutableStateOf(true) }
@ -168,7 +168,7 @@ fun MainScreen(
state = resultBottomSheetState,
sheetVisibility = displayResult,
onDismiss = { setDisplayResult(false) },
bet = wonBet,
bet = it,
username = currentUser.user.username,
coinAmount = 1630,
stake = 1630,
@ -177,6 +177,15 @@ fun MainScreen(
)
}
toConfirm?.let {
BetConfirmationBottomSheet(
state = resultBottomSheetState,
sheetVisibility = displayResult,
betDetail = it,
onDismiss = { setDisplayResult(false) }
) { /*TODO*/ }
}
BetStatusBottomSheet(
state = statusBottomSheetState,
sheetVisibility = statusVisibility.value,

@ -8,7 +8,13 @@ import androidx.lifecycle.viewModelScope
import dagger.hilt.android.lifecycle.HiltViewModel
import fr.iut.alldev.allin.data.model.User
import fr.iut.alldev.allin.data.model.bet.Bet
import fr.iut.alldev.allin.data.model.bet.BetFinishedStatus
import fr.iut.alldev.allin.data.model.bet.BetStatus
import fr.iut.alldev.allin.data.model.bet.NO_VALUE
import fr.iut.alldev.allin.data.model.bet.Participation
import fr.iut.alldev.allin.data.model.bet.YES_VALUE
import fr.iut.alldev.allin.data.model.bet.YesNoBet
import fr.iut.alldev.allin.data.model.bet.vo.BetAnswerDetail
import fr.iut.alldev.allin.data.model.bet.vo.BetDetail
import fr.iut.alldev.allin.data.repository.BetRepository
import fr.iut.alldev.allin.di.AllInCurrentUser
@ -17,6 +23,7 @@ import fr.iut.alldev.allin.ui.core.snackbar.SnackbarType
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
import java.time.ZonedDateTime
import javax.inject.Inject
class UserState(val user: User) {
@ -48,6 +55,39 @@ class MainViewModel @Inject constructor(
)*/
)
val toConfirmBet = mutableStateOf<BetDetail?>(
BetDetail(
bet = YesNoBet(
id = "1",
theme = "Theme",
phrase = "Phrase",
endRegisterDate = ZonedDateTime.now(),
endBetDate = ZonedDateTime.now(),
isPublic = true,
betStatus = BetStatus.Finished(BetFinishedStatus.WON),
creator = "creator",
),
answers = listOf(
BetAnswerDetail(
response = YES_VALUE,
totalStakes = 300,
totalParticipants = 2,
highestStake = 200,
odds = 1.0f
),
BetAnswerDetail(
response = NO_VALUE,
totalStakes = 150,
totalParticipants = 1,
highestStake = 150,
odds = 2.0f
)
),
participations = emptyList(),
userParticipation = null
)
)
val snackbarContent: MutableState<SnackbarContent?> by lazy { mutableStateOf(null) }
fun putSnackbarContent(content: SnackbarContent) {
snackbarContent.value = content

@ -25,7 +25,7 @@ class BetDetailPreviewProvider : PreviewParameterProvider<BetDetail> {
totalParticipants = 1,
highestStake = 150,
odds = 2.0f
),
)
),
participations = listOf(
Participation(

@ -14,6 +14,7 @@
<string name="Validate">Valider</string>
<string name="Cancel">Annuler</string>
<string name="Ok">OK</string>
<string name="FieldError_Mandatory">Ce champ est obligatoire.</string>
<string name="FieldError_TooShort">Le %s doit contenir au moins %d caractères.</string>
<string name="FieldError_BadFormat">Le %s a un mauvais format : %s.</string>
<string name="FieldError_NotIdentical">Les champs ne sont pas identiques.</string>

@ -16,6 +16,7 @@
<string name="Validate">Validate</string>
<string name="Cancel">Cancel</string>
<string name="Ok">OK</string>
<string name="FieldError_Mandatory">This field is mandatory.</string>
<string name="FieldError_TooShort">The %s must contain at least %d characters.</string>
<string name="FieldError_BadFormat">The %s has bad format : %s.</string>
<string name="FieldError_NotIdentical">The fields are not identical.</string>

@ -11,5 +11,5 @@ abstract class BetRepository {
abstract suspend fun getCurrentBets(): Flow<List<Bet>>
abstract suspend fun getBet(id: String, token: String): BetDetail
abstract suspend fun participateToBet(participation: Participation, token: String)
abstract suspend fun getAllBets(): Flow<List<Bet>>
abstract suspend fun getAllBets(): List<Bet>
}

@ -110,10 +110,8 @@ class BetRepositoryImpl @Inject constructor(
api.participateToBet(token = token.formatBearerToken(), body = participation.toRequestParticipation())
}
override suspend fun getAllBets(): Flow<List<Bet>> {
return flowOf(
api.getAllBets().map { it.toBet() }
)
override suspend fun getAllBets(): List<Bet> {
return api.getAllBets().map { it.toBet() }
}
}
Loading…
Cancel
Save