update competition

This commit is contained in:
DimazzP 2025-02-26 01:40:22 +07:00
parent 217f587965
commit 940378ce37
9 changed files with 256 additions and 44 deletions

View File

@ -58,10 +58,17 @@ class MainActivity : ComponentActivity() {
repository.createUser(newUser) { success, newUserId -> repository.createUser(newUser) { success, newUserId ->
if (success) { 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 -> repository.loginUser(email, password) { loginSuccess, loggedInUserId ->
if (loginSuccess) { 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 { } else {
Log.e("MainActivity", "Login Gagal setelah membuat akun!") Log.e("MainActivity", "Login Gagal setelah membuat akun!")
} }
@ -91,6 +98,13 @@ fun MyApp() {
composable("write") { WriteScreen(navController) } composable("write") { WriteScreen(navController) }
composable("screening") { ScreeningScreen(navController) } composable("screening") { ScreeningScreen(navController) }
// composable("resultscreening") { ResultScreeningScreen(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("learnAlphabet") { LearnAlphabetScreen(navController) }
composable("learnNumber") { LearnNumberScreen(navController) } composable("learnNumber") { LearnNumberScreen(navController) }
// composable( // composable(
@ -115,7 +129,7 @@ fun MyApp() {
?.get<Int>("indexValue") ?: -1 ?.get<Int>("indexValue") ?: -1
if (materialList != null) { if (materialList != null) {
DetailMaterialScreen(navController, materialList, indexValue) DetailMaterialScreen(navController, materialList, indexValue)
}else{ } else {
println("masuk kondisi else") println("masuk kondisi else")
} }
} }

View File

@ -0,0 +1,7 @@
package com.example.lexilearn.data.model
data class CompetitionScore(
val userId: String = "",
val name: String = "",
val score: Int = 0
)

View File

@ -9,6 +9,9 @@ import com.example.lexilearn.data.model.UserModel
import com.google.firebase.database.* import com.google.firebase.database.*
import com.google.firebase.database.DatabaseReference import com.google.firebase.database.DatabaseReference
import com.google.firebase.database.FirebaseDatabase import com.google.firebase.database.FirebaseDatabase
import java.text.SimpleDateFormat
import java.util.Date
import java.util.Locale
class FirebaseHelper(private val context: Context) { class FirebaseHelper(private val context: Context) {
private val sharedPrefHelper = SharedPrefHelper(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")
}
} }

View File

@ -23,6 +23,7 @@ import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.foundation.verticalScroll import androidx.compose.foundation.verticalScroll
import androidx.compose.material3.Text import androidx.compose.material3.Text
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
import androidx.compose.runtime.collectAsState
import androidx.compose.ui.Alignment import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.clip import androidx.compose.ui.draw.clip
@ -52,10 +53,9 @@ import com.example.lexilearn.ui.theme.cwhite
@Composable @Composable
fun HomeScreen(navController: NavController) { fun HomeScreen(navController: NavController, viewModel: HomeViewModel = viewModel()) {
val viewModel: HomeViewModel = viewModel()
val scrollState = rememberScrollState() val scrollState = rememberScrollState()
val leaderBoard = listOf("Tono (987)", "Budi (965)") val competitionScore = viewModel.topScores.collectAsState()
Column( Column(
modifier = Modifier modifier = Modifier
@ -139,9 +139,9 @@ fun HomeScreen(navController: NavController) {
) )
Spacer(modifier = Modifier.height(10.dp)) Spacer(modifier = Modifier.height(10.dp))
LazyColumn { LazyColumn {
itemsIndexed(leaderBoard) { index, player -> itemsIndexed(competitionScore.value) { index, player ->
Text( Text(
text = "${index + 1}. $player", text = "${index + 1}. ${player.name} (${player.score})",
color = cAccent, color = cAccent,
fontWeight = FontWeight.Bold, fontWeight = FontWeight.Bold,
fontSize = 14.sp fontSize = 14.sp
@ -157,7 +157,7 @@ fun HomeScreen(navController: NavController) {
) )
.width(160.dp) .width(160.dp)
.clickable { .clickable {
viewModel.prepareCompetitionQuiz {dataMaterial-> viewModel.prepareCompetitionQuiz { dataMaterial ->
navController.currentBackStackEntry navController.currentBackStackEntry
?.savedStateHandle ?.savedStateHandle
?.set("selectedMaterialList", dataMaterial) ?.set("selectedMaterialList", dataMaterial)

View File

@ -1,22 +1,78 @@
package com.example.lexilearn.ui.views.pHome package com.example.lexilearn.ui.views.pHome
import android.app.Application import android.app.Application
import android.util.Log
import androidx.lifecycle.AndroidViewModel import androidx.lifecycle.AndroidViewModel
import androidx.lifecycle.LiveData import androidx.lifecycle.LiveData
import androidx.lifecycle.MutableLiveData import androidx.lifecycle.MutableLiveData
import androidx.lifecycle.ViewModel import androidx.lifecycle.ViewModel
import com.example.lexilearn.data.model.CompetitionScore
import com.example.lexilearn.data.model.MaterialDataModel import com.example.lexilearn.data.model.MaterialDataModel
import com.example.lexilearn.data.model.QuizState import com.example.lexilearn.data.model.QuizState
import com.example.lexilearn.data.repository.MaterialRepository import com.example.lexilearn.data.repository.MaterialRepository
import com.example.lexilearn.data.repository.QuizRepository 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) { class HomeViewModel(application: Application) : AndroidViewModel(application) {
private val totalDataCompetition = 3
private val _materialRepository = MaterialRepository(application) private val _materialRepository = MaterialRepository(application)
private val _quizRepository = QuizRepository() 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) { fun prepareCompetitionQuiz(callback: (List<MaterialDataModel>) -> Unit) {
_materialRepository.getAllMaterialData("all") { materials -> _materialRepository.getAllMaterialData("all") { materials ->
_quizRepository.getRandomMaterials(3, materials) { randomData -> _quizRepository.getRandomMaterials(totalDataCompetition, materials) { randomData ->
randomData.forEach { println(it) } randomData.forEach { println(it) }
callback(randomData) callback(randomData)
} }

View File

@ -169,6 +169,14 @@ fun QuizScreen(
quizViewModel.onSnackbarShown() quizViewModel.onSnackbarShown()
} }
} }
val finalScore by quizViewModel.finalScore.collectAsState()
LaunchedEffect(finalScore) {
finalScore?.let { scoreFin ->
navController.navigate("resultscreening/$scoreFin") {
popUpTo("quizscreen") { inclusive = true }
}
}
}
Scaffold( Scaffold(
snackbarHost = { SnackbarHost(hostState = snackbarHostState) } snackbarHost = { SnackbarHost(hostState = snackbarHostState) }
) { innerPadding -> ) { innerPadding ->

View File

@ -284,6 +284,8 @@ class QuizViewModel(application: Application) : AndroidViewModel(application),
val liveCurrentScore: StateFlow<Int> get() = _liveCurrentScore.asStateFlow() val liveCurrentScore: StateFlow<Int> get() = _liveCurrentScore.asStateFlow()
private val _formattedResponseTime = MutableStateFlow("0.0") private val _formattedResponseTime = MutableStateFlow("0.0")
val formattedResponseTime: StateFlow<String> get() = _formattedResponseTime.asStateFlow() 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 questionList = listOf<MaterialDataModel>()
private var currentQuestionIndex = 0 private var currentQuestionIndex = 0
@ -361,7 +363,7 @@ class QuizViewModel(application: Application) : AndroidViewModel(application),
_liveCurrentScore.value = baseScore _liveCurrentScore.value = baseScore
startStopwatch() // Restart stopwatch startStopwatch() // Restart stopwatch
}else{ }else{
_finalScore.value = _totalScore.value
} }
} }

View File

@ -12,13 +12,10 @@ import androidx.compose.material3.Text
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
import androidx.compose.ui.Alignment import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color import androidx.lifecycle.viewmodel.compose.viewModel
import androidx.compose.ui.graphics.Outline
import androidx.compose.ui.graphics.Shape
import androidx.compose.ui.res.stringResource import androidx.compose.ui.res.stringResource
import androidx.compose.ui.text.font.FontWeight import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.text.style.TextAlign import androidx.compose.ui.text.style.TextAlign
import androidx.compose.ui.unit.LayoutDirection
import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp import androidx.compose.ui.unit.sp
import androidx.constraintlayout.compose.ConstraintLayout 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.layout.size
import androidx.compose.foundation.shape.CircleShape import androidx.compose.foundation.shape.CircleShape
import androidx.compose.foundation.shape.RoundedCornerShape 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.draw.clip
import androidx.compose.ui.geometry.RoundRect
import androidx.compose.ui.graphics.Path
import androidx.constraintlayout.compose.Dimension import androidx.constraintlayout.compose.Dimension
import com.example.lexilearn.ui.components.CustomButton import com.example.lexilearn.ui.components.CustomButton
import com.example.lexilearn.ui.theme.cprimary import com.example.lexilearn.ui.theme.cprimary
import com.example.lexilearn.ui.theme.ctextBlack import com.example.lexilearn.ui.theme.ctextBlack
import com.example.lexilearn.ui.theme.cwhite import com.example.lexilearn.ui.theme.cwhite
import com.example.lexilearn.ui.views.pScreening.ScreeningViewModel
@Composable @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( GradientScreening(
backButton = { navController.popBackStack() }, backButton = { navController.popBackStack() },
headerText = stringResource(id = R.string.rescreentitle), headerText = "Skor Kompetisi",
modifier = Modifier.fillMaxSize() modifier = Modifier.fillMaxSize()
) { ) {
ConstraintLayout( ConstraintLayout(
@ -86,13 +92,13 @@ fun ResultScreeningScreen(navController: NavController, totalScore: Int, viewMod
) { ) {
Spacer(modifier = Modifier.height(30.dp)) Spacer(modifier = Modifier.height(30.dp))
Text( Text(
text = "Dyslexia", text = "Skor",
fontSize = 20.sp, fontSize = 20.sp,
fontWeight = FontWeight.Bold, fontWeight = FontWeight.Bold,
color = ctextBlack color = ctextBlack
) )
Text( Text(
text = "66%", text = "$totalScore",
fontSize = 50.sp, fontSize = 50.sp,
fontWeight = FontWeight.Bold, fontWeight = FontWeight.Bold,
color = cprimary, color = cprimary,
@ -112,33 +118,37 @@ fun ResultScreeningScreen(navController: NavController, totalScore: Int, viewMod
end.linkTo(parent.end) end.linkTo(parent.end)
}) { }) {
Column(horizontalAlignment = Alignment.CenterHorizontally) { Column(horizontalAlignment = Alignment.CenterHorizontally) {
Text( Text(
text = "Anda menjawab \"ya\" pada 2 dari 3 pertanyaan yang menunjukkan 66% kemungkinan Anda menderita disleksia.", text = "Wow, hebat sekali! 🎉 Kamu menduduki peringkat ${scorePosition.value} hari ini! 🚀",
fontSize = 16.sp, fontSize = 16.sp,
color = ctextBlack, color = ctextBlack,
textAlign = TextAlign.Center, textAlign = TextAlign.Center,
modifier = Modifier.padding(top = 24.dp) modifier = Modifier.padding(top = 24.dp)
) )
Text( Text(
text = stringResource(id = R.string.rescreendesc), text = "Petualanganmu belum berakhir! 🔥 Coba lagi dan naikkan peringkatmu! 🏆",
fontSize = 14.sp, fontSize = 14.sp,
color = ctextGray, color = ctextGray,
textAlign = TextAlign.Center, textAlign = TextAlign.Center,
modifier = Modifier.padding(top = 16.dp, bottom = 20.dp) modifier = Modifier.padding(top = 16.dp, bottom = 20.dp)
) )
} }
} }
} }
} }
CustomButton( CustomButton(
text = stringResource(id = R.string.close), text = stringResource(id = R.string.close),
onClick = { }, onClick = {
modifier = Modifier.padding(horizontal = 24.dp, vertical = 12.dp).constrainAs(buttonRef) { navController.popBackStack()
bottom.linkTo(parent.bottom, margin = 12.dp) },
end.linkTo(parent.end, margin = 12.dp) modifier = Modifier
start.linkTo(parent.start, margin = 12.dp) .padding(horizontal = 24.dp, vertical = 12.dp)
width = Dimension.fillToConstraints .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
})
} }
} }
} }

View File

@ -1,8 +1,89 @@
package com.example.lexilearn.ui.views.pResultScreening package com.example.lexilearn.ui.views.pResultScreening
import android.app.Application import android.app.Application
import android.util.Log
import androidx.lifecycle.AndroidViewModel 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) { 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}")
}
})
}
} }