contact details ui
This commit is contained in:
@@ -22,5 +22,21 @@
|
||||
}
|
||||
],
|
||||
"elementType": "File",
|
||||
"baselineProfiles": [
|
||||
{
|
||||
"minApi": 28,
|
||||
"maxApi": 30,
|
||||
"baselineProfiles": [
|
||||
"baselineProfiles/1/app-arm64-v8a-release.dm"
|
||||
]
|
||||
},
|
||||
{
|
||||
"minApi": 31,
|
||||
"maxApi": 2147483647,
|
||||
"baselineProfiles": [
|
||||
"baselineProfiles/0/app-arm64-v8a-release.dm"
|
||||
]
|
||||
}
|
||||
],
|
||||
"minSdkVersionForDexing": 26
|
||||
}
|
||||
@@ -0,0 +1,57 @@
|
||||
package com.bitcointxoko.gudariwallet.ui.common
|
||||
|
||||
import androidx.compose.foundation.Canvas
|
||||
import androidx.compose.foundation.layout.Box
|
||||
import androidx.compose.foundation.layout.fillMaxSize
|
||||
import androidx.compose.foundation.layout.size
|
||||
import androidx.compose.foundation.shape.CircleShape
|
||||
import androidx.compose.material3.MaterialTheme
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.remember
|
||||
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.SolidColor
|
||||
import androidx.compose.ui.text.font.FontWeight
|
||||
import androidx.compose.ui.unit.Dp
|
||||
import androidx.compose.ui.unit.dp
|
||||
import kotlin.math.abs
|
||||
|
||||
// ── Contact avatar ───────────────────────────────────────────
|
||||
|
||||
private fun String.toHslColor(
|
||||
saturation: Float = 0.5f,
|
||||
lightness: Float = 0.4f
|
||||
): Color {
|
||||
val hue = fold(0) { acc, char -> char.code + acc * 37 } % 360
|
||||
return Color.hsl(abs(hue).toFloat(), saturation, lightness)
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun ContactAvatar(
|
||||
name: String,
|
||||
modifier: Modifier = Modifier,
|
||||
size: Dp = 40.dp
|
||||
) {
|
||||
val bgColor = remember(name) {
|
||||
name.toHslColor()
|
||||
}
|
||||
val initial = name.firstOrNull()?.uppercaseChar()?.toString() ?: "?"
|
||||
Box(
|
||||
modifier = modifier
|
||||
.size(size)
|
||||
.clip(CircleShape),
|
||||
contentAlignment = Alignment.Center
|
||||
) {
|
||||
Canvas(modifier = Modifier.fillMaxSize()) {
|
||||
drawCircle(SolidColor(bgColor))
|
||||
}
|
||||
Text(
|
||||
text = initial,
|
||||
color = Color.White,
|
||||
style = MaterialTheme.typography.titleMedium.copy(fontWeight = FontWeight.Bold)
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -20,6 +20,7 @@ import androidx.compose.material.icons.automirrored.filled.ArrowBack
|
||||
import androidx.compose.material.icons.filled.ArrowDownward
|
||||
import androidx.compose.material.icons.filled.ArrowUpward
|
||||
import androidx.compose.material.icons.filled.Close
|
||||
import androidx.compose.material.icons.filled.Edit
|
||||
import androidx.compose.material.icons.filled.PersonAdd
|
||||
import androidx.compose.material3.Card
|
||||
import androidx.compose.material3.CircularProgressIndicator
|
||||
@@ -55,6 +56,7 @@ import com.bitcointxoko.gudariwallet.api.PaymentRecord
|
||||
import com.bitcointxoko.gudariwallet.data.db.PaymentAddressType
|
||||
import com.bitcointxoko.gudariwallet.ui.DetailState
|
||||
import com.bitcointxoko.gudariwallet.ui.common.AmountWithFiatColumn
|
||||
import com.bitcointxoko.gudariwallet.ui.common.ContactAvatar
|
||||
import com.bitcointxoko.gudariwallet.ui.common.CopyIconButton
|
||||
import com.bitcointxoko.gudariwallet.ui.common.DetailRow
|
||||
import com.bitcointxoko.gudariwallet.ui.common.DetailSection
|
||||
@@ -303,14 +305,11 @@ private fun PaymentDetailContent(
|
||||
|
||||
// ── Contact section ────────────────────────────────────────────────────
|
||||
item {
|
||||
DetailSection(title = "Contact") {
|
||||
if (linkedContacts.isEmpty()) {
|
||||
// No contact linked — show assign button
|
||||
// No contacts — just show an assign button
|
||||
OutlinedButton(
|
||||
onClick = onAssignContact,
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.padding(horizontal = 16.dp, vertical = 4.dp)
|
||||
modifier = Modifier.fillMaxWidth()
|
||||
) {
|
||||
Icon(
|
||||
imageVector = Icons.Default.PersonAdd,
|
||||
@@ -321,19 +320,58 @@ private fun PaymentDetailContent(
|
||||
Text("Assign contact")
|
||||
}
|
||||
} else {
|
||||
// Show each linked contact with a remove button
|
||||
linkedContacts.forEach { contact ->
|
||||
// Show contacts with avatar + name, plus edit button
|
||||
Card(modifier = Modifier.fillMaxWidth()) {
|
||||
Column(modifier = Modifier.padding(16.dp)) {
|
||||
// Header row: title + edit button
|
||||
Row(
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
horizontalArrangement = Arrangement.SpaceBetween,
|
||||
verticalAlignment = Alignment.CenterVertically
|
||||
) {
|
||||
Text(
|
||||
text = "Contacts",
|
||||
style = MaterialTheme.typography.titleSmall,
|
||||
color = MaterialTheme.colorScheme.onSurfaceVariant
|
||||
)
|
||||
IconButton(
|
||||
onClick = onAssignContact,
|
||||
modifier = Modifier.size(32.dp)
|
||||
) {
|
||||
Icon(
|
||||
imageVector = Icons.Default.Edit,
|
||||
contentDescription = "Edit contacts",
|
||||
modifier = Modifier.size(18.dp),
|
||||
tint = MaterialTheme.colorScheme.primary
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
Spacer(Modifier.height(8.dp))
|
||||
|
||||
// Each linked contact: avatar + name + remove
|
||||
linkedContacts.forEachIndexed { index, contact ->
|
||||
val name = contact.localAlias
|
||||
?: contact.displayName
|
||||
?: contact.name
|
||||
?: "Unknown"
|
||||
DetailRow(
|
||||
label = "Contact",
|
||||
value = name,
|
||||
trailingContent = {
|
||||
|
||||
Row(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.padding(vertical = 4.dp),
|
||||
verticalAlignment = Alignment.CenterVertically
|
||||
) {
|
||||
ContactAvatar(name = name)
|
||||
Spacer(Modifier.width(12.dp))
|
||||
Text(
|
||||
text = name,
|
||||
style = MaterialTheme.typography.bodyLarge,
|
||||
modifier = Modifier.weight(1f)
|
||||
)
|
||||
IconButton(
|
||||
onClick = { onUnassignContact(contact.id) },
|
||||
modifier = Modifier.size(32.dp)
|
||||
modifier = Modifier.size(28.dp)
|
||||
) {
|
||||
Icon(
|
||||
imageVector = Icons.Default.Close,
|
||||
@@ -343,35 +381,19 @@ private fun PaymentDetailContent(
|
||||
)
|
||||
}
|
||||
}
|
||||
)
|
||||
}
|
||||
// Allow assigning additional contacts
|
||||
|
||||
if (index < linkedContacts.lastIndex) {
|
||||
HorizontalDivider(
|
||||
modifier = Modifier.padding(horizontal = 16.dp),
|
||||
thickness = 0.5.dp
|
||||
)
|
||||
Row(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.padding(horizontal = 16.dp, vertical = 4.dp),
|
||||
horizontalArrangement = Arrangement.End
|
||||
) {
|
||||
androidx.compose.material3.TextButton(onClick = onAssignContact) {
|
||||
Icon(
|
||||
imageVector = Icons.Default.PersonAdd,
|
||||
contentDescription = null,
|
||||
modifier = Modifier.size(16.dp)
|
||||
)
|
||||
Spacer(Modifier.width(4.dp))
|
||||
Text(
|
||||
text = "Add another",
|
||||
style = MaterialTheme.typography.labelMedium
|
||||
thickness = 0.5.dp,
|
||||
color = MaterialTheme.colorScheme.outlineVariant
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// ── Basic info ─────────────────────────────────────────────────────────
|
||||
item {
|
||||
|
||||
Reference in New Issue
Block a user