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 nip44Encrypt(...)
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 |
|---|---|---|---|---|
| 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) | `""` |
| 30384 | `addressables/AddressableAssertionEvent` | addressable | **target coordinate** `kind:pubkey:dtag` | `""` |
| 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
`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.
## 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.
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
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`).
`commons/.../ContactCardsState.kt` keys everything on `author == account` and ignores
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) —
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):
| Tag name | Accessor | Type | Semantics |
@@ -138,7 +138,7 @@ val providers: List<ServiceProviderTag> = updated.serviceProviders() //
val private = updated.privateTags(signer)?.serviceProviders() // private side
// Provider-style contact card (public metrics) — the GrapeRankPublisher pattern:
val card = ContactCardEvent.create(
val card = UserAssertionEvent.create(
targetUser = subjectPubkey,
signer = providerSigner,
publicInitializer = {
@@ -79,28 +79,28 @@ companion object {
### Zap Request/Receipt (kinds 9734, 9735)
```kotlin
class LnZapRequestEvent(...) : Event(...)
class ZapRequestEvent(...) : Event(...)
// Created by client, sent to Lightning Address
class LnZapEvent(...) : Event(...)
class ZapReceiptEvent(...) : Event(...)
// 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 */ }
```
### Long-Form Content (kind 30023)
```kotlin
class LongTextNoteEvent(...) : BaseAddressableEvent(...)
class LongFormContentEvent(...) : BaseAddressableEvent(...)
// Blog posts, articles
// Addressable via kind:pubkey:d-tag
```
### Lists (kinds 10000-30004)
```kotlin
sealed class PeopleListEvent : BaseAddressableEvent {
object MuteList : PeopleListEvent(10000)
object PinList : PeopleListEvent(10001)
object BookmarkList : PeopleListEvent(10003)
sealed class FollowSetEvent : BaseAddressableEvent {
object MuteList : FollowSetEvent(10000)
object PinList : FollowSetEvent(10001)
object BookmarkList : FollowSetEvent(10003)
// ... 18 list types total
}
```
@@ -279,7 +279,7 @@ val metadata2 = MetadataEvent.createNew(name = "Alice Updated", picture = "url2"
### Event Deletion
```kotlin
// Delete events
val deletion = DeletionEvent.create(
val deletion = DeletionRequestEvent.create(
deleteEvents = listOf(eventId1, eventId2),
reason = "Spam",
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) |
| 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 |
| 09 | `nip09Deletions/` | DeletionEvent.kt | Event deletion requests (kind 5) |
| 09 | `nip09Deletions/` | DeletionRequestEvent.kt | Event deletion requests (kind 5) |
| 11 | `nip11RelayInfo/` | RelayInformation.kt | Relay metadata |
| 13 | `nip13Pow/` | ProofOfWork.kt | Proof of work |
| 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) |
| 18 | `nip18Reposts/` | RepostEvent.kt, GenericRepostEvent.kt | Reposts (kind 6, 16) |
| 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) |
| 31 | `nip31Alts/` | Alt tags | Alt description tags |
| 36 | `nip36SensitiveContent/` | Content warnings | Content warning tags |
@@ -62,7 +62,7 @@ ent for NIP-04) |
| 46 | `nip46RemoteSigner/` | NostrConnectEvent.kt | Remote signer protocol (bunker) |
| 47 | `nip47WalletConnect/` | Nostr Wallet Connect | Wallet connection protocol |
| 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) |
| 59 | `nip59Giftwrap/` | GiftWrapEvent.kt | Gift-wrapped events for privacy |
| 75 | `nip75ZapGoals/` | ZapGoalEvent.kt | Zap goals (kind 9041) |
@@ -76,7 +76,7 @@ ent for NIP-04) |
| 35 | `nip35Torrents/` | Torrent events | Torrent tracking |
| 52 | `nip52Calendar/` | Calendar events | Calendar time-based/date-based (kinds 31922-31925) |
| 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 |
| 71 | `nip71Video/` | 7 video event types | Video events (kinds 34235, 35235, 1234, 1235) |
| 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) |
| 90 | `nip90Dvms/` | DVM job events | Data Vending Machines (DVMs) (kinds 5000-7000) |
| 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 |
| 99 | `nip99Classifieds/` | ClassifiedsEvent.kt | Classifieds/marketplace (kind 30402) |
| A0 | `nipA0VoiceMessages/` | Voice messages | Voice message events |
@@ -93,7 +93,7 @@ ent for NIP-04) |
### Web/Storage/Other
| 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) |
| 61 | `nip61PaymentRequest/` | Nut zaps | Cashu payment requests |
| 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)
```kotlin
import com.vitorpamplona.quartz.nip57Zaps.LnZapRequestEvent
import com.vitorpamplona.quartz.nip57Zaps.ZapRequestEvent
val template = LnZapRequestEvent.build(
val template = ZapRequestEvent.build(
message = "Great post!",
relays = listOf("wss://relay.damus.io"),
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)
@@ -678,9 +678,9 @@ val (dmEvent, giftWrap) = NIP17Factory.create(
### NIP-23 — Long-form article (kind 30023)
```kotlin
import com.vitorpamplona.quartz.nip23LongContent.LongTextNoteEvent
import com.vitorpamplona.quartz.nip23LongContent.LongFormContentEvent
val template = LongTextNoteEvent.build(
val template = LongFormContentEvent.build(
body = markdownContent,
title = "My Article",
image = "https://example.com/cover.jpg",
@@ -882,16 +882,16 @@ use `Nip11RelayInformation.fromJson(json)`.
| 0 | User metadata | 01 | `MetadataEvent` |
| 1 | Text note | 10 | `TextNoteEvent` |
| 3 | Follow list | 02 | `ContactListEvent` |
| 4 | Legacy DM | 04 | `PrivateDmEvent` |
| 5 | Deletion | 09 | `DeletionEvent` |
| 4 | Legacy DM | 04 | `EncryptedDmEvent` |
| 5 | Deletion | 09 | `DeletionRequestEvent` |
| 6 | Repost | 18 | `RepostEvent` |
| 7 | Reaction | 25 | `ReactionEvent` |
| 14 | Chat message (sealed) | 17 | `NIP17GroupMessage` |
| 1059 | Gift wrap | 59 | `GiftWrapEvent` |
| 9734 | Zap request | 57 | `LnZapRequestEvent` |
| 9735 | Zap receipt | 57 | `LnZapEvent` |
| 9734 | Zap request | 57 | `ZapRequestEvent` |
| 9735 | Zap receipt | 57 | `ZapReceiptEvent` |
| 10002 | Relay list | 65 | `AdvertisedRelayListEvent` |
| 30023 | Long-form content | 23 | `LongTextNoteEvent` |
| 30023 | Long-form content | 23 | `LongFormContentEvent` |
## Related Skills
@@ -26,7 +26,7 @@ Separator legend: **NL** = `joinToString("\n")`, **SP** = `joinToString(" ")`.
| 42 | ChannelMessageEvent | nip28PublicChat/message | `content` |
| 54 | PodcastEpisodeEvent | nipF4Podcasts/episode | `listOfNotNull(title(), description(), content)` NL |
| 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 |
| 1068 | PollEvent | nip88Polls/poll | `buildString { append(content); options().forEach { append('\n').append(it.label) } }` |
| 1111 | CommentEvent | nip22Comments | `(listOf(content) + tags.hashtags())` NL |
@@ -48,20 +48,20 @@ Separator legend: **NL** = `joinToString("\n")`, **SP** = `joinToString(" ")`.
| 2004 | TorrentCommentEvent | nip35Torrents | `content` |
| 2473 | BirdDetectionEvent | experimental/birdstar | `listOfNotNull(summary(), speciesName())` NL |
| 3302 | ConcordChatEditEvent | concord/cord03Channels | `content` |
| 5050 | NIP90TextGenerationRequestEvent | nip90Dvms/textGeneration | `inputs().filter { it.type == "prompt" \|\| it.type == "text" }.joinToString(" ") { it.value }` (SP) |
| 5100 | NIP90ImageGenerationRequestEvent | nip90Dvms/imageGeneration | `listOfNotNull(prompt(), negativePrompt()).joinToString(" ")` (SP) |
| 5050 | DvmTextGenerationRequestEvent | nip90Dvms/textGeneration | `inputs().filter { it.type == "prompt" \|\| it.type == "text" }.joinToString(" ") { it.value }` (SP) |
| 5100 | DvmImageGenerationRequestEvent | nip90Dvms/imageGeneration | `listOfNotNull(prompt(), negativePrompt()).joinToString(" ")` (SP) |
| 5129 | NappletSnapshotEvent | nip5dNapplets | `listOfNotNull(title(), description())` NL |
| 5250 | NIP90TextToSpeechRequestEvent | nip90Dvms/textToSpeech | `text() ?: ""` |
| 5302 | NIP90ContentSearchRequestEvent | nip90Dvms/contentSearch | `searchQuery() ?: ""` |
| 5303 | NIP90PeopleSearchRequestEvent | nip90Dvms/peopleSearch | `searchQuery() ?: ""` |
| 5250 | DvmTextToSpeechRequestEvent | nip90Dvms/textToSpeech | `text() ?: ""` |
| 5302 | DvmContentSearchRequestEvent | nip90Dvms/contentSearch | `searchQuery() ?: ""` |
| 5303 | DvmPeopleSearchRequestEvent | nip90Dvms/peopleSearch | `searchQuery() ?: ""` |
| 6969 | ZapPollEvent | experimental/zapPolls | `buildString { append(content); pollOptionsArray().forEach { append('\n').append(it.descriptor) } }` |
| 7516 | GeocacheFoundLogEvent | nipCCGeocaching/foundLog | `content` |
| 8333 | OnchainZapEvent | nipBCOnchainZaps/zap | `content` |
| 9002 | EditMetadataEvent | nip29RelayGroups/moderation | `(listOfNotNull(name(), about()) + hashtags())` NL |
| 9041 | GoalEvent | nip75ZapGoals | `listOfNotNull(summary(), content)` NL |
| 9002 | GroupEditMetadataEvent | nip29RelayGroups/moderation | `(listOfNotNull(name(), about()) + hashtags())` NL |
| 9041 | ZapGoalEvent | nip75ZapGoals | `listOfNotNull(summary(), content)` NL |
| 9321 | NutzapEvent | nip61Nutzaps/nutzap | `content` |
| 9734 | LnZapRequestEvent | nip57Zaps | `content` |
| 9735 | LnZapEvent | nip57Zaps | `zapRequest?.content.orEmpty()` — indexes the **embedded 9734's** content |
| 9734 | ZapRequestEvent | nip57Zaps | `content` |
| 9735 | ZapReceiptEvent | nip57Zaps | `zapRequest?.content.orEmpty()` — indexes the **embedded 9734's** content |
| 9736 | Bolt12ZapEvent | nipB1Bolt12Zaps/zap | `content` |
| 9737 | Bolt12ZapIntentEvent | nipB1Bolt12Zaps/intent | `content` |
| 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 |
| 15128 | RootSiteEvent | nip5aStaticWebsites | `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 |
| 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 |
| 30005 | VideoCurationSetEvent | nip51Lists/videoCurationSet | `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") } ?: ""` |
| 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") } ?: ""` |
| 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 |
| 30054 | Podcasting20EpisodeEvent | nipXXPodcasting20/episode | `(listOfNotNull(title(), description(), content) + topics())` 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 |
| 30312 | MeetingSpaceEvent | nip53LiveActivities/meetingSpaces | `listOfNotNull(room(), summary(), content)` NL |
| 30313 | MeetingRoomEvent | nip53LiveActivities/meetingSpaces | `listOfNotNull(title(), summary())` NL |
| 30315 | StatusEvent | nip38UserStatus | `content` |
| 30382 | ContactCardEvent | nip85TrustedAssertions/users | `(listOfNotNull(petName(), summary()) + topics())` NL — public tags only, never the NIP-44 content |
| 30315 | UserStatusEvent | nip38UserStatus | `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 |
| 30393 | EventTrustedListEvent | experimental/trustedLists/events | 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 |
| 30620 | WorkflowDefEvent | buzz/workflow | `listOfNotNull(name(), 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 |
| 31871 | AttestationEvent | experimental/attestations/attestation | `content` |
| 31872 | AttestationRequestEvent | experimental/attestations/request | `content` |
@@ -119,15 +119,15 @@ Separator legend: **NL** = `joinToString("\n")`, **SP** = `joinToString(" ")`.
| 31890 | FeedDefinitionEvent | feedDefinition | `title().orEmpty()` |
| 31922 | CalendarDateSlotEvent | nip52Calendar/appt/day | `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` |
| 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 |
| 33401 | ExerciseTemplateEvent | experimental/fitness/workout | `listOfNotNull(title(), content)` NL |
| 33863 | FundraiserEvent | experimental/agora | `listOfNotNull(title(), content)` NL |
| 34139 | MusicPlaylistEvent | experimental/music/playlist | `listOfNotNull(title(), description(), content)` NL |
| 34235 | VideoHorizontalEvent | nip71Video | inherited `AddressableVideoEvent`: `listOfNotNull(title(), content)` NL |
| 34236 | VideoVerticalEvent | nip71Video | inherited `AddressableVideoEvent`: `listOfNotNull(title(), content)` NL |
| 34235 | AddressableNormalVideoEvent | 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 |
| 35128 | NamedSiteEvent | nip5aStaticWebsites | `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 |
| 38383 | P2POrderEvent | nip69P2pOrderEvents | `(listOfNotNull(makerName(), currency()) + paymentMethods().orEmpty()).joinToString(" ")` (SP) |
| 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 |
| 39307 | TextTrackEvent | nip71Video/textTrack | `content` (the WebVTT cue text) |
| 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.metadata.MetadataEvent
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.nip28PublicChat.list.ChannelListEvent
import com.vitorpamplona.quartz.nip28PublicChat.list.PublicChatListEvent
import com.vitorpamplona.quartz.nip37Drafts.privateOutbox.PrivateOutboxRelayListEvent
import com.vitorpamplona.quartz.nip47WalletConnect.Nip47WalletConnect
import com.vitorpamplona.quartz.nip50Search.SearchRelayListEvent
import com.vitorpamplona.quartz.nip51Lists.favoriteAlgoFeedsList.FavoriteAlgoFeedsListEvent
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.relayLists.BlockedRelayListEvent
import com.vitorpamplona.quartz.nip51Lists.relayLists.FavoriteRelayListEvent
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.simpleGroupList.SimpleGroupListEvent
import com.vitorpamplona.quartz.nip65RelayList.AdvertisedRelayListEvent
@@ -887,19 +887,19 @@ object LocalPreferences {
val pendingAttestations = async { parseOrNull<Map<HexKey, String>>(pendingAttestationsStr) ?: mapOf() }
val latestUserMetadata = async { parseEventOrNull<MetadataEvent>(latestUserMetadataStr) }
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 latestSearchRelayList = async { parseEventOrNull<SearchRelayListEvent>(latestSearchRelayListStr) }
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 latestTrustedRelayList = async { parseEventOrNull<TrustedRelayListEvent>(latestTrustedRelayListStr) }
val latestMuteList = async { parseEventOrNull<MuteListEvent>(latestMuteListStr) }
val latestPrivateHomeRelayList = async { parseEventOrNull<PrivateOutboxRelayListEvent>(latestPrivateHomeRelayListStr) }
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 latestHashtagList = async { parseEventOrNull<HashtagListEvent>(latestHashtagListStr) }
val latestHashtagList = async { parseEventOrNull<InterestListEvent>(latestHashtagListStr) }
val latestGeohashList = async { parseEventOrNull<GeohashListEvent>(latestGeohashListStr) }
val latestEphemeralList = async { parseEventOrNull<EphemeralChatListEvent>(latestEphemeralListStr) }
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.nip03Timestamp.OtsResolver
import com.vitorpamplona.quartz.nip04Dm.PrivateDMCache
import com.vitorpamplona.quartz.nip04Dm.messages.PrivateDmEvent
import com.vitorpamplona.quartz.nip09Deletions.DeletionEvent
import com.vitorpamplona.quartz.nip04Dm.messages.EncryptedDmEvent
import com.vitorpamplona.quartz.nip09Deletions.DeletionRequestEvent
import com.vitorpamplona.quartz.nip10Notes.TextNoteEvent
import com.vitorpamplona.quartz.nip10Notes.content.findHashtags
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.nip56Reports.ReportEvent
import com.vitorpamplona.quartz.nip56Reports.ReportType
import com.vitorpamplona.quartz.nip57Zaps.LnZapEvent
import com.vitorpamplona.quartz.nip57Zaps.LnZapPrivateEvent
import com.vitorpamplona.quartz.nip57Zaps.LnZapRequestEvent
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.zapSplits
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.nip89AppHandlers.clientTag.NostrSignerWithClientTag
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.imetas
import com.vitorpamplona.quartz.nip94FileMetadata.FileHeaderEvent
import com.vitorpamplona.quartz.nip94FileMetadata.FileMetadataEvent
import com.vitorpamplona.quartz.nip94FileMetadata.blurhash
import com.vitorpamplona.quartz.nip94FileMetadata.dimension
import com.vitorpamplona.quartz.nip94FileMetadata.fileSize
@@ -1283,7 +1283,7 @@ class Account(
suspend fun updateZapAmounts(
amountSet: List<Long>,
selectedZapType: LnZapEvent.ZapType,
selectedZapType: ZapReceiptEvent.ZapType,
nip47Update: Nip47WalletConnect.Nip47URINorm?,
) {
var changed = false
@@ -1707,7 +1707,7 @@ class Account(
if (myNotes.isNotEmpty()) {
// chunks in 200 elements to avoid going over the 65KB limit for events.
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 myRelayList = outboxRelays.flow.value.toMutableSet()
chunkedList.forEach {
@@ -1738,7 +1738,7 @@ class Account(
val recipients = (targetEvent.taggedUserIds() + targetEvent.pubKey).distinct().minus(signer.pubKey)
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 (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)
cache.justConsumeMyOwnEvent(deletionEvent)
}
@@ -2176,7 +2176,7 @@ class Account(
suspend fun deleteWebBookmark(event: WebBookmarkEvent) {
if (!isWriteable()) return
val template = DeletionEvent.build(listOf(event))
val template = DeletionRequestEvent.build(listOf(event))
val signedEvent = signer.sign(template)
cache.justConsumeMyOwnEvent(signedEvent)
@@ -2212,7 +2212,7 @@ class Account(
if (!isWriteable()) return
if (event.pubKey != signer.pubKey) return
val template = DeletionEvent.build(listOf(event))
val template = DeletionRequestEvent.build(listOf(event))
val signedEvent = signer.sign(template)
cache.justConsumeMyOwnEvent(signedEvent)
@@ -2597,7 +2597,7 @@ class Account(
}
}
} else {
FileHeaderEvent.build(url, alt) {
FileMetadataEvent.build(url, alt) {
hash(headerInfo.hash)
fileSize(headerInfo.size)
@@ -2711,7 +2711,7 @@ class Account(
val extraRelays = draftNote.relays
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()
@@ -2937,7 +2937,7 @@ class Account(
broadcast.forEach { client.publish(it, relayList) }
}
override suspend fun sendNip04PrivateMessage(eventTemplate: EventTemplate<PrivateDmEvent>) {
override suspend fun sendNip04PrivateMessage(eventTemplate: EventTemplate<EncryptedDmEvent>) {
if (!isWriteable()) return
val newEvent = signer.sign(eventTemplate)
@@ -3481,10 +3481,10 @@ class Account(
suspend fun requestDVMContentDiscovery(
dvmPublicKey: User,
onReady: (event: NIP90ContentDiscoveryRequestEvent, relays: Set<NormalizedRelayUrl>) -> Unit,
onReady: (event: DvmContentDiscoveryRequestEvent, relays: Set<NormalizedRelayUrl>) -> Unit,
) {
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 =
dvmPublicKey.inboxRelays()?.toSet()?.ifEmpty { null }
@@ -3503,8 +3503,8 @@ class Account(
return if (isWriteable()) {
when {
event is PrivateDmEvent -> privateDMDecryptionCache.cachedDM(event)
event is LnZapRequestEvent && event.isPrivateZap() -> privateZapsDecryptionCache.cachedPrivateZap(event)?.content
event is EncryptedDmEvent -> privateDMDecryptionCache.cachedDM(event)
event is ZapRequestEvent && event.isPrivateZap() -> privateZapsDecryptionCache.cachedPrivateZap(event)?.content
event is DraftWrapEvent -> draftsDecryptionCache.preCachedDraft(event)?.content
else -> event.content
}
@@ -3519,11 +3519,11 @@ class Account(
suspend fun decryptContent(note: Note): String? {
val event = note.event
return when {
event is PrivateDmEvent && isWriteable() -> {
event is EncryptedDmEvent && isWriteable() -> {
privateDMDecryptionCache.decryptDM(event)
}
event is LnZapRequestEvent && isWriteable() -> {
event is ZapRequestEvent && isWriteable() -> {
if (event.isPrivateZap()) {
if (isWriteable()) {
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) }
@@ -3602,7 +3602,7 @@ class Account(
note.countReportAuthorsBy(followingKeySet()) < reportWarningThreshold
}
fun isDecryptedContentHidden(noteEvent: PrivateDmEvent): Boolean =
fun isDecryptedContentHidden(noteEvent: EncryptedDmEvent): Boolean =
if (hiddenUsers.flow.value.hiddenWordsCase
.isNotEmpty()
) {
@@ -58,15 +58,15 @@ import com.vitorpamplona.quartz.nip29RelayGroups.GroupId
import com.vitorpamplona.quartz.nip29RelayGroups.hTag
import com.vitorpamplona.quartz.nip29RelayGroups.metadata.GroupMetadataEvent
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.EditMetadataEvent
import com.vitorpamplona.quartz.nip29RelayGroups.moderation.PutUserEvent
import com.vitorpamplona.quartz.nip29RelayGroups.moderation.RemoveUserEvent
import com.vitorpamplona.quartz.nip29RelayGroups.moderation.UpdatePinListEvent
import com.vitorpamplona.quartz.nip29RelayGroups.moderation.GroupCreateInviteEvent
import com.vitorpamplona.quartz.nip29RelayGroups.moderation.GroupEditMetadataEvent
import com.vitorpamplona.quartz.nip29RelayGroups.moderation.GroupPutUserEvent
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.request.JoinRequestEvent
import com.vitorpamplona.quartz.nip29RelayGroups.request.LeaveRequestEvent
import com.vitorpamplona.quartz.nip29RelayGroups.request.GroupJoinRequestEvent
import com.vitorpamplona.quartz.nip29RelayGroups.request.GroupLeaveRequestEvent
import com.vitorpamplona.quartz.nip29RelayGroups.tags.GroupIdTag
import com.vitorpamplona.quartz.nip7DThreads.ThreadEvent
import com.vitorpamplona.quartz.utils.RandomInstance
@@ -92,7 +92,7 @@ class AccountRelayGroupActions(
channel: RelayGroupChannel,
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.follow(channel)
}
@@ -295,7 +295,7 @@ class AccountRelayGroupActions(
/** Send a kind 9022 leave request to the host relay and drop it from our list. */
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.unfollow(channel)
}
@@ -351,7 +351,7 @@ class AccountRelayGroupActions(
) { listOf(relay) }
val edit =
EditMetadataEvent.build(
GroupEditMetadataEvent.build(
groupId,
name = name,
about = about,
@@ -405,7 +405,7 @@ class AccountRelayGroupActions(
channel: RelayGroupChannel,
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() }
}
@@ -418,7 +418,7 @@ class AccountRelayGroupActions(
channel: RelayGroupChannel,
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() }
}
@@ -445,7 +445,7 @@ class AccountRelayGroupActions(
channel: RelayGroupChannel,
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() }
}
@@ -470,7 +470,7 @@ class AccountRelayGroupActions(
} else {
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() }
}
@@ -525,7 +525,7 @@ class AccountRelayGroupActions(
// 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 template =
EditMetadataEvent.build(
GroupEditMetadataEvent.build(
channel.groupId.id,
name = name,
about = about,
@@ -549,7 +549,7 @@ class AccountRelayGroupActions(
channel: RelayGroupChannel,
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() }
}
}
@@ -59,8 +59,8 @@ import com.vitorpamplona.quartz.nip01Core.crypto.KeyPair
import com.vitorpamplona.quartz.nip01Core.metadata.MetadataEvent
import com.vitorpamplona.quartz.nip02FollowList.ContactListEvent
import com.vitorpamplona.quartz.nip17Dm.base.ChatroomKey
import com.vitorpamplona.quartz.nip17Dm.settings.ChatMessageRelayListEvent
import com.vitorpamplona.quartz.nip28PublicChat.list.ChannelListEvent
import com.vitorpamplona.quartz.nip17Dm.settings.DmRelayListEvent
import com.vitorpamplona.quartz.nip28PublicChat.list.PublicChatListEvent
import com.vitorpamplona.quartz.nip37Drafts.DraftWrapEvent
import com.vitorpamplona.quartz.nip37Drafts.privateOutbox.PrivateOutboxRelayListEvent
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.nip51Lists.favoriteAlgoFeedsList.FavoriteAlgoFeedsListEvent
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.relayLists.BlockedRelayListEvent
import com.vitorpamplona.quartz.nip51Lists.relayLists.FavoriteRelayListEvent
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.simpleGroupList.SimpleGroupListEvent
import com.vitorpamplona.quartz.nip55AndroidSigner.api.CommandType
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.nip61Nutzaps.info.NutzapInfoEvent
import com.vitorpamplona.quartz.nip65RelayList.AdvertisedRelayListEvent
@@ -203,20 +203,20 @@ class AccountSettings(
val showMessagesInNotifications: MutableStateFlow<Boolean> = MutableStateFlow(true),
var backupUserMetadata: MetadataEvent? = null,
var backupContactList: ContactListEvent? = null,
var backupDMRelayList: ChatMessageRelayListEvent? = null,
var backupDMRelayList: DmRelayListEvent? = null,
var backupKeyPackageRelayList: KeyPackageRelayListEvent? = null,
var backupNIP65RelayList: AdvertisedRelayListEvent? = null,
var backupSearchRelayList: SearchRelayListEvent? = null,
var backupIndexRelayList: IndexerRelayListEvent? = null,
var backupRelayFeedsList: RelayFeedsListEvent? = null,
var backupRelayFeedsList: FavoriteRelayListEvent? = null,
var backupBlockedRelayList: BlockedRelayListEvent? = null,
var backupTrustedRelayList: TrustedRelayListEvent? = null,
var backupMuteList: MuteListEvent? = null,
var backupPrivateHomeRelayList: PrivateOutboxRelayListEvent? = null,
var backupAppSpecificData: AppSpecificDataEvent? = null,
var backupChannelList: ChannelListEvent? = null,
var backupChannelList: PublicChatListEvent? = null,
var backupCommunityList: CommunityListEvent? = null,
var backupHashtagList: HashtagListEvent? = null,
var backupHashtagList: InterestListEvent? = null,
var backupFavoriteAlgoFeedsList: FavoriteAlgoFeedsListEvent? = null,
var backupGeohashList: GeohashListEvent? = null,
var backupEphemeralChatList: EphemeralChatListEvent? = null,
@@ -375,7 +375,7 @@ class AccountSettings(
// Zaps and Reactions
// ---
fun changeDefaultZapType(zapType: LnZapEvent.ZapType): Boolean {
fun changeDefaultZapType(zapType: ZapReceiptEvent.ZapType): Boolean {
if (syncedSettings.zaps.defaultZapType.value != zapType) {
syncedSettings.zaps.defaultZapType.tryEmit(zapType)
saveAccountSettings()
@@ -1075,7 +1075,7 @@ class AccountSettings(
when (incoming) {
is MetadataEvent -> updateUserMetadata(incoming)
is ContactListEvent -> updateContactListTo(incoming)
is ChatMessageRelayListEvent -> updateDMRelayList(incoming)
is DmRelayListEvent -> updateDMRelayList(incoming)
is KeyPackageRelayListEvent -> updateKeyPackageRelayList(incoming)
is AdvertisedRelayListEvent -> updateNIP65RelayList(incoming)
is CashuWalletEvent -> updateCashuWallet(incoming)
@@ -1084,13 +1084,13 @@ class AccountSettings(
is Bolt12OfferListEvent -> updateBolt12Offers(incoming)
is SearchRelayListEvent -> updateSearchRelayList(incoming)
is IndexerRelayListEvent -> updateIndexRelayList(incoming)
is RelayFeedsListEvent -> updateRelayFeedList(incoming)
is FavoriteRelayListEvent -> updateRelayFeedList(incoming)
is BlockedRelayListEvent -> updateBlockedRelayList(incoming)
is TrustedRelayListEvent -> updateTrustedRelayList(incoming)
is PrivateOutboxRelayListEvent -> updatePrivateHomeRelayList(incoming)
is ChannelListEvent -> updateChannelListTo(incoming)
is PublicChatListEvent -> updateChannelListTo(incoming)
is GeohashListEvent -> updateGeohashListTo(incoming)
is HashtagListEvent -> updateHashtagListTo(incoming)
is InterestListEvent -> updateHashtagListTo(incoming)
is FavoriteAlgoFeedsListEvent -> updateFavoriteAlgoFeedsListTo(incoming)
is CommunityListEvent -> updateCommunityListTo(incoming)
is EphemeralChatListEvent -> updateEphemeralChatListTo(incoming)
@@ -1140,7 +1140,7 @@ class AccountSettings(
}
}
fun updateDMRelayList(newDMRelayList: ChatMessageRelayListEvent?) {
fun updateDMRelayList(newDMRelayList: DmRelayListEvent?) {
if (newDMRelayList == null) return
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 (backupGuard.accept(backupRelayFeedsList, newRelayFeedList, isEmpty = newRelayFeedList.tags.isEmpty()) { updateRelayFeedList(newRelayFeedList) }) {
@@ -1322,7 +1322,7 @@ class AccountSettings(
override fun channelList() = backupChannelList
override fun updateChannelListTo(newChannelList: ChannelListEvent?) {
override fun updateChannelListTo(newChannelList: PublicChatListEvent?) {
if (newChannelList == null) return
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 (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.ui.screen.loggedIn.notifications.equalImmutableLists
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.toImmutableList
import kotlinx.coroutines.flow.MutableStateFlow
@@ -265,7 +265,7 @@ class AccountVideoPlayerPreferences(
@Stable
class AccountZapPreferences(
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.service.pow.PoWCategory
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 java.util.Locale
@@ -207,7 +207,7 @@ class AccountZapPreferencesInternal(
// and so the on-chain-eligible subset round-trips back for them; on load it
// is unioned into [zapAmountChoices]. See AccountSyncedSettings.
var onchainZapAmountChoices: List<Long> = DefaultOnchainZapAmounts,
val defaultZapType: LnZapEvent.ZapType = LnZapEvent.ZapType.PUBLIC,
val defaultZapType: ZapReceiptEvent.ZapType = ZapReceiptEvent.ZapType.PUBLIC,
)
@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.Request
import com.vitorpamplona.quartz.nip47WalletConnect.rpc.Response
import com.vitorpamplona.quartz.nip57Zaps.LnZapEvent
import com.vitorpamplona.quartz.nip57Zaps.LnZapRequestEvent
import com.vitorpamplona.quartz.nip57Zaps.ZapReceiptEvent
import com.vitorpamplona.quartz.nip57Zaps.ZapRequestEvent
import com.vitorpamplona.quartz.nipB1Bolt12Zaps.builder.Bolt12ZapBuilder
import com.vitorpamplona.quartz.nipB1Bolt12Zaps.verify.Bolt12ZapValidation
import com.vitorpamplona.quartz.utils.Log
@@ -74,12 +74,12 @@ class AccountZapActions(
event: Event,
pollOption: Int?,
message: String = "",
zapType: LnZapEvent.ZapType,
zapType: ZapReceiptEvent.ZapType,
toUser: User?,
additionalRelays: Set<NormalizedRelayUrl>? = null,
amountMillisats: Long? = null,
lnurl: String? = null,
) = LnZapRequestEvent.create(
) = ZapRequestEvent.create(
zappedEvent = event,
// 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
@@ -205,7 +205,7 @@ class AccountZapActions(
offer: String,
amountMillisats: Long,
message: String,
zapType: LnZapEvent.ZapType,
zapType: ZapReceiptEvent.ZapType,
// (messageResId, detail) — the caller localizes; detail carries a wallet error, if any.
onError: (StringResource, String?) -> Unit,
// (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
// 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 ->
account.scope.launch {
try {
@@ -228,7 +228,7 @@ class AccountZapActions(
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
// 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
@@ -284,12 +284,12 @@ class AccountZapActions(
suspend fun createZapRequestFor(
user: User,
message: String = "",
zapType: LnZapEvent.ZapType,
zapType: ZapReceiptEvent.ZapType,
amountMillisats: Long? = null,
lnurl: String? = null,
): LnZapRequestEvent {
): ZapRequestEvent {
val zapRequest =
LnZapRequestEvent.create(
ZapRequestEvent.create(
userHex = user.pubkeyHex,
relays = account.nip65RelayList.inboxFlow.value + (user.inboxRelays() ?: emptyList()),
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.NostrSignerInternal
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.nip37Drafts.DraftWrapEvent
import com.vitorpamplona.quartz.nip51Lists.bookmarkList.BookmarkListEvent
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.MeetingSpaceEvent
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.nip60Cashu.wallet.CashuWalletEvent
import com.vitorpamplona.quartz.nip61Nutzaps.info.NutzapInfoEvent
@@ -121,7 +121,7 @@ class EventBroadcaster(
event is AppSpecificDataEvent ||
event is BookmarkListEvent ||
event is OldBookmarkListEvent ||
event is LabeledBookmarkListEvent
event is BookmarkSetEvent
) {
return false
}
@@ -166,7 +166,7 @@ class EventBroadcaster(
}
// Seals, inner DM messages, and unsigned rumors never get broadcast
// 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()
}
@@ -322,7 +322,7 @@ class EventBroadcaster(
.toList()
}
val filter =
if (host.kind == SealedRumorEvent.KIND) {
if (host.kind == SealEvent.KIND) {
Filter(
kinds = listOf(host.kind),
ids = listOf(host.id),
@@ -383,7 +383,7 @@ class EventBroadcaster(
fun sendRestoredVersion(event: Event) {
when (event) {
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 CashuWalletEvent -> sendLiterallyEverywhere(event)
// 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.relay.filters.Filter
import com.vitorpamplona.quartz.nip01Core.relay.normalizer.NormalizedRelayUrl
import com.vitorpamplona.quartz.nip90Dvms.contentDiscoveryResponse.NIP90ContentDiscoveryResponseEvent
import com.vitorpamplona.quartz.nip90Dvms.status.NIP90StatusEvent
import com.vitorpamplona.quartz.nip90Dvms.contentDiscoveryResponse.DvmContentDiscoveryResponseEvent
import com.vitorpamplona.quartz.nip90Dvms.status.DvmStatusEvent
import com.vitorpamplona.quartz.utils.Log
import kotlinx.coroutines.CancellationException
import kotlinx.coroutines.CoroutineScope
@@ -60,7 +60,7 @@ data class FavoriteAlgoFeedsSnapshot(
val responseRelays: Set<NormalizedRelayUrl> = emptySet(),
val ids: Set<HexKey> = emptySet(),
val addresses: Set<String> = emptySet(),
val latestStatus: NIP90StatusEvent? = null,
val latestStatus: DvmStatusEvent? = null,
val errorMessage: String? = null,
)
@@ -139,9 +139,9 @@ class FavoriteAlgoFeedsOrchestrator(
launch {
account.cache
.observeLatestEvent<NIP90ContentDiscoveryResponseEvent>(
.observeLatestEvent<DvmContentDiscoveryResponseEvent>(
Filter(
kinds = listOf(NIP90ContentDiscoveryResponseEvent.KIND),
kinds = listOf(DvmContentDiscoveryResponseEvent.KIND),
tags = mapOf("e" to listOf(requestId)),
limit = 1,
),
@@ -159,9 +159,9 @@ class FavoriteAlgoFeedsOrchestrator(
launch {
account.cache
.observeLatestEvent<NIP90StatusEvent>(
.observeLatestEvent<DvmStatusEvent>(
Filter(
kinds = listOf(NIP90StatusEvent.KIND),
kinds = listOf(DvmStatusEvent.KIND),
tags = mapOf("e" to listOf(requestId)),
limit = 1,
),
@@ -26,7 +26,7 @@ import com.vitorpamplona.amethyst.commons.model.cache.LocalCache
import com.vitorpamplona.amethyst.model.AccountSettings
import com.vitorpamplona.quartz.nip01Core.relay.normalizer.NormalizedRelayUrl
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 kotlinx.coroutines.CoroutineScope
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
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 getDMRelayList(): ChatMessageRelayListEvent? = dmListNote.event as? ChatMessageRelayListEvent
fun getDMRelayList(): DmRelayListEvent? = dmListNote.event as? DmRelayListEvent
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()
}
@@ -70,16 +70,16 @@ class DmRelayListState(
emptySet(),
)
suspend fun saveRelayList(dmRelays: List<NormalizedRelayUrl>): ChatMessageRelayListEvent {
suspend fun saveRelayList(dmRelays: List<NormalizedRelayUrl>): DmRelayListEvent {
val relayListForDMs = getDMRelayList()
return if (relayListForDMs != null && relayListForDMs.tags.isNotEmpty()) {
ChatMessageRelayListEvent.updateRelayList(
DmRelayListEvent.updateRelayList(
earlierVersion = relayListForDMs,
relays = dmRelays,
signer = signer,
)
} else {
ChatMessageRelayListEvent.create(
DmRelayListEvent.create(
relays = dmRelays,
signer = signer,
)
@@ -99,7 +99,7 @@ class DmRelayListState(
Log.d("AccountRegisterObservers", "NIP-17 Relay List Collector Start")
getDMRelayListFlow().collect {
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)
}
}
@@ -33,7 +33,7 @@ import com.vitorpamplona.amethyst.model.Account
import com.vitorpamplona.quartz.nip01Core.signers.NostrSigner
import com.vitorpamplona.quartz.nip01Core.signers.SignerExceptions
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.emoji
import com.vitorpamplona.quartz.nip30CustomEmoji.pack.EmojiPackEvent
@@ -129,12 +129,12 @@ class OwnedEmojiPacksState(
.onStart { emit(listFeedFlow.value.getPack(dTag)) }
.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 =
notes.anyNotNullEvent { event ->
if (event.pubKey == signer.pubKey) {
event is EmojiPackEvent || (event is DeletionEvent && event.hasAnyDeletedOwnedEmojiPacks())
event is EmojiPackEvent || (event is DeletionRequestEvent && event.hasAnyDeletedOwnedEmojiPacks())
} else {
false
}
@@ -269,7 +269,7 @@ class OwnedEmojiPacksState(
account: Account,
) {
val packEvent = getOwnedEmojiPackEvent(dTag) ?: return
val deletionEventTemplate = DeletionEvent.build(listOf(packEvent))
val deletionEventTemplate = DeletionRequestEvent.build(listOf(packEvent))
val deletionEvent = signer.sign(deletionEventTemplate)
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.cache.NostrWalletConnectRequestCache
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.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.PayInvoiceMethod
import com.vitorpamplona.quartz.nip47WalletConnect.rpc.PaymentReceivedNotification
@@ -199,12 +199,12 @@ class NwcSignerState(
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
return zapPaymentRequestDecryptionCache.value.decryptRequest(event)
}
override suspend fun decryptResponse(event: LnZapPaymentResponseEvent): Response? {
override suspend fun decryptResponse(event: NwcResponseEvent): Response? {
if (!hasWalletConnectSetup()) return null
return zapPaymentResponseDecryptionCache.value.decryptResponse(event)
}
@@ -252,7 +252,7 @@ class NwcSignerState(
request: Request,
onTimeout: () -> 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.
@@ -266,14 +266,14 @@ class NwcSignerState(
request: Request,
onTimeout: () -> Unit = {},
onResponse: (Response?) -> Unit,
): Pair<LnZapPaymentRequestEvent, NormalizedRelayUrl> {
): Pair<NwcRequestEvent, NormalizedRelayUrl> {
val walletService = walletUri ?: throw IllegalArgumentException("No NIP47 setup")
val walletSigner = buildSigner(walletService) ?: signer
val info = walletInfo(walletService)
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 =
NWCPaymentQueryState(
@@ -314,7 +314,7 @@ class NwcSignerState(
onTimeout: () -> Unit = {},
metadata: Map<String, Any?>? = null,
onResponse: (Response?) -> Unit,
): Pair<LnZapPaymentRequestEvent, NormalizedRelayUrl> {
): Pair<NwcRequestEvent, NormalizedRelayUrl> {
val walletService = defaultWalletUri.value ?: throw IllegalArgumentException("No NIP47 setup")
val info = walletInfo(walletService)
@@ -322,7 +322,7 @@ class NwcSignerState(
request.dropMetadataIfUnsupported(info)
val event =
LnZapPaymentRequestEvent.createRequest(
NwcRequestEvent.createRequest(
request,
walletService.pubKeyHex,
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.model.AccountSettings
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 kotlinx.coroutines.CoroutineScope
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
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 getHashtagList(): HashtagListEvent? = hashtagListNote.event as? HashtagListEvent
fun getHashtagList(): InterestListEvent? = hashtagListNote.event as? InterestListEvent
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()
}
@@ -75,31 +75,31 @@ class HashtagListState(
emptySet(),
)
suspend fun follow(hashtags: List<String>): HashtagListEvent {
suspend fun follow(hashtags: List<String>): InterestListEvent {
val hashtagList = getHashtagList()
return if (hashtagList == null) {
HashtagListEvent.create(hashtags, true, signer)
InterestListEvent.create(hashtags, true, signer)
} 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()
return if (hashtagList == null) {
HashtagListEvent.create(hashtag.lowercase(), true, signer)
InterestListEvent.create(hashtag.lowercase(), true, signer)
} 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()
return if (hashtagList != null) {
HashtagListEvent.remove(hashtagList, hashtag, signer)
InterestListEvent.remove(hashtagList, hashtag, signer)
} else {
null
}
@@ -118,7 +118,7 @@ class HashtagListState(
Log.d("AccountRegisterObservers", "Hashtag List Collector Start")
getHashtagListFlow().collect {
Log.d("AccountRegisterObservers") { "Hashtag List for ${signer.pubKey}" }
(it.note.event as? HashtagListEvent)?.let {
(it.note.event as? InterestListEvent)?.let {
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.model.Account
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.tags.TitleTag
import kotlinx.coroutines.CoroutineScope
@@ -106,12 +106,12 @@ class InterestSetsState(
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 =
notes.anyNotNullEvent { event ->
if (event.pubKey == signer.pubKey) {
event is InterestSetEvent || (event is DeletionEvent && event.hasAnyDeletedInterestSets())
event is InterestSetEvent || (event is DeletionRequestEvent && event.hasAnyDeletedInterestSets())
} else {
false
}
@@ -178,7 +178,7 @@ class InterestSetsState(
account: Account,
) {
val event = getInterestSetEvent(identifier)
val template = DeletionEvent.build(listOf(event))
val template = DeletionRequestEvent.build(listOf(event))
val deletionEvent = account.signer.sign(template)
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.signers.NostrSigner
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.BookmarkIdTag
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.image
import com.vitorpamplona.quartz.nip51Lists.labeledBookmarkList.title
@@ -62,7 +62,7 @@ class LabeledBookmarkListsState(
) {
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)
@@ -81,14 +81,14 @@ class LabeledBookmarkListsState(
.stateIn(scope, SharingStarted.Eagerly, emptySet())
@OptIn(ExperimentalCoroutinesApi::class)
val latestBookmarkLists: StateFlow<List<LabeledBookmarkListEvent>> =
val latestBookmarkLists: StateFlow<List<BookmarkSetEvent>> =
labeledBookmarkListNotes
.transformLatest { emitAll(it.updateFlow<LabeledBookmarkListEvent>()) }
.transformLatest { emitAll(it.updateFlow<BookmarkSetEvent>()) }
.onStart { emit(labeledBookmarkListNotes.value.events()) }
.flowOn(Dispatchers.IO)
.stateIn(scope, SharingStarted.Eagerly, emptyList())
suspend fun LabeledBookmarkListEvent.toLabeledBookmarkList() =
suspend fun BookmarkSetEvent.toLabeledBookmarkList() =
LabeledBookmarkList(
identifier = dTag(),
title = titleOrName() ?: dTag(),
@@ -98,7 +98,7 @@ class LabeledBookmarkListsState(
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 =
latestBookmarkLists
@@ -114,12 +114,12 @@ class LabeledBookmarkListsState(
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 =
notes.anyNotNullEvent { event ->
if (event.pubKey == signer.pubKey) {
event is LabeledBookmarkListEvent || (event is DeletionEvent && event.hasAnyDeletedBookmarkLists())
event is BookmarkSetEvent || (event is DeletionRequestEvent && event.hasAnyDeletedBookmarkLists())
} else {
false
}
@@ -143,7 +143,7 @@ class LabeledBookmarkListsState(
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) =
listFeedFlow
@@ -163,7 +163,7 @@ class LabeledBookmarkListsState(
account: Account,
) {
val newList =
LabeledBookmarkListEvent.create(
BookmarkSetEvent.create(
title = listName,
description = listDescription,
image = listImage,
@@ -202,7 +202,7 @@ class LabeledBookmarkListsState(
) {
val listEvent = getLabeledBookmarkListEvent(bookmarkList.identifier)
val renamedList =
LabeledBookmarkListEvent.modifyName(
BookmarkSetEvent.modifyName(
earlierVersion = listEvent,
newTitle = newName,
signer = account.signer,
@@ -217,7 +217,7 @@ class LabeledBookmarkListsState(
) {
val listEvent = getLabeledBookmarkListEvent(bookmarkList.identifier)
val modifiedList =
LabeledBookmarkListEvent.modifyDescription(
BookmarkSetEvent.modifyDescription(
earlierVersion = listEvent,
newDescription = newDescription,
signer = account.signer,
@@ -232,7 +232,7 @@ class LabeledBookmarkListsState(
account: Account,
) {
val clonedList =
LabeledBookmarkListEvent.create(
BookmarkSetEvent.create(
title = customCloneName ?: currentBookmarkList.title,
description = customCloneDescription ?: currentBookmarkList.description,
publicBookmarks = currentBookmarkList.publicBookmarks.toList(),
@@ -247,7 +247,7 @@ class LabeledBookmarkListsState(
account: Account,
) {
val listEvent = getLabeledBookmarkListEvent(bookmarkListIdentifier)
val deletionEventTemplate = DeletionEvent.build(listOf(listEvent))
val deletionEventTemplate = DeletionRequestEvent.build(listOf(listEvent))
val deletionEvent = account.signer.sign(deletionEventTemplate)
account.sendMyPublicAndPrivateOutbox(deletionEvent)
}
@@ -260,7 +260,7 @@ class LabeledBookmarkListsState(
) {
val currentBookmarkList = getLabeledBookmarkListEvent(bookmarkListIdentifier)
val updatedList =
LabeledBookmarkListEvent.addBookmark(
BookmarkSetEvent.addBookmark(
earlierVersion = currentBookmarkList,
bookmarkIdTag = bookmark,
isPrivate = isBookmarkPrivate,
@@ -277,7 +277,7 @@ class LabeledBookmarkListsState(
) {
val bookmarkList = getLabeledBookmarkListEvent(bookmarkListIdentifier)
val updatedList =
LabeledBookmarkListEvent.moveBookmark(
BookmarkSetEvent.moveBookmark(
earlierVersion = bookmarkList,
bookmarkIdTag = bookmark,
isCurrentlyPrivate = isBookmarkCurrentlyPrivate,
@@ -294,7 +294,7 @@ class LabeledBookmarkListsState(
) {
val currentBookmarkList = getLabeledBookmarkListEvent(bookmarkListIdentifier)
val updatedList =
LabeledBookmarkListEvent.removeBookmark(
BookmarkSetEvent.removeBookmark(
earlierVersion = currentBookmarkList,
bookmarkIdTag = bookmark,
isPrivate = isBookmarkPrivate,
@@ -311,7 +311,7 @@ class LabeledBookmarkListsState(
) {
if (deletedEventIds.isEmpty() && deletedAddresses.isEmpty()) return
val currentList = getLabeledBookmarkListNote(bookmarkListIdentifier)?.event as? LabeledBookmarkListEvent ?: return
val currentList = getLabeledBookmarkListNote(bookmarkListIdentifier)?.event as? BookmarkSetEvent ?: return
val newPublicTags =
currentList.tags
@@ -328,7 +328,7 @@ class LabeledBookmarkListsState(
val updatedList =
if (oldPrivateTags == null) {
if (newPublicTags.size == currentList.tags.size) return
LabeledBookmarkListEvent.resign(
BookmarkSetEvent.resign(
content = currentList.content,
tags = newPublicTags,
signer = account.signer,
@@ -344,7 +344,7 @@ class LabeledBookmarkListsState(
}
}.toTypedArray()
if (newPublicTags.size == currentList.tags.size && newPrivateTags.size == oldPrivateTags.size) return
LabeledBookmarkListEvent.resign(
BookmarkSetEvent.resign(
tags = newPublicTags,
privateTags = newPrivateTags,
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.update
import com.vitorpamplona.quartz.nip01Core.tags.dTag.dTag
import com.vitorpamplona.quartz.nip09Deletions.DeletionEvent
import com.vitorpamplona.quartz.nip51Lists.followList.FollowListEvent
import com.vitorpamplona.quartz.nip09Deletions.DeletionRequestEvent
import com.vitorpamplona.quartz.nip51Lists.followList.StarterPackEvent
import com.vitorpamplona.quartz.nip51Lists.followList.description
import com.vitorpamplona.quartz.nip51Lists.followList.image
import com.vitorpamplona.quartz.nip51Lists.followList.person
@@ -79,7 +79,7 @@ class FollowListsState(
// them from relays.
fun existingPeopleListNotes() =
cache.addressables
.filter(FollowListEvent.KIND, user.pubkeyHex)
.filter(StarterPackEvent.KIND, user.pubkeyHex)
.filter { it.event != null || !cache.hasBeenDeleted(it.address) }
val followListVersions = MutableStateFlow(0)
@@ -99,14 +99,14 @@ class FollowListsState(
.stateIn(scope, SharingStarted.Eagerly, emptySet())
@OptIn(ExperimentalCoroutinesApi::class)
val latestLists: StateFlow<List<FollowListEvent>> =
val latestLists: StateFlow<List<StarterPackEvent>> =
followListNotes
.transformLatest { emitAll(it.updateFlow<FollowListEvent>()) }
.transformLatest { emitAll(it.updateFlow<StarterPackEvent>()) }
.onStart { emit(followListNotes.value.events()) }
.flowOn(Dispatchers.IO)
.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>> =
latestLists
@@ -115,7 +115,7 @@ class FollowListsState(
.flowOn(Dispatchers.IO)
.stateIn(scope, SharingStarted.Eagerly, emptySet())
fun FollowListEvent.toUI() =
fun StarterPackEvent.toUI() =
PeopleList(
identifierTag = this.dTag(),
title = this.title() ?: this.dTag(),
@@ -125,7 +125,7 @@ class FollowListsState(
publicMembers = cache.load(this.followIdSet()),
)
fun List<FollowListEvent>.toUI() = this.map { it.toUI() }
fun List<StarterPackEvent>.toUI() = this.map { it.toUI() }
val uiListFlow =
latestLists
@@ -148,12 +148,12 @@ class FollowListsState(
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 =
notes.anyNotNullEvent { event ->
if (event.pubKey == signer.pubKey) {
event is FollowListEvent || (event is DeletionEvent && event.hasDeletedAnyFollowList())
event is StarterPackEvent || (event is DeletionRequestEvent && event.hasDeletedAnyFollowList())
} else {
false
}
@@ -181,7 +181,7 @@ class FollowListsState(
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())
@@ -198,7 +198,7 @@ class FollowListsState(
val dTag = UUID.randomUUID().toString()
val newListTemplate =
FollowListEvent.build(
StarterPackEvent.build(
name = name,
people = if (!isPrivate && member != null) listOf(member.toUserTag()) else emptyList(),
dTag = dTag,
@@ -262,7 +262,7 @@ class FollowListsState(
account: Account,
) {
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)
// 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
@@ -35,9 +35,9 @@ import com.vitorpamplona.amethyst.model.Account
import com.vitorpamplona.quartz.nip01Core.core.HexKey
import com.vitorpamplona.quartz.nip01Core.signers.NostrSigner
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.peopleList.PeopleListEvent
import com.vitorpamplona.quartz.nip51Lists.peopleList.FollowSetEvent
import com.vitorpamplona.quartz.nip51Lists.peopleList.description
import com.vitorpamplona.quartz.nip51Lists.peopleList.image
import com.vitorpamplona.quartz.nip51Lists.peopleList.title
@@ -78,8 +78,8 @@ class PeopleListsState(
// can subscribe and fetch them from relays.
fun existingPeopleListNotes() =
cache.addressables
.filter(PeopleListEvent.KIND, user.pubkeyHex)
.filter { it.dTag() != PeopleListEvent.BLOCK_LIST_D_TAG || it.event != null }
.filter(FollowSetEvent.KIND, user.pubkeyHex)
.filter { it.dTag() != FollowSetEvent.BLOCK_LIST_D_TAG || it.event != null }
.filter { it.event != null || !cache.hasBeenDeleted(it.address) }
val peopleListVersions = MutableStateFlow(0)
@@ -99,21 +99,21 @@ class PeopleListsState(
.stateIn(scope, SharingStarted.Eagerly, emptySet())
@OptIn(ExperimentalCoroutinesApi::class)
val latestLists: StateFlow<List<PeopleListEvent>> =
val latestLists: StateFlow<List<FollowSetEvent>> =
peopleListNotes
.transformLatest { emitAll(it.updateFlow<PeopleListEvent>()) }
.transformLatest { emitAll(it.updateFlow<FollowSetEvent>()) }
.onStart { emit(peopleListNotes.value.events()) }
.flowOn(Dispatchers.IO)
.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
.mapNotNull {
if (it.dTag() != PeopleListEvent.BLOCK_LIST_D_TAG) {
if (it.dTag() != FollowSetEvent.BLOCK_LIST_D_TAG) {
it.userIdSet()
} else {
null
@@ -127,7 +127,7 @@ class PeopleListsState(
.flowOn(Dispatchers.IO)
.stateIn(scope, SharingStarted.Eagerly, emptySet())
suspend fun PeopleListEvent.toUI() =
suspend fun FollowSetEvent.toUI() =
PeopleList(
identifierTag = this.dTag(),
title = this.titleOrName() ?: this.dTag(),
@@ -137,7 +137,7 @@ class PeopleListsState(
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 =
latestLists
@@ -158,12 +158,12 @@ class PeopleListsState(
.map { it.select(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 =
notes.anyNotNullEvent { event ->
if (event.pubKey == signer.pubKey) {
event is PeopleListEvent || (event is DeletionEvent && event.hasDeletedAnyPeopleList())
event is FollowSetEvent || (event is DeletionRequestEvent && event.hasDeletedAnyPeopleList())
} else {
false
}
@@ -191,7 +191,7 @@ class PeopleListsState(
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())
@@ -207,7 +207,7 @@ class PeopleListsState(
): String {
val dTag = UUID.randomUUID().toString()
val newListTemplate =
PeopleListEvent.build(
FollowSetEvent.build(
dTag = dTag,
title = listName,
publicMembers = if (!isPrivate && member != null) listOf(member.toUserTag()) else emptyList(),
@@ -252,7 +252,7 @@ class PeopleListsState(
account: Account,
) {
val newList =
PeopleListEvent.createListWithDescription(
FollowSetEvent.createListWithDescription(
dTag = UUID.randomUUID().toString(),
title = customCloneName ?: currentPeopleList.title,
description = customCloneDescription ?: currentPeopleList.description,
@@ -268,7 +268,7 @@ class PeopleListsState(
account: Account,
) {
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)
// 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
@@ -284,7 +284,7 @@ class PeopleListsState(
) {
val followListEvent = getPeopleList(identifierTag)
val newList =
PeopleListEvent.addUser(
FollowSetEvent.addUser(
earlierVersion = followListEvent,
pubKeyHex = user.pubkeyHex,
relayHint = user.bestRelayHint(),
@@ -302,7 +302,7 @@ class PeopleListsState(
) {
val followListEvent = getPeopleList(identifierTag)
val newList =
PeopleListEvent.addUserFirst(
FollowSetEvent.addUserFirst(
earlierVersion = followListEvent,
pubKeyHex = user.pubkeyHex,
relayHint = user.bestRelayHint(),
@@ -320,7 +320,7 @@ class PeopleListsState(
) {
val followListEvent = getPeopleList(identifierTag)
val newList =
PeopleListEvent.removeUser(
FollowSetEvent.removeUser(
earlierVersion = followListEvent,
pubKeyHex = user.pubkeyHex,
isUserPrivate = isPrivate,
@@ -27,7 +27,7 @@ import com.vitorpamplona.amethyst.commons.model.nip51Lists.relayFeeds.RelayFeeds
import com.vitorpamplona.amethyst.model.AccountSettings
import com.vitorpamplona.quartz.nip01Core.relay.normalizer.NormalizedRelayUrl
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 kotlinx.coroutines.CoroutineScope
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
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 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()
@@ -84,30 +84,30 @@ class RelayFeedListState(
emptySet(),
)
suspend fun addRelay(relay: NormalizedRelayUrl): RelayFeedsListEvent {
suspend fun addRelay(relay: NormalizedRelayUrl): FavoriteRelayListEvent {
val current = normalizeRelayFeedsListWithBackupNoDefaults(relayFeedListNote).toMutableList()
if (relay !in current) current.add(relay)
return saveRelayList(current)
}
suspend fun removeRelay(relay: NormalizedRelayUrl): RelayFeedsListEvent? {
suspend fun removeRelay(relay: NormalizedRelayUrl): FavoriteRelayListEvent? {
val current = normalizeRelayFeedsListWithBackupNoDefaults(relayFeedListNote).toMutableList()
if (relay !in current) return null
current.remove(relay)
return saveRelayList(current)
}
suspend fun saveRelayList(relayFeeds: List<NormalizedRelayUrl>): RelayFeedsListEvent {
suspend fun saveRelayList(relayFeeds: List<NormalizedRelayUrl>): FavoriteRelayListEvent {
val relayFeedsList = getRelayFeedsList()
return if (relayFeedsList != null && relayFeedsList.tags.isNotEmpty()) {
RelayFeedsListEvent.updateRelayList(
FavoriteRelayListEvent.updateRelayList(
earlierVersion = relayFeedsList,
relays = relayFeeds,
signer = signer,
)
} else {
RelayFeedsListEvent.create(
FavoriteRelayListEvent.create(
relays = relayFeeds,
signer = signer,
)
@@ -125,7 +125,7 @@ class RelayFeedListState(
Log.d("AccountRegisterObservers", "Relay feeds list Collector Start")
getRelayFeedsListFlow().collect {
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)
}
}
@@ -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.signers.NostrSigner
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.mintApi.DeterministicSecretFactory
import com.vitorpamplona.quartz.nip60Cashu.mintApi.MeltQuoteBolt11ResponseDto
@@ -486,7 +486,7 @@ class CashuWalletState(
// Process our own NIP-09 deletions inline rather than
// 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
// LargeSoftCache backed by WeakReferences, which can
// be cleared on any GC cycle. When the weak ref is
@@ -503,7 +503,7 @@ class CashuWalletState(
val ourDeleteIds =
all
.asSequence()
.filterIsInstance<DeletionEvent>()
.filterIsInstance<DeletionRequestEvent>()
.filter { it.pubKey == pubKey }
.flatMap { it.deleteEventIds().asSequence() }
.toSet()
@@ -1447,7 +1447,7 @@ class CashuWalletState(
}
val redundantIds = redundant.map { it.event.id }.toSet()
runCatching {
val template = DeletionEvent.build(redundant.map { it.event })
val template = DeletionRequestEvent.build(redundant.map { it.event })
val signed = signer.sign(template)
publishEvent(signed)
}.onFailure {
@@ -67,7 +67,7 @@ import com.vitorpamplona.amethyst.model.Account
import com.vitorpamplona.quartz.lightning.LnInvoiceUtil
import com.vitorpamplona.quartz.nip01Core.core.fastForEach
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.nip65RelayList.AdvertisedRelayListEvent
import org.jetbrains.compose.resources.PluralStringResource
@@ -199,7 +199,7 @@ class NappletConsentSummary(
oneRemoved = Res.string.napplet_consent_diff_unmute_one,
)
// 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"))
?.let { Consequence(it) }
// 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.quartz.nip01Core.relay.filters.Filter
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.definition.BadgeDefinitionEvent
import kotlinx.serialization.json.add
@@ -137,7 +137,7 @@ class AccountIdentityReader(
val receipts =
cache
.filter(Filter(kinds = listOf(9735), tags = mapOf("p" to listOf(pubkey))))
.mapNotNull { it.event as? LnZapEvent }
.mapNotNull { it.event as? ZapReceiptEvent }
return buildJsonArray {
receipts.forEach { zap ->
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.Response
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.podcasts.PodcastBoostagram
import com.vitorpamplona.quartz.podcasts.PodcastValue
@@ -91,7 +91,7 @@ class V4VPaymentHandler(
onProgress: (percent: Float) -> Unit,
onPayInvoicesViaIntent: (invoices: List<String>) -> Unit,
asZap: Boolean = false,
zapType: LnZapEvent.ZapType = LnZapEvent.ZapType.PUBLIC,
zapType: ZapReceiptEvent.ZapType = ZapReceiptEvent.ZapType.PUBLIC,
) = withContext(Dispatchers.IO) {
val shares = value.computeShares(totalMilliSats)
if (shares.isEmpty()) {
@@ -190,7 +190,7 @@ class V4VPaymentHandler(
shares: List<PodcastValueShare>,
message: String,
asZap: Boolean,
zapType: LnZapEvent.ZapType,
zapType: ZapReceiptEvent.ZapType,
zappedNote: Note?,
okHttpClient: (String) -> OkHttpClient,
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.NwcTransactionMetadata
import com.vitorpamplona.quartz.nip53LiveActivities.streaming.LiveActivitiesEvent
import com.vitorpamplona.quartz.nip57Zaps.LnZapEvent
import com.vitorpamplona.quartz.nip57Zaps.LnZapRequestEvent
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.ZapSplitSetupLnAddress
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.
// Carried so the NWC payment can name the payee (NWC-06 `metadata`); null for
// a NONZAP split, which has no zap request to send.
val zapRequest: LnZapRequestEvent? = null,
val zapRequest: ZapRequestEvent? = null,
val message: String = "",
)
@@ -122,7 +122,7 @@ class ZapPaymentHandler(
onError: (String, String, User?) -> Unit,
onProgress: (percent: Float) -> Unit,
onPayViaIntent: (ImmutableList<Payable>) -> Unit,
zapType: LnZapEvent.ZapType,
zapType: ZapReceiptEvent.ZapType,
) = withContext(Dispatchers.IO) {
val noteEvent = note.event
val zapSplitSetup = noteEvent?.zapSplitSetup()
@@ -297,7 +297,7 @@ class ZapPaymentHandler(
note: Note,
pollOption: Int?,
message: String,
zapType: LnZapEvent.ZapType,
zapType: ZapReceiptEvent.ZapType,
totalAmountMilliSats: Long,
totalWeight: Double,
okHttpClient: (String) -> OkHttpClient,
@@ -360,14 +360,14 @@ class ZapPaymentHandler(
class ZapRequestReady(
val inputSetup: MyZapSplitSetup,
val zapRequest: LnZapRequestEvent?,
val zapRequest: ZapRequestEvent?,
)
suspend fun signAllZapRequests(
note: Note,
pollOption: Int?,
message: String,
zapType: LnZapEvent.ZapType,
zapType: ZapReceiptEvent.ZapType,
zapsToSend: List<MyZapSplitSetup>,
totalAmountMilliSats: Long,
// 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 zapRequest =
if (zapType != LnZapEvent.ZapType.NONZAP && noteEvent != null) {
if (zapType != ZapReceiptEvent.ZapType.NONZAP && noteEvent != null) {
account.zaps.createZapRequestFor(
event = noteEvent,
pollOption = pollOption,
@@ -535,7 +535,7 @@ class ZapPaymentHandler(
totalAmountMilliSats: Long,
totalWeight: Double,
message: String,
zapType: LnZapEvent.ZapType,
zapType: ZapReceiptEvent.ZapType,
okHttpClient: (String) -> OkHttpClient,
onError: (String, String, User?) -> Unit,
onProgress: (percent: Float) -> Unit,
@@ -672,7 +672,7 @@ class ZapPaymentHandler(
private suspend fun assembleInvoice(
lud16: String,
splitSetup: MyZapSplitSetup,
nostrZapRequest: LnZapRequestEvent?,
nostrZapRequest: ZapRequestEvent?,
zapValue: Long,
message: String,
okHttpClient: (String) -> OkHttpClient,
@@ -683,7 +683,7 @@ class ZapPaymentHandler(
// Only the request the provider actually accepted may be claimed as bound to
// this invoice; see lnAddressInvoice's onZapRequestSent.
var sentZapRequest: LnZapRequestEvent? = null
var sentZapRequest: ZapRequestEvent? = null
val invoice =
LightningAddressResolver().lnAddressInvoice(
@@ -38,7 +38,7 @@ import com.vitorpamplona.amethyst.commons.ui.loadStringRes
import com.vitorpamplona.amethyst.service.HttpStatusMessages
import com.vitorpamplona.quartz.lightning.LnInvoiceUtil
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.LnurlEndpointInfo
import com.vitorpamplona.quartz.utils.Log
@@ -128,7 +128,7 @@ class LightningAddressResolver {
lnCallback: String,
milliSats: Long,
message: String,
nostrRequest: LnZapRequestEvent? = null,
nostrRequest: ZapRequestEvent? = null,
okHttpClient: (String) -> OkHttpClient,
context: Context,
): String {
@@ -219,11 +219,11 @@ class LightningAddressResolver {
lnAddress: String,
milliSats: Long,
message: String,
nostrRequest: LnZapRequestEvent? = null,
nostrRequest: ZapRequestEvent? = null,
okHttpClient: (String) -> OkHttpClient,
onProgress: (percent: Float) -> Unit,
context: Context,
onZapRequestSent: (LnZapRequestEvent?) -> Unit = {},
onZapRequestSent: (ZapRequestEvent?) -> Unit = {},
): String {
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.toHexKey
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.nip17Dm.files.ChatMessageEncryptedFileHeaderEvent
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.nip19Bech32.bech32.bechToBytes
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.nip28PublicChat.message.ChannelMessageEvent
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.GitStatusDraftEvent
import com.vitorpamplona.quartz.nip34Git.status.GitStatusOpenEvent
import com.vitorpamplona.quartz.nip54Wiki.WikiNoteEvent
import com.vitorpamplona.quartz.nip57Zaps.LnZapEvent
import com.vitorpamplona.quartz.nip54Wiki.WikiArticleEvent
import com.vitorpamplona.quartz.nip57Zaps.ZapReceiptEvent
import com.vitorpamplona.quartz.nip58Badges.award.BadgeAwardEvent
import com.vitorpamplona.quartz.nip61Nutzaps.nutzap.NutzapEvent
import com.vitorpamplona.quartz.nip64Chess.challenge.accept.LiveChessGameAcceptEvent
import com.vitorpamplona.quartz.nip64Chess.move.LiveChessMoveEvent
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.VideoShortEvent
import com.vitorpamplona.quartz.nip71Video.VideoVerticalEvent
import com.vitorpamplona.quartz.nip84Highlights.HighlightEvent
import com.vitorpamplona.quartz.nip88Polls.poll.PollEvent
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).
// 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.
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()
if (target != null &&
(
@@ -247,7 +247,7 @@ class EventNotificationConsumer(
}
when (event) {
is PrivateDmEvent -> DirectMessageNotification.notify(applicationContext, account, event)
is EncryptedDmEvent -> DirectMessageNotification.notify(applicationContext, account, event)
is ChatMessageEvent -> 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 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 OnchainZapEvent -> ZapNotification.notify(applicationContext, account, event)
@@ -274,15 +274,15 @@ class EventNotificationConsumer(
is PictureEvent,
is VideoNormalEvent,
is VideoShortEvent,
is VideoHorizontalEvent,
is VideoVerticalEvent,
is AddressableNormalVideoEvent,
is AddressableShortVideoEvent,
-> MediaNotification.notify(applicationContext, account, event)
is PollEvent -> MentionNotification.notify(applicationContext, account, event, titleRes = Res.string.app_notification_poll_channel_message)
is HighlightEvent,
is LongTextNoteEvent,
is WikiNoteEvent,
is LongFormContentEvent,
is WikiArticleEvent,
-> ArticleNotification.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.quartz.nip01Core.core.Event
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.entities.NProfile
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.nip71Video.VideoEvent
@@ -177,7 +177,7 @@ object NotificationContent {
}
suspend fun decryptZapContentAuthor(
event: LnZapRequestEvent,
event: ZapRequestEvent,
signer: NostrSigner,
): Event? =
if (event.isPrivateZap() && event.zappedAuthor().contains(event.pubKey)) {
@@ -191,8 +191,8 @@ object NotificationContent {
signer: NostrSigner,
): String? =
when (val event = note.event) {
is PrivateDmEvent -> event.decryptContent(signer)
is LnZapRequestEvent -> decryptZapContentAuthor(event, signer)?.content
is EncryptedDmEvent -> event.decryptContent(signer)
is ZapRequestEvent -> decryptZapContentAuthor(event, signer)?.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.toHexKey
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.nip17Dm.files.ChatMessageEncryptedFileHeaderEvent
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.nip19Bech32.bech32.bechToBytes
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.nip28PublicChat.message.ChannelMessageEvent
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.GitStatusDraftEvent
import com.vitorpamplona.quartz.nip34Git.status.GitStatusOpenEvent
import com.vitorpamplona.quartz.nip54Wiki.WikiNoteEvent
import com.vitorpamplona.quartz.nip57Zaps.LnZapEvent
import com.vitorpamplona.quartz.nip54Wiki.WikiArticleEvent
import com.vitorpamplona.quartz.nip57Zaps.ZapReceiptEvent
import com.vitorpamplona.quartz.nip58Badges.award.BadgeAwardEvent
import com.vitorpamplona.quartz.nip61Nutzaps.nutzap.NutzapEvent
import com.vitorpamplona.quartz.nip64Chess.challenge.accept.LiveChessGameAcceptEvent
import com.vitorpamplona.quartz.nip64Chess.move.LiveChessMoveEvent
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.VideoShortEvent
import com.vitorpamplona.quartz.nip71Video.VideoVerticalEvent
import com.vitorpamplona.quartz.nip84Highlights.HighlightEvent
import com.vitorpamplona.quartz.nip88Polls.poll.PollEvent
import com.vitorpamplona.quartz.nipACWebRtcCalls.events.CallOfferEvent
@@ -118,8 +118,8 @@ class NotificationDispatcher(
internal val NOTIFICATION_KINDS: Set<Int> =
setOf(
// Direct-arrival
PrivateDmEvent.KIND,
LnZapEvent.KIND,
EncryptedDmEvent.KIND,
ZapReceiptEvent.KIND,
NutzapEvent.KIND,
OnchainZapEvent.KIND,
Bolt12ZapEvent.KIND,
@@ -133,8 +133,8 @@ class NotificationDispatcher(
PictureEvent.KIND,
VideoNormalEvent.KIND,
VideoShortEvent.KIND,
VideoHorizontalEvent.KIND,
VideoVerticalEvent.KIND,
AddressableNormalVideoEvent.KIND,
AddressableShortVideoEvent.KIND,
ChannelMessageEvent.KIND,
PollEvent.KIND,
GitPatchEvent.KIND,
@@ -150,8 +150,8 @@ class NotificationDispatcher(
GitStatusClosedEvent.KIND,
GitStatusDraftEvent.KIND,
HighlightEvent.KIND,
LongTextNoteEvent.KIND,
WikiNoteEvent.KIND,
LongFormContentEvent.KIND,
WikiArticleEvent.KIND,
LiveChessGameAcceptEvent.KIND,
LiveChessMoveEvent.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.postConversation
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.files.ChatMessageEncryptedFileHeaderEvent
import com.vitorpamplona.quartz.nip17Dm.messages.ChatMessageEvent
@@ -64,7 +64,7 @@ object DirectMessageNotification {
suspend fun notify(
context: Context,
account: Account,
event: PrivateDmEvent,
event: EncryptedDmEvent,
) {
if (account.signer.pubKey != event.verifiedRecipientPubKey()) return
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.notificationManager
import com.vitorpamplona.amethyst.ui.note.showAmount
import com.vitorpamplona.quartz.nip57Zaps.LnZapEvent
import com.vitorpamplona.quartz.nip57Zaps.LnZapRequestEvent
import com.vitorpamplona.quartz.nip57Zaps.ZapReceiptEvent
import com.vitorpamplona.quartz.nip57Zaps.ZapRequestEvent
import com.vitorpamplona.quartz.nip61Nutzaps.nutzap.NutzapEvent
import com.vitorpamplona.quartz.nipBCOnchainZaps.zap.OnchainZapEvent
import org.jetbrains.compose.resources.StringResource
@@ -59,7 +59,7 @@ object ZapNotification {
suspend fun notify(
context: Context,
account: Account,
event: LnZapEvent,
event: ZapReceiptEvent,
) {
LocalCache.getNoteIfExists(event.id) ?: return
val zapRequestNote = event.zapRequest?.id?.let { LocalCache.checkGetOrCreateNote(it) } ?: return
@@ -67,7 +67,7 @@ object ZapNotification {
if (!account.isAcceptable(zappedNote)) 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.
val decrypted = NotificationContent.decryptZapContentAuthor(zapRequestEvent, account.signer) ?: return
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.normalizer.NormalizedRelayUrl
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.nip38UserStatus.StatusEvent
import com.vitorpamplona.quartz.nip38UserStatus.UserStatusEvent
import com.vitorpamplona.quartz.nip50Search.SearchRelayListEvent
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.relayLists.BlockedRelayListEvent
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.ProxyRelayListEvent
import com.vitorpamplona.quartz.nip51Lists.relayLists.RelayFeedsListEvent
import com.vitorpamplona.quartz.nip51Lists.relayLists.TrustedRelayListEvent
import com.vitorpamplona.quartz.nip51Lists.simpleGroupList.SimpleGroupListEvent
import com.vitorpamplona.quartz.nip53LiveActivities.nestsServers.NestsServersEvent
@@ -60,9 +60,9 @@ val AccountInfoAndListsFromKeyKinds =
listOf(
MetadataEvent.KIND,
ContactListEvent.KIND,
StatusEvent.KIND,
UserStatusEvent.KIND,
AdvertisedRelayListEvent.KIND,
ChatMessageRelayListEvent.KIND,
DmRelayListEvent.KIND,
KeyPackageRelayListEvent.KIND,
SearchRelayListEvent.KIND,
FileServersEvent.KIND,
@@ -78,12 +78,12 @@ val AccountInfoAndListsFromKeyKinds2 =
BroadcastRelayListEvent.KIND,
IndexerRelayListEvent.KIND,
ProxyRelayListEvent.KIND,
HashtagListEvent.KIND,
InterestListEvent.KIND,
GeohashListEvent.KIND,
TrustProviderListEvent.KIND,
PaymentTargetsEvent.KIND,
Bolt12OfferListEvent.KIND,
RelayFeedsListEvent.KIND,
FavoriteRelayListEvent.KIND,
InterestSetEvent.KIND,
// 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,
@@ -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.subscriptions.KeyDataSourceSubscription
import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountViewModel
import com.vitorpamplona.quartz.nip47WalletConnect.events.LnZapPaymentRequestEvent
import com.vitorpamplona.quartz.nip47WalletConnect.events.NwcRequestEvent
@SuppressLint("StateFlowValueCalledInComposition")
@Composable
@@ -50,7 +50,7 @@ fun NWCFinderFilterAssemblerSubscription(
val states =
remember(note) {
val zapPaymentRequestNote = note
(zapPaymentRequestNote.event as? LnZapPaymentRequestEvent)?.let { noteEvent ->
(zapPaymentRequestNote.event as? NwcRequestEvent)?.let { noteEvent ->
noteEvent.walletServicePubKey()?.let {
zapPaymentRequestNote.relays.map {
NWCPaymentQueryState(
@@ -39,7 +39,7 @@ import com.vitorpamplona.quartz.nip02FollowList.ContactListEvent
import com.vitorpamplona.quartz.nip51Lists.PinListEvent
import com.vitorpamplona.quartz.nip51Lists.bookmarkList.BookmarkListEvent
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.coroutines.Dispatchers
import kotlinx.coroutines.ExperimentalCoroutinesApi
@@ -132,9 +132,9 @@ fun observeUserTagFollowCount(
.metadata.stateFlow
.sample(1000)
.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 {
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()
.flowOn(Dispatchers.IO)
}
@@ -160,9 +160,9 @@ fun observeUserTagFollows(
.metadata.stateFlow
.sample(200)
.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 {
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()
.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.nip55AndroidSigner.client.NostrSignerExternal
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
/**
@@ -89,7 +89,7 @@ class MeteringNostrSigner(
fromPublicKey: HexKey,
): 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)
@@ -26,9 +26,9 @@ import com.davotoula.lightcompressor.hls.HlsUploaded
import com.vitorpamplona.amethyst.service.uploads.MediaUploadResult
import com.vitorpamplona.quartz.nip01Core.signers.EventTemplate
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.VideoVerticalEvent
import com.vitorpamplona.quartz.nip71Video.duration
import com.vitorpamplona.quartz.nip71Video.title
import com.vitorpamplona.quartz.nip71Video.videoIMetas
@@ -61,11 +61,11 @@ data class HlsVideoPublishInput(
sealed class HlsVideoEventTemplate {
data class Horizontal(
val template: EventTemplate<VideoHorizontalEvent>,
val template: EventTemplate<AddressableNormalVideoEvent>,
) : HlsVideoEventTemplate()
data class Vertical(
val template: EventTemplate<VideoVerticalEvent>,
val template: EventTemplate<AddressableShortVideoEvent>,
) : 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
* (height > width) selects kind 34236, otherwise 34235.
*
@@ -142,7 +142,7 @@ object HlsVideoEventBuilder {
val template =
if (isVertical) {
HlsVideoEventTemplate.Vertical(
VideoVerticalEvent.build(input.description, dTag, createdAt) {
AddressableShortVideoEvent.build(input.description, dTag, createdAt) {
videoIMetas(videoMetas)
title(input.title)
input.durationSeconds?.let { duration(it) }
@@ -151,7 +151,7 @@ object HlsVideoEventBuilder {
)
} else {
HlsVideoEventTemplate.Horizontal(
VideoHorizontalEvent.build(input.description, dTag, createdAt) {
AddressableNormalVideoEvent.build(input.description, dTag, createdAt) {
videoIMetas(videoMetas)
title(input.title)
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.uriToRoute
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 kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.runBlocking
@@ -425,7 +425,7 @@ fun RenderContentAsMarkdownNotePreview() {
runBlocking {
withContext(Dispatchers.IO) {
val blogPost =
LongTextNoteEvent(
LongFormContentEvent(
id = "e69fa72a221d0b7362f8e63fd6ab84cc6b5e7b505fe49cefe6823a47a6a4b583",
pubKey = "88cc134b1a65f54ef48acc1df3665063d3ea45f04eab8af4646e561c5ae99079",
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.nip10Notes.threadRootIdOrSelf
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
class FilterByListParams(
@@ -105,7 +105,7 @@ class FilterByListParams(
fun showHiddenKey(
selectedListName: String,
userHex: String,
) = selectedListName == PeopleListEvent.blockListFor(userHex) || selectedListName == MuteListEvent.blockListFor(userHex)
) = selectedListName == FollowSetEvent.blockListFor(userHex) || selectedListName == MuteListEvent.blockListFor(userHex)
fun create(
followLists: IFeedTopNavFilter?,
@@ -298,7 +298,7 @@ private class WarmTargets {
/**
* 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
* `m` mime type (a hashed URL may carry no extension). The decode listener
* 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
// `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.
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.ChatroomKeyable
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.base.IsInPublicChatChannel
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.repository.GitRepositoryEvent
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.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.seals.SealedRumorEvent
import com.vitorpamplona.quartz.nip59Giftwrap.seals.SealEvent
import com.vitorpamplona.quartz.nip59Giftwrap.wraps.GiftWrapEvent
import com.vitorpamplona.quartz.nip68Picture.PictureEvent
import com.vitorpamplona.quartz.nip71Video.VideoNormalEvent
@@ -247,7 +247,7 @@ fun routeForInner(
}
}
is FollowListEvent -> {
is StarterPackEvent -> {
Route.FollowPack(noteEvent.address())
}
@@ -294,7 +294,7 @@ fun routeForInner(
Route.GeocacheHunt(noteEvent.kind, noteEvent.pubKey, noteEvent.dTag())
}
is GiftWrapEvent, is SealedRumorEvent -> {
is GiftWrapEvent, is SealEvent -> {
val wrap = noteEvent as HasInnerEvent
wrap.innerEventId?.let {
routeFor(LocalCache.getOrCreateNote(it), loggedIn)
@@ -456,7 +456,7 @@ fun routeReplyTo(
}
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.
routeToMessage(
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.
// When we hold the decrypted sender (we are the zap recipient), reply
// in their DM room instead of the public comment composer.
@@ -532,7 +532,7 @@ suspend fun routeEditDraftTo(
Route.NewShortNote(draft = note.idHex)
}
is LongTextNoteEvent -> {
is LongFormContentEvent -> {
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.people.firstTaggedUserId
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.ImmutableMap
import kotlinx.collections.immutable.persistentMapOf
@@ -219,14 +219,14 @@ fun DisplayStatus(
nav: INav,
) {
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)
}
@Composable
fun DisplayStatus(
event: StatusEvent,
event: UserStatusEvent,
accountViewModel: AccountViewModel,
nav: INav,
) {
@@ -266,7 +266,7 @@ fun DisplayStatusInner(
nav: INav,
) {
when (type) {
StatusEvent.MUSIC -> {
UserStatusEvent.MUSIC -> {
Icon(
imageVector = CustomHashTagIcons.Tunestr,
null,
@@ -331,16 +331,16 @@ import com.vitorpamplona.quartz.nip01Core.core.HexKey
import com.vitorpamplona.quartz.nip01Core.metadata.MetadataEvent
import com.vitorpamplona.quartz.nip01Core.tags.geohash.geoHashOrScope
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.TextNoteEvent
import com.vitorpamplona.quartz.nip13Pow.strongPoWOrNull
import com.vitorpamplona.quartz.nip17Dm.files.ChatMessageEncryptedFileHeaderEvent
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.RepostEvent
import com.vitorpamplona.quartz.nip23LongContent.LongTextNoteEvent
import com.vitorpamplona.quartz.nip23LongContent.LongFormContentEvent
import com.vitorpamplona.quartz.nip25Reactions.ExternalReactionEvent
import com.vitorpamplona.quartz.nip25Reactions.ReactionEvent
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.OldBookmarkListEvent
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.gitAuthorList.GitAuthorListEvent
import com.vitorpamplona.quartz.nip51Lists.gitRepositoryList.GitRepositoryListEvent
import com.vitorpamplona.quartz.nip51Lists.goodWikiAuthorList.GoodWikiAuthorListEvent
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.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.mediaStarterPack.MediaStarterPackEvent
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.relayLists.BlockedRelayListEvent
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.ProxyRelayListEvent
import com.vitorpamplona.quartz.nip51Lists.relayLists.RelayFeedsListEvent
import com.vitorpamplona.quartz.nip51Lists.relayLists.TrustedRelayListEvent
import com.vitorpamplona.quartz.nip51Lists.relaySets.RelaySetEvent
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.nip52Calendar.appt.day.CalendarDateSlotEvent
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.nip53LiveActivities.chat.LiveActivitiesChatMessageEvent
import com.vitorpamplona.quartz.nip53LiveActivities.clip.LiveActivitiesClipEvent
import com.vitorpamplona.quartz.nip53LiveActivities.meetingSpaces.MeetingRoomEvent
import com.vitorpamplona.quartz.nip53LiveActivities.meetingSpaces.MeetingSpaceEvent
import com.vitorpamplona.quartz.nip53LiveActivities.streaming.LiveActivitiesEvent
import com.vitorpamplona.quartz.nip54Wiki.WikiArticleEvent
import com.vitorpamplona.quartz.nip54Wiki.WikiMergeAcceptanceEvent
import com.vitorpamplona.quartz.nip54Wiki.WikiMergeRequestEvent
import com.vitorpamplona.quartz.nip54Wiki.WikiNoteEvent
import com.vitorpamplona.quartz.nip54Wiki.WikiRedirectEvent
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.nip58Badges.award.BadgeAwardEvent
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.definition.CommunityDefinitionEvent
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.nip7DThreads.ThreadEvent
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.nip89AppHandlers.definition.AppDefinitionEvent
import com.vitorpamplona.quartz.nip89AppHandlers.recommendation.AppRecommendationEvent
import com.vitorpamplona.quartz.nip90Dvms.contentDiscoveryResponse.NIP90ContentDiscoveryResponseEvent
import com.vitorpamplona.quartz.nip90Dvms.status.NIP90StatusEvent
import com.vitorpamplona.quartz.nip94FileMetadata.FileHeaderEvent
import com.vitorpamplona.quartz.nip90Dvms.contentDiscoveryResponse.DvmContentDiscoveryResponseEvent
import com.vitorpamplona.quartz.nip90Dvms.status.DvmStatusEvent
import com.vitorpamplona.quartz.nip94FileMetadata.FileMetadataEvent
import com.vitorpamplona.quartz.nip99Classifieds.ClassifiedsEvent
import com.vitorpamplona.quartz.nipA0VoiceMessages.BaseVoiceEvent
import com.vitorpamplona.quartz.nipA4PublicMessages.PublicMessageEvent
@@ -1145,7 +1145,7 @@ private fun RenderNoteRow(
RenderReport(baseNote, quotesLeft, backgroundColor, accountViewModel, nav)
}
is LongTextNoteEvent -> {
is LongFormContentEvent -> {
RenderLongFormContent(baseNote, accountViewModel, nav)
}
@@ -1171,7 +1171,7 @@ private fun RenderNoteRow(
RenderCodeSnippetEvent(baseNote)
}
is WikiNoteEvent -> {
is WikiArticleEvent -> {
RenderWikiContent(baseNote, accountViewModel, nav)
}
@@ -1191,7 +1191,7 @@ private fun RenderNoteRow(
BadgeDisplay(baseNote = baseNote, accountViewModel = accountViewModel, nav = nav)
}
is LnZapEvent -> {
is ZapReceiptEvent -> {
RenderLnZap(baseNote, quotesLeft, backgroundColor, accountViewModel, nav)
}
@@ -1215,11 +1215,11 @@ private fun RenderNoteRow(
RenderFhirResource(baseNote, accountViewModel, nav)
}
is PeopleListEvent -> {
is FollowSetEvent -> {
DisplayPeopleList(baseNote, backgroundColor, accountViewModel, nav)
}
is FollowListEvent -> {
is StarterPackEvent -> {
DisplayFollowList(baseNote, true, accountViewModel, nav)
}
@@ -1231,7 +1231,7 @@ private fun RenderNoteRow(
DisplayRelaySet(baseNote, backgroundColor, accountViewModel, nav)
}
is ChatMessageRelayListEvent -> {
is DmRelayListEvent -> {
DisplayDMRelayList(baseNote, backgroundColor, accountViewModel, nav)
}
@@ -1251,7 +1251,7 @@ private fun RenderNoteRow(
DisplayTrustedRelayList(baseNote, backgroundColor, accountViewModel, nav)
}
is RelayFeedsListEvent -> {
is FavoriteRelayListEvent -> {
DisplayRelayFeedsList(baseNote, backgroundColor, accountViewModel, nav)
}
@@ -1384,7 +1384,7 @@ private fun RenderNoteRow(
)
}
is PrivateDmEvent -> {
is EncryptedDmEvent -> {
RenderPrivateMessage(
baseNote,
makeItShort,
@@ -1494,7 +1494,7 @@ private fun RenderNoteRow(
RenderCalendarDateSlotEvent(baseNote, accountViewModel, nav)
}
is CalendarEvent -> {
is CalendarCollectionEvent -> {
RenderCalendarCollectionEvent(baseNote, accountViewModel, nav)
}
@@ -1502,7 +1502,7 @@ private fun RenderNoteRow(
RenderCalendarRSVPEvent(baseNote, accountViewModel, nav)
}
is GoalEvent -> {
is ZapGoalEvent -> {
RenderGoal(baseNote, accountViewModel, nav)
}
@@ -1550,7 +1550,7 @@ private fun RenderNoteRow(
)
}
is NIP90ContentDiscoveryResponseEvent -> {
is DvmContentDiscoveryResponseEvent -> {
RenderNIP90ContentDiscoveryResponse(
baseNote,
makeItShort,
@@ -1562,7 +1562,7 @@ private fun RenderNoteRow(
)
}
is NIP90StatusEvent -> {
is DvmStatusEvent -> {
RenderNIP90Status(
baseNote,
accountViewModel,
@@ -1640,7 +1640,7 @@ private fun RenderNoteRow(
)
}
is FileHeaderEvent -> {
is FileMetadataEvent -> {
FileHeaderDisplay(baseNote, true, ContentScale.FillWidth, accountViewModel)
}
@@ -1879,7 +1879,7 @@ private fun RenderNoteRow(
RenderOldBookmarkList(baseNote, quotesLeft, backgroundColor, accountViewModel, nav)
}
is LabeledBookmarkListEvent -> {
is BookmarkSetEvent -> {
RenderLabeledBookmarkList(baseNote, quotesLeft, backgroundColor, accountViewModel, nav)
}
@@ -1923,7 +1923,7 @@ private fun RenderNoteRow(
RenderMediaStarterPack(baseNote, backgroundColor, accountViewModel, nav)
}
is HashtagListEvent -> {
is InterestListEvent -> {
RenderHashtagList(baseNote, backgroundColor, accountViewModel, nav)
}
@@ -2172,7 +2172,7 @@ fun FirstUserInfoRow(
// Zap receipts are signed by the recipient's lightning provider; show the
// sender from the embedded zap request instead of the service key.
val zapSender =
if (baseNote.event is LnZapEvent) {
if (baseNote.event is ZapReceiptEvent) {
observeZapSender(baseNote, accountViewModel).value
} else {
null
@@ -2411,7 +2411,7 @@ fun RenderAuthorImages(
} else {
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
// sender from the embedded zap request instead of the service key, matching
// 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.quartz.experimental.bounties.bountyBaseReward
import com.vitorpamplona.quartz.nip28PublicChat.message.ChannelMessageEvent
import com.vitorpamplona.quartz.nip51Lists.followList.FollowListEvent
import com.vitorpamplona.quartz.nip51Lists.peopleList.PeopleListEvent
import com.vitorpamplona.quartz.nip51Lists.followList.StarterPackEvent
import com.vitorpamplona.quartz.nip51Lists.peopleList.FollowSetEvent
import kotlinx.coroutines.launch
import org.jetbrains.compose.resources.StringResource
@@ -149,8 +149,8 @@ val externalLinkForNote = { note: Note ->
if (note is AddressableNote) {
when {
note.event?.bountyBaseReward() != null -> "https://nostrbounties.com/b/${note.toNAddr()}"
note.event is PeopleListEvent -> "https://listr.lol/a/${note.toNAddr()}"
note.event is FollowListEvent -> "https://following.space/d/${note.address.dTag}?p=${note.address.pubKeyHex}"
note.event is FollowSetEvent -> "https://listr.lol/a/${note.toNAddr()}"
note.event is StarterPackEvent -> "https://following.space/d/${note.address.dTag}?p=${note.address.pubKeyHex}"
else -> njumpLink(note.toNAddr())
}
} else {
@@ -95,7 +95,7 @@ import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountViewModel
import com.vitorpamplona.quartz.nip01Core.core.Address
import com.vitorpamplona.quartz.nip30CustomEmoji.CustomEmoji
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.persistentListOf
import kotlinx.collections.immutable.toImmutableList
@@ -361,7 +361,7 @@ private fun EmojiSelector(
accountViewModel.account.emoji.getEmojiPackSelectionAddress(),
) { emptyNote ->
emptyNote?.let { usersEmojiList ->
val collections by observeNoteEventAndMapNotNull(usersEmojiList, accountViewModel) { event: EmojiPackSelectionEvent ->
val collections by observeNoteEventAndMapNotNull(usersEmojiList, accountViewModel) { event: EmojiListEvent ->
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.keyBackup.getFragmentActivity
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.coroutines.launch
@@ -153,22 +153,22 @@ fun UpdateZapAmountContent(
val zapTypes =
listOf(
Triple(
LnZapEvent.ZapType.PUBLIC,
ZapReceiptEvent.ZapType.PUBLIC,
stringRes(id = Res.string.zap_type_public),
stringRes(id = Res.string.zap_type_public_explainer),
),
Triple(
LnZapEvent.ZapType.PRIVATE,
ZapReceiptEvent.ZapType.PRIVATE,
stringRes(id = Res.string.zap_type_private),
stringRes(id = Res.string.zap_type_private_explainer),
),
Triple(
LnZapEvent.ZapType.ANONYMOUS,
ZapReceiptEvent.ZapType.ANONYMOUS,
stringRes(id = Res.string.zap_type_anonymous),
stringRes(id = Res.string.zap_type_anonymous_explainer),
),
Triple(
LnZapEvent.ZapType.NONZAP,
ZapReceiptEvent.ZapType.NONZAP,
stringRes(id = Res.string.zap_type_nonzap),
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.decodePublicKey
import com.vitorpamplona.quartz.nip47WalletConnect.Nip47WalletConnect
import com.vitorpamplona.quartz.nip57Zaps.LnZapEvent
import com.vitorpamplona.quartz.nip57Zaps.ZapReceiptEvent
import kotlinx.coroutines.CancellationException
@Stable
@@ -44,7 +44,7 @@ class UpdateZapAmountViewModel : ViewModel() {
var walletConnectRelay by mutableStateOf(TextFieldValue(""))
var walletConnectPubkey 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
// 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.wallet.OnchainZapSendDialog
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.coroutines.CancellationException
@@ -163,35 +163,35 @@ fun ZapCustomDialog(
val zapTypes =
listOf(
Triple(
LnZapEvent.ZapType.PUBLIC,
ZapReceiptEvent.ZapType.PUBLIC,
stringRes(id = Res.string.zap_type_public),
stringRes(id = Res.string.zap_type_public_explainer),
),
Triple(
LnZapEvent.ZapType.PRIVATE,
ZapReceiptEvent.ZapType.PRIVATE,
stringRes(id = Res.string.zap_type_private),
stringRes(id = Res.string.zap_type_private_explainer),
),
Triple(
LnZapEvent.ZapType.ANONYMOUS,
ZapReceiptEvent.ZapType.ANONYMOUS,
stringRes(id = Res.string.zap_type_anonymous),
stringRes(id = Res.string.zap_type_anonymous_explainer),
),
Triple(
LnZapEvent.ZapType.NONZAP,
ZapReceiptEvent.ZapType.NONZAP,
stringRes(id = Res.string.zap_type_nonzap),
stringRes(id = Res.string.zap_type_nonzap_explainer),
),
).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
remember(accountViewModel, baseNote) {
val default = accountViewModel.defaultZapType()
mutableStateOf(
if (isPrivateTarget && default != LnZapEvent.ZapType.NONZAP) {
LnZapEvent.ZapType.PRIVATE
if (isPrivateTarget && default != ZapReceiptEvent.ZapType.NONZAP) {
ZapReceiptEvent.ZapType.PRIVATE
} else {
default
},
@@ -327,15 +327,15 @@ fun ZapCustomDialog(
OutlinedTextField(
label = {
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))
}
LnZapEvent.ZapType.PRIVATE -> {
ZapReceiptEvent.ZapType.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))
}
}
@@ -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.mockVitorAccountViewModel
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.coroutines.Dispatchers
import kotlinx.coroutines.launch
@@ -145,7 +145,7 @@ fun ZapZapPollNotePreview() {
)
val zapVote =
LnZapEvent(
ZapReceiptEvent(
id = "2a17fdcd0e387d1623c7313d7aa2848e18dde8a942cfe8a2d6b686ea5f68f01a",
pubKey = "79f00d3f5a19ec806189fcab03c1be4ff81d18ee4f653c88fac41fe03570f432",
createdAt = 1729186293,
@@ -30,8 +30,8 @@ import com.vitorpamplona.amethyst.commons.model.Note
import com.vitorpamplona.amethyst.commons.model.User
import com.vitorpamplona.amethyst.model.Account
import com.vitorpamplona.quartz.experimental.zapPolls.ZapPollEvent
import com.vitorpamplona.quartz.nip57Zaps.LnZapEvent
import com.vitorpamplona.quartz.nip57Zaps.LnZapRequestEvent
import com.vitorpamplona.quartz.nip57Zaps.ZapReceiptEvent
import com.vitorpamplona.quartz.nip57Zaps.ZapRequestEvent
import com.vitorpamplona.quartz.utils.TimeUtils
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
@@ -233,9 +233,9 @@ class PollNoteViewModel : ViewModel() {
user: User,
): Boolean =
pollNote!!.zaps.any {
val zapEvent = it.value?.event as? LnZapEvent
val zapEvent = it.value?.event as? ZapReceiptEvent
val privateZapAuthor =
(it.key.event as? LnZapRequestEvent)?.let {
(it.key.event as? ZapRequestEvent)?.let {
account.privateZapsDecryptionCache.cachedPrivateZap(it)
}
zapEvent?.zappedPollOption() == option &&
@@ -244,7 +244,7 @@ class PollNoteViewModel : ViewModel() {
private fun zappedPollOptionAmount(option: Int): BigDecimal =
pollNote?.zaps?.values?.sumOf {
val event = it?.event as? LnZapEvent
val event = it?.event as? ZapReceiptEvent
val zapAmount = event?.amount ?: BigDecimal.ZERO
val isValidAmount = isValidInputVoteAmount(event?.amount)
@@ -258,7 +258,7 @@ class PollNoteViewModel : ViewModel() {
private fun totalZapped(): BigDecimal =
pollNote?.zaps?.values?.sumOf {
val zapEvent = (it?.event as? LnZapEvent)
val zapEvent = (it?.event as? ZapReceiptEvent)
val zapAmount = zapEvent?.amount ?: BigDecimal.ZERO
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.screen.loggedIn.AccountViewModel
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
@Composable
fun WatchAndLoadMyEmojiList(accountViewModel: AccountViewModel) {
LoadAddressableNote(
EmojiPackSelectionEvent.createAddress(accountViewModel.userProfile().pubkeyHex),
EmojiListEvent.createAddress(accountViewModel.userProfile().pubkeyHex),
) { emptyNote ->
emptyNote?.let { usersEmojiList ->
val collections by observeNoteEventAndMapNotNull(usersEmojiList, accountViewModel) { event: EmojiPackSelectionEvent ->
val collections by observeNoteEventAndMapNotNull(usersEmojiList, accountViewModel) { event: EmojiListEvent ->
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.relays.dm.AddDMRelayListDialog
import com.vitorpamplona.quartz.nip01Core.core.HexKey
import com.vitorpamplona.quartz.nip17Dm.settings.ChatMessageRelayListEvent
import com.vitorpamplona.quartz.nip17Dm.settings.DmRelayListEvent
@Preview
@Composable
@@ -88,7 +88,7 @@ fun ObserveRelayListForDMsAndDisplayIfNotFound(
@Composable
fun ObserveRelayListForDMs(
accountViewModel: AccountViewModel,
inner: @Composable (relayListEvent: ChatMessageRelayListEvent?) -> Unit,
inner: @Composable (relayListEvent: DmRelayListEvent?) -> Unit,
) {
ObserveRelayListForDMs(
pubkey = accountViewModel.account.userProfile().pubkeyHex,
@@ -102,13 +102,13 @@ fun ObserveRelayListForDMs(
fun ObserveRelayListForDMs(
pubkey: HexKey,
accountViewModel: AccountViewModel,
inner: @Composable (relayListEvent: ChatMessageRelayListEvent?) -> Unit,
inner: @Composable (relayListEvent: DmRelayListEvent?) -> Unit,
) {
LoadAddressableNote(
ChatMessageRelayListEvent.createAddress(pubkey),
DmRelayListEvent.createAddress(pubkey),
) { relayList ->
if (relayList != null) {
val relayListEvent by observeNoteEvent<ChatMessageRelayListEvent>(relayList, accountViewModel)
val relayListEvent by observeNoteEvent<DmRelayListEvent>(relayList, accountViewModel)
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.nip01Core.jackson.JacksonMapper
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.nip30CustomEmoji.pack.EmojiPackEvent
import kotlinx.coroutines.Dispatchers
@@ -222,7 +222,7 @@ fun noteActionSections(
// 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
// 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(
NoteAction(MaterialSymbols.FormatQuote, stringRes(Res.string.highlight_action)) {
val author = note.author?.pubkeyHex
@@ -256,7 +256,7 @@ fun noteActionSections(
onClick = handlers.onEditPost,
),
)
} else if (note.event is LongTextNoteEvent && state.isLoggedUser) {
} else if (note.event is LongFormContentEvent && state.isLoggedUser) {
add(
NoteAction(MaterialSymbols.Edit, stringRes(Res.string.edit_article)) {
nav.nav { Route.NewLongFormPost(version = note.idHex) }
@@ -343,10 +343,10 @@ fun noteActionSections(
}
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(
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))
} else {
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.nip37Drafts.DraftWrapEvent
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.zapraiser.zapraiser
import com.vitorpamplona.quartz.nip57Zaps.zapraiser.zapraiserAmount
@@ -352,7 +352,7 @@ open class CommentPostViewModel :
this.externalIdentity = (post.event as? CommentEvent)?.scope()
mutedNotifies = emptySet()
notifyProvenance = emptyMap()
(post.event as? LnZapEvent)?.let { zap ->
(post.event as? ZapReceiptEvent)?.let { zap ->
notifying = listOfNotNull(zapSenderToNotify(zap))
}
observeCommunityRules(post)
@@ -366,7 +366,7 @@ open class CommentPostViewModel :
* by an ephemeral key: skip those — tagging the throwaway key is useless, and
* 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
if (request.hasAnonTag()) 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
// 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.
(replyingTo?.event as? LnZapEvent)?.let { zap ->
(replyingTo?.event as? ZapReceiptEvent)?.let { zap ->
zapSenderToNotify(zap)?.let { sender ->
notifying = ((notifying ?: emptyList()) + sender).distinct()
if (!draftEvent.tags.mapNotNull(PTag::parseKey).contains(sender.pubkeyHex)) {
@@ -726,7 +726,7 @@ open class CommentPostViewModel :
null
}
}
} else if (replyingToEvent is LnZapEvent) {
} else if (replyingToEvent is ZapReceiptEvent) {
val sender = zapSenderToNotify(replyingToEvent)
// 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.
@@ -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.quartz.experimental.profileGallery.ProfileGalleryEntryEvent
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.isSensitiveOrNSFW
import com.vitorpamplona.quartz.nip53LiveActivities.clip.LiveActivitiesClipEvent
@@ -278,7 +278,7 @@ private fun secondaryLineFor(
return when {
event is PictureEvent -> stringRes(Res.string.share_as_qr_kind_picture)
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)
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.
if (isGated) return null
if (event is LongTextNoteEvent) {
if (event is LongFormContentEvent) {
val title = event.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.note.CalendarCollectionCard
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
@@ -38,7 +38,7 @@ fun RenderCalendarCollectionEvent(
accountViewModel: AccountViewModel,
nav: INav,
) {
val event = note.event as? CalendarEvent ?: return
val event = note.event as? CalendarCollectionEvent ?: return
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.screen.loggedIn.AccountViewModel
import com.vitorpamplona.quartz.nip31Alts.alt
import com.vitorpamplona.quartz.nip94FileMetadata.FileHeaderEvent
import com.vitorpamplona.quartz.nip94FileMetadata.FileMetadataEvent
@Composable
fun FileHeaderDisplay(
@@ -44,7 +44,7 @@ fun FileHeaderDisplay(
contentScale: ContentScale,
accountViewModel: AccountViewModel,
) {
val event = (note.event as? FileHeaderEvent) ?: return
val event = (note.event as? FileMetadataEvent) ?: return
val fullUrl = event.url() ?: return
val mimeType = remember(note) { event.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
* the file belongs in [FileHeaderAttachmentCard] rather than being pushed into the video player.
*/
internal fun FileHeaderEvent.toMediaContent(
internal fun FileMetadataEvent.toMediaContent(
note: Note,
url: 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. */
@Composable
internal fun FileHeaderAttachmentCard(
event: FileHeaderEvent,
event: FileMetadataEvent,
url: 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. */
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.discover.nip51FollowSets.FollowSetCard
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.toImmutableList
@@ -79,7 +79,7 @@ fun DisplayFollowList(
nav: INav,
) {
val card =
observeNoteEventAndMap(baseNote, accountViewModel) { event: FollowListEvent? ->
observeNoteEventAndMap(baseNote, accountViewModel) { event: StarterPackEvent? ->
if (event == null) {
FollowSetCard(
name = "",
@@ -190,7 +190,7 @@ fun RenderFollowSetThumbPreview() {
val accountViewModel = mockAccountViewModel()
val followCard =
FollowListEvent(
StarterPackEvent(
id = "eca31634fce7c9068b56fa8db9f387da70bdcceb3986a77ca1a9844f3128eb5f",
pubKey = "3c39a7b53dec9ac85acf08b267637a9841e6df7b7b0f5e2ac56a8cf107de37da",
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.showAmount
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 java.math.BigDecimal
import kotlin.math.roundToInt
@@ -71,14 +71,14 @@ fun RenderGoal(
accountViewModel: AccountViewModel,
nav: INav,
) {
val noteEvent = note.event as? GoalEvent ?: return
val noteEvent = note.event as? ZapGoalEvent ?: return
GoalHeader(noteEvent, note, accountViewModel, nav)
}
@Composable
fun GoalHeader(
noteEvent: GoalEvent,
noteEvent: ZapGoalEvent,
note: Note,
accountViewModel: AccountViewModel,
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.TimeAgo
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
private const val WORDS_PER_MINUTE = 225
@@ -77,7 +77,7 @@ fun RenderLongFormContent(
accountViewModel: AccountViewModel,
nav: INav,
) {
val noteEvent = note.event as? LongTextNoteEvent ?: return
val noteEvent = note.event as? LongFormContentEvent ?: return
LongFormHeader(noteEvent, note, accountViewModel)
}
@@ -85,7 +85,7 @@ fun RenderLongFormContent(
@OptIn(ExperimentalLayoutApi::class)
@Composable
fun LongFormHeader(
noteEvent: LongTextNoteEvent,
noteEvent: LongFormContentEvent,
note: Note,
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.screen.loggedIn.AccountViewModel
import com.vitorpamplona.quartz.nip01Core.tags.hashtags.hasHashtags
import com.vitorpamplona.quartz.nip90Dvms.contentDiscoveryResponse.NIP90ContentDiscoveryResponseEvent
import com.vitorpamplona.quartz.nip90Dvms.contentDiscoveryResponse.DvmContentDiscoveryResponseEvent
@Composable
fun RenderNIP90ContentDiscoveryResponse(
@@ -47,7 +47,7 @@ fun RenderNIP90ContentDiscoveryResponse(
accountViewModel: AccountViewModel,
nav: INav,
) {
val noteEvent = note.event as? NIP90ContentDiscoveryResponseEvent ?: return
val noteEvent = note.event as? DvmContentDiscoveryResponseEvent ?: return
val callbackUri = remember(note) { note.toNostrUri() }
SensitivityWarning(
@@ -25,7 +25,7 @@ import androidx.compose.runtime.Composable
import com.vitorpamplona.amethyst.commons.model.Note
import com.vitorpamplona.amethyst.commons.ui.navigation.navs.INav
import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountViewModel
import com.vitorpamplona.quartz.nip90Dvms.status.NIP90StatusEvent
import com.vitorpamplona.quartz.nip90Dvms.status.DvmStatusEvent
@Composable
fun RenderNIP90Status(
@@ -33,7 +33,7 @@ fun RenderNIP90Status(
accountViewModel: AccountViewModel,
nav: INav,
) {
val noteEvent = note.event as? NIP90StatusEvent ?: return
val noteEvent = note.event as? DvmStatusEvent ?: return
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.screen.loggedIn.AccountViewModel
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.persistentListOf
@@ -68,7 +68,7 @@ fun DisplayPeopleList(
accountViewModel: AccountViewModel,
nav: INav,
) {
val noteEvent = baseNote.event as? PeopleListEvent ?: return
val noteEvent = baseNote.event as? FollowSetEvent ?: return
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.chats.privateDM.header.ChatroomHeader
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.nip19Bech32.toNpub
@@ -67,7 +67,7 @@ fun RenderPrivateMessage(
accountViewModel: AccountViewModel,
nav: INav,
) {
val noteEvent = note.event as? PrivateDmEvent ?: return
val noteEvent = note.event as? EncryptedDmEvent ?: return
val userRoom by
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.PublicationSectionRef
import com.vitorpamplona.quartz.nip01Core.core.Event
import com.vitorpamplona.quartz.nip23LongContent.LongTextNoteEvent
import com.vitorpamplona.quartz.nip54Wiki.WikiNoteEvent
import com.vitorpamplona.quartz.nip23LongContent.LongFormContentEvent
import com.vitorpamplona.quartz.nip54Wiki.WikiArticleEvent
import kotlinx.collections.immutable.toImmutableList
// 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.
is PublicationIndexEvent -> event.titleOrIdentifier()
is PublicationContentEvent -> event.titleOrIdentifier()
is LongTextNoteEvent -> event.title()
is WikiNoteEvent -> event.title()
is LongFormContentEvent -> event.title()
is WikiArticleEvent -> event.title()
// 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
// 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.nip19Bech32.entities.NAddress
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.
@@ -331,12 +331,12 @@ private fun wikilinkResolver(event: PublicationContentEvent): (String) -> String
link == null -> 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 ->
"nostr:" +
NAddress.create(
WikiNoteEvent.KIND,
WikiArticleEvent.KIND,
link.pubKey!!,
PublicationContentEvent.normalizeWikilink(link.target),
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.relay.normalizer.NormalizedRelayUrl
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.nip65RelayList.AdvertisedRelayListEvent
import kotlinx.collections.immutable.toImmutableList
@@ -158,7 +158,7 @@ fun DisplayDMRelayList(
accountViewModel: AccountViewModel,
nav: INav,
) {
val noteEvent = baseNote.event as? ChatMessageRelayListEvent ?: return
val noteEvent = baseNote.event as? DmRelayListEvent ?: return
val relays =
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.quartz.experimental.nip82SoftwareApps.application.SoftwareApplicationEvent
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.tags.AppIdTag
import com.vitorpamplona.quartz.nip01Core.relay.filters.Filter
import com.vitorpamplona.quartz.nip01Core.tags.dTag.dTag
import com.vitorpamplona.quartz.nip51Lists.releaseArtifactSet.ReleaseArtifactSetEvent
import kotlinx.collections.immutable.toImmutableList
import kotlinx.coroutines.Dispatchers
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
* `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) {
val filter =
Filter(
kinds = listOf(SoftwareReleaseEvent.KIND),
kinds = listOf(ReleaseArtifactSetEvent.KIND),
authors = listOf(app.pubKey),
tags = mapOf(AppIdTag.TAG_NAME to listOf(app.appId())),
)
@@ -243,13 +242,13 @@ fun produceLatestReleaseVersion(app: SoftwareApplicationEvent): State<String?> {
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. */
private fun latestNip82Release(
notes: Collection<Note>,
app: SoftwareApplicationEvent,
): SoftwareReleaseEvent? {
): ReleaseArtifactSetEvent? {
val prefix = "${app.dTag()}@"
return notes
.mapNotNull { it.asNip82ReleaseFor(prefix) }
@@ -259,25 +258,22 @@ private fun latestNip82Release(
/** kind-30063 addressables authored by [app] whose `d` tag is `<app-id>@<version>`. */
private fun nip82ReleaseNotesFor(app: SoftwareApplicationEvent): Set<Note> {
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
ev.isNip82SoftwareRelease() && ev.dTag().startsWith(prefix)
}
}
/**
* Re-reads this note as the NIP-82 release for [prefix] (`<app-id>@`). kind 30063
* collides with NIP-51 `ReleaseArtifactSetEvent`, which is what `EventFactory`
* builds, so we re-parse matching tag arrays as the NIP-82 form.
* This note as the NIP-82 release for [prefix] (`<app-id>@`). kind 30063 is shared
* with NIP-51 release artifact sets, so only events carrying the NIP-82 tags qualify.
*/
private fun Note.asNip82ReleaseFor(prefix: String): SoftwareReleaseEvent? =
when (val ev = event) {
null -> null
is SoftwareReleaseEvent -> ev.takeIf { it.dTag().startsWith(prefix) }
else -> if (ev.isNip82SoftwareRelease() && ev.dTag().startsWith(prefix)) ev.asSoftwareRelease() else null
}
private fun Note.asNip82ReleaseFor(prefix: String): ReleaseArtifactSetEvent? {
val ev = event as? ReleaseArtifactSetEvent ?: return null
return ev.takeIf { it.isNip82SoftwareRelease() && it.dTag().startsWith(prefix) }
}
fun findAllNip82Releases(app: SoftwareApplicationEvent): List<SoftwareReleaseEvent> {
fun findAllNip82Releases(app: SoftwareApplicationEvent): List<ReleaseArtifactSetEvent> {
val prefix = "${app.dTag()}@"
return nip82ReleaseNotesFor(app)
.mapNotNull { it.asNip82ReleaseFor(prefix) }
@@ -486,25 +482,17 @@ fun RenderSoftwareRelease(
accountViewModel: AccountViewModel,
nav: INav,
) {
// kind 30063 is shared between NIP-51 ReleaseArtifactSetEvent and NIP-82 SoftwareReleaseEvent;
// EventFactory always returns the NIP-51 class, so we re-parse the same tag array as the
// NIP-82 form when the tag signature matches.
val rawEvent = note.event ?: return
val event: SoftwareReleaseEvent =
if (rawEvent is SoftwareReleaseEvent) {
rawEvent
} else if (rawEvent.isNip82SoftwareRelease()) {
rawEvent.asSoftwareRelease()
} else {
return
}
// kind 30063 is shared between NIP-51 release artifact sets and NIP-82 software
// releases; only render the NIP-82 form here.
val event = note.event as? ReleaseArtifactSetEvent ?: return
if (!event.isNip82SoftwareRelease()) return
RenderSoftwareReleaseBody(event = event, accountViewModel = accountViewModel, nav = nav)
}
@Composable
fun RenderSoftwareReleaseBody(
event: SoftwareReleaseEvent,
event: ReleaseArtifactSetEvent,
accountViewModel: AccountViewModel,
nav: INav,
showAppId: Boolean = true,
@@ -60,7 +60,7 @@ import com.vitorpamplona.quartz.nip10Notes.BaseThreadedEvent
import com.vitorpamplona.quartz.nip10Notes.TextNoteEvent
import com.vitorpamplona.quartz.nip14Subject.subject
import com.vitorpamplona.quartz.nip22Comments.CommentEvent
import com.vitorpamplona.quartz.nip57Zaps.LnZapEvent
import com.vitorpamplona.quartz.nip57Zaps.ZapReceiptEvent
enum class ReplyRenderType {
FULL,
@@ -129,7 +129,7 @@ fun RenderTextEvent(
// Zap receipts are signed by the recipient's lightning provider;
// label the reply with the zap sender instead of the service key.
val zapSender =
if (parentNote.event is LnZapEvent) {
if (parentNote.event is ZapReceiptEvent) {
observeZapSender(parentNote, accountViewModel).value
} else {
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.DefaultImageHeaderBackground
import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountViewModel
import com.vitorpamplona.quartz.nip54Wiki.WikiNoteEvent
import com.vitorpamplona.quartz.nip54Wiki.WikiArticleEvent
@Composable
fun RenderWikiContent(
@@ -58,14 +58,14 @@ fun RenderWikiContent(
accountViewModel: AccountViewModel,
nav: INav,
) {
val noteEvent = note.event as? WikiNoteEvent ?: return
val noteEvent = note.event as? WikiArticleEvent ?: return
WikiNoteHeader(noteEvent, note, accountViewModel, nav)
}
@Composable
private fun WikiNoteHeader(
noteEvent: WikiNoteEvent,
noteEvent: WikiArticleEvent,
note: Note,
accountViewModel: AccountViewModel,
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.screen.loggedIn.AccountViewModel
import com.vitorpamplona.quartz.nip01Core.core.Address
import com.vitorpamplona.quartz.nip54Wiki.WikiArticleEvent
import com.vitorpamplona.quartz.nip54Wiki.WikiMergeAcceptanceEvent
import com.vitorpamplona.quartz.nip54Wiki.WikiMergeRequestEvent
import com.vitorpamplona.quartz.nip54Wiki.WikiNoteEvent
import com.vitorpamplona.quartz.nip54Wiki.WikiRedirectEvent
/**
@@ -215,7 +215,7 @@ private fun WikiArticleRow(
) {
LoadAddressableNote(address) { articleNote ->
if (articleNote != null) {
val article by observeNoteEvent<WikiNoteEvent>(articleNote, accountViewModel)
val article by observeNoteEvent<WikiArticleEvent>(articleNote, accountViewModel)
WikiRow(
// 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 ->
if (versionNote != null) {
val article by observeNoteEvent<WikiNoteEvent>(versionNote, accountViewModel)
val article by observeNoteEvent<WikiArticleEvent>(versionNote, accountViewModel)
WikiRow(
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.screen.loggedIn.AccountViewModel
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.runBlocking
import kotlinx.coroutines.withContext
@@ -104,7 +104,7 @@ fun RenderLnZap(
accountViewModel: AccountViewModel,
nav: INav,
) {
val zapEvent = note.event as? LnZapEvent ?: return
val zapEvent = note.event as? ZapReceiptEvent ?: return
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.OldBookmarkListEvent
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
/** NIP-51 kind 10003: the user's bookmarks — notes, articles, hashtags and links. */
@@ -108,7 +108,7 @@ fun RenderLabeledBookmarkList(
accountViewModel: AccountViewModel,
nav: INav,
) {
val noteEvent = baseNote.event as? LabeledBookmarkListEvent ?: return
val noteEvent = baseNote.event as? BookmarkSetEvent ?: return
val public = remember(noteEvent) { noteEvent.publicBookmarks() }
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.quartz.nip01Core.tags.hashtags.HashtagTag
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
/** NIP-51 kind 10015: the hashtags this user follows. */
@@ -47,10 +47,10 @@ fun RenderHashtagList(
accountViewModel: AccountViewModel,
nav: INav,
) {
val noteEvent = baseNote.event as? HashtagListEvent ?: return
val noteEvent = baseNote.event as? InterestListEvent ?: return
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.
val private by loadPrivateItems(noteEvent, accountViewModel) { signer ->
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.RelayUrlNormalizer
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.peopleList.PeopleListEvent
import com.vitorpamplona.quartz.nip51Lists.peopleList.FollowSetEvent
import com.vitorpamplona.quartz.nip72ModCommunities.definition.CommunityDefinitionEvent
import com.vitorpamplona.quartz.nip89AppHandlers.definition.AppDefinitionEvent
import com.vitorpamplona.quartz.utils.Log
@@ -594,9 +594,9 @@ class PeopleListName(
NoteBackedName {
override fun name(): String {
val noteEvent = note.event
return if (noteEvent is PeopleListEvent) {
return if (noteEvent is FollowSetEvent) {
noteEvent.titleOrName() ?: note.dTag()
} else if (noteEvent is FollowListEvent) {
} else if (noteEvent is StarterPackEvent) {
noteEvent.title() ?: note.dTag()
} else {
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.nip02FollowList.ContactListEvent
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.INip05Client
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.OldBookmarkListEvent
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.nip57Zaps.LnZapEvent
import com.vitorpamplona.quartz.nip57Zaps.LnZapRequestEvent
import com.vitorpamplona.quartz.nip57Zaps.ZapReceiptEvent
import com.vitorpamplona.quartz.nip57Zaps.ZapRequestEvent
import com.vitorpamplona.quartz.nip57Zaps.validate.LnurlForm
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.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.nip94FileMetadata.tags.DimensionTag
import com.vitorpamplona.quartz.podcasts.PodcastBoostagram
@@ -725,7 +725,7 @@ class AccountViewModel(
val isHiddenAuthor = note.author?.let { account.isHidden(it) } == true
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) {
// Spam + Blocked Users + Hidden Words + Sensitive Content
@@ -924,14 +924,14 @@ class AccountViewModel(
it.request.event
?.content
?.ifBlank { null },
showAmountInteger((it.response.event as? LnZapEvent)?.amount),
showAmountInteger((it.response.event as? ZapReceiptEvent)?.amount),
it.response,
)
}.toMutableMap()
val results =
mapNotNullAsync(
zaps.filter { (it.request.event as? LnZapRequestEvent)?.isPrivateZap() == true },
zaps.filter { (it.request.event as? ZapRequestEvent)?.isPrivateZap() == true },
) { next ->
val info = innerDecryptAmountMessage(next.request, next.response)
if (info != null) {
@@ -950,14 +950,14 @@ class AccountViewModel(
fun cachedDecryptAmountMessageInGroup(zapNotes: List<CombinedZap>): ImmutableList<ZapAmountCommentNotification> =
zapNotes
.map {
val request = it.request.event as? LnZapRequestEvent
val request = it.request.event as? ZapRequestEvent
if (request?.isPrivateZap() == true) {
val cachedPrivateRequest = account.privateZapsDecryptionCache.cachedPrivateZap(request)
if (cachedPrivateRequest != null) {
ZapAmountCommentNotification(
LocalCache.getUserIfExists(cachedPrivateRequest.pubKey) ?: it.request.author,
cachedPrivateRequest.content.ifBlank { null },
showAmountInteger((it.response.event as? LnZapEvent)?.amount),
showAmountInteger((it.response.event as? ZapReceiptEvent)?.amount),
it.response,
)
} else {
@@ -966,7 +966,7 @@ class AccountViewModel(
it.request.event
?.content
?.ifBlank { null },
showAmountInteger((it.response.event as? LnZapEvent)?.amount),
showAmountInteger((it.response.event as? ZapReceiptEvent)?.amount),
it.response,
)
}
@@ -976,7 +976,7 @@ class AccountViewModel(
it.request.event
?.content
?.ifBlank { null },
showAmountInteger((it.response.event as? LnZapEvent)?.amount),
showAmountInteger((it.response.event as? ZapReceiptEvent)?.amount),
it.response,
)
}
@@ -987,14 +987,14 @@ class AccountViewModel(
return myList
.map {
val request = it.first.event as? LnZapRequestEvent
val request = it.first.event as? ZapRequestEvent
if (request?.isPrivateZap() == true) {
val cachedPrivateRequest = account.privateZapsDecryptionCache.cachedPrivateZap(request)
if (cachedPrivateRequest != null) {
ZapAmountCommentNotification(
LocalCache.getUserIfExists(cachedPrivateRequest.pubKey) ?: it.first.author,
cachedPrivateRequest.content.ifBlank { null },
showAmountInteger((it.second?.event as? LnZapEvent)?.amount),
showAmountInteger((it.second?.event as? ZapReceiptEvent)?.amount),
it.second,
)
} else {
@@ -1003,7 +1003,7 @@ class AccountViewModel(
it.first.event
?.content
?.ifBlank { null },
showAmountInteger((it.second?.event as? LnZapEvent)?.amount),
showAmountInteger((it.second?.event as? ZapReceiptEvent)?.amount),
it.second,
)
}
@@ -1013,7 +1013,7 @@ class AccountViewModel(
it.first.event
?.content
?.ifBlank { null },
showAmountInteger((it.second?.event as? LnZapEvent)?.amount),
showAmountInteger((it.second?.event as? ZapReceiptEvent)?.amount),
it.second,
)
}
@@ -1036,7 +1036,7 @@ class AccountViewModel(
it.first.event
?.content
?.ifBlank { null },
showAmountInteger((it.second?.event as? LnZapEvent)?.amount),
showAmountInteger((it.second?.event as? ZapReceiptEvent)?.amount),
it.second,
)
}.toMutableMap()
@@ -1073,7 +1073,7 @@ class AccountViewModel(
}
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
return innerDecryptAmountMessage(zapRequest, zapEvent)?.copy(zapNote = zapNote)
@@ -1083,14 +1083,14 @@ class AccountViewModel(
zapRequest: Note,
zapEvent: Note,
): ZapAmountCommentNotification? {
val zapEvent = zapEvent.event as? LnZapEvent ?: return null
val zapRequest = zapRequest.event as? LnZapRequestEvent ?: return null
val zapEvent = zapEvent.event as? ZapReceiptEvent ?: return null
val zapRequest = zapRequest.event as? ZapRequestEvent ?: return null
return innerDecryptAmountMessage(zapRequest, zapEvent)
}
suspend fun innerDecryptAmountMessage(
zapRequestEvent: LnZapRequestEvent,
zapEvent: LnZapEvent,
zapRequestEvent: ZapRequestEvent,
zapEvent: ZapReceiptEvent,
): ZapAmountCommentNotification? {
val amount = showAmountInteger(zapEvent.amount)
return if (zapRequestEvent.isPrivateZap()) {
@@ -1148,7 +1148,7 @@ class AccountViewModel(
onError: (String, String, User?) -> Unit,
onProgress: (percent: Float) -> Unit,
onPayViaIntent: (ImmutableList<ZapPaymentHandler.Payable>) -> Unit,
zapType: LnZapEvent.ZapType? = null,
zapType: ZapReceiptEvent.ZapType? = null,
) = launchSigner {
// 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
@@ -1177,8 +1177,8 @@ class AccountViewModel(
// comment stay encrypted. NONZAP is kept: paying without a zap
// request produces no receipt at all, which is even more private.
val effectiveType =
if (note.isPrivateRumor() && requestedType != LnZapEvent.ZapType.NONZAP) {
LnZapEvent.ZapType.PRIVATE
if (note.isPrivateRumor() && requestedType != ZapReceiptEvent.ZapType.NONZAP) {
ZapReceiptEvent.ZapType.PRIVATE
} else {
requestedType
}
@@ -1299,7 +1299,7 @@ class AccountViewModel(
zappedNote = zappedNote,
context = context,
asZap = !streaming,
zapType = LnZapEvent.ZapType.PUBLIC,
zapType = ZapReceiptEvent.ZapType.PUBLIC,
okHttpClient = httpClientBuilder::okHttpClientForMoney,
onError = { 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 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))
@@ -2122,7 +2122,7 @@ class AccountViewModel(
fun updateZapAmounts(
amountSet: List<Long>,
selectedZapType: LnZapEvent.ZapType,
selectedZapType: ZapReceiptEvent.ZapType,
nip47Update: Nip47WalletConnect.Nip47URINorm?,
) = 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
return if (cacheInnerEventId != null) {
val existingNoteEvent = LocalCache.getNoteIfExists(cacheInnerEventId)?.event
@@ -2882,7 +2882,7 @@ class AccountViewModel(
private suspend fun unwrapIfNeeded(event: Event): Note? =
when (event) {
is GiftWrapEvent -> unwrapGiftWrap(event)
is SealedRumorEvent -> unwrapSeal(event)
is SealEvent -> unwrapSeal(event)
else -> LocalCache.getNoteIfExists(event.id)
}
@@ -2936,7 +2936,7 @@ class AccountViewModel(
LocalCache.notes.maxOrNullOf(
filter = { _, note ->
val noteEvent = note.event
noteEvent is NIP90ContentDiscoveryResponseEvent &&
noteEvent is DvmContentDiscoveryResponseEvent &&
noteEvent.pubKey == pubkeyHex &&
noteEvent.isTaggedUser(account.signer.pubKey) &&
noteEvent.createdAt > fifteenMinsAgo
@@ -3005,13 +3005,13 @@ class AccountViewModel(
onError: (String, String) -> Unit,
onProgress: (percent: Float) -> Unit,
context: Context,
zapType: LnZapEvent.ZapType? = null,
zapType: ZapReceiptEvent.ZapType? = null,
) {
viewModelScope.launch(Dispatchers.IO) {
try {
val effectiveZapType = zapType ?: defaultZapType()
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.
val splitLnurl = LnurlForm.toUrl(lnAddress)?.let(LnurlForm::urlToBech32)
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.nip37Drafts.DraftWrapEvent
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.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.GiftWrapEvent
import com.vitorpamplona.quartz.nipACWebRtcCalls.events.CallAnswerEvent
@@ -112,9 +112,9 @@ class EventProcessor(
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 DraftWrapEvent -> draftHandler.delete(event, note)
is GiftWrapEvent -> giftWrapHandler.delete(event, note)
is SealedRumorEvent -> sealHandler.delete(event, note)
is LnZapRequestEvent -> zapRequest.delete(event, note)
is LnZapEvent -> zapEvent.delete(event, note)
is SealEvent -> sealHandler.delete(event, note)
is ZapRequestEvent -> zapRequest.delete(event, note)
is ZapReceiptEvent -> zapEvent.delete(event, note)
}
}
@@ -457,9 +457,9 @@ class SealedRumorEventHandler(
private val account: Account,
private val cache: LocalCache,
private val eventProcessor: EventProcessor,
) : EventHandler<SealedRumorEvent> {
) : EventHandler<SealEvent> {
override suspend fun add(
event: SealedRumorEvent,
event: SealEvent,
eventNote: Note,
publicNote: Note,
) {
@@ -477,7 +477,7 @@ class SealedRumorEventHandler(
}
override suspend fun delete(
event: SealedRumorEvent,
event: SealEvent,
eventNote: Note,
) {
event.innerEventId?.let { rumorId ->
@@ -489,7 +489,7 @@ class SealedRumorEventHandler(
}
private suspend fun processNewSealedRumor(
event: SealedRumorEvent,
event: SealEvent,
eventNote: Note,
publicNote: Note,
) {
@@ -538,9 +538,9 @@ class SealedRumorEventHandler(
class LnZapRequestEventHandler(
val decryptionCache: PrivateZapCache,
) : EventHandler<LnZapRequestEvent> {
) : EventHandler<ZapRequestEvent> {
override suspend fun add(
event: LnZapRequestEvent,
event: ZapRequestEvent,
eventNote: Note,
publicNote: Note,
) {
@@ -550,7 +550,7 @@ class LnZapRequestEventHandler(
}
override suspend fun delete(
event: LnZapRequestEvent,
event: ZapRequestEvent,
eventNote: Note,
) {
if (event.isPrivateZap()) {
@@ -561,9 +561,9 @@ class LnZapRequestEventHandler(
class LnZapEventHandler(
val decryptionCache: PrivateZapCache,
) : EventHandler<LnZapEvent> {
) : EventHandler<ZapReceiptEvent> {
override suspend fun delete(
event: LnZapEvent,
event: ZapReceiptEvent,
eventNote: Note,
) {
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.ui.screen.loggedIn.AccountViewModel
import com.vitorpamplona.amethyst.ui.screen.loggedIn.discover.ChannelCardCompose
import com.vitorpamplona.quartz.nip23LongContent.LongTextNoteEvent
import com.vitorpamplona.quartz.nip23LongContent.LongFormContentEvent
@Composable
fun ArticlesFeedLoaded(
@@ -61,7 +61,7 @@ fun ArticlesFeedLoaded(
ChannelCardCompose(
baseNote = item,
modifier = Modifier.fillMaxWidth(),
forceEventKind = LongTextNoteEvent.KIND,
forceEventKind = LongFormContentEvent.KIND,
accountViewModel = accountViewModel,
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.TopNavFilterState
import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountViewModel
import com.vitorpamplona.quartz.nip23LongContent.LongTextNoteEvent
import com.vitorpamplona.quartz.nip23LongContent.LongFormContentEvent
@Composable
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
// geohash says itself as a token; a follow set does not, and seeds nothing (see asSearchQuery).
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) {
ArticlesTopNavFilterBar(
@@ -28,7 +28,7 @@ import com.vitorpamplona.amethyst.commons.model.topNavFeeds.TopFilter
import com.vitorpamplona.amethyst.model.Account
import com.vitorpamplona.amethyst.ui.dal.FilterByListParams
import com.vitorpamplona.amethyst.ui.dal.sortedByDefaultFeedOrder
import com.vitorpamplona.quartz.nip23LongContent.LongTextNoteEvent
import com.vitorpamplona.quartz.nip23LongContent.LongFormContentEvent
class ArticlesFeedFilter(
val account: Account,
@@ -50,9 +50,9 @@ class ArticlesFeedFilter(
override fun feed(): List<Note> {
val params = buildFilterParams(account)
val notes =
LocalCache.addressables.filterIntoSet(LongTextNoteEvent.KIND) { _, it ->
LocalCache.addressables.filterIntoSet(LongFormContentEvent.KIND) { _, it ->
val noteEvent = it.event
noteEvent is LongTextNoteEvent && params.match(noteEvent, it.relays)
noteEvent is LongFormContentEvent && params.match(noteEvent, it.relays)
}
return sort(notes)
}
@@ -70,7 +70,7 @@ class ArticlesFeedFilter(
return collection.filterTo(HashSet()) {
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.ListDiff
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.ChannelListEvent
import com.vitorpamplona.quartz.nip28PublicChat.list.PublicChatListEvent
import com.vitorpamplona.quartz.nip28PublicChat.list.channels
import com.vitorpamplona.quartz.nip37Drafts.privateOutbox.PrivateOutboxRelayListEvent
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.GeohashListEvent
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.FavoriteRelayListEvent
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.TrustedRelayListEvent
import com.vitorpamplona.quartz.nip60Cashu.wallet.CashuWalletDiff
@@ -137,7 +137,7 @@ internal fun LazyListScope.listDiffItems(
is CommunityListDiff -> communityItems(diff, conflict, accountViewModel, nav)
is EphemeralChatListDiff -> ephemeralRoomItems(diff, conflict, 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 TrustProviderListDiff -> trustProviderItems(diff, conflict, accountViewModel, nav)
is PaymentTargetsDiff -> paymentTargetItems(diff, conflict)
@@ -236,11 +236,11 @@ private fun framingOf(type: BackupEventType): RelayFraming =
private fun savedRelaysOf(event: Event): List<NormalizedRelayUrl> =
when (event) {
is ChatMessageRelayListEvent -> event.relays()
is DmRelayListEvent -> event.relays()
is KeyPackageRelayListEvent -> event.relays()
is SearchRelayListEvent -> event.publicRelays()
is IndexerRelayListEvent -> event.publicRelays()
is RelayFeedsListEvent -> event.publicRelays()
is FavoriteRelayListEvent -> event.publicRelays()
is BlockedRelayListEvent -> event.publicRelays()
is TrustedRelayListEvent -> event.publicRelays()
is PrivateOutboxRelayListEvent -> event.publicRelays()
@@ -451,7 +451,7 @@ private fun LazyListScope.publicChatItems(
accountViewModel: AccountViewModel,
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 ->
LoadPublicChatChannel(tag.eventId) { channel ->
// 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.report.ReportNoteDialog
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.launch
@@ -281,7 +281,7 @@ fun BookmarkGroupItemOptionsMenu(
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)) {
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.TopNavFilterState
import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountViewModel
import com.vitorpamplona.quartz.nip52Calendar.calendar.CalendarEvent
import com.vitorpamplona.quartz.nip52Calendar.calendar.CalendarCollectionEvent
@Composable
fun CalendarCollectionsTopBar(
@@ -49,7 +49,7 @@ fun CalendarCollectionsTopBar(
// 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.
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) {
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.nip52Calendar.appt.day.CalendarDateSlotEvent
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
@Composable
@@ -153,7 +153,7 @@ fun CalendarCollectionCard(
accountViewModel: AccountViewModel,
nav: INav,
) {
val event = note.event as? CalendarEvent ?: return
val event = note.event as? CalendarCollectionEvent ?: return
val title = remember(note.idHex) { event.title() }
val description = remember(note.idHex) { event.content.take(180) }
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
* [IcsExport.calendarToIcs].
*/
private fun collectMembers(calendar: CalendarEvent): List<Pair<Address, Any>> =
private fun collectMembers(calendar: CalendarCollectionEvent): List<Pair<Address, Any>> =
calendar
.calendarEventAddresses()
.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.HexKey
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.ExperimentalCoroutinesApi
import kotlinx.coroutines.flow.MutableStateFlow
@@ -162,13 +162,13 @@ class CalendarsViewModel : ViewModel() {
* changes about once a week.
*/
@OptIn(ExperimentalCoroutinesApi::class)
val ownCalendars: StateFlow<List<CalendarEvent>> =
val ownCalendars: StateFlow<List<CalendarCollectionEvent>> =
inputs
.filterNotNull()
.flatMapLatest { (myPubKey, _) ->
LocalCache
.observeEvents<CalendarEvent>(
Filter(kinds = listOf(CalendarEvent.KIND), authors = listOf(myPubKey)),
.observeEvents<CalendarCollectionEvent>(
Filter(kinds = listOf(CalendarCollectionEvent.KIND), authors = listOf(myPubKey)),
).map { calendars -> calendars.sortedBy { it.title()?.lowercase() ?: "" } }
}
// 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.nip52Calendar.appt.day.CalendarDateSlotEvent
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 kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.Job
@@ -62,7 +62,7 @@ class NewCalendarCollectionViewModel : ViewModel() {
private var dTag: String? = null
/** 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 availableAppointments = mutableStateOf<List<OwnedAppointmentSummary>>(emptyList())
@@ -81,9 +81,9 @@ class NewCalendarCollectionViewModel : ViewModel() {
dTag = editDTag
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)
(existingNote?.event as? CalendarEvent)?.let { existing ->
(existingNote?.event as? CalendarCollectionEvent)?.let { existing ->
loadedEvent = existing
title.value = existing.title().orEmpty()
description.value = existing.content
@@ -140,7 +140,7 @@ class NewCalendarCollectionViewModel : ViewModel() {
account.signAndComputeBroadcast(
if (effectiveDTag != null) {
CalendarEvent.build(
CalendarCollectionEvent.build(
title = parsedTitle,
content = parsedDescription,
dTag = effectiveDTag,
@@ -148,7 +148,7 @@ class NewCalendarCollectionViewModel : ViewModel() {
if (selected.isNotEmpty()) aTags(selected.map { ATag(it) })
}
} else {
CalendarEvent.build(
CalendarCollectionEvent.build(
title = parsedTitle,
content = parsedDescription,
) {
@@ -27,7 +27,7 @@ import com.vitorpamplona.amethyst.commons.model.topNavFeeds.TopFilter
import com.vitorpamplona.amethyst.model.Account
import com.vitorpamplona.amethyst.ui.dal.FilterByListParams
import com.vitorpamplona.amethyst.ui.dal.sortedByDefaultFeedOrder
import com.vitorpamplona.quartz.nip52Calendar.calendar.CalendarEvent
import com.vitorpamplona.quartz.nip52Calendar.calendar.CalendarCollectionEvent
class CalendarCollectionsFeedFilter(
val account: Account,
@@ -49,7 +49,7 @@ class CalendarCollectionsFeedFilter(
val notes =
LocalCache.addressables.filterIntoSet { _, it ->
val e = it.event
e is CalendarEvent && params.match(e, it.relays)
e is CalendarCollectionEvent && params.match(e, it.relays)
}
return sort(notes)
}
@@ -66,7 +66,7 @@ class CalendarCollectionsFeedFilter(
val params = buildFilterParams(account)
return collection.filterTo(HashSet()) {
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.tags.aTag.ATag
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
@@ -74,7 +74,7 @@ fun AddToCalendarSheet(
val sheetState = rememberModalBottomSheetState(skipPartiallyExpanded = true)
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 {
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
.filterIntoSet { _, note ->
val e = note.event
e is CalendarEvent && e.pubKey == myPubKey
}.mapNotNull { it.event as? CalendarEvent }
e is CalendarCollectionEvent && e.pubKey == myPubKey
}.mapNotNull { it.event as? CalendarCollectionEvent }
.sortedBy { it.title()?.lowercase() ?: "" }
private fun toggleMembership(
accountViewModel: AccountViewModel,
calendar: CalendarEvent,
calendar: CalendarCollectionEvent,
targetAddress: Address,
isCurrentlyMember: Boolean,
) {
@@ -177,7 +177,7 @@ private fun toggleMembership(
}
accountViewModel.launchSigner {
accountViewModel.account.signAndComputeBroadcast(
CalendarEvent.build(
CalendarCollectionEvent.build(
title = calendar.title().orEmpty(),
content = calendar.content,
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.tags.RSVPStatusTag
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.utils.TimeUtils
import kotlinx.coroutines.Dispatchers
@@ -287,7 +287,7 @@ fun CalendarEventDetailScreen(
targetAddress = targetAddress,
)
is CalendarEvent ->
is CalendarCollectionEvent ->
CollectionBody(
note = targetNote,
accountViewModel = accountViewModel,
@@ -432,7 +432,7 @@ private fun CollectionBody(
nav: INav,
targetAddress: Address,
) {
val event = note.event as? CalendarEvent ?: return
val event = note.event as? CalendarCollectionEvent ?: return
val title = remember(note.idHex) { event.title() }
val memberAddresses = remember(note.idHex) { event.calendarEventAddresses() }
@@ -736,7 +736,7 @@ private fun InCalendarsSection(
.clickable {
nav.nav(
Route.CalendarEventDetail(
kind = CalendarEvent.KIND,
kind = CalendarCollectionEvent.KIND,
pubKeyHex = calendar.pubKey,
dTag = calendar.dTag(),
),
@@ -889,11 +889,11 @@ private fun rememberRsvpsFor(targetAddress: Address): State<List<CalendarRSVPEve
}
@Composable
private fun rememberCalendarsContaining(targetAddress: Address): State<List<CalendarEvent>> {
private fun rememberCalendarsContaining(targetAddress: Address): State<List<CalendarCollectionEvent>> {
val calendars =
remember(targetAddress) {
LocalCache
.observeEvents<CalendarEvent>(Filter(kinds = listOf(CalendarEvent.KIND)))
.observeEvents<CalendarCollectionEvent>(Filter(kinds = listOf(CalendarCollectionEvent.KIND)))
.map { all ->
all
.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.marmot.foundation.appEvents.MarmotAppEvent
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.nip17Dm.base.ChatroomKeyable
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.nip53LiveActivities.clip.LiveActivitiesClipEvent
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.zapraiser.zapraiserAmount
@@ -156,7 +156,7 @@ fun ChatroomMessageCompose(
}
val event = baseNote.event
if (event is LnZapEvent) {
if (event is ZapReceiptEvent) {
RenderChatZap(baseNote, accountViewModel, nav)
} else if (event is LiveActivitiesRaidEvent) {
RenderChatRaid(baseNote, accountViewModel, nav)
@@ -248,10 +248,10 @@ fun NormalChatNote(
when {
// Own messages: normally no name; in a multi-identity chat, show it (unless a DM,
// 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
noteEvent is PrivateDmEvent -> false
noteEvent is EncryptedDmEvent -> false
// one-on-one, never shows it.
// only shows in a group chat.
@@ -588,7 +588,7 @@ private fun RenderReply(
is ChatMessageEvent, is ChatMessageEncryptedFileHeaderEvent -> {
{ LoadingReplyNote(DmReplyProtocol.NIP17, accountViewModel) }
}
is PrivateDmEvent -> {
is EncryptedDmEvent -> {
{ LoadingReplyNote(DmReplyProtocol.NIP04, accountViewModel) }
}
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.screen.loggedIn.AccountViewModel
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.nip40Expiration.expiration
@@ -84,7 +84,7 @@ fun chatFooterHasMeta(
): Boolean {
val event = note.event ?: return false
val geo = event.geoHashOrScope()
return event is PrivateDmEvent ||
return event is EncryptedDmEvent ||
event.expiration() != null ||
(geo != null && geo != suppressGeohash) ||
event.strongPoWOrNull() != null ||
@@ -132,7 +132,7 @@ fun ChatMessageFooter(
}
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)
// 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.nip53LiveActivities.clip.LiveActivitiesClipEvent
import com.vitorpamplona.quartz.nip53LiveActivities.raid.LiveActivitiesRaidEvent
import com.vitorpamplona.quartz.nip57Zaps.LnZapEvent
import com.vitorpamplona.quartz.nip57Zaps.ZapReceiptEvent
import kotlin.math.abs
/**
@@ -96,7 +96,7 @@ internal const val CHAT_GROUP_WINDOW_SECONDS = 10 * 60L
*/
private fun isGroupableEvent(event: Event?): Boolean =
event != null &&
event !is LnZapEvent &&
event !is ZapReceiptEvent &&
event !is LiveActivitiesRaidEvent &&
event !is LiveActivitiesClipEvent &&
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.showAmountInteger
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
@@ -65,7 +65,7 @@ fun RenderChatZap(
accountViewModel: AccountViewModel,
nav: INav,
) {
val zapEvent = baseNote.event as? LnZapEvent ?: return
val zapEvent = baseNote.event as? ZapReceiptEvent ?: return
val card by produceState(
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.relay.normalizer.NormalizedRelayUrl
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 kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.flow.MutableStateFlow
@@ -122,7 +122,7 @@ class GeohashChatViewModel : ViewModel() {
val signer = NostrSignerInternal(keyPair)
val template =
if (mine.isNotEmpty()) {
DeletionEvent.build(mine)
DeletionRequestEvent.build(mine)
} else {
val hint = note.toEventHint<Event>() ?: return@launch
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.normalizer.NormalizedRelayUrl
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 kotlinx.collections.immutable.persistentListOf
import kotlinx.coroutines.delay
@@ -130,7 +130,7 @@ fun ChatroomView(
// Reactively check if recipients have DM relays for NIP-17 delivery
for (userHex in room.users) {
LoadAddressableNote(
ChatMessageRelayListEvent.createAddress(userHex),
DmRelayListEvent.createAddress(userHex),
) { note ->
if (note != null) {
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.relay.client.pool.RelayBasedFilter
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
/**
@@ -114,7 +114,7 @@ private fun toMeFilter(
filter =
ExplainedFilter(
purpose = SubPurpose.DIRECT_MESSAGES,
kinds = listOf(PrivateDmEvent.KIND),
kinds = listOf(EncryptedDmEvent.KIND),
authors = authors.toList(),
tags = mapOf("p" to listOf(account.userProfile().pubkeyHex)),
since = since,
@@ -135,7 +135,7 @@ private fun fromMeFilter(
filter =
ExplainedFilter(
purpose = SubPurpose.DIRECT_MESSAGES,
kinds = listOf(PrivateDmEvent.KIND),
kinds = listOf(EncryptedDmEvent.KIND),
authors = listOf(account.userProfile().pubkeyHex),
tags = mapOf("p" to pTags.toList()),
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.hashtags.hashtags
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.findNostrEventUris
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.files.ChatMessageEncryptedFileHeaderEvent
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.nip19Bech32.toNpub
import com.vitorpamplona.quartz.nip30CustomEmoji.emojis
@@ -184,7 +184,7 @@ class ChatNewMessageViewModel :
combine(dmRelayListNoteFlows) { dmRelayListNotes ->
dmRelayListNotes
.mapNotNull { noteState ->
val noteEvent = noteState.note.event as? ChatMessageRelayListEvent
val noteEvent = noteState.note.event as? DmRelayListEvent
if (noteEvent == null || noteEvent.relays().isEmpty()) {
noteState.note.author
} else {
@@ -409,7 +409,7 @@ class ChatNewMessageViewModel :
if (replyId != null) {
replyTo.value = LocalCache.checkGetOrCreateNote(replyId)
}
} else if (draftEvent is PrivateDmEvent) {
} else if (draftEvent is EncryptedDmEvent) {
val recipientNPub = draftEvent.verifiedRecipientPubKey()?.let { Hex.decode(it).toNpub() }
toUsers.setTextAndPlaceCursorAtEnd("@$recipientNPub")
@@ -420,7 +420,7 @@ class ChatNewMessageViewModel :
}
val draftText =
if (draftEvent is PrivateDmEvent) {
if (draftEvent is EncryptedDmEvent) {
accountViewModel.account.privateDMDecryptionCache.cachedDM(draftEvent) ?: ""
} else {
draftEvent.content

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