[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 { 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-serialization-json:1.6.2")
implementation("org.jetbrains.kotlinx:kotlinx-coroutines-core:1.7.3") implementation("org.jetbrains.kotlinx:kotlinx-coroutines-core:1.7.3")
implementation("org.jetbrains.kotlinx:kotlinx-coroutines-javafx: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 android.os.Bundle
import androidx.activity.ComponentActivity import androidx.activity.ComponentActivity
import androidx.activity.compose.setContent import androidx.activity.compose.setContent
import androidx.activity.viewModels
import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.material3.MaterialTheme import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Surface import androidx.compose.material3.Surface
import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier
import fr.iut.sciencequest.navigation.NavHost import fr.iut.sciencequest.navigation.NavHost
import fr.iut.sciencequest.ui.theme.ScienceQuestTheme import fr.iut.sciencequest.ui.theme.ScienceQuestTheme
import fr.iut.sciencequest.viewModels.KahootViewModel
class MainActivity : ComponentActivity() { class MainActivity : ComponentActivity() {
override fun onCreate(savedInstanceState: Bundle?) { override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState) super.onCreate(savedInstanceState)
val vm = viewModels<KahootViewModel> { KahootViewModel.ApiFactory }
setContent { setContent {
ScienceQuestTheme { ScienceQuestTheme {
// A surface container using the 'background' color from the theme // A surface container using the 'background' color from the theme
@ -20,7 +23,7 @@ class MainActivity : ComponentActivity() {
modifier = Modifier.fillMaxSize(), modifier = Modifier.fillMaxSize(),
color = MaterialTheme.colorScheme.background 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 = fun createRequestService(): Retrofit =
Retrofit.Builder() Retrofit.Builder()
.baseUrl(API_BASE_URL) .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) .client(httpClient)
.build() .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 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 package fr.iut.sciencequest.model.dto.joueur
import fr.iut.sciencequest.model.dto.PartieDTO import fr.iut.sciencequest.model.dto.partie.PartieDTO
class JoueurDTO ( class JoueurDTO (
val id: Int, 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 package fr.iut.sciencequest.model.dto.joueur
import kotlinx.serialization.Serializable
@Serializable
class JoueurSimpleDTO ( class JoueurSimpleDTO (
val id: Int, val id: Int,
val pseudo: String, 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 import fr.iut.sciencequest.model.dto.joueur.JoueurSimpleDTO
class PartieDTO ( 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.foundation.layout.fillMaxSize
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
import androidx.compose.runtime.collectAsState
import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier
import androidx.lifecycle.viewmodel.compose.viewModel
import androidx.navigation.compose.NavHost 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 fr.iut.sciencequest.view.AccountScreen import fr.iut.sciencequest.view.AccountScreen
import fr.iut.sciencequest.view.HomeScreen import fr.iut.sciencequest.view.HomeScreen
import fr.iut.sciencequest.view.LoginScreen 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.PenduScreen
import fr.iut.sciencequest.view.games.QuiScreen 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.view.scientifiques.scientifiqueListeScreen
import fr.iut.sciencequest.viewModels.KahootViewModel
@Composable @Composable
fun NavHost() { fun NavHost(kahootVM: KahootViewModel) {
val navController = rememberNavController() val navController = rememberNavController()
NavHost( NavHost(
modifier = Modifier.fillMaxSize(), modifier = Modifier.fillMaxSize(),
@ -35,7 +41,7 @@ fun NavHost() {
navController.navigate("pendu") navController.navigate("pendu")
}, },
goToKahoot = { goToKahoot = {
navController.navigate("kahoot") navController.navigate("kahootMenu")
}, },
goToQui = { goToQui = {
navController.navigate("qui") navController.navigate("qui")
@ -76,14 +82,61 @@ fun NavHost() {
) )
} }
composable(route= "kahoot"){ composable(route= "kahootGame"){
KahootScreen( KahootScreen(
goToAccount = { goToAccount = {
navController.navigate("account") navController.navigate("account")
}, },
goToHome = { goToHome = {
navController.navigate("home") 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() val state = viewModel.uiState.collectAsState()
Column(modifier = Modifier.fillMaxWidth()) { Column(modifier = Modifier.fillMaxWidth()) {
TopBar(goToAccount, goToHome, stringResource(id = R.string.kahoot)) TopBar(goToAccount, goToHome, stringResource(id = R.string.kahoot))
QuiPlayer(state.value.question) { QuiPlayer(state.value.questionPartie.question) {
viewModel.ajouterPoints(it) // viewModel.ajouterPoints(it)
} }
} }
} }
@ -59,16 +59,16 @@ fun QuiPlayerPreview(){
@Composable @Composable
fun QuiPlayer(question: QuestionWithSimpleReponse, fun QuiPlayer(question: QuestionWithSimpleReponse?,
sendReponse: (Long) -> Unit){ sendReponse: (Long) -> Unit){
val context = LocalContext.current; val context = LocalContext.current;
val currTime = System.currentTimeMillis() val currTime = System.currentTimeMillis()
Column (horizontalAlignment = Alignment.CenterHorizontally){ Column (horizontalAlignment = Alignment.CenterHorizontally){
QuiQuestion(question = question.question) //QuiQuestion(question = question.question)
QuiReponses(reponses = question.reponses) { //QuiReponses(reponses = question.reponses) {
sendReponse(currTime - System.currentTimeMillis()) // sendReponse(currTime - System.currentTimeMillis())
Toast.makeText(context, it.reponse, Toast.LENGTH_SHORT).show() // 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 android.widget.Toast
import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Column
@ -31,40 +32,44 @@ import fr.iut.sciencequest.stub.StubQuestionWithReponses
import fr.iut.sciencequest.view.TopBar import fr.iut.sciencequest.view.TopBar
@Composable @Composable
fun KahootScreen(viewModel: KahootViewModel = viewModel(factory = KahootViewModel.ApiFactory), fun KahootScreen(viewModel: KahootViewModel,
goToAccount: () -> Unit, goToAccount: () -> Unit,
goToHome: () -> Unit) { goToHome: () -> Unit,
val state = viewModel.uiState.collectAsState() goToResult: () -> Unit) {
LaunchedEffect(key1 = Unit) { LaunchedEffect(key1 = Unit) {
Log.d("Kahoot","Je lance la partie")
viewModel.lancerPartie() viewModel.lancerPartie()
Log.d("Kahoot","Je vais chercher la question")
viewModel.updateQuestion(goToResult)
} }
val state = viewModel.uiState.collectAsState()
Column(modifier = Modifier.fillMaxWidth()) { Column(modifier = Modifier.fillMaxWidth()) {
TopBar(goToAccount, goToHome, stringResource(id = R.string.kahoot)) TopBar(goToAccount, goToHome, stringResource(id = R.string.kahoot))
Log.d("Kahoot",state.value.questionPartie.question!!.question)
KahootPlayer(state.value.question) { KahootPlayer(state.value.questionPartie.question) { pts, rep ->
viewModel.ajouterPoints(it) viewModel.ajouterPoints(pts, rep)
} }
} }
} }
@Preview // @Preview
@Composable // @Composable
fun KahootScreenPreview(){ // fun KahootScreenPreview(){
KahootScreen(goToAccount = {}, goToHome = {}) // KahootScreen(goToAccount = {}, goToHome = {}, goToResult = {})
} // }
@Preview // @Preview
@Composable // @Composable
fun KahootPlayerPreview(){ // fun KahootPlayerPreview(){
val i = 0 // val i = 0
KahootPlayer(question = StubQuestionWithReponses.ToModel()) {} // KahootPlayer(question = StubQuestionWithReponses.ToModel()) {}
} // }
@Composable @Composable
fun KahootPlayer(question: QuestionWithSimpleReponse, fun KahootPlayer(question: QuestionWithSimpleReponse?,
sendReponse: (Long) -> Unit){ sendReponse: (Long, Int) -> Unit){
val context = LocalContext.current; val context = LocalContext.current;
val currTime = System.currentTimeMillis() val currTime = System.currentTimeMillis()
@ -73,13 +78,15 @@ fun KahootPlayer(question: QuestionWithSimpleReponse,
verticalArrangement = Arrangement.SpaceEvenly, verticalArrangement = Arrangement.SpaceEvenly,
modifier = Modifier.fillMaxHeight() modifier = Modifier.fillMaxHeight()
) { ) {
if (question != null) {
KahootQuestion(question = question.question) KahootQuestion(question = question.question)
KahootReponses(reponses = question.reponses) { KahootReponses(reponses = question.reponses) {
sendReponse(System.currentTimeMillis() - currTime) sendReponse(System.currentTimeMillis() - currTime, it.id)
Toast.makeText(context, it.reponse, Toast.LENGTH_SHORT).show() Toast.makeText(context, it.reponse, Toast.LENGTH_SHORT).show()
} }
} }
} }
}
@Composable @Composable

@ -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.ViewModel
import androidx.lifecycle.ViewModelProvider import androidx.lifecycle.ViewModelProvider
import androidx.lifecycle.viewModelScope import androidx.lifecycle.viewModelScope
import fr.iut.sciencequest.model.repositories.question.IQuestionRepository import fr.iut.sciencequest.model.dto.partie.NouvellePartieDTO
import fr.iut.sciencequest.model.repositories.question.QuestionAPIRepository import fr.iut.sciencequest.model.repositories.kahootPartie.IKahootPartieRepository
import fr.iut.sciencequest.model.repositories.kahootPartie.KahootAPIRepository
import fr.iut.sciencequest.viewModels.uixStates.KahootUIState import fr.iut.sciencequest.viewModels.uixStates.KahootUIState
import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.asStateFlow import kotlinx.coroutines.flow.asStateFlow
import kotlinx.coroutines.launch 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( class KahootViewModel(
val questionRepo: IQuestionRepository val kahootRepo: IKahootPartieRepository
): ViewModel() { ): ViewModel() {
private val _uiState = MutableStateFlow(KahootUIState()) private val _uiState = MutableStateFlow(KahootUIState())
val uiState = _uiState.asStateFlow() val uiState = _uiState.asStateFlow()
private val handler = Handler(Looper.getMainLooper()) 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() { fun lancerPartie() {
viewModelScope.launch { viewModelScope.launch {
questionRepo.fetchQuestions(2) kahootRepo.startPartie(_uiState.value.partie.code)
_uiState.value = KahootUIState( _uiState.value = KahootUIState(
questionRepo.questions.value.get(0), partie = kahootRepo.partie.value,
dureePartie = uiState.value.dureePartie, nbPoints = _uiState.value.nbPoints,
nbPoints = uiState.value.nbPoints, reponseChoisie = false,
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( _uiState.value = KahootUIState(
questionRepo.questions.value.get(index), questionPartie = kahootRepo.question.value,
dureePartie = uiState.value.dureePartie, nbPoints = _uiState.value.nbPoints,
nbPoints = uiState.value.nbPoints, reponseChoisie = false,
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 // 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) { if (tpsReponse < 0) {
throw IllegalArgumentException("ERREUR: Temps négatif donné à l'ajout de points") throw IllegalArgumentException("ERREUR: Temps négatif donné à l'ajout de points")
} else if (tpsReponse > uiState.value.dureePartie) { } else if (tpsReponse > uiState.value.dureePartie) {
@ -57,12 +98,18 @@ class KahootViewModel(
if (uiState.value.reponseChoisie) { if (uiState.value.reponseChoisie) {
return return
} }
val nbPoints: Int = (uiState.value.dureePartie - tpsReponse).toInt() val nbPoints: Int = if (isResponseValid) {
_uiState.value = KahootUIState(uiState.value.question, (uiState.value.dureePartie - tpsReponse).toInt()
} else {
0
}
_uiState.value = KahootUIState(
questionPartie = uiState.value.questionPartie,
dureePartie = uiState.value.dureePartie, dureePartie = uiState.value.dureePartie,
nbPoints = uiState.value.nbPoints + nbPoints, nbPoints = uiState.value.nbPoints + nbPoints,
reponseChoisie = true) reponseChoisie = true)
} }
companion object { companion object {
val ApiFactory: ViewModelProvider.Factory = object : ViewModelProvider.Factory { val ApiFactory: ViewModelProvider.Factory = object : ViewModelProvider.Factory {
@ -71,7 +118,7 @@ class KahootViewModel(
modelClass: Class<T> modelClass: Class<T>
): T { ): T {
return KahootViewModel( return KahootViewModel(
QuestionAPIRepository() KahootAPIRepository()
) as T ) 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 package fr.iut.sciencequest.viewModels.uixStates
import fr.iut.sciencequest.model.dto.extensions.ToModel 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.model.metier.question.QuestionWithSimpleReponse
import fr.iut.sciencequest.stub.StubQuestionWithReponses import fr.iut.sciencequest.stub.StubQuestionWithReponses
import kotlinx.datetime.LocalDateTime
import kotlinx.datetime.TimeZone
import kotlinx.datetime.toInstant
data class KahootUIState ( 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, val reponseChoisie: Boolean = false,
// NOTE : Supposé en millisecondes // NOTE : Supposé en millisecondes
val dureePartie: Long = 10_000, 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="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="coming_soon">Coming soon...</string>
<string name="reset_game">Nouvelle Partie</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> </resources>
Loading…
Cancel
Save