Fix events
continuous-integration/drone/push Build is passing Details

pull/4/head
Arthur VALIN 1 year ago
parent 58d788f58f
commit 53b6eacfff

@ -11,7 +11,7 @@ object Bets {
endRegisterDate = ZonedDateTime.now(),
endBetDate = ZonedDateTime.now(),
isPublic = true,
betStatus = BetStatus.InProgress,
betStatus = BetStatus.IN_PROGRESS,
creator = "creator",
id = ""
),
@ -21,7 +21,7 @@ object Bets {
endRegisterDate = ZonedDateTime.now(),
endBetDate = ZonedDateTime.now(),
isPublic = true,
betStatus = BetStatus.InProgress,
betStatus = BetStatus.IN_PROGRESS,
nameTeam1 = "Team_1",
nameTeam2 = "Team_2",
creator = "creator",
@ -33,7 +33,7 @@ object Bets {
endRegisterDate = ZonedDateTime.now(),
endBetDate = ZonedDateTime.now(),
isPublic = true,
betStatus = BetStatus.InProgress,
betStatus = BetStatus.IN_PROGRESS,
creator = "creator",
possibleAnswers = listOf(
"Answer 1",

@ -6,24 +6,24 @@ import androidx.compose.ui.graphics.Brush
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.graphics.SolidColor
import fr.iut.alldev.allin.R
import fr.iut.alldev.allin.data.model.bet.BetFinishedStatus.LOST
import fr.iut.alldev.allin.data.model.bet.BetFinishedStatus.WON
import fr.iut.alldev.allin.data.model.bet.BetStatus
import fr.iut.alldev.allin.theme.AllInTheme
@StringRes
fun BetStatus.getTitleId(): Int {
return when (this) {
is BetStatus.Finished -> R.string.bet_status_finished
BetStatus.InProgress -> R.string.bet_status_in_progress
BetStatus.Waiting -> R.string.bet_status_waiting
BetStatus.IN_PROGRESS -> R.string.bet_status_in_progress
BetStatus.WAITING -> R.string.bet_status_waiting
BetStatus.CLOSING -> R.string.bet_status_closing
BetStatus.FINISHED -> R.string.bet_status_finished
BetStatus.CANCELLED -> R.string.bet_status_cancelled
}
}
@StringRes
fun BetStatus.getDateStartLabelId(): Int {
return when (this) {
is BetStatus.Finished -> R.string.Started
BetStatus.CLOSING, BetStatus.FINISHED, BetStatus.CANCELLED -> R.string.Started
else -> R.string.Starting
}
}
@ -31,7 +31,7 @@ fun BetStatus.getDateStartLabelId(): Int {
@StringRes
fun BetStatus.getDateEndLabelId(): Int {
return when (this) {
is BetStatus.Finished -> R.string.Ended
BetStatus.CLOSING, BetStatus.FINISHED, BetStatus.CANCELLED -> R.string.Ended
else -> R.string.Ends
}
}
@ -39,40 +39,38 @@ fun BetStatus.getDateEndLabelId(): Int {
@Composable
fun BetStatus.getColor(): Color {
return when (this) {
is BetStatus.Finished -> AllInTheme.colors.allInBetFinish
BetStatus.InProgress -> AllInTheme.colors.allInBetInProgress
BetStatus.Waiting -> AllInTheme.colors.allInBetWaiting
BetStatus.FINISHED -> AllInTheme.colors.allInBetFinish
BetStatus.IN_PROGRESS -> AllInTheme.colors.allInBetInProgress
BetStatus.WAITING -> AllInTheme.colors.allInBetWaiting
else -> AllInTheme.colors.allInBetFinish // TODO
}
}
@Composable
fun BetStatus.getTextColor(): Color {
return when (this) {
is BetStatus.Finished -> AllInTheme.colors.allInBetFinishText
BetStatus.InProgress -> AllInTheme.colors.allInBetInProgressText
BetStatus.Waiting -> AllInTheme.colors.allInBetWaitingText
BetStatus.FINISHED -> AllInTheme.colors.allInBetFinishText
BetStatus.IN_PROGRESS -> AllInTheme.colors.allInBetInProgressText
BetStatus.WAITING -> AllInTheme.colors.allInBetWaitingText
else -> AllInTheme.colors.allInBetFinishText // TODO
}
}
@StringRes
fun BetStatus.getBetHistoryPhrase(): Int {
fun BetStatus.getBetHistoryPhrase(won: Boolean): Int {
return when (this) {
is BetStatus.Finished -> when (this.status) {
WON -> R.string.bet_history_status_won
LOST -> R.string.bet_history_status_lost
}
BetStatus.FINISHED ->
if (won) R.string.bet_history_status_won else R.string.bet_history_status_lost
else -> R.string.bet_history_status_in_progress
}
}
@Composable
fun BetStatus.getBetHistoryStatusColor(): Brush {
fun BetStatus.getBetHistoryStatusColor(won: Boolean): Brush {
return when (this) {
is BetStatus.Finished -> when (this.status) {
WON -> AllInTheme.colors.allInMainGradient
LOST -> AllInTheme.colors.allInDarkGradient
}
BetStatus.FINISHED ->
if (won) AllInTheme.colors.allInMainGradient else AllInTheme.colors.allInDarkGradient
else -> SolidColor(AllInTheme.colors.allInDarkGrey100)
}

@ -12,7 +12,7 @@ import fr.iut.alldev.allin.data.model.bet.BetType
@StringRes
fun BetType.getTitleId(): Int {
return when (this) {
BetType.YES_NO -> R.string.yes_no
BetType.BINARY -> R.string.yes_no
BetType.MATCH -> R.string.sport_match
BetType.CUSTOM -> R.string.custom_answers
}
@ -20,7 +20,7 @@ fun BetType.getTitleId(): Int {
fun BetType.getIcon(): ImageVector {
return when (this) {
BetType.YES_NO -> Icons.AutoMirrored.Default.HelpOutline
BetType.BINARY -> Icons.AutoMirrored.Default.HelpOutline
BetType.MATCH -> Icons.Default.SportsSoccer
BetType.CUSTOM -> Icons.Default.Edit
}

@ -8,4 +8,5 @@ abstract class AllInKeystoreManager {
abstract fun putToken(token: String)
abstract fun getToken(): String?
abstract fun deleteToken()
fun getTokenOrEmpty() = getToken() ?: ""
}

@ -5,6 +5,7 @@ import androidx.lifecycle.viewModelScope
import dagger.hilt.android.lifecycle.HiltViewModel
import fr.iut.alldev.allin.data.model.bet.Bet
import fr.iut.alldev.allin.data.repository.BetRepository
import fr.iut.alldev.allin.keystore.AllInKeystoreManager
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.SharingStarted
import kotlinx.coroutines.flow.StateFlow
@ -16,6 +17,7 @@ import javax.inject.Inject
@HiltViewModel
class BetViewModel @Inject constructor(
private val keystoreManager: AllInKeystoreManager,
private val betRepository: BetRepository
) : ViewModel() {
@ -45,7 +47,7 @@ class BetViewModel @Inject constructor(
private suspend fun refreshData() {
runCatching {
_bets.emit(betRepository.getAllBets())
_bets.emit(betRepository.getAllBets(keystoreManager.getTokenOrEmpty()))
}
}

@ -41,7 +41,6 @@ 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.BetFinishedStatus
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
@ -140,7 +139,8 @@ fun ConfirmationAnswers(
onClick: (String) -> Unit
) {
val configuration = LocalConfiguration.current
val locale = remember { ConfigurationCompat.getLocales(configuration).get(0) ?: Locale.getDefault() }
val locale =
remember { ConfigurationCompat.getLocales(configuration).get(0) ?: Locale.getDefault() }
LazyColumn(
verticalArrangement = Arrangement.spacedBy(8.dp)
@ -148,7 +148,10 @@ fun ConfirmationAnswers(
when (betDetail.bet) {
is CustomBet -> items((betDetail.bet as CustomBet).possibleAnswers) {
betDetail.getAnswerOfResponse(it)?.let {
val opacity by animateFloatAsState(targetValue = if (selectedAnswer != null && selectedAnswer != it.response) .5f else 1f, label = "")
val opacity by animateFloatAsState(
targetValue = if (selectedAnswer != null && selectedAnswer != it.response) .5f else 1f,
label = ""
)
BetConfirmationBottomSheetAnswer(
@ -166,7 +169,10 @@ fun ConfirmationAnswers(
val bet = (betDetail.bet as MatchBet)
item {
betDetail.getAnswerOfResponse(bet.nameTeam1)?.let {
val opacity by animateFloatAsState(targetValue = if (selectedAnswer != null && selectedAnswer != it.response) .5f else 1f, label = "")
val opacity by animateFloatAsState(
targetValue = if (selectedAnswer != null && selectedAnswer != it.response) .5f else 1f,
label = ""
)
BetConfirmationBottomSheetAnswer(
text = it.response,
odds = it.odds,
@ -179,7 +185,10 @@ fun ConfirmationAnswers(
}
item {
betDetail.getAnswerOfResponse(bet.nameTeam2)?.let {
val opacity by animateFloatAsState(targetValue = if (selectedAnswer != null && selectedAnswer != it.response) .5f else 1f, label = "")
val opacity by animateFloatAsState(
targetValue = if (selectedAnswer != null && selectedAnswer != it.response) .5f else 1f,
label = ""
)
BetConfirmationBottomSheetAnswer(
text = it.response,
@ -198,7 +207,10 @@ fun ConfirmationAnswers(
is YesNoBet -> {
item {
betDetail.getAnswerOfResponse(YES_VALUE)?.let {
val opacity by animateFloatAsState(targetValue = if (selectedAnswer != null && selectedAnswer != it.response) .5f else 1f, label = "")
val opacity by animateFloatAsState(
targetValue = if (selectedAnswer != null && selectedAnswer != it.response) .5f else 1f,
label = ""
)
val scale by animateFloatAsState(
targetValue = if (selectedAnswer == null) 1f
else if (selectedAnswer != it.response) .95f else 1.05f,
@ -219,10 +231,13 @@ fun ConfirmationAnswers(
}
item {
betDetail.getAnswerOfResponse(NO_VALUE)?.let {
val opacity by animateFloatAsState(targetValue = if (selectedAnswer != null && selectedAnswer != it.response) .5f else 1f, label = "")
val opacity by animateFloatAsState(
targetValue = if (selectedAnswer != null && selectedAnswer != it.response) .5f else 1f,
label = ""
)
val scale by animateFloatAsState(
targetValue = if (selectedAnswer == null) 1f
else if (selectedAnswer != it.response) .95f else 1.05f,
else if (selectedAnswer != it.response) .95f else 1f,
label = ""
)
@ -358,7 +373,7 @@ private fun BetConfirmationBottomSheetContentPreview() {
endRegisterDate = ZonedDateTime.now(),
endBetDate = ZonedDateTime.now(),
isPublic = true,
betStatus = BetStatus.Finished(BetFinishedStatus.WON),
betStatus = BetStatus.FINISHED,
creator = "creator",
),
answers = listOf(

@ -30,7 +30,7 @@ class BetCreationViewModel @Inject constructor(
val registerDate = mutableStateOf(ZonedDateTime.now())
val betDate = mutableStateOf(ZonedDateTime.now())
var isPublic = mutableStateOf(true)
var selectedBetType = mutableStateOf(BetType.YES_NO)
var selectedBetType = mutableStateOf(BetType.BINARY)
val themeError = mutableStateOf<FieldErrorState>(FieldErrorState.NoError)
val phraseError = mutableStateOf<FieldErrorState>(FieldErrorState.NoError)
@ -111,7 +111,7 @@ class BetCreationViewModel @Inject constructor(
possibleAnswers = listOf(),
creator = currentUser.username
)
betRepository.createBet(bet, keystoreManager.getToken() ?: "")
betRepository.createBet(bet, keystoreManager.getTokenOrEmpty())
onSuccess()
} catch (e: AllInAPIException) {
Timber.e(e)

@ -43,7 +43,7 @@ fun BetCreationScreenAnswerTab(
)
Spacer(modifier = Modifier.height(26.dp))
when (selectedBetType) {
BetType.YES_NO -> {
BetType.BINARY -> {
Column(
modifier = Modifier.padding(vertical = 20.dp),
verticalArrangement = Arrangement.spacedBy(17.dp)

@ -0,0 +1,30 @@
package fr.iut.alldev.allin.ui.betHistory
import androidx.compose.runtime.Composable
import androidx.compose.runtime.collectAsState
import androidx.compose.runtime.getValue
import androidx.compose.ui.res.stringResource
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.ui.betHistory.components.GenericHistory
@Composable
fun BetCurrentScreen(
viewModel: BetCurrentViewModel = hiltViewModel()
) {
val bets by viewModel.bets.collectAsState()
GenericHistory(
title = stringResource(id = R.string.bet_history_current_title),
bets = bets,
getTitle = { it.bet.phrase },
getCreator = { it.bet.creator },
getCategory = { it.bet.theme },
getEndRegisterDate = { it.bet.endRegisterDate.formatToMediumDateNoYear() },
getEndBetTime = { it.bet.endBetDate.formatToTime() },
getStatus = { it.bet.betStatus },
getNbCoins = { it.userParticipation?.stake ?: 0 },
getWon = { true }
)
}

@ -0,0 +1,44 @@
package fr.iut.alldev.allin.ui.betHistory
import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
import dagger.hilt.android.lifecycle.HiltViewModel
import fr.iut.alldev.allin.data.model.bet.vo.BetDetail
import fr.iut.alldev.allin.data.repository.BetRepository
import fr.iut.alldev.allin.keystore.AllInKeystoreManager
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.SharingStarted
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.asStateFlow
import kotlinx.coroutines.flow.filterNotNull
import kotlinx.coroutines.flow.stateIn
import kotlinx.coroutines.launch
import javax.inject.Inject
@HiltViewModel
class BetCurrentViewModel @Inject constructor(
private val betRepository: BetRepository,
private val keystoreManager: AllInKeystoreManager
) : ViewModel() {
private val _bets: MutableStateFlow<List<BetDetail>> by lazy {
MutableStateFlow(emptyList())
}
val bets: StateFlow<List<BetDetail>> by lazy {
_bets.asStateFlow()
.filterNotNull()
.stateIn(
viewModelScope,
SharingStarted.WhileSubscribed(5_000L),
emptyList()
)
}
init {
viewModelScope.launch {
_bets.emit(
betRepository.getToConfirm(keystoreManager.getTokenOrEmpty())
)
}
}
}

@ -1,65 +1,30 @@
package fr.iut.alldev.allin.ui.betHistory
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.PaddingValues
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.lazy.items
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.collectAsState
import androidx.compose.runtime.getValue
import androidx.compose.ui.Modifier
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.text.style.TextAlign
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp
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.theme.AllInTheme
import fr.iut.alldev.allin.ui.betHistory.components.BetHistoryScreenCard
import fr.iut.alldev.allin.ui.betHistory.components.GenericHistory
@Composable
fun BetHistoryScreen(
isCurrent: Boolean,
viewModel: BetHistoryViewModel = hiltViewModel(),
viewModel: BetHistoryViewModel = hiltViewModel()
) {
val bets by viewModel.bets.collectAsState()
LazyColumn(
modifier = Modifier.fillMaxSize(),
contentPadding = PaddingValues(horizontal = 24.dp, vertical = 18.dp),
verticalArrangement = Arrangement.spacedBy(18.dp),
) {
item {
Text(
text = stringResource(
id = if (isCurrent) R.string.bet_history_current_title
else R.string.bet_history_title
),
style = AllInTheme.typography.h1,
color = AllInTheme.colors.allInGrey,
fontSize = 24.sp,
textAlign = TextAlign.Center,
modifier = Modifier.fillMaxWidth()
)
}
bets?.let { bets ->
items(bets) {
BetHistoryScreenCard(
title = it.phrase,
creator = it.creator,
category = it.theme,
date = it.endRegisterDate.formatToMediumDateNoYear(),
time = it.endRegisterDate.formatToTime(),
status = it.betStatus,
nbCoins = 230
)
}
}
}
GenericHistory(
title = stringResource(id = R.string.bet_history_title),
bets = bets,
getTitle = { it.bet.phrase },
getCreator = { it.bet.creator },
getCategory = { it.bet.theme },
getEndRegisterDate = { it.bet.endRegisterDate.formatToMediumDateNoYear() },
getEndBetTime = { it.bet.endBetDate.formatToTime() },
getStatus = { it.bet.betStatus },
getNbCoins = { it.amount },
getWon = { it.won }
)
}

@ -1,35 +1,44 @@
package fr.iut.alldev.allin.ui.betHistory
import androidx.lifecycle.SavedStateHandle
import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
import dagger.hilt.android.lifecycle.HiltViewModel
import fr.iut.alldev.allin.data.model.bet.Bet
import fr.iut.alldev.allin.data.model.bet.BetResultDetail
import fr.iut.alldev.allin.data.repository.BetRepository
import fr.iut.alldev.allin.ui.navigation.NavArguments
import fr.iut.alldev.allin.keystore.AllInKeystoreManager
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.SharingStarted
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.asStateFlow
import kotlinx.coroutines.flow.filterNotNull
import kotlinx.coroutines.flow.flatMapConcat
import kotlinx.coroutines.flow.flowOf
import kotlinx.coroutines.flow.stateIn
import kotlinx.coroutines.launch
import javax.inject.Inject
@HiltViewModel
class BetHistoryViewModel @Inject constructor(
savedStateHandle: SavedStateHandle,
private val betRepository: BetRepository,
private val keystoreManager: AllInKeystoreManager
) : ViewModel() {
private val isCurrent: Boolean? = savedStateHandle[NavArguments.ARG_BET_HISTORY_IS_CURRENT]
private val _bets: MutableStateFlow<List<BetResultDetail>> by lazy {
MutableStateFlow(emptyList())
}
val bets: StateFlow<List<BetResultDetail>> by lazy {
_bets.asStateFlow()
.filterNotNull()
.stateIn(
viewModelScope,
SharingStarted.WhileSubscribed(5_000L),
emptyList()
)
}
val bets: StateFlow<List<Bet>?> by lazy {
flowOf(isCurrent).filterNotNull().flatMapConcat {
if (it) betRepository.getCurrentBets()
else betRepository.getHistory()
}.stateIn(
viewModelScope,
SharingStarted.WhileSubscribed(5_000L),
null
)
init {
viewModelScope.launch {
_bets.emit(
betRepository.getHistory(keystoreManager.getTokenOrEmpty())
)
}
}
}

@ -51,14 +51,15 @@ val betHistoryStatusInlineContent = mapOf(
@Composable
fun BetHistoryBetStatus(
status: BetStatus,
won: Boolean,
nbCoins: Int,
) {
val betHistoryPhrase = stringResource(id = status.getBetHistoryPhrase(), nbCoins)
val betHistoryPhrase = stringResource(id = status.getBetHistoryPhrase(won), nbCoins)
Row(
modifier = Modifier
.fillMaxWidth()
.background(status.getBetHistoryStatusColor())
.background(status.getBetHistoryStatusColor(won))
.padding(16.dp),
horizontalArrangement = Arrangement.Center,
verticalAlignment = Alignment.CenterVertically
@ -85,7 +86,8 @@ private fun BetHistoryBetStatusPreview(
AllInTheme {
BetHistoryBetStatus(
status = betStatus,
nbCoins = 230
nbCoins = 230,
won = true
)
}
}

@ -20,6 +20,7 @@ fun BetHistoryScreenCard(
time: String,
status: BetStatus,
nbCoins: Int,
won: Boolean
) {
BetCard(
title = title,
@ -32,7 +33,8 @@ fun BetHistoryScreenCard(
) {
BetHistoryBetStatus(
status = status,
nbCoins = nbCoins
nbCoins = nbCoins,
won = won
)
}
}
@ -51,7 +53,8 @@ private fun BetHistoryScreenCardPreview(
date = "Date",
time = "Time",
status = betStatus,
nbCoins = 123
nbCoins = 123,
won = true
)
}
}

@ -0,0 +1,60 @@
package fr.iut.alldev.allin.ui.betHistory.components
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.PaddingValues
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.lazy.items
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.compose.ui.text.style.TextAlign
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp
import fr.iut.alldev.allin.data.model.bet.BetStatus
import fr.iut.alldev.allin.theme.AllInTheme
@Composable
fun <T> GenericHistory(
title: String,
bets: List<T>,
getTitle: (T) -> String,
getCreator: (T) -> String,
getCategory: (T) -> String,
getEndRegisterDate: (T) -> String,
getEndBetTime: (T) -> String,
getStatus: (T) -> BetStatus,
getNbCoins: (T) -> Int,
getWon: (T) -> Boolean,
) {
LazyColumn(
modifier = Modifier.fillMaxSize(),
contentPadding = PaddingValues(horizontal = 24.dp, vertical = 18.dp),
verticalArrangement = Arrangement.spacedBy(18.dp),
) {
item {
Text(
text = title,
style = AllInTheme.typography.h1,
color = AllInTheme.colors.allInGrey,
fontSize = 24.sp,
textAlign = TextAlign.Center,
modifier = Modifier.fillMaxWidth()
)
}
items(bets) {
BetHistoryScreenCard(
title = getTitle(it),
creator = getCreator(it),
category = getCategory(it),
date = getEndRegisterDate(it),
time = getEndBetTime(it),
status = getStatus(it),
nbCoins = getNbCoins(it),
won = getWon(it)
)
}
}
}

@ -139,7 +139,7 @@ private fun BetResultBottomSheetContentPreview() {
endRegisterDate = ZonedDateTime.now(),
endBetDate = ZonedDateTime.now(),
isPublic = true,
betStatus = BetStatus.InProgress,
betStatus = BetStatus.IN_PROGRESS,
creator = "creator",
),
stake = 4175,

@ -4,7 +4,6 @@ import android.content.res.Configuration
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.compose.ui.tooling.preview.Preview
import fr.iut.alldev.allin.data.model.bet.BetFinishedStatus
import fr.iut.alldev.allin.data.model.bet.BetStatus
import fr.iut.alldev.allin.theme.AllInTheme
import fr.iut.alldev.allin.ui.core.bet.BetCard
@ -51,7 +50,7 @@ private fun BetResultBottomSheetBetCardPreview() {
title = "Title",
date = "Date",
time = "Time",
status = BetStatus.Finished(BetFinishedStatus.WON),
status = BetStatus.FINISHED,
stake = 2446,
winnings = 6930,
odds = 2.3f

@ -1,9 +1,21 @@
package fr.iut.alldev.allin.ui.betStatus
import androidx.compose.animation.*
import androidx.compose.foundation.layout.*
import androidx.compose.material3.*
import androidx.compose.runtime.*
import androidx.compose.animation.AnimatedVisibility
import androidx.compose.animation.slideInVertically
import androidx.compose.animation.slideOutVertically
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.PaddingValues
import androidx.compose.foundation.layout.fillMaxHeight
import androidx.compose.material3.ExperimentalMaterial3Api
import androidx.compose.material3.SheetState
import androidx.compose.material3.rememberModalBottomSheetState
import androidx.compose.runtime.Composable
import androidx.compose.runtime.MutableIntState
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableIntStateOf
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.setValue
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import fr.iut.alldev.allin.data.model.bet.BetStatus
@ -56,7 +68,7 @@ fun BetStatusBottomSheet(
scrimColor = Color.Transparent
) {
var selectedAnswer by remember { mutableStateOf(0) }
var selectedAnswer by remember { mutableIntStateOf(0) }
var stake by remember { mutableStateOf<Int?>(null) }
Column(
@ -67,7 +79,9 @@ fun BetStatusBottomSheet(
displayBet(it)
BetStatusParticipationBottomSheet(
sheetVisibility = participateSheetVisibility && betDetail.bet.betStatus == BetStatus.Waiting && state.hasExpandedState,
sheetVisibility = participateSheetVisibility &&
betDetail.bet.betStatus == BetStatus.IN_PROGRESS &&
state.hasExpandedState,
safeBottomPadding = paddingValues.calculateBottomPadding(),
odds = betDetail.answers.getOrNull(selectedAnswer)?.odds ?: 1f,
betPhrase = betDetail.bet.phrase,

@ -2,7 +2,19 @@ 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.layout.Arrangement
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.IntrinsicSize
import androidx.compose.foundation.layout.PaddingValues
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.fillMaxHeight
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.size
import androidx.compose.foundation.layout.width
import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.lazy.items
import androidx.compose.material.icons.Icons
@ -12,7 +24,9 @@ import androidx.compose.material.icons.filled.WorkspacePremium
import androidx.compose.material3.ExperimentalMaterial3Api
import androidx.compose.material3.HorizontalDivider
import androidx.compose.material3.Text
import androidx.compose.runtime.*
import androidx.compose.runtime.Composable
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.vector.rememberVectorPainter
@ -65,7 +79,8 @@ class BetStatusBottomSheetBetDisplayer(
) {
val safeBottomPadding = paddingValues.value.calculateBottomPadding()
val configuration = LocalConfiguration.current
val locale = remember { ConfigurationCompat.getLocales(configuration).get(0) ?: Locale.getDefault() }
val locale =
remember { ConfigurationCompat.getLocales(configuration).get(0) ?: Locale.getDefault() }
val response1Answer = remember { betDetail.getAnswerOfResponse(response1) }
val response2Answer = remember { betDetail.getAnswerOfResponse(response2) }
@ -99,7 +114,7 @@ class BetStatusBottomSheetBetDisplayer(
}
Spacer(modifier = Modifier.height(20.dp))
}
if (betDetail.bet.betStatus is BetStatus.Finished) {
if (betDetail.bet.betStatus == BetStatus.FINISHED) {
BetStatusWinner(
answer = response1Display,
color = AllInTheme.colors.allInBlue,
@ -119,8 +134,10 @@ class BetStatusBottomSheetBetDisplayer(
Spacer(modifier = Modifier.height(20.dp))
BinaryStatBar(
response1Percentage = remember {
val total = (response1Answer?.totalParticipants ?: 0) + (response2Answer?.totalParticipants ?: 0)
if (total == 0) .5f else (response1Answer?.totalParticipants ?: 0) / total.toFloat()
val total = (response1Answer?.totalParticipants
?: 0) + (response2Answer?.totalParticipants ?: 0)
if (total == 0) .5f else (response1Answer?.totalParticipants
?: 0) / total.toFloat()
},
response1 = response1Display,
response2 = response2Display
@ -184,13 +201,13 @@ class BetStatusBottomSheetBetDisplayer(
}
}
}
if (betDetail.bet.betStatus !is BetStatus.Finished && betDetail.userParticipation == null) {
if (betDetail.bet.betStatus != BetStatus.FINISHED && betDetail.userParticipation == null) {
RainbowButton(
modifier = Modifier
.align(Alignment.BottomCenter)
.padding(horizontal = 7.dp),
text = stringResource(id = R.string.Participate),
enabled = betDetail.bet.betStatus == BetStatus.Waiting,
enabled = betDetail.bet.betStatus == BetStatus.IN_PROGRESS,
onClick = openParticipateSheet
)
}

@ -68,8 +68,8 @@ private fun BetCardPreview() {
title = "Title",
date = "Date",
time = "Time",
status = BetStatus.Waiting
){
status = BetStatus.WAITING
) {
Text("Content")
}
}

@ -2,17 +2,30 @@ package fr.iut.alldev.allin.ui.main
import androidx.activity.compose.BackHandler
import androidx.compose.foundation.background
import androidx.compose.foundation.layout.*
import androidx.compose.material3.*
import androidx.compose.runtime.*
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.padding
import androidx.compose.material3.DrawerState
import androidx.compose.material3.DrawerValue
import androidx.compose.material3.ExperimentalMaterial3Api
import androidx.compose.material3.SheetValue
import androidx.compose.material3.SnackbarDuration
import androidx.compose.material3.SnackbarHostState
import androidx.compose.material3.rememberDrawerState
import androidx.compose.material3.rememberModalBottomSheetState
import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.rememberCoroutineScope
import androidx.compose.runtime.setValue
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.hilt.navigation.compose.hiltViewModel
import androidx.navigation.NavHostController
import androidx.navigation.compose.rememberNavController
import fr.iut.alldev.allin.theme.AllInTheme
import fr.iut.alldev.allin.ui.betConfirmation.BetConfirmationBottomSheet
import fr.iut.alldev.allin.ui.betResult.BetResultBottomSheet
import fr.iut.alldev.allin.ui.betStatus.BetStatusBottomSheet
import fr.iut.alldev.allin.ui.betStatus.vo.BetStatusBottomSheetBetDisplayer
import fr.iut.alldev.allin.ui.core.AllInLoading
@ -33,28 +46,6 @@ private val topLevelDestinations = listOf(
TopLevelDestination.CurrentBets
)
@Composable
private fun rememberBetStatusVisibilities()
: Triple<MutableState<Boolean>, MutableState<Boolean>, (Boolean) -> Unit> {
val statusVisibility = remember {
mutableStateOf(false)
}
val sheetBackVisibility = remember {
mutableStateOf(false)
}
val setStatusVisibility = { it: Boolean ->
statusVisibility.value = it
if (it) sheetBackVisibility.value = true
}
return Triple(
statusVisibility,
sheetBackVisibility,
setStatusVisibility,
)
}
@OptIn(ExperimentalMaterial3Api::class)
@Composable
fun MainScreen(
@ -66,15 +57,19 @@ fun MainScreen(
) {
val scope = rememberCoroutineScope()
var loading by remember { mainViewModel.loading }
val (loading, setLoading) = remember { mainViewModel.loading }
val currentUser = remember { mainViewModel.currentUserState }
val selectedBet by remember { mainViewModel.selectedBet }
val wonBet by remember { mainViewModel.wonBet }
val toConfirm by remember { mainViewModel.toConfirmBet }
val (statusVisibility, sheetBackVisibility, setStatusVisibility) = rememberBetStatusVisibilities()
val statusVisibility = remember { mutableStateOf(false) }
val sheetBackVisibility = remember { mutableStateOf(false) }
val setStatusVisibility = { it: Boolean ->
statusVisibility.value = it
if (it) sheetBackVisibility.value = true
}
val (participateSheetVisibility, setParticipateSheetVisibility) = remember { mutableStateOf(false) }
val (displayResult, setDisplayResult) = remember { mutableStateOf(true) }
val events = remember { mainViewModel.events }
val eventBottomSheetState = rememberModalBottomSheetState(skipPartiallyExpanded = true)
val betStatusDisplayer = remember {
BetStatusBottomSheetBetDisplayer(
@ -113,10 +108,6 @@ fun MainScreen(
}
)
val resultBottomSheetState = rememberModalBottomSheetState(
skipPartiallyExpanded = true
)
AllInDrawer(
drawerState = drawerState,
destinations = topLevelDestinations,
@ -126,6 +117,7 @@ fun MainScreen(
nbBets = 35,
bestWin = 362,
navigateTo = { route ->
mainViewModel.fetchEvents()
navController.popUpTo(route, startDestination)
},
logout = {
@ -135,7 +127,7 @@ fun MainScreen(
) {
AllInScaffold(
onMenuClicked = { scope.launch { drawerState.open() } },
coinAmount = currentUser.userCoins.value,
coinAmount = currentUser.userCoins.intValue,
drawerState = drawerState,
snackbarHostState = snackbarHostState
) {
@ -156,34 +148,26 @@ fun MainScreen(
setParticipateSheetVisibility(participate)
setStatusVisibility(true)
},
setLoading = { loading = it },
putSnackbarContent = { mainViewModel.putSnackbarContent(it) }
setLoading = setLoading,
putSnackbarContent = { mainViewModel.putSnackbarContent(it) },
backHandlers = {
BackHandler(enabled = drawerState.isOpen) {
scope.launch {
drawerState.close()
}
}
}
)
}
}
}
wonBet?.let {
BetResultBottomSheet(
state = resultBottomSheetState,
sheetVisibility = displayResult,
onDismiss = { setDisplayResult(false) },
bet = it,
username = currentUser.user.username,
coinAmount = 1630,
stake = 1630,
winnings = 1630,
odds = 3.62f
)
}
toConfirm?.let {
BetConfirmationBottomSheet(
state = resultBottomSheetState,
sheetVisibility = displayResult,
betDetail = it,
onDismiss = { setDisplayResult(false) }
) { /*TODO*/ }
events.firstOrNull()?.let {
it.Display(sheetState = eventBottomSheetState) {
mainViewModel.dismissedEvents += it
events.removeFirstOrNull()
}
}
BetStatusBottomSheet(
@ -200,14 +184,6 @@ fun MainScreen(
setParticipateSheetVisibility = setParticipateSheetVisibility
)
AllInLoading(visible = loading)
BackHandler(
enabled = drawerState.isOpen
) {
scope.launch {
drawerState.close()
}
}
}

@ -2,28 +2,25 @@ package fr.iut.alldev.allin.ui.main
import androidx.compose.runtime.MutableState
import androidx.compose.runtime.mutableIntStateOf
import androidx.compose.runtime.mutableStateListOf
import androidx.compose.runtime.mutableStateOf
import androidx.lifecycle.ViewModel
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.NO_VALUE
import fr.iut.alldev.allin.data.model.bet.Participation
import fr.iut.alldev.allin.data.model.bet.YES_VALUE
import fr.iut.alldev.allin.data.model.bet.YesNoBet
import fr.iut.alldev.allin.data.model.bet.vo.BetAnswerDetail
import fr.iut.alldev.allin.data.model.bet.vo.BetDetail
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 fr.iut.alldev.allin.ui.main.event.AllInEvent
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.launch
import kotlinx.coroutines.withContext
import java.time.ZonedDateTime
import javax.inject.Inject
class UserState(val user: User) {
@ -41,61 +38,49 @@ class MainViewModel @Inject constructor(
val currentUserState = UserState(currentUser)
val selectedBet = mutableStateOf<BetDetail?>(null)
val wonBet = mutableStateOf<Bet?>(
null
/* YesNoBet(
id = "1",
theme = "Theme",
phrase = "Phrase",
endRegisterDate = ZonedDateTime.now(),
endBetDate = ZonedDateTime.now(),
isPublic = true,
betStatus = BetStatus.Finished(BetFinishedStatus.WON),
creator = "creator"
)*/
)
val toConfirmBet = mutableStateOf<BetDetail?>(
BetDetail(
bet = YesNoBet(
id = "1",
theme = "Theme",
phrase = "Phrase",
endRegisterDate = ZonedDateTime.now(),
endBetDate = ZonedDateTime.now(),
isPublic = true,
betStatus = BetStatus.Finished(BetFinishedStatus.WON),
creator = "creator",
),
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 = emptyList(),
userParticipation = null
)
)
val dismissedEvents = mutableStateListOf<AllInEvent>()
val events = mutableStateListOf<AllInEvent>()
val snackbarContent: MutableState<SnackbarContent?> by lazy { mutableStateOf(null) }
fun putSnackbarContent(content: SnackbarContent) {
snackbarContent.value = content
}
init {
fetchEvents()
}
fun fetchEvents() {
viewModelScope.launch {
val token = keystoreManager.getTokenOrEmpty()
events.addAll(
buildList {
addAll(betRepository.getToConfirm(token).map { bet ->
ToConfirmBet(
betDetail = bet,
onConfirm = {
confirmBet(
response = it,
betId = bet.bet.id
)
}
)
})
addAll(betRepository.getWon(token).map { result ->
WonBet(
user = currentUser,
betResult = result
)
})
}.filter { it !in dismissedEvents }
)
}
}
fun openBetDetail(bet: Bet) {
viewModelScope.launch {
selectedBet.value = betRepository.getBet(bet.id, keystoreManager.getToken() ?: "")
selectedBet.value = betRepository.getBet(bet.id, keystoreManager.getTokenOrEmpty())
}
}
@ -117,13 +102,23 @@ class MainViewModel @Inject constructor(
response = response,
stake = stake
)
betRepository.participateToBet(participation, keystoreManager.getToken() ?: "")
betRepository.participateToBet(participation, keystoreManager.getTokenOrEmpty())
}
loading.value = false
}
}
}
private fun confirmBet(response: String, betId: String) {
viewModelScope.launch {
betRepository.confirmBet(
token = keystoreManager.getTokenOrEmpty(),
id = betId,
response = response
)
}
}
class SnackbarContent(
val text: String,
val type: SnackbarType = SnackbarType.STANDARD

@ -0,0 +1,12 @@
package fr.iut.alldev.allin.ui.main.event
import androidx.compose.material3.SheetState
import androidx.compose.runtime.Composable
sealed class AllInEvent {
@Composable
abstract fun Display(
sheetState: SheetState,
onDismiss: () -> Unit
)
}

@ -0,0 +1,39 @@
package fr.iut.alldev.allin.ui.main.event
import androidx.compose.material3.SheetState
import androidx.compose.runtime.Composable
import fr.iut.alldev.allin.data.model.bet.vo.BetDetail
import fr.iut.alldev.allin.ui.betConfirmation.BetConfirmationBottomSheet
data class ToConfirmBet(
private val betDetail: BetDetail,
private val onConfirm: (String) -> Unit
) : AllInEvent() {
@Composable
override fun Display(
sheetState: SheetState,
onDismiss: () -> Unit
) {
BetConfirmationBottomSheet(
state = sheetState,
sheetVisibility = true,
betDetail = betDetail,
onDismiss = onDismiss,
onConfirm = onConfirm
)
}
override fun hashCode(): Int {
return betDetail.hashCode()
}
override fun equals(other: Any?): Boolean {
if (this === other) return true
if (javaClass != other?.javaClass) return false
other as ToConfirmBet
return betDetail == other.betDetail
}
}

@ -0,0 +1,31 @@
package fr.iut.alldev.allin.ui.main.event
import androidx.compose.material3.SheetState
import androidx.compose.runtime.Composable
import fr.iut.alldev.allin.data.model.User
import fr.iut.alldev.allin.data.model.bet.BetResultDetail
import fr.iut.alldev.allin.ui.betResult.BetResultBottomSheet
data class WonBet(
private val user: User,
private val betResult: BetResultDetail,
) : AllInEvent() {
@Composable
override fun Display(
sheetState: SheetState,
onDismiss: () -> Unit
) {
BetResultBottomSheet(
state = sheetState,
sheetVisibility = true,
onDismiss = onDismiss,
bet = betResult.bet,
username = user.username,
coinAmount = betResult.amount,
stake = betResult.participation.stake,
winnings = betResult.amount,
odds = 1f
)
}
}

@ -12,19 +12,17 @@ import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.compose.ui.res.stringResource
import androidx.hilt.navigation.compose.hiltViewModel
import androidx.navigation.NavGraphBuilder
import androidx.navigation.NavHostController
import androidx.navigation.NavType
import androidx.navigation.compose.NavHost
import androidx.navigation.compose.composable
import androidx.navigation.compose.rememberNavController
import androidx.navigation.navArgument
import fr.iut.alldev.allin.R
import fr.iut.alldev.allin.data.model.bet.Bet
import fr.iut.alldev.allin.theme.AllInTheme
import fr.iut.alldev.allin.ui.bet.BetScreen
import fr.iut.alldev.allin.ui.betCreation.BetCreationScreen
import fr.iut.alldev.allin.ui.betHistory.BetCurrentScreen
import fr.iut.alldev.allin.ui.betHistory.BetHistoryScreen
import fr.iut.alldev.allin.ui.core.snackbar.SnackbarType
import fr.iut.alldev.allin.ui.login.LoginScreen
@ -41,14 +39,11 @@ object Routes {
const val PUBLIC_BETS = "PUBLIC_BETS"
const val BET_CREATION = "BET_CREATION"
const val BET_HISTORY = "BET_HISTORY"
const val BET_CURRENT = "BET_CURRENT"
const val FRIENDS = "FRIENDS"
}
object NavArguments {
const val ARG_BET_HISTORY_IS_CURRENT = "ARG_BET_HISTORY_IS_CURRENT"
}
internal fun NavHostController.popUpTo(route: String, baseRoute: String) {
this.navigate(route) {
launchSingleTop = true
@ -99,7 +94,8 @@ internal fun AllInDrawerNavHost(
selectBet: (Bet, Boolean) -> Unit,
startDestination: String = Routes.PUBLIC_BETS,
setLoading: (Boolean) -> Unit,
putSnackbarContent: (MainViewModel.SnackbarContent) -> Unit
putSnackbarContent: (MainViewModel.SnackbarContent) -> Unit,
backHandlers: @Composable () -> Unit
) {
NavHost(
navController = navController,
@ -108,12 +104,15 @@ internal fun AllInDrawerNavHost(
enterTransition = { EnterTransition.None },
exitTransition = { ExitTransition.None }
) {
composable(route = Routes.PUBLIC_BETS) {
backHandlers()
BetScreen(
selectBet = selectBet
)
}
composable(route = Routes.BET_CREATION) {
backHandlers()
val creationSuccessMessage = stringResource(id = R.string.bet_creation_success_message)
BetCreationScreen(
setLoading = setLoading,
@ -130,20 +129,17 @@ internal fun AllInDrawerNavHost(
}
composable(
route = "${Routes.BET_HISTORY}/{${NavArguments.ARG_BET_HISTORY_IS_CURRENT}}",
arguments = listOf(
navArgument(NavArguments.ARG_BET_HISTORY_IS_CURRENT) {
type = NavType.BoolType
}
)
route = Routes.BET_HISTORY
) {
backHandlers()
BetHistoryScreen()
}
composable(
route = Routes.BET_CURRENT
) {
val isCurrent =
it.arguments?.getBoolean(NavArguments.ARG_BET_HISTORY_IS_CURRENT) ?: false
BetHistoryScreen(
isCurrent = isCurrent,
viewModel = hiltViewModel(it, isCurrent.toString())
)
backHandlers()
BetCurrentScreen()
}
}
}

@ -23,7 +23,7 @@ sealed class TopLevelDestination(
)
data object BetHistory : TopLevelDestination(
route = "${Routes.BET_HISTORY}/false",
route = Routes.BET_HISTORY,
title = R.string.bet_history,
subtitle = R.string.bet_history_subtitle,
emoji = R.drawable.eyes
@ -37,7 +37,7 @@ sealed class TopLevelDestination(
)
data object CurrentBets : TopLevelDestination(
route = "${Routes.BET_HISTORY}/true",
route = Routes.BET_CURRENT,
title = R.string.current_bets,
subtitle = R.string.current_bets_subtitle,
emoji = R.drawable.money_with_wings

@ -2,7 +2,6 @@ package fr.iut.alldev.allin.ui.preview
import androidx.compose.ui.tooling.preview.PreviewParameterProvider
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.CustomBet
import fr.iut.alldev.allin.data.model.bet.MatchBet
@ -18,7 +17,7 @@ class BetPreviewProvider : PreviewParameterProvider<Bet> {
endRegisterDate = ZonedDateTime.now(),
endBetDate = ZonedDateTime.now(),
isPublic = true,
betStatus = BetStatus.Finished(BetFinishedStatus.WON),
betStatus = BetStatus.FINISHED,
creator = "creator"
),
MatchBet(
@ -28,7 +27,7 @@ class BetPreviewProvider : PreviewParameterProvider<Bet> {
endRegisterDate = ZonedDateTime.now(),
endBetDate = ZonedDateTime.now(),
isPublic = true,
betStatus = BetStatus.Finished(BetFinishedStatus.WON),
betStatus = BetStatus.FINISHED,
creator = "creator",
nameTeam1 = "The Monarchs",
nameTeam2 = "Climate Change"
@ -40,7 +39,7 @@ class BetPreviewProvider : PreviewParameterProvider<Bet> {
endRegisterDate = ZonedDateTime.now(),
endBetDate = ZonedDateTime.now(),
isPublic = true,
betStatus = BetStatus.Finished(BetFinishedStatus.WON),
betStatus = BetStatus.FINISHED,
creator = "creator",
possibleAnswers = listOf(
"Answer 1",

@ -4,5 +4,5 @@ import androidx.compose.ui.tooling.preview.PreviewParameterProvider
import fr.iut.alldev.allin.data.model.bet.BetStatus
class BetStatusPreviewProvider : PreviewParameterProvider<BetStatus> {
override val values = BetStatus.entries
override val values = BetStatus.entries.asSequence()
}

@ -10,7 +10,7 @@ import java.time.ZonedDateTime
class BetWithStatusPreviewProvider : PreviewParameterProvider<Bet> {
override val values = BetStatus.entries.flatMap { status ->
override val values = BetStatus.entries.asSequence().flatMap { status ->
sequenceOf(
YesNoBet(
id = "1",

@ -118,6 +118,8 @@
<string name="bet_status_finished">Terminé !</string>
<string name="bet_status_in_progress">En cours…</string>
<string name="bet_status_waiting">En attente…</string>
<string name="bet_status_closing">Fermeture…</string>
<string name="bet_status_cancelled">Annulé</string>
<string name="place_your_bets">Faites vos paris</string>
<string name="bet_status_participants_list">Liste des participants</string>

@ -118,6 +118,8 @@
<string name="bet_status_finished">Finished !</string>
<string name="bet_status_in_progress">In progress…</string>
<string name="bet_status_waiting">Waiting…</string>
<string name="bet_status_closing">Closing…</string>
<string name="bet_status_cancelled">Cancelled</string>
<string name="place_your_bets">Place your bets</string>
<string name="bet_status_participants_list">Participants</string>

@ -6,6 +6,7 @@ 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.ResponseBetResultDetail
import fr.iut.alldev.allin.data.api.model.ResponseUser
import retrofit2.http.Body
import retrofit2.http.GET
@ -31,11 +32,42 @@ interface AllInApi {
suspend fun createBet(@Header("Authorization") token: String, @Body body: RequestBet)
@GET("bets/gets")
suspend fun getAllBets(): List<ResponseBet>
suspend fun getAllBets(@Header("Authorization") token: String): List<ResponseBet>
@GET("bets/toConfirm")
suspend fun getToConfirm(@Header("Authorization") token: String): List<ResponseBetDetail>
@POST("bets/confirm/{id}")
suspend fun confirmBet(
@Header("Authorization") token: String,
@Path("id") id: String,
@Body value: String
)
@GET("betdetail/get/{id}")
suspend fun getBet(@Header("Authorization") token: String, @Path("id") id: String): ResponseBetDetail
suspend fun getBet(
@Header("Authorization") token: String,
@Path("id") id: String
): ResponseBetDetail
@GET("bets/getCurrent")
suspend fun getBetCurrent(
@Header("Authorization") token: String
): List<ResponseBetDetail>
@GET("bets/history")
suspend fun getBetHistory(
@Header("Authorization") token: String
): List<ResponseBetResultDetail>
@GET("bets/getWon")
suspend fun getWon(
@Header("Authorization") token: String
): List<ResponseBetResultDetail>
@POST("participations/add")
suspend fun participateToBet(@Header("Authorization") token: String, @Body body: RequestParticipation)
suspend fun participateToBet(
@Header("Authorization") token: String,
@Body body: RequestParticipation
)
}

@ -8,19 +8,26 @@ import fr.iut.alldev.allin.data.api.model.RequestUser
import fr.iut.alldev.allin.data.api.model.ResponseBet
import fr.iut.alldev.allin.data.api.model.ResponseBetAnswerDetail
import fr.iut.alldev.allin.data.api.model.ResponseBetDetail
import fr.iut.alldev.allin.data.api.model.ResponseBetResultDetail
import fr.iut.alldev.allin.data.api.model.ResponseParticipation
import fr.iut.alldev.allin.data.api.model.ResponseUser
import fr.iut.alldev.allin.data.model.bet.BetStatus
import fr.iut.alldev.allin.data.model.bet.BetType
import fr.iut.alldev.allin.data.model.bet.NO_VALUE
import fr.iut.alldev.allin.data.model.bet.YES_VALUE
import fr.iut.alldev.allin.data.model.bet.vo.BetResult
import java.time.ZonedDateTime
import java.util.UUID
class MockAllInApi : AllInApi {
private fun getUserFromToken(token: String) =
mockUsers.find { it.first.token == token }
mockUsers.find { it.first.token == token.removePrefix("Bearer ") }
private fun getAnswerDetails(bet: ResponseBet, participations: List<ResponseParticipation>): List<ResponseBetAnswerDetail> {
private fun getAnswerDetails(
bet: ResponseBet,
participations: List<ResponseParticipation>
): List<ResponseBetAnswerDetail> {
return bet.response.map { response ->
val responseParticipations = participations.filter { it.answer == response }
ResponseBetAnswerDetail(
@ -65,12 +72,47 @@ class MockAllInApi : AllInApi {
endBet = body.endBet,
isPrivate = body.isPrivate,
response = body.response,
type = BetType.BINARY,
status = BetStatus.WAITING,
createdBy = ""
)
)
}
override suspend fun getAllBets(): List<ResponseBet> = mockBets.toList()
override suspend fun getAllBets(token: String): List<ResponseBet> {
getUserFromToken(token) ?: throw AllInAPIException("Invalid login/password.")
return mockBets
}
override suspend fun getToConfirm(token: String): List<ResponseBetDetail> {
val user = getUserFromToken(token) ?: throw AllInAPIException("Invalid login/password.")
return mockBets.filter {
it.createdBy == user.first.username && it.status == BetStatus.CLOSING
}.map { bet ->
val betParticipations = mockParticipations.filter { it.betId == bet.id }
val userParticipation = betParticipations.find { it.username == user.first.username }
ResponseBetDetail(
bet = bet,
answers = getAnswerDetails(bet, betParticipations),
participations = betParticipations,
userParticipation = userParticipation
)
}
}
override suspend fun confirmBet(token: String, id: String, value: String) {
getUserFromToken(token) ?: throw AllInAPIException("Invalid login/password.")
val bet = mockBets.find { it.id == id } ?: throw AllInAPIException("Unauthorized")
mockResults.add(
BetResult(
betId = id,
result = value
)
)
mockBets[mockBets.indexOf(bet)] = bet.copy(status = BetStatus.FINISHED)
}
override suspend fun getBet(token: String, id: String): ResponseBetDetail {
val bet = mockBets.find { it.id == id } ?: throw AllInAPIException("Bet not found")
val user = getUserFromToken(token) ?: throw AllInAPIException("Invalid login/password.")
@ -85,6 +127,18 @@ class MockAllInApi : AllInApi {
)
}
override suspend fun getBetCurrent(token: String): List<ResponseBetDetail> {
return emptyList()
}
override suspend fun getBetHistory(token: String): List<ResponseBetResultDetail> {
return emptyList()
}
override suspend fun getWon(token: String): List<ResponseBetResultDetail> {
return emptyList()
}
override suspend fun participateToBet(token: String, body: RequestParticipation) {
getUserFromToken(token)?.let {
mockParticipations.add(
@ -214,7 +268,9 @@ private val mockBets = mutableListOf(
endBet = ZonedDateTime.now().plusDays(4),
isPrivate = false,
response = listOf(YES_VALUE, NO_VALUE),
createdBy = "Armure"
createdBy = "Armure",
type = BetType.BINARY,
status = BetStatus.WAITING,
),
ResponseBet(
id = "UUID2",
@ -224,16 +280,22 @@ private val mockBets = mutableListOf(
endBet = ZonedDateTime.now().plusDays(4),
isPrivate = false,
response = listOf("Answer 1", "Answer 2", "Answer 3", "Answer 4"),
createdBy = "User 2"
createdBy = "User 2",
type = BetType.BINARY,
status = BetStatus.WAITING,
),
ResponseBet(
id = "UUID3",
theme = "Sport",
sentenceBet = "Nouveau record du monde ?",
endRegistration = ZonedDateTime.now().plusDays(3),
endBet = ZonedDateTime.now().plusDays(4),
endRegistration = ZonedDateTime.now().minusDays(3),
endBet = ZonedDateTime.now().minusDays(2),
isPrivate = false,
response = listOf(YES_VALUE, NO_VALUE),
createdBy = "Armure"
createdBy = "User 1",
type = BetType.BINARY,
status = BetStatus.CLOSING,
)
)
private val mockResults by lazy { mutableListOf<BetResult>() }

@ -2,7 +2,10 @@ package fr.iut.alldev.allin.data.api.model
import androidx.annotation.Keep
import fr.iut.alldev.allin.data.model.bet.Bet
import fr.iut.alldev.allin.data.model.bet.BetResult
import fr.iut.alldev.allin.data.model.bet.BetResultDetail
import fr.iut.alldev.allin.data.model.bet.BetStatus
import fr.iut.alldev.allin.data.model.bet.BetType
import fr.iut.alldev.allin.data.model.bet.CustomBet
import fr.iut.alldev.allin.data.model.bet.NO_VALUE
import fr.iut.alldev.allin.data.model.bet.YES_VALUE
@ -18,6 +21,8 @@ import java.time.ZonedDateTime
data class ResponseBet(
val id: String?,
val theme: String,
val type: BetType,
val status: BetStatus,
val sentenceBet: String,
@Serializable(ZonedDateTimeSerializer::class) val endRegistration: ZonedDateTime,
@Serializable(ZonedDateTimeSerializer::class) var endBet: ZonedDateTime,
@ -34,7 +39,7 @@ data class ResponseBet(
endRegisterDate = endRegistration,
endBetDate = endBet,
isPublic = !isPrivate,
betStatus = BetStatus.Waiting,
betStatus = status,
creator = createdBy
)
} else {
@ -45,7 +50,7 @@ data class ResponseBet(
endRegisterDate = endRegistration,
endBetDate = endBet,
isPublic = !isPrivate,
betStatus = BetStatus.Waiting,
betStatus = status,
creator = createdBy,
possibleAnswers = response
)
@ -58,6 +63,7 @@ data class ResponseBet(
data class RequestBet(
val id: String = "",
val theme: String,
val type: BetType,
val sentenceBet: String,
@Serializable(ZonedDateTimeSerializer::class) val endRegistration: ZonedDateTime,
@Serializable(ZonedDateTimeSerializer::class) var endBet: ZonedDateTime,
@ -101,3 +107,33 @@ data class ResponseBetDetail(
)
}
@Serializable
data class ResponseBetResult(
val betId: String,
val result: String
) {
fun toBetResult() =
BetResult(
betId = betId,
result = result
)
}
@Serializable
data class ResponseBetResultDetail(
val betResult: ResponseBetResult,
val bet: ResponseBet,
val participation: ResponseParticipation,
val amount: Int,
val won: Boolean
) {
fun toBetResultDetail() =
BetResultDetail(
betResult = betResult.toBetResult(),
bet = bet.toBet(),
participation = participation.toParticipation(),
amount = amount,
won = won
)
}

@ -25,7 +25,7 @@ data class ResponseUser(
id = id,
username = username,
email = email,
coins = nbCoins.toInt()
coins = nbCoins
)
}

@ -13,16 +13,18 @@ sealed class Bet(
open val isPublic: Boolean,
open val betStatus: BetStatus,
) {
abstract fun getBetType(): BetType
abstract fun getResponses(): List<String>
fun toRequestBet(): RequestBet {
return RequestBet(
id = "",
id = id,
theme = theme,
sentenceBet = phrase,
endRegistration = endRegisterDate,
endBet = endBetDate,
isPrivate = !isPublic,
response = getResponses()
response = getResponses(),
type = getBetType()
)
}
}

@ -19,7 +19,7 @@ class BetFactory {
): Bet =
when (betType) {
BetType.YES_NO -> {
BetType.BINARY -> {
YesNoBet(
id = id,
theme = theme,
@ -28,7 +28,7 @@ class BetFactory {
endRegisterDate = endRegisterDate,
endBetDate = endBetDate,
isPublic = isPublic,
betStatus = BetStatus.Waiting
betStatus = BetStatus.WAITING
)
}
@ -41,7 +41,7 @@ class BetFactory {
endRegisterDate = endRegisterDate,
endBetDate = endBetDate,
isPublic = isPublic,
betStatus = BetStatus.Waiting,
betStatus = BetStatus.WAITING,
nameTeam1 = nameTeam1,
nameTeam2 = nameTeam2
)
@ -57,7 +57,7 @@ class BetFactory {
endRegisterDate = endRegisterDate,
endBetDate = endBetDate,
isPublic = isPublic,
betStatus = BetStatus.Waiting,
betStatus = BetStatus.WAITING,
possibleAnswers = possibleAnswers
)
}

@ -0,0 +1,14 @@
package fr.iut.alldev.allin.data.model.bet
data class BetResult(
val betId: String,
val result: String
)
data class BetResultDetail(
val betResult: BetResult,
val bet: Bet,
val participation: Participation,
val amount: Int,
val won: Boolean
)

@ -1,19 +1,9 @@
package fr.iut.alldev.allin.data.model.bet
sealed class BetStatus {
data class Finished(val status: BetFinishedStatus) : BetStatus()
data object InProgress : BetStatus()
data object Waiting : BetStatus()
companion object {
val entries = sequenceOf(
InProgress,
Waiting,
Finished(BetFinishedStatus.WON),
Finished(BetFinishedStatus.LOST)
)
}
enum class BetStatus {
IN_PROGRESS,
WAITING,
CLOSING,
FINISHED,
CANCELLED
}

@ -1,7 +1,7 @@
package fr.iut.alldev.allin.data.model.bet
enum class BetType {
YES_NO,
BINARY,
MATCH,
CUSTOM
}

@ -22,5 +22,7 @@ data class CustomBet(
isPublic,
betStatus
) {
override fun getBetType() = BetType.CUSTOM
override fun getResponses(): List<String> = possibleAnswers
}

@ -23,6 +23,7 @@ data class MatchBet(
isPublic,
betStatus
) {
override fun getBetType() = BetType.MATCH
override fun getResponses(): List<String> = listOf(nameTeam1, nameTeam2)
}

@ -24,5 +24,6 @@ data class YesNoBet(
isPublic,
betStatus
) {
override fun getBetType() = BetType.BINARY
override fun getResponses(): List<String> = listOf(YES_VALUE, NO_VALUE)
}

@ -1,10 +1,6 @@
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
val betId: String,
val result: String
)

@ -1,15 +1,18 @@
package fr.iut.alldev.allin.data.repository
import fr.iut.alldev.allin.data.model.bet.Bet
import fr.iut.alldev.allin.data.model.bet.BetResultDetail
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 getHistory(token: String): List<BetResultDetail>
abstract suspend fun getCurrentBets(token: String): List<BetDetail>
abstract suspend fun getBet(id: String, token: String): BetDetail
abstract suspend fun participateToBet(participation: Participation, token: String)
abstract suspend fun getAllBets(): List<Bet>
abstract suspend fun getAllBets(token: String): List<Bet>
abstract suspend fun confirmBet(token: String, id: String, response: String)
abstract suspend fun getToConfirm(token: String): List<BetDetail>
abstract suspend fun getWon(token: String): List<BetResultDetail>
}

@ -3,15 +3,10 @@ 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.BetResultDetail
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
import java.time.ZonedDateTime
import javax.inject.Inject
class BetRepositoryImpl @Inject constructor(
@ -24,79 +19,16 @@ class BetRepositoryImpl @Inject constructor(
)
}
override suspend fun getHistory(): Flow<List<Bet>> {
return flowOf(
listOf(
YesNoBet(
id = "1",
creator = "Lucas",
theme = "Theme",
phrase = "Bet phrase 1",
endRegisterDate = ZonedDateTime.now().minusDays(4),
endBetDate = ZonedDateTime.now().minusDays(2),
isPublic = false,
betStatus = BetStatus.Finished(BetFinishedStatus.WON)
),
YesNoBet(
id = "2",
creator = "Lucas",
theme = "Theme",
phrase = "Bet phrase 2",
endRegisterDate = ZonedDateTime.now().minusDays(3),
endBetDate = ZonedDateTime.now().minusDays(1),
isPublic = true,
betStatus = BetStatus.Finished(BetFinishedStatus.LOST)
),
YesNoBet(
id = "3",
creator = "Lucas",
theme = "Theme",
phrase = "Bet phrase 3",
endRegisterDate = ZonedDateTime.now().minusDays(15),
endBetDate = ZonedDateTime.now().minusDays(7),
isPublic = false,
betStatus = BetStatus.Finished(BetFinishedStatus.LOST)
)
)
)
override suspend fun getHistory(token: String): List<BetResultDetail> {
return api.getBetHistory(token.formatBearerToken()).map {
it.toBetResultDetail()
}
}
override suspend fun getCurrentBets(): Flow<List<Bet>> {
// TODO
return flowOf(
listOf(
YesNoBet(
id = "1",
creator = "Lucas",
theme = "Theme",
phrase = "Bet phrase 1",
endRegisterDate = ZonedDateTime.now().plusDays(5),
endBetDate = ZonedDateTime.now().plusDays(7),
isPublic = false,
betStatus = BetStatus.InProgress
),
YesNoBet(
id = "2",
creator = "Lucas",
theme = "Theme",
phrase = "Bet phrase 2",
endRegisterDate = ZonedDateTime.now().plusDays(1),
endBetDate = ZonedDateTime.now().plusDays(2),
isPublic = true,
betStatus = BetStatus.InProgress
),
YesNoBet(
id = "3",
creator = "Lucas",
theme = "Theme",
phrase = "Bet phrase 3",
endRegisterDate = ZonedDateTime.now().plusDays(3),
endBetDate = ZonedDateTime.now().plusDays(4),
isPublic = false,
betStatus = BetStatus.InProgress
)
)
)
override suspend fun getCurrentBets(token: String): List<BetDetail> {
return api.getToConfirm(token.formatBearerToken()).map {
it.toBetDetail()
}
}
override suspend fun getBet(id: String, token: String): BetDetail {
@ -107,11 +39,24 @@ class BetRepositoryImpl @Inject constructor(
}
override suspend fun participateToBet(participation: Participation, token: String) {
api.participateToBet(token = token.formatBearerToken(), body = participation.toRequestParticipation())
api.participateToBet(
token = token.formatBearerToken(),
body = participation.toRequestParticipation()
)
}
override suspend fun getAllBets(): List<Bet> {
return api.getAllBets().map { it.toBet() }
override suspend fun getAllBets(token: String): List<Bet> =
api.getAllBets(token.formatBearerToken()).map { it.toBet() }
override suspend fun getToConfirm(token: String): List<BetDetail> =
api.getToConfirm(token.formatBearerToken()).map { it.toBetDetail() }
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)
}
}
Loading…
Cancel
Save