From e646e8ff618df058b07fbb3348c25274c349f4a0 Mon Sep 17 00:00:00 2001 From: fodorpatrik2000 <fodorpatrik2000@sch.bme.hu> Date: Mon, 16 May 2022 01:14:09 +0200 Subject: [PATCH] Profile page --- .../szobatarsch/composable/MyDropdownMenu.kt | 86 ++++++ .../szobatarsch/composable/MyIntTextField.kt | 28 ++ .../composable/MyLabelledCheckBox.kt | 43 +++ .../kszk/szobatarsch/composable/MySlider.kt | 33 +++ .../composable/MyStringTextField.kt | 27 ++ .../kszk/szobatarsch/composable/Profile.kt | 280 ++++++++++++++++++ .../szobatarsch/composable/ProfileState.kt | 45 +++ app/src/main/res/values-hu/strings.xml | 5 + app/src/main/res/values/strings.xml | 3 +- 9 files changed, 549 insertions(+), 1 deletion(-) create mode 100644 app/src/main/java/hu/bme/kszk/szobatarsch/composable/MyDropdownMenu.kt create mode 100644 app/src/main/java/hu/bme/kszk/szobatarsch/composable/MyIntTextField.kt create mode 100644 app/src/main/java/hu/bme/kszk/szobatarsch/composable/MyLabelledCheckBox.kt create mode 100644 app/src/main/java/hu/bme/kszk/szobatarsch/composable/MySlider.kt create mode 100644 app/src/main/java/hu/bme/kszk/szobatarsch/composable/MyStringTextField.kt create mode 100644 app/src/main/java/hu/bme/kszk/szobatarsch/composable/Profile.kt create mode 100644 app/src/main/java/hu/bme/kszk/szobatarsch/composable/ProfileState.kt create mode 100644 app/src/main/res/values-hu/strings.xml diff --git a/app/src/main/java/hu/bme/kszk/szobatarsch/composable/MyDropdownMenu.kt b/app/src/main/java/hu/bme/kszk/szobatarsch/composable/MyDropdownMenu.kt new file mode 100644 index 0000000..8d63cb3 --- /dev/null +++ b/app/src/main/java/hu/bme/kszk/szobatarsch/composable/MyDropdownMenu.kt @@ -0,0 +1,86 @@ +package hu.bme.kszk.szobatarsch.composable + +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.material3.* +import androidx.compose.runtime.* +import androidx.compose.ui.Modifier +import androidx.compose.ui.graphics.vector.ImageVector +import androidx.compose.ui.window.PopupProperties + +data class DropDownItem( + val title: String, + val image: ImageVector, +) + +@OptIn(ExperimentalMaterial3Api::class) +@Composable +fun MyDropdownMenu( + items: List<DropDownItem>, + label: String, + state: MutableState<Int>, + editable: MutableState<Boolean> +) { + var expanded by remember { mutableStateOf(false) } + + ExposedDropdownMenuBox( + modifier = Modifier.fillMaxWidth(), + expanded = expanded, + onExpandedChange = { + if (editable.value) { + expanded = !expanded + } + } + ) { + TextField( + modifier = Modifier.fillMaxWidth(), + readOnly = true, + enabled = editable.value, + value = items[state.value].title, + onValueChange = { }, + label = { Text(label) }, + trailingIcon = { + ExposedDropdownMenuDefaults.TrailingIcon( + expanded = expanded + ) + }, + //colors = ExposedDropdownMenuDefaults.textFieldColors(), + leadingIcon = { Icon(items[state.value].image, null) }, + textStyle = MaterialTheme.typography.bodyMedium + ) + + DropdownMenu( + modifier = Modifier.exposedDropdownSize(), + expanded = expanded, + onDismissRequest = { + expanded = false + }, + properties = PopupProperties( + focusable = true, + dismissOnClickOutside = true, + dismissOnBackPress = true + ), + ) { + items.forEachIndexed { index, item -> + DropdownMenuItem( + modifier = Modifier.fillMaxWidth(), + onClick = { + state.value = index + expanded = false + }, + text = { + Text( + text = item.title, + style = MaterialTheme.typography.bodyMedium + ) + }, + leadingIcon = { + Icon( + item.image, + contentDescription = null + ) + } + ) + } + } + } +} \ No newline at end of file diff --git a/app/src/main/java/hu/bme/kszk/szobatarsch/composable/MyIntTextField.kt b/app/src/main/java/hu/bme/kszk/szobatarsch/composable/MyIntTextField.kt new file mode 100644 index 0000000..9ef1772 --- /dev/null +++ b/app/src/main/java/hu/bme/kszk/szobatarsch/composable/MyIntTextField.kt @@ -0,0 +1,28 @@ +package hu.bme.kszk.szobatarsch.composable + +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.text.KeyboardOptions +import androidx.compose.material3.MaterialTheme +import androidx.compose.material3.Text +import androidx.compose.material3.TextField +import androidx.compose.runtime.Composable +import androidx.compose.runtime.MutableState +import androidx.compose.ui.Modifier +import androidx.compose.ui.text.input.KeyboardType + +@Composable +fun MyIntTextField( + state: MutableState<Int>, + editable: Boolean, + label: String +) { + TextField( + modifier = Modifier.fillMaxWidth(), + keyboardOptions = KeyboardOptions.Default.copy(keyboardType = KeyboardType.Number), + value = state.value.toString(), + onValueChange = { state.value = it.toInt() }, + enabled = editable, + label = { Text(text = label) }, + textStyle = MaterialTheme.typography.bodyMedium, + ) +} \ No newline at end of file diff --git a/app/src/main/java/hu/bme/kszk/szobatarsch/composable/MyLabelledCheckBox.kt b/app/src/main/java/hu/bme/kszk/szobatarsch/composable/MyLabelledCheckBox.kt new file mode 100644 index 0000000..67c88b7 --- /dev/null +++ b/app/src/main/java/hu/bme/kszk/szobatarsch/composable/MyLabelledCheckBox.kt @@ -0,0 +1,43 @@ +package hu.bme.kszk.szobatarsch.composable + +import androidx.compose.foundation.clickable +import androidx.compose.foundation.layout.Arrangement +import androidx.compose.foundation.layout.Row +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.padding +import androidx.compose.material3.Checkbox +import androidx.compose.material3.ExperimentalMaterial3Api +import androidx.compose.material3.MaterialTheme +import androidx.compose.material3.Text +import androidx.compose.runtime.Composable +import androidx.compose.runtime.MutableState +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.unit.dp + +@OptIn(ExperimentalMaterial3Api::class) +@Composable +fun MyLabelledCheckBox(state: MutableState<Boolean>, label: String, editable: Boolean) { + Row( + modifier = Modifier + .padding(8.dp) + .fillMaxWidth(), + verticalAlignment = Alignment.CenterVertically, + horizontalArrangement = Arrangement.Center + ) { + Checkbox( + checked = state.value, + onCheckedChange = { + state.value = it + }, + enabled = editable, + ) + + Text( + modifier = Modifier + .clickable { state.value = !state.value }, + text = label, + style = MaterialTheme.typography.bodyMedium + ) + } +} \ No newline at end of file diff --git a/app/src/main/java/hu/bme/kszk/szobatarsch/composable/MySlider.kt b/app/src/main/java/hu/bme/kszk/szobatarsch/composable/MySlider.kt new file mode 100644 index 0000000..147d8a7 --- /dev/null +++ b/app/src/main/java/hu/bme/kszk/szobatarsch/composable/MySlider.kt @@ -0,0 +1,33 @@ +package hu.bme.kszk.szobatarsch.composable + +import androidx.compose.material3.MaterialTheme +import androidx.compose.material3.Slider +import androidx.compose.material3.Text +import androidx.compose.runtime.Composable +import androidx.compose.runtime.MutableState +import androidx.compose.ui.text.style.TextAlign +import kotlin.math.roundToInt + +@Composable +fun MySlider( + state: MutableState<Float>, + editable: Boolean, + steps: Int, + valueRange: ClosedFloatingPointRange<Float>, + label: String, + textValues: List<String> +) { + Text( + text = label + ": " + textValues[state.value.roundToInt()], + style = MaterialTheme.typography.bodyMedium.copy(textAlign = TextAlign.Center) + ) + Slider( + value = state.value, + onValueChange = { + state.value = it + }, + enabled = editable, + steps = steps, + valueRange = valueRange, + ) +} \ No newline at end of file diff --git a/app/src/main/java/hu/bme/kszk/szobatarsch/composable/MyStringTextField.kt b/app/src/main/java/hu/bme/kszk/szobatarsch/composable/MyStringTextField.kt new file mode 100644 index 0000000..0cd6c35 --- /dev/null +++ b/app/src/main/java/hu/bme/kszk/szobatarsch/composable/MyStringTextField.kt @@ -0,0 +1,27 @@ +package hu.bme.kszk.szobatarsch.composable + +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.material3.MaterialTheme +import androidx.compose.material3.Text +import androidx.compose.material3.TextField +import androidx.compose.runtime.Composable +import androidx.compose.runtime.MutableState +import androidx.compose.ui.Modifier + +@Composable +fun MyStringTextField( + state: MutableState<String>, + editable: Boolean, + label: String, + singleLine: Boolean +) { + TextField( + modifier = Modifier.fillMaxWidth(), + value = state.value, + onValueChange = { state.value = it }, + enabled = editable, + singleLine = singleLine, + label = { Text(text = label) }, + textStyle = MaterialTheme.typography.bodyMedium, + ) +} \ No newline at end of file diff --git a/app/src/main/java/hu/bme/kszk/szobatarsch/composable/Profile.kt b/app/src/main/java/hu/bme/kszk/szobatarsch/composable/Profile.kt new file mode 100644 index 0000000..1fbffc9 --- /dev/null +++ b/app/src/main/java/hu/bme/kszk/szobatarsch/composable/Profile.kt @@ -0,0 +1,280 @@ +package hu.bme.kszk.szobatarsch.composable + +import androidx.compose.foundation.layout.* +import androidx.compose.foundation.rememberScrollState +import androidx.compose.foundation.verticalScroll +import androidx.compose.material.icons.Icons +import androidx.compose.material.icons.filled.* +import androidx.compose.material3.Button +import androidx.compose.material3.Text +import androidx.compose.runtime.* +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.unit.dp +import hu.bme.kszk.szobatarsch.firebase.Preferences +import hu.bme.kszk.szobatarsch.firebase.User +import hu.bme.kszk.szobatarsch.firebase.userData as currentUser +import hu.bme.kszk.szobatarsch.firebase.registerOrOverwriteUser +import hu.bme.kszk.szobatarsch.firebaseUser +import kotlinx.coroutines.launch + + +@Composable +fun Profile(isEditable: Boolean = false) { + val user = remember { mutableStateOf(User()) } + val profileState = rememberProfileState() + + val editable = remember { mutableStateOf(isEditable) } + + LaunchedEffect(Unit) { + currentUser.collect { newUser -> + if (newUser != user.value) { + user.value = newUser + + firebaseUserDataToProfileState(profileState, user) + } + } + } + + val coroutineScope = rememberCoroutineScope() + + Column( + Modifier + .fillMaxSize() + .verticalScroll(rememberScrollState()) + .padding(16.dp), + verticalArrangement = Arrangement.spacedBy(16.dp), + horizontalAlignment = Alignment.CenterHorizontally, + ) { + MyStringTextField(profileState.name, editable.value, "Név", true) + MyStringTextField(profileState.description, editable.value, "Leírás", false) + MyIntTextField(profileState.age, editable.value, "Életkor") + + MyDropdownMenu( + items = GenderDropDownItems.DropDownItems, + "Nem", + state = profileState.gender, + editable = editable + ) + + MyDropdownMenu( + items = MajorDropDownItems.DropDownItems, + "Szak", + state = profileState.major, + editable = editable + ) + + MyIntTextField(profileState.semester, editable.value, "Félév") + MyStringTextField(profileState.contacts, editable.value, "Elérhetőség", false) + + MySlider( + state = profileState.bedTimeSliderPosition, + editable = editable.value, + steps = 8, + valueRange = 0f..9f, + label = "Lefekszem kb", + textValues = listOf( + "20 órakor", + "21 órakor", + "22 órakor", + "23 órakor", + "00 órakor", + "01 órakor", + "02 órakor", + "03 órakor", + "04 órakor", + "Később" + ) + ) + + MySlider( + state = profileState.wakeTimeSliderPosition, + editable = editable.value, + steps = 8, + valueRange = 0f..9f, + label = "Felkelek kb", + textValues = listOf( + "5 órakor", + "6 órakor", + "7 órakor", + "8 órakor", + "9 órakor", + "10 órakor", + "11 órakor", + "12 órakor", + "13 órakor", + "Később" + ) + ) + + MySlider( + state = profileState.cleaningSliderPosition, + editable = editable.value, + steps = 3, + valueRange = 0f..4f, + label = "Takarítás gyakorisága", + textValues = listOf( + "Ritkán", + "Néha", + "So-so", + "Gyakran", + "Nagyon gyakran" + ) + ) + + MySlider( + state = profileState.loudnessSliderPosition, + editable = editable.value, + steps = 3, + valueRange = 0f..4f, + label = "Átlagos hangerő", + textValues = listOf( + "Maga vagyok a csend", + "Néha hangos vagyok", + "Az esetek felében hangos vagyok", + "Gyakran vagyok hangos", + "Csak ordibálva beszélek és/vagy hangosan hallgatok zenét" + ) + ) + + MySlider( + state = profileState.storageSliderPosition, + editable = editable.value, + steps = 3, + valueRange = 0f..4f, + label = "Ennyi cuccom van", + textValues = listOf( + "Nincs", + "Egy bőrönd", + "Átlagos", + "Kicsit sok cuccom van", + "Még legalább egy családnak elég lenne", + ) + ) + + MyLabelledCheckBox( + profileState.morningShower, + "Reggel szoktam zuhanyozni", + editable = editable.value + ) + MyLabelledCheckBox( + profileState.eveningShower, + "Este szoktam zuhanyozni", + editable = editable.value + ) + MyLabelledCheckBox( + profileState.frequentNightTimeGuest, + "Gyakran fogadok éjszakai vendéget", + editable = editable.value + ) + MyLabelledCheckBox(profileState.ghost, "Ghost akarok lenni", editable = editable.value) + + if (!editable.value) { + Button(onClick = { editable.value = true }) { + Text(text = "Szerkesztés") + } + } else { + Row(modifier = Modifier.fillMaxWidth(), + horizontalArrangement = Arrangement.SpaceAround) { + Button(onClick = { + editable.value = false + firebaseUserDataToProfileState(profileState, user) + }) { + Text(text = "Mégse") + } + Button(onClick = { + editable.value = false + + coroutineScope.launch { + registerOrOverwriteUser( + User( + id = user.value.id, + name = profileState.name.value, + description = profileState.description.value, + preferences = Preferences( + gender = profileState.gender.value, + age = profileState.age.value, + major = profileState.major.value, + semester = profileState.semester.value, + bedTime = profileState.bedTimeSliderPosition.value.toInt(), + wakeUpTime = profileState.wakeTimeSliderPosition.value.toInt(), + morningShower = profileState.morningShower.value, + eveningShower = profileState.eveningShower.value, + frequentNightTimeGuest = profileState.frequentNightTimeGuest.value, + frequencyOfCleaning = profileState.cleaningSliderPosition.value.toInt(), + loudness = profileState.loudnessSliderPosition.value.toInt(), + storageSpace = profileState.storageSliderPosition.value.toInt(), + ghost = profileState.ghost.value + ), + contacts = profileState.contacts.value, + dismissedUsers = user.value.dismissedUsers, + likedUsers = user.value.likedUsers + ) + ) + } + + }) { + Text(text = "Mentés") + } + } + } + } +} + +private fun firebaseUserDataToProfileState( + profileState: ProfileState, + user: MutableState<User> +) { + profileState.name.value = user.value.name + profileState.age.value = user.value.preferences.age + profileState.gender.value = user.value.preferences.gender + profileState.major.value = user.value.preferences.major + profileState.semester.value = user.value.preferences.semester + profileState.description.value = user.value.description + profileState.contacts.value = user.value.contacts + profileState.bedTimeSliderPosition.value = user.value.preferences.bedTime.toFloat() + profileState.wakeTimeSliderPosition.value = + user.value.preferences.wakeUpTime.toFloat() + profileState.cleaningSliderPosition.value = + user.value.preferences.frequencyOfCleaning.toFloat() + profileState.loudnessSliderPosition.value = + user.value.preferences.loudness.toFloat() + profileState.storageSliderPosition.value = + user.value.preferences.storageSpace.toFloat() + profileState.morningShower.value = user.value.preferences.morningShower + profileState.eveningShower.value = user.value.preferences.eveningShower + profileState.frequentNightTimeGuest.value = + user.value.preferences.frequentNightTimeGuest + profileState.ghost.value = user.value.preferences.ghost +} + +object GenderDropDownItems { + val DropDownItems = listOf( + DropDownItem( + title = "Male", + image = Icons.Default.Male, + ), + DropDownItem( + title = "Female", + image = Icons.Default.Female, + ), + ) +} + + +object MajorDropDownItems { + val DropDownItems = listOf( + DropDownItem( + title = "Mérnökinformatikus", + image = Icons.Default.Computer, + ), + DropDownItem( + title = "Villamos mérnök", + image = Icons.Default.Bolt, + ), + DropDownItem( + title = "Épitész", + image = Icons.Default.Architecture, + ), + ) +} \ No newline at end of file diff --git a/app/src/main/java/hu/bme/kszk/szobatarsch/composable/ProfileState.kt b/app/src/main/java/hu/bme/kszk/szobatarsch/composable/ProfileState.kt new file mode 100644 index 0000000..cfb213a --- /dev/null +++ b/app/src/main/java/hu/bme/kszk/szobatarsch/composable/ProfileState.kt @@ -0,0 +1,45 @@ +package hu.bme.kszk.szobatarsch.composable + +import androidx.compose.runtime.Composable +import androidx.compose.runtime.MutableState +import androidx.compose.runtime.mutableStateOf +import androidx.compose.runtime.remember + +data class ProfileState( + val name: MutableState<String>, + val age: MutableState<Int>, + val gender: MutableState<Int>, + val major: MutableState<Int>, + val semester: MutableState<Int>, + val description: MutableState<String>, + val contacts: MutableState<String>, + val bedTimeSliderPosition: MutableState<Float>, + val wakeTimeSliderPosition: MutableState<Float>, + val cleaningSliderPosition: MutableState<Float>, + val loudnessSliderPosition: MutableState<Float>, + val storageSliderPosition: MutableState<Float>, + val morningShower: MutableState<Boolean>, + val eveningShower: MutableState<Boolean>, + val frequentNightTimeGuest: MutableState<Boolean>, + val ghost: MutableState<Boolean>, +) + +@Composable +fun rememberProfileState() = ProfileState( + name = remember { mutableStateOf("") }, + age = remember { mutableStateOf(18) }, + gender = remember { mutableStateOf(0) }, + major = remember { mutableStateOf(0) }, + semester = remember { mutableStateOf(1) }, + description = remember { mutableStateOf("") }, + contacts = remember { mutableStateOf("") }, + bedTimeSliderPosition = remember { mutableStateOf(0f) }, + wakeTimeSliderPosition = remember { mutableStateOf(0f) }, + cleaningSliderPosition = remember { mutableStateOf(0f) }, + loudnessSliderPosition = remember { mutableStateOf(0f) }, + storageSliderPosition = remember { mutableStateOf(0f) }, + morningShower = remember { mutableStateOf(false) }, + eveningShower = remember { mutableStateOf(true) }, + frequentNightTimeGuest = remember { mutableStateOf(false) }, + ghost = remember { mutableStateOf(false) }, +) \ No newline at end of file diff --git a/app/src/main/res/values-hu/strings.xml b/app/src/main/res/values-hu/strings.xml new file mode 100644 index 0000000..9e8d764 --- /dev/null +++ b/app/src/main/res/values-hu/strings.xml @@ -0,0 +1,5 @@ +<?xml version="1.0" encoding="utf-8"?> +<resources> + <string name="app_name">SzobatárSCH</string> + <string name="log_out">Kijelentkezés</string> +</resources> \ No newline at end of file diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 086b68f..50499c3 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -1,3 +1,4 @@ <resources> - <string name="app_name">SzobatarSCH</string> + <string name="app_name">RoommateSCH</string> + <string name="log_out">Sign Out</string> </resources> \ No newline at end of file -- GitLab