Bet creation
continuous-integration/drone/push Build is passing Details

pull/3/head
Arthur VALIN 1 year ago
parent b575f6e157
commit 269dfdd433

@ -31,7 +31,6 @@ import fr.iut.alldev.allin.ui.core.AllInTimePicker
import fr.iut.alldev.allin.ui.core.RainbowButton import fr.iut.alldev.allin.ui.core.RainbowButton
import fr.iut.alldev.allin.ui.core.SectionElement import fr.iut.alldev.allin.ui.core.SectionElement
import fr.iut.alldev.allin.ui.core.SelectionElement import fr.iut.alldev.allin.ui.core.SelectionElement
import fr.iut.alldev.allin.ui.main.MainViewModel
import java.time.Instant import java.time.Instant
import java.time.ZoneId import java.time.ZoneId
import java.time.ZonedDateTime import java.time.ZonedDateTime
@ -39,7 +38,8 @@ import java.time.ZonedDateTime
@Composable @Composable
fun BetCreationScreen( fun BetCreationScreen(
viewModel: BetCreationViewModel = hiltViewModel(), viewModel: BetCreationViewModel = hiltViewModel(),
mainViewModel: MainViewModel, setLoading: (Boolean) -> Unit,
onCreation: () -> Unit
) { ) {
val interactionSource = remember { MutableInteractionSource() } val interactionSource = remember { MutableInteractionSource() }
val betTypes = remember { BetType.values().toList() } val betTypes = remember { BetType.values().toList() }
@ -154,8 +154,11 @@ fun BetCreationScreen(
phraseFieldName = phraseFieldName, phraseFieldName = phraseFieldName,
registerDateFieldName = registerDateFieldName, registerDateFieldName = registerDateFieldName,
betDateFieldName = betDateFieldName, betDateFieldName = betDateFieldName,
setLoading = { mainViewModel.loading.value = it }, setLoading = setLoading,
onError = { hasError = true } onError = { hasError = true },
onSuccess = {
onCreation()
}
) )
} }
) )

@ -11,6 +11,7 @@ import fr.iut.alldev.allin.data.model.bet.BetType
import fr.iut.alldev.allin.data.repository.BetRepository import fr.iut.alldev.allin.data.repository.BetRepository
import fr.iut.alldev.allin.di.AllInCurrentUser import fr.iut.alldev.allin.di.AllInCurrentUser
import fr.iut.alldev.allin.ext.FieldErrorState import fr.iut.alldev.allin.ext.FieldErrorState
import fr.iut.alldev.allin.keystore.AllInKeystoreManager
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
import timber.log.Timber import timber.log.Timber
import java.time.ZonedDateTime import java.time.ZonedDateTime
@ -23,6 +24,7 @@ const val PHRASE_MIN_SIZE = 5
class BetCreationViewModel @Inject constructor( class BetCreationViewModel @Inject constructor(
@AllInCurrentUser val currentUser: User, @AllInCurrentUser val currentUser: User,
private val betRepository: BetRepository, private val betRepository: BetRepository,
private val keystoreManager: AllInKeystoreManager
) : ViewModel() { ) : ViewModel() {
var hasError = mutableStateOf(false) var hasError = mutableStateOf(false)
@ -92,6 +94,7 @@ class BetCreationViewModel @Inject constructor(
betDateFieldName: String, betDateFieldName: String,
onError: () -> Unit, onError: () -> Unit,
setLoading: (Boolean) -> Unit, setLoading: (Boolean) -> Unit,
onSuccess: () -> Unit
) { ) {
viewModelScope.launch { viewModelScope.launch {
setLoading(true) setLoading(true)
@ -118,7 +121,8 @@ class BetCreationViewModel @Inject constructor(
possibleAnswers = setOf(), possibleAnswers = setOf(),
creator = currentUser.username creator = currentUser.username
) )
betRepository.createBet(bet) betRepository.createBet(bet, keystoreManager.getToken() ?: "")
onSuccess()
} catch (e: AllInAPIException) { } catch (e: AllInAPIException) {
Timber.e(e) Timber.e(e)
onError() onError()

@ -0,0 +1,148 @@
package fr.iut.alldev.allin.ui.core.snackbar
import androidx.compose.foundation.background
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.size
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.Close
import androidx.compose.material3.ExperimentalMaterial3Api
import androidx.compose.material3.Icon
import androidx.compose.material3.IconButton
import androidx.compose.material3.SnackbarHost
import androidx.compose.material3.SnackbarHostState
import androidx.compose.material3.Surface
import androidx.compose.material3.SwipeToDismissBox
import androidx.compose.material3.SwipeToDismissValue
import androidx.compose.material3.Text
import androidx.compose.material3.rememberSwipeToDismissState
import androidx.compose.runtime.Composable
import androidx.compose.runtime.remember
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.graphics.vector.ImageVector
import androidx.compose.ui.text.style.TextOverflow
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.tooling.preview.PreviewParameter
import androidx.compose.ui.tooling.preview.PreviewParameterProvider
import androidx.compose.ui.unit.dp
import fr.iut.alldev.allin.theme.AllInTheme
@OptIn(ExperimentalMaterial3Api::class)
@Composable
fun AllInSnackbar(
snackbarState: SnackbarHostState
) {
SnackbarHost(
hostState = snackbarState
) { snackbarData ->
val dismissState = rememberSwipeToDismissState(
confirmValueChange = { value ->
if (value != SwipeToDismissValue.Settled) {
snackbarState.currentSnackbarData?.dismiss()
true
} else {
false
}
}
)
SwipeToDismissBox(
state = dismissState,
backgroundContent = {},
modifier = Modifier.padding(8.dp)
) {
val snackbarType = remember {
if (snackbarData.visuals is AllInSnackbarVisualsImpl) {
(snackbarData.visuals as AllInSnackbarVisualsImpl).type
} else {
SnackbarType.STANDARD
}
}
AllInSnackbarContent(
backgroundColor = snackbarType.getBackgroundColor(),
contentColor = AllInTheme.colors.white,
text = snackbarData.visuals.message,
icon = snackbarType.getIcon(),
dismiss = { snackbarState.currentSnackbarData?.dismiss() }
)
}
}
}
@Composable
fun AllInSnackbarContent(
backgroundColor: Color,
contentColor: Color,
text: String,
icon: ImageVector,
dismiss: () -> Unit
) {
Surface(
shape = RoundedCornerShape(16.dp),
shadowElevation = 4.dp,
) {
Row(
verticalAlignment = Alignment.CenterVertically,
horizontalArrangement = Arrangement.spacedBy(8.dp),
modifier = Modifier
.fillMaxWidth()
.background(backgroundColor)
.padding(8.dp)
) {
Icon(
imageVector = icon,
contentDescription = null,
tint = contentColor,
modifier = Modifier.size(24.dp)
)
Text(
text = text,
color = contentColor,
style = AllInTheme.typography.r,
overflow = TextOverflow.Ellipsis,
maxLines = 5,
modifier = Modifier.weight(1f)
)
IconButton(
onClick = dismiss,
modifier = Modifier
.size(24.dp)
) {
Icon(
imageVector = Icons.Default.Close,
contentDescription = null,
tint = contentColor,
modifier = Modifier.size(24.dp)
)
}
}
}
}
private class SnackbarTypePreviewProvider : PreviewParameterProvider<SnackbarType> {
override val values = SnackbarType.entries.asSequence()
}
@Preview
@Composable
private fun AllInSnackbarContentPreview(
@PreviewParameter(SnackbarTypePreviewProvider::class) snackbarType: SnackbarType
) {
AllInTheme {
AllInSnackbarContent(
backgroundColor = snackbarType.getBackgroundColor(),
contentColor = AllInTheme.colors.white,
text = "Lorem Ipsum",
icon = snackbarType.getIcon(),
dismiss = {}
)
}
}

@ -0,0 +1,36 @@
package fr.iut.alldev.allin.ui.core.snackbar
import androidx.compose.material3.SnackbarDuration
import androidx.compose.material3.SnackbarVisuals
class AllInSnackbarVisualsImpl(
override val message: String,
override val actionLabel: String? = null,
override val withDismissAction: Boolean,
override val duration: SnackbarDuration,
val type: SnackbarType
) : SnackbarVisuals {
override fun equals(other: Any?): Boolean {
if (this === other) return true
if (other == null || this::class != other::class) return false
other as AllInSnackbarVisualsImpl
if (message != other.message) return false
if (actionLabel != other.actionLabel) return false
if (withDismissAction != other.withDismissAction) return false
if (duration != other.duration) return false
if (type != other.type) return false
return true
}
override fun hashCode(): Int {
var result = message.hashCode()
result = 31 * result + actionLabel.hashCode()
result = 31 * result + withDismissAction.hashCode()
result = 31 * result + duration.hashCode()
result = 31 * result + type.hashCode()
return result
}
}

@ -0,0 +1,35 @@
package fr.iut.alldev.allin.ui.core.snackbar
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.CheckCircle
import androidx.compose.material.icons.filled.Error
import androidx.compose.material.icons.filled.Info
import androidx.compose.runtime.Composable
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.graphics.vector.ImageVector
import fr.iut.alldev.allin.theme.AllInTheme
import fr.iut.alldev.allin.ui.core.snackbar.SnackbarType.ERROR
import fr.iut.alldev.allin.ui.core.snackbar.SnackbarType.STANDARD
import fr.iut.alldev.allin.ui.core.snackbar.SnackbarType.SUCCESS
enum class SnackbarType {
STANDARD,
SUCCESS,
ERROR
}
@Composable
fun SnackbarType.getBackgroundColor(): Color =
when (this) {
STANDARD -> AllInTheme.colors.allInDark
SUCCESS -> AllInTheme.colors.allInPurple
ERROR -> AllInTheme.colors.allInBetWaiting
}
@Composable
fun SnackbarType.getIcon(): ImageVector =
when (this) {
STANDARD -> Icons.Default.Info
ERROR -> Icons.Default.Error
SUCCESS -> Icons.Default.CheckCircle
}

@ -14,6 +14,7 @@ import fr.iut.alldev.allin.theme.AllInTheme
import fr.iut.alldev.allin.ui.betStatus.BetStatusBottomSheet import fr.iut.alldev.allin.ui.betStatus.BetStatusBottomSheet
import fr.iut.alldev.allin.ui.betStatus.visitor.BetStatusBottomSheetDisplayBetVisitor import fr.iut.alldev.allin.ui.betStatus.visitor.BetStatusBottomSheetDisplayBetVisitor
import fr.iut.alldev.allin.ui.core.AllInLoading import fr.iut.alldev.allin.ui.core.AllInLoading
import fr.iut.alldev.allin.ui.core.snackbar.AllInSnackbarVisualsImpl
import fr.iut.alldev.allin.ui.main.components.AllInScaffold import fr.iut.alldev.allin.ui.main.components.AllInScaffold
import fr.iut.alldev.allin.ui.navigation.AllInDrawerNavHost import fr.iut.alldev.allin.ui.navigation.AllInDrawerNavHost
import fr.iut.alldev.allin.ui.navigation.Routes import fr.iut.alldev.allin.ui.navigation.Routes
@ -62,15 +63,12 @@ fun MainScreen(
mainViewModel: MainViewModel = hiltViewModel(), mainViewModel: MainViewModel = hiltViewModel(),
navigateToWelcomeScreen: () -> Unit navigateToWelcomeScreen: () -> Unit
) { ) {
val loading by remember { mainViewModel.loading } val scope = rememberCoroutineScope()
val currentUser = remember { var loading by remember { mainViewModel.loading }
mainViewModel.currentUserState val currentUser = remember { mainViewModel.currentUserState }
} val (selectedBet, setSelectedBet) = remember { mainViewModel.selectedBet }
val (statusVisibility, sheetBackVisibility, setStatusVisibility) = rememberBetStatusVisibilities()
val (selectedBet, setSelectedBet) = remember {
mainViewModel.selectedBet
}
val betStatusDisplayVisitor = remember { val betStatusDisplayVisitor = remember {
BetStatusBottomSheetDisplayBetVisitor( BetStatusBottomSheetDisplayBetVisitor(
@ -81,10 +79,26 @@ fun MainScreen(
) )
} }
val scope = rememberCoroutineScope() val snackbarHostState = remember { SnackbarHostState() }
var snackbarContent by remember { mainViewModel.snackbarContent }
val (statusVisibility, sheetBackVisibility, setStatusVisibility)
= rememberBetStatusVisibilities() LaunchedEffect(snackbarContent) {
snackbarContent?.let {
scope.launch {
snackbarHostState.currentSnackbarData?.dismiss()
snackbarHostState.showSnackbar(
AllInSnackbarVisualsImpl(
message = it.text,
withDismissAction = false,
duration = SnackbarDuration.Short,
type = it.type
)
)
snackbarHostState.currentSnackbarData
snackbarContent = null
}
}
}
val bottomSheetState = rememberModalBottomSheetState( val bottomSheetState = rememberModalBottomSheetState(
skipPartiallyExpanded = true, skipPartiallyExpanded = true,
@ -115,7 +129,8 @@ fun MainScreen(
AllInScaffold( AllInScaffold(
onMenuClicked = { scope.launch { drawerState.open() } }, onMenuClicked = { scope.launch { drawerState.open() } },
coinAmount = currentUser.userCoins.value, coinAmount = currentUser.userCoins.value,
drawerState = drawerState drawerState = drawerState,
snackbarHostState = snackbarHostState
) { ) {
LaunchedEffect(key1 = it) { LaunchedEffect(key1 = it) {
betStatusDisplayVisitor.paddingValues.value = it betStatusDisplayVisitor.paddingValues.value = it
@ -129,12 +144,13 @@ fun MainScreen(
) { ) {
AllInDrawerNavHost( AllInDrawerNavHost(
navController = navController, navController = navController,
mainViewModel = mainViewModel,
selectBet = { bet, participate -> selectBet = { bet, participate ->
setSelectedBet(bet) setSelectedBet(bet)
betStatusDisplayVisitor.participateBottomSheetVisibility.value = participate betStatusDisplayVisitor.participateBottomSheetVisibility.value = participate
setStatusVisibility(true) setStatusVisibility(true)
} },
setLoading = { loading = it },
putSnackbarContent = { mainViewModel.putSnackbarContent(it) }
) )
} }
} }

@ -1,5 +1,6 @@
package fr.iut.alldev.allin.ui.main package fr.iut.alldev.allin.ui.main
import androidx.compose.runtime.MutableState
import androidx.compose.runtime.mutableIntStateOf import androidx.compose.runtime.mutableIntStateOf
import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.mutableStateOf
import androidx.lifecycle.ViewModel import androidx.lifecycle.ViewModel
@ -9,6 +10,7 @@ import fr.iut.alldev.allin.data.model.User
import fr.iut.alldev.allin.data.model.bet.Bet import fr.iut.alldev.allin.data.model.bet.Bet
import fr.iut.alldev.allin.di.AllInCurrentUser import fr.iut.alldev.allin.di.AllInCurrentUser
import fr.iut.alldev.allin.keystore.AllInKeystoreManager import fr.iut.alldev.allin.keystore.AllInKeystoreManager
import fr.iut.alldev.allin.ui.core.snackbar.SnackbarType
import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext import kotlinx.coroutines.withContext
@ -29,6 +31,12 @@ class MainViewModel @Inject constructor(
val currentUserState = UserState(currentUser) val currentUserState = UserState(currentUser)
val selectedBet = mutableStateOf<Bet?>(null) val selectedBet = mutableStateOf<Bet?>(null)
val snackbarContent: MutableState<SnackbarContent?> by lazy { mutableStateOf(null) }
fun putSnackbarContent(content: SnackbarContent) {
snackbarContent.value = content
}
fun deleteToken() { fun deleteToken() {
viewModelScope.launch { viewModelScope.launch {
keystoreManager.deleteToken() keystoreManager.deleteToken()
@ -46,4 +54,8 @@ class MainViewModel @Inject constructor(
} }
} }
class SnackbarContent(
val text: String,
val type: SnackbarType = SnackbarType.STANDARD
)
} }

@ -4,6 +4,7 @@ import androidx.compose.foundation.layout.PaddingValues
import androidx.compose.foundation.layout.offset import androidx.compose.foundation.layout.offset
import androidx.compose.material3.DrawerState import androidx.compose.material3.DrawerState
import androidx.compose.material3.Scaffold import androidx.compose.material3.Scaffold
import androidx.compose.material3.SnackbarHostState
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.derivedStateOf import androidx.compose.runtime.derivedStateOf
@ -14,6 +15,7 @@ import androidx.compose.runtime.setValue
import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier
import androidx.compose.ui.platform.LocalDensity import androidx.compose.ui.platform.LocalDensity
import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.dp
import fr.iut.alldev.allin.ui.core.snackbar.AllInSnackbar
import fr.iut.alldev.allin.ui.core.topbar.AllInTopBar import fr.iut.alldev.allin.ui.core.topbar.AllInTopBar
import kotlin.math.abs import kotlin.math.abs
@ -22,6 +24,7 @@ fun AllInScaffold(
onMenuClicked: () -> Unit, onMenuClicked: () -> Unit,
coinAmount: Int, coinAmount: Int,
drawerState: DrawerState, drawerState: DrawerState,
snackbarHostState: SnackbarHostState,
content: @Composable (PaddingValues) -> Unit, content: @Composable (PaddingValues) -> Unit,
) { ) {
@ -42,6 +45,9 @@ fun AllInScaffold(
Scaffold( Scaffold(
modifier = Modifier.offset(x = contentOffset), modifier = Modifier.offset(x = contentOffset),
snackbarHost = {
AllInSnackbar(snackbarState = snackbarHostState)
},
topBar = { topBar = {
AllInTopBar( AllInTopBar(
onMenuClicked = onMenuClicked, onMenuClicked = onMenuClicked,

@ -11,6 +11,7 @@ import androidx.compose.foundation.background
import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier
import androidx.compose.ui.res.stringResource
import androidx.hilt.navigation.compose.hiltViewModel import androidx.hilt.navigation.compose.hiltViewModel
import androidx.navigation.NavGraphBuilder import androidx.navigation.NavGraphBuilder
import androidx.navigation.NavHostController import androidx.navigation.NavHostController
@ -19,11 +20,13 @@ import androidx.navigation.compose.NavHost
import androidx.navigation.compose.composable import androidx.navigation.compose.composable
import androidx.navigation.compose.rememberNavController import androidx.navigation.compose.rememberNavController
import androidx.navigation.navArgument import androidx.navigation.navArgument
import fr.iut.alldev.allin.R
import fr.iut.alldev.allin.data.model.bet.Bet import fr.iut.alldev.allin.data.model.bet.Bet
import fr.iut.alldev.allin.theme.AllInTheme import fr.iut.alldev.allin.theme.AllInTheme
import fr.iut.alldev.allin.ui.bet.BetScreen import fr.iut.alldev.allin.ui.bet.BetScreen
import fr.iut.alldev.allin.ui.betCreation.BetCreationScreen import fr.iut.alldev.allin.ui.betCreation.BetCreationScreen
import fr.iut.alldev.allin.ui.betHistory.BetHistoryScreen import fr.iut.alldev.allin.ui.betHistory.BetHistoryScreen
import fr.iut.alldev.allin.ui.core.snackbar.SnackbarType
import fr.iut.alldev.allin.ui.login.LoginScreen import fr.iut.alldev.allin.ui.login.LoginScreen
import fr.iut.alldev.allin.ui.main.MainScreen import fr.iut.alldev.allin.ui.main.MainScreen
import fr.iut.alldev.allin.ui.main.MainViewModel import fr.iut.alldev.allin.ui.main.MainViewModel
@ -93,9 +96,10 @@ fun AllInNavHost(
internal fun AllInDrawerNavHost( internal fun AllInDrawerNavHost(
modifier: Modifier = Modifier, modifier: Modifier = Modifier,
navController: NavHostController, navController: NavHostController,
mainViewModel: MainViewModel,
selectBet: (Bet, Boolean) -> Unit, selectBet: (Bet, Boolean) -> Unit,
startDestination: String = Routes.PUBLIC_BETS, startDestination: String = Routes.PUBLIC_BETS,
setLoading: (Boolean) -> Unit,
putSnackbarContent: (MainViewModel.SnackbarContent) -> Unit
) { ) {
NavHost( NavHost(
navController = navController, navController = navController,
@ -110,8 +114,18 @@ internal fun AllInDrawerNavHost(
) )
} }
composable(route = Routes.BET_CREATION) { composable(route = Routes.BET_CREATION) {
val creationSuccessMessage = stringResource(id = R.string.bet_creation_success_message)
BetCreationScreen( BetCreationScreen(
mainViewModel = mainViewModel setLoading = setLoading,
onCreation = {
putSnackbarContent(
MainViewModel.SnackbarContent(
text = creationSuccessMessage,
type = SnackbarType.SUCCESS
)
)
navController.popUpTo(Routes.PUBLIC_BETS, Routes.BET_CREATION)
}
) )
} }

@ -82,6 +82,7 @@
<string name="yes_no_bottom_text_2">Aucune autre réponse ne sera acceptée.</string> <string name="yes_no_bottom_text_2">Aucune autre réponse ne sera acceptée.</string>
<string name="custom_answers">Réponses personnalisées</string> <string name="custom_answers">Réponses personnalisées</string>
<string name="bet_creation_error">Erreur lors de la création du bet, veuillez rééssayer.</string> <string name="bet_creation_error">Erreur lors de la création du bet, veuillez rééssayer.</string>
<string name="bet_creation_success_message">Bet créé !</string>
<!--Bet Page--> <!--Bet Page-->
<string name="Popular">Populaire</string> <string name="Popular">Populaire</string>

@ -85,6 +85,7 @@
<string name="sport_match">Sport match</string> <string name="sport_match">Sport match</string>
<string name="custom_answers">Custom answers</string> <string name="custom_answers">Custom answers</string>
<string name="bet_creation_error">Error while creating the bet. Please try again.</string> <string name="bet_creation_error">Error while creating the bet. Please try again.</string>
<string name="bet_creation_success_message">Bet created !</string>
<!--Bet Page--> <!--Bet Page-->
<string name="Popular">Popular</string> <string name="Popular">Popular</string>

@ -1,8 +1,8 @@
package fr.iut.alldev.allin.data.api package fr.iut.alldev.allin.data.api
import fr.iut.alldev.allin.data.api.model.CheckUser import fr.iut.alldev.allin.data.api.model.CheckUser
import fr.iut.alldev.allin.data.api.model.RequestBet
import fr.iut.alldev.allin.data.api.model.RequestUser import fr.iut.alldev.allin.data.api.model.RequestUser
import fr.iut.alldev.allin.data.api.model.ResponseBet
import fr.iut.alldev.allin.data.api.model.ResponseUser import fr.iut.alldev.allin.data.api.model.ResponseUser
import retrofit2.http.Body import retrofit2.http.Body
import retrofit2.http.GET import retrofit2.http.GET
@ -20,5 +20,5 @@ interface AllInApi {
suspend fun login(@Header("Authorization") token: String): ResponseUser suspend fun login(@Header("Authorization") token: String): ResponseUser
@POST("bets/add") @POST("bets/add")
suspend fun createBet(@Body body: ResponseBet) suspend fun createBet(@Body body: RequestBet)
} }

@ -5,7 +5,7 @@ import fr.iut.alldev.allin.data.model.bet.Bet
import fr.iut.alldev.allin.data.model.bet.BetStatus 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.CustomBet
import fr.iut.alldev.allin.data.model.bet.YesNoBet import fr.iut.alldev.allin.data.model.bet.YesNoBet
import fr.iut.alldev.allin.data.serialization.ZonedDateTimeSerializer import fr.iut.alldev.allin.data.serialization.SimpleDateSerializer
import kotlinx.serialization.Serializable import kotlinx.serialization.Serializable
import java.time.ZonedDateTime import java.time.ZonedDateTime
@ -15,11 +15,11 @@ data class ResponseBet(
val id: Int?, val id: Int?,
val theme: String, val theme: String,
val sentenceBet: String, val sentenceBet: String,
@Serializable(ZonedDateTimeSerializer::class) val endRegistration: ZonedDateTime, @Serializable(SimpleDateSerializer::class) val endRegistration: ZonedDateTime,
@Serializable(ZonedDateTimeSerializer::class) var endBet: ZonedDateTime, @Serializable(SimpleDateSerializer::class) var endBet: ZonedDateTime,
var isPrivate: Boolean, var isPrivate: Boolean,
var response: List<String>, var response: List<String>,
val createdBy: String, val createdBy: String
) { ) {
fun toBet(): Bet { fun toBet(): Bet {
if (response.toSet() == setOf("Yes", "No")) { if (response.toSet() == setOf("Yes", "No")) {
@ -46,3 +46,15 @@ data class ResponseBet(
} }
} }
} }
@Keep
@Serializable
data class RequestBet(
val theme: String,
val sentenceBet: String,
@Serializable(SimpleDateSerializer::class) val endRegistration: ZonedDateTime,
@Serializable(SimpleDateSerializer::class) var endBet: ZonedDateTime,
var isPrivate: Boolean,
var response: List<String>,
val createdBy: String
)

@ -1,6 +1,6 @@
package fr.iut.alldev.allin.data.model.bet package fr.iut.alldev.allin.data.model.bet
import fr.iut.alldev.allin.data.api.model.ResponseBet import fr.iut.alldev.allin.data.api.model.RequestBet
import java.time.ZonedDateTime import java.time.ZonedDateTime
abstract class Bet( abstract class Bet(
@ -14,9 +14,8 @@ abstract class Bet(
open val betStatus: BetStatus, open val betStatus: BetStatus,
) { ) {
abstract fun getResponses(): List<String> abstract fun getResponses(): List<String>
fun toResponseBet(): ResponseBet { fun toRequestBet(): RequestBet {
return ResponseBet( return RequestBet(
id = id,
theme = theme, theme = theme,
sentenceBet = phrase, sentenceBet = phrase,
endRegistration = endRegisterDate, endRegistration = endRegisterDate,

@ -4,7 +4,7 @@ import fr.iut.alldev.allin.data.model.bet.Bet
import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.Flow
abstract class BetRepository { abstract class BetRepository {
abstract suspend fun createBet(bet: Bet) abstract suspend fun createBet(bet: Bet, token: String)
abstract suspend fun getHistory(): Flow<List<Bet>> abstract suspend fun getHistory(): Flow<List<Bet>>
abstract suspend fun getCurrentBets(): Flow<List<Bet>> abstract suspend fun getCurrentBets(): Flow<List<Bet>>
} }

@ -14,8 +14,10 @@ import javax.inject.Inject
class BetRepositoryImpl @Inject constructor( class BetRepositoryImpl @Inject constructor(
private val api: AllInApi private val api: AllInApi
) : BetRepository() { ) : BetRepository() {
override suspend fun createBet(bet: Bet) { override suspend fun createBet(bet: Bet, token: String) {
api.createBet(bet.toResponseBet()) api.createBet(
bet.toRequestBet().copy(createdBy = token)
)
} }
override suspend fun getHistory(): Flow<List<Bet>> { override suspend fun getHistory(): Flow<List<Bet>> {

@ -7,8 +7,10 @@ import kotlinx.serialization.descriptors.SerialDescriptor
import kotlinx.serialization.encoding.Decoder import kotlinx.serialization.encoding.Decoder
import kotlinx.serialization.encoding.Encoder import kotlinx.serialization.encoding.Encoder
import java.time.Instant import java.time.Instant
import java.time.LocalDate
import java.time.ZoneId import java.time.ZoneId
import java.time.ZonedDateTime import java.time.ZonedDateTime
import java.time.format.DateTimeFormatter
object ZonedDateTimeSerializer : KSerializer<ZonedDateTime> { object ZonedDateTimeSerializer : KSerializer<ZonedDateTime> {
override val descriptor: SerialDescriptor = override val descriptor: SerialDescriptor =
@ -23,3 +25,20 @@ object ZonedDateTimeSerializer : KSerializer<ZonedDateTime> {
return ZonedDateTime.ofInstant(Instant.ofEpochSecond(epoch), ZoneId.systemDefault()) return ZonedDateTime.ofInstant(Instant.ofEpochSecond(epoch), ZoneId.systemDefault())
} }
} }
class SimpleDateSerializer : KSerializer<ZonedDateTime> {
override val descriptor: SerialDescriptor =
PrimitiveSerialDescriptor("Date", PrimitiveKind.STRING)
private val formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")
override fun deserialize(decoder: Decoder): ZonedDateTime {
val date = LocalDate.parse(decoder.decodeString(), formatter)
return date.atStartOfDay(ZoneId.systemDefault())
}
override fun serialize(encoder: Encoder, value: ZonedDateTime) {
val dateString = formatter.format(value)
encoder.encodeString(dateString)
}
}

Loading…
Cancel
Save