Add custom bet status visuals
continuous-integration/drone/push Build is passing Details

pull/5/head
avalin 9 months ago
parent cdcca58940
commit a35e8a1d6b

@ -29,6 +29,7 @@ data class AllInColors(
val allInDarkBlue: Color,
val allInBarPurple: Color,
val allInBarPink: Color,
val allInBarViolet: Color,
val allInBetFinish: Color,
val allInBetInProgress: Color,
val allInBetWaiting: Color,
@ -64,6 +65,7 @@ internal val LocalColors = staticCompositionLocalOf {
allInLoginPurple = Color.Unspecified,
allInBarPurple = Color.Unspecified,
allInBarPink = Color.Unspecified,
allInBarViolet = Color.Unspecified,
allInBlue = Color.Unspecified,
allInMint = Color.Unspecified,
allInDarkBlue = Color.Unspecified,

@ -38,6 +38,7 @@ fun AllInTheme(
allInLoginPurple = Color(0xFF7F7BFB),
allInBarPurple = Color(0xFF846AC9),
allInBarPink = Color(0xFFFE2B8A),
allInBarViolet = Color(0xFFC249A8),
allInBlue = Color(0xFF6a89fa),
allInMint = Color(0xFFC4DEE9),
allInDarkBlue = Color(0xFF323078),

@ -13,7 +13,7 @@ import fr.iut.alldev.allin.ui.core.AllInTextIcon
import fr.iut.alldev.allin.ui.core.IconPosition
@Composable
fun YesNoDetailsLine(
fun BinaryDetailsLine(
icon: Painter,
yesText: String,
noText: String,
@ -46,7 +46,7 @@ fun YesNoDetailsLine(
@Composable
private fun YesNoDetailsLinePreview() {
AllInTheme {
YesNoDetailsLine(
BinaryDetailsLine(
icon = AllInTheme.icons.allCoins(),
yesText = "550",
noText = "330"

@ -56,15 +56,15 @@ fun BinaryStatBar(
}
private class YesNoStatBarPreviewProvider : PreviewParameterProvider<Float> {
private class BinaryStatBarPreviewProvider : PreviewParameterProvider<Float> {
override val values = sequenceOf(0f, .33f, .5f, .66f, 1f)
}
@Preview
@Composable
private fun YesNoStatBarPreview(
@PreviewParameter(YesNoStatBarPreviewProvider::class) percentage: Float,
private fun BinaryStatBarPreview(
@PreviewParameter(BinaryStatBarPreviewProvider::class) percentage: Float,
) {
AllInTheme {
BinaryStatBar(

@ -0,0 +1,61 @@
package fr.iut.alldev.allin.ui.betStatus.components
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Row
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.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 SimpleDetailsLine(
icon: Painter,
text: String,
isWin: Boolean
) {
Row(
modifier = Modifier.fillMaxWidth(),
horizontalArrangement = Arrangement.End
) {
AllInTextIcon(
text = text,
color = if (isWin) {
AllInTheme.colors.allInBarViolet
} else {
AllInTheme.colors.allInBlue
},
icon = icon,
position = IconPosition.TRAILING,
size = 15,
iconSize = 15
)
}
}
@Preview
@Composable
private fun SimpleDetailsLineWinPreview() {
AllInTheme {
SimpleDetailsLine(
icon = AllInTheme.icons.allCoins(),
text = "550",
isWin = true
)
}
}
@Preview
@Composable
private fun SimpleDetailsLinePreview() {
AllInTheme {
SimpleDetailsLine(
icon = AllInTheme.icons.allCoins(),
text = "550",
isWin = false
)
}
}

@ -0,0 +1,121 @@
package fr.iut.alldev.allin.ui.betStatus.components
import androidx.compose.foundation.background
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.offset
import androidx.compose.foundation.layout.size
import androidx.compose.foundation.shape.AbsoluteRoundedCornerShape
import androidx.compose.material3.Icon
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.res.painterResource
import androidx.compose.ui.text.font.FontStyle
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp
import fr.iut.alldev.allin.R
import fr.iut.alldev.allin.data.ext.toPercentageString
import fr.iut.alldev.allin.theme.AllInTheme
@Composable
fun SimpleStatBar(
percentage: Float,
response: String,
isWin: Boolean,
modifier: Modifier = Modifier,
) {
Column(modifier) {
Text(
text = response,
color = if (isWin) {
AllInTheme.colors.allInBarPink
} else {
AllInTheme.colors.allInBlue
},
style = AllInTheme.typography.sm2,
fontStyle = FontStyle.Italic,
fontSize = 18.sp
)
Row(verticalAlignment = Alignment.CenterVertically) {
Box(
modifier = Modifier
.height(20.dp)
.let { itModifier ->
if (percentage != 0f && !percentage.isNaN()) itModifier.weight(percentage)
else itModifier
}
.clip(
AbsoluteRoundedCornerShape(
topLeftPercent = 50,
bottomLeftPercent = 50,
topRightPercent = 0,
bottomRightPercent = 0
)
)
.background(
if (isWin) {
AllInTheme.colors.allInBar2ndGradient
} else {
AllInTheme.colors.allInBar1stGradient
}
)
)
Icon(
painter = painterResource(id = R.drawable.fire_solid),
tint = if (isWin) {
AllInTheme.colors.allInBarViolet
} else {
AllInTheme.colors.allInBarPurple
},
contentDescription = null,
modifier = Modifier.size(32.dp).offset((-7).dp)
)
Text(
modifier = Modifier.let { itModifier ->
if (percentage != 1f && !percentage.isNaN()) itModifier.weight(1 - percentage)
else itModifier
},
text = percentage.toPercentageString(),
style = AllInTheme.typography.h1.copy(
fontSize = if (isWin) 24.sp else 16.sp
),
color = if (isWin) {
AllInTheme.colors.allInBarViolet
} else {
AllInTheme.colors.allInBarPurple
}
)
}
}
}
@Preview
@Composable
private fun SimpleStatBarBarWinPreview() {
AllInTheme {
SimpleStatBar(
percentage = .8f,
response = "Answer",
isWin = true
)
}
}
@Preview
@Composable
private fun SimpleStatBarBarPreview() {
AllInTheme {
SimpleStatBar(
percentage = .4f,
response = "Answer",
isWin = false
)
}
}

@ -18,7 +18,9 @@ import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.size
import androidx.compose.foundation.layout.width
import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.lazy.LazyListScope
import androidx.compose.foundation.lazy.items
import androidx.compose.foundation.lazy.itemsIndexed
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.EmojiEvents
import androidx.compose.material.icons.filled.People
@ -49,6 +51,7 @@ 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.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
@ -59,8 +62,10 @@ import fr.iut.alldev.allin.ext.getDateEndLabelId
import fr.iut.alldev.allin.ext.getDateStartLabelId
import fr.iut.alldev.allin.theme.AllInTheme
import fr.iut.alldev.allin.ui.betStatus.components.BetStatusWinner
import fr.iut.alldev.allin.ui.betStatus.components.BinaryDetailsLine
import fr.iut.alldev.allin.ui.betStatus.components.BinaryStatBar
import fr.iut.alldev.allin.ui.betStatus.components.YesNoDetailsLine
import fr.iut.alldev.allin.ui.betStatus.components.SimpleDetailsLine
import fr.iut.alldev.allin.ui.betStatus.components.SimpleStatBar
import fr.iut.alldev.allin.ui.core.AllInCoinCount
import fr.iut.alldev.allin.ui.core.AllInDetailsDrawer
import fr.iut.alldev.allin.ui.core.ProfilePicture
@ -75,19 +80,10 @@ class BetStatusBottomSheetBetDisplayer(
val openParticipateSheet: () -> Unit
) : BetDisplayer {
@Composable
private fun DisplayBinaryBet(
private fun DisplayBet(
betDetail: BetDetail,
response1: String,
response2: String,
response1Display: String = response1,
response2Display: String = response2
statBar: LazyListScope.() -> Unit
) {
val configuration = LocalConfiguration.current
val locale = remember { ConfigurationCompat.getLocales(configuration).get(0) ?: Locale.getDefault() }
val response1Answer = remember { betDetail.getAnswerOfResponse(response1) }
val response2Answer = remember { betDetail.getAnswerOfResponse(response2) }
Box(Modifier) {
Column {
Column(Modifier.padding(horizontal = 20.dp)) {
@ -119,7 +115,7 @@ class BetStatusBottomSheetBetDisplayer(
}
if (betDetail.bet.betStatus == BetStatus.FINISHED) {
BetStatusWinner(
answer = response1Display,
answer = YES_VALUE,
color = AllInTheme.colors.allInBlue,
coinAmount = 442,
username = "Imri",
@ -139,51 +135,23 @@ class BetStatusBottomSheetBetDisplayer(
source: NestedScrollSource
) = available.copy(x = 0f)
}),
contentPadding = WindowInsets.navigationBars.asPaddingValues(horizontal = 20.dp)
contentPadding = WindowInsets.navigationBars.asPaddingValues(top = 20.dp, start = 20.dp, end = 20.dp)
) {
item {
Spacer(modifier = Modifier.height(20.dp))
BinaryStatBar(
response1Percentage = remember {
val total = (response1Answer?.totalParticipants
?: 0) + (response2Answer?.totalParticipants ?: 0)
if (total == 0) .5f else (response1Answer?.totalParticipants
?: 0) / total.toFloat()
},
response1 = response1Display,
response2 = response2Display
)
AllInDetailsDrawer {
YesNoDetailsLine(
icon = AllInTheme.icons.allCoins(),
yesText = response1Answer?.totalStakes?.toString() ?: "0",
noText = response2Answer?.totalStakes?.toString() ?: "0"
)
YesNoDetailsLine(
icon = rememberVectorPainter(image = Icons.Filled.People),
yesText = response1Answer?.totalParticipants?.toString() ?: "0",
noText = response2Answer?.totalParticipants?.toString() ?: "0"
)
YesNoDetailsLine(
icon = rememberVectorPainter(image = Icons.Filled.WorkspacePremium),
yesText = response1Answer?.highestStake?.toString() ?: "0",
noText = response2Answer?.highestStake?.toString() ?: "0"
)
YesNoDetailsLine(
icon = rememberVectorPainter(image = Icons.Filled.EmojiEvents),
yesText = "x${response1Answer?.odds?.formatToSimple(locale) ?: "1.00"}",
noText = "x${response2Answer?.odds?.formatToSimple(locale) ?: "1.00"}"
statBar(this)
if (betDetail.participations.isNotEmpty() || betDetail.userParticipation != null) {
item {
Text(
text = stringResource(id = R.string.bet_status_participants_list),
fontSize = 20.sp,
color = AllInTheme.themeColors.onMainSurface,
style = AllInTheme.typography.h1,
modifier = Modifier.padding(vertical = 36.dp)
)
}
Text(
text = stringResource(id = R.string.bet_status_participants_list),
fontSize = 20.sp,
color = AllInTheme.themeColors.onMainSurface,
style = AllInTheme.typography.h1,
modifier = Modifier.padding(vertical = 36.dp)
)
}
betDetail.userParticipation?.let {
item {
BetStatusParticipant(
@ -233,30 +201,143 @@ class BetStatusBottomSheetBetDisplayer(
}
}
private fun LazyListScope.displayBinaryStatBar(
betDetail: BetDetail,
response1: String,
response2: String,
response1Display: @Composable () -> String = { response1 },
response2Display: @Composable () -> String = { response2 }
) {
item {
val configuration = LocalConfiguration.current
val locale = remember { ConfigurationCompat.getLocales(configuration).get(0) ?: Locale.getDefault() }
val response1Answer = remember { betDetail.getAnswerOfResponse(response1) }
val response2Answer = remember { betDetail.getAnswerOfResponse(response2) }
BinaryStatBar(
response1Percentage = remember {
response1Answer?.let { betDetail.getPercentageOfAnswer(response1Answer) } ?: 0f
},
response1 = response1Display(),
response2 = response2Display()
)
AllInDetailsDrawer {
BinaryDetailsLine(
icon = AllInTheme.icons.allCoins(),
yesText = response1Answer?.totalStakes?.toString() ?: "0",
noText = response2Answer?.totalStakes?.toString() ?: "0"
)
BinaryDetailsLine(
icon = rememberVectorPainter(image = Icons.Filled.People),
yesText = response1Answer?.totalParticipants?.toString() ?: "0",
noText = response2Answer?.totalParticipants?.toString() ?: "0"
)
BinaryDetailsLine(
icon = rememberVectorPainter(image = Icons.Filled.WorkspacePremium),
yesText = response1Answer?.highestStake?.toString() ?: "0",
noText = response2Answer?.highestStake?.toString() ?: "0"
)
BinaryDetailsLine(
icon = rememberVectorPainter(image = Icons.Filled.EmojiEvents),
yesText = "x${response1Answer?.odds?.formatToSimple(locale) ?: "1.00"}",
noText = "x${response2Answer?.odds?.formatToSimple(locale) ?: "1.00"}"
)
}
}
}
private fun LazyListScope.displayMultiStatBar(
betDetail: BetDetail,
responses: List<String>
) {
val responsesWithDetail = responses.mapNotNull {
betDetail.getAnswerOfResponse(it)
}.associateWith {
betDetail.getPercentageOfAnswer(it)
}
itemsIndexed(responsesWithDetail.toList().sortedByDescending { it.second }) { idx, (answer, percentage) ->
val isWin = remember { idx == 0 }
val configuration = LocalConfiguration.current
val locale = remember { ConfigurationCompat.getLocales(configuration).get(0) ?: Locale.getDefault() }
SimpleStatBar(
percentage = percentage,
response = answer.response,
isWin = isWin
)
AllInDetailsDrawer {
SimpleDetailsLine(
icon = AllInTheme.icons.allCoins(),
text = answer.totalStakes.toString(),
isWin = isWin
)
SimpleDetailsLine(
icon = rememberVectorPainter(image = Icons.Filled.People),
text = answer.totalParticipants.toString(),
isWin = isWin
)
SimpleDetailsLine(
icon = rememberVectorPainter(image = Icons.Filled.WorkspacePremium),
text = answer.highestStake.toString(),
isWin = isWin
)
SimpleDetailsLine(
icon = rememberVectorPainter(image = Icons.Filled.EmojiEvents),
text = "x${answer.odds.formatToSimple(locale)}",
isWin = isWin
)
}
}
}
@Composable
override fun DisplayYesNoBet(betDetail: BetDetail) {
DisplayBinaryBet(
betDetail = betDetail,
response1 = YES_VALUE,
response2 = NO_VALUE,
response1Display = stringResource(id = R.string.Yes).uppercase(),
response2Display = stringResource(id = R.string.No).uppercase()
)
DisplayBet(betDetail = betDetail) {
displayBinaryStatBar(
betDetail = betDetail,
response1 = YES_VALUE,
response2 = NO_VALUE,
response1Display = { stringResource(id = R.string.Yes).uppercase() },
response2Display = { stringResource(id = R.string.No).uppercase() }
)
}
}
@Composable
override fun DisplayMatchBet(betDetail: BetDetail) {
val bet = remember { betDetail.bet as MatchBet }
DisplayBinaryBet(
betDetail = betDetail,
response1 = bet.nameTeam1,
response2 = bet.nameTeam2
)
val matchBet = remember { betDetail.bet as MatchBet }
DisplayBet(betDetail = betDetail) {
displayBinaryStatBar(
betDetail = betDetail,
response1 = matchBet.nameTeam1,
response2 = matchBet.nameTeam2
)
}
}
@Composable
override fun DisplayCustomBet(betDetail: BetDetail) {
Text("This is a CUSTOM BET")
val customBet = remember { betDetail.bet as CustomBet }
DisplayBet(betDetail = betDetail) {
if (customBet.possibleAnswers.size == 2) {
displayBinaryStatBar(
betDetail = betDetail,
response1 = customBet.possibleAnswers.first(),
response2 = customBet.possibleAnswers.last()
)
} else {
displayMultiStatBar(
betDetail = betDetail,
responses = customBet.getResponses()
)
}
}
}
}

@ -4,17 +4,17 @@ import androidx.compose.foundation.Image
import androidx.compose.foundation.background
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.size
import androidx.compose.foundation.layout.width
import androidx.compose.foundation.shape.AbsoluteRoundedCornerShape
import androidx.compose.material3.Icon
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.graphics.Brush
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp
@ -24,58 +24,60 @@ import fr.iut.alldev.allin.theme.AllInTheme
@Composable
fun StatBar(
percentage: Float,
modifier: Modifier = Modifier,
leadingBrush: Brush = AllInTheme.colors.allInBar1stGradient,
trailingBrush: Brush = AllInTheme.colors.allInBar2ndGradient,
icon: (@Composable () -> Unit)? = null,
) {
val radius100percent = if (percentage == 1f) 50 else 0
val radius0percent = if (percentage == 0f) 50 else 0
Box {
Box(modifier = modifier) {
Row(
Modifier.align(Alignment.Center)
modifier = Modifier.align(Alignment.Center),
verticalAlignment = Alignment.CenterVertically
) {
Box(
modifier = Modifier
.height(20.dp)
.fillMaxWidth(percentage)
.padding(end = if (percentage == 1f) 25.dp else 0.dp)
.clip(
AbsoluteRoundedCornerShape(
topLeftPercent = 50,
bottomLeftPercent = 50,
topRightPercent = radius100percent,
bottomRightPercent = radius100percent
topRightPercent = 0,
bottomRightPercent = 0
)
)
.background(AllInTheme.colors.allInBar1stGradient)
.background(leadingBrush)
)
if (percentage != 0f && percentage != 1f) {
Spacer(modifier = Modifier.width(15.dp))
}
Box(
modifier = Modifier
.height(20.dp)
.fillMaxWidth()
.padding(start = if (percentage == 0f) 25.dp else 15.dp)
.clip(
AbsoluteRoundedCornerShape(
topLeftPercent = radius0percent,
bottomLeftPercent = radius0percent,
topLeftPercent = 0,
bottomLeftPercent = 0,
topRightPercent = 50,
bottomRightPercent = 50
)
)
.background(AllInTheme.colors.allInBar2ndGradient)
.background(trailingBrush)
)
}
PercentagePositionnedElement(percentage = percentage) {
when (percentage) {
icon?.invoke() ?: when (percentage) {
0f -> Icon(
painter = painterResource(id = R.drawable.fire_solid),
tint = AllInTheme.colors.allInBarPink,
tint = AllInTheme.colors.allInPink,
contentDescription = null,
modifier = Modifier.size(32.dp)
)
1f -> Icon(
painter = painterResource(id = R.drawable.fire_solid),
tint = AllInTheme.colors.allInBarPurple,
tint = AllInTheme.colors.allInPurple,
contentDescription = null,
modifier = Modifier.size(32.dp)
)

@ -1,9 +1,12 @@
package fr.iut.alldev.allin.ui.preview
import androidx.compose.ui.tooling.preview.PreviewParameterProvider
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.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
@ -11,22 +14,70 @@ class BetDetailPreviewProvider : PreviewParameterProvider<BetDetail> {
override val values = BetWithStatusPreviewProvider().values.map {
BetDetail(
bet = it,
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
answers = when (it) {
is CustomBet -> listOf(
BetAnswerDetail(
response = "Answer 1",
totalStakes = 300,
totalParticipants = 8,
highestStake = 200,
odds = 1.0f
),
BetAnswerDetail(
response = "Answer 2",
totalStakes = 300,
totalParticipants = 4,
highestStake = 200,
odds = 1.0f
),
BetAnswerDetail(
response = "Answer 3",
totalStakes = 300,
totalParticipants = 2,
highestStake = 200,
odds = 1.0f
),
BetAnswerDetail(
response = "Answer 4",
totalStakes = 300,
totalParticipants = 1,
highestStake = 200,
odds = 1.0f
)
)
),
is MatchBet -> listOf(
BetAnswerDetail(
response = "The Monarchs",
totalStakes = 300,
totalParticipants = 2,
highestStake = 200,
odds = 1.0f
),
BetAnswerDetail(
response = "Climate Change",
totalStakes = 150,
totalParticipants = 1,
highestStake = 150,
odds = 2.0f
)
)
is YesNoBet -> 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 = listOf(
Participation(
betId = it.id,

@ -0,0 +1,346 @@
package fr.iut.alldev.allin.data.api
import fr.iut.alldev.allin.data.api.interceptors.AllInAPIException
import fr.iut.alldev.allin.data.api.model.CheckUser
import fr.iut.alldev.allin.data.api.model.RequestBet
import fr.iut.alldev.allin.data.api.model.RequestParticipation
import fr.iut.alldev.allin.data.api.model.RequestUser
import fr.iut.alldev.allin.data.api.model.ResponseBet
import fr.iut.alldev.allin.data.api.model.ResponseBetAnswerDetail
import fr.iut.alldev.allin.data.api.model.ResponseBetDetail
import fr.iut.alldev.allin.data.api.model.ResponseBetResultDetail
import fr.iut.alldev.allin.data.api.model.ResponseParticipation
import fr.iut.alldev.allin.data.api.model.ResponseUser
import fr.iut.alldev.allin.data.model.bet.BetStatus
import fr.iut.alldev.allin.data.model.bet.BetType
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.vo.BetResult
import java.time.ZonedDateTime
import java.util.UUID
class MockAllInApi : AllInApi {
private fun getUserFromToken(token: String) =
mockUsers.find { it.first.token == token.removePrefix("Bearer ") }
private fun getAnswerDetails(
bet: ResponseBet,
participations: List<ResponseParticipation>
): List<ResponseBetAnswerDetail> {
return bet.response.map { response ->
val responseParticipations = participations.filter { it.answer == response }
ResponseBetAnswerDetail(
response = response,
totalStakes = responseParticipations.sumOf { it.stake },
totalParticipants = responseParticipations.size,
highestStake = responseParticipations.maxOfOrNull { it.stake } ?: 0,
odds = if (participations.isEmpty()) 0.0f else responseParticipations.size / participations.size.toFloat()
)
}
}
override suspend fun login(body: CheckUser): ResponseUser {
return mockUsers.find { it.first.username == body.login && it.second == body.password }?.first
?: throw AllInAPIException("Invalid login/password.")
}
override suspend fun login(token: String): ResponseUser {
return getUserFromToken(token)?.first
?: throw AllInAPIException("Invalid token")
}
override suspend fun register(body: RequestUser): ResponseUser {
val response = ResponseUser(
id = UUID.randomUUID().toString(),
username = body.username,
email = body.email,
nbCoins = 500,
token = "${body.username} ${mockUsers.size}"
) to body.password
mockUsers.add(response)
return response.first
}
override suspend fun createBet(token: String, body: RequestBet) {
mockBets.add(
ResponseBet(
id = UUID.randomUUID().toString(),
theme = body.theme,
sentenceBet = body.sentenceBet,
endRegistration = body.endRegistration,
endBet = body.endBet,
isPrivate = body.isPrivate,
response = body.response,
type = BetType.BINARY,
status = BetStatus.WAITING,
createdBy = ""
)
)
}
override suspend fun getAllBets(token: String): List<ResponseBet> {
getUserFromToken(token) ?: throw AllInAPIException("Invalid login/password.")
return mockBets
}
override suspend fun getToConfirm(token: String): List<ResponseBetDetail> {
val user = getUserFromToken(token) ?: throw AllInAPIException("Invalid login/password.")
return mockBets.filter {
it.createdBy == user.first.username && it.status == BetStatus.CLOSING
}.map { bet ->
val betParticipations = mockParticipations.filter { it.betId == bet.id }
val userParticipation = betParticipations.find { it.username == user.first.username }
ResponseBetDetail(
bet = bet,
answers = getAnswerDetails(bet, betParticipations),
participations = betParticipations,
userParticipation = userParticipation
)
}
}
override suspend fun confirmBet(token: String, id: String, value: String) {
getUserFromToken(token) ?: throw AllInAPIException("Invalid login/password.")
val bet = mockBets.find { it.id == id } ?: throw AllInAPIException("Unauthorized")
mockResults.add(
BetResult(
betId = id,
result = value
)
)
mockBets[mockBets.indexOf(bet)] = bet.copy(status = BetStatus.FINISHED)
}
override suspend fun getBet(token: String, id: String): ResponseBetDetail {
val bet = mockBets.find { it.id == id } ?: throw AllInAPIException("Bet not found")
val user = getUserFromToken(token) ?: throw AllInAPIException("Invalid login/password.")
val betParticipations = mockParticipations.filter { it.betId == bet.id }
val userParticipation = betParticipations.find { it.username == user.first.username }
return ResponseBetDetail(
bet = bet,
answers = getAnswerDetails(bet, betParticipations),
participations = betParticipations,
userParticipation = userParticipation
)
}
override suspend fun getBetCurrent(token: String): List<ResponseBetDetail> {
return emptyList()
}
override suspend fun getBetHistory(token: String): List<ResponseBetResultDetail> {
return emptyList()
}
override suspend fun getWon(token: String): List<ResponseBetResultDetail> {
return emptyList()
}
override suspend fun participateToBet(token: String, body: RequestParticipation) {
getUserFromToken(token)?.let {
mockParticipations.add(
ResponseParticipation(
id = "",
betId = body.betId,
username = it.first.username,
answer = body.answer,
stake = body.stake
)
)
} ?: throw AllInAPIException("Invalid token")
}
companion object {
private val mockUsers = mutableListOf(
ResponseUser(
id = "UUID 1",
username = "User 1",
email = "john@doe.fr",
nbCoins = 250,
token = "token 1"
) to "12345",
ResponseUser(
id = "UUID 2",
username = "User 2",
email = "john@doe.fr",
nbCoins = 250,
token = "token 2"
) to "12345",
ResponseUser(
id = "UUID 3",
username = "User 3",
email = "john@doe.fr",
nbCoins = 250,
token = "token 3"
) to "12345",
ResponseUser(
id = "UUID 4",
username = "User 4",
email = "john@doe.fr",
nbCoins = 250,
token = "token 4"
) to "12345",
ResponseUser(
id = "UUID 5",
username = "User 5",
email = "john@doe.fr",
nbCoins = 250,
token = "token 5"
) to "12345",
ResponseUser(
id = "UUID 6",
username = "User 6",
email = "john@doe.fr",
nbCoins = 250,
token = "token 6"
) to "12345",
ResponseUser(
id = "UUID 7",
username = "User 7",
email = "john@doe.fr",
nbCoins = 250,
token = "token 7"
) to "12345"
)
private val mockParticipations = mutableListOf(
ResponseParticipation(
id = "",
betId = "UUID1",
username = mockUsers[1].first.username,
answer = NO_VALUE,
stake = 1500
),
ResponseParticipation(
id = "",
betId = "UUID1",
username = mockUsers[2].first.username,
answer = YES_VALUE,
stake = 300
),
ResponseParticipation(
id = "",
betId = "UUID1",
username = mockUsers[3].first.username,
answer = YES_VALUE,
stake = 25
),
ResponseParticipation(
id = "",
betId = "UUID1",
username = mockUsers[4].first.username,
answer = NO_VALUE,
stake = 222
),
ResponseParticipation(
id = "",
betId = "UUID1",
username = mockUsers[5].first.username,
answer = NO_VALUE,
stake = 222
),
ResponseParticipation(
id = "",
betId = "UUID1",
username = mockUsers[6].first.username,
answer = NO_VALUE,
stake = 222
),
ResponseParticipation(
id = "",
betId = "UUID2",
username = mockUsers[0].first.username,
answer = "Answer 1",
stake = 200
),
ResponseParticipation(
id = "",
betId = "UUID2",
username = mockUsers[2].first.username,
answer = "Answer 1",
stake = 200
),
ResponseParticipation(
id = "",
betId = "UUID2",
username = mockUsers[3].first.username,
answer = "Answer 2",
stake = 200
),
ResponseParticipation(
id = "",
betId = "UUID2",
username = mockUsers[4].first.username,
answer = "Answer 3",
stake = 100
),
ResponseParticipation(
id = "",
betId = "UUID2",
username = mockUsers[5].first.username,
answer = "Answer 3",
stake = 400
),
ResponseParticipation(
id = "",
betId = "UUID2",
username = mockUsers[6].first.username,
answer = "Answer 1",
stake = 50
),
ResponseParticipation(
id = "",
betId = "UUID3",
username = mockUsers[1].first.username,
answer = "The Monarchs",
stake = 420
)
)
private val mockBets = mutableListOf(
ResponseBet(
id = "UUID1",
theme = "Études",
sentenceBet = "Dave va arriver en retard demain matin ?",
endRegistration = ZonedDateTime.now().plusDays(3),
endBet = ZonedDateTime.now().plusDays(4),
isPrivate = false,
response = listOf(YES_VALUE, NO_VALUE),
createdBy = "Armure",
type = BetType.BINARY,
status = BetStatus.IN_PROGRESS,
),
ResponseBet(
id = "UUID2",
theme = "Études",
sentenceBet = "Quoi ?",
endRegistration = ZonedDateTime.now().plusDays(3),
endBet = ZonedDateTime.now().plusDays(4),
isPrivate = false,
response = listOf("Answer 1", "Answer 2", "Answer 3", "Answer 4"),
createdBy = "User 2",
type = BetType.CUSTOM,
status = BetStatus.IN_PROGRESS,
),
ResponseBet(
id = "UUID3",
theme = "Sport",
sentenceBet = "Quelle équipe va gagner ?",
endRegistration = ZonedDateTime.now().minusDays(3),
endBet = ZonedDateTime.now().minusDays(2),
isPrivate = false,
response = listOf("The Monarchs", "Climate Change"),
createdBy = "User 1",
type = BetType.MATCH,
status = BetStatus.IN_PROGRESS,
)
)
private val mockResults by lazy { mutableListOf<BetResult>() }
}
}

@ -1,294 +0,0 @@
package fr.iut.alldev.allin.data.api
import fr.iut.alldev.allin.data.api.interceptors.AllInAPIException
import fr.iut.alldev.allin.data.api.model.CheckUser
import fr.iut.alldev.allin.data.api.model.RequestBet
import fr.iut.alldev.allin.data.api.model.RequestParticipation
import fr.iut.alldev.allin.data.api.model.RequestUser
import fr.iut.alldev.allin.data.api.model.ResponseBet
import fr.iut.alldev.allin.data.api.model.ResponseBetAnswerDetail
import fr.iut.alldev.allin.data.api.model.ResponseBetDetail
import fr.iut.alldev.allin.data.api.model.ResponseBetResultDetail
import fr.iut.alldev.allin.data.api.model.ResponseParticipation
import fr.iut.alldev.allin.data.api.model.ResponseUser
import fr.iut.alldev.allin.data.model.bet.BetStatus
import fr.iut.alldev.allin.data.model.bet.BetType
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.vo.BetResult
import java.time.ZonedDateTime
import java.util.UUID
class MockAllInApi : AllInApi {
private fun getUserFromToken(token: String) =
mockUsers.find { it.first.token == token.removePrefix("Bearer ") }
private fun getAnswerDetails(
bet: ResponseBet,
participations: List<ResponseParticipation>
): List<ResponseBetAnswerDetail> {
return bet.response.map { response ->
val responseParticipations = participations.filter { it.answer == response }
ResponseBetAnswerDetail(
response = response,
totalStakes = responseParticipations.sumOf { it.stake },
totalParticipants = responseParticipations.size,
highestStake = responseParticipations.maxOfOrNull { it.stake } ?: 0,
odds = if (participations.isEmpty()) 0.0f else responseParticipations.size / participations.size.toFloat()
)
}
}
override suspend fun login(body: CheckUser): ResponseUser {
return mockUsers.find { it.first.username == body.login && it.second == body.password }?.first
?: throw AllInAPIException("Invalid login/password.")
}
override suspend fun login(token: String): ResponseUser {
return getUserFromToken(token)?.first
?: throw AllInAPIException("Invalid token")
}
override suspend fun register(body: RequestUser): ResponseUser {
val response = ResponseUser(
id = UUID.randomUUID().toString(),
username = body.username,
email = body.email,
nbCoins = 500,
token = "${body.username} ${mockUsers.size}"
) to body.password
mockUsers.add(response)
return response.first
}
override suspend fun createBet(token: String, body: RequestBet) {
mockBets.add(
ResponseBet(
id = UUID.randomUUID().toString(),
theme = body.theme,
sentenceBet = body.sentenceBet,
endRegistration = body.endRegistration,
endBet = body.endBet,
isPrivate = body.isPrivate,
response = body.response,
type = BetType.BINARY,
status = BetStatus.WAITING,
createdBy = ""
)
)
}
override suspend fun getAllBets(token: String): List<ResponseBet> {
getUserFromToken(token) ?: throw AllInAPIException("Invalid login/password.")
return mockBets
}
override suspend fun getToConfirm(token: String): List<ResponseBetDetail> {
val user = getUserFromToken(token) ?: throw AllInAPIException("Invalid login/password.")
return mockBets.filter {
it.createdBy == user.first.username && it.status == BetStatus.CLOSING
}.map { bet ->
val betParticipations = mockParticipations.filter { it.betId == bet.id }
val userParticipation = betParticipations.find { it.username == user.first.username }
ResponseBetDetail(
bet = bet,
answers = getAnswerDetails(bet, betParticipations),
participations = betParticipations,
userParticipation = userParticipation
)
}
}
override suspend fun confirmBet(token: String, id: String, value: String) {
getUserFromToken(token) ?: throw AllInAPIException("Invalid login/password.")
val bet = mockBets.find { it.id == id } ?: throw AllInAPIException("Unauthorized")
mockResults.add(
BetResult(
betId = id,
result = value
)
)
mockBets[mockBets.indexOf(bet)] = bet.copy(status = BetStatus.FINISHED)
}
override suspend fun getBet(token: String, id: String): ResponseBetDetail {
val bet = mockBets.find { it.id == id } ?: throw AllInAPIException("Bet not found")
val user = getUserFromToken(token) ?: throw AllInAPIException("Invalid login/password.")
val betParticipations = mockParticipations.filter { it.betId == bet.id }
val userParticipation = betParticipations.find { it.username == user.first.username }
return ResponseBetDetail(
bet = bet,
answers = getAnswerDetails(bet, betParticipations),
participations = betParticipations,
userParticipation = userParticipation
)
}
override suspend fun getBetCurrent(token: String): List<ResponseBetDetail> {
return emptyList()
}
override suspend fun getBetHistory(token: String): List<ResponseBetResultDetail> {
return emptyList()
}
override suspend fun getWon(token: String): List<ResponseBetResultDetail> {
return emptyList()
}
override suspend fun participateToBet(token: String, body: RequestParticipation) {
getUserFromToken(token)?.let {
mockParticipations.add(
ResponseParticipation(
id = "",
betId = body.betId,
username = it.first.username,
answer = body.answer,
stake = body.stake
)
)
} ?: throw AllInAPIException("Invalid token")
}
}
private val mockUsers = mutableListOf(
ResponseUser(
id = "UUID 1",
username = "User 1",
email = "john@doe.fr",
nbCoins = 250,
token = "token 1"
) to "12345",
ResponseUser(
id = "UUID 2",
username = "User 2",
email = "john@doe.fr",
nbCoins = 250,
token = "token 2"
) to "12345",
ResponseUser(
id = "UUID 3",
username = "User 3",
email = "john@doe.fr",
nbCoins = 250,
token = "token 3"
) to "12345",
ResponseUser(
id = "UUID 4",
username = "User 4",
email = "john@doe.fr",
nbCoins = 250,
token = "token 4"
) to "12345",
ResponseUser(
id = "UUID 5",
username = "User 5",
email = "john@doe.fr",
nbCoins = 250,
token = "token 5"
) to "12345",
ResponseUser(
id = "UUID 6",
username = "User 6",
email = "john@doe.fr",
nbCoins = 250,
token = "token 6"
) to "12345",
ResponseUser(
id = "UUID 7",
username = "User 7",
email = "john@doe.fr",
nbCoins = 250,
token = "token 7"
) to "12345"
)
private val mockParticipations = mutableListOf(
ResponseParticipation(
id = "",
betId = "UUID1",
username = mockUsers[1].first.username,
answer = NO_VALUE,
stake = 1500
),
ResponseParticipation(
id = "",
betId = "UUID1",
username = mockUsers[2].first.username,
answer = YES_VALUE,
stake = 300
),
ResponseParticipation(
id = "",
betId = "UUID1",
username = mockUsers[3].first.username,
answer = YES_VALUE,
stake = 25
),
ResponseParticipation(
id = "",
betId = "UUID1",
username = mockUsers[4].first.username,
answer = NO_VALUE,
stake = 222
),
ResponseParticipation(
id = "",
betId = "UUID1",
username = mockUsers[5].first.username,
answer = NO_VALUE,
stake = 222
),
ResponseParticipation(
id = "",
betId = "UUID1",
username = mockUsers[6].first.username,
answer = NO_VALUE,
stake = 222
)
)
private val mockBets = mutableListOf(
ResponseBet(
id = "UUID1",
theme = "Études",
sentenceBet = "Dave va arriver en retard demain matin ?",
endRegistration = ZonedDateTime.now().plusDays(3),
endBet = ZonedDateTime.now().plusDays(4),
isPrivate = false,
response = listOf(YES_VALUE, NO_VALUE),
createdBy = "Armure",
type = BetType.BINARY,
status = BetStatus.IN_PROGRESS,
),
ResponseBet(
id = "UUID2",
theme = "Études",
sentenceBet = "Dave va arriver en retard demain matin ?",
endRegistration = ZonedDateTime.now().plusDays(3),
endBet = ZonedDateTime.now().plusDays(4),
isPrivate = false,
response = listOf("Answer 1", "Answer 2", "Answer 3", "Answer 4"),
createdBy = "User 2",
type = BetType.BINARY,
status = BetStatus.IN_PROGRESS,
),
ResponseBet(
id = "UUID3",
theme = "Sport",
sentenceBet = "Nouveau record du monde ?",
endRegistration = ZonedDateTime.now().minusDays(3),
endBet = ZonedDateTime.now().minusDays(2),
isPrivate = false,
response = listOf(YES_VALUE, NO_VALUE),
createdBy = "User 1",
type = BetType.BINARY,
status = BetStatus.CLOSING,
)
)
private val mockResults by lazy { mutableListOf<BetResult>() }

@ -7,6 +7,7 @@ import fr.iut.alldev.allin.data.model.bet.BetResultDetail
import fr.iut.alldev.allin.data.model.bet.BetStatus
import fr.iut.alldev.allin.data.model.bet.BetType
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
@ -30,9 +31,9 @@ data class ResponseBet(
var response: List<String>,
val createdBy: String
) {
fun toBet(): Bet {
if (response.toSet() == setOf(YES_VALUE, NO_VALUE)) {
return YesNoBet(
fun toBet(): Bet = when {
response.toSet() == setOf(YES_VALUE, NO_VALUE) -> {
YesNoBet(
id = id ?: "",
theme = theme,
phrase = sentenceBet,
@ -42,8 +43,25 @@ data class ResponseBet(
betStatus = status,
creator = createdBy
)
} else {
return CustomBet(
}
type == BetType.MATCH -> {
MatchBet(
id = id ?: "",
theme = theme,
phrase = sentenceBet,
endRegisterDate = endRegistration,
endBetDate = endBet,
isPublic = !isPrivate,
betStatus = status,
creator = createdBy,
nameTeam1 = response.firstOrNull() ?: "",
nameTeam2 = response.lastOrNull() ?: ""
)
}
else -> {
CustomBet(
id = id ?: "",
theme = theme,
phrase = sentenceBet,
@ -56,6 +74,7 @@ data class ResponseBet(
)
}
}
}
@Keep

@ -11,4 +11,13 @@ data class BetDetail(
) {
fun getAnswerOfResponse(response: String) =
answers.find { it.response == response }
fun getPercentageOfAnswer(answerDetail: BetAnswerDetail): Float =
(answerDetail.totalParticipants.toFloat() / answers.sumOf { it.totalParticipants }).let {
if (it.isNaN() || it.isInfinite()) {
1f / this.answers.size
} else {
it
}
}
}
Loading…
Cancel
Save