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