Fix mock API
continuous-integration/drone/push Build is passing Details

pull/5/head
avalin 10 months ago
parent c32c4ab561
commit b83c0686c6

@ -320,7 +320,8 @@ private fun BetConfirmationBottomSheetContentPreview() {
) )
), ),
participations = emptyList(), participations = emptyList(),
userParticipation = null userParticipation = null,
wonParticipation = null
), ),
onConfirm = { } onConfirm = { }
) { ) {

@ -8,10 +8,12 @@ import androidx.hilt.navigation.compose.hiltViewModel
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.ui.betHistory.components.GenericHistory import fr.iut.alldev.allin.ui.betHistory.components.GenericHistory
@Composable @Composable
fun BetCurrentScreen( fun BetCurrentScreen(
selectBet: (Bet, Boolean) -> Unit,
viewModel: BetCurrentViewModel = hiltViewModel() viewModel: BetCurrentViewModel = hiltViewModel()
) { ) {
val bets by viewModel.bets.collectAsState() val bets by viewModel.bets.collectAsState()
@ -25,6 +27,7 @@ fun BetCurrentScreen(
getEndBetTime = { it.bet.endBetDate.formatToTime() }, getEndBetTime = { it.bet.endBetDate.formatToTime() },
getStatus = { it.bet.betStatus }, getStatus = { it.bet.betStatus },
getNbCoins = { it.userParticipation?.stake ?: 0 }, getNbCoins = { it.userParticipation?.stake ?: 0 },
getWon = { true } getWon = { true },
onClick = { selectBet(it.bet, false) },
) )
} }

@ -37,7 +37,7 @@ class BetCurrentViewModel @Inject constructor(
init { init {
viewModelScope.launch { viewModelScope.launch {
_bets.emit( _bets.emit(
betRepository.getToConfirm(keystoreManager.getTokenOrEmpty()) betRepository.getCurrentBets(keystoreManager.getTokenOrEmpty())
) )
} }
} }

@ -8,10 +8,12 @@ import androidx.hilt.navigation.compose.hiltViewModel
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.ui.betHistory.components.GenericHistory import fr.iut.alldev.allin.ui.betHistory.components.GenericHistory
@Composable @Composable
fun BetHistoryScreen( fun BetHistoryScreen(
selectBet: (Bet, Boolean) -> Unit,
viewModel: BetHistoryViewModel = hiltViewModel() viewModel: BetHistoryViewModel = hiltViewModel()
) { ) {
val bets by viewModel.bets.collectAsState() val bets by viewModel.bets.collectAsState()
@ -25,6 +27,7 @@ fun BetHistoryScreen(
getEndBetTime = { it.bet.endBetDate.formatToTime() }, getEndBetTime = { it.bet.endBetDate.formatToTime() },
getStatus = { it.bet.betStatus }, getStatus = { it.bet.betStatus },
getNbCoins = { it.amount }, getNbCoins = { it.amount },
getWon = { it.won } getWon = { it.won },
onClick = { selectBet(it.bet, false) }
) )
} }

@ -13,6 +13,7 @@ import fr.iut.alldev.allin.ui.preview.BetStatusPreviewProvider
@Composable @Composable
fun BetHistoryScreenCard( fun BetHistoryScreenCard(
modifier: Modifier = Modifier, modifier: Modifier = Modifier,
onClick: () -> Unit,
title: String, title: String,
creator: String, creator: String,
category: String, category: String,
@ -29,6 +30,7 @@ fun BetHistoryScreenCard(
date = date, date = date,
time = time, time = time,
status = status, status = status,
onClick = onClick,
modifier = modifier modifier = modifier
) { ) {
BetHistoryBetStatus( BetHistoryBetStatus(
@ -47,6 +49,7 @@ private fun BetHistoryScreenCardPreview(
) { ) {
AllInTheme { AllInTheme {
BetHistoryScreenCard( BetHistoryScreenCard(
onClick = {},
creator = "Creator", creator = "Creator",
category = "Category", category = "Category",
title = "Title", title = "Title",

@ -30,6 +30,7 @@ fun <T> GenericHistory(
getStatus: (T) -> BetStatus, getStatus: (T) -> BetStatus,
getNbCoins: (T) -> Int, getNbCoins: (T) -> Int,
getWon: (T) -> Boolean, getWon: (T) -> Boolean,
onClick: (T) -> Unit,
) { ) {
LazyColumn( LazyColumn(
modifier = Modifier.fillMaxSize(), modifier = Modifier.fillMaxSize(),
@ -56,7 +57,8 @@ fun <T> GenericHistory(
time = getEndBetTime(it), time = getEndBetTime(it),
status = getStatus(it), status = getStatus(it),
nbCoins = getNbCoins(it), nbCoins = getNbCoins(it),
won = getWon(it) won = getWon(it),
onClick = { onClick(it) }
) )
} }
} }

@ -38,7 +38,7 @@ fun BetStatusWinner(
modifier = Modifier modifier = Modifier
.fillMaxWidth() .fillMaxWidth()
.background(color.copy(alpha = .2f)) .background(color.copy(alpha = .2f))
.padding(vertical = 20.dp) .padding(20.dp)
) { ) {
AllInTextIcon( AllInTextIcon(
text = answer, text = answer,

@ -85,6 +85,7 @@ class BetStatusBottomSheetBetDisplayer(
private fun DisplayBet( private fun DisplayBet(
betDetail: BetDetail, betDetail: BetDetail,
currentUser: User, currentUser: User,
winnerColor: @Composable () -> Color,
statBar: LazyListScope.() -> Unit statBar: LazyListScope.() -> Unit
) { ) {
Box(Modifier) { Box(Modifier) {
@ -117,13 +118,16 @@ class BetStatusBottomSheetBetDisplayer(
Spacer(modifier = Modifier.height(20.dp)) Spacer(modifier = Modifier.height(20.dp))
} }
if (betDetail.bet.betStatus == BetStatus.FINISHED) { if (betDetail.bet.betStatus == BetStatus.FINISHED) {
betDetail.wonParticipation?.let { wonParticipation ->
BetStatusWinner( BetStatusWinner(
answer = YES_VALUE, answer = wonParticipation.response,
color = AllInColorToken.allInBlue, color = winnerColor(),
coinAmount = 442, coinAmount = wonParticipation.stake,
username = "Imri", username = wonParticipation.username,
multiplier = 1.2f multiplier = betDetail.getAnswerOfResponse(wonParticipation.response)
?.odds ?: .5f
) )
}
} else { } else {
HorizontalDivider(color = AllInTheme.colors.border) HorizontalDivider(color = AllInTheme.colors.border)
} }
@ -301,13 +305,20 @@ class BetStatusBottomSheetBetDisplayer(
@Composable @Composable
override fun DisplayYesNoBet(betDetail: BetDetail, currentUser: User) { override fun DisplayYesNoBet(betDetail: BetDetail, currentUser: User) {
DisplayBet(betDetail = betDetail, currentUser = currentUser) { DisplayBet(
betDetail = betDetail,
currentUser = currentUser,
winnerColor = {
if (betDetail.wonParticipation?.response == YES_VALUE) AllInColorToken.allInBlue
else AllInColorToken.allInPink
}
) {
displayBinaryStatBar( displayBinaryStatBar(
betDetail = betDetail, betDetail = betDetail,
response1 = YES_VALUE, response1 = YES_VALUE,
response2 = NO_VALUE, response2 = NO_VALUE,
response1Display = { stringResource(id = R.string.Yes).uppercase() }, response1Display = { stringResource(id = R.string.Yes).uppercase() },
response2Display = { stringResource(id = R.string.No).uppercase() } response2Display = { stringResource(id = R.string.No).uppercase() },
) )
} }
} }
@ -316,7 +327,14 @@ class BetStatusBottomSheetBetDisplayer(
override fun DisplayMatchBet(betDetail: BetDetail, currentUser: User) { override fun DisplayMatchBet(betDetail: BetDetail, currentUser: User) {
val matchBet = remember { betDetail.bet as MatchBet } val matchBet = remember { betDetail.bet as MatchBet }
DisplayBet(betDetail = betDetail, currentUser = currentUser) { DisplayBet(
betDetail = betDetail,
currentUser = currentUser,
winnerColor = {
if (betDetail.wonParticipation?.response == matchBet.nameTeam1) AllInColorToken.allInBlue
else AllInColorToken.allInPink
}
) {
displayBinaryStatBar( displayBinaryStatBar(
betDetail = betDetail, betDetail = betDetail,
response1 = matchBet.nameTeam1, response1 = matchBet.nameTeam1,
@ -331,7 +349,18 @@ class BetStatusBottomSheetBetDisplayer(
val configuration = LocalConfiguration.current val configuration = LocalConfiguration.current
val locale = remember { ConfigurationCompat.getLocales(configuration).get(0) ?: Locale.getDefault() } val locale = remember { ConfigurationCompat.getLocales(configuration).get(0) ?: Locale.getDefault() }
DisplayBet(betDetail = betDetail, currentUser = currentUser) { DisplayBet(
betDetail = betDetail,
currentUser = currentUser,
winnerColor = {
val isBinary = remember { customBet.possibleAnswers.size == 2 }
if (isBinary) {
if (betDetail.wonParticipation?.response == customBet.possibleAnswers.first()) AllInColorToken.allInBlue
else AllInColorToken.allInPink
} else AllInTheme.colors.onMainSurface
}
) {
if (customBet.possibleAnswers.size == 2) { if (customBet.possibleAnswers.size == 2) {
displayBinaryStatBar( displayBinaryStatBar(
betDetail = betDetail, betDetail = betDetail,

@ -17,6 +17,7 @@ import androidx.compose.ui.graphics.painter.Painter
import androidx.compose.ui.graphics.vector.rememberVectorPainter import androidx.compose.ui.graphics.vector.rememberVectorPainter
import androidx.compose.ui.platform.LocalLayoutDirection import androidx.compose.ui.platform.LocalLayoutDirection
import androidx.compose.ui.text.TextStyle import androidx.compose.ui.text.TextStyle
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.unit.LayoutDirection import androidx.compose.ui.unit.LayoutDirection
import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.dp
@ -57,7 +58,10 @@ fun AllInTextIcon(
text = text, text = text,
color = color, color = color,
style = brush?.let { textStyle.copy(brush = it) } ?: textStyle, style = brush?.let { textStyle.copy(brush = it) } ?: textStyle,
fontSize = size.sp fontSize = size.sp,
maxLines = 1,
overflow = TextOverflow.Ellipsis,
modifier = Modifier.weight(1f, fill = false)
) )
Icon( Icon(
painter = icon, painter = icon,

@ -16,6 +16,7 @@ import androidx.compose.ui.unit.dp
import fr.iut.alldev.allin.data.model.bet.BetStatus import fr.iut.alldev.allin.data.model.bet.BetStatus
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.core.AllInBouncyCard
import fr.iut.alldev.allin.ui.core.AllInCard import fr.iut.alldev.allin.ui.core.AllInCard
@Composable @Composable
@ -57,6 +58,47 @@ fun BetCard(
} }
} }
@Composable
fun BetCard(
modifier: Modifier = Modifier,
title: String,
creator: String,
category: String,
date: String,
time: String,
status: BetStatus,
onClick: () -> Unit,
content: @Composable () -> Unit
) {
AllInBouncyCard(
modifier = modifier.fillMaxWidth(),
radius = 16.dp,
onClick = onClick
) {
Column(
Modifier.padding(horizontal = 19.dp, vertical = 11.dp)
) {
BetTitleHeader(
title = title,
category = category,
creator = creator,
modifier = Modifier.fillMaxWidth()
)
Spacer(modifier = Modifier.height(11.dp))
BetDateTimeRow(
label = stringResource(id = status.getDateStartLabelId()),
date = date,
time = time
)
}
HorizontalDivider(
thickness = 1.dp,
color = AllInTheme.colors.border
)
content()
}
}
@Preview @Preview
@Preview(uiMode = Configuration.UI_MODE_NIGHT_YES) @Preview(uiMode = Configuration.UI_MODE_NIGHT_YES)
@Composable @Composable

@ -1,5 +1,10 @@
package fr.iut.alldev.allin.ui.core.topbar package fr.iut.alldev.allin.ui.core.topbar
import androidx.compose.animation.AnimatedContent
import androidx.compose.animation.core.tween
import androidx.compose.animation.slideInVertically
import androidx.compose.animation.slideOutVertically
import androidx.compose.animation.togetherWith
import androidx.compose.foundation.background import androidx.compose.foundation.background
import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Row import androidx.compose.foundation.layout.Row
@ -10,6 +15,11 @@ import androidx.compose.material3.Card
import androidx.compose.material3.Icon import androidx.compose.material3.Icon
import androidx.compose.material3.Text import androidx.compose.material3.Text
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableIntStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.setValue
import androidx.compose.ui.Alignment import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color import androidx.compose.ui.graphics.Color
@ -27,6 +37,14 @@ fun AllInTopBarCoinCounter(
textColor: Color = AllInColorToken.allInDark, textColor: Color = AllInColorToken.allInDark,
iconColor: Color = AllInColorToken.allInBlue, iconColor: Color = AllInColorToken.allInBlue,
) { ) {
var oldAmount by remember { mutableIntStateOf(amount) }
LaunchedEffect(amount) {
oldAmount = amount
}
val countString = remember(amount) { amount.toString() }
val oldCountString = remember(oldAmount) { oldAmount.toString() }
Card( Card(
modifier = modifier.wrapContentSize(), modifier = modifier.wrapContentSize(),
shape = RoundedCornerShape(topStartPercent = 50, bottomStartPercent = 50) shape = RoundedCornerShape(topStartPercent = 50, bottomStartPercent = 50)
@ -34,20 +52,45 @@ fun AllInTopBarCoinCounter(
Row( Row(
modifier = Modifier modifier = Modifier
.background(backgroundColor) .background(backgroundColor)
.padding(horizontal = 13.dp, vertical = 5.dp), .padding(horizontal = 13.dp),
horizontalArrangement = Arrangement.spacedBy(7.dp), horizontalArrangement = Arrangement.spacedBy(7.dp),
verticalAlignment = Alignment.CenterVertically verticalAlignment = Alignment.CenterVertically
) { ) {
Row {
for (i in countString.indices) {
val oldChar = oldCountString.getOrNull(i)
val newChar = countString[i]
val char = if (oldChar == newChar) {
oldCountString[i]
} else {
countString[i]
}
AnimatedContent(
targetState = char,
transitionSpec = {
val delayMillis = (countString.indices.count() - i) * 50
(slideInVertically(tween(delayMillis)) { it } togetherWith
slideOutVertically(tween(delayMillis)) { -it })
},
label = ""
) { char ->
Text( Text(
text = amount.toString(), text = char.toString(),
color = textColor,
style = AllInTheme.typography.h1, style = AllInTheme.typography.h1,
fontSize = 20.sp color = textColor,
fontSize = 20.sp,
softWrap = false,
modifier = Modifier.padding(vertical = 5.dp)
) )
}
}
}
Icon( Icon(
painter = AllInTheme.icons.allCoins(), painter = AllInTheme.icons.allCoins(),
tint = iconColor, tint = iconColor,
contentDescription = null, contentDescription = null,
modifier = Modifier.padding(vertical = 5.dp)
) )
} }
} }

@ -72,7 +72,6 @@ fun MainScreen(
val (loading, setLoading) = remember { mainViewModel.loading } val (loading, setLoading) = remember { mainViewModel.loading }
val currentUser by mainViewModel.currentUser.collectAsStateWithLifecycle() val currentUser by mainViewModel.currentUser.collectAsStateWithLifecycle()
val userCoins by mainViewModel.userCoins.collectAsStateWithLifecycle()
val selectedBet by remember { mainViewModel.selectedBet } val selectedBet by remember { mainViewModel.selectedBet }
val statusVisibility = remember { mutableStateOf(false) } val statusVisibility = remember { mutableStateOf(false) }
val sheetBackVisibility = remember { mutableStateOf(false) } val sheetBackVisibility = remember { mutableStateOf(false) }
@ -141,7 +140,7 @@ fun MainScreen(
) { ) {
AllInScaffold( AllInScaffold(
onMenuClicked = { scope.launch { drawerState.open() } }, onMenuClicked = { scope.launch { drawerState.open() } },
coinAmount = userCoins ?: 0, coinAmount = currentUser?.coins ?: 0,
drawerState = drawerState, drawerState = drawerState,
snackbarHostState = snackbarHostState snackbarHostState = snackbarHostState
) { ) {
@ -200,6 +199,7 @@ fun MainScreen(
scope.launch { scope.launch {
eventBottomSheetState.hide() eventBottomSheetState.hide()
delay(EVENT_DISMISS_DELAY_MS) delay(EVENT_DISMISS_DELAY_MS)
mainViewModel.increaseCoins(event.betResult.amount)
events.removeFirstOrNull() events.removeFirstOrNull()
} }
} }
@ -215,6 +215,7 @@ fun MainScreen(
(events.firstOrNull() as? DailyReward)?.let { (events.firstOrNull() as? DailyReward)?.let {
it.Display( it.Display(
onDismiss = { onDismiss = {
mainViewModel.increaseCoins(it.amount)
dailyRewardVisible = false dailyRewardVisible = false
mainViewModel.dismissedEvents += it mainViewModel.dismissedEvents += it
scope.launch { scope.launch {
@ -238,7 +239,7 @@ fun MainScreen(
displayBet = { detail -> displayBet = { detail ->
currentUser?.let { user -> betStatusDisplayer.DisplayBet(betDetail = detail, currentUser = user) } currentUser?.let { user -> betStatusDisplayer.DisplayBet(betDetail = detail, currentUser = user) }
}, },
userCoinAmount = userCoins ?: 0, userCoinAmount = currentUser?.coins ?: 0,
onParticipate = { stake, response -> mainViewModel.participateToBet(stake, response) }, onParticipate = { stake, response -> mainViewModel.participateToBet(stake, response) },
participateSheetVisibility = participateSheetVisibility, participateSheetVisibility = participateSheetVisibility,
setParticipateSheetVisibility = setParticipateSheetVisibility setParticipateSheetVisibility = setParticipateSheetVisibility

@ -18,7 +18,6 @@ import fr.iut.alldev.allin.ui.main.event.DailyReward
import fr.iut.alldev.allin.ui.main.event.ToConfirmBet import fr.iut.alldev.allin.ui.main.event.ToConfirmBet
import fr.iut.alldev.allin.ui.main.event.WonBet import fr.iut.alldev.allin.ui.main.event.WonBet
import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.flow.asStateFlow
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext import kotlinx.coroutines.withContext
import javax.inject.Inject import javax.inject.Inject
@ -32,8 +31,7 @@ class MainViewModel @Inject constructor(
var loading = mutableStateOf(false) var loading = mutableStateOf(false)
val currentUser = userRepository.currentUser.asStateFlow() val currentUser = userRepository.currentUser
val userCoins = userRepository.userCoins.asStateFlow()
val selectedBet = mutableStateOf<BetDetail?>(null) val selectedBet = mutableStateOf<BetDetail?>(null)
val dismissedEvents = mutableStateListOf<AllInEvent>() val dismissedEvents = mutableStateListOf<AllInEvent>()
val events = mutableStateListOf<AllInEvent>() val events = mutableStateListOf<AllInEvent>()
@ -53,8 +51,11 @@ class MainViewModel @Inject constructor(
val token = keystoreManager.getTokenOrEmpty() val token = keystoreManager.getTokenOrEmpty()
events.addAll( events.addAll(
buildList { buildList {
add(DailyReward(125)) runCatching {
add(DailyReward(userRepository.dailyGift(token)))
}
runCatching {
addAll(betRepository.getToConfirm(token).map { bet -> addAll(betRepository.getToConfirm(token).map { bet ->
ToConfirmBet( ToConfirmBet(
betDetail = bet, betDetail = bet,
@ -66,6 +67,9 @@ class MainViewModel @Inject constructor(
} }
) )
}) })
}
runCatching {
addAll(betRepository.getWon(token).mapNotNull { result -> addAll(betRepository.getWon(token).mapNotNull { result ->
currentUser.value?.let { currentUser.value?.let {
WonBet( WonBet(
@ -74,6 +78,7 @@ class MainViewModel @Inject constructor(
) )
} }
}) })
}
}.filter { it !in dismissedEvents } }.filter { it !in dismissedEvents }
) )
} }
@ -90,17 +95,23 @@ class MainViewModel @Inject constructor(
fun onLogout() { fun onLogout() {
viewModelScope.launch { viewModelScope.launch {
keystoreManager.deleteToken() keystoreManager.deleteToken()
userRepository.currentUser.emit(null) userRepository.resetCurrentUser()
userRepository.userCoins.emit(null)
} }
} }
private fun decreaseCoins(amount: Int) {
fun increaseCoins(amount: Int) {
viewModelScope.launch { viewModelScope.launch {
userRepository.userCoins.value?.let { currentUser.value?.let {
val newAmount = it - amount userRepository.updateCurrentUserCoins(it.coins + amount)
userRepository.userCoins.emit(newAmount) }
}
}
private fun decreaseCoins(amount: Int) {
viewModelScope.launch {
currentUser.value?.let {
userRepository.updateCurrentUserCoins(it.coins - amount)
} }
} }
} }

@ -3,7 +3,7 @@ package fr.iut.alldev.allin.ui.main.event
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
import fr.iut.alldev.allin.ui.dailyReward.DailyRewardScreen import fr.iut.alldev.allin.ui.dailyReward.DailyRewardScreen
data class DailyReward(private val amount: Int) : AllInEvent() { data class DailyReward(val amount: Int) : AllInEvent() {
@Composable @Composable
fun Display(onDismiss: () -> Unit) { fun Display(onDismiss: () -> Unit) {
DailyRewardScreen( DailyRewardScreen(

@ -8,7 +8,7 @@ import fr.iut.alldev.allin.ui.betResult.BetResultBottomSheet
data class WonBet( data class WonBet(
private val user: User, private val user: User,
private val betResult: BetResultDetail, val betResult: BetResultDetail,
) : AllInEvent() { ) : AllInEvent() {
@Composable @Composable
fun Display( fun Display(

@ -137,7 +137,7 @@ internal fun AllInDrawerNavHost(
route = Routes.BET_HISTORY route = Routes.BET_HISTORY
) { ) {
backHandlers() backHandlers()
BetHistoryScreen() BetHistoryScreen(selectBet = selectBet)
} }
composable( composable(
@ -151,7 +151,7 @@ internal fun AllInDrawerNavHost(
route = Routes.BET_CURRENT route = Routes.BET_CURRENT
) { ) {
backHandlers() backHandlers()
BetCurrentScreen() BetCurrentScreen(selectBet = selectBet)
} }
} }
} }

@ -12,9 +12,8 @@ import fr.iut.alldev.allin.data.model.bet.vo.BetDetail
class BetDetailPreviewProvider : PreviewParameterProvider<BetDetail> { class BetDetailPreviewProvider : PreviewParameterProvider<BetDetail> {
override val values = BetWithStatusPreviewProvider().values.map { override val values = BetWithStatusPreviewProvider().values.map {
BetDetail(
bet = it, val answers = when (it) {
answers = when (it) {
is CustomBet -> listOf( is CustomBet -> listOf(
BetAnswerDetail( BetAnswerDetail(
response = "Answer 1", response = "Answer 1",
@ -77,33 +76,37 @@ class BetDetailPreviewProvider : PreviewParameterProvider<BetDetail> {
odds = 2.0f odds = 2.0f
) )
) )
}, }
BetDetail(
bet = it,
answers = answers,
participations = listOf( participations = listOf(
Participation( Participation(
betId = it.id, betId = it.id,
username = "User1", username = "User1",
response = YES_VALUE, response = answers.first().response,
stake = 200
),
Participation(
betId = it.id,
username = "User2",
response = YES_VALUE,
stake = 100 stake = 100
), ),
Participation( Participation(
betId = it.id, betId = it.id,
username = "MyUser", username = "User 2",
response = NO_VALUE, response = answers.last().response,
stake = 150 stake = 150
) )
), ),
userParticipation = null /*Participation( userParticipation = Participation(
betId = it.id, betId = it.id,
username = "MyUser", username = "User1",
response = NO_VALUE, response = answers.first().response,
stake = 150 stake = 100
)*/ ),
wonParticipation = Participation(
betId = it.id,
username = "User1",
response = answers.first().response,
stake = 100
)
) )
} }
} }

@ -1,6 +1,7 @@
package fr.iut.alldev.allin.data.api 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.interceptors.AllInUnauthorizedException
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.RequestParticipation
@ -8,6 +9,7 @@ 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.ResponseBetAnswerDetail
import fr.iut.alldev.allin.data.api.model.ResponseBetDetail import fr.iut.alldev.allin.data.api.model.ResponseBetDetail
import fr.iut.alldev.allin.data.api.model.ResponseBetResult
import fr.iut.alldev.allin.data.api.model.ResponseBetResultDetail import fr.iut.alldev.allin.data.api.model.ResponseBetResultDetail
import fr.iut.alldev.allin.data.api.model.ResponseParticipation import fr.iut.alldev.allin.data.api.model.ResponseParticipation
import fr.iut.alldev.allin.data.api.model.ResponseUser import fr.iut.alldev.allin.data.api.model.ResponseUser
@ -15,12 +17,39 @@ import fr.iut.alldev.allin.data.model.bet.BetStatus
import fr.iut.alldev.allin.data.model.bet.BetType import fr.iut.alldev.allin.data.model.bet.BetType
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.vo.BetResult import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.delay
import kotlinx.coroutines.launch
import java.time.Duration
import java.time.ZonedDateTime import java.time.ZonedDateTime
import java.util.UUID import java.util.UUID
class MockAllInApi : AllInApi { class MockAllInApi : AllInApi {
init {
CoroutineScope(Dispatchers.Default).launch {
while (true) {
delay(10_000)
updateBets(ZonedDateTime.now())
}
}
}
private fun updateBets(date: ZonedDateTime) {
mockBets.forEachIndexed { idx, bet ->
if (bet.status != BetStatus.CANCELLED && bet.status != BetStatus.FINISHED) {
if (date >= bet.endRegistration) {
if (date >= bet.endBet) {
mockBets[idx] = bet.copy(status = BetStatus.CLOSING)
} else {
mockBets[idx] = bet.copy(status = BetStatus.IN_PROGRESS)
}
}
}
}
}
private fun getUserFromToken(token: String) = private fun getUserFromToken(token: String) =
mockUsers.find { it.first.token == token.removePrefix("Bearer ") } mockUsers.find { it.first.token == token.removePrefix("Bearer ") }
@ -79,6 +108,31 @@ class MockAllInApi : AllInApi {
) )
} }
override suspend fun dailyGift(token: String): Int {
getUserFromToken(token) ?: throw AllInAPIException("Invalid login/password.")
if (mockGifts[token] == null) {
mockGifts[token] = ZonedDateTime.now().minusDays(1)
}
val duration = Duration.between(mockGifts[token], ZonedDateTime.now())
if (duration.toDays() >= 1) {
val amount = (10..150).random()
mockGifts[token] = ZonedDateTime.now()
mockUsers.replaceAll {
if (it.first.token == token) {
it.copy(
first = it.first.copy(
nbCoins = it.first.nbCoins + amount
)
)
} else it
}
return amount
} else throw AllInUnauthorizedException("Gift already taken today")
}
override suspend fun getAllBets(token: String): List<ResponseBet> { override suspend fun getAllBets(token: String): List<ResponseBet> {
getUserFromToken(token) ?: throw AllInAPIException("Invalid login/password.") getUserFromToken(token) ?: throw AllInAPIException("Invalid login/password.")
return mockBets return mockBets
@ -105,7 +159,7 @@ class MockAllInApi : AllInApi {
getUserFromToken(token) ?: throw AllInAPIException("Invalid login/password.") getUserFromToken(token) ?: throw AllInAPIException("Invalid login/password.")
val bet = mockBets.find { it.id == id } ?: throw AllInAPIException("Unauthorized") val bet = mockBets.find { it.id == id } ?: throw AllInAPIException("Unauthorized")
mockResults.add( mockResults.add(
BetResult( ResponseBetResult(
betId = id, betId = id,
result = value result = value
) )
@ -118,25 +172,72 @@ class MockAllInApi : AllInApi {
val user = getUserFromToken(token) ?: throw AllInAPIException("Invalid login/password.") val user = getUserFromToken(token) ?: throw AllInAPIException("Invalid login/password.")
val betParticipations = mockParticipations.filter { it.betId == bet.id } val betParticipations = mockParticipations.filter { it.betId == bet.id }
val userParticipation = betParticipations.find { it.username == user.first.username } val userParticipation = betParticipations.find { it.username == user.first.username }
val wonParticipation = if (bet.status == BetStatus.FINISHED) {
val result = mockResults.find { it.betId == bet.id }
betParticipations.filter { it.answer == result?.result }.maxByOrNull { it.stake }
} else null
return ResponseBetDetail( return ResponseBetDetail(
bet = bet, bet = bet,
answers = getAnswerDetails(bet, betParticipations), answers = getAnswerDetails(bet, betParticipations),
participations = betParticipations, participations = betParticipations,
userParticipation = userParticipation userParticipation = userParticipation,
wonParticipation = wonParticipation
) )
} }
override suspend fun getBetCurrent(token: String): List<ResponseBetDetail> { override suspend fun getBetCurrent(token: String): List<ResponseBetDetail> {
return emptyList() val user = getUserFromToken(token) ?: throw AllInAPIException("Invalid login/password.")
val betParticipations = mockParticipations.filter { it.username == user.first.username }
return betParticipations.map { p ->
getBet(token, p.betId)
}.filter {
it.bet.status !in listOf(BetStatus.FINISHED, BetStatus.CANCELLED)
}
} }
override suspend fun getBetHistory(token: String): List<ResponseBetResultDetail> { override suspend fun getBetHistory(token: String): List<ResponseBetResultDetail> {
return emptyList() val user = getUserFromToken(token) ?: throw AllInAPIException("Invalid login/password.")
val betParticipations = mockParticipations.filter { it.username == user.first.username }
return betParticipations.map { p ->
getBet(token, p.betId)
}.filter {
it.bet.status in listOf(BetStatus.FINISHED, BetStatus.CANCELLED)
}.mapNotNull { bet ->
val participation = bet.userParticipation ?: return@mapNotNull null
val result = mockResults.find { it.betId == bet.bet.id } ?: return@mapNotNull null
ResponseBetResultDetail(
betResult = result,
bet = bet.bet,
participation = participation,
amount = participation.stake, // TODO won amount
won = participation.answer == result.result
)
}
} }
override suspend fun getWon(token: String): List<ResponseBetResultDetail> { override suspend fun getWon(token: String): List<ResponseBetResultDetail> {
return emptyList() return getBetHistory(token).filter {
val isWon = it.won && mockWon[token]?.contains(it.bet.id ?: "") != true
if (isWon) {
mockWon[token]?.add(it.bet.id ?: "") ?: run {
mockWon[token] = mutableListOf(it.bet.id ?: "")
}
mockUsers.replaceAll { user ->
if (user.first.token == token) {
user.copy(
first = user.first.copy(
nbCoins = user.first.nbCoins + it.amount
)
)
} else user
}
true
} else false
}
} }
override suspend fun participateToBet(token: String, body: RequestParticipation) { override suspend fun participateToBet(token: String, body: RequestParticipation) {
@ -340,7 +441,11 @@ class MockAllInApi : AllInApi {
) )
) )
private val mockResults by lazy { mutableListOf<BetResult>() } private val mockResults by lazy { mutableListOf<ResponseBetResult>() }
private val mockWon by lazy { mutableMapOf<String, MutableList<String>>() }
private val mockGifts by lazy { mutableMapOf<String, ZonedDateTime>() }
} }
} }

@ -31,6 +31,9 @@ interface AllInApi {
@POST("bets/add") @POST("bets/add")
suspend fun createBet(@Header("Authorization") token: String, @Body body: RequestBet) suspend fun createBet(@Header("Authorization") token: String, @Body body: RequestBet)
@GET("users/gift")
suspend fun dailyGift(@Header("Authorization") token: String): Int
@GET("bets/gets") @GET("bets/gets")
suspend fun getAllBets(@Header("Authorization") token: String): List<ResponseBet> suspend fun getAllBets(@Header("Authorization") token: String): List<ResponseBet>

@ -115,15 +115,16 @@ data class ResponseBetDetail(
val bet: ResponseBet, val bet: ResponseBet,
val answers: List<ResponseBetAnswerDetail>, val answers: List<ResponseBetAnswerDetail>,
val participations: List<ResponseParticipation>, val participations: List<ResponseParticipation>,
val userParticipation: ResponseParticipation? = null val userParticipation: ResponseParticipation? = null,
val wonParticipation: ResponseParticipation? = null
) { ) {
fun toBetDetail() = fun toBetDetail() =
BetDetail( BetDetail(
bet = bet.toBet(), bet = bet.toBet(),
answers = answers.map { it.toAnswerDetail() }, answers = answers.map { it.toAnswerDetail() },
participations = participations.map { it.toParticipation() }, participations = participations.map { it.toParticipation() },
userParticipation = userParticipation?.toParticipation() userParticipation = userParticipation?.toParticipation(),
wonParticipation = wonParticipation?.toParticipation()
) )
} }

@ -7,7 +7,8 @@ data class BetDetail(
val bet: Bet, val bet: Bet,
val answers: List<BetAnswerDetail>, val answers: List<BetAnswerDetail>,
val participations: List<Participation>, val participations: List<Participation>,
val userParticipation: Participation? val userParticipation: Participation?,
val wonParticipation: Participation?
) { ) {
fun getAnswerOfResponse(response: String) = fun getAnswerOfResponse(response: String) =
answers.find { it.response == response } answers.find { it.response == response }

@ -2,15 +2,25 @@ package fr.iut.alldev.allin.data.repository
import fr.iut.alldev.allin.data.model.User import fr.iut.alldev.allin.data.model.User
import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.asStateFlow
abstract class UserRepository { abstract class UserRepository {
val currentUser by lazy { MutableStateFlow<User?>(null) } internal val _currentUser by lazy { MutableStateFlow<User?>(null) }
val userCoins by lazy { MutableStateFlow<Int?>(null) } val currentUser by lazy { _currentUser.asStateFlow() }
internal suspend fun updateUser(user: User) { suspend fun resetCurrentUser() {
currentUser.emit(user) _currentUser.emit(null)
userCoins.emit(user.coins) }
suspend fun updateCurrentUserCoins(value: Int) {
currentUser.value?.let { user ->
_currentUser.emit(
user.copy(
coins = value
)
)
}
} }
abstract suspend fun login(username: String, password: String): String? abstract suspend fun login(username: String, password: String): String?
@ -18,4 +28,5 @@ abstract class UserRepository {
abstract suspend fun login(token: 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?
abstract suspend fun dailyGift(token: String): Int
} }

@ -26,7 +26,7 @@ class BetRepositoryImpl @Inject constructor(
} }
override suspend fun getCurrentBets(token: String): List<BetDetail> { override suspend fun getCurrentBets(token: String): List<BetDetail> {
return api.getToConfirm(token.formatBearerToken()).map { return api.getBetCurrent(token.formatBearerToken()).map {
it.toBetDetail() it.toBetDetail()
} }
} }
@ -54,7 +54,6 @@ class BetRepositoryImpl @Inject constructor(
override suspend fun getWon(token: String): List<BetResultDetail> = override suspend fun getWon(token: String): List<BetResultDetail> =
api.getWon(token.formatBearerToken()).map { it.toBetResultDetail() } api.getWon(token.formatBearerToken()).map { it.toBetResultDetail() }
override suspend fun confirmBet(token: String, id: String, response: String) { override suspend fun confirmBet(token: String, id: String, response: String) {
api.confirmBet(token.formatBearerToken(), id, response) api.confirmBet(token.formatBearerToken(), id, response)
} }

@ -18,13 +18,13 @@ class UserRepositoryImpl @Inject constructor(
password = password password = password
) )
) )
updateUser(response.toUser()) _currentUser.emit(response.toUser())
return response.token return response.token
} }
override suspend fun login(token: String): String? { override suspend fun login(token: String): String? {
val response = api.login(token = token.formatBearerToken()) val response = api.login(token = token.formatBearerToken())
updateUser(response.toUser()) _currentUser.emit(response.toUser())
return response.token return response.token
} }
@ -37,7 +37,10 @@ class UserRepositoryImpl @Inject constructor(
password = password password = password
) )
) )
updateUser(response.toUser()) _currentUser.emit(response.toUser())
return response.token return response.token
} }
override suspend fun dailyGift(token: String): Int =
api.dailyGift(token)
} }
Loading…
Cancel
Save