refactor(quartz): rename event classes to match their NIP names

Old class names drifted from the NIP wording (or described the wrong
kind). Every old name stays available as a @Deprecated typealias with a
ReplaceWith, so external Quartz users get an IDE quick-fix instead of a
break. Wire format is unchanged: parsing goes by kind.

Renames:
- FollowListEvent (39089) -> StarterPackEvent
- LnZapPaymentRequestEvent/ResponseEvent (NIP-47) -> NwcRequestEvent/NwcResponseEvent
- VideoHorizontalEvent/VideoVerticalEvent -> AddressableNormalVideoEvent/AddressableShortVideoEvent
- CalendarEvent (31924) -> CalendarCollectionEvent
- HashtagListEvent -> InterestListEvent
- ContactCardEvent (30382) -> UserAssertionEvent
- LnZapEvent/LnZapRequestEvent/LnZapEventInterface -> ZapReceiptEvent/ZapRequestEvent/ZapReceiptEventInterface
- PrivateDmEvent -> EncryptedDmEvent, DeletionEvent -> DeletionRequestEvent
- LongTextNoteEvent -> LongFormContentEvent, WikiNoteEvent -> WikiArticleEvent
- PeopleListEvent -> FollowSetEvent, LabeledBookmarkListEvent -> BookmarkSetEvent
- RelayFeedsListEvent -> FavoriteRelayListEvent, EmojiPackSelectionEvent -> EmojiListEvent
- ChannelListEvent -> PublicChatListEvent, ChatMessageRelayListEvent -> DmRelayListEvent
- SealedRumorEvent -> SealEvent, FileHeaderEvent -> FileMetadataEvent
- GoalEvent -> ZapGoalEvent, StatusEvent -> UserStatusEvent
- NIP90*Event -> Dvm*Event (all NIP-90 events)
- NIP-29 group events get the Group prefix to separate them from NIP-43:
  PutUser, RemoveUser, EditMetadata, DeleteEvent, CreateInvite,
  UpdatePinList, JoinRequest, LeaveRequest; SupportedRolesEvent -> GroupRolesEvent

Merged duplicate classes for the same kind:
- nip61 TokenEvent (7375) was unreachable in EventFactory; now an alias of CashuTokenEvent.
- nip61 NutzapRedemptionEvent (7376) was unreachable; CashuSpendingHistoryEvent now
  carries its hint providers and redeemedNutzaps(), plus
  CashuSpendingHistoryEvent.buildNutzapRedemption.
- NIP-82 SoftwareReleaseEvent (30063) was never built by EventFactory;
  ReleaseArtifactSetEvent now exposes appId/version/channel/assets/releaseNotes
  and buildSoftwareRelease, and the app no longer re-parses the event.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_015VvJ8XpeYqBe8bYT3bJ7UD
This commit is contained in:
Claude
2026-09-26 20:19:55 +00:00
parent 9fba896533
commit 6d2c37c346
546 changed files with 3851 additions and 3343 deletions
+1 -1
View File
@@ -29,7 +29,7 @@ abstract class NostrSigner(val pubKey: HexKey) {
abstract fun nip04Decrypt(ciphertext: String, fromPubKey: HexKey, onReady: (String) -> Unit) abstract fun nip04Decrypt(ciphertext: String, fromPubKey: HexKey, onReady: (String) -> Unit)
abstract fun nip44Encrypt(...) abstract fun nip44Encrypt(...)
abstract fun nip44Decrypt(...) abstract fun nip44Decrypt(...)
abstract fun decryptZapEvent(event: LnZapRequestEvent, onReady: (LnZapRequestEvent) -> Unit) abstract fun decryptZapEvent(event: ZapRequestEvent, onReady: (ZapRequestEvent) -> Unit)
} }
``` ```
@@ -26,16 +26,16 @@ event signatures; the 10040→assertion link is **consumer-side convention** (se
| Kind | Class | Kind class | d-tag = the subject | Content | | Kind | Class | Kind class | d-tag = the subject | Content |
|---|---|---|---|---| |---|---|---|---|---|
| 10040 | `list/TrustProviderListEvent` | replaceable | *(none — always `""`)* | NIP-44 private provider entries (optional) | | 10040 | `list/TrustProviderListEvent` | replaceable | *(none — always `""`)* | NIP-44 private provider entries (optional) |
| 30382 | `users/ContactCardEvent` | addressable | **target user's pubkey** (hex) | NIP-44 private tags (petname/summary/emoji) | | 30382 | `users/UserAssertionEvent` | addressable | **target user's pubkey** (hex) | NIP-44 private tags (petname/summary/emoji) |
| 30383 | `events/EventAssertionEvent` | addressable | **target event id** (hex) | `""` | | 30383 | `events/EventAssertionEvent` | addressable | **target event id** (hex) | `""` |
| 30384 | `addressables/AddressableAssertionEvent` | addressable | **target coordinate** `kind:pubkey:dtag` | `""` | | 30384 | `addressables/AddressableAssertionEvent` | addressable | **target coordinate** `kind:pubkey:dtag` | `""` |
| 30385 | `externalIds/ExternalIdAssertionEvent` | addressable | **external identifier** (e.g. `isbn:978-0-13-468599-1`) | `""` | | 30385 | `externalIds/ExternalIdAssertionEvent` | addressable | **external identifier** (e.g. `isbn:978-0-13-468599-1`) | `""` |
Addresses: `ContactCardEvent.createAddress(owner, target)` → `Address(30382, owner, target)` Addresses: `UserAssertionEvent.createAddress(owner, target)` → `Address(30382, owner, target)`
(owner = signer, target = subject). `TrustProviderListEvent.createAddress(pubKey)` uses (owner = signer, target = subject). `TrustProviderListEvent.createAddress(pubKey)` uses
`FIXED_D_TAG = ""`. `AssertionEventTest.eventKindsAreCorrect` pins all five numbers. `FIXED_D_TAG = ""`. `AssertionEventTest.eventKindsAreCorrect` pins all five numbers.
`ContactCardEvent` is also a `SearchableEvent` — it indexes only the **public** petname/summary `UserAssertionEvent` is also a `SearchableEvent` — it indexes only the **public** petname/summary
tags plus topics; the encrypted card content is intentionally never indexed. tags plus topics; the encrypted card content is intentionally never indexed.
## The 10040 provider entry (`ServiceProviderTag` / `ServiceType`) ## The 10040 provider entry (`ServiceProviderTag` / `ServiceType`)
@@ -91,7 +91,7 @@ The same kind serves two roles, distinguished **by author**:
`followers`, `hops`, …); this is what 10040 discovery points at. `followers`, `hops`, …); this is what 10040 discovery points at.
2. **The account's own contact cards (nicknames, NIP-81-style)** — signed by the account, 2. **The account's own contact cards (nicknames, NIP-81-style)** — signed by the account,
one per target user. The petname, summary, and their NIP-30 emoji mappings **always live in one per target user. The petname, summary, and their NIP-30 emoji mappings **always live in
the NIP-44 encrypted content, never in public tags** (`ContactCardEvent.build`/ the NIP-44 encrypted content, never in public tags** (`UserAssertionEvent.build`/
`updatePetNameAndSummary` strip stray public copies; asserted by `ContactCardPetNameTest`). `updatePetNameAndSummary` strip stray public copies; asserted by `ContactCardPetNameTest`).
`commons/.../ContactCardsState.kt` keys everything on `author == account` and ignores `commons/.../ContactCardsState.kt` keys everything on `author == account` and ignores
provider cards. provider cards.
@@ -104,7 +104,7 @@ value — a bad tag is *dropped*, never an error) + `assemble(value)` → `[name
validation (rank isn't clamped, hours aren't checked against 0–23, counts may be negative) — validation (rank isn't clamped, hours aren't checked against 0–23, counts may be negative) —
consumers must defend. consumers must defend.
**On 30382** (`users/tags/`, accessors on `ContactCardEvent` and as `TagArray` extensions in **On 30382** (`users/tags/`, accessors on `UserAssertionEvent` and as `TagArray` extensions in
`users/TagArrayExt.kt` so they also work on decrypted private arrays): `users/TagArrayExt.kt` so they also work on decrypted private arrays):
| Tag name | Accessor | Type | Semantics | | Tag name | Accessor | Type | Semantics |
@@ -138,7 +138,7 @@ val providers: List<ServiceProviderTag> = updated.serviceProviders() //
val private = updated.privateTags(signer)?.serviceProviders() // private side val private = updated.privateTags(signer)?.serviceProviders() // private side
// Provider-style contact card (public metrics) — the GrapeRankPublisher pattern: // Provider-style contact card (public metrics) — the GrapeRankPublisher pattern:
val card = ContactCardEvent.create( val card = UserAssertionEvent.create(
targetUser = subjectPubkey, targetUser = subjectPubkey,
signer = providerSigner, signer = providerSigner,
publicInitializer = { publicInitializer = {
@@ -79,28 +79,28 @@ companion object {
### Zap Request/Receipt (kinds 9734, 9735) ### Zap Request/Receipt (kinds 9734, 9735)
```kotlin ```kotlin
class LnZapRequestEvent(...) : Event(...) class ZapRequestEvent(...) : Event(...)
// Created by client, sent to Lightning Address // Created by client, sent to Lightning Address
class LnZapEvent(...) : Event(...) class ZapReceiptEvent(...) : Event(...)
// Receipt from LSP, contains bolt11 + embedded zap request // Receipt from LSP, contains bolt11 + embedded zap request
val zapRequest: LnZapRequestEvent? by lazy { containedPost() } val zapRequest: ZapRequestEvent? by lazy { containedPost() }
val amount: BigDecimal? by lazy { /* parse from bolt11 */ } val amount: BigDecimal? by lazy { /* parse from bolt11 */ }
``` ```
### Long-Form Content (kind 30023) ### Long-Form Content (kind 30023)
```kotlin ```kotlin
class LongTextNoteEvent(...) : BaseAddressableEvent(...) class LongFormContentEvent(...) : BaseAddressableEvent(...)
// Blog posts, articles // Blog posts, articles
// Addressable via kind:pubkey:d-tag // Addressable via kind:pubkey:d-tag
``` ```
### Lists (kinds 10000-30004) ### Lists (kinds 10000-30004)
```kotlin ```kotlin
sealed class PeopleListEvent : BaseAddressableEvent { sealed class FollowSetEvent : BaseAddressableEvent {
object MuteList : PeopleListEvent(10000) object MuteList : FollowSetEvent(10000)
object PinList : PeopleListEvent(10001) object PinList : FollowSetEvent(10001)
object BookmarkList : PeopleListEvent(10003) object BookmarkList : FollowSetEvent(10003)
// ... 18 list types total // ... 18 list types total
} }
``` ```
@@ -279,7 +279,7 @@ val metadata2 = MetadataEvent.createNew(name = "Alice Updated", picture = "url2"
### Event Deletion ### Event Deletion
```kotlin ```kotlin
// Delete events // Delete events
val deletion = DeletionEvent.create( val deletion = DeletionRequestEvent.create(
deleteEvents = listOf(eventId1, eventId2), deleteEvents = listOf(eventId1, eventId2),
reason = "Spam", reason = "Spam",
signer = signer signer = signer
@@ -15,7 +15,7 @@ under `experimental/`**. The categorized list below may lag behind —
| 04 | `nip04Dm/` | EncryptedDmEvent.kt | Legacy encrypted DMs (deprecated for NIP-17) | | 04 | `nip04Dm/` | EncryptedDmEvent.kt | Legacy encrypted DMs (deprecated for NIP-17) |
| 05 | `nip05DnsIdentifiers/` | UserHexResolver.kt, Nip05Client.kt | Internet identifiers; `resolveUserHexOrNull` resolves hex/npub/nprofile/`name@domain` → pubkey (see references/nip05-identifiers.md) | | 05 | `nip05DnsIdentifiers/` | UserHexResolver.kt, Nip05Client.kt | Internet identifiers; `resolveUserHexOrNull` resolves hex/npub/nprofile/`name@domain` → pubkey (see references/nip05-identifiers.md) |
| 06 | `nip06KeyDerivation/` | Mnemonic-related | BIP-39 key derivation | | 06 | `nip06KeyDerivation/` | Mnemonic-related | BIP-39 key derivation |
| 09 | `nip09Deletions/` | DeletionEvent.kt | Event deletion requests (kind 5) | | 09 | `nip09Deletions/` | DeletionRequestEvent.kt | Event deletion requests (kind 5) |
| 11 | `nip11RelayInfo/` | RelayInformation.kt | Relay metadata | | 11 | `nip11RelayInfo/` | RelayInformation.kt | Relay metadata |
| 13 | `nip13Pow/` | ProofOfWork.kt | Proof of work | | 13 | `nip13Pow/` | ProofOfWork.kt | Proof of work |
| 14 | `nip14Subject/` | Subject tags | Subject tags for text notes | | 14 | `nip14Subject/` | Subject tags | Subject tags for text notes |
@@ -33,7 +33,7 @@ ent for NIP-04) |
| 10 | `nip10Notes/` | TextNoteEvent.kt | Text notes with threading (kind 1) | | 10 | `nip10Notes/` | TextNoteEvent.kt | Text notes with threading (kind 1) |
| 18 | `nip18Reposts/` | RepostEvent.kt, GenericRepostEvent.kt | Reposts (kind 6, 16) | | 18 | `nip18Reposts/` | RepostEvent.kt, GenericRepostEvent.kt | Reposts (kind 6, 16) |
| 22 | `nip22Comments/` | CommentEvent.kt | Comments (kind 1111) | | 22 | `nip22Comments/` | CommentEvent.kt | Comments (kind 1111) |
| 23 | `nip23LongContent/` | LongTextNoteEvent.kt | Long-form content (kind 30023) | | 23 | `nip23LongContent/` | LongFormContentEvent.kt | Long-form content (kind 30023) |
| 25 | `nip25Reactions/` | ReactionEvent.kt | Reactions (kind 7) | | 25 | `nip25Reactions/` | ReactionEvent.kt | Reactions (kind 7) |
| 31 | `nip31Alts/` | Alt tags | Alt description tags | | 31 | `nip31Alts/` | Alt tags | Alt description tags |
| 36 | `nip36SensitiveContent/` | Content warnings | Content warning tags | | 36 | `nip36SensitiveContent/` | Content warnings | Content warning tags |
@@ -62,7 +62,7 @@ ent for NIP-04) |
| 46 | `nip46RemoteSigner/` | NostrConnectEvent.kt | Remote signer protocol (bunker) | | 46 | `nip46RemoteSigner/` | NostrConnectEvent.kt | Remote signer protocol (bunker) |
| 47 | `nip47WalletConnect/` | Nostr Wallet Connect | Wallet connection protocol | | 47 | `nip47WalletConnect/` | Nostr Wallet Connect | Wallet connection protocol |
| 56 | `nip56Reports/` | ReportEvent.kt | Reports (kind 1984) | | 56 | `nip56Reports/` | ReportEvent.kt | Reports (kind 1984) |
| 57 | `nip57Zaps/` | LnZapEvent.kt, LnZapRequestEvent.kt | Lightning zaps (kinds 9734, 9735) | | 57 | `nip57Zaps/` | ZapReceiptEvent.kt, ZapRequestEvent.kt | Lightning zaps (kinds 9734, 9735) |
| 58 | `nip58Badges/` | Badge events | Badge definitions & awards (kinds 30009, 8) | | 58 | `nip58Badges/` | Badge events | Badge definitions & awards (kinds 30009, 8) |
| 59 | `nip59Giftwrap/` | GiftWrapEvent.kt | Gift-wrapped events for privacy | | 59 | `nip59Giftwrap/` | GiftWrapEvent.kt | Gift-wrapped events for privacy |
| 75 | `nip75ZapGoals/` | ZapGoalEvent.kt | Zap goals (kind 9041) | | 75 | `nip75ZapGoals/` | ZapGoalEvent.kt | Zap goals (kind 9041) |
@@ -76,7 +76,7 @@ ent for NIP-04) |
| 35 | `nip35Torrents/` | Torrent events | Torrent tracking | | 35 | `nip35Torrents/` | Torrent events | Torrent tracking |
| 52 | `nip52Calendar/` | Calendar events | Calendar time-based/date-based (kinds 31922-31925) | | 52 | `nip52Calendar/` | Calendar events | Calendar time-based/date-based (kinds 31922-31925) |
| 53 | `nip53LiveActivities/` | LiveActivitiesEvent.kt | Live events/streaming (kind 30311) | | 53 | `nip53LiveActivities/` | LiveActivitiesEvent.kt | Live events/streaming (kind 30311) |
| 54 | `nip54Wiki/` | WikiNoteEvent.kt | Wiki pages (kind 30818) | | 54 | `nip54Wiki/` | WikiArticleEvent.kt | Wiki pages (kind 30818) |
| 68 | `nip68Picture/` | Picture metadata | Picture metadata | | 68 | `nip68Picture/` | Picture metadata | Picture metadata |
| 71 | `nip71Video/` | 7 video event types | Video events (kinds 34235, 35235, 1234, 1235) | | 71 | `nip71Video/` | 7 video event types | Video events (kinds 34235, 35235, 1234, 1235) |
| 72 | `nip72ModCommunities/` | Community events | Moderated communities (kinds 34550, 34551, 9041) | | 72 | `nip72ModCommunities/` | Community events | Moderated communities (kinds 34550, 34551, 9041) |
@@ -84,7 +84,7 @@ ent for NIP-04) |
| 89 | `nip89AppHandlers/` | AppDefinitionEvent.kt | App recommendations (kinds 31990, 31989) | | 89 | `nip89AppHandlers/` | AppDefinitionEvent.kt | App recommendations (kinds 31990, 31989) |
| 90 | `nip90Dvms/` | DVM job events | Data Vending Machines (DVMs) (kinds 5000-7000) | | 90 | `nip90Dvms/` | DVM job events | Data Vending Machines (DVMs) (kinds 5000-7000) |
| 92 | `nip92IMeta/` | IMeta tags | Image metadata tags | | 92 | `nip92IMeta/` | IMeta tags | Image metadata tags |
| 94 | `nip94FileMetadata/` | FileHeaderEvent.kt, FileStorageEvent.kt | File metadata (kind 1063) | | 94 | `nip94FileMetadata/` | FileMetadataEvent.kt, FileStorageEvent.kt | File metadata (kind 1063) |
| 96 | `nip96FileStorage/` | HTTP file storage | HTTP-based file storage | | 96 | `nip96FileStorage/` | HTTP file storage | HTTP-based file storage |
| 99 | `nip99Classifieds/` | ClassifiedsEvent.kt | Classifieds/marketplace (kind 30402) | | 99 | `nip99Classifieds/` | ClassifiedsEvent.kt | Classifieds/marketplace (kind 30402) |
| A0 | `nipA0VoiceMessages/` | Voice messages | Voice message events | | A0 | `nipA0VoiceMessages/` | Voice messages | Voice message events |
@@ -93,7 +93,7 @@ ent for NIP-04) |
### Web/Storage/Other ### Web/Storage/Other
| NIP | Directory | Key Files | Description | | NIP | Directory | Key Files | Description |
|-----|-----------|-----------|-------------| |-----|-----------|-----------|-------------|
| 38 | `nip38UserStatus/` | StatusEvent.kt | User status (kind 30315) | | 38 | `nip38UserStatus/` | UserStatusEvent.kt | User status (kind 30315) |
| 60 | `nip60Payment/` | Wallet events | Wallet info (kind 13194) | | 60 | `nip60Payment/` | Wallet events | Wallet info (kind 13194) |
| 61 | `nip61PaymentRequest/` | Nut zaps | Cashu payment requests | | 61 | `nip61PaymentRequest/` | Nut zaps | Cashu payment requests |
| 64 | `nip64Chess/` | Chess moves | Chess move events | | 64 | `nip64Chess/` | Chess moves | Chess move events |
+11 -11
View File
@@ -650,15 +650,15 @@ val custom = ReactionEvent.build("🤙", targetEvent)
### NIP-57 — Zap request (kind 9734) ### NIP-57 — Zap request (kind 9734)
```kotlin ```kotlin
import com.vitorpamplona.quartz.nip57Zaps.LnZapRequestEvent import com.vitorpamplona.quartz.nip57Zaps.ZapRequestEvent
val template = LnZapRequestEvent.build( val template = ZapRequestEvent.build(
message = "Great post!", message = "Great post!",
relays = listOf("wss://relay.damus.io"), relays = listOf("wss://relay.damus.io"),
target = targetEvent, target = targetEvent,
zapType = LnZapRequestEvent.ZapType.PUBLIC zapType = ZapRequestEvent.ZapType.PUBLIC
) )
val zapRequest: LnZapRequestEvent = signer.sign(template) val zapRequest: ZapRequestEvent = signer.sign(template)
``` ```
### NIP-59 — Gift wrap / sealed DM (kind 1059 + 14) ### NIP-59 — Gift wrap / sealed DM (kind 1059 + 14)
@@ -678,9 +678,9 @@ val (dmEvent, giftWrap) = NIP17Factory.create(
### NIP-23 — Long-form article (kind 30023) ### NIP-23 — Long-form article (kind 30023)
```kotlin ```kotlin
import com.vitorpamplona.quartz.nip23LongContent.LongTextNoteEvent import com.vitorpamplona.quartz.nip23LongContent.LongFormContentEvent
val template = LongTextNoteEvent.build( val template = LongFormContentEvent.build(
body = markdownContent, body = markdownContent,
title = "My Article", title = "My Article",
image = "https://example.com/cover.jpg", image = "https://example.com/cover.jpg",
@@ -882,16 +882,16 @@ use `Nip11RelayInformation.fromJson(json)`.
| 0 | User metadata | 01 | `MetadataEvent` | | 0 | User metadata | 01 | `MetadataEvent` |
| 1 | Text note | 10 | `TextNoteEvent` | | 1 | Text note | 10 | `TextNoteEvent` |
| 3 | Follow list | 02 | `ContactListEvent` | | 3 | Follow list | 02 | `ContactListEvent` |
| 4 | Legacy DM | 04 | `PrivateDmEvent` | | 4 | Legacy DM | 04 | `EncryptedDmEvent` |
| 5 | Deletion | 09 | `DeletionEvent` | | 5 | Deletion | 09 | `DeletionRequestEvent` |
| 6 | Repost | 18 | `RepostEvent` | | 6 | Repost | 18 | `RepostEvent` |
| 7 | Reaction | 25 | `ReactionEvent` | | 7 | Reaction | 25 | `ReactionEvent` |
| 14 | Chat message (sealed) | 17 | `NIP17GroupMessage` | | 14 | Chat message (sealed) | 17 | `NIP17GroupMessage` |
| 1059 | Gift wrap | 59 | `GiftWrapEvent` | | 1059 | Gift wrap | 59 | `GiftWrapEvent` |
| 9734 | Zap request | 57 | `LnZapRequestEvent` | | 9734 | Zap request | 57 | `ZapRequestEvent` |
| 9735 | Zap receipt | 57 | `LnZapEvent` | | 9735 | Zap receipt | 57 | `ZapReceiptEvent` |
| 10002 | Relay list | 65 | `AdvertisedRelayListEvent` | | 10002 | Relay list | 65 | `AdvertisedRelayListEvent` |
| 30023 | Long-form content | 23 | `LongTextNoteEvent` | | 30023 | Long-form content | 23 | `LongFormContentEvent` |
## Related Skills ## Related Skills
@@ -26,7 +26,7 @@ Separator legend: **NL** = `joinToString("\n")`, **SP** = `joinToString(" ")`.
| 42 | ChannelMessageEvent | nip28PublicChat/message | `content` | | 42 | ChannelMessageEvent | nip28PublicChat/message | `content` |
| 54 | PodcastEpisodeEvent | nipF4Podcasts/episode | `listOfNotNull(title(), description(), content)` NL | | 54 | PodcastEpisodeEvent | nipF4Podcasts/episode | `listOfNotNull(title(), description(), content)` NL |
| 1010 | TextNoteModificationEvent | experimental/edits | `listOfNotNull(content, summary())` NL (content first) | | 1010 | TextNoteModificationEvent | experimental/edits | `listOfNotNull(content, summary())` NL (content first) |
| 1063 | FileHeaderEvent | nip94FileMetadata | `listOfNotNull(summary(), content)` NL | | 1063 | FileMetadataEvent | nip94FileMetadata | `listOfNotNull(summary(), content)` NL |
| 1065 | FileStorageHeaderEvent | experimental/nip95/header | `listOfNotNull(summary())` NL | | 1065 | FileStorageHeaderEvent | experimental/nip95/header | `listOfNotNull(summary())` NL |
| 1068 | PollEvent | nip88Polls/poll | `buildString { append(content); options().forEach { append('\n').append(it.label) } }` | | 1068 | PollEvent | nip88Polls/poll | `buildString { append(content); options().forEach { append('\n').append(it.label) } }` |
| 1111 | CommentEvent | nip22Comments | `(listOf(content) + tags.hashtags())` NL | | 1111 | CommentEvent | nip22Comments | `(listOf(content) + tags.hashtags())` NL |
@@ -48,20 +48,20 @@ Separator legend: **NL** = `joinToString("\n")`, **SP** = `joinToString(" ")`.
| 2004 | TorrentCommentEvent | nip35Torrents | `content` | | 2004 | TorrentCommentEvent | nip35Torrents | `content` |
| 2473 | BirdDetectionEvent | experimental/birdstar | `listOfNotNull(summary(), speciesName())` NL | | 2473 | BirdDetectionEvent | experimental/birdstar | `listOfNotNull(summary(), speciesName())` NL |
| 3302 | ConcordChatEditEvent | concord/cord03Channels | `content` | | 3302 | ConcordChatEditEvent | concord/cord03Channels | `content` |
| 5050 | NIP90TextGenerationRequestEvent | nip90Dvms/textGeneration | `inputs().filter { it.type == "prompt" \|\| it.type == "text" }.joinToString(" ") { it.value }` (SP) | | 5050 | DvmTextGenerationRequestEvent | nip90Dvms/textGeneration | `inputs().filter { it.type == "prompt" \|\| it.type == "text" }.joinToString(" ") { it.value }` (SP) |
| 5100 | NIP90ImageGenerationRequestEvent | nip90Dvms/imageGeneration | `listOfNotNull(prompt(), negativePrompt()).joinToString(" ")` (SP) | | 5100 | DvmImageGenerationRequestEvent | nip90Dvms/imageGeneration | `listOfNotNull(prompt(), negativePrompt()).joinToString(" ")` (SP) |
| 5129 | NappletSnapshotEvent | nip5dNapplets | `listOfNotNull(title(), description())` NL | | 5129 | NappletSnapshotEvent | nip5dNapplets | `listOfNotNull(title(), description())` NL |
| 5250 | NIP90TextToSpeechRequestEvent | nip90Dvms/textToSpeech | `text() ?: ""` | | 5250 | DvmTextToSpeechRequestEvent | nip90Dvms/textToSpeech | `text() ?: ""` |
| 5302 | NIP90ContentSearchRequestEvent | nip90Dvms/contentSearch | `searchQuery() ?: ""` | | 5302 | DvmContentSearchRequestEvent | nip90Dvms/contentSearch | `searchQuery() ?: ""` |
| 5303 | NIP90PeopleSearchRequestEvent | nip90Dvms/peopleSearch | `searchQuery() ?: ""` | | 5303 | DvmPeopleSearchRequestEvent | nip90Dvms/peopleSearch | `searchQuery() ?: ""` |
| 6969 | ZapPollEvent | experimental/zapPolls | `buildString { append(content); pollOptionsArray().forEach { append('\n').append(it.descriptor) } }` | | 6969 | ZapPollEvent | experimental/zapPolls | `buildString { append(content); pollOptionsArray().forEach { append('\n').append(it.descriptor) } }` |
| 7516 | GeocacheFoundLogEvent | nipCCGeocaching/foundLog | `content` | | 7516 | GeocacheFoundLogEvent | nipCCGeocaching/foundLog | `content` |
| 8333 | OnchainZapEvent | nipBCOnchainZaps/zap | `content` | | 8333 | OnchainZapEvent | nipBCOnchainZaps/zap | `content` |
| 9002 | EditMetadataEvent | nip29RelayGroups/moderation | `(listOfNotNull(name(), about()) + hashtags())` NL | | 9002 | GroupEditMetadataEvent | nip29RelayGroups/moderation | `(listOfNotNull(name(), about()) + hashtags())` NL |
| 9041 | GoalEvent | nip75ZapGoals | `listOfNotNull(summary(), content)` NL | | 9041 | ZapGoalEvent | nip75ZapGoals | `listOfNotNull(summary(), content)` NL |
| 9321 | NutzapEvent | nip61Nutzaps/nutzap | `content` | | 9321 | NutzapEvent | nip61Nutzaps/nutzap | `content` |
| 9734 | LnZapRequestEvent | nip57Zaps | `content` | | 9734 | ZapRequestEvent | nip57Zaps | `content` |
| 9735 | LnZapEvent | nip57Zaps | `zapRequest?.content.orEmpty()` — indexes the **embedded 9734's** content | | 9735 | ZapReceiptEvent | nip57Zaps | `zapRequest?.content.orEmpty()` — indexes the **embedded 9734's** content |
| 9736 | Bolt12ZapEvent | nipB1Bolt12Zaps/zap | `content` | | 9736 | Bolt12ZapEvent | nipB1Bolt12Zaps/zap | `content` |
| 9737 | Bolt12ZapIntentEvent | nipB1Bolt12Zaps/intent | `content` | | 9737 | Bolt12ZapIntentEvent | nipB1Bolt12Zaps/intent | `content` |
| 9802 | HighlightEvent | nip84Highlights | `listOfNotNull(comment(), context(), content)` NL | | 9802 | HighlightEvent | nip84Highlights | `listOfNotNull(comment(), context(), content)` NL |
@@ -74,10 +74,10 @@ Separator legend: **NL** = `joinToString("\n")`, **SP** = `joinToString(" ")`.
| 12473 | BirdexEvent | experimental/birdstar | `(listOfNotNull(summary()) + speciesNames())` NL | | 12473 | BirdexEvent | experimental/birdstar | `(listOfNotNull(summary()) + speciesNames())` NL |
| 15128 | RootSiteEvent | nip5aStaticWebsites | `listOfNotNull(title(), description())` NL | | 15128 | RootSiteEvent | nip5aStaticWebsites | `listOfNotNull(title(), description())` NL |
| 15129 | RootNappletEvent | nip5dNapplets | `listOfNotNull(title(), description())` NL | | 15129 | RootNappletEvent | nip5dNapplets | `listOfNotNull(title(), description())` NL |
| 30000 | PeopleListEvent | nip51Lists/peopleList | `listOfNotNull(titleOrName(), description())` NL | | 30000 | FollowSetEvent | nip51Lists/peopleList | `listOfNotNull(titleOrName(), description())` NL |
| 30001 | OldBookmarkListEvent | nip51Lists/bookmarkList | `listOfNotNull(title())` NL | | 30001 | OldBookmarkListEvent | nip51Lists/bookmarkList | `listOfNotNull(title())` NL |
| 30002 | RelaySetEvent | nip51Lists/relaySets | `listOfNotNull(title(), description())` NL | | 30002 | RelaySetEvent | nip51Lists/relaySets | `listOfNotNull(title(), description())` NL |
| 30003 | LabeledBookmarkListEvent | nip51Lists/labeledBookmarkList | `listOfNotNull(titleOrName(), description())` NL | | 30003 | BookmarkSetEvent | nip51Lists/labeledBookmarkList | `listOfNotNull(titleOrName(), description())` NL |
| 30004 | ArticleCurationSetEvent | nip51Lists/articleCurationSet | `listOfNotNull(title(), description())` NL | | 30004 | ArticleCurationSetEvent | nip51Lists/articleCurationSet | `listOfNotNull(title(), description())` NL |
| 30005 | VideoCurationSetEvent | nip51Lists/videoCurationSet | `listOfNotNull(title(), description())` NL | | 30005 | VideoCurationSetEvent | nip51Lists/videoCurationSet | `listOfNotNull(title(), description())` NL |
| 30006 | PictureCurationSetEvent | nip51Lists/pictureCurationSet | `listOfNotNull(title(), description())` NL | | 30006 | PictureCurationSetEvent | nip51Lists/pictureCurationSet | `listOfNotNull(title(), description())` NL |
@@ -87,7 +87,7 @@ Separator legend: **NL** = `joinToString("\n")`, **SP** = `joinToString(" ")`.
| 30018 | ProductEvent | nip15Marketplace/product | `productData()?.let { (listOfNotNull(it.name, it.description) + categories()).joinToString("\n") } ?: ""` | | 30018 | ProductEvent | nip15Marketplace/product | `productData()?.let { (listOfNotNull(it.name, it.description) + categories()).joinToString("\n") } ?: ""` |
| 30019 | MarketplaceEvent | nip15Marketplace/marketplace | `marketplaceData()?.let { listOfNotNull(it.name, it.about).joinToString("\n") } ?: ""` | | 30019 | MarketplaceEvent | nip15Marketplace/marketplace | `marketplaceData()?.let { listOfNotNull(it.name, it.about).joinToString("\n") } ?: ""` |
| 30020 | AuctionEvent | nip15Marketplace/auction | `auctionData()?.let { (listOfNotNull(it.name, it.description) + tags.hashtags()).joinToString("\n") } ?: ""` | | 30020 | AuctionEvent | nip15Marketplace/auction | `auctionData()?.let { (listOfNotNull(it.name, it.description) + tags.hashtags()).joinToString("\n") } ?: ""` |
| 30023 | LongTextNoteEvent | nip23LongContent | `listOfNotNull(title(), summary(), content)` NL | | 30023 | LongFormContentEvent | nip23LongContent | `listOfNotNull(title(), summary(), content)` NL |
| 30030 | EmojiPackEvent | nip30CustomEmoji/pack | `listOfNotNull(titleOrName(), description(), content)` NL | | 30030 | EmojiPackEvent | nip30CustomEmoji/pack | `listOfNotNull(titleOrName(), description(), content)` NL |
| 30054 | Podcasting20EpisodeEvent | nipXXPodcasting20/episode | `(listOfNotNull(title(), description(), content) + topics())` NL | | 30054 | Podcasting20EpisodeEvent | nipXXPodcasting20/episode | `(listOfNotNull(title(), description(), content) + topics())` NL |
| 30055 | Podcasting20TrailerEvent | nipXXPodcasting20/trailer | `listOfNotNull(title(), content)` NL | | 30055 | Podcasting20TrailerEvent | nipXXPodcasting20/trailer | `listOfNotNull(title(), content)` NL |
@@ -101,8 +101,8 @@ Separator legend: **NL** = `joinToString("\n")`, **SP** = `joinToString(" ")`.
| 30311 | LiveActivitiesEvent | nip53LiveActivities/streaming | `listOfNotNull(title(), summary(), content)` NL | | 30311 | LiveActivitiesEvent | nip53LiveActivities/streaming | `listOfNotNull(title(), summary(), content)` NL |
| 30312 | MeetingSpaceEvent | nip53LiveActivities/meetingSpaces | `listOfNotNull(room(), summary(), content)` NL | | 30312 | MeetingSpaceEvent | nip53LiveActivities/meetingSpaces | `listOfNotNull(room(), summary(), content)` NL |
| 30313 | MeetingRoomEvent | nip53LiveActivities/meetingSpaces | `listOfNotNull(title(), summary())` NL | | 30313 | MeetingRoomEvent | nip53LiveActivities/meetingSpaces | `listOfNotNull(title(), summary())` NL |
| 30315 | StatusEvent | nip38UserStatus | `content` | | 30315 | UserStatusEvent | nip38UserStatus | `content` |
| 30382 | ContactCardEvent | nip85TrustedAssertions/users | `(listOfNotNull(petName(), summary()) + topics())` NL — public tags only, never the NIP-44 content | | 30382 | UserAssertionEvent | nip85TrustedAssertions/users | `(listOfNotNull(petName(), summary()) + topics())` NL — public tags only, never the NIP-44 content |
| 30392 | UserTrustedListEvent | experimental/trustedLists/users | inherited `TrustedListEvent`: `title() ?: ""` — the label only; `metric`/`d` are machine ids and `content` is a JSON echo of the membership | | 30392 | UserTrustedListEvent | experimental/trustedLists/users | inherited `TrustedListEvent`: `title() ?: ""` — the label only; `metric`/`d` are machine ids and `content` is a JSON echo of the membership |
| 30393 | EventTrustedListEvent | experimental/trustedLists/events | inherited `TrustedListEvent`: `title() ?: ""` | | 30393 | EventTrustedListEvent | experimental/trustedLists/events | inherited `TrustedListEvent`: `title() ?: ""` |
| 30394 | AddressableTrustedListEvent | experimental/trustedLists/addressables | inherited `TrustedListEvent`: `title() ?: ""` | | 30394 | AddressableTrustedListEvent | experimental/trustedLists/addressables | inherited `TrustedListEvent`: `title() ?: ""` |
@@ -111,7 +111,7 @@ Separator legend: **NL** = `joinToString("\n")`, **SP** = `joinToString(" ")`.
| 30617 | GitRepositoryEvent | nip34Git/repository | `listOfNotNull(name(), description(), content)` NL | | 30617 | GitRepositoryEvent | nip34Git/repository | `listOfNotNull(name(), description(), content)` NL |
| 30620 | WorkflowDefEvent | buzz/workflow | `listOfNotNull(name(), content)` NL | | 30620 | WorkflowDefEvent | buzz/workflow | `listOfNotNull(name(), content)` NL |
| 30817 | NipTextEvent | experimental/nipsOnNostr | `listOfNotNull(title(), content)` NL | | 30817 | NipTextEvent | experimental/nipsOnNostr | `listOfNotNull(title(), content)` NL |
| 30818 | WikiNoteEvent | nip54Wiki | `listOfNotNull(title(), summary(), content)` NL | | 30818 | WikiArticleEvent | nip54Wiki | `listOfNotNull(title(), summary(), content)` NL |
| 31337 | AudioTrackEvent | experimental/audio/track | `listOfNotNull(subject())` NL | | 31337 | AudioTrackEvent | experimental/audio/track | `listOfNotNull(subject())` NL |
| 31871 | AttestationEvent | experimental/attestations/attestation | `content` | | 31871 | AttestationEvent | experimental/attestations/attestation | `content` |
| 31872 | AttestationRequestEvent | experimental/attestations/request | `content` | | 31872 | AttestationRequestEvent | experimental/attestations/request | `content` |
@@ -119,15 +119,15 @@ Separator legend: **NL** = `joinToString("\n")`, **SP** = `joinToString(" ")`.
| 31890 | FeedDefinitionEvent | feedDefinition | `title().orEmpty()` | | 31890 | FeedDefinitionEvent | feedDefinition | `title().orEmpty()` |
| 31922 | CalendarDateSlotEvent | nip52Calendar/appt/day | `listOfNotNull(title(), summary(), content)` NL | | 31922 | CalendarDateSlotEvent | nip52Calendar/appt/day | `listOfNotNull(title(), summary(), content)` NL |
| 31923 | CalendarTimeSlotEvent | nip52Calendar/appt/time | `listOfNotNull(title(), summary(), content)` NL | | 31923 | CalendarTimeSlotEvent | nip52Calendar/appt/time | `listOfNotNull(title(), summary(), content)` NL |
| 31924 | CalendarEvent | nip52Calendar/calendar | `listOfNotNull(title(), content)` NL | | 31924 | CalendarCollectionEvent | nip52Calendar/calendar | `listOfNotNull(title(), content)` NL |
| 31925 | CalendarRSVPEvent | nip52Calendar/rsvp | `content` | | 31925 | CalendarRSVPEvent | nip52Calendar/rsvp | `content` |
| 31990 | AppDefinitionEvent | nip89AppHandlers/definition | `appMetaData()?.let { listOfNotNull(it.name, it.username, it.displayName, it.about, it.nip05, it.lud06, it.lud16, it.website, it.picture, it.banner, it.image).joinToString(" ") } ?: ""` (SP) | | 31990 | AppDefinitionEvent | nip89AppHandlers/definition | `appMetaData()?.let { listOfNotNull(it.name, it.username, it.displayName, it.about, it.nip05, it.lud06, it.lud16, it.website, it.picture, it.banner, it.image).joinToString(" ") } ?: ""` (SP) |
| 32267 | SoftwareApplicationEvent | experimental/nip82SoftwareApps/application | `listOfNotNull(name(), summary(), content)` NL | | 32267 | SoftwareApplicationEvent | experimental/nip82SoftwareApps/application | `listOfNotNull(name(), summary(), content)` NL |
| 33401 | ExerciseTemplateEvent | experimental/fitness/workout | `listOfNotNull(title(), content)` NL | | 33401 | ExerciseTemplateEvent | experimental/fitness/workout | `listOfNotNull(title(), content)` NL |
| 33863 | FundraiserEvent | experimental/agora | `listOfNotNull(title(), content)` NL | | 33863 | FundraiserEvent | experimental/agora | `listOfNotNull(title(), content)` NL |
| 34139 | MusicPlaylistEvent | experimental/music/playlist | `listOfNotNull(title(), description(), content)` NL | | 34139 | MusicPlaylistEvent | experimental/music/playlist | `listOfNotNull(title(), description(), content)` NL |
| 34235 | VideoHorizontalEvent | nip71Video | inherited `AddressableVideoEvent`: `listOfNotNull(title(), content)` NL | | 34235 | AddressableNormalVideoEvent | nip71Video | inherited `AddressableVideoEvent`: `listOfNotNull(title(), content)` NL |
| 34236 | VideoVerticalEvent | nip71Video | inherited `AddressableVideoEvent`: `listOfNotNull(title(), content)` NL | | 34236 | AddressableShortVideoEvent | nip71Video | inherited `AddressableVideoEvent`: `listOfNotNull(title(), content)` NL |
| 34550 | CommunityDefinitionEvent | nip72ModCommunities/definition | `listOfNotNull(name(), description(), rules(), content)` NL | | 34550 | CommunityDefinitionEvent | nip72ModCommunities/definition | `listOfNotNull(name(), description(), rules(), content)` NL |
| 35128 | NamedSiteEvent | nip5aStaticWebsites | `listOfNotNull(title(), description())` NL | | 35128 | NamedSiteEvent | nip5aStaticWebsites | `listOfNotNull(title(), description())` NL |
| 35129 | NamedNappletEvent | nip5dNapplets | `listOfNotNull(title(), description())` NL | | 35129 | NamedNappletEvent | nip5dNapplets | `listOfNotNull(title(), description())` NL |
@@ -138,7 +138,7 @@ Separator legend: **NL** = `joinToString("\n")`, **SP** = `joinToString(" ")`.
| 38192 | Ps1SaveEvent | experimental/ps1saves | `listOfNotNull(summary(), saveTitle(), region(), filename())` NL | | 38192 | Ps1SaveEvent | experimental/ps1saves | `listOfNotNull(summary(), saveTitle(), region(), filename())` NL |
| 38383 | P2POrderEvent | nip69P2pOrderEvents | `(listOfNotNull(makerName(), currency()) + paymentMethods().orEmpty()).joinToString(" ")` (SP) | | 38383 | P2POrderEvent | nip69P2pOrderEvents | `(listOfNotNull(makerName(), currency()) + paymentMethods().orEmpty()).joinToString(" ")` (SP) |
| 39000 | GroupMetadataEvent | nip29RelayGroups/metadata | `listOfNotNull(name(), about())` NL | | 39000 | GroupMetadataEvent | nip29RelayGroups/metadata | `listOfNotNull(name(), about())` NL |
| 39089 | FollowListEvent | nip51Lists/followList | `listOfNotNull(title(), description())` NL | | 39089 | StarterPackEvent | nip51Lists/followList | `listOfNotNull(title(), description())` NL |
| 39092 | MediaStarterPackEvent | nip51Lists/mediaStarterPack | `listOfNotNull(title(), description())` NL | | 39092 | MediaStarterPackEvent | nip51Lists/mediaStarterPack | `listOfNotNull(title(), description())` NL |
| 39307 | TextTrackEvent | nip71Video/textTrack | `content` (the WebVTT cue text) | | 39307 | TextTrackEvent | nip71Video/textTrack | `content` (the WebVTT cue text) |
| 39701 | WebBookmarkEvent | nipB0WebBookmarks | `listOfNotNull(title(), description())` NL | | 39701 | WebBookmarkEvent | nipB0WebBookmarks | `listOfNotNull(title(), description())` NL |
@@ -54,19 +54,19 @@ import com.vitorpamplona.quartz.nip01Core.core.toHexKey
import com.vitorpamplona.quartz.nip01Core.crypto.KeyPair import com.vitorpamplona.quartz.nip01Core.crypto.KeyPair
import com.vitorpamplona.quartz.nip01Core.metadata.MetadataEvent import com.vitorpamplona.quartz.nip01Core.metadata.MetadataEvent
import com.vitorpamplona.quartz.nip02FollowList.ContactListEvent import com.vitorpamplona.quartz.nip02FollowList.ContactListEvent
import com.vitorpamplona.quartz.nip17Dm.settings.ChatMessageRelayListEvent import com.vitorpamplona.quartz.nip17Dm.settings.DmRelayListEvent
import com.vitorpamplona.quartz.nip19Bech32.toNpub import com.vitorpamplona.quartz.nip19Bech32.toNpub
import com.vitorpamplona.quartz.nip28PublicChat.list.ChannelListEvent import com.vitorpamplona.quartz.nip28PublicChat.list.PublicChatListEvent
import com.vitorpamplona.quartz.nip37Drafts.privateOutbox.PrivateOutboxRelayListEvent import com.vitorpamplona.quartz.nip37Drafts.privateOutbox.PrivateOutboxRelayListEvent
import com.vitorpamplona.quartz.nip47WalletConnect.Nip47WalletConnect import com.vitorpamplona.quartz.nip47WalletConnect.Nip47WalletConnect
import com.vitorpamplona.quartz.nip50Search.SearchRelayListEvent import com.vitorpamplona.quartz.nip50Search.SearchRelayListEvent
import com.vitorpamplona.quartz.nip51Lists.favoriteAlgoFeedsList.FavoriteAlgoFeedsListEvent import com.vitorpamplona.quartz.nip51Lists.favoriteAlgoFeedsList.FavoriteAlgoFeedsListEvent
import com.vitorpamplona.quartz.nip51Lists.geohashList.GeohashListEvent import com.vitorpamplona.quartz.nip51Lists.geohashList.GeohashListEvent
import com.vitorpamplona.quartz.nip51Lists.hashtagList.HashtagListEvent import com.vitorpamplona.quartz.nip51Lists.hashtagList.InterestListEvent
import com.vitorpamplona.quartz.nip51Lists.muteList.MuteListEvent import com.vitorpamplona.quartz.nip51Lists.muteList.MuteListEvent
import com.vitorpamplona.quartz.nip51Lists.relayLists.BlockedRelayListEvent import com.vitorpamplona.quartz.nip51Lists.relayLists.BlockedRelayListEvent
import com.vitorpamplona.quartz.nip51Lists.relayLists.FavoriteRelayListEvent
import com.vitorpamplona.quartz.nip51Lists.relayLists.IndexerRelayListEvent import com.vitorpamplona.quartz.nip51Lists.relayLists.IndexerRelayListEvent
import com.vitorpamplona.quartz.nip51Lists.relayLists.RelayFeedsListEvent
import com.vitorpamplona.quartz.nip51Lists.relayLists.TrustedRelayListEvent import com.vitorpamplona.quartz.nip51Lists.relayLists.TrustedRelayListEvent
import com.vitorpamplona.quartz.nip51Lists.simpleGroupList.SimpleGroupListEvent import com.vitorpamplona.quartz.nip51Lists.simpleGroupList.SimpleGroupListEvent
import com.vitorpamplona.quartz.nip65RelayList.AdvertisedRelayListEvent import com.vitorpamplona.quartz.nip65RelayList.AdvertisedRelayListEvent
@@ -887,19 +887,19 @@ object LocalPreferences {
val pendingAttestations = async { parseOrNull<Map<HexKey, String>>(pendingAttestationsStr) ?: mapOf() } val pendingAttestations = async { parseOrNull<Map<HexKey, String>>(pendingAttestationsStr) ?: mapOf() }
val latestUserMetadata = async { parseEventOrNull<MetadataEvent>(latestUserMetadataStr) } val latestUserMetadata = async { parseEventOrNull<MetadataEvent>(latestUserMetadataStr) }
val latestContactList = async { parseEventOrNull<ContactListEvent>(latestContactListStr) } val latestContactList = async { parseEventOrNull<ContactListEvent>(latestContactListStr) }
val latestDmRelayList = async { parseEventOrNull<ChatMessageRelayListEvent>(latestDmRelayListStr) } val latestDmRelayList = async { parseEventOrNull<DmRelayListEvent>(latestDmRelayListStr) }
val latestNip65RelayList = async { parseEventOrNull<AdvertisedRelayListEvent>(latestNip65RelayListStr) } val latestNip65RelayList = async { parseEventOrNull<AdvertisedRelayListEvent>(latestNip65RelayListStr) }
val latestSearchRelayList = async { parseEventOrNull<SearchRelayListEvent>(latestSearchRelayListStr) } val latestSearchRelayList = async { parseEventOrNull<SearchRelayListEvent>(latestSearchRelayListStr) }
val latestIndexRelayList = async { parseEventOrNull<IndexerRelayListEvent>(latestIndexRelayListStr) } val latestIndexRelayList = async { parseEventOrNull<IndexerRelayListEvent>(latestIndexRelayListStr) }
val latestRelayFeedsList = async { parseEventOrNull<RelayFeedsListEvent>(latestRelayFeedsListStr) } val latestRelayFeedsList = async { parseEventOrNull<FavoriteRelayListEvent>(latestRelayFeedsListStr) }
val latestBlockedRelayList = async { parseEventOrNull<BlockedRelayListEvent>(latestBlockedRelayListStr) } val latestBlockedRelayList = async { parseEventOrNull<BlockedRelayListEvent>(latestBlockedRelayListStr) }
val latestTrustedRelayList = async { parseEventOrNull<TrustedRelayListEvent>(latestTrustedRelayListStr) } val latestTrustedRelayList = async { parseEventOrNull<TrustedRelayListEvent>(latestTrustedRelayListStr) }
val latestMuteList = async { parseEventOrNull<MuteListEvent>(latestMuteListStr) } val latestMuteList = async { parseEventOrNull<MuteListEvent>(latestMuteListStr) }
val latestPrivateHomeRelayList = async { parseEventOrNull<PrivateOutboxRelayListEvent>(latestPrivateHomeRelayListStr) } val latestPrivateHomeRelayList = async { parseEventOrNull<PrivateOutboxRelayListEvent>(latestPrivateHomeRelayListStr) }
val latestAppSpecificData = async { parseEventOrNull<AppSpecificDataEvent>(latestAppSpecificDataStr) } val latestAppSpecificData = async { parseEventOrNull<AppSpecificDataEvent>(latestAppSpecificDataStr) }
val latestChannelList = async { parseEventOrNull<ChannelListEvent>(latestChannelListStr) } val latestChannelList = async { parseEventOrNull<PublicChatListEvent>(latestChannelListStr) }
val latestCommunityList = async { parseEventOrNull<CommunityListEvent>(latestCommunityListStr) } val latestCommunityList = async { parseEventOrNull<CommunityListEvent>(latestCommunityListStr) }
val latestHashtagList = async { parseEventOrNull<HashtagListEvent>(latestHashtagListStr) } val latestHashtagList = async { parseEventOrNull<InterestListEvent>(latestHashtagListStr) }
val latestGeohashList = async { parseEventOrNull<GeohashListEvent>(latestGeohashListStr) } val latestGeohashList = async { parseEventOrNull<GeohashListEvent>(latestGeohashListStr) }
val latestEphemeralList = async { parseEventOrNull<EphemeralChatListEvent>(latestEphemeralListStr) } val latestEphemeralList = async { parseEventOrNull<EphemeralChatListEvent>(latestEphemeralListStr) }
val latestRelayGroupList = async { parseEventOrNull<SimpleGroupListEvent>(latestRelayGroupListStr) } val latestRelayGroupList = async { parseEventOrNull<SimpleGroupListEvent>(latestRelayGroupListStr) }
@@ -238,8 +238,8 @@ import com.vitorpamplona.quartz.nip01Core.tags.people.taggedUserIds
import com.vitorpamplona.quartz.nip01Core.tags.references.references import com.vitorpamplona.quartz.nip01Core.tags.references.references
import com.vitorpamplona.quartz.nip03Timestamp.OtsResolver import com.vitorpamplona.quartz.nip03Timestamp.OtsResolver
import com.vitorpamplona.quartz.nip04Dm.PrivateDMCache import com.vitorpamplona.quartz.nip04Dm.PrivateDMCache
import com.vitorpamplona.quartz.nip04Dm.messages.PrivateDmEvent import com.vitorpamplona.quartz.nip04Dm.messages.EncryptedDmEvent
import com.vitorpamplona.quartz.nip09Deletions.DeletionEvent import com.vitorpamplona.quartz.nip09Deletions.DeletionRequestEvent
import com.vitorpamplona.quartz.nip10Notes.TextNoteEvent import com.vitorpamplona.quartz.nip10Notes.TextNoteEvent
import com.vitorpamplona.quartz.nip10Notes.content.findHashtags import com.vitorpamplona.quartz.nip10Notes.content.findHashtags
import com.vitorpamplona.quartz.nip10Notes.content.findNostrUris import com.vitorpamplona.quartz.nip10Notes.content.findNostrUris
@@ -281,10 +281,10 @@ import com.vitorpamplona.quartz.nip51Lists.bookmarkList.BookmarkListEvent
import com.vitorpamplona.quartz.nip51Lists.bookmarkList.tags.AddressBookmark import com.vitorpamplona.quartz.nip51Lists.bookmarkList.tags.AddressBookmark
import com.vitorpamplona.quartz.nip56Reports.ReportEvent import com.vitorpamplona.quartz.nip56Reports.ReportEvent
import com.vitorpamplona.quartz.nip56Reports.ReportType import com.vitorpamplona.quartz.nip56Reports.ReportType
import com.vitorpamplona.quartz.nip57Zaps.LnZapEvent
import com.vitorpamplona.quartz.nip57Zaps.LnZapPrivateEvent import com.vitorpamplona.quartz.nip57Zaps.LnZapPrivateEvent
import com.vitorpamplona.quartz.nip57Zaps.LnZapRequestEvent
import com.vitorpamplona.quartz.nip57Zaps.PrivateZapCache import com.vitorpamplona.quartz.nip57Zaps.PrivateZapCache
import com.vitorpamplona.quartz.nip57Zaps.ZapReceiptEvent
import com.vitorpamplona.quartz.nip57Zaps.ZapRequestEvent
import com.vitorpamplona.quartz.nip57Zaps.splits.ZapSplitSetup import com.vitorpamplona.quartz.nip57Zaps.splits.ZapSplitSetup
import com.vitorpamplona.quartz.nip57Zaps.splits.zapSplits import com.vitorpamplona.quartz.nip57Zaps.splits.zapSplits
import com.vitorpamplona.quartz.nip57Zaps.zapraiser.zapraiser import com.vitorpamplona.quartz.nip57Zaps.zapraiser.zapraiser
@@ -320,10 +320,10 @@ import com.vitorpamplona.quartz.nip88Polls.poll.tags.PollType
import com.vitorpamplona.quartz.nip88Polls.response.PollResponseEvent import com.vitorpamplona.quartz.nip88Polls.response.PollResponseEvent
import com.vitorpamplona.quartz.nip89AppHandlers.clientTag.NostrSignerWithClientTag import com.vitorpamplona.quartz.nip89AppHandlers.clientTag.NostrSignerWithClientTag
import com.vitorpamplona.quartz.nip89AppHandlers.clientTag.withoutClientTag import com.vitorpamplona.quartz.nip89AppHandlers.clientTag.withoutClientTag
import com.vitorpamplona.quartz.nip90Dvms.contentDiscoveryRequest.NIP90ContentDiscoveryRequestEvent import com.vitorpamplona.quartz.nip90Dvms.contentDiscoveryRequest.DvmContentDiscoveryRequestEvent
import com.vitorpamplona.quartz.nip92IMeta.IMetaTag import com.vitorpamplona.quartz.nip92IMeta.IMetaTag
import com.vitorpamplona.quartz.nip92IMeta.imetas import com.vitorpamplona.quartz.nip92IMeta.imetas
import com.vitorpamplona.quartz.nip94FileMetadata.FileHeaderEvent import com.vitorpamplona.quartz.nip94FileMetadata.FileMetadataEvent
import com.vitorpamplona.quartz.nip94FileMetadata.blurhash import com.vitorpamplona.quartz.nip94FileMetadata.blurhash
import com.vitorpamplona.quartz.nip94FileMetadata.dimension import com.vitorpamplona.quartz.nip94FileMetadata.dimension
import com.vitorpamplona.quartz.nip94FileMetadata.fileSize import com.vitorpamplona.quartz.nip94FileMetadata.fileSize
@@ -1283,7 +1283,7 @@ class Account(
suspend fun updateZapAmounts( suspend fun updateZapAmounts(
amountSet: List<Long>, amountSet: List<Long>,
selectedZapType: LnZapEvent.ZapType, selectedZapType: ZapReceiptEvent.ZapType,
nip47Update: Nip47WalletConnect.Nip47URINorm?, nip47Update: Nip47WalletConnect.Nip47URINorm?,
) { ) {
var changed = false var changed = false
@@ -1707,7 +1707,7 @@ class Account(
if (myNotes.isNotEmpty()) { if (myNotes.isNotEmpty()) {
// chunks in 200 elements to avoid going over the 65KB limit for events. // chunks in 200 elements to avoid going over the 65KB limit for events.
myNotes.chunked(200).forEach { chunkedList -> myNotes.chunked(200).forEach { chunkedList ->
val template = DeletionEvent.build(chunkedList.mapNotNull { it.event }) val template = DeletionRequestEvent.build(chunkedList.mapNotNull { it.event })
val deletionEvent = signer.sign(template) val deletionEvent = signer.sign(template)
val myRelayList = outboxRelays.flow.value.toMutableSet() val myRelayList = outboxRelays.flow.value.toMutableSet()
chunkedList.forEach { chunkedList.forEach {
@@ -1738,7 +1738,7 @@ class Account(
val recipients = (targetEvent.taggedUserIds() + targetEvent.pubKey).distinct().minus(signer.pubKey) val recipients = (targetEvent.taggedUserIds() + targetEvent.pubKey).distinct().minus(signer.pubKey)
broadcastPrivately( broadcastPrivately(
NIP17Factory().createDeletionNIP17(DeletionEvent.build(myRumors), recipients, signer), NIP17Factory().createDeletionNIP17(DeletionRequestEvent.build(myRumors), recipients, signer),
) )
} }
@@ -1749,7 +1749,7 @@ class Account(
if (!isWriteable()) return if (!isWriteable()) return
if (event.pubKey != signer.pubKey) return if (event.pubKey != signer.pubKey) return
val deletionEvent = signer.sign(DeletionEvent.build(listOf(event))) val deletionEvent = signer.sign(DeletionRequestEvent.build(listOf(event)))
client.publish(deletionEvent, outboxRelays.flow.value + additionalRelays) client.publish(deletionEvent, outboxRelays.flow.value + additionalRelays)
cache.justConsumeMyOwnEvent(deletionEvent) cache.justConsumeMyOwnEvent(deletionEvent)
} }
@@ -2176,7 +2176,7 @@ class Account(
suspend fun deleteWebBookmark(event: WebBookmarkEvent) { suspend fun deleteWebBookmark(event: WebBookmarkEvent) {
if (!isWriteable()) return if (!isWriteable()) return
val template = DeletionEvent.build(listOf(event)) val template = DeletionRequestEvent.build(listOf(event))
val signedEvent = signer.sign(template) val signedEvent = signer.sign(template)
cache.justConsumeMyOwnEvent(signedEvent) cache.justConsumeMyOwnEvent(signedEvent)
@@ -2212,7 +2212,7 @@ class Account(
if (!isWriteable()) return if (!isWriteable()) return
if (event.pubKey != signer.pubKey) return if (event.pubKey != signer.pubKey) return
val template = DeletionEvent.build(listOf(event)) val template = DeletionRequestEvent.build(listOf(event))
val signedEvent = signer.sign(template) val signedEvent = signer.sign(template)
cache.justConsumeMyOwnEvent(signedEvent) cache.justConsumeMyOwnEvent(signedEvent)
@@ -2597,7 +2597,7 @@ class Account(
} }
} }
} else { } else {
FileHeaderEvent.build(url, alt) { FileMetadataEvent.build(url, alt) {
hash(headerInfo.hash) hash(headerInfo.hash)
fileSize(headerInfo.size) fileSize(headerInfo.size)
@@ -2711,7 +2711,7 @@ class Account(
val extraRelays = draftNote.relays val extraRelays = draftNote.relays
val deletedDraft = DraftWrapEvent.createDeletedEvent(draftTag, signer) val deletedDraft = DraftWrapEvent.createDeletedEvent(draftTag, signer)
val deletionEvent = signer.sign(DeletionEvent.build(listOf(deletedDraft))) val deletionEvent = signer.sign(DeletionRequestEvent.build(listOf(deletedDraft)))
val relayList = (privateStorageRelayList.flow.value + localRelayList.flow.value + extraRelays).toSet() val relayList = (privateStorageRelayList.flow.value + localRelayList.flow.value + extraRelays).toSet()
@@ -2937,7 +2937,7 @@ class Account(
broadcast.forEach { client.publish(it, relayList) } broadcast.forEach { client.publish(it, relayList) }
} }
override suspend fun sendNip04PrivateMessage(eventTemplate: EventTemplate<PrivateDmEvent>) { override suspend fun sendNip04PrivateMessage(eventTemplate: EventTemplate<EncryptedDmEvent>) {
if (!isWriteable()) return if (!isWriteable()) return
val newEvent = signer.sign(eventTemplate) val newEvent = signer.sign(eventTemplate)
@@ -3481,10 +3481,10 @@ class Account(
suspend fun requestDVMContentDiscovery( suspend fun requestDVMContentDiscovery(
dvmPublicKey: User, dvmPublicKey: User,
onReady: (event: NIP90ContentDiscoveryRequestEvent, relays: Set<NormalizedRelayUrl>) -> Unit, onReady: (event: DvmContentDiscoveryRequestEvent, relays: Set<NormalizedRelayUrl>) -> Unit,
) { ) {
val relays = nip65RelayList.inboxFlow.value.toSet() val relays = nip65RelayList.inboxFlow.value.toSet()
val request = signer.sign<NIP90ContentDiscoveryRequestEvent>(NIP90ContentDiscoveryRequestEvent.build(dvmPublicKey.pubkeyHex, signer.pubKey, relays)) val request = signer.sign<DvmContentDiscoveryRequestEvent>(DvmContentDiscoveryRequestEvent.build(dvmPublicKey.pubkeyHex, signer.pubKey, relays))
val relayList = val relayList =
dvmPublicKey.inboxRelays()?.toSet()?.ifEmpty { null } dvmPublicKey.inboxRelays()?.toSet()?.ifEmpty { null }
@@ -3503,8 +3503,8 @@ class Account(
return if (isWriteable()) { return if (isWriteable()) {
when { when {
event is PrivateDmEvent -> privateDMDecryptionCache.cachedDM(event) event is EncryptedDmEvent -> privateDMDecryptionCache.cachedDM(event)
event is LnZapRequestEvent && event.isPrivateZap() -> privateZapsDecryptionCache.cachedPrivateZap(event)?.content event is ZapRequestEvent && event.isPrivateZap() -> privateZapsDecryptionCache.cachedPrivateZap(event)?.content
event is DraftWrapEvent -> draftsDecryptionCache.preCachedDraft(event)?.content event is DraftWrapEvent -> draftsDecryptionCache.preCachedDraft(event)?.content
else -> event.content else -> event.content
} }
@@ -3519,11 +3519,11 @@ class Account(
suspend fun decryptContent(note: Note): String? { suspend fun decryptContent(note: Note): String? {
val event = note.event val event = note.event
return when { return when {
event is PrivateDmEvent && isWriteable() -> { event is EncryptedDmEvent && isWriteable() -> {
privateDMDecryptionCache.decryptDM(event) privateDMDecryptionCache.decryptDM(event)
} }
event is LnZapRequestEvent && isWriteable() -> { event is ZapRequestEvent && isWriteable() -> {
if (event.isPrivateZap()) { if (event.isPrivateZap()) {
if (isWriteable()) { if (isWriteable()) {
privateZapsDecryptionCache.decryptPrivateZap(event)?.content privateZapsDecryptionCache.decryptPrivateZap(event)?.content
@@ -3550,7 +3550,7 @@ class Account(
} }
} }
suspend fun decryptZapOrNull(event: LnZapRequestEvent): LnZapPrivateEvent? = if (event.isPrivateZap() && isWriteable()) privateZapsDecryptionCache.decryptPrivateZap(event) else null suspend fun decryptZapOrNull(event: ZapRequestEvent): LnZapPrivateEvent? = if (event.isPrivateZap() && isWriteable()) privateZapsDecryptionCache.decryptPrivateZap(event) else null
fun isAllHidden(users: Set<HexKey>): Boolean = users.all { isHidden(it) } fun isAllHidden(users: Set<HexKey>): Boolean = users.all { isHidden(it) }
@@ -3602,7 +3602,7 @@ class Account(
note.countReportAuthorsBy(followingKeySet()) < reportWarningThreshold note.countReportAuthorsBy(followingKeySet()) < reportWarningThreshold
} }
fun isDecryptedContentHidden(noteEvent: PrivateDmEvent): Boolean = fun isDecryptedContentHidden(noteEvent: EncryptedDmEvent): Boolean =
if (hiddenUsers.flow.value.hiddenWordsCase if (hiddenUsers.flow.value.hiddenWordsCase
.isNotEmpty() .isNotEmpty()
) { ) {
@@ -58,15 +58,15 @@ import com.vitorpamplona.quartz.nip29RelayGroups.GroupId
import com.vitorpamplona.quartz.nip29RelayGroups.hTag import com.vitorpamplona.quartz.nip29RelayGroups.hTag
import com.vitorpamplona.quartz.nip29RelayGroups.metadata.GroupMetadataEvent import com.vitorpamplona.quartz.nip29RelayGroups.metadata.GroupMetadataEvent
import com.vitorpamplona.quartz.nip29RelayGroups.moderation.CreateGroupEvent import com.vitorpamplona.quartz.nip29RelayGroups.moderation.CreateGroupEvent
import com.vitorpamplona.quartz.nip29RelayGroups.moderation.CreateInviteEvent
import com.vitorpamplona.quartz.nip29RelayGroups.moderation.DeleteGroupEvent import com.vitorpamplona.quartz.nip29RelayGroups.moderation.DeleteGroupEvent
import com.vitorpamplona.quartz.nip29RelayGroups.moderation.EditMetadataEvent import com.vitorpamplona.quartz.nip29RelayGroups.moderation.GroupCreateInviteEvent
import com.vitorpamplona.quartz.nip29RelayGroups.moderation.PutUserEvent import com.vitorpamplona.quartz.nip29RelayGroups.moderation.GroupEditMetadataEvent
import com.vitorpamplona.quartz.nip29RelayGroups.moderation.RemoveUserEvent import com.vitorpamplona.quartz.nip29RelayGroups.moderation.GroupPutUserEvent
import com.vitorpamplona.quartz.nip29RelayGroups.moderation.UpdatePinListEvent import com.vitorpamplona.quartz.nip29RelayGroups.moderation.GroupRemoveUserEvent
import com.vitorpamplona.quartz.nip29RelayGroups.moderation.GroupUpdatePinListEvent
import com.vitorpamplona.quartz.nip29RelayGroups.moderation.previous import com.vitorpamplona.quartz.nip29RelayGroups.moderation.previous
import com.vitorpamplona.quartz.nip29RelayGroups.request.JoinRequestEvent import com.vitorpamplona.quartz.nip29RelayGroups.request.GroupJoinRequestEvent
import com.vitorpamplona.quartz.nip29RelayGroups.request.LeaveRequestEvent import com.vitorpamplona.quartz.nip29RelayGroups.request.GroupLeaveRequestEvent
import com.vitorpamplona.quartz.nip29RelayGroups.tags.GroupIdTag import com.vitorpamplona.quartz.nip29RelayGroups.tags.GroupIdTag
import com.vitorpamplona.quartz.nip7DThreads.ThreadEvent import com.vitorpamplona.quartz.nip7DThreads.ThreadEvent
import com.vitorpamplona.quartz.utils.RandomInstance import com.vitorpamplona.quartz.utils.RandomInstance
@@ -92,7 +92,7 @@ class AccountRelayGroupActions(
channel: RelayGroupChannel, channel: RelayGroupChannel,
code: String? = null, code: String? = null,
) { ) {
val template = JoinRequestEvent.build(channel.groupId.id, inviteCode = code) val template = GroupJoinRequestEvent.build(channel.groupId.id, inviteCode = code)
account.broadcaster.signAndSendPrivatelyOrBroadcast(template) { channel.relays().toList() } account.broadcaster.signAndSendPrivatelyOrBroadcast(template) { channel.relays().toList() }
account.follow(channel) account.follow(channel)
} }
@@ -295,7 +295,7 @@ class AccountRelayGroupActions(
/** Send a kind 9022 leave request to the host relay and drop it from our list. */ /** Send a kind 9022 leave request to the host relay and drop it from our list. */
suspend fun leaveRelayGroup(channel: RelayGroupChannel) { suspend fun leaveRelayGroup(channel: RelayGroupChannel) {
val template = LeaveRequestEvent.build(channel.groupId.id) val template = GroupLeaveRequestEvent.build(channel.groupId.id)
account.broadcaster.signAndSendPrivatelyOrBroadcast(template) { channel.relays().toList() } account.broadcaster.signAndSendPrivatelyOrBroadcast(template) { channel.relays().toList() }
account.unfollow(channel) account.unfollow(channel)
} }
@@ -351,7 +351,7 @@ class AccountRelayGroupActions(
) { listOf(relay) } ) { listOf(relay) }
val edit = val edit =
EditMetadataEvent.build( GroupEditMetadataEvent.build(
groupId, groupId,
name = name, name = name,
about = about, about = about,
@@ -405,7 +405,7 @@ class AccountRelayGroupActions(
channel: RelayGroupChannel, channel: RelayGroupChannel,
code: String, code: String,
) { ) {
val template = CreateInviteEvent.build(channel.groupId.id, code) val template = GroupCreateInviteEvent.build(channel.groupId.id, code)
account.broadcaster.signAndSendPrivatelyOrBroadcast(template) { channel.relays().toList() } account.broadcaster.signAndSendPrivatelyOrBroadcast(template) { channel.relays().toList() }
} }
@@ -418,7 +418,7 @@ class AccountRelayGroupActions(
channel: RelayGroupChannel, channel: RelayGroupChannel,
pinnedEventIds: List<HexKey>, pinnedEventIds: List<HexKey>,
) { ) {
val template = UpdatePinListEvent.build(channel.groupId.id, pinnedEventIds) val template = GroupUpdatePinListEvent.build(channel.groupId.id, pinnedEventIds)
account.broadcaster.signAndSendPrivatelyOrBroadcast(template) { channel.relays().toList() } account.broadcaster.signAndSendPrivatelyOrBroadcast(template) { channel.relays().toList() }
} }
@@ -445,7 +445,7 @@ class AccountRelayGroupActions(
channel: RelayGroupChannel, channel: RelayGroupChannel,
pubkey: HexKey, pubkey: HexKey,
) { ) {
val template = RemoveUserEvent.build(channel.groupId.id, listOf(pubkey)) val template = GroupRemoveUserEvent.build(channel.groupId.id, listOf(pubkey))
account.broadcaster.signAndSendPrivatelyOrBroadcast(template) { channel.relays().toList() } account.broadcaster.signAndSendPrivatelyOrBroadcast(template) { channel.relays().toList() }
} }
@@ -470,7 +470,7 @@ class AccountRelayGroupActions(
} else { } else {
null null
} }
val template = PutUserEvent.build(channel.groupId.id, listOf(pubkey to roles), buzzRole = buzzRole) val template = GroupPutUserEvent.build(channel.groupId.id, listOf(pubkey to roles), buzzRole = buzzRole)
account.broadcaster.signAndSendPrivatelyOrBroadcast(template) { channel.relays().toList() } account.broadcaster.signAndSendPrivatelyOrBroadcast(template) { channel.relays().toList() }
} }
@@ -525,7 +525,7 @@ class AccountRelayGroupActions(
// edit when we send that tag. A plain NIP-29 relay ignores it and honours the status flag. // edit when we send that tag. A plain NIP-29 relay ignores it and honours the status flag.
val isBuzz = BuzzRelayDialect.isBuzz(channel.groupId.relayUrl) val isBuzz = BuzzRelayDialect.isBuzz(channel.groupId.relayUrl)
val template = val template =
EditMetadataEvent.build( GroupEditMetadataEvent.build(
channel.groupId.id, channel.groupId.id,
name = name, name = name,
about = about, about = about,
@@ -549,7 +549,7 @@ class AccountRelayGroupActions(
channel: RelayGroupChannel, channel: RelayGroupChannel,
archived: Boolean, archived: Boolean,
) { ) {
val template = EditMetadataEvent.build(channel.groupId.id, archived = archived) val template = GroupEditMetadataEvent.build(channel.groupId.id, archived = archived)
account.broadcaster.signAndSendPrivatelyOrBroadcast(template) { channel.relays().toList() } account.broadcaster.signAndSendPrivatelyOrBroadcast(template) { channel.relays().toList() }
} }
} }
@@ -59,8 +59,8 @@ import com.vitorpamplona.quartz.nip01Core.crypto.KeyPair
import com.vitorpamplona.quartz.nip01Core.metadata.MetadataEvent import com.vitorpamplona.quartz.nip01Core.metadata.MetadataEvent
import com.vitorpamplona.quartz.nip02FollowList.ContactListEvent import com.vitorpamplona.quartz.nip02FollowList.ContactListEvent
import com.vitorpamplona.quartz.nip17Dm.base.ChatroomKey import com.vitorpamplona.quartz.nip17Dm.base.ChatroomKey
import com.vitorpamplona.quartz.nip17Dm.settings.ChatMessageRelayListEvent import com.vitorpamplona.quartz.nip17Dm.settings.DmRelayListEvent
import com.vitorpamplona.quartz.nip28PublicChat.list.ChannelListEvent import com.vitorpamplona.quartz.nip28PublicChat.list.PublicChatListEvent
import com.vitorpamplona.quartz.nip37Drafts.DraftWrapEvent import com.vitorpamplona.quartz.nip37Drafts.DraftWrapEvent
import com.vitorpamplona.quartz.nip37Drafts.privateOutbox.PrivateOutboxRelayListEvent import com.vitorpamplona.quartz.nip37Drafts.privateOutbox.PrivateOutboxRelayListEvent
import com.vitorpamplona.quartz.nip42RelayAuth.RelayAuthEvent import com.vitorpamplona.quartz.nip42RelayAuth.RelayAuthEvent
@@ -68,16 +68,16 @@ import com.vitorpamplona.quartz.nip47WalletConnect.Nip47WalletConnect
import com.vitorpamplona.quartz.nip50Search.SearchRelayListEvent import com.vitorpamplona.quartz.nip50Search.SearchRelayListEvent
import com.vitorpamplona.quartz.nip51Lists.favoriteAlgoFeedsList.FavoriteAlgoFeedsListEvent import com.vitorpamplona.quartz.nip51Lists.favoriteAlgoFeedsList.FavoriteAlgoFeedsListEvent
import com.vitorpamplona.quartz.nip51Lists.geohashList.GeohashListEvent import com.vitorpamplona.quartz.nip51Lists.geohashList.GeohashListEvent
import com.vitorpamplona.quartz.nip51Lists.hashtagList.HashtagListEvent import com.vitorpamplona.quartz.nip51Lists.hashtagList.InterestListEvent
import com.vitorpamplona.quartz.nip51Lists.muteList.MuteListEvent import com.vitorpamplona.quartz.nip51Lists.muteList.MuteListEvent
import com.vitorpamplona.quartz.nip51Lists.relayLists.BlockedRelayListEvent import com.vitorpamplona.quartz.nip51Lists.relayLists.BlockedRelayListEvent
import com.vitorpamplona.quartz.nip51Lists.relayLists.FavoriteRelayListEvent
import com.vitorpamplona.quartz.nip51Lists.relayLists.IndexerRelayListEvent import com.vitorpamplona.quartz.nip51Lists.relayLists.IndexerRelayListEvent
import com.vitorpamplona.quartz.nip51Lists.relayLists.RelayFeedsListEvent
import com.vitorpamplona.quartz.nip51Lists.relayLists.TrustedRelayListEvent import com.vitorpamplona.quartz.nip51Lists.relayLists.TrustedRelayListEvent
import com.vitorpamplona.quartz.nip51Lists.simpleGroupList.SimpleGroupListEvent import com.vitorpamplona.quartz.nip51Lists.simpleGroupList.SimpleGroupListEvent
import com.vitorpamplona.quartz.nip55AndroidSigner.api.CommandType import com.vitorpamplona.quartz.nip55AndroidSigner.api.CommandType
import com.vitorpamplona.quartz.nip55AndroidSigner.api.permission.Permission import com.vitorpamplona.quartz.nip55AndroidSigner.api.permission.Permission
import com.vitorpamplona.quartz.nip57Zaps.LnZapEvent import com.vitorpamplona.quartz.nip57Zaps.ZapReceiptEvent
import com.vitorpamplona.quartz.nip60Cashu.wallet.CashuWalletEvent import com.vitorpamplona.quartz.nip60Cashu.wallet.CashuWalletEvent
import com.vitorpamplona.quartz.nip61Nutzaps.info.NutzapInfoEvent import com.vitorpamplona.quartz.nip61Nutzaps.info.NutzapInfoEvent
import com.vitorpamplona.quartz.nip65RelayList.AdvertisedRelayListEvent import com.vitorpamplona.quartz.nip65RelayList.AdvertisedRelayListEvent
@@ -203,20 +203,20 @@ class AccountSettings(
val showMessagesInNotifications: MutableStateFlow<Boolean> = MutableStateFlow(true), val showMessagesInNotifications: MutableStateFlow<Boolean> = MutableStateFlow(true),
var backupUserMetadata: MetadataEvent? = null, var backupUserMetadata: MetadataEvent? = null,
var backupContactList: ContactListEvent? = null, var backupContactList: ContactListEvent? = null,
var backupDMRelayList: ChatMessageRelayListEvent? = null, var backupDMRelayList: DmRelayListEvent? = null,
var backupKeyPackageRelayList: KeyPackageRelayListEvent? = null, var backupKeyPackageRelayList: KeyPackageRelayListEvent? = null,
var backupNIP65RelayList: AdvertisedRelayListEvent? = null, var backupNIP65RelayList: AdvertisedRelayListEvent? = null,
var backupSearchRelayList: SearchRelayListEvent? = null, var backupSearchRelayList: SearchRelayListEvent? = null,
var backupIndexRelayList: IndexerRelayListEvent? = null, var backupIndexRelayList: IndexerRelayListEvent? = null,
var backupRelayFeedsList: RelayFeedsListEvent? = null, var backupRelayFeedsList: FavoriteRelayListEvent? = null,
var backupBlockedRelayList: BlockedRelayListEvent? = null, var backupBlockedRelayList: BlockedRelayListEvent? = null,
var backupTrustedRelayList: TrustedRelayListEvent? = null, var backupTrustedRelayList: TrustedRelayListEvent? = null,
var backupMuteList: MuteListEvent? = null, var backupMuteList: MuteListEvent? = null,
var backupPrivateHomeRelayList: PrivateOutboxRelayListEvent? = null, var backupPrivateHomeRelayList: PrivateOutboxRelayListEvent? = null,
var backupAppSpecificData: AppSpecificDataEvent? = null, var backupAppSpecificData: AppSpecificDataEvent? = null,
var backupChannelList: ChannelListEvent? = null, var backupChannelList: PublicChatListEvent? = null,
var backupCommunityList: CommunityListEvent? = null, var backupCommunityList: CommunityListEvent? = null,
var backupHashtagList: HashtagListEvent? = null, var backupHashtagList: InterestListEvent? = null,
var backupFavoriteAlgoFeedsList: FavoriteAlgoFeedsListEvent? = null, var backupFavoriteAlgoFeedsList: FavoriteAlgoFeedsListEvent? = null,
var backupGeohashList: GeohashListEvent? = null, var backupGeohashList: GeohashListEvent? = null,
var backupEphemeralChatList: EphemeralChatListEvent? = null, var backupEphemeralChatList: EphemeralChatListEvent? = null,
@@ -375,7 +375,7 @@ class AccountSettings(
// Zaps and Reactions // Zaps and Reactions
// --- // ---
fun changeDefaultZapType(zapType: LnZapEvent.ZapType): Boolean { fun changeDefaultZapType(zapType: ZapReceiptEvent.ZapType): Boolean {
if (syncedSettings.zaps.defaultZapType.value != zapType) { if (syncedSettings.zaps.defaultZapType.value != zapType) {
syncedSettings.zaps.defaultZapType.tryEmit(zapType) syncedSettings.zaps.defaultZapType.tryEmit(zapType)
saveAccountSettings() saveAccountSettings()
@@ -1075,7 +1075,7 @@ class AccountSettings(
when (incoming) { when (incoming) {
is MetadataEvent -> updateUserMetadata(incoming) is MetadataEvent -> updateUserMetadata(incoming)
is ContactListEvent -> updateContactListTo(incoming) is ContactListEvent -> updateContactListTo(incoming)
is ChatMessageRelayListEvent -> updateDMRelayList(incoming) is DmRelayListEvent -> updateDMRelayList(incoming)
is KeyPackageRelayListEvent -> updateKeyPackageRelayList(incoming) is KeyPackageRelayListEvent -> updateKeyPackageRelayList(incoming)
is AdvertisedRelayListEvent -> updateNIP65RelayList(incoming) is AdvertisedRelayListEvent -> updateNIP65RelayList(incoming)
is CashuWalletEvent -> updateCashuWallet(incoming) is CashuWalletEvent -> updateCashuWallet(incoming)
@@ -1084,13 +1084,13 @@ class AccountSettings(
is Bolt12OfferListEvent -> updateBolt12Offers(incoming) is Bolt12OfferListEvent -> updateBolt12Offers(incoming)
is SearchRelayListEvent -> updateSearchRelayList(incoming) is SearchRelayListEvent -> updateSearchRelayList(incoming)
is IndexerRelayListEvent -> updateIndexRelayList(incoming) is IndexerRelayListEvent -> updateIndexRelayList(incoming)
is RelayFeedsListEvent -> updateRelayFeedList(incoming) is FavoriteRelayListEvent -> updateRelayFeedList(incoming)
is BlockedRelayListEvent -> updateBlockedRelayList(incoming) is BlockedRelayListEvent -> updateBlockedRelayList(incoming)
is TrustedRelayListEvent -> updateTrustedRelayList(incoming) is TrustedRelayListEvent -> updateTrustedRelayList(incoming)
is PrivateOutboxRelayListEvent -> updatePrivateHomeRelayList(incoming) is PrivateOutboxRelayListEvent -> updatePrivateHomeRelayList(incoming)
is ChannelListEvent -> updateChannelListTo(incoming) is PublicChatListEvent -> updateChannelListTo(incoming)
is GeohashListEvent -> updateGeohashListTo(incoming) is GeohashListEvent -> updateGeohashListTo(incoming)
is HashtagListEvent -> updateHashtagListTo(incoming) is InterestListEvent -> updateHashtagListTo(incoming)
is FavoriteAlgoFeedsListEvent -> updateFavoriteAlgoFeedsListTo(incoming) is FavoriteAlgoFeedsListEvent -> updateFavoriteAlgoFeedsListTo(incoming)
is CommunityListEvent -> updateCommunityListTo(incoming) is CommunityListEvent -> updateCommunityListTo(incoming)
is EphemeralChatListEvent -> updateEphemeralChatListTo(incoming) is EphemeralChatListEvent -> updateEphemeralChatListTo(incoming)
@@ -1140,7 +1140,7 @@ class AccountSettings(
} }
} }
fun updateDMRelayList(newDMRelayList: ChatMessageRelayListEvent?) { fun updateDMRelayList(newDMRelayList: DmRelayListEvent?) {
if (newDMRelayList == null) return if (newDMRelayList == null) return
if (backupGuard.accept(backupDMRelayList, newDMRelayList, isEmpty = newDMRelayList.tags.isEmpty()) { updateDMRelayList(newDMRelayList) }) { if (backupGuard.accept(backupDMRelayList, newDMRelayList, isEmpty = newDMRelayList.tags.isEmpty()) { updateDMRelayList(newDMRelayList) }) {
@@ -1284,7 +1284,7 @@ class AccountSettings(
} }
} }
fun updateRelayFeedList(newRelayFeedList: RelayFeedsListEvent?) { fun updateRelayFeedList(newRelayFeedList: FavoriteRelayListEvent?) {
if (newRelayFeedList == null) return if (newRelayFeedList == null) return
if (backupGuard.accept(backupRelayFeedsList, newRelayFeedList, isEmpty = newRelayFeedList.tags.isEmpty()) { updateRelayFeedList(newRelayFeedList) }) { if (backupGuard.accept(backupRelayFeedsList, newRelayFeedList, isEmpty = newRelayFeedList.tags.isEmpty()) { updateRelayFeedList(newRelayFeedList) }) {
@@ -1322,7 +1322,7 @@ class AccountSettings(
override fun channelList() = backupChannelList override fun channelList() = backupChannelList
override fun updateChannelListTo(newChannelList: ChannelListEvent?) { override fun updateChannelListTo(newChannelList: PublicChatListEvent?) {
if (newChannelList == null) return if (newChannelList == null) return
if (backupGuard.accept(backupChannelList, newChannelList, isEmpty = newChannelList.tags.isEmpty()) { updateChannelListTo(newChannelList) }) { if (backupGuard.accept(backupChannelList, newChannelList, isEmpty = newChannelList.tags.isEmpty()) { updateChannelListTo(newChannelList) }) {
@@ -1340,7 +1340,7 @@ class AccountSettings(
} }
} }
fun updateHashtagListTo(newHashtagList: HashtagListEvent?) { fun updateHashtagListTo(newHashtagList: InterestListEvent?) {
if (newHashtagList == null) return if (newHashtagList == null) return
if (backupGuard.accept(backupHashtagList, newHashtagList, isEmpty = newHashtagList.tags.isEmpty()) { updateHashtagListTo(newHashtagList) }) { if (backupGuard.accept(backupHashtagList, newHashtagList, isEmpty = newHashtagList.tags.isEmpty()) { updateHashtagListTo(newHashtagList) }) {
@@ -31,7 +31,7 @@ import com.vitorpamplona.amethyst.commons.service.pow.PoWCategory
import com.vitorpamplona.amethyst.commons.service.pow.PoWPolicy import com.vitorpamplona.amethyst.commons.service.pow.PoWPolicy
import com.vitorpamplona.amethyst.ui.screen.loggedIn.notifications.equalImmutableLists import com.vitorpamplona.amethyst.ui.screen.loggedIn.notifications.equalImmutableLists
import com.vitorpamplona.quartz.nip17Dm.base.ChatroomKey import com.vitorpamplona.quartz.nip17Dm.base.ChatroomKey
import com.vitorpamplona.quartz.nip57Zaps.LnZapEvent import com.vitorpamplona.quartz.nip57Zaps.ZapReceiptEvent
import kotlinx.collections.immutable.ImmutableList import kotlinx.collections.immutable.ImmutableList
import kotlinx.collections.immutable.toImmutableList import kotlinx.collections.immutable.toImmutableList
import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.MutableStateFlow
@@ -265,7 +265,7 @@ class AccountVideoPlayerPreferences(
@Stable @Stable
class AccountZapPreferences( class AccountZapPreferences(
var zapAmountChoices: MutableStateFlow<ImmutableList<Long>>, var zapAmountChoices: MutableStateFlow<ImmutableList<Long>>,
val defaultZapType: MutableStateFlow<LnZapEvent.ZapType>, val defaultZapType: MutableStateFlow<ZapReceiptEvent.ZapType>,
) )
/** /**
@@ -25,7 +25,7 @@ import androidx.core.os.ConfigurationCompat
import com.vitorpamplona.amethyst.commons.model.navigation.BottomBarEntry import com.vitorpamplona.amethyst.commons.model.navigation.BottomBarEntry
import com.vitorpamplona.amethyst.commons.service.pow.PoWCategory import com.vitorpamplona.amethyst.commons.service.pow.PoWCategory
import com.vitorpamplona.amethyst.ui.navigation.bottombars.DefaultBottomBarEntries import com.vitorpamplona.amethyst.ui.navigation.bottombars.DefaultBottomBarEntries
import com.vitorpamplona.quartz.nip57Zaps.LnZapEvent import com.vitorpamplona.quartz.nip57Zaps.ZapReceiptEvent
import kotlinx.serialization.Serializable import kotlinx.serialization.Serializable
import java.util.Locale import java.util.Locale
@@ -207,7 +207,7 @@ class AccountZapPreferencesInternal(
// and so the on-chain-eligible subset round-trips back for them; on load it // and so the on-chain-eligible subset round-trips back for them; on load it
// is unioned into [zapAmountChoices]. See AccountSyncedSettings. // is unioned into [zapAmountChoices]. See AccountSyncedSettings.
var onchainZapAmountChoices: List<Long> = DefaultOnchainZapAmounts, var onchainZapAmountChoices: List<Long> = DefaultOnchainZapAmounts,
val defaultZapType: LnZapEvent.ZapType = LnZapEvent.ZapType.PUBLIC, val defaultZapType: ZapReceiptEvent.ZapType = ZapReceiptEvent.ZapType.PUBLIC,
) )
@Serializable @Serializable
@@ -48,8 +48,8 @@ import com.vitorpamplona.quartz.nip47WalletConnect.rpc.PayMethod
import com.vitorpamplona.quartz.nip47WalletConnect.rpc.PaySuccessResponse import com.vitorpamplona.quartz.nip47WalletConnect.rpc.PaySuccessResponse
import com.vitorpamplona.quartz.nip47WalletConnect.rpc.Request import com.vitorpamplona.quartz.nip47WalletConnect.rpc.Request
import com.vitorpamplona.quartz.nip47WalletConnect.rpc.Response import com.vitorpamplona.quartz.nip47WalletConnect.rpc.Response
import com.vitorpamplona.quartz.nip57Zaps.LnZapEvent import com.vitorpamplona.quartz.nip57Zaps.ZapReceiptEvent
import com.vitorpamplona.quartz.nip57Zaps.LnZapRequestEvent import com.vitorpamplona.quartz.nip57Zaps.ZapRequestEvent
import com.vitorpamplona.quartz.nipB1Bolt12Zaps.builder.Bolt12ZapBuilder import com.vitorpamplona.quartz.nipB1Bolt12Zaps.builder.Bolt12ZapBuilder
import com.vitorpamplona.quartz.nipB1Bolt12Zaps.verify.Bolt12ZapValidation import com.vitorpamplona.quartz.nipB1Bolt12Zaps.verify.Bolt12ZapValidation
import com.vitorpamplona.quartz.utils.Log import com.vitorpamplona.quartz.utils.Log
@@ -74,12 +74,12 @@ class AccountZapActions(
event: Event, event: Event,
pollOption: Int?, pollOption: Int?,
message: String = "", message: String = "",
zapType: LnZapEvent.ZapType, zapType: ZapReceiptEvent.ZapType,
toUser: User?, toUser: User?,
additionalRelays: Set<NormalizedRelayUrl>? = null, additionalRelays: Set<NormalizedRelayUrl>? = null,
amountMillisats: Long? = null, amountMillisats: Long? = null,
lnurl: String? = null, lnurl: String? = null,
) = LnZapRequestEvent.create( ) = ZapRequestEvent.create(
zappedEvent = event, zappedEvent = event,
// Where the provider should publish the receipt. Zapping group content pins that to the room's // Where the provider should publish the receipt. Zapping group content pins that to the room's
// host relay: the receipt belongs where the message it pays for lives, so the room can show it // host relay: the receipt belongs where the message it pays for lives, so the room can show it
@@ -205,7 +205,7 @@ class AccountZapActions(
offer: String, offer: String,
amountMillisats: Long, amountMillisats: Long,
message: String, message: String,
zapType: LnZapEvent.ZapType, zapType: ZapReceiptEvent.ZapType,
// (messageResId, detail) — the caller localizes; detail carries a wallet error, if any. // (messageResId, detail) — the caller localizes; detail carries a wallet error, if any.
onError: (StringResource, String?) -> Unit, onError: (StringResource, String?) -> Unit,
// (code, detail) — the wallet refused or failed the payment; no funds moved. // (code, detail) — the wallet refused or failed the payment; no funds moved.
@@ -215,7 +215,7 @@ class AccountZapActions(
) { ) {
// NONZAP means "pay, but publish no receipt" — settle the offer without binding // NONZAP means "pay, but publish no receipt" — settle the offer without binding
// a zap intent or emitting a 9736, matching the privacy of a bolt11 NONZAP. // a zap intent or emitting a 9736, matching the privacy of a bolt11 NONZAP.
if (zapType == LnZapEvent.ZapType.NONZAP) { if (zapType == ZapReceiptEvent.ZapType.NONZAP) {
sendNwcRequest(PayMethod.create("bitcoin:?lno=$offer", amountMillisats), onTimeout) { response -> sendNwcRequest(PayMethod.create("bitcoin:?lno=$offer", amountMillisats), onTimeout) { response ->
account.scope.launch { account.scope.launch {
try { try {
@@ -228,7 +228,7 @@ class AccountZapActions(
return return
} }
val anonymous = zapType == LnZapEvent.ZapType.ANONYMOUS val anonymous = zapType == ZapReceiptEvent.ZapType.ANONYMOUS
// The 9737 intent and the 9736 zap MUST be signed by the same key. An anonymous // The 9737 intent and the 9736 zap MUST be signed by the same key. An anonymous
// zap uses a fresh ephemeral key so it carries no `P` tag and isn't traceable. // zap uses a fresh ephemeral key so it carries no `P` tag and isn't traceable.
val zapSigner = if (anonymous) NostrSignerInternal(KeyPair()) else account.signer val zapSigner = if (anonymous) NostrSignerInternal(KeyPair()) else account.signer
@@ -284,12 +284,12 @@ class AccountZapActions(
suspend fun createZapRequestFor( suspend fun createZapRequestFor(
user: User, user: User,
message: String = "", message: String = "",
zapType: LnZapEvent.ZapType, zapType: ZapReceiptEvent.ZapType,
amountMillisats: Long? = null, amountMillisats: Long? = null,
lnurl: String? = null, lnurl: String? = null,
): LnZapRequestEvent { ): ZapRequestEvent {
val zapRequest = val zapRequest =
LnZapRequestEvent.create( ZapRequestEvent.create(
userHex = user.pubkeyHex, userHex = user.pubkeyHex,
relays = account.nip65RelayList.inboxFlow.value + (user.inboxRelays() ?: emptyList()), relays = account.nip65RelayList.inboxFlow.value + (user.inboxRelays() ?: emptyList()),
signer = account.signer, signer = account.signer,
@@ -40,16 +40,16 @@ import com.vitorpamplona.quartz.nip01Core.signers.EventTemplate
import com.vitorpamplona.quartz.nip01Core.signers.NostrSigner import com.vitorpamplona.quartz.nip01Core.signers.NostrSigner
import com.vitorpamplona.quartz.nip01Core.signers.NostrSignerInternal import com.vitorpamplona.quartz.nip01Core.signers.NostrSignerInternal
import com.vitorpamplona.quartz.nip17Dm.base.BaseDMGroupEvent import com.vitorpamplona.quartz.nip17Dm.base.BaseDMGroupEvent
import com.vitorpamplona.quartz.nip17Dm.settings.ChatMessageRelayListEvent import com.vitorpamplona.quartz.nip17Dm.settings.DmRelayListEvent
import com.vitorpamplona.quartz.nip29RelayGroups.isGroupScoped import com.vitorpamplona.quartz.nip29RelayGroups.isGroupScoped
import com.vitorpamplona.quartz.nip37Drafts.DraftWrapEvent import com.vitorpamplona.quartz.nip37Drafts.DraftWrapEvent
import com.vitorpamplona.quartz.nip51Lists.bookmarkList.BookmarkListEvent import com.vitorpamplona.quartz.nip51Lists.bookmarkList.BookmarkListEvent
import com.vitorpamplona.quartz.nip51Lists.bookmarkList.OldBookmarkListEvent import com.vitorpamplona.quartz.nip51Lists.bookmarkList.OldBookmarkListEvent
import com.vitorpamplona.quartz.nip51Lists.labeledBookmarkList.LabeledBookmarkListEvent import com.vitorpamplona.quartz.nip51Lists.labeledBookmarkList.BookmarkSetEvent
import com.vitorpamplona.quartz.nip53LiveActivities.meetingSpaces.MeetingRoomEvent import com.vitorpamplona.quartz.nip53LiveActivities.meetingSpaces.MeetingRoomEvent
import com.vitorpamplona.quartz.nip53LiveActivities.meetingSpaces.MeetingSpaceEvent import com.vitorpamplona.quartz.nip53LiveActivities.meetingSpaces.MeetingSpaceEvent
import com.vitorpamplona.quartz.nip53LiveActivities.streaming.LiveActivitiesEvent import com.vitorpamplona.quartz.nip53LiveActivities.streaming.LiveActivitiesEvent
import com.vitorpamplona.quartz.nip59Giftwrap.seals.SealedRumorEvent import com.vitorpamplona.quartz.nip59Giftwrap.seals.SealEvent
import com.vitorpamplona.quartz.nip59Giftwrap.wraps.GiftWrapEvent import com.vitorpamplona.quartz.nip59Giftwrap.wraps.GiftWrapEvent
import com.vitorpamplona.quartz.nip60Cashu.wallet.CashuWalletEvent import com.vitorpamplona.quartz.nip60Cashu.wallet.CashuWalletEvent
import com.vitorpamplona.quartz.nip61Nutzaps.info.NutzapInfoEvent import com.vitorpamplona.quartz.nip61Nutzaps.info.NutzapInfoEvent
@@ -121,7 +121,7 @@ class EventBroadcaster(
event is AppSpecificDataEvent || event is AppSpecificDataEvent ||
event is BookmarkListEvent || event is BookmarkListEvent ||
event is OldBookmarkListEvent || event is OldBookmarkListEvent ||
event is LabeledBookmarkListEvent event is BookmarkSetEvent
) { ) {
return false return false
} }
@@ -166,7 +166,7 @@ class EventBroadcaster(
} }
// Seals, inner DM messages, and unsigned rumors never get broadcast // Seals, inner DM messages, and unsigned rumors never get broadcast
// relays: they only travel inside gift wraps. // relays: they only travel inside gift wraps.
if (event is SealedRumorEvent || event is BaseDMGroupEvent || event.sig.isEmpty()) { if (event is SealEvent || event is BaseDMGroupEvent || event.sig.isEmpty()) {
return emptySet() return emptySet()
} }
@@ -322,7 +322,7 @@ class EventBroadcaster(
.toList() .toList()
} }
val filter = val filter =
if (host.kind == SealedRumorEvent.KIND) { if (host.kind == SealEvent.KIND) {
Filter( Filter(
kinds = listOf(host.kind), kinds = listOf(host.kind),
ids = listOf(host.id), ids = listOf(host.id),
@@ -383,7 +383,7 @@ class EventBroadcaster(
fun sendRestoredVersion(event: Event) { fun sendRestoredVersion(event: Event) {
when (event) { when (event) {
is AdvertisedRelayListEvent -> sendEverywhereAnd(event, event.relays().mapTo(mutableSetOf()) { it.relayUrl }) is AdvertisedRelayListEvent -> sendEverywhereAnd(event, event.relays().mapTo(mutableSetOf()) { it.relayUrl })
is ChatMessageRelayListEvent -> sendEverywhereAnd(event, event.relays().toSet()) is DmRelayListEvent -> sendEverywhereAnd(event, event.relays().toSet())
is KeyPackageRelayListEvent -> sendEverywhereAnd(event, event.relays().toSet()) is KeyPackageRelayListEvent -> sendEverywhereAnd(event, event.relays().toSet())
is CashuWalletEvent -> sendLiterallyEverywhere(event) is CashuWalletEvent -> sendLiterallyEverywhere(event)
// Profiles and nutzap info are normally saved everywhere so others can find them; // Profiles and nutzap info are normally saved everywhere so others can find them;
@@ -25,8 +25,8 @@ import com.vitorpamplona.quartz.nip01Core.core.Address
import com.vitorpamplona.quartz.nip01Core.core.HexKey import com.vitorpamplona.quartz.nip01Core.core.HexKey
import com.vitorpamplona.quartz.nip01Core.relay.filters.Filter import com.vitorpamplona.quartz.nip01Core.relay.filters.Filter
import com.vitorpamplona.quartz.nip01Core.relay.normalizer.NormalizedRelayUrl import com.vitorpamplona.quartz.nip01Core.relay.normalizer.NormalizedRelayUrl
import com.vitorpamplona.quartz.nip90Dvms.contentDiscoveryResponse.NIP90ContentDiscoveryResponseEvent import com.vitorpamplona.quartz.nip90Dvms.contentDiscoveryResponse.DvmContentDiscoveryResponseEvent
import com.vitorpamplona.quartz.nip90Dvms.status.NIP90StatusEvent import com.vitorpamplona.quartz.nip90Dvms.status.DvmStatusEvent
import com.vitorpamplona.quartz.utils.Log import com.vitorpamplona.quartz.utils.Log
import kotlinx.coroutines.CancellationException import kotlinx.coroutines.CancellationException
import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.CoroutineScope
@@ -60,7 +60,7 @@ data class FavoriteAlgoFeedsSnapshot(
val responseRelays: Set<NormalizedRelayUrl> = emptySet(), val responseRelays: Set<NormalizedRelayUrl> = emptySet(),
val ids: Set<HexKey> = emptySet(), val ids: Set<HexKey> = emptySet(),
val addresses: Set<String> = emptySet(), val addresses: Set<String> = emptySet(),
val latestStatus: NIP90StatusEvent? = null, val latestStatus: DvmStatusEvent? = null,
val errorMessage: String? = null, val errorMessage: String? = null,
) )
@@ -139,9 +139,9 @@ class FavoriteAlgoFeedsOrchestrator(
launch { launch {
account.cache account.cache
.observeLatestEvent<NIP90ContentDiscoveryResponseEvent>( .observeLatestEvent<DvmContentDiscoveryResponseEvent>(
Filter( Filter(
kinds = listOf(NIP90ContentDiscoveryResponseEvent.KIND), kinds = listOf(DvmContentDiscoveryResponseEvent.KIND),
tags = mapOf("e" to listOf(requestId)), tags = mapOf("e" to listOf(requestId)),
limit = 1, limit = 1,
), ),
@@ -159,9 +159,9 @@ class FavoriteAlgoFeedsOrchestrator(
launch { launch {
account.cache account.cache
.observeLatestEvent<NIP90StatusEvent>( .observeLatestEvent<DvmStatusEvent>(
Filter( Filter(
kinds = listOf(NIP90StatusEvent.KIND), kinds = listOf(DvmStatusEvent.KIND),
tags = mapOf("e" to listOf(requestId)), tags = mapOf("e" to listOf(requestId)),
limit = 1, limit = 1,
), ),
@@ -26,7 +26,7 @@ import com.vitorpamplona.amethyst.commons.model.cache.LocalCache
import com.vitorpamplona.amethyst.model.AccountSettings import com.vitorpamplona.amethyst.model.AccountSettings
import com.vitorpamplona.quartz.nip01Core.relay.normalizer.NormalizedRelayUrl import com.vitorpamplona.quartz.nip01Core.relay.normalizer.NormalizedRelayUrl
import com.vitorpamplona.quartz.nip01Core.signers.NostrSigner import com.vitorpamplona.quartz.nip01Core.signers.NostrSigner
import com.vitorpamplona.quartz.nip17Dm.settings.ChatMessageRelayListEvent import com.vitorpamplona.quartz.nip17Dm.settings.DmRelayListEvent
import com.vitorpamplona.quartz.utils.Log import com.vitorpamplona.quartz.utils.Log
import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.DelicateCoroutinesApi import kotlinx.coroutines.DelicateCoroutinesApi
@@ -48,14 +48,14 @@ class DmRelayListState(
// Creates a long-term reference for this note so that the GC doesn't collect the note it self // Creates a long-term reference for this note so that the GC doesn't collect the note it self
val dmListNote = cache.getOrCreateAddressableNote(getDMRelayListAddress()) val dmListNote = cache.getOrCreateAddressableNote(getDMRelayListAddress())
fun getDMRelayListAddress() = ChatMessageRelayListEvent.createAddress(signer.pubKey) fun getDMRelayListAddress() = DmRelayListEvent.createAddress(signer.pubKey)
fun getDMRelayListFlow(): StateFlow<NoteState> = dmListNote.flow().metadata.stateFlow fun getDMRelayListFlow(): StateFlow<NoteState> = dmListNote.flow().metadata.stateFlow
fun getDMRelayList(): ChatMessageRelayListEvent? = dmListNote.event as? ChatMessageRelayListEvent fun getDMRelayList(): DmRelayListEvent? = dmListNote.event as? DmRelayListEvent
fun normalizeDMRelayListWithBackup(note: Note): Set<NormalizedRelayUrl> { fun normalizeDMRelayListWithBackup(note: Note): Set<NormalizedRelayUrl> {
val event = note.event as? ChatMessageRelayListEvent ?: settings.backupDMRelayList val event = note.event as? DmRelayListEvent ?: settings.backupDMRelayList
return event?.relays()?.toSet() ?: emptySet() return event?.relays()?.toSet() ?: emptySet()
} }
@@ -70,16 +70,16 @@ class DmRelayListState(
emptySet(), emptySet(),
) )
suspend fun saveRelayList(dmRelays: List<NormalizedRelayUrl>): ChatMessageRelayListEvent { suspend fun saveRelayList(dmRelays: List<NormalizedRelayUrl>): DmRelayListEvent {
val relayListForDMs = getDMRelayList() val relayListForDMs = getDMRelayList()
return if (relayListForDMs != null && relayListForDMs.tags.isNotEmpty()) { return if (relayListForDMs != null && relayListForDMs.tags.isNotEmpty()) {
ChatMessageRelayListEvent.updateRelayList( DmRelayListEvent.updateRelayList(
earlierVersion = relayListForDMs, earlierVersion = relayListForDMs,
relays = dmRelays, relays = dmRelays,
signer = signer, signer = signer,
) )
} else { } else {
ChatMessageRelayListEvent.create( DmRelayListEvent.create(
relays = dmRelays, relays = dmRelays,
signer = signer, signer = signer,
) )
@@ -99,7 +99,7 @@ class DmRelayListState(
Log.d("AccountRegisterObservers", "NIP-17 Relay List Collector Start") Log.d("AccountRegisterObservers", "NIP-17 Relay List Collector Start")
getDMRelayListFlow().collect { getDMRelayListFlow().collect {
Log.d("AccountRegisterObservers") { "Updating DM Relay List for ${signer.pubKey}" } Log.d("AccountRegisterObservers") { "Updating DM Relay List for ${signer.pubKey}" }
(it.note.event as? ChatMessageRelayListEvent)?.let { (it.note.event as? DmRelayListEvent)?.let {
settings.updateDMRelayList(it) settings.updateDMRelayList(it)
} }
} }
@@ -33,7 +33,7 @@ import com.vitorpamplona.amethyst.model.Account
import com.vitorpamplona.quartz.nip01Core.signers.NostrSigner import com.vitorpamplona.quartz.nip01Core.signers.NostrSigner
import com.vitorpamplona.quartz.nip01Core.signers.SignerExceptions import com.vitorpamplona.quartz.nip01Core.signers.SignerExceptions
import com.vitorpamplona.quartz.nip01Core.signers.update import com.vitorpamplona.quartz.nip01Core.signers.update
import com.vitorpamplona.quartz.nip09Deletions.DeletionEvent import com.vitorpamplona.quartz.nip09Deletions.DeletionRequestEvent
import com.vitorpamplona.quartz.nip30CustomEmoji.EmojiUrlTag import com.vitorpamplona.quartz.nip30CustomEmoji.EmojiUrlTag
import com.vitorpamplona.quartz.nip30CustomEmoji.emoji import com.vitorpamplona.quartz.nip30CustomEmoji.emoji
import com.vitorpamplona.quartz.nip30CustomEmoji.pack.EmojiPackEvent import com.vitorpamplona.quartz.nip30CustomEmoji.pack.EmojiPackEvent
@@ -129,12 +129,12 @@ class OwnedEmojiPacksState(
.onStart { emit(listFeedFlow.value.getPack(dTag)) } .onStart { emit(listFeedFlow.value.getPack(dTag)) }
.flowOn(Dispatchers.IO) .flowOn(Dispatchers.IO)
fun DeletionEvent.hasAnyDeletedOwnedEmojiPacks() = deleteAddressesWithKind(EmojiPackEvent.KIND) || deletesAnyEventIn(ownedEmojiPackEventIds.value) fun DeletionRequestEvent.hasAnyDeletedOwnedEmojiPacks() = deleteAddressesWithKind(EmojiPackEvent.KIND) || deletesAnyEventIn(ownedEmojiPackEventIds.value)
fun hasItemInNoteList(notes: Set<Note>): Boolean = fun hasItemInNoteList(notes: Set<Note>): Boolean =
notes.anyNotNullEvent { event -> notes.anyNotNullEvent { event ->
if (event.pubKey == signer.pubKey) { if (event.pubKey == signer.pubKey) {
event is EmojiPackEvent || (event is DeletionEvent && event.hasAnyDeletedOwnedEmojiPacks()) event is EmojiPackEvent || (event is DeletionRequestEvent && event.hasAnyDeletedOwnedEmojiPacks())
} else { } else {
false false
} }
@@ -269,7 +269,7 @@ class OwnedEmojiPacksState(
account: Account, account: Account,
) { ) {
val packEvent = getOwnedEmojiPackEvent(dTag) ?: return val packEvent = getOwnedEmojiPackEvent(dTag) ?: return
val deletionEventTemplate = DeletionEvent.build(listOf(packEvent)) val deletionEventTemplate = DeletionRequestEvent.build(listOf(packEvent))
val deletionEvent = signer.sign(deletionEventTemplate) val deletionEvent = signer.sign(deletionEventTemplate)
account.sendMyPublicAndPrivateOutbox(deletionEvent) account.sendMyPublicAndPrivateOutbox(deletionEvent)
} }
@@ -36,10 +36,10 @@ import com.vitorpamplona.quartz.nip01Core.signers.NostrSignerInternal
import com.vitorpamplona.quartz.nip47WalletConnect.Nip47WalletConnect import com.vitorpamplona.quartz.nip47WalletConnect.Nip47WalletConnect
import com.vitorpamplona.quartz.nip47WalletConnect.cache.NostrWalletConnectRequestCache import com.vitorpamplona.quartz.nip47WalletConnect.cache.NostrWalletConnectRequestCache
import com.vitorpamplona.quartz.nip47WalletConnect.cache.NostrWalletConnectResponseCache import com.vitorpamplona.quartz.nip47WalletConnect.cache.NostrWalletConnectResponseCache
import com.vitorpamplona.quartz.nip47WalletConnect.events.LnZapPaymentRequestEvent
import com.vitorpamplona.quartz.nip47WalletConnect.events.LnZapPaymentResponseEvent
import com.vitorpamplona.quartz.nip47WalletConnect.events.NwcInfoEvent import com.vitorpamplona.quartz.nip47WalletConnect.events.NwcInfoEvent
import com.vitorpamplona.quartz.nip47WalletConnect.events.NwcNotificationEvent import com.vitorpamplona.quartz.nip47WalletConnect.events.NwcNotificationEvent
import com.vitorpamplona.quartz.nip47WalletConnect.events.NwcRequestEvent
import com.vitorpamplona.quartz.nip47WalletConnect.events.NwcResponseEvent
import com.vitorpamplona.quartz.nip47WalletConnect.rpc.NwcTransaction import com.vitorpamplona.quartz.nip47WalletConnect.rpc.NwcTransaction
import com.vitorpamplona.quartz.nip47WalletConnect.rpc.PayInvoiceMethod import com.vitorpamplona.quartz.nip47WalletConnect.rpc.PayInvoiceMethod
import com.vitorpamplona.quartz.nip47WalletConnect.rpc.PaymentReceivedNotification import com.vitorpamplona.quartz.nip47WalletConnect.rpc.PaymentReceivedNotification
@@ -199,12 +199,12 @@ class NwcSignerState(
override fun isNIP47Author(pubKey: HexKey?): Boolean = nip47Signer.value.pubKey == pubKey override fun isNIP47Author(pubKey: HexKey?): Boolean = nip47Signer.value.pubKey == pubKey
override suspend fun decryptRequest(event: LnZapPaymentRequestEvent): Request? { override suspend fun decryptRequest(event: NwcRequestEvent): Request? {
if (!hasWalletConnectSetup()) return null if (!hasWalletConnectSetup()) return null
return zapPaymentRequestDecryptionCache.value.decryptRequest(event) return zapPaymentRequestDecryptionCache.value.decryptRequest(event)
} }
override suspend fun decryptResponse(event: LnZapPaymentResponseEvent): Response? { override suspend fun decryptResponse(event: NwcResponseEvent): Response? {
if (!hasWalletConnectSetup()) return null if (!hasWalletConnectSetup()) return null
return zapPaymentResponseDecryptionCache.value.decryptResponse(event) return zapPaymentResponseDecryptionCache.value.decryptResponse(event)
} }
@@ -252,7 +252,7 @@ class NwcSignerState(
request: Request, request: Request,
onTimeout: () -> Unit = {}, onTimeout: () -> Unit = {},
onResponse: (Response?) -> Unit, onResponse: (Response?) -> Unit,
): Pair<LnZapPaymentRequestEvent, NormalizedRelayUrl> = sendNwcRequestToWallet(defaultWalletUri.value, request, onTimeout, onResponse) ): Pair<NwcRequestEvent, NormalizedRelayUrl> = sendNwcRequestToWallet(defaultWalletUri.value, request, onTimeout, onResponse)
/** /**
* Sends a generic NIP-47 request to a specific wallet. * Sends a generic NIP-47 request to a specific wallet.
@@ -266,14 +266,14 @@ class NwcSignerState(
request: Request, request: Request,
onTimeout: () -> Unit = {}, onTimeout: () -> Unit = {},
onResponse: (Response?) -> Unit, onResponse: (Response?) -> Unit,
): Pair<LnZapPaymentRequestEvent, NormalizedRelayUrl> { ): Pair<NwcRequestEvent, NormalizedRelayUrl> {
val walletService = walletUri ?: throw IllegalArgumentException("No NIP47 setup") val walletService = walletUri ?: throw IllegalArgumentException("No NIP47 setup")
val walletSigner = buildSigner(walletService) ?: signer val walletSigner = buildSigner(walletService) ?: signer
val info = walletInfo(walletService) val info = walletInfo(walletService)
request.dropMetadataIfUnsupported(info) request.dropMetadataIfUnsupported(info)
val event = LnZapPaymentRequestEvent.createRequest(request, walletService.pubKeyHex, walletSigner, useNip44 = prefersNip44(info)) val event = NwcRequestEvent.createRequest(request, walletService.pubKeyHex, walletSigner, useNip44 = prefersNip44(info))
val filter = val filter =
NWCPaymentQueryState( NWCPaymentQueryState(
@@ -314,7 +314,7 @@ class NwcSignerState(
onTimeout: () -> Unit = {}, onTimeout: () -> Unit = {},
metadata: Map<String, Any?>? = null, metadata: Map<String, Any?>? = null,
onResponse: (Response?) -> Unit, onResponse: (Response?) -> Unit,
): Pair<LnZapPaymentRequestEvent, NormalizedRelayUrl> { ): Pair<NwcRequestEvent, NormalizedRelayUrl> {
val walletService = defaultWalletUri.value ?: throw IllegalArgumentException("No NIP47 setup") val walletService = defaultWalletUri.value ?: throw IllegalArgumentException("No NIP47 setup")
val info = walletInfo(walletService) val info = walletInfo(walletService)
@@ -322,7 +322,7 @@ class NwcSignerState(
request.dropMetadataIfUnsupported(info) request.dropMetadataIfUnsupported(info)
val event = val event =
LnZapPaymentRequestEvent.createRequest( NwcRequestEvent.createRequest(
request, request,
walletService.pubKeyHex, walletService.pubKeyHex,
nip47Signer.value, nip47Signer.value,
@@ -26,7 +26,7 @@ import com.vitorpamplona.amethyst.commons.model.cache.LocalCache
import com.vitorpamplona.amethyst.commons.model.nip51Lists.hashtagLists.HashtagListDecryptionCache import com.vitorpamplona.amethyst.commons.model.nip51Lists.hashtagLists.HashtagListDecryptionCache
import com.vitorpamplona.amethyst.model.AccountSettings import com.vitorpamplona.amethyst.model.AccountSettings
import com.vitorpamplona.quartz.nip01Core.signers.NostrSigner import com.vitorpamplona.quartz.nip01Core.signers.NostrSigner
import com.vitorpamplona.quartz.nip51Lists.hashtagList.HashtagListEvent import com.vitorpamplona.quartz.nip51Lists.hashtagList.InterestListEvent
import com.vitorpamplona.quartz.utils.Log import com.vitorpamplona.quartz.utils.Log
import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.DelicateCoroutinesApi import kotlinx.coroutines.DelicateCoroutinesApi
@@ -50,14 +50,14 @@ class HashtagListState(
// Creates a long-term reference for this note so that the GC doesn't collect the note it self // Creates a long-term reference for this note so that the GC doesn't collect the note it self
val hashtagListNote = cache.getOrCreateAddressableNote(getHashtagListAddress()) val hashtagListNote = cache.getOrCreateAddressableNote(getHashtagListAddress())
fun getHashtagListAddress() = HashtagListEvent.createAddress(signer.pubKey) fun getHashtagListAddress() = InterestListEvent.createAddress(signer.pubKey)
fun getHashtagListFlow(): StateFlow<NoteState> = hashtagListNote.flow().metadata.stateFlow fun getHashtagListFlow(): StateFlow<NoteState> = hashtagListNote.flow().metadata.stateFlow
fun getHashtagList(): HashtagListEvent? = hashtagListNote.event as? HashtagListEvent fun getHashtagList(): InterestListEvent? = hashtagListNote.event as? InterestListEvent
suspend fun hashtagListWithBackup(note: Note): Set<String> { suspend fun hashtagListWithBackup(note: Note): Set<String> {
val event = note.event as? HashtagListEvent ?: settings.backupHashtagList val event = note.event as? InterestListEvent ?: settings.backupHashtagList
return event?.let { decryptionCache.hashtags(it).mapTo(mutableSetOf()) { it.lowercase() } } ?: emptySet() return event?.let { decryptionCache.hashtags(it).mapTo(mutableSetOf()) { it.lowercase() } } ?: emptySet()
} }
@@ -75,31 +75,31 @@ class HashtagListState(
emptySet(), emptySet(),
) )
suspend fun follow(hashtags: List<String>): HashtagListEvent { suspend fun follow(hashtags: List<String>): InterestListEvent {
val hashtagList = getHashtagList() val hashtagList = getHashtagList()
return if (hashtagList == null) { return if (hashtagList == null) {
HashtagListEvent.create(hashtags, true, signer) InterestListEvent.create(hashtags, true, signer)
} else { } else {
HashtagListEvent.add(hashtagList, hashtags, true, signer) InterestListEvent.add(hashtagList, hashtags, true, signer)
} }
} }
suspend fun follow(hashtag: String): HashtagListEvent { suspend fun follow(hashtag: String): InterestListEvent {
val hashtagList = getHashtagList() val hashtagList = getHashtagList()
return if (hashtagList == null) { return if (hashtagList == null) {
HashtagListEvent.create(hashtag.lowercase(), true, signer) InterestListEvent.create(hashtag.lowercase(), true, signer)
} else { } else {
HashtagListEvent.add(hashtagList, hashtag.lowercase(), true, signer) InterestListEvent.add(hashtagList, hashtag.lowercase(), true, signer)
} }
} }
suspend fun unfollow(hashtag: String): HashtagListEvent? { suspend fun unfollow(hashtag: String): InterestListEvent? {
val hashtagList = getHashtagList() val hashtagList = getHashtagList()
return if (hashtagList != null) { return if (hashtagList != null) {
HashtagListEvent.remove(hashtagList, hashtag, signer) InterestListEvent.remove(hashtagList, hashtag, signer)
} else { } else {
null null
} }
@@ -118,7 +118,7 @@ class HashtagListState(
Log.d("AccountRegisterObservers", "Hashtag List Collector Start") Log.d("AccountRegisterObservers", "Hashtag List Collector Start")
getHashtagListFlow().collect { getHashtagListFlow().collect {
Log.d("AccountRegisterObservers") { "Hashtag List for ${signer.pubKey}" } Log.d("AccountRegisterObservers") { "Hashtag List for ${signer.pubKey}" }
(it.note.event as? HashtagListEvent)?.let { (it.note.event as? InterestListEvent)?.let {
settings.updateHashtagListTo(it) settings.updateHashtagListTo(it)
} }
} }
@@ -31,7 +31,7 @@ import com.vitorpamplona.amethyst.commons.model.nip51Lists.interestSets.Interest
import com.vitorpamplona.amethyst.commons.model.updateFlow import com.vitorpamplona.amethyst.commons.model.updateFlow
import com.vitorpamplona.amethyst.model.Account import com.vitorpamplona.amethyst.model.Account
import com.vitorpamplona.quartz.nip01Core.signers.NostrSigner import com.vitorpamplona.quartz.nip01Core.signers.NostrSigner
import com.vitorpamplona.quartz.nip09Deletions.DeletionEvent import com.vitorpamplona.quartz.nip09Deletions.DeletionRequestEvent
import com.vitorpamplona.quartz.nip51Lists.interestSet.InterestSetEvent import com.vitorpamplona.quartz.nip51Lists.interestSet.InterestSetEvent
import com.vitorpamplona.quartz.nip51Lists.tags.TitleTag import com.vitorpamplona.quartz.nip51Lists.tags.TitleTag
import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.CoroutineScope
@@ -106,12 +106,12 @@ class InterestSetsState(
fun getInterestSet(dTag: String) = listFeedFlow.value.getSet(dTag) fun getInterestSet(dTag: String) = listFeedFlow.value.getSet(dTag)
fun DeletionEvent.hasAnyDeletedInterestSets() = deleteAddressesWithKind(InterestSetEvent.KIND) || deletesAnyEventIn(interestSetEventIds.value) fun DeletionRequestEvent.hasAnyDeletedInterestSets() = deleteAddressesWithKind(InterestSetEvent.KIND) || deletesAnyEventIn(interestSetEventIds.value)
fun hasItemInNoteList(notes: Set<Note>): Boolean = fun hasItemInNoteList(notes: Set<Note>): Boolean =
notes.anyNotNullEvent { event -> notes.anyNotNullEvent { event ->
if (event.pubKey == signer.pubKey) { if (event.pubKey == signer.pubKey) {
event is InterestSetEvent || (event is DeletionEvent && event.hasAnyDeletedInterestSets()) event is InterestSetEvent || (event is DeletionRequestEvent && event.hasAnyDeletedInterestSets())
} else { } else {
false false
} }
@@ -178,7 +178,7 @@ class InterestSetsState(
account: Account, account: Account,
) { ) {
val event = getInterestSetEvent(identifier) val event = getInterestSetEvent(identifier)
val template = DeletionEvent.build(listOf(event)) val template = DeletionRequestEvent.build(listOf(event))
val deletionEvent = account.signer.sign(template) val deletionEvent = account.signer.sign(template)
account.sendMyPublicAndPrivateOutbox(deletionEvent) account.sendMyPublicAndPrivateOutbox(deletionEvent)
} }
@@ -33,11 +33,11 @@ import com.vitorpamplona.amethyst.model.Account
import com.vitorpamplona.quartz.nip01Core.core.Address import com.vitorpamplona.quartz.nip01Core.core.Address
import com.vitorpamplona.quartz.nip01Core.signers.NostrSigner import com.vitorpamplona.quartz.nip01Core.signers.NostrSigner
import com.vitorpamplona.quartz.nip01Core.signers.update import com.vitorpamplona.quartz.nip01Core.signers.update
import com.vitorpamplona.quartz.nip09Deletions.DeletionEvent import com.vitorpamplona.quartz.nip09Deletions.DeletionRequestEvent
import com.vitorpamplona.quartz.nip51Lists.bookmarkList.tags.AddressBookmark import com.vitorpamplona.quartz.nip51Lists.bookmarkList.tags.AddressBookmark
import com.vitorpamplona.quartz.nip51Lists.bookmarkList.tags.BookmarkIdTag import com.vitorpamplona.quartz.nip51Lists.bookmarkList.tags.BookmarkIdTag
import com.vitorpamplona.quartz.nip51Lists.bookmarkList.tags.EventBookmark import com.vitorpamplona.quartz.nip51Lists.bookmarkList.tags.EventBookmark
import com.vitorpamplona.quartz.nip51Lists.labeledBookmarkList.LabeledBookmarkListEvent import com.vitorpamplona.quartz.nip51Lists.labeledBookmarkList.BookmarkSetEvent
import com.vitorpamplona.quartz.nip51Lists.labeledBookmarkList.description import com.vitorpamplona.quartz.nip51Lists.labeledBookmarkList.description
import com.vitorpamplona.quartz.nip51Lists.labeledBookmarkList.image import com.vitorpamplona.quartz.nip51Lists.labeledBookmarkList.image
import com.vitorpamplona.quartz.nip51Lists.labeledBookmarkList.title import com.vitorpamplona.quartz.nip51Lists.labeledBookmarkList.title
@@ -62,7 +62,7 @@ class LabeledBookmarkListsState(
) { ) {
val user = cache.getOrCreateUser(signer.pubKey) val user = cache.getOrCreateUser(signer.pubKey)
fun existingLabeledBookmarkNotes() = cache.addressables.filter(LabeledBookmarkListEvent.KIND, user.pubkeyHex) fun existingLabeledBookmarkNotes() = cache.addressables.filter(BookmarkSetEvent.KIND, user.pubkeyHex)
val labeledBookmarkListVersions = MutableStateFlow(0) val labeledBookmarkListVersions = MutableStateFlow(0)
@@ -81,14 +81,14 @@ class LabeledBookmarkListsState(
.stateIn(scope, SharingStarted.Eagerly, emptySet()) .stateIn(scope, SharingStarted.Eagerly, emptySet())
@OptIn(ExperimentalCoroutinesApi::class) @OptIn(ExperimentalCoroutinesApi::class)
val latestBookmarkLists: StateFlow<List<LabeledBookmarkListEvent>> = val latestBookmarkLists: StateFlow<List<BookmarkSetEvent>> =
labeledBookmarkListNotes labeledBookmarkListNotes
.transformLatest { emitAll(it.updateFlow<LabeledBookmarkListEvent>()) } .transformLatest { emitAll(it.updateFlow<BookmarkSetEvent>()) }
.onStart { emit(labeledBookmarkListNotes.value.events()) } .onStart { emit(labeledBookmarkListNotes.value.events()) }
.flowOn(Dispatchers.IO) .flowOn(Dispatchers.IO)
.stateIn(scope, SharingStarted.Eagerly, emptyList()) .stateIn(scope, SharingStarted.Eagerly, emptyList())
suspend fun LabeledBookmarkListEvent.toLabeledBookmarkList() = suspend fun BookmarkSetEvent.toLabeledBookmarkList() =
LabeledBookmarkList( LabeledBookmarkList(
identifier = dTag(), identifier = dTag(),
title = titleOrName() ?: dTag(), title = titleOrName() ?: dTag(),
@@ -98,7 +98,7 @@ class LabeledBookmarkListsState(
publicBookmarks = publicBookmarks().toSet(), publicBookmarks = publicBookmarks().toSet(),
) )
suspend fun List<LabeledBookmarkListEvent>.toLabeledBookmarkListsFeed() = map { it.toLabeledBookmarkList() }.sortedBy { it.title } suspend fun List<BookmarkSetEvent>.toLabeledBookmarkListsFeed() = map { it.toLabeledBookmarkList() }.sortedBy { it.title }
val listFeedFlow = val listFeedFlow =
latestBookmarkLists latestBookmarkLists
@@ -114,12 +114,12 @@ class LabeledBookmarkListsState(
fun getBookmarkList(dTag: String) = listFeedFlow.value.getList(bookmarkListId = dTag) fun getBookmarkList(dTag: String) = listFeedFlow.value.getList(bookmarkListId = dTag)
fun DeletionEvent.hasAnyDeletedBookmarkLists() = deleteAddressesWithKind(LabeledBookmarkListEvent.KIND) || deletesAnyEventIn(labeledBookmarkListEventIds.value) fun DeletionRequestEvent.hasAnyDeletedBookmarkLists() = deleteAddressesWithKind(BookmarkSetEvent.KIND) || deletesAnyEventIn(labeledBookmarkListEventIds.value)
fun hasItemInNoteList(notes: Set<Note>): Boolean = fun hasItemInNoteList(notes: Set<Note>): Boolean =
notes.anyNotNullEvent { event -> notes.anyNotNullEvent { event ->
if (event.pubKey == signer.pubKey) { if (event.pubKey == signer.pubKey) {
event is LabeledBookmarkListEvent || (event is DeletionEvent && event.hasAnyDeletedBookmarkLists()) event is BookmarkSetEvent || (event is DeletionRequestEvent && event.hasAnyDeletedBookmarkLists())
} else { } else {
false false
} }
@@ -143,7 +143,7 @@ class LabeledBookmarkListsState(
fun getLabeledBookmarkListNote(bookmarkIdentifier: String): AddressableNote? = existingLabeledBookmarkNotes().find { it.dTag() == bookmarkIdentifier } fun getLabeledBookmarkListNote(bookmarkIdentifier: String): AddressableNote? = existingLabeledBookmarkNotes().find { it.dTag() == bookmarkIdentifier }
fun getLabeledBookmarkListEvent(bookmarkIdentifier: String): LabeledBookmarkListEvent = getLabeledBookmarkListNote(bookmarkIdentifier)?.event as LabeledBookmarkListEvent fun getLabeledBookmarkListEvent(bookmarkIdentifier: String): BookmarkSetEvent = getLabeledBookmarkListNote(bookmarkIdentifier)?.event as BookmarkSetEvent
fun getLabeledBookmarkListFlow(bookmarkIdentifier: String) = fun getLabeledBookmarkListFlow(bookmarkIdentifier: String) =
listFeedFlow listFeedFlow
@@ -163,7 +163,7 @@ class LabeledBookmarkListsState(
account: Account, account: Account,
) { ) {
val newList = val newList =
LabeledBookmarkListEvent.create( BookmarkSetEvent.create(
title = listName, title = listName,
description = listDescription, description = listDescription,
image = listImage, image = listImage,
@@ -202,7 +202,7 @@ class LabeledBookmarkListsState(
) { ) {
val listEvent = getLabeledBookmarkListEvent(bookmarkList.identifier) val listEvent = getLabeledBookmarkListEvent(bookmarkList.identifier)
val renamedList = val renamedList =
LabeledBookmarkListEvent.modifyName( BookmarkSetEvent.modifyName(
earlierVersion = listEvent, earlierVersion = listEvent,
newTitle = newName, newTitle = newName,
signer = account.signer, signer = account.signer,
@@ -217,7 +217,7 @@ class LabeledBookmarkListsState(
) { ) {
val listEvent = getLabeledBookmarkListEvent(bookmarkList.identifier) val listEvent = getLabeledBookmarkListEvent(bookmarkList.identifier)
val modifiedList = val modifiedList =
LabeledBookmarkListEvent.modifyDescription( BookmarkSetEvent.modifyDescription(
earlierVersion = listEvent, earlierVersion = listEvent,
newDescription = newDescription, newDescription = newDescription,
signer = account.signer, signer = account.signer,
@@ -232,7 +232,7 @@ class LabeledBookmarkListsState(
account: Account, account: Account,
) { ) {
val clonedList = val clonedList =
LabeledBookmarkListEvent.create( BookmarkSetEvent.create(
title = customCloneName ?: currentBookmarkList.title, title = customCloneName ?: currentBookmarkList.title,
description = customCloneDescription ?: currentBookmarkList.description, description = customCloneDescription ?: currentBookmarkList.description,
publicBookmarks = currentBookmarkList.publicBookmarks.toList(), publicBookmarks = currentBookmarkList.publicBookmarks.toList(),
@@ -247,7 +247,7 @@ class LabeledBookmarkListsState(
account: Account, account: Account,
) { ) {
val listEvent = getLabeledBookmarkListEvent(bookmarkListIdentifier) val listEvent = getLabeledBookmarkListEvent(bookmarkListIdentifier)
val deletionEventTemplate = DeletionEvent.build(listOf(listEvent)) val deletionEventTemplate = DeletionRequestEvent.build(listOf(listEvent))
val deletionEvent = account.signer.sign(deletionEventTemplate) val deletionEvent = account.signer.sign(deletionEventTemplate)
account.sendMyPublicAndPrivateOutbox(deletionEvent) account.sendMyPublicAndPrivateOutbox(deletionEvent)
} }
@@ -260,7 +260,7 @@ class LabeledBookmarkListsState(
) { ) {
val currentBookmarkList = getLabeledBookmarkListEvent(bookmarkListIdentifier) val currentBookmarkList = getLabeledBookmarkListEvent(bookmarkListIdentifier)
val updatedList = val updatedList =
LabeledBookmarkListEvent.addBookmark( BookmarkSetEvent.addBookmark(
earlierVersion = currentBookmarkList, earlierVersion = currentBookmarkList,
bookmarkIdTag = bookmark, bookmarkIdTag = bookmark,
isPrivate = isBookmarkPrivate, isPrivate = isBookmarkPrivate,
@@ -277,7 +277,7 @@ class LabeledBookmarkListsState(
) { ) {
val bookmarkList = getLabeledBookmarkListEvent(bookmarkListIdentifier) val bookmarkList = getLabeledBookmarkListEvent(bookmarkListIdentifier)
val updatedList = val updatedList =
LabeledBookmarkListEvent.moveBookmark( BookmarkSetEvent.moveBookmark(
earlierVersion = bookmarkList, earlierVersion = bookmarkList,
bookmarkIdTag = bookmark, bookmarkIdTag = bookmark,
isCurrentlyPrivate = isBookmarkCurrentlyPrivate, isCurrentlyPrivate = isBookmarkCurrentlyPrivate,
@@ -294,7 +294,7 @@ class LabeledBookmarkListsState(
) { ) {
val currentBookmarkList = getLabeledBookmarkListEvent(bookmarkListIdentifier) val currentBookmarkList = getLabeledBookmarkListEvent(bookmarkListIdentifier)
val updatedList = val updatedList =
LabeledBookmarkListEvent.removeBookmark( BookmarkSetEvent.removeBookmark(
earlierVersion = currentBookmarkList, earlierVersion = currentBookmarkList,
bookmarkIdTag = bookmark, bookmarkIdTag = bookmark,
isPrivate = isBookmarkPrivate, isPrivate = isBookmarkPrivate,
@@ -311,7 +311,7 @@ class LabeledBookmarkListsState(
) { ) {
if (deletedEventIds.isEmpty() && deletedAddresses.isEmpty()) return if (deletedEventIds.isEmpty() && deletedAddresses.isEmpty()) return
val currentList = getLabeledBookmarkListNote(bookmarkListIdentifier)?.event as? LabeledBookmarkListEvent ?: return val currentList = getLabeledBookmarkListNote(bookmarkListIdentifier)?.event as? BookmarkSetEvent ?: return
val newPublicTags = val newPublicTags =
currentList.tags currentList.tags
@@ -328,7 +328,7 @@ class LabeledBookmarkListsState(
val updatedList = val updatedList =
if (oldPrivateTags == null) { if (oldPrivateTags == null) {
if (newPublicTags.size == currentList.tags.size) return if (newPublicTags.size == currentList.tags.size) return
LabeledBookmarkListEvent.resign( BookmarkSetEvent.resign(
content = currentList.content, content = currentList.content,
tags = newPublicTags, tags = newPublicTags,
signer = account.signer, signer = account.signer,
@@ -344,7 +344,7 @@ class LabeledBookmarkListsState(
} }
}.toTypedArray() }.toTypedArray()
if (newPublicTags.size == currentList.tags.size && newPrivateTags.size == oldPrivateTags.size) return if (newPublicTags.size == currentList.tags.size && newPrivateTags.size == oldPrivateTags.size) return
LabeledBookmarkListEvent.resign( BookmarkSetEvent.resign(
tags = newPublicTags, tags = newPublicTags,
privateTags = newPrivateTags, privateTags = newPrivateTags,
signer = account.signer, signer = account.signer,
@@ -35,8 +35,8 @@ import com.vitorpamplona.quartz.nip01Core.core.HexKey
import com.vitorpamplona.quartz.nip01Core.signers.NostrSigner import com.vitorpamplona.quartz.nip01Core.signers.NostrSigner
import com.vitorpamplona.quartz.nip01Core.signers.update import com.vitorpamplona.quartz.nip01Core.signers.update
import com.vitorpamplona.quartz.nip01Core.tags.dTag.dTag import com.vitorpamplona.quartz.nip01Core.tags.dTag.dTag
import com.vitorpamplona.quartz.nip09Deletions.DeletionEvent import com.vitorpamplona.quartz.nip09Deletions.DeletionRequestEvent
import com.vitorpamplona.quartz.nip51Lists.followList.FollowListEvent import com.vitorpamplona.quartz.nip51Lists.followList.StarterPackEvent
import com.vitorpamplona.quartz.nip51Lists.followList.description import com.vitorpamplona.quartz.nip51Lists.followList.description
import com.vitorpamplona.quartz.nip51Lists.followList.image import com.vitorpamplona.quartz.nip51Lists.followList.image
import com.vitorpamplona.quartz.nip51Lists.followList.person import com.vitorpamplona.quartz.nip51Lists.followList.person
@@ -79,7 +79,7 @@ class FollowListsState(
// them from relays. // them from relays.
fun existingPeopleListNotes() = fun existingPeopleListNotes() =
cache.addressables cache.addressables
.filter(FollowListEvent.KIND, user.pubkeyHex) .filter(StarterPackEvent.KIND, user.pubkeyHex)
.filter { it.event != null || !cache.hasBeenDeleted(it.address) } .filter { it.event != null || !cache.hasBeenDeleted(it.address) }
val followListVersions = MutableStateFlow(0) val followListVersions = MutableStateFlow(0)
@@ -99,14 +99,14 @@ class FollowListsState(
.stateIn(scope, SharingStarted.Eagerly, emptySet()) .stateIn(scope, SharingStarted.Eagerly, emptySet())
@OptIn(ExperimentalCoroutinesApi::class) @OptIn(ExperimentalCoroutinesApi::class)
val latestLists: StateFlow<List<FollowListEvent>> = val latestLists: StateFlow<List<StarterPackEvent>> =
followListNotes followListNotes
.transformLatest { emitAll(it.updateFlow<FollowListEvent>()) } .transformLatest { emitAll(it.updateFlow<StarterPackEvent>()) }
.onStart { emit(followListNotes.value.events()) } .onStart { emit(followListNotes.value.events()) }
.flowOn(Dispatchers.IO) .flowOn(Dispatchers.IO)
.stateIn(scope, SharingStarted.Eagerly, emptyList()) .stateIn(scope, SharingStarted.Eagerly, emptyList())
fun List<FollowListEvent>.mapToUserIdSet() = this.map { it.followIdSet() }.flattenToSet() fun List<StarterPackEvent>.mapToUserIdSet() = this.map { it.followIdSet() }.flattenToSet()
val allPeopleListProfiles: StateFlow<Set<HexKey>> = val allPeopleListProfiles: StateFlow<Set<HexKey>> =
latestLists latestLists
@@ -115,7 +115,7 @@ class FollowListsState(
.flowOn(Dispatchers.IO) .flowOn(Dispatchers.IO)
.stateIn(scope, SharingStarted.Eagerly, emptySet()) .stateIn(scope, SharingStarted.Eagerly, emptySet())
fun FollowListEvent.toUI() = fun StarterPackEvent.toUI() =
PeopleList( PeopleList(
identifierTag = this.dTag(), identifierTag = this.dTag(),
title = this.title() ?: this.dTag(), title = this.title() ?: this.dTag(),
@@ -125,7 +125,7 @@ class FollowListsState(
publicMembers = cache.load(this.followIdSet()), publicMembers = cache.load(this.followIdSet()),
) )
fun List<FollowListEvent>.toUI() = this.map { it.toUI() } fun List<StarterPackEvent>.toUI() = this.map { it.toUI() }
val uiListFlow = val uiListFlow =
latestLists latestLists
@@ -148,12 +148,12 @@ class FollowListsState(
fun isUserInFollowSets(user: User): Boolean = allPeopleListProfiles.value.contains(user.pubkeyHex) fun isUserInFollowSets(user: User): Boolean = allPeopleListProfiles.value.contains(user.pubkeyHex)
fun DeletionEvent.hasDeletedAnyFollowList() = deleteAddressesWithKind(FollowListEvent.KIND) || deletesAnyEventIn(followListsEventIds.value) fun DeletionRequestEvent.hasDeletedAnyFollowList() = deleteAddressesWithKind(StarterPackEvent.KIND) || deletesAnyEventIn(followListsEventIds.value)
fun hasItemInNoteList(notes: Set<Note>): Boolean = fun hasItemInNoteList(notes: Set<Note>): Boolean =
notes.anyNotNullEvent { event -> notes.anyNotNullEvent { event ->
if (event.pubKey == signer.pubKey) { if (event.pubKey == signer.pubKey) {
event is FollowListEvent || (event is DeletionEvent && event.hasDeletedAnyFollowList()) event is StarterPackEvent || (event is DeletionRequestEvent && event.hasDeletedAnyFollowList())
} else { } else {
false false
} }
@@ -181,7 +181,7 @@ class FollowListsState(
fun getPeopleListNote(noteIdentifier: String): AddressableNote? = existingPeopleListNotes().find { it.dTag() == noteIdentifier } fun getPeopleListNote(noteIdentifier: String): AddressableNote? = existingPeopleListNotes().find { it.dTag() == noteIdentifier }
fun getPeopleList(noteIdentifier: String): FollowListEvent = getPeopleListNote(noteIdentifier)?.event as FollowListEvent fun getPeopleList(noteIdentifier: String): StarterPackEvent = getPeopleListNote(noteIdentifier)?.event as StarterPackEvent
fun User.toUserTag() = UserTag(this.pubkeyHex, this.bestRelayHint()) fun User.toUserTag() = UserTag(this.pubkeyHex, this.bestRelayHint())
@@ -198,7 +198,7 @@ class FollowListsState(
val dTag = UUID.randomUUID().toString() val dTag = UUID.randomUUID().toString()
val newListTemplate = val newListTemplate =
FollowListEvent.build( StarterPackEvent.build(
name = name, name = name,
people = if (!isPrivate && member != null) listOf(member.toUserTag()) else emptyList(), people = if (!isPrivate && member != null) listOf(member.toUserTag()) else emptyList(),
dTag = dTag, dTag = dTag,
@@ -262,7 +262,7 @@ class FollowListsState(
account: Account, account: Account,
) { ) {
val followListEvent = getPeopleList(identifierTag) val followListEvent = getPeopleList(identifierTag)
val deletionEvent = account.signer.sign(DeletionEvent.build(listOf(followListEvent))) val deletionEvent = account.signer.sign(DeletionRequestEvent.build(listOf(followListEvent)))
account.sendMyPublicAndPrivateOutbox(deletionEvent) account.sendMyPublicAndPrivateOutbox(deletionEvent)
// Any screen whose persisted feed filter still points at this follow pack would // Any screen whose persisted feed filter still points at this follow pack would
// keep re-creating an empty shell for its address (and render the dTag/UUID in // keep re-creating an empty shell for its address (and render the dTag/UUID in
@@ -35,9 +35,9 @@ import com.vitorpamplona.amethyst.model.Account
import com.vitorpamplona.quartz.nip01Core.core.HexKey import com.vitorpamplona.quartz.nip01Core.core.HexKey
import com.vitorpamplona.quartz.nip01Core.signers.NostrSigner import com.vitorpamplona.quartz.nip01Core.signers.NostrSigner
import com.vitorpamplona.quartz.nip01Core.signers.update import com.vitorpamplona.quartz.nip01Core.signers.update
import com.vitorpamplona.quartz.nip09Deletions.DeletionEvent import com.vitorpamplona.quartz.nip09Deletions.DeletionRequestEvent
import com.vitorpamplona.quartz.nip51Lists.muteList.tags.UserTag import com.vitorpamplona.quartz.nip51Lists.muteList.tags.UserTag
import com.vitorpamplona.quartz.nip51Lists.peopleList.PeopleListEvent import com.vitorpamplona.quartz.nip51Lists.peopleList.FollowSetEvent
import com.vitorpamplona.quartz.nip51Lists.peopleList.description import com.vitorpamplona.quartz.nip51Lists.peopleList.description
import com.vitorpamplona.quartz.nip51Lists.peopleList.image import com.vitorpamplona.quartz.nip51Lists.peopleList.image
import com.vitorpamplona.quartz.nip51Lists.peopleList.title import com.vitorpamplona.quartz.nip51Lists.peopleList.title
@@ -78,8 +78,8 @@ class PeopleListsState(
// can subscribe and fetch them from relays. // can subscribe and fetch them from relays.
fun existingPeopleListNotes() = fun existingPeopleListNotes() =
cache.addressables cache.addressables
.filter(PeopleListEvent.KIND, user.pubkeyHex) .filter(FollowSetEvent.KIND, user.pubkeyHex)
.filter { it.dTag() != PeopleListEvent.BLOCK_LIST_D_TAG || it.event != null } .filter { it.dTag() != FollowSetEvent.BLOCK_LIST_D_TAG || it.event != null }
.filter { it.event != null || !cache.hasBeenDeleted(it.address) } .filter { it.event != null || !cache.hasBeenDeleted(it.address) }
val peopleListVersions = MutableStateFlow(0) val peopleListVersions = MutableStateFlow(0)
@@ -99,21 +99,21 @@ class PeopleListsState(
.stateIn(scope, SharingStarted.Eagerly, emptySet()) .stateIn(scope, SharingStarted.Eagerly, emptySet())
@OptIn(ExperimentalCoroutinesApi::class) @OptIn(ExperimentalCoroutinesApi::class)
val latestLists: StateFlow<List<PeopleListEvent>> = val latestLists: StateFlow<List<FollowSetEvent>> =
peopleListNotes peopleListNotes
.transformLatest { emitAll(it.updateFlow<PeopleListEvent>()) } .transformLatest { emitAll(it.updateFlow<FollowSetEvent>()) }
.onStart { emit(peopleListNotes.value.events()) } .onStart { emit(peopleListNotes.value.events()) }
.flowOn(Dispatchers.IO) .flowOn(Dispatchers.IO)
.stateIn(scope, SharingStarted.Eagerly, emptyList()) .stateIn(scope, SharingStarted.Eagerly, emptyList())
suspend fun PeopleListEvent.userIdSet() = decryptionCache.userIdSet(this) suspend fun FollowSetEvent.userIdSet() = decryptionCache.userIdSet(this)
suspend fun List<PeopleListEvent>.mapToUserIdSet() = this.map { it.userIdSet() }.flattenToSet() suspend fun List<FollowSetEvent>.mapToUserIdSet() = this.map { it.userIdSet() }.flattenToSet()
suspend fun List<PeopleListEvent>.mapGoodUsersToIdSet() = suspend fun List<FollowSetEvent>.mapGoodUsersToIdSet() =
this this
.mapNotNull { .mapNotNull {
if (it.dTag() != PeopleListEvent.BLOCK_LIST_D_TAG) { if (it.dTag() != FollowSetEvent.BLOCK_LIST_D_TAG) {
it.userIdSet() it.userIdSet()
} else { } else {
null null
@@ -127,7 +127,7 @@ class PeopleListsState(
.flowOn(Dispatchers.IO) .flowOn(Dispatchers.IO)
.stateIn(scope, SharingStarted.Eagerly, emptySet()) .stateIn(scope, SharingStarted.Eagerly, emptySet())
suspend fun PeopleListEvent.toUI() = suspend fun FollowSetEvent.toUI() =
PeopleList( PeopleList(
identifierTag = this.dTag(), identifierTag = this.dTag(),
title = this.titleOrName() ?: this.dTag(), title = this.titleOrName() ?: this.dTag(),
@@ -137,7 +137,7 @@ class PeopleListsState(
publicMembers = cache.load(this.publicUsersIdSet()), publicMembers = cache.load(this.publicUsersIdSet()),
) )
suspend fun List<PeopleListEvent>.toUI() = this.map { it.toUI() }.sortedBy { it.title } suspend fun List<FollowSetEvent>.toUI() = this.map { it.toUI() }.sortedBy { it.title }
val uiListFlow = val uiListFlow =
latestLists latestLists
@@ -158,12 +158,12 @@ class PeopleListsState(
.map { it.select(dTag) } .map { it.select(dTag) }
.onStart { emit(selectList(dTag)) } .onStart { emit(selectList(dTag)) }
fun DeletionEvent.hasDeletedAnyPeopleList() = deleteAddressesWithKind(PeopleListEvent.KIND) || deletesAnyEventIn(peopleListsEventIds.value) fun DeletionRequestEvent.hasDeletedAnyPeopleList() = deleteAddressesWithKind(FollowSetEvent.KIND) || deletesAnyEventIn(peopleListsEventIds.value)
fun hasItemInNoteList(notes: Set<Note>): Boolean = fun hasItemInNoteList(notes: Set<Note>): Boolean =
notes.anyNotNullEvent { event -> notes.anyNotNullEvent { event ->
if (event.pubKey == signer.pubKey) { if (event.pubKey == signer.pubKey) {
event is PeopleListEvent || (event is DeletionEvent && event.hasDeletedAnyPeopleList()) event is FollowSetEvent || (event is DeletionRequestEvent && event.hasDeletedAnyPeopleList())
} else { } else {
false false
} }
@@ -191,7 +191,7 @@ class PeopleListsState(
fun getPeopleListNote(noteIdentifier: String): AddressableNote? = existingPeopleListNotes().find { it.dTag() == noteIdentifier } fun getPeopleListNote(noteIdentifier: String): AddressableNote? = existingPeopleListNotes().find { it.dTag() == noteIdentifier }
fun getPeopleList(noteIdentifier: String): PeopleListEvent = getPeopleListNote(noteIdentifier)?.event as PeopleListEvent fun getPeopleList(noteIdentifier: String): FollowSetEvent = getPeopleListNote(noteIdentifier)?.event as FollowSetEvent
fun User.toUserTag() = UserTag(this.pubkeyHex, this.bestRelayHint()) fun User.toUserTag() = UserTag(this.pubkeyHex, this.bestRelayHint())
@@ -207,7 +207,7 @@ class PeopleListsState(
): String { ): String {
val dTag = UUID.randomUUID().toString() val dTag = UUID.randomUUID().toString()
val newListTemplate = val newListTemplate =
PeopleListEvent.build( FollowSetEvent.build(
dTag = dTag, dTag = dTag,
title = listName, title = listName,
publicMembers = if (!isPrivate && member != null) listOf(member.toUserTag()) else emptyList(), publicMembers = if (!isPrivate && member != null) listOf(member.toUserTag()) else emptyList(),
@@ -252,7 +252,7 @@ class PeopleListsState(
account: Account, account: Account,
) { ) {
val newList = val newList =
PeopleListEvent.createListWithDescription( FollowSetEvent.createListWithDescription(
dTag = UUID.randomUUID().toString(), dTag = UUID.randomUUID().toString(),
title = customCloneName ?: currentPeopleList.title, title = customCloneName ?: currentPeopleList.title,
description = customCloneDescription ?: currentPeopleList.description, description = customCloneDescription ?: currentPeopleList.description,
@@ -268,7 +268,7 @@ class PeopleListsState(
account: Account, account: Account,
) { ) {
val followListEvent = getPeopleList(identifierTag) val followListEvent = getPeopleList(identifierTag)
val deletionEvent = account.signer.sign(DeletionEvent.build(listOf(followListEvent))) val deletionEvent = account.signer.sign(DeletionRequestEvent.build(listOf(followListEvent)))
account.sendMyPublicAndPrivateOutbox(deletionEvent) account.sendMyPublicAndPrivateOutbox(deletionEvent)
// Any screen whose persisted feed filter still points at this list would keep // Any screen whose persisted feed filter still points at this list would keep
// re-creating an empty shell for its address (and render the dTag/UUID in the // re-creating an empty shell for its address (and render the dTag/UUID in the
@@ -284,7 +284,7 @@ class PeopleListsState(
) { ) {
val followListEvent = getPeopleList(identifierTag) val followListEvent = getPeopleList(identifierTag)
val newList = val newList =
PeopleListEvent.addUser( FollowSetEvent.addUser(
earlierVersion = followListEvent, earlierVersion = followListEvent,
pubKeyHex = user.pubkeyHex, pubKeyHex = user.pubkeyHex,
relayHint = user.bestRelayHint(), relayHint = user.bestRelayHint(),
@@ -302,7 +302,7 @@ class PeopleListsState(
) { ) {
val followListEvent = getPeopleList(identifierTag) val followListEvent = getPeopleList(identifierTag)
val newList = val newList =
PeopleListEvent.addUserFirst( FollowSetEvent.addUserFirst(
earlierVersion = followListEvent, earlierVersion = followListEvent,
pubKeyHex = user.pubkeyHex, pubKeyHex = user.pubkeyHex,
relayHint = user.bestRelayHint(), relayHint = user.bestRelayHint(),
@@ -320,7 +320,7 @@ class PeopleListsState(
) { ) {
val followListEvent = getPeopleList(identifierTag) val followListEvent = getPeopleList(identifierTag)
val newList = val newList =
PeopleListEvent.removeUser( FollowSetEvent.removeUser(
earlierVersion = followListEvent, earlierVersion = followListEvent,
pubKeyHex = user.pubkeyHex, pubKeyHex = user.pubkeyHex,
isUserPrivate = isPrivate, isUserPrivate = isPrivate,
@@ -27,7 +27,7 @@ import com.vitorpamplona.amethyst.commons.model.nip51Lists.relayFeeds.RelayFeeds
import com.vitorpamplona.amethyst.model.AccountSettings import com.vitorpamplona.amethyst.model.AccountSettings
import com.vitorpamplona.quartz.nip01Core.relay.normalizer.NormalizedRelayUrl import com.vitorpamplona.quartz.nip01Core.relay.normalizer.NormalizedRelayUrl
import com.vitorpamplona.quartz.nip01Core.signers.NostrSigner import com.vitorpamplona.quartz.nip01Core.signers.NostrSigner
import com.vitorpamplona.quartz.nip51Lists.relayLists.RelayFeedsListEvent import com.vitorpamplona.quartz.nip51Lists.relayLists.FavoriteRelayListEvent
import com.vitorpamplona.quartz.utils.Log import com.vitorpamplona.quartz.utils.Log
import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.DelicateCoroutinesApi import kotlinx.coroutines.DelicateCoroutinesApi
@@ -50,13 +50,13 @@ class RelayFeedListState(
// Creates a long-term reference for this note so that the GC doesn't collect the note it self // Creates a long-term reference for this note so that the GC doesn't collect the note it self
val relayFeedListNote = cache.getOrCreateAddressableNote(getRelayFeedsListAddress()) val relayFeedListNote = cache.getOrCreateAddressableNote(getRelayFeedsListAddress())
fun getRelayFeedsListAddress() = RelayFeedsListEvent.createAddress(signer.pubKey) fun getRelayFeedsListAddress() = FavoriteRelayListEvent.createAddress(signer.pubKey)
fun getRelayFeedsListFlow(): StateFlow<NoteState> = relayFeedListNote.flow().metadata.stateFlow fun getRelayFeedsListFlow(): StateFlow<NoteState> = relayFeedListNote.flow().metadata.stateFlow
fun getRelayFeedsList(): RelayFeedsListEvent? = relayFeedListNote.event as? RelayFeedsListEvent fun getRelayFeedsList(): FavoriteRelayListEvent? = relayFeedListNote.event as? FavoriteRelayListEvent
fun relayFeedsListEvent(note: Note) = note.event as? RelayFeedsListEvent ?: settings.backupRelayFeedsList fun relayFeedsListEvent(note: Note) = note.event as? FavoriteRelayListEvent ?: settings.backupRelayFeedsList
suspend fun normalizeRelayFeedsListWithBackup(note: Note): Set<NormalizedRelayUrl> = relayFeedsListEvent(note)?.let { decryptionCache.relays(it) }?.ifEmpty { null } ?: emptySet() suspend fun normalizeRelayFeedsListWithBackup(note: Note): Set<NormalizedRelayUrl> = relayFeedsListEvent(note)?.let { decryptionCache.relays(it) }?.ifEmpty { null } ?: emptySet()
@@ -84,30 +84,30 @@ class RelayFeedListState(
emptySet(), emptySet(),
) )
suspend fun addRelay(relay: NormalizedRelayUrl): RelayFeedsListEvent { suspend fun addRelay(relay: NormalizedRelayUrl): FavoriteRelayListEvent {
val current = normalizeRelayFeedsListWithBackupNoDefaults(relayFeedListNote).toMutableList() val current = normalizeRelayFeedsListWithBackupNoDefaults(relayFeedListNote).toMutableList()
if (relay !in current) current.add(relay) if (relay !in current) current.add(relay)
return saveRelayList(current) return saveRelayList(current)
} }
suspend fun removeRelay(relay: NormalizedRelayUrl): RelayFeedsListEvent? { suspend fun removeRelay(relay: NormalizedRelayUrl): FavoriteRelayListEvent? {
val current = normalizeRelayFeedsListWithBackupNoDefaults(relayFeedListNote).toMutableList() val current = normalizeRelayFeedsListWithBackupNoDefaults(relayFeedListNote).toMutableList()
if (relay !in current) return null if (relay !in current) return null
current.remove(relay) current.remove(relay)
return saveRelayList(current) return saveRelayList(current)
} }
suspend fun saveRelayList(relayFeeds: List<NormalizedRelayUrl>): RelayFeedsListEvent { suspend fun saveRelayList(relayFeeds: List<NormalizedRelayUrl>): FavoriteRelayListEvent {
val relayFeedsList = getRelayFeedsList() val relayFeedsList = getRelayFeedsList()
return if (relayFeedsList != null && relayFeedsList.tags.isNotEmpty()) { return if (relayFeedsList != null && relayFeedsList.tags.isNotEmpty()) {
RelayFeedsListEvent.updateRelayList( FavoriteRelayListEvent.updateRelayList(
earlierVersion = relayFeedsList, earlierVersion = relayFeedsList,
relays = relayFeeds, relays = relayFeeds,
signer = signer, signer = signer,
) )
} else { } else {
RelayFeedsListEvent.create( FavoriteRelayListEvent.create(
relays = relayFeeds, relays = relayFeeds,
signer = signer, signer = signer,
) )
@@ -125,7 +125,7 @@ class RelayFeedListState(
Log.d("AccountRegisterObservers", "Relay feeds list Collector Start") Log.d("AccountRegisterObservers", "Relay feeds list Collector Start")
getRelayFeedsListFlow().collect { getRelayFeedsListFlow().collect {
Log.d("AccountRegisterObservers") { "Updating Relay feeds list for ${signer.pubKey}" } Log.d("AccountRegisterObservers") { "Updating Relay feeds list for ${signer.pubKey}" }
(it.note.event as? RelayFeedsListEvent)?.let { (it.note.event as? FavoriteRelayListEvent)?.let {
settings.updateRelayFeedList(it) settings.updateRelayFeedList(it)
} }
} }
@@ -41,7 +41,7 @@ import com.vitorpamplona.quartz.nip01Core.relay.client.accessories.fetchAllPages
import com.vitorpamplona.quartz.nip01Core.relay.normalizer.NormalizedRelayUrl import com.vitorpamplona.quartz.nip01Core.relay.normalizer.NormalizedRelayUrl
import com.vitorpamplona.quartz.nip01Core.signers.NostrSigner import com.vitorpamplona.quartz.nip01Core.signers.NostrSigner
import com.vitorpamplona.quartz.nip01Core.signers.NostrSignerInternal import com.vitorpamplona.quartz.nip01Core.signers.NostrSignerInternal
import com.vitorpamplona.quartz.nip09Deletions.DeletionEvent import com.vitorpamplona.quartz.nip09Deletions.DeletionRequestEvent
import com.vitorpamplona.quartz.nip60Cashu.history.CashuSpendingHistoryEvent import com.vitorpamplona.quartz.nip60Cashu.history.CashuSpendingHistoryEvent
import com.vitorpamplona.quartz.nip60Cashu.mintApi.DeterministicSecretFactory import com.vitorpamplona.quartz.nip60Cashu.mintApi.DeterministicSecretFactory
import com.vitorpamplona.quartz.nip60Cashu.mintApi.MeltQuoteBolt11ResponseDto import com.vitorpamplona.quartz.nip60Cashu.mintApi.MeltQuoteBolt11ResponseDto
@@ -486,7 +486,7 @@ class CashuWalletState(
// Process our own NIP-09 deletions inline rather than // Process our own NIP-09 deletions inline rather than
// relying on LocalCache's `deletedEventBundles`. That // relying on LocalCache's `deletedEventBundles`. That
// path only fires when `consume(DeletionEvent)` finds // path only fires when `consume(DeletionRequestEvent)` finds
// the target Note still resident in `notes` — a // the target Note still resident in `notes` — a
// LargeSoftCache backed by WeakReferences, which can // LargeSoftCache backed by WeakReferences, which can
// be cleared on any GC cycle. When the weak ref is // be cleared on any GC cycle. When the weak ref is
@@ -503,7 +503,7 @@ class CashuWalletState(
val ourDeleteIds = val ourDeleteIds =
all all
.asSequence() .asSequence()
.filterIsInstance<DeletionEvent>() .filterIsInstance<DeletionRequestEvent>()
.filter { it.pubKey == pubKey } .filter { it.pubKey == pubKey }
.flatMap { it.deleteEventIds().asSequence() } .flatMap { it.deleteEventIds().asSequence() }
.toSet() .toSet()
@@ -1447,7 +1447,7 @@ class CashuWalletState(
} }
val redundantIds = redundant.map { it.event.id }.toSet() val redundantIds = redundant.map { it.event.id }.toSet()
runCatching { runCatching {
val template = DeletionEvent.build(redundant.map { it.event }) val template = DeletionRequestEvent.build(redundant.map { it.event })
val signed = signer.sign(template) val signed = signer.sign(template)
publishEvent(signed) publishEvent(signed)
}.onFailure { }.onFailure {
@@ -67,7 +67,7 @@ import com.vitorpamplona.amethyst.model.Account
import com.vitorpamplona.quartz.lightning.LnInvoiceUtil import com.vitorpamplona.quartz.lightning.LnInvoiceUtil
import com.vitorpamplona.quartz.nip01Core.core.fastForEach import com.vitorpamplona.quartz.nip01Core.core.fastForEach
import com.vitorpamplona.quartz.nip02FollowList.ContactListEvent import com.vitorpamplona.quartz.nip02FollowList.ContactListEvent
import com.vitorpamplona.quartz.nip09Deletions.DeletionEvent import com.vitorpamplona.quartz.nip09Deletions.DeletionRequestEvent
import com.vitorpamplona.quartz.nip51Lists.muteList.MuteListEvent import com.vitorpamplona.quartz.nip51Lists.muteList.MuteListEvent
import com.vitorpamplona.quartz.nip65RelayList.AdvertisedRelayListEvent import com.vitorpamplona.quartz.nip65RelayList.AdvertisedRelayListEvent
import org.jetbrains.compose.resources.PluralStringResource import org.jetbrains.compose.resources.PluralStringResource
@@ -199,7 +199,7 @@ class NappletConsentSummary(
oneRemoved = Res.string.napplet_consent_diff_unmute_one, oneRemoved = Res.string.napplet_consent_diff_unmute_one,
) )
// Deletions have no prior version to compare against — the tags are the whole request. // Deletions have no prior version to compare against — the tags are the whole request.
DeletionEvent.KIND -> DeletionRequestEvent.KIND ->
pluralFor(Res.plurals.napplet_consent_effect_deletes, countTag(tags, "e") + countTag(tags, "a")) pluralFor(Res.plurals.napplet_consent_effect_deletes, countTag(tags, "e") + countTag(tags, "a"))
?.let { Consequence(it) } ?.let { Consequence(it) }
// Any other kind: at least tell the user tags exist and can be inspected, so an empty // Any other kind: at least tell the user tags exist and can be inspected, so an empty
@@ -23,7 +23,7 @@ package com.vitorpamplona.amethyst.napplet.gateways
import com.vitorpamplona.amethyst.model.Account import com.vitorpamplona.amethyst.model.Account
import com.vitorpamplona.quartz.nip01Core.relay.filters.Filter import com.vitorpamplona.quartz.nip01Core.relay.filters.Filter
import com.vitorpamplona.quartz.nip51Lists.muteList.tags.UserTag import com.vitorpamplona.quartz.nip51Lists.muteList.tags.UserTag
import com.vitorpamplona.quartz.nip57Zaps.LnZapEvent import com.vitorpamplona.quartz.nip57Zaps.ZapReceiptEvent
import com.vitorpamplona.quartz.nip58Badges.award.BadgeAwardEvent import com.vitorpamplona.quartz.nip58Badges.award.BadgeAwardEvent
import com.vitorpamplona.quartz.nip58Badges.definition.BadgeDefinitionEvent import com.vitorpamplona.quartz.nip58Badges.definition.BadgeDefinitionEvent
import kotlinx.serialization.json.add import kotlinx.serialization.json.add
@@ -137,7 +137,7 @@ class AccountIdentityReader(
val receipts = val receipts =
cache cache
.filter(Filter(kinds = listOf(9735), tags = mapOf("p" to listOf(pubkey)))) .filter(Filter(kinds = listOf(9735), tags = mapOf("p" to listOf(pubkey))))
.mapNotNull { it.event as? LnZapEvent } .mapNotNull { it.event as? ZapReceiptEvent }
return buildJsonArray { return buildJsonArray {
receipts.forEach { zap -> receipts.forEach { zap ->
addJsonObject { addJsonObject {
@@ -40,7 +40,7 @@ import com.vitorpamplona.quartz.nip01Core.core.toHexKey
import com.vitorpamplona.quartz.nip47WalletConnect.rpc.PayKeysendMethod import com.vitorpamplona.quartz.nip47WalletConnect.rpc.PayKeysendMethod
import com.vitorpamplona.quartz.nip47WalletConnect.rpc.Response import com.vitorpamplona.quartz.nip47WalletConnect.rpc.Response
import com.vitorpamplona.quartz.nip47WalletConnect.rpc.TlvRecord import com.vitorpamplona.quartz.nip47WalletConnect.rpc.TlvRecord
import com.vitorpamplona.quartz.nip57Zaps.LnZapEvent import com.vitorpamplona.quartz.nip57Zaps.ZapReceiptEvent
import com.vitorpamplona.quartz.nip57Zaps.validate.LnurlForm import com.vitorpamplona.quartz.nip57Zaps.validate.LnurlForm
import com.vitorpamplona.quartz.podcasts.PodcastBoostagram import com.vitorpamplona.quartz.podcasts.PodcastBoostagram
import com.vitorpamplona.quartz.podcasts.PodcastValue import com.vitorpamplona.quartz.podcasts.PodcastValue
@@ -91,7 +91,7 @@ class V4VPaymentHandler(
onProgress: (percent: Float) -> Unit, onProgress: (percent: Float) -> Unit,
onPayInvoicesViaIntent: (invoices: List<String>) -> Unit, onPayInvoicesViaIntent: (invoices: List<String>) -> Unit,
asZap: Boolean = false, asZap: Boolean = false,
zapType: LnZapEvent.ZapType = LnZapEvent.ZapType.PUBLIC, zapType: ZapReceiptEvent.ZapType = ZapReceiptEvent.ZapType.PUBLIC,
) = withContext(Dispatchers.IO) { ) = withContext(Dispatchers.IO) {
val shares = value.computeShares(totalMilliSats) val shares = value.computeShares(totalMilliSats)
if (shares.isEmpty()) { if (shares.isEmpty()) {
@@ -190,7 +190,7 @@ class V4VPaymentHandler(
shares: List<PodcastValueShare>, shares: List<PodcastValueShare>,
message: String, message: String,
asZap: Boolean, asZap: Boolean,
zapType: LnZapEvent.ZapType, zapType: ZapReceiptEvent.ZapType,
zappedNote: Note?, zappedNote: Note?,
okHttpClient: (String) -> OkHttpClient, okHttpClient: (String) -> OkHttpClient,
context: Context, context: Context,
@@ -48,8 +48,8 @@ import com.vitorpamplona.quartz.nip01Core.relay.normalizer.NormalizedRelayUrl
import com.vitorpamplona.quartz.nip47WalletConnect.rpc.NwcErrorCode import com.vitorpamplona.quartz.nip47WalletConnect.rpc.NwcErrorCode
import com.vitorpamplona.quartz.nip47WalletConnect.rpc.NwcTransactionMetadata import com.vitorpamplona.quartz.nip47WalletConnect.rpc.NwcTransactionMetadata
import com.vitorpamplona.quartz.nip53LiveActivities.streaming.LiveActivitiesEvent import com.vitorpamplona.quartz.nip53LiveActivities.streaming.LiveActivitiesEvent
import com.vitorpamplona.quartz.nip57Zaps.LnZapEvent import com.vitorpamplona.quartz.nip57Zaps.ZapReceiptEvent
import com.vitorpamplona.quartz.nip57Zaps.LnZapRequestEvent import com.vitorpamplona.quartz.nip57Zaps.ZapRequestEvent
import com.vitorpamplona.quartz.nip57Zaps.splits.ZapSplitSetup import com.vitorpamplona.quartz.nip57Zaps.splits.ZapSplitSetup
import com.vitorpamplona.quartz.nip57Zaps.splits.ZapSplitSetupLnAddress import com.vitorpamplona.quartz.nip57Zaps.splits.ZapSplitSetupLnAddress
import com.vitorpamplona.quartz.nip57Zaps.splits.zapSplitSetup import com.vitorpamplona.quartz.nip57Zaps.splits.zapSplitSetup
@@ -79,7 +79,7 @@ class ZapPaymentHandler(
// The signed kind 9734 this invoice was fetched with, and the message on it. // The signed kind 9734 this invoice was fetched with, and the message on it.
// Carried so the NWC payment can name the payee (NWC-06 `metadata`); null for // Carried so the NWC payment can name the payee (NWC-06 `metadata`); null for
// a NONZAP split, which has no zap request to send. // a NONZAP split, which has no zap request to send.
val zapRequest: LnZapRequestEvent? = null, val zapRequest: ZapRequestEvent? = null,
val message: String = "", val message: String = "",
) )
@@ -122,7 +122,7 @@ class ZapPaymentHandler(
onError: (String, String, User?) -> Unit, onError: (String, String, User?) -> Unit,
onProgress: (percent: Float) -> Unit, onProgress: (percent: Float) -> Unit,
onPayViaIntent: (ImmutableList<Payable>) -> Unit, onPayViaIntent: (ImmutableList<Payable>) -> Unit,
zapType: LnZapEvent.ZapType, zapType: ZapReceiptEvent.ZapType,
) = withContext(Dispatchers.IO) { ) = withContext(Dispatchers.IO) {
val noteEvent = note.event val noteEvent = note.event
val zapSplitSetup = noteEvent?.zapSplitSetup() val zapSplitSetup = noteEvent?.zapSplitSetup()
@@ -297,7 +297,7 @@ class ZapPaymentHandler(
note: Note, note: Note,
pollOption: Int?, pollOption: Int?,
message: String, message: String,
zapType: LnZapEvent.ZapType, zapType: ZapReceiptEvent.ZapType,
totalAmountMilliSats: Long, totalAmountMilliSats: Long,
totalWeight: Double, totalWeight: Double,
okHttpClient: (String) -> OkHttpClient, okHttpClient: (String) -> OkHttpClient,
@@ -360,14 +360,14 @@ class ZapPaymentHandler(
class ZapRequestReady( class ZapRequestReady(
val inputSetup: MyZapSplitSetup, val inputSetup: MyZapSplitSetup,
val zapRequest: LnZapRequestEvent?, val zapRequest: ZapRequestEvent?,
) )
suspend fun signAllZapRequests( suspend fun signAllZapRequests(
note: Note, note: Note,
pollOption: Int?, pollOption: Int?,
message: String, message: String,
zapType: LnZapEvent.ZapType, zapType: ZapReceiptEvent.ZapType,
zapsToSend: List<MyZapSplitSetup>, zapsToSend: List<MyZapSplitSetup>,
totalAmountMilliSats: Long, totalAmountMilliSats: Long,
// Shared across the lightning + BOLT12 lanes so a mixed split stays proportional. // Shared across the lightning + BOLT12 lanes so a mixed split stays proportional.
@@ -389,7 +389,7 @@ class ZapPaymentHandler(
val splitLnurl = LnurlForm.toUrl(next.lnAddress)?.let(LnurlForm::urlToBech32) val splitLnurl = LnurlForm.toUrl(next.lnAddress)?.let(LnurlForm::urlToBech32)
val zapRequest = val zapRequest =
if (zapType != LnZapEvent.ZapType.NONZAP && noteEvent != null) { if (zapType != ZapReceiptEvent.ZapType.NONZAP && noteEvent != null) {
account.zaps.createZapRequestFor( account.zaps.createZapRequestFor(
event = noteEvent, event = noteEvent,
pollOption = pollOption, pollOption = pollOption,
@@ -535,7 +535,7 @@ class ZapPaymentHandler(
totalAmountMilliSats: Long, totalAmountMilliSats: Long,
totalWeight: Double, totalWeight: Double,
message: String, message: String,
zapType: LnZapEvent.ZapType, zapType: ZapReceiptEvent.ZapType,
okHttpClient: (String) -> OkHttpClient, okHttpClient: (String) -> OkHttpClient,
onError: (String, String, User?) -> Unit, onError: (String, String, User?) -> Unit,
onProgress: (percent: Float) -> Unit, onProgress: (percent: Float) -> Unit,
@@ -672,7 +672,7 @@ class ZapPaymentHandler(
private suspend fun assembleInvoice( private suspend fun assembleInvoice(
lud16: String, lud16: String,
splitSetup: MyZapSplitSetup, splitSetup: MyZapSplitSetup,
nostrZapRequest: LnZapRequestEvent?, nostrZapRequest: ZapRequestEvent?,
zapValue: Long, zapValue: Long,
message: String, message: String,
okHttpClient: (String) -> OkHttpClient, okHttpClient: (String) -> OkHttpClient,
@@ -683,7 +683,7 @@ class ZapPaymentHandler(
// Only the request the provider actually accepted may be claimed as bound to // Only the request the provider actually accepted may be claimed as bound to
// this invoice; see lnAddressInvoice's onZapRequestSent. // this invoice; see lnAddressInvoice's onZapRequestSent.
var sentZapRequest: LnZapRequestEvent? = null var sentZapRequest: ZapRequestEvent? = null
val invoice = val invoice =
LightningAddressResolver().lnAddressInvoice( LightningAddressResolver().lnAddressInvoice(
@@ -38,7 +38,7 @@ import com.vitorpamplona.amethyst.commons.ui.loadStringRes
import com.vitorpamplona.amethyst.service.HttpStatusMessages import com.vitorpamplona.amethyst.service.HttpStatusMessages
import com.vitorpamplona.quartz.lightning.LnInvoiceUtil import com.vitorpamplona.quartz.lightning.LnInvoiceUtil
import com.vitorpamplona.quartz.lightning.Lud06 import com.vitorpamplona.quartz.lightning.Lud06
import com.vitorpamplona.quartz.nip57Zaps.LnZapRequestEvent import com.vitorpamplona.quartz.nip57Zaps.ZapRequestEvent
import com.vitorpamplona.quartz.nip57Zaps.validate.LnurlEndpointCache import com.vitorpamplona.quartz.nip57Zaps.validate.LnurlEndpointCache
import com.vitorpamplona.quartz.nip57Zaps.validate.LnurlEndpointInfo import com.vitorpamplona.quartz.nip57Zaps.validate.LnurlEndpointInfo
import com.vitorpamplona.quartz.utils.Log import com.vitorpamplona.quartz.utils.Log
@@ -128,7 +128,7 @@ class LightningAddressResolver {
lnCallback: String, lnCallback: String,
milliSats: Long, milliSats: Long,
message: String, message: String,
nostrRequest: LnZapRequestEvent? = null, nostrRequest: ZapRequestEvent? = null,
okHttpClient: (String) -> OkHttpClient, okHttpClient: (String) -> OkHttpClient,
context: Context, context: Context,
): String { ): String {
@@ -219,11 +219,11 @@ class LightningAddressResolver {
lnAddress: String, lnAddress: String,
milliSats: Long, milliSats: Long,
message: String, message: String,
nostrRequest: LnZapRequestEvent? = null, nostrRequest: ZapRequestEvent? = null,
okHttpClient: (String) -> OkHttpClient, okHttpClient: (String) -> OkHttpClient,
onProgress: (percent: Float) -> Unit, onProgress: (percent: Float) -> Unit,
context: Context, context: Context,
onZapRequestSent: (LnZapRequestEvent?) -> Unit = {}, onZapRequestSent: (ZapRequestEvent?) -> Unit = {},
): String { ): String {
val mapper = ObjectMapper() val mapper = ObjectMapper()
@@ -64,7 +64,7 @@ import com.vitorpamplona.quartz.marmot.mip02Welcome.WelcomeEvent
import com.vitorpamplona.quartz.nip01Core.core.Event import com.vitorpamplona.quartz.nip01Core.core.Event
import com.vitorpamplona.quartz.nip01Core.core.toHexKey import com.vitorpamplona.quartz.nip01Core.core.toHexKey
import com.vitorpamplona.quartz.nip01Core.tags.people.isTaggedUser import com.vitorpamplona.quartz.nip01Core.tags.people.isTaggedUser
import com.vitorpamplona.quartz.nip04Dm.messages.PrivateDmEvent import com.vitorpamplona.quartz.nip04Dm.messages.EncryptedDmEvent
import com.vitorpamplona.quartz.nip10Notes.TextNoteEvent import com.vitorpamplona.quartz.nip10Notes.TextNoteEvent
import com.vitorpamplona.quartz.nip17Dm.files.ChatMessageEncryptedFileHeaderEvent import com.vitorpamplona.quartz.nip17Dm.files.ChatMessageEncryptedFileHeaderEvent
import com.vitorpamplona.quartz.nip17Dm.messages.ChatMessageEvent import com.vitorpamplona.quartz.nip17Dm.messages.ChatMessageEvent
@@ -72,7 +72,7 @@ import com.vitorpamplona.quartz.nip18Reposts.GenericRepostEvent
import com.vitorpamplona.quartz.nip18Reposts.RepostEvent import com.vitorpamplona.quartz.nip18Reposts.RepostEvent
import com.vitorpamplona.quartz.nip19Bech32.bech32.bechToBytes import com.vitorpamplona.quartz.nip19Bech32.bech32.bechToBytes
import com.vitorpamplona.quartz.nip22Comments.CommentEvent import com.vitorpamplona.quartz.nip22Comments.CommentEvent
import com.vitorpamplona.quartz.nip23LongContent.LongTextNoteEvent import com.vitorpamplona.quartz.nip23LongContent.LongFormContentEvent
import com.vitorpamplona.quartz.nip25Reactions.ReactionEvent import com.vitorpamplona.quartz.nip25Reactions.ReactionEvent
import com.vitorpamplona.quartz.nip28PublicChat.message.ChannelMessageEvent import com.vitorpamplona.quartz.nip28PublicChat.message.ChannelMessageEvent
import com.vitorpamplona.quartz.nip34Git.issue.GitIssueEvent import com.vitorpamplona.quartz.nip34Git.issue.GitIssueEvent
@@ -84,17 +84,17 @@ import com.vitorpamplona.quartz.nip34Git.status.GitStatusAppliedEvent
import com.vitorpamplona.quartz.nip34Git.status.GitStatusClosedEvent import com.vitorpamplona.quartz.nip34Git.status.GitStatusClosedEvent
import com.vitorpamplona.quartz.nip34Git.status.GitStatusDraftEvent import com.vitorpamplona.quartz.nip34Git.status.GitStatusDraftEvent
import com.vitorpamplona.quartz.nip34Git.status.GitStatusOpenEvent import com.vitorpamplona.quartz.nip34Git.status.GitStatusOpenEvent
import com.vitorpamplona.quartz.nip54Wiki.WikiNoteEvent import com.vitorpamplona.quartz.nip54Wiki.WikiArticleEvent
import com.vitorpamplona.quartz.nip57Zaps.LnZapEvent import com.vitorpamplona.quartz.nip57Zaps.ZapReceiptEvent
import com.vitorpamplona.quartz.nip58Badges.award.BadgeAwardEvent import com.vitorpamplona.quartz.nip58Badges.award.BadgeAwardEvent
import com.vitorpamplona.quartz.nip61Nutzaps.nutzap.NutzapEvent import com.vitorpamplona.quartz.nip61Nutzaps.nutzap.NutzapEvent
import com.vitorpamplona.quartz.nip64Chess.challenge.accept.LiveChessGameAcceptEvent import com.vitorpamplona.quartz.nip64Chess.challenge.accept.LiveChessGameAcceptEvent
import com.vitorpamplona.quartz.nip64Chess.move.LiveChessMoveEvent import com.vitorpamplona.quartz.nip64Chess.move.LiveChessMoveEvent
import com.vitorpamplona.quartz.nip68Picture.PictureEvent import com.vitorpamplona.quartz.nip68Picture.PictureEvent
import com.vitorpamplona.quartz.nip71Video.VideoHorizontalEvent import com.vitorpamplona.quartz.nip71Video.AddressableNormalVideoEvent
import com.vitorpamplona.quartz.nip71Video.AddressableShortVideoEvent
import com.vitorpamplona.quartz.nip71Video.VideoNormalEvent import com.vitorpamplona.quartz.nip71Video.VideoNormalEvent
import com.vitorpamplona.quartz.nip71Video.VideoShortEvent import com.vitorpamplona.quartz.nip71Video.VideoShortEvent
import com.vitorpamplona.quartz.nip71Video.VideoVerticalEvent
import com.vitorpamplona.quartz.nip84Highlights.HighlightEvent import com.vitorpamplona.quartz.nip84Highlights.HighlightEvent
import com.vitorpamplona.quartz.nip88Polls.poll.PollEvent import com.vitorpamplona.quartz.nip88Polls.poll.PollEvent
import com.vitorpamplona.quartz.nipACWebRtcCalls.events.CallOfferEvent import com.vitorpamplona.quartz.nipACWebRtcCalls.events.CallOfferEvent
@@ -234,7 +234,7 @@ class EventNotificationConsumer(
// public chat the user has silenced (matches the in-app feed, which mutes all four). // public chat the user has silenced (matches the in-app feed, which mutes all four).
// Without the second check, muting a channel still let a like on your own message // Without the second check, muting a channel still let a like on your own message
// there notify you — the row's glyph promises silence, so it has to mean it. // there notify you — the row's glyph promises silence, so it has to mean it.
if (event is ReactionEvent || event is LnZapEvent || event is RepostEvent || event is GenericRepostEvent) { if (event is ReactionEvent || event is ZapReceiptEvent || event is RepostEvent || event is GenericRepostEvent) {
val target = LocalCache.getNoteIfExists(event)?.replyTo?.lastOrNull() val target = LocalCache.getNoteIfExists(event)?.replyTo?.lastOrNull()
if (target != null && if (target != null &&
( (
@@ -247,7 +247,7 @@ class EventNotificationConsumer(
} }
when (event) { when (event) {
is PrivateDmEvent -> DirectMessageNotification.notify(applicationContext, account, event) is EncryptedDmEvent -> DirectMessageNotification.notify(applicationContext, account, event)
is ChatMessageEvent -> DirectMessageNotification.notify(applicationContext, account, event) is ChatMessageEvent -> DirectMessageNotification.notify(applicationContext, account, event)
is ChatMessageEncryptedFileHeaderEvent -> DirectMessageNotification.notify(applicationContext, account, event) is ChatMessageEncryptedFileHeaderEvent -> DirectMessageNotification.notify(applicationContext, account, event)
@@ -256,7 +256,7 @@ class EventNotificationConsumer(
is StreamMessageV2Event -> BuzzDmNotification.notify(applicationContext, account, event) is StreamMessageV2Event -> BuzzDmNotification.notify(applicationContext, account, event)
is ChatEvent -> BuzzDmNotification.notify(applicationContext, account, event) is ChatEvent -> BuzzDmNotification.notify(applicationContext, account, event)
is LnZapEvent -> ZapNotification.notify(applicationContext, account, event) is ZapReceiptEvent -> ZapNotification.notify(applicationContext, account, event)
is NutzapEvent -> ZapNotification.notify(applicationContext, account, event) is NutzapEvent -> ZapNotification.notify(applicationContext, account, event)
is OnchainZapEvent -> ZapNotification.notify(applicationContext, account, event) is OnchainZapEvent -> ZapNotification.notify(applicationContext, account, event)
@@ -274,15 +274,15 @@ class EventNotificationConsumer(
is PictureEvent, is PictureEvent,
is VideoNormalEvent, is VideoNormalEvent,
is VideoShortEvent, is VideoShortEvent,
is VideoHorizontalEvent, is AddressableNormalVideoEvent,
is VideoVerticalEvent, is AddressableShortVideoEvent,
-> MediaNotification.notify(applicationContext, account, event) -> MediaNotification.notify(applicationContext, account, event)
is PollEvent -> MentionNotification.notify(applicationContext, account, event, titleRes = Res.string.app_notification_poll_channel_message) is PollEvent -> MentionNotification.notify(applicationContext, account, event, titleRes = Res.string.app_notification_poll_channel_message)
is HighlightEvent, is HighlightEvent,
is LongTextNoteEvent, is LongFormContentEvent,
is WikiNoteEvent, is WikiArticleEvent,
-> ArticleNotification.notify(applicationContext, account, event) -> ArticleNotification.notify(applicationContext, account, event)
is GitIssueEvent -> CodeNotification.notify(applicationContext, account, event) is GitIssueEvent -> CodeNotification.notify(applicationContext, account, event)
@@ -27,11 +27,11 @@ import com.vitorpamplona.amethyst.commons.model.nip71Video.selectVideoTrack
import com.vitorpamplona.amethyst.commons.richtext.RichTextParser import com.vitorpamplona.amethyst.commons.richtext.RichTextParser
import com.vitorpamplona.quartz.nip01Core.core.Event import com.vitorpamplona.quartz.nip01Core.core.Event
import com.vitorpamplona.quartz.nip01Core.signers.NostrSigner import com.vitorpamplona.quartz.nip01Core.signers.NostrSigner
import com.vitorpamplona.quartz.nip04Dm.messages.PrivateDmEvent import com.vitorpamplona.quartz.nip04Dm.messages.EncryptedDmEvent
import com.vitorpamplona.quartz.nip19Bech32.Nip19Parser import com.vitorpamplona.quartz.nip19Bech32.Nip19Parser
import com.vitorpamplona.quartz.nip19Bech32.entities.NProfile import com.vitorpamplona.quartz.nip19Bech32.entities.NProfile
import com.vitorpamplona.quartz.nip19Bech32.entities.NPub import com.vitorpamplona.quartz.nip19Bech32.entities.NPub
import com.vitorpamplona.quartz.nip57Zaps.LnZapRequestEvent import com.vitorpamplona.quartz.nip57Zaps.ZapRequestEvent
import com.vitorpamplona.quartz.nip68Picture.PictureEvent import com.vitorpamplona.quartz.nip68Picture.PictureEvent
import com.vitorpamplona.quartz.nip71Video.VideoEvent import com.vitorpamplona.quartz.nip71Video.VideoEvent
@@ -177,7 +177,7 @@ object NotificationContent {
} }
suspend fun decryptZapContentAuthor( suspend fun decryptZapContentAuthor(
event: LnZapRequestEvent, event: ZapRequestEvent,
signer: NostrSigner, signer: NostrSigner,
): Event? = ): Event? =
if (event.isPrivateZap() && event.zappedAuthor().contains(event.pubKey)) { if (event.isPrivateZap() && event.zappedAuthor().contains(event.pubKey)) {
@@ -191,8 +191,8 @@ object NotificationContent {
signer: NostrSigner, signer: NostrSigner,
): String? = ): String? =
when (val event = note.event) { when (val event = note.event) {
is PrivateDmEvent -> event.decryptContent(signer) is EncryptedDmEvent -> event.decryptContent(signer)
is LnZapRequestEvent -> decryptZapContentAuthor(event, signer)?.content is ZapRequestEvent -> decryptZapContentAuthor(event, signer)?.content
else -> event?.content else -> event?.content
} }
@@ -32,7 +32,7 @@ import com.vitorpamplona.quartz.marmot.mip02Welcome.WelcomeEvent
import com.vitorpamplona.quartz.nip01Core.core.Event import com.vitorpamplona.quartz.nip01Core.core.Event
import com.vitorpamplona.quartz.nip01Core.core.toHexKey import com.vitorpamplona.quartz.nip01Core.core.toHexKey
import com.vitorpamplona.quartz.nip01Core.tags.people.isTaggedUser import com.vitorpamplona.quartz.nip01Core.tags.people.isTaggedUser
import com.vitorpamplona.quartz.nip04Dm.messages.PrivateDmEvent import com.vitorpamplona.quartz.nip04Dm.messages.EncryptedDmEvent
import com.vitorpamplona.quartz.nip10Notes.TextNoteEvent import com.vitorpamplona.quartz.nip10Notes.TextNoteEvent
import com.vitorpamplona.quartz.nip17Dm.files.ChatMessageEncryptedFileHeaderEvent import com.vitorpamplona.quartz.nip17Dm.files.ChatMessageEncryptedFileHeaderEvent
import com.vitorpamplona.quartz.nip17Dm.messages.ChatMessageEvent import com.vitorpamplona.quartz.nip17Dm.messages.ChatMessageEvent
@@ -40,7 +40,7 @@ import com.vitorpamplona.quartz.nip18Reposts.GenericRepostEvent
import com.vitorpamplona.quartz.nip18Reposts.RepostEvent import com.vitorpamplona.quartz.nip18Reposts.RepostEvent
import com.vitorpamplona.quartz.nip19Bech32.bech32.bechToBytes import com.vitorpamplona.quartz.nip19Bech32.bech32.bechToBytes
import com.vitorpamplona.quartz.nip22Comments.CommentEvent import com.vitorpamplona.quartz.nip22Comments.CommentEvent
import com.vitorpamplona.quartz.nip23LongContent.LongTextNoteEvent import com.vitorpamplona.quartz.nip23LongContent.LongFormContentEvent
import com.vitorpamplona.quartz.nip25Reactions.ReactionEvent import com.vitorpamplona.quartz.nip25Reactions.ReactionEvent
import com.vitorpamplona.quartz.nip28PublicChat.message.ChannelMessageEvent import com.vitorpamplona.quartz.nip28PublicChat.message.ChannelMessageEvent
import com.vitorpamplona.quartz.nip34Git.issue.GitIssueEvent import com.vitorpamplona.quartz.nip34Git.issue.GitIssueEvent
@@ -52,17 +52,17 @@ import com.vitorpamplona.quartz.nip34Git.status.GitStatusAppliedEvent
import com.vitorpamplona.quartz.nip34Git.status.GitStatusClosedEvent import com.vitorpamplona.quartz.nip34Git.status.GitStatusClosedEvent
import com.vitorpamplona.quartz.nip34Git.status.GitStatusDraftEvent import com.vitorpamplona.quartz.nip34Git.status.GitStatusDraftEvent
import com.vitorpamplona.quartz.nip34Git.status.GitStatusOpenEvent import com.vitorpamplona.quartz.nip34Git.status.GitStatusOpenEvent
import com.vitorpamplona.quartz.nip54Wiki.WikiNoteEvent import com.vitorpamplona.quartz.nip54Wiki.WikiArticleEvent
import com.vitorpamplona.quartz.nip57Zaps.LnZapEvent import com.vitorpamplona.quartz.nip57Zaps.ZapReceiptEvent
import com.vitorpamplona.quartz.nip58Badges.award.BadgeAwardEvent import com.vitorpamplona.quartz.nip58Badges.award.BadgeAwardEvent
import com.vitorpamplona.quartz.nip61Nutzaps.nutzap.NutzapEvent import com.vitorpamplona.quartz.nip61Nutzaps.nutzap.NutzapEvent
import com.vitorpamplona.quartz.nip64Chess.challenge.accept.LiveChessGameAcceptEvent import com.vitorpamplona.quartz.nip64Chess.challenge.accept.LiveChessGameAcceptEvent
import com.vitorpamplona.quartz.nip64Chess.move.LiveChessMoveEvent import com.vitorpamplona.quartz.nip64Chess.move.LiveChessMoveEvent
import com.vitorpamplona.quartz.nip68Picture.PictureEvent import com.vitorpamplona.quartz.nip68Picture.PictureEvent
import com.vitorpamplona.quartz.nip71Video.VideoHorizontalEvent import com.vitorpamplona.quartz.nip71Video.AddressableNormalVideoEvent
import com.vitorpamplona.quartz.nip71Video.AddressableShortVideoEvent
import com.vitorpamplona.quartz.nip71Video.VideoNormalEvent import com.vitorpamplona.quartz.nip71Video.VideoNormalEvent
import com.vitorpamplona.quartz.nip71Video.VideoShortEvent import com.vitorpamplona.quartz.nip71Video.VideoShortEvent
import com.vitorpamplona.quartz.nip71Video.VideoVerticalEvent
import com.vitorpamplona.quartz.nip84Highlights.HighlightEvent import com.vitorpamplona.quartz.nip84Highlights.HighlightEvent
import com.vitorpamplona.quartz.nip88Polls.poll.PollEvent import com.vitorpamplona.quartz.nip88Polls.poll.PollEvent
import com.vitorpamplona.quartz.nipACWebRtcCalls.events.CallOfferEvent import com.vitorpamplona.quartz.nipACWebRtcCalls.events.CallOfferEvent
@@ -118,8 +118,8 @@ class NotificationDispatcher(
internal val NOTIFICATION_KINDS: Set<Int> = internal val NOTIFICATION_KINDS: Set<Int> =
setOf( setOf(
// Direct-arrival // Direct-arrival
PrivateDmEvent.KIND, EncryptedDmEvent.KIND,
LnZapEvent.KIND, ZapReceiptEvent.KIND,
NutzapEvent.KIND, NutzapEvent.KIND,
OnchainZapEvent.KIND, OnchainZapEvent.KIND,
Bolt12ZapEvent.KIND, Bolt12ZapEvent.KIND,
@@ -133,8 +133,8 @@ class NotificationDispatcher(
PictureEvent.KIND, PictureEvent.KIND,
VideoNormalEvent.KIND, VideoNormalEvent.KIND,
VideoShortEvent.KIND, VideoShortEvent.KIND,
VideoHorizontalEvent.KIND, AddressableNormalVideoEvent.KIND,
VideoVerticalEvent.KIND, AddressableShortVideoEvent.KIND,
ChannelMessageEvent.KIND, ChannelMessageEvent.KIND,
PollEvent.KIND, PollEvent.KIND,
GitPatchEvent.KIND, GitPatchEvent.KIND,
@@ -150,8 +150,8 @@ class NotificationDispatcher(
GitStatusClosedEvent.KIND, GitStatusClosedEvent.KIND,
GitStatusDraftEvent.KIND, GitStatusDraftEvent.KIND,
HighlightEvent.KIND, HighlightEvent.KIND,
LongTextNoteEvent.KIND, LongFormContentEvent.KIND,
WikiNoteEvent.KIND, WikiArticleEvent.KIND,
LiveChessGameAcceptEvent.KIND, LiveChessGameAcceptEvent.KIND,
LiveChessMoveEvent.KIND, LiveChessMoveEvent.KIND,
WakeUpEvent.KIND, WakeUpEvent.KIND,
@@ -32,7 +32,7 @@ import com.vitorpamplona.amethyst.service.notifications.NotificationUtils.Conver
import com.vitorpamplona.amethyst.service.notifications.NotificationUtils.ReplyAction import com.vitorpamplona.amethyst.service.notifications.NotificationUtils.ReplyAction
import com.vitorpamplona.amethyst.service.notifications.NotificationUtils.postConversation import com.vitorpamplona.amethyst.service.notifications.NotificationUtils.postConversation
import com.vitorpamplona.amethyst.service.notifications.notificationManager import com.vitorpamplona.amethyst.service.notifications.notificationManager
import com.vitorpamplona.quartz.nip04Dm.messages.PrivateDmEvent import com.vitorpamplona.quartz.nip04Dm.messages.EncryptedDmEvent
import com.vitorpamplona.quartz.nip17Dm.base.ChatroomKey import com.vitorpamplona.quartz.nip17Dm.base.ChatroomKey
import com.vitorpamplona.quartz.nip17Dm.files.ChatMessageEncryptedFileHeaderEvent import com.vitorpamplona.quartz.nip17Dm.files.ChatMessageEncryptedFileHeaderEvent
import com.vitorpamplona.quartz.nip17Dm.messages.ChatMessageEvent import com.vitorpamplona.quartz.nip17Dm.messages.ChatMessageEvent
@@ -64,7 +64,7 @@ object DirectMessageNotification {
suspend fun notify( suspend fun notify(
context: Context, context: Context,
account: Account, account: Account,
event: PrivateDmEvent, event: EncryptedDmEvent,
) { ) {
if (account.signer.pubKey != event.verifiedRecipientPubKey()) return if (account.signer.pubKey != event.verifiedRecipientPubKey()) return
notifyRoom(context, account, event.id, event.createdAt, event.chatroomKey(account.signer.pubKey), decrypt = true) notifyRoom(context, account, event.id, event.createdAt, event.chatroomKey(account.signer.pubKey), decrypt = true)
@@ -39,8 +39,8 @@ import com.vitorpamplona.amethyst.service.notifications.NotificationRoutes
import com.vitorpamplona.amethyst.service.notifications.NotificationUtils.postStandard import com.vitorpamplona.amethyst.service.notifications.NotificationUtils.postStandard
import com.vitorpamplona.amethyst.service.notifications.notificationManager import com.vitorpamplona.amethyst.service.notifications.notificationManager
import com.vitorpamplona.amethyst.ui.note.showAmount import com.vitorpamplona.amethyst.ui.note.showAmount
import com.vitorpamplona.quartz.nip57Zaps.LnZapEvent import com.vitorpamplona.quartz.nip57Zaps.ZapReceiptEvent
import com.vitorpamplona.quartz.nip57Zaps.LnZapRequestEvent import com.vitorpamplona.quartz.nip57Zaps.ZapRequestEvent
import com.vitorpamplona.quartz.nip61Nutzaps.nutzap.NutzapEvent import com.vitorpamplona.quartz.nip61Nutzaps.nutzap.NutzapEvent
import com.vitorpamplona.quartz.nipBCOnchainZaps.zap.OnchainZapEvent import com.vitorpamplona.quartz.nipBCOnchainZaps.zap.OnchainZapEvent
import org.jetbrains.compose.resources.StringResource import org.jetbrains.compose.resources.StringResource
@@ -59,7 +59,7 @@ object ZapNotification {
suspend fun notify( suspend fun notify(
context: Context, context: Context,
account: Account, account: Account,
event: LnZapEvent, event: ZapReceiptEvent,
) { ) {
LocalCache.getNoteIfExists(event.id) ?: return LocalCache.getNoteIfExists(event.id) ?: return
val zapRequestNote = event.zapRequest?.id?.let { LocalCache.checkGetOrCreateNote(it) } ?: return val zapRequestNote = event.zapRequest?.id?.let { LocalCache.checkGetOrCreateNote(it) } ?: return
@@ -67,7 +67,7 @@ object ZapNotification {
if (!account.isAcceptable(zappedNote)) return if (!account.isAcceptable(zappedNote)) return
if ((event.amount ?: BigDecimal.ZERO) < MIN_ZAP_AMOUNT) return if ((event.amount ?: BigDecimal.ZERO) < MIN_ZAP_AMOUNT) return
val zapRequestEvent = zapRequestNote.event as? LnZapRequestEvent ?: return val zapRequestEvent = zapRequestNote.event as? ZapRequestEvent ?: return
// Resolve the (possibly private) zapper once — never re-decrypt per tick. // Resolve the (possibly private) zapper once — never re-decrypt per tick.
val decrypted = NotificationContent.decryptZapContentAuthor(zapRequestEvent, account.signer) ?: return val decrypted = NotificationContent.decryptZapContentAuthor(zapRequestEvent, account.signer) ?: return
val sender = LocalCache.getOrCreateUser(decrypted.pubKey) val sender = LocalCache.getOrCreateUser(decrypted.pubKey)
@@ -32,18 +32,18 @@ import com.vitorpamplona.quartz.nip01Core.metadata.MetadataEvent
import com.vitorpamplona.quartz.nip01Core.relay.client.pool.RelayBasedFilter import com.vitorpamplona.quartz.nip01Core.relay.client.pool.RelayBasedFilter
import com.vitorpamplona.quartz.nip01Core.relay.normalizer.NormalizedRelayUrl import com.vitorpamplona.quartz.nip01Core.relay.normalizer.NormalizedRelayUrl
import com.vitorpamplona.quartz.nip02FollowList.ContactListEvent import com.vitorpamplona.quartz.nip02FollowList.ContactListEvent
import com.vitorpamplona.quartz.nip17Dm.settings.ChatMessageRelayListEvent import com.vitorpamplona.quartz.nip17Dm.settings.DmRelayListEvent
import com.vitorpamplona.quartz.nip37Drafts.privateOutbox.PrivateOutboxRelayListEvent import com.vitorpamplona.quartz.nip37Drafts.privateOutbox.PrivateOutboxRelayListEvent
import com.vitorpamplona.quartz.nip38UserStatus.StatusEvent import com.vitorpamplona.quartz.nip38UserStatus.UserStatusEvent
import com.vitorpamplona.quartz.nip50Search.SearchRelayListEvent import com.vitorpamplona.quartz.nip50Search.SearchRelayListEvent
import com.vitorpamplona.quartz.nip51Lists.geohashList.GeohashListEvent import com.vitorpamplona.quartz.nip51Lists.geohashList.GeohashListEvent
import com.vitorpamplona.quartz.nip51Lists.hashtagList.HashtagListEvent import com.vitorpamplona.quartz.nip51Lists.hashtagList.InterestListEvent
import com.vitorpamplona.quartz.nip51Lists.interestSet.InterestSetEvent import com.vitorpamplona.quartz.nip51Lists.interestSet.InterestSetEvent
import com.vitorpamplona.quartz.nip51Lists.relayLists.BlockedRelayListEvent import com.vitorpamplona.quartz.nip51Lists.relayLists.BlockedRelayListEvent
import com.vitorpamplona.quartz.nip51Lists.relayLists.BroadcastRelayListEvent import com.vitorpamplona.quartz.nip51Lists.relayLists.BroadcastRelayListEvent
import com.vitorpamplona.quartz.nip51Lists.relayLists.FavoriteRelayListEvent
import com.vitorpamplona.quartz.nip51Lists.relayLists.IndexerRelayListEvent import com.vitorpamplona.quartz.nip51Lists.relayLists.IndexerRelayListEvent
import com.vitorpamplona.quartz.nip51Lists.relayLists.ProxyRelayListEvent import com.vitorpamplona.quartz.nip51Lists.relayLists.ProxyRelayListEvent
import com.vitorpamplona.quartz.nip51Lists.relayLists.RelayFeedsListEvent
import com.vitorpamplona.quartz.nip51Lists.relayLists.TrustedRelayListEvent import com.vitorpamplona.quartz.nip51Lists.relayLists.TrustedRelayListEvent
import com.vitorpamplona.quartz.nip51Lists.simpleGroupList.SimpleGroupListEvent import com.vitorpamplona.quartz.nip51Lists.simpleGroupList.SimpleGroupListEvent
import com.vitorpamplona.quartz.nip53LiveActivities.nestsServers.NestsServersEvent import com.vitorpamplona.quartz.nip53LiveActivities.nestsServers.NestsServersEvent
@@ -60,9 +60,9 @@ val AccountInfoAndListsFromKeyKinds =
listOf( listOf(
MetadataEvent.KIND, MetadataEvent.KIND,
ContactListEvent.KIND, ContactListEvent.KIND,
StatusEvent.KIND, UserStatusEvent.KIND,
AdvertisedRelayListEvent.KIND, AdvertisedRelayListEvent.KIND,
ChatMessageRelayListEvent.KIND, DmRelayListEvent.KIND,
KeyPackageRelayListEvent.KIND, KeyPackageRelayListEvent.KIND,
SearchRelayListEvent.KIND, SearchRelayListEvent.KIND,
FileServersEvent.KIND, FileServersEvent.KIND,
@@ -78,12 +78,12 @@ val AccountInfoAndListsFromKeyKinds2 =
BroadcastRelayListEvent.KIND, BroadcastRelayListEvent.KIND,
IndexerRelayListEvent.KIND, IndexerRelayListEvent.KIND,
ProxyRelayListEvent.KIND, ProxyRelayListEvent.KIND,
HashtagListEvent.KIND, InterestListEvent.KIND,
GeohashListEvent.KIND, GeohashListEvent.KIND,
TrustProviderListEvent.KIND, TrustProviderListEvent.KIND,
PaymentTargetsEvent.KIND, PaymentTargetsEvent.KIND,
Bolt12OfferListEvent.KIND, Bolt12OfferListEvent.KIND,
RelayFeedsListEvent.KIND, FavoriteRelayListEvent.KIND,
InterestSetEvent.KIND, InterestSetEvent.KIND,
// NIP-51 "simple groups" list (kind 10009): the user's joined NIP-29 groups + servers. // NIP-51 "simple groups" list (kind 10009): the user's joined NIP-29 groups + servers.
// Loaded up-front so "My Groups" and group memberships resolve immediately at login, // Loaded up-front so "My Groups" and group memberships resolve immediately at login,
@@ -28,7 +28,7 @@ import com.vitorpamplona.amethyst.commons.relayClient.nip47WalletConnect.NWCPaym
import com.vitorpamplona.amethyst.commons.relayClient.nip47WalletConnect.NWCPaymentQueryState import com.vitorpamplona.amethyst.commons.relayClient.nip47WalletConnect.NWCPaymentQueryState
import com.vitorpamplona.amethyst.commons.relayClient.subscriptions.KeyDataSourceSubscription import com.vitorpamplona.amethyst.commons.relayClient.subscriptions.KeyDataSourceSubscription
import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountViewModel import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountViewModel
import com.vitorpamplona.quartz.nip47WalletConnect.events.LnZapPaymentRequestEvent import com.vitorpamplona.quartz.nip47WalletConnect.events.NwcRequestEvent
@SuppressLint("StateFlowValueCalledInComposition") @SuppressLint("StateFlowValueCalledInComposition")
@Composable @Composable
@@ -50,7 +50,7 @@ fun NWCFinderFilterAssemblerSubscription(
val states = val states =
remember(note) { remember(note) {
val zapPaymentRequestNote = note val zapPaymentRequestNote = note
(zapPaymentRequestNote.event as? LnZapPaymentRequestEvent)?.let { noteEvent -> (zapPaymentRequestNote.event as? NwcRequestEvent)?.let { noteEvent ->
noteEvent.walletServicePubKey()?.let { noteEvent.walletServicePubKey()?.let {
zapPaymentRequestNote.relays.map { zapPaymentRequestNote.relays.map {
NWCPaymentQueryState( NWCPaymentQueryState(
@@ -39,7 +39,7 @@ import com.vitorpamplona.quartz.nip02FollowList.ContactListEvent
import com.vitorpamplona.quartz.nip51Lists.PinListEvent import com.vitorpamplona.quartz.nip51Lists.PinListEvent
import com.vitorpamplona.quartz.nip51Lists.bookmarkList.BookmarkListEvent import com.vitorpamplona.quartz.nip51Lists.bookmarkList.BookmarkListEvent
import com.vitorpamplona.quartz.nip51Lists.bookmarkList.OldBookmarkListEvent import com.vitorpamplona.quartz.nip51Lists.bookmarkList.OldBookmarkListEvent
import com.vitorpamplona.quartz.nip51Lists.hashtagList.HashtagListEvent import com.vitorpamplona.quartz.nip51Lists.hashtagList.InterestListEvent
import kotlinx.collections.immutable.ImmutableList import kotlinx.collections.immutable.ImmutableList
import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.ExperimentalCoroutinesApi
@@ -132,9 +132,9 @@ fun observeUserTagFollowCount(
.metadata.stateFlow .metadata.stateFlow
.sample(1000) .sample(1000)
.mapLatest { noteState -> .mapLatest { noteState ->
(noteState.note.event as? HashtagListEvent)?.let { accountViewModel.account.hashtagListDecryptionCache.hashtags(it) }?.size ?: 0 (noteState.note.event as? InterestListEvent)?.let { accountViewModel.account.hashtagListDecryptionCache.hashtags(it) }?.size ?: 0
}.onStart { }.onStart {
emit((accountViewModel.hashtagFollows(user).event as? HashtagListEvent)?.let { accountViewModel.account.hashtagListDecryptionCache.hashtags(it) }?.size ?: 0) emit((accountViewModel.hashtagFollows(user).event as? InterestListEvent)?.let { accountViewModel.account.hashtagListDecryptionCache.hashtags(it) }?.size ?: 0)
}.distinctUntilChanged() }.distinctUntilChanged()
.flowOn(Dispatchers.IO) .flowOn(Dispatchers.IO)
} }
@@ -160,9 +160,9 @@ fun observeUserTagFollows(
.metadata.stateFlow .metadata.stateFlow
.sample(200) .sample(200)
.mapLatest { noteState -> .mapLatest { noteState ->
(noteState.note.event as? HashtagListEvent)?.let { accountViewModel.account.hashtagListDecryptionCache.hashtags(it) }?.sorted() ?: emptyList() (noteState.note.event as? InterestListEvent)?.let { accountViewModel.account.hashtagListDecryptionCache.hashtags(it) }?.sorted() ?: emptyList()
}.onStart { }.onStart {
emit((accountViewModel.hashtagFollows(user).event as? HashtagListEvent)?.let { accountViewModel.account.hashtagListDecryptionCache.hashtags(it) }?.sorted() ?: emptyList()) emit((accountViewModel.hashtagFollows(user).event as? InterestListEvent)?.let { accountViewModel.account.hashtagListDecryptionCache.hashtags(it) }?.sorted() ?: emptyList())
}.distinctUntilChanged() }.distinctUntilChanged()
.flowOn(Dispatchers.IO) .flowOn(Dispatchers.IO)
} }
@@ -27,7 +27,7 @@ import com.vitorpamplona.quartz.nip01Core.signers.NostrSignerInternal
import com.vitorpamplona.quartz.nip46RemoteSigner.signer.NostrSignerRemote import com.vitorpamplona.quartz.nip46RemoteSigner.signer.NostrSignerRemote
import com.vitorpamplona.quartz.nip55AndroidSigner.client.NostrSignerExternal import com.vitorpamplona.quartz.nip55AndroidSigner.client.NostrSignerExternal
import com.vitorpamplona.quartz.nip57Zaps.LnZapPrivateEvent import com.vitorpamplona.quartz.nip57Zaps.LnZapPrivateEvent
import com.vitorpamplona.quartz.nip57Zaps.LnZapRequestEvent import com.vitorpamplona.quartz.nip57Zaps.ZapRequestEvent
import com.vitorpamplona.quartz.nip89AppHandlers.clientTag.NostrSignerWithClientTag import com.vitorpamplona.quartz.nip89AppHandlers.clientTag.NostrSignerWithClientTag
/** /**
@@ -89,7 +89,7 @@ class MeteringNostrSigner(
fromPublicKey: HexKey, fromPublicKey: HexKey,
): String = metered(UsageKeys.DECRYPT_COUNT, UsageKeys.DECRYPT_US) { inner.nip44Decrypt(ciphertext, fromPublicKey) } ): String = metered(UsageKeys.DECRYPT_COUNT, UsageKeys.DECRYPT_US) { inner.nip44Decrypt(ciphertext, fromPublicKey) }
override suspend fun decryptZapEvent(event: LnZapRequestEvent): LnZapPrivateEvent = metered(UsageKeys.DECRYPT_COUNT, UsageKeys.DECRYPT_US) { inner.decryptZapEvent(event) } override suspend fun decryptZapEvent(event: ZapRequestEvent): LnZapPrivateEvent = metered(UsageKeys.DECRYPT_COUNT, UsageKeys.DECRYPT_US) { inner.decryptZapEvent(event) }
override suspend fun deriveKey(nonce: HexKey): HexKey = inner.deriveKey(nonce) override suspend fun deriveKey(nonce: HexKey): HexKey = inner.deriveKey(nonce)
@@ -26,9 +26,9 @@ import com.davotoula.lightcompressor.hls.HlsUploaded
import com.vitorpamplona.amethyst.service.uploads.MediaUploadResult import com.vitorpamplona.amethyst.service.uploads.MediaUploadResult
import com.vitorpamplona.quartz.nip01Core.signers.EventTemplate import com.vitorpamplona.quartz.nip01Core.signers.EventTemplate
import com.vitorpamplona.quartz.nip36SensitiveContent.contentWarning import com.vitorpamplona.quartz.nip36SensitiveContent.contentWarning
import com.vitorpamplona.quartz.nip71Video.VideoHorizontalEvent import com.vitorpamplona.quartz.nip71Video.AddressableNormalVideoEvent
import com.vitorpamplona.quartz.nip71Video.AddressableShortVideoEvent
import com.vitorpamplona.quartz.nip71Video.VideoMeta import com.vitorpamplona.quartz.nip71Video.VideoMeta
import com.vitorpamplona.quartz.nip71Video.VideoVerticalEvent
import com.vitorpamplona.quartz.nip71Video.duration import com.vitorpamplona.quartz.nip71Video.duration
import com.vitorpamplona.quartz.nip71Video.title import com.vitorpamplona.quartz.nip71Video.title
import com.vitorpamplona.quartz.nip71Video.videoIMetas import com.vitorpamplona.quartz.nip71Video.videoIMetas
@@ -61,11 +61,11 @@ data class HlsVideoPublishInput(
sealed class HlsVideoEventTemplate { sealed class HlsVideoEventTemplate {
data class Horizontal( data class Horizontal(
val template: EventTemplate<VideoHorizontalEvent>, val template: EventTemplate<AddressableNormalVideoEvent>,
) : HlsVideoEventTemplate() ) : HlsVideoEventTemplate()
data class Vertical( data class Vertical(
val template: EventTemplate<VideoVerticalEvent>, val template: EventTemplate<AddressableShortVideoEvent>,
) : HlsVideoEventTemplate() ) : HlsVideoEventTemplate()
} }
@@ -80,7 +80,7 @@ data class HlsBuiltTemplate(
) )
/** /**
* Assembles a NIP-71 VideoHorizontalEvent / VideoVerticalEvent template from an HLS upload * Assembles a NIP-71 AddressableNormalVideoEvent / AddressableShortVideoEvent template from an HLS upload
* result. Orientation is decided from the first rendition's width/height: portrait * result. Orientation is decided from the first rendition's width/height: portrait
* (height > width) selects kind 34236, otherwise 34235. * (height > width) selects kind 34236, otherwise 34235.
* *
@@ -142,7 +142,7 @@ object HlsVideoEventBuilder {
val template = val template =
if (isVertical) { if (isVertical) {
HlsVideoEventTemplate.Vertical( HlsVideoEventTemplate.Vertical(
VideoVerticalEvent.build(input.description, dTag, createdAt) { AddressableShortVideoEvent.build(input.description, dTag, createdAt) {
videoIMetas(videoMetas) videoIMetas(videoMetas)
title(input.title) title(input.title)
input.durationSeconds?.let { duration(it) } input.durationSeconds?.let { duration(it) }
@@ -151,7 +151,7 @@ object HlsVideoEventBuilder {
) )
} else { } else {
HlsVideoEventTemplate.Horizontal( HlsVideoEventTemplate.Horizontal(
VideoHorizontalEvent.build(input.description, dTag, createdAt) { AddressableNormalVideoEvent.build(input.description, dTag, createdAt) {
videoIMetas(videoMetas) videoIMetas(videoMetas)
title(input.title) title(input.title)
input.durationSeconds?.let { duration(it) } input.durationSeconds?.let { duration(it) }
@@ -54,7 +54,7 @@ import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountViewModel
import com.vitorpamplona.amethyst.ui.screen.loggedIn.mockAccountViewModel import com.vitorpamplona.amethyst.ui.screen.loggedIn.mockAccountViewModel
import com.vitorpamplona.amethyst.ui.uriToRoute import com.vitorpamplona.amethyst.ui.uriToRoute
import com.vitorpamplona.quartz.nip01Core.metadata.MetadataEvent import com.vitorpamplona.quartz.nip01Core.metadata.MetadataEvent
import com.vitorpamplona.quartz.nip23LongContent.LongTextNoteEvent import com.vitorpamplona.quartz.nip23LongContent.LongFormContentEvent
import com.vitorpamplona.quartz.nip92IMeta.imetasByUrl import com.vitorpamplona.quartz.nip92IMeta.imetasByUrl
import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.runBlocking import kotlinx.coroutines.runBlocking
@@ -425,7 +425,7 @@ fun RenderContentAsMarkdownNotePreview() {
runBlocking { runBlocking {
withContext(Dispatchers.IO) { withContext(Dispatchers.IO) {
val blogPost = val blogPost =
LongTextNoteEvent( LongFormContentEvent(
id = "e69fa72a221d0b7362f8e63fd6ab84cc6b5e7b505fe49cefe6823a47a6a4b583", id = "e69fa72a221d0b7362f8e63fd6ab84cc6b5e7b505fe49cefe6823a47a6a4b583",
pubKey = "88cc134b1a65f54ef48acc1df3665063d3ea45f04eab8af4646e561c5ae99079", pubKey = "88cc134b1a65f54ef48acc1df3665063d3ea45f04eab8af4646e561c5ae99079",
createdAt = 1718785725, createdAt = 1718785725,
@@ -33,7 +33,7 @@ import com.vitorpamplona.quartz.nip01Core.relay.normalizer.NormalizedRelayUrl
import com.vitorpamplona.quartz.nip01Core.tags.hashtags.hasMoreHashtagsThan import com.vitorpamplona.quartz.nip01Core.tags.hashtags.hasMoreHashtagsThan
import com.vitorpamplona.quartz.nip10Notes.threadRootIdOrSelf import com.vitorpamplona.quartz.nip10Notes.threadRootIdOrSelf
import com.vitorpamplona.quartz.nip51Lists.muteList.MuteListEvent import com.vitorpamplona.quartz.nip51Lists.muteList.MuteListEvent
import com.vitorpamplona.quartz.nip51Lists.peopleList.PeopleListEvent import com.vitorpamplona.quartz.nip51Lists.peopleList.FollowSetEvent
import com.vitorpamplona.quartz.utils.TimeUtils import com.vitorpamplona.quartz.utils.TimeUtils
class FilterByListParams( class FilterByListParams(
@@ -105,7 +105,7 @@ class FilterByListParams(
fun showHiddenKey( fun showHiddenKey(
selectedListName: String, selectedListName: String,
userHex: String, userHex: String,
) = selectedListName == PeopleListEvent.blockListFor(userHex) || selectedListName == MuteListEvent.blockListFor(userHex) ) = selectedListName == FollowSetEvent.blockListFor(userHex) || selectedListName == MuteListEvent.blockListFor(userHex)
fun create( fun create(
followLists: IFeedTopNavFilter?, followLists: IFeedTopNavFilter?,
@@ -298,7 +298,7 @@ private class WarmTargets {
/** /**
* Adds NIP-94 file-header media: blobs declared as top-level `url` tags (not * Adds NIP-94 file-header media: blobs declared as top-level `url` tags (not
* imeta), as used by FileHeaderEvent and the gallery/file kinds. Only image * imeta), as used by FileMetadataEvent and the gallery/file kinds. Only image
* files are prefetched — detected by the URL extension or the event's top-level * files are prefetched — detected by the URL extension or the event's top-level
* `m` mime type (a hashed URL may carry no extension). The decode listener * `m` mime type (a hashed URL may carry no extension). The decode listener
* records their dims like any other image. * records their dims like any other image.
@@ -333,7 +333,7 @@ private fun Note.collectWarmTargets(): WarmTargets? {
} }
// NIP-94 file-header convention: the blob is a top-level `url` tag with sibling // NIP-94 file-header convention: the blob is a top-level `url` tag with sibling
// `m`/`dim`, not imeta (FileHeaderEvent and the gallery/file kinds). The generic // `m`/`dim`, not imeta (FileMetadataEvent and the gallery/file kinds). The generic
// imetas() above misses these entirely. // imetas() above misses these entirely.
targets.addFileHeaderMedia(ev) targets.addFileHeaderMedia(ev)
@@ -44,7 +44,7 @@ import com.vitorpamplona.quartz.nip10Notes.TextNoteEvent
import com.vitorpamplona.quartz.nip17Dm.base.ChatroomKey import com.vitorpamplona.quartz.nip17Dm.base.ChatroomKey
import com.vitorpamplona.quartz.nip17Dm.base.ChatroomKeyable import com.vitorpamplona.quartz.nip17Dm.base.ChatroomKeyable
import com.vitorpamplona.quartz.nip22Comments.CommentEvent import com.vitorpamplona.quartz.nip22Comments.CommentEvent
import com.vitorpamplona.quartz.nip23LongContent.LongTextNoteEvent import com.vitorpamplona.quartz.nip23LongContent.LongFormContentEvent
import com.vitorpamplona.quartz.nip28PublicChat.admin.ChannelCreateEvent import com.vitorpamplona.quartz.nip28PublicChat.admin.ChannelCreateEvent
import com.vitorpamplona.quartz.nip28PublicChat.base.IsInPublicChatChannel import com.vitorpamplona.quartz.nip28PublicChat.base.IsInPublicChatChannel
import com.vitorpamplona.quartz.nip28PublicChat.message.ChannelMessageEvent import com.vitorpamplona.quartz.nip28PublicChat.message.ChannelMessageEvent
@@ -57,12 +57,12 @@ import com.vitorpamplona.quartz.nip34Git.pr.GitPullRequestEvent
import com.vitorpamplona.quartz.nip34Git.pr.GitPullRequestUpdateEvent import com.vitorpamplona.quartz.nip34Git.pr.GitPullRequestUpdateEvent
import com.vitorpamplona.quartz.nip34Git.repository.GitRepositoryEvent import com.vitorpamplona.quartz.nip34Git.repository.GitRepositoryEvent
import com.vitorpamplona.quartz.nip37Drafts.DraftWrapEvent import com.vitorpamplona.quartz.nip37Drafts.DraftWrapEvent
import com.vitorpamplona.quartz.nip51Lists.followList.FollowListEvent import com.vitorpamplona.quartz.nip51Lists.followList.StarterPackEvent
import com.vitorpamplona.quartz.nip53LiveActivities.chat.LiveActivitiesChatMessageEvent import com.vitorpamplona.quartz.nip53LiveActivities.chat.LiveActivitiesChatMessageEvent
import com.vitorpamplona.quartz.nip53LiveActivities.streaming.LiveActivitiesEvent import com.vitorpamplona.quartz.nip53LiveActivities.streaming.LiveActivitiesEvent
import com.vitorpamplona.quartz.nip57Zaps.LnZapEvent import com.vitorpamplona.quartz.nip57Zaps.ZapReceiptEvent
import com.vitorpamplona.quartz.nip59Giftwrap.HasInnerEvent import com.vitorpamplona.quartz.nip59Giftwrap.HasInnerEvent
import com.vitorpamplona.quartz.nip59Giftwrap.seals.SealedRumorEvent import com.vitorpamplona.quartz.nip59Giftwrap.seals.SealEvent
import com.vitorpamplona.quartz.nip59Giftwrap.wraps.GiftWrapEvent import com.vitorpamplona.quartz.nip59Giftwrap.wraps.GiftWrapEvent
import com.vitorpamplona.quartz.nip68Picture.PictureEvent import com.vitorpamplona.quartz.nip68Picture.PictureEvent
import com.vitorpamplona.quartz.nip71Video.VideoNormalEvent import com.vitorpamplona.quartz.nip71Video.VideoNormalEvent
@@ -247,7 +247,7 @@ fun routeForInner(
} }
} }
is FollowListEvent -> { is StarterPackEvent -> {
Route.FollowPack(noteEvent.address()) Route.FollowPack(noteEvent.address())
} }
@@ -294,7 +294,7 @@ fun routeForInner(
Route.GeocacheHunt(noteEvent.kind, noteEvent.pubKey, noteEvent.dTag()) Route.GeocacheHunt(noteEvent.kind, noteEvent.pubKey, noteEvent.dTag())
} }
is GiftWrapEvent, is SealedRumorEvent -> { is GiftWrapEvent, is SealEvent -> {
val wrap = noteEvent as HasInnerEvent val wrap = noteEvent as HasInnerEvent
wrap.innerEventId?.let { wrap.innerEventId?.let {
routeFor(LocalCache.getOrCreateNote(it), loggedIn) routeFor(LocalCache.getOrCreateNote(it), loggedIn)
@@ -456,7 +456,7 @@ fun routeReplyTo(
} }
is ChatroomKeyable -> { is ChatroomKeyable -> {
// Covers PrivateDmEvent (NIP-04) and BaseDMGroupEvent (NIP-17) — both // Covers EncryptedDmEvent (NIP-04) and BaseDMGroupEvent (NIP-17) — both
// implement ChatroomKeyable with identical routing semantics here. // implement ChatroomKeyable with identical routing semantics here.
routeToMessage( routeToMessage(
room = noteEvent.chatroomKey(account.userProfile().pubkeyHex), room = noteEvent.chatroomKey(account.userProfile().pubkeyHex),
@@ -477,7 +477,7 @@ fun routeReplyTo(
} }
} }
is LnZapEvent -> { is ZapReceiptEvent -> {
// A public reply can't tag a private zapper without exposing them. // A public reply can't tag a private zapper without exposing them.
// When we hold the decrypted sender (we are the zap recipient), reply // When we hold the decrypted sender (we are the zap recipient), reply
// in their DM room instead of the public comment composer. // in their DM room instead of the public comment composer.
@@ -532,7 +532,7 @@ suspend fun routeEditDraftTo(
Route.NewShortNote(draft = note.idHex) Route.NewShortNote(draft = note.idHex)
} }
is LongTextNoteEvent -> { is LongFormContentEvent -> {
Route.NewLongFormPost(draft = note.idHex) Route.NewLongFormPost(draft = note.idHex)
} }
@@ -84,7 +84,7 @@ import com.vitorpamplona.quartz.nip01Core.tags.events.ETag
import com.vitorpamplona.quartz.nip01Core.tags.events.firstTaggedEvent import com.vitorpamplona.quartz.nip01Core.tags.events.firstTaggedEvent
import com.vitorpamplona.quartz.nip01Core.tags.people.firstTaggedUserId import com.vitorpamplona.quartz.nip01Core.tags.people.firstTaggedUserId
import com.vitorpamplona.quartz.nip30CustomEmoji.taggedEmojis import com.vitorpamplona.quartz.nip30CustomEmoji.taggedEmojis
import com.vitorpamplona.quartz.nip38UserStatus.StatusEvent import com.vitorpamplona.quartz.nip38UserStatus.UserStatusEvent
import kotlinx.collections.immutable.ImmutableList import kotlinx.collections.immutable.ImmutableList
import kotlinx.collections.immutable.ImmutableMap import kotlinx.collections.immutable.ImmutableMap
import kotlinx.collections.immutable.persistentMapOf import kotlinx.collections.immutable.persistentMapOf
@@ -219,14 +219,14 @@ fun DisplayStatus(
nav: INav, nav: INav,
) { ) {
val noteState by observeNote(addressableNote, accountViewModel) val noteState by observeNote(addressableNote, accountViewModel)
val noteEvent = noteState.note.event as? StatusEvent ?: return val noteEvent = noteState.note.event as? UserStatusEvent ?: return
DisplayStatus(noteEvent, accountViewModel, nav) DisplayStatus(noteEvent, accountViewModel, nav)
} }
@Composable @Composable
fun DisplayStatus( fun DisplayStatus(
event: StatusEvent, event: UserStatusEvent,
accountViewModel: AccountViewModel, accountViewModel: AccountViewModel,
nav: INav, nav: INav,
) { ) {
@@ -266,7 +266,7 @@ fun DisplayStatusInner(
nav: INav, nav: INav,
) { ) {
when (type) { when (type) {
StatusEvent.MUSIC -> { UserStatusEvent.MUSIC -> {
Icon( Icon(
imageVector = CustomHashTagIcons.Tunestr, imageVector = CustomHashTagIcons.Tunestr,
null, null,
@@ -331,16 +331,16 @@ import com.vitorpamplona.quartz.nip01Core.core.HexKey
import com.vitorpamplona.quartz.nip01Core.metadata.MetadataEvent import com.vitorpamplona.quartz.nip01Core.metadata.MetadataEvent
import com.vitorpamplona.quartz.nip01Core.tags.geohash.geoHashOrScope import com.vitorpamplona.quartz.nip01Core.tags.geohash.geoHashOrScope
import com.vitorpamplona.quartz.nip02FollowList.ContactListEvent import com.vitorpamplona.quartz.nip02FollowList.ContactListEvent
import com.vitorpamplona.quartz.nip04Dm.messages.PrivateDmEvent import com.vitorpamplona.quartz.nip04Dm.messages.EncryptedDmEvent
import com.vitorpamplona.quartz.nip10Notes.BaseThreadedEvent import com.vitorpamplona.quartz.nip10Notes.BaseThreadedEvent
import com.vitorpamplona.quartz.nip10Notes.TextNoteEvent import com.vitorpamplona.quartz.nip10Notes.TextNoteEvent
import com.vitorpamplona.quartz.nip13Pow.strongPoWOrNull import com.vitorpamplona.quartz.nip13Pow.strongPoWOrNull
import com.vitorpamplona.quartz.nip17Dm.files.ChatMessageEncryptedFileHeaderEvent import com.vitorpamplona.quartz.nip17Dm.files.ChatMessageEncryptedFileHeaderEvent
import com.vitorpamplona.quartz.nip17Dm.messages.ChatMessageEvent import com.vitorpamplona.quartz.nip17Dm.messages.ChatMessageEvent
import com.vitorpamplona.quartz.nip17Dm.settings.ChatMessageRelayListEvent import com.vitorpamplona.quartz.nip17Dm.settings.DmRelayListEvent
import com.vitorpamplona.quartz.nip18Reposts.GenericRepostEvent import com.vitorpamplona.quartz.nip18Reposts.GenericRepostEvent
import com.vitorpamplona.quartz.nip18Reposts.RepostEvent import com.vitorpamplona.quartz.nip18Reposts.RepostEvent
import com.vitorpamplona.quartz.nip23LongContent.LongTextNoteEvent import com.vitorpamplona.quartz.nip23LongContent.LongFormContentEvent
import com.vitorpamplona.quartz.nip25Reactions.ExternalReactionEvent import com.vitorpamplona.quartz.nip25Reactions.ExternalReactionEvent
import com.vitorpamplona.quartz.nip25Reactions.ReactionEvent import com.vitorpamplona.quartz.nip25Reactions.ReactionEvent
import com.vitorpamplona.quartz.nip28PublicChat.admin.ChannelCreateEvent import com.vitorpamplona.quartz.nip28PublicChat.admin.ChannelCreateEvent
@@ -368,26 +368,26 @@ import com.vitorpamplona.quartz.nip51Lists.articleCurationSet.ArticleCurationSet
import com.vitorpamplona.quartz.nip51Lists.bookmarkList.BookmarkListEvent import com.vitorpamplona.quartz.nip51Lists.bookmarkList.BookmarkListEvent
import com.vitorpamplona.quartz.nip51Lists.bookmarkList.OldBookmarkListEvent import com.vitorpamplona.quartz.nip51Lists.bookmarkList.OldBookmarkListEvent
import com.vitorpamplona.quartz.nip51Lists.favoriteAlgoFeedsList.FavoriteAlgoFeedsListEvent import com.vitorpamplona.quartz.nip51Lists.favoriteAlgoFeedsList.FavoriteAlgoFeedsListEvent
import com.vitorpamplona.quartz.nip51Lists.followList.FollowListEvent import com.vitorpamplona.quartz.nip51Lists.followList.StarterPackEvent
import com.vitorpamplona.quartz.nip51Lists.geohashList.GeohashListEvent import com.vitorpamplona.quartz.nip51Lists.geohashList.GeohashListEvent
import com.vitorpamplona.quartz.nip51Lists.gitAuthorList.GitAuthorListEvent import com.vitorpamplona.quartz.nip51Lists.gitAuthorList.GitAuthorListEvent
import com.vitorpamplona.quartz.nip51Lists.gitRepositoryList.GitRepositoryListEvent import com.vitorpamplona.quartz.nip51Lists.gitRepositoryList.GitRepositoryListEvent
import com.vitorpamplona.quartz.nip51Lists.goodWikiAuthorList.GoodWikiAuthorListEvent import com.vitorpamplona.quartz.nip51Lists.goodWikiAuthorList.GoodWikiAuthorListEvent
import com.vitorpamplona.quartz.nip51Lists.goodWikiRelayList.GoodWikiRelayListEvent import com.vitorpamplona.quartz.nip51Lists.goodWikiRelayList.GoodWikiRelayListEvent
import com.vitorpamplona.quartz.nip51Lists.hashtagList.HashtagListEvent import com.vitorpamplona.quartz.nip51Lists.hashtagList.InterestListEvent
import com.vitorpamplona.quartz.nip51Lists.interestSet.InterestSetEvent import com.vitorpamplona.quartz.nip51Lists.interestSet.InterestSetEvent
import com.vitorpamplona.quartz.nip51Lists.kindMuteSet.KindMuteSetEvent import com.vitorpamplona.quartz.nip51Lists.kindMuteSet.KindMuteSetEvent
import com.vitorpamplona.quartz.nip51Lists.labeledBookmarkList.LabeledBookmarkListEvent import com.vitorpamplona.quartz.nip51Lists.labeledBookmarkList.BookmarkSetEvent
import com.vitorpamplona.quartz.nip51Lists.mediaFollowList.MediaFollowListEvent import com.vitorpamplona.quartz.nip51Lists.mediaFollowList.MediaFollowListEvent
import com.vitorpamplona.quartz.nip51Lists.mediaStarterPack.MediaStarterPackEvent import com.vitorpamplona.quartz.nip51Lists.mediaStarterPack.MediaStarterPackEvent
import com.vitorpamplona.quartz.nip51Lists.muteList.MuteListEvent import com.vitorpamplona.quartz.nip51Lists.muteList.MuteListEvent
import com.vitorpamplona.quartz.nip51Lists.peopleList.PeopleListEvent import com.vitorpamplona.quartz.nip51Lists.peopleList.FollowSetEvent
import com.vitorpamplona.quartz.nip51Lists.pictureCurationSet.PictureCurationSetEvent import com.vitorpamplona.quartz.nip51Lists.pictureCurationSet.PictureCurationSetEvent
import com.vitorpamplona.quartz.nip51Lists.relayLists.BlockedRelayListEvent import com.vitorpamplona.quartz.nip51Lists.relayLists.BlockedRelayListEvent
import com.vitorpamplona.quartz.nip51Lists.relayLists.BroadcastRelayListEvent import com.vitorpamplona.quartz.nip51Lists.relayLists.BroadcastRelayListEvent
import com.vitorpamplona.quartz.nip51Lists.relayLists.FavoriteRelayListEvent
import com.vitorpamplona.quartz.nip51Lists.relayLists.IndexerRelayListEvent import com.vitorpamplona.quartz.nip51Lists.relayLists.IndexerRelayListEvent
import com.vitorpamplona.quartz.nip51Lists.relayLists.ProxyRelayListEvent import com.vitorpamplona.quartz.nip51Lists.relayLists.ProxyRelayListEvent
import com.vitorpamplona.quartz.nip51Lists.relayLists.RelayFeedsListEvent
import com.vitorpamplona.quartz.nip51Lists.relayLists.TrustedRelayListEvent import com.vitorpamplona.quartz.nip51Lists.relayLists.TrustedRelayListEvent
import com.vitorpamplona.quartz.nip51Lists.relaySets.RelaySetEvent import com.vitorpamplona.quartz.nip51Lists.relaySets.RelaySetEvent
import com.vitorpamplona.quartz.nip51Lists.releaseArtifactSet.ReleaseArtifactSetEvent import com.vitorpamplona.quartz.nip51Lists.releaseArtifactSet.ReleaseArtifactSetEvent
@@ -395,19 +395,19 @@ import com.vitorpamplona.quartz.nip51Lists.simpleGroupList.SimpleGroupListEvent
import com.vitorpamplona.quartz.nip51Lists.videoCurationSet.VideoCurationSetEvent import com.vitorpamplona.quartz.nip51Lists.videoCurationSet.VideoCurationSetEvent
import com.vitorpamplona.quartz.nip52Calendar.appt.day.CalendarDateSlotEvent import com.vitorpamplona.quartz.nip52Calendar.appt.day.CalendarDateSlotEvent
import com.vitorpamplona.quartz.nip52Calendar.appt.time.CalendarTimeSlotEvent import com.vitorpamplona.quartz.nip52Calendar.appt.time.CalendarTimeSlotEvent
import com.vitorpamplona.quartz.nip52Calendar.calendar.CalendarEvent import com.vitorpamplona.quartz.nip52Calendar.calendar.CalendarCollectionEvent
import com.vitorpamplona.quartz.nip52Calendar.rsvp.CalendarRSVPEvent import com.vitorpamplona.quartz.nip52Calendar.rsvp.CalendarRSVPEvent
import com.vitorpamplona.quartz.nip53LiveActivities.chat.LiveActivitiesChatMessageEvent import com.vitorpamplona.quartz.nip53LiveActivities.chat.LiveActivitiesChatMessageEvent
import com.vitorpamplona.quartz.nip53LiveActivities.clip.LiveActivitiesClipEvent import com.vitorpamplona.quartz.nip53LiveActivities.clip.LiveActivitiesClipEvent
import com.vitorpamplona.quartz.nip53LiveActivities.meetingSpaces.MeetingRoomEvent import com.vitorpamplona.quartz.nip53LiveActivities.meetingSpaces.MeetingRoomEvent
import com.vitorpamplona.quartz.nip53LiveActivities.meetingSpaces.MeetingSpaceEvent import com.vitorpamplona.quartz.nip53LiveActivities.meetingSpaces.MeetingSpaceEvent
import com.vitorpamplona.quartz.nip53LiveActivities.streaming.LiveActivitiesEvent import com.vitorpamplona.quartz.nip53LiveActivities.streaming.LiveActivitiesEvent
import com.vitorpamplona.quartz.nip54Wiki.WikiArticleEvent
import com.vitorpamplona.quartz.nip54Wiki.WikiMergeAcceptanceEvent import com.vitorpamplona.quartz.nip54Wiki.WikiMergeAcceptanceEvent
import com.vitorpamplona.quartz.nip54Wiki.WikiMergeRequestEvent import com.vitorpamplona.quartz.nip54Wiki.WikiMergeRequestEvent
import com.vitorpamplona.quartz.nip54Wiki.WikiNoteEvent
import com.vitorpamplona.quartz.nip54Wiki.WikiRedirectEvent import com.vitorpamplona.quartz.nip54Wiki.WikiRedirectEvent
import com.vitorpamplona.quartz.nip56Reports.ReportEvent import com.vitorpamplona.quartz.nip56Reports.ReportEvent
import com.vitorpamplona.quartz.nip57Zaps.LnZapEvent import com.vitorpamplona.quartz.nip57Zaps.ZapReceiptEvent
import com.vitorpamplona.quartz.nip57Zaps.splits.hasZapSplitSetupBesidesAuthor import com.vitorpamplona.quartz.nip57Zaps.splits.hasZapSplitSetupBesidesAuthor
import com.vitorpamplona.quartz.nip58Badges.award.BadgeAwardEvent import com.vitorpamplona.quartz.nip58Badges.award.BadgeAwardEvent
import com.vitorpamplona.quartz.nip58Badges.definition.BadgeDefinitionEvent import com.vitorpamplona.quartz.nip58Badges.definition.BadgeDefinitionEvent
@@ -427,7 +427,7 @@ import com.vitorpamplona.quartz.nip72ModCommunities.approval.CommunityPostApprov
import com.vitorpamplona.quartz.nip72ModCommunities.communityAddress import com.vitorpamplona.quartz.nip72ModCommunities.communityAddress
import com.vitorpamplona.quartz.nip72ModCommunities.definition.CommunityDefinitionEvent import com.vitorpamplona.quartz.nip72ModCommunities.definition.CommunityDefinitionEvent
import com.vitorpamplona.quartz.nip72ModCommunities.isACommunityPost import com.vitorpamplona.quartz.nip72ModCommunities.isACommunityPost
import com.vitorpamplona.quartz.nip75ZapGoals.GoalEvent import com.vitorpamplona.quartz.nip75ZapGoals.ZapGoalEvent
import com.vitorpamplona.quartz.nip78AppData.AppSpecificDataEvent import com.vitorpamplona.quartz.nip78AppData.AppSpecificDataEvent
import com.vitorpamplona.quartz.nip7DThreads.ThreadEvent import com.vitorpamplona.quartz.nip7DThreads.ThreadEvent
import com.vitorpamplona.quartz.nip84Highlights.HighlightEvent import com.vitorpamplona.quartz.nip84Highlights.HighlightEvent
@@ -437,9 +437,9 @@ import com.vitorpamplona.quartz.nip87Ecash.recommendation.MintRecommendationEven
import com.vitorpamplona.quartz.nip88Polls.poll.PollEvent import com.vitorpamplona.quartz.nip88Polls.poll.PollEvent
import com.vitorpamplona.quartz.nip89AppHandlers.definition.AppDefinitionEvent import com.vitorpamplona.quartz.nip89AppHandlers.definition.AppDefinitionEvent
import com.vitorpamplona.quartz.nip89AppHandlers.recommendation.AppRecommendationEvent import com.vitorpamplona.quartz.nip89AppHandlers.recommendation.AppRecommendationEvent
import com.vitorpamplona.quartz.nip90Dvms.contentDiscoveryResponse.NIP90ContentDiscoveryResponseEvent import com.vitorpamplona.quartz.nip90Dvms.contentDiscoveryResponse.DvmContentDiscoveryResponseEvent
import com.vitorpamplona.quartz.nip90Dvms.status.NIP90StatusEvent import com.vitorpamplona.quartz.nip90Dvms.status.DvmStatusEvent
import com.vitorpamplona.quartz.nip94FileMetadata.FileHeaderEvent import com.vitorpamplona.quartz.nip94FileMetadata.FileMetadataEvent
import com.vitorpamplona.quartz.nip99Classifieds.ClassifiedsEvent import com.vitorpamplona.quartz.nip99Classifieds.ClassifiedsEvent
import com.vitorpamplona.quartz.nipA0VoiceMessages.BaseVoiceEvent import com.vitorpamplona.quartz.nipA0VoiceMessages.BaseVoiceEvent
import com.vitorpamplona.quartz.nipA4PublicMessages.PublicMessageEvent import com.vitorpamplona.quartz.nipA4PublicMessages.PublicMessageEvent
@@ -1145,7 +1145,7 @@ private fun RenderNoteRow(
RenderReport(baseNote, quotesLeft, backgroundColor, accountViewModel, nav) RenderReport(baseNote, quotesLeft, backgroundColor, accountViewModel, nav)
} }
is LongTextNoteEvent -> { is LongFormContentEvent -> {
RenderLongFormContent(baseNote, accountViewModel, nav) RenderLongFormContent(baseNote, accountViewModel, nav)
} }
@@ -1171,7 +1171,7 @@ private fun RenderNoteRow(
RenderCodeSnippetEvent(baseNote) RenderCodeSnippetEvent(baseNote)
} }
is WikiNoteEvent -> { is WikiArticleEvent -> {
RenderWikiContent(baseNote, accountViewModel, nav) RenderWikiContent(baseNote, accountViewModel, nav)
} }
@@ -1191,7 +1191,7 @@ private fun RenderNoteRow(
BadgeDisplay(baseNote = baseNote, accountViewModel = accountViewModel, nav = nav) BadgeDisplay(baseNote = baseNote, accountViewModel = accountViewModel, nav = nav)
} }
is LnZapEvent -> { is ZapReceiptEvent -> {
RenderLnZap(baseNote, quotesLeft, backgroundColor, accountViewModel, nav) RenderLnZap(baseNote, quotesLeft, backgroundColor, accountViewModel, nav)
} }
@@ -1215,11 +1215,11 @@ private fun RenderNoteRow(
RenderFhirResource(baseNote, accountViewModel, nav) RenderFhirResource(baseNote, accountViewModel, nav)
} }
is PeopleListEvent -> { is FollowSetEvent -> {
DisplayPeopleList(baseNote, backgroundColor, accountViewModel, nav) DisplayPeopleList(baseNote, backgroundColor, accountViewModel, nav)
} }
is FollowListEvent -> { is StarterPackEvent -> {
DisplayFollowList(baseNote, true, accountViewModel, nav) DisplayFollowList(baseNote, true, accountViewModel, nav)
} }
@@ -1231,7 +1231,7 @@ private fun RenderNoteRow(
DisplayRelaySet(baseNote, backgroundColor, accountViewModel, nav) DisplayRelaySet(baseNote, backgroundColor, accountViewModel, nav)
} }
is ChatMessageRelayListEvent -> { is DmRelayListEvent -> {
DisplayDMRelayList(baseNote, backgroundColor, accountViewModel, nav) DisplayDMRelayList(baseNote, backgroundColor, accountViewModel, nav)
} }
@@ -1251,7 +1251,7 @@ private fun RenderNoteRow(
DisplayTrustedRelayList(baseNote, backgroundColor, accountViewModel, nav) DisplayTrustedRelayList(baseNote, backgroundColor, accountViewModel, nav)
} }
is RelayFeedsListEvent -> { is FavoriteRelayListEvent -> {
DisplayRelayFeedsList(baseNote, backgroundColor, accountViewModel, nav) DisplayRelayFeedsList(baseNote, backgroundColor, accountViewModel, nav)
} }
@@ -1384,7 +1384,7 @@ private fun RenderNoteRow(
) )
} }
is PrivateDmEvent -> { is EncryptedDmEvent -> {
RenderPrivateMessage( RenderPrivateMessage(
baseNote, baseNote,
makeItShort, makeItShort,
@@ -1494,7 +1494,7 @@ private fun RenderNoteRow(
RenderCalendarDateSlotEvent(baseNote, accountViewModel, nav) RenderCalendarDateSlotEvent(baseNote, accountViewModel, nav)
} }
is CalendarEvent -> { is CalendarCollectionEvent -> {
RenderCalendarCollectionEvent(baseNote, accountViewModel, nav) RenderCalendarCollectionEvent(baseNote, accountViewModel, nav)
} }
@@ -1502,7 +1502,7 @@ private fun RenderNoteRow(
RenderCalendarRSVPEvent(baseNote, accountViewModel, nav) RenderCalendarRSVPEvent(baseNote, accountViewModel, nav)
} }
is GoalEvent -> { is ZapGoalEvent -> {
RenderGoal(baseNote, accountViewModel, nav) RenderGoal(baseNote, accountViewModel, nav)
} }
@@ -1550,7 +1550,7 @@ private fun RenderNoteRow(
) )
} }
is NIP90ContentDiscoveryResponseEvent -> { is DvmContentDiscoveryResponseEvent -> {
RenderNIP90ContentDiscoveryResponse( RenderNIP90ContentDiscoveryResponse(
baseNote, baseNote,
makeItShort, makeItShort,
@@ -1562,7 +1562,7 @@ private fun RenderNoteRow(
) )
} }
is NIP90StatusEvent -> { is DvmStatusEvent -> {
RenderNIP90Status( RenderNIP90Status(
baseNote, baseNote,
accountViewModel, accountViewModel,
@@ -1640,7 +1640,7 @@ private fun RenderNoteRow(
) )
} }
is FileHeaderEvent -> { is FileMetadataEvent -> {
FileHeaderDisplay(baseNote, true, ContentScale.FillWidth, accountViewModel) FileHeaderDisplay(baseNote, true, ContentScale.FillWidth, accountViewModel)
} }
@@ -1879,7 +1879,7 @@ private fun RenderNoteRow(
RenderOldBookmarkList(baseNote, quotesLeft, backgroundColor, accountViewModel, nav) RenderOldBookmarkList(baseNote, quotesLeft, backgroundColor, accountViewModel, nav)
} }
is LabeledBookmarkListEvent -> { is BookmarkSetEvent -> {
RenderLabeledBookmarkList(baseNote, quotesLeft, backgroundColor, accountViewModel, nav) RenderLabeledBookmarkList(baseNote, quotesLeft, backgroundColor, accountViewModel, nav)
} }
@@ -1923,7 +1923,7 @@ private fun RenderNoteRow(
RenderMediaStarterPack(baseNote, backgroundColor, accountViewModel, nav) RenderMediaStarterPack(baseNote, backgroundColor, accountViewModel, nav)
} }
is HashtagListEvent -> { is InterestListEvent -> {
RenderHashtagList(baseNote, backgroundColor, accountViewModel, nav) RenderHashtagList(baseNote, backgroundColor, accountViewModel, nav)
} }
@@ -2172,7 +2172,7 @@ fun FirstUserInfoRow(
// Zap receipts are signed by the recipient's lightning provider; show the // Zap receipts are signed by the recipient's lightning provider; show the
// sender from the embedded zap request instead of the service key. // sender from the embedded zap request instead of the service key.
val zapSender = val zapSender =
if (baseNote.event is LnZapEvent) { if (baseNote.event is ZapReceiptEvent) {
observeZapSender(baseNote, accountViewModel).value observeZapSender(baseNote, accountViewModel).value
} else { } else {
null null
@@ -2411,7 +2411,7 @@ fun RenderAuthorImages(
} else { } else {
NoteAuthorPicture(baseNote, Size55dp, accountViewModel = accountViewModel, nav = nav) NoteAuthorPicture(baseNote, Size55dp, accountViewModel = accountViewModel, nav = nav)
} }
} else if (noteEvent is LnZapEvent) { } else if (noteEvent is ZapReceiptEvent) {
// Zap receipts are signed by the recipient's lightning provider; show the // Zap receipts are signed by the recipient's lightning provider; show the
// sender from the embedded zap request instead of the service key, matching // sender from the embedded zap request instead of the service key, matching
// how the thread's master note resolves the author. // how the thread's master note resolves the author.
@@ -116,8 +116,8 @@ import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountViewModel
import com.vitorpamplona.amethyst.ui.screen.loggedIn.report.ReportNoteDialog import com.vitorpamplona.amethyst.ui.screen.loggedIn.report.ReportNoteDialog
import com.vitorpamplona.quartz.experimental.bounties.bountyBaseReward import com.vitorpamplona.quartz.experimental.bounties.bountyBaseReward
import com.vitorpamplona.quartz.nip28PublicChat.message.ChannelMessageEvent import com.vitorpamplona.quartz.nip28PublicChat.message.ChannelMessageEvent
import com.vitorpamplona.quartz.nip51Lists.followList.FollowListEvent import com.vitorpamplona.quartz.nip51Lists.followList.StarterPackEvent
import com.vitorpamplona.quartz.nip51Lists.peopleList.PeopleListEvent import com.vitorpamplona.quartz.nip51Lists.peopleList.FollowSetEvent
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
import org.jetbrains.compose.resources.StringResource import org.jetbrains.compose.resources.StringResource
@@ -149,8 +149,8 @@ val externalLinkForNote = { note: Note ->
if (note is AddressableNote) { if (note is AddressableNote) {
when { when {
note.event?.bountyBaseReward() != null -> "https://nostrbounties.com/b/${note.toNAddr()}" note.event?.bountyBaseReward() != null -> "https://nostrbounties.com/b/${note.toNAddr()}"
note.event is PeopleListEvent -> "https://listr.lol/a/${note.toNAddr()}" note.event is FollowSetEvent -> "https://listr.lol/a/${note.toNAddr()}"
note.event is FollowListEvent -> "https://following.space/d/${note.address.dTag}?p=${note.address.pubKeyHex}" note.event is StarterPackEvent -> "https://following.space/d/${note.address.dTag}?p=${note.address.pubKeyHex}"
else -> njumpLink(note.toNAddr()) else -> njumpLink(note.toNAddr())
} }
} else { } else {
@@ -95,7 +95,7 @@ import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountViewModel
import com.vitorpamplona.quartz.nip01Core.core.Address import com.vitorpamplona.quartz.nip01Core.core.Address
import com.vitorpamplona.quartz.nip30CustomEmoji.CustomEmoji import com.vitorpamplona.quartz.nip30CustomEmoji.CustomEmoji
import com.vitorpamplona.quartz.nip30CustomEmoji.EmojiUrlTag import com.vitorpamplona.quartz.nip30CustomEmoji.EmojiUrlTag
import com.vitorpamplona.quartz.nip30CustomEmoji.selection.EmojiPackSelectionEvent import com.vitorpamplona.quartz.nip30CustomEmoji.selection.EmojiListEvent
import kotlinx.collections.immutable.ImmutableList import kotlinx.collections.immutable.ImmutableList
import kotlinx.collections.immutable.persistentListOf import kotlinx.collections.immutable.persistentListOf
import kotlinx.collections.immutable.toImmutableList import kotlinx.collections.immutable.toImmutableList
@@ -361,7 +361,7 @@ private fun EmojiSelector(
accountViewModel.account.emoji.getEmojiPackSelectionAddress(), accountViewModel.account.emoji.getEmojiPackSelectionAddress(),
) { emptyNote -> ) { emptyNote ->
emptyNote?.let { usersEmojiList -> emptyNote?.let { usersEmojiList ->
val collections by observeNoteEventAndMapNotNull(usersEmojiList, accountViewModel) { event: EmojiPackSelectionEvent -> val collections by observeNoteEventAndMapNotNull(usersEmojiList, accountViewModel) { event: EmojiListEvent ->
event.emojiPacks().toImmutableList() event.emojiPacks().toImmutableList()
} }
@@ -121,7 +121,7 @@ import com.vitorpamplona.amethyst.commons.ui.theme.placeholderText
import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountViewModel import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountViewModel
import com.vitorpamplona.amethyst.ui.screen.loggedIn.keyBackup.getFragmentActivity import com.vitorpamplona.amethyst.ui.screen.loggedIn.keyBackup.getFragmentActivity
import com.vitorpamplona.amethyst.ui.screen.loggedIn.mockAccountViewModel import com.vitorpamplona.amethyst.ui.screen.loggedIn.mockAccountViewModel
import com.vitorpamplona.quartz.nip57Zaps.LnZapEvent import com.vitorpamplona.quartz.nip57Zaps.ZapReceiptEvent
import kotlinx.collections.immutable.toImmutableList import kotlinx.collections.immutable.toImmutableList
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
@@ -153,22 +153,22 @@ fun UpdateZapAmountContent(
val zapTypes = val zapTypes =
listOf( listOf(
Triple( Triple(
LnZapEvent.ZapType.PUBLIC, ZapReceiptEvent.ZapType.PUBLIC,
stringRes(id = Res.string.zap_type_public), stringRes(id = Res.string.zap_type_public),
stringRes(id = Res.string.zap_type_public_explainer), stringRes(id = Res.string.zap_type_public_explainer),
), ),
Triple( Triple(
LnZapEvent.ZapType.PRIVATE, ZapReceiptEvent.ZapType.PRIVATE,
stringRes(id = Res.string.zap_type_private), stringRes(id = Res.string.zap_type_private),
stringRes(id = Res.string.zap_type_private_explainer), stringRes(id = Res.string.zap_type_private_explainer),
), ),
Triple( Triple(
LnZapEvent.ZapType.ANONYMOUS, ZapReceiptEvent.ZapType.ANONYMOUS,
stringRes(id = Res.string.zap_type_anonymous), stringRes(id = Res.string.zap_type_anonymous),
stringRes(id = Res.string.zap_type_anonymous_explainer), stringRes(id = Res.string.zap_type_anonymous_explainer),
), ),
Triple( Triple(
LnZapEvent.ZapType.NONZAP, ZapReceiptEvent.ZapType.NONZAP,
stringRes(id = Res.string.zap_type_nonzap), stringRes(id = Res.string.zap_type_nonzap),
stringRes(id = Res.string.zap_type_nonzap_explainer), stringRes(id = Res.string.zap_type_nonzap_explainer),
), ),
@@ -32,7 +32,7 @@ import com.vitorpamplona.quartz.nip01Core.relay.normalizer.RelayUrlNormalizer
import com.vitorpamplona.quartz.nip19Bech32.decodePrivateKeyAsHexOrNull import com.vitorpamplona.quartz.nip19Bech32.decodePrivateKeyAsHexOrNull
import com.vitorpamplona.quartz.nip19Bech32.decodePublicKey import com.vitorpamplona.quartz.nip19Bech32.decodePublicKey
import com.vitorpamplona.quartz.nip47WalletConnect.Nip47WalletConnect import com.vitorpamplona.quartz.nip47WalletConnect.Nip47WalletConnect
import com.vitorpamplona.quartz.nip57Zaps.LnZapEvent import com.vitorpamplona.quartz.nip57Zaps.ZapReceiptEvent
import kotlinx.coroutines.CancellationException import kotlinx.coroutines.CancellationException
@Stable @Stable
@@ -44,7 +44,7 @@ class UpdateZapAmountViewModel : ViewModel() {
var walletConnectRelay by mutableStateOf(TextFieldValue("")) var walletConnectRelay by mutableStateOf(TextFieldValue(""))
var walletConnectPubkey by mutableStateOf(TextFieldValue("")) var walletConnectPubkey by mutableStateOf(TextFieldValue(""))
var walletConnectSecret by mutableStateOf(TextFieldValue("")) var walletConnectSecret by mutableStateOf(TextFieldValue(""))
var selectedZapType by mutableStateOf(LnZapEvent.ZapType.PRIVATE) var selectedZapType by mutableStateOf(ZapReceiptEvent.ZapType.PRIVATE)
// A local UI preference rather than synced account state, but it is edited on // A local UI preference rather than synced account state, but it is edited on
// this screen, so it follows this screen's Save/Cancel contract instead of // this screen, so it follows this screen's Save/Cancel contract instead of
@@ -117,7 +117,7 @@ import com.vitorpamplona.amethyst.service.ZapPaymentHandler
import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountViewModel import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountViewModel
import com.vitorpamplona.amethyst.ui.screen.loggedIn.wallet.OnchainZapSendDialog import com.vitorpamplona.amethyst.ui.screen.loggedIn.wallet.OnchainZapSendDialog
import com.vitorpamplona.quartz.nip01Core.core.Event import com.vitorpamplona.quartz.nip01Core.core.Event
import com.vitorpamplona.quartz.nip57Zaps.LnZapEvent import com.vitorpamplona.quartz.nip57Zaps.ZapReceiptEvent
import kotlinx.collections.immutable.ImmutableList import kotlinx.collections.immutable.ImmutableList
import kotlinx.coroutines.CancellationException import kotlinx.coroutines.CancellationException
@@ -163,35 +163,35 @@ fun ZapCustomDialog(
val zapTypes = val zapTypes =
listOf( listOf(
Triple( Triple(
LnZapEvent.ZapType.PUBLIC, ZapReceiptEvent.ZapType.PUBLIC,
stringRes(id = Res.string.zap_type_public), stringRes(id = Res.string.zap_type_public),
stringRes(id = Res.string.zap_type_public_explainer), stringRes(id = Res.string.zap_type_public_explainer),
), ),
Triple( Triple(
LnZapEvent.ZapType.PRIVATE, ZapReceiptEvent.ZapType.PRIVATE,
stringRes(id = Res.string.zap_type_private), stringRes(id = Res.string.zap_type_private),
stringRes(id = Res.string.zap_type_private_explainer), stringRes(id = Res.string.zap_type_private_explainer),
), ),
Triple( Triple(
LnZapEvent.ZapType.ANONYMOUS, ZapReceiptEvent.ZapType.ANONYMOUS,
stringRes(id = Res.string.zap_type_anonymous), stringRes(id = Res.string.zap_type_anonymous),
stringRes(id = Res.string.zap_type_anonymous_explainer), stringRes(id = Res.string.zap_type_anonymous_explainer),
), ),
Triple( Triple(
LnZapEvent.ZapType.NONZAP, ZapReceiptEvent.ZapType.NONZAP,
stringRes(id = Res.string.zap_type_nonzap), stringRes(id = Res.string.zap_type_nonzap),
stringRes(id = Res.string.zap_type_nonzap_explainer), stringRes(id = Res.string.zap_type_nonzap_explainer),
), ),
).filter { ).filter {
!isPrivateTarget || it.first == LnZapEvent.ZapType.PRIVATE || it.first == LnZapEvent.ZapType.NONZAP !isPrivateTarget || it.first == ZapReceiptEvent.ZapType.PRIVATE || it.first == ZapReceiptEvent.ZapType.NONZAP
} }
var selectedZapType by var selectedZapType by
remember(accountViewModel, baseNote) { remember(accountViewModel, baseNote) {
val default = accountViewModel.defaultZapType() val default = accountViewModel.defaultZapType()
mutableStateOf( mutableStateOf(
if (isPrivateTarget && default != LnZapEvent.ZapType.NONZAP) { if (isPrivateTarget && default != ZapReceiptEvent.ZapType.NONZAP) {
LnZapEvent.ZapType.PRIVATE ZapReceiptEvent.ZapType.PRIVATE
} else { } else {
default default
}, },
@@ -327,15 +327,15 @@ fun ZapCustomDialog(
OutlinedTextField( OutlinedTextField(
label = { label = {
when (selectedZapType) { when (selectedZapType) {
LnZapEvent.ZapType.PUBLIC, LnZapEvent.ZapType.ANONYMOUS -> { ZapReceiptEvent.ZapType.PUBLIC, ZapReceiptEvent.ZapType.ANONYMOUS -> {
Text(text = stringRes(id = Res.string.custom_zaps_add_a_message)) Text(text = stringRes(id = Res.string.custom_zaps_add_a_message))
} }
LnZapEvent.ZapType.PRIVATE -> { ZapReceiptEvent.ZapType.PRIVATE -> {
Text(text = stringRes(id = Res.string.custom_zaps_add_a_message_private)) Text(text = stringRes(id = Res.string.custom_zaps_add_a_message_private))
} }
LnZapEvent.ZapType.NONZAP -> { ZapReceiptEvent.ZapType.NONZAP -> {
Text(text = stringRes(id = Res.string.custom_zaps_add_a_message_nonzap)) Text(text = stringRes(id = Res.string.custom_zaps_add_a_message_nonzap))
} }
} }
@@ -113,7 +113,7 @@ import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountViewModel
import com.vitorpamplona.amethyst.ui.screen.loggedIn.mockAccountViewModel import com.vitorpamplona.amethyst.ui.screen.loggedIn.mockAccountViewModel
import com.vitorpamplona.amethyst.ui.screen.loggedIn.mockVitorAccountViewModel import com.vitorpamplona.amethyst.ui.screen.loggedIn.mockVitorAccountViewModel
import com.vitorpamplona.quartz.experimental.zapPolls.ZapPollEvent import com.vitorpamplona.quartz.experimental.zapPolls.ZapPollEvent
import com.vitorpamplona.quartz.nip57Zaps.LnZapEvent import com.vitorpamplona.quartz.nip57Zaps.ZapReceiptEvent
import kotlinx.collections.immutable.ImmutableList import kotlinx.collections.immutable.ImmutableList
import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
@@ -145,7 +145,7 @@ fun ZapZapPollNotePreview() {
) )
val zapVote = val zapVote =
LnZapEvent( ZapReceiptEvent(
id = "2a17fdcd0e387d1623c7313d7aa2848e18dde8a942cfe8a2d6b686ea5f68f01a", id = "2a17fdcd0e387d1623c7313d7aa2848e18dde8a942cfe8a2d6b686ea5f68f01a",
pubKey = "79f00d3f5a19ec806189fcab03c1be4ff81d18ee4f653c88fac41fe03570f432", pubKey = "79f00d3f5a19ec806189fcab03c1be4ff81d18ee4f653c88fac41fe03570f432",
createdAt = 1729186293, createdAt = 1729186293,
@@ -30,8 +30,8 @@ import com.vitorpamplona.amethyst.commons.model.Note
import com.vitorpamplona.amethyst.commons.model.User import com.vitorpamplona.amethyst.commons.model.User
import com.vitorpamplona.amethyst.model.Account import com.vitorpamplona.amethyst.model.Account
import com.vitorpamplona.quartz.experimental.zapPolls.ZapPollEvent import com.vitorpamplona.quartz.experimental.zapPolls.ZapPollEvent
import com.vitorpamplona.quartz.nip57Zaps.LnZapEvent import com.vitorpamplona.quartz.nip57Zaps.ZapReceiptEvent
import com.vitorpamplona.quartz.nip57Zaps.LnZapRequestEvent import com.vitorpamplona.quartz.nip57Zaps.ZapRequestEvent
import com.vitorpamplona.quartz.utils.TimeUtils import com.vitorpamplona.quartz.utils.TimeUtils
import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
@@ -233,9 +233,9 @@ class PollNoteViewModel : ViewModel() {
user: User, user: User,
): Boolean = ): Boolean =
pollNote!!.zaps.any { pollNote!!.zaps.any {
val zapEvent = it.value?.event as? LnZapEvent val zapEvent = it.value?.event as? ZapReceiptEvent
val privateZapAuthor = val privateZapAuthor =
(it.key.event as? LnZapRequestEvent)?.let { (it.key.event as? ZapRequestEvent)?.let {
account.privateZapsDecryptionCache.cachedPrivateZap(it) account.privateZapsDecryptionCache.cachedPrivateZap(it)
} }
zapEvent?.zappedPollOption() == option && zapEvent?.zappedPollOption() == option &&
@@ -244,7 +244,7 @@ class PollNoteViewModel : ViewModel() {
private fun zappedPollOptionAmount(option: Int): BigDecimal = private fun zappedPollOptionAmount(option: Int): BigDecimal =
pollNote?.zaps?.values?.sumOf { pollNote?.zaps?.values?.sumOf {
val event = it?.event as? LnZapEvent val event = it?.event as? ZapReceiptEvent
val zapAmount = event?.amount ?: BigDecimal.ZERO val zapAmount = event?.amount ?: BigDecimal.ZERO
val isValidAmount = isValidInputVoteAmount(event?.amount) val isValidAmount = isValidInputVoteAmount(event?.amount)
@@ -258,7 +258,7 @@ class PollNoteViewModel : ViewModel() {
private fun totalZapped(): BigDecimal = private fun totalZapped(): BigDecimal =
pollNote?.zaps?.values?.sumOf { pollNote?.zaps?.values?.sumOf {
val zapEvent = (it?.event as? LnZapEvent) val zapEvent = (it?.event as? ZapReceiptEvent)
val zapAmount = zapEvent?.amount ?: BigDecimal.ZERO val zapAmount = zapEvent?.amount ?: BigDecimal.ZERO
val isValidAmount = isValidInputVoteAmount(zapEvent?.amount) val isValidAmount = isValidInputVoteAmount(zapEvent?.amount)
@@ -27,16 +27,16 @@ import com.vitorpamplona.amethyst.service.relayClient.reqCommand.event.observeNo
import com.vitorpamplona.amethyst.ui.note.LoadAddressableNote import com.vitorpamplona.amethyst.ui.note.LoadAddressableNote
import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountViewModel import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountViewModel
import com.vitorpamplona.quartz.nip01Core.tags.aTag.taggedAddresses import com.vitorpamplona.quartz.nip01Core.tags.aTag.taggedAddresses
import com.vitorpamplona.quartz.nip30CustomEmoji.selection.EmojiPackSelectionEvent import com.vitorpamplona.quartz.nip30CustomEmoji.selection.EmojiListEvent
import kotlinx.collections.immutable.toImmutableList import kotlinx.collections.immutable.toImmutableList
@Composable @Composable
fun WatchAndLoadMyEmojiList(accountViewModel: AccountViewModel) { fun WatchAndLoadMyEmojiList(accountViewModel: AccountViewModel) {
LoadAddressableNote( LoadAddressableNote(
EmojiPackSelectionEvent.createAddress(accountViewModel.userProfile().pubkeyHex), EmojiListEvent.createAddress(accountViewModel.userProfile().pubkeyHex),
) { emptyNote -> ) { emptyNote ->
emptyNote?.let { usersEmojiList -> emptyNote?.let { usersEmojiList ->
val collections by observeNoteEventAndMapNotNull(usersEmojiList, accountViewModel) { event: EmojiPackSelectionEvent -> val collections by observeNoteEventAndMapNotNull(usersEmojiList, accountViewModel) { event: EmojiListEvent ->
event.taggedAddresses().toImmutableList() event.taggedAddresses().toImmutableList()
} }
@@ -55,7 +55,7 @@ import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountViewModel
import com.vitorpamplona.amethyst.ui.screen.loggedIn.mockAccountViewModel import com.vitorpamplona.amethyst.ui.screen.loggedIn.mockAccountViewModel
import com.vitorpamplona.amethyst.ui.screen.loggedIn.relays.dm.AddDMRelayListDialog import com.vitorpamplona.amethyst.ui.screen.loggedIn.relays.dm.AddDMRelayListDialog
import com.vitorpamplona.quartz.nip01Core.core.HexKey import com.vitorpamplona.quartz.nip01Core.core.HexKey
import com.vitorpamplona.quartz.nip17Dm.settings.ChatMessageRelayListEvent import com.vitorpamplona.quartz.nip17Dm.settings.DmRelayListEvent
@Preview @Preview
@Composable @Composable
@@ -88,7 +88,7 @@ fun ObserveRelayListForDMsAndDisplayIfNotFound(
@Composable @Composable
fun ObserveRelayListForDMs( fun ObserveRelayListForDMs(
accountViewModel: AccountViewModel, accountViewModel: AccountViewModel,
inner: @Composable (relayListEvent: ChatMessageRelayListEvent?) -> Unit, inner: @Composable (relayListEvent: DmRelayListEvent?) -> Unit,
) { ) {
ObserveRelayListForDMs( ObserveRelayListForDMs(
pubkey = accountViewModel.account.userProfile().pubkeyHex, pubkey = accountViewModel.account.userProfile().pubkeyHex,
@@ -102,13 +102,13 @@ fun ObserveRelayListForDMs(
fun ObserveRelayListForDMs( fun ObserveRelayListForDMs(
pubkey: HexKey, pubkey: HexKey,
accountViewModel: AccountViewModel, accountViewModel: AccountViewModel,
inner: @Composable (relayListEvent: ChatMessageRelayListEvent?) -> Unit, inner: @Composable (relayListEvent: DmRelayListEvent?) -> Unit,
) { ) {
LoadAddressableNote( LoadAddressableNote(
ChatMessageRelayListEvent.createAddress(pubkey), DmRelayListEvent.createAddress(pubkey),
) { relayList -> ) { relayList ->
if (relayList != null) { if (relayList != null) {
val relayListEvent by observeNoteEvent<ChatMessageRelayListEvent>(relayList, accountViewModel) val relayListEvent by observeNoteEvent<DmRelayListEvent>(relayList, accountViewModel)
inner(relayListEvent) inner(relayListEvent)
} }
@@ -80,7 +80,7 @@ import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountViewModel
import com.vitorpamplona.quartz.experimental.music.track.MusicTrackEvent import com.vitorpamplona.quartz.experimental.music.track.MusicTrackEvent
import com.vitorpamplona.quartz.nip01Core.jackson.JacksonMapper import com.vitorpamplona.quartz.nip01Core.jackson.JacksonMapper
import com.vitorpamplona.quartz.nip10Notes.TextNoteEvent import com.vitorpamplona.quartz.nip10Notes.TextNoteEvent
import com.vitorpamplona.quartz.nip23LongContent.LongTextNoteEvent import com.vitorpamplona.quartz.nip23LongContent.LongFormContentEvent
import com.vitorpamplona.quartz.nip28PublicChat.message.ChannelMessageEvent import com.vitorpamplona.quartz.nip28PublicChat.message.ChannelMessageEvent
import com.vitorpamplona.quartz.nip30CustomEmoji.pack.EmojiPackEvent import com.vitorpamplona.quartz.nip30CustomEmoji.pack.EmojiPackEvent
import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Dispatchers
@@ -222,7 +222,7 @@ fun noteActionSections(
// nostr source pre-tagged (`a` for an addressable article, else `e`) plus the author, // nostr source pre-tagged (`a` for an addressable article, else `e`) plus the author,
// and the passage left for the user to type or paste. Prose kinds only, and never a // and the passage left for the user to type or paste. Prose kinds only, and never a
// private rumor (a public highlight would e-tag the unsigned rumor onto relays). // private rumor (a public highlight would e-tag the unsigned rumor onto relays).
if (!isPrivateRumor && (note.event is TextNoteEvent || note.event is LongTextNoteEvent)) { if (!isPrivateRumor && (note.event is TextNoteEvent || note.event is LongFormContentEvent)) {
add( add(
NoteAction(MaterialSymbols.FormatQuote, stringRes(Res.string.highlight_action)) { NoteAction(MaterialSymbols.FormatQuote, stringRes(Res.string.highlight_action)) {
val author = note.author?.pubkeyHex val author = note.author?.pubkeyHex
@@ -256,7 +256,7 @@ fun noteActionSections(
onClick = handlers.onEditPost, onClick = handlers.onEditPost,
), ),
) )
} else if (note.event is LongTextNoteEvent && state.isLoggedUser) { } else if (note.event is LongFormContentEvent && state.isLoggedUser) {
add( add(
NoteAction(MaterialSymbols.Edit, stringRes(Res.string.edit_article)) { NoteAction(MaterialSymbols.Edit, stringRes(Res.string.edit_article)) {
nav.nav { Route.NewLongFormPost(version = note.idHex) } nav.nav { Route.NewLongFormPost(version = note.idHex) }
@@ -343,10 +343,10 @@ fun noteActionSections(
} }
else -> { else -> {
val noteBookmarkType = if (note.event is LongTextNoteEvent) stringRes(Res.string.article) else stringRes(Res.string.post) val noteBookmarkType = if (note.event is LongFormContentEvent) stringRes(Res.string.article) else stringRes(Res.string.post)
add( add(
NoteAction(MaterialSymbols.BookmarkAdd, stringRes(Res.string.manage_bookmark_label, noteBookmarkType)) { NoteAction(MaterialSymbols.BookmarkAdd, stringRes(Res.string.manage_bookmark_label, noteBookmarkType)) {
if (note.event is LongTextNoteEvent) { if (note.event is LongFormContentEvent) {
nav.nav(Route.ArticleBookmarkManagement((note as AddressableNote).address)) nav.nav(Route.ArticleBookmarkManagement((note as AddressableNote).address))
} else { } else {
nav.nav(Route.PostBookmarkManagement(note.idHex)) nav.nav(Route.PostBookmarkManagement(note.idHex))
@@ -109,7 +109,7 @@ import com.vitorpamplona.quartz.nip36SensitiveContent.contentWarningReason
import com.vitorpamplona.quartz.nip36SensitiveContent.isSensitive import com.vitorpamplona.quartz.nip36SensitiveContent.isSensitive
import com.vitorpamplona.quartz.nip37Drafts.DraftWrapEvent import com.vitorpamplona.quartz.nip37Drafts.DraftWrapEvent
import com.vitorpamplona.quartz.nip40Expiration.expiration import com.vitorpamplona.quartz.nip40Expiration.expiration
import com.vitorpamplona.quartz.nip57Zaps.LnZapEvent import com.vitorpamplona.quartz.nip57Zaps.ZapReceiptEvent
import com.vitorpamplona.quartz.nip57Zaps.splits.zapSplits import com.vitorpamplona.quartz.nip57Zaps.splits.zapSplits
import com.vitorpamplona.quartz.nip57Zaps.zapraiser.zapraiser import com.vitorpamplona.quartz.nip57Zaps.zapraiser.zapraiser
import com.vitorpamplona.quartz.nip57Zaps.zapraiser.zapraiserAmount import com.vitorpamplona.quartz.nip57Zaps.zapraiser.zapraiserAmount
@@ -352,7 +352,7 @@ open class CommentPostViewModel :
this.externalIdentity = (post.event as? CommentEvent)?.scope() this.externalIdentity = (post.event as? CommentEvent)?.scope()
mutedNotifies = emptySet() mutedNotifies = emptySet()
notifyProvenance = emptyMap() notifyProvenance = emptyMap()
(post.event as? LnZapEvent)?.let { zap -> (post.event as? ZapReceiptEvent)?.let { zap ->
notifying = listOfNotNull(zapSenderToNotify(zap)) notifying = listOfNotNull(zapSenderToNotify(zap))
} }
observeCommunityRules(post) observeCommunityRules(post)
@@ -366,7 +366,7 @@ open class CommentPostViewModel :
* by an ephemeral key: skip those — tagging the throwaway key is useless, and * by an ephemeral key: skip those — tagging the throwaway key is useless, and
* tagging the decrypted sender would publicly expose a private zapper. * tagging the decrypted sender would publicly expose a private zapper.
*/ */
private fun zapSenderToNotify(zapEvent: LnZapEvent): User? { private fun zapSenderToNotify(zapEvent: ZapReceiptEvent): User? {
val request = zapEvent.zapRequest ?: return null val request = zapEvent.zapRequest ?: return null
if (request.hasAnonTag()) return null if (request.hasAnonTag()) return null
if (request.pubKey == account.signer.pubKey) return null if (request.pubKey == account.signer.pubKey) return null
@@ -554,7 +554,7 @@ open class CommentPostViewModel :
// Replies to zaps notify the zap sender through a plain p tag (the receipt's // Replies to zaps notify the zap sender through a plain p tag (the receipt's
// author keys above are the lightning provider). The sender chip always comes // author keys above are the lightning provider). The sender chip always comes
// back; a missing p tag in the draft means the user muted their bell. // back; a missing p tag in the draft means the user muted their bell.
(replyingTo?.event as? LnZapEvent)?.let { zap -> (replyingTo?.event as? ZapReceiptEvent)?.let { zap ->
zapSenderToNotify(zap)?.let { sender -> zapSenderToNotify(zap)?.let { sender ->
notifying = ((notifying ?: emptyList()) + sender).distinct() notifying = ((notifying ?: emptyList()) + sender).distinct()
if (!draftEvent.tags.mapNotNull(PTag::parseKey).contains(sender.pubkeyHex)) { if (!draftEvent.tags.mapNotNull(PTag::parseKey).contains(sender.pubkeyHex)) {
@@ -726,7 +726,7 @@ open class CommentPostViewModel :
null null
} }
} }
} else if (replyingToEvent is LnZapEvent) { } else if (replyingToEvent is ZapReceiptEvent) {
val sender = zapSenderToNotify(replyingToEvent) val sender = zapSenderToNotify(replyingToEvent)
// The sender's chip stays in the list; a muted bell means // The sender's chip stays in the list; a muted bell means
// the user doesn't want to ping them, so don't tag them. // the user doesn't want to ping them, so don't tag them.
@@ -63,7 +63,7 @@ import com.vitorpamplona.amethyst.ui.screen.loggedIn.profile.gallery.GalleryThum
import com.vitorpamplona.amethyst.ui.screen.loggedIn.profile.gallery.UrlImageView import com.vitorpamplona.amethyst.ui.screen.loggedIn.profile.gallery.UrlImageView
import com.vitorpamplona.quartz.experimental.profileGallery.ProfileGalleryEntryEvent import com.vitorpamplona.quartz.experimental.profileGallery.ProfileGalleryEntryEvent
import com.vitorpamplona.quartz.nip01Core.core.Event import com.vitorpamplona.quartz.nip01Core.core.Event
import com.vitorpamplona.quartz.nip23LongContent.LongTextNoteEvent import com.vitorpamplona.quartz.nip23LongContent.LongFormContentEvent
import com.vitorpamplona.quartz.nip36SensitiveContent.ContentWarningTag import com.vitorpamplona.quartz.nip36SensitiveContent.ContentWarningTag
import com.vitorpamplona.quartz.nip36SensitiveContent.isSensitiveOrNSFW import com.vitorpamplona.quartz.nip36SensitiveContent.isSensitiveOrNSFW
import com.vitorpamplona.quartz.nip53LiveActivities.clip.LiveActivitiesClipEvent import com.vitorpamplona.quartz.nip53LiveActivities.clip.LiveActivitiesClipEvent
@@ -278,7 +278,7 @@ private fun secondaryLineFor(
return when { return when {
event is PictureEvent -> stringRes(Res.string.share_as_qr_kind_picture) event is PictureEvent -> stringRes(Res.string.share_as_qr_kind_picture)
event is VideoEvent -> stringRes(Res.string.kind_video) event is VideoEvent -> stringRes(Res.string.kind_video)
event is LongTextNoteEvent -> stringRes(Res.string.article) event is LongFormContentEvent -> stringRes(Res.string.article)
hasContentImage -> stringRes(Res.string.share_as_qr_kind_picture) hasContentImage -> stringRes(Res.string.share_as_qr_kind_picture)
else -> "" else -> ""
} }
@@ -293,7 +293,7 @@ internal fun secondaryBodyTextFor(
// Gated: never surface the note's own title, content or alt text, only the kind label above. // Gated: never surface the note's own title, content or alt text, only the kind label above.
if (isGated) return null if (isGated) return null
if (event is LongTextNoteEvent) { if (event is LongFormContentEvent) {
val title = event.title() val title = event.title()
if (!title.isNullOrBlank()) return title if (!title.isNullOrBlank()) return title
} }
@@ -25,7 +25,7 @@ import com.vitorpamplona.amethyst.commons.model.Note
import com.vitorpamplona.amethyst.commons.ui.navigation.navs.INav import com.vitorpamplona.amethyst.commons.ui.navigation.navs.INav
import com.vitorpamplona.amethyst.commons.ui.note.CalendarCollectionCard import com.vitorpamplona.amethyst.commons.ui.note.CalendarCollectionCard
import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountViewModel import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountViewModel
import com.vitorpamplona.quartz.nip52Calendar.calendar.CalendarEvent import com.vitorpamplona.quartz.nip52Calendar.calendar.CalendarCollectionEvent
/** /**
* Entry for a NIP-52 calendar collection: decodes the [Note] and renders the shared commons * Entry for a NIP-52 calendar collection: decodes the [Note] and renders the shared commons
@@ -38,7 +38,7 @@ fun RenderCalendarCollectionEvent(
accountViewModel: AccountViewModel, accountViewModel: AccountViewModel,
nav: INav, nav: INav,
) { ) {
val event = note.event as? CalendarEvent ?: return val event = note.event as? CalendarCollectionEvent ?: return
CalendarCollectionCard(event) CalendarCollectionCard(event)
} }
@@ -35,7 +35,7 @@ import com.vitorpamplona.amethyst.ui.components.SensitivityWarning
import com.vitorpamplona.amethyst.ui.components.ZoomableContentView import com.vitorpamplona.amethyst.ui.components.ZoomableContentView
import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountViewModel import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountViewModel
import com.vitorpamplona.quartz.nip31Alts.alt import com.vitorpamplona.quartz.nip31Alts.alt
import com.vitorpamplona.quartz.nip94FileMetadata.FileHeaderEvent import com.vitorpamplona.quartz.nip94FileMetadata.FileMetadataEvent
@Composable @Composable
fun FileHeaderDisplay( fun FileHeaderDisplay(
@@ -44,7 +44,7 @@ fun FileHeaderDisplay(
contentScale: ContentScale, contentScale: ContentScale,
accountViewModel: AccountViewModel, accountViewModel: AccountViewModel,
) { ) {
val event = (note.event as? FileHeaderEvent) ?: return val event = (note.event as? FileMetadataEvent) ?: return
val fullUrl = event.url() ?: return val fullUrl = event.url() ?: return
val mimeType = remember(note) { event.mimeType() } val mimeType = remember(note) { event.mimeType() }
val content = remember(note) { event.toMediaContent(note, fullUrl, mimeType) } val content = remember(note) { event.toMediaContent(note, fullUrl, mimeType) }
@@ -72,7 +72,7 @@ fun FileHeaderDisplay(
* video event the kind itself asserts nothing about how to render the payload. A null here means * video event the kind itself asserts nothing about how to render the payload. A null here means
* the file belongs in [FileHeaderAttachmentCard] rather than being pushed into the video player. * the file belongs in [FileHeaderAttachmentCard] rather than being pushed into the video player.
*/ */
internal fun FileHeaderEvent.toMediaContent( internal fun FileMetadataEvent.toMediaContent(
note: Note, note: Note,
url: String, url: String,
mimeType: String?, mimeType: String?,
@@ -129,7 +129,7 @@ internal fun FileHeaderEvent.toMediaContent(
/** The link card a kind-1063 header falls back to when [toMediaContent] returns null. */ /** The link card a kind-1063 header falls back to when [toMediaContent] returns null. */
@Composable @Composable
internal fun FileHeaderAttachmentCard( internal fun FileHeaderAttachmentCard(
event: FileHeaderEvent, event: FileMetadataEvent,
url: String, url: String,
mimeType: String?, mimeType: String?,
) { ) {
@@ -145,4 +145,4 @@ internal fun FileHeaderAttachmentCard(
} }
/** The human-facing name of the file: NIP-94 `content` when present, else the `alt` tag. */ /** The human-facing name of the file: NIP-94 `content` when present, else the `alt` tag. */
private fun FileHeaderEvent.fileDescription(): String? = content.ifEmpty { null } ?: alt() private fun FileMetadataEvent.fileDescription(): String? = content.ifEmpty { null } ?: alt()
@@ -66,7 +66,7 @@ import com.vitorpamplona.amethyst.ui.note.elements.DefaultImageHeaderBackground
import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountViewModel import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountViewModel
import com.vitorpamplona.amethyst.ui.screen.loggedIn.discover.nip51FollowSets.FollowSetCard import com.vitorpamplona.amethyst.ui.screen.loggedIn.discover.nip51FollowSets.FollowSetCard
import com.vitorpamplona.amethyst.ui.screen.loggedIn.mockAccountViewModel import com.vitorpamplona.amethyst.ui.screen.loggedIn.mockAccountViewModel
import com.vitorpamplona.quartz.nip51Lists.followList.FollowListEvent import com.vitorpamplona.quartz.nip51Lists.followList.StarterPackEvent
import kotlinx.collections.immutable.persistentListOf import kotlinx.collections.immutable.persistentListOf
import kotlinx.collections.immutable.toImmutableList import kotlinx.collections.immutable.toImmutableList
@@ -79,7 +79,7 @@ fun DisplayFollowList(
nav: INav, nav: INav,
) { ) {
val card = val card =
observeNoteEventAndMap(baseNote, accountViewModel) { event: FollowListEvent? -> observeNoteEventAndMap(baseNote, accountViewModel) { event: StarterPackEvent? ->
if (event == null) { if (event == null) {
FollowSetCard( FollowSetCard(
name = "", name = "",
@@ -190,7 +190,7 @@ fun RenderFollowSetThumbPreview() {
val accountViewModel = mockAccountViewModel() val accountViewModel = mockAccountViewModel()
val followCard = val followCard =
FollowListEvent( StarterPackEvent(
id = "eca31634fce7c9068b56fa8db9f387da70bdcceb3986a77ca1a9844f3128eb5f", id = "eca31634fce7c9068b56fa8db9f387da70bdcceb3986a77ca1a9844f3128eb5f",
pubKey = "3c39a7b53dec9ac85acf08b267637a9841e6df7b7b0f5e2ac56a8cf107de37da", pubKey = "3c39a7b53dec9ac85acf08b267637a9841e6df7b7b0f5e2ac56a8cf107de37da",
createdAt = 1761736286, createdAt = 1761736286,
@@ -60,7 +60,7 @@ import com.vitorpamplona.amethyst.ui.note.elements.DefaultImageHeader
import com.vitorpamplona.amethyst.ui.note.elements.DefaultImageHeaderBackground import com.vitorpamplona.amethyst.ui.note.elements.DefaultImageHeaderBackground
import com.vitorpamplona.amethyst.ui.note.showAmount import com.vitorpamplona.amethyst.ui.note.showAmount
import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountViewModel import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountViewModel
import com.vitorpamplona.quartz.nip75ZapGoals.GoalEvent import com.vitorpamplona.quartz.nip75ZapGoals.ZapGoalEvent
import com.vitorpamplona.quartz.utils.TimeUtils import com.vitorpamplona.quartz.utils.TimeUtils
import java.math.BigDecimal import java.math.BigDecimal
import kotlin.math.roundToInt import kotlin.math.roundToInt
@@ -71,14 +71,14 @@ fun RenderGoal(
accountViewModel: AccountViewModel, accountViewModel: AccountViewModel,
nav: INav, nav: INav,
) { ) {
val noteEvent = note.event as? GoalEvent ?: return val noteEvent = note.event as? ZapGoalEvent ?: return
GoalHeader(noteEvent, note, accountViewModel, nav) GoalHeader(noteEvent, note, accountViewModel, nav)
} }
@Composable @Composable
fun GoalHeader( fun GoalHeader(
noteEvent: GoalEvent, noteEvent: ZapGoalEvent,
note: Note, note: Note,
accountViewModel: AccountViewModel, accountViewModel: AccountViewModel,
nav: INav, nav: INav,
@@ -65,7 +65,7 @@ import com.vitorpamplona.amethyst.ui.note.elements.DefaultImageHeader
import com.vitorpamplona.amethyst.ui.note.elements.DefaultImageHeaderBackground import com.vitorpamplona.amethyst.ui.note.elements.DefaultImageHeaderBackground
import com.vitorpamplona.amethyst.ui.note.elements.TimeAgo import com.vitorpamplona.amethyst.ui.note.elements.TimeAgo
import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountViewModel import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountViewModel
import com.vitorpamplona.quartz.nip23LongContent.LongTextNoteEvent import com.vitorpamplona.quartz.nip23LongContent.LongFormContentEvent
import kotlinx.collections.immutable.toImmutableList import kotlinx.collections.immutable.toImmutableList
private const val WORDS_PER_MINUTE = 225 private const val WORDS_PER_MINUTE = 225
@@ -77,7 +77,7 @@ fun RenderLongFormContent(
accountViewModel: AccountViewModel, accountViewModel: AccountViewModel,
nav: INav, nav: INav,
) { ) {
val noteEvent = note.event as? LongTextNoteEvent ?: return val noteEvent = note.event as? LongFormContentEvent ?: return
LongFormHeader(noteEvent, note, accountViewModel) LongFormHeader(noteEvent, note, accountViewModel)
} }
@@ -85,7 +85,7 @@ fun RenderLongFormContent(
@OptIn(ExperimentalLayoutApi::class) @OptIn(ExperimentalLayoutApi::class)
@Composable @Composable
fun LongFormHeader( fun LongFormHeader(
noteEvent: LongTextNoteEvent, noteEvent: LongFormContentEvent,
note: Note, note: Note,
accountViewModel: AccountViewModel, accountViewModel: AccountViewModel,
) { ) {
@@ -35,7 +35,7 @@ import com.vitorpamplona.amethyst.ui.components.TranslatableRichTextViewer
import com.vitorpamplona.amethyst.ui.note.elements.DisplayUncitedHashtags import com.vitorpamplona.amethyst.ui.note.elements.DisplayUncitedHashtags
import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountViewModel import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountViewModel
import com.vitorpamplona.quartz.nip01Core.tags.hashtags.hasHashtags import com.vitorpamplona.quartz.nip01Core.tags.hashtags.hasHashtags
import com.vitorpamplona.quartz.nip90Dvms.contentDiscoveryResponse.NIP90ContentDiscoveryResponseEvent import com.vitorpamplona.quartz.nip90Dvms.contentDiscoveryResponse.DvmContentDiscoveryResponseEvent
@Composable @Composable
fun RenderNIP90ContentDiscoveryResponse( fun RenderNIP90ContentDiscoveryResponse(
@@ -47,7 +47,7 @@ fun RenderNIP90ContentDiscoveryResponse(
accountViewModel: AccountViewModel, accountViewModel: AccountViewModel,
nav: INav, nav: INav,
) { ) {
val noteEvent = note.event as? NIP90ContentDiscoveryResponseEvent ?: return val noteEvent = note.event as? DvmContentDiscoveryResponseEvent ?: return
val callbackUri = remember(note) { note.toNostrUri() } val callbackUri = remember(note) { note.toNostrUri() }
SensitivityWarning( SensitivityWarning(
@@ -25,7 +25,7 @@ import androidx.compose.runtime.Composable
import com.vitorpamplona.amethyst.commons.model.Note import com.vitorpamplona.amethyst.commons.model.Note
import com.vitorpamplona.amethyst.commons.ui.navigation.navs.INav import com.vitorpamplona.amethyst.commons.ui.navigation.navs.INav
import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountViewModel import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountViewModel
import com.vitorpamplona.quartz.nip90Dvms.status.NIP90StatusEvent import com.vitorpamplona.quartz.nip90Dvms.status.DvmStatusEvent
@Composable @Composable
fun RenderNIP90Status( fun RenderNIP90Status(
@@ -33,7 +33,7 @@ fun RenderNIP90Status(
accountViewModel: AccountViewModel, accountViewModel: AccountViewModel,
nav: INav, nav: INav,
) { ) {
val noteEvent = note.event as? NIP90StatusEvent ?: return val noteEvent = note.event as? DvmStatusEvent ?: return
Text(text = noteEvent.content) Text(text = noteEvent.content)
} }
@@ -56,7 +56,7 @@ import com.vitorpamplona.amethyst.ui.note.UserCompose
import com.vitorpamplona.amethyst.ui.note.getGradient import com.vitorpamplona.amethyst.ui.note.getGradient
import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountViewModel import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountViewModel
import com.vitorpamplona.quartz.nip01Core.tags.people.taggedUserIds import com.vitorpamplona.quartz.nip01Core.tags.people.taggedUserIds
import com.vitorpamplona.quartz.nip51Lists.peopleList.PeopleListEvent import com.vitorpamplona.quartz.nip51Lists.peopleList.FollowSetEvent
import kotlinx.collections.immutable.ImmutableList import kotlinx.collections.immutable.ImmutableList
import kotlinx.collections.immutable.persistentListOf import kotlinx.collections.immutable.persistentListOf
@@ -68,7 +68,7 @@ fun DisplayPeopleList(
accountViewModel: AccountViewModel, accountViewModel: AccountViewModel,
nav: INav, nav: INav,
) { ) {
val noteEvent = baseNote.event as? PeopleListEvent ?: return val noteEvent = baseNote.event as? FollowSetEvent ?: return
var members by remember { mutableStateOf<ImmutableList<User>>(persistentListOf()) } var members by remember { mutableStateOf<ImmutableList<User>>(persistentListOf()) }
@@ -53,7 +53,7 @@ import com.vitorpamplona.amethyst.ui.note.elements.DisplayUncitedHashtags
import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountViewModel import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountViewModel
import com.vitorpamplona.amethyst.ui.screen.loggedIn.chats.privateDM.header.ChatroomHeader import com.vitorpamplona.amethyst.ui.screen.loggedIn.chats.privateDM.header.ChatroomHeader
import com.vitorpamplona.quartz.nip01Core.tags.hashtags.hasHashtags import com.vitorpamplona.quartz.nip01Core.tags.hashtags.hasHashtags
import com.vitorpamplona.quartz.nip04Dm.messages.PrivateDmEvent import com.vitorpamplona.quartz.nip04Dm.messages.EncryptedDmEvent
import com.vitorpamplona.quartz.nip17Dm.base.ChatroomKeyable import com.vitorpamplona.quartz.nip17Dm.base.ChatroomKeyable
import com.vitorpamplona.quartz.nip19Bech32.toNpub import com.vitorpamplona.quartz.nip19Bech32.toNpub
@@ -67,7 +67,7 @@ fun RenderPrivateMessage(
accountViewModel: AccountViewModel, accountViewModel: AccountViewModel,
nav: INav, nav: INav,
) { ) {
val noteEvent = note.event as? PrivateDmEvent ?: return val noteEvent = note.event as? EncryptedDmEvent ?: return
val userRoom by val userRoom by
remember(note) { remember(note) {
@@ -80,8 +80,8 @@ import com.vitorpamplona.quartz.experimental.publications.PublicationContentEven
import com.vitorpamplona.quartz.experimental.publications.PublicationIndexEvent import com.vitorpamplona.quartz.experimental.publications.PublicationIndexEvent
import com.vitorpamplona.quartz.experimental.publications.PublicationSectionRef import com.vitorpamplona.quartz.experimental.publications.PublicationSectionRef
import com.vitorpamplona.quartz.nip01Core.core.Event import com.vitorpamplona.quartz.nip01Core.core.Event
import com.vitorpamplona.quartz.nip23LongContent.LongTextNoteEvent import com.vitorpamplona.quartz.nip23LongContent.LongFormContentEvent
import com.vitorpamplona.quartz.nip54Wiki.WikiNoteEvent import com.vitorpamplona.quartz.nip54Wiki.WikiArticleEvent
import kotlinx.collections.immutable.toImmutableList import kotlinx.collections.immutable.toImmutableList
// A 2:3 portrait cover — a book jacket, which is what NKBIP-01's default `book` type is. The // A 2:3 portrait cover — a book jacket, which is what NKBIP-01's default `book` type is. The
@@ -355,8 +355,8 @@ private fun ObservedSectionRow(
// list long-form, wiki and spec events as sections too. // list long-form, wiki and spec events as sections too.
is PublicationIndexEvent -> event.titleOrIdentifier() is PublicationIndexEvent -> event.titleOrIdentifier()
is PublicationContentEvent -> event.titleOrIdentifier() is PublicationContentEvent -> event.titleOrIdentifier()
is LongTextNoteEvent -> event.title() is LongFormContentEvent -> event.title()
is WikiNoteEvent -> event.title() is WikiArticleEvent -> event.title()
// A bookshelf directory lists whatever it likes, including another directory, so the // A bookshelf directory lists whatever it likes, including another directory, so the
// row has to name the library kinds too -- otherwise a nested entry falls through to // row has to name the library kinds too -- otherwise a nested entry falls through to
// the section placeholder and reads "Untitled section", which it is not. // the section placeholder and reads "Untitled section", which it is not.
@@ -67,7 +67,7 @@ import com.vitorpamplona.quartz.experimental.publications.PublicationIndexEvent
import com.vitorpamplona.quartz.experimental.publications.PublicationSectionRef import com.vitorpamplona.quartz.experimental.publications.PublicationSectionRef
import com.vitorpamplona.quartz.nip19Bech32.entities.NAddress import com.vitorpamplona.quartz.nip19Bech32.entities.NAddress
import com.vitorpamplona.quartz.nip19Bech32.entities.NEvent import com.vitorpamplona.quartz.nip19Bech32.entities.NEvent
import com.vitorpamplona.quartz.nip54Wiki.WikiNoteEvent import com.vitorpamplona.quartz.nip54Wiki.WikiArticleEvent
/** /**
* Renders a kind-30041 NKBIP-01 publication section — a chapter, zettel or episode. * Renders a kind-30041 NKBIP-01 publication section — a chapter, zettel or episode.
@@ -331,12 +331,12 @@ private fun wikilinkResolver(event: PublicationContentEvent): (String) -> String
link == null -> null link == null -> null
link.eventId != null -> link.eventId != null ->
"nostr:" + NEvent.create(link.eventId!!, link.pubKey, WikiNoteEvent.KIND, link.relay) "nostr:" + NEvent.create(link.eventId!!, link.pubKey, WikiArticleEvent.KIND, link.relay)
link.pubKey != null -> link.pubKey != null ->
"nostr:" + "nostr:" +
NAddress.create( NAddress.create(
WikiNoteEvent.KIND, WikiArticleEvent.KIND,
link.pubKey!!, link.pubKey!!,
PublicationContentEvent.normalizeWikilink(link.target), PublicationContentEvent.normalizeWikilink(link.target),
link.relay, link.relay,
@@ -70,7 +70,7 @@ import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountViewModel
import com.vitorpamplona.quartz.nip01Core.core.firstTagValueFor import com.vitorpamplona.quartz.nip01Core.core.firstTagValueFor
import com.vitorpamplona.quartz.nip01Core.relay.normalizer.NormalizedRelayUrl import com.vitorpamplona.quartz.nip01Core.relay.normalizer.NormalizedRelayUrl
import com.vitorpamplona.quartz.nip01Core.relay.normalizer.displayUrl import com.vitorpamplona.quartz.nip01Core.relay.normalizer.displayUrl
import com.vitorpamplona.quartz.nip17Dm.settings.ChatMessageRelayListEvent import com.vitorpamplona.quartz.nip17Dm.settings.DmRelayListEvent
import com.vitorpamplona.quartz.nip51Lists.relaySets.RelaySetEvent import com.vitorpamplona.quartz.nip51Lists.relaySets.RelaySetEvent
import com.vitorpamplona.quartz.nip65RelayList.AdvertisedRelayListEvent import com.vitorpamplona.quartz.nip65RelayList.AdvertisedRelayListEvent
import kotlinx.collections.immutable.toImmutableList import kotlinx.collections.immutable.toImmutableList
@@ -158,7 +158,7 @@ fun DisplayDMRelayList(
accountViewModel: AccountViewModel, accountViewModel: AccountViewModel,
nav: INav, nav: INav,
) { ) {
val noteEvent = baseNote.event as? ChatMessageRelayListEvent ?: return val noteEvent = baseNote.event as? DmRelayListEvent ?: return
val relays = val relays =
remember(noteEvent) { remember(noteEvent) {
@@ -96,12 +96,11 @@ import com.vitorpamplona.amethyst.ui.note.elements.MoreOptionsButton
import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountViewModel import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountViewModel
import com.vitorpamplona.quartz.experimental.nip82SoftwareApps.application.SoftwareApplicationEvent import com.vitorpamplona.quartz.experimental.nip82SoftwareApps.application.SoftwareApplicationEvent
import com.vitorpamplona.quartz.experimental.nip82SoftwareApps.asset.SoftwareAssetEvent import com.vitorpamplona.quartz.experimental.nip82SoftwareApps.asset.SoftwareAssetEvent
import com.vitorpamplona.quartz.experimental.nip82SoftwareApps.release.SoftwareReleaseEvent
import com.vitorpamplona.quartz.experimental.nip82SoftwareApps.release.asSoftwareRelease
import com.vitorpamplona.quartz.experimental.nip82SoftwareApps.release.isNip82SoftwareRelease import com.vitorpamplona.quartz.experimental.nip82SoftwareApps.release.isNip82SoftwareRelease
import com.vitorpamplona.quartz.experimental.nip82SoftwareApps.release.tags.AppIdTag import com.vitorpamplona.quartz.experimental.nip82SoftwareApps.release.tags.AppIdTag
import com.vitorpamplona.quartz.nip01Core.relay.filters.Filter import com.vitorpamplona.quartz.nip01Core.relay.filters.Filter
import com.vitorpamplona.quartz.nip01Core.tags.dTag.dTag import com.vitorpamplona.quartz.nip01Core.tags.dTag.dTag
import com.vitorpamplona.quartz.nip51Lists.releaseArtifactSet.ReleaseArtifactSetEvent
import kotlinx.collections.immutable.toImmutableList import kotlinx.collections.immutable.toImmutableList
import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.flow.distinctUntilChanged import kotlinx.coroutines.flow.distinctUntilChanged
@@ -209,7 +208,7 @@ fun RenderSoftwareApplication(
} }
/** /**
* Latest NIP-82 [SoftwareReleaseEvent] version for [app], kept live. * Latest NIP-82 [ReleaseArtifactSetEvent] version for [app], kept live.
* *
* Releases (kind 30063) are separate events that point back to the app via an * Releases (kind 30063) are separate events that point back to the app via an
* `i` tag rather than an `a` tag, so they are never indexed as replies to the * `i` tag rather than an `a` tag, so they are never indexed as replies to the
@@ -230,7 +229,7 @@ fun produceLatestReleaseVersion(app: SoftwareApplicationEvent): State<String?> {
remember(app.pubKey, app.id) { remember(app.pubKey, app.id) {
val filter = val filter =
Filter( Filter(
kinds = listOf(SoftwareReleaseEvent.KIND), kinds = listOf(ReleaseArtifactSetEvent.KIND),
authors = listOf(app.pubKey), authors = listOf(app.pubKey),
tags = mapOf(AppIdTag.TAG_NAME to listOf(app.appId())), tags = mapOf(AppIdTag.TAG_NAME to listOf(app.appId())),
) )
@@ -243,13 +242,13 @@ fun produceLatestReleaseVersion(app: SoftwareApplicationEvent): State<String?> {
return flow.collectAsStateWithLifecycle(initialValue = null) return flow.collectAsStateWithLifecycle(initialValue = null)
} }
fun findLatestNip82Release(app: SoftwareApplicationEvent): SoftwareReleaseEvent? = latestNip82Release(nip82ReleaseNotesFor(app), app) fun findLatestNip82Release(app: SoftwareApplicationEvent): ReleaseArtifactSetEvent? = latestNip82Release(nip82ReleaseNotesFor(app), app)
/** Picks the newest NIP-82 release for [app] out of an already-narrowed [notes] collection. */ /** Picks the newest NIP-82 release for [app] out of an already-narrowed [notes] collection. */
private fun latestNip82Release( private fun latestNip82Release(
notes: Collection<Note>, notes: Collection<Note>,
app: SoftwareApplicationEvent, app: SoftwareApplicationEvent,
): SoftwareReleaseEvent? { ): ReleaseArtifactSetEvent? {
val prefix = "${app.dTag()}@" val prefix = "${app.dTag()}@"
return notes return notes
.mapNotNull { it.asNip82ReleaseFor(prefix) } .mapNotNull { it.asNip82ReleaseFor(prefix) }
@@ -259,25 +258,22 @@ private fun latestNip82Release(
/** kind-30063 addressables authored by [app] whose `d` tag is `<app-id>@<version>`. */ /** kind-30063 addressables authored by [app] whose `d` tag is `<app-id>@<version>`. */
private fun nip82ReleaseNotesFor(app: SoftwareApplicationEvent): Set<Note> { private fun nip82ReleaseNotesFor(app: SoftwareApplicationEvent): Set<Note> {
val prefix = "${app.dTag()}@" val prefix = "${app.dTag()}@"
return LocalCache.addressables.filterIntoSet(SoftwareReleaseEvent.KIND, app.pubKey) { _, addr -> return LocalCache.addressables.filterIntoSet(ReleaseArtifactSetEvent.KIND, app.pubKey) { _, addr ->
val ev = addr.event ?: return@filterIntoSet false val ev = addr.event ?: return@filterIntoSet false
ev.isNip82SoftwareRelease() && ev.dTag().startsWith(prefix) ev.isNip82SoftwareRelease() && ev.dTag().startsWith(prefix)
} }
} }
/** /**
* Re-reads this note as the NIP-82 release for [prefix] (`<app-id>@`). kind 30063 * This note as the NIP-82 release for [prefix] (`<app-id>@`). kind 30063 is shared
* collides with NIP-51 `ReleaseArtifactSetEvent`, which is what `EventFactory` * with NIP-51 release artifact sets, so only events carrying the NIP-82 tags qualify.
* builds, so we re-parse matching tag arrays as the NIP-82 form.
*/ */
private fun Note.asNip82ReleaseFor(prefix: String): SoftwareReleaseEvent? = private fun Note.asNip82ReleaseFor(prefix: String): ReleaseArtifactSetEvent? {
when (val ev = event) { val ev = event as? ReleaseArtifactSetEvent ?: return null
null -> null return ev.takeIf { it.isNip82SoftwareRelease() && it.dTag().startsWith(prefix) }
is SoftwareReleaseEvent -> ev.takeIf { it.dTag().startsWith(prefix) } }
else -> if (ev.isNip82SoftwareRelease() && ev.dTag().startsWith(prefix)) ev.asSoftwareRelease() else null
}
fun findAllNip82Releases(app: SoftwareApplicationEvent): List<SoftwareReleaseEvent> { fun findAllNip82Releases(app: SoftwareApplicationEvent): List<ReleaseArtifactSetEvent> {
val prefix = "${app.dTag()}@" val prefix = "${app.dTag()}@"
return nip82ReleaseNotesFor(app) return nip82ReleaseNotesFor(app)
.mapNotNull { it.asNip82ReleaseFor(prefix) } .mapNotNull { it.asNip82ReleaseFor(prefix) }
@@ -486,25 +482,17 @@ fun RenderSoftwareRelease(
accountViewModel: AccountViewModel, accountViewModel: AccountViewModel,
nav: INav, nav: INav,
) { ) {
// kind 30063 is shared between NIP-51 ReleaseArtifactSetEvent and NIP-82 SoftwareReleaseEvent; // kind 30063 is shared between NIP-51 release artifact sets and NIP-82 software
// EventFactory always returns the NIP-51 class, so we re-parse the same tag array as the // releases; only render the NIP-82 form here.
// NIP-82 form when the tag signature matches. val event = note.event as? ReleaseArtifactSetEvent ?: return
val rawEvent = note.event ?: return if (!event.isNip82SoftwareRelease()) return
val event: SoftwareReleaseEvent =
if (rawEvent is SoftwareReleaseEvent) {
rawEvent
} else if (rawEvent.isNip82SoftwareRelease()) {
rawEvent.asSoftwareRelease()
} else {
return
}
RenderSoftwareReleaseBody(event = event, accountViewModel = accountViewModel, nav = nav) RenderSoftwareReleaseBody(event = event, accountViewModel = accountViewModel, nav = nav)
} }
@Composable @Composable
fun RenderSoftwareReleaseBody( fun RenderSoftwareReleaseBody(
event: SoftwareReleaseEvent, event: ReleaseArtifactSetEvent,
accountViewModel: AccountViewModel, accountViewModel: AccountViewModel,
nav: INav, nav: INav,
showAppId: Boolean = true, showAppId: Boolean = true,
@@ -60,7 +60,7 @@ import com.vitorpamplona.quartz.nip10Notes.BaseThreadedEvent
import com.vitorpamplona.quartz.nip10Notes.TextNoteEvent import com.vitorpamplona.quartz.nip10Notes.TextNoteEvent
import com.vitorpamplona.quartz.nip14Subject.subject import com.vitorpamplona.quartz.nip14Subject.subject
import com.vitorpamplona.quartz.nip22Comments.CommentEvent import com.vitorpamplona.quartz.nip22Comments.CommentEvent
import com.vitorpamplona.quartz.nip57Zaps.LnZapEvent import com.vitorpamplona.quartz.nip57Zaps.ZapReceiptEvent
enum class ReplyRenderType { enum class ReplyRenderType {
FULL, FULL,
@@ -129,7 +129,7 @@ fun RenderTextEvent(
// Zap receipts are signed by the recipient's lightning provider; // Zap receipts are signed by the recipient's lightning provider;
// label the reply with the zap sender instead of the service key. // label the reply with the zap sender instead of the service key.
val zapSender = val zapSender =
if (parentNote.event is LnZapEvent) { if (parentNote.event is ZapReceiptEvent) {
observeZapSender(parentNote, accountViewModel).value observeZapSender(parentNote, accountViewModel).value
} else { } else {
null null
@@ -50,7 +50,7 @@ import com.vitorpamplona.amethyst.ui.components.MyAsyncImage
import com.vitorpamplona.amethyst.ui.note.elements.DefaultImageHeader import com.vitorpamplona.amethyst.ui.note.elements.DefaultImageHeader
import com.vitorpamplona.amethyst.ui.note.elements.DefaultImageHeaderBackground import com.vitorpamplona.amethyst.ui.note.elements.DefaultImageHeaderBackground
import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountViewModel import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountViewModel
import com.vitorpamplona.quartz.nip54Wiki.WikiNoteEvent import com.vitorpamplona.quartz.nip54Wiki.WikiArticleEvent
@Composable @Composable
fun RenderWikiContent( fun RenderWikiContent(
@@ -58,14 +58,14 @@ fun RenderWikiContent(
accountViewModel: AccountViewModel, accountViewModel: AccountViewModel,
nav: INav, nav: INav,
) { ) {
val noteEvent = note.event as? WikiNoteEvent ?: return val noteEvent = note.event as? WikiArticleEvent ?: return
WikiNoteHeader(noteEvent, note, accountViewModel, nav) WikiNoteHeader(noteEvent, note, accountViewModel, nav)
} }
@Composable @Composable
private fun WikiNoteHeader( private fun WikiNoteHeader(
noteEvent: WikiNoteEvent, noteEvent: WikiArticleEvent,
note: Note, note: Note,
accountViewModel: AccountViewModel, accountViewModel: AccountViewModel,
nav: INav, nav: INav,
@@ -55,9 +55,9 @@ import com.vitorpamplona.amethyst.ui.components.TranslatableRichTextViewer
import com.vitorpamplona.amethyst.ui.note.LoadAddressableNote import com.vitorpamplona.amethyst.ui.note.LoadAddressableNote
import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountViewModel import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountViewModel
import com.vitorpamplona.quartz.nip01Core.core.Address import com.vitorpamplona.quartz.nip01Core.core.Address
import com.vitorpamplona.quartz.nip54Wiki.WikiArticleEvent
import com.vitorpamplona.quartz.nip54Wiki.WikiMergeAcceptanceEvent import com.vitorpamplona.quartz.nip54Wiki.WikiMergeAcceptanceEvent
import com.vitorpamplona.quartz.nip54Wiki.WikiMergeRequestEvent import com.vitorpamplona.quartz.nip54Wiki.WikiMergeRequestEvent
import com.vitorpamplona.quartz.nip54Wiki.WikiNoteEvent
import com.vitorpamplona.quartz.nip54Wiki.WikiRedirectEvent import com.vitorpamplona.quartz.nip54Wiki.WikiRedirectEvent
/** /**
@@ -215,7 +215,7 @@ private fun WikiArticleRow(
) { ) {
LoadAddressableNote(address) { articleNote -> LoadAddressableNote(address) { articleNote ->
if (articleNote != null) { if (articleNote != null) {
val article by observeNoteEvent<WikiNoteEvent>(articleNote, accountViewModel) val article by observeNoteEvent<WikiArticleEvent>(articleNote, accountViewModel)
WikiRow( WikiRow(
// The coordinate's own identifier is a readable slug, so it stands in until the // The coordinate's own identifier is a readable slug, so it stands in until the
@@ -240,7 +240,7 @@ private fun WikiVersionRow(
) { ) {
LoadNote(eventId) { versionNote -> LoadNote(eventId) { versionNote ->
if (versionNote != null) { if (versionNote != null) {
val article by observeNoteEvent<WikiNoteEvent>(versionNote, accountViewModel) val article by observeNoteEvent<WikiArticleEvent>(versionNote, accountViewModel)
WikiRow( WikiRow(
title = article?.title() ?: eventId.take(8), title = article?.title() ?: eventId.take(8),
@@ -64,7 +64,7 @@ import com.vitorpamplona.amethyst.ui.note.UserPicture
import com.vitorpamplona.amethyst.ui.note.ZapAmountCommentNotification import com.vitorpamplona.amethyst.ui.note.ZapAmountCommentNotification
import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountViewModel import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountViewModel
import com.vitorpamplona.amethyst.ui.screen.loggedIn.mockAccountViewModel import com.vitorpamplona.amethyst.ui.screen.loggedIn.mockAccountViewModel
import com.vitorpamplona.quartz.nip57Zaps.LnZapEvent import com.vitorpamplona.quartz.nip57Zaps.ZapReceiptEvent
import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.runBlocking import kotlinx.coroutines.runBlocking
import kotlinx.coroutines.withContext import kotlinx.coroutines.withContext
@@ -104,7 +104,7 @@ fun RenderLnZap(
accountViewModel: AccountViewModel, accountViewModel: AccountViewModel,
nav: INav, nav: INav,
) { ) {
val zapEvent = note.event as? LnZapEvent ?: return val zapEvent = note.event as? ZapReceiptEvent ?: return
val card by parseAuthorCommentAndAmount(note, accountViewModel) val card by parseAuthorCommentAndAmount(note, accountViewModel)
@@ -38,7 +38,7 @@ import com.vitorpamplona.quartz.nip51Lists.articleCurationSet.ArticleCurationSet
import com.vitorpamplona.quartz.nip51Lists.bookmarkList.BookmarkListEvent import com.vitorpamplona.quartz.nip51Lists.bookmarkList.BookmarkListEvent
import com.vitorpamplona.quartz.nip51Lists.bookmarkList.OldBookmarkListEvent import com.vitorpamplona.quartz.nip51Lists.bookmarkList.OldBookmarkListEvent
import com.vitorpamplona.quartz.nip51Lists.bookmarkList.tags.BookmarkIdTag import com.vitorpamplona.quartz.nip51Lists.bookmarkList.tags.BookmarkIdTag
import com.vitorpamplona.quartz.nip51Lists.labeledBookmarkList.LabeledBookmarkListEvent import com.vitorpamplona.quartz.nip51Lists.labeledBookmarkList.BookmarkSetEvent
import com.vitorpamplona.quartz.nip51Lists.pictureCurationSet.PictureCurationSetEvent import com.vitorpamplona.quartz.nip51Lists.pictureCurationSet.PictureCurationSetEvent
/** NIP-51 kind 10003: the user's bookmarks — notes, articles, hashtags and links. */ /** NIP-51 kind 10003: the user's bookmarks — notes, articles, hashtags and links. */
@@ -108,7 +108,7 @@ fun RenderLabeledBookmarkList(
accountViewModel: AccountViewModel, accountViewModel: AccountViewModel,
nav: INav, nav: INav,
) { ) {
val noteEvent = baseNote.event as? LabeledBookmarkListEvent ?: return val noteEvent = baseNote.event as? BookmarkSetEvent ?: return
val public = remember(noteEvent) { noteEvent.publicBookmarks() } val public = remember(noteEvent) { noteEvent.publicBookmarks() }
val private by loadPrivateItems(noteEvent, accountViewModel) { noteEvent.privateBookmarks(it) } val private by loadPrivateItems(noteEvent, accountViewModel) { noteEvent.privateBookmarks(it) }
@@ -36,7 +36,7 @@ import com.vitorpamplona.amethyst.commons.ui.stringRes
import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountViewModel import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountViewModel
import com.vitorpamplona.quartz.nip01Core.tags.hashtags.HashtagTag import com.vitorpamplona.quartz.nip01Core.tags.hashtags.HashtagTag
import com.vitorpamplona.quartz.nip51Lists.geohashList.GeohashListEvent import com.vitorpamplona.quartz.nip51Lists.geohashList.GeohashListEvent
import com.vitorpamplona.quartz.nip51Lists.hashtagList.HashtagListEvent import com.vitorpamplona.quartz.nip51Lists.hashtagList.InterestListEvent
import com.vitorpamplona.quartz.nip51Lists.interestSet.InterestSetEvent import com.vitorpamplona.quartz.nip51Lists.interestSet.InterestSetEvent
/** NIP-51 kind 10015: the hashtags this user follows. */ /** NIP-51 kind 10015: the hashtags this user follows. */
@@ -47,10 +47,10 @@ fun RenderHashtagList(
accountViewModel: AccountViewModel, accountViewModel: AccountViewModel,
nav: INav, nav: INav,
) { ) {
val noteEvent = baseNote.event as? HashtagListEvent ?: return val noteEvent = baseNote.event as? InterestListEvent ?: return
val public = remember(noteEvent) { noteEvent.publicHashtags() } val public = remember(noteEvent) { noteEvent.publicHashtags() }
// HashtagListEvent exposes no private accessor of its own, but it is a PrivateTagArrayEvent // InterestListEvent exposes no private accessor of its own, but it is a PrivateTagArrayEvent
// like the rest, so its private half is read the same way InterestSetEvent reads its own. // like the rest, so its private half is read the same way InterestSetEvent reads its own.
val private by loadPrivateItems(noteEvent, accountViewModel) { signer -> val private by loadPrivateItems(noteEvent, accountViewModel) { signer ->
noteEvent.privateTags(signer)?.mapNotNull(HashtagTag::parse) noteEvent.privateTags(signer)?.mapNotNull(HashtagTag::parse)
@@ -43,9 +43,9 @@ import com.vitorpamplona.amethyst.service.checkNotInMainThread
import com.vitorpamplona.quartz.nip01Core.relay.normalizer.NormalizedRelayUrl import com.vitorpamplona.quartz.nip01Core.relay.normalizer.NormalizedRelayUrl
import com.vitorpamplona.quartz.nip01Core.relay.normalizer.RelayUrlNormalizer import com.vitorpamplona.quartz.nip01Core.relay.normalizer.RelayUrlNormalizer
import com.vitorpamplona.quartz.nip01Core.relay.normalizer.displayUrl import com.vitorpamplona.quartz.nip01Core.relay.normalizer.displayUrl
import com.vitorpamplona.quartz.nip51Lists.followList.FollowListEvent import com.vitorpamplona.quartz.nip51Lists.followList.StarterPackEvent
import com.vitorpamplona.quartz.nip51Lists.interestSet.InterestSetEvent import com.vitorpamplona.quartz.nip51Lists.interestSet.InterestSetEvent
import com.vitorpamplona.quartz.nip51Lists.peopleList.PeopleListEvent import com.vitorpamplona.quartz.nip51Lists.peopleList.FollowSetEvent
import com.vitorpamplona.quartz.nip72ModCommunities.definition.CommunityDefinitionEvent import com.vitorpamplona.quartz.nip72ModCommunities.definition.CommunityDefinitionEvent
import com.vitorpamplona.quartz.nip89AppHandlers.definition.AppDefinitionEvent import com.vitorpamplona.quartz.nip89AppHandlers.definition.AppDefinitionEvent
import com.vitorpamplona.quartz.utils.Log import com.vitorpamplona.quartz.utils.Log
@@ -594,9 +594,9 @@ class PeopleListName(
NoteBackedName { NoteBackedName {
override fun name(): String { override fun name(): String {
val noteEvent = note.event val noteEvent = note.event
return if (noteEvent is PeopleListEvent) { return if (noteEvent is FollowSetEvent) {
noteEvent.titleOrName() ?: note.dTag() noteEvent.titleOrName() ?: note.dTag()
} else if (noteEvent is FollowListEvent) { } else if (noteEvent is StarterPackEvent) {
noteEvent.title() ?: note.dTag() noteEvent.title() ?: note.dTag()
} else { } else {
note.dTag() note.dTag()
@@ -172,7 +172,7 @@ import com.vitorpamplona.quartz.nip01Core.tags.people.PubKeyReferenceTag
import com.vitorpamplona.quartz.nip01Core.tags.people.isTaggedUser import com.vitorpamplona.quartz.nip01Core.tags.people.isTaggedUser
import com.vitorpamplona.quartz.nip02FollowList.ContactListEvent import com.vitorpamplona.quartz.nip02FollowList.ContactListEvent
import com.vitorpamplona.quartz.nip03Timestamp.EmptyOtsResolverBuilder import com.vitorpamplona.quartz.nip03Timestamp.EmptyOtsResolverBuilder
import com.vitorpamplona.quartz.nip04Dm.messages.PrivateDmEvent import com.vitorpamplona.quartz.nip04Dm.messages.EncryptedDmEvent
import com.vitorpamplona.quartz.nip05DnsIdentifiers.EmptyNip05Client import com.vitorpamplona.quartz.nip05DnsIdentifiers.EmptyNip05Client
import com.vitorpamplona.quartz.nip05DnsIdentifiers.INip05Client import com.vitorpamplona.quartz.nip05DnsIdentifiers.INip05Client
import com.vitorpamplona.quartz.nip05DnsIdentifiers.Nip05Client import com.vitorpamplona.quartz.nip05DnsIdentifiers.Nip05Client
@@ -202,16 +202,16 @@ import com.vitorpamplona.quartz.nip51Lists.PinListEvent
import com.vitorpamplona.quartz.nip51Lists.bookmarkList.BookmarkListEvent import com.vitorpamplona.quartz.nip51Lists.bookmarkList.BookmarkListEvent
import com.vitorpamplona.quartz.nip51Lists.bookmarkList.OldBookmarkListEvent import com.vitorpamplona.quartz.nip51Lists.bookmarkList.OldBookmarkListEvent
import com.vitorpamplona.quartz.nip51Lists.bookmarkList.tags.AddressBookmark import com.vitorpamplona.quartz.nip51Lists.bookmarkList.tags.AddressBookmark
import com.vitorpamplona.quartz.nip51Lists.hashtagList.HashtagListEvent import com.vitorpamplona.quartz.nip51Lists.hashtagList.InterestListEvent
import com.vitorpamplona.quartz.nip56Reports.ReportType import com.vitorpamplona.quartz.nip56Reports.ReportType
import com.vitorpamplona.quartz.nip57Zaps.LnZapEvent import com.vitorpamplona.quartz.nip57Zaps.ZapReceiptEvent
import com.vitorpamplona.quartz.nip57Zaps.LnZapRequestEvent import com.vitorpamplona.quartz.nip57Zaps.ZapRequestEvent
import com.vitorpamplona.quartz.nip57Zaps.validate.LnurlForm import com.vitorpamplona.quartz.nip57Zaps.validate.LnurlForm
import com.vitorpamplona.quartz.nip57Zaps.zapraiser.zapraiserAmount import com.vitorpamplona.quartz.nip57Zaps.zapraiser.zapraiserAmount
import com.vitorpamplona.quartz.nip59Giftwrap.seals.SealedRumorEvent import com.vitorpamplona.quartz.nip59Giftwrap.seals.SealEvent
import com.vitorpamplona.quartz.nip59Giftwrap.wraps.GiftWrapEvent import com.vitorpamplona.quartz.nip59Giftwrap.wraps.GiftWrapEvent
import com.vitorpamplona.quartz.nip60Cashu.token.CashuToken import com.vitorpamplona.quartz.nip60Cashu.token.CashuToken
import com.vitorpamplona.quartz.nip90Dvms.contentDiscoveryResponse.NIP90ContentDiscoveryResponseEvent import com.vitorpamplona.quartz.nip90Dvms.contentDiscoveryResponse.DvmContentDiscoveryResponseEvent
import com.vitorpamplona.quartz.nip92IMeta.imeta import com.vitorpamplona.quartz.nip92IMeta.imeta
import com.vitorpamplona.quartz.nip94FileMetadata.tags.DimensionTag import com.vitorpamplona.quartz.nip94FileMetadata.tags.DimensionTag
import com.vitorpamplona.quartz.podcasts.PodcastBoostagram import com.vitorpamplona.quartz.podcasts.PodcastBoostagram
@@ -725,7 +725,7 @@ class AccountViewModel(
val isHiddenAuthor = note.author?.let { account.isHidden(it) } == true val isHiddenAuthor = note.author?.let { account.isHidden(it) } == true
val noteEvent = note.event val noteEvent = note.event
val isDecryptedPostHidden = if (noteEvent is PrivateDmEvent) account.isDecryptedContentHidden(noteEvent) else false val isDecryptedPostHidden = if (noteEvent is EncryptedDmEvent) account.isDecryptedContentHidden(noteEvent) else false
return if (isPostHidden || isDecryptedPostHidden) { return if (isPostHidden || isDecryptedPostHidden) {
// Spam + Blocked Users + Hidden Words + Sensitive Content // Spam + Blocked Users + Hidden Words + Sensitive Content
@@ -924,14 +924,14 @@ class AccountViewModel(
it.request.event it.request.event
?.content ?.content
?.ifBlank { null }, ?.ifBlank { null },
showAmountInteger((it.response.event as? LnZapEvent)?.amount), showAmountInteger((it.response.event as? ZapReceiptEvent)?.amount),
it.response, it.response,
) )
}.toMutableMap() }.toMutableMap()
val results = val results =
mapNotNullAsync( mapNotNullAsync(
zaps.filter { (it.request.event as? LnZapRequestEvent)?.isPrivateZap() == true }, zaps.filter { (it.request.event as? ZapRequestEvent)?.isPrivateZap() == true },
) { next -> ) { next ->
val info = innerDecryptAmountMessage(next.request, next.response) val info = innerDecryptAmountMessage(next.request, next.response)
if (info != null) { if (info != null) {
@@ -950,14 +950,14 @@ class AccountViewModel(
fun cachedDecryptAmountMessageInGroup(zapNotes: List<CombinedZap>): ImmutableList<ZapAmountCommentNotification> = fun cachedDecryptAmountMessageInGroup(zapNotes: List<CombinedZap>): ImmutableList<ZapAmountCommentNotification> =
zapNotes zapNotes
.map { .map {
val request = it.request.event as? LnZapRequestEvent val request = it.request.event as? ZapRequestEvent
if (request?.isPrivateZap() == true) { if (request?.isPrivateZap() == true) {
val cachedPrivateRequest = account.privateZapsDecryptionCache.cachedPrivateZap(request) val cachedPrivateRequest = account.privateZapsDecryptionCache.cachedPrivateZap(request)
if (cachedPrivateRequest != null) { if (cachedPrivateRequest != null) {
ZapAmountCommentNotification( ZapAmountCommentNotification(
LocalCache.getUserIfExists(cachedPrivateRequest.pubKey) ?: it.request.author, LocalCache.getUserIfExists(cachedPrivateRequest.pubKey) ?: it.request.author,
cachedPrivateRequest.content.ifBlank { null }, cachedPrivateRequest.content.ifBlank { null },
showAmountInteger((it.response.event as? LnZapEvent)?.amount), showAmountInteger((it.response.event as? ZapReceiptEvent)?.amount),
it.response, it.response,
) )
} else { } else {
@@ -966,7 +966,7 @@ class AccountViewModel(
it.request.event it.request.event
?.content ?.content
?.ifBlank { null }, ?.ifBlank { null },
showAmountInteger((it.response.event as? LnZapEvent)?.amount), showAmountInteger((it.response.event as? ZapReceiptEvent)?.amount),
it.response, it.response,
) )
} }
@@ -976,7 +976,7 @@ class AccountViewModel(
it.request.event it.request.event
?.content ?.content
?.ifBlank { null }, ?.ifBlank { null },
showAmountInteger((it.response.event as? LnZapEvent)?.amount), showAmountInteger((it.response.event as? ZapReceiptEvent)?.amount),
it.response, it.response,
) )
} }
@@ -987,14 +987,14 @@ class AccountViewModel(
return myList return myList
.map { .map {
val request = it.first.event as? LnZapRequestEvent val request = it.first.event as? ZapRequestEvent
if (request?.isPrivateZap() == true) { if (request?.isPrivateZap() == true) {
val cachedPrivateRequest = account.privateZapsDecryptionCache.cachedPrivateZap(request) val cachedPrivateRequest = account.privateZapsDecryptionCache.cachedPrivateZap(request)
if (cachedPrivateRequest != null) { if (cachedPrivateRequest != null) {
ZapAmountCommentNotification( ZapAmountCommentNotification(
LocalCache.getUserIfExists(cachedPrivateRequest.pubKey) ?: it.first.author, LocalCache.getUserIfExists(cachedPrivateRequest.pubKey) ?: it.first.author,
cachedPrivateRequest.content.ifBlank { null }, cachedPrivateRequest.content.ifBlank { null },
showAmountInteger((it.second?.event as? LnZapEvent)?.amount), showAmountInteger((it.second?.event as? ZapReceiptEvent)?.amount),
it.second, it.second,
) )
} else { } else {
@@ -1003,7 +1003,7 @@ class AccountViewModel(
it.first.event it.first.event
?.content ?.content
?.ifBlank { null }, ?.ifBlank { null },
showAmountInteger((it.second?.event as? LnZapEvent)?.amount), showAmountInteger((it.second?.event as? ZapReceiptEvent)?.amount),
it.second, it.second,
) )
} }
@@ -1013,7 +1013,7 @@ class AccountViewModel(
it.first.event it.first.event
?.content ?.content
?.ifBlank { null }, ?.ifBlank { null },
showAmountInteger((it.second?.event as? LnZapEvent)?.amount), showAmountInteger((it.second?.event as? ZapReceiptEvent)?.amount),
it.second, it.second,
) )
} }
@@ -1036,7 +1036,7 @@ class AccountViewModel(
it.first.event it.first.event
?.content ?.content
?.ifBlank { null }, ?.ifBlank { null },
showAmountInteger((it.second?.event as? LnZapEvent)?.amount), showAmountInteger((it.second?.event as? ZapReceiptEvent)?.amount),
it.second, it.second,
) )
}.toMutableMap() }.toMutableMap()
@@ -1073,7 +1073,7 @@ class AccountViewModel(
} }
suspend fun innerDecryptAmountMessage(zapNote: Note): ZapAmountCommentNotification? { suspend fun innerDecryptAmountMessage(zapNote: Note): ZapAmountCommentNotification? {
val zapEvent = zapNote.event as? LnZapEvent ?: return null val zapEvent = zapNote.event as? ZapReceiptEvent ?: return null
val zapRequest = zapEvent.zapRequest ?: return null val zapRequest = zapEvent.zapRequest ?: return null
return innerDecryptAmountMessage(zapRequest, zapEvent)?.copy(zapNote = zapNote) return innerDecryptAmountMessage(zapRequest, zapEvent)?.copy(zapNote = zapNote)
@@ -1083,14 +1083,14 @@ class AccountViewModel(
zapRequest: Note, zapRequest: Note,
zapEvent: Note, zapEvent: Note,
): ZapAmountCommentNotification? { ): ZapAmountCommentNotification? {
val zapEvent = zapEvent.event as? LnZapEvent ?: return null val zapEvent = zapEvent.event as? ZapReceiptEvent ?: return null
val zapRequest = zapRequest.event as? LnZapRequestEvent ?: return null val zapRequest = zapRequest.event as? ZapRequestEvent ?: return null
return innerDecryptAmountMessage(zapRequest, zapEvent) return innerDecryptAmountMessage(zapRequest, zapEvent)
} }
suspend fun innerDecryptAmountMessage( suspend fun innerDecryptAmountMessage(
zapRequestEvent: LnZapRequestEvent, zapRequestEvent: ZapRequestEvent,
zapEvent: LnZapEvent, zapEvent: ZapReceiptEvent,
): ZapAmountCommentNotification? { ): ZapAmountCommentNotification? {
val amount = showAmountInteger(zapEvent.amount) val amount = showAmountInteger(zapEvent.amount)
return if (zapRequestEvent.isPrivateZap()) { return if (zapRequestEvent.isPrivateZap()) {
@@ -1148,7 +1148,7 @@ class AccountViewModel(
onError: (String, String, User?) -> Unit, onError: (String, String, User?) -> Unit,
onProgress: (percent: Float) -> Unit, onProgress: (percent: Float) -> Unit,
onPayViaIntent: (ImmutableList<ZapPaymentHandler.Payable>) -> Unit, onPayViaIntent: (ImmutableList<ZapPaymentHandler.Payable>) -> Unit,
zapType: LnZapEvent.ZapType? = null, zapType: ZapReceiptEvent.ZapType? = null,
) = launchSigner { ) = launchSigner {
// A podcast note (episode or show) can carry a Podcasting-2.0 value-for-value block. When it // A podcast note (episode or show) can carry a Podcasting-2.0 value-for-value block. When it
// does, "zapping" it means paying that split — lnaddress recipients go out as real zaps (with // does, "zapping" it means paying that split — lnaddress recipients go out as real zaps (with
@@ -1177,8 +1177,8 @@ class AccountViewModel(
// comment stay encrypted. NONZAP is kept: paying without a zap // comment stay encrypted. NONZAP is kept: paying without a zap
// request produces no receipt at all, which is even more private. // request produces no receipt at all, which is even more private.
val effectiveType = val effectiveType =
if (note.isPrivateRumor() && requestedType != LnZapEvent.ZapType.NONZAP) { if (note.isPrivateRumor() && requestedType != ZapReceiptEvent.ZapType.NONZAP) {
LnZapEvent.ZapType.PRIVATE ZapReceiptEvent.ZapType.PRIVATE
} else { } else {
requestedType requestedType
} }
@@ -1299,7 +1299,7 @@ class AccountViewModel(
zappedNote = zappedNote, zappedNote = zappedNote,
context = context, context = context,
asZap = !streaming, asZap = !streaming,
zapType = LnZapEvent.ZapType.PUBLIC, zapType = ZapReceiptEvent.ZapType.PUBLIC,
okHttpClient = httpClientBuilder::okHttpClientForMoney, okHttpClient = httpClientBuilder::okHttpClientForMoney,
onError = { title, message -> onError = { title, message ->
if (!streaming) toastManager.toast(title, message) if (!streaming) toastManager.toast(title, message)
@@ -1463,7 +1463,7 @@ class AccountViewModel(
fun follows(user: User): Note = LocalCache.getOrCreateAddressableNote(ContactListEvent.createAddress(user.pubkeyHex)) fun follows(user: User): Note = LocalCache.getOrCreateAddressableNote(ContactListEvent.createAddress(user.pubkeyHex))
fun hashtagFollows(user: User): Note = LocalCache.getOrCreateAddressableNote(HashtagListEvent.createAddress(user.pubkeyHex)) fun hashtagFollows(user: User): Note = LocalCache.getOrCreateAddressableNote(InterestListEvent.createAddress(user.pubkeyHex))
fun bookmarks(user: User): Note = LocalCache.getOrCreateAddressableNote(BookmarkListEvent.createBookmarkAddress(user.pubkeyHex)) fun bookmarks(user: User): Note = LocalCache.getOrCreateAddressableNote(BookmarkListEvent.createBookmarkAddress(user.pubkeyHex))
@@ -2122,7 +2122,7 @@ class AccountViewModel(
fun updateZapAmounts( fun updateZapAmounts(
amountSet: List<Long>, amountSet: List<Long>,
selectedZapType: LnZapEvent.ZapType, selectedZapType: ZapReceiptEvent.ZapType,
nip47Update: Nip47WalletConnect.Nip47URINorm?, nip47Update: Nip47WalletConnect.Nip47URINorm?,
) = launchSigner { account.updateZapAmounts(amountSet, selectedZapType, nip47Update) } ) = launchSigner { account.updateZapAmounts(amountSet, selectedZapType, nip47Update) }
@@ -2848,7 +2848,7 @@ class AccountViewModel(
} }
} }
private suspend fun unwrapSeal(event: SealedRumorEvent): Note? { private suspend fun unwrapSeal(event: SealEvent): Note? {
val cacheInnerEventId = event.innerEventId val cacheInnerEventId = event.innerEventId
return if (cacheInnerEventId != null) { return if (cacheInnerEventId != null) {
val existingNoteEvent = LocalCache.getNoteIfExists(cacheInnerEventId)?.event val existingNoteEvent = LocalCache.getNoteIfExists(cacheInnerEventId)?.event
@@ -2882,7 +2882,7 @@ class AccountViewModel(
private suspend fun unwrapIfNeeded(event: Event): Note? = private suspend fun unwrapIfNeeded(event: Event): Note? =
when (event) { when (event) {
is GiftWrapEvent -> unwrapGiftWrap(event) is GiftWrapEvent -> unwrapGiftWrap(event)
is SealedRumorEvent -> unwrapSeal(event) is SealEvent -> unwrapSeal(event)
else -> LocalCache.getNoteIfExists(event.id) else -> LocalCache.getNoteIfExists(event.id)
} }
@@ -2936,7 +2936,7 @@ class AccountViewModel(
LocalCache.notes.maxOrNullOf( LocalCache.notes.maxOrNullOf(
filter = { _, note -> filter = { _, note ->
val noteEvent = note.event val noteEvent = note.event
noteEvent is NIP90ContentDiscoveryResponseEvent && noteEvent is DvmContentDiscoveryResponseEvent &&
noteEvent.pubKey == pubkeyHex && noteEvent.pubKey == pubkeyHex &&
noteEvent.isTaggedUser(account.signer.pubKey) && noteEvent.isTaggedUser(account.signer.pubKey) &&
noteEvent.createdAt > fifteenMinsAgo noteEvent.createdAt > fifteenMinsAgo
@@ -3005,13 +3005,13 @@ class AccountViewModel(
onError: (String, String) -> Unit, onError: (String, String) -> Unit,
onProgress: (percent: Float) -> Unit, onProgress: (percent: Float) -> Unit,
context: Context, context: Context,
zapType: LnZapEvent.ZapType? = null, zapType: ZapReceiptEvent.ZapType? = null,
) { ) {
viewModelScope.launch(Dispatchers.IO) { viewModelScope.launch(Dispatchers.IO) {
try { try {
val effectiveZapType = zapType ?: defaultZapType() val effectiveZapType = zapType ?: defaultZapType()
val zapRequest = val zapRequest =
if (effectiveZapType != LnZapEvent.ZapType.NONZAP) { if (effectiveZapType != ZapReceiptEvent.ZapType.NONZAP) {
// NIP-57 Appendix F: include amount + lnurl so the receipt can be validated. // NIP-57 Appendix F: include amount + lnurl so the receipt can be validated.
val splitLnurl = LnurlForm.toUrl(lnAddress)?.let(LnurlForm::urlToBech32) val splitLnurl = LnurlForm.toUrl(lnAddress)?.let(LnurlForm::urlToBech32)
account.zaps.createZapRequestFor( account.zaps.createZapRequestFor(
@@ -42,10 +42,10 @@ import com.vitorpamplona.quartz.nip17Dm.base.ChatroomKeyable
import com.vitorpamplona.quartz.nip28PublicChat.message.ChannelMessageEvent import com.vitorpamplona.quartz.nip28PublicChat.message.ChannelMessageEvent
import com.vitorpamplona.quartz.nip37Drafts.DraftWrapEvent import com.vitorpamplona.quartz.nip37Drafts.DraftWrapEvent
import com.vitorpamplona.quartz.nip53LiveActivities.chat.LiveActivitiesChatMessageEvent import com.vitorpamplona.quartz.nip53LiveActivities.chat.LiveActivitiesChatMessageEvent
import com.vitorpamplona.quartz.nip57Zaps.LnZapEvent
import com.vitorpamplona.quartz.nip57Zaps.LnZapRequestEvent
import com.vitorpamplona.quartz.nip57Zaps.PrivateZapCache import com.vitorpamplona.quartz.nip57Zaps.PrivateZapCache
import com.vitorpamplona.quartz.nip59Giftwrap.seals.SealedRumorEvent import com.vitorpamplona.quartz.nip57Zaps.ZapReceiptEvent
import com.vitorpamplona.quartz.nip57Zaps.ZapRequestEvent
import com.vitorpamplona.quartz.nip59Giftwrap.seals.SealEvent
import com.vitorpamplona.quartz.nip59Giftwrap.wraps.EphemeralGiftWrapEvent import com.vitorpamplona.quartz.nip59Giftwrap.wraps.EphemeralGiftWrapEvent
import com.vitorpamplona.quartz.nip59Giftwrap.wraps.GiftWrapEvent import com.vitorpamplona.quartz.nip59Giftwrap.wraps.GiftWrapEvent
import com.vitorpamplona.quartz.nipACWebRtcCalls.events.CallAnswerEvent import com.vitorpamplona.quartz.nipACWebRtcCalls.events.CallAnswerEvent
@@ -112,9 +112,9 @@ class EventProcessor(
is GiftWrapEvent -> giftWrapHandler.add(event, eventNote, publicNote) is GiftWrapEvent -> giftWrapHandler.add(event, eventNote, publicNote)
is SealedRumorEvent -> sealHandler.add(event, eventNote, publicNote) is SealEvent -> sealHandler.add(event, eventNote, publicNote)
is LnZapRequestEvent -> zapRequest.add(event, eventNote, publicNote) is ZapRequestEvent -> zapRequest.add(event, eventNote, publicNote)
} }
} }
@@ -153,9 +153,9 @@ class EventProcessor(
is ChatroomKeyable -> chatHandler.delete(event, note) is ChatroomKeyable -> chatHandler.delete(event, note)
is DraftWrapEvent -> draftHandler.delete(event, note) is DraftWrapEvent -> draftHandler.delete(event, note)
is GiftWrapEvent -> giftWrapHandler.delete(event, note) is GiftWrapEvent -> giftWrapHandler.delete(event, note)
is SealedRumorEvent -> sealHandler.delete(event, note) is SealEvent -> sealHandler.delete(event, note)
is LnZapRequestEvent -> zapRequest.delete(event, note) is ZapRequestEvent -> zapRequest.delete(event, note)
is LnZapEvent -> zapEvent.delete(event, note) is ZapReceiptEvent -> zapEvent.delete(event, note)
} }
} }
@@ -457,9 +457,9 @@ class SealedRumorEventHandler(
private val account: Account, private val account: Account,
private val cache: LocalCache, private val cache: LocalCache,
private val eventProcessor: EventProcessor, private val eventProcessor: EventProcessor,
) : EventHandler<SealedRumorEvent> { ) : EventHandler<SealEvent> {
override suspend fun add( override suspend fun add(
event: SealedRumorEvent, event: SealEvent,
eventNote: Note, eventNote: Note,
publicNote: Note, publicNote: Note,
) { ) {
@@ -477,7 +477,7 @@ class SealedRumorEventHandler(
} }
override suspend fun delete( override suspend fun delete(
event: SealedRumorEvent, event: SealEvent,
eventNote: Note, eventNote: Note,
) { ) {
event.innerEventId?.let { rumorId -> event.innerEventId?.let { rumorId ->
@@ -489,7 +489,7 @@ class SealedRumorEventHandler(
} }
private suspend fun processNewSealedRumor( private suspend fun processNewSealedRumor(
event: SealedRumorEvent, event: SealEvent,
eventNote: Note, eventNote: Note,
publicNote: Note, publicNote: Note,
) { ) {
@@ -538,9 +538,9 @@ class SealedRumorEventHandler(
class LnZapRequestEventHandler( class LnZapRequestEventHandler(
val decryptionCache: PrivateZapCache, val decryptionCache: PrivateZapCache,
) : EventHandler<LnZapRequestEvent> { ) : EventHandler<ZapRequestEvent> {
override suspend fun add( override suspend fun add(
event: LnZapRequestEvent, event: ZapRequestEvent,
eventNote: Note, eventNote: Note,
publicNote: Note, publicNote: Note,
) { ) {
@@ -550,7 +550,7 @@ class LnZapRequestEventHandler(
} }
override suspend fun delete( override suspend fun delete(
event: LnZapRequestEvent, event: ZapRequestEvent,
eventNote: Note, eventNote: Note,
) { ) {
if (event.isPrivateZap()) { if (event.isPrivateZap()) {
@@ -561,9 +561,9 @@ class LnZapRequestEventHandler(
class LnZapEventHandler( class LnZapEventHandler(
val decryptionCache: PrivateZapCache, val decryptionCache: PrivateZapCache,
) : EventHandler<LnZapEvent> { ) : EventHandler<ZapReceiptEvent> {
override suspend fun delete( override suspend fun delete(
event: LnZapEvent, event: ZapReceiptEvent,
eventNote: Note, eventNote: Note,
) { ) {
event.zapRequest?.let { req -> event.zapRequest?.let { req ->
@@ -37,7 +37,7 @@ import com.vitorpamplona.amethyst.commons.ui.theme.DividerThickness
import com.vitorpamplona.amethyst.commons.ui.theme.FeedPadding import com.vitorpamplona.amethyst.commons.ui.theme.FeedPadding
import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountViewModel import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountViewModel
import com.vitorpamplona.amethyst.ui.screen.loggedIn.discover.ChannelCardCompose import com.vitorpamplona.amethyst.ui.screen.loggedIn.discover.ChannelCardCompose
import com.vitorpamplona.quartz.nip23LongContent.LongTextNoteEvent import com.vitorpamplona.quartz.nip23LongContent.LongFormContentEvent
@Composable @Composable
fun ArticlesFeedLoaded( fun ArticlesFeedLoaded(
@@ -61,7 +61,7 @@ fun ArticlesFeedLoaded(
ChannelCardCompose( ChannelCardCompose(
baseNote = item, baseNote = item,
modifier = Modifier.fillMaxWidth(), modifier = Modifier.fillMaxWidth(),
forceEventKind = LongTextNoteEvent.KIND, forceEventKind = LongFormContentEvent.KIND,
accountViewModel = accountViewModel, accountViewModel = accountViewModel,
nav = nav, nav = nav,
) )
@@ -36,7 +36,7 @@ import com.vitorpamplona.amethyst.ui.navigation.topbars.UserDrawerSearchTopBar
import com.vitorpamplona.amethyst.ui.screen.FeedDefinition import com.vitorpamplona.amethyst.ui.screen.FeedDefinition
import com.vitorpamplona.amethyst.ui.screen.TopNavFilterState import com.vitorpamplona.amethyst.ui.screen.TopNavFilterState
import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountViewModel import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountViewModel
import com.vitorpamplona.quartz.nip23LongContent.LongTextNoteEvent import com.vitorpamplona.quartz.nip23LongContent.LongFormContentEvent
@Composable @Composable
fun ArticlesTopBar( fun ArticlesTopBar(
@@ -49,7 +49,7 @@ fun ArticlesTopBar(
// The feed's own kind window, plus whatever the list spinner narrowed it to — a hashtag or a // The feed's own kind window, plus whatever the list spinner narrowed it to — a hashtag or a
// geohash says itself as a token; a follow set does not, and seeds nothing (see asSearchQuery). // geohash says itself as a token; a follow set does not, and seeds nothing (see asSearchQuery).
val me = accountViewModel.userProfile().pubkeyHex val me = accountViewModel.userProfile().pubkeyHex
val seed = remember(list, me) { SearchSeed.merge(SearchSeed.ofKinds(LongTextNoteEvent.KIND), list.asSearchQuery(me)) } val seed = remember(list, me) { SearchSeed.merge(SearchSeed.ofKinds(LongFormContentEvent.KIND), list.asSearchQuery(me)) }
UserDrawerSearchTopBar(accountViewModel, nav, seed) { UserDrawerSearchTopBar(accountViewModel, nav, seed) {
ArticlesTopNavFilterBar( ArticlesTopNavFilterBar(
@@ -28,7 +28,7 @@ import com.vitorpamplona.amethyst.commons.model.topNavFeeds.TopFilter
import com.vitorpamplona.amethyst.model.Account import com.vitorpamplona.amethyst.model.Account
import com.vitorpamplona.amethyst.ui.dal.FilterByListParams import com.vitorpamplona.amethyst.ui.dal.FilterByListParams
import com.vitorpamplona.amethyst.ui.dal.sortedByDefaultFeedOrder import com.vitorpamplona.amethyst.ui.dal.sortedByDefaultFeedOrder
import com.vitorpamplona.quartz.nip23LongContent.LongTextNoteEvent import com.vitorpamplona.quartz.nip23LongContent.LongFormContentEvent
class ArticlesFeedFilter( class ArticlesFeedFilter(
val account: Account, val account: Account,
@@ -50,9 +50,9 @@ class ArticlesFeedFilter(
override fun feed(): List<Note> { override fun feed(): List<Note> {
val params = buildFilterParams(account) val params = buildFilterParams(account)
val notes = val notes =
LocalCache.addressables.filterIntoSet(LongTextNoteEvent.KIND) { _, it -> LocalCache.addressables.filterIntoSet(LongFormContentEvent.KIND) { _, it ->
val noteEvent = it.event val noteEvent = it.event
noteEvent is LongTextNoteEvent && params.match(noteEvent, it.relays) noteEvent is LongFormContentEvent && params.match(noteEvent, it.relays)
} }
return sort(notes) return sort(notes)
} }
@@ -70,7 +70,7 @@ class ArticlesFeedFilter(
return collection.filterTo(HashSet()) { return collection.filterTo(HashSet()) {
val noteEvent = it.event val noteEvent = it.event
noteEvent is LongTextNoteEvent && params.match(noteEvent, it.relays) noteEvent is LongFormContentEvent && params.match(noteEvent, it.relays)
} }
} }
@@ -92,9 +92,9 @@ import com.vitorpamplona.quartz.nip01Core.core.HexKey
import com.vitorpamplona.quartz.nip01Core.diff.ContentChange import com.vitorpamplona.quartz.nip01Core.diff.ContentChange
import com.vitorpamplona.quartz.nip01Core.diff.ListDiff import com.vitorpamplona.quartz.nip01Core.diff.ListDiff
import com.vitorpamplona.quartz.nip01Core.relay.normalizer.NormalizedRelayUrl import com.vitorpamplona.quartz.nip01Core.relay.normalizer.NormalizedRelayUrl
import com.vitorpamplona.quartz.nip17Dm.settings.ChatMessageRelayListEvent import com.vitorpamplona.quartz.nip17Dm.settings.DmRelayListEvent
import com.vitorpamplona.quartz.nip28PublicChat.list.ChannelListDiff import com.vitorpamplona.quartz.nip28PublicChat.list.ChannelListDiff
import com.vitorpamplona.quartz.nip28PublicChat.list.ChannelListEvent import com.vitorpamplona.quartz.nip28PublicChat.list.PublicChatListEvent
import com.vitorpamplona.quartz.nip28PublicChat.list.channels import com.vitorpamplona.quartz.nip28PublicChat.list.channels
import com.vitorpamplona.quartz.nip37Drafts.privateOutbox.PrivateOutboxRelayListEvent import com.vitorpamplona.quartz.nip37Drafts.privateOutbox.PrivateOutboxRelayListEvent
import com.vitorpamplona.quartz.nip50Search.SearchRelayListEvent import com.vitorpamplona.quartz.nip50Search.SearchRelayListEvent
@@ -103,10 +103,10 @@ import com.vitorpamplona.quartz.nip51Lists.favoriteAlgoFeedsList.FavoriteAlgoFee
import com.vitorpamplona.quartz.nip51Lists.geohashList.GeohashListDiff import com.vitorpamplona.quartz.nip51Lists.geohashList.GeohashListDiff
import com.vitorpamplona.quartz.nip51Lists.geohashList.GeohashListEvent import com.vitorpamplona.quartz.nip51Lists.geohashList.GeohashListEvent
import com.vitorpamplona.quartz.nip51Lists.hashtagList.HashtagListDiff import com.vitorpamplona.quartz.nip51Lists.hashtagList.HashtagListDiff
import com.vitorpamplona.quartz.nip51Lists.hashtagList.HashtagListEvent import com.vitorpamplona.quartz.nip51Lists.hashtagList.InterestListEvent
import com.vitorpamplona.quartz.nip51Lists.relayLists.BlockedRelayListEvent import com.vitorpamplona.quartz.nip51Lists.relayLists.BlockedRelayListEvent
import com.vitorpamplona.quartz.nip51Lists.relayLists.FavoriteRelayListEvent
import com.vitorpamplona.quartz.nip51Lists.relayLists.IndexerRelayListEvent import com.vitorpamplona.quartz.nip51Lists.relayLists.IndexerRelayListEvent
import com.vitorpamplona.quartz.nip51Lists.relayLists.RelayFeedsListEvent
import com.vitorpamplona.quartz.nip51Lists.relayLists.RelayListDiff import com.vitorpamplona.quartz.nip51Lists.relayLists.RelayListDiff
import com.vitorpamplona.quartz.nip51Lists.relayLists.TrustedRelayListEvent import com.vitorpamplona.quartz.nip51Lists.relayLists.TrustedRelayListEvent
import com.vitorpamplona.quartz.nip60Cashu.wallet.CashuWalletDiff import com.vitorpamplona.quartz.nip60Cashu.wallet.CashuWalletDiff
@@ -137,7 +137,7 @@ internal fun LazyListScope.listDiffItems(
is CommunityListDiff -> communityItems(diff, conflict, accountViewModel, nav) is CommunityListDiff -> communityItems(diff, conflict, accountViewModel, nav)
is EphemeralChatListDiff -> ephemeralRoomItems(diff, conflict, nav) is EphemeralChatListDiff -> ephemeralRoomItems(diff, conflict, nav)
is FavoriteAlgoFeedsListDiff -> favoriteFeedItems(diff, conflict, accountViewModel, nav) is FavoriteAlgoFeedsListDiff -> favoriteFeedItems(diff, conflict, accountViewModel, nav)
is HashtagListDiff -> topicItems(diff.hashtags, (conflict.saved as? HashtagListEvent)?.publicHashtags().orEmpty(), diff.privateItems, isPlace = false, nav) is HashtagListDiff -> topicItems(diff.hashtags, (conflict.saved as? InterestListEvent)?.publicHashtags().orEmpty(), diff.privateItems, isPlace = false, nav)
is GeohashListDiff -> topicItems(diff.geohashes, (conflict.saved as? GeohashListEvent)?.publicGeohashes().orEmpty(), diff.privateItems, isPlace = true, nav) is GeohashListDiff -> topicItems(diff.geohashes, (conflict.saved as? GeohashListEvent)?.publicGeohashes().orEmpty(), diff.privateItems, isPlace = true, nav)
is TrustProviderListDiff -> trustProviderItems(diff, conflict, accountViewModel, nav) is TrustProviderListDiff -> trustProviderItems(diff, conflict, accountViewModel, nav)
is PaymentTargetsDiff -> paymentTargetItems(diff, conflict) is PaymentTargetsDiff -> paymentTargetItems(diff, conflict)
@@ -236,11 +236,11 @@ private fun framingOf(type: BackupEventType): RelayFraming =
private fun savedRelaysOf(event: Event): List<NormalizedRelayUrl> = private fun savedRelaysOf(event: Event): List<NormalizedRelayUrl> =
when (event) { when (event) {
is ChatMessageRelayListEvent -> event.relays() is DmRelayListEvent -> event.relays()
is KeyPackageRelayListEvent -> event.relays() is KeyPackageRelayListEvent -> event.relays()
is SearchRelayListEvent -> event.publicRelays() is SearchRelayListEvent -> event.publicRelays()
is IndexerRelayListEvent -> event.publicRelays() is IndexerRelayListEvent -> event.publicRelays()
is RelayFeedsListEvent -> event.publicRelays() is FavoriteRelayListEvent -> event.publicRelays()
is BlockedRelayListEvent -> event.publicRelays() is BlockedRelayListEvent -> event.publicRelays()
is TrustedRelayListEvent -> event.publicRelays() is TrustedRelayListEvent -> event.publicRelays()
is PrivateOutboxRelayListEvent -> event.publicRelays() is PrivateOutboxRelayListEvent -> event.publicRelays()
@@ -451,7 +451,7 @@ private fun LazyListScope.publicChatItems(
accountViewModel: AccountViewModel, accountViewModel: AccountViewModel,
nav: INav, nav: INav,
) { ) {
val saved = (conflict.saved as? ChannelListEvent)?.tags?.channels().orEmpty() val saved = (conflict.saved as? PublicChatListEvent)?.tags?.channels().orEmpty()
spaceGrid("chat-", fates(saved, diff.channels) { it.eventId }, { it.eventId }) { tag, fate, modifier -> spaceGrid("chat-", fates(saved, diff.channels) { it.eventId }, { it.eventId }) { tag, fate, modifier ->
LoadPublicChatChannel(tag.eventId) { channel -> LoadPublicChatChannel(tag.eventId) { channel ->
// Subscribes to the channel's metadata on relays and recomposes when it arrives. // Subscribes to the channel's metadata on relays and recomposes when it arrives.
@@ -75,7 +75,7 @@ import com.vitorpamplona.amethyst.ui.note.types.EditState
import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountViewModel import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountViewModel
import com.vitorpamplona.amethyst.ui.screen.loggedIn.report.ReportNoteDialog import com.vitorpamplona.amethyst.ui.screen.loggedIn.report.ReportNoteDialog
import com.vitorpamplona.quartz.nip10Notes.TextNoteEvent import com.vitorpamplona.quartz.nip10Notes.TextNoteEvent
import com.vitorpamplona.quartz.nip23LongContent.LongTextNoteEvent import com.vitorpamplona.quartz.nip23LongContent.LongFormContentEvent
import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
@@ -281,7 +281,7 @@ fun BookmarkGroupItemOptionsMenu(
wantsToEditPost.value = true wantsToEditPost.value = true
} }
} }
} else if (note.event is LongTextNoteEvent && state.isLoggedUser) { } else if (note.event is LongFormContentEvent && state.isLoggedUser) {
M3ActionRow(icon = MaterialSymbols.Edit, text = stringRes(Res.string.edit_article)) { M3ActionRow(icon = MaterialSymbols.Edit, text = stringRes(Res.string.edit_article)) {
nav.nav { Route.NewLongFormPost(version = note.idHex) } nav.nav { Route.NewLongFormPost(version = note.idHex) }
} }
@@ -36,7 +36,7 @@ import com.vitorpamplona.amethyst.ui.navigation.topbars.UserDrawerSearchTopBar
import com.vitorpamplona.amethyst.ui.screen.FeedDefinition import com.vitorpamplona.amethyst.ui.screen.FeedDefinition
import com.vitorpamplona.amethyst.ui.screen.TopNavFilterState import com.vitorpamplona.amethyst.ui.screen.TopNavFilterState
import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountViewModel import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountViewModel
import com.vitorpamplona.quartz.nip52Calendar.calendar.CalendarEvent import com.vitorpamplona.quartz.nip52Calendar.calendar.CalendarCollectionEvent
@Composable @Composable
fun CalendarCollectionsTopBar( fun CalendarCollectionsTopBar(
@@ -49,7 +49,7 @@ fun CalendarCollectionsTopBar(
// The feed's own kind window, plus whatever the list spinner narrowed it to — a hashtag // The feed's own kind window, plus whatever the list spinner narrowed it to — a hashtag
// or a geohash says itself as a token; a follow set does not, and seeds nothing. // or a geohash says itself as a token; a follow set does not, and seeds nothing.
val me = accountViewModel.userProfile().pubkeyHex val me = accountViewModel.userProfile().pubkeyHex
val seed = remember(list, me) { SearchSeed.merge(SearchSeed.ofKinds(CalendarEvent.KIND), list.asSearchQuery(me)) } val seed = remember(list, me) { SearchSeed.merge(SearchSeed.ofKinds(CalendarCollectionEvent.KIND), list.asSearchQuery(me)) }
UserDrawerSearchTopBar(accountViewModel, nav, seed) { UserDrawerSearchTopBar(accountViewModel, nav, seed) {
CalendarCollectionsTopNavFilterBar( CalendarCollectionsTopNavFilterBar(
@@ -76,7 +76,7 @@ import com.vitorpamplona.amethyst.ui.screen.loggedIn.video.UserCardHeader
import com.vitorpamplona.quartz.nip01Core.core.Address import com.vitorpamplona.quartz.nip01Core.core.Address
import com.vitorpamplona.quartz.nip52Calendar.appt.day.CalendarDateSlotEvent import com.vitorpamplona.quartz.nip52Calendar.appt.day.CalendarDateSlotEvent
import com.vitorpamplona.quartz.nip52Calendar.appt.time.CalendarTimeSlotEvent import com.vitorpamplona.quartz.nip52Calendar.appt.time.CalendarTimeSlotEvent
import com.vitorpamplona.quartz.nip52Calendar.calendar.CalendarEvent import com.vitorpamplona.quartz.nip52Calendar.calendar.CalendarCollectionEvent
import com.vitorpamplona.quartz.utils.TimeUtils import com.vitorpamplona.quartz.utils.TimeUtils
@Composable @Composable
@@ -153,7 +153,7 @@ fun CalendarCollectionCard(
accountViewModel: AccountViewModel, accountViewModel: AccountViewModel,
nav: INav, nav: INav,
) { ) {
val event = note.event as? CalendarEvent ?: return val event = note.event as? CalendarCollectionEvent ?: return
val title = remember(note.idHex) { event.title() } val title = remember(note.idHex) { event.title() }
val description = remember(note.idHex) { event.content.take(180) } val description = remember(note.idHex) { event.content.take(180) }
val count = remember(note.idHex) { event.calendarEventAddresses().size } val count = remember(note.idHex) { event.calendarEventAddresses().size }
@@ -241,7 +241,7 @@ fun CalendarCollectionCard(
* arrived from relays yet or aren't appointments. Returns a list compatible with * arrived from relays yet or aren't appointments. Returns a list compatible with
* [IcsExport.calendarToIcs]. * [IcsExport.calendarToIcs].
*/ */
private fun collectMembers(calendar: CalendarEvent): List<Pair<Address, Any>> = private fun collectMembers(calendar: CalendarCollectionEvent): List<Pair<Address, Any>> =
calendar calendar
.calendarEventAddresses() .calendarEventAddresses()
.mapNotNull { addr -> .mapNotNull { addr ->
@@ -39,7 +39,7 @@ import com.vitorpamplona.amethyst.commons.model.nip52Calendar.groupByDayKeyExpan
import com.vitorpamplona.quartz.nip01Core.core.Address import com.vitorpamplona.quartz.nip01Core.core.Address
import com.vitorpamplona.quartz.nip01Core.core.HexKey import com.vitorpamplona.quartz.nip01Core.core.HexKey
import com.vitorpamplona.quartz.nip01Core.relay.filters.Filter import com.vitorpamplona.quartz.nip01Core.relay.filters.Filter
import com.vitorpamplona.quartz.nip52Calendar.calendar.CalendarEvent import com.vitorpamplona.quartz.nip52Calendar.calendar.CalendarCollectionEvent
import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.MutableStateFlow
@@ -162,13 +162,13 @@ class CalendarsViewModel : ViewModel() {
* changes about once a week. * changes about once a week.
*/ */
@OptIn(ExperimentalCoroutinesApi::class) @OptIn(ExperimentalCoroutinesApi::class)
val ownCalendars: StateFlow<List<CalendarEvent>> = val ownCalendars: StateFlow<List<CalendarCollectionEvent>> =
inputs inputs
.filterNotNull() .filterNotNull()
.flatMapLatest { (myPubKey, _) -> .flatMapLatest { (myPubKey, _) ->
LocalCache LocalCache
.observeEvents<CalendarEvent>( .observeEvents<CalendarCollectionEvent>(
Filter(kinds = listOf(CalendarEvent.KIND), authors = listOf(myPubKey)), Filter(kinds = listOf(CalendarCollectionEvent.KIND), authors = listOf(myPubKey)),
).map { calendars -> calendars.sortedBy { it.title()?.lowercase() ?: "" } } ).map { calendars -> calendars.sortedBy { it.title()?.lowercase() ?: "" } }
} }
// The seed scan inside observeEvents walks the whole notes cache (LocalCache.filter // The seed scan inside observeEvents walks the whole notes cache (LocalCache.filter
@@ -33,7 +33,7 @@ import com.vitorpamplona.quartz.nip01Core.tags.aTag.ATag
import com.vitorpamplona.quartz.nip01Core.tags.aTag.aTags import com.vitorpamplona.quartz.nip01Core.tags.aTag.aTags
import com.vitorpamplona.quartz.nip52Calendar.appt.day.CalendarDateSlotEvent import com.vitorpamplona.quartz.nip52Calendar.appt.day.CalendarDateSlotEvent
import com.vitorpamplona.quartz.nip52Calendar.appt.time.CalendarTimeSlotEvent import com.vitorpamplona.quartz.nip52Calendar.appt.time.CalendarTimeSlotEvent
import com.vitorpamplona.quartz.nip52Calendar.calendar.CalendarEvent import com.vitorpamplona.quartz.nip52Calendar.calendar.CalendarCollectionEvent
import com.vitorpamplona.quartz.utils.TimeUtils import com.vitorpamplona.quartz.utils.TimeUtils
import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.Job import kotlinx.coroutines.Job
@@ -62,7 +62,7 @@ class NewCalendarCollectionViewModel : ViewModel() {
private var dTag: String? = null private var dTag: String? = null
/** The full original event in edit mode; needed to publish a NIP-09 deletion. */ /** The full original event in edit mode; needed to publish a NIP-09 deletion. */
private var loadedEvent: com.vitorpamplona.quartz.nip52Calendar.calendar.CalendarEvent? = null private var loadedEvent: com.vitorpamplona.quartz.nip52Calendar.calendar.CalendarCollectionEvent? = null
val selectedAddresses = mutableStateListOf<Address>() val selectedAddresses = mutableStateListOf<Address>()
val availableAppointments = mutableStateOf<List<OwnedAppointmentSummary>>(emptyList()) val availableAppointments = mutableStateOf<List<OwnedAppointmentSummary>>(emptyList())
@@ -81,9 +81,9 @@ class NewCalendarCollectionViewModel : ViewModel() {
dTag = editDTag dTag = editDTag
editDTag?.let { existingDTag -> editDTag?.let { existingDTag ->
val existingAddress = Address(CalendarEvent.KIND, account.userProfile().pubkeyHex, existingDTag) val existingAddress = Address(CalendarCollectionEvent.KIND, account.userProfile().pubkeyHex, existingDTag)
val existingNote = LocalCache.addressables.get(existingAddress) val existingNote = LocalCache.addressables.get(existingAddress)
(existingNote?.event as? CalendarEvent)?.let { existing -> (existingNote?.event as? CalendarCollectionEvent)?.let { existing ->
loadedEvent = existing loadedEvent = existing
title.value = existing.title().orEmpty() title.value = existing.title().orEmpty()
description.value = existing.content description.value = existing.content
@@ -140,7 +140,7 @@ class NewCalendarCollectionViewModel : ViewModel() {
account.signAndComputeBroadcast( account.signAndComputeBroadcast(
if (effectiveDTag != null) { if (effectiveDTag != null) {
CalendarEvent.build( CalendarCollectionEvent.build(
title = parsedTitle, title = parsedTitle,
content = parsedDescription, content = parsedDescription,
dTag = effectiveDTag, dTag = effectiveDTag,
@@ -148,7 +148,7 @@ class NewCalendarCollectionViewModel : ViewModel() {
if (selected.isNotEmpty()) aTags(selected.map { ATag(it) }) if (selected.isNotEmpty()) aTags(selected.map { ATag(it) })
} }
} else { } else {
CalendarEvent.build( CalendarCollectionEvent.build(
title = parsedTitle, title = parsedTitle,
content = parsedDescription, content = parsedDescription,
) { ) {
@@ -27,7 +27,7 @@ import com.vitorpamplona.amethyst.commons.model.topNavFeeds.TopFilter
import com.vitorpamplona.amethyst.model.Account import com.vitorpamplona.amethyst.model.Account
import com.vitorpamplona.amethyst.ui.dal.FilterByListParams import com.vitorpamplona.amethyst.ui.dal.FilterByListParams
import com.vitorpamplona.amethyst.ui.dal.sortedByDefaultFeedOrder import com.vitorpamplona.amethyst.ui.dal.sortedByDefaultFeedOrder
import com.vitorpamplona.quartz.nip52Calendar.calendar.CalendarEvent import com.vitorpamplona.quartz.nip52Calendar.calendar.CalendarCollectionEvent
class CalendarCollectionsFeedFilter( class CalendarCollectionsFeedFilter(
val account: Account, val account: Account,
@@ -49,7 +49,7 @@ class CalendarCollectionsFeedFilter(
val notes = val notes =
LocalCache.addressables.filterIntoSet { _, it -> LocalCache.addressables.filterIntoSet { _, it ->
val e = it.event val e = it.event
e is CalendarEvent && params.match(e, it.relays) e is CalendarCollectionEvent && params.match(e, it.relays)
} }
return sort(notes) return sort(notes)
} }
@@ -66,7 +66,7 @@ class CalendarCollectionsFeedFilter(
val params = buildFilterParams(account) val params = buildFilterParams(account)
return collection.filterTo(HashSet()) { return collection.filterTo(HashSet()) {
val e = it.event val e = it.event
e is CalendarEvent && params.match(e, it.relays) e is CalendarCollectionEvent && params.match(e, it.relays)
} }
} }
@@ -53,7 +53,7 @@ import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountViewModel
import com.vitorpamplona.quartz.nip01Core.core.Address import com.vitorpamplona.quartz.nip01Core.core.Address
import com.vitorpamplona.quartz.nip01Core.tags.aTag.ATag import com.vitorpamplona.quartz.nip01Core.tags.aTag.ATag
import com.vitorpamplona.quartz.nip01Core.tags.aTag.aTags import com.vitorpamplona.quartz.nip01Core.tags.aTag.aTags
import com.vitorpamplona.quartz.nip52Calendar.calendar.CalendarEvent import com.vitorpamplona.quartz.nip52Calendar.calendar.CalendarCollectionEvent
/** /**
* Bottom sheet that lists the current user's own kind-31924 calendars with a checkbox per * Bottom sheet that lists the current user's own kind-31924 calendars with a checkbox per
@@ -74,7 +74,7 @@ fun AddToCalendarSheet(
val sheetState = rememberModalBottomSheetState(skipPartiallyExpanded = true) val sheetState = rememberModalBottomSheetState(skipPartiallyExpanded = true)
val myPubKey = accountViewModel.userProfile().pubkeyHex val myPubKey = accountViewModel.userProfile().pubkeyHex
val ownCalendars by produceState<List<CalendarEvent>>(initialValue = ownCalendars(myPubKey), myPubKey) { val ownCalendars by produceState<List<CalendarCollectionEvent>>(initialValue = ownCalendars(myPubKey), myPubKey) {
LocalCache.live.newEventBundles.collect { LocalCache.live.newEventBundles.collect {
value = ownCalendars(myPubKey) value = ownCalendars(myPubKey)
} }
@@ -152,17 +152,17 @@ private fun CalendarPickerRow(
} }
} }
private fun ownCalendars(myPubKey: String): List<CalendarEvent> = private fun ownCalendars(myPubKey: String): List<CalendarCollectionEvent> =
LocalCache.addressables LocalCache.addressables
.filterIntoSet { _, note -> .filterIntoSet { _, note ->
val e = note.event val e = note.event
e is CalendarEvent && e.pubKey == myPubKey e is CalendarCollectionEvent && e.pubKey == myPubKey
}.mapNotNull { it.event as? CalendarEvent } }.mapNotNull { it.event as? CalendarCollectionEvent }
.sortedBy { it.title()?.lowercase() ?: "" } .sortedBy { it.title()?.lowercase() ?: "" }
private fun toggleMembership( private fun toggleMembership(
accountViewModel: AccountViewModel, accountViewModel: AccountViewModel,
calendar: CalendarEvent, calendar: CalendarCollectionEvent,
targetAddress: Address, targetAddress: Address,
isCurrentlyMember: Boolean, isCurrentlyMember: Boolean,
) { ) {
@@ -177,7 +177,7 @@ private fun toggleMembership(
} }
accountViewModel.launchSigner { accountViewModel.launchSigner {
accountViewModel.account.signAndComputeBroadcast( accountViewModel.account.signAndComputeBroadcast(
CalendarEvent.build( CalendarCollectionEvent.build(
title = calendar.title().orEmpty(), title = calendar.title().orEmpty(),
content = calendar.content, content = calendar.content,
dTag = calendar.dTag(), dTag = calendar.dTag(),
@@ -116,7 +116,7 @@ import com.vitorpamplona.quartz.nip19Bech32.entities.NAddress
import com.vitorpamplona.quartz.nip52Calendar.appt.day.CalendarDateSlotEvent import com.vitorpamplona.quartz.nip52Calendar.appt.day.CalendarDateSlotEvent
import com.vitorpamplona.quartz.nip52Calendar.appt.tags.RSVPStatusTag import com.vitorpamplona.quartz.nip52Calendar.appt.tags.RSVPStatusTag
import com.vitorpamplona.quartz.nip52Calendar.appt.time.CalendarTimeSlotEvent import com.vitorpamplona.quartz.nip52Calendar.appt.time.CalendarTimeSlotEvent
import com.vitorpamplona.quartz.nip52Calendar.calendar.CalendarEvent import com.vitorpamplona.quartz.nip52Calendar.calendar.CalendarCollectionEvent
import com.vitorpamplona.quartz.nip52Calendar.rsvp.CalendarRSVPEvent import com.vitorpamplona.quartz.nip52Calendar.rsvp.CalendarRSVPEvent
import com.vitorpamplona.quartz.utils.TimeUtils import com.vitorpamplona.quartz.utils.TimeUtils
import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Dispatchers
@@ -287,7 +287,7 @@ fun CalendarEventDetailScreen(
targetAddress = targetAddress, targetAddress = targetAddress,
) )
is CalendarEvent -> is CalendarCollectionEvent ->
CollectionBody( CollectionBody(
note = targetNote, note = targetNote,
accountViewModel = accountViewModel, accountViewModel = accountViewModel,
@@ -432,7 +432,7 @@ private fun CollectionBody(
nav: INav, nav: INav,
targetAddress: Address, targetAddress: Address,
) { ) {
val event = note.event as? CalendarEvent ?: return val event = note.event as? CalendarCollectionEvent ?: return
val title = remember(note.idHex) { event.title() } val title = remember(note.idHex) { event.title() }
val memberAddresses = remember(note.idHex) { event.calendarEventAddresses() } val memberAddresses = remember(note.idHex) { event.calendarEventAddresses() }
@@ -736,7 +736,7 @@ private fun InCalendarsSection(
.clickable { .clickable {
nav.nav( nav.nav(
Route.CalendarEventDetail( Route.CalendarEventDetail(
kind = CalendarEvent.KIND, kind = CalendarCollectionEvent.KIND,
pubKeyHex = calendar.pubKey, pubKeyHex = calendar.pubKey,
dTag = calendar.dTag(), dTag = calendar.dTag(),
), ),
@@ -889,11 +889,11 @@ private fun rememberRsvpsFor(targetAddress: Address): State<List<CalendarRSVPEve
} }
@Composable @Composable
private fun rememberCalendarsContaining(targetAddress: Address): State<List<CalendarEvent>> { private fun rememberCalendarsContaining(targetAddress: Address): State<List<CalendarCollectionEvent>> {
val calendars = val calendars =
remember(targetAddress) { remember(targetAddress) {
LocalCache LocalCache
.observeEvents<CalendarEvent>(Filter(kinds = listOf(CalendarEvent.KIND))) .observeEvents<CalendarCollectionEvent>(Filter(kinds = listOf(CalendarCollectionEvent.KIND)))
.map { all -> .map { all ->
all all
.filter { it.calendarEventAddresses().contains(targetAddress) } .filter { it.calendarEventAddresses().contains(targetAddress) }
@@ -87,7 +87,7 @@ import com.vitorpamplona.quartz.buzz.stream.SystemMessageEvent
import com.vitorpamplona.quartz.concord.cord03Channels.ConcordChatEditEvent import com.vitorpamplona.quartz.concord.cord03Channels.ConcordChatEditEvent
import com.vitorpamplona.quartz.marmot.foundation.appEvents.MarmotAppEvent import com.vitorpamplona.quartz.marmot.foundation.appEvents.MarmotAppEvent
import com.vitorpamplona.quartz.nip01Core.core.HexKey import com.vitorpamplona.quartz.nip01Core.core.HexKey
import com.vitorpamplona.quartz.nip04Dm.messages.PrivateDmEvent import com.vitorpamplona.quartz.nip04Dm.messages.EncryptedDmEvent
import com.vitorpamplona.quartz.nip10Notes.BaseNoteEvent import com.vitorpamplona.quartz.nip10Notes.BaseNoteEvent
import com.vitorpamplona.quartz.nip17Dm.base.ChatroomKeyable import com.vitorpamplona.quartz.nip17Dm.base.ChatroomKeyable
import com.vitorpamplona.quartz.nip17Dm.files.ChatMessageEncryptedFileHeaderEvent import com.vitorpamplona.quartz.nip17Dm.files.ChatMessageEncryptedFileHeaderEvent
@@ -97,7 +97,7 @@ import com.vitorpamplona.quartz.nip28PublicChat.admin.ChannelMetadataEvent
import com.vitorpamplona.quartz.nip37Drafts.DraftWrapEvent import com.vitorpamplona.quartz.nip37Drafts.DraftWrapEvent
import com.vitorpamplona.quartz.nip53LiveActivities.clip.LiveActivitiesClipEvent import com.vitorpamplona.quartz.nip53LiveActivities.clip.LiveActivitiesClipEvent
import com.vitorpamplona.quartz.nip53LiveActivities.raid.LiveActivitiesRaidEvent import com.vitorpamplona.quartz.nip53LiveActivities.raid.LiveActivitiesRaidEvent
import com.vitorpamplona.quartz.nip57Zaps.LnZapEvent import com.vitorpamplona.quartz.nip57Zaps.ZapReceiptEvent
import com.vitorpamplona.quartz.nip57Zaps.splits.hasZapSplitSetup import com.vitorpamplona.quartz.nip57Zaps.splits.hasZapSplitSetup
import com.vitorpamplona.quartz.nip57Zaps.zapraiser.zapraiserAmount import com.vitorpamplona.quartz.nip57Zaps.zapraiser.zapraiserAmount
@@ -156,7 +156,7 @@ fun ChatroomMessageCompose(
} }
val event = baseNote.event val event = baseNote.event
if (event is LnZapEvent) { if (event is ZapReceiptEvent) {
RenderChatZap(baseNote, accountViewModel, nav) RenderChatZap(baseNote, accountViewModel, nav)
} else if (event is LiveActivitiesRaidEvent) { } else if (event is LiveActivitiesRaidEvent) {
RenderChatRaid(baseNote, accountViewModel, nav) RenderChatRaid(baseNote, accountViewModel, nav)
@@ -248,10 +248,10 @@ fun NormalChatNote(
when { when {
// Own messages: normally no name; in a multi-identity chat, show it (unless a DM, // Own messages: normally no name; in a multi-identity chat, show it (unless a DM,
// which never draws the user's own author info). // which never draws the user's own author info).
isLoggedInUser -> showSelfAuthorName && noteEvent !is PrivateDmEvent isLoggedInUser -> showSelfAuthorName && noteEvent !is EncryptedDmEvent
// never shows the user's pictures // never shows the user's pictures
noteEvent is PrivateDmEvent -> false noteEvent is EncryptedDmEvent -> false
// one-on-one, never shows it. // one-on-one, never shows it.
// only shows in a group chat. // only shows in a group chat.
@@ -588,7 +588,7 @@ private fun RenderReply(
is ChatMessageEvent, is ChatMessageEncryptedFileHeaderEvent -> { is ChatMessageEvent, is ChatMessageEncryptedFileHeaderEvent -> {
{ LoadingReplyNote(DmReplyProtocol.NIP17, accountViewModel) } { LoadingReplyNote(DmReplyProtocol.NIP17, accountViewModel) }
} }
is PrivateDmEvent -> { is EncryptedDmEvent -> {
{ LoadingReplyNote(DmReplyProtocol.NIP04, accountViewModel) } { LoadingReplyNote(DmReplyProtocol.NIP04, accountViewModel) }
} }
else -> null else -> null
@@ -51,7 +51,7 @@ import com.vitorpamplona.amethyst.ui.note.elements.ToggleableTimeAgoText
import com.vitorpamplona.amethyst.ui.note.timeAheadNoDot import com.vitorpamplona.amethyst.ui.note.timeAheadNoDot
import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountViewModel import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountViewModel
import com.vitorpamplona.quartz.nip01Core.tags.geohash.geoHashOrScope import com.vitorpamplona.quartz.nip01Core.tags.geohash.geoHashOrScope
import com.vitorpamplona.quartz.nip04Dm.messages.PrivateDmEvent import com.vitorpamplona.quartz.nip04Dm.messages.EncryptedDmEvent
import com.vitorpamplona.quartz.nip13Pow.strongPoWOrNull import com.vitorpamplona.quartz.nip13Pow.strongPoWOrNull
import com.vitorpamplona.quartz.nip40Expiration.expiration import com.vitorpamplona.quartz.nip40Expiration.expiration
@@ -84,7 +84,7 @@ fun chatFooterHasMeta(
): Boolean { ): Boolean {
val event = note.event ?: return false val event = note.event ?: return false
val geo = event.geoHashOrScope() val geo = event.geoHashOrScope()
return event is PrivateDmEvent || return event is EncryptedDmEvent ||
event.expiration() != null || event.expiration() != null ||
(geo != null && geo != suppressGeohash) || (geo != null && geo != suppressGeohash) ||
event.strongPoWOrNull() != null || event.strongPoWOrNull() != null ||
@@ -132,7 +132,7 @@ fun ChatMessageFooter(
} }
if (showTime) { if (showTime) {
val hasGlyph = note.isPinnedInRelayGroup() || event is PrivateDmEvent || event?.expiration() != null || geo != null || pow != null val hasGlyph = note.isPinnedInRelayGroup() || event is EncryptedDmEvent || event?.expiration() != null || geo != null || pow != null
if (hasGlyph) Spacer(StdHorzSpacer) if (hasGlyph) Spacer(StdHorzSpacer)
// Drafts aren't published, so no relay/delivery detail; everything else gets // Drafts aren't published, so no relay/delivery detail; everything else gets
@@ -41,7 +41,7 @@ import com.vitorpamplona.quartz.nip28PublicChat.admin.ChannelCreateEvent
import com.vitorpamplona.quartz.nip28PublicChat.admin.ChannelMetadataEvent import com.vitorpamplona.quartz.nip28PublicChat.admin.ChannelMetadataEvent
import com.vitorpamplona.quartz.nip53LiveActivities.clip.LiveActivitiesClipEvent import com.vitorpamplona.quartz.nip53LiveActivities.clip.LiveActivitiesClipEvent
import com.vitorpamplona.quartz.nip53LiveActivities.raid.LiveActivitiesRaidEvent import com.vitorpamplona.quartz.nip53LiveActivities.raid.LiveActivitiesRaidEvent
import com.vitorpamplona.quartz.nip57Zaps.LnZapEvent import com.vitorpamplona.quartz.nip57Zaps.ZapReceiptEvent
import kotlin.math.abs import kotlin.math.abs
/** /**
@@ -96,7 +96,7 @@ internal const val CHAT_GROUP_WINDOW_SECONDS = 10 * 60L
*/ */
private fun isGroupableEvent(event: Event?): Boolean = private fun isGroupableEvent(event: Event?): Boolean =
event != null && event != null &&
event !is LnZapEvent && event !is ZapReceiptEvent &&
event !is LiveActivitiesRaidEvent && event !is LiveActivitiesRaidEvent &&
event !is LiveActivitiesClipEvent && event !is LiveActivitiesClipEvent &&
event !is ChannelCreateEvent && event !is ChannelCreateEvent &&
@@ -55,7 +55,7 @@ import com.vitorpamplona.amethyst.ui.note.UsernameDisplay
import com.vitorpamplona.amethyst.ui.note.ZapAmountCommentNotification import com.vitorpamplona.amethyst.ui.note.ZapAmountCommentNotification
import com.vitorpamplona.amethyst.ui.note.showAmountInteger import com.vitorpamplona.amethyst.ui.note.showAmountInteger
import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountViewModel import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountViewModel
import com.vitorpamplona.quartz.nip57Zaps.LnZapEvent import com.vitorpamplona.quartz.nip57Zaps.ZapReceiptEvent
private const val BIG_ZAP_THRESHOLD_SATS = 50_000L private const val BIG_ZAP_THRESHOLD_SATS = 50_000L
@@ -65,7 +65,7 @@ fun RenderChatZap(
accountViewModel: AccountViewModel, accountViewModel: AccountViewModel,
nav: INav, nav: INav,
) { ) {
val zapEvent = baseNote.event as? LnZapEvent ?: return val zapEvent = baseNote.event as? ZapReceiptEvent ?: return
val card by produceState( val card by produceState(
ZapAmountCommentNotification(user = null, comment = null, amount = null), ZapAmountCommentNotification(user = null, comment = null, amount = null),
@@ -29,7 +29,7 @@ import com.vitorpamplona.quartz.nip01Core.core.Event
import com.vitorpamplona.quartz.nip01Core.core.toHexKey import com.vitorpamplona.quartz.nip01Core.core.toHexKey
import com.vitorpamplona.quartz.nip01Core.relay.normalizer.NormalizedRelayUrl import com.vitorpamplona.quartz.nip01Core.relay.normalizer.NormalizedRelayUrl
import com.vitorpamplona.quartz.nip01Core.signers.NostrSignerInternal import com.vitorpamplona.quartz.nip01Core.signers.NostrSignerInternal
import com.vitorpamplona.quartz.nip09Deletions.DeletionEvent import com.vitorpamplona.quartz.nip09Deletions.DeletionRequestEvent
import com.vitorpamplona.quartz.nip25Reactions.ReactionEvent import com.vitorpamplona.quartz.nip25Reactions.ReactionEvent
import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.MutableStateFlow
@@ -122,7 +122,7 @@ class GeohashChatViewModel : ViewModel() {
val signer = NostrSignerInternal(keyPair) val signer = NostrSignerInternal(keyPair)
val template = val template =
if (mine.isNotEmpty()) { if (mine.isNotEmpty()) {
DeletionEvent.build(mine) DeletionRequestEvent.build(mine)
} else { } else {
val hint = note.toEventHint<Event>() ?: return@launch val hint = note.toEventHint<Event>() ?: return@launch
ReactionEvent.build(reaction, hint) ReactionEvent.build(reaction, hint)
@@ -71,7 +71,7 @@ import com.vitorpamplona.quartz.nip01Core.core.HexKey
import com.vitorpamplona.quartz.nip01Core.relay.client.paging.RelayPagingProgress import com.vitorpamplona.quartz.nip01Core.relay.client.paging.RelayPagingProgress
import com.vitorpamplona.quartz.nip01Core.relay.normalizer.NormalizedRelayUrl import com.vitorpamplona.quartz.nip01Core.relay.normalizer.NormalizedRelayUrl
import com.vitorpamplona.quartz.nip17Dm.base.ChatroomKey import com.vitorpamplona.quartz.nip17Dm.base.ChatroomKey
import com.vitorpamplona.quartz.nip17Dm.settings.ChatMessageRelayListEvent import com.vitorpamplona.quartz.nip17Dm.settings.DmRelayListEvent
import com.vitorpamplona.quartz.utils.Log import com.vitorpamplona.quartz.utils.Log
import kotlinx.collections.immutable.persistentListOf import kotlinx.collections.immutable.persistentListOf
import kotlinx.coroutines.delay import kotlinx.coroutines.delay
@@ -130,7 +130,7 @@ fun ChatroomView(
// Reactively check if recipients have DM relays for NIP-17 delivery // Reactively check if recipients have DM relays for NIP-17 delivery
for (userHex in room.users) { for (userHex in room.users) {
LoadAddressableNote( LoadAddressableNote(
ChatMessageRelayListEvent.createAddress(userHex), DmRelayListEvent.createAddress(userHex),
) { note -> ) { note ->
if (note != null) { if (note != null) {
EventFinderFilterAssemblerSubscription(note, accountViewModel) EventFinderFilterAssemblerSubscription(note, accountViewModel)
@@ -27,7 +27,7 @@ import com.vitorpamplona.amethyst.model.Account
import com.vitorpamplona.quartz.nip01Core.core.HexKey import com.vitorpamplona.quartz.nip01Core.core.HexKey
import com.vitorpamplona.quartz.nip01Core.relay.client.pool.RelayBasedFilter import com.vitorpamplona.quartz.nip01Core.relay.client.pool.RelayBasedFilter
import com.vitorpamplona.quartz.nip01Core.relay.normalizer.NormalizedRelayUrl import com.vitorpamplona.quartz.nip01Core.relay.normalizer.NormalizedRelayUrl
import com.vitorpamplona.quartz.nip04Dm.messages.PrivateDmEvent import com.vitorpamplona.quartz.nip04Dm.messages.EncryptedDmEvent
import com.vitorpamplona.quartz.nip65RelayList.AdvertisedRelayListEvent import com.vitorpamplona.quartz.nip65RelayList.AdvertisedRelayListEvent
/** /**
@@ -114,7 +114,7 @@ private fun toMeFilter(
filter = filter =
ExplainedFilter( ExplainedFilter(
purpose = SubPurpose.DIRECT_MESSAGES, purpose = SubPurpose.DIRECT_MESSAGES,
kinds = listOf(PrivateDmEvent.KIND), kinds = listOf(EncryptedDmEvent.KIND),
authors = authors.toList(), authors = authors.toList(),
tags = mapOf("p" to listOf(account.userProfile().pubkeyHex)), tags = mapOf("p" to listOf(account.userProfile().pubkeyHex)),
since = since, since = since,
@@ -135,7 +135,7 @@ private fun fromMeFilter(
filter = filter =
ExplainedFilter( ExplainedFilter(
purpose = SubPurpose.DIRECT_MESSAGES, purpose = SubPurpose.DIRECT_MESSAGES,
kinds = listOf(PrivateDmEvent.KIND), kinds = listOf(EncryptedDmEvent.KIND),
authors = listOf(account.userProfile().pubkeyHex), authors = listOf(account.userProfile().pubkeyHex),
tags = mapOf("p" to pTags.toList()), tags = mapOf("p" to pTags.toList()),
since = since, since = since,
@@ -68,7 +68,7 @@ import com.vitorpamplona.quartz.nip01Core.tags.geohash.geohash
import com.vitorpamplona.quartz.nip01Core.tags.geohash.getGeoHash import com.vitorpamplona.quartz.nip01Core.tags.geohash.getGeoHash
import com.vitorpamplona.quartz.nip01Core.tags.hashtags.hashtags import com.vitorpamplona.quartz.nip01Core.tags.hashtags.hashtags
import com.vitorpamplona.quartz.nip01Core.tags.references.references import com.vitorpamplona.quartz.nip01Core.tags.references.references
import com.vitorpamplona.quartz.nip04Dm.messages.PrivateDmEvent import com.vitorpamplona.quartz.nip04Dm.messages.EncryptedDmEvent
import com.vitorpamplona.quartz.nip10Notes.content.findHashtags import com.vitorpamplona.quartz.nip10Notes.content.findHashtags
import com.vitorpamplona.quartz.nip10Notes.content.findNostrEventUris import com.vitorpamplona.quartz.nip10Notes.content.findNostrEventUris
import com.vitorpamplona.quartz.nip10Notes.content.findURLs import com.vitorpamplona.quartz.nip10Notes.content.findURLs
@@ -78,7 +78,7 @@ import com.vitorpamplona.quartz.nip17Dm.base.ChatroomKey
import com.vitorpamplona.quartz.nip17Dm.base.NIP17Group import com.vitorpamplona.quartz.nip17Dm.base.NIP17Group
import com.vitorpamplona.quartz.nip17Dm.files.ChatMessageEncryptedFileHeaderEvent import com.vitorpamplona.quartz.nip17Dm.files.ChatMessageEncryptedFileHeaderEvent
import com.vitorpamplona.quartz.nip17Dm.messages.ChatMessageEvent import com.vitorpamplona.quartz.nip17Dm.messages.ChatMessageEvent
import com.vitorpamplona.quartz.nip17Dm.settings.ChatMessageRelayListEvent import com.vitorpamplona.quartz.nip17Dm.settings.DmRelayListEvent
import com.vitorpamplona.quartz.nip18Reposts.quotes.quotes import com.vitorpamplona.quartz.nip18Reposts.quotes.quotes
import com.vitorpamplona.quartz.nip19Bech32.toNpub import com.vitorpamplona.quartz.nip19Bech32.toNpub
import com.vitorpamplona.quartz.nip30CustomEmoji.emojis import com.vitorpamplona.quartz.nip30CustomEmoji.emojis
@@ -184,7 +184,7 @@ class ChatNewMessageViewModel :
combine(dmRelayListNoteFlows) { dmRelayListNotes -> combine(dmRelayListNoteFlows) { dmRelayListNotes ->
dmRelayListNotes dmRelayListNotes
.mapNotNull { noteState -> .mapNotNull { noteState ->
val noteEvent = noteState.note.event as? ChatMessageRelayListEvent val noteEvent = noteState.note.event as? DmRelayListEvent
if (noteEvent == null || noteEvent.relays().isEmpty()) { if (noteEvent == null || noteEvent.relays().isEmpty()) {
noteState.note.author noteState.note.author
} else { } else {
@@ -409,7 +409,7 @@ class ChatNewMessageViewModel :
if (replyId != null) { if (replyId != null) {
replyTo.value = LocalCache.checkGetOrCreateNote(replyId) replyTo.value = LocalCache.checkGetOrCreateNote(replyId)
} }
} else if (draftEvent is PrivateDmEvent) { } else if (draftEvent is EncryptedDmEvent) {
val recipientNPub = draftEvent.verifiedRecipientPubKey()?.let { Hex.decode(it).toNpub() } val recipientNPub = draftEvent.verifiedRecipientPubKey()?.let { Hex.decode(it).toNpub() }
toUsers.setTextAndPlaceCursorAtEnd("@$recipientNPub") toUsers.setTextAndPlaceCursorAtEnd("@$recipientNPub")
@@ -420,7 +420,7 @@ class ChatNewMessageViewModel :
} }
val draftText = val draftText =
if (draftEvent is PrivateDmEvent) { if (draftEvent is EncryptedDmEvent) {
accountViewModel.account.privateDMDecryptionCache.cachedDM(draftEvent) ?: "" accountViewModel.account.privateDMDecryptionCache.cachedDM(draftEvent) ?: ""
} else { } else {
draftEvent.content draftEvent.content

Some files were not shown because too many files have changed in this diff Show More