diff --git a/src/app/src/androidTest/java/fr/iut/alldev/allin/test/mock/Bets.kt b/src/app/src/androidTest/java/fr/iut/alldev/allin/test/mock/Bets.kt index 846c540..17e254c 100644 --- a/src/app/src/androidTest/java/fr/iut/alldev/allin/test/mock/Bets.kt +++ b/src/app/src/androidTest/java/fr/iut/alldev/allin/test/mock/Bets.kt @@ -1,49 +1,48 @@ package fr.iut.alldev.allin.test.mock -import fr.iut.alldev.allin.data.model.bet.BetStatus -import fr.iut.alldev.allin.data.model.bet.CustomBet -import fr.iut.alldev.allin.data.model.bet.MatchBet -import fr.iut.alldev.allin.data.model.bet.YesNoBet -import java.time.ZonedDateTime +import fr.iut.alldev.allin.data.model.bet.vo.BetDetail object Bets { val bets by lazy { - listOf( - YesNoBet( - theme = "Theme", - phrase = "Phrase", - endRegisterDate = ZonedDateTime.now(), - endBetDate = ZonedDateTime.now(), - isPublic = true, - betStatus = BetStatus.InProgress, - creator = "creator", - ), - MatchBet( - theme = "Theme", - phrase = "Phrase", - endRegisterDate = ZonedDateTime.now(), - endBetDate = ZonedDateTime.now(), - isPublic = true, - betStatus = BetStatus.InProgress, - nameTeam1 = "Team_1", - nameTeam2 = "Team_2", - creator = "creator" - ), - CustomBet( - theme = "Theme", - phrase = "Phrase", - endRegisterDate = ZonedDateTime.now(), - endBetDate = ZonedDateTime.now(), - isPublic = true, - betStatus = BetStatus.InProgress, - creator = "creator", - possibleAnswers = listOf( - "Answer 1", - "Answer 2", - "Answer 3", - "Answer 4" - ) - ), + listOf( + /* YesNoBet( + theme = "Theme", + phrase = "Phrase", + endRegisterDate = ZonedDateTime.now(), + endBetDate = ZonedDateTime.now(), + isPublic = true, + betStatus = BetStatus.IN_PROGRESS, + creator = "creator", + id = "" + ), + MatchBet( + theme = "Theme", + phrase = "Phrase", + endRegisterDate = ZonedDateTime.now(), + endBetDate = ZonedDateTime.now(), + isPublic = true, + betStatus = BetStatus.IN_PROGRESS, + nameTeam1 = "Team_1", + nameTeam2 = "Team_2", + creator = "creator", + id = "" + ), + CustomBet( + theme = "Theme", + phrase = "Phrase", + endRegisterDate = ZonedDateTime.now(), + endBetDate = ZonedDateTime.now(), + isPublic = true, + betStatus = BetStatus.IN_PROGRESS, + creator = "creator", + possibleAnswers = listOf( + "Answer 1", + "Answer 2", + "Answer 3", + "Answer 4" + ), + id = "" + )*/ ) } } \ No newline at end of file diff --git a/src/app/src/androidTest/java/fr/iut/alldev/allin/vo/bet/displayer/BetVOTestVisitor.kt b/src/app/src/androidTest/java/fr/iut/alldev/allin/vo/bet/displayer/BetVOTestVisitor.kt index b8abc92..3acb14d 100644 --- a/src/app/src/androidTest/java/fr/iut/alldev/allin/vo/bet/displayer/BetVOTestVisitor.kt +++ b/src/app/src/androidTest/java/fr/iut/alldev/allin/vo/bet/displayer/BetVOTestVisitor.kt @@ -4,25 +4,23 @@ import androidx.compose.material3.Text import androidx.compose.runtime.Composable import androidx.compose.ui.Modifier import androidx.compose.ui.platform.testTag -import fr.iut.alldev.allin.data.model.bet.CustomBet -import fr.iut.alldev.allin.data.model.bet.MatchBet -import fr.iut.alldev.allin.data.model.bet.YesNoBet +import fr.iut.alldev.allin.data.model.bet.vo.BetDetail import fr.iut.alldev.allin.test.TestTags import fr.iut.alldev.allin.vo.bet.BetDisplayer class BetTestDisplayer : BetDisplayer { @Composable - override fun DisplayYesNoBet(bet: YesNoBet) { + override fun DisplayYesNoBet(betDetail: BetDetail) { Text("This is a YesNo Bet", Modifier.testTag(TestTags.YES_NO_BET.tag)) } @Composable - override fun DisplayMatchBet(bet: MatchBet) { + override fun DisplayMatchBet(betDetail: BetDetail) { Text("This is a Match Bet", Modifier.testTag(TestTags.MATCH_BET.tag)) } @Composable - override fun DisplayCustomBet(bet: CustomBet) { + override fun DisplayCustomBet(betDetail: BetDetail) { Text("This is a Custom Bet", Modifier.testTag(TestTags.CUSTOM_BET.tag)) } } \ No newline at end of file diff --git a/src/app/src/main/java/fr/iut/alldev/allin/ext/BetStatusExt.kt b/src/app/src/main/java/fr/iut/alldev/allin/ext/BetStatusExt.kt index 979019e..aecabb2 100644 --- a/src/app/src/main/java/fr/iut/alldev/allin/ext/BetStatusExt.kt +++ b/src/app/src/main/java/fr/iut/alldev/allin/ext/BetStatusExt.kt @@ -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) } diff --git a/src/app/src/main/java/fr/iut/alldev/allin/ext/BetTypeExt.kt b/src/app/src/main/java/fr/iut/alldev/allin/ext/BetTypeExt.kt index 43c66a0..34831fb 100644 --- a/src/app/src/main/java/fr/iut/alldev/allin/ext/BetTypeExt.kt +++ b/src/app/src/main/java/fr/iut/alldev/allin/ext/BetTypeExt.kt @@ -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 } diff --git a/src/app/src/main/java/fr/iut/alldev/allin/ext/FieldExt.kt b/src/app/src/main/java/fr/iut/alldev/allin/ext/FieldExt.kt index 8181d68..bf23cf8 100644 --- a/src/app/src/main/java/fr/iut/alldev/allin/ext/FieldExt.kt +++ b/src/app/src/main/java/fr/iut/alldev/allin/ext/FieldExt.kt @@ -14,6 +14,9 @@ sealed class FieldErrorState( ) { data object NoError : FieldErrorState() + data object Mandatory : FieldErrorState(R.string.FieldError_Mandatory) + + data class TooShort(val fieldName: String, val minChar: Int) : FieldErrorState(R.string.FieldError_TooShort, fieldName, minChar) diff --git a/src/app/src/main/java/fr/iut/alldev/allin/keystore/AllInKeystoreManager.kt b/src/app/src/main/java/fr/iut/alldev/allin/keystore/AllInKeystoreManager.kt index 79aab93..737434e 100644 --- a/src/app/src/main/java/fr/iut/alldev/allin/keystore/AllInKeystoreManager.kt +++ b/src/app/src/main/java/fr/iut/alldev/allin/keystore/AllInKeystoreManager.kt @@ -8,4 +8,5 @@ abstract class AllInKeystoreManager { abstract fun putToken(token: String) abstract fun getToken(): String? abstract fun deleteToken() + fun getTokenOrEmpty() = getToken() ?: "" } \ No newline at end of file diff --git a/src/app/src/main/java/fr/iut/alldev/allin/ui/bet/BetViewModel.kt b/src/app/src/main/java/fr/iut/alldev/allin/ui/bet/BetViewModel.kt index e7712ba..a9e5e11 100644 --- a/src/app/src/main/java/fr/iut/alldev/allin/ui/bet/BetViewModel.kt +++ b/src/app/src/main/java/fr/iut/alldev/allin/ui/bet/BetViewModel.kt @@ -5,32 +5,32 @@ 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 kotlinx.coroutines.Dispatchers +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.emitAll import kotlinx.coroutines.flow.filterNotNull -import kotlinx.coroutines.flow.flow import kotlinx.coroutines.flow.stateIn import kotlinx.coroutines.launch -import kotlinx.coroutines.withContext import javax.inject.Inject @HiltViewModel class BetViewModel @Inject constructor( + private val keystoreManager: AllInKeystoreManager, private val betRepository: BetRepository ) : ViewModel() { - private val _isRefreshing = MutableStateFlow(false) + private val _isRefreshing by lazy { MutableStateFlow(false) } val isRefreshing: StateFlow get() = _isRefreshing.asStateFlow() + private val _bets: MutableStateFlow> by lazy { + MutableStateFlow(emptyList()) + } + val bets: StateFlow> by lazy { - flow { - kotlin.runCatching { emitAll(betRepository.getAllBets()) } - } + _bets.asStateFlow() .filterNotNull() .stateIn( viewModelScope, @@ -39,16 +39,22 @@ class BetViewModel @Inject constructor( ) } - private fun refreshData() { - Thread.sleep(1000) + init { + viewModelScope.launch { + refreshData() + } + } + + private suspend fun refreshData() { + runCatching { + _bets.emit(betRepository.getAllBets(keystoreManager.getTokenOrEmpty())) + } } fun refresh() { viewModelScope.launch { _isRefreshing.emit(true) - withContext(Dispatchers.IO) { - refreshData() - } + refreshData() _isRefreshing.emit(false) } } diff --git a/src/app/src/main/java/fr/iut/alldev/allin/ui/betConfirmation/BetConfirmationBottomSheet.kt b/src/app/src/main/java/fr/iut/alldev/allin/ui/betConfirmation/BetConfirmationBottomSheet.kt new file mode 100644 index 0000000..7723035 --- /dev/null +++ b/src/app/src/main/java/fr/iut/alldev/allin/ui/betConfirmation/BetConfirmationBottomSheet.kt @@ -0,0 +1,403 @@ +package fr.iut.alldev.allin.ui.betConfirmation + +import androidx.compose.animation.core.animateFloatAsState +import androidx.compose.foundation.background +import androidx.compose.foundation.layout.Arrangement +import androidx.compose.foundation.layout.Box +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.Row +import androidx.compose.foundation.layout.Spacer +import androidx.compose.foundation.layout.fillMaxSize +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.lazy.LazyColumn +import androidx.compose.foundation.lazy.items +import androidx.compose.material.icons.Icons +import androidx.compose.material.icons.filled.Close +import androidx.compose.material3.Icon +import androidx.compose.material3.IconButton +import androidx.compose.material3.SheetState +import androidx.compose.material3.Text +import androidx.compose.runtime.Composable +import androidx.compose.runtime.getValue +import androidx.compose.runtime.mutableStateOf +import androidx.compose.runtime.remember +import androidx.compose.runtime.setValue +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.draw.alpha +import androidx.compose.ui.draw.scale +import androidx.compose.ui.graphics.Color +import androidx.compose.ui.platform.LocalConfiguration +import androidx.compose.ui.res.painterResource +import androidx.compose.ui.res.stringResource +import androidx.compose.ui.text.style.TextAlign +import androidx.compose.ui.tooling.preview.Preview +import androidx.compose.ui.unit.dp +import androidx.compose.ui.unit.sp +import androidx.core.os.ConfigurationCompat +import fr.iut.alldev.allin.R +import fr.iut.alldev.allin.data.ext.formatToMediumDateNoYear +import fr.iut.alldev.allin.data.ext.formatToTime +import fr.iut.alldev.allin.data.model.bet.BetStatus +import fr.iut.alldev.allin.data.model.bet.CustomBet +import fr.iut.alldev.allin.data.model.bet.MatchBet +import fr.iut.alldev.allin.data.model.bet.NO_VALUE +import fr.iut.alldev.allin.data.model.bet.YES_VALUE +import fr.iut.alldev.allin.data.model.bet.YesNoBet +import fr.iut.alldev.allin.data.model.bet.vo.BetAnswerDetail +import fr.iut.alldev.allin.data.model.bet.vo.BetDetail +import fr.iut.alldev.allin.ext.formatToSimple +import fr.iut.alldev.allin.theme.AllInTheme +import fr.iut.alldev.allin.ui.core.AllInBottomSheet +import fr.iut.alldev.allin.ui.core.AllInButton +import fr.iut.alldev.allin.ui.core.AllInCard +import fr.iut.alldev.allin.ui.core.AllInMarqueeBox +import fr.iut.alldev.allin.ui.core.bet.BetCard +import java.time.ZonedDateTime +import java.util.Locale + +@Composable +fun BetConfirmationBottomSheet( + state: SheetState, + sheetVisibility: Boolean, + betDetail: BetDetail, + onDismiss: () -> Unit, + onConfirm: (selectedAnswer: String) -> Unit +) { + AllInBottomSheet( + sheetVisibility = sheetVisibility, + onDismiss = onDismiss, + state = state, + dragHandle = null + ) { + BetConfirmationBottomSheetContent( + betDetail = betDetail, + onConfirm = { + onConfirm(it) + onDismiss() + }, + onClose = onDismiss + ) + } +} + +@Composable +fun BetConfirmationBottomSheetAnswer( + text: String, + odds: Float, + modifier: Modifier = Modifier, + color: Color = AllInTheme.colors.allInBlue, + isSelected: Boolean, + locale: Locale, + onClick: () -> Unit +) { + val backColor = if (isSelected) AllInTheme.colors.allInPurple else AllInTheme.colors.white + val contentColor = if (isSelected) AllInTheme.colors.white else null + + AllInCard( + backgroundColor = backColor, + onClick = onClick, + modifier = modifier + ) { + Box( + modifier = Modifier + .fillMaxWidth() + .padding(horizontal = 12.dp, vertical = 18.dp), + ) { + Text( + text = text.uppercase(), + color = contentColor ?: color, + style = AllInTheme.typography.h1, + fontSize = 40.sp, + modifier = Modifier.align(Alignment.Center) + ) + + AllInCard( + radius = 50.dp, + backgroundColor = contentColor ?: AllInTheme.colors.allInPurple, + modifier = Modifier.align(Alignment.CenterEnd) + ) { + Box(Modifier.padding(vertical = 4.dp, horizontal = 8.dp)) { + Text( + text = "x${odds.formatToSimple(locale)}", + color = backColor, + style = AllInTheme.typography.h2 + ) + } + } + } + } +} + +@Composable +fun ConfirmationAnswers( + betDetail: BetDetail, + selectedAnswer: String?, + onClick: (String) -> Unit +) { + val configuration = LocalConfiguration.current + val locale = + remember { ConfigurationCompat.getLocales(configuration).get(0) ?: Locale.getDefault() } + + LazyColumn( + verticalArrangement = Arrangement.spacedBy(8.dp) + ) { + 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 = "" + ) + + + BetConfirmationBottomSheetAnswer( + text = it.response, + odds = it.odds, + locale = locale, + onClick = { onClick(it.response) }, + isSelected = selectedAnswer == it.response, + modifier = Modifier.alpha(opacity) + ) + } + } + + is MatchBet -> { + 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 = "" + ) + BetConfirmationBottomSheetAnswer( + text = it.response, + odds = it.odds, + locale = locale, + onClick = { onClick(it.response) }, + isSelected = selectedAnswer == it.response, + modifier = Modifier.alpha(opacity) + ) + } + } + item { + betDetail.getAnswerOfResponse(bet.nameTeam2)?.let { + val opacity by animateFloatAsState( + targetValue = if (selectedAnswer != null && selectedAnswer != it.response) .5f else 1f, + label = "" + ) + + BetConfirmationBottomSheetAnswer( + text = it.response, + color = AllInTheme.colors.allInBarPink, + odds = it.odds, + locale = locale, + onClick = { onClick(it.response) }, + isSelected = selectedAnswer == it.response, + modifier = Modifier + .alpha(opacity) + ) + } + } + } + + is YesNoBet -> { + item { + betDetail.getAnswerOfResponse(YES_VALUE)?.let { + 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, + label = "" + ) + + BetConfirmationBottomSheetAnswer( + text = it.response, + odds = it.odds, + locale = locale, + onClick = { onClick(it.response) }, + isSelected = selectedAnswer == it.response, + modifier = Modifier + .alpha(opacity) + .scale(scale) + ) + } + } + item { + betDetail.getAnswerOfResponse(NO_VALUE)?.let { + 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 1f, + label = "" + ) + + BetConfirmationBottomSheetAnswer( + text = it.response, + color = AllInTheme.colors.allInBarPink, + odds = it.odds, + locale = locale, + onClick = { onClick(it.response) }, + isSelected = selectedAnswer == it.response, + modifier = Modifier + .alpha(opacity) + .scale(scale) + ) + } + } + } + } + } +} + +@Composable +fun BetConfirmationBottomSheetContent( + betDetail: BetDetail, + onConfirm: (String) -> Unit, + onClose: () -> Unit +) { + var selectedAnswer by remember { mutableStateOf(null) } + + AllInMarqueeBox(backgroundColor = AllInTheme.colors.allInDarkGrey300) { + Box( + modifier = Modifier + .fillMaxSize() + .padding(16.dp) + ) { + IconButton( + onClick = onClose, + modifier = Modifier + .size(24.dp) + .align(Alignment.TopStart) + ) { + Icon( + imageVector = Icons.Default.Close, + tint = AllInTheme.colors.white, + contentDescription = null, + modifier = Modifier.size(24.dp) + ) + } + + Icon( + painter = painterResource(R.drawable.allin), + contentDescription = null, + tint = AllInTheme.colors.white, + modifier = Modifier + .size(40.dp) + .align(Alignment.TopCenter) + ) + + Column( + modifier = Modifier.align(Alignment.Center), + verticalArrangement = Arrangement.spacedBy(22.dp), + horizontalAlignment = Alignment.CenterHorizontally + ) { + BetCard( + title = betDetail.bet.phrase, + creator = betDetail.bet.creator, + category = betDetail.bet.theme, + date = betDetail.bet.endBetDate.formatToMediumDateNoYear(), + time = betDetail.bet.endBetDate.formatToTime(), + status = betDetail.bet.betStatus + ) { + Row( + modifier = Modifier + .fillMaxWidth() + .background(AllInTheme.colors.allInMainGradient) + .padding(16.dp), + horizontalArrangement = Arrangement.Center, + verticalAlignment = Alignment.CenterVertically + ) { + Text( + text = stringResource(id = R.string.Finished), + color = AllInTheme.colors.white, + style = AllInTheme.typography.h1, + fontSize = 24.sp + ) + } + } + Text( + text = "Ce bet est arrivé à la date de fin. Vous devez à présent distribuer les gains en validant le pari gagnant.", + color = AllInTheme.colors.allInLightGrey200, + style = AllInTheme.typography.p2, + textAlign = TextAlign.Center + ) + + Spacer(modifier = Modifier.height(16.dp)) + + Text( + text = "Veuillez choisir la réponse finale :", + fontSize = 20.sp, + color = AllInTheme.colors.white, + style = AllInTheme.typography.h1, + modifier = Modifier.fillMaxWidth() + ) + ConfirmationAnswers( + betDetail = betDetail, + selectedAnswer = selectedAnswer + ) { selectedAnswer = if (selectedAnswer == it) null else it } + } + if (selectedAnswer != null) { + AllInButton( + color = AllInTheme.colors.allInPurple, + text = stringResource(id = R.string.Validate), + textColor = AllInTheme.colors.white, + radius = 5.dp, + onClick = { selectedAnswer?.let(onConfirm) }, + modifier = Modifier.align(Alignment.BottomCenter) + ) + } + } + } +} + +@Preview +@Composable +private fun BetConfirmationBottomSheetContentPreview() { + AllInTheme { + BetConfirmationBottomSheetContent( + betDetail = BetDetail( + bet = YesNoBet( + id = "1", + theme = "Theme", + phrase = "Phrase", + endRegisterDate = ZonedDateTime.now(), + endBetDate = ZonedDateTime.now(), + isPublic = true, + betStatus = BetStatus.FINISHED, + 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 + ), + onConfirm = { } + ) { + + } + } +} \ No newline at end of file diff --git a/src/app/src/main/java/fr/iut/alldev/allin/ui/betCreation/BetCreationScreen.kt b/src/app/src/main/java/fr/iut/alldev/allin/ui/betCreation/BetCreationScreen.kt index 2fd0126..7812f01 100644 --- a/src/app/src/main/java/fr/iut/alldev/allin/ui/betCreation/BetCreationScreen.kt +++ b/src/app/src/main/java/fr/iut/alldev/allin/ui/betCreation/BetCreationScreen.kt @@ -64,8 +64,6 @@ fun BetCreationScreen( val themeFieldName = stringResource(id = R.string.Theme) val phraseFieldName = stringResource(id = R.string.Bet_Phrase) - val registerDateFieldName = stringResource(id = R.string.End_registration_date) - val betDateFieldName = stringResource(id = R.string.End_bet_date) LaunchedEffect(key1 = betTypes) { selectionElements = betTypes.map { @@ -152,8 +150,6 @@ fun BetCreationScreen( viewModel.createBet( themeFieldName = themeFieldName, phraseFieldName = phraseFieldName, - registerDateFieldName = registerDateFieldName, - betDateFieldName = betDateFieldName, setLoading = setLoading, onError = { hasError = true }, onSuccess = { diff --git a/src/app/src/main/java/fr/iut/alldev/allin/ui/betCreation/BetCreationViewModel.kt b/src/app/src/main/java/fr/iut/alldev/allin/ui/betCreation/BetCreationViewModel.kt index 5e420ea..a3836c5 100644 --- a/src/app/src/main/java/fr/iut/alldev/allin/ui/betCreation/BetCreationViewModel.kt +++ b/src/app/src/main/java/fr/iut/alldev/allin/ui/betCreation/BetCreationViewModel.kt @@ -17,9 +17,6 @@ import timber.log.Timber import java.time.ZonedDateTime import javax.inject.Inject -const val THEME_MIN_SIZE = 3 -const val PHRASE_MIN_SIZE = 5 - @HiltViewModel class BetCreationViewModel @Inject constructor( @AllInCurrentUser val currentUser: User, @@ -27,13 +24,13 @@ class BetCreationViewModel @Inject constructor( private val keystoreManager: AllInKeystoreManager ) : ViewModel() { - var hasError = mutableStateOf(false) + private var hasError = mutableStateOf(false) var theme = mutableStateOf("") var phrase = mutableStateOf("") 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.NoError) val phraseError = mutableStateOf(FieldErrorState.NoError) @@ -49,20 +46,16 @@ class BetCreationViewModel @Inject constructor( } private fun verifyField( - themeFieldName: String, - phraseFieldName: String, registerDateFieldName: String, betDateFieldName: String, ) { - if (theme.value.length < THEME_MIN_SIZE) { - themeError.value = - FieldErrorState.TooShort(themeFieldName.lowercase(), THEME_MIN_SIZE) + if (theme.value.isBlank()) { + themeError.value = FieldErrorState.Mandatory hasError.value = true } - if (phrase.value.length < PHRASE_MIN_SIZE) { - phraseError.value = - FieldErrorState.TooShort(phraseFieldName.lowercase(), PHRASE_MIN_SIZE) + if (phrase.value.isBlank()) { + phraseError.value = FieldErrorState.Mandatory hasError.value = true } @@ -90,8 +83,6 @@ class BetCreationViewModel @Inject constructor( fun createBet( themeFieldName: String, phraseFieldName: String, - registerDateFieldName: String, - betDateFieldName: String, onError: () -> Unit, setLoading: (Boolean) -> Unit, onSuccess: () -> Unit @@ -103,8 +94,6 @@ class BetCreationViewModel @Inject constructor( verifyField( themeFieldName, phraseFieldName, - registerDateFieldName, - betDateFieldName, ) if (!hasError.value) { @@ -122,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) diff --git a/src/app/src/main/java/fr/iut/alldev/allin/ui/betCreation/tabs/BetCreationScreenAnswerTab.kt b/src/app/src/main/java/fr/iut/alldev/allin/ui/betCreation/tabs/BetCreationScreenAnswerTab.kt index 95c03fd..6f6ac3f 100644 --- a/src/app/src/main/java/fr/iut/alldev/allin/ui/betCreation/tabs/BetCreationScreenAnswerTab.kt +++ b/src/app/src/main/java/fr/iut/alldev/allin/ui/betCreation/tabs/BetCreationScreenAnswerTab.kt @@ -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) diff --git a/src/app/src/main/java/fr/iut/alldev/allin/ui/betHistory/BetCurrentScreen.kt b/src/app/src/main/java/fr/iut/alldev/allin/ui/betHistory/BetCurrentScreen.kt new file mode 100644 index 0000000..540febe --- /dev/null +++ b/src/app/src/main/java/fr/iut/alldev/allin/ui/betHistory/BetCurrentScreen.kt @@ -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 } + ) +} \ No newline at end of file diff --git a/src/app/src/main/java/fr/iut/alldev/allin/ui/betHistory/BetCurrentViewModel.kt b/src/app/src/main/java/fr/iut/alldev/allin/ui/betHistory/BetCurrentViewModel.kt new file mode 100644 index 0000000..59634f5 --- /dev/null +++ b/src/app/src/main/java/fr/iut/alldev/allin/ui/betHistory/BetCurrentViewModel.kt @@ -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> by lazy { + MutableStateFlow(emptyList()) + } + + val bets: StateFlow> by lazy { + _bets.asStateFlow() + .filterNotNull() + .stateIn( + viewModelScope, + SharingStarted.WhileSubscribed(5_000L), + emptyList() + ) + } + + init { + viewModelScope.launch { + _bets.emit( + betRepository.getToConfirm(keystoreManager.getTokenOrEmpty()) + ) + } + } +} \ No newline at end of file diff --git a/src/app/src/main/java/fr/iut/alldev/allin/ui/betHistory/BetHistoryScreen.kt b/src/app/src/main/java/fr/iut/alldev/allin/ui/betHistory/BetHistoryScreen.kt index 60ee7ed..06c7025 100644 --- a/src/app/src/main/java/fr/iut/alldev/allin/ui/betHistory/BetHistoryScreen.kt +++ b/src/app/src/main/java/fr/iut/alldev/allin/ui/betHistory/BetHistoryScreen.kt @@ -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 } + ) } \ No newline at end of file diff --git a/src/app/src/main/java/fr/iut/alldev/allin/ui/betHistory/BetHistoryViewModel.kt b/src/app/src/main/java/fr/iut/alldev/allin/ui/betHistory/BetHistoryViewModel.kt index e4172a6..1c6dc09 100644 --- a/src/app/src/main/java/fr/iut/alldev/allin/ui/betHistory/BetHistoryViewModel.kt +++ b/src/app/src/main/java/fr/iut/alldev/allin/ui/betHistory/BetHistoryViewModel.kt @@ -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> by lazy { + MutableStateFlow(emptyList()) + } + + val bets: StateFlow> by lazy { + _bets.asStateFlow() + .filterNotNull() + .stateIn( + viewModelScope, + SharingStarted.WhileSubscribed(5_000L), + emptyList() + ) + } - val bets: StateFlow?> 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()) + ) + } } } \ No newline at end of file diff --git a/src/app/src/main/java/fr/iut/alldev/allin/ui/betHistory/components/BetHistoryBetStatus.kt b/src/app/src/main/java/fr/iut/alldev/allin/ui/betHistory/components/BetHistoryBetStatus.kt index cbc726d..31e970d 100644 --- a/src/app/src/main/java/fr/iut/alldev/allin/ui/betHistory/components/BetHistoryBetStatus.kt +++ b/src/app/src/main/java/fr/iut/alldev/allin/ui/betHistory/components/BetHistoryBetStatus.kt @@ -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 ) } } \ No newline at end of file diff --git a/src/app/src/main/java/fr/iut/alldev/allin/ui/betHistory/components/BetHistoryScreenCard.kt b/src/app/src/main/java/fr/iut/alldev/allin/ui/betHistory/components/BetHistoryScreenCard.kt index 6375306..5b633ed 100644 --- a/src/app/src/main/java/fr/iut/alldev/allin/ui/betHistory/components/BetHistoryScreenCard.kt +++ b/src/app/src/main/java/fr/iut/alldev/allin/ui/betHistory/components/BetHistoryScreenCard.kt @@ -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 ) } } \ No newline at end of file diff --git a/src/app/src/main/java/fr/iut/alldev/allin/ui/betHistory/components/GenericHistory.kt b/src/app/src/main/java/fr/iut/alldev/allin/ui/betHistory/components/GenericHistory.kt new file mode 100644 index 0000000..250b764 --- /dev/null +++ b/src/app/src/main/java/fr/iut/alldev/allin/ui/betHistory/components/GenericHistory.kt @@ -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 GenericHistory( + title: String, + bets: List, + 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) + ) + } + } +} \ No newline at end of file diff --git a/src/app/src/main/java/fr/iut/alldev/allin/ui/betResult/BetResultBottomSheet.kt b/src/app/src/main/java/fr/iut/alldev/allin/ui/betResult/BetResultBottomSheet.kt index 7fd4eeb..30e816c 100644 --- a/src/app/src/main/java/fr/iut/alldev/allin/ui/betResult/BetResultBottomSheet.kt +++ b/src/app/src/main/java/fr/iut/alldev/allin/ui/betResult/BetResultBottomSheet.kt @@ -1,13 +1,9 @@ package fr.iut.alldev.allin.ui.betResult -import androidx.compose.foundation.MarqueeSpacing -import androidx.compose.foundation.background -import androidx.compose.foundation.basicMarquee import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.fillMaxSize -import androidx.compose.foundation.layout.offset import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.size import androidx.compose.material.icons.Icons @@ -18,8 +14,6 @@ import androidx.compose.material3.SheetState import androidx.compose.runtime.Composable import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier -import androidx.compose.ui.draw.rotate -import androidx.compose.ui.draw.scale import androidx.compose.ui.res.painterResource import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.unit.dp @@ -34,6 +28,7 @@ import fr.iut.alldev.allin.ui.betResult.components.BetResultBottomSheetBetCard import fr.iut.alldev.allin.ui.betResult.components.BetResultBottomSheetContentCoinAmount import fr.iut.alldev.allin.ui.betResult.components.BetResultBottomSheetContentCongratulations import fr.iut.alldev.allin.ui.core.AllInBottomSheet +import fr.iut.alldev.allin.ui.core.AllInMarqueeBox import java.time.ZonedDateTime @Composable @@ -76,22 +71,7 @@ fun BetResultBottomSheetContent( odds: Float, onClose: () -> Unit ) { - Box( - modifier = Modifier - .fillMaxSize() - .background(AllInTheme.colors.allInMainGradientReverse), - ) { - Icon( - painter = painterResource(id = R.drawable.allin_marquee), - contentDescription = null, - modifier = Modifier - .fillMaxSize() - .rotate(11f) - .scale(1.2f) - .offset(x = (-24).dp) - .basicMarquee(spacing = MarqueeSpacing(0.dp)), - tint = AllInTheme.colors.white.copy(alpha = .05f) - ) + AllInMarqueeBox(backgroundBrush = AllInTheme.colors.allInMainGradientReverse) { Box( modifier = Modifier .fillMaxSize() @@ -146,7 +126,6 @@ fun BetResultBottomSheetContent( } @Preview -@Preview(widthDp = 800, heightDp = 1280) @Composable private fun BetResultBottomSheetContentPreview() { AllInTheme { @@ -160,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, diff --git a/src/app/src/main/java/fr/iut/alldev/allin/ui/betResult/components/BetResultBottomSheetBetCard.kt b/src/app/src/main/java/fr/iut/alldev/allin/ui/betResult/components/BetResultBottomSheetBetCard.kt index d0050dc..547ea16 100644 --- a/src/app/src/main/java/fr/iut/alldev/allin/ui/betResult/components/BetResultBottomSheetBetCard.kt +++ b/src/app/src/main/java/fr/iut/alldev/allin/ui/betResult/components/BetResultBottomSheetBetCard.kt @@ -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 diff --git a/src/app/src/main/java/fr/iut/alldev/allin/ui/betStatus/BetStatusBottomSheet.kt b/src/app/src/main/java/fr/iut/alldev/allin/ui/betStatus/BetStatusBottomSheet.kt index 7e193fc..ce5b661 100644 --- a/src/app/src/main/java/fr/iut/alldev/allin/ui/betStatus/BetStatusBottomSheet.kt +++ b/src/app/src/main/java/fr/iut/alldev/allin/ui/betStatus/BetStatusBottomSheet.kt @@ -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(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, diff --git a/src/app/src/main/java/fr/iut/alldev/allin/ui/betStatus/components/YesNoDetailsLine.kt b/src/app/src/main/java/fr/iut/alldev/allin/ui/betStatus/components/YesNoDetailsLine.kt index 784b368..78f0f91 100644 --- a/src/app/src/main/java/fr/iut/alldev/allin/ui/betStatus/components/YesNoDetailsLine.kt +++ b/src/app/src/main/java/fr/iut/alldev/allin/ui/betStatus/components/YesNoDetailsLine.kt @@ -7,42 +7,11 @@ import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.runtime.Composable import androidx.compose.ui.Modifier import androidx.compose.ui.graphics.painter.Painter -import androidx.compose.ui.graphics.vector.ImageVector -import androidx.compose.ui.graphics.vector.rememberVectorPainter import androidx.compose.ui.tooling.preview.Preview import fr.iut.alldev.allin.theme.AllInTheme import fr.iut.alldev.allin.ui.core.AllInTextIcon import fr.iut.alldev.allin.ui.core.IconPosition -@Composable -fun YesNoDetailsLine( - icon: ImageVector, - yesText: String, - noText: String, -) { - Row( - modifier = Modifier.fillMaxWidth(), - horizontalArrangement = Arrangement.SpaceBetween - ) { - AllInTextIcon( - text = yesText, - color = AllInTheme.colors.allInBlue, - icon = rememberVectorPainter(image = icon), - position = IconPosition.LEADING, - size = 10, - iconSize = 15 - ) - AllInTextIcon( - text = noText, - color = AllInTheme.colors.allInBarPink, - icon = rememberVectorPainter(image = icon), - position = IconPosition.TRAILING, - size = 10, - iconSize = 15 - ) - } -} - @Composable fun YesNoDetailsLine( icon: Painter, @@ -58,7 +27,7 @@ fun YesNoDetailsLine( color = AllInTheme.colors.allInBlue, icon = icon, position = IconPosition.LEADING, - size = 10, + size = 15, iconSize = 15 ) AllInTextIcon( @@ -66,7 +35,7 @@ fun YesNoDetailsLine( color = AllInTheme.colors.allInBarPink, icon = icon, position = IconPosition.TRAILING, - size = 10, + size = 15, iconSize = 15 ) } diff --git a/src/app/src/main/java/fr/iut/alldev/allin/ui/betStatus/vo/BetStatusBottomSheetBetDisplayer.kt b/src/app/src/main/java/fr/iut/alldev/allin/ui/betStatus/vo/BetStatusBottomSheetBetDisplayer.kt index e316c59..a4dda14 100644 --- a/src/app/src/main/java/fr/iut/alldev/allin/ui/betStatus/vo/BetStatusBottomSheetBetDisplayer.kt +++ b/src/app/src/main/java/fr/iut/alldev/allin/ui/betStatus/vo/BetStatusBottomSheetBetDisplayer.kt @@ -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,9 +24,12 @@ 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 import androidx.compose.ui.platform.LocalConfiguration import androidx.compose.ui.res.stringResource import androidx.compose.ui.text.font.FontWeight @@ -64,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) } @@ -98,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, @@ -118,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 @@ -131,17 +149,17 @@ class BetStatusBottomSheetBetDisplayer( noText = response2Answer?.totalStakes?.toString() ?: "0" ) YesNoDetailsLine( - icon = Icons.Filled.People, + icon = rememberVectorPainter(image = Icons.Filled.People), yesText = response1Answer?.totalParticipants?.toString() ?: "0", noText = response2Answer?.totalParticipants?.toString() ?: "0" ) YesNoDetailsLine( - icon = Icons.Filled.WorkspacePremium, + icon = rememberVectorPainter(image = Icons.Filled.WorkspacePremium), yesText = response1Answer?.highestStake?.toString() ?: "0", noText = response2Answer?.highestStake?.toString() ?: "0" ) YesNoDetailsLine( - icon = Icons.Filled.EmojiEvents, + icon = rememberVectorPainter(image = Icons.Filled.EmojiEvents), yesText = "x${response1Answer?.odds?.formatToSimple(locale) ?: "1.00"}", noText = "x${response2Answer?.odds?.formatToSimple(locale) ?: "1.00"}" ) @@ -183,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 ) } diff --git a/src/app/src/main/java/fr/iut/alldev/allin/ui/core/AllInMarqueeBox.kt b/src/app/src/main/java/fr/iut/alldev/allin/ui/core/AllInMarqueeBox.kt new file mode 100644 index 0000000..22c420e --- /dev/null +++ b/src/app/src/main/java/fr/iut/alldev/allin/ui/core/AllInMarqueeBox.kt @@ -0,0 +1,65 @@ +package fr.iut.alldev.allin.ui.core + +import androidx.compose.foundation.MarqueeSpacing +import androidx.compose.foundation.background +import androidx.compose.foundation.basicMarquee +import androidx.compose.foundation.layout.Box +import androidx.compose.foundation.layout.BoxScope +import androidx.compose.foundation.layout.aspectRatio +import androidx.compose.foundation.layout.fillMaxHeight +import androidx.compose.foundation.layout.fillMaxSize +import androidx.compose.material3.Text +import androidx.compose.runtime.Composable +import androidx.compose.ui.Modifier +import androidx.compose.ui.draw.rotate +import androidx.compose.ui.draw.scale +import androidx.compose.ui.graphics.Brush +import androidx.compose.ui.graphics.Color +import androidx.compose.ui.res.painterResource +import androidx.compose.ui.tooling.preview.Preview +import androidx.compose.ui.unit.dp +import fr.iut.alldev.allin.R +import fr.iut.alldev.allin.theme.AllInTheme + +@Composable +fun AllInMarqueeBox( + backgroundColor: Color = AllInTheme.themeColors.mainSurface, + backgroundBrush: Brush? = null, + content: @Composable BoxScope.() -> Unit + +) { + Box( + modifier = Modifier + .fillMaxSize().let { itModifier -> + backgroundBrush?.let { + itModifier.background(it) + } ?: itModifier.background(backgroundColor) + } + ) { + Icon( + painter = painterResource(id = R.drawable.allin_marquee), + contentDescription = null, + modifier = Modifier + .fillMaxHeight() + .aspectRatio(1f, true) + .scale(1.2f) + .rotate(11f) + .basicMarquee(spacing = MarqueeSpacing(0.dp)), + tint = AllInTheme.colors.white.copy(alpha = .05f) + ) + content() + } +} + +@Preview +@Preview(widthDp = 800, heightDp = 1280) +@Composable +private fun AllInMarqueeBoxPreview() { + AllInTheme { + AllInMarqueeBox( + backgroundBrush = AllInTheme.colors.allInMainGradient, + ) { + Text("CONTENT") + } + } +} \ No newline at end of file diff --git a/src/app/src/main/java/fr/iut/alldev/allin/ui/core/bet/BetCard.kt b/src/app/src/main/java/fr/iut/alldev/allin/ui/core/bet/BetCard.kt index a7e6613..51e8048 100644 --- a/src/app/src/main/java/fr/iut/alldev/allin/ui/core/bet/BetCard.kt +++ b/src/app/src/main/java/fr/iut/alldev/allin/ui/core/bet/BetCard.kt @@ -68,8 +68,8 @@ private fun BetCardPreview() { title = "Title", date = "Date", time = "Time", - status = BetStatus.Waiting - ){ + status = BetStatus.WAITING + ) { Text("Content") } } diff --git a/src/app/src/main/java/fr/iut/alldev/allin/ui/main/MainScreen.kt b/src/app/src/main/java/fr/iut/alldev/allin/ui/main/MainScreen.kt index fdd9642..72f683c 100644 --- a/src/app/src/main/java/fr/iut/alldev/allin/ui/main/MainScreen.kt +++ b/src/app/src/main/java/fr/iut/alldev/allin/ui/main/MainScreen.kt @@ -2,16 +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.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 @@ -32,28 +46,6 @@ private val topLevelDestinations = listOf( TopLevelDestination.CurrentBets ) -@Composable -private fun rememberBetStatusVisibilities() - : Triple, MutableState, (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( @@ -65,16 +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, setWonBet) = remember { mainViewModel.wonBet } - val (statusVisibility, sheetBackVisibility, setStatusVisibility) = rememberBetStatusVisibilities() - val (participateSheetVisibility, setParticipateSheetVisibility) = remember { - mutableStateOf(false) + 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,25 +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 = wonBet, - username = currentUser.user.username, - coinAmount = 1630, - stake = 1630, - winnings = 1630, - odds = 3.62f - ) + + events.firstOrNull()?.let { + it.Display(sheetState = eventBottomSheetState) { + mainViewModel.dismissedEvents += it + events.removeFirstOrNull() + } } BetStatusBottomSheet( @@ -191,14 +184,6 @@ fun MainScreen( setParticipateSheetVisibility = setParticipateSheetVisibility ) AllInLoading(visible = loading) - BackHandler( - enabled = drawerState.isOpen - ) { - scope.launch { - drawerState.close() - } - } - } diff --git a/src/app/src/main/java/fr/iut/alldev/allin/ui/main/MainViewModel.kt b/src/app/src/main/java/fr/iut/alldev/allin/ui/main/MainViewModel.kt index a666bef..6f30214 100644 --- a/src/app/src/main/java/fr/iut/alldev/allin/ui/main/MainViewModel.kt +++ b/src/app/src/main/java/fr/iut/alldev/allin/ui/main/MainViewModel.kt @@ -2,6 +2,7 @@ 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 @@ -14,6 +15,9 @@ 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 @@ -34,28 +38,49 @@ class MainViewModel @Inject constructor( val currentUserState = UserState(currentUser) val selectedBet = mutableStateOf(null) - val wonBet = mutableStateOf( - null - /* YesNoBet( - id = "1", - theme = "Theme", - phrase = "Phrase", - endRegisterDate = ZonedDateTime.now(), - endBetDate = ZonedDateTime.now(), - isPublic = true, - betStatus = BetStatus.Finished(BetFinishedStatus.WON), - creator = "creator" - )*/ - ) + val dismissedEvents = mutableStateListOf() + val events = mutableStateListOf() val snackbarContent: MutableState 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()) } } @@ -77,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 diff --git a/src/app/src/main/java/fr/iut/alldev/allin/ui/main/event/AllInEvent.kt b/src/app/src/main/java/fr/iut/alldev/allin/ui/main/event/AllInEvent.kt new file mode 100644 index 0000000..6ef18a0 --- /dev/null +++ b/src/app/src/main/java/fr/iut/alldev/allin/ui/main/event/AllInEvent.kt @@ -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 + ) +} \ No newline at end of file diff --git a/src/app/src/main/java/fr/iut/alldev/allin/ui/main/event/ToConfirmBet.kt b/src/app/src/main/java/fr/iut/alldev/allin/ui/main/event/ToConfirmBet.kt new file mode 100644 index 0000000..021ac6b --- /dev/null +++ b/src/app/src/main/java/fr/iut/alldev/allin/ui/main/event/ToConfirmBet.kt @@ -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 + } + +} diff --git a/src/app/src/main/java/fr/iut/alldev/allin/ui/main/event/WonBet.kt b/src/app/src/main/java/fr/iut/alldev/allin/ui/main/event/WonBet.kt new file mode 100644 index 0000000..cda4d09 --- /dev/null +++ b/src/app/src/main/java/fr/iut/alldev/allin/ui/main/event/WonBet.kt @@ -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 + ) + } + +} diff --git a/src/app/src/main/java/fr/iut/alldev/allin/ui/navigation/NavHost.kt b/src/app/src/main/java/fr/iut/alldev/allin/ui/navigation/NavHost.kt index e36ba79..e8f4dc2 100644 --- a/src/app/src/main/java/fr/iut/alldev/allin/ui/navigation/NavHost.kt +++ b/src/app/src/main/java/fr/iut/alldev/allin/ui/navigation/NavHost.kt @@ -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() } } } diff --git a/src/app/src/main/java/fr/iut/alldev/allin/ui/navigation/TopLevelDestination.kt b/src/app/src/main/java/fr/iut/alldev/allin/ui/navigation/TopLevelDestination.kt index b6d2f3e..e5ac929 100644 --- a/src/app/src/main/java/fr/iut/alldev/allin/ui/navigation/TopLevelDestination.kt +++ b/src/app/src/main/java/fr/iut/alldev/allin/ui/navigation/TopLevelDestination.kt @@ -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 diff --git a/src/app/src/main/java/fr/iut/alldev/allin/ui/preview/BetDetailPreviewProvider.kt b/src/app/src/main/java/fr/iut/alldev/allin/ui/preview/BetDetailPreviewProvider.kt index 154f04c..e3d9d95 100644 --- a/src/app/src/main/java/fr/iut/alldev/allin/ui/preview/BetDetailPreviewProvider.kt +++ b/src/app/src/main/java/fr/iut/alldev/allin/ui/preview/BetDetailPreviewProvider.kt @@ -25,7 +25,7 @@ class BetDetailPreviewProvider : PreviewParameterProvider { totalParticipants = 1, highestStake = 150, odds = 2.0f - ), + ) ), participations = listOf( Participation( diff --git a/src/app/src/main/java/fr/iut/alldev/allin/ui/preview/BetPreviewProvider.kt b/src/app/src/main/java/fr/iut/alldev/allin/ui/preview/BetPreviewProvider.kt index 3a97a59..4be8aed 100644 --- a/src/app/src/main/java/fr/iut/alldev/allin/ui/preview/BetPreviewProvider.kt +++ b/src/app/src/main/java/fr/iut/alldev/allin/ui/preview/BetPreviewProvider.kt @@ -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 { 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 { 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 { endRegisterDate = ZonedDateTime.now(), endBetDate = ZonedDateTime.now(), isPublic = true, - betStatus = BetStatus.Finished(BetFinishedStatus.WON), + betStatus = BetStatus.FINISHED, creator = "creator", possibleAnswers = listOf( "Answer 1", diff --git a/src/app/src/main/java/fr/iut/alldev/allin/ui/preview/BetStatusPreviewProvider.kt b/src/app/src/main/java/fr/iut/alldev/allin/ui/preview/BetStatusPreviewProvider.kt index ccb3cd8..30f4808 100644 --- a/src/app/src/main/java/fr/iut/alldev/allin/ui/preview/BetStatusPreviewProvider.kt +++ b/src/app/src/main/java/fr/iut/alldev/allin/ui/preview/BetStatusPreviewProvider.kt @@ -4,5 +4,5 @@ import androidx.compose.ui.tooling.preview.PreviewParameterProvider import fr.iut.alldev.allin.data.model.bet.BetStatus class BetStatusPreviewProvider : PreviewParameterProvider { - override val values = BetStatus.entries + override val values = BetStatus.entries.asSequence() } \ No newline at end of file diff --git a/src/app/src/main/java/fr/iut/alldev/allin/ui/preview/BetWithStatusPreviewProvider.kt b/src/app/src/main/java/fr/iut/alldev/allin/ui/preview/BetWithStatusPreviewProvider.kt index e5b3721..3cfab61 100644 --- a/src/app/src/main/java/fr/iut/alldev/allin/ui/preview/BetWithStatusPreviewProvider.kt +++ b/src/app/src/main/java/fr/iut/alldev/allin/ui/preview/BetWithStatusPreviewProvider.kt @@ -10,7 +10,7 @@ import java.time.ZonedDateTime class BetWithStatusPreviewProvider : PreviewParameterProvider { - override val values = BetStatus.entries.flatMap { status -> + override val values = BetStatus.entries.asSequence().flatMap { status -> sequenceOf( YesNoBet( id = "1", diff --git a/src/app/src/main/res/values-fr/strings.xml b/src/app/src/main/res/values-fr/strings.xml index 0f1e299..4839222 100644 --- a/src/app/src/main/res/values-fr/strings.xml +++ b/src/app/src/main/res/values-fr/strings.xml @@ -14,6 +14,7 @@ Valider Annuler OK + Ce champ est obligatoire. Le %s doit contenir au moins %d caractères. Le %s a un mauvais format : %s. Les champs ne sont pas identiques. @@ -117,6 +118,8 @@ Terminé ! En cours… En attente… + Fermeture… + Annulé Faites vos paris Liste des participants diff --git a/src/app/src/main/res/values/strings.xml b/src/app/src/main/res/values/strings.xml index 328e981..4409c33 100644 --- a/src/app/src/main/res/values/strings.xml +++ b/src/app/src/main/res/values/strings.xml @@ -16,6 +16,7 @@ Validate Cancel OK + This field is mandatory. The %s must contain at least %d characters. The %s has bad format : %s. The fields are not identical. @@ -117,6 +118,8 @@ Finished ! In progress… Waiting… + Closing… + Cancelled Place your bets Participants diff --git a/src/data/src/main/java/fr/iut/alldev/allin/data/api/AllInApi.kt b/src/data/src/main/java/fr/iut/alldev/allin/data/api/AllInApi.kt index 550c182..1ade41b 100644 --- a/src/data/src/main/java/fr/iut/alldev/allin/data/api/AllInApi.kt +++ b/src/data/src/main/java/fr/iut/alldev/allin/data/api/AllInApi.kt @@ -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 + suspend fun getAllBets(@Header("Authorization") token: String): List + + @GET("bets/toConfirm") + suspend fun getToConfirm(@Header("Authorization") token: String): List + + @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 + + @GET("bets/history") + suspend fun getBetHistory( + @Header("Authorization") token: String + ): List + + @GET("bets/getWon") + suspend fun getWon( + @Header("Authorization") token: String + ): List @POST("participations/add") - suspend fun participateToBet(@Header("Authorization") token: String, @Body body: RequestParticipation) + suspend fun participateToBet( + @Header("Authorization") token: String, + @Body body: RequestParticipation + ) } \ No newline at end of file diff --git a/src/data/src/main/java/fr/iut/alldev/allin/data/api/MockAllInApi.kt b/src/data/src/main/java/fr/iut/alldev/allin/data/api/MockAllInApi.kt index f2b7f72..1cfeab6 100644 --- a/src/data/src/main/java/fr/iut/alldev/allin/data/api/MockAllInApi.kt +++ b/src/data/src/main/java/fr/iut/alldev/allin/data/api/MockAllInApi.kt @@ -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): List { + private fun getAnswerDetails( + bet: ResponseBet, + participations: List + ): List { 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 = mockBets.toList() + override suspend fun getAllBets(token: String): List { + getUserFromToken(token) ?: throw AllInAPIException("Invalid login/password.") + return mockBets + } + + override suspend fun getToConfirm(token: String): List { + 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 { + return emptyList() + } + + override suspend fun getBetHistory(token: String): List { + return emptyList() + } + + override suspend fun getWon(token: String): List { + 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, ) -) \ No newline at end of file +) + +private val mockResults by lazy { mutableListOf() } diff --git a/src/data/src/main/java/fr/iut/alldev/allin/data/api/model/ApiBet.kt b/src/data/src/main/java/fr/iut/alldev/allin/data/api/model/ApiBet.kt index 3610752..0b6ad62 100644 --- a/src/data/src/main/java/fr/iut/alldev/allin/data/api/model/ApiBet.kt +++ b/src/data/src/main/java/fr/iut/alldev/allin/data/api/model/ApiBet.kt @@ -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, @@ -100,4 +106,34 @@ data class ResponseBetDetail( userParticipation = userParticipation?.toParticipation() ) +} + +@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 + ) } \ No newline at end of file diff --git a/src/data/src/main/java/fr/iut/alldev/allin/data/api/model/ApiUser.kt b/src/data/src/main/java/fr/iut/alldev/allin/data/api/model/ApiUser.kt index f6e021c..79562d4 100644 --- a/src/data/src/main/java/fr/iut/alldev/allin/data/api/model/ApiUser.kt +++ b/src/data/src/main/java/fr/iut/alldev/allin/data/api/model/ApiUser.kt @@ -25,7 +25,7 @@ data class ResponseUser( id = id, username = username, email = email, - coins = nbCoins.toInt() + coins = nbCoins ) } diff --git a/src/data/src/main/java/fr/iut/alldev/allin/data/model/bet/Bet.kt b/src/data/src/main/java/fr/iut/alldev/allin/data/model/bet/Bet.kt index 1f9cc17..fff2d72 100644 --- a/src/data/src/main/java/fr/iut/alldev/allin/data/model/bet/Bet.kt +++ b/src/data/src/main/java/fr/iut/alldev/allin/data/model/bet/Bet.kt @@ -13,16 +13,18 @@ sealed class Bet( open val isPublic: Boolean, open val betStatus: BetStatus, ) { + abstract fun getBetType(): BetType abstract fun getResponses(): List fun toRequestBet(): RequestBet { return RequestBet( - id = "", + id = id, theme = theme, sentenceBet = phrase, endRegistration = endRegisterDate, endBet = endBetDate, isPrivate = !isPublic, - response = getResponses() + response = getResponses(), + type = getBetType() ) } } \ No newline at end of file diff --git a/src/data/src/main/java/fr/iut/alldev/allin/data/model/bet/BetFactory.kt b/src/data/src/main/java/fr/iut/alldev/allin/data/model/bet/BetFactory.kt index 8df4afb..b4ea653 100644 --- a/src/data/src/main/java/fr/iut/alldev/allin/data/model/bet/BetFactory.kt +++ b/src/data/src/main/java/fr/iut/alldev/allin/data/model/bet/BetFactory.kt @@ -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 ) } diff --git a/src/data/src/main/java/fr/iut/alldev/allin/data/model/bet/BetResult.kt b/src/data/src/main/java/fr/iut/alldev/allin/data/model/bet/BetResult.kt new file mode 100644 index 0000000..de461d1 --- /dev/null +++ b/src/data/src/main/java/fr/iut/alldev/allin/data/model/bet/BetResult.kt @@ -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 +) \ No newline at end of file diff --git a/src/data/src/main/java/fr/iut/alldev/allin/data/model/bet/BetStatus.kt b/src/data/src/main/java/fr/iut/alldev/allin/data/model/bet/BetStatus.kt index 66d27a8..5d1adae 100644 --- a/src/data/src/main/java/fr/iut/alldev/allin/data/model/bet/BetStatus.kt +++ b/src/data/src/main/java/fr/iut/alldev/allin/data/model/bet/BetStatus.kt @@ -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 } \ No newline at end of file diff --git a/src/data/src/main/java/fr/iut/alldev/allin/data/model/bet/BetType.kt b/src/data/src/main/java/fr/iut/alldev/allin/data/model/bet/BetType.kt index 2fa2bc7..65464f1 100644 --- a/src/data/src/main/java/fr/iut/alldev/allin/data/model/bet/BetType.kt +++ b/src/data/src/main/java/fr/iut/alldev/allin/data/model/bet/BetType.kt @@ -1,7 +1,7 @@ package fr.iut.alldev.allin.data.model.bet enum class BetType { - YES_NO, + BINARY, MATCH, CUSTOM } diff --git a/src/data/src/main/java/fr/iut/alldev/allin/data/model/bet/CustomBet.kt b/src/data/src/main/java/fr/iut/alldev/allin/data/model/bet/CustomBet.kt index a252e51..fa7d295 100644 --- a/src/data/src/main/java/fr/iut/alldev/allin/data/model/bet/CustomBet.kt +++ b/src/data/src/main/java/fr/iut/alldev/allin/data/model/bet/CustomBet.kt @@ -22,5 +22,7 @@ data class CustomBet( isPublic, betStatus ) { + override fun getBetType() = BetType.CUSTOM + override fun getResponses(): List = possibleAnswers } diff --git a/src/data/src/main/java/fr/iut/alldev/allin/data/model/bet/MatchBet.kt b/src/data/src/main/java/fr/iut/alldev/allin/data/model/bet/MatchBet.kt index 03a829e..d05736a 100644 --- a/src/data/src/main/java/fr/iut/alldev/allin/data/model/bet/MatchBet.kt +++ b/src/data/src/main/java/fr/iut/alldev/allin/data/model/bet/MatchBet.kt @@ -23,6 +23,7 @@ data class MatchBet( isPublic, betStatus ) { + override fun getBetType() = BetType.MATCH override fun getResponses(): List = listOf(nameTeam1, nameTeam2) } \ No newline at end of file diff --git a/src/data/src/main/java/fr/iut/alldev/allin/data/model/bet/YesNoBet.kt b/src/data/src/main/java/fr/iut/alldev/allin/data/model/bet/YesNoBet.kt index 5e28f06..fc1cf47 100644 --- a/src/data/src/main/java/fr/iut/alldev/allin/data/model/bet/YesNoBet.kt +++ b/src/data/src/main/java/fr/iut/alldev/allin/data/model/bet/YesNoBet.kt @@ -24,5 +24,6 @@ data class YesNoBet( isPublic, betStatus ) { + override fun getBetType() = BetType.BINARY override fun getResponses(): List = listOf(YES_VALUE, NO_VALUE) } \ No newline at end of file diff --git a/src/data/src/main/java/fr/iut/alldev/allin/data/model/bet/vo/BetResult.kt b/src/data/src/main/java/fr/iut/alldev/allin/data/model/bet/vo/BetResult.kt index 407bc4e..dc485b5 100644 --- a/src/data/src/main/java/fr/iut/alldev/allin/data/model/bet/vo/BetResult.kt +++ b/src/data/src/main/java/fr/iut/alldev/allin/data/model/bet/vo/BetResult.kt @@ -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, - val answerDetail: BetAnswerDetail + val betId: String, + val result: String ) \ No newline at end of file diff --git a/src/data/src/main/java/fr/iut/alldev/allin/data/repository/BetRepository.kt b/src/data/src/main/java/fr/iut/alldev/allin/data/repository/BetRepository.kt index ed4bcd8..a890896 100644 --- a/src/data/src/main/java/fr/iut/alldev/allin/data/repository/BetRepository.kt +++ b/src/data/src/main/java/fr/iut/alldev/allin/data/repository/BetRepository.kt @@ -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> - abstract suspend fun getCurrentBets(): Flow> + abstract suspend fun getHistory(token: String): List + abstract suspend fun getCurrentBets(token: String): List abstract suspend fun getBet(id: String, token: String): BetDetail abstract suspend fun participateToBet(participation: Participation, token: String) - abstract suspend fun getAllBets(): Flow> + abstract suspend fun getAllBets(token: String): List + abstract suspend fun confirmBet(token: String, id: String, response: String) + abstract suspend fun getToConfirm(token: String): List + abstract suspend fun getWon(token: String): List } \ No newline at end of file diff --git a/src/data/src/main/java/fr/iut/alldev/allin/data/repository/impl/BetRepositoryImpl.kt b/src/data/src/main/java/fr/iut/alldev/allin/data/repository/impl/BetRepositoryImpl.kt index 7748092..7232099 100644 --- a/src/data/src/main/java/fr/iut/alldev/allin/data/repository/impl/BetRepositoryImpl.kt +++ b/src/data/src/main/java/fr/iut/alldev/allin/data/repository/impl/BetRepositoryImpl.kt @@ -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> { - 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 { + return api.getBetHistory(token.formatBearerToken()).map { + it.toBetResultDetail() + } } - override suspend fun getCurrentBets(): Flow> { - // 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 { + return api.getToConfirm(token.formatBearerToken()).map { + it.toBetDetail() + } } override suspend fun getBet(id: String, token: String): BetDetail { @@ -107,13 +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(): Flow> { - return flowOf( - api.getAllBets().map { it.toBet() } - ) + override suspend fun getAllBets(token: String): List = + api.getAllBets(token.formatBearerToken()).map { it.toBet() } + + override suspend fun getToConfirm(token: String): List = + api.getToConfirm(token.formatBearerToken()).map { it.toBetDetail() } + + override suspend fun getWon(token: String): List = + api.getWon(token.formatBearerToken()).map { it.toBetResultDetail() } + + + override suspend fun confirmBet(token: String, id: String, response: String) { + api.confirmBet(token.formatBearerToken(), id, response) } } \ No newline at end of file