Code review:

- read fundraiser value tags via shared helpers
This commit is contained in:
davotoula
2026-06-06 12:50:29 +02:00
parent be12abcb21
commit 7aa04e773b
5 changed files with 12 additions and 139 deletions
@@ -21,11 +21,10 @@
package com.vitorpamplona.quartz.experimental.agora
import androidx.compose.runtime.Immutable
import com.vitorpamplona.quartz.experimental.agora.tags.DeadlineTag
import com.vitorpamplona.quartz.experimental.agora.tags.GoalAmountTag
import com.vitorpamplona.quartz.experimental.agora.tags.WalletTag
import com.vitorpamplona.quartz.nip01Core.core.BaseAddressableEvent
import com.vitorpamplona.quartz.nip01Core.core.HexKey
import com.vitorpamplona.quartz.nip01Core.core.firstTagValueAsLong
import com.vitorpamplona.quartz.nip01Core.core.mapValueTagged
import com.vitorpamplona.quartz.nip01Core.metadata.tags.BannerTag
import com.vitorpamplona.quartz.nip01Core.tags.hashtags.hashtags
import com.vitorpamplona.quartz.nip01Core.tags.publishedAt.PublishedAtProvider
@@ -71,13 +70,16 @@ class FundraiserEvent(
fun coverImage() = banner() ?: image()
/** Fundraising target in sats, from the `goal` tag. */
fun goal() = tags.firstNotNullOfOrNull(GoalAmountTag::parse)
fun goal() = tags.firstTagValueAsLong("goal")
/** Deadline (unix seconds) from the `deadline` tag. */
fun deadline() = tags.firstNotNullOfOrNull(DeadlineTag::parse)
fun deadline() = tags.firstTagValueAsLong("deadline")
/** On-chain donation addresses from the `w` tags (may be empty). */
fun wallets() = tags.mapNotNull(WalletTag::parse)
/**
* On-chain donation addresses from the `w` tags (Bitcoin / silent payment;
* may be empty). Display/copy only — Amethyst does not send on-chain.
*/
fun wallets() = tags.mapValueTagged("w") { it }
fun topics() = tags.hashtags()
@@ -1,43 +0,0 @@
/*
* 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.quartz.experimental.agora.tags
import com.vitorpamplona.quartz.nip01Core.core.has
import com.vitorpamplona.quartz.utils.ensure
/**
* The deadline (unix seconds) by which an Agora fundraiser (kind 33863) aims to
* reach its goal — e.g. `["deadline", "9700905600"]`.
*/
class DeadlineTag {
companion object {
const val TAG_NAME = "deadline"
fun parse(tag: Array<String>): Long? {
ensure(tag.has(1)) { return null }
ensure(tag[0] == TAG_NAME) { return null }
ensure(tag[1].isNotEmpty()) { return null }
return tag[1].toLongOrNull()
}
fun assemble(deadlineUnixSeconds: Long) = arrayOf(TAG_NAME, deadlineUnixSeconds.toString())
}
}
@@ -1,43 +0,0 @@
/*
* 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.quartz.experimental.agora.tags
import com.vitorpamplona.quartz.nip01Core.core.has
import com.vitorpamplona.quartz.utils.ensure
/**
* The fundraising target for an Agora fundraiser (kind 33863), as a plain
* integer in sats — e.g. `["goal", "10000"]`.
*/
class GoalAmountTag {
companion object {
const val TAG_NAME = "goal"
fun parse(tag: Array<String>): Long? {
ensure(tag.has(1)) { return null }
ensure(tag[0] == TAG_NAME) { return null }
ensure(tag[1].isNotEmpty()) { return null }
return tag[1].toLongOrNull()
}
fun assemble(goalInSats: Long) = arrayOf(TAG_NAME, goalInSats.toString())
}
}
@@ -1,45 +0,0 @@
/*
* 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.quartz.experimental.agora.tags
import com.vitorpamplona.quartz.nip01Core.core.has
import com.vitorpamplona.quartz.utils.ensure
/**
* An on-chain donation address for an Agora fundraiser (kind 33863) — a Bitcoin
* address or BIP-352 silent-payment address — e.g. `["w", "bc1p..."]`. A
* fundraiser may carry several. These are display/copy targets only; Amethyst
* does not send on-chain payments.
*/
class WalletTag {
companion object {
const val TAG_NAME = "w"
fun parse(tag: Array<String>): String? {
ensure(tag.has(1)) { return null }
ensure(tag[0] == TAG_NAME) { return null }
ensure(tag[1].isNotEmpty()) { return null }
return tag[1]
}
fun assemble(address: String) = arrayOf(TAG_NAME, address)
}
}
@@ -24,6 +24,7 @@ import com.vitorpamplona.quartz.nip01Core.core.Event
import com.vitorpamplona.quartz.utils.EventFactory
import kotlin.test.Test
import kotlin.test.assertEquals
import kotlin.test.assertIs
import kotlin.test.assertTrue
class FundraiserEventTest {
@@ -65,7 +66,8 @@ class FundraiserEventTest {
@Test
fun parsesFundraiserFields() {
val event = sampleEvent() as FundraiserEvent
val event = sampleEvent()
assertIs<FundraiserEvent>(event)
assertEquals("Please help me, I'm Amira from Gaza", event.title())
assertEquals("https://blossom.primal.net/abc.jpg", event.coverImage())