232 lines
9.5 KiB
Kotlin
232 lines
9.5 KiB
Kotlin
package app.closer.ui.settings
|
|
|
|
import androidx.compose.foundation.background
|
|
import androidx.compose.foundation.clickable
|
|
import androidx.compose.foundation.layout.Arrangement
|
|
import androidx.compose.foundation.layout.Column
|
|
import androidx.compose.foundation.layout.Row
|
|
import androidx.compose.foundation.layout.Spacer
|
|
import androidx.compose.foundation.layout.fillMaxSize
|
|
import androidx.compose.foundation.layout.fillMaxWidth
|
|
import androidx.compose.foundation.layout.height
|
|
import androidx.compose.foundation.layout.navigationBarsPadding
|
|
import androidx.compose.foundation.layout.padding
|
|
import androidx.compose.foundation.layout.safeDrawingPadding
|
|
import androidx.compose.foundation.layout.size
|
|
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.automirrored.filled.ArrowForwardIos
|
|
import androidx.compose.material.icons.filled.ContentCopy
|
|
import androidx.compose.material.icons.filled.Delete
|
|
import androidx.compose.material.icons.filled.Key
|
|
import androidx.compose.material3.Card
|
|
import androidx.compose.material3.CardDefaults
|
|
import androidx.compose.material3.ExperimentalMaterial3Api
|
|
import androidx.compose.material3.Icon
|
|
import androidx.compose.material3.IconButton
|
|
import androidx.compose.material3.MaterialTheme
|
|
import androidx.compose.material3.Scaffold
|
|
import androidx.compose.material3.SnackbarHost
|
|
import androidx.compose.material3.SnackbarHostState
|
|
import androidx.compose.material3.Text
|
|
import androidx.compose.material3.TopAppBar
|
|
import androidx.compose.material3.TopAppBarDefaults
|
|
import androidx.compose.runtime.Composable
|
|
import androidx.compose.runtime.collectAsState
|
|
import androidx.compose.runtime.getValue
|
|
import androidx.compose.runtime.remember
|
|
import androidx.compose.runtime.rememberCoroutineScope
|
|
import androidx.compose.ui.Alignment
|
|
import androidx.compose.ui.Modifier
|
|
import androidx.compose.ui.graphics.Color
|
|
import androidx.compose.ui.graphics.vector.ImageVector
|
|
import androidx.compose.ui.platform.LocalClipboardManager
|
|
import androidx.compose.ui.res.stringResource
|
|
import androidx.compose.ui.text.AnnotatedString
|
|
import androidx.compose.ui.text.font.FontFamily
|
|
import androidx.compose.ui.text.font.FontWeight
|
|
import androidx.compose.ui.text.style.TextOverflow
|
|
import androidx.compose.ui.unit.dp
|
|
import androidx.hilt.navigation.compose.hiltViewModel
|
|
import app.closer.R
|
|
import app.closer.core.navigation.AppRoute
|
|
import kotlinx.coroutines.launch
|
|
|
|
@OptIn(ExperimentalMaterial3Api::class)
|
|
@Composable
|
|
fun AccountScreen(
|
|
onNavigate: (String) -> Unit = {},
|
|
viewModel: EditProfileViewModel = hiltViewModel()
|
|
) {
|
|
val state by viewModel.uiState.collectAsState()
|
|
val snackbar = remember { SnackbarHostState() }
|
|
val scope = rememberCoroutineScope()
|
|
val clipboard = LocalClipboardManager.current
|
|
val phrasecopiedMsg = stringResource(R.string.account_recovery_phrase_copied)
|
|
|
|
Scaffold(
|
|
snackbarHost = { SnackbarHost(snackbar) },
|
|
containerColor = Color.Transparent,
|
|
modifier = Modifier.background(SettingsBackgroundBrush),
|
|
topBar = {
|
|
TopAppBar(
|
|
title = { Text(stringResource(R.string.account_title), color = SettingsInk) },
|
|
navigationIcon = {
|
|
IconButton(onClick = { onNavigate("back") }) {
|
|
Icon(
|
|
Icons.AutoMirrored.Filled.ArrowBack,
|
|
contentDescription = stringResource(R.string.action_back),
|
|
tint = SettingsInk
|
|
)
|
|
}
|
|
},
|
|
colors = TopAppBarDefaults.topAppBarColors(containerColor = Color.Transparent)
|
|
)
|
|
}
|
|
) { padding ->
|
|
Column(
|
|
modifier = Modifier
|
|
.fillMaxSize()
|
|
.safeDrawingPadding()
|
|
.navigationBarsPadding()
|
|
.verticalScroll(rememberScrollState())
|
|
.padding(padding)
|
|
.padding(horizontal = 16.dp, vertical = 8.dp),
|
|
verticalArrangement = Arrangement.spacedBy(12.dp)
|
|
) {
|
|
EditProfileContent(
|
|
modifier = Modifier.fillMaxWidth(),
|
|
snackbar = snackbar,
|
|
viewModel = viewModel
|
|
)
|
|
|
|
Spacer(Modifier.height(8.dp))
|
|
|
|
// Recovery phrase — only shown when the couple key is stored locally
|
|
val phrase = state.recoveryPhrase
|
|
if (phrase != null) {
|
|
Card(
|
|
modifier = Modifier.fillMaxWidth(),
|
|
shape = RoundedCornerShape(16.dp),
|
|
colors = CardDefaults.cardColors(containerColor = SettingsCard)
|
|
) {
|
|
Column(
|
|
modifier = Modifier.padding(horizontal = 16.dp, vertical = 14.dp),
|
|
verticalArrangement = Arrangement.spacedBy(8.dp)
|
|
) {
|
|
Row(
|
|
verticalAlignment = Alignment.CenterVertically,
|
|
horizontalArrangement = Arrangement.spacedBy(10.dp)
|
|
) {
|
|
Icon(
|
|
Icons.Filled.Key,
|
|
contentDescription = null,
|
|
tint = SettingsMuted,
|
|
modifier = Modifier.size(20.dp)
|
|
)
|
|
Text(
|
|
text = stringResource(R.string.account_recovery_phrase_title),
|
|
style = MaterialTheme.typography.bodyLarge,
|
|
color = SettingsInk,
|
|
fontWeight = FontWeight.Medium,
|
|
modifier = Modifier.weight(1f)
|
|
)
|
|
IconButton(
|
|
onClick = {
|
|
clipboard.setText(AnnotatedString(phrase))
|
|
scope.launch { snackbar.showSnackbar(phrasecopiedMsg) }
|
|
},
|
|
modifier = Modifier.size(36.dp)
|
|
) {
|
|
Icon(
|
|
Icons.Filled.ContentCopy,
|
|
contentDescription = stringResource(R.string.account_recovery_phrase_copy_desc),
|
|
tint = SettingsMuted,
|
|
modifier = Modifier.size(18.dp)
|
|
)
|
|
}
|
|
}
|
|
Text(
|
|
text = phrase,
|
|
style = MaterialTheme.typography.bodyMedium.copy(fontFamily = FontFamily.Monospace),
|
|
color = SettingsInk,
|
|
modifier = Modifier
|
|
.fillMaxWidth()
|
|
.background(
|
|
color = SettingsSoft,
|
|
shape = RoundedCornerShape(8.dp)
|
|
)
|
|
.padding(horizontal = 12.dp, vertical = 10.dp)
|
|
)
|
|
Text(
|
|
text = stringResource(R.string.account_recovery_phrase_footer),
|
|
style = MaterialTheme.typography.bodySmall,
|
|
color = SettingsMuted
|
|
)
|
|
}
|
|
}
|
|
}
|
|
|
|
// Account lifecycle
|
|
Card(
|
|
modifier = Modifier.fillMaxWidth(),
|
|
shape = RoundedCornerShape(16.dp),
|
|
colors = CardDefaults.cardColors(containerColor = SettingsCard)
|
|
) {
|
|
AccountRow(
|
|
icon = Icons.Filled.Delete,
|
|
label = stringResource(R.string.action_delete_account),
|
|
tint = SettingsDanger,
|
|
onClick = { onNavigate(AppRoute.DELETE_ACCOUNT) }
|
|
)
|
|
}
|
|
|
|
Spacer(Modifier.height(8.dp))
|
|
}
|
|
}
|
|
}
|
|
|
|
@Composable
|
|
private fun AccountRow(
|
|
icon: ImageVector,
|
|
label: String,
|
|
onClick: () -> Unit,
|
|
modifier: Modifier = Modifier,
|
|
enabled: Boolean = true,
|
|
tint: Color = SettingsMuted
|
|
) {
|
|
Row(
|
|
modifier = modifier
|
|
.fillMaxWidth()
|
|
.clickable(enabled = enabled, onClick = onClick)
|
|
.padding(horizontal = 16.dp, vertical = 14.dp),
|
|
verticalAlignment = Alignment.CenterVertically,
|
|
horizontalArrangement = Arrangement.spacedBy(12.dp)
|
|
) {
|
|
Icon(icon, contentDescription = null, tint = if (enabled) tint else SettingsMuted.copy(alpha = 0.5f))
|
|
Text(
|
|
text = label,
|
|
style = MaterialTheme.typography.bodyLarge,
|
|
color = when {
|
|
!enabled -> SettingsMuted.copy(alpha = 0.5f)
|
|
tint == SettingsMuted -> SettingsInk
|
|
else -> tint
|
|
},
|
|
modifier = Modifier.weight(1f),
|
|
maxLines = 1,
|
|
overflow = TextOverflow.Ellipsis
|
|
)
|
|
if (enabled) {
|
|
Icon(
|
|
Icons.AutoMirrored.Filled.ArrowForwardIos,
|
|
contentDescription = null,
|
|
modifier = Modifier.size(14.dp),
|
|
tint = SettingsMuted
|
|
)
|
|
}
|
|
}
|
|
}
|