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 {
@AllInCurrentUser
@Provides
fun provideUser(
userRepository: UserRepository
) = userRepository.currentUser
fun provideUser(userRepository: UserRepository) = userRepository.currentUser
}

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

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

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

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

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

@ -1,82 +1,138 @@
package fr.iut.alldev.allin.ui.betStatus.components
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.Row
import androidx.compose.foundation.layout.RowScope
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.padding
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.remember
import androidx.compose.ui.Alignment
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.PreviewParameter
import androidx.compose.ui.unit.dp
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.CustomBet
import fr.iut.alldev.allin.data.model.bet.MatchBet
import fr.iut.alldev.allin.data.model.bet.NO_VALUE
import fr.iut.alldev.allin.data.model.bet.YES_VALUE
import fr.iut.alldev.allin.data.model.bet.YesNoBet
import fr.iut.alldev.allin.data.model.bet.vo.BetDetail
import fr.iut.alldev.allin.ext.formatToSimple
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
@Composable
fun Bet.getParticipationAnswers(): List<@Composable RowScope.() -> Unit> =
when (this) {
is CustomBet -> this.possibleAnswers.map {
fun BetDetail.getParticipationAnswers(): List<@Composable RowScope.() -> Unit> {
val configuration = LocalConfiguration.current
val locale = remember { ConfigurationCompat.getLocales(configuration).get(0) ?: Locale.getDefault() }
return when (this.bet) {
is CustomBet -> (this.bet as CustomBet).possibleAnswers.map {
{
Text(
text = it,
color = AllInTheme.colors.allInBlue,
style = AllInTheme.typography.h1,
fontSize = participationAnswerFontSize
)
this@getParticipationAnswers.getAnswerOfResponse(it)?.let {
ParticipationAnswerLine(
text = it.response,
odds = it.odds,
locale = locale
)
}
}
}
is MatchBet -> listOf(
{
Text(
text = this@getParticipationAnswers.nameTeam1,
color = AllInTheme.colors.allInBlue,
style = AllInTheme.typography.h1,
fontSize = participationAnswerFontSize
)
},
{
Text(
text = this@getParticipationAnswers.nameTeam2,
color = AllInTheme.colors.allInBarPink,
style = AllInTheme.typography.h1,
fontSize = participationAnswerFontSize
)
is MatchBet -> buildList {
val bet = (this@getParticipationAnswers.bet as MatchBet)
add {
this@getParticipationAnswers.getAnswerOfResponse(bet.nameTeam1)?.let {
ParticipationAnswerLine(
text = it.response,
odds = it.odds,
locale = locale
)
}
}
add {
this@getParticipationAnswers.getAnswerOfResponse(bet.nameTeam2)?.let {
ParticipationAnswerLine(
text = it.response,
color = AllInTheme.colors.allInBarPink,
odds = it.odds,
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
)
}
}
}
}
}
@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.uppercase(),
color = color,
style = AllInTheme.typography.h1,
fontSize = participationAnswerFontSize
)
is YesNoBet -> listOf(
{
Text(
text = stringResource(id = R.string.Yes).uppercase(),
color = AllInTheme.colors.allInBlue,
style = AllInTheme.typography.h1,
fontSize = participationAnswerFontSize
)
},
{
AllInCard(
radius = 50.dp,
backgroundColor = AllInTheme.colors.allInPurple
) {
Box(Modifier.padding(vertical = 4.dp, horizontal = 8.dp)) {
Text(
text = stringResource(id = R.string.No).uppercase(),
color = AllInTheme.colors.allInBarPink,
style = AllInTheme.typography.h1,
fontSize = participationAnswerFontSize
text = "x${odds.formatToSimple(locale)}",
color = AllInTheme.colors.white,
style = AllInTheme.typography.h2
)
}
)
}
}
}
fun Bet.getAnswerFromParticipationIdx(idx: Int) =
when (this) {
@ -98,7 +154,7 @@ fun Bet.getAnswerFromParticipationIdx(idx: Int) =
@Preview(uiMode = Configuration.UI_MODE_NIGHT_YES)
@Composable
private fun ParticipationAnswersPreview(
@PreviewParameter(BetPreviewProvider::class) bet: Bet,
@PreviewParameter(BetDetailPreviewProvider::class) bet: BetDetail,
) {
AllInTheme {
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.topbar.AllInTopBarCoinCounter
import kotlinx.coroutines.launch
import kotlin.math.roundToInt
@OptIn(ExperimentalMaterial3Api::class)
@Composable
@ -35,6 +36,7 @@ fun BetStatusParticipationBottomSheet(
onDismiss: () -> Unit,
state: SheetState,
enabled: Boolean,
odds: Float,
stake: Int?,
setStake: (Int?) -> Unit,
elements: List<@Composable RowScope.() -> Unit>,
@ -58,6 +60,7 @@ fun BetStatusParticipationBottomSheet(
setElement = setElement,
enabled = enabled,
stake = stake,
odds = odds,
setStake = setStake
) {
scope.launch {
@ -76,6 +79,7 @@ private fun ColumnScope.BetStatusParticipationBottomSheetContent(
coinAmount: Int,
enabled: Boolean,
stake: Int?,
odds: Float,
setStake: (Int?) -> Unit,
selectedElement: (@Composable RowScope.() -> Unit)?,
elements: List<@Composable RowScope.() -> Unit>,
@ -150,7 +154,7 @@ private fun ColumnScope.BetStatusParticipationBottomSheetContent(
color = AllInTheme.themeColors.onBackground
)
AllInCoinCount(
amount = 121,
amount = stake?.let { (it + (it * odds)).roundToInt() } ?: 0,
color = AllInTheme.themeColors.onBackground
)
}
@ -181,6 +185,7 @@ private fun BetStatusParticipationBottomSheetContentPreview() {
selectedElement = null,
enabled = true,
stake = 123,
odds = 0.42f,
setStake = {}
)
}

@ -4,27 +4,27 @@ import androidx.compose.foundation.layout.*
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.text.font.FontStyle
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.tooling.preview.PreviewParameter
import androidx.compose.ui.tooling.preview.PreviewParameterProvider
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
import fr.iut.alldev.allin.ui.core.PercentagePositionnedElement
import fr.iut.alldev.allin.ui.core.StatBar
@Composable
fun YesNoStatBar(
yesPercentage: Float,
fun BinaryStatBar(
response1Percentage: Float,
response1: String,
response2: String,
modifier: Modifier = Modifier,
) {
Column(modifier) {
Row {
Text(
text = stringResource(id = R.string.Yes).uppercase(),
text = response1,
color = AllInTheme.colors.allInBlue,
style = AllInTheme.typography.h1,
fontStyle = FontStyle.Italic,
@ -32,19 +32,19 @@ fun YesNoStatBar(
modifier = Modifier.weight(1f)
)
Text(
text = stringResource(id = R.string.No).uppercase(),
text = response2,
style = AllInTheme.typography.h1,
fontStyle = FontStyle.Italic,
fontSize = 30.sp,
color = AllInTheme.colors.allInBarPink
)
}
StatBar(percentage = yesPercentage)
StatBar(percentage = response1Percentage)
PercentagePositionnedElement(
percentage = yesPercentage
percentage = response1Percentage
) {
Text(
text = yesPercentage.toPercentageString(),
text = response1Percentage.toPercentageString(),
style = AllInTheme.typography.sm1,
color = AllInTheme.colors.allInBarPurple
)
@ -64,6 +64,10 @@ private fun YesNoStatBarPreview(
@PreviewParameter(YesNoStatBarPreviewProvider::class) percentage: Float,
) {
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 androidx.compose.foundation.background
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.filled.EmojiEvents
import androidx.compose.material.icons.filled.People
@ -13,48 +15,67 @@ import androidx.compose.material3.Text
import androidx.compose.runtime.*
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.platform.LocalConfiguration
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.PreviewParameter
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp
import androidx.core.os.ConfigurationCompat
import fr.iut.alldev.allin.R
import fr.iut.alldev.allin.data.ext.formatToMediumDateNoYear
import fr.iut.alldev.allin.data.ext.formatToTime
import fr.iut.alldev.allin.data.model.bet.Bet
import fr.iut.alldev.allin.data.model.bet.BetStatus
import fr.iut.alldev.allin.data.model.bet.CustomBet
import fr.iut.alldev.allin.data.model.bet.MatchBet
import fr.iut.alldev.allin.data.model.bet.YesNoBet
import 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.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.BinaryStatBar
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.ProfilePicture
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.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 java.util.Locale
class BetStatusBottomSheetBetDisplayer(
val openParticipateSheet: () -> Unit
) : BetDisplayer {
val paddingValues = mutableStateOf(PaddingValues())
@OptIn(ExperimentalMaterial3Api::class)
@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 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)) {
Column {
Column(Modifier.padding(horizontal = 20.dp)) {
BetTitleHeader(
title = bet.phrase,
category = bet.theme,
creator = bet.creator,
title = betDetail.bet.phrase,
category = betDetail.bet.theme,
creator = betDetail.bet.creator,
modifier = Modifier.fillMaxWidth()
)
Spacer(modifier = Modifier.height(20.dp))
@ -62,24 +83,24 @@ class BetStatusBottomSheetBetDisplayer(
horizontalAlignment = Alignment.End
) {
BetDateTimeRow(
label = stringResource(id = bet.betStatus.getDateStartLabelId()),
date = bet.endRegisterDate.formatToMediumDateNoYear(),
time = bet.endRegisterDate.formatToTime(),
label = stringResource(id = betDetail.bet.betStatus.getDateStartLabelId()),
date = betDetail.bet.endRegisterDate.formatToMediumDateNoYear(),
time = betDetail.bet.endRegisterDate.formatToTime(),
modifier = Modifier.width(IntrinsicSize.Max)
)
Spacer(modifier = Modifier.height(15.dp))
BetDateTimeRow(
label = stringResource(id = bet.betStatus.getDateEndLabelId()),
date = bet.endBetDate.formatToMediumDateNoYear(),
time = bet.endBetDate.formatToTime(),
label = stringResource(id = betDetail.bet.betStatus.getDateEndLabelId()),
date = betDetail.bet.endBetDate.formatToMediumDateNoYear(),
time = betDetail.bet.endBetDate.formatToTime(),
modifier = Modifier.width(IntrinsicSize.Max)
)
}
Spacer(modifier = Modifier.height(20.dp))
}
if (bet.betStatus is BetStatus.Finished) {
if (betDetail.bet.betStatus is BetStatus.Finished) {
BetStatusWinner(
answer = stringResource(id = R.string.Yes),
answer = response1Display,
color = AllInTheme.colors.allInBlue,
coinAmount = 442,
username = "Imri",
@ -95,63 +116,150 @@ class BetStatusBottomSheetBetDisplayer(
.padding(horizontal = 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 {
YesNoDetailsLine(
icon = AllInTheme.icons.allCoins(),
yesText = "550",
noText = "330",
yesText = response1Answer?.totalStakes?.toString() ?: "0",
noText = response2Answer?.totalStakes?.toString() ?: "0"
)
YesNoDetailsLine(
icon = Icons.Filled.People,
yesText = "12",
noText = "5"
yesText = response1Answer?.totalParticipants?.toString() ?: "0",
noText = response2Answer?.totalParticipants?.toString() ?: "0"
)
YesNoDetailsLine(
icon = Icons.Filled.WorkspacePremium,
yesText = "45",
noText = "45"
yesText = response1Answer?.highestStake?.toString() ?: "0",
noText = response2Answer?.highestStake?.toString() ?: "0"
)
YesNoDetailsLine(
icon = Icons.Filled.EmojiEvents,
yesText = "x1.2",
noText = "x1.45"
yesText = "x${response1Answer?.odds?.formatToSimple(locale) ?: "1.00"}",
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(
modifier = Modifier
.align(Alignment.BottomCenter)
.padding(horizontal = 7.dp),
text = stringResource(id = R.string.Participate),
enabled = bet.betStatus == BetStatus.Waiting,
enabled = betDetail.bet.betStatus == BetStatus.Waiting,
onClick = openParticipateSheet
)
}
}
}
@OptIn(ExperimentalMaterial3Api::class)
@Composable
override fun DisplayMatchBet(bet: MatchBet) {
Text("This is a MATCH BET")
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
override fun DisplayCustomBet(bet: CustomBet) {
override fun DisplayMatchBet(betDetail: BetDetail) {
val bet = remember { betDetail.bet as MatchBet }
DisplayBinaryBet(
betDetail = betDetail,
response1 = bet.nameTeam1,
response2 = bet.nameTeam2
)
}
@Composable
override fun DisplayCustomBet(betDetail: BetDetail) {
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(uiMode = Configuration.UI_MODE_NIGHT_YES)
@Composable
private fun BetStatusBottomSheetPreview(
@PreviewParameter(BetWithStatusPreviewProvider::class) bet: Bet
@PreviewParameter(BetDetailPreviewProvider::class) bet: BetDetail
) {
AllInTheme {
val coins = remember { mutableIntStateOf(100) }
BetStatusBottomSheetBetDisplayer {}.DisplayBet(bet)
BetStatusBottomSheetBetDisplayer {
}.DisplayBet(bet)
}
}

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

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

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

@ -13,6 +13,7 @@ class BetWithStatusPreviewProvider : PreviewParameterProvider<Bet> {
override val values = BetStatus.entries.flatMap { status ->
sequenceOf(
YesNoBet(
id = "1",
theme = "Theme",
phrase = "Phrase",
endRegisterDate = ZonedDateTime.now(),
@ -22,6 +23,7 @@ class BetWithStatusPreviewProvider : PreviewParameterProvider<Bet> {
creator = "creator"
),
MatchBet(
id = "2",
theme = "Theme",
phrase = "Phrase",
endRegisterDate = ZonedDateTime.now(),
@ -33,6 +35,7 @@ class BetWithStatusPreviewProvider : PreviewParameterProvider<Bet> {
nameTeam2 = "Climate Change"
),
CustomBet(
id = "3",
theme = "Theme",
phrase = "Phrase",
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
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.MatchBet
import fr.iut.alldev.allin.data.model.bet.YesNoBet
import fr.iut.alldev.allin.data.model.bet.vo.BetDetail
interface BetDisplayer {
@Composable
fun DisplayBet(bet: Bet){
when(bet){
is CustomBet -> DisplayCustomBet(bet)
is MatchBet -> DisplayMatchBet(bet)
is YesNoBet -> DisplayYesNoBet(bet)
fun DisplayBet(betDetail: BetDetail) {
when (betDetail.bet) {
is CustomBet -> DisplayCustomBet(betDetail)
is MatchBet -> DisplayMatchBet(betDetail)
is YesNoBet -> DisplayYesNoBet(betDetail)
}
}
@Composable
fun DisplayYesNoBet(bet: YesNoBet)
fun DisplayYesNoBet(betDetail: BetDetail)
@Composable
fun DisplayMatchBet(bet: MatchBet)
fun DisplayMatchBet(betDetail: BetDetail)
@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_waiting">En attente…</string>
<string name="place_your_bets">Faites vos paris</string>
<string name="bet_status_participants_list">Liste des participants</string>
<!--Bet history-->
<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_waiting">Waiting…</string>
<string name="place_your_bets">Place your bets</string>
<string name="bet_status_participants_list">Participants</string>
<!--Bet history-->
<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.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.ResponseBetDetail
import fr.iut.alldev.allin.data.api.model.ResponseUser
import retrofit2.http.Body
import retrofit2.http.GET
import retrofit2.http.Header
import retrofit2.http.POST
import retrofit2.http.Path
interface AllInApi {
companion object {
fun String.formatBearerToken() = "Bearer $this"
}
@POST("users/login")
suspend fun login(@Body body: CheckUser): ResponseUser
@ -25,4 +32,10 @@ interface AllInApi {
@GET("bets/gets")
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.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.ResponseParticipation
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.YES_VALUE
@ -12,13 +16,30 @@ import java.time.ZonedDateTime
import java.util.UUID
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 {
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.")
}
override suspend fun login(token: String): ResponseUser {
return users.find { it.first.token == token }?.first
return getUserFromToken(token)?.first
?: throw AllInAPIException("Invalid token")
}
@ -27,10 +48,10 @@ class MockAllInApi : AllInApi {
id = UUID.randomUUID().toString(),
username = body.username,
email = body.email,
nbCoins = body.nbCoins,
token = "${body.username} ${users.size}"
nbCoins = 500,
token = "${body.username} ${mockUsers.size}"
) to body.password
users.add(response)
mockUsers.add(response)
return response.first
}
@ -50,16 +71,138 @@ class MockAllInApi : AllInApi {
}
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(
id = "UUID 1",
username = "User 1",
email = "john@doe.fr",
nbCoins = 250,
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(

@ -14,14 +14,15 @@ class ErrorInterceptor : Interceptor {
override fun intercept(chain: Interceptor.Chain): Response {
val request = chain.request()
val response = chain.proceed(request)
if(!response.isSuccessful){
if (!response.isSuccessful) {
when (response.code) {
404 -> throw AllInNotFoundException(response.message)
401 -> throw AllInUnauthorizedException(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)
}

@ -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.YES_VALUE
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 java.time.ZonedDateTime
@ -17,8 +19,8 @@ data class ResponseBet(
val id: String?,
val theme: String,
val sentenceBet: String,
@Serializable(SimpleDateSerializer::class) val endRegistration: ZonedDateTime,
@Serializable(SimpleDateSerializer::class) var endBet: ZonedDateTime,
@Serializable(ZonedDateTimeSerializer::class) val endRegistration: ZonedDateTime,
@Serializable(ZonedDateTimeSerializer::class) var endBet: ZonedDateTime,
var isPrivate: Boolean,
var response: List<String>,
val createdBy: String
@ -26,6 +28,7 @@ data class ResponseBet(
fun toBet(): Bet {
if (response.toSet() == setOf(YES_VALUE, NO_VALUE)) {
return YesNoBet(
id = id ?: "",
theme = theme,
phrase = sentenceBet,
endRegisterDate = endRegistration,
@ -36,6 +39,7 @@ data class ResponseBet(
)
} else {
return CustomBet(
id = id ?: "",
theme = theme,
phrase = sentenceBet,
endRegisterDate = endRegistration,
@ -52,10 +56,48 @@ data class ResponseBet(
@Keep
@Serializable
data class RequestBet(
val id: String = "",
val theme: String,
val sentenceBet: String,
@Serializable(SimpleDateSerializer::class) val endRegistration: ZonedDateTime,
@Serializable(SimpleDateSerializer::class) var endBet: ZonedDateTime,
@Serializable(ZonedDateTimeSerializer::class) val endRegistration: ZonedDateTime,
@Serializable(ZonedDateTimeSerializer::class) var endBet: ZonedDateTime,
var isPrivate: Boolean,
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(
val username: String,
val email: String,
val password: String,
var nbCoins: Int,
val password: String
)
@Keep
@Serializable
data class ResponseUser(
val id: String,
val id: String = "",
val username: String,
val email: String,
var nbCoins: Int,
var token: String? = null,
var token: String? = null
) {
fun toUser() = User(
id = id,
username = username,
email = email,
coins = nbCoins
coins = nbCoins.toInt()
)
}

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

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

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

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

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

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

@ -3,7 +3,7 @@ package fr.iut.alldev.allin.data.model.bet
import java.time.ZonedDateTime
data class MatchBet(
override val id: String? = null,
override val id: String,
override val creator: String,
override val theme: 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"
data class YesNoBet(
override val id: String? = null,
override val id: String,
override val creator: String,
override val theme: 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
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
abstract class BetRepository {
abstract suspend fun createBet(bet: Bet, token: String)
abstract suspend fun getHistory(): 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>>
}

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

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

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

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