feat(clink): let users set a noffer on their profile

Adds a 'CLINK Offer (noffer)' field to the profile editor so users can advertise
a payment offer in their kind-0 metadata:
- UserMetadataState.sendNewUserMetadata threads clinkOffer into the kind-0 build.
- NewUserMetadataViewModel loads/saves/clears the clinkOffer field.
- NewUserMetadataScreen renders the input (placeholder noffer1…).

:amethyst compiles. The read side (NIP-05 clink_offer discovery + preferring it)
is the next step.
This commit is contained in:
Claude
2026-06-09 23:28:26 +00:00
parent 50b4ed8c1e
commit def5cbc4e9
4 changed files with 24 additions and 0 deletions
@@ -68,6 +68,7 @@ class UserMetadataState(
nip05: String? = null,
lnAddress: String? = null,
lnURL: String? = null,
clinkOffer: String? = null,
): MetadataEvent {
val latest = getUserMetadataEvent()
@@ -85,6 +86,7 @@ class UserMetadataState(
nip05 = nip05,
lnAddress = lnAddress,
lnURL = lnURL,
clinkOffer = clinkOffer,
)
} else {
MetadataEvent.createNew(
@@ -98,6 +100,7 @@ class UserMetadataState(
nip05 = nip05,
lnAddress = lnAddress,
lnURL = lnURL,
clinkOffer = clinkOffer,
)
}
@@ -303,6 +303,22 @@ fun NewUserMetadataScreen(
singleLine = true,
)
Spacer(modifier = Modifier.height(10.dp))
OutlinedTextField(
label = { Text(text = stringRes(R.string.clink_offer_label)) },
modifier = Modifier.fillMaxWidth(),
value = postViewModel.clinkOffer.value,
onValueChange = { postViewModel.clinkOffer.value = it },
placeholder = {
Text(
text = "noffer1…",
color = MaterialTheme.colorScheme.placeholderText,
)
},
singleLine = true,
)
// -- Social Proofs --
ExpandableSection(
title = stringRes(R.string.social_proof),
@@ -60,6 +60,7 @@ class NewUserMetadataViewModel : ViewModel() {
val nip05 = mutableStateOf("")
val lnAddress = mutableStateOf("")
val lnURL = mutableStateOf("")
val clinkOffer = mutableStateOf("")
val twitter = mutableStateOf("")
val github = mutableStateOf("")
@@ -85,6 +86,7 @@ class NewUserMetadataViewModel : ViewModel() {
nip05.value = it.info.nip05 ?: ""
lnAddress.value = it.info.lud16 ?: ""
lnURL.value = it.info.lud06 ?: ""
clinkOffer.value = it.info.clinkOffer ?: ""
}
twitter.value = ""
@@ -124,6 +126,7 @@ class NewUserMetadataViewModel : ViewModel() {
nip05 = nip05.value,
lnAddress = lnAddress.value,
lnURL = lnURL.value,
clinkOffer = clinkOffer.value,
)
val identities =
@@ -149,6 +152,7 @@ class NewUserMetadataViewModel : ViewModel() {
nip05.value = ""
lnAddress.value = ""
lnURL.value = ""
clinkOffer.value = ""
twitter.value = ""
github.value = ""
mastodon.value = ""
+1
View File
@@ -121,6 +121,7 @@
<string name="clink_offer_amount_sats">Amount (sats)</string>
<string name="clink_offer_invalid_amount">Enter a valid amount for this offer.</string>
<string name="clink_offer_amount_range">Allowed range: %1$s%2$s sats</string>
<string name="clink_offer_label">CLINK Offer (noffer)</string>
<string name="clink_debit_pay_only">Pay only</string>
<string name="wallet_add_clink_title">CLINK Debit</string>
<string name="wallet_add_clink_description">Pay and zap from a wallet that pre-authorized your account. Spend only — no balance or history.</string>