diff --git a/src/app/src/main/java/fr/iut/alldev/allin/ui/betCreation/BetCreationScreen.kt b/src/app/src/main/java/fr/iut/alldev/allin/ui/betCreation/BetCreationScreen.kt index 6fea697..2fd0126 100644 --- a/src/app/src/main/java/fr/iut/alldev/allin/ui/betCreation/BetCreationScreen.kt +++ b/src/app/src/main/java/fr/iut/alldev/allin/ui/betCreation/BetCreationScreen.kt @@ -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.SectionElement import fr.iut.alldev.allin.ui.core.SelectionElement -import fr.iut.alldev.allin.ui.main.MainViewModel import java.time.Instant import java.time.ZoneId import java.time.ZonedDateTime @@ -39,7 +38,8 @@ import java.time.ZonedDateTime @Composable fun BetCreationScreen( viewModel: BetCreationViewModel = hiltViewModel(), - mainViewModel: MainViewModel, + setLoading: (Boolean) -> Unit, + onCreation: () -> Unit ) { val interactionSource = remember { MutableInteractionSource() } val betTypes = remember { BetType.values().toList() } @@ -154,8 +154,11 @@ fun BetCreationScreen( phraseFieldName = phraseFieldName, registerDateFieldName = registerDateFieldName, betDateFieldName = betDateFieldName, - setLoading = { mainViewModel.loading.value = it }, - onError = { hasError = true } + setLoading = setLoading, + onError = { hasError = true }, + onSuccess = { + onCreation() + } ) } ) diff --git a/src/app/src/main/java/fr/iut/alldev/allin/ui/betCreation/BetCreationViewModel.kt b/src/app/src/main/java/fr/iut/alldev/allin/ui/betCreation/BetCreationViewModel.kt index 67fbdbf..620bd63 100644 --- a/src/app/src/main/java/fr/iut/alldev/allin/ui/betCreation/BetCreationViewModel.kt +++ b/src/app/src/main/java/fr/iut/alldev/allin/ui/betCreation/BetCreationViewModel.kt @@ -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.di.AllInCurrentUser import fr.iut.alldev.allin.ext.FieldErrorState +import fr.iut.alldev.allin.keystore.AllInKeystoreManager import kotlinx.coroutines.launch import timber.log.Timber import java.time.ZonedDateTime @@ -23,6 +24,7 @@ const val PHRASE_MIN_SIZE = 5 class BetCreationViewModel @Inject constructor( @AllInCurrentUser val currentUser: User, private val betRepository: BetRepository, + private val keystoreManager: AllInKeystoreManager ) : ViewModel() { var hasError = mutableStateOf(false) @@ -92,6 +94,7 @@ class BetCreationViewModel @Inject constructor( betDateFieldName: String, onError: () -> Unit, setLoading: (Boolean) -> Unit, + onSuccess: () -> Unit ) { viewModelScope.launch { setLoading(true) @@ -118,7 +121,8 @@ class BetCreationViewModel @Inject constructor( possibleAnswers = setOf(), creator = currentUser.username ) - betRepository.createBet(bet) + betRepository.createBet(bet, keystoreManager.getToken() ?: "") + onSuccess() } catch (e: AllInAPIException) { Timber.e(e) onError() diff --git a/src/app/src/main/java/fr/iut/alldev/allin/ui/core/snackbar/AllInSnackBar.kt b/src/app/src/main/java/fr/iut/alldev/allin/ui/core/snackbar/AllInSnackBar.kt new file mode 100644 index 0000000..d9b2129 --- /dev/null +++ b/src/app/src/main/java/fr/iut/alldev/allin/ui/core/snackbar/AllInSnackBar.kt @@ -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 { + 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 = {} + ) + } +} + diff --git a/src/app/src/main/java/fr/iut/alldev/allin/ui/core/snackbar/AllInSnackbarVisualsImpl.kt b/src/app/src/main/java/fr/iut/alldev/allin/ui/core/snackbar/AllInSnackbarVisualsImpl.kt new file mode 100644 index 0000000..359a654 --- /dev/null +++ b/src/app/src/main/java/fr/iut/alldev/allin/ui/core/snackbar/AllInSnackbarVisualsImpl.kt @@ -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 + } +} diff --git a/src/app/src/main/java/fr/iut/alldev/allin/ui/core/snackbar/SnackBarType.kt b/src/app/src/main/java/fr/iut/alldev/allin/ui/core/snackbar/SnackBarType.kt new file mode 100644 index 0000000..cbf3871 --- /dev/null +++ b/src/app/src/main/java/fr/iut/alldev/allin/ui/core/snackbar/SnackBarType.kt @@ -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 + } \ No newline at end of file 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 6abdb46..c55cad1 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 @@ -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.visitor.BetStatusBottomSheetDisplayBetVisitor 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.navigation.AllInDrawerNavHost import fr.iut.alldev.allin.ui.navigation.Routes @@ -62,15 +63,12 @@ fun MainScreen( mainViewModel: MainViewModel = hiltViewModel(), navigateToWelcomeScreen: () -> Unit ) { - val loading by remember { mainViewModel.loading } + val scope = rememberCoroutineScope() - val currentUser = remember { - mainViewModel.currentUserState - } - - val (selectedBet, setSelectedBet) = remember { - mainViewModel.selectedBet - } + var loading by remember { mainViewModel.loading } + val currentUser = remember { mainViewModel.currentUserState } + val (selectedBet, setSelectedBet) = remember { mainViewModel.selectedBet } + val (statusVisibility, sheetBackVisibility, setStatusVisibility) = rememberBetStatusVisibilities() val betStatusDisplayVisitor = remember { BetStatusBottomSheetDisplayBetVisitor( @@ -81,10 +79,26 @@ fun MainScreen( ) } - val scope = rememberCoroutineScope() - - val (statusVisibility, sheetBackVisibility, setStatusVisibility) - = rememberBetStatusVisibilities() + val snackbarHostState = remember { SnackbarHostState() } + var snackbarContent by remember { mainViewModel.snackbarContent } + + 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( skipPartiallyExpanded = true, @@ -115,7 +129,8 @@ fun MainScreen( AllInScaffold( onMenuClicked = { scope.launch { drawerState.open() } }, coinAmount = currentUser.userCoins.value, - drawerState = drawerState + drawerState = drawerState, + snackbarHostState = snackbarHostState ) { LaunchedEffect(key1 = it) { betStatusDisplayVisitor.paddingValues.value = it @@ -129,12 +144,13 @@ fun MainScreen( ) { AllInDrawerNavHost( navController = navController, - mainViewModel = mainViewModel, selectBet = { bet, participate -> setSelectedBet(bet) betStatusDisplayVisitor.participateBottomSheetVisibility.value = participate setStatusVisibility(true) - } + }, + setLoading = { loading = it }, + putSnackbarContent = { mainViewModel.putSnackbarContent(it) } ) } } 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 7ee0273..33d7f3e 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 @@ -1,5 +1,6 @@ package fr.iut.alldev.allin.ui.main +import androidx.compose.runtime.MutableState import androidx.compose.runtime.mutableIntStateOf import androidx.compose.runtime.mutableStateOf 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.di.AllInCurrentUser import fr.iut.alldev.allin.keystore.AllInKeystoreManager +import fr.iut.alldev.allin.ui.core.snackbar.SnackbarType import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.launch import kotlinx.coroutines.withContext @@ -29,6 +31,12 @@ class MainViewModel @Inject constructor( val currentUserState = UserState(currentUser) val selectedBet = mutableStateOf(null) + val snackbarContent: MutableState by lazy { mutableStateOf(null) } + fun putSnackbarContent(content: SnackbarContent) { + snackbarContent.value = content + } + + fun deleteToken() { viewModelScope.launch { keystoreManager.deleteToken() @@ -46,4 +54,8 @@ class MainViewModel @Inject constructor( } } + class SnackbarContent( + val text: String, + val type: SnackbarType = SnackbarType.STANDARD + ) } \ No newline at end of file diff --git a/src/app/src/main/java/fr/iut/alldev/allin/ui/main/components/AllInScaffold.kt b/src/app/src/main/java/fr/iut/alldev/allin/ui/main/components/AllInScaffold.kt index 3199beb..589927b 100644 --- a/src/app/src/main/java/fr/iut/alldev/allin/ui/main/components/AllInScaffold.kt +++ b/src/app/src/main/java/fr/iut/alldev/allin/ui/main/components/AllInScaffold.kt @@ -4,6 +4,7 @@ import androidx.compose.foundation.layout.PaddingValues import androidx.compose.foundation.layout.offset import androidx.compose.material3.DrawerState import androidx.compose.material3.Scaffold +import androidx.compose.material3.SnackbarHostState import androidx.compose.runtime.Composable import androidx.compose.runtime.LaunchedEffect import androidx.compose.runtime.derivedStateOf @@ -14,6 +15,7 @@ import androidx.compose.runtime.setValue import androidx.compose.ui.Modifier import androidx.compose.ui.platform.LocalDensity 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 kotlin.math.abs @@ -22,6 +24,7 @@ fun AllInScaffold( onMenuClicked: () -> Unit, coinAmount: Int, drawerState: DrawerState, + snackbarHostState: SnackbarHostState, content: @Composable (PaddingValues) -> Unit, ) { @@ -42,6 +45,9 @@ fun AllInScaffold( Scaffold( modifier = Modifier.offset(x = contentOffset), + snackbarHost = { + AllInSnackbar(snackbarState = snackbarHostState) + }, topBar = { AllInTopBar( onMenuClicked = onMenuClicked, diff --git a/src/app/src/main/java/fr/iut/alldev/allin/ui/navigation/NavHost.kt b/src/app/src/main/java/fr/iut/alldev/allin/ui/navigation/NavHost.kt index d7d0040..e36ba79 100644 --- a/src/app/src/main/java/fr/iut/alldev/allin/ui/navigation/NavHost.kt +++ b/src/app/src/main/java/fr/iut/alldev/allin/ui/navigation/NavHost.kt @@ -11,6 +11,7 @@ import androidx.compose.foundation.background import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.runtime.Composable import androidx.compose.ui.Modifier +import androidx.compose.ui.res.stringResource import androidx.hilt.navigation.compose.hiltViewModel import androidx.navigation.NavGraphBuilder import androidx.navigation.NavHostController @@ -19,11 +20,13 @@ import androidx.navigation.compose.NavHost import androidx.navigation.compose.composable import androidx.navigation.compose.rememberNavController import androidx.navigation.navArgument +import fr.iut.alldev.allin.R import fr.iut.alldev.allin.data.model.bet.Bet import fr.iut.alldev.allin.theme.AllInTheme import fr.iut.alldev.allin.ui.bet.BetScreen import fr.iut.alldev.allin.ui.betCreation.BetCreationScreen import fr.iut.alldev.allin.ui.betHistory.BetHistoryScreen +import fr.iut.alldev.allin.ui.core.snackbar.SnackbarType import fr.iut.alldev.allin.ui.login.LoginScreen import fr.iut.alldev.allin.ui.main.MainScreen import fr.iut.alldev.allin.ui.main.MainViewModel @@ -93,9 +96,10 @@ fun AllInNavHost( internal fun AllInDrawerNavHost( modifier: Modifier = Modifier, navController: NavHostController, - mainViewModel: MainViewModel, selectBet: (Bet, Boolean) -> Unit, startDestination: String = Routes.PUBLIC_BETS, + setLoading: (Boolean) -> Unit, + putSnackbarContent: (MainViewModel.SnackbarContent) -> Unit ) { NavHost( navController = navController, @@ -110,8 +114,18 @@ internal fun AllInDrawerNavHost( ) } composable(route = Routes.BET_CREATION) { + val creationSuccessMessage = stringResource(id = R.string.bet_creation_success_message) BetCreationScreen( - mainViewModel = mainViewModel + setLoading = setLoading, + onCreation = { + putSnackbarContent( + MainViewModel.SnackbarContent( + text = creationSuccessMessage, + type = SnackbarType.SUCCESS + ) + ) + navController.popUpTo(Routes.PUBLIC_BETS, Routes.BET_CREATION) + } ) } diff --git a/src/app/src/main/res/values-fr/strings.xml b/src/app/src/main/res/values-fr/strings.xml index 204d9ef..07009f3 100644 --- a/src/app/src/main/res/values-fr/strings.xml +++ b/src/app/src/main/res/values-fr/strings.xml @@ -82,6 +82,7 @@ Aucune autre réponse ne sera acceptée. Réponses personnalisées Erreur lors de la création du bet, veuillez rééssayer. + Bet créé ! Populaire diff --git a/src/app/src/main/res/values/strings.xml b/src/app/src/main/res/values/strings.xml index e54b9c9..bf11bd0 100644 --- a/src/app/src/main/res/values/strings.xml +++ b/src/app/src/main/res/values/strings.xml @@ -85,6 +85,7 @@ Sport match Custom answers Error while creating the bet. Please try again. + Bet created ! Popular diff --git a/src/data/src/main/java/fr/iut/alldev/allin/data/api/AllInApi.kt b/src/data/src/main/java/fr/iut/alldev/allin/data/api/AllInApi.kt index 98a19d6..8db6d5b 100644 --- a/src/data/src/main/java/fr/iut/alldev/allin/data/api/AllInApi.kt +++ b/src/data/src/main/java/fr/iut/alldev/allin/data/api/AllInApi.kt @@ -1,8 +1,8 @@ package fr.iut.alldev.allin.data.api 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.ResponseBet import fr.iut.alldev.allin.data.api.model.ResponseUser import retrofit2.http.Body import retrofit2.http.GET @@ -20,5 +20,5 @@ interface AllInApi { suspend fun login(@Header("Authorization") token: String): ResponseUser @POST("bets/add") - suspend fun createBet(@Body body: ResponseBet) + suspend fun createBet(@Body body: RequestBet) } \ No newline at end of file diff --git a/src/data/src/main/java/fr/iut/alldev/allin/data/api/model/ResponseBet.kt b/src/data/src/main/java/fr/iut/alldev/allin/data/api/model/ApiBet.kt similarity index 69% rename from src/data/src/main/java/fr/iut/alldev/allin/data/api/model/ResponseBet.kt rename to src/data/src/main/java/fr/iut/alldev/allin/data/api/model/ApiBet.kt index d500386..ba3c2e8 100644 --- a/src/data/src/main/java/fr/iut/alldev/allin/data/api/model/ResponseBet.kt +++ b/src/data/src/main/java/fr/iut/alldev/allin/data/api/model/ApiBet.kt @@ -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.CustomBet 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 java.time.ZonedDateTime @@ -15,11 +15,11 @@ data class ResponseBet( val id: Int?, val theme: String, val sentenceBet: String, - @Serializable(ZonedDateTimeSerializer::class) val endRegistration: ZonedDateTime, - @Serializable(ZonedDateTimeSerializer::class) var endBet: ZonedDateTime, + @Serializable(SimpleDateSerializer::class) val endRegistration: ZonedDateTime, + @Serializable(SimpleDateSerializer::class) var endBet: ZonedDateTime, var isPrivate: Boolean, var response: List, - val createdBy: String, + val createdBy: String ) { fun toBet(): Bet { 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, + val createdBy: String +) diff --git a/src/data/src/main/java/fr/iut/alldev/allin/data/api/model/ResponseUser.kt b/src/data/src/main/java/fr/iut/alldev/allin/data/api/model/ApiUser.kt similarity index 100% rename from src/data/src/main/java/fr/iut/alldev/allin/data/api/model/ResponseUser.kt rename to src/data/src/main/java/fr/iut/alldev/allin/data/api/model/ApiUser.kt diff --git a/src/data/src/main/java/fr/iut/alldev/allin/data/model/bet/Bet.kt b/src/data/src/main/java/fr/iut/alldev/allin/data/model/bet/Bet.kt index 0d92ede..20f10bf 100644 --- a/src/data/src/main/java/fr/iut/alldev/allin/data/model/bet/Bet.kt +++ b/src/data/src/main/java/fr/iut/alldev/allin/data/model/bet/Bet.kt @@ -1,6 +1,6 @@ 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 abstract class Bet( @@ -14,9 +14,8 @@ abstract class Bet( open val betStatus: BetStatus, ) { abstract fun getResponses(): List - fun toResponseBet(): ResponseBet { - return ResponseBet( - id = id, + fun toRequestBet(): RequestBet { + return RequestBet( theme = theme, sentenceBet = phrase, endRegistration = endRegisterDate, diff --git a/src/data/src/main/java/fr/iut/alldev/allin/data/repository/BetRepository.kt b/src/data/src/main/java/fr/iut/alldev/allin/data/repository/BetRepository.kt index e846158..81cf458 100644 --- a/src/data/src/main/java/fr/iut/alldev/allin/data/repository/BetRepository.kt +++ b/src/data/src/main/java/fr/iut/alldev/allin/data/repository/BetRepository.kt @@ -4,7 +4,7 @@ import fr.iut.alldev.allin.data.model.bet.Bet import kotlinx.coroutines.flow.Flow abstract class BetRepository { - abstract suspend fun createBet(bet: Bet) + abstract suspend fun createBet(bet: Bet, token: String) abstract suspend fun getHistory(): Flow> abstract suspend fun getCurrentBets(): Flow> } \ No newline at end of file diff --git a/src/data/src/main/java/fr/iut/alldev/allin/data/repository/impl/BetRepositoryImpl.kt b/src/data/src/main/java/fr/iut/alldev/allin/data/repository/impl/BetRepositoryImpl.kt index 425cfdd..06ef875 100644 --- a/src/data/src/main/java/fr/iut/alldev/allin/data/repository/impl/BetRepositoryImpl.kt +++ b/src/data/src/main/java/fr/iut/alldev/allin/data/repository/impl/BetRepositoryImpl.kt @@ -14,8 +14,10 @@ import javax.inject.Inject class BetRepositoryImpl @Inject constructor( private val api: AllInApi ) : BetRepository() { - override suspend fun createBet(bet: Bet) { - api.createBet(bet.toResponseBet()) + override suspend fun createBet(bet: Bet, token: String) { + api.createBet( + bet.toRequestBet().copy(createdBy = token) + ) } override suspend fun getHistory(): Flow> { diff --git a/src/data/src/main/java/fr/iut/alldev/allin/data/serialization/ZonedDateTimeSerializer.kt b/src/data/src/main/java/fr/iut/alldev/allin/data/serialization/ZonedDateTimeSerializer.kt index 1a20345..5810189 100644 --- a/src/data/src/main/java/fr/iut/alldev/allin/data/serialization/ZonedDateTimeSerializer.kt +++ b/src/data/src/main/java/fr/iut/alldev/allin/data/serialization/ZonedDateTimeSerializer.kt @@ -7,8 +7,10 @@ import kotlinx.serialization.descriptors.SerialDescriptor import kotlinx.serialization.encoding.Decoder import kotlinx.serialization.encoding.Encoder import java.time.Instant +import java.time.LocalDate import java.time.ZoneId import java.time.ZonedDateTime +import java.time.format.DateTimeFormatter object ZonedDateTimeSerializer : KSerializer { override val descriptor: SerialDescriptor = @@ -22,4 +24,21 @@ object ZonedDateTimeSerializer : KSerializer { val epoch = decoder.decodeLong() return ZonedDateTime.ofInstant(Instant.ofEpochSecond(epoch), ZoneId.systemDefault()) } -} \ No newline at end of file +} + +class SimpleDateSerializer : KSerializer { + 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) + } +}