extract Nip05OrPubkeyLine into shared composable
Extract nip05 value calculation into helper
This commit is contained in:
+2
-28
@@ -48,10 +48,9 @@ import androidx.compose.ui.Modifier
|
|||||||
import androidx.compose.ui.text.font.FontWeight
|
import androidx.compose.ui.text.font.FontWeight
|
||||||
import androidx.compose.ui.text.style.TextOverflow
|
import androidx.compose.ui.text.style.TextOverflow
|
||||||
import androidx.compose.ui.unit.dp
|
import androidx.compose.ui.unit.dp
|
||||||
import androidx.lifecycle.compose.collectAsStateWithLifecycle
|
|
||||||
import androidx.lifecycle.viewmodel.compose.viewModel
|
import androidx.lifecycle.viewmodel.compose.viewModel
|
||||||
import com.vitorpamplona.amethyst.R
|
import com.vitorpamplona.amethyst.R
|
||||||
import com.vitorpamplona.amethyst.commons.model.nip05DnsIdentifiers.Nip05State
|
import com.vitorpamplona.amethyst.commons.ui.components.Nip05OrPubkeyLine
|
||||||
import com.vitorpamplona.amethyst.model.User
|
import com.vitorpamplona.amethyst.model.User
|
||||||
import com.vitorpamplona.amethyst.ui.navigation.navs.INav
|
import com.vitorpamplona.amethyst.ui.navigation.navs.INav
|
||||||
import com.vitorpamplona.amethyst.ui.navigation.topbars.SavingTopBar
|
import com.vitorpamplona.amethyst.ui.navigation.topbars.SavingTopBar
|
||||||
@@ -231,35 +230,10 @@ private fun SelectedUserRow(
|
|||||||
maxLines = 1,
|
maxLines = 1,
|
||||||
overflow = TextOverflow.Ellipsis,
|
overflow = TextOverflow.Ellipsis,
|
||||||
)
|
)
|
||||||
UserSecondaryLine(user)
|
Nip05OrPubkeyLine(user)
|
||||||
}
|
}
|
||||||
TextButton(onClick = onClear) {
|
TextButton(onClick = onClear) {
|
||||||
Text(stringRes(R.string.award_badge_remove_recipient))
|
Text(stringRes(R.string.award_badge_remove_recipient))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Composable
|
|
||||||
private fun UserSecondaryLine(user: User) {
|
|
||||||
val nip05StateMetadata by user.nip05State().flow.collectAsStateWithLifecycle()
|
|
||||||
|
|
||||||
val text =
|
|
||||||
when (val state = nip05StateMetadata) {
|
|
||||||
is Nip05State.Exists -> {
|
|
||||||
val name = state.nip05.name
|
|
||||||
if (name == "_") state.nip05.domain else "$name@${state.nip05.domain}"
|
|
||||||
}
|
|
||||||
|
|
||||||
else -> {
|
|
||||||
user.pubkeyDisplayHex()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Text(
|
|
||||||
text = text,
|
|
||||||
style = MaterialTheme.typography.bodySmall,
|
|
||||||
color = MaterialTheme.colorScheme.onSurfaceVariant,
|
|
||||||
maxLines = 1,
|
|
||||||
overflow = TextOverflow.Ellipsis,
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|||||||
+2
-28
@@ -65,14 +65,13 @@ import androidx.compose.ui.text.input.KeyboardCapitalization
|
|||||||
import androidx.compose.ui.text.style.TextAlign
|
import androidx.compose.ui.text.style.TextAlign
|
||||||
import androidx.compose.ui.text.style.TextOverflow
|
import androidx.compose.ui.text.style.TextOverflow
|
||||||
import androidx.compose.ui.unit.dp
|
import androidx.compose.ui.unit.dp
|
||||||
import androidx.lifecycle.compose.collectAsStateWithLifecycle
|
|
||||||
import androidx.lifecycle.viewmodel.compose.viewModel
|
import androidx.lifecycle.viewmodel.compose.viewModel
|
||||||
import coil3.compose.AsyncImage
|
import coil3.compose.AsyncImage
|
||||||
import com.vitorpamplona.amethyst.Amethyst
|
import com.vitorpamplona.amethyst.Amethyst
|
||||||
import com.vitorpamplona.amethyst.R
|
import com.vitorpamplona.amethyst.R
|
||||||
import com.vitorpamplona.amethyst.commons.icons.symbols.Icon
|
import com.vitorpamplona.amethyst.commons.icons.symbols.Icon
|
||||||
import com.vitorpamplona.amethyst.commons.icons.symbols.MaterialSymbols
|
import com.vitorpamplona.amethyst.commons.icons.symbols.MaterialSymbols
|
||||||
import com.vitorpamplona.amethyst.commons.model.nip05DnsIdentifiers.Nip05State
|
import com.vitorpamplona.amethyst.commons.ui.components.Nip05OrPubkeyLine
|
||||||
import com.vitorpamplona.amethyst.model.LocalCache
|
import com.vitorpamplona.amethyst.model.LocalCache
|
||||||
import com.vitorpamplona.amethyst.model.User
|
import com.vitorpamplona.amethyst.model.User
|
||||||
import com.vitorpamplona.amethyst.ui.actions.StrippingFailureDialog
|
import com.vitorpamplona.amethyst.ui.actions.StrippingFailureDialog
|
||||||
@@ -497,7 +496,7 @@ private fun SelectedModeratorRow(
|
|||||||
maxLines = 1,
|
maxLines = 1,
|
||||||
overflow = TextOverflow.Ellipsis,
|
overflow = TextOverflow.Ellipsis,
|
||||||
)
|
)
|
||||||
ModeratorSecondaryLine(user)
|
Nip05OrPubkeyLine(user)
|
||||||
}
|
}
|
||||||
|
|
||||||
if (isOwner) {
|
if (isOwner) {
|
||||||
@@ -522,31 +521,6 @@ private fun SelectedModeratorRow(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Composable
|
|
||||||
private fun ModeratorSecondaryLine(user: User) {
|
|
||||||
val nip05StateMetadata by user.nip05State().flow.collectAsStateWithLifecycle()
|
|
||||||
|
|
||||||
val text =
|
|
||||||
when (val state = nip05StateMetadata) {
|
|
||||||
is Nip05State.Exists -> {
|
|
||||||
val name = state.nip05.name
|
|
||||||
if (name == "_") state.nip05.domain else "$name@${state.nip05.domain}"
|
|
||||||
}
|
|
||||||
|
|
||||||
else -> {
|
|
||||||
user.pubkeyDisplayHex()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Text(
|
|
||||||
text = text,
|
|
||||||
style = MaterialTheme.typography.bodySmall,
|
|
||||||
color = MaterialTheme.colorScheme.onSurfaceVariant,
|
|
||||||
maxLines = 1,
|
|
||||||
overflow = TextOverflow.Ellipsis,
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
// --- Relays ------------------------------------------------------------------------------------
|
// --- Relays ------------------------------------------------------------------------------------
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
|
|||||||
+1
-3
@@ -338,10 +338,8 @@ fun DisplayNip05ProfileStatus(
|
|||||||
Text(
|
Text(
|
||||||
text =
|
text =
|
||||||
remember(nip05State) {
|
remember(nip05State) {
|
||||||
val name = nip05State.nip05.name
|
|
||||||
val display = if (name == "_") nip05State.nip05.domain else "$name@${nip05State.nip05.domain}"
|
|
||||||
buildAnnotatedString {
|
buildAnnotatedString {
|
||||||
appendLink(display, color) {
|
appendLink(nip05State.nip05.toDisplayValue(), color) {
|
||||||
runCatching { uri.openUri("https://${nip05State.nip05.domain}") }
|
runCatching { uri.openUri("https://${nip05State.nip05.domain}") }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+54
@@ -0,0 +1,54 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2025 Vitor Pamplona
|
||||||
|
*
|
||||||
|
* Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||||
|
* this software and associated documentation files (the "Software"), to deal in
|
||||||
|
* the Software without restriction, including without limitation the rights to use,
|
||||||
|
* copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the
|
||||||
|
* Software, and to permit persons to whom the Software is furnished to do so,
|
||||||
|
* subject to the following conditions:
|
||||||
|
*
|
||||||
|
* The above copyright notice and this permission notice shall be included in all
|
||||||
|
* copies or substantial portions of the Software.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
|
||||||
|
* FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
|
||||||
|
* COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
|
||||||
|
* AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||||
|
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||||
|
*/
|
||||||
|
package com.vitorpamplona.amethyst.commons.ui.components
|
||||||
|
|
||||||
|
import androidx.compose.material3.MaterialTheme
|
||||||
|
import androidx.compose.material3.Text
|
||||||
|
import androidx.compose.runtime.Composable
|
||||||
|
import androidx.compose.runtime.getValue
|
||||||
|
import androidx.compose.ui.text.style.TextOverflow
|
||||||
|
import androidx.lifecycle.compose.collectAsStateWithLifecycle
|
||||||
|
import com.vitorpamplona.amethyst.commons.model.User
|
||||||
|
import com.vitorpamplona.amethyst.commons.model.nip05DnsIdentifiers.Nip05State
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Single-line bodySmall label for a user secondary identifier: shows the
|
||||||
|
* verified NIP-05 (formatted via [com.vitorpamplona.quartz.nip05DnsIdentifiers.Nip05Id.toDisplayValue])
|
||||||
|
* when present, otherwise a shortened pubkey.
|
||||||
|
*/
|
||||||
|
@Composable
|
||||||
|
fun Nip05OrPubkeyLine(user: User) {
|
||||||
|
val nip05StateMetadata by user.nip05State().flow.collectAsStateWithLifecycle()
|
||||||
|
|
||||||
|
val text =
|
||||||
|
when (val state = nip05StateMetadata) {
|
||||||
|
is Nip05State.Exists -> state.nip05.toDisplayValue()
|
||||||
|
else -> user.pubkeyDisplayHex()
|
||||||
|
}
|
||||||
|
|
||||||
|
Text(
|
||||||
|
text = text,
|
||||||
|
style = MaterialTheme.typography.bodySmall,
|
||||||
|
color = MaterialTheme.colorScheme.onSurfaceVariant,
|
||||||
|
maxLines = 1,
|
||||||
|
overflow = TextOverflow.Ellipsis,
|
||||||
|
)
|
||||||
|
}
|
||||||
@@ -29,6 +29,13 @@ data class Nip05Id(
|
|||||||
) {
|
) {
|
||||||
fun toValue(): String = assemble(name, domain)
|
fun toValue(): String = assemble(name, domain)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Renders the address for users to read. When [name] is `"_"`, NIP-05
|
||||||
|
* specifies the address should display as just the domain. Use this in UI;
|
||||||
|
* use [toValue] for network lookups and storage.
|
||||||
|
*/
|
||||||
|
fun toDisplayValue(): String = if (name == "_") domain else assemble(name, domain)
|
||||||
|
|
||||||
fun toUserUrl(): String = userUrl(name, domain)
|
fun toUserUrl(): String = userUrl(name, domain)
|
||||||
|
|
||||||
fun toDomainUrl(): String = domainUrl(domain)
|
fun toDomainUrl(): String = domainUrl(domain)
|
||||||
|
|||||||
+14
@@ -109,6 +109,20 @@ class Nip05Test {
|
|||||||
assertEquals("https://$domain/.well-known/nostr.json", parsedNip05.toDomainUrl())
|
assertEquals("https://$domain/.well-known/nostr.json", parsedNip05.toDomainUrl())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `toDisplayValue with regular name returns name@domain`() {
|
||||||
|
val nip05 = Nip05Id.parse("alice@example.com")
|
||||||
|
assertNotNull(nip05)
|
||||||
|
assertEquals("alice@example.com", nip05.toDisplayValue())
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `toDisplayValue with underscore name returns domain only`() {
|
||||||
|
val nip05 = Nip05Id.parse("_@example.com")
|
||||||
|
assertNotNull(nip05)
|
||||||
|
assertEquals("example.com", nip05.toDisplayValue())
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun `test json parsing with relays`() {
|
fun `test json parsing with relays`() {
|
||||||
val parsedNip05 = Nip05Id.parse("bob@test.com")
|
val parsedNip05 = Nip05Id.parse("bob@test.com")
|
||||||
|
|||||||
Reference in New Issue
Block a user