package com.vgmlr.kiln
import androidx.compose.foundation.background
import androidx.compose.foundation.border
import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.*
import androidx.compose.foundation.rememberScrollState
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.foundation.verticalScroll
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.automirrored.filled.ArrowBack
import androidx.compose.material.icons.filled.Add
import androidx.compose.material.icons.filled.KeyboardArrowDown
import androidx.compose.material.icons.filled.KeyboardArrowUp
import androidx.compose.material.icons.outlined.Clear
import androidx.compose.material.icons.outlined.Face
import androidx.compose.material.icons.outlined.Healing
import androidx.compose.material.icons.outlined.ManageAccounts
import androidx.compose.material.icons.outlined.Notifications
import androidx.compose.material3.*
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.draw.clip
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.graphics.vector.ImageVector
import androidx.compose.ui.text.TextStyle
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp
import androidx.compose.foundation.text.BasicTextField
import androidx.compose.material.icons.filled.WaterDrop
import androidx.compose.material.icons.outlined.WaterDrop
import androidx.compose.runtime.key
import androidx.compose.ui.graphics.SolidColor
import androidx.compose.material.icons.outlined.WarningAmber
import androidx.compose.material.icons.outlined.Block
private val PurgeRed = Color(0xFF8B0000)
private val BOOL_OPTIONS = listOf("False", "True")
private val ADD_ICON = 20.dp
private val MOVE_ICON = 23.dp
private val EDIT_ICON = 18.dp
private val ICON_GAP = 20.dp
private val ICON_END = (ADD_ICON - EDIT_ICON) / 2
private const val ICON_FADE = 0.4f
private const val SYMPTOM_HINT = "Symptom"
@Composable
fun KilnAccountSettings(vm: KilnViewModel, onBack: () -> Unit, onDeleted: () -> Unit) {
val account by vm.activeKilnAccount.collectAsState()
val buttons by vm.buttons.collectAsState()
val hair = KilnHair.of(account?.hair)
var confirming by remember(account?.id) { mutableStateOf(false) }
var pmsMsg by remember(account?.id) { mutableStateOf(account?.pmsMessage.orEmpty()) }
var mensMsg by remember(account?.id) {
mutableStateOf(account?.menstruationMessage.orEmpty())
}
var name by remember(account?.id) { mutableStateOf(account?.name.orEmpty()) }
val topInset = WindowInsets.statusBars.asPaddingValues().calculateTopPadding()
Box(Modifier.fillMaxSize().background(CBg)) {
Column(
Modifier.fillMaxSize()
.imePadding()
.navigationBarsPadding()
.verticalScroll(rememberScrollState())
.padding(horizontal = BAR_INSET_H)
) {
Spacer(Modifier.height(topInset + TOP_BAR_H + 12.dp))
WgSection("Profile", Icons.Outlined.Face) {
WgRow("Account Name", edit = true) {
WgTextField(name, placeholder = "name") {
name = it
vm.setName(it)
}
}
WgDivider()
WgRow("Hair Color") {
WgImageDropdown(
value = KilnFaces.happy(hair),
options = KilnHair.entries.map { KilnFaces.happy(it) }
) { i -> vm.setHair(KilnHair.entries[i]) }
}
}
WgSection("Notifications", Icons.Outlined.Notifications) {
WgRow("PMS Alert") {
BoolRadio(account?.pmsAlert == true) { vm.setPmsAlert(it) }
}
WgDivider()
WgRow("Message", edit = true) {
WgTextField(pmsMsg, placeholder = "none") {
pmsMsg = it
vm.setPmsMessage(it)
}
}
WgDivider()
WgRow("Menstruation Alert") {
BoolRadio(account?.menstruationAlert == true) { vm.setMenstruationAlert(it) }
}
WgDivider()
WgRow("Message", edit = true) {
WgTextField(mensMsg, placeholder = "none") {
mensMsg = it
vm.setMenstruationMessage(it)
}
}
WgDivider()
WgRow("Menstruation Reminder") {
BoolRadio(
value = account?.menstruationReminder == true,
enabled = account?.menstruationAlert == true
) { vm.setMenstruationReminder(it) }
}
}
WgSection("Symptoms", Icons.Outlined.Healing) {
buttons.forEachIndexed { i, b ->
if (i > 0) WgDivider()
key(b.id) {
SymptomRow(b, i > 0, i < buttons.lastIndex, vm)
}
}
if (buttons.isNotEmpty()) WgDivider()
WgRow("New Symptom", bg = CChip) {
IconButton(
{ vm.addButton("", KilnButtonKind.NOT_PERIOD) },
Modifier.size(ADD_ICON)
) {
Icon(Icons.Default.Add, "New Symptom", Modifier.size(ADD_ICON), tint = CText)
}
}
}
WgSection("Account", Icons.Outlined.ManageAccounts) {
WgRow("Terminate Account") {
Box(
Modifier
.height(28.dp)
.clip(RoundedCornerShape(6.dp))
.background(PurgeRed)
.border(1.dp, CMuted.copy(alpha = 0.30f), RoundedCornerShape(6.dp))
.clickable {
if (confirming) {
vm.deleteActiveAccount()
onDeleted()
} else confirming = true
}
.padding(horizontal = 16.dp),
contentAlignment = Alignment.Center
) {
if (confirming) {
Row(verticalAlignment = Alignment.CenterVertically) {
Icon(Icons.Outlined.WarningAmber, null, Modifier.size(16.dp), tint = CText)
Spacer(Modifier.width(4.dp))
Text("Confirm", color = CText, fontSize = 14.sp)
}
} else {
Text("Abort", color = CText, fontSize = 14.sp)
}
}
}
}
Spacer(Modifier.height(8.dp))
}
Row(
Modifier.fillMaxWidth()
.padding(top = topInset)
.height(TOP_BAR_H)
.padding(horizontal = BAR_INSET_H),
horizontalArrangement = Arrangement.spacedBy(BAR_GAP),
verticalAlignment = Alignment.CenterVertically
) {
KilnBarButton(Icons.AutoMirrored.Filled.ArrowBack, "Back", onClick = onBack)
KilnBlankButton(Modifier.weight(1f), "Settings", Alignment.CenterStart)
}
}
}
@Composable
private fun SymptomRow(
button: KilnButton,
canUp: Boolean,
canDown: Boolean,
vm: KilnViewModel
) {
var text by remember { mutableStateOf(button.label) }
val kind = button.kindEnum
val period = kind == KilnButtonKind.PERIOD
Row(
Modifier.fillMaxWidth()
.heightIn(min = ROW_H)
.padding(horizontal = 16.dp, vertical = 8.dp),
verticalAlignment = Alignment.CenterVertically
) {
BasicTextField(
value = text,
onValueChange = {
text = it
vm.updateButton(button, it, button.kindEnum)
},
singleLine = true,
modifier = Modifier.weight(1f),
textStyle = TextStyle(color = CText.copy(alpha = 0.95f), fontSize = FS),
cursorBrush = SolidColor(CText),
decorationBox = { inner ->
Box(contentAlignment = Alignment.CenterStart) {
if (text.isEmpty()) {
Text(SYMPTOM_HINT, color = CMuted, fontSize = FS, maxLines = 1)
}
inner()
}
}
)
Spacer(Modifier.width(12.dp))
Row(
modifier = Modifier.padding(end = ICON_END),
horizontalArrangement = Arrangement.spacedBy(ICON_GAP),
verticalAlignment = Alignment.CenterVertically
) {
MoveButton(Icons.Default.KeyboardArrowUp, "Move Up", canUp) {
vm.moveButton(button, -1)
}
MoveButton(Icons.Default.KeyboardArrowDown, "Move Down", canDown) {
vm.moveButton(button, 1)
}
IconButton({ vm.updateButton(button, text, kind.next()) }, Modifier.size(EDIT_ICON)) {
Icon(
when (kind) {
KilnButtonKind.PERIOD -> Icons.Filled.WaterDrop
KilnButtonKind.NOT_PERIOD -> Icons.Outlined.WaterDrop
KilnButtonKind.OTHER -> Icons.Outlined.Block
},
kind.label,
Modifier.size(EDIT_ICON),
tint = if (period) PurgeRed else CText.copy(alpha = ICON_FADE)
)
}
IconButton({ vm.deleteButton(button) }, Modifier.size(EDIT_ICON)) {
Icon(Icons.Outlined.Clear, "Delete", Modifier.size(EDIT_ICON), tint = CText)
}
}
}
}
@Composable
private fun MoveButton(icon: ImageVector, desc: String, enabled: Boolean, onClick: () -> Unit) {
IconButton(onClick, Modifier.size(MOVE_ICON), enabled) {
Icon(
icon, desc, Modifier.size(MOVE_ICON),
tint = CText.copy(alpha = if (enabled) ICON_FADE else 0f)
)
}
}
@Composable
private fun BoolRadio(value: Boolean, enabled: Boolean = true, onSet: (Boolean) -> Unit) {
WgRadio(
value = if (value) "True" else "False",
options = BOOL_OPTIONS,
disabledOptions = if (enabled) emptyList() else BOOL_OPTIONS
) { onSet(it == "True") }
}