feat(contacts): P2 шаг1 — типы TEL/EMAIL (парсинг из raw, подписи в карточке, сохранение при правке) +4 теста
This commit is contained in:
+37
-24
@@ -180,31 +180,27 @@ private fun ContactDetailContent(
|
||||
.padding(horizontal = 16.dp, vertical = 4.dp),
|
||||
verticalArrangement = Arrangement.spacedBy(0.dp),
|
||||
) {
|
||||
if (contact.emailLines.isNotEmpty()) {
|
||||
contact.emailLines.forEachIndexed { index, email ->
|
||||
ContactDetailField(
|
||||
label = if (index == 0) "Email" else "Email ${index + 1}",
|
||||
value = email,
|
||||
onClick = {
|
||||
context.startActivity(
|
||||
Intent(Intent.ACTION_SENDTO, Uri.parse("mailto:$email")),
|
||||
)
|
||||
},
|
||||
)
|
||||
}
|
||||
contact.emailEntries.forEachIndexed { index, entry ->
|
||||
ContactDetailField(
|
||||
label = ContactUi.emailTypeLabel(entry.type, index),
|
||||
value = entry.value,
|
||||
onClick = {
|
||||
context.startActivity(
|
||||
Intent(Intent.ACTION_SENDTO, Uri.parse("mailto:${entry.value}")),
|
||||
)
|
||||
},
|
||||
)
|
||||
}
|
||||
if (contact.phoneLines.isNotEmpty()) {
|
||||
contact.phoneLines.forEachIndexed { index, phone ->
|
||||
ContactDetailField(
|
||||
label = if (index == 0) "Телефон" else "Телефон ${index + 1}",
|
||||
value = phone,
|
||||
onClick = {
|
||||
context.startActivity(
|
||||
Intent(Intent.ACTION_DIAL, Uri.parse("tel:$phone")),
|
||||
)
|
||||
},
|
||||
)
|
||||
}
|
||||
contact.phoneEntries.forEachIndexed { index, entry ->
|
||||
ContactDetailField(
|
||||
label = ContactUi.phoneTypeLabel(entry.type, index),
|
||||
value = entry.value,
|
||||
onClick = {
|
||||
context.startActivity(
|
||||
Intent(Intent.ACTION_DIAL, Uri.parse("tel:${entry.value}")),
|
||||
)
|
||||
},
|
||||
)
|
||||
}
|
||||
if (contact.address.isNotBlank()) {
|
||||
ContactDetailField(label = "Адрес", value = contact.address)
|
||||
@@ -372,4 +368,21 @@ internal object ContactUi {
|
||||
android.util.Base64.decode(base64, android.util.Base64.DEFAULT)
|
||||
}.getOrNull()?.takeIf { it.isNotEmpty() }
|
||||
}
|
||||
|
||||
// P2: подпись типа EMAIL/TEL по токену TYPE (WORK/HOME/CELL/…); index — фолбэк-нумерация.
|
||||
fun emailTypeLabel(type: String, index: Int): String = when (type.uppercase()) {
|
||||
"WORK" -> "Рабочий"
|
||||
"HOME" -> "Личный"
|
||||
"" -> if (index == 0) "Email" else "Email ${index + 1}"
|
||||
else -> type.lowercase().replaceFirstChar { it.uppercase() }
|
||||
}
|
||||
|
||||
fun phoneTypeLabel(type: String, index: Int): String = when (type.uppercase()) {
|
||||
"CELL", "MOBILE" -> "Мобильный"
|
||||
"WORK" -> "Рабочий"
|
||||
"HOME" -> "Домашний"
|
||||
"FAX" -> "Факс"
|
||||
"" -> if (index == 0) "Телефон" else "Телефон ${index + 1}"
|
||||
else -> type.lowercase().replaceFirstChar { it.uppercase() }
|
||||
}
|
||||
}
|
||||
|
||||
+16
@@ -10,6 +10,7 @@ import ru.forbion.f7cloud.core.database.F7Database
|
||||
import ru.forbion.f7cloud.core.network.CardDavClient
|
||||
import ru.forbion.f7cloud.core.network.NetworkFactory
|
||||
import ru.forbion.f7cloud.core.network.VCardFields
|
||||
import ru.forbion.f7cloud.core.network.VCardTypedValue
|
||||
|
||||
data class ContactItem(
|
||||
val uid: String,
|
||||
@@ -45,6 +46,21 @@ data class ContactItem(
|
||||
.filter { it.isNotBlank() }
|
||||
.distinct()
|
||||
|
||||
// P2: значения с типом (WORK/HOME/CELL) из сырого vCard; фолбэк на значения без типа.
|
||||
val emailEntries: List<VCardTypedValue>
|
||||
get() = if (raw.isNotBlank()) {
|
||||
CardDavClient.parseTypedValues(raw, "EMAIL")
|
||||
} else {
|
||||
emailLines.map { VCardTypedValue(it) }
|
||||
}
|
||||
|
||||
val phoneEntries: List<VCardTypedValue>
|
||||
get() = if (raw.isNotBlank()) {
|
||||
CardDavClient.parseTypedValues(raw, "TEL")
|
||||
} else {
|
||||
phoneLines.map { VCardTypedValue(it) }
|
||||
}
|
||||
|
||||
val subtitle: String?
|
||||
get() = when {
|
||||
title.isNotBlank() && organization.isNotBlank() -> "$title · $organization"
|
||||
|
||||
+6
-2
@@ -169,12 +169,16 @@ private fun EditContactDialog(
|
||||
var website by remember(contact.uid) { mutableStateOf(contact.website) }
|
||||
var birthday by remember(contact.uid) { mutableStateOf(contact.birthday) }
|
||||
|
||||
// Типы сохраняем по значению: неизменённые адреса/номера не теряют TYPE (fix P1-каветата).
|
||||
val emailTypeByValue = contact.emailEntries.associate { it.value to it.type }
|
||||
val phoneTypeByValue = contact.phoneEntries.associate { it.value to it.type }
|
||||
|
||||
fun buildFields(): VCardFields {
|
||||
fun lines(v: String) = v.lines().map { it.trim() }.filter { it.isNotBlank() }
|
||||
return VCardFields(
|
||||
displayName = name.trim(),
|
||||
emails = lines(emails).map { VCardTypedValue(it) },
|
||||
phones = lines(phones).map { VCardTypedValue(it) },
|
||||
emails = lines(emails).map { VCardTypedValue(it, emailTypeByValue[it].orEmpty()) },
|
||||
phones = lines(phones).map { VCardTypedValue(it, phoneTypeByValue[it].orEmpty()) },
|
||||
organization = organization.trim(),
|
||||
title = title.trim(),
|
||||
address = address.trim(),
|
||||
|
||||
Reference in New Issue
Block a user