Merge pull request 'Sprint_4' (#4) from Sprint_4 into master
continuous-integration/drone/push Build is passing
Details
continuous-integration/drone/push Build is passing
Details
Reviewed-on: #4master V4
commit
b40788676f
@ -0,0 +1,403 @@
|
||||
package fr.iut.alldev.allin.ui.betConfirmation
|
||||
|
||||
import androidx.compose.animation.core.animateFloatAsState
|
||||
import androidx.compose.foundation.background
|
||||
import androidx.compose.foundation.layout.Arrangement
|
||||
import androidx.compose.foundation.layout.Box
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.Row
|
||||
import androidx.compose.foundation.layout.Spacer
|
||||
import androidx.compose.foundation.layout.fillMaxSize
|
||||
import androidx.compose.foundation.layout.fillMaxWidth
|
||||
import androidx.compose.foundation.layout.height
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.layout.size
|
||||
import androidx.compose.foundation.lazy.LazyColumn
|
||||
import androidx.compose.foundation.lazy.items
|
||||
import androidx.compose.material.icons.Icons
|
||||
import androidx.compose.material.icons.filled.Close
|
||||
import androidx.compose.material3.Icon
|
||||
import androidx.compose.material3.IconButton
|
||||
import androidx.compose.material3.SheetState
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.runtime.mutableStateOf
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.runtime.setValue
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.draw.alpha
|
||||
import androidx.compose.ui.draw.scale
|
||||
import androidx.compose.ui.graphics.Color
|
||||
import androidx.compose.ui.platform.LocalConfiguration
|
||||
import androidx.compose.ui.res.painterResource
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.text.style.TextAlign
|
||||
import androidx.compose.ui.tooling.preview.Preview
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.compose.ui.unit.sp
|
||||
import androidx.core.os.ConfigurationCompat
|
||||
import fr.iut.alldev.allin.R
|
||||
import fr.iut.alldev.allin.data.ext.formatToMediumDateNoYear
|
||||
import fr.iut.alldev.allin.data.ext.formatToTime
|
||||
import fr.iut.alldev.allin.data.model.bet.BetStatus
|
||||
import fr.iut.alldev.allin.data.model.bet.CustomBet
|
||||
import fr.iut.alldev.allin.data.model.bet.MatchBet
|
||||
import fr.iut.alldev.allin.data.model.bet.NO_VALUE
|
||||
import fr.iut.alldev.allin.data.model.bet.YES_VALUE
|
||||
import fr.iut.alldev.allin.data.model.bet.YesNoBet
|
||||
import fr.iut.alldev.allin.data.model.bet.vo.BetAnswerDetail
|
||||
import fr.iut.alldev.allin.data.model.bet.vo.BetDetail
|
||||
import fr.iut.alldev.allin.ext.formatToSimple
|
||||
import fr.iut.alldev.allin.theme.AllInTheme
|
||||
import fr.iut.alldev.allin.ui.core.AllInBottomSheet
|
||||
import fr.iut.alldev.allin.ui.core.AllInButton
|
||||
import fr.iut.alldev.allin.ui.core.AllInCard
|
||||
import fr.iut.alldev.allin.ui.core.AllInMarqueeBox
|
||||
import fr.iut.alldev.allin.ui.core.bet.BetCard
|
||||
import java.time.ZonedDateTime
|
||||
import java.util.Locale
|
||||
|
||||
@Composable
|
||||
fun BetConfirmationBottomSheet(
|
||||
state: SheetState,
|
||||
sheetVisibility: Boolean,
|
||||
betDetail: BetDetail,
|
||||
onDismiss: () -> Unit,
|
||||
onConfirm: (selectedAnswer: String) -> Unit
|
||||
) {
|
||||
AllInBottomSheet(
|
||||
sheetVisibility = sheetVisibility,
|
||||
onDismiss = onDismiss,
|
||||
state = state,
|
||||
dragHandle = null
|
||||
) {
|
||||
BetConfirmationBottomSheetContent(
|
||||
betDetail = betDetail,
|
||||
onConfirm = {
|
||||
onConfirm(it)
|
||||
onDismiss()
|
||||
},
|
||||
onClose = onDismiss
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun BetConfirmationBottomSheetAnswer(
|
||||
text: String,
|
||||
odds: Float,
|
||||
modifier: Modifier = Modifier,
|
||||
color: Color = AllInTheme.colors.allInBlue,
|
||||
isSelected: Boolean,
|
||||
locale: Locale,
|
||||
onClick: () -> Unit
|
||||
) {
|
||||
val backColor = if (isSelected) AllInTheme.colors.allInPurple else AllInTheme.colors.white
|
||||
val contentColor = if (isSelected) AllInTheme.colors.white else null
|
||||
|
||||
AllInCard(
|
||||
backgroundColor = backColor,
|
||||
onClick = onClick,
|
||||
modifier = modifier
|
||||
) {
|
||||
Box(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.padding(horizontal = 12.dp, vertical = 18.dp),
|
||||
) {
|
||||
Text(
|
||||
text = text.uppercase(),
|
||||
color = contentColor ?: color,
|
||||
style = AllInTheme.typography.h1,
|
||||
fontSize = 40.sp,
|
||||
modifier = Modifier.align(Alignment.Center)
|
||||
)
|
||||
|
||||
AllInCard(
|
||||
radius = 50.dp,
|
||||
backgroundColor = contentColor ?: AllInTheme.colors.allInPurple,
|
||||
modifier = Modifier.align(Alignment.CenterEnd)
|
||||
) {
|
||||
Box(Modifier.padding(vertical = 4.dp, horizontal = 8.dp)) {
|
||||
Text(
|
||||
text = "x${odds.formatToSimple(locale)}",
|
||||
color = backColor,
|
||||
style = AllInTheme.typography.h2
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun ConfirmationAnswers(
|
||||
betDetail: BetDetail,
|
||||
selectedAnswer: String?,
|
||||
onClick: (String) -> Unit
|
||||
) {
|
||||
val configuration = LocalConfiguration.current
|
||||
val locale =
|
||||
remember { ConfigurationCompat.getLocales(configuration).get(0) ?: Locale.getDefault() }
|
||||
|
||||
LazyColumn(
|
||||
verticalArrangement = Arrangement.spacedBy(8.dp)
|
||||
) {
|
||||
when (betDetail.bet) {
|
||||
is CustomBet -> items((betDetail.bet as CustomBet).possibleAnswers) {
|
||||
betDetail.getAnswerOfResponse(it)?.let {
|
||||
val opacity by animateFloatAsState(
|
||||
targetValue = if (selectedAnswer != null && selectedAnswer != it.response) .5f else 1f,
|
||||
label = ""
|
||||
)
|
||||
|
||||
|
||||
BetConfirmationBottomSheetAnswer(
|
||||
text = it.response,
|
||||
odds = it.odds,
|
||||
locale = locale,
|
||||
onClick = { onClick(it.response) },
|
||||
isSelected = selectedAnswer == it.response,
|
||||
modifier = Modifier.alpha(opacity)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
is MatchBet -> {
|
||||
val bet = (betDetail.bet as MatchBet)
|
||||
item {
|
||||
betDetail.getAnswerOfResponse(bet.nameTeam1)?.let {
|
||||
val opacity by animateFloatAsState(
|
||||
targetValue = if (selectedAnswer != null && selectedAnswer != it.response) .5f else 1f,
|
||||
label = ""
|
||||
)
|
||||
BetConfirmationBottomSheetAnswer(
|
||||
text = it.response,
|
||||
odds = it.odds,
|
||||
locale = locale,
|
||||
onClick = { onClick(it.response) },
|
||||
isSelected = selectedAnswer == it.response,
|
||||
modifier = Modifier.alpha(opacity)
|
||||
)
|
||||
}
|
||||
}
|
||||
item {
|
||||
betDetail.getAnswerOfResponse(bet.nameTeam2)?.let {
|
||||
val opacity by animateFloatAsState(
|
||||
targetValue = if (selectedAnswer != null && selectedAnswer != it.response) .5f else 1f,
|
||||
label = ""
|
||||
)
|
||||
|
||||
BetConfirmationBottomSheetAnswer(
|
||||
text = it.response,
|
||||
color = AllInTheme.colors.allInBarPink,
|
||||
odds = it.odds,
|
||||
locale = locale,
|
||||
onClick = { onClick(it.response) },
|
||||
isSelected = selectedAnswer == it.response,
|
||||
modifier = Modifier
|
||||
.alpha(opacity)
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
is YesNoBet -> {
|
||||
item {
|
||||
betDetail.getAnswerOfResponse(YES_VALUE)?.let {
|
||||
val opacity by animateFloatAsState(
|
||||
targetValue = if (selectedAnswer != null && selectedAnswer != it.response) .5f else 1f,
|
||||
label = ""
|
||||
)
|
||||
val scale by animateFloatAsState(
|
||||
targetValue = if (selectedAnswer == null) 1f
|
||||
else if (selectedAnswer != it.response) .95f else 1.05f,
|
||||
label = ""
|
||||
)
|
||||
|
||||
BetConfirmationBottomSheetAnswer(
|
||||
text = it.response,
|
||||
odds = it.odds,
|
||||
locale = locale,
|
||||
onClick = { onClick(it.response) },
|
||||
isSelected = selectedAnswer == it.response,
|
||||
modifier = Modifier
|
||||
.alpha(opacity)
|
||||
.scale(scale)
|
||||
)
|
||||
}
|
||||
}
|
||||
item {
|
||||
betDetail.getAnswerOfResponse(NO_VALUE)?.let {
|
||||
val opacity by animateFloatAsState(
|
||||
targetValue = if (selectedAnswer != null && selectedAnswer != it.response) .5f else 1f,
|
||||
label = ""
|
||||
)
|
||||
val scale by animateFloatAsState(
|
||||
targetValue = if (selectedAnswer == null) 1f
|
||||
else if (selectedAnswer != it.response) .95f else 1f,
|
||||
label = ""
|
||||
)
|
||||
|
||||
BetConfirmationBottomSheetAnswer(
|
||||
text = it.response,
|
||||
color = AllInTheme.colors.allInBarPink,
|
||||
odds = it.odds,
|
||||
locale = locale,
|
||||
onClick = { onClick(it.response) },
|
||||
isSelected = selectedAnswer == it.response,
|
||||
modifier = Modifier
|
||||
.alpha(opacity)
|
||||
.scale(scale)
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun BetConfirmationBottomSheetContent(
|
||||
betDetail: BetDetail,
|
||||
onConfirm: (String) -> Unit,
|
||||
onClose: () -> Unit
|
||||
) {
|
||||
var selectedAnswer by remember { mutableStateOf<String?>(null) }
|
||||
|
||||
AllInMarqueeBox(backgroundColor = AllInTheme.colors.allInDarkGrey300) {
|
||||
Box(
|
||||
modifier = Modifier
|
||||
.fillMaxSize()
|
||||
.padding(16.dp)
|
||||
) {
|
||||
IconButton(
|
||||
onClick = onClose,
|
||||
modifier = Modifier
|
||||
.size(24.dp)
|
||||
.align(Alignment.TopStart)
|
||||
) {
|
||||
Icon(
|
||||
imageVector = Icons.Default.Close,
|
||||
tint = AllInTheme.colors.white,
|
||||
contentDescription = null,
|
||||
modifier = Modifier.size(24.dp)
|
||||
)
|
||||
}
|
||||
|
||||
Icon(
|
||||
painter = painterResource(R.drawable.allin),
|
||||
contentDescription = null,
|
||||
tint = AllInTheme.colors.white,
|
||||
modifier = Modifier
|
||||
.size(40.dp)
|
||||
.align(Alignment.TopCenter)
|
||||
)
|
||||
|
||||
Column(
|
||||
modifier = Modifier.align(Alignment.Center),
|
||||
verticalArrangement = Arrangement.spacedBy(22.dp),
|
||||
horizontalAlignment = Alignment.CenterHorizontally
|
||||
) {
|
||||
BetCard(
|
||||
title = betDetail.bet.phrase,
|
||||
creator = betDetail.bet.creator,
|
||||
category = betDetail.bet.theme,
|
||||
date = betDetail.bet.endBetDate.formatToMediumDateNoYear(),
|
||||
time = betDetail.bet.endBetDate.formatToTime(),
|
||||
status = betDetail.bet.betStatus
|
||||
) {
|
||||
Row(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.background(AllInTheme.colors.allInMainGradient)
|
||||
.padding(16.dp),
|
||||
horizontalArrangement = Arrangement.Center,
|
||||
verticalAlignment = Alignment.CenterVertically
|
||||
) {
|
||||
Text(
|
||||
text = stringResource(id = R.string.Finished),
|
||||
color = AllInTheme.colors.white,
|
||||
style = AllInTheme.typography.h1,
|
||||
fontSize = 24.sp
|
||||
)
|
||||
}
|
||||
}
|
||||
Text(
|
||||
text = "Ce bet est arrivé à la date de fin. Vous devez à présent distribuer les gains en validant le pari gagnant.",
|
||||
color = AllInTheme.colors.allInLightGrey200,
|
||||
style = AllInTheme.typography.p2,
|
||||
textAlign = TextAlign.Center
|
||||
)
|
||||
|
||||
Spacer(modifier = Modifier.height(16.dp))
|
||||
|
||||
Text(
|
||||
text = "Veuillez choisir la réponse finale :",
|
||||
fontSize = 20.sp,
|
||||
color = AllInTheme.colors.white,
|
||||
style = AllInTheme.typography.h1,
|
||||
modifier = Modifier.fillMaxWidth()
|
||||
)
|
||||
ConfirmationAnswers(
|
||||
betDetail = betDetail,
|
||||
selectedAnswer = selectedAnswer
|
||||
) { selectedAnswer = if (selectedAnswer == it) null else it }
|
||||
}
|
||||
if (selectedAnswer != null) {
|
||||
AllInButton(
|
||||
color = AllInTheme.colors.allInPurple,
|
||||
text = stringResource(id = R.string.Validate),
|
||||
textColor = AllInTheme.colors.white,
|
||||
radius = 5.dp,
|
||||
onClick = { selectedAnswer?.let(onConfirm) },
|
||||
modifier = Modifier.align(Alignment.BottomCenter)
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Preview
|
||||
@Composable
|
||||
private fun BetConfirmationBottomSheetContentPreview() {
|
||||
AllInTheme {
|
||||
BetConfirmationBottomSheetContent(
|
||||
betDetail = BetDetail(
|
||||
bet = YesNoBet(
|
||||
id = "1",
|
||||
theme = "Theme",
|
||||
phrase = "Phrase",
|
||||
endRegisterDate = ZonedDateTime.now(),
|
||||
endBetDate = ZonedDateTime.now(),
|
||||
isPublic = true,
|
||||
betStatus = BetStatus.FINISHED,
|
||||
creator = "creator",
|
||||
),
|
||||
answers = listOf(
|
||||
BetAnswerDetail(
|
||||
response = YES_VALUE,
|
||||
totalStakes = 300,
|
||||
totalParticipants = 2,
|
||||
highestStake = 200,
|
||||
odds = 1.0f
|
||||
),
|
||||
BetAnswerDetail(
|
||||
response = NO_VALUE,
|
||||
totalStakes = 150,
|
||||
totalParticipants = 1,
|
||||
highestStake = 150,
|
||||
odds = 2.0f
|
||||
)
|
||||
),
|
||||
participations = emptyList(),
|
||||
userParticipation = null
|
||||
),
|
||||
onConfirm = { }
|
||||
) {
|
||||
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,30 @@
|
||||
package fr.iut.alldev.allin.ui.betHistory
|
||||
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.collectAsState
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.hilt.navigation.compose.hiltViewModel
|
||||
import fr.iut.alldev.allin.R
|
||||
import fr.iut.alldev.allin.data.ext.formatToMediumDateNoYear
|
||||
import fr.iut.alldev.allin.data.ext.formatToTime
|
||||
import fr.iut.alldev.allin.ui.betHistory.components.GenericHistory
|
||||
|
||||
@Composable
|
||||
fun BetCurrentScreen(
|
||||
viewModel: BetCurrentViewModel = hiltViewModel()
|
||||
) {
|
||||
val bets by viewModel.bets.collectAsState()
|
||||
GenericHistory(
|
||||
title = stringResource(id = R.string.bet_history_current_title),
|
||||
bets = bets,
|
||||
getTitle = { it.bet.phrase },
|
||||
getCreator = { it.bet.creator },
|
||||
getCategory = { it.bet.theme },
|
||||
getEndRegisterDate = { it.bet.endRegisterDate.formatToMediumDateNoYear() },
|
||||
getEndBetTime = { it.bet.endBetDate.formatToTime() },
|
||||
getStatus = { it.bet.betStatus },
|
||||
getNbCoins = { it.userParticipation?.stake ?: 0 },
|
||||
getWon = { true }
|
||||
)
|
||||
}
|
@ -0,0 +1,44 @@
|
||||
package fr.iut.alldev.allin.ui.betHistory
|
||||
|
||||
import androidx.lifecycle.ViewModel
|
||||
import androidx.lifecycle.viewModelScope
|
||||
import dagger.hilt.android.lifecycle.HiltViewModel
|
||||
import fr.iut.alldev.allin.data.model.bet.vo.BetDetail
|
||||
import fr.iut.alldev.allin.data.repository.BetRepository
|
||||
import fr.iut.alldev.allin.keystore.AllInKeystoreManager
|
||||
import kotlinx.coroutines.flow.MutableStateFlow
|
||||
import kotlinx.coroutines.flow.SharingStarted
|
||||
import kotlinx.coroutines.flow.StateFlow
|
||||
import kotlinx.coroutines.flow.asStateFlow
|
||||
import kotlinx.coroutines.flow.filterNotNull
|
||||
import kotlinx.coroutines.flow.stateIn
|
||||
import kotlinx.coroutines.launch
|
||||
import javax.inject.Inject
|
||||
|
||||
@HiltViewModel
|
||||
class BetCurrentViewModel @Inject constructor(
|
||||
private val betRepository: BetRepository,
|
||||
private val keystoreManager: AllInKeystoreManager
|
||||
) : ViewModel() {
|
||||
private val _bets: MutableStateFlow<List<BetDetail>> by lazy {
|
||||
MutableStateFlow(emptyList())
|
||||
}
|
||||
|
||||
val bets: StateFlow<List<BetDetail>> by lazy {
|
||||
_bets.asStateFlow()
|
||||
.filterNotNull()
|
||||
.stateIn(
|
||||
viewModelScope,
|
||||
SharingStarted.WhileSubscribed(5_000L),
|
||||
emptyList()
|
||||
)
|
||||
}
|
||||
|
||||
init {
|
||||
viewModelScope.launch {
|
||||
_bets.emit(
|
||||
betRepository.getToConfirm(keystoreManager.getTokenOrEmpty())
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
@ -1,65 +1,30 @@
|
||||
package fr.iut.alldev.allin.ui.betHistory
|
||||
|
||||
import androidx.compose.foundation.layout.Arrangement
|
||||
import androidx.compose.foundation.layout.PaddingValues
|
||||
import androidx.compose.foundation.layout.fillMaxSize
|
||||
import androidx.compose.foundation.layout.fillMaxWidth
|
||||
import androidx.compose.foundation.lazy.LazyColumn
|
||||
import androidx.compose.foundation.lazy.items
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.collectAsState
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.text.style.TextAlign
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.compose.ui.unit.sp
|
||||
import androidx.hilt.navigation.compose.hiltViewModel
|
||||
import fr.iut.alldev.allin.R
|
||||
import fr.iut.alldev.allin.data.ext.formatToMediumDateNoYear
|
||||
import fr.iut.alldev.allin.data.ext.formatToTime
|
||||
import fr.iut.alldev.allin.theme.AllInTheme
|
||||
import fr.iut.alldev.allin.ui.betHistory.components.BetHistoryScreenCard
|
||||
import fr.iut.alldev.allin.ui.betHistory.components.GenericHistory
|
||||
|
||||
@Composable
|
||||
fun BetHistoryScreen(
|
||||
isCurrent: Boolean,
|
||||
viewModel: BetHistoryViewModel = hiltViewModel(),
|
||||
viewModel: BetHistoryViewModel = hiltViewModel()
|
||||
) {
|
||||
val bets by viewModel.bets.collectAsState()
|
||||
|
||||
LazyColumn(
|
||||
modifier = Modifier.fillMaxSize(),
|
||||
contentPadding = PaddingValues(horizontal = 24.dp, vertical = 18.dp),
|
||||
verticalArrangement = Arrangement.spacedBy(18.dp),
|
||||
) {
|
||||
item {
|
||||
Text(
|
||||
text = stringResource(
|
||||
id = if (isCurrent) R.string.bet_history_current_title
|
||||
else R.string.bet_history_title
|
||||
),
|
||||
style = AllInTheme.typography.h1,
|
||||
color = AllInTheme.colors.allInGrey,
|
||||
fontSize = 24.sp,
|
||||
textAlign = TextAlign.Center,
|
||||
modifier = Modifier.fillMaxWidth()
|
||||
)
|
||||
}
|
||||
|
||||
bets?.let { bets ->
|
||||
items(bets) {
|
||||
BetHistoryScreenCard(
|
||||
title = it.phrase,
|
||||
creator = it.creator,
|
||||
category = it.theme,
|
||||
date = it.endRegisterDate.formatToMediumDateNoYear(),
|
||||
time = it.endRegisterDate.formatToTime(),
|
||||
status = it.betStatus,
|
||||
nbCoins = 230
|
||||
GenericHistory(
|
||||
title = stringResource(id = R.string.bet_history_title),
|
||||
bets = bets,
|
||||
getTitle = { it.bet.phrase },
|
||||
getCreator = { it.bet.creator },
|
||||
getCategory = { it.bet.theme },
|
||||
getEndRegisterDate = { it.bet.endRegisterDate.formatToMediumDateNoYear() },
|
||||
getEndBetTime = { it.bet.endBetDate.formatToTime() },
|
||||
getStatus = { it.bet.betStatus },
|
||||
getNbCoins = { it.amount },
|
||||
getWon = { it.won }
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -1,35 +1,44 @@
|
||||
package fr.iut.alldev.allin.ui.betHistory
|
||||
|
||||
import androidx.lifecycle.SavedStateHandle
|
||||
import androidx.lifecycle.ViewModel
|
||||
import androidx.lifecycle.viewModelScope
|
||||
import dagger.hilt.android.lifecycle.HiltViewModel
|
||||
import fr.iut.alldev.allin.data.model.bet.Bet
|
||||
import fr.iut.alldev.allin.data.model.bet.BetResultDetail
|
||||
import fr.iut.alldev.allin.data.repository.BetRepository
|
||||
import fr.iut.alldev.allin.ui.navigation.NavArguments
|
||||
import fr.iut.alldev.allin.keystore.AllInKeystoreManager
|
||||
import kotlinx.coroutines.flow.MutableStateFlow
|
||||
import kotlinx.coroutines.flow.SharingStarted
|
||||
import kotlinx.coroutines.flow.StateFlow
|
||||
import kotlinx.coroutines.flow.asStateFlow
|
||||
import kotlinx.coroutines.flow.filterNotNull
|
||||
import kotlinx.coroutines.flow.flatMapConcat
|
||||
import kotlinx.coroutines.flow.flowOf
|
||||
import kotlinx.coroutines.flow.stateIn
|
||||
import kotlinx.coroutines.launch
|
||||
import javax.inject.Inject
|
||||
|
||||
@HiltViewModel
|
||||
class BetHistoryViewModel @Inject constructor(
|
||||
savedStateHandle: SavedStateHandle,
|
||||
private val betRepository: BetRepository,
|
||||
private val keystoreManager: AllInKeystoreManager
|
||||
) : ViewModel() {
|
||||
private val isCurrent: Boolean? = savedStateHandle[NavArguments.ARG_BET_HISTORY_IS_CURRENT]
|
||||
private val _bets: MutableStateFlow<List<BetResultDetail>> by lazy {
|
||||
MutableStateFlow(emptyList())
|
||||
}
|
||||
|
||||
val bets: StateFlow<List<Bet>?> by lazy {
|
||||
flowOf(isCurrent).filterNotNull().flatMapConcat {
|
||||
if (it) betRepository.getCurrentBets()
|
||||
else betRepository.getHistory()
|
||||
}.stateIn(
|
||||
val bets: StateFlow<List<BetResultDetail>> by lazy {
|
||||
_bets.asStateFlow()
|
||||
.filterNotNull()
|
||||
.stateIn(
|
||||
viewModelScope,
|
||||
SharingStarted.WhileSubscribed(5_000L),
|
||||
null
|
||||
emptyList()
|
||||
)
|
||||
}
|
||||
|
||||
init {
|
||||
viewModelScope.launch {
|
||||
_bets.emit(
|
||||
betRepository.getHistory(keystoreManager.getTokenOrEmpty())
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,60 @@
|
||||
package fr.iut.alldev.allin.ui.betHistory.components
|
||||
|
||||
import androidx.compose.foundation.layout.Arrangement
|
||||
import androidx.compose.foundation.layout.PaddingValues
|
||||
import androidx.compose.foundation.layout.fillMaxSize
|
||||
import androidx.compose.foundation.layout.fillMaxWidth
|
||||
import androidx.compose.foundation.lazy.LazyColumn
|
||||
import androidx.compose.foundation.lazy.items
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.text.style.TextAlign
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.compose.ui.unit.sp
|
||||
import fr.iut.alldev.allin.data.model.bet.BetStatus
|
||||
import fr.iut.alldev.allin.theme.AllInTheme
|
||||
|
||||
@Composable
|
||||
fun <T> GenericHistory(
|
||||
title: String,
|
||||
bets: List<T>,
|
||||
getTitle: (T) -> String,
|
||||
getCreator: (T) -> String,
|
||||
getCategory: (T) -> String,
|
||||
getEndRegisterDate: (T) -> String,
|
||||
getEndBetTime: (T) -> String,
|
||||
getStatus: (T) -> BetStatus,
|
||||
getNbCoins: (T) -> Int,
|
||||
getWon: (T) -> Boolean,
|
||||
) {
|
||||
LazyColumn(
|
||||
modifier = Modifier.fillMaxSize(),
|
||||
contentPadding = PaddingValues(horizontal = 24.dp, vertical = 18.dp),
|
||||
verticalArrangement = Arrangement.spacedBy(18.dp),
|
||||
) {
|
||||
item {
|
||||
Text(
|
||||
text = title,
|
||||
style = AllInTheme.typography.h1,
|
||||
color = AllInTheme.colors.allInGrey,
|
||||
fontSize = 24.sp,
|
||||
textAlign = TextAlign.Center,
|
||||
modifier = Modifier.fillMaxWidth()
|
||||
)
|
||||
}
|
||||
|
||||
items(bets) {
|
||||
BetHistoryScreenCard(
|
||||
title = getTitle(it),
|
||||
creator = getCreator(it),
|
||||
category = getCategory(it),
|
||||
date = getEndRegisterDate(it),
|
||||
time = getEndBetTime(it),
|
||||
status = getStatus(it),
|
||||
nbCoins = getNbCoins(it),
|
||||
won = getWon(it)
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,65 @@
|
||||
package fr.iut.alldev.allin.ui.core
|
||||
|
||||
import androidx.compose.foundation.MarqueeSpacing
|
||||
import androidx.compose.foundation.background
|
||||
import androidx.compose.foundation.basicMarquee
|
||||
import androidx.compose.foundation.layout.Box
|
||||
import androidx.compose.foundation.layout.BoxScope
|
||||
import androidx.compose.foundation.layout.aspectRatio
|
||||
import androidx.compose.foundation.layout.fillMaxHeight
|
||||
import androidx.compose.foundation.layout.fillMaxSize
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.draw.rotate
|
||||
import androidx.compose.ui.draw.scale
|
||||
import androidx.compose.ui.graphics.Brush
|
||||
import androidx.compose.ui.graphics.Color
|
||||
import androidx.compose.ui.res.painterResource
|
||||
import androidx.compose.ui.tooling.preview.Preview
|
||||
import androidx.compose.ui.unit.dp
|
||||
import fr.iut.alldev.allin.R
|
||||
import fr.iut.alldev.allin.theme.AllInTheme
|
||||
|
||||
@Composable
|
||||
fun AllInMarqueeBox(
|
||||
backgroundColor: Color = AllInTheme.themeColors.mainSurface,
|
||||
backgroundBrush: Brush? = null,
|
||||
content: @Composable BoxScope.() -> Unit
|
||||
|
||||
) {
|
||||
Box(
|
||||
modifier = Modifier
|
||||
.fillMaxSize().let { itModifier ->
|
||||
backgroundBrush?.let {
|
||||
itModifier.background(it)
|
||||
} ?: itModifier.background(backgroundColor)
|
||||
}
|
||||
) {
|
||||
Icon(
|
||||
painter = painterResource(id = R.drawable.allin_marquee),
|
||||
contentDescription = null,
|
||||
modifier = Modifier
|
||||
.fillMaxHeight()
|
||||
.aspectRatio(1f, true)
|
||||
.scale(1.2f)
|
||||
.rotate(11f)
|
||||
.basicMarquee(spacing = MarqueeSpacing(0.dp)),
|
||||
tint = AllInTheme.colors.white.copy(alpha = .05f)
|
||||
)
|
||||
content()
|
||||
}
|
||||
}
|
||||
|
||||
@Preview
|
||||
@Preview(widthDp = 800, heightDp = 1280)
|
||||
@Composable
|
||||
private fun AllInMarqueeBoxPreview() {
|
||||
AllInTheme {
|
||||
AllInMarqueeBox(
|
||||
backgroundBrush = AllInTheme.colors.allInMainGradient,
|
||||
) {
|
||||
Text("CONTENT")
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,12 @@
|
||||
package fr.iut.alldev.allin.ui.main.event
|
||||
|
||||
import androidx.compose.material3.SheetState
|
||||
import androidx.compose.runtime.Composable
|
||||
|
||||
sealed class AllInEvent {
|
||||
@Composable
|
||||
abstract fun Display(
|
||||
sheetState: SheetState,
|
||||
onDismiss: () -> Unit
|
||||
)
|
||||
}
|
@ -0,0 +1,39 @@
|
||||
package fr.iut.alldev.allin.ui.main.event
|
||||
|
||||
import androidx.compose.material3.SheetState
|
||||
import androidx.compose.runtime.Composable
|
||||
import fr.iut.alldev.allin.data.model.bet.vo.BetDetail
|
||||
import fr.iut.alldev.allin.ui.betConfirmation.BetConfirmationBottomSheet
|
||||
|
||||
data class ToConfirmBet(
|
||||
private val betDetail: BetDetail,
|
||||
private val onConfirm: (String) -> Unit
|
||||
) : AllInEvent() {
|
||||
@Composable
|
||||
override fun Display(
|
||||
sheetState: SheetState,
|
||||
onDismiss: () -> Unit
|
||||
) {
|
||||
BetConfirmationBottomSheet(
|
||||
state = sheetState,
|
||||
sheetVisibility = true,
|
||||
betDetail = betDetail,
|
||||
onDismiss = onDismiss,
|
||||
onConfirm = onConfirm
|
||||
)
|
||||
}
|
||||
|
||||
override fun hashCode(): Int {
|
||||
return betDetail.hashCode()
|
||||
}
|
||||
|
||||
override fun equals(other: Any?): Boolean {
|
||||
if (this === other) return true
|
||||
if (javaClass != other?.javaClass) return false
|
||||
|
||||
other as ToConfirmBet
|
||||
|
||||
return betDetail == other.betDetail
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,31 @@
|
||||
package fr.iut.alldev.allin.ui.main.event
|
||||
|
||||
import androidx.compose.material3.SheetState
|
||||
import androidx.compose.runtime.Composable
|
||||
import fr.iut.alldev.allin.data.model.User
|
||||
import fr.iut.alldev.allin.data.model.bet.BetResultDetail
|
||||
import fr.iut.alldev.allin.ui.betResult.BetResultBottomSheet
|
||||
|
||||
data class WonBet(
|
||||
private val user: User,
|
||||
private val betResult: BetResultDetail,
|
||||
) : AllInEvent() {
|
||||
@Composable
|
||||
override fun Display(
|
||||
sheetState: SheetState,
|
||||
onDismiss: () -> Unit
|
||||
) {
|
||||
BetResultBottomSheet(
|
||||
state = sheetState,
|
||||
sheetVisibility = true,
|
||||
onDismiss = onDismiss,
|
||||
bet = betResult.bet,
|
||||
username = user.username,
|
||||
coinAmount = betResult.amount,
|
||||
stake = betResult.participation.stake,
|
||||
winnings = betResult.amount,
|
||||
odds = 1f
|
||||
)
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,14 @@
|
||||
package fr.iut.alldev.allin.data.model.bet
|
||||
|
||||
data class BetResult(
|
||||
val betId: String,
|
||||
val result: String
|
||||
)
|
||||
|
||||
data class BetResultDetail(
|
||||
val betResult: BetResult,
|
||||
val bet: Bet,
|
||||
val participation: Participation,
|
||||
val amount: Int,
|
||||
val won: Boolean
|
||||
)
|
@ -1,19 +1,9 @@
|
||||
package fr.iut.alldev.allin.data.model.bet
|
||||
|
||||
sealed class BetStatus {
|
||||
data class Finished(val status: BetFinishedStatus) : BetStatus()
|
||||
|
||||
data object InProgress : BetStatus()
|
||||
|
||||
data object Waiting : BetStatus()
|
||||
|
||||
companion object {
|
||||
val entries = sequenceOf(
|
||||
InProgress,
|
||||
Waiting,
|
||||
Finished(BetFinishedStatus.WON),
|
||||
Finished(BetFinishedStatus.LOST)
|
||||
)
|
||||
}
|
||||
|
||||
enum class BetStatus {
|
||||
IN_PROGRESS,
|
||||
WAITING,
|
||||
CLOSING,
|
||||
FINISHED,
|
||||
CANCELLED
|
||||
}
|
@ -1,7 +1,7 @@
|
||||
package fr.iut.alldev.allin.data.model.bet
|
||||
|
||||
enum class BetType {
|
||||
YES_NO,
|
||||
BINARY,
|
||||
MATCH,
|
||||
CUSTOM
|
||||
}
|
||||
|
@ -1,10 +1,6 @@
|
||||
package fr.iut.alldev.allin.data.model.bet.vo
|
||||
|
||||
import fr.iut.alldev.allin.data.model.bet.Bet
|
||||
import fr.iut.alldev.allin.data.model.bet.Participation
|
||||
|
||||
data class BetResult(
|
||||
val bet: Bet,
|
||||
val participations: List<Participation>,
|
||||
val answerDetail: BetAnswerDetail
|
||||
val betId: String,
|
||||
val result: String
|
||||
)
|
@ -1,15 +1,18 @@
|
||||
package fr.iut.alldev.allin.data.repository
|
||||
|
||||
import fr.iut.alldev.allin.data.model.bet.Bet
|
||||
import fr.iut.alldev.allin.data.model.bet.BetResultDetail
|
||||
import fr.iut.alldev.allin.data.model.bet.Participation
|
||||
import fr.iut.alldev.allin.data.model.bet.vo.BetDetail
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
|
||||
abstract class BetRepository {
|
||||
abstract suspend fun createBet(bet: Bet, token: String)
|
||||
abstract suspend fun getHistory(): Flow<List<Bet>>
|
||||
abstract suspend fun getCurrentBets(): Flow<List<Bet>>
|
||||
abstract suspend fun getHistory(token: String): List<BetResultDetail>
|
||||
abstract suspend fun getCurrentBets(token: String): List<BetDetail>
|
||||
abstract suspend fun getBet(id: String, token: String): BetDetail
|
||||
abstract suspend fun participateToBet(participation: Participation, token: String)
|
||||
abstract suspend fun getAllBets(): Flow<List<Bet>>
|
||||
abstract suspend fun getAllBets(token: String): List<Bet>
|
||||
abstract suspend fun confirmBet(token: String, id: String, response: String)
|
||||
abstract suspend fun getToConfirm(token: String): List<BetDetail>
|
||||
abstract suspend fun getWon(token: String): List<BetResultDetail>
|
||||
}
|
Loading…
Reference in new issue