Fix bottomsheets safe areas and bet status screen
continuous-integration/drone/push Build is passing Details

pull/5/head
avalin 12 months ago
parent 666610a85f
commit 71deba6f12

@ -2,15 +2,14 @@ package fr.iut.alldev.allin
import android.app.Application import android.app.Application
import dagger.hilt.android.HiltAndroidApp import dagger.hilt.android.HiltAndroidApp
import racra.compose.smooth_corner_rect_library.BuildConfig
import timber.log.Timber import timber.log.Timber
@HiltAndroidApp @HiltAndroidApp
class AllInApplication : Application(){ class AllInApplication : Application() {
override fun onCreate() { override fun onCreate() {
super.onCreate() super.onCreate()
if(BuildConfig.DEBUG){ if (BuildConfig.DEBUG) {
Timber.plant(Timber.DebugTree()) Timber.plant(Timber.DebugTree())
} }
} }

@ -5,8 +5,11 @@ import androidx.compose.foundation.layout.WindowInsets
import androidx.compose.foundation.layout.asPaddingValues import androidx.compose.foundation.layout.asPaddingValues
import androidx.compose.foundation.layout.calculateEndPadding import androidx.compose.foundation.layout.calculateEndPadding
import androidx.compose.foundation.layout.calculateStartPadding 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.Composable
import androidx.compose.runtime.ReadOnlyComposable import androidx.compose.runtime.ReadOnlyComposable
import androidx.compose.ui.platform.LocalDensity
import androidx.compose.ui.platform.LocalLayoutDirection import androidx.compose.ui.platform.LocalLayoutDirection
import androidx.compose.ui.unit.Dp import androidx.compose.ui.unit.Dp
import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.dp
@ -25,15 +28,38 @@ operator fun PaddingValues.plus(paddingValues: PaddingValues): PaddingValues {
@ReadOnlyComposable @ReadOnlyComposable
@Composable @Composable
fun WindowInsets.asPaddingValues(top: Dp = 0.dp, bottom: Dp = 0.dp, start: Dp = 0.dp, end: Dp = 0.dp): PaddingValues 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) this.asPaddingValues() + PaddingValues(start, top, end, bottom)
@ReadOnlyComposable @ReadOnlyComposable
@Composable @Composable
fun WindowInsets.asPaddingValues(horizontal: Dp = 0.dp, vertical: Dp = 0.dp): PaddingValues fun WindowInsets.asPaddingValues(horizontal: Dp = 0.dp, vertical: Dp = 0.dp): PaddingValues =
= this.asPaddingValues() + PaddingValues(horizontal, vertical) this.asPaddingValues() + PaddingValues(horizontal, vertical)
@ReadOnlyComposable @ReadOnlyComposable
@Composable @Composable
fun WindowInsets.asPaddingValues(all: Dp = 0.dp): PaddingValues fun WindowInsets.asPaddingValues(all: Dp = 0.dp): PaddingValues = this.asPaddingValues() + PaddingValues(all)
= 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()

@ -52,9 +52,7 @@ fun BetStatusBottomSheet(
) )
) { ) {
betDetail?.let { betDetail?.let {
BetStatusBottomSheetBack( BetStatusBottomSheetBack(status = it.bet.betStatus)
status = it.bet.betStatus
)
} }
} }
@ -89,8 +87,7 @@ fun BetStatusBottomSheet(
stake = stake, stake = stake,
setStake = { stake = it }, setStake = { stake = it },
setElement = { idx -> selectedAnswer = idx }, setElement = { idx -> selectedAnswer = idx },
enabled = stake != null && enabled = (stake ?: 0) != 0 && (stake ?: 0) <= userCoinAmount
(stake ?: 0) <= userCoinAmount
) { ) {
stake?.let { stake -> stake?.let { stake ->
onParticipate( onParticipate(
@ -99,7 +96,6 @@ fun BetStatusBottomSheet(
) )
} }
} }
} }
} }

@ -1,7 +1,11 @@
package fr.iut.alldev.allin.ui.betStatus.components package fr.iut.alldev.allin.ui.betStatus.components
import androidx.compose.foundation.background 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.Icon
import androidx.compose.material3.Text import androidx.compose.material3.Text
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
@ -29,7 +33,6 @@ fun BetStatusBottomSheetBack(
status: BetStatus, status: BetStatus,
modifier: Modifier = Modifier, modifier: Modifier = Modifier,
) { ) {
Box( Box(
modifier modifier
.fillMaxSize() .fillMaxSize()
@ -38,7 +41,9 @@ fun BetStatusBottomSheetBack(
) { ) {
Row( Row(
verticalAlignment = Alignment.CenterVertically, verticalAlignment = Alignment.CenterVertically,
modifier = Modifier.fillMaxHeight(1 - SHEET_HEIGHT) modifier = Modifier
.background(status.getColor())
.fillMaxHeight(1 - SHEET_HEIGHT)
) { ) {
Text( Text(
text = stringResource(id = status.getTitleId()), text = stringResource(id = status.getTitleId()),

@ -1,6 +1,7 @@
package fr.iut.alldev.allin.ui.betStatus.components package fr.iut.alldev.allin.ui.betStatus.components
import android.content.res.Configuration import android.content.res.Configuration
import androidx.compose.animation.core.animateFloatAsState
import androidx.compose.foundation.background import androidx.compose.foundation.background
import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Column 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.Spacer
import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.height import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.navigationBarsPadding
import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.padding
import androidx.compose.material3.ExperimentalMaterial3Api import androidx.compose.material3.ExperimentalMaterial3Api
import androidx.compose.material3.HorizontalDivider import androidx.compose.material3.HorizontalDivider
import androidx.compose.material3.SheetState import androidx.compose.material3.SheetState
import androidx.compose.material3.Text import androidx.compose.material3.Text
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember import androidx.compose.runtime.remember
import androidx.compose.runtime.rememberCoroutineScope 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.dp
import androidx.compose.ui.unit.sp import androidx.compose.ui.unit.sp
import fr.iut.alldev.allin.R 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.AllInColorToken
import fr.iut.alldev.allin.theme.AllInTheme import fr.iut.alldev.allin.theme.AllInTheme
import fr.iut.alldev.allin.ui.core.AllInBottomSheet 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.AllInSelectionBox
import fr.iut.alldev.allin.ui.core.topbar.AllInTopBarCoinCounter import fr.iut.alldev.allin.ui.core.topbar.AllInTopBarCoinCounter
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
import kotlin.math.ln
import kotlin.math.roundToInt import kotlin.math.roundToInt
@OptIn(ExperimentalMaterial3Api::class) @OptIn(ExperimentalMaterial3Api::class)
@ -52,8 +55,9 @@ fun BetStatusParticipationBottomSheet(
elements: List<@Composable RowScope.() -> Unit>, elements: List<@Composable RowScope.() -> Unit>,
selectedElement: (@Composable RowScope.() -> Unit)?, selectedElement: (@Composable RowScope.() -> Unit)?,
setElement: (Int) -> Unit, setElement: (Int) -> Unit,
onParticipate: () -> Unit onParticipate: () -> Unit,
) {
) {
val scope = rememberCoroutineScope() val scope = rememberCoroutineScope()
AllInBottomSheet( AllInBottomSheet(
sheetVisibility = sheetVisibility, sheetVisibility = sheetVisibility,
@ -95,6 +99,12 @@ private fun BetStatusParticipationBottomSheetContent(
onButtonClick: () -> Unit onButtonClick: () -> Unit
) { ) {
val (answersBoxIsOpen, setAnswersBoxIsOpen) = remember { mutableStateOf(false) } 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( Row(
modifier = Modifier.fillMaxWidth(), modifier = Modifier.fillMaxWidth(),
@ -115,6 +125,7 @@ private fun BetStatusParticipationBottomSheetContent(
iconColor = AllInColorToken.white, iconColor = AllInColorToken.white,
) )
} }
Column( Column(
modifier = Modifier.padding(horizontal = 18.dp) modifier = Modifier.padding(horizontal = 18.dp)
) { ) {
@ -135,13 +146,15 @@ private fun BetStatusParticipationBottomSheetContent(
Spacer(modifier = Modifier.height(8.dp)) Spacer(modifier = Modifier.height(8.dp))
AllInIntTextField( AllInIntTextField(
value = stake, value = stake,
setValue = setStake, setValue = { setStake(it?.coerceAtMost(coinAmount)) },
textStyle = AllInTheme.typography.h1.copy( textStyle = AllInTheme.typography.h1.copy(
fontSize = 20.sp, fontSize = 20.sp,
color = AllInTheme.colors.onBackground color = AllInColorToken.allInPurple
), ),
placeholder = stringResource(id = R.string.bet_result_stake), placeholder = stringResource(id = R.string.bet_result_stake),
trailingIcon = AllInTheme.icons.allCoins(), 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(), modifier = Modifier.fillMaxWidth(),
maxChar = null maxChar = null
) )
@ -176,8 +189,10 @@ private fun BetStatusParticipationBottomSheetContent(
textColor = AllInColorToken.white, textColor = AllInColorToken.white,
radius = 5.dp, radius = 5.dp,
onClick = onButtonClick, onClick = onButtonClick,
modifier = Modifier.navigationBarsPadding() modifier = Modifier
) )
Spacer(modifier = Modifier.padding(bottomSheetNavigationBarsPadding()))
} }
} }
@ -202,3 +217,25 @@ 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 = {}
)
}
}
}

@ -8,12 +8,9 @@ import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.IntrinsicSize import androidx.compose.foundation.layout.IntrinsicSize
import androidx.compose.foundation.layout.Row import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.Spacer import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.WindowInsets
import androidx.compose.foundation.layout.fillMaxHeight import androidx.compose.foundation.layout.fillMaxHeight
import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.height 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.padding
import androidx.compose.foundation.layout.size import androidx.compose.foundation.layout.size
import androidx.compose.foundation.layout.width 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.MatchBet
import fr.iut.alldev.allin.data.model.bet.NO_VALUE 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.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.data.model.bet.vo.BetDetail
import fr.iut.alldev.allin.ext.asPaddingValues 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.formatToSimple
import fr.iut.alldev.allin.ext.getDateEndLabelId import fr.iut.alldev.allin.ext.getDateEndLabelId
import fr.iut.alldev.allin.ext.getDateStartLabelId import fr.iut.alldev.allin.ext.getDateStartLabelId
@ -82,7 +81,7 @@ class BetStatusBottomSheetBetDisplayer(
val openParticipateSheet: () -> Unit val openParticipateSheet: () -> Unit
) : BetDisplayer { ) : BetDisplayer {
@Composable @Composable
private fun DisplayBet( private fun DisplayBetDail(
betDetail: BetDetail, betDetail: BetDetail,
currentUser: User, currentUser: User,
winnerColor: @Composable () -> Color, winnerColor: @Composable () -> Color,
@ -142,7 +141,7 @@ class BetStatusBottomSheetBetDisplayer(
source: NestedScrollSource source: NestedScrollSource
) = available.copy(x = 0f) ) = 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) statBar(this)
@ -202,8 +201,7 @@ class BetStatusBottomSheetBetDisplayer(
.5f to AllInTheme.colors.background2 .5f to AllInTheme.colors.background2
) )
) )
.padding(7.dp) .padding(bottomSheetNavigationBarsInsets().asPaddingValues(7.dp)),
.navigationBarsPadding(),
text = stringResource(id = R.string.Participate), text = stringResource(id = R.string.Participate),
enabled = betDetail.bet.betStatus == BetStatus.IN_PROGRESS, enabled = betDetail.bet.betStatus == BetStatus.IN_PROGRESS,
onClick = openParticipateSheet onClick = openParticipateSheet
@ -223,11 +221,11 @@ class BetStatusBottomSheetBetDisplayer(
val configuration = LocalConfiguration.current 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 response1Answer = remember(betDetail) { betDetail.getAnswerOfResponse(response1) }
val response2Answer = remember { betDetail.getAnswerOfResponse(response2) } val response2Answer = remember(betDetail) { betDetail.getAnswerOfResponse(response2) }
BinaryStatBar( BinaryStatBar(
response1Percentage = remember { response1Percentage = remember(betDetail) {
response1Answer?.let { betDetail.getPercentageOfAnswer(response1Answer) } ?: 0f response1Answer?.let { betDetail.getPercentageOfAnswer(response1Answer) } ?: 0f
}, },
response1 = response1Display(), response1 = response1Display(),
@ -259,17 +257,10 @@ class BetStatusBottomSheetBetDisplayer(
} }
private fun LazyListScope.displayMultiStatBar( private fun LazyListScope.displayMultiStatBar(
betDetail: BetDetail, responsesWithDetail: List<Pair<BetAnswerDetail, Float>>,
responses: List<String>,
locale: Locale locale: Locale
) { ) {
val responsesWithDetail = responses.mapNotNull { itemsIndexed(responsesWithDetail) { idx, (answer, percentage) ->
betDetail.getAnswerOfResponse(it)
}.associateWith {
betDetail.getPercentageOfAnswer(it)
}
itemsIndexed(responsesWithDetail.toList().sortedByDescending { it.second }) { idx, (answer, percentage) ->
val isWin = remember { idx == 0 } val isWin = remember { idx == 0 }
SimpleStatBar( SimpleStatBar(
@ -305,7 +296,7 @@ class BetStatusBottomSheetBetDisplayer(
@Composable @Composable
override fun DisplayYesNoBet(betDetail: BetDetail, currentUser: User) { override fun DisplayYesNoBet(betDetail: BetDetail, currentUser: User) {
DisplayBet( DisplayBetDail(
betDetail = betDetail, betDetail = betDetail,
currentUser = currentUser, currentUser = currentUser,
winnerColor = { winnerColor = {
@ -327,7 +318,7 @@ class BetStatusBottomSheetBetDisplayer(
override fun DisplayMatchBet(betDetail: BetDetail, currentUser: User) { override fun DisplayMatchBet(betDetail: BetDetail, currentUser: User) {
val matchBet = remember { betDetail.bet as MatchBet } val matchBet = remember { betDetail.bet as MatchBet }
DisplayBet( DisplayBetDail(
betDetail = betDetail, betDetail = betDetail,
currentUser = currentUser, currentUser = currentUser,
winnerColor = { winnerColor = {
@ -349,7 +340,19 @@ class BetStatusBottomSheetBetDisplayer(
val configuration = LocalConfiguration.current val configuration = LocalConfiguration.current
val locale = remember { ConfigurationCompat.getLocales(configuration).get(0) ?: Locale.getDefault() } 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, betDetail = betDetail,
currentUser = currentUser, currentUser = currentUser,
winnerColor = { winnerColor = {
@ -369,8 +372,7 @@ class BetStatusBottomSheetBetDisplayer(
) )
} else { } else {
displayMultiStatBar( displayMultiStatBar(
betDetail = betDetail, responsesWithDetail = responsesWithDetail,
responses = customBet.getResponses(),
locale = locale locale = locale
) )
} }

@ -1,7 +1,11 @@
package fr.iut.alldev.allin.ui.core package fr.iut.alldev.allin.ui.core
import androidx.compose.foundation.layout.* import androidx.compose.foundation.layout.ColumnScope
import androidx.compose.material3.* 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.runtime.Composable
import androidx.compose.ui.graphics.Color import androidx.compose.ui.graphics.Color
import androidx.compose.ui.platform.LocalDensity import androidx.compose.ui.platform.LocalDensity

@ -39,7 +39,8 @@ fun AllInTextField(
maxChar: Int? = null, maxChar: Int? = null,
enabled: Boolean = true, enabled: Boolean = true,
trailingIcon: Painter? = null, trailingIcon: Painter? = null,
trailingContent: @Composable() (() -> Unit)? = null, trailingIconColor: Color? = null,
trailingContent: @Composable (() -> Unit)? = null,
placeholderFontSize: TextUnit = 18.sp, placeholderFontSize: TextUnit = 18.sp,
multiLine: Boolean = false, multiLine: Boolean = false,
errorText: String? = null, errorText: String? = null,
@ -84,7 +85,7 @@ fun AllInTextField(
Icon( Icon(
painter = it, painter = it,
contentDescription = null, contentDescription = null,
tint = AllInColorToken.allInLightGrey300 tint = trailingIconColor ?: AllInColorToken.allInLightGrey300
) )
} }
}, },
@ -107,7 +108,6 @@ fun AllInTextField(
) )
} }
@OptIn(ExperimentalFoundationApi::class)
@Composable @Composable
fun AllInPasswordField( fun AllInPasswordField(
placeholder: String, placeholder: String,
@ -152,6 +152,11 @@ fun AllInFloatTextfield(
textStyle: TextStyle = AllInTheme.typography.p1, textStyle: TextStyle = AllInTheme.typography.p1,
placeholder: String? = null, placeholder: String? = null,
trailingIcon: Painter? = 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, maxChar: Int? = 5,
setValue: (Float?) -> Unit setValue: (Float?) -> Unit
) { ) {
@ -168,7 +173,12 @@ fun AllInFloatTextfield(
maxChar = maxChar, maxChar = maxChar,
textStyle = textStyle, textStyle = textStyle,
trailingIcon = trailingIcon, trailingIcon = trailingIcon,
keyboardType = KeyboardType.Number trailingIconColor = trailingIconColor,
keyboardType = KeyboardType.Number,
borderColor = borderColor,
containerColor = containerColor,
textColor = textColor,
placeholderColor = placeholderColor
) { ) {
it.verifyIsFloat(locale)?.let { it.verifyIsFloat(locale)?.let {
stringValue = it stringValue = it
@ -190,7 +200,12 @@ fun AllInIntTextField(
textStyle: TextStyle = AllInTheme.typography.p1, textStyle: TextStyle = AllInTheme.typography.p1,
placeholder: String? = null, placeholder: String? = null,
trailingIcon: Painter? = null, trailingIcon: Painter? = null,
trailingIconColor: Color? = null,
maxChar: Int? = 3, 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 setValue: (Int?) -> Unit
) { ) {
AllInTextField( AllInTextField(
@ -199,8 +214,13 @@ fun AllInIntTextField(
modifier = modifier, modifier = modifier,
maxChar = maxChar, maxChar = maxChar,
trailingIcon = trailingIcon, trailingIcon = trailingIcon,
trailingIconColor = trailingIconColor,
textStyle = textStyle, textStyle = textStyle,
keyboardType = KeyboardType.NumberPassword keyboardType = KeyboardType.NumberPassword,
borderColor = borderColor,
containerColor = containerColor,
textColor = textColor,
placeholderColor = placeholderColor
) { ) {
if (it.isEmpty()) setValue(null) if (it.isEmpty()) setValue(null)
else if (it.isDigitsOnly()) { else if (it.isDigitsOnly()) {
@ -226,7 +246,6 @@ private fun AllInTextFieldPlaceholderPreview() {
} }
@OptIn(ExperimentalFoundationApi::class)
@Preview @Preview
@Preview(uiMode = Configuration.UI_MODE_NIGHT_YES) @Preview(uiMode = Configuration.UI_MODE_NIGHT_YES)
@Composable @Composable
@ -240,8 +259,8 @@ private fun AllInTextFieldValuePreview() {
} }
} }
@OptIn(ExperimentalFoundationApi::class)
@Preview @Preview
@Preview(uiMode = Configuration.UI_MODE_NIGHT_YES)
@Composable @Composable
private fun AllInTextFieldErrorPreview() { private fun AllInTextFieldErrorPreview() {
AllInTheme { AllInTheme {
@ -254,8 +273,8 @@ private fun AllInTextFieldErrorPreview() {
} }
} }
@OptIn(ExperimentalFoundationApi::class)
@Preview @Preview
@Preview(uiMode = Configuration.UI_MODE_NIGHT_YES)
@Composable @Composable
private fun AllInTextFieldPasswordPreview() { private fun AllInTextFieldPasswordPreview() {
AllInTheme { AllInTheme {
@ -269,6 +288,7 @@ private fun AllInTextFieldPasswordPreview() {
} }
@Preview @Preview
@Preview(uiMode = Configuration.UI_MODE_NIGHT_YES)
@Composable @Composable
private fun AllInTextFieldMultilinePreview() { private fun AllInTextFieldMultilinePreview() {
AllInTheme { AllInTheme {

@ -71,7 +71,6 @@ fun MainScreen(
val focusManager = LocalFocusManager.current val focusManager = LocalFocusManager.current
val drawerState = rememberDrawerState(initialValue = DrawerValue.Closed) val drawerState = rememberDrawerState(initialValue = DrawerValue.Closed)
LaunchedEffect(key1 = drawerState.targetValue) { LaunchedEffect(key1 = drawerState.targetValue) {
focusManager.clearFocus() focusManager.clearFocus()
} }
@ -99,33 +98,26 @@ fun MainScreen(
val (loading, setLoading) = remember { mainViewModel.loading } val (loading, setLoading) = remember { mainViewModel.loading }
val currentUser by mainViewModel.currentUser.collectAsStateWithLifecycle() val currentUser by mainViewModel.currentUser.collectAsStateWithLifecycle()
val selectedBet by remember { mainViewModel.selectedBet } val selectedBet by remember { mainViewModel.selectedBet }
val statusVisibility = remember { mutableStateOf(false) } var statusVisibility by remember { mutableStateOf(false) }
val sheetBackVisibility = remember { mutableStateOf(false) } var statusVisibilityConfirm by remember { mutableStateOf<SheetValue?>(null) }
val setStatusVisibility = { it: Boolean ->
statusVisibility.value = it
if (it) sheetBackVisibility.value = true
}
val (participateSheetVisibility, setParticipateSheetVisibility) = remember { mutableStateOf(false) } 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( val statusBottomSheetState = rememberModalBottomSheetState(
skipPartiallyExpanded = true, skipPartiallyExpanded = true,
confirmValueChange = { confirmValueChange = {
if (it == SheetValue.Hidden) { statusVisibilityConfirm = it
sheetBackVisibility.value = false
}
true true
} }
) )
val betStatusDisplayer = remember {
BetStatusBottomSheetBetDisplayer(
openParticipateSheet = { setParticipateSheetVisibility(true) }
)
}
val events = remember { mainViewModel.events }
val eventBottomSheetState = rememberModalBottomSheetState(skipPartiallyExpanded = true)
AllInDrawer( AllInDrawer(
drawerState = drawerState, drawerState = drawerState,
@ -161,7 +153,7 @@ fun MainScreen(
navController = navController, navController = navController,
selectBet = { bet, participate -> selectBet = { bet, participate ->
mainViewModel.openBetDetail(bet) { detail -> mainViewModel.openBetDetail(bet) { detail ->
setStatusVisibility(true) statusVisibility = true
if ( if (
detail.bet.betStatus == BetStatus.IN_PROGRESS && detail.bet.betStatus == BetStatus.IN_PROGRESS &&
detail.userParticipation == null && detail.userParticipation == null &&
@ -238,9 +230,9 @@ fun MainScreen(
BetStatusBottomSheet( BetStatusBottomSheet(
state = statusBottomSheetState, state = statusBottomSheetState,
sheetVisibility = statusVisibility.value, sheetVisibility = statusVisibility,
sheetBackVisibility = sheetBackVisibility.value, sheetBackVisibility = statusBottomSheetState.targetValue == SheetValue.Expanded || statusVisibilityConfirm == SheetValue.Expanded,
onDismiss = { setStatusVisibility(false) }, onDismiss = { statusVisibility = false },
betDetail = selectedBet, betDetail = selectedBet,
displayBet = { detail -> displayBet = { detail ->
currentUser?.let { user -> betStatusDisplayer.DisplayBet(betDetail = detail, currentUser = user) } currentUser?.let { user -> betStatusDisplayer.DisplayBet(betDetail = detail, currentUser = user) }

@ -8,6 +8,7 @@ import androidx.lifecycle.viewModelScope
import dagger.hilt.android.lifecycle.HiltViewModel import dagger.hilt.android.lifecycle.HiltViewModel
import fr.iut.alldev.allin.data.model.bet.Bet 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.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.model.bet.vo.BetDetail
import fr.iut.alldev.allin.data.repository.BetRepository import fr.iut.alldev.allin.data.repository.BetRepository
import fr.iut.alldev.allin.data.repository.UserRepository import fr.iut.alldev.allin.data.repository.UserRepository
@ -122,13 +123,21 @@ class MainViewModel @Inject constructor(
loading.value = true loading.value = true
currentUser.value?.let { user -> currentUser.value?.let { user ->
decreaseCoins(stake) decreaseCoins(stake)
selectedBet.value?.bet?.let { selectedBet.value?.let {
val participation = Participation( val participation = Participation(
betId = it.id, betId = it.bet.id,
username = user.username, username = user.username,
response = response, response = response,
stake = stake stake = stake
) )
selectedBet.value = it.copy(
userParticipation = participation,
participations = it.participations + participation,
answers = getAnswerDetails(it.bet, it.participations + participation),
)
betRepository.participateToBet(participation, keystoreManager.getTokenOrEmpty()) betRepository.participateToBet(participation, keystoreManager.getTokenOrEmpty())
} }
} }
@ -137,6 +146,20 @@ class MainViewModel @Inject constructor(
} }
} }
private fun getAnswerDetails(bet: Bet, participations: List<Participation>): List<BetAnswerDetail> {
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) { private fun confirmBet(response: String, betId: String) {
viewModelScope.launch { viewModelScope.launch {
betRepository.confirmBet( betRepository.confirmBet(

@ -316,39 +316,11 @@ class MockAllInApi : AllInApi {
answer = NO_VALUE, answer = NO_VALUE,
stake = 1500 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( ResponseParticipation(
id = "", id = "",
betId = "UUID1", betId = "UUID1",
username = mockUsers[6].first.username, username = mockUsers[6].first.username,
answer = NO_VALUE, answer = YES_VALUE,
stake = 222 stake = 222
), ),
ResponseParticipation( ResponseParticipation(
@ -365,27 +337,6 @@ class MockAllInApi : AllInApi {
answer = "Answer 1", answer = "Answer 1",
stake = 200 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( ResponseParticipation(
id = "", id = "",
betId = "UUID2", betId = "UUID2",
@ -431,13 +382,13 @@ class MockAllInApi : AllInApi {
id = "UUID3", id = "UUID3",
theme = "Sport", theme = "Sport",
sentenceBet = "Quelle équipe va gagner ?", sentenceBet = "Quelle équipe va gagner ?",
endRegistration = ZonedDateTime.now().minusDays(3), endRegistration = ZonedDateTime.now().plusDays(3),
endBet = ZonedDateTime.now().minusDays(2), endBet = ZonedDateTime.now().plusDays(2),
isPrivate = false, isPrivate = false,
response = listOf("The Monarchs", "Climate Change"), response = listOf("The Monarchs", "Climate Change"),
createdBy = "User 1", createdBy = "User 1",
type = BetType.MATCH, type = BetType.MATCH,
status = BetStatus.CLOSING, status = BetStatus.IN_PROGRESS,
) )
) )

Loading…
Cancel
Save