[ADD] Kahoot fonctionne avec partie (push tres atomique)
continuous-integration/drone/push Build is passing Details

Android
Renaud BEURET 1 year ago
parent 0032bedb05
commit 8bc7609765

@ -51,6 +51,7 @@ android {
}
dependencies {
implementation("org.jetbrains.kotlinx:kotlinx-datetime:0.6.0-RC.2")
implementation("org.jetbrains.kotlinx:kotlinx-serialization-json:1.6.2")
implementation("org.jetbrains.kotlinx:kotlinx-coroutines-core:1.7.3")
implementation("org.jetbrains.kotlinx:kotlinx-coroutines-javafx:1.7.3")

@ -3,16 +3,19 @@ package fr.iut.sciencequest
import android.os.Bundle
import androidx.activity.ComponentActivity
import androidx.activity.compose.setContent
import androidx.activity.viewModels
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Surface
import androidx.compose.ui.Modifier
import fr.iut.sciencequest.navigation.NavHost
import fr.iut.sciencequest.ui.theme.ScienceQuestTheme
import fr.iut.sciencequest.viewModels.KahootViewModel
class MainActivity : ComponentActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
val vm = viewModels<KahootViewModel> { KahootViewModel.ApiFactory }
setContent {
ScienceQuestTheme {
// A surface container using the 'background' color from the theme
@ -20,7 +23,7 @@ class MainActivity : ComponentActivity() {
modifier = Modifier.fillMaxSize(),
color = MaterialTheme.colorScheme.background
) {
NavHost()
NavHost(vm.value)
}
}
}

@ -0,0 +1,27 @@
package fr.iut.sciencequest.model.buisness
import fr.iut.sciencequest.model.dto.partie.LaunchedPartieDTO
import fr.iut.sciencequest.model.dto.partie.PartieKahootDTO
import fr.iut.sciencequest.model.dto.partie.NouvellePartieDTO
import fr.iut.sciencequest.model.dto.question.QuestionPartieDTO
import fr.iut.sciencequest.model.dto.reponse.ReponseInfoDTO
import fr.iut.sciencequest.model.dto.reponseValidityDTO
import retrofit2.http.Body
import retrofit2.http.GET
import retrofit2.http.POST
import retrofit2.http.Path
import retrofit2.http.Query
interface KahootPartieRequestService {
@POST("partie/kahoot")
suspend fun createPartie(@Body gameInfo: NouvellePartieDTO): PartieKahootDTO
@POST("partie/kahoot/{codePartie}/demarrer")
suspend fun launchPartie(@Path("codePartie") code: String): LaunchedPartieDTO
@GET("partie/kahoot/{codePartie}/question")
suspend fun getQuestionFromPartie(@Path("codePartie") code: String): QuestionPartieDTO
@POST("partie/kahoot/{codeInvitation}/reponse")
suspend fun postResponse(@Path("codeInvitation") code:String, @Body reponseInfo: ReponseInfoDTO): reponseValidityDTO
}

@ -13,6 +13,6 @@ val httpClient = OkHttpClient()
fun createRequestService(): Retrofit =
Retrofit.Builder()
.baseUrl(API_BASE_URL)
.addConverterFactory(Json { ignoreUnknownKeys = true }.asConverterFactory(MediaType.get("application/json")))
.addConverterFactory(Json { ignoreUnknownKeys = true; coerceInputValues = true }.asConverterFactory(MediaType.get("application/json")))
.client(httpClient)
.build()

@ -0,0 +1,12 @@
package fr.iut.sciencequest.model.dto.extensions
import fr.iut.sciencequest.model.dto.JeuDTO
import fr.iut.sciencequest.model.metier.Jeu
fun JeuDTO.toModel(): Jeu {
return Jeu(
this.id,
this.nom,
this.nbrParties
)
}

@ -0,0 +1,19 @@
package fr.iut.sciencequest.model.dto.extensions
import fr.iut.sciencequest.model.dto.joueur.JoueurSimpleDTO
import fr.iut.sciencequest.model.metier.joueur.JoueurSimple
fun JoueurSimpleDTO.toModel(): JoueurSimple {
return JoueurSimple(
this.id,
this.pseudo
)
}
fun List<JoueurSimpleDTO>.toModel(): List<JoueurSimple> {
val liste = mutableListOf<JoueurSimple>()
for (joueur in this) {
liste.add(joueur.toModel())
}
return liste
}

@ -0,0 +1,14 @@
package fr.iut.sciencequest.model.dto.extensions
import fr.iut.sciencequest.model.dto.partie.PartieDTO
import fr.iut.sciencequest.model.metier.partie.Partie
fun PartieDTO.toModel(): Partie {
return Partie(
this.id,
this.codeInvitation,
this.joueurs.toModel(),
this.jeu.toModel(),
this.thematiques.toModel()
)
}

@ -0,0 +1,14 @@
package fr.iut.sciencequest.model.dto.extensions
import fr.iut.sciencequest.model.dto.partie.PartieKahootDTO
import fr.iut.sciencequest.model.metier.partie.PartieKahoot
fun PartieKahootDTO.toModel(): PartieKahoot {
return PartieKahoot(
this.id,
this.code,
this.thematiques.toModel(),
this.joueurs.toModel(),
this.difficulte.ToModel()
)
}

@ -0,0 +1,21 @@
package fr.iut.sciencequest.model.dto.extensions
import fr.iut.sciencequest.model.dto.question.QuestionPartieDTO
import fr.iut.sciencequest.model.metier.question.QuestionPartie
import kotlinx.datetime.Instant
import kotlinx.datetime.LocalDateTime
import kotlinx.datetime.TimeZone
import kotlinx.datetime.toInstant
fun QuestionPartieDTO.toModel(): QuestionPartie {
var limite: Instant
if (this.limitTime == null) {
limite = LocalDateTime(1,1,1,1,1,1,1).toInstant(TimeZone.UTC)
} else {
limite = Instant.parse(this.limitTime)
}
return QuestionPartie(
this.question?.ToModel(),
limite
)
}

@ -10,3 +10,11 @@ fun ThematiqueDTO.ToModel(): Thematique {
)
return model
}
fun List<ThematiqueDTO>.toModel(): List<Thematique> {
val liste = mutableListOf<Thematique>()
for (thematique in this) {
liste.add(thematique.ToModel())
}
return liste
}

@ -1,6 +1,6 @@
package fr.iut.sciencequest.model.dto.joueur
import fr.iut.sciencequest.model.dto.PartieDTO
import fr.iut.sciencequest.model.dto.partie.PartieDTO
class JoueurDTO (
val id: Int,

@ -0,0 +1,10 @@
package fr.iut.sciencequest.model.dto.joueur
import fr.iut.sciencequest.model.metier.joueur.JoueurSimple
import kotlinx.serialization.Serializable
@Serializable
class JoueurScoreDTO (
val joueur: JoueurSimpleDTO,
val score: Int
)

@ -1,5 +1,8 @@
package fr.iut.sciencequest.model.dto.joueur
import kotlinx.serialization.Serializable
@Serializable
class JoueurSimpleDTO (
val id: Int,
val pseudo: String,

@ -0,0 +1,10 @@
package fr.iut.sciencequest.model.dto.partie
import fr.iut.sciencequest.model.dto.joueur.JoueurScoreDTO
import kotlinx.serialization.Serializable
@Serializable
class LaunchedPartieDTO (
val status: String,
val scores: List<JoueurScoreDTO>
)

@ -0,0 +1,10 @@
package fr.iut.sciencequest.model.dto.partie
import kotlinx.serialization.Serializable
@Serializable
data class NouvellePartieDTO (
val idJoueur: Int,
val thematiques: List<Int>,
val idDifficulte: Int
)

@ -1,5 +1,7 @@
package fr.iut.sciencequest.model.dto
package fr.iut.sciencequest.model.dto.partie
import fr.iut.sciencequest.model.dto.JeuDTO
import fr.iut.sciencequest.model.dto.ThematiqueDTO
import fr.iut.sciencequest.model.dto.joueur.JoueurSimpleDTO
class PartieDTO (

@ -0,0 +1,17 @@
package fr.iut.sciencequest.model.dto.partie
import fr.iut.sciencequest.model.dto.ThematiqueDTO
import fr.iut.sciencequest.model.dto.difficulte.DifficulteDTO
import fr.iut.sciencequest.model.dto.joueur.JoueurSimpleDTO
import kotlinx.serialization.SerialName
import kotlinx.serialization.Serializable
@Serializable
class PartieKahootDTO (
val id: Int,
@SerialName("codeInvitation")
val code: String,
val thematiques: List<ThematiqueDTO>,
val joueurs: List<JoueurSimpleDTO>,
val difficulte: DifficulteDTO
)

@ -0,0 +1,12 @@
package fr.iut.sciencequest.model.dto.question
import kotlinx.serialization.SerialName
import kotlinx.serialization.Serializable
@Serializable
class QuestionPartieDTO (
@SerialName("questionActuel")
val question: QuestionWithSimpleResponseDTO?,
@SerialName("tempsLimiteReponse")
val limitTime: String?
)

@ -0,0 +1,9 @@
package fr.iut.sciencequest.model.dto.reponse
import kotlinx.serialization.Serializable
@Serializable
data class ReponseInfoDTO (
val idJoueur: Int,
val idReponse: Int
)

@ -0,0 +1,10 @@
package fr.iut.sciencequest.model.dto
import kotlinx.serialization.SerialName
import kotlinx.serialization.Serializable
@Serializable
class reponseValidityDTO (
@SerialName("estValide")
val isValid: Boolean
)

@ -0,0 +1,7 @@
package fr.iut.sciencequest.model.metier
class Jeu (
val id: Int,
val nom: String,
val nbrParties: UInt
)

@ -0,0 +1,6 @@
package fr.iut.sciencequest.model.metier.joueur
class JoueurSimple (
val id: Int,
val pseudo: String
)

@ -0,0 +1,13 @@
package fr.iut.sciencequest.model.metier.partie
import fr.iut.sciencequest.model.metier.Jeu
import fr.iut.sciencequest.model.metier.Thematique
import fr.iut.sciencequest.model.metier.joueur.JoueurSimple
class Partie (
val id: Int,
val codeInvitation: String,
val joueurs: List<JoueurSimple>,
val jeu: Jeu,
val thematiques: List<Thematique>
)

@ -0,0 +1,13 @@
package fr.iut.sciencequest.model.metier.partie
import fr.iut.sciencequest.model.metier.Difficulte
import fr.iut.sciencequest.model.metier.Thematique
import fr.iut.sciencequest.model.metier.joueur.JoueurSimple
class PartieKahoot (
val id: Int,
val code: String,
val thematiques: List<Thematique>,
val joueurs: List<JoueurSimple>,
val difficulte: Difficulte
)

@ -0,0 +1,9 @@
package fr.iut.sciencequest.model.metier.question
import kotlinx.datetime.Instant
import kotlinx.datetime.LocalDateTime
class QuestionPartie (
val question: QuestionWithSimpleReponse?,
val date: Instant
)

@ -0,0 +1,18 @@
package fr.iut.sciencequest.model.repositories.kahootPartie
import fr.iut.sciencequest.model.dto.partie.NouvellePartieDTO
import fr.iut.sciencequest.model.metier.partie.PartieKahoot
import fr.iut.sciencequest.model.metier.question.QuestionPartie
import kotlinx.coroutines.flow.StateFlow
interface IKahootPartieRepository {
val partie: StateFlow<PartieKahoot>
val question: StateFlow<QuestionPartie>
val isStarted: StateFlow<Boolean>
val isReponseValid: StateFlow<Boolean>
suspend fun createPartie(nvPartie: NouvellePartieDTO)
suspend fun startPartie(code: String)
suspend fun getQuestion(code: String)
suspend fun postReponse(code: String, idJoueur: Int, idReponse: Int)
}

@ -0,0 +1,81 @@
package fr.iut.sciencequest.model.repositories.kahootPartie
import android.util.Log
import fr.iut.sciencequest.model.buisness.KahootPartieRequestService
import fr.iut.sciencequest.model.buisness.createRequestService
import fr.iut.sciencequest.model.dto.extensions.toModel
import fr.iut.sciencequest.model.dto.partie.NouvellePartieDTO
import fr.iut.sciencequest.model.dto.reponse.ReponseInfoDTO
import fr.iut.sciencequest.model.metier.Difficulte
import fr.iut.sciencequest.model.metier.partie.PartieKahoot
import fr.iut.sciencequest.model.metier.question.QuestionPartie
import fr.iut.sciencequest.model.metier.question.QuestionWithSimpleReponse
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.asStateFlow
import kotlinx.datetime.LocalDateTime
import kotlinx.datetime.TimeZone
import kotlinx.datetime.toInstant
import retrofit2.create
class KahootAPIRepository: IKahootPartieRepository {
private val _partie = MutableStateFlow(PartieKahoot(0,"", emptyList(),emptyList(), Difficulte(0,"")))
override val partie: StateFlow<PartieKahoot>
get() = _partie.asStateFlow()
private val _question = MutableStateFlow(QuestionPartie(QuestionWithSimpleReponse(0,"", emptyList()), LocalDateTime(1,1,1,1,1 ).toInstant(
TimeZone.UTC)))
override val question: StateFlow<QuestionPartie>
get() = _question.asStateFlow()
private val _isStarted = MutableStateFlow(false)
override val isStarted: StateFlow<Boolean>
get() = _isStarted.asStateFlow()
private val _isValidResponse = MutableStateFlow(false)
override val isReponseValid: StateFlow<Boolean>
get() = _isValidResponse.asStateFlow()
override suspend fun createPartie(nvPartie: NouvellePartieDTO) {
val serviceClient = createRequestService().create<KahootPartieRequestService>()
try {
_partie.value = serviceClient.createPartie(nvPartie).toModel()
Log.d("Req partie crea",_partie.value.code)
} catch (e: Exception) {
Log.e("Requete API Partie crea", e.message.toString())
}
}
override suspend fun startPartie(code: String) {
val serviceClient = createRequestService().create<KahootPartieRequestService>()
try {
val partieStatus = serviceClient.launchPartie(code)
_isStarted.value = partieStatus.status == "Started"
} catch (e: Exception) {
Log.e("Requete API Partie star", e.message.toString())
}
}
override suspend fun getQuestion(code: String) {
val serviceClient = createRequestService().create<KahootPartieRequestService>()
try {
_question.value = serviceClient.getQuestionFromPartie(code).toModel()
} catch (e: Exception) {
Log.e("Requete API Partie getq", e.message.toString())
_question.value = QuestionPartie(
question = _question.value.question,
date = LocalDateTime(1,1,1,1,1,1,1).toInstant(TimeZone.UTC)
)
}
}
override suspend fun postReponse(code: String, idJoueur: Int, idReponse: Int) {
val serviceClient = createRequestService().create<KahootPartieRequestService>()
try {
_isValidResponse.value = serviceClient.postResponse(code, ReponseInfoDTO(idJoueur, idReponse)).isValid
} catch (e: Exception) {
Log.e("Requete API Partie post", e.message.toString())
}
}
}

@ -2,21 +2,27 @@ package fr.iut.sciencequest.navigation
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.runtime.Composable
import androidx.compose.runtime.collectAsState
import androidx.compose.ui.Modifier
import androidx.lifecycle.viewmodel.compose.viewModel
import androidx.navigation.compose.NavHost
import androidx.navigation.compose.composable
import androidx.navigation.compose.rememberNavController
import fr.iut.sciencequest.view.AccountScreen
import fr.iut.sciencequest.view.HomeScreen
import fr.iut.sciencequest.view.LoginScreen
import fr.iut.sciencequest.view.games.KahootScreen
import fr.iut.sciencequest.view.games.kahoot.KahootScreen
import fr.iut.sciencequest.view.games.PenduScreen
import fr.iut.sciencequest.view.games.QuiScreen
import fr.iut.sciencequest.view.games.kahoot.MenuKahoot
import fr.iut.sciencequest.view.games.kahoot.ResultatKahoot
import fr.iut.sciencequest.view.games.kahoot.WaitingScreen
import fr.iut.sciencequest.view.scientifiques.scientifiqueListeScreen
import fr.iut.sciencequest.viewModels.KahootViewModel
@Composable
fun NavHost() {
fun NavHost(kahootVM: KahootViewModel) {
val navController = rememberNavController()
NavHost(
modifier = Modifier.fillMaxSize(),
@ -35,7 +41,7 @@ fun NavHost() {
navController.navigate("pendu")
},
goToKahoot = {
navController.navigate("kahoot")
navController.navigate("kahootMenu")
},
goToQui = {
navController.navigate("qui")
@ -76,14 +82,61 @@ fun NavHost() {
)
}
composable(route= "kahoot"){
composable(route= "kahootGame"){
KahootScreen(
goToAccount = {
navController.navigate("account")
},
goToHome = {
navController.navigate("home")
},
goToResult = {
navController.navigate("kahootResult")
},
viewModel = kahootVM
)
}
composable(route = "kahootResult") {
ResultatKahoot(
goToAccount = {
navController.navigate("account")
},
goToHome = {
navController.navigate("home")
},
kahootViewModel = kahootVM
)
}
composable(route= "kahootMenu"){
MenuKahoot(
goToAccount = {
navController.navigate("account")
},
goToHome = {
navController.navigate("home")
},
goToWaiting = {
kahootVM.createPartie(1,1, listOf(1))
navController.navigate("kahootWaiting")
},
kahootViewModel = kahootVM
)
}
composable(route = "kahootWaiting") {
WaitingScreen(
goToAccount = {
navController.navigate("account")
},
goToHome = {
navController.navigate("home")
},
goToGame = {
navController.navigate("kahootGame")
},
kahootViewModel = kahootVM
)
}

@ -37,8 +37,8 @@ fun QuiScreen(viewModel: KahootViewModel = viewModel(),
val state = viewModel.uiState.collectAsState()
Column(modifier = Modifier.fillMaxWidth()) {
TopBar(goToAccount, goToHome, stringResource(id = R.string.kahoot))
QuiPlayer(state.value.question) {
viewModel.ajouterPoints(it)
QuiPlayer(state.value.questionPartie.question) {
// viewModel.ajouterPoints(it)
}
}
}
@ -59,16 +59,16 @@ fun QuiPlayerPreview(){
@Composable
fun QuiPlayer(question: QuestionWithSimpleReponse,
fun QuiPlayer(question: QuestionWithSimpleReponse?,
sendReponse: (Long) -> Unit){
val context = LocalContext.current;
val currTime = System.currentTimeMillis()
Column (horizontalAlignment = Alignment.CenterHorizontally){
QuiQuestion(question = question.question)
QuiReponses(reponses = question.reponses) {
sendReponse(currTime - System.currentTimeMillis())
Toast.makeText(context, it.reponse, Toast.LENGTH_SHORT).show()
}
//QuiQuestion(question = question.question)
//QuiReponses(reponses = question.reponses) {
// sendReponse(currTime - System.currentTimeMillis())
// Toast.makeText(context, it.reponse, Toast.LENGTH_SHORT).show()
//}
}
}

@ -1,5 +1,6 @@
package fr.iut.sciencequest.view.games
package fr.iut.sciencequest.view.games.kahoot
import android.util.Log
import android.widget.Toast
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Column
@ -31,40 +32,44 @@ import fr.iut.sciencequest.stub.StubQuestionWithReponses
import fr.iut.sciencequest.view.TopBar
@Composable
fun KahootScreen(viewModel: KahootViewModel = viewModel(factory = KahootViewModel.ApiFactory),
fun KahootScreen(viewModel: KahootViewModel,
goToAccount: () -> Unit,
goToHome: () -> Unit) {
val state = viewModel.uiState.collectAsState()
goToHome: () -> Unit,
goToResult: () -> Unit) {
LaunchedEffect(key1 = Unit) {
Log.d("Kahoot","Je lance la partie")
viewModel.lancerPartie()
Log.d("Kahoot","Je vais chercher la question")
viewModel.updateQuestion(goToResult)
}
val state = viewModel.uiState.collectAsState()
Column(modifier = Modifier.fillMaxWidth()) {
TopBar(goToAccount, goToHome, stringResource(id = R.string.kahoot))
KahootPlayer(state.value.question) {
viewModel.ajouterPoints(it)
Log.d("Kahoot",state.value.questionPartie.question!!.question)
KahootPlayer(state.value.questionPartie.question) { pts, rep ->
viewModel.ajouterPoints(pts, rep)
}
}
}
@Preview
@Composable
fun KahootScreenPreview(){
KahootScreen(goToAccount = {}, goToHome = {})
}
// @Preview
// @Composable
// fun KahootScreenPreview(){
// KahootScreen(goToAccount = {}, goToHome = {}, goToResult = {})
// }
@Preview
@Composable
fun KahootPlayerPreview(){
val i = 0
KahootPlayer(question = StubQuestionWithReponses.ToModel()) {}
}
// @Preview
// @Composable
// fun KahootPlayerPreview(){
// val i = 0
// KahootPlayer(question = StubQuestionWithReponses.ToModel()) {}
// }
@Composable
fun KahootPlayer(question: QuestionWithSimpleReponse,
sendReponse: (Long) -> Unit){
fun KahootPlayer(question: QuestionWithSimpleReponse?,
sendReponse: (Long, Int) -> Unit){
val context = LocalContext.current;
val currTime = System.currentTimeMillis()
@ -73,12 +78,14 @@ fun KahootPlayer(question: QuestionWithSimpleReponse,
verticalArrangement = Arrangement.SpaceEvenly,
modifier = Modifier.fillMaxHeight()
) {
if (question != null) {
KahootQuestion(question = question.question)
KahootReponses(reponses = question.reponses) {
sendReponse(System.currentTimeMillis() - currTime)
sendReponse(System.currentTimeMillis() - currTime, it.id)
Toast.makeText(context, it.reponse, Toast.LENGTH_SHORT).show()
}
}
}
}

@ -0,0 +1,29 @@
package fr.iut.sciencequest.view.games.kahoot
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.material3.Button
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.compose.ui.res.stringResource
import androidx.lifecycle.viewmodel.compose.viewModel
import fr.iut.sciencequest.R
import fr.iut.sciencequest.view.TopBar
import fr.iut.sciencequest.viewModels.KahootJoinViewModel
import fr.iut.sciencequest.viewModels.KahootViewModel
@Composable
fun MenuKahoot(kahootViewModel: KahootViewModel = viewModel(factory = KahootViewModel.ApiFactory),
kahootJoinVM: KahootJoinViewModel = viewModel(),
goToAccount: () -> Unit,
goToHome: () -> Unit,
goToWaiting: (KahootViewModel) -> Unit) {
Column(modifier = Modifier.fillMaxWidth()) {
TopBar(goToAccount, goToHome, stringResource(id = R.string.kahoot))
RejoindreKahootAvecCodeContainer()
Button(onClick = { goToWaiting(kahootViewModel) }) {
Text("Creer une partie")
}
}
}

@ -0,0 +1,45 @@
package fr.iut.sciencequest.view.games.kahoot
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.material3.Button
import androidx.compose.material3.Text
import androidx.compose.material3.TextField
import androidx.compose.runtime.Composable
import androidx.compose.runtime.collectAsState
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.tooling.preview.Preview
import androidx.lifecycle.viewmodel.compose.viewModel
import fr.iut.sciencequest.R
import fr.iut.sciencequest.viewModels.KahootJoinViewModel
@Preview
@Composable
fun RejoindreKahootAvecCodeContainer(kahootJoinVM: KahootJoinViewModel = viewModel()) {
Row(
modifier = Modifier.fillMaxWidth(),
verticalAlignment = Alignment.CenterVertically,
) {
Text(stringResource(id = R.string.join_game))
KahootCodeInputContainer(kahootJoinVM)
}
}
@Preview
@Composable
fun KahootCodeInputContainer(kahootJoinVM: KahootJoinViewModel = viewModel()) {
val state = kahootJoinVM.uiState.collectAsState()
Column (
horizontalAlignment = Alignment.CenterHorizontally
){
TextField(value = state.value.code,
onValueChange = {kahootJoinVM.setCode(it)})
Button(onClick = { /*TODO*/ }) {
Text(text = stringResource(id = R.string.join))
}
}
}

@ -0,0 +1,33 @@
package fr.iut.sciencequest.view.games.kahoot
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.fillMaxHeight
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.collectAsState
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.res.stringResource
import androidx.lifecycle.viewmodel.compose.viewModel
import fr.iut.sciencequest.R
import fr.iut.sciencequest.view.TopBar
import fr.iut.sciencequest.viewModels.KahootJoinViewModel
import fr.iut.sciencequest.viewModels.KahootViewModel
@Composable
fun ResultatKahoot(goToAccount: () -> Unit,
goToHome: () -> Unit,
kahootViewModel: KahootViewModel) {
val state = kahootViewModel.uiState.collectAsState()
Column(modifier = Modifier.fillMaxWidth()) {
TopBar(goToAccount, goToHome, stringResource(id = R.string.kahoot))
Column(modifier = Modifier.fillMaxHeight(),
verticalArrangement = Arrangement.Center,
horizontalAlignment = Alignment.CenterHorizontally) {
Text("Vous avez " + state.value.nbPoints + "points")
}
}
}

@ -0,0 +1,51 @@
package fr.iut.sciencequest.view.games.kahoot
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.material3.Button
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.collectAsState
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.res.stringResource
import androidx.lifecycle.viewmodel.compose.viewModel
import fr.iut.sciencequest.R
import fr.iut.sciencequest.model.metier.joueur.JoueurSimple
import fr.iut.sciencequest.view.TopBar
import fr.iut.sciencequest.viewModels.KahootJoinViewModel
import fr.iut.sciencequest.viewModels.KahootViewModel
@Composable
fun WaitingScreen(kahootViewModel: KahootViewModel = viewModel(factory = KahootViewModel.ApiFactory),
goToAccount: () -> Unit,
goToHome: () -> Unit,
goToGame: () -> Unit) {
val state = kahootViewModel.uiState.collectAsState()
Column(modifier = Modifier.fillMaxWidth(),
horizontalAlignment = Alignment.CenterHorizontally) {
TopBar(goToAccount, goToHome, stringResource(id = R.string.kahoot))
Column {
Text(text = stringResource(id = R.string.user_in_game))
ListeJoueurs(state.value.partie.joueurs)
Button(onClick = { goToGame() }) {
Text(stringResource(id = R.string.start_game))
}
}
}
}
@Composable
fun ListeJoueurs(joueurs: List<JoueurSimple>) {
for (joueur in joueurs) {
JoueurContainer(joueur)
}
}
@Composable
fun JoueurContainer(joueur: JoueurSimple) {
Row {
Text("Pseudo: " + joueur.pseudo)
}
}

@ -0,0 +1,18 @@
package fr.iut.sciencequest.viewModels
import androidx.lifecycle.ViewModel
import fr.iut.sciencequest.viewModels.uiStates.KahootJoinUIState
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.asStateFlow
class KahootJoinViewModel : ViewModel() {
private val _uiState = MutableStateFlow(KahootJoinUIState())
val uiState = _uiState.asStateFlow()
fun setCode(code: String) {
if (code.length >= 6) {
return
}
_uiState.value = KahootJoinUIState(code)
}
}

@ -6,49 +6,90 @@ import android.util.Log
import androidx.lifecycle.ViewModel
import androidx.lifecycle.ViewModelProvider
import androidx.lifecycle.viewModelScope
import fr.iut.sciencequest.model.repositories.question.IQuestionRepository
import fr.iut.sciencequest.model.repositories.question.QuestionAPIRepository
import fr.iut.sciencequest.model.dto.partie.NouvellePartieDTO
import fr.iut.sciencequest.model.repositories.kahootPartie.IKahootPartieRepository
import fr.iut.sciencequest.model.repositories.kahootPartie.KahootAPIRepository
import fr.iut.sciencequest.viewModels.uixStates.KahootUIState
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.asStateFlow
import kotlinx.coroutines.launch
import kotlinx.datetime.Clock
import kotlinx.datetime.Instant
import kotlinx.datetime.LocalDateTime
import kotlinx.datetime.TimeZone
import kotlinx.datetime.toInstant
import kotlinx.datetime.toLocalDateTime
class KahootViewModel(
val questionRepo: IQuestionRepository
val kahootRepo: IKahootPartieRepository
): ViewModel() {
private val _uiState = MutableStateFlow(KahootUIState())
val uiState = _uiState.asStateFlow()
private val handler = Handler(Looper.getMainLooper())
fun createPartie(idJoueur: Int, idDifficulte: Int, thematiques: List<Int>) {
viewModelScope.launch {
val infosToCreate = NouvellePartieDTO(idJoueur, thematiques, idDifficulte)
kahootRepo.createPartie(infosToCreate)
_uiState.value = KahootUIState(
dureePartie = _uiState.value.dureePartie,
nbPoints = _uiState.value.nbPoints,
reponseChoisie = false,
partie = kahootRepo.partie.value
)
}
}
fun lancerPartie() {
viewModelScope.launch {
questionRepo.fetchQuestions(2)
kahootRepo.startPartie(_uiState.value.partie.code)
_uiState.value = KahootUIState(
questionRepo.questions.value.get(0),
dureePartie = uiState.value.dureePartie,
nbPoints = uiState.value.nbPoints,
reponseChoisie = false
partie = kahootRepo.partie.value,
nbPoints = _uiState.value.nbPoints,
reponseChoisie = false,
dureePartie = uiState.value.dureePartie
)
for (index: Int in 1..questionRepo.questions.value.size) {
handler.postDelayed(
{
}
}
fun updateQuestion(goToResult: () -> Unit) {
viewModelScope.launch {
Log.d("Hop j'attends","a")
kahootRepo.getQuestion(_uiState.value.partie.code)
if (kahootRepo.question.value.date == LocalDateTime(1,1,1,1,1,1,1).toInstant(TimeZone.UTC)) {
Log.d("Je passe là","j'ai fini le kahoot")
goToResult()
} else {
_uiState.value = KahootUIState(
questionRepo.questions.value.get(index),
dureePartie = uiState.value.dureePartie,
nbPoints = uiState.value.nbPoints,
reponseChoisie = false
questionPartie = kahootRepo.question.value,
nbPoints = _uiState.value.nbPoints,
reponseChoisie = false,
partie = _uiState.value.partie
)
val now: Instant = Clock.System.now()
val today: LocalDateTime = now.toLocalDateTime(TimeZone.currentSystemDefault())
handler.postDelayed(
{
Log.d("PostDelayed","je vais chercher la prochaine question")
updateQuestion(goToResult)
},
uiState.value.dureePartie * index
uiState.value.dureePartie
)
}
}
}
// NOTE : tpsReponse en ms
fun ajouterPoints(tpsReponse: Long) {
fun ajouterPoints(tpsReponse: Long, reponseId: Int) {
var isResponseValid = false
viewModelScope.launch {
kahootRepo.postReponse(_uiState.value.partie.code,1,reponseId)
isResponseValid = kahootRepo.isReponseValid.value
}
if (tpsReponse < 0) {
throw IllegalArgumentException("ERREUR: Temps négatif donné à l'ajout de points")
} else if (tpsReponse > uiState.value.dureePartie) {
@ -57,12 +98,18 @@ class KahootViewModel(
if (uiState.value.reponseChoisie) {
return
}
val nbPoints: Int = (uiState.value.dureePartie - tpsReponse).toInt()
_uiState.value = KahootUIState(uiState.value.question,
val nbPoints: Int = if (isResponseValid) {
(uiState.value.dureePartie - tpsReponse).toInt()
} else {
0
}
_uiState.value = KahootUIState(
questionPartie = uiState.value.questionPartie,
dureePartie = uiState.value.dureePartie,
nbPoints = uiState.value.nbPoints + nbPoints,
reponseChoisie = true)
}
companion object {
val ApiFactory: ViewModelProvider.Factory = object : ViewModelProvider.Factory {
@ -71,7 +118,7 @@ class KahootViewModel(
modelClass: Class<T>
): T {
return KahootViewModel(
QuestionAPIRepository()
KahootAPIRepository()
) as T
}
}

@ -0,0 +1,5 @@
package fr.iut.sciencequest.viewModels.uiStates
data class KahootJoinUIState (
val code: String = ""
)

@ -1,11 +1,20 @@
package fr.iut.sciencequest.viewModels.uixStates
import fr.iut.sciencequest.model.dto.extensions.ToModel
import fr.iut.sciencequest.model.metier.Difficulte
import fr.iut.sciencequest.model.metier.partie.Partie
import fr.iut.sciencequest.model.metier.partie.PartieKahoot
import fr.iut.sciencequest.model.metier.question.QuestionPartie
import fr.iut.sciencequest.model.metier.question.QuestionWithSimpleReponse
import fr.iut.sciencequest.stub.StubQuestionWithReponses
import kotlinx.datetime.LocalDateTime
import kotlinx.datetime.TimeZone
import kotlinx.datetime.toInstant
data class KahootUIState (
val question: QuestionWithSimpleReponse = StubQuestionWithReponses.ToModel(),
val partie: PartieKahoot = PartieKahoot(0,"", emptyList(), emptyList(), Difficulte(0,"")),
val questionPartie: QuestionPartie = QuestionPartie(QuestionWithSimpleReponse(0,"", emptyList()), LocalDateTime(1,1,1,1,1).toInstant(
TimeZone.UTC)),
val reponseChoisie: Boolean = false,
// NOTE : Supposé en millisecondes
val dureePartie: Long = 10_000,

@ -14,4 +14,8 @@
<string name="no_account_details">Si vous n\'avez pas encore de compte, vous avez deux possibilitées, soit vous continuez sans compte, soit vous vous inscrivez</string>
<string name="coming_soon">Coming soon...</string>
<string name="reset_game">Nouvelle Partie</string>
<string name="join_game">Rejoindre une Partie</string>
<string name="join">Rejoindre</string>
<string name="user_in_game">Joueurs dans la partie: </string>
<string name="start_game">Lancer la partie</string>
</resources>
Loading…
Cancel
Save