update competition
This commit is contained in:
parent
217f587965
commit
940378ce37
|
@ -58,10 +58,17 @@ class MainActivity : ComponentActivity() {
|
|||
|
||||
repository.createUser(newUser) { success, newUserId ->
|
||||
if (success) {
|
||||
Toast.makeText(this, "User Baru Dibuat! Login Sekarang...", Toast.LENGTH_SHORT).show()
|
||||
Toast.makeText(
|
||||
this,
|
||||
"User Baru Dibuat! Login Sekarang...",
|
||||
Toast.LENGTH_SHORT
|
||||
).show()
|
||||
repository.loginUser(email, password) { loginSuccess, loggedInUserId ->
|
||||
if (loginSuccess) {
|
||||
Log.d("MainActivity", "Login Berhasil setelah buat akun! User ID: $loggedInUserId")
|
||||
Log.d(
|
||||
"MainActivity",
|
||||
"Login Berhasil setelah buat akun! User ID: $loggedInUserId"
|
||||
)
|
||||
} else {
|
||||
Log.e("MainActivity", "Login Gagal setelah membuat akun!")
|
||||
}
|
||||
|
@ -91,6 +98,13 @@ fun MyApp() {
|
|||
composable("write") { WriteScreen(navController) }
|
||||
composable("screening") { ScreeningScreen(navController) }
|
||||
// composable("resultscreening") { ResultScreeningScreen(navController) }
|
||||
composable(
|
||||
route = "resultscreening/{totalScore}",
|
||||
arguments = listOf(navArgument("totalScore") { type = NavType.IntType })
|
||||
) { backStackEntry ->
|
||||
val totalScore = backStackEntry.arguments?.getInt("totalScore") ?: 0
|
||||
ResultScreeningScreen(navController, totalScore)
|
||||
}
|
||||
composable("learnAlphabet") { LearnAlphabetScreen(navController) }
|
||||
composable("learnNumber") { LearnNumberScreen(navController) }
|
||||
// composable(
|
||||
|
@ -115,7 +129,7 @@ fun MyApp() {
|
|||
?.get<Int>("indexValue") ?: -1
|
||||
if (materialList != null) {
|
||||
DetailMaterialScreen(navController, materialList, indexValue)
|
||||
}else{
|
||||
} else {
|
||||
println("masuk kondisi else")
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,7 @@
|
|||
package com.example.lexilearn.data.model
|
||||
|
||||
data class CompetitionScore(
|
||||
val userId: String = "",
|
||||
val name: String = "",
|
||||
val score: Int = 0
|
||||
)
|
|
@ -9,6 +9,9 @@ import com.example.lexilearn.data.model.UserModel
|
|||
import com.google.firebase.database.*
|
||||
import com.google.firebase.database.DatabaseReference
|
||||
import com.google.firebase.database.FirebaseDatabase
|
||||
import java.text.SimpleDateFormat
|
||||
import java.util.Date
|
||||
import java.util.Locale
|
||||
|
||||
class FirebaseHelper(private val context: Context) {
|
||||
private val sharedPrefHelper = SharedPrefHelper(context)
|
||||
|
@ -122,4 +125,35 @@ class FirebaseHelper(private val context: Context) {
|
|||
}
|
||||
})
|
||||
}
|
||||
fun fetchTop10Scores(callback: (List<Pair<String, Int>>) -> Unit) {
|
||||
val todayDate = getTodayDate()
|
||||
val databaseReference: DatabaseReference =
|
||||
FirebaseDatabase.getInstance().getReference("competitions/$todayDate")
|
||||
|
||||
databaseReference.addListenerForSingleValueEvent(object : ValueEventListener {
|
||||
override fun onDataChange(snapshot: DataSnapshot) {
|
||||
val scoreList = mutableListOf<Pair<String, Int>>()
|
||||
|
||||
for (scoreSnapshot in snapshot.children) {
|
||||
val name = scoreSnapshot.child("name").getValue(String::class.java) ?: "Unknown"
|
||||
val score = scoreSnapshot.child("score").getValue(Int::class.java) ?: 0
|
||||
scoreList.add(Pair(name, score))
|
||||
}
|
||||
|
||||
// Urutkan berdasarkan skor tertinggi dan ambil 10 data
|
||||
val top10Scores = scoreList.sortedByDescending { it.second }.take(10)
|
||||
callback(top10Scores)
|
||||
}
|
||||
|
||||
override fun onCancelled(error: DatabaseError) {
|
||||
Log.e("FirebaseHelper", "Error fetching scores: ${error.message}")
|
||||
callback(emptyList()) // Jika error, kembalikan list kosong
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
fun getTodayDate(): String {
|
||||
val dateFormat = SimpleDateFormat("dd-MM-yyyy", Locale.getDefault())
|
||||
return dateFormat.format(Date()) // Mendapatkan tanggal hari ini (misalnya "25-02-2025")
|
||||
}
|
||||
}
|
||||
|
|
|
@ -23,6 +23,7 @@ import androidx.compose.foundation.shape.RoundedCornerShape
|
|||
import androidx.compose.foundation.verticalScroll
|
||||
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.draw.clip
|
||||
|
@ -52,10 +53,9 @@ import com.example.lexilearn.ui.theme.cwhite
|
|||
|
||||
|
||||
@Composable
|
||||
fun HomeScreen(navController: NavController) {
|
||||
val viewModel: HomeViewModel = viewModel()
|
||||
fun HomeScreen(navController: NavController, viewModel: HomeViewModel = viewModel()) {
|
||||
val scrollState = rememberScrollState()
|
||||
val leaderBoard = listOf("Tono (987)", "Budi (965)")
|
||||
val competitionScore = viewModel.topScores.collectAsState()
|
||||
|
||||
Column(
|
||||
modifier = Modifier
|
||||
|
@ -139,9 +139,9 @@ fun HomeScreen(navController: NavController) {
|
|||
)
|
||||
Spacer(modifier = Modifier.height(10.dp))
|
||||
LazyColumn {
|
||||
itemsIndexed(leaderBoard) { index, player ->
|
||||
itemsIndexed(competitionScore.value) { index, player ->
|
||||
Text(
|
||||
text = "${index + 1}. $player",
|
||||
text = "${index + 1}. ${player.name} (${player.score})",
|
||||
color = cAccent,
|
||||
fontWeight = FontWeight.Bold,
|
||||
fontSize = 14.sp
|
||||
|
@ -157,7 +157,7 @@ fun HomeScreen(navController: NavController) {
|
|||
)
|
||||
.width(160.dp)
|
||||
.clickable {
|
||||
viewModel.prepareCompetitionQuiz {dataMaterial->
|
||||
viewModel.prepareCompetitionQuiz { dataMaterial ->
|
||||
navController.currentBackStackEntry
|
||||
?.savedStateHandle
|
||||
?.set("selectedMaterialList", dataMaterial)
|
||||
|
|
|
@ -1,22 +1,78 @@
|
|||
package com.example.lexilearn.ui.views.pHome
|
||||
|
||||
import android.app.Application
|
||||
import android.util.Log
|
||||
import androidx.lifecycle.AndroidViewModel
|
||||
import androidx.lifecycle.LiveData
|
||||
import androidx.lifecycle.MutableLiveData
|
||||
import androidx.lifecycle.ViewModel
|
||||
import com.example.lexilearn.data.model.CompetitionScore
|
||||
import com.example.lexilearn.data.model.MaterialDataModel
|
||||
import com.example.lexilearn.data.model.QuizState
|
||||
import com.example.lexilearn.data.repository.MaterialRepository
|
||||
import com.example.lexilearn.data.repository.QuizRepository
|
||||
import com.google.firebase.database.DataSnapshot
|
||||
import com.google.firebase.database.DatabaseError
|
||||
import com.google.firebase.database.DatabaseReference
|
||||
import com.google.firebase.database.FirebaseDatabase
|
||||
import com.google.firebase.database.ValueEventListener
|
||||
import kotlinx.coroutines.flow.MutableStateFlow
|
||||
import kotlinx.coroutines.flow.StateFlow
|
||||
import kotlinx.coroutines.flow.asStateFlow
|
||||
import java.text.SimpleDateFormat
|
||||
import java.util.Date
|
||||
import java.util.Locale
|
||||
|
||||
class HomeViewModel(application: Application) : AndroidViewModel(application) {
|
||||
|
||||
private val totalDataCompetition = 3
|
||||
|
||||
private val _materialRepository = MaterialRepository(application)
|
||||
private val _quizRepository = QuizRepository()
|
||||
|
||||
private val database: DatabaseReference = FirebaseDatabase.getInstance().getReference("competitions")
|
||||
|
||||
private val _topScores = MutableStateFlow<List<CompetitionScore>>(emptyList())
|
||||
val topScores: StateFlow<List<CompetitionScore>> get() = _topScores.asStateFlow()
|
||||
|
||||
// 🔥 Mendapatkan tanggal hari ini dalam format "dd-MM-yyyy"
|
||||
private fun getTodayDate(): String {
|
||||
val dateFormat = SimpleDateFormat("dd-MM-yyyy", Locale.getDefault())
|
||||
return dateFormat.format(Date())
|
||||
}
|
||||
|
||||
init {
|
||||
fetchTop10Scores()
|
||||
}
|
||||
|
||||
// 🔥 Fungsi untuk mengambil 10 skor tertinggi secara real-time dari Firebase
|
||||
fun fetchTop10Scores() {
|
||||
val todayDate = getTodayDate()
|
||||
val dateRef = database.child(todayDate)
|
||||
|
||||
dateRef.orderByChild("score").limitToLast(10) // Ambil 10 skor tertinggi
|
||||
.addValueEventListener(object : ValueEventListener {
|
||||
override fun onDataChange(snapshot: DataSnapshot) {
|
||||
val scoreList = mutableListOf<CompetitionScore>()
|
||||
|
||||
for (scoreSnapshot in snapshot.children) {
|
||||
val scoreData = scoreSnapshot.getValue(CompetitionScore::class.java)
|
||||
scoreData?.let { scoreList.add(it) }
|
||||
}
|
||||
|
||||
// Urutkan dari yang tertinggi ke terendah
|
||||
_topScores.value = scoreList.sortedByDescending { it.score }
|
||||
}
|
||||
|
||||
override fun onCancelled(error: DatabaseError) {
|
||||
Log.e("CompetitionViewModel", "Error fetching scores: ${error.message}")
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
fun prepareCompetitionQuiz(callback: (List<MaterialDataModel>) -> Unit) {
|
||||
_materialRepository.getAllMaterialData("all") { materials ->
|
||||
_quizRepository.getRandomMaterials(3, materials) { randomData ->
|
||||
_quizRepository.getRandomMaterials(totalDataCompetition, materials) { randomData ->
|
||||
randomData.forEach { println(it) }
|
||||
callback(randomData)
|
||||
}
|
||||
|
|
|
@ -169,6 +169,14 @@ fun QuizScreen(
|
|||
quizViewModel.onSnackbarShown()
|
||||
}
|
||||
}
|
||||
val finalScore by quizViewModel.finalScore.collectAsState()
|
||||
LaunchedEffect(finalScore) {
|
||||
finalScore?.let { scoreFin ->
|
||||
navController.navigate("resultscreening/$scoreFin") {
|
||||
popUpTo("quizscreen") { inclusive = true }
|
||||
}
|
||||
}
|
||||
}
|
||||
Scaffold(
|
||||
snackbarHost = { SnackbarHost(hostState = snackbarHostState) }
|
||||
) { innerPadding ->
|
||||
|
|
|
@ -284,6 +284,8 @@ class QuizViewModel(application: Application) : AndroidViewModel(application),
|
|||
val liveCurrentScore: StateFlow<Int> get() = _liveCurrentScore.asStateFlow()
|
||||
private val _formattedResponseTime = MutableStateFlow("0.0")
|
||||
val formattedResponseTime: StateFlow<String> get() = _formattedResponseTime.asStateFlow()
|
||||
private val _finalScore = MutableStateFlow<Int?>(null) // Menyimpan skor akhir untuk navigasi
|
||||
val finalScore: StateFlow<Int?> get() = _finalScore.asStateFlow()
|
||||
|
||||
private var questionList = listOf<MaterialDataModel>()
|
||||
private var currentQuestionIndex = 0
|
||||
|
@ -361,7 +363,7 @@ class QuizViewModel(application: Application) : AndroidViewModel(application),
|
|||
_liveCurrentScore.value = baseScore
|
||||
startStopwatch() // Restart stopwatch
|
||||
}else{
|
||||
|
||||
_finalScore.value = _totalScore.value
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -12,13 +12,10 @@ import androidx.compose.material3.Text
|
|||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.graphics.Color
|
||||
import androidx.compose.ui.graphics.Outline
|
||||
import androidx.compose.ui.graphics.Shape
|
||||
import androidx.lifecycle.viewmodel.compose.viewModel
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.text.font.FontWeight
|
||||
import androidx.compose.ui.text.style.TextAlign
|
||||
import androidx.compose.ui.unit.LayoutDirection
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.compose.ui.unit.sp
|
||||
import androidx.constraintlayout.compose.ConstraintLayout
|
||||
|
@ -30,21 +27,30 @@ import androidx.compose.foundation.layout.offset
|
|||
import androidx.compose.foundation.layout.size
|
||||
import androidx.compose.foundation.shape.CircleShape
|
||||
import androidx.compose.foundation.shape.RoundedCornerShape
|
||||
import androidx.compose.runtime.LaunchedEffect
|
||||
import androidx.compose.runtime.collectAsState
|
||||
import androidx.compose.ui.draw.clip
|
||||
import androidx.compose.ui.geometry.RoundRect
|
||||
import androidx.compose.ui.graphics.Path
|
||||
import androidx.constraintlayout.compose.Dimension
|
||||
import com.example.lexilearn.ui.components.CustomButton
|
||||
import com.example.lexilearn.ui.theme.cprimary
|
||||
import com.example.lexilearn.ui.theme.ctextBlack
|
||||
import com.example.lexilearn.ui.theme.cwhite
|
||||
import com.example.lexilearn.ui.views.pScreening.ScreeningViewModel
|
||||
|
||||
|
||||
@Composable
|
||||
fun ResultScreeningScreen(navController: NavController, totalScore: Int, viewModel: ScreeningViewModel) {
|
||||
fun ResultScreeningScreen(
|
||||
navController: NavController,
|
||||
totalScore: Int,
|
||||
viewModel: ResultScreeningViewModel = viewModel()
|
||||
) {
|
||||
|
||||
val scorePosition = viewModel.positionScore.collectAsState()
|
||||
LaunchedEffect(Unit) {
|
||||
viewModel.submitScore(totalScore)
|
||||
}
|
||||
GradientScreening(
|
||||
backButton = { navController.popBackStack() },
|
||||
headerText = stringResource(id = R.string.rescreentitle),
|
||||
headerText = "Skor Kompetisi",
|
||||
modifier = Modifier.fillMaxSize()
|
||||
) {
|
||||
ConstraintLayout(
|
||||
|
@ -86,13 +92,13 @@ fun ResultScreeningScreen(navController: NavController, totalScore: Int, viewMod
|
|||
) {
|
||||
Spacer(modifier = Modifier.height(30.dp))
|
||||
Text(
|
||||
text = "Dyslexia",
|
||||
text = "Skor",
|
||||
fontSize = 20.sp,
|
||||
fontWeight = FontWeight.Bold,
|
||||
color = ctextBlack
|
||||
)
|
||||
Text(
|
||||
text = "66%",
|
||||
text = "$totalScore",
|
||||
fontSize = 50.sp,
|
||||
fontWeight = FontWeight.Bold,
|
||||
color = cprimary,
|
||||
|
@ -112,33 +118,37 @@ fun ResultScreeningScreen(navController: NavController, totalScore: Int, viewMod
|
|||
end.linkTo(parent.end)
|
||||
}) {
|
||||
Column(horizontalAlignment = Alignment.CenterHorizontally) {
|
||||
Text(
|
||||
text = "Anda menjawab \"ya\" pada 2 dari 3 pertanyaan yang menunjukkan 66% kemungkinan Anda menderita disleksia.",
|
||||
fontSize = 16.sp,
|
||||
color = ctextBlack,
|
||||
textAlign = TextAlign.Center,
|
||||
modifier = Modifier.padding(top = 24.dp)
|
||||
)
|
||||
Text(
|
||||
text = stringResource(id = R.string.rescreendesc),
|
||||
fontSize = 14.sp,
|
||||
color = ctextGray,
|
||||
textAlign = TextAlign.Center,
|
||||
modifier = Modifier.padding(top = 16.dp, bottom = 20.dp)
|
||||
)
|
||||
Text(
|
||||
text = "Wow, hebat sekali! 🎉 Kamu menduduki peringkat ${scorePosition.value} hari ini! 🚀",
|
||||
fontSize = 16.sp,
|
||||
color = ctextBlack,
|
||||
textAlign = TextAlign.Center,
|
||||
modifier = Modifier.padding(top = 24.dp)
|
||||
)
|
||||
Text(
|
||||
text = "Petualanganmu belum berakhir! 🔥 Coba lagi dan naikkan peringkatmu! 🏆",
|
||||
fontSize = 14.sp,
|
||||
color = ctextGray,
|
||||
textAlign = TextAlign.Center,
|
||||
modifier = Modifier.padding(top = 16.dp, bottom = 20.dp)
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
CustomButton(
|
||||
text = stringResource(id = R.string.close),
|
||||
onClick = { },
|
||||
modifier = Modifier.padding(horizontal = 24.dp, vertical = 12.dp).constrainAs(buttonRef) {
|
||||
bottom.linkTo(parent.bottom, margin = 12.dp)
|
||||
end.linkTo(parent.end, margin = 12.dp)
|
||||
start.linkTo(parent.start, margin = 12.dp)
|
||||
width = Dimension.fillToConstraints
|
||||
})
|
||||
onClick = {
|
||||
navController.popBackStack()
|
||||
},
|
||||
modifier = Modifier
|
||||
.padding(horizontal = 24.dp, vertical = 12.dp)
|
||||
.constrainAs(buttonRef) {
|
||||
bottom.linkTo(parent.bottom, margin = 12.dp)
|
||||
end.linkTo(parent.end, margin = 12.dp)
|
||||
start.linkTo(parent.start, margin = 12.dp)
|
||||
width = Dimension.fillToConstraints
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,8 +1,89 @@
|
|||
package com.example.lexilearn.ui.views.pResultScreening
|
||||
|
||||
import android.app.Application
|
||||
import android.util.Log
|
||||
import androidx.lifecycle.AndroidViewModel
|
||||
import com.example.lexilearn.data.model.CompetitionScore
|
||||
import com.example.lexilearn.data.repository.MaterialRepository
|
||||
import com.example.lexilearn.data.repository.QuizRepository
|
||||
import com.google.firebase.database.DataSnapshot
|
||||
import com.google.firebase.database.DatabaseError
|
||||
import com.google.firebase.database.DatabaseReference
|
||||
import com.google.firebase.database.FirebaseDatabase
|
||||
import com.google.firebase.database.ValueEventListener
|
||||
import kotlinx.coroutines.flow.MutableStateFlow
|
||||
import kotlinx.coroutines.flow.StateFlow
|
||||
import java.text.SimpleDateFormat
|
||||
import java.util.Date
|
||||
import java.util.Locale
|
||||
import java.util.UUID
|
||||
|
||||
class ResultScreeningViewModel(application: Application) : AndroidViewModel(application) {
|
||||
private val database: DatabaseReference = FirebaseDatabase.getInstance().getReference("competitions")
|
||||
private val materialRepo = MaterialRepository(application)
|
||||
|
||||
private val _positionScore = MutableStateFlow(0)
|
||||
val positionScore: StateFlow<Int> = _positionScore
|
||||
|
||||
private fun getTodayDate(): String {
|
||||
val dateFormat = SimpleDateFormat("dd-MM-yyyy", Locale.getDefault())
|
||||
return dateFormat.format(Date())
|
||||
}
|
||||
|
||||
fun submitScore(score: Int) {
|
||||
|
||||
materialRepo.getUserData {dataUser->
|
||||
val todayDate = getTodayDate()
|
||||
val dateRef = database.child(todayDate)
|
||||
|
||||
// Buat ID unik untuk skor
|
||||
val scoreId = dateRef.push().key ?: UUID.randomUUID().toString()
|
||||
val scoreData = mapOf("score" to score, "name" to "${dataUser?.name}")
|
||||
|
||||
dateRef.child(scoreId).setValue(scoreData)
|
||||
.addOnSuccessListener {
|
||||
Log.d("CompetitionViewModel", "Score added successfully!")
|
||||
checkUserRanking(todayDate, scoreId, score)
|
||||
}
|
||||
.addOnFailureListener {
|
||||
Log.e("CompetitionViewModel", "Error saving score: ${it.message}")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun checkUserRanking(todayDate: String, scoreId: String, userScore: Int) {
|
||||
val dateRef = database.child(todayDate)
|
||||
|
||||
dateRef.orderByChild("score").addListenerForSingleValueEvent(object : ValueEventListener {
|
||||
override fun onDataChange(snapshot: DataSnapshot) {
|
||||
val scoreList = mutableListOf<Pair<String, Int>>() // (scoreId, score)
|
||||
|
||||
for (scoreSnapshot in snapshot.children) {
|
||||
val id = scoreSnapshot.key ?: continue
|
||||
val score = scoreSnapshot.child("score").getValue(Int::class.java) ?: 0
|
||||
scoreList.add(id to score)
|
||||
}
|
||||
|
||||
// Urutkan skor dari tertinggi ke terendah
|
||||
val sortedScores = scoreList.sortedByDescending { it.second }
|
||||
|
||||
// Cari posisi user
|
||||
// val userRank = sortedScores.indexOfFirst { it.first == scoreId } + 1
|
||||
val userRank = maxOf(1, sortedScores.indexOfFirst { it.first == scoreId } + 1)
|
||||
|
||||
_positionScore.value = userRank
|
||||
|
||||
if (userRank > 0) {
|
||||
Log.d("ResultScreeningVM", "User is ranked: #$userRank out of ${sortedScores.size}")
|
||||
} else {
|
||||
Log.d("ResultScreeningVM", "User rank not found.")
|
||||
}
|
||||
}
|
||||
|
||||
override fun onCancelled(error: DatabaseError) {
|
||||
Log.e("ResultScreeningVM", "Error fetching scores: ${error.message}")
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
}
|
Loading…
Reference in New Issue