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

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

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

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

@ -37,7 +37,7 @@ class BetCurrentViewModel @Inject constructor(
init {
viewModelScope.launch {
_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.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.ui.betHistory.components.GenericHistory
@Composable
fun BetHistoryScreen(
selectBet: (Bet, Boolean) -> Unit,
viewModel: BetHistoryViewModel = hiltViewModel()
) {
val bets by viewModel.bets.collectAsState()
@ -25,6 +27,7 @@ fun BetHistoryScreen(
getEndBetTime = { it.bet.endBetDate.formatToTime() },
getStatus = { it.bet.betStatus },
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
fun BetHistoryScreenCard(
modifier: Modifier = Modifier,
onClick: () -> Unit,
title: String,
creator: String,
category: String,
@ -29,6 +30,7 @@ fun BetHistoryScreenCard(
date = date,
time = time,
status = status,
onClick = onClick,
modifier = modifier
) {
BetHistoryBetStatus(
@ -47,6 +49,7 @@ private fun BetHistoryScreenCardPreview(
) {
AllInTheme {
BetHistoryScreenCard(
onClick = {},
creator = "Creator",
category = "Category",
title = "Title",

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

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

@ -85,6 +85,7 @@ class BetStatusBottomSheetBetDisplayer(
private fun DisplayBet(
betDetail: BetDetail,
currentUser: User,
winnerColor: @Composable () -> Color,
statBar: LazyListScope.() -> Unit
) {
Box(Modifier) {
@ -117,13 +118,16 @@ class BetStatusBottomSheetBetDisplayer(
Spacer(modifier = Modifier.height(20.dp))
}
if (betDetail.bet.betStatus == BetStatus.FINISHED) {
BetStatusWinner(
answer = YES_VALUE,
color = AllInColorToken.allInBlue,
coinAmount = 442,
username = "Imri",
multiplier = 1.2f
)
betDetail.wonParticipation?.let { wonParticipation ->
BetStatusWinner(
answer = wonParticipation.response,
color = winnerColor(),
coinAmount = wonParticipation.stake,
username = wonParticipation.username,
multiplier = betDetail.getAnswerOfResponse(wonParticipation.response)
?.odds ?: .5f
)
}
} else {
HorizontalDivider(color = AllInTheme.colors.border)
}
@ -301,13 +305,20 @@ class BetStatusBottomSheetBetDisplayer(
@Composable
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(
betDetail = betDetail,
response1 = YES_VALUE,
response2 = NO_VALUE,
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) {
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(
betDetail = betDetail,
response1 = matchBet.nameTeam1,
@ -331,7 +349,18 @@ class BetStatusBottomSheetBetDisplayer(
val configuration = LocalConfiguration.current
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) {
displayBinaryStatBar(
betDetail = betDetail,

@ -17,6 +17,7 @@ import androidx.compose.ui.graphics.painter.Painter
import androidx.compose.ui.graphics.vector.rememberVectorPainter
import androidx.compose.ui.platform.LocalLayoutDirection
import androidx.compose.ui.text.TextStyle
import androidx.compose.ui.text.style.TextOverflow
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.LayoutDirection
import androidx.compose.ui.unit.dp
@ -57,7 +58,10 @@ fun AllInTextIcon(
text = text,
color = color,
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(
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.ext.getDateStartLabelId
import fr.iut.alldev.allin.theme.AllInTheme
import fr.iut.alldev.allin.ui.core.AllInBouncyCard
import fr.iut.alldev.allin.ui.core.AllInCard
@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(uiMode = Configuration.UI_MODE_NIGHT_YES)
@Composable

@ -1,5 +1,10 @@
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.layout.Arrangement
import androidx.compose.foundation.layout.Row
@ -10,6 +15,11 @@ import androidx.compose.material3.Card
import androidx.compose.material3.Icon
import androidx.compose.material3.Text
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.Modifier
import androidx.compose.ui.graphics.Color
@ -27,6 +37,14 @@ fun AllInTopBarCoinCounter(
textColor: Color = AllInColorToken.allInDark,
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(
modifier = modifier.wrapContentSize(),
shape = RoundedCornerShape(topStartPercent = 50, bottomStartPercent = 50)
@ -34,20 +52,45 @@ fun AllInTopBarCoinCounter(
Row(
modifier = Modifier
.background(backgroundColor)
.padding(horizontal = 13.dp, vertical = 5.dp),
.padding(horizontal = 13.dp),
horizontalArrangement = Arrangement.spacedBy(7.dp),
verticalAlignment = Alignment.CenterVertically
) {
Text(
text = amount.toString(),
color = textColor,
style = AllInTheme.typography.h1,
fontSize = 20.sp
)
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 = char.toString(),
style = AllInTheme.typography.h1,
color = textColor,
fontSize = 20.sp,
softWrap = false,
modifier = Modifier.padding(vertical = 5.dp)
)
}
}
}
Icon(
painter = AllInTheme.icons.allCoins(),
tint = iconColor,
contentDescription = null,
modifier = Modifier.padding(vertical = 5.dp)
)
}
}

@ -72,7 +72,6 @@ fun MainScreen(
val (loading, setLoading) = remember { mainViewModel.loading }
val currentUser by mainViewModel.currentUser.collectAsStateWithLifecycle()
val userCoins by mainViewModel.userCoins.collectAsStateWithLifecycle()
val selectedBet by remember { mainViewModel.selectedBet }
val statusVisibility = remember { mutableStateOf(false) }
val sheetBackVisibility = remember { mutableStateOf(false) }
@ -141,7 +140,7 @@ fun MainScreen(
) {
AllInScaffold(
onMenuClicked = { scope.launch { drawerState.open() } },
coinAmount = userCoins ?: 0,
coinAmount = currentUser?.coins ?: 0,
drawerState = drawerState,
snackbarHostState = snackbarHostState
) {
@ -200,6 +199,7 @@ fun MainScreen(
scope.launch {
eventBottomSheetState.hide()
delay(EVENT_DISMISS_DELAY_MS)
mainViewModel.increaseCoins(event.betResult.amount)
events.removeFirstOrNull()
}
}
@ -215,6 +215,7 @@ fun MainScreen(
(events.firstOrNull() as? DailyReward)?.let {
it.Display(
onDismiss = {
mainViewModel.increaseCoins(it.amount)
dailyRewardVisible = false
mainViewModel.dismissedEvents += it
scope.launch {
@ -238,7 +239,7 @@ fun MainScreen(
displayBet = { detail ->
currentUser?.let { user -> betStatusDisplayer.DisplayBet(betDetail = detail, currentUser = user) }
},
userCoinAmount = userCoins ?: 0,
userCoinAmount = currentUser?.coins ?: 0,
onParticipate = { stake, response -> mainViewModel.participateToBet(stake, response) },
participateSheetVisibility = participateSheetVisibility,
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.WonBet
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.flow.asStateFlow
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
import javax.inject.Inject
@ -32,8 +31,7 @@ class MainViewModel @Inject constructor(
var loading = mutableStateOf(false)
val currentUser = userRepository.currentUser.asStateFlow()
val userCoins = userRepository.userCoins.asStateFlow()
val currentUser = userRepository.currentUser
val selectedBet = mutableStateOf<BetDetail?>(null)
val dismissedEvents = mutableStateListOf<AllInEvent>()
val events = mutableStateListOf<AllInEvent>()
@ -53,27 +51,34 @@ class MainViewModel @Inject constructor(
val token = keystoreManager.getTokenOrEmpty()
events.addAll(
buildList {
add(DailyReward(125))
addAll(betRepository.getToConfirm(token).map { bet ->
ToConfirmBet(
betDetail = bet,
onConfirm = {
confirmBet(
response = it,
betId = bet.bet.id
runCatching {
add(DailyReward(userRepository.dailyGift(token)))
}
runCatching {
addAll(betRepository.getToConfirm(token).map { bet ->
ToConfirmBet(
betDetail = bet,
onConfirm = {
confirmBet(
response = it,
betId = bet.bet.id
)
}
)
})
}
runCatching {
addAll(betRepository.getWon(token).mapNotNull { result ->
currentUser.value?.let {
WonBet(
user = it,
betResult = result
)
}
)
})
addAll(betRepository.getWon(token).mapNotNull { result ->
currentUser.value?.let {
WonBet(
user = it,
betResult = result
)
}
})
})
}
}.filter { it !in dismissedEvents }
)
}
@ -90,17 +95,23 @@ class MainViewModel @Inject constructor(
fun onLogout() {
viewModelScope.launch {
keystoreManager.deleteToken()
userRepository.currentUser.emit(null)
userRepository.userCoins.emit(null)
userRepository.resetCurrentUser()
}
}
private fun decreaseCoins(amount: Int) {
fun increaseCoins(amount: Int) {
viewModelScope.launch {
userRepository.userCoins.value?.let {
val newAmount = it - amount
userRepository.userCoins.emit(newAmount)
currentUser.value?.let {
userRepository.updateCurrentUserCoins(it.coins + amount)
}
}
}
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 fr.iut.alldev.allin.ui.dailyReward.DailyRewardScreen
data class DailyReward(private val amount: Int) : AllInEvent() {
data class DailyReward(val amount: Int) : AllInEvent() {
@Composable
fun Display(onDismiss: () -> Unit) {
DailyRewardScreen(

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

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

@ -12,98 +12,101 @@ import fr.iut.alldev.allin.data.model.bet.vo.BetDetail
class BetDetailPreviewProvider : PreviewParameterProvider<BetDetail> {
override val values = BetWithStatusPreviewProvider().values.map {
BetDetail(
bet = it,
answers = when (it) {
is CustomBet -> listOf(
BetAnswerDetail(
response = "Answer 1",
totalStakes = 300,
totalParticipants = 8,
highestStake = 200,
odds = 1.0f
),
BetAnswerDetail(
response = "Answer 2",
totalStakes = 300,
totalParticipants = 4,
highestStake = 200,
odds = 1.0f
),
BetAnswerDetail(
response = "Answer 3",
totalStakes = 300,
totalParticipants = 2,
highestStake = 200,
odds = 1.0f
),
BetAnswerDetail(
response = "Answer 4",
totalStakes = 300,
totalParticipants = 1,
highestStake = 200,
odds = 1.0f
)
val answers = when (it) {
is CustomBet -> listOf(
BetAnswerDetail(
response = "Answer 1",
totalStakes = 300,
totalParticipants = 8,
highestStake = 200,
odds = 1.0f
),
BetAnswerDetail(
response = "Answer 2",
totalStakes = 300,
totalParticipants = 4,
highestStake = 200,
odds = 1.0f
),
BetAnswerDetail(
response = "Answer 3",
totalStakes = 300,
totalParticipants = 2,
highestStake = 200,
odds = 1.0f
),
BetAnswerDetail(
response = "Answer 4",
totalStakes = 300,
totalParticipants = 1,
highestStake = 200,
odds = 1.0f
)
is MatchBet -> listOf(
BetAnswerDetail(
response = "The Monarchs",
totalStakes = 300,
totalParticipants = 2,
highestStake = 200,
odds = 1.0f
),
BetAnswerDetail(
response = "Climate Change",
totalStakes = 150,
totalParticipants = 1,
highestStake = 150,
odds = 2.0f
)
)
is MatchBet -> listOf(
BetAnswerDetail(
response = "The Monarchs",
totalStakes = 300,
totalParticipants = 2,
highestStake = 200,
odds = 1.0f
),
BetAnswerDetail(
response = "Climate Change",
totalStakes = 150,
totalParticipants = 1,
highestStake = 150,
odds = 2.0f
)
is YesNoBet -> listOf(
BetAnswerDetail(
response = YES_VALUE,
totalStakes = 300,
totalParticipants = 2,
highestStake = 200,
odds = 1.0f
),
BetAnswerDetail(
response = NO_VALUE,
totalStakes = 150,
totalParticipants = 1,
highestStake = 150,
odds = 2.0f
)
)
is YesNoBet -> listOf(
BetAnswerDetail(
response = YES_VALUE,
totalStakes = 300,
totalParticipants = 2,
highestStake = 200,
odds = 1.0f
),
BetAnswerDetail(
response = NO_VALUE,
totalStakes = 150,
totalParticipants = 1,
highestStake = 150,
odds = 2.0f
)
},
)
}
BetDetail(
bet = it,
answers = answers,
participations = listOf(
Participation(
betId = it.id,
username = "User1",
response = YES_VALUE,
stake = 200
),
Participation(
betId = it.id,
username = "User2",
response = YES_VALUE,
response = answers.first().response,
stake = 100
),
Participation(
betId = it.id,
username = "MyUser",
response = NO_VALUE,
username = "User 2",
response = answers.last().response,
stake = 150
)
),
userParticipation = null /*Participation(
userParticipation = Participation(
betId = it.id,
username = "User1",
response = answers.first().response,
stake = 100
),
wonParticipation = Participation(
betId = it.id,
username = "MyUser",
response = NO_VALUE,
stake = 150
)*/
username = "User1",
response = answers.first().response,
stake = 100
)
)
}
}

@ -1,6 +1,7 @@
package fr.iut.alldev.allin.data.api
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.RequestBet
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.ResponseBetAnswerDetail
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.ResponseParticipation
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.NO_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.util.UUID
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) =
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> {
getUserFromToken(token) ?: throw AllInAPIException("Invalid login/password.")
return mockBets
@ -105,7 +159,7 @@ class MockAllInApi : AllInApi {
getUserFromToken(token) ?: throw AllInAPIException("Invalid login/password.")
val bet = mockBets.find { it.id == id } ?: throw AllInAPIException("Unauthorized")
mockResults.add(
BetResult(
ResponseBetResult(
betId = id,
result = value
)
@ -118,25 +172,72 @@ class MockAllInApi : AllInApi {
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 }
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(
bet = bet,
answers = getAnswerDetails(bet, betParticipations),
participations = betParticipations,
userParticipation = userParticipation
userParticipation = userParticipation,
wonParticipation = wonParticipation
)
}
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> {
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> {
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) {
@ -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")
suspend fun createBet(@Header("Authorization") token: String, @Body body: RequestBet)
@GET("users/gift")
suspend fun dailyGift(@Header("Authorization") token: String): Int
@GET("bets/gets")
suspend fun getAllBets(@Header("Authorization") token: String): List<ResponseBet>

@ -115,15 +115,16 @@ data class ResponseBetDetail(
val bet: ResponseBet,
val answers: List<ResponseBetAnswerDetail>,
val participations: List<ResponseParticipation>,
val userParticipation: ResponseParticipation? = null
val userParticipation: ResponseParticipation? = null,
val wonParticipation: ResponseParticipation? = null
) {
fun toBetDetail() =
BetDetail(
bet = bet.toBet(),
answers = answers.map { it.toAnswerDetail() },
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 answers: List<BetAnswerDetail>,
val participations: List<Participation>,
val userParticipation: Participation?
val userParticipation: Participation?,
val wonParticipation: Participation?
) {
fun getAnswerOfResponse(response: String) =
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 kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.asStateFlow
abstract class UserRepository {
val currentUser by lazy { MutableStateFlow<User?>(null) }
val userCoins by lazy { MutableStateFlow<Int?>(null) }
internal val _currentUser by lazy { MutableStateFlow<User?>(null) }
val currentUser by lazy { _currentUser.asStateFlow() }
internal suspend fun updateUser(user: User) {
currentUser.emit(user)
userCoins.emit(user.coins)
suspend fun resetCurrentUser() {
_currentUser.emit(null)
}
suspend fun updateCurrentUserCoins(value: Int) {
currentUser.value?.let { user ->
_currentUser.emit(
user.copy(
coins = value
)
)
}
}
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 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> {
return api.getToConfirm(token.formatBearerToken()).map {
return api.getBetCurrent(token.formatBearerToken()).map {
it.toBetDetail()
}
}
@ -54,7 +54,6 @@ class BetRepositoryImpl @Inject constructor(
override suspend fun getWon(token: String): List<BetResultDetail> =
api.getWon(token.formatBearerToken()).map { it.toBetResultDetail() }
override suspend fun confirmBet(token: String, id: String, response: String) {
api.confirmBet(token.formatBearerToken(), id, response)
}

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