feat: set modification
This commit is contained in:
parent
b5922d7280
commit
871c29d487
9 changed files with 305 additions and 9 deletions
|
@ -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))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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) }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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") }
|
||||||
|
})
|
||||||
|
|
|
@ -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"
|
||||||
|
|
|
@ -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)
|
||||||
}
|
}
|
||||||
|
|
|
@ -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
|
||||||
)
|
)
|
||||||
|
|
|
@ -0,0 +1,5 @@
|
||||||
|
package fr.uparis.diamantkennel.memorisationapplication.ui
|
||||||
|
|
||||||
|
enum class ActionModifySet {
|
||||||
|
AJOUT, AUCUN, MODIFICATION, SUPPRIMER
|
||||||
|
}
|
|
@ -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!!)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -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>
|
||||||
|
|
Reference in a new issue