From 53b6eacfff9eb0786518bdeb80e62987c697cc52 Mon Sep 17 00:00:00 2001 From: "arthur.valin" Date: Fri, 9 Feb 2024 12:31:58 +0100 Subject: [PATCH] Fix events --- .../fr/iut/alldev/allin/test/mock/Bets.kt | 6 +- .../fr/iut/alldev/allin/ext/BetStatusExt.kt | 44 ++++--- .../fr/iut/alldev/allin/ext/BetTypeExt.kt | 4 +- .../allin/keystore/AllInKeystoreManager.kt | 1 + .../iut/alldev/allin/ui/bet/BetViewModel.kt | 4 +- .../BetConfirmationBottomSheet.kt | 33 ++++-- .../ui/betCreation/BetCreationViewModel.kt | 4 +- .../tabs/BetCreationScreenAnswerTab.kt | 2 +- .../allin/ui/betHistory/BetCurrentScreen.kt | 30 +++++ .../ui/betHistory/BetCurrentViewModel.kt | 44 +++++++ .../allin/ui/betHistory/BetHistoryScreen.kt | 63 +++------- .../ui/betHistory/BetHistoryViewModel.kt | 41 ++++--- .../components/BetHistoryBetStatus.kt | 8 +- .../components/BetHistoryScreenCard.kt | 7 +- .../betHistory/components/GenericHistory.kt | 60 ++++++++++ .../ui/betResult/BetResultBottomSheet.kt | 2 +- .../components/BetResultBottomSheetBetCard.kt | 3 +- .../ui/betStatus/BetStatusBottomSheet.kt | 26 ++++- .../vo/BetStatusBottomSheetBetDisplayer.kt | 33 ++++-- .../iut/alldev/allin/ui/core/bet/BetCard.kt | 4 +- .../fr/iut/alldev/allin/ui/main/MainScreen.kt | 110 +++++++----------- .../iut/alldev/allin/ui/main/MainViewModel.kt | 105 ++++++++--------- .../alldev/allin/ui/main/event/AllInEvent.kt | 12 ++ .../allin/ui/main/event/ToConfirmBet.kt | 39 +++++++ .../iut/alldev/allin/ui/main/event/WonBet.kt | 31 +++++ .../iut/alldev/allin/ui/navigation/NavHost.kt | 36 +++--- .../ui/navigation/TopLevelDestination.kt | 4 +- .../allin/ui/preview/BetPreviewProvider.kt | 7 +- .../ui/preview/BetStatusPreviewProvider.kt | 2 +- .../preview/BetWithStatusPreviewProvider.kt | 2 +- src/app/src/main/res/values-fr/strings.xml | 2 + src/app/src/main/res/values/strings.xml | 2 + .../fr/iut/alldev/allin/data/api/AllInApi.kt | 38 +++++- .../iut/alldev/allin/data/api/MockAllInApi.kt | 80 +++++++++++-- .../iut/alldev/allin/data/api/model/ApiBet.kt | 40 ++++++- .../alldev/allin/data/api/model/ApiUser.kt | 2 +- .../fr/iut/alldev/allin/data/model/bet/Bet.kt | 6 +- .../alldev/allin/data/model/bet/BetFactory.kt | 8 +- .../alldev/allin/data/model/bet/BetResult.kt | 14 +++ .../alldev/allin/data/model/bet/BetStatus.kt | 22 +--- .../alldev/allin/data/model/bet/BetType.kt | 2 +- .../alldev/allin/data/model/bet/CustomBet.kt | 2 + .../alldev/allin/data/model/bet/MatchBet.kt | 1 + .../alldev/allin/data/model/bet/YesNoBet.kt | 1 + .../allin/data/model/bet/vo/BetResult.kt | 8 +- .../allin/data/repository/BetRepository.kt | 11 +- .../data/repository/impl/BetRepositoryImpl.kt | 105 ++++------------- 47 files changed, 703 insertions(+), 408 deletions(-) create mode 100644 src/app/src/main/java/fr/iut/alldev/allin/ui/betHistory/BetCurrentScreen.kt create mode 100644 src/app/src/main/java/fr/iut/alldev/allin/ui/betHistory/BetCurrentViewModel.kt create mode 100644 src/app/src/main/java/fr/iut/alldev/allin/ui/betHistory/components/GenericHistory.kt create mode 100644 src/app/src/main/java/fr/iut/alldev/allin/ui/main/event/AllInEvent.kt create mode 100644 src/app/src/main/java/fr/iut/alldev/allin/ui/main/event/ToConfirmBet.kt create mode 100644 src/app/src/main/java/fr/iut/alldev/allin/ui/main/event/WonBet.kt create mode 100644 src/data/src/main/java/fr/iut/alldev/allin/data/model/bet/BetResult.kt 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 9b38b75..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 @@ -11,7 +11,7 @@ object Bets { endRegisterDate = ZonedDateTime.now(), endBetDate = ZonedDateTime.now(), isPublic = true, - betStatus = BetStatus.InProgress, + betStatus = BetStatus.IN_PROGRESS, creator = "creator", id = "" ), @@ -21,7 +21,7 @@ object Bets { endRegisterDate = ZonedDateTime.now(), endBetDate = ZonedDateTime.now(), isPublic = true, - betStatus = BetStatus.InProgress, + betStatus = BetStatus.IN_PROGRESS, nameTeam1 = "Team_1", nameTeam2 = "Team_2", creator = "creator", @@ -33,7 +33,7 @@ object Bets { endRegisterDate = ZonedDateTime.now(), endBetDate = ZonedDateTime.now(), isPublic = true, - betStatus = BetStatus.InProgress, + betStatus = BetStatus.IN_PROGRESS, creator = "creator", possibleAnswers = listOf( "Answer 1", 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/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 c72f746..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,6 +5,7 @@ import androidx.lifecycle.viewModelScope import dagger.hilt.android.lifecycle.HiltViewModel import fr.iut.alldev.allin.data.model.bet.Bet import fr.iut.alldev.allin.data.repository.BetRepository +import fr.iut.alldev.allin.keystore.AllInKeystoreManager import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.SharingStarted import kotlinx.coroutines.flow.StateFlow @@ -16,6 +17,7 @@ import javax.inject.Inject @HiltViewModel class BetViewModel @Inject constructor( + private val keystoreManager: AllInKeystoreManager, private val betRepository: BetRepository ) : ViewModel() { @@ -45,7 +47,7 @@ class BetViewModel @Inject constructor( private suspend fun refreshData() { runCatching { - _bets.emit(betRepository.getAllBets()) + _bets.emit(betRepository.getAllBets(keystoreManager.getTokenOrEmpty())) } } 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 index de62423..7723035 100644 --- 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 @@ -41,7 +41,6 @@ import androidx.core.os.ConfigurationCompat import fr.iut.alldev.allin.R import fr.iut.alldev.allin.data.ext.formatToMediumDateNoYear import fr.iut.alldev.allin.data.ext.formatToTime -import fr.iut.alldev.allin.data.model.bet.BetFinishedStatus import fr.iut.alldev.allin.data.model.bet.BetStatus import fr.iut.alldev.allin.data.model.bet.CustomBet import fr.iut.alldev.allin.data.model.bet.MatchBet @@ -140,7 +139,8 @@ fun ConfirmationAnswers( onClick: (String) -> Unit ) { val configuration = LocalConfiguration.current - val locale = remember { ConfigurationCompat.getLocales(configuration).get(0) ?: Locale.getDefault() } + val locale = + remember { ConfigurationCompat.getLocales(configuration).get(0) ?: Locale.getDefault() } LazyColumn( verticalArrangement = Arrangement.spacedBy(8.dp) @@ -148,7 +148,10 @@ fun ConfirmationAnswers( when (betDetail.bet) { is CustomBet -> items((betDetail.bet as CustomBet).possibleAnswers) { betDetail.getAnswerOfResponse(it)?.let { - val opacity by animateFloatAsState(targetValue = if (selectedAnswer != null && selectedAnswer != it.response) .5f else 1f, label = "") + val opacity by animateFloatAsState( + targetValue = if (selectedAnswer != null && selectedAnswer != it.response) .5f else 1f, + label = "" + ) BetConfirmationBottomSheetAnswer( @@ -166,7 +169,10 @@ fun ConfirmationAnswers( val bet = (betDetail.bet as MatchBet) item { betDetail.getAnswerOfResponse(bet.nameTeam1)?.let { - val opacity by animateFloatAsState(targetValue = if (selectedAnswer != null && selectedAnswer != it.response) .5f else 1f, label = "") + val opacity by animateFloatAsState( + targetValue = if (selectedAnswer != null && selectedAnswer != it.response) .5f else 1f, + label = "" + ) BetConfirmationBottomSheetAnswer( text = it.response, odds = it.odds, @@ -179,7 +185,10 @@ fun ConfirmationAnswers( } item { betDetail.getAnswerOfResponse(bet.nameTeam2)?.let { - val opacity by animateFloatAsState(targetValue = if (selectedAnswer != null && selectedAnswer != it.response) .5f else 1f, label = "") + val opacity by animateFloatAsState( + targetValue = if (selectedAnswer != null && selectedAnswer != it.response) .5f else 1f, + label = "" + ) BetConfirmationBottomSheetAnswer( text = it.response, @@ -198,7 +207,10 @@ fun ConfirmationAnswers( is YesNoBet -> { item { betDetail.getAnswerOfResponse(YES_VALUE)?.let { - val opacity by animateFloatAsState(targetValue = if (selectedAnswer != null && selectedAnswer != it.response) .5f else 1f, label = "") + val opacity by animateFloatAsState( + targetValue = if (selectedAnswer != null && selectedAnswer != it.response) .5f else 1f, + label = "" + ) val scale by animateFloatAsState( targetValue = if (selectedAnswer == null) 1f else if (selectedAnswer != it.response) .95f else 1.05f, @@ -219,10 +231,13 @@ fun ConfirmationAnswers( } item { betDetail.getAnswerOfResponse(NO_VALUE)?.let { - val opacity by animateFloatAsState(targetValue = if (selectedAnswer != null && selectedAnswer != it.response) .5f else 1f, label = "") + val opacity by animateFloatAsState( + targetValue = if (selectedAnswer != null && selectedAnswer != it.response) .5f else 1f, + label = "" + ) val scale by animateFloatAsState( targetValue = if (selectedAnswer == null) 1f - else if (selectedAnswer != it.response) .95f else 1.05f, + else if (selectedAnswer != it.response) .95f else 1f, label = "" ) @@ -358,7 +373,7 @@ private fun BetConfirmationBottomSheetContentPreview() { endRegisterDate = ZonedDateTime.now(), endBetDate = ZonedDateTime.now(), isPublic = true, - betStatus = BetStatus.Finished(BetFinishedStatus.WON), + betStatus = BetStatus.FINISHED, creator = "creator", ), answers = listOf( 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 03a2f4b..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 @@ -30,7 +30,7 @@ class BetCreationViewModel @Inject constructor( val registerDate = mutableStateOf(ZonedDateTime.now()) val betDate = mutableStateOf(ZonedDateTime.now()) var isPublic = mutableStateOf(true) - var selectedBetType = mutableStateOf(BetType.YES_NO) + var selectedBetType = mutableStateOf(BetType.BINARY) val themeError = mutableStateOf(FieldErrorState.NoError) val phraseError = mutableStateOf(FieldErrorState.NoError) @@ -111,7 +111,7 @@ class BetCreationViewModel @Inject constructor( possibleAnswers = listOf(), creator = currentUser.username ) - betRepository.createBet(bet, keystoreManager.getToken() ?: "") + betRepository.createBet(bet, keystoreManager.getTokenOrEmpty()) onSuccess() } catch (e: AllInAPIException) { Timber.e(e) 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 b7d30b3..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 @@ -139,7 +139,7 @@ private fun BetResultBottomSheetContentPreview() { endRegisterDate = ZonedDateTime.now(), endBetDate = ZonedDateTime.now(), isPublic = true, - betStatus = BetStatus.InProgress, + betStatus = BetStatus.IN_PROGRESS, creator = "creator", ), stake = 4175, 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/vo/BetStatusBottomSheetBetDisplayer.kt b/src/app/src/main/java/fr/iut/alldev/allin/ui/betStatus/vo/BetStatusBottomSheetBetDisplayer.kt index a190ee8..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,7 +24,9 @@ import androidx.compose.material.icons.filled.WorkspacePremium import androidx.compose.material3.ExperimentalMaterial3Api import androidx.compose.material3.HorizontalDivider import androidx.compose.material3.Text -import androidx.compose.runtime.* +import androidx.compose.runtime.Composable +import androidx.compose.runtime.mutableStateOf +import androidx.compose.runtime.remember import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.graphics.vector.rememberVectorPainter @@ -65,7 +79,8 @@ class BetStatusBottomSheetBetDisplayer( ) { val safeBottomPadding = paddingValues.value.calculateBottomPadding() val configuration = LocalConfiguration.current - val locale = remember { ConfigurationCompat.getLocales(configuration).get(0) ?: Locale.getDefault() } + val locale = + remember { ConfigurationCompat.getLocales(configuration).get(0) ?: Locale.getDefault() } val response1Answer = remember { betDetail.getAnswerOfResponse(response1) } val response2Answer = remember { betDetail.getAnswerOfResponse(response2) } @@ -99,7 +114,7 @@ class BetStatusBottomSheetBetDisplayer( } Spacer(modifier = Modifier.height(20.dp)) } - if (betDetail.bet.betStatus is BetStatus.Finished) { + if (betDetail.bet.betStatus == BetStatus.FINISHED) { BetStatusWinner( answer = response1Display, color = AllInTheme.colors.allInBlue, @@ -119,8 +134,10 @@ class BetStatusBottomSheetBetDisplayer( Spacer(modifier = Modifier.height(20.dp)) BinaryStatBar( response1Percentage = remember { - val total = (response1Answer?.totalParticipants ?: 0) + (response2Answer?.totalParticipants ?: 0) - if (total == 0) .5f else (response1Answer?.totalParticipants ?: 0) / total.toFloat() + val total = (response1Answer?.totalParticipants + ?: 0) + (response2Answer?.totalParticipants ?: 0) + if (total == 0) .5f else (response1Answer?.totalParticipants + ?: 0) / total.toFloat() }, response1 = response1Display, response2 = response2Display @@ -184,13 +201,13 @@ class BetStatusBottomSheetBetDisplayer( } } } - if (betDetail.bet.betStatus !is BetStatus.Finished && betDetail.userParticipation == null) { + if (betDetail.bet.betStatus != BetStatus.FINISHED && betDetail.userParticipation == null) { RainbowButton( modifier = Modifier .align(Alignment.BottomCenter) .padding(horizontal = 7.dp), text = stringResource(id = R.string.Participate), - enabled = betDetail.bet.betStatus == BetStatus.Waiting, + enabled = betDetail.bet.betStatus == BetStatus.IN_PROGRESS, onClick = openParticipateSheet ) } 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 2e8f57c..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,17 +2,30 @@ package fr.iut.alldev.allin.ui.main import androidx.activity.compose.BackHandler import androidx.compose.foundation.background -import androidx.compose.foundation.layout.* -import androidx.compose.material3.* -import androidx.compose.runtime.* +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.fillMaxSize +import androidx.compose.foundation.layout.padding +import androidx.compose.material3.DrawerState +import androidx.compose.material3.DrawerValue +import androidx.compose.material3.ExperimentalMaterial3Api +import androidx.compose.material3.SheetValue +import androidx.compose.material3.SnackbarDuration +import androidx.compose.material3.SnackbarHostState +import androidx.compose.material3.rememberDrawerState +import androidx.compose.material3.rememberModalBottomSheetState +import androidx.compose.runtime.Composable +import androidx.compose.runtime.LaunchedEffect +import androidx.compose.runtime.getValue +import androidx.compose.runtime.mutableStateOf +import androidx.compose.runtime.remember +import androidx.compose.runtime.rememberCoroutineScope +import androidx.compose.runtime.setValue import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.hilt.navigation.compose.hiltViewModel import androidx.navigation.NavHostController import androidx.navigation.compose.rememberNavController import fr.iut.alldev.allin.theme.AllInTheme -import fr.iut.alldev.allin.ui.betConfirmation.BetConfirmationBottomSheet -import fr.iut.alldev.allin.ui.betResult.BetResultBottomSheet import fr.iut.alldev.allin.ui.betStatus.BetStatusBottomSheet import fr.iut.alldev.allin.ui.betStatus.vo.BetStatusBottomSheetBetDisplayer import fr.iut.alldev.allin.ui.core.AllInLoading @@ -33,28 +46,6 @@ private val topLevelDestinations = listOf( TopLevelDestination.CurrentBets ) -@Composable -private fun rememberBetStatusVisibilities() - : Triple, MutableState, (Boolean) -> Unit> { - val statusVisibility = remember { - mutableStateOf(false) - } - - val sheetBackVisibility = remember { - mutableStateOf(false) - } - - val setStatusVisibility = { it: Boolean -> - statusVisibility.value = it - if (it) sheetBackVisibility.value = true - } - return Triple( - statusVisibility, - sheetBackVisibility, - setStatusVisibility, - ) -} - @OptIn(ExperimentalMaterial3Api::class) @Composable fun MainScreen( @@ -66,15 +57,19 @@ fun MainScreen( ) { val scope = rememberCoroutineScope() - var loading by remember { mainViewModel.loading } + val (loading, setLoading) = remember { mainViewModel.loading } val currentUser = remember { mainViewModel.currentUserState } val selectedBet by remember { mainViewModel.selectedBet } - val wonBet by remember { mainViewModel.wonBet } - val toConfirm by remember { mainViewModel.toConfirmBet } - val (statusVisibility, sheetBackVisibility, setStatusVisibility) = rememberBetStatusVisibilities() + val statusVisibility = remember { mutableStateOf(false) } + val sheetBackVisibility = remember { mutableStateOf(false) } + val setStatusVisibility = { it: Boolean -> + statusVisibility.value = it + if (it) sheetBackVisibility.value = true + } val (participateSheetVisibility, setParticipateSheetVisibility) = remember { mutableStateOf(false) } - val (displayResult, setDisplayResult) = remember { mutableStateOf(true) } + val events = remember { mainViewModel.events } + val eventBottomSheetState = rememberModalBottomSheetState(skipPartiallyExpanded = true) val betStatusDisplayer = remember { BetStatusBottomSheetBetDisplayer( @@ -113,10 +108,6 @@ fun MainScreen( } ) - val resultBottomSheetState = rememberModalBottomSheetState( - skipPartiallyExpanded = true - ) - AllInDrawer( drawerState = drawerState, destinations = topLevelDestinations, @@ -126,6 +117,7 @@ fun MainScreen( nbBets = 35, bestWin = 362, navigateTo = { route -> + mainViewModel.fetchEvents() navController.popUpTo(route, startDestination) }, logout = { @@ -135,7 +127,7 @@ fun MainScreen( ) { AllInScaffold( onMenuClicked = { scope.launch { drawerState.open() } }, - coinAmount = currentUser.userCoins.value, + coinAmount = currentUser.userCoins.intValue, drawerState = drawerState, snackbarHostState = snackbarHostState ) { @@ -156,34 +148,26 @@ fun MainScreen( setParticipateSheetVisibility(participate) setStatusVisibility(true) }, - setLoading = { loading = it }, - putSnackbarContent = { mainViewModel.putSnackbarContent(it) } + setLoading = setLoading, + putSnackbarContent = { mainViewModel.putSnackbarContent(it) }, + backHandlers = { + BackHandler(enabled = drawerState.isOpen) { + scope.launch { + drawerState.close() + } + } + } ) } } } - wonBet?.let { - BetResultBottomSheet( - state = resultBottomSheetState, - sheetVisibility = displayResult, - onDismiss = { setDisplayResult(false) }, - bet = it, - username = currentUser.user.username, - coinAmount = 1630, - stake = 1630, - winnings = 1630, - odds = 3.62f - ) - } - toConfirm?.let { - BetConfirmationBottomSheet( - state = resultBottomSheetState, - sheetVisibility = displayResult, - betDetail = it, - onDismiss = { setDisplayResult(false) } - ) { /*TODO*/ } + events.firstOrNull()?.let { + it.Display(sheetState = eventBottomSheetState) { + mainViewModel.dismissedEvents += it + events.removeFirstOrNull() + } } BetStatusBottomSheet( @@ -200,14 +184,6 @@ fun MainScreen( setParticipateSheetVisibility = setParticipateSheetVisibility ) AllInLoading(visible = loading) - BackHandler( - enabled = drawerState.isOpen - ) { - scope.launch { - drawerState.close() - } - } - } 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 6f96a21..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,28 +2,25 @@ package fr.iut.alldev.allin.ui.main import androidx.compose.runtime.MutableState import androidx.compose.runtime.mutableIntStateOf +import androidx.compose.runtime.mutableStateListOf import androidx.compose.runtime.mutableStateOf import androidx.lifecycle.ViewModel import androidx.lifecycle.viewModelScope import dagger.hilt.android.lifecycle.HiltViewModel import fr.iut.alldev.allin.data.model.User import fr.iut.alldev.allin.data.model.bet.Bet -import fr.iut.alldev.allin.data.model.bet.BetFinishedStatus -import fr.iut.alldev.allin.data.model.bet.BetStatus -import fr.iut.alldev.allin.data.model.bet.NO_VALUE import fr.iut.alldev.allin.data.model.bet.Participation -import fr.iut.alldev.allin.data.model.bet.YES_VALUE -import fr.iut.alldev.allin.data.model.bet.YesNoBet -import fr.iut.alldev.allin.data.model.bet.vo.BetAnswerDetail import fr.iut.alldev.allin.data.model.bet.vo.BetDetail import fr.iut.alldev.allin.data.repository.BetRepository import fr.iut.alldev.allin.di.AllInCurrentUser import fr.iut.alldev.allin.keystore.AllInKeystoreManager import fr.iut.alldev.allin.ui.core.snackbar.SnackbarType +import fr.iut.alldev.allin.ui.main.event.AllInEvent +import fr.iut.alldev.allin.ui.main.event.ToConfirmBet +import fr.iut.alldev.allin.ui.main.event.WonBet import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.launch import kotlinx.coroutines.withContext -import java.time.ZonedDateTime import javax.inject.Inject class UserState(val user: User) { @@ -41,61 +38,49 @@ class MainViewModel @Inject constructor( val currentUserState = UserState(currentUser) val selectedBet = mutableStateOf(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 toConfirmBet = mutableStateOf( - BetDetail( - bet = YesNoBet( - id = "1", - theme = "Theme", - phrase = "Phrase", - endRegisterDate = ZonedDateTime.now(), - endBetDate = ZonedDateTime.now(), - isPublic = true, - betStatus = BetStatus.Finished(BetFinishedStatus.WON), - creator = "creator", - ), - answers = listOf( - BetAnswerDetail( - response = YES_VALUE, - totalStakes = 300, - totalParticipants = 2, - highestStake = 200, - odds = 1.0f - ), - BetAnswerDetail( - response = NO_VALUE, - totalStakes = 150, - totalParticipants = 1, - highestStake = 150, - odds = 2.0f - ) - ), - participations = emptyList(), - userParticipation = null - ) - ) + val dismissedEvents = mutableStateListOf() + 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()) } } @@ -117,13 +102,23 @@ class MainViewModel @Inject constructor( response = response, stake = stake ) - betRepository.participateToBet(participation, keystoreManager.getToken() ?: "") + betRepository.participateToBet(participation, keystoreManager.getTokenOrEmpty()) } loading.value = false } } } + private fun confirmBet(response: String, betId: String) { + viewModelScope.launch { + betRepository.confirmBet( + token = keystoreManager.getTokenOrEmpty(), + id = betId, + response = response + ) + } + } + class SnackbarContent( val text: String, val type: SnackbarType = SnackbarType.STANDARD 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/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 f24b3b4..4839222 100644 --- a/src/app/src/main/res/values-fr/strings.xml +++ b/src/app/src/main/res/values-fr/strings.xml @@ -118,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 fb8050c..4409c33 100644 --- a/src/app/src/main/res/values/strings.xml +++ b/src/app/src/main/res/values/strings.xml @@ -118,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 f6f345b..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(): List + 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 0df5d67..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,11 +39,24 @@ class BetRepositoryImpl @Inject constructor( } override suspend fun participateToBet(participation: Participation, token: String) { - api.participateToBet(token = token.formatBearerToken(), body = participation.toRequestParticipation()) + api.participateToBet( + token = token.formatBearerToken(), + body = participation.toRequestParticipation() + ) } - override suspend fun getAllBets(): List { - return 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