feat: set modification

This commit is contained in:
Mylloon 2024-01-04 00:51:02 +01:00
parent b5922d7280
commit 871c29d487
Signed by: Anri
GPG key ID: A82D63DFF8D1317F
9 changed files with 305 additions and 9 deletions

View file

@ -151,6 +151,8 @@ private fun DeleteRow(
@Composable @Composable
private fun ActionRow(context: Context, model: HomeViewModel, navController: NavController) { private fun ActionRow(context: Context, model: HomeViewModel, navController: NavController) {
val selection by model.selected
Row(modifier = Modifier.fillMaxWidth(), horizontalArrangement = Arrangement.Center) { Row(modifier = Modifier.fillMaxWidth(), horizontalArrangement = Arrangement.Center) {
Button(onClick = { (model::doAction)(ActionHome.CREATION) }) { Button(onClick = { (model::doAction)(ActionHome.CREATION) }) {
Text(text = context.getString(R.string.main_button_create)) Text(text = context.getString(R.string.main_button_create))
@ -158,7 +160,9 @@ private fun ActionRow(context: Context, model: HomeViewModel, navController: Nav
Spacer(modifier = Modifier.padding(2.dp)) Spacer(modifier = Modifier.padding(2.dp))
Button(onClick = { navController.navigate(MODIFY_SET) }) { Button(
enabled = selection != null,
onClick = { navController.navigate("$MODIFY_SET/${selection?.idSet}") }) {
Text(text = context.getString(R.string.main_button_modify)) Text(text = context.getString(R.string.main_button_modify))
} }

View file

@ -67,7 +67,14 @@ fun MainScreen() {
modifier = Modifier.padding(padding) modifier = Modifier.padding(padding)
) { ) {
composable(HOME) { HomeScreen(padding, navController) } composable(HOME) { HomeScreen(padding, navController) }
composable(MODIFY_SET) { ModifySetScreen(padding, navController) } composable("$MODIFY_SET/{$MODIFY_SET_ARGS}") {
it.arguments?.getString(MODIFY_SET_ARGS)?.let { idSet ->
ModifySetScreen(
padding,
idSet.toInt()
)
}
}
composable(PLAY) { PlayScreen(padding, navController) } composable(PLAY) { PlayScreen(padding, navController) }
} }
} }

View file

@ -1,14 +1,212 @@
package fr.uparis.diamantkennel.memorisationapplication package fr.uparis.diamantkennel.memorisationapplication
import android.widget.Toast import android.content.res.Resources
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.PaddingValues import androidx.compose.foundation.layout.PaddingValues
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.fillMaxHeight
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.lazy.itemsIndexed
import androidx.compose.material3.AlertDialog
import androidx.compose.material3.Button
import androidx.compose.material3.Card
import androidx.compose.material3.CardDefaults
import androidx.compose.material3.ExperimentalMaterial3Api
import androidx.compose.material3.OutlinedTextField
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
import androidx.compose.runtime.collectAsState
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.setValue
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.platform.LocalContext
import androidx.navigation.NavController import androidx.compose.ui.res.colorResource
import androidx.compose.ui.unit.dp
import androidx.lifecycle.viewmodel.compose.viewModel
import fr.uparis.diamantkennel.memorisationapplication.data.Question
import fr.uparis.diamantkennel.memorisationapplication.ui.ActionModifySet
import fr.uparis.diamantkennel.memorisationapplication.ui.ModifySetViewModel
import kotlin.text.Typography.ellipsis
@Composable @Composable
fun ModifySetScreen(padding: PaddingValues, navController: NavController) { fun ModifySetScreen(
val context = LocalContext.current padding: PaddingValues,
idSet: Int,
model: ModifySetViewModel = viewModel()
) {
// First update the list and set ID
model.setId.value = idSet
model.updateQuestionList(idSet)
Toast.makeText(context, "Modify", Toast.LENGTH_SHORT).show() val context = LocalContext.current
val currentSelection by model.selection
val questions by model.questions.collectAsState(listOf())
var action by model.action
if (action == ActionModifySet.AJOUT || action == ActionModifySet.MODIFICATION) {
AjoutModifDialog(action, currentSelection, model::ajoutQuestion)
{ action = ActionModifySet.AUCUN }
}
if (action == ActionModifySet.SUPPRIMER) {
RemoveDialog(model::removeQuestion)
{ action = ActionModifySet.AUCUN }
}
Column(
modifier = Modifier.padding(padding),
horizontalAlignment = Alignment.CenterHorizontally
) {
showList(model, questions, currentSelection)
Row(modifier = Modifier.fillMaxWidth(), horizontalArrangement = Arrangement.Center) {
Button(
enabled = currentSelection != null,
onClick = { action = ActionModifySet.MODIFICATION }) {
Text(text = context.getString(R.string.modify_button_modify))
}
Spacer(modifier = Modifier.padding(2.dp))
Button(onClick = { action = ActionModifySet.AJOUT }) {
Text(text = context.getString(R.string.modify_button_add))
}
Spacer(modifier = Modifier.padding(2.dp))
Button(
enabled = currentSelection != null,
onClick = { action = ActionModifySet.SUPPRIMER }) {
Text(text = context.getString(R.string.modify_button_delete))
}
}
}
} }
@Composable
fun showList(model: ModifySetViewModel, questions: List<Question>, currentSelection: Question?) {
LazyColumn(
Modifier.fillMaxHeight(0.6f)
) {
itemsIndexed(questions) { index, item ->
listItem(index, item, currentSelection, model::updateSelection)
}
}
}
@OptIn(ExperimentalMaterial3Api::class)
@Composable
fun listItem(
index: Int,
question: Question,
currentSelection: Question?,
updateSelection: (Question) -> Unit
) {
val maxLength = Resources.getSystem().displayMetrics.widthPixels / 33
val containerColor = when {
currentSelection == question -> colorResource(id = R.color.selected)
index % 2 == 0 -> colorResource(id = R.color.list_alternate)
else -> colorResource(id = R.color.list)
}
Card(
onClick = { updateSelection(question) },
Modifier.fillMaxSize(),
colors = CardDefaults.cardColors(containerColor)
) {
Row(
modifier = Modifier.fillMaxWidth(), horizontalArrangement = Arrangement.SpaceEvenly
) {
Text(
question.enonce.take(maxLength)
.let { if (question.enonce.length > maxLength) it + ellipsis else it },
modifier = Modifier.padding(2.dp)
)
}
}
}
@OptIn(ExperimentalMaterial3Api::class)
@Composable
fun AjoutModifDialog(
type: ActionModifySet,
selection: Question?,
confirm: (String, String) -> Unit,
dismiss: () -> Unit
) {
var enonce by remember {
mutableStateOf(
if (type == ActionModifySet.MODIFICATION) {
selection?.enonce!!
} else {
""
}
)
}
var reponse by remember {
mutableStateOf(
if (type == ActionModifySet.MODIFICATION) {
selection?.reponse!!
} else {
""
}
)
}
AlertDialog(onDismissRequest = dismiss,
title = {
Text(
text = if (selection != null) {
"Mettre à jour la question"
} else {
"Ajouter une question"
}
)
},
text = {
Column {
Row {
OutlinedTextField(
value = enonce,
onValueChange = { enonce = it },
label = { Text(text = "Question") })
}
Row {
OutlinedTextField(
value = reponse,
onValueChange = { reponse = it },
label = { Text(text = "Réponse") })
}
}
},
confirmButton = {
Button(onClick = {
confirm(enonce, reponse)
dismiss()
}) { Text(text = "Ok") }
})
}
@Composable
fun RemoveDialog(confirm: () -> Unit, dismiss: () -> Unit) =
AlertDialog(onDismissRequest = dismiss,
title = { Text(text = "Supprimer la question") },
text = { Text(text = "Voulez-vous supprimer la question ?") },
confirmButton = {
Button(onClick = {
confirm()
dismiss()
}) { Text(text = "Ok") }
})

View file

@ -2,4 +2,5 @@ package fr.uparis.diamantkennel.memorisationapplication
const val HOME = "home" const val HOME = "home"
const val MODIFY_SET = "modify_set" const val MODIFY_SET = "modify_set"
const val MODIFY_SET_ARGS = "id_set"
const val PLAY = "play" const val PLAY = "play"

View file

@ -4,6 +4,7 @@ import androidx.room.Dao
import androidx.room.Delete import androidx.room.Delete
import androidx.room.Insert import androidx.room.Insert
import androidx.room.Query import androidx.room.Query
import androidx.room.Update
import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.Flow
@Dao @Dao
@ -14,12 +15,24 @@ interface MemoDao {
@Insert @Insert
suspend fun insertQuestion(question: Question) suspend fun insertQuestion(question: Question)
@Update
fun updateQuestion(question: Question)
@Query("SELECT * FROM SetQuestions") @Query("SELECT * FROM SetQuestions")
fun loadAllSets(): Flow<List<SetOfQuestions>> fun loadAllSets(): Flow<List<SetOfQuestions>>
@Query("SELECT * FROM Question WHERE setId = :idSet")
fun loadQuestions(idSet: Int): Flow<List<Question>>
@Query("SELECT * FROM SetQuestions WHERE idSet = :requestedId")
fun getSet(requestedId: Int): SetOfQuestions
@Query("DELETE FROM SetQuestions") @Query("DELETE FROM SetQuestions")
fun deleteTable() fun deleteTable()
@Delete @Delete
fun delete(set: SetQuestions) fun delete(set: SetQuestions)
@Delete
fun deleteQuestion(question: Question)
} }

View file

@ -15,6 +15,6 @@ import androidx.room.PrimaryKey
data class Question( data class Question(
@PrimaryKey(autoGenerate = true) val idQuestion: Int = 0, @PrimaryKey(autoGenerate = true) val idQuestion: Int = 0,
val setId: Int, // Foreign key linking to SetQuestions val setId: Int, // Foreign key linking to SetQuestions
val enonce: String, var enonce: String,
val reponse: String var reponse: String
) )

View file

@ -0,0 +1,5 @@
package fr.uparis.diamantkennel.memorisationapplication.ui
enum class ActionModifySet {
AJOUT, AUCUN, MODIFICATION, SUPPRIMER
}

View file

@ -0,0 +1,65 @@
package fr.uparis.diamantkennel.memorisationapplication.ui
import android.app.Application
import androidx.compose.runtime.mutableStateOf
import androidx.lifecycle.AndroidViewModel
import androidx.lifecycle.viewModelScope
import fr.uparis.diamantkennel.memorisationapplication.MemoApplication
import fr.uparis.diamantkennel.memorisationapplication.data.Question
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
class ModifySetViewModel(application: Application) : AndroidViewModel(application) {
private val dao = (application as MemoApplication).database.memoDao()
var setId = mutableStateOf(-1)
var questions = dao.loadQuestions(setId.value)
var selection = mutableStateOf<Question?>(null)
var action = mutableStateOf(ActionModifySet.AUCUN)
fun updateQuestionList(setId: Int) {
viewModelScope.launch(Dispatchers.IO) {
questions = dao.loadQuestions(setId)
}
}
fun updateSelection(question: Question) {
if (selection.value == question) {
selection.value = null
} else {
selection.value = question
}
}
fun ajoutQuestion(enonce: String, reponse: String) {
when (action.value) {
ActionModifySet.AJOUT -> viewModelScope.launch(Dispatchers.IO) {
dao.insertQuestion(
Question(
setId = setId.value,
enonce = enonce,
reponse = reponse
)
)
}
ActionModifySet.MODIFICATION -> viewModelScope.launch(Dispatchers.IO) {
val question = selection.value!!
question.enonce = enonce
question.reponse = reponse
dao.updateQuestion(question)
}
else -> {
/* Ce cas n'arrivera jamais */
}
}
}
fun removeQuestion() {
viewModelScope.launch(Dispatchers.IO) {
dao.deleteQuestion(selection.value!!)
}
}
}

View file

@ -9,4 +9,7 @@
<string name="main_button_start">Lancer</string> <string name="main_button_start">Lancer</string>
<string name="error_bad_entry">Erreur : entrée invalide</string> <string name="error_bad_entry">Erreur : entrée invalide</string>
<string name="error_duplicate">Erreur : doublon</string> <string name="error_duplicate">Erreur : doublon</string>
<string name="modify_button_add">Ajouter</string>
<string name="modify_button_modify">Modifier</string>
<string name="modify_button_delete">Supprimer</string>
</resources> </resources>