BetDetail and Participation
continuous-integration/drone/push Build is passing Details

pull/3/head
Arthur VALIN 1 year ago
parent 2432599726
commit 7c643ebe74

@ -16,7 +16,5 @@ annotation class AllInCurrentUser
internal object CurrentUserModule { internal object CurrentUserModule {
@AllInCurrentUser @AllInCurrentUser
@Provides @Provides
fun provideUser( fun provideUser(userRepository: UserRepository) = userRepository.currentUser
userRepository: UserRepository
) = userRepository.currentUser
} }

@ -9,7 +9,7 @@ import androidx.compose.ui.graphics.painter.Painter
@Immutable @Immutable
data class AllInIcons( data class AllInIcons(
val allCoins: @Composable ()->Painter, val allCoins: @Composable () -> Painter,
) )
internal val LocalIcons = staticCompositionLocalOf { internal val LocalIcons = staticCompositionLocalOf {

@ -110,6 +110,7 @@ class BetCreationViewModel @Inject constructor(
if (!hasError.value) { if (!hasError.value) {
try { try {
val bet = BetFactory.createBet( val bet = BetFactory.createBet(
id = "",
betType = selectedBetType.value, betType = selectedBetType.value,
theme = theme.value, theme = theme.value,
phrase = phrase.value, phrase = phrase.value,

@ -1,10 +1,10 @@
package fr.iut.alldev.allin.ui.betCreation.tabs package fr.iut.alldev.allin.ui.betCreation.tabs
import androidx.compose.foundation.ExperimentalFoundationApi
import androidx.compose.foundation.interaction.MutableInteractionSource import androidx.compose.foundation.interaction.MutableInteractionSource
import androidx.compose.foundation.layout.* import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.relocation.BringIntoViewRequester import androidx.compose.foundation.layout.Spacer
import androidx.compose.runtime.* import androidx.compose.foundation.layout.height
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier
import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.dp
import fr.iut.alldev.allin.data.ext.formatToMediumDate import fr.iut.alldev.allin.data.ext.formatToMediumDate
@ -14,7 +14,6 @@ import fr.iut.alldev.allin.ui.betCreation.tabs.sections.QuestionTabPrivacySectio
import fr.iut.alldev.allin.ui.betCreation.tabs.sections.QuestionTabThemePhraseSection import fr.iut.alldev.allin.ui.betCreation.tabs.sections.QuestionTabThemePhraseSection
import java.time.ZonedDateTime import java.time.ZonedDateTime
@OptIn(ExperimentalFoundationApi::class)
@Composable @Composable
fun BetCreationScreenQuestionTab( fun BetCreationScreenQuestionTab(
modifier: Modifier = Modifier, modifier: Modifier = Modifier,
@ -38,7 +37,6 @@ fun BetCreationScreenQuestionTab(
setEndTimeDialog: (Boolean) -> Unit, setEndTimeDialog: (Boolean) -> Unit,
interactionSource: MutableInteractionSource interactionSource: MutableInteractionSource
) { ) {
val bringIntoViewRequester = remember { BringIntoViewRequester() }
Column(modifier) { Column(modifier) {
QuestionTabThemePhraseSection( QuestionTabThemePhraseSection(
betTheme = betTheme, betTheme = betTheme,

@ -154,6 +154,7 @@ private fun BetResultBottomSheetContentPreview() {
username = "Pseudo", username = "Pseudo",
coinAmount = 3976, coinAmount = 3976,
bet = YesNoBet( bet = YesNoBet(
id = "1",
theme = "Theme", theme = "Theme",
phrase = "Phrase", phrase = "Phrase",
endRegisterDate = ZonedDateTime.now(), endRegisterDate = ZonedDateTime.now(),

@ -6,10 +6,11 @@ import androidx.compose.material3.*
import androidx.compose.runtime.* import androidx.compose.runtime.*
import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color import androidx.compose.ui.graphics.Color
import fr.iut.alldev.allin.data.model.bet.Bet
import fr.iut.alldev.allin.data.model.bet.BetStatus import fr.iut.alldev.allin.data.model.bet.BetStatus
import fr.iut.alldev.allin.data.model.bet.vo.BetDetail
import fr.iut.alldev.allin.ui.betStatus.components.BetStatusBottomSheetBack import fr.iut.alldev.allin.ui.betStatus.components.BetStatusBottomSheetBack
import fr.iut.alldev.allin.ui.betStatus.components.BetStatusParticipationBottomSheet import fr.iut.alldev.allin.ui.betStatus.components.BetStatusParticipationBottomSheet
import fr.iut.alldev.allin.ui.betStatus.components.getAnswerFromParticipationIdx
import fr.iut.alldev.allin.ui.betStatus.components.getParticipationAnswers import fr.iut.alldev.allin.ui.betStatus.components.getParticipationAnswers
import fr.iut.alldev.allin.ui.core.AllInBottomSheet import fr.iut.alldev.allin.ui.core.AllInBottomSheet
@ -23,14 +24,14 @@ fun BetStatusBottomSheet(
state: SheetState, state: SheetState,
sheetVisibility: Boolean, sheetVisibility: Boolean,
sheetBackVisibility: Boolean, sheetBackVisibility: Boolean,
bet: Bet?, betDetail: BetDetail?,
paddingValues: PaddingValues, paddingValues: PaddingValues,
userCoinAmount: MutableIntState, userCoinAmount: MutableIntState,
onParticipate: (Int) -> Unit, onParticipate: (stake: Int, response: String) -> Unit,
onDismiss: () -> Unit, onDismiss: () -> Unit,
participateSheetVisibility: Boolean, participateSheetVisibility: Boolean,
setParticipateSheetVisibility: (Boolean) -> Unit, setParticipateSheetVisibility: (Boolean) -> Unit,
displayBet: @Composable (Bet) -> Unit displayBet: @Composable (BetDetail) -> Unit
) { ) {
AnimatedVisibility( AnimatedVisibility(
visible = sheetBackVisibility, visible = sheetBackVisibility,
@ -41,9 +42,9 @@ fun BetStatusBottomSheet(
targetOffsetY = { it } targetOffsetY = { it }
) )
) { ) {
bet?.let { betDetail?.let {
BetStatusBottomSheetBack( BetStatusBottomSheetBack(
status = it.betStatus status = it.bet.betStatus
) )
} }
} }
@ -61,14 +62,15 @@ fun BetStatusBottomSheet(
Column( Column(
Modifier.fillMaxHeight(SHEET_HEIGHT) Modifier.fillMaxHeight(SHEET_HEIGHT)
) { ) {
bet?.let { betDetail?.let {
val elements = bet.getParticipationAnswers() val elements = betDetail.getParticipationAnswers()
displayBet(it) displayBet(it)
BetStatusParticipationBottomSheet( BetStatusParticipationBottomSheet(
sheetVisibility = participateSheetVisibility && bet.betStatus == BetStatus.Waiting && state.hasExpandedState, sheetVisibility = participateSheetVisibility && betDetail.bet.betStatus == BetStatus.Waiting && state.hasExpandedState,
safeBottomPadding = paddingValues.calculateBottomPadding(), safeBottomPadding = paddingValues.calculateBottomPadding(),
betPhrase = bet.phrase, odds = betDetail.answers.getOrNull(selectedAnswer)?.odds ?: 1f,
betPhrase = betDetail.bet.phrase,
coinAmount = userCoinAmount.intValue, coinAmount = userCoinAmount.intValue,
onDismiss = { setParticipateSheetVisibility(false) }, onDismiss = { setParticipateSheetVisibility(false) },
state = rememberModalBottomSheetState(skipPartiallyExpanded = true), state = rememberModalBottomSheetState(skipPartiallyExpanded = true),
@ -80,8 +82,11 @@ fun BetStatusBottomSheet(
enabled = stake != null && enabled = stake != null &&
(stake ?: 0) <= userCoinAmount.intValue (stake ?: 0) <= userCoinAmount.intValue
) { ) {
stake?.let { stake?.let { stake ->
onParticipate(it) onParticipate(
stake,
betDetail.bet.getAnswerFromParticipationIdx(selectedAnswer)
)
} }
} }

@ -1,82 +1,138 @@
package fr.iut.alldev.allin.ui.betStatus.components package fr.iut.alldev.allin.ui.betStatus.components
import android.content.res.Configuration import android.content.res.Configuration
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Row import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.RowScope import androidx.compose.foundation.layout.RowScope
import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.padding
import androidx.compose.material3.Text import androidx.compose.material3.Text
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
import androidx.compose.runtime.remember
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier
import androidx.compose.ui.res.stringResource import androidx.compose.ui.graphics.Color
import androidx.compose.ui.platform.LocalConfiguration
import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.tooling.preview.PreviewParameter import androidx.compose.ui.tooling.preview.PreviewParameter
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp import androidx.compose.ui.unit.sp
import fr.iut.alldev.allin.R import androidx.core.os.ConfigurationCompat
import fr.iut.alldev.allin.data.model.bet.Bet import fr.iut.alldev.allin.data.model.bet.Bet
import fr.iut.alldev.allin.data.model.bet.CustomBet 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.MatchBet
import fr.iut.alldev.allin.data.model.bet.NO_VALUE 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.YES_VALUE
import fr.iut.alldev.allin.data.model.bet.YesNoBet import fr.iut.alldev.allin.data.model.bet.YesNoBet
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.theme.AllInTheme
import fr.iut.alldev.allin.ui.preview.BetPreviewProvider import fr.iut.alldev.allin.ui.core.AllInCard
import fr.iut.alldev.allin.ui.preview.BetDetailPreviewProvider
import java.util.Locale
private val participationAnswerFontSize = 25.sp private val participationAnswerFontSize = 25.sp
@Composable @Composable
fun Bet.getParticipationAnswers(): List<@Composable RowScope.() -> Unit> = fun BetDetail.getParticipationAnswers(): List<@Composable RowScope.() -> Unit> {
when (this) { val configuration = LocalConfiguration.current
is CustomBet -> this.possibleAnswers.map { val locale = remember { ConfigurationCompat.getLocales(configuration).get(0) ?: Locale.getDefault() }
return when (this.bet) {
is CustomBet -> (this.bet as CustomBet).possibleAnswers.map {
{ {
Text( this@getParticipationAnswers.getAnswerOfResponse(it)?.let {
text = it, ParticipationAnswerLine(
color = AllInTheme.colors.allInBlue, text = it.response,
style = AllInTheme.typography.h1, odds = it.odds,
fontSize = participationAnswerFontSize locale = locale
) )
} }
} }
}
is MatchBet -> listOf( is MatchBet -> buildList {
{ val bet = (this@getParticipationAnswers.bet as MatchBet)
Text( add {
text = this@getParticipationAnswers.nameTeam1, this@getParticipationAnswers.getAnswerOfResponse(bet.nameTeam1)?.let {
color = AllInTheme.colors.allInBlue, ParticipationAnswerLine(
style = AllInTheme.typography.h1, text = it.response,
fontSize = participationAnswerFontSize odds = it.odds,
locale = locale
) )
}, }
{ }
Text( add {
text = this@getParticipationAnswers.nameTeam2, this@getParticipationAnswers.getAnswerOfResponse(bet.nameTeam2)?.let {
ParticipationAnswerLine(
text = it.response,
color = AllInTheme.colors.allInBarPink, color = AllInTheme.colors.allInBarPink,
style = AllInTheme.typography.h1, odds = it.odds,
fontSize = participationAnswerFontSize locale = locale
)
}
}
}
is YesNoBet -> buildList {
add {
this@getParticipationAnswers.getAnswerOfResponse(YES_VALUE)?.let {
ParticipationAnswerLine(
text = it.response,
odds = it.odds,
locale = locale
) )
} }
}
add {
this@getParticipationAnswers.getAnswerOfResponse(NO_VALUE)?.let {
ParticipationAnswerLine(
text = it.response,
color = AllInTheme.colors.allInBarPink,
odds = it.odds,
locale = locale
) )
}
}
}
}
}
is YesNoBet -> listOf( @Composable
{ private fun ParticipationAnswerLine(
text: String,
color: Color = AllInTheme.colors.allInBlue,
locale: Locale,
odds: Float
) {
Row(
modifier = Modifier.fillMaxWidth(),
verticalAlignment = Alignment.CenterVertically,
horizontalArrangement = Arrangement.spacedBy(8.dp)
) {
Text( Text(
text = stringResource(id = R.string.Yes).uppercase(), text = text.uppercase(),
color = AllInTheme.colors.allInBlue, color = color,
style = AllInTheme.typography.h1, style = AllInTheme.typography.h1,
fontSize = participationAnswerFontSize fontSize = participationAnswerFontSize
) )
},
{ AllInCard(
radius = 50.dp,
backgroundColor = AllInTheme.colors.allInPurple
) {
Box(Modifier.padding(vertical = 4.dp, horizontal = 8.dp)) {
Text( Text(
text = stringResource(id = R.string.No).uppercase(), text = "x${odds.formatToSimple(locale)}",
color = AllInTheme.colors.allInBarPink, color = AllInTheme.colors.white,
style = AllInTheme.typography.h1, style = AllInTheme.typography.h2
fontSize = participationAnswerFontSize
) )
} }
)
} }
}
}
fun Bet.getAnswerFromParticipationIdx(idx: Int) = fun Bet.getAnswerFromParticipationIdx(idx: Int) =
when (this) { when (this) {
@ -98,7 +154,7 @@ fun Bet.getAnswerFromParticipationIdx(idx: Int) =
@Preview(uiMode = Configuration.UI_MODE_NIGHT_YES) @Preview(uiMode = Configuration.UI_MODE_NIGHT_YES)
@Composable @Composable
private fun ParticipationAnswersPreview( private fun ParticipationAnswersPreview(
@PreviewParameter(BetPreviewProvider::class) bet: Bet, @PreviewParameter(BetDetailPreviewProvider::class) bet: BetDetail,
) { ) {
AllInTheme { AllInTheme {
Column { Column {

@ -24,6 +24,7 @@ import fr.iut.alldev.allin.ui.core.AllInIntTextField
import fr.iut.alldev.allin.ui.core.AllInSelectionBox import fr.iut.alldev.allin.ui.core.AllInSelectionBox
import fr.iut.alldev.allin.ui.core.topbar.AllInTopBarCoinCounter import fr.iut.alldev.allin.ui.core.topbar.AllInTopBarCoinCounter
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
import kotlin.math.roundToInt
@OptIn(ExperimentalMaterial3Api::class) @OptIn(ExperimentalMaterial3Api::class)
@Composable @Composable
@ -35,6 +36,7 @@ fun BetStatusParticipationBottomSheet(
onDismiss: () -> Unit, onDismiss: () -> Unit,
state: SheetState, state: SheetState,
enabled: Boolean, enabled: Boolean,
odds: Float,
stake: Int?, stake: Int?,
setStake: (Int?) -> Unit, setStake: (Int?) -> Unit,
elements: List<@Composable RowScope.() -> Unit>, elements: List<@Composable RowScope.() -> Unit>,
@ -58,6 +60,7 @@ fun BetStatusParticipationBottomSheet(
setElement = setElement, setElement = setElement,
enabled = enabled, enabled = enabled,
stake = stake, stake = stake,
odds = odds,
setStake = setStake setStake = setStake
) { ) {
scope.launch { scope.launch {
@ -76,6 +79,7 @@ private fun ColumnScope.BetStatusParticipationBottomSheetContent(
coinAmount: Int, coinAmount: Int,
enabled: Boolean, enabled: Boolean,
stake: Int?, stake: Int?,
odds: Float,
setStake: (Int?) -> Unit, setStake: (Int?) -> Unit,
selectedElement: (@Composable RowScope.() -> Unit)?, selectedElement: (@Composable RowScope.() -> Unit)?,
elements: List<@Composable RowScope.() -> Unit>, elements: List<@Composable RowScope.() -> Unit>,
@ -150,7 +154,7 @@ private fun ColumnScope.BetStatusParticipationBottomSheetContent(
color = AllInTheme.themeColors.onBackground color = AllInTheme.themeColors.onBackground
) )
AllInCoinCount( AllInCoinCount(
amount = 121, amount = stake?.let { (it + (it * odds)).roundToInt() } ?: 0,
color = AllInTheme.themeColors.onBackground color = AllInTheme.themeColors.onBackground
) )
} }
@ -181,6 +185,7 @@ private fun BetStatusParticipationBottomSheetContentPreview() {
selectedElement = null, selectedElement = null,
enabled = true, enabled = true,
stake = 123, stake = 123,
odds = 0.42f,
setStake = {} setStake = {}
) )
} }

@ -4,27 +4,27 @@ import androidx.compose.foundation.layout.*
import androidx.compose.material3.Text import androidx.compose.material3.Text
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.text.font.FontStyle import androidx.compose.ui.text.font.FontStyle
import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.tooling.preview.PreviewParameter import androidx.compose.ui.tooling.preview.PreviewParameter
import androidx.compose.ui.tooling.preview.PreviewParameterProvider import androidx.compose.ui.tooling.preview.PreviewParameterProvider
import androidx.compose.ui.unit.sp 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.data.ext.toPercentageString
import fr.iut.alldev.allin.theme.AllInTheme import fr.iut.alldev.allin.theme.AllInTheme
import fr.iut.alldev.allin.ui.core.PercentagePositionnedElement import fr.iut.alldev.allin.ui.core.PercentagePositionnedElement
import fr.iut.alldev.allin.ui.core.StatBar import fr.iut.alldev.allin.ui.core.StatBar
@Composable @Composable
fun YesNoStatBar( fun BinaryStatBar(
yesPercentage: Float, response1Percentage: Float,
response1: String,
response2: String,
modifier: Modifier = Modifier, modifier: Modifier = Modifier,
) { ) {
Column(modifier) { Column(modifier) {
Row { Row {
Text( Text(
text = stringResource(id = R.string.Yes).uppercase(), text = response1,
color = AllInTheme.colors.allInBlue, color = AllInTheme.colors.allInBlue,
style = AllInTheme.typography.h1, style = AllInTheme.typography.h1,
fontStyle = FontStyle.Italic, fontStyle = FontStyle.Italic,
@ -32,19 +32,19 @@ fun YesNoStatBar(
modifier = Modifier.weight(1f) modifier = Modifier.weight(1f)
) )
Text( Text(
text = stringResource(id = R.string.No).uppercase(), text = response2,
style = AllInTheme.typography.h1, style = AllInTheme.typography.h1,
fontStyle = FontStyle.Italic, fontStyle = FontStyle.Italic,
fontSize = 30.sp, fontSize = 30.sp,
color = AllInTheme.colors.allInBarPink color = AllInTheme.colors.allInBarPink
) )
} }
StatBar(percentage = yesPercentage) StatBar(percentage = response1Percentage)
PercentagePositionnedElement( PercentagePositionnedElement(
percentage = yesPercentage percentage = response1Percentage
) { ) {
Text( Text(
text = yesPercentage.toPercentageString(), text = response1Percentage.toPercentageString(),
style = AllInTheme.typography.sm1, style = AllInTheme.typography.sm1,
color = AllInTheme.colors.allInBarPurple color = AllInTheme.colors.allInBarPurple
) )
@ -64,6 +64,10 @@ private fun YesNoStatBarPreview(
@PreviewParameter(YesNoStatBarPreviewProvider::class) percentage: Float, @PreviewParameter(YesNoStatBarPreviewProvider::class) percentage: Float,
) { ) {
AllInTheme { AllInTheme {
YesNoStatBar(percentage) BinaryStatBar(
percentage,
"Answer 1",
"Answer 2"
)
} }
} }

@ -3,6 +3,8 @@ package fr.iut.alldev.allin.ui.betStatus.vo
import android.content.res.Configuration import android.content.res.Configuration
import androidx.compose.foundation.background import androidx.compose.foundation.background
import androidx.compose.foundation.layout.* import androidx.compose.foundation.layout.*
import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.lazy.items
import androidx.compose.material.icons.Icons import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.EmojiEvents import androidx.compose.material.icons.filled.EmojiEvents
import androidx.compose.material.icons.filled.People import androidx.compose.material.icons.filled.People
@ -13,48 +15,67 @@ import androidx.compose.material3.Text
import androidx.compose.runtime.* import androidx.compose.runtime.*
import androidx.compose.ui.Alignment import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier
import androidx.compose.ui.platform.LocalConfiguration
import androidx.compose.ui.res.stringResource import androidx.compose.ui.res.stringResource
import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.text.style.TextOverflow
import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.tooling.preview.PreviewParameter import androidx.compose.ui.tooling.preview.PreviewParameter
import androidx.compose.ui.unit.dp 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.R
import fr.iut.alldev.allin.data.ext.formatToMediumDateNoYear import fr.iut.alldev.allin.data.ext.formatToMediumDateNoYear
import fr.iut.alldev.allin.data.ext.formatToTime import fr.iut.alldev.allin.data.ext.formatToTime
import fr.iut.alldev.allin.data.model.bet.Bet
import fr.iut.alldev.allin.data.model.bet.BetStatus 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.MatchBet
import fr.iut.alldev.allin.data.model.bet.YesNoBet 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.BetDetail
import fr.iut.alldev.allin.ext.formatToSimple
import fr.iut.alldev.allin.ext.getDateEndLabelId import fr.iut.alldev.allin.ext.getDateEndLabelId
import fr.iut.alldev.allin.ext.getDateStartLabelId import fr.iut.alldev.allin.ext.getDateStartLabelId
import fr.iut.alldev.allin.theme.AllInTheme import fr.iut.alldev.allin.theme.AllInTheme
import fr.iut.alldev.allin.ui.betStatus.components.BetStatusWinner import fr.iut.alldev.allin.ui.betStatus.components.BetStatusWinner
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.YesNoDetailsLine
import fr.iut.alldev.allin.ui.betStatus.components.YesNoStatBar import fr.iut.alldev.allin.ui.core.AllInCoinCount
import fr.iut.alldev.allin.ui.core.AllInDetailsDrawer import fr.iut.alldev.allin.ui.core.AllInDetailsDrawer
import fr.iut.alldev.allin.ui.core.ProfilePicture
import fr.iut.alldev.allin.ui.core.RainbowButton import fr.iut.alldev.allin.ui.core.RainbowButton
import fr.iut.alldev.allin.ui.core.bet.BetDateTimeRow import fr.iut.alldev.allin.ui.core.bet.BetDateTimeRow
import fr.iut.alldev.allin.ui.core.bet.BetTitleHeader import fr.iut.alldev.allin.ui.core.bet.BetTitleHeader
import fr.iut.alldev.allin.ui.preview.BetWithStatusPreviewProvider import fr.iut.alldev.allin.ui.preview.BetDetailPreviewProvider
import fr.iut.alldev.allin.vo.bet.BetDisplayer import fr.iut.alldev.allin.vo.bet.BetDisplayer
import java.util.Locale
class BetStatusBottomSheetBetDisplayer( class BetStatusBottomSheetBetDisplayer(
val openParticipateSheet: () -> Unit val openParticipateSheet: () -> Unit
) : BetDisplayer { ) : BetDisplayer {
val paddingValues = mutableStateOf(PaddingValues()) val paddingValues = mutableStateOf(PaddingValues())
@OptIn(ExperimentalMaterial3Api::class)
@Composable @Composable
override fun DisplayYesNoBet(bet: YesNoBet) { private fun DisplayBinaryBet(
betDetail: BetDetail,
response1: String,
response2: String,
response1Display: String = response1,
response2Display: String = response2
) {
val safeBottomPadding = paddingValues.value.calculateBottomPadding() val safeBottomPadding = paddingValues.value.calculateBottomPadding()
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.padding(bottom = safeBottomPadding)) { Box(Modifier.padding(bottom = safeBottomPadding)) {
Column { Column {
Column(Modifier.padding(horizontal = 20.dp)) { Column(Modifier.padding(horizontal = 20.dp)) {
BetTitleHeader( BetTitleHeader(
title = bet.phrase, title = betDetail.bet.phrase,
category = bet.theme, category = betDetail.bet.theme,
creator = bet.creator, creator = betDetail.bet.creator,
modifier = Modifier.fillMaxWidth() modifier = Modifier.fillMaxWidth()
) )
Spacer(modifier = Modifier.height(20.dp)) Spacer(modifier = Modifier.height(20.dp))
@ -62,24 +83,24 @@ class BetStatusBottomSheetBetDisplayer(
horizontalAlignment = Alignment.End horizontalAlignment = Alignment.End
) { ) {
BetDateTimeRow( BetDateTimeRow(
label = stringResource(id = bet.betStatus.getDateStartLabelId()), label = stringResource(id = betDetail.bet.betStatus.getDateStartLabelId()),
date = bet.endRegisterDate.formatToMediumDateNoYear(), date = betDetail.bet.endRegisterDate.formatToMediumDateNoYear(),
time = bet.endRegisterDate.formatToTime(), time = betDetail.bet.endRegisterDate.formatToTime(),
modifier = Modifier.width(IntrinsicSize.Max) modifier = Modifier.width(IntrinsicSize.Max)
) )
Spacer(modifier = Modifier.height(15.dp)) Spacer(modifier = Modifier.height(15.dp))
BetDateTimeRow( BetDateTimeRow(
label = stringResource(id = bet.betStatus.getDateEndLabelId()), label = stringResource(id = betDetail.bet.betStatus.getDateEndLabelId()),
date = bet.endBetDate.formatToMediumDateNoYear(), date = betDetail.bet.endBetDate.formatToMediumDateNoYear(),
time = bet.endBetDate.formatToTime(), time = betDetail.bet.endBetDate.formatToTime(),
modifier = Modifier.width(IntrinsicSize.Max) modifier = Modifier.width(IntrinsicSize.Max)
) )
} }
Spacer(modifier = Modifier.height(20.dp)) Spacer(modifier = Modifier.height(20.dp))
} }
if (bet.betStatus is BetStatus.Finished) { if (betDetail.bet.betStatus is BetStatus.Finished) {
BetStatusWinner( BetStatusWinner(
answer = stringResource(id = R.string.Yes), answer = response1Display,
color = AllInTheme.colors.allInBlue, color = AllInTheme.colors.allInBlue,
coinAmount = 442, coinAmount = 442,
username = "Imri", username = "Imri",
@ -95,63 +116,150 @@ class BetStatusBottomSheetBetDisplayer(
.padding(horizontal = 20.dp) .padding(horizontal = 20.dp)
) { ) {
Spacer(modifier = Modifier.height(20.dp)) Spacer(modifier = Modifier.height(20.dp))
YesNoStatBar(yesPercentage = .86f) 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 { AllInDetailsDrawer {
YesNoDetailsLine( YesNoDetailsLine(
icon = AllInTheme.icons.allCoins(), icon = AllInTheme.icons.allCoins(),
yesText = "550", yesText = response1Answer?.totalStakes?.toString() ?: "0",
noText = "330", noText = response2Answer?.totalStakes?.toString() ?: "0"
) )
YesNoDetailsLine( YesNoDetailsLine(
icon = Icons.Filled.People, icon = Icons.Filled.People,
yesText = "12", yesText = response1Answer?.totalParticipants?.toString() ?: "0",
noText = "5" noText = response2Answer?.totalParticipants?.toString() ?: "0"
) )
YesNoDetailsLine( YesNoDetailsLine(
icon = Icons.Filled.WorkspacePremium, icon = Icons.Filled.WorkspacePremium,
yesText = "45", yesText = response1Answer?.highestStake?.toString() ?: "0",
noText = "45" noText = response2Answer?.highestStake?.toString() ?: "0"
) )
YesNoDetailsLine( YesNoDetailsLine(
icon = Icons.Filled.EmojiEvents, icon = Icons.Filled.EmojiEvents,
yesText = "x1.2", yesText = "x${response1Answer?.odds?.formatToSimple(locale) ?: "1.00"}",
noText = "x1.45" noText = "x${response2Answer?.odds?.formatToSimple(locale) ?: "1.00"}"
) )
} }
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)
)
LazyColumn(
verticalArrangement = Arrangement.spacedBy(8.dp),
horizontalAlignment = Alignment.CenterHorizontally,
contentPadding = PaddingValues(horizontal = 13.dp, vertical = 8.dp),
modifier = Modifier.fillMaxHeight()
) {
betDetail.userParticipation?.let {
item {
BetStatusParticipant(
username = it.username,
allCoinsAmount = it.stake
)
HorizontalDivider(
color = AllInTheme.themeColors.border,
modifier = Modifier.padding(vertical = 8.dp, horizontal = 25.dp)
)
}
}
items(betDetail.participations) {
if (it.username != betDetail.userParticipation?.username) {
BetStatusParticipant(
username = it.username,
allCoinsAmount = it.stake
)
}
}
}
} }
} }
if (bet.betStatus !is BetStatus.Finished) { if (betDetail.bet.betStatus !is BetStatus.Finished && betDetail.userParticipation == null) {
RainbowButton( RainbowButton(
modifier = Modifier modifier = Modifier
.align(Alignment.BottomCenter) .align(Alignment.BottomCenter)
.padding(horizontal = 7.dp), .padding(horizontal = 7.dp),
text = stringResource(id = R.string.Participate), text = stringResource(id = R.string.Participate),
enabled = bet.betStatus == BetStatus.Waiting, enabled = betDetail.bet.betStatus == BetStatus.Waiting,
onClick = openParticipateSheet onClick = openParticipateSheet
) )
} }
} }
} }
@OptIn(ExperimentalMaterial3Api::class)
@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()
)
}
@Composable @Composable
override fun DisplayMatchBet(bet: MatchBet) { override fun DisplayMatchBet(betDetail: BetDetail) {
Text("This is a MATCH BET") val bet = remember { betDetail.bet as MatchBet }
DisplayBinaryBet(
betDetail = betDetail,
response1 = bet.nameTeam1,
response2 = bet.nameTeam2
)
} }
@Composable @Composable
override fun DisplayCustomBet(bet: CustomBet) { override fun DisplayCustomBet(betDetail: BetDetail) {
Text("This is a CUSTOM BET") Text("This is a CUSTOM BET")
} }
} }
@Composable
fun BetStatusParticipant(
username: String,
allCoinsAmount: Int
) {
Row(
modifier = Modifier.fillMaxWidth(),
horizontalArrangement = Arrangement.spacedBy(7.dp),
verticalAlignment = Alignment.CenterVertically
) {
ProfilePicture(modifier = Modifier.size(25.dp))
Text(
text = username,
fontWeight = FontWeight.Bold,
style = AllInTheme.typography.h2,
color = AllInTheme.themeColors.onMainSurface,
maxLines = 1,
overflow = TextOverflow.Ellipsis,
modifier = Modifier.weight(1f)
)
AllInCoinCount(
amount = allCoinsAmount,
color = AllInTheme.colors.allInPurple
)
}
}
@Preview @Preview
@Preview(uiMode = Configuration.UI_MODE_NIGHT_YES) @Preview(uiMode = Configuration.UI_MODE_NIGHT_YES)
@Composable @Composable
private fun BetStatusBottomSheetPreview( private fun BetStatusBottomSheetPreview(
@PreviewParameter(BetWithStatusPreviewProvider::class) bet: Bet @PreviewParameter(BetDetailPreviewProvider::class) bet: BetDetail
) { ) {
AllInTheme { AllInTheme {
val coins = remember { mutableIntStateOf(100) } BetStatusBottomSheetBetDisplayer {
BetStatusBottomSheetBetDisplayer {}.DisplayBet(bet)
}.DisplayBet(bet)
} }
} }

@ -67,7 +67,7 @@ fun MainScreen(
var loading by remember { mainViewModel.loading } var loading by remember { mainViewModel.loading }
val currentUser = remember { mainViewModel.currentUserState } val currentUser = remember { mainViewModel.currentUserState }
val (selectedBet, setSelectedBet) = remember { mainViewModel.selectedBet } val selectedBet by remember { mainViewModel.selectedBet }
val (wonBet, setWonBet) = remember { mainViewModel.wonBet } val (wonBet, setWonBet) = remember { mainViewModel.wonBet }
val (statusVisibility, sheetBackVisibility, setStatusVisibility) = rememberBetStatusVisibilities() val (statusVisibility, sheetBackVisibility, setStatusVisibility) = rememberBetStatusVisibilities()
val (participateSheetVisibility, setParticipateSheetVisibility) = remember { val (participateSheetVisibility, setParticipateSheetVisibility) = remember {
@ -152,7 +152,7 @@ fun MainScreen(
AllInDrawerNavHost( AllInDrawerNavHost(
navController = navController, navController = navController,
selectBet = { bet, participate -> selectBet = { bet, participate ->
setSelectedBet(bet) mainViewModel.openBetDetail(bet)
setParticipateSheetVisibility(participate) setParticipateSheetVisibility(participate)
setStatusVisibility(true) setStatusVisibility(true)
}, },
@ -182,11 +182,11 @@ fun MainScreen(
sheetVisibility = statusVisibility.value, sheetVisibility = statusVisibility.value,
sheetBackVisibility = sheetBackVisibility.value, sheetBackVisibility = sheetBackVisibility.value,
onDismiss = { setStatusVisibility(false) }, onDismiss = { setStatusVisibility(false) },
bet = selectedBet, betDetail = selectedBet,
paddingValues = betStatusDisplayer.paddingValues.value, paddingValues = betStatusDisplayer.paddingValues.value,
displayBet = { betStatusDisplayer.DisplayBet(it) }, displayBet = { betStatusDisplayer.DisplayBet(it) },
userCoinAmount = mainViewModel.currentUserState.userCoins, userCoinAmount = mainViewModel.currentUserState.userCoins,
onParticipate = { mainViewModel.participateToBet(it) }, onParticipate = { stake, response -> mainViewModel.participateToBet(stake, response) },
participateSheetVisibility = participateSheetVisibility, participateSheetVisibility = participateSheetVisibility,
setParticipateSheetVisibility = setParticipateSheetVisibility setParticipateSheetVisibility = setParticipateSheetVisibility
) )

@ -8,16 +8,15 @@ import androidx.lifecycle.viewModelScope
import dagger.hilt.android.lifecycle.HiltViewModel import dagger.hilt.android.lifecycle.HiltViewModel
import fr.iut.alldev.allin.data.model.User 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.Bet
import fr.iut.alldev.allin.data.model.bet.BetFinishedStatus import fr.iut.alldev.allin.data.model.bet.Participation
import fr.iut.alldev.allin.data.model.bet.BetStatus import fr.iut.alldev.allin.data.model.bet.vo.BetDetail
import fr.iut.alldev.allin.data.model.bet.YesNoBet import fr.iut.alldev.allin.data.repository.BetRepository
import fr.iut.alldev.allin.di.AllInCurrentUser import fr.iut.alldev.allin.di.AllInCurrentUser
import fr.iut.alldev.allin.keystore.AllInKeystoreManager import fr.iut.alldev.allin.keystore.AllInKeystoreManager
import fr.iut.alldev.allin.ui.core.snackbar.SnackbarType import fr.iut.alldev.allin.ui.core.snackbar.SnackbarType
import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext import kotlinx.coroutines.withContext
import java.time.ZonedDateTime
import javax.inject.Inject import javax.inject.Inject
class UserState(val user: User) { class UserState(val user: User) {
@ -27,15 +26,18 @@ class UserState(val user: User) {
@HiltViewModel @HiltViewModel
class MainViewModel @Inject constructor( class MainViewModel @Inject constructor(
@AllInCurrentUser val currentUser: User, @AllInCurrentUser val currentUser: User,
private val betRepository: BetRepository,
private val keystoreManager: AllInKeystoreManager private val keystoreManager: AllInKeystoreManager
) : ViewModel() { ) : ViewModel() {
var loading = mutableStateOf(false) var loading = mutableStateOf(false)
val currentUserState = UserState(currentUser) val currentUserState = UserState(currentUser)
val selectedBet = mutableStateOf<Bet?>(null) val selectedBet = mutableStateOf<BetDetail?>(null)
val wonBet = mutableStateOf<Bet?>( val wonBet = mutableStateOf<Bet?>(
YesNoBet( null
/* YesNoBet(
id = "1",
theme = "Theme", theme = "Theme",
phrase = "Phrase", phrase = "Phrase",
endRegisterDate = ZonedDateTime.now(), endRegisterDate = ZonedDateTime.now(),
@ -43,7 +45,7 @@ class MainViewModel @Inject constructor(
isPublic = true, isPublic = true,
betStatus = BetStatus.Finished(BetFinishedStatus.WON), betStatus = BetStatus.Finished(BetFinishedStatus.WON),
creator = "creator" creator = "creator"
) )*/
) )
val snackbarContent: MutableState<SnackbarContent?> by lazy { mutableStateOf(null) } val snackbarContent: MutableState<SnackbarContent?> by lazy { mutableStateOf(null) }
@ -51,6 +53,11 @@ class MainViewModel @Inject constructor(
snackbarContent.value = content snackbarContent.value = content
} }
fun openBetDetail(bet: Bet) {
viewModelScope.launch {
selectedBet.value = betRepository.getBet(bet.id, keystoreManager.getToken() ?: "")
}
}
fun deleteToken() { fun deleteToken() {
viewModelScope.launch { viewModelScope.launch {
@ -58,12 +65,20 @@ class MainViewModel @Inject constructor(
} }
} }
fun participateToBet(stake: Int) { fun participateToBet(stake: Int, response: String) {
viewModelScope.launch { viewModelScope.launch {
withContext(Dispatchers.IO) { withContext(Dispatchers.IO) {
loading.value = true loading.value = true
currentUserState.userCoins.intValue -= stake currentUserState.userCoins.intValue -= stake
Thread.sleep(1000) selectedBet.value?.bet?.let {
val participation = Participation(
betId = it.id,
username = currentUser.username,
response = response,
stake = stake
)
betRepository.participateToBet(participation, keystoreManager.getToken() ?: "")
}
loading.value = false loading.value = false
} }
} }

@ -0,0 +1,58 @@
package fr.iut.alldev.allin.ui.preview
import androidx.compose.ui.tooling.preview.PreviewParameterProvider
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.vo.BetAnswerDetail
import fr.iut.alldev.allin.data.model.bet.vo.BetDetail
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
),
),
participations = listOf(
Participation(
betId = it.id,
username = "User1",
response = YES_VALUE,
stake = 200
),
Participation(
betId = it.id,
username = "User2",
response = YES_VALUE,
stake = 100
),
Participation(
betId = it.id,
username = "MyUser",
response = NO_VALUE,
stake = 150
)
),
userParticipation = Participation(
betId = it.id,
username = "MyUser",
response = NO_VALUE,
stake = 150
)
)
}
}

@ -12,6 +12,7 @@ import java.time.ZonedDateTime
class BetPreviewProvider : PreviewParameterProvider<Bet> { class BetPreviewProvider : PreviewParameterProvider<Bet> {
override val values = sequenceOf( override val values = sequenceOf(
YesNoBet( YesNoBet(
id = "1",
theme = "Theme", theme = "Theme",
phrase = "Phrase", phrase = "Phrase",
endRegisterDate = ZonedDateTime.now(), endRegisterDate = ZonedDateTime.now(),
@ -21,6 +22,7 @@ class BetPreviewProvider : PreviewParameterProvider<Bet> {
creator = "creator" creator = "creator"
), ),
MatchBet( MatchBet(
id = "2",
theme = "Theme", theme = "Theme",
phrase = "Phrase", phrase = "Phrase",
endRegisterDate = ZonedDateTime.now(), endRegisterDate = ZonedDateTime.now(),
@ -32,6 +34,7 @@ class BetPreviewProvider : PreviewParameterProvider<Bet> {
nameTeam2 = "Climate Change" nameTeam2 = "Climate Change"
), ),
CustomBet( CustomBet(
id = "3",
theme = "Theme", theme = "Theme",
phrase = "Phrase", phrase = "Phrase",
endRegisterDate = ZonedDateTime.now(), endRegisterDate = ZonedDateTime.now(),

@ -13,6 +13,7 @@ class BetWithStatusPreviewProvider : PreviewParameterProvider<Bet> {
override val values = BetStatus.entries.flatMap { status -> override val values = BetStatus.entries.flatMap { status ->
sequenceOf( sequenceOf(
YesNoBet( YesNoBet(
id = "1",
theme = "Theme", theme = "Theme",
phrase = "Phrase", phrase = "Phrase",
endRegisterDate = ZonedDateTime.now(), endRegisterDate = ZonedDateTime.now(),
@ -22,6 +23,7 @@ class BetWithStatusPreviewProvider : PreviewParameterProvider<Bet> {
creator = "creator" creator = "creator"
), ),
MatchBet( MatchBet(
id = "2",
theme = "Theme", theme = "Theme",
phrase = "Phrase", phrase = "Phrase",
endRegisterDate = ZonedDateTime.now(), endRegisterDate = ZonedDateTime.now(),
@ -33,6 +35,7 @@ class BetWithStatusPreviewProvider : PreviewParameterProvider<Bet> {
nameTeam2 = "Climate Change" nameTeam2 = "Climate Change"
), ),
CustomBet( CustomBet(
id = "3",
theme = "Theme", theme = "Theme",
phrase = "Phrase", phrase = "Phrase",
endRegisterDate = ZonedDateTime.now(), endRegisterDate = ZonedDateTime.now(),

@ -1,12 +0,0 @@
package fr.iut.alldev.allin.utils
import java.io.Serializable
data class Quadruple<out A, out B, out C, out D>(
val first: A,
val second: B,
val third: C,
val fourth: D
) : Serializable {
override fun toString(): String = "($first, $second, $third, $fourth)"
}

@ -1,27 +1,27 @@
package fr.iut.alldev.allin.vo.bet package fr.iut.alldev.allin.vo.bet
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
import fr.iut.alldev.allin.data.model.bet.Bet
import fr.iut.alldev.allin.data.model.bet.CustomBet 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.MatchBet
import fr.iut.alldev.allin.data.model.bet.YesNoBet import fr.iut.alldev.allin.data.model.bet.YesNoBet
import fr.iut.alldev.allin.data.model.bet.vo.BetDetail
interface BetDisplayer { interface BetDisplayer {
@Composable @Composable
fun DisplayBet(bet: Bet){ fun DisplayBet(betDetail: BetDetail) {
when(bet){ when (betDetail.bet) {
is CustomBet -> DisplayCustomBet(bet) is CustomBet -> DisplayCustomBet(betDetail)
is MatchBet -> DisplayMatchBet(bet) is MatchBet -> DisplayMatchBet(betDetail)
is YesNoBet -> DisplayYesNoBet(bet) is YesNoBet -> DisplayYesNoBet(betDetail)
} }
} }
@Composable @Composable
fun DisplayYesNoBet(bet: YesNoBet) fun DisplayYesNoBet(betDetail: BetDetail)
@Composable @Composable
fun DisplayMatchBet(bet: MatchBet) fun DisplayMatchBet(betDetail: BetDetail)
@Composable @Composable
fun DisplayCustomBet(bet: CustomBet) fun DisplayCustomBet(betDetail: BetDetail)
} }

@ -118,6 +118,7 @@
<string name="bet_status_in_progress">En cours…</string> <string name="bet_status_in_progress">En cours…</string>
<string name="bet_status_waiting">En attente…</string> <string name="bet_status_waiting">En attente…</string>
<string name="place_your_bets">Faites vos paris</string> <string name="place_your_bets">Faites vos paris</string>
<string name="bet_status_participants_list">Liste des participants</string>
<!--Bet history--> <!--Bet history-->
<string name="bet_history_current_title">En cours</string> <string name="bet_history_current_title">En cours</string>

@ -118,6 +118,7 @@
<string name="bet_status_in_progress">In progress…</string> <string name="bet_status_in_progress">In progress…</string>
<string name="bet_status_waiting">Waiting…</string> <string name="bet_status_waiting">Waiting…</string>
<string name="place_your_bets">Place your bets</string> <string name="place_your_bets">Place your bets</string>
<string name="bet_status_participants_list">Participants</string>
<!--Bet history--> <!--Bet history-->
<string name="bet_history_current_title">Current</string> <string name="bet_history_current_title">Current</string>

@ -2,15 +2,22 @@ package fr.iut.alldev.allin.data.api
import fr.iut.alldev.allin.data.api.model.CheckUser 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.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.RequestUser
import fr.iut.alldev.allin.data.api.model.ResponseBet import fr.iut.alldev.allin.data.api.model.ResponseBet
import fr.iut.alldev.allin.data.api.model.ResponseBetDetail
import fr.iut.alldev.allin.data.api.model.ResponseUser import fr.iut.alldev.allin.data.api.model.ResponseUser
import retrofit2.http.Body import retrofit2.http.Body
import retrofit2.http.GET import retrofit2.http.GET
import retrofit2.http.Header import retrofit2.http.Header
import retrofit2.http.POST import retrofit2.http.POST
import retrofit2.http.Path
interface AllInApi { interface AllInApi {
companion object {
fun String.formatBearerToken() = "Bearer $this"
}
@POST("users/login") @POST("users/login")
suspend fun login(@Body body: CheckUser): ResponseUser suspend fun login(@Body body: CheckUser): ResponseUser
@ -25,4 +32,10 @@ interface AllInApi {
@GET("bets/gets") @GET("bets/gets")
suspend fun getAllBets(): List<ResponseBet> suspend fun getAllBets(): List<ResponseBet>
@GET("betdetail/get/{id}")
suspend fun getBet(@Header("Authorization") token: String, @Path("id") id: String): ResponseBetDetail
@POST("participations/add")
suspend fun participateToBet(@Header("Authorization") token: String, @Body body: RequestParticipation)
} }

@ -3,8 +3,12 @@ package fr.iut.alldev.allin.data.api
import fr.iut.alldev.allin.data.api.interceptors.AllInAPIException 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.CheckUser
import fr.iut.alldev.allin.data.api.model.RequestBet 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.RequestUser
import fr.iut.alldev.allin.data.api.model.ResponseBet 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.ResponseParticipation
import fr.iut.alldev.allin.data.api.model.ResponseUser import fr.iut.alldev.allin.data.api.model.ResponseUser
import fr.iut.alldev.allin.data.model.bet.NO_VALUE 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.YES_VALUE
@ -12,13 +16,30 @@ import java.time.ZonedDateTime
import java.util.UUID import java.util.UUID
class MockAllInApi : AllInApi { class MockAllInApi : AllInApi {
private fun getUserFromToken(token: String) =
mockUsers.find { it.first.token == token }
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 { override suspend fun login(body: CheckUser): ResponseUser {
return users.find { it.first.username == body.login && it.second == body.password }?.first return mockUsers.find { it.first.username == body.login && it.second == body.password }?.first
?: throw AllInAPIException("Invalid login/password.") ?: throw AllInAPIException("Invalid login/password.")
} }
override suspend fun login(token: String): ResponseUser { override suspend fun login(token: String): ResponseUser {
return users.find { it.first.token == token }?.first return getUserFromToken(token)?.first
?: throw AllInAPIException("Invalid token") ?: throw AllInAPIException("Invalid token")
} }
@ -27,10 +48,10 @@ class MockAllInApi : AllInApi {
id = UUID.randomUUID().toString(), id = UUID.randomUUID().toString(),
username = body.username, username = body.username,
email = body.email, email = body.email,
nbCoins = body.nbCoins, nbCoins = 500,
token = "${body.username} ${users.size}" token = "${body.username} ${mockUsers.size}"
) to body.password ) to body.password
users.add(response) mockUsers.add(response)
return response.first return response.first
} }
@ -50,16 +71,138 @@ class MockAllInApi : AllInApi {
} }
override suspend fun getAllBets(): List<ResponseBet> = mockBets.toList() override suspend fun getAllBets(): List<ResponseBet> = mockBets.toList()
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 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 users = mutableListOf( private val mockUsers = mutableListOf(
ResponseUser( ResponseUser(
id = "UUID 1", id = "UUID 1",
username = "User 1", username = "User 1",
email = "john@doe.fr", email = "john@doe.fr",
nbCoins = 250, nbCoins = 250,
token = "token 1" token = "token 1"
) to "psswrd" ) 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[0].first.username,
answer = YES_VALUE,
stake = 200
),
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( private val mockBets = mutableListOf(

@ -14,14 +14,15 @@ class ErrorInterceptor : Interceptor {
override fun intercept(chain: Interceptor.Chain): Response { override fun intercept(chain: Interceptor.Chain): Response {
val request = chain.request() val request = chain.request()
val response = chain.proceed(request) val response = chain.proceed(request)
if(!response.isSuccessful){ if (!response.isSuccessful) {
when (response.code) { when (response.code) {
404 -> throw AllInNotFoundException(response.message) 404 -> throw AllInNotFoundException(response.message)
401 -> throw AllInUnauthorizedException(response.message) 401 -> throw AllInUnauthorizedException(response.message)
else -> throw AllInUnsuccessfulException(response.message) else -> throw AllInUnsuccessfulException(response.message)
} }
} }
if (response.body?.contentType()?.subtype != "json") {
response.body?.contentType()?.subtype?.takeIf { it != "json" }?.let {
throw AllInAPIException(response.message) throw AllInAPIException(response.message)
} }

@ -7,7 +7,9 @@ import fr.iut.alldev.allin.data.model.bet.CustomBet
import fr.iut.alldev.allin.data.model.bet.NO_VALUE 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.YES_VALUE
import fr.iut.alldev.allin.data.model.bet.YesNoBet import fr.iut.alldev.allin.data.model.bet.YesNoBet
import fr.iut.alldev.allin.data.serialization.SimpleDateSerializer 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.serialization.ZonedDateTimeSerializer
import kotlinx.serialization.Serializable import kotlinx.serialization.Serializable
import java.time.ZonedDateTime import java.time.ZonedDateTime
@ -17,8 +19,8 @@ data class ResponseBet(
val id: String?, val id: String?,
val theme: String, val theme: String,
val sentenceBet: String, val sentenceBet: String,
@Serializable(SimpleDateSerializer::class) val endRegistration: ZonedDateTime, @Serializable(ZonedDateTimeSerializer::class) val endRegistration: ZonedDateTime,
@Serializable(SimpleDateSerializer::class) var endBet: ZonedDateTime, @Serializable(ZonedDateTimeSerializer::class) var endBet: ZonedDateTime,
var isPrivate: Boolean, var isPrivate: Boolean,
var response: List<String>, var response: List<String>,
val createdBy: String val createdBy: String
@ -26,6 +28,7 @@ data class ResponseBet(
fun toBet(): Bet { fun toBet(): Bet {
if (response.toSet() == setOf(YES_VALUE, NO_VALUE)) { if (response.toSet() == setOf(YES_VALUE, NO_VALUE)) {
return YesNoBet( return YesNoBet(
id = id ?: "",
theme = theme, theme = theme,
phrase = sentenceBet, phrase = sentenceBet,
endRegisterDate = endRegistration, endRegisterDate = endRegistration,
@ -36,6 +39,7 @@ data class ResponseBet(
) )
} else { } else {
return CustomBet( return CustomBet(
id = id ?: "",
theme = theme, theme = theme,
phrase = sentenceBet, phrase = sentenceBet,
endRegisterDate = endRegistration, endRegisterDate = endRegistration,
@ -52,10 +56,48 @@ data class ResponseBet(
@Keep @Keep
@Serializable @Serializable
data class RequestBet( data class RequestBet(
val id: String = "",
val theme: String, val theme: String,
val sentenceBet: String, val sentenceBet: String,
@Serializable(SimpleDateSerializer::class) val endRegistration: ZonedDateTime, @Serializable(ZonedDateTimeSerializer::class) val endRegistration: ZonedDateTime,
@Serializable(SimpleDateSerializer::class) var endBet: ZonedDateTime, @Serializable(ZonedDateTimeSerializer::class) var endBet: ZonedDateTime,
var isPrivate: Boolean, var isPrivate: Boolean,
var response: List<String> var response: List<String>
) )
@Keep
@Serializable
data class ResponseBetAnswerDetail(
val response: String,
val totalStakes: Int,
val totalParticipants: Int,
val highestStake: Int,
val odds: Float
) {
fun toAnswerDetail() =
BetAnswerDetail(
response = response,
totalStakes = totalStakes,
totalParticipants = totalParticipants,
highestStake = highestStake,
odds = odds
)
}
@Keep
@Serializable
data class ResponseBetDetail(
val bet: ResponseBet,
val answers: List<ResponseBetAnswerDetail>,
val participations: List<ResponseParticipation>,
val userParticipation: ResponseParticipation? = null
) {
fun toBetDetail() =
BetDetail(
bet = bet.toBet(),
answers = answers.map { it.toAnswerDetail() },
participations = participations.map { it.toParticipation() },
userParticipation = userParticipation?.toParticipation()
)
}

@ -0,0 +1,31 @@
package fr.iut.alldev.allin.data.api.model
import androidx.annotation.Keep
import fr.iut.alldev.allin.data.model.bet.Participation
import kotlinx.serialization.Serializable
@Keep
@Serializable
data class ResponseParticipation(
val id: String,
val betId: String,
val username: String,
val answer: String,
val stake: Int
) {
fun toParticipation() =
Participation(
betId = betId,
username = username,
response = answer,
stake = stake
)
}
@Keep
@Serializable
data class RequestParticipation(
val betId: String,
val answer: String,
val stake: Int
)

@ -9,24 +9,23 @@ import kotlinx.serialization.Serializable
data class RequestUser( data class RequestUser(
val username: String, val username: String,
val email: String, val email: String,
val password: String, val password: String
var nbCoins: Int,
) )
@Keep @Keep
@Serializable @Serializable
data class ResponseUser( data class ResponseUser(
val id: String, val id: String = "",
val username: String, val username: String,
val email: String, val email: String,
var nbCoins: Int, var nbCoins: Int,
var token: String? = null, var token: String? = null
) { ) {
fun toUser() = User( fun toUser() = User(
id = id, id = id,
username = username, username = username,
email = email, email = email,
coins = nbCoins coins = nbCoins.toInt()
) )
} }

@ -11,7 +11,7 @@ import okhttp3.HttpUrl
import okhttp3.OkHttpClient import okhttp3.OkHttpClient
import javax.inject.Singleton import javax.inject.Singleton
const val mock = true const val mock = false
@Module @Module
@InstallIn(SingletonComponent::class) @InstallIn(SingletonComponent::class)

@ -17,7 +17,7 @@ import javax.inject.Qualifier
internal val json by lazy { internal val json by lazy {
Json { Json {
ignoreUnknownKeys = true ignoreUnknownKeys = true
encodeDefaults = false encodeDefaults = true
} }
} }

@ -1,8 +1,8 @@
package fr.iut.alldev.allin.data.model package fr.iut.alldev.allin.data.model
data class User( data class User(
val id : String, val id: String,
val username : String, val username: String,
val email : String, val email: String,
val coins : Int val coins: Int
) )

@ -4,7 +4,7 @@ import fr.iut.alldev.allin.data.api.model.RequestBet
import java.time.ZonedDateTime import java.time.ZonedDateTime
sealed class Bet( sealed class Bet(
open val id: String? = null, open val id: String,
open val creator: String, open val creator: String,
open val theme: String, open val theme: String,
open val phrase: String, open val phrase: String,
@ -16,6 +16,7 @@ sealed class Bet(
abstract fun getResponses(): List<String> abstract fun getResponses(): List<String>
fun toRequestBet(): RequestBet { fun toRequestBet(): RequestBet {
return RequestBet( return RequestBet(
id = "",
theme = theme, theme = theme,
sentenceBet = phrase, sentenceBet = phrase,
endRegistration = endRegisterDate, endRegistration = endRegisterDate,

@ -5,6 +5,7 @@ import java.time.ZonedDateTime
class BetFactory { class BetFactory {
companion object { companion object {
fun createBet( fun createBet(
id: String,
betType: BetType, betType: BetType,
creator: String, creator: String,
theme: String, theme: String,
@ -20,6 +21,7 @@ class BetFactory {
when (betType) { when (betType) {
BetType.YES_NO -> { BetType.YES_NO -> {
YesNoBet( YesNoBet(
id = id,
theme = theme, theme = theme,
creator = creator, creator = creator,
phrase = phrase, phrase = phrase,
@ -32,6 +34,7 @@ class BetFactory {
BetType.MATCH -> { BetType.MATCH -> {
MatchBet( MatchBet(
id = id,
theme = theme, theme = theme,
creator = creator, creator = creator,
phrase = phrase, phrase = phrase,
@ -47,6 +50,7 @@ class BetFactory {
BetType.CUSTOM -> { BetType.CUSTOM -> {
CustomBet( CustomBet(
id = id,
theme = theme, theme = theme,
creator = creator, creator = creator,
phrase = phrase, phrase = phrase,

@ -3,7 +3,7 @@ package fr.iut.alldev.allin.data.model.bet
import java.time.ZonedDateTime import java.time.ZonedDateTime
data class CustomBet( data class CustomBet(
override val id: String? = null, override val id: String,
override val creator: String, override val creator: String,
override val theme: String, override val theme: String,
override val phrase: String, override val phrase: String,

@ -3,7 +3,7 @@ package fr.iut.alldev.allin.data.model.bet
import java.time.ZonedDateTime import java.time.ZonedDateTime
data class MatchBet( data class MatchBet(
override val id: String? = null, override val id: String,
override val creator: String, override val creator: String,
override val theme: String, override val theme: String,
override val phrase: String, override val phrase: String,

@ -0,0 +1,17 @@
package fr.iut.alldev.allin.data.model.bet
import fr.iut.alldev.allin.data.api.model.RequestParticipation
data class Participation(
val betId: String,
val username: String,
val response: String,
val stake: Int
) {
fun toRequestParticipation() =
RequestParticipation(
betId = betId,
answer = response,
stake = stake
)
}

@ -6,7 +6,7 @@ const val YES_VALUE = "Yes"
const val NO_VALUE = "No" const val NO_VALUE = "No"
data class YesNoBet( data class YesNoBet(
override val id: String? = null, override val id: String,
override val creator: String, override val creator: String,
override val theme: String, override val theme: String,
override val phrase: String, override val phrase: String,

@ -0,0 +1,9 @@
package fr.iut.alldev.allin.data.model.bet.vo
data class BetAnswerDetail(
val response: String,
val totalStakes: Int,
val totalParticipants: Int,
val highestStake: Int,
val odds: Float
)

@ -0,0 +1,14 @@
package fr.iut.alldev.allin.data.model.bet.vo
import fr.iut.alldev.allin.data.model.bet.Bet
import fr.iut.alldev.allin.data.model.bet.Participation
data class BetDetail(
val bet: Bet,
val answers: List<BetAnswerDetail>,
val participations: List<Participation>,
val userParticipation: Participation?
) {
fun getAnswerOfResponse(response: String) =
answers.find { it.response == response }
}

@ -0,0 +1,10 @@
package fr.iut.alldev.allin.data.model.bet.vo
import fr.iut.alldev.allin.data.model.bet.Bet
import fr.iut.alldev.allin.data.model.bet.Participation
data class BetResult(
val bet: Bet,
val participations: List<Participation>,
val answerDetail: BetAnswerDetail
)

@ -1,11 +1,15 @@
package fr.iut.alldev.allin.data.repository package fr.iut.alldev.allin.data.repository
import fr.iut.alldev.allin.data.model.bet.Bet import fr.iut.alldev.allin.data.model.bet.Bet
import fr.iut.alldev.allin.data.model.bet.Participation
import fr.iut.alldev.allin.data.model.bet.vo.BetDetail
import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.Flow
abstract class BetRepository { abstract class BetRepository {
abstract suspend fun createBet(bet: Bet, token: String) abstract suspend fun createBet(bet: Bet, token: String)
abstract suspend fun getHistory(): Flow<List<Bet>> abstract suspend fun getHistory(): Flow<List<Bet>>
abstract suspend fun getCurrentBets(): Flow<List<Bet>> 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(): Flow<List<Bet>>
} }

@ -4,16 +4,9 @@ import fr.iut.alldev.allin.data.model.User
abstract class UserRepository { abstract class UserRepository {
lateinit var currentUser: User lateinit var currentUser: User
abstract suspend fun login( abstract suspend fun login(username: String, password: String): String?
username: String,
password: String
): String?
abstract suspend fun login(token: String): String? abstract suspend fun login(token: String): String?
abstract suspend fun register( abstract suspend fun register(username: String, email: String, password: String): String?
username: String,
email: String,
password: String
): String?
} }

@ -1,10 +1,13 @@
package fr.iut.alldev.allin.data.repository.impl package fr.iut.alldev.allin.data.repository.impl
import fr.iut.alldev.allin.data.api.AllInApi import fr.iut.alldev.allin.data.api.AllInApi
import fr.iut.alldev.allin.data.api.AllInApi.Companion.formatBearerToken
import fr.iut.alldev.allin.data.model.bet.Bet 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.BetFinishedStatus
import fr.iut.alldev.allin.data.model.bet.BetStatus import fr.iut.alldev.allin.data.model.bet.BetStatus
import fr.iut.alldev.allin.data.model.bet.Participation
import fr.iut.alldev.allin.data.model.bet.YesNoBet import fr.iut.alldev.allin.data.model.bet.YesNoBet
import fr.iut.alldev.allin.data.model.bet.vo.BetDetail
import fr.iut.alldev.allin.data.repository.BetRepository import fr.iut.alldev.allin.data.repository.BetRepository
import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.flowOf import kotlinx.coroutines.flow.flowOf
@ -16,7 +19,7 @@ class BetRepositoryImpl @Inject constructor(
) : BetRepository() { ) : BetRepository() {
override suspend fun createBet(bet: Bet, token: String) { override suspend fun createBet(bet: Bet, token: String) {
api.createBet( api.createBet(
token = token, token = token.formatBearerToken(),
body = bet.toRequestBet() body = bet.toRequestBet()
) )
} }
@ -25,6 +28,7 @@ class BetRepositoryImpl @Inject constructor(
return flowOf( return flowOf(
listOf( listOf(
YesNoBet( YesNoBet(
id = "1",
creator = "Lucas", creator = "Lucas",
theme = "Theme", theme = "Theme",
phrase = "Bet phrase 1", phrase = "Bet phrase 1",
@ -34,6 +38,7 @@ class BetRepositoryImpl @Inject constructor(
betStatus = BetStatus.Finished(BetFinishedStatus.WON) betStatus = BetStatus.Finished(BetFinishedStatus.WON)
), ),
YesNoBet( YesNoBet(
id = "2",
creator = "Lucas", creator = "Lucas",
theme = "Theme", theme = "Theme",
phrase = "Bet phrase 2", phrase = "Bet phrase 2",
@ -43,6 +48,7 @@ class BetRepositoryImpl @Inject constructor(
betStatus = BetStatus.Finished(BetFinishedStatus.LOST) betStatus = BetStatus.Finished(BetFinishedStatus.LOST)
), ),
YesNoBet( YesNoBet(
id = "3",
creator = "Lucas", creator = "Lucas",
theme = "Theme", theme = "Theme",
phrase = "Bet phrase 3", phrase = "Bet phrase 3",
@ -60,6 +66,7 @@ class BetRepositoryImpl @Inject constructor(
return flowOf( return flowOf(
listOf( listOf(
YesNoBet( YesNoBet(
id = "1",
creator = "Lucas", creator = "Lucas",
theme = "Theme", theme = "Theme",
phrase = "Bet phrase 1", phrase = "Bet phrase 1",
@ -69,6 +76,7 @@ class BetRepositoryImpl @Inject constructor(
betStatus = BetStatus.InProgress betStatus = BetStatus.InProgress
), ),
YesNoBet( YesNoBet(
id = "2",
creator = "Lucas", creator = "Lucas",
theme = "Theme", theme = "Theme",
phrase = "Bet phrase 2", phrase = "Bet phrase 2",
@ -78,6 +86,7 @@ class BetRepositoryImpl @Inject constructor(
betStatus = BetStatus.InProgress betStatus = BetStatus.InProgress
), ),
YesNoBet( YesNoBet(
id = "3",
creator = "Lucas", creator = "Lucas",
theme = "Theme", theme = "Theme",
phrase = "Bet phrase 3", phrase = "Bet phrase 3",
@ -90,6 +99,17 @@ class BetRepositoryImpl @Inject constructor(
) )
} }
override suspend fun getBet(id: String, token: String): BetDetail {
return api.getBet(
token = token.formatBearerToken(),
id = id
).toBetDetail()
}
override suspend fun participateToBet(participation: Participation, token: String) {
api.participateToBet(token = token.formatBearerToken(), body = participation.toRequestParticipation())
}
override suspend fun getAllBets(): Flow<List<Bet>> { override suspend fun getAllBets(): Flow<List<Bet>> {
return flowOf( return flowOf(
api.getAllBets().map { it.toBet() } api.getAllBets().map { it.toBet() }

@ -1,6 +1,7 @@
package fr.iut.alldev.allin.data.repository.impl package fr.iut.alldev.allin.data.repository.impl
import fr.iut.alldev.allin.data.api.AllInApi import fr.iut.alldev.allin.data.api.AllInApi
import fr.iut.alldev.allin.data.api.AllInApi.Companion.formatBearerToken
import fr.iut.alldev.allin.data.api.model.CheckUser import fr.iut.alldev.allin.data.api.model.CheckUser
import fr.iut.alldev.allin.data.api.model.RequestUser import fr.iut.alldev.allin.data.api.model.RequestUser
import fr.iut.alldev.allin.data.repository.UserRepository import fr.iut.alldev.allin.data.repository.UserRepository
@ -22,7 +23,7 @@ class UserRepositoryImpl @Inject constructor(
} }
override suspend fun login(token: String): String? { override suspend fun login(token: String): String? {
val response = api.login(token = "Bearer $token") val response = api.login(token = token.formatBearerToken())
currentUser = response.toUser() currentUser = response.toUser()
return response.token return response.token
} }
@ -33,8 +34,7 @@ class UserRepositoryImpl @Inject constructor(
RequestUser( RequestUser(
username = username, username = username,
email = email, email = email,
password = password, password = password
nbCoins = 0
) )
) )
currentUser = response.toUser() currentUser = response.toUser()

@ -6,39 +6,22 @@ import kotlinx.serialization.descriptors.PrimitiveSerialDescriptor
import kotlinx.serialization.descriptors.SerialDescriptor import kotlinx.serialization.descriptors.SerialDescriptor
import kotlinx.serialization.encoding.Decoder import kotlinx.serialization.encoding.Decoder
import kotlinx.serialization.encoding.Encoder import kotlinx.serialization.encoding.Encoder
import java.time.Instant
import java.time.LocalDate
import java.time.ZoneId
import java.time.ZonedDateTime import java.time.ZonedDateTime
import java.time.format.DateTimeFormatter import java.time.format.DateTimeFormatter
object ZonedDateTimeSerializer : KSerializer<ZonedDateTime> { object ZonedDateTimeSerializer : KSerializer<ZonedDateTime> {
private val formatter: DateTimeFormatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss Z")
override val descriptor: SerialDescriptor = override val descriptor: SerialDescriptor =
PrimitiveSerialDescriptor("ZonedDateTime", PrimitiveKind.LONG) PrimitiveSerialDescriptor("ZonedDateTime", PrimitiveKind.STRING)
override fun serialize(encoder: Encoder, value: ZonedDateTime) { override fun serialize(encoder: Encoder, value: ZonedDateTime) {
encoder.encodeLong(value.toEpochSecond()) encoder.encodeString(formatter.format(value))
} }
override fun deserialize(decoder: Decoder): ZonedDateTime { override fun deserialize(decoder: Decoder): ZonedDateTime {
val epoch = decoder.decodeLong() val dateString = decoder.decodeString()
return ZonedDateTime.ofInstant(Instant.ofEpochSecond(epoch), ZoneId.systemDefault()) return ZonedDateTime.parse(dateString, formatter)
} }
} }
class SimpleDateSerializer : KSerializer<ZonedDateTime> {
override val descriptor: SerialDescriptor =
PrimitiveSerialDescriptor("Date", PrimitiveKind.STRING)
private val formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")
override fun deserialize(decoder: Decoder): ZonedDateTime {
val date = LocalDate.parse(decoder.decodeString(), formatter)
return date.atStartOfDay(ZoneId.systemDefault())
}
override fun serialize(encoder: Encoder, value: ZonedDateTime) {
val dateString = formatter.format(value)
encoder.encodeString(dateString)
}
}

Loading…
Cancel
Save