From 71deba6f1279eaf18d0ef869688b525d61c9a201 Mon Sep 17 00:00:00 2001 From: avalin Date: Tue, 7 May 2024 17:55:01 +0200 Subject: [PATCH] Fix bottomsheets safe areas and bet status screen --- .../fr/iut/alldev/allin/AllInApplication.kt | 5 +- .../iut/alldev/allin/ext/PaddingValuesExt.kt | 38 +++++++++++-- .../ui/betStatus/BetStatusBottomSheet.kt | 8 +-- .../components/BetStatusBottomSheetBack.kt | 11 +++- .../BetStatusParticipationBottomSheet.kt | 49 ++++++++++++++-- .../vo/BetStatusBottomSheetBetDisplayer.kt | 50 ++++++++-------- .../alldev/allin/ui/core/AllInBottomSheet.kt | 8 ++- .../alldev/allin/ui/core/AllInTextField.kt | 36 +++++++++--- .../fr/iut/alldev/allin/ui/main/MainScreen.kt | 40 ++++++------- .../iut/alldev/allin/ui/main/MainViewModel.kt | 27 ++++++++- .../iut/alldev/allin/data/api/MockAllInApi.kt | 57 ++----------------- 11 files changed, 192 insertions(+), 137 deletions(-) diff --git a/src/app/src/main/java/fr/iut/alldev/allin/AllInApplication.kt b/src/app/src/main/java/fr/iut/alldev/allin/AllInApplication.kt index f5e6aa6..40105b1 100644 --- a/src/app/src/main/java/fr/iut/alldev/allin/AllInApplication.kt +++ b/src/app/src/main/java/fr/iut/alldev/allin/AllInApplication.kt @@ -2,15 +2,14 @@ package fr.iut.alldev.allin import android.app.Application import dagger.hilt.android.HiltAndroidApp -import racra.compose.smooth_corner_rect_library.BuildConfig import timber.log.Timber @HiltAndroidApp -class AllInApplication : Application(){ +class AllInApplication : Application() { override fun onCreate() { super.onCreate() - if(BuildConfig.DEBUG){ + if (BuildConfig.DEBUG) { Timber.plant(Timber.DebugTree()) } } diff --git a/src/app/src/main/java/fr/iut/alldev/allin/ext/PaddingValuesExt.kt b/src/app/src/main/java/fr/iut/alldev/allin/ext/PaddingValuesExt.kt index 6d97fba..1a88802 100644 --- a/src/app/src/main/java/fr/iut/alldev/allin/ext/PaddingValuesExt.kt +++ b/src/app/src/main/java/fr/iut/alldev/allin/ext/PaddingValuesExt.kt @@ -5,8 +5,11 @@ import androidx.compose.foundation.layout.WindowInsets import androidx.compose.foundation.layout.asPaddingValues import androidx.compose.foundation.layout.calculateEndPadding import androidx.compose.foundation.layout.calculateStartPadding +import androidx.compose.foundation.layout.navigationBars +import androidx.compose.foundation.layout.safeContent import androidx.compose.runtime.Composable import androidx.compose.runtime.ReadOnlyComposable +import androidx.compose.ui.platform.LocalDensity import androidx.compose.ui.platform.LocalLayoutDirection import androidx.compose.ui.unit.Dp import androidx.compose.ui.unit.dp @@ -25,15 +28,38 @@ operator fun PaddingValues.plus(paddingValues: PaddingValues): PaddingValues { @ReadOnlyComposable @Composable -fun WindowInsets.asPaddingValues(top: Dp = 0.dp, bottom: Dp = 0.dp, start: Dp = 0.dp, end: Dp = 0.dp): PaddingValues - = this.asPaddingValues() + PaddingValues(start, top, end, bottom) +fun WindowInsets.asPaddingValues(top: Dp = 0.dp, bottom: Dp = 0.dp, start: Dp = 0.dp, end: Dp = 0.dp): PaddingValues = + this.asPaddingValues() + PaddingValues(start, top, end, bottom) @ReadOnlyComposable @Composable -fun WindowInsets.asPaddingValues(horizontal: Dp = 0.dp, vertical: Dp = 0.dp): PaddingValues - = this.asPaddingValues() + PaddingValues(horizontal, vertical) +fun WindowInsets.asPaddingValues(horizontal: Dp = 0.dp, vertical: Dp = 0.dp): PaddingValues = + this.asPaddingValues() + PaddingValues(horizontal, vertical) @ReadOnlyComposable @Composable -fun WindowInsets.asPaddingValues(all: Dp = 0.dp): PaddingValues - = this.asPaddingValues() + PaddingValues(all) \ No newline at end of file +fun WindowInsets.asPaddingValues(all: Dp = 0.dp): PaddingValues = this.asPaddingValues() + PaddingValues(all) + +@ReadOnlyComposable +@Composable +fun WindowInsets.takeBottomOnly(): WindowInsets { + val density = LocalDensity.current + return WindowInsets(bottom = this.getBottom(density)) +} + +@Composable +fun bottomSheetNavigationBarsInsets(): WindowInsets { + val density = LocalDensity.current + val navBar = WindowInsets.navigationBars + if (navBar.getBottom(density) == 0) { + val safeContent = WindowInsets.safeContent + if (navBar.getBottom(density) == 0) { + return WindowInsets(bottom = 40.dp) + } + return safeContent.takeBottomOnly() + } + return navBar +} + +@Composable +fun bottomSheetNavigationBarsPadding(): PaddingValues = bottomSheetNavigationBarsInsets().asPaddingValues() \ No newline at end of file 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 22606bb..815e4ef 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 @@ -52,9 +52,7 @@ fun BetStatusBottomSheet( ) ) { betDetail?.let { - BetStatusBottomSheetBack( - status = it.bet.betStatus - ) + BetStatusBottomSheetBack(status = it.bet.betStatus) } } @@ -89,8 +87,7 @@ fun BetStatusBottomSheet( stake = stake, setStake = { stake = it }, setElement = { idx -> selectedAnswer = idx }, - enabled = stake != null && - (stake ?: 0) <= userCoinAmount + enabled = (stake ?: 0) != 0 && (stake ?: 0) <= userCoinAmount ) { stake?.let { stake -> onParticipate( @@ -99,7 +96,6 @@ fun BetStatusBottomSheet( ) } } - } } diff --git a/src/app/src/main/java/fr/iut/alldev/allin/ui/betStatus/components/BetStatusBottomSheetBack.kt b/src/app/src/main/java/fr/iut/alldev/allin/ui/betStatus/components/BetStatusBottomSheetBack.kt index 2ebd0c5..0d52fc7 100644 --- a/src/app/src/main/java/fr/iut/alldev/allin/ui/betStatus/components/BetStatusBottomSheetBack.kt +++ b/src/app/src/main/java/fr/iut/alldev/allin/ui/betStatus/components/BetStatusBottomSheetBack.kt @@ -1,7 +1,11 @@ package fr.iut.alldev.allin.ui.betStatus.components import androidx.compose.foundation.background -import androidx.compose.foundation.layout.* +import androidx.compose.foundation.layout.Box +import androidx.compose.foundation.layout.Row +import androidx.compose.foundation.layout.fillMaxHeight +import androidx.compose.foundation.layout.fillMaxSize +import androidx.compose.foundation.layout.padding import androidx.compose.material3.Icon import androidx.compose.material3.Text import androidx.compose.runtime.Composable @@ -29,7 +33,6 @@ fun BetStatusBottomSheetBack( status: BetStatus, modifier: Modifier = Modifier, ) { - Box( modifier .fillMaxSize() @@ -38,7 +41,9 @@ fun BetStatusBottomSheetBack( ) { Row( verticalAlignment = Alignment.CenterVertically, - modifier = Modifier.fillMaxHeight(1 - SHEET_HEIGHT) + modifier = Modifier + .background(status.getColor()) + .fillMaxHeight(1 - SHEET_HEIGHT) ) { Text( text = stringResource(id = status.getTitleId()), diff --git a/src/app/src/main/java/fr/iut/alldev/allin/ui/betStatus/components/BetStatusParticipationBottomSheet.kt b/src/app/src/main/java/fr/iut/alldev/allin/ui/betStatus/components/BetStatusParticipationBottomSheet.kt index edda634..447c83b 100644 --- a/src/app/src/main/java/fr/iut/alldev/allin/ui/betStatus/components/BetStatusParticipationBottomSheet.kt +++ b/src/app/src/main/java/fr/iut/alldev/allin/ui/betStatus/components/BetStatusParticipationBottomSheet.kt @@ -1,6 +1,7 @@ package fr.iut.alldev.allin.ui.betStatus.components import android.content.res.Configuration +import androidx.compose.animation.core.animateFloatAsState import androidx.compose.foundation.background import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Column @@ -9,13 +10,13 @@ import androidx.compose.foundation.layout.RowScope import androidx.compose.foundation.layout.Spacer import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.height -import androidx.compose.foundation.layout.navigationBarsPadding import androidx.compose.foundation.layout.padding import androidx.compose.material3.ExperimentalMaterial3Api import androidx.compose.material3.HorizontalDivider 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.rememberCoroutineScope @@ -26,6 +27,7 @@ import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.sp import fr.iut.alldev.allin.R +import fr.iut.alldev.allin.ext.bottomSheetNavigationBarsPadding import fr.iut.alldev.allin.theme.AllInColorToken import fr.iut.alldev.allin.theme.AllInTheme import fr.iut.alldev.allin.ui.core.AllInBottomSheet @@ -35,6 +37,7 @@ import fr.iut.alldev.allin.ui.core.AllInIntTextField import fr.iut.alldev.allin.ui.core.AllInSelectionBox import fr.iut.alldev.allin.ui.core.topbar.AllInTopBarCoinCounter import kotlinx.coroutines.launch +import kotlin.math.ln import kotlin.math.roundToInt @OptIn(ExperimentalMaterial3Api::class) @@ -52,8 +55,9 @@ fun BetStatusParticipationBottomSheet( elements: List<@Composable RowScope.() -> Unit>, selectedElement: (@Composable RowScope.() -> Unit)?, setElement: (Int) -> Unit, - onParticipate: () -> Unit -) { + onParticipate: () -> Unit, + + ) { val scope = rememberCoroutineScope() AllInBottomSheet( sheetVisibility = sheetVisibility, @@ -95,6 +99,12 @@ private fun BetStatusParticipationBottomSheetContent( onButtonClick: () -> Unit ) { val (answersBoxIsOpen, setAnswersBoxIsOpen) = remember { mutableStateOf(false) } + val betStrength by animateFloatAsState( + targetValue = if (enabled) { + (ln(stake?.toFloat() ?: 0f) / ln(coinAmount.toFloat())).coerceIn(.42f..1f) + } else .42f, + label = "" + ) Row( modifier = Modifier.fillMaxWidth(), @@ -115,6 +125,7 @@ private fun BetStatusParticipationBottomSheetContent( iconColor = AllInColorToken.white, ) } + Column( modifier = Modifier.padding(horizontal = 18.dp) ) { @@ -135,13 +146,15 @@ private fun BetStatusParticipationBottomSheetContent( Spacer(modifier = Modifier.height(8.dp)) AllInIntTextField( value = stake, - setValue = setStake, + setValue = { setStake(it?.coerceAtMost(coinAmount)) }, textStyle = AllInTheme.typography.h1.copy( fontSize = 20.sp, - color = AllInTheme.colors.onBackground + color = AllInColorToken.allInPurple ), placeholder = stringResource(id = R.string.bet_result_stake), trailingIcon = AllInTheme.icons.allCoins(), + trailingIconColor = if (enabled) AllInColorToken.allInPurple else null, + borderColor = if (enabled) AllInColorToken.allInPurple.copy(alpha = betStrength) else AllInTheme.colors.border, modifier = Modifier.fillMaxWidth(), maxChar = null ) @@ -176,8 +189,10 @@ private fun BetStatusParticipationBottomSheetContent( textColor = AllInColorToken.white, radius = 5.dp, onClick = onButtonClick, - modifier = Modifier.navigationBarsPadding() + modifier = Modifier ) + + Spacer(modifier = Modifier.padding(bottomSheetNavigationBarsPadding())) } } @@ -201,4 +216,26 @@ private fun BetStatusParticipationBottomSheetContentPreview() { ) } } +} + +@Preview +@Preview(uiMode = Configuration.UI_MODE_NIGHT_YES) +@Composable +private fun BetStatusParticipationBottomSheetContentEmptyPreview() { + AllInTheme { + Column { + BetStatusParticipationBottomSheetContent( + betPhrase = "Bet phrase", + coinAmount = 3620, + onButtonClick = {}, + elements = emptyList(), + setElement = {}, + selectedElement = null, + enabled = true, + stake = null, + odds = 0.42f, + setStake = {} + ) + } + } } \ No newline at end of file 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 599f40f..12ba3bc 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 @@ -8,12 +8,9 @@ import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.IntrinsicSize import androidx.compose.foundation.layout.Row import androidx.compose.foundation.layout.Spacer -import androidx.compose.foundation.layout.WindowInsets import androidx.compose.foundation.layout.fillMaxHeight import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.height -import androidx.compose.foundation.layout.navigationBars -import androidx.compose.foundation.layout.navigationBarsPadding import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.size import androidx.compose.foundation.layout.width @@ -56,8 +53,10 @@ 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.vo.BetAnswerDetail import fr.iut.alldev.allin.data.model.bet.vo.BetDetail import fr.iut.alldev.allin.ext.asPaddingValues +import fr.iut.alldev.allin.ext.bottomSheetNavigationBarsInsets import fr.iut.alldev.allin.ext.formatToSimple import fr.iut.alldev.allin.ext.getDateEndLabelId import fr.iut.alldev.allin.ext.getDateStartLabelId @@ -82,7 +81,7 @@ class BetStatusBottomSheetBetDisplayer( val openParticipateSheet: () -> Unit ) : BetDisplayer { @Composable - private fun DisplayBet( + private fun DisplayBetDail( betDetail: BetDetail, currentUser: User, winnerColor: @Composable () -> Color, @@ -142,7 +141,7 @@ class BetStatusBottomSheetBetDisplayer( source: NestedScrollSource ) = available.copy(x = 0f) }), - contentPadding = WindowInsets.navigationBars.asPaddingValues(top = 20.dp, start = 20.dp, end = 20.dp) + contentPadding = bottomSheetNavigationBarsInsets().asPaddingValues(top = 20.dp, start = 20.dp, end = 20.dp) ) { statBar(this) @@ -202,8 +201,7 @@ class BetStatusBottomSheetBetDisplayer( .5f to AllInTheme.colors.background2 ) ) - .padding(7.dp) - .navigationBarsPadding(), + .padding(bottomSheetNavigationBarsInsets().asPaddingValues(7.dp)), text = stringResource(id = R.string.Participate), enabled = betDetail.bet.betStatus == BetStatus.IN_PROGRESS, onClick = openParticipateSheet @@ -223,11 +221,11 @@ class BetStatusBottomSheetBetDisplayer( val configuration = LocalConfiguration.current val locale = remember { ConfigurationCompat.getLocales(configuration).get(0) ?: Locale.getDefault() } - val response1Answer = remember { betDetail.getAnswerOfResponse(response1) } - val response2Answer = remember { betDetail.getAnswerOfResponse(response2) } + val response1Answer = remember(betDetail) { betDetail.getAnswerOfResponse(response1) } + val response2Answer = remember(betDetail) { betDetail.getAnswerOfResponse(response2) } BinaryStatBar( - response1Percentage = remember { + response1Percentage = remember(betDetail) { response1Answer?.let { betDetail.getPercentageOfAnswer(response1Answer) } ?: 0f }, response1 = response1Display(), @@ -259,17 +257,10 @@ class BetStatusBottomSheetBetDisplayer( } private fun LazyListScope.displayMultiStatBar( - betDetail: BetDetail, - responses: List, + responsesWithDetail: List>, locale: Locale ) { - val responsesWithDetail = responses.mapNotNull { - betDetail.getAnswerOfResponse(it) - }.associateWith { - betDetail.getPercentageOfAnswer(it) - } - - itemsIndexed(responsesWithDetail.toList().sortedByDescending { it.second }) { idx, (answer, percentage) -> + itemsIndexed(responsesWithDetail) { idx, (answer, percentage) -> val isWin = remember { idx == 0 } SimpleStatBar( @@ -305,7 +296,7 @@ class BetStatusBottomSheetBetDisplayer( @Composable override fun DisplayYesNoBet(betDetail: BetDetail, currentUser: User) { - DisplayBet( + DisplayBetDail( betDetail = betDetail, currentUser = currentUser, winnerColor = { @@ -327,7 +318,7 @@ class BetStatusBottomSheetBetDisplayer( override fun DisplayMatchBet(betDetail: BetDetail, currentUser: User) { val matchBet = remember { betDetail.bet as MatchBet } - DisplayBet( + DisplayBetDail( betDetail = betDetail, currentUser = currentUser, winnerColor = { @@ -349,7 +340,19 @@ class BetStatusBottomSheetBetDisplayer( val configuration = LocalConfiguration.current val locale = remember { ConfigurationCompat.getLocales(configuration).get(0) ?: Locale.getDefault() } - DisplayBet( + val responsesWithDetail = remember(betDetail) { + if (customBet.possibleAnswers.size > 2) { + customBet.getResponses().mapNotNull { + betDetail.getAnswerOfResponse(it) + }.associateWith { + betDetail.getPercentageOfAnswer(it) + } + .toList() + .sortedByDescending { it.second } + } else emptyList() + } + + DisplayBetDail( betDetail = betDetail, currentUser = currentUser, winnerColor = { @@ -369,8 +372,7 @@ class BetStatusBottomSheetBetDisplayer( ) } else { displayMultiStatBar( - betDetail = betDetail, - responses = customBet.getResponses(), + responsesWithDetail = responsesWithDetail, locale = locale ) } diff --git a/src/app/src/main/java/fr/iut/alldev/allin/ui/core/AllInBottomSheet.kt b/src/app/src/main/java/fr/iut/alldev/allin/ui/core/AllInBottomSheet.kt index 54025fb..1fe3f75 100644 --- a/src/app/src/main/java/fr/iut/alldev/allin/ui/core/AllInBottomSheet.kt +++ b/src/app/src/main/java/fr/iut/alldev/allin/ui/core/AllInBottomSheet.kt @@ -1,7 +1,11 @@ package fr.iut.alldev.allin.ui.core -import androidx.compose.foundation.layout.* -import androidx.compose.material3.* +import androidx.compose.foundation.layout.ColumnScope +import androidx.compose.foundation.layout.WindowInsets +import androidx.compose.material3.BottomSheetDefaults +import androidx.compose.material3.ExperimentalMaterial3Api +import androidx.compose.material3.ModalBottomSheet +import androidx.compose.material3.SheetState import androidx.compose.runtime.Composable import androidx.compose.ui.graphics.Color import androidx.compose.ui.platform.LocalDensity diff --git a/src/app/src/main/java/fr/iut/alldev/allin/ui/core/AllInTextField.kt b/src/app/src/main/java/fr/iut/alldev/allin/ui/core/AllInTextField.kt index ee0e9e9..241dacf 100644 --- a/src/app/src/main/java/fr/iut/alldev/allin/ui/core/AllInTextField.kt +++ b/src/app/src/main/java/fr/iut/alldev/allin/ui/core/AllInTextField.kt @@ -39,7 +39,8 @@ fun AllInTextField( maxChar: Int? = null, enabled: Boolean = true, trailingIcon: Painter? = null, - trailingContent: @Composable() (() -> Unit)? = null, + trailingIconColor: Color? = null, + trailingContent: @Composable (() -> Unit)? = null, placeholderFontSize: TextUnit = 18.sp, multiLine: Boolean = false, errorText: String? = null, @@ -84,7 +85,7 @@ fun AllInTextField( Icon( painter = it, contentDescription = null, - tint = AllInColorToken.allInLightGrey300 + tint = trailingIconColor ?: AllInColorToken.allInLightGrey300 ) } }, @@ -107,7 +108,6 @@ fun AllInTextField( ) } -@OptIn(ExperimentalFoundationApi::class) @Composable fun AllInPasswordField( placeholder: String, @@ -152,6 +152,11 @@ fun AllInFloatTextfield( textStyle: TextStyle = AllInTheme.typography.p1, placeholder: String? = null, trailingIcon: Painter? = null, + trailingIconColor: Color? = null, + borderColor: Color = AllInTheme.colors.onBackground2, + containerColor: Color = AllInTheme.colors.background, + textColor: Color = AllInTheme.colors.onMainSurface, + placeholderColor: Color = AllInTheme.colors.onBackground2, maxChar: Int? = 5, setValue: (Float?) -> Unit ) { @@ -168,7 +173,12 @@ fun AllInFloatTextfield( maxChar = maxChar, textStyle = textStyle, trailingIcon = trailingIcon, - keyboardType = KeyboardType.Number + trailingIconColor = trailingIconColor, + keyboardType = KeyboardType.Number, + borderColor = borderColor, + containerColor = containerColor, + textColor = textColor, + placeholderColor = placeholderColor ) { it.verifyIsFloat(locale)?.let { stringValue = it @@ -190,7 +200,12 @@ fun AllInIntTextField( textStyle: TextStyle = AllInTheme.typography.p1, placeholder: String? = null, trailingIcon: Painter? = null, + trailingIconColor: Color? = null, maxChar: Int? = 3, + borderColor: Color = AllInTheme.colors.onBackground2, + containerColor: Color = AllInTheme.colors.background, + textColor: Color = AllInTheme.colors.onMainSurface, + placeholderColor: Color = AllInTheme.colors.onBackground2, setValue: (Int?) -> Unit ) { AllInTextField( @@ -199,8 +214,13 @@ fun AllInIntTextField( modifier = modifier, maxChar = maxChar, trailingIcon = trailingIcon, + trailingIconColor = trailingIconColor, textStyle = textStyle, - keyboardType = KeyboardType.NumberPassword + keyboardType = KeyboardType.NumberPassword, + borderColor = borderColor, + containerColor = containerColor, + textColor = textColor, + placeholderColor = placeholderColor ) { if (it.isEmpty()) setValue(null) else if (it.isDigitsOnly()) { @@ -226,7 +246,6 @@ private fun AllInTextFieldPlaceholderPreview() { } -@OptIn(ExperimentalFoundationApi::class) @Preview @Preview(uiMode = Configuration.UI_MODE_NIGHT_YES) @Composable @@ -240,8 +259,8 @@ private fun AllInTextFieldValuePreview() { } } -@OptIn(ExperimentalFoundationApi::class) @Preview +@Preview(uiMode = Configuration.UI_MODE_NIGHT_YES) @Composable private fun AllInTextFieldErrorPreview() { AllInTheme { @@ -254,8 +273,8 @@ private fun AllInTextFieldErrorPreview() { } } -@OptIn(ExperimentalFoundationApi::class) @Preview +@Preview(uiMode = Configuration.UI_MODE_NIGHT_YES) @Composable private fun AllInTextFieldPasswordPreview() { AllInTheme { @@ -269,6 +288,7 @@ private fun AllInTextFieldPasswordPreview() { } @Preview +@Preview(uiMode = Configuration.UI_MODE_NIGHT_YES) @Composable private fun AllInTextFieldMultilinePreview() { AllInTheme { 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 b61423a..549ee7b 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 @@ -71,7 +71,6 @@ fun MainScreen( val focusManager = LocalFocusManager.current val drawerState = rememberDrawerState(initialValue = DrawerValue.Closed) - LaunchedEffect(key1 = drawerState.targetValue) { focusManager.clearFocus() } @@ -99,33 +98,26 @@ fun MainScreen( val (loading, setLoading) = remember { mainViewModel.loading } val currentUser by mainViewModel.currentUser.collectAsStateWithLifecycle() + val selectedBet by remember { mainViewModel.selectedBet } - val statusVisibility = remember { mutableStateOf(false) } - val sheetBackVisibility = remember { mutableStateOf(false) } - val setStatusVisibility = { it: Boolean -> - statusVisibility.value = it - if (it) sheetBackVisibility.value = true - } + var statusVisibility by remember { mutableStateOf(false) } + var statusVisibilityConfirm by remember { mutableStateOf(null) } val (participateSheetVisibility, setParticipateSheetVisibility) = remember { mutableStateOf(false) } - - val events = remember { mainViewModel.events } - val eventBottomSheetState = rememberModalBottomSheetState(skipPartiallyExpanded = true) - - val betStatusDisplayer = remember { - BetStatusBottomSheetBetDisplayer( - openParticipateSheet = { setParticipateSheetVisibility(true) } - ) - } - val statusBottomSheetState = rememberModalBottomSheetState( skipPartiallyExpanded = true, confirmValueChange = { - if (it == SheetValue.Hidden) { - sheetBackVisibility.value = false - } + statusVisibilityConfirm = it true } ) + val betStatusDisplayer = remember { + BetStatusBottomSheetBetDisplayer( + openParticipateSheet = { setParticipateSheetVisibility(true) } + ) + } + + val events = remember { mainViewModel.events } + val eventBottomSheetState = rememberModalBottomSheetState(skipPartiallyExpanded = true) AllInDrawer( drawerState = drawerState, @@ -161,7 +153,7 @@ fun MainScreen( navController = navController, selectBet = { bet, participate -> mainViewModel.openBetDetail(bet) { detail -> - setStatusVisibility(true) + statusVisibility = true if ( detail.bet.betStatus == BetStatus.IN_PROGRESS && detail.userParticipation == null && @@ -238,9 +230,9 @@ fun MainScreen( BetStatusBottomSheet( state = statusBottomSheetState, - sheetVisibility = statusVisibility.value, - sheetBackVisibility = sheetBackVisibility.value, - onDismiss = { setStatusVisibility(false) }, + sheetVisibility = statusVisibility, + sheetBackVisibility = statusBottomSheetState.targetValue == SheetValue.Expanded || statusVisibilityConfirm == SheetValue.Expanded, + onDismiss = { statusVisibility = false }, betDetail = selectedBet, displayBet = { detail -> currentUser?.let { user -> betStatusDisplayer.DisplayBet(betDetail = detail, currentUser = user) } 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 da4b448..ec82643 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 @@ -8,6 +8,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.model.bet.Participation +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.data.repository.UserRepository @@ -122,13 +123,21 @@ class MainViewModel @Inject constructor( loading.value = true currentUser.value?.let { user -> decreaseCoins(stake) - selectedBet.value?.bet?.let { + selectedBet.value?.let { val participation = Participation( - betId = it.id, + betId = it.bet.id, username = user.username, response = response, stake = stake ) + + selectedBet.value = it.copy( + userParticipation = participation, + participations = it.participations + participation, + answers = getAnswerDetails(it.bet, it.participations + participation), + + ) + betRepository.participateToBet(participation, keystoreManager.getTokenOrEmpty()) } } @@ -137,6 +146,20 @@ class MainViewModel @Inject constructor( } } + private fun getAnswerDetails(bet: Bet, participations: List): List { + return bet.getResponses().map { response -> + val responseParticipations = participations.filter { it.response == response } + BetAnswerDetail( + response = response, + totalStakes = responseParticipations.sumOf { it.stake }, + totalParticipants = responseParticipations.size, + highestStake = responseParticipations.maxOfOrNull { it.stake } ?: 0, + odds = if (participations.isEmpty()) 0.0f else responseParticipations.size / participations.size.toFloat() + ) + } + } + + private fun confirmBet(response: String, betId: String) { viewModelScope.launch { betRepository.confirmBet( diff --git a/src/data/src/dev/java/fr/iut/alldev/allin/data/api/MockAllInApi.kt b/src/data/src/dev/java/fr/iut/alldev/allin/data/api/MockAllInApi.kt index edb2a37..d90e251 100644 --- a/src/data/src/dev/java/fr/iut/alldev/allin/data/api/MockAllInApi.kt +++ b/src/data/src/dev/java/fr/iut/alldev/allin/data/api/MockAllInApi.kt @@ -316,39 +316,11 @@ class MockAllInApi : AllInApi { answer = NO_VALUE, stake = 1500 ), - ResponseParticipation( - id = "", - betId = "UUID1", - username = mockUsers[2].first.username, - answer = YES_VALUE, - stake = 300 - ), - ResponseParticipation( - id = "", - betId = "UUID1", - username = mockUsers[3].first.username, - answer = YES_VALUE, - stake = 25 - ), - ResponseParticipation( - id = "", - betId = "UUID1", - username = mockUsers[4].first.username, - answer = NO_VALUE, - stake = 222 - ), - ResponseParticipation( - id = "", - betId = "UUID1", - username = mockUsers[5].first.username, - answer = NO_VALUE, - stake = 222 - ), ResponseParticipation( id = "", betId = "UUID1", username = mockUsers[6].first.username, - answer = NO_VALUE, + answer = YES_VALUE, stake = 222 ), ResponseParticipation( @@ -365,27 +337,6 @@ class MockAllInApi : AllInApi { answer = "Answer 1", stake = 200 ), - ResponseParticipation( - id = "", - betId = "UUID2", - username = mockUsers[3].first.username, - answer = "Answer 2", - stake = 200 - ), - ResponseParticipation( - id = "", - betId = "UUID2", - username = mockUsers[4].first.username, - answer = "Answer 3", - stake = 100 - ), - ResponseParticipation( - id = "", - betId = "UUID2", - username = mockUsers[5].first.username, - answer = "Answer 3", - stake = 400 - ), ResponseParticipation( id = "", betId = "UUID2", @@ -431,13 +382,13 @@ class MockAllInApi : AllInApi { id = "UUID3", theme = "Sport", sentenceBet = "Quelle équipe va gagner ?", - endRegistration = ZonedDateTime.now().minusDays(3), - endBet = ZonedDateTime.now().minusDays(2), + endRegistration = ZonedDateTime.now().plusDays(3), + endBet = ZonedDateTime.now().plusDays(2), isPrivate = false, response = listOf("The Monarchs", "Climate Change"), createdBy = "User 1", type = BetType.MATCH, - status = BetStatus.CLOSING, + status = BetStatus.IN_PROGRESS, ) )