mirror of
https://github.com/vitorpamplona/amethyst.git
synced 2026-10-05 11:18:24 +00:00
refactor: rename packages, helpers and app code after the NIP event renames
Follow-up to the event class renames: the names around those classes now
match too.
Quartz (published API, every old name kept as a @Deprecated alias/forwarder):
- nip51Lists packages moved: followList -> starterPack, hashtagList -> interestList,
peopleList -> followSet, labeledBookmarkList -> bookmarkSet. The old packages
keep a Deprecated.kt with typealiases and forwarding extension functions
(ReplaceWith points at the new package).
- LnZapPrivateEvent -> PrivateZapEvent, LnZapReceiptValidator -> ZapReceiptValidator,
ChannelListDiff -> PublicChatListDiff, HashtagListDiff -> InterestListDiff.
- Kind 30063 now indexes NIP-82 release notes for search (never NIP-51 content,
which can hold encrypted private items). searchable-events docs updated.
App code (amethyst, commons, desktopApp, cli; no aliases needed):
- model packages nip51Lists.{hashtagLists,peopleList,labeledBookmarkLists,relayFeeds}
-> {interestLists,followSets,bookmarkSets,favoriteRelays}.
- State/cache/UI names derived from the old event names: HashtagListState ->
InterestListState, PeopleListsState -> FollowSetsState, FollowListsState ->
StarterPacksState, LabeledBookmarkList -> BookmarkSet, RelayFeedListState ->
FavoriteRelayListState, ContactCardsState -> UserAssertionsState, NIP90* view
models/renderers -> Dvm*, LnZap* handlers -> Zap*, SealedRumor* -> Seal*, and
Account.peopleLists/followLists/hashtagList -> followSets/starterPacks/interestList.
- String literals were left untouched, so preference keys and @SerialName values
stored on disk are unchanged.
- The generic PeopleList UI model (shared by follow sets and starter packs) and the
EmojiPackSelection route (which shows a kind 30030 pack) keep their names.
Docs: plans, brainstorms, changelogs, skills and READMEs use the new names.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_015VvJ8XpeYqBe8bYT3bJ7UD
This commit is contained in:
@@ -43,10 +43,10 @@ Relay frame ──► LocalCache.insertOrUpdateNote() ──► LocalCacheFlow e
|
||||
- `kind3FollowList = Kind3FollowListState(...)` ← NIP-02 ContactList (kind 3)
|
||||
- `nip65RelayList = Nip65RelayListState(...)` ← NIP-65 RelayList (kind 10002), plus siblings `dmRelayList`, `searchRelayList`, `blockedRelayList`, `trustedRelayList`, `proxyRelayList`, `broadcastRelayList`, `indexerRelayList`, …
|
||||
- `muteList = MuteListState(...)` ← NIP-51 MuteList (kind 10000)
|
||||
- `bookmarkState = BookmarkListState(...)` ← NIP-51 Bookmarks (kind 10003), plus `labeledBookmarkLists`, `pinState`, `interestSets`, `peopleLists`, `followLists`, `hashtagList`, `geohashList`, `communityList`, `emoji`, `blossomServers`, …
|
||||
- `bookmarkState = BookmarkListState(...)` ← NIP-51 Bookmarks (kind 10003), plus `bookmarkSets`, `pinState`, `interestSets`, `peopleLists`, `followLists`, `hashtagList`, `geohashList`, `communityList`, `emoji`, `blossomServers`, …
|
||||
- Derived/merged views: `hiddenUsers`, `allFollows`, `homeRelays`, `outboxRelays`, `dmRelays`, `notificationRelays`, `trustedRelays`, and the `live*FollowListsPerRelay` outbox loaders.
|
||||
- **The pattern:** each `XState` class pins its addressable note via `cache.getOrCreateAddressableNote(address)` (a long-term reference so GC/eviction can't drop it), exposes `val flow: StateFlow<…>` derived from the note's metadata flow (decrypted through a per-feature `DecryptionCache`, with backup fallback from `AccountSettings`, `stateIn(scope, Eagerly, …)`), and offers suspend mutation helpers (e.g. `MuteListState.hideUser(pubkey)`) that build the updated signed event. Consumers read `account.muteList.flow`, never a raw `MutableStateFlow` on `Account`.
|
||||
- Encrypted lists pair the state object with a `DecryptionCache` sibling (`muteListDecryptionCache`, `peopleListDecryptionCache`, …) so NIP-44 decryption results are cached per event.
|
||||
- Encrypted lists pair the state object with a `DecryptionCache` sibling (`muteListDecryptionCache`, `followSetDecryptionCache`, …) so NIP-44 decryption results are cached per event.
|
||||
- UI reads via `collectAsStateWithLifecycle` on Android and `collectAsState` on Desktop.
|
||||
- Sibling files per feature live alongside: `AccountSettings.kt`, `AccountSyncedSettings.kt`, plus per-NIP state classes under `model/nip02FollowLists/`, `model/nip51Lists/`, `model/nip65RelayList/`, etc.
|
||||
|
||||
|
||||
@@ -16,8 +16,8 @@ been renamed, grep `Account.kt` for the class name.)
|
||||
| `userMetadata` | `UserMetadataState` | 0 | `amethyst/.../model/nip01UserMetadata/` |
|
||||
| `kind3FollowList` | `Kind3FollowListState` | 3 | `model/nip02FollowLists/` |
|
||||
| `muteList` (+ `muteListDecryptionCache`) | `MuteListState` | 10000 | `model/nip51Lists/muteList/` |
|
||||
| `blockPeopleList`, `peopleLists` | `BlockPeopleListState`, `PeopleListsState` | NIP-51 people sets | `model/nip51Lists/peopleList/` |
|
||||
| `followLists` | `FollowListsState` | NIP-51 follow sets | `model/nip51Lists/peopleList/` |
|
||||
| `blockPeopleList`, `peopleLists` | `BlockPeopleListState`, `FollowSetsState` | NIP-51 people sets | `model/nip51Lists/followSets/` |
|
||||
| `followLists` | `StarterPacksState` | NIP-51 follow sets | `model/nip51Lists/followSets/` |
|
||||
| `hiddenUsers` | `HiddenUsersState` — derived from `muteList.flow` + `blockPeopleList.flow` | — | `model/nip51Lists/` |
|
||||
| `allFollows` | `MergedFollowListsState` — merges kind3 + people/follow/hashtag/geohash/community lists | — | `model/serverList/` |
|
||||
|
||||
@@ -31,7 +31,7 @@ been renamed, grep `Account.kt` for the class name.)
|
||||
| `blockedRelayList` | `BlockedRelayListState` | 10006 | `model/nip51Lists/blockedRelays/` |
|
||||
| `localRelayList` | `LocalRelayListState` | local | `model/localRelays/` |
|
||||
| `privateStorageRelayList` | `PrivateStorageRelayListState` | private storage | `model/edits/` |
|
||||
| `keyPackageRelayList`, `trustedRelayList`, `proxyRelayList`, `broadcastRelayList`, `indexerRelayList`, `relayFeedsList` | per-feature `…RelayListState` classes, each with a `DecryptionCache` sibling | custom relay sets | `model/nip51Lists/…` |
|
||||
| `keyPackageRelayList`, `trustedRelayList`, `proxyRelayList`, `broadcastRelayList`, `indexerRelayList`, `favoriteRelayList` | per-feature `…RelayListState` classes, each with a `DecryptionCache` sibling | custom relay sets | `model/nip51Lists/…` |
|
||||
|
||||
Derived relay views (merge several of the above): `homeRelays`
|
||||
(`AccountHomeRelayState`), `outboxRelays`, `dmRelays`, `notificationRelays`,
|
||||
@@ -43,10 +43,10 @@ Derived relay views (merge several of the above): `homeRelays`
|
||||
| Account property | State class | Kind | Package |
|
||||
|------------------|-------------|------|---------|
|
||||
| `bookmarkState` (and legacy `oldBookmarkState`) | `BookmarkListState` | 10003 | `model/nip51Lists/` |
|
||||
| `labeledBookmarkLists` | `LabeledBookmarkListsState` | NIP-51 bookmark sets | `model/nip51Lists/labeledBookmarkLists/` |
|
||||
| `bookmarkSets` | `BookmarkSetsState` | NIP-51 bookmark sets | `model/nip51Lists/bookmarkSets/` |
|
||||
| `pinState` | `PinListState` | NIP-51 | `model/nip51Lists/` |
|
||||
| `interestSets` | `InterestSetsState` | NIP-51 interest sets | `model/nip51Lists/interestSets/` |
|
||||
| `hashtagList` / `geohashList` | `HashtagListState` / `GeohashListState` | NIP-51 | `model/nip51Lists/hashtagLists/`, `…/geohashLists/` |
|
||||
| `hashtagList` / `geohashList` | `InterestListState` / `GeohashListState` | NIP-51 | `model/nip51Lists/interestLists/`, `…/geohashLists/` |
|
||||
| `communityList` | `CommunityListState` | NIP-72 communities | `model/nip72Communities/` |
|
||||
| `favoriteAlgoFeedsList` | `FavoriteAlgoFeedsListState` | NIP-51 | `model/nip51Lists/` |
|
||||
| `emoji`, `ownedEmojiPacks` | `EmojiPackState`, `OwnedEmojiPacksState` | 10030 | `commons/.../commons/model/nip30CustomEmojis/` |
|
||||
|
||||
@@ -92,8 +92,8 @@ The same kind serves two roles, distinguished **by author**:
|
||||
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** (`UserAssertionEvent.build`/
|
||||
`updatePetNameAndSummary` strip stray public copies; asserted by `ContactCardPetNameTest`).
|
||||
`commons/.../ContactCardsState.kt` keys everything on `author == account` and ignores
|
||||
`updatePetNameAndSummary` strip stray public copies; asserted by `UserAssertionPetNameTest`).
|
||||
`commons/.../UserAssertionsState.kt` keys everything on `author == account` and ignores
|
||||
provider cards.
|
||||
|
||||
## Tag vocabulary and value semantics
|
||||
@@ -225,8 +225,8 @@ card's `a`-tag (`30382:<provider>:<target>`).
|
||||
|
||||
- **Publisher**: `quartz/.../experimental/graperank/GrapeRankPublisher.kt` (canonical 30382
|
||||
writer), `cli/.../graperank/` (`amy graperank register|unregister|providers|publish`).
|
||||
- **Client model**: `commons/.../model/nip85TrustedAssertions/` (`ContactCardsState`,
|
||||
`UserCardsCache`, `ContactCardDecryptionCache`, `TrustProviderListDecryptionCache`),
|
||||
- **Client model**: `commons/.../model/nip85TrustedAssertions/` (`UserAssertionsState`,
|
||||
`UserCardsCache`, `UserAssertionDecryptionCache`, `TrustProviderListDecryptionCache`),
|
||||
`amethyst/.../model/trustedAssertions/TrustProviderListState.kt`.
|
||||
- **Relay plumbing**: `commons/.../relayClient/assemblers/ContactCardFilters.kt`,
|
||||
`amethyst/.../reqCommand/user/watchers/UserCardsSubAssembler.kt`.
|
||||
|
||||
@@ -45,10 +45,9 @@ Notables that surprise people:
|
||||
(`zapRequest?.content.orEmpty()`) — receipts are searchable by the zapper's comment.
|
||||
- **Kind 0 / 31990** index many profile fields space-joined (name, about, nip05, lud16,
|
||||
website, picture URL, …).
|
||||
- **Kind 30063 is claimed twice** (`ReleaseArtifactSetEvent` in nip51Lists and the experimental
|
||||
`SoftwareReleaseEvent`); `EventFactory` resolves 30063 to `ReleaseArtifactSetEvent`, so
|
||||
`title()\ndescription()` is what actually gets indexed — `SoftwareReleaseEvent.indexableContent()`
|
||||
is dead on the store path.
|
||||
- **Kind 30063** (`ReleaseArtifactSetEvent`) serves both NIP-51 release artifact sets and NIP-82
|
||||
software releases: it indexes `title()\ndescription()`, plus the release notes in `content` only
|
||||
for NIP-82 releases (NIP-51 `content` can be encrypted private items).
|
||||
- Poll kinds (1068, 6969) append each option label on its own line.
|
||||
|
||||
## MANDATORY maintenance when you touch this surface
|
||||
|
||||
@@ -5,7 +5,7 @@ expression. **Update this file in the same PR as any change to the searchable se
|
||||
`indexableContent()` body** (see SKILL.md). Verified against the code 2026-09-17.
|
||||
|
||||
Counts: 137 concrete classes covering 140 kind values (`GitStatusEvent` spans 4 kinds;
|
||||
kind 30063 has a collision — see the footnote). File paths are under
|
||||
kind 30063 is shared by two NIPs — see the footnote). File paths are under
|
||||
`quartz/src/commonMain/kotlin/com/vitorpamplona/quartz/`.
|
||||
|
||||
Separator legend: **NL** = `joinToString("\n")`, **SP** = `joinToString(" ")`.
|
||||
@@ -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 | FollowSetEvent | nip51Lists/peopleList | `listOfNotNull(titleOrName(), description())` NL |
|
||||
| 30000 | FollowSetEvent | nip51Lists/followSet | `listOfNotNull(titleOrName(), description())` NL |
|
||||
| 30001 | OldBookmarkListEvent | nip51Lists/bookmarkList | `listOfNotNull(title())` NL |
|
||||
| 30002 | RelaySetEvent | nip51Lists/relaySets | `listOfNotNull(title(), description())` NL |
|
||||
| 30003 | BookmarkSetEvent | nip51Lists/labeledBookmarkList | `listOfNotNull(titleOrName(), description())` NL |
|
||||
| 30003 | BookmarkSetEvent | nip51Lists/bookmarkSet | `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 |
|
||||
@@ -91,7 +91,7 @@ Separator legend: **NL** = `joinToString("\n")`, **SP** = `joinToString(" ")`.
|
||||
| 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 |
|
||||
| 30063 | ReleaseArtifactSetEvent † | nip51Lists/releaseArtifactSet | `listOfNotNull(title(), description())` NL |
|
||||
| 30063 | ReleaseArtifactSetEvent † | nip51Lists/releaseArtifactSet | `listOfNotNull(title(), description(), content if NIP-82)` NL |
|
||||
| 30175 | PersonaEvent | buzz/apPersonas | `personaOrNull()?.let { listOfNotNull(it.displayName, it.systemPrompt).joinToString("\n") } ?: ""` |
|
||||
| 30176 | TeamEvent | buzz/teams | `teamOrNull()?.let { listOfNotNull(it.name, it.description, it.instructions).joinToString("\n") } ?: ""` |
|
||||
| 30177 | ManagedAgentEvent | buzz/managedAgents | `agentOrNull()?.let { listOfNotNull(it.name, it.systemPrompt).joinToString("\n") } ?: ""` |
|
||||
@@ -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 | StarterPackEvent | nip51Lists/followList | `listOfNotNull(title(), description())` NL |
|
||||
| 39089 | StarterPackEvent | nip51Lists/starterPack | `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 |
|
||||
@@ -150,10 +150,10 @@ Separator legend: **NL** = `joinToString("\n")`, **SP** = `joinToString(" ")`.
|
||||
| 45003 | ForumCommentEvent | buzz/forum | `content` |
|
||||
| 48106 | HuddleGuidelinesEvent | buzz/huddles | `content` |
|
||||
|
||||
† **Kind 30063 collision:** `experimental/nip82SoftwareApps/release/SoftwareReleaseEvent` also
|
||||
declares `KIND = 30063` and implements `SearchableEvent` (`content`), but `EventFactory` maps
|
||||
30063 to `ReleaseArtifactSetEvent`, so on every store path kind 30063 indexes
|
||||
`title()\ndescription()`. If the factory mapping ever changes, this table changes with it.
|
||||
† **Kind 30063 is shared** by NIP-51 release artifact sets and NIP-82 software releases, and
|
||||
`ReleaseArtifactSetEvent` parses both. It indexes `title()` and `description()`, plus `content`
|
||||
(the release notes) only when the event carries the NIP-82 `i` + `version` tags — a NIP-51 set
|
||||
may hold encrypted private items in `content`, which must never be indexed.
|
||||
|
||||
## Abstract bases (no kind of their own)
|
||||
|
||||
|
||||
@@ -68,7 +68,7 @@ The NIP-42 plumbing and a first-cut permission gate are already in place:
|
||||
| Settings screen (global policy + per-relay list) | `amethyst/.../ui/screen/loggedIn/relayauth/RelayAuthSettingsScreen.kt` |
|
||||
| Global policy setting, persisted local-only | `AccountSettings.defaultRelayAuthPolicy`, `LocalPreferences` key `DEFAULT_RELAY_AUTH_POLICY` |
|
||||
| Blocked-relay list (kind 10006) | `amethyst/.../model/nip51Lists/blockedRelays/BlockedRelayListState.kt` (`.flow`) |
|
||||
| Follow checks | `Account.isFollowing(...)`, `Account.allFollows.flow.value.authors`, `FollowListsState.isUserInFollowSets(...)` |
|
||||
| Follow checks | `Account.isFollowing(...)`, `Account.allFollows.flow.value.authors`, `StarterPacksState.isUserInFollowSets(...)` |
|
||||
| DM / NIP-65 relay lookups | `DmRelayListState`, `Nip65RelayListState` (+ per-user via `LocalCache`) |
|
||||
|
||||
## The gap
|
||||
@@ -180,7 +180,7 @@ class RelayAuthContext(val relayUrl: String, val purposes: List<AuthPurpose>)
|
||||
- `IF_IN_MY_LIST` → `ALLOW` if relay ∈ my relay lists, else fall through
|
||||
- `TRUSTED_FOLLOWS` *(new — see idea A below)* → `ALLOW` if relay ∈ my lists
|
||||
**or** any counterparty in `ctx.purposes` is followed (`Account.allFollows`
|
||||
/ `FollowListsState.isUserInFollowSets`) and the purpose permits it; else
|
||||
/ `StarterPacksState.isUserInFollowSets`) and the purpose permits it; else
|
||||
fall through.
|
||||
4. **Fall-through**: `ASK` if the purpose is attributable (we can show a reason);
|
||||
otherwise `DENY` silently (don't prompt for anonymous stranger challenges).
|
||||
|
||||
@@ -392,11 +392,11 @@ can be a group event if it carries `h` and the relay accepts the kind."
|
||||
has:
|
||||
|
||||
- `metadata/` — `GroupMetadataEvent` (kind **39000**), `GroupAdminsEvent`
|
||||
(39001), `GroupMembersEvent` (39002), `SupportedRolesEvent` (39003).
|
||||
- `moderation/` — `CreateGroupEvent` (9007), `EditMetadataEvent` (9002),
|
||||
`PutUserEvent` (9000), `RemoveUserEvent` (9001), `DeleteEventEvent` (9005),
|
||||
`DeleteGroupEvent` (9008), `CreateInviteEvent` (9009), plus tag helpers.
|
||||
- `request/` — `JoinRequestEvent` (9021), `LeaveRequestEvent` (9022).
|
||||
(39001), `GroupMembersEvent` (39002), `GroupRolesEvent` (39003).
|
||||
- `moderation/` — `CreateGroupEvent` (9007), `GroupEditMetadataEvent` (9002),
|
||||
`GroupPutUserEvent` (9000), `GroupRemoveUserEvent` (9001), `GroupDeleteEventEvent` (9005),
|
||||
`DeleteGroupEvent` (9008), `GroupCreateInviteEvent` (9009), plus tag helpers.
|
||||
- `request/` — `GroupJoinRequestEvent` (9021), `GroupLeaveRequestEvent` (9022).
|
||||
- `tags/` — `GroupIdTag` (the `h` tag), `CodeTag`, `GroupAdminTag`, `RoleTag`,
|
||||
and even a `PreviousTag`.
|
||||
|
||||
@@ -427,7 +427,7 @@ NIP-04/17 DMs, NIP-28 public channels, and NIP-C7 ephemeral chats — all under
|
||||
### 5.2 The main protocol gap
|
||||
|
||||
There is **no kind-9 group chat message event** in `nip29RelayGroups/`
|
||||
(`CreateInviteEvent` at 9009 is the highest kind present; nothing for kind 9).
|
||||
(`GroupCreateInviteEvent` at 9009 is the highest kind present; nothing for kind 9).
|
||||
The 9xxx moderation, 39xxx metadata, and 9021/9022 request events exist, but the
|
||||
actual message carrier does not. This is the first thing to build:
|
||||
|
||||
|
||||
@@ -71,7 +71,7 @@ Feed-level:
|
||||
marker update (only the newest visible timestamp matters; the marker is
|
||||
monotonic). One coroutine per scroll session instead of one per row.
|
||||
6. **Jumbo without a coroutine (#4)**: only launch the decrypt effect for
|
||||
encrypted kinds (`PrivateDmEvent`, sealed rumors not yet in the decrypt
|
||||
encrypted kinds (`EncryptedDmEvent`, sealed rumors not yet in the decrypt
|
||||
cache). Plaintext kinds (public chats, NIP-17 rumors already unwrapped —
|
||||
the vast majority) take the synchronous path only.
|
||||
7. **Retire settled delivery ticks (#6)**: once a message is fully accepted (or
|
||||
|
||||
@@ -55,7 +55,7 @@ Bitchat's Nostr side has two chat features. Amethyst is a pure-Nostr client
|
||||
|
||||
1. **Encrypted DMs (Phase 2).** Port the `bitchat1:` binary packet
|
||||
(`BitchatPacket` TLV + `NoisePayloadType`) into quartz, wrap/unwrap it in the
|
||||
existing NIP-17 stack (`GiftWrapEvent`/`SealedRumorEvent`/`ChatMessageEvent`),
|
||||
existing NIP-17 stack (`GiftWrapEvent`/`SealEvent`/`ChatMessageEvent`),
|
||||
handle geohash DMs (to a per-geohash pubkey) and delivery/read receipts.
|
||||
Add `amy geochat dm` for interop testing.
|
||||
2. **Desktop UI.** The shared pieces (quartz events, `GeoRelayDirectory`) are
|
||||
|
||||
@@ -178,10 +178,10 @@ This keeps the *policy* (who gets notified) exactly where it is and isolates the
|
||||
|
||||
| Kind(s) | Category | Style | Accent / icon | Title → Body | Actions |
|
||||
| --- | --- | --- | --- | --- | --- |
|
||||
| NIP-17 `ChatMessageEvent` (14), file (15), NIP-04 `PrivateDmEvent` (4), Marmot group | DIRECT_MESSAGE | `MessagingStyle` + Conversation shortcut + Bubble | blue-green / `chat` | sender → message | Reply, Mark-read |
|
||||
| NIP-17 `ChatMessageEvent` (14), file (15), NIP-04 `EncryptedDmEvent` (4), Marmot group | DIRECT_MESSAGE | `MessagingStyle` + Conversation shortcut + Bubble | blue-green / `chat` | sender → message | Reply, Mark-read |
|
||||
| `TextNoteEvent`(1)/`CommentEvent`(1111)/`ChannelMessageEvent`(42) **reply to me** | REPLY | `MessagingStyle` (parent as prior message) | purple / `reply` | "X replied" → excerpt (+ quoted parent) | Reply, View thread, Mute thread |
|
||||
| Same kinds, **mention/quote only** | MENTION | plain (BigText) | purple / `alternate_email` | "X mentioned you" → excerpt | View, Mute thread |
|
||||
| `LnZapEvent`(9735) | ZAP | **colorized** amount card (BigText) | gold / `bolt` | "⚡ 2,100 sats from X" → zap comment + zapped-note excerpt | Zap back, View |
|
||||
| `ZapReceiptEvent`(9735) | ZAP | **colorized** amount card (BigText) | gold / `bolt` | "⚡ 2,100 sats from X" → zap comment + zapped-note excerpt | Zap back, View |
|
||||
| `NutzapEvent`(9321) *(new)* | ZAP | colorized amount card | gold / `bolt` (cashu tint) | "🥜 X nutzapped you 2,100 sats" → … | Zap back, View |
|
||||
| `OnchainZapEvent`(8333) *(new)* | ZAP | colorized amount card | gold / `bolt` | "⛓ X sent an onchain zap" → … | View |
|
||||
| `ReactionEvent`(7) | REACTION | aggregated `InboxStyle` | heart-red / `favorite` | "🤙 X & N others liked your post" → note excerpt | Like back, View |
|
||||
@@ -189,7 +189,7 @@ This keeps the *policy* (who gets notified) exactly where it is and isolates the
|
||||
| `PictureEvent`(20)/`VideoNormal`/`Short`/`Vertical`/`Horizontal` | MEDIA | `BigPictureStyle` | purple / `image` | "X shared a photo/video" → caption, image inline | View |
|
||||
| `PollEvent`/`ZapPollEvent` | MENTION | plain | purple / `ballot` | "X asked a question" → poll title | Vote, View |
|
||||
| `HighlightEvent`(9802) | ARTICLE | plain | purple / `format_quote` | "X highlighted your article" → highlighted text | View |
|
||||
| `LongTextNoteEvent`(30023)/`WikiNoteEvent`(30818) | ARTICLE | plain | purple / `article` | "X mentioned you in an article" → title | View |
|
||||
| `LongFormContentEvent`(30023)/`WikiArticleEvent`(30818) | ARTICLE | plain | purple / `article` | "X mentioned you in an article" → title | View |
|
||||
| `GitIssueEvent`/`GitPatchEvent`/`GitPullRequestEvent`/`…Update` | CODE | plain | slate / `merge` | "X opened an issue/PR" → subject | View |
|
||||
| `BadgeAwardEvent`(8) *(new)* | BADGE | `BigPictureStyle` (badge art) | gold / `award_star` | "You earned a badge" → badge name, image | View |
|
||||
| `LiveChessGameAcceptEvent`/`LiveChessMoveEvent` | CHESS | plain | brown / `chess` | "X accepted your challenge" / "your turn" | Open board |
|
||||
|
||||
@@ -59,7 +59,7 @@ and dispatches the decrypted `Response`, so a new method needs no changes there:
|
||||
decrypt-on-arrival.
|
||||
- `NwcPaymentTracker` (`commons/…/service/nwc/`) — request↔response match with the
|
||||
author-spoof gate.
|
||||
- `LocalCache.consume(LnZapPaymentResponseEvent)` — routes 23195 back to the callback.
|
||||
- `LocalCache.consume(NwcResponseEvent)` — routes 23195 back to the callback.
|
||||
- Wallet storage: `AccountSettings.nwcWallets` + `defaultPaymentSourceId`;
|
||||
`PaymentSourceResolver`.
|
||||
- Pay rail entry: `ZapPaymentHandler.zap()` → `payViaNWC()` (the `PaymentSource.Nwc`
|
||||
|
||||
@@ -41,9 +41,9 @@ storm.
|
||||
|---|---|
|
||||
| The chip row + "Add" chip | `ui/note/creators/notify/Notifying.kt` (`Notifying`, `NotifyUserChip`, `AddUserChip`) |
|
||||
| Audience state | `ShortNotePostViewModel.pTags: List<User>?`, `mutedNotifies: Set<HexKey>`, `activeNotifies()`, `addToReplyList(user)` |
|
||||
| My NIP-51 people lists (kind 30000, public **and** decrypted private members) | `account.peopleLists.uiListFlow: StateFlow<List<PeopleList>>` (`model/nip51Lists/peopleList/PeopleListsState.kt`) |
|
||||
| My follow packs (kind 39089, public members) | `account.followLists.uiListFlow` (`model/nip51Lists/peopleList/FollowListsState.kt`) |
|
||||
| `PeopleList` UI model (`title`, `image`, `publicMembers`, `privateMembers` as `Set<User>`) | `model/nip51Lists/peopleList/PeopleList.kt` |
|
||||
| My NIP-51 people lists (kind 30000, public **and** decrypted private members) | `account.peopleLists.uiListFlow: StateFlow<List<PeopleList>>` (`model/nip51Lists/followSets/FollowSetsState.kt`) |
|
||||
| My follow packs (kind 39089, public members) | `account.followLists.uiListFlow` (`model/nip51Lists/followSets/StarterPacksState.kt`) |
|
||||
| `PeopleList` UI model (`title`, `image`, `publicMembers`, `privateMembers` as `Set<User>`) | `model/nip51Lists/followSets/PeopleList.kt` |
|
||||
| Two-column list catalog rendering | `ui/screen/loggedIn/lists/memberEdit/FollowListAndPackAndUserView.kt` — same "Follow sets" + "Discover follows" sectioning |
|
||||
| Multi-select member review (count header, select-all checkbox, per-user row, confirm button) | `ui/screen/loggedIn/newUser/ImportFollowListPickFollowsScreen.kt` (`PreviewList` / `FollowEntryRow`) |
|
||||
| Bottom-sheet picker shell w/ search field | `ui/screen/loggedIn/chats/publicChannels/relayGroup/RelayGroupParentPicker.kt` |
|
||||
|
||||
@@ -281,7 +281,7 @@ In `quartz/src/commonMain/kotlin/com/vitorpamplona/quartz/utils/EventFactory.kt`
|
||||
```kotlin
|
||||
import com.vitorpamplona.quartz.nip90Dvms.dvmHeartbeat.DvmHeartbeatEvent
|
||||
```
|
||||
2. Add a dispatch branch inside the `when (kind)` near the other NIP-90 kinds (next to line 736 `NIP90StatusEvent.KIND -> ...`):
|
||||
2. Add a dispatch branch inside the `when (kind)` near the other NIP-90 kinds (next to line 736 `DvmStatusEvent.KIND -> ...`):
|
||||
```kotlin
|
||||
DvmHeartbeatEvent.KIND -> DvmHeartbeatEvent(id, pubKey, createdAt, tags, content, sig)
|
||||
```
|
||||
@@ -304,7 +304,7 @@ private val knownDTagReaders =
|
||||
|
||||
- [ ] **Step 6: Update the design doc §2**
|
||||
|
||||
In `amethyst/plans/2026-09-10-dvm-heartbeat-liveness.md`, replace the §2 bullet that says the class "extends `BaseReplaceableEvent`" / "Overrides `dTag()`" with the as-built wording: extends `BaseAddressableEvent` (the codebase convention for 10xxx events with real `d` tags, e.g. `FollowListEvent`), so `dTag()`/`address()`/`addressTag()` come from the base; note `MAX_AGE_SECONDS`, `isFreshAt`, and the `knownDTagReaders` entry live in quartz too (commons imports them).
|
||||
In `amethyst/plans/2026-09-10-dvm-heartbeat-liveness.md`, replace the §2 bullet that says the class "extends `BaseReplaceableEvent`" / "Overrides `dTag()`" with the as-built wording: extends `BaseAddressableEvent` (the codebase convention for 10xxx events with real `d` tags, e.g. `StarterPackEvent`), so `dTag()`/`address()`/`addressTag()` come from the base; note `MAX_AGE_SECONDS`, `isFreshAt`, and the `knownDTagReaders` entry live in quartz too (commons imports them).
|
||||
|
||||
- [ ] **Step 7: Run the tests to verify they pass**
|
||||
|
||||
|
||||
@@ -45,7 +45,7 @@ feeds and the detail surface show an offline state instead.
|
||||
New `quartz/.../nip90Dvms/dvmHeartbeat/DvmHeartbeatEvent.kt`:
|
||||
|
||||
- `class DvmHeartbeatEvent(...) : BaseAddressableEvent(...)`, `KIND = 11998` — the codebase
|
||||
convention for 10xxx events with real `d` tags (e.g. `FollowListEvent`), so `dTag()` /
|
||||
convention for 10xxx events with real `d` tags (e.g. `StarterPackEvent`), so `dTag()` /
|
||||
`address()` / `addressTag()` come from the base. The cache address is
|
||||
`Address(11998, dvmPubkey, dTag)`, the exact mirror of the announcement's
|
||||
`Address(31990, dvmPubkey, dTag)`.
|
||||
|
||||
@@ -83,7 +83,7 @@ The listing event is mostly existing tags wearing a new kind number:
|
||||
| `r` | `nip51Lists/tags/RelayTag` (`"r"`) |
|
||||
| `a` | `nip01Core/tags/aTag/ATag` (curation list + found log) |
|
||||
| `title`, `description` (37517) | `nip51Lists/tags/TitleTag`, `.../DescriptionTag` |
|
||||
| embedded 7517 | `Event.fromJson(...)` (same trick `LnZapEvent.zapRequest()` uses) |
|
||||
| embedded 7517 | `Event.fromJson(...)` (same trick `ZapReceiptEvent.zapRequest()` uses) |
|
||||
| signature check | `Event.verifySignature()` in `nip01Core/crypto/EventExt.kt` |
|
||||
| kind 1111 logs | `nip22Comments/CommentEvent` **as-is** — it already models `A/K/P` + `a/k/p` addressable root+parent (`rootAddress()`, `replyAddress()`, `hasRootAddress()`). Only the `t` log-type vocabulary is new. |
|
||||
| required-tag gate | `containsAllTagNamesWithValues(REQUIRED_FIELDS)`, as `ClassifiedsEvent.isWellFormed()` does |
|
||||
|
||||
@@ -94,7 +94,7 @@ Section labels: "Bitcoin" and "Lightning".
|
||||
## Subscription edits — extend existing kind lists
|
||||
|
||||
No new assemblers. Add `OnchainZapEvent.KIND` (8333) to the existing
|
||||
`LnZapEvent.KIND` (9735) sites:
|
||||
`ZapReceiptEvent.KIND` (9735) sites:
|
||||
|
||||
| File | Edit |
|
||||
|---|---|
|
||||
@@ -109,7 +109,7 @@ No new assemblers. Add `OnchainZapEvent.KIND` (8333) to the existing
|
||||
|
||||
## Display path — fold into `Note.zapsAmount`
|
||||
|
||||
Today: `LocalCache.consume(LnZapEvent)` → `Note.addZap()` → `Note.updateZapTotal()`
|
||||
Today: `LocalCache.consume(ZapReceiptEvent)` → `Note.addZap()` → `Note.updateZapTotal()`
|
||||
sums lightning amounts into `Note.zapsAmount`, which `ReactionsRow` /
|
||||
`ObserveZapAmountText` / `SlidingAnimationAmount` render. We add onchain zap
|
||||
sats to the same `Note.zapsAmount` — no UI changes required.
|
||||
@@ -118,7 +118,7 @@ sats to the same `Note.zapsAmount` — no UI changes required.
|
||||
- Add `var onchainZaps = mapOf<...>()` (separate map from `zaps`).
|
||||
- Extend `updateZapTotal()` to add **verified** onchain sats. Unverified or
|
||||
pending tx amounts are NOT counted.
|
||||
- `amethyst/.../model/LocalCache.kt` (after the `consume(LnZapEvent)` block ~line 1667)
|
||||
- `amethyst/.../model/LocalCache.kt` (after the `consume(ZapReceiptEvent)` block ~line 1667)
|
||||
- New `consume(event: OnchainZapEvent)` handler.
|
||||
- Reject self-zap.
|
||||
- Enqueue verification against the configured `OnchainBackend`.
|
||||
|
||||
@@ -34,7 +34,7 @@ and what remains as future work.
|
||||
2. **Private mute/block leak via `identity.getMutes`/`getBlocked`.** These read
|
||||
`muteList.flow` / `blockPeopleList.flow`, which contain **decrypted private** entries.
|
||||
Now they read the events' **public** tags only (`MuteListEvent.publicMutes()`,
|
||||
`PeopleListEvent.publicUsersIdSet()`).
|
||||
`FollowSetEvent.publicUsersIdSet()`).
|
||||
|
||||
3. **Silent "allow-always" actions + UI-redress.** Added persistent **trusted chrome**
|
||||
(a sandbox bar the applet can't draw over: shield + name + tap-to-see "what it can
|
||||
|
||||
@@ -62,7 +62,7 @@ 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.InterestListEvent
|
||||
import com.vitorpamplona.quartz.nip51Lists.interestList.InterestListEvent
|
||||
import com.vitorpamplona.quartz.nip51Lists.muteList.MuteListEvent
|
||||
import com.vitorpamplona.quartz.nip51Lists.relayLists.BlockedRelayListEvent
|
||||
import com.vitorpamplona.quartz.nip51Lists.relayLists.FavoriteRelayListEvent
|
||||
@@ -599,7 +599,7 @@ object LocalPreferences {
|
||||
putOrRemove(PrefKeys.LATEST_NIP65_RELAY_LIST, settings.backupNIP65RelayList)
|
||||
putOrRemove(PrefKeys.LATEST_SEARCH_RELAY_LIST, settings.backupSearchRelayList)
|
||||
putOrRemove(PrefKeys.LATEST_INDEX_RELAY_LIST, settings.backupIndexRelayList)
|
||||
putOrRemove(PrefKeys.LATEST_RELAY_FEEDS_LIST, settings.backupRelayFeedsList)
|
||||
putOrRemove(PrefKeys.LATEST_RELAY_FEEDS_LIST, settings.backupFavoriteRelayList)
|
||||
putOrRemove(PrefKeys.LATEST_BLOCKED_RELAY_LIST, settings.backupBlockedRelayList)
|
||||
putOrRemove(PrefKeys.LATEST_TRUSTED_RELAY_LIST, settings.backupTrustedRelayList)
|
||||
|
||||
@@ -613,9 +613,9 @@ object LocalPreferences {
|
||||
putOrRemove(PrefKeys.LATEST_PRIVATE_HOME_RELAY_LIST, settings.backupPrivateHomeRelayList)
|
||||
putOrRemove(PrefKeys.LATEST_APP_SPECIFIC_DATA, settings.backupAppSpecificData)
|
||||
|
||||
putOrRemove(PrefKeys.LATEST_CHANNEL_LIST, settings.backupChannelList)
|
||||
putOrRemove(PrefKeys.LATEST_CHANNEL_LIST, settings.backupPublicChatList)
|
||||
putOrRemove(PrefKeys.LATEST_COMMUNITY_LIST, settings.backupCommunityList)
|
||||
putOrRemove(PrefKeys.LATEST_HASHTAG_LIST, settings.backupHashtagList)
|
||||
putOrRemove(PrefKeys.LATEST_HASHTAG_LIST, settings.backupInterestList)
|
||||
putOrRemove(PrefKeys.LATEST_GEOHASH_LIST, settings.backupGeohashList)
|
||||
putOrRemove(PrefKeys.LATEST_EPHEMERAL_LIST, settings.backupEphemeralChatList)
|
||||
putOrRemove(PrefKeys.LATEST_RELAY_GROUP_LIST, settings.backupRelayGroupList)
|
||||
@@ -827,15 +827,15 @@ object LocalPreferences {
|
||||
val latestNip65RelayListStr = getString(PrefKeys.LATEST_NIP65_RELAY_LIST, null)
|
||||
val latestSearchRelayListStr = getString(PrefKeys.LATEST_SEARCH_RELAY_LIST, null)
|
||||
val latestIndexRelayListStr = getString(PrefKeys.LATEST_INDEX_RELAY_LIST, null)
|
||||
val latestRelayFeedsListStr = getString(PrefKeys.LATEST_RELAY_FEEDS_LIST, null)
|
||||
val latestFavoriteRelayListStr = getString(PrefKeys.LATEST_RELAY_FEEDS_LIST, null)
|
||||
val latestBlockedRelayListStr = getString(PrefKeys.LATEST_BLOCKED_RELAY_LIST, null)
|
||||
val latestTrustedRelayListStr = getString(PrefKeys.LATEST_TRUSTED_RELAY_LIST, null)
|
||||
val latestMuteListStr = getString(PrefKeys.LATEST_MUTE_LIST, null)
|
||||
val latestPrivateHomeRelayListStr = getString(PrefKeys.LATEST_PRIVATE_HOME_RELAY_LIST, null)
|
||||
val latestAppSpecificDataStr = getString(PrefKeys.LATEST_APP_SPECIFIC_DATA, null)
|
||||
val latestChannelListStr = getString(PrefKeys.LATEST_CHANNEL_LIST, null)
|
||||
val latestPublicChatListStr = getString(PrefKeys.LATEST_CHANNEL_LIST, null)
|
||||
val latestCommunityListStr = getString(PrefKeys.LATEST_COMMUNITY_LIST, null)
|
||||
val latestHashtagListStr = getString(PrefKeys.LATEST_HASHTAG_LIST, null)
|
||||
val latestInterestListStr = getString(PrefKeys.LATEST_HASHTAG_LIST, null)
|
||||
val latestGeohashListStr = getString(PrefKeys.LATEST_GEOHASH_LIST, null)
|
||||
val latestEphemeralListStr = getString(PrefKeys.LATEST_EPHEMERAL_LIST, null)
|
||||
val latestRelayGroupListStr = getString(PrefKeys.LATEST_RELAY_GROUP_LIST, null)
|
||||
@@ -891,15 +891,15 @@ object LocalPreferences {
|
||||
val latestNip65RelayList = async { parseEventOrNull<AdvertisedRelayListEvent>(latestNip65RelayListStr) }
|
||||
val latestSearchRelayList = async { parseEventOrNull<SearchRelayListEvent>(latestSearchRelayListStr) }
|
||||
val latestIndexRelayList = async { parseEventOrNull<IndexerRelayListEvent>(latestIndexRelayListStr) }
|
||||
val latestRelayFeedsList = async { parseEventOrNull<FavoriteRelayListEvent>(latestRelayFeedsListStr) }
|
||||
val latestFavoriteRelayList = async { parseEventOrNull<FavoriteRelayListEvent>(latestFavoriteRelayListStr) }
|
||||
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<PublicChatListEvent>(latestChannelListStr) }
|
||||
val latestPublicChatList = async { parseEventOrNull<PublicChatListEvent>(latestPublicChatListStr) }
|
||||
val latestCommunityList = async { parseEventOrNull<CommunityListEvent>(latestCommunityListStr) }
|
||||
val latestHashtagList = async { parseEventOrNull<InterestListEvent>(latestHashtagListStr) }
|
||||
val latestInterestList = async { parseEventOrNull<InterestListEvent>(latestInterestListStr) }
|
||||
val latestGeohashList = async { parseEventOrNull<GeohashListEvent>(latestGeohashListStr) }
|
||||
val latestEphemeralList = async { parseEventOrNull<EphemeralChatListEvent>(latestEphemeralListStr) }
|
||||
val latestRelayGroupList = async { parseEventOrNull<SimpleGroupListEvent>(latestRelayGroupListStr) }
|
||||
@@ -947,15 +947,15 @@ object LocalPreferences {
|
||||
val latestNip65RelayListResolved = latestNip65RelayList.await()
|
||||
val latestSearchRelayListResolved = latestSearchRelayList.await()
|
||||
val latestIndexRelayListResolved = latestIndexRelayList.await()
|
||||
val latestRelayFeedsListResolved = latestRelayFeedsList.await()
|
||||
val latestFavoriteRelayListResolved = latestFavoriteRelayList.await()
|
||||
val latestBlockedRelayListResolved = latestBlockedRelayList.await()
|
||||
val latestTrustedRelayListResolved = latestTrustedRelayList.await()
|
||||
val latestMuteListResolved = latestMuteList.await()
|
||||
val latestPrivateHomeRelayListResolved = latestPrivateHomeRelayList.await()
|
||||
val latestAppSpecificDataResolved = latestAppSpecificData.await()
|
||||
val latestChannelListResolved = latestChannelList.await()
|
||||
val latestPublicChatListResolved = latestPublicChatList.await()
|
||||
val latestCommunityListResolved = latestCommunityList.await()
|
||||
val latestHashtagListResolved = latestHashtagList.await()
|
||||
val latestInterestListResolved = latestInterestList.await()
|
||||
val latestGeohashListResolved = latestGeohashList.await()
|
||||
val latestEphemeralListResolved = latestEphemeralList.await()
|
||||
val latestRelayGroupListResolved = latestRelayGroupList.await()
|
||||
@@ -1049,15 +1049,15 @@ object LocalPreferences {
|
||||
backupDMRelayList = latestDmRelayListResolved,
|
||||
backupSearchRelayList = latestSearchRelayListResolved,
|
||||
backupIndexRelayList = latestIndexRelayListResolved,
|
||||
backupRelayFeedsList = latestRelayFeedsListResolved,
|
||||
backupFavoriteRelayList = latestFavoriteRelayListResolved,
|
||||
backupBlockedRelayList = latestBlockedRelayListResolved,
|
||||
backupTrustedRelayList = latestTrustedRelayListResolved,
|
||||
backupPrivateHomeRelayList = latestPrivateHomeRelayListResolved,
|
||||
backupMuteList = latestMuteListResolved,
|
||||
backupAppSpecificData = latestAppSpecificDataResolved,
|
||||
backupChannelList = latestChannelListResolved,
|
||||
backupPublicChatList = latestPublicChatListResolved,
|
||||
backupCommunityList = latestCommunityListResolved,
|
||||
backupHashtagList = latestHashtagListResolved,
|
||||
backupInterestList = latestInterestListResolved,
|
||||
backupGeohashList = latestGeohashListResolved,
|
||||
backupEphemeralChatList = latestEphemeralListResolved,
|
||||
backupRelayGroupList = latestRelayGroupListResolved,
|
||||
|
||||
@@ -75,21 +75,21 @@ import com.vitorpamplona.amethyst.commons.model.nip51Lists.blockPeopleList.Block
|
||||
import com.vitorpamplona.amethyst.commons.model.nip51Lists.blockedRelays.BlockedRelayListDecryptionCache
|
||||
import com.vitorpamplona.amethyst.commons.model.nip51Lists.broadcastRelays.BroadcastRelayListDecryptionCache
|
||||
import com.vitorpamplona.amethyst.commons.model.nip51Lists.favoriteAlgoFeedsLists.FavoriteAlgoFeedsListDecryptionCache
|
||||
import com.vitorpamplona.amethyst.commons.model.nip51Lists.favoriteRelays.FavoriteRelayListDecryptionCache
|
||||
import com.vitorpamplona.amethyst.commons.model.nip51Lists.followSets.FollowSetDecryptionCache
|
||||
import com.vitorpamplona.amethyst.commons.model.nip51Lists.geohashLists.GeohashListDecryptionCache
|
||||
import com.vitorpamplona.amethyst.commons.model.nip51Lists.hashtagLists.HashtagListDecryptionCache
|
||||
import com.vitorpamplona.amethyst.commons.model.nip51Lists.indexerRelays.IndexerRelayListDecryptionCache
|
||||
import com.vitorpamplona.amethyst.commons.model.nip51Lists.interestLists.InterestListDecryptionCache
|
||||
import com.vitorpamplona.amethyst.commons.model.nip51Lists.muteList.MuteListDecryptionCache
|
||||
import com.vitorpamplona.amethyst.commons.model.nip51Lists.peopleList.PeopleListDecryptionCache
|
||||
import com.vitorpamplona.amethyst.commons.model.nip51Lists.proxyRelays.ProxyRelayListDecryptionCache
|
||||
import com.vitorpamplona.amethyst.commons.model.nip51Lists.relayFeeds.RelayFeedsListDecryptionCache
|
||||
import com.vitorpamplona.amethyst.commons.model.nip51Lists.searchRelays.SearchRelayListDecryptionCache
|
||||
import com.vitorpamplona.amethyst.commons.model.nip51Lists.trustedRelays.TrustedRelayListDecryptionCache
|
||||
import com.vitorpamplona.amethyst.commons.model.nip56Reports.ReportAction
|
||||
import com.vitorpamplona.amethyst.commons.model.nip62Vanish.VanishRequestsState
|
||||
import com.vitorpamplona.amethyst.commons.model.nip72Communities.CommunityListDecryptionCache
|
||||
import com.vitorpamplona.amethyst.commons.model.nip85TrustedAssertions.ContactCardDecryptionCache
|
||||
import com.vitorpamplona.amethyst.commons.model.nip85TrustedAssertions.ContactCardsState
|
||||
import com.vitorpamplona.amethyst.commons.model.nip85TrustedAssertions.TrustProviderListDecryptionCache
|
||||
import com.vitorpamplona.amethyst.commons.model.nip85TrustedAssertions.UserAssertionDecryptionCache
|
||||
import com.vitorpamplona.amethyst.commons.model.nip85TrustedAssertions.UserAssertionsState
|
||||
import com.vitorpamplona.amethyst.commons.model.nipBCOnchainZaps.OnchainWalletState
|
||||
import com.vitorpamplona.amethyst.commons.model.privateChatLastReadRoute
|
||||
import com.vitorpamplona.amethyst.commons.model.privateChats.hasEncryptedContent
|
||||
@@ -145,18 +145,18 @@ import com.vitorpamplona.amethyst.model.nip46Signer.Nip46SignerState
|
||||
import com.vitorpamplona.amethyst.model.nip47WalletConnect.NwcSignerState
|
||||
import com.vitorpamplona.amethyst.model.nip51Lists.HiddenUsersState
|
||||
import com.vitorpamplona.amethyst.model.nip51Lists.blockedRelays.BlockedRelayListState
|
||||
import com.vitorpamplona.amethyst.model.nip51Lists.bookmarkSets.BookmarkSetsState
|
||||
import com.vitorpamplona.amethyst.model.nip51Lists.broadcastRelays.BroadcastRelayListState
|
||||
import com.vitorpamplona.amethyst.model.nip51Lists.favoriteAlgoFeedsLists.FavoriteAlgoFeedsListState
|
||||
import com.vitorpamplona.amethyst.model.nip51Lists.favoriteRelays.FavoriteRelayListState
|
||||
import com.vitorpamplona.amethyst.model.nip51Lists.followSets.FollowSetsState
|
||||
import com.vitorpamplona.amethyst.model.nip51Lists.followSets.StarterPacksState
|
||||
import com.vitorpamplona.amethyst.model.nip51Lists.geohashLists.GeohashListState
|
||||
import com.vitorpamplona.amethyst.model.nip51Lists.hashtagLists.HashtagListState
|
||||
import com.vitorpamplona.amethyst.model.nip51Lists.indexerRelays.IndexerRelayListState
|
||||
import com.vitorpamplona.amethyst.model.nip51Lists.interestLists.InterestListState
|
||||
import com.vitorpamplona.amethyst.model.nip51Lists.interestSets.InterestSetsState
|
||||
import com.vitorpamplona.amethyst.model.nip51Lists.labeledBookmarkLists.LabeledBookmarkListsState
|
||||
import com.vitorpamplona.amethyst.model.nip51Lists.muteList.MuteListState
|
||||
import com.vitorpamplona.amethyst.model.nip51Lists.peopleList.FollowListsState
|
||||
import com.vitorpamplona.amethyst.model.nip51Lists.peopleList.PeopleListsState
|
||||
import com.vitorpamplona.amethyst.model.nip51Lists.proxyRelays.ProxyRelayListState
|
||||
import com.vitorpamplona.amethyst.model.nip51Lists.relayFeeds.RelayFeedListState
|
||||
import com.vitorpamplona.amethyst.model.nip51Lists.searchRelays.SearchRelayListState
|
||||
import com.vitorpamplona.amethyst.model.nip51Lists.trustedRelays.TrustedRelayListState
|
||||
import com.vitorpamplona.amethyst.model.nip65RelayList.Nip65RelayListState
|
||||
@@ -281,8 +281,8 @@ 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.LnZapPrivateEvent
|
||||
import com.vitorpamplona.quartz.nip57Zaps.PrivateZapCache
|
||||
import com.vitorpamplona.quartz.nip57Zaps.PrivateZapEvent
|
||||
import com.vitorpamplona.quartz.nip57Zaps.ZapReceiptEvent
|
||||
import com.vitorpamplona.quartz.nip57Zaps.ZapRequestEvent
|
||||
import com.vitorpamplona.quartz.nip57Zaps.splits.ZapSplitSetup
|
||||
@@ -639,8 +639,8 @@ class Account(
|
||||
val indexerRelayListDecryptionCache = IndexerRelayListDecryptionCache(signer)
|
||||
val indexerRelayList = IndexerRelayListState(signer, cache, indexerRelayListDecryptionCache, scope, settings)
|
||||
|
||||
val relayFeedsListDecryptionCache = RelayFeedsListDecryptionCache(signer)
|
||||
val relayFeedsList = RelayFeedListState(signer, cache, relayFeedsListDecryptionCache, scope, settings)
|
||||
val favoriteRelayListDecryptionCache = FavoriteRelayListDecryptionCache(signer)
|
||||
val favoriteRelayList = FavoriteRelayListState(signer, cache, favoriteRelayListDecryptionCache, scope, settings)
|
||||
|
||||
val blockedRelayListDecryptionCache = BlockedRelayListDecryptionCache(signer)
|
||||
val blockedRelayList = BlockedRelayListState(signer, cache, blockedRelayListDecryptionCache, scope, settings)
|
||||
@@ -758,8 +758,8 @@ class Account(
|
||||
val communityListDecryptionCache = CommunityListDecryptionCache(signer)
|
||||
val communityList = CommunityListState(signer, cache, communityListDecryptionCache, scope, settings)
|
||||
|
||||
val hashtagListDecryptionCache = HashtagListDecryptionCache(signer)
|
||||
val hashtagList = HashtagListState(signer, cache, hashtagListDecryptionCache, scope, settings)
|
||||
val interestListDecryptionCache = InterestListDecryptionCache(signer)
|
||||
val interestList = InterestListState(signer, cache, interestListDecryptionCache, scope, settings)
|
||||
|
||||
val favoriteAlgoFeedsListDecryptionCache = FavoriteAlgoFeedsListDecryptionCache(signer)
|
||||
val favoriteAlgoFeedsList = FavoriteAlgoFeedsListState(signer, cache, favoriteAlgoFeedsListDecryptionCache, scope, settings)
|
||||
@@ -777,14 +777,14 @@ class Account(
|
||||
val trustProviderListDecryptionCache = TrustProviderListDecryptionCache(signer)
|
||||
val trustProviderList = TrustProviderListState(signer, cache, trustProviderListDecryptionCache, scope, settings)
|
||||
|
||||
val peopleListDecryptionCache = PeopleListDecryptionCache(signer)
|
||||
val blockPeopleList = BlockPeopleListState(signer, cache, peopleListDecryptionCache, scope)
|
||||
val peopleLists = PeopleListsState(signer, cache, peopleListDecryptionCache, scope)
|
||||
val followLists = FollowListsState(signer, cache, scope)
|
||||
val followSetDecryptionCache = FollowSetDecryptionCache(signer)
|
||||
val blockPeopleList = BlockPeopleListState(signer, cache, followSetDecryptionCache, scope)
|
||||
val followSets = FollowSetsState(signer, cache, followSetDecryptionCache, scope)
|
||||
val starterPacks = StarterPacksState(signer, cache, scope)
|
||||
|
||||
val hiddenUsers = HiddenUsersState(muteList.flow, blockPeopleList.flow, scope, settings)
|
||||
|
||||
val labeledBookmarkLists = LabeledBookmarkListsState(signer, cache, scope)
|
||||
val bookmarkSets = BookmarkSetsState(signer, cache, scope)
|
||||
val interestSets = InterestSetsState(signer, cache, scope)
|
||||
val appRecommendations = AppRecommendationsState(signer, cache, scope)
|
||||
val oldBookmarkState = OldBookmarkListState(signer, cache, scope)
|
||||
@@ -795,8 +795,8 @@ class Account(
|
||||
val ownedEmojiPacks = OwnedEmojiPacksState(signer, cache, scope)
|
||||
|
||||
// needs `emoji` above: nickname edits resolve :shortcodes: against the account's packs
|
||||
val contactCardDecryptionCache = ContactCardDecryptionCache(signer)
|
||||
val contactCards = ContactCardsState(signer, cache, contactCardDecryptionCache, emoji)
|
||||
val userAssertionDecryptionCache = UserAssertionDecryptionCache(signer)
|
||||
val userAssertions = UserAssertionsState(signer, cache, userAssertionDecryptionCache, emoji)
|
||||
|
||||
val vanish = VanishRequestsState(signer, cache, client, scope)
|
||||
|
||||
@@ -893,7 +893,7 @@ class Account(
|
||||
val followsPerRelay = FollowsPerOutboxRelay(kind3FollowList, blockedRelayList, proxyRelayList, cache, scope).flow
|
||||
|
||||
// Merges all follow lists to create a single All Follows feed.
|
||||
val allFollows = MergedFollowListsState(kind3FollowList, peopleLists, followLists, hashtagList, geohashList, communityList, scope)
|
||||
val allFollows = MergedFollowListsState(kind3FollowList, followSets, starterPacks, interestList, geohashList, communityList, scope)
|
||||
|
||||
val privateDMDecryptionCache = PrivateDMCache(signer)
|
||||
override val privateZapsDecryptionCache = PrivateZapCache(signer)
|
||||
@@ -1030,10 +1030,10 @@ class Account(
|
||||
|
||||
val feedDecryptionCaches =
|
||||
FeedDecryptionCaches(
|
||||
peopleListCache = peopleListDecryptionCache,
|
||||
peopleListCache = followSetDecryptionCache,
|
||||
muteListCache = muteListDecryptionCache,
|
||||
communityListCache = communityListDecryptionCache,
|
||||
hashtagCache = hashtagListDecryptionCache,
|
||||
hashtagCache = interestListDecryptionCache,
|
||||
geohashCache = geohashListDecryptionCache,
|
||||
)
|
||||
|
||||
@@ -1047,7 +1047,7 @@ class Account(
|
||||
blockedRelays = blockedRelayList.flow,
|
||||
proxyRelays = proxyRelayList.flow,
|
||||
mineRelays = mineRelays.flow,
|
||||
relayFeeds = relayFeedsList.flow,
|
||||
relayFeeds = favoriteRelayList.flow,
|
||||
caches = feedDecryptionCaches,
|
||||
signer = signer,
|
||||
scope = scope,
|
||||
@@ -2126,9 +2126,9 @@ class Account(
|
||||
|
||||
suspend fun unfollow(community: AddressableNote) = sendMyPublicAndPrivateOutbox(communityList.unfollow(community))
|
||||
|
||||
suspend fun followHashtag(tag: String) = sendMyPublicAndPrivateOutbox(hashtagList.follow(tag))
|
||||
suspend fun followHashtag(tag: String) = sendMyPublicAndPrivateOutbox(interestList.follow(tag))
|
||||
|
||||
suspend fun unfollowHashtag(tag: String) = sendMyPublicAndPrivateOutbox(hashtagList.unfollow(tag))
|
||||
suspend fun unfollowHashtag(tag: String) = sendMyPublicAndPrivateOutbox(interestList.unfollow(tag))
|
||||
|
||||
suspend fun followFavoriteAlgoFeed(dvm: AddressBookmark) = sendMyPublicAndPrivateOutbox(favoriteAlgoFeedsList.follow(dvm))
|
||||
|
||||
@@ -3440,11 +3440,11 @@ class Account(
|
||||
* encrypted in the content. `null` clears a field. Goes out through the
|
||||
* account's extended outbox relays.
|
||||
*/
|
||||
suspend fun updateContactCardPetName(
|
||||
suspend fun updateUserAssertionPetName(
|
||||
pubkeyHex: HexKey,
|
||||
petName: String?,
|
||||
summary: String?,
|
||||
) = sendMyPublicAndPrivateOutbox(contactCards.updatePetNameAndSummary(pubkeyHex, petName, summary))
|
||||
) = sendMyPublicAndPrivateOutbox(userAssertions.updatePetNameAndSummary(pubkeyHex, petName, summary))
|
||||
|
||||
suspend fun showUser(pubkeyHex: HexKey) {
|
||||
sendMyPublicAndPrivateOutbox(blockPeopleList.showUser(pubkeyHex))
|
||||
@@ -3550,7 +3550,7 @@ class Account(
|
||||
}
|
||||
}
|
||||
|
||||
suspend fun decryptZapOrNull(event: ZapRequestEvent): LnZapPrivateEvent? = if (event.isPrivateZap() && isWriteable()) privateZapsDecryptionCache.decryptPrivateZap(event) else null
|
||||
suspend fun decryptZapOrNull(event: ZapRequestEvent): PrivateZapEvent? = if (event.isPrivateZap() && isWriteable()) privateZapsDecryptionCache.decryptPrivateZap(event) else null
|
||||
|
||||
fun isAllHidden(users: Set<HexKey>): Boolean = users.all { isHidden(it) }
|
||||
|
||||
@@ -3747,11 +3747,11 @@ class Account(
|
||||
|
||||
suspend fun saveTrustedRelayList(trustedRelays: List<NormalizedRelayUrl>) = sendMyPublicAndPrivateOutbox(trustedRelayList.saveRelayList(trustedRelays))
|
||||
|
||||
suspend fun saveRelayFeedsList(trustedRelays: List<NormalizedRelayUrl>) = sendMyPublicAndPrivateOutbox(relayFeedsList.saveRelayList(trustedRelays))
|
||||
suspend fun saveFavoriteRelayList(trustedRelays: List<NormalizedRelayUrl>) = sendMyPublicAndPrivateOutbox(favoriteRelayList.saveRelayList(trustedRelays))
|
||||
|
||||
suspend fun followRelayFeed(url: NormalizedRelayUrl) = sendMyPublicAndPrivateOutbox(relayFeedsList.addRelay(url))
|
||||
suspend fun followRelayFeed(url: NormalizedRelayUrl) = sendMyPublicAndPrivateOutbox(favoriteRelayList.addRelay(url))
|
||||
|
||||
suspend fun unfollowRelayFeed(url: NormalizedRelayUrl) = sendMyPublicAndPrivateOutbox(relayFeedsList.removeRelay(url))
|
||||
suspend fun unfollowRelayFeed(url: NormalizedRelayUrl) = sendMyPublicAndPrivateOutbox(favoriteRelayList.removeRelay(url))
|
||||
|
||||
suspend fun saveBlockedRelayList(blockedRelays: List<NormalizedRelayUrl>) = sendMyPublicAndPrivateOutbox(blockedRelayList.saveRelayList(blockedRelays))
|
||||
|
||||
@@ -3783,7 +3783,7 @@ class Account(
|
||||
proxyRelayList.getProxyRelayList(),
|
||||
broadcastRelayList.getBroadcastRelayList(),
|
||||
indexerRelayList.getIndexerRelayList(),
|
||||
relayFeedsList.getRelayFeedsList(),
|
||||
favoriteRelayList.getFavoriteRelayList(),
|
||||
blockedRelayList.getBlockedRelayList(),
|
||||
muteList.getMuteList(),
|
||||
bookmarkState.getBookmarkList(),
|
||||
@@ -4056,9 +4056,9 @@ class Account(
|
||||
logTime("Account ${userProfile().toBestDisplayName()} newEventBundle Update with ${newNotes.size} new notes") {
|
||||
upgradeAttestations()
|
||||
newNotesPreProcessor.runNew(newNotes)
|
||||
peopleLists.newNotes(newNotes)
|
||||
followLists.newNotes(newNotes)
|
||||
labeledBookmarkLists.newNotes(newNotes)
|
||||
followSets.newNotes(newNotes)
|
||||
starterPacks.newNotes(newNotes)
|
||||
bookmarkSets.newNotes(newNotes)
|
||||
interestSets.newNotes(newNotes)
|
||||
ownedEmojiPacks.newNotes(newNotes)
|
||||
}
|
||||
@@ -4069,9 +4069,9 @@ class Account(
|
||||
cache.live.deletedEventBundles.collect { deletedNotes ->
|
||||
logTime("Account ${userProfile().toBestDisplayName()} deletedEventBundle Update with ${deletedNotes.size} new notes") {
|
||||
newNotesPreProcessor.runDeleted(deletedNotes)
|
||||
peopleLists.deletedNotes(deletedNotes)
|
||||
followLists.deletedNotes(deletedNotes)
|
||||
labeledBookmarkLists.deletedNotes(deletedNotes)
|
||||
followSets.deletedNotes(deletedNotes)
|
||||
starterPacks.deletedNotes(deletedNotes)
|
||||
bookmarkSets.deletedNotes(deletedNotes)
|
||||
interestSets.deletedNotes(deletedNotes)
|
||||
ownedEmojiPacks.deletedNotes(deletedNotes)
|
||||
}
|
||||
|
||||
@@ -68,7 +68,7 @@ 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.InterestListEvent
|
||||
import com.vitorpamplona.quartz.nip51Lists.interestList.InterestListEvent
|
||||
import com.vitorpamplona.quartz.nip51Lists.muteList.MuteListEvent
|
||||
import com.vitorpamplona.quartz.nip51Lists.relayLists.BlockedRelayListEvent
|
||||
import com.vitorpamplona.quartz.nip51Lists.relayLists.FavoriteRelayListEvent
|
||||
@@ -208,15 +208,15 @@ class AccountSettings(
|
||||
var backupNIP65RelayList: AdvertisedRelayListEvent? = null,
|
||||
var backupSearchRelayList: SearchRelayListEvent? = null,
|
||||
var backupIndexRelayList: IndexerRelayListEvent? = null,
|
||||
var backupRelayFeedsList: FavoriteRelayListEvent? = null,
|
||||
var backupFavoriteRelayList: FavoriteRelayListEvent? = null,
|
||||
var backupBlockedRelayList: BlockedRelayListEvent? = null,
|
||||
var backupTrustedRelayList: TrustedRelayListEvent? = null,
|
||||
var backupMuteList: MuteListEvent? = null,
|
||||
var backupPrivateHomeRelayList: PrivateOutboxRelayListEvent? = null,
|
||||
var backupAppSpecificData: AppSpecificDataEvent? = null,
|
||||
var backupChannelList: PublicChatListEvent? = null,
|
||||
var backupPublicChatList: PublicChatListEvent? = null,
|
||||
var backupCommunityList: CommunityListEvent? = null,
|
||||
var backupHashtagList: InterestListEvent? = null,
|
||||
var backupInterestList: InterestListEvent? = null,
|
||||
var backupFavoriteAlgoFeedsList: FavoriteAlgoFeedsListEvent? = null,
|
||||
var backupGeohashList: GeohashListEvent? = null,
|
||||
var backupEphemeralChatList: EphemeralChatListEvent? = null,
|
||||
@@ -1084,13 +1084,13 @@ class AccountSettings(
|
||||
is Bolt12OfferListEvent -> updateBolt12Offers(incoming)
|
||||
is SearchRelayListEvent -> updateSearchRelayList(incoming)
|
||||
is IndexerRelayListEvent -> updateIndexRelayList(incoming)
|
||||
is FavoriteRelayListEvent -> updateRelayFeedList(incoming)
|
||||
is FavoriteRelayListEvent -> updateFavoriteRelayList(incoming)
|
||||
is BlockedRelayListEvent -> updateBlockedRelayList(incoming)
|
||||
is TrustedRelayListEvent -> updateTrustedRelayList(incoming)
|
||||
is PrivateOutboxRelayListEvent -> updatePrivateHomeRelayList(incoming)
|
||||
is PublicChatListEvent -> updateChannelListTo(incoming)
|
||||
is PublicChatListEvent -> updatePublicChatListTo(incoming)
|
||||
is GeohashListEvent -> updateGeohashListTo(incoming)
|
||||
is InterestListEvent -> updateHashtagListTo(incoming)
|
||||
is InterestListEvent -> updateInterestListTo(incoming)
|
||||
is FavoriteAlgoFeedsListEvent -> updateFavoriteAlgoFeedsListTo(incoming)
|
||||
is CommunityListEvent -> updateCommunityListTo(incoming)
|
||||
is EphemeralChatListEvent -> updateEphemeralChatListTo(incoming)
|
||||
@@ -1284,11 +1284,11 @@ class AccountSettings(
|
||||
}
|
||||
}
|
||||
|
||||
fun updateRelayFeedList(newRelayFeedList: FavoriteRelayListEvent?) {
|
||||
if (newRelayFeedList == null) return
|
||||
fun updateFavoriteRelayList(newFavoriteRelayList: FavoriteRelayListEvent?) {
|
||||
if (newFavoriteRelayList == null) return
|
||||
|
||||
if (backupGuard.accept(backupRelayFeedsList, newRelayFeedList, isEmpty = newRelayFeedList.tags.isEmpty()) { updateRelayFeedList(newRelayFeedList) }) {
|
||||
backupRelayFeedsList = newRelayFeedList
|
||||
if (backupGuard.accept(backupFavoriteRelayList, newFavoriteRelayList, isEmpty = newFavoriteRelayList.tags.isEmpty()) { updateFavoriteRelayList(newFavoriteRelayList) }) {
|
||||
backupFavoriteRelayList = newFavoriteRelayList
|
||||
saveAccountSettings()
|
||||
}
|
||||
}
|
||||
@@ -1320,13 +1320,13 @@ class AccountSettings(
|
||||
}
|
||||
}
|
||||
|
||||
override fun channelList() = backupChannelList
|
||||
override fun publicChatList() = backupPublicChatList
|
||||
|
||||
override fun updateChannelListTo(newChannelList: PublicChatListEvent?) {
|
||||
if (newChannelList == null) return
|
||||
override fun updatePublicChatListTo(newPublicChatList: PublicChatListEvent?) {
|
||||
if (newPublicChatList == null) return
|
||||
|
||||
if (backupGuard.accept(backupChannelList, newChannelList, isEmpty = newChannelList.tags.isEmpty()) { updateChannelListTo(newChannelList) }) {
|
||||
backupChannelList = newChannelList
|
||||
if (backupGuard.accept(backupPublicChatList, newPublicChatList, isEmpty = newPublicChatList.tags.isEmpty()) { updatePublicChatListTo(newPublicChatList) }) {
|
||||
backupPublicChatList = newPublicChatList
|
||||
saveAccountSettings()
|
||||
}
|
||||
}
|
||||
@@ -1340,11 +1340,11 @@ class AccountSettings(
|
||||
}
|
||||
}
|
||||
|
||||
fun updateHashtagListTo(newHashtagList: InterestListEvent?) {
|
||||
if (newHashtagList == null) return
|
||||
fun updateInterestListTo(newInterestList: InterestListEvent?) {
|
||||
if (newInterestList == null) return
|
||||
|
||||
if (backupGuard.accept(backupHashtagList, newHashtagList, isEmpty = newHashtagList.tags.isEmpty()) { updateHashtagListTo(newHashtagList) }) {
|
||||
backupHashtagList = newHashtagList
|
||||
if (backupGuard.accept(backupInterestList, newInterestList, isEmpty = newInterestList.tags.isEmpty()) { updateInterestListTo(newInterestList) }) {
|
||||
backupInterestList = newInterestList
|
||||
saveAccountSettings()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -45,7 +45,7 @@ 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.BookmarkSetEvent
|
||||
import com.vitorpamplona.quartz.nip51Lists.bookmarkSet.BookmarkSetEvent
|
||||
import com.vitorpamplona.quartz.nip53LiveActivities.meetingSpaces.MeetingRoomEvent
|
||||
import com.vitorpamplona.quartz.nip53LiveActivities.meetingSpaces.MeetingSpaceEvent
|
||||
import com.vitorpamplona.quartz.nip53LiveActivities.streaming.LiveActivitiesEvent
|
||||
|
||||
+42
-42
@@ -18,7 +18,7 @@
|
||||
* AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
package com.vitorpamplona.amethyst.model.nip51Lists.labeledBookmarkLists
|
||||
package com.vitorpamplona.amethyst.model.nip51Lists.bookmarkSets
|
||||
|
||||
import com.vitorpamplona.amethyst.commons.model.AddressableNote
|
||||
import com.vitorpamplona.amethyst.commons.model.Note
|
||||
@@ -27,7 +27,7 @@ import com.vitorpamplona.amethyst.commons.model.cache.LocalCache
|
||||
import com.vitorpamplona.amethyst.commons.model.cache.filter
|
||||
import com.vitorpamplona.amethyst.commons.model.eventIdSet
|
||||
import com.vitorpamplona.amethyst.commons.model.events
|
||||
import com.vitorpamplona.amethyst.commons.model.nip51Lists.labeledBookmarkLists.LabeledBookmarkList
|
||||
import com.vitorpamplona.amethyst.commons.model.nip51Lists.bookmarkSets.BookmarkSet
|
||||
import com.vitorpamplona.amethyst.commons.model.updateFlow
|
||||
import com.vitorpamplona.amethyst.model.Account
|
||||
import com.vitorpamplona.quartz.nip01Core.core.Address
|
||||
@@ -37,10 +37,10 @@ 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.BookmarkSetEvent
|
||||
import com.vitorpamplona.quartz.nip51Lists.labeledBookmarkList.description
|
||||
import com.vitorpamplona.quartz.nip51Lists.labeledBookmarkList.image
|
||||
import com.vitorpamplona.quartz.nip51Lists.labeledBookmarkList.title
|
||||
import com.vitorpamplona.quartz.nip51Lists.bookmarkSet.BookmarkSetEvent
|
||||
import com.vitorpamplona.quartz.nip51Lists.bookmarkSet.description
|
||||
import com.vitorpamplona.quartz.nip51Lists.bookmarkSet.image
|
||||
import com.vitorpamplona.quartz.nip51Lists.bookmarkSet.title
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.ExperimentalCoroutinesApi
|
||||
@@ -55,41 +55,41 @@ import kotlinx.coroutines.flow.stateIn
|
||||
import kotlinx.coroutines.flow.transformLatest
|
||||
import kotlinx.coroutines.flow.update
|
||||
|
||||
class LabeledBookmarkListsState(
|
||||
class BookmarkSetsState(
|
||||
val signer: NostrSigner,
|
||||
val cache: LocalCache,
|
||||
val scope: CoroutineScope,
|
||||
) {
|
||||
val user = cache.getOrCreateUser(signer.pubKey)
|
||||
|
||||
fun existingLabeledBookmarkNotes() = cache.addressables.filter(BookmarkSetEvent.KIND, user.pubkeyHex)
|
||||
fun existingBookmarkSetNotes() = cache.addressables.filter(BookmarkSetEvent.KIND, user.pubkeyHex)
|
||||
|
||||
val labeledBookmarkListVersions = MutableStateFlow(0)
|
||||
val bookmarkSetVersions = MutableStateFlow(0)
|
||||
|
||||
val labeledBookmarkListNotes =
|
||||
labeledBookmarkListVersions
|
||||
.map { existingLabeledBookmarkNotes() }
|
||||
.onStart { emit(existingLabeledBookmarkNotes()) }
|
||||
val bookmarkSetNotes =
|
||||
bookmarkSetVersions
|
||||
.map { existingBookmarkSetNotes() }
|
||||
.onStart { emit(existingBookmarkSetNotes()) }
|
||||
.flowOn(Dispatchers.IO)
|
||||
.stateIn(scope, SharingStarted.Eagerly, emptyList())
|
||||
|
||||
val labeledBookmarkListEventIds =
|
||||
labeledBookmarkListNotes
|
||||
val bookmarkSetEventIds =
|
||||
bookmarkSetNotes
|
||||
.map { it.eventIdSet() }
|
||||
.onStart { emit(labeledBookmarkListNotes.value.eventIdSet()) }
|
||||
.onStart { emit(bookmarkSetNotes.value.eventIdSet()) }
|
||||
.flowOn(Dispatchers.IO)
|
||||
.stateIn(scope, SharingStarted.Eagerly, emptySet())
|
||||
|
||||
@OptIn(ExperimentalCoroutinesApi::class)
|
||||
val latestBookmarkLists: StateFlow<List<BookmarkSetEvent>> =
|
||||
labeledBookmarkListNotes
|
||||
bookmarkSetNotes
|
||||
.transformLatest { emitAll(it.updateFlow<BookmarkSetEvent>()) }
|
||||
.onStart { emit(labeledBookmarkListNotes.value.events()) }
|
||||
.onStart { emit(bookmarkSetNotes.value.events()) }
|
||||
.flowOn(Dispatchers.IO)
|
||||
.stateIn(scope, SharingStarted.Eagerly, emptyList())
|
||||
|
||||
suspend fun BookmarkSetEvent.toLabeledBookmarkList() =
|
||||
LabeledBookmarkList(
|
||||
suspend fun BookmarkSetEvent.toBookmarkSet() =
|
||||
BookmarkSet(
|
||||
identifier = dTag(),
|
||||
title = titleOrName() ?: dTag(),
|
||||
description = description(),
|
||||
@@ -98,23 +98,23 @@ class LabeledBookmarkListsState(
|
||||
publicBookmarks = publicBookmarks().toSet(),
|
||||
)
|
||||
|
||||
suspend fun List<BookmarkSetEvent>.toLabeledBookmarkListsFeed() = map { it.toLabeledBookmarkList() }.sortedBy { it.title }
|
||||
suspend fun List<BookmarkSetEvent>.toBookmarkSetsFeed() = map { it.toBookmarkSet() }.sortedBy { it.title }
|
||||
|
||||
val listFeedFlow =
|
||||
latestBookmarkLists
|
||||
.map { it.toLabeledBookmarkListsFeed() }
|
||||
.onStart { emit(latestBookmarkLists.value.toLabeledBookmarkListsFeed()) }
|
||||
.map { it.toBookmarkSetsFeed() }
|
||||
.onStart { emit(latestBookmarkLists.value.toBookmarkSetsFeed()) }
|
||||
.flowOn(Dispatchers.IO)
|
||||
.stateIn(scope, SharingStarted.Eagerly, emptyList())
|
||||
|
||||
fun List<LabeledBookmarkList>.getList(bookmarkListId: String) =
|
||||
fun List<BookmarkSet>.getList(bookmarkListId: String) =
|
||||
this.firstOrNull {
|
||||
it.identifier == bookmarkListId
|
||||
}
|
||||
|
||||
fun getBookmarkList(dTag: String) = listFeedFlow.value.getList(bookmarkListId = dTag)
|
||||
|
||||
fun DeletionRequestEvent.hasAnyDeletedBookmarkLists() = deleteAddressesWithKind(BookmarkSetEvent.KIND) || deletesAnyEventIn(labeledBookmarkListEventIds.value)
|
||||
fun DeletionRequestEvent.hasAnyDeletedBookmarkLists() = deleteAddressesWithKind(BookmarkSetEvent.KIND) || deletesAnyEventIn(bookmarkSetEventIds.value)
|
||||
|
||||
fun hasItemInNoteList(notes: Set<Note>): Boolean =
|
||||
notes.anyNotNullEvent { event ->
|
||||
@@ -138,14 +138,14 @@ class LabeledBookmarkListsState(
|
||||
}
|
||||
|
||||
fun forceRefresh() {
|
||||
labeledBookmarkListVersions.update { it + 1 }
|
||||
bookmarkSetVersions.update { it + 1 }
|
||||
}
|
||||
|
||||
fun getLabeledBookmarkListNote(bookmarkIdentifier: String): AddressableNote? = existingLabeledBookmarkNotes().find { it.dTag() == bookmarkIdentifier }
|
||||
fun getBookmarkSetNote(bookmarkIdentifier: String): AddressableNote? = existingBookmarkSetNotes().find { it.dTag() == bookmarkIdentifier }
|
||||
|
||||
fun getLabeledBookmarkListEvent(bookmarkIdentifier: String): BookmarkSetEvent = getLabeledBookmarkListNote(bookmarkIdentifier)?.event as BookmarkSetEvent
|
||||
fun getBookmarkSetEvent(bookmarkIdentifier: String): BookmarkSetEvent = getBookmarkSetNote(bookmarkIdentifier)?.event as BookmarkSetEvent
|
||||
|
||||
fun getLabeledBookmarkListFlow(bookmarkIdentifier: String) =
|
||||
fun getBookmarkSetFlow(bookmarkIdentifier: String) =
|
||||
listFeedFlow
|
||||
.map { it.getList(bookmarkIdentifier) }
|
||||
.onStart {
|
||||
@@ -154,7 +154,7 @@ class LabeledBookmarkListsState(
|
||||
)
|
||||
}.flowOn(Dispatchers.IO)
|
||||
|
||||
suspend fun addLabeledBookmarkList(
|
||||
suspend fun addBookmarkSet(
|
||||
listName: String,
|
||||
listDescription: String? = null,
|
||||
listImage: String? = null,
|
||||
@@ -178,10 +178,10 @@ class LabeledBookmarkListsState(
|
||||
listName: String?,
|
||||
listDescription: String?,
|
||||
listImage: String?,
|
||||
bookmarkList: LabeledBookmarkList,
|
||||
bookmarkList: BookmarkSet,
|
||||
account: Account,
|
||||
) {
|
||||
val listEvent = getLabeledBookmarkListEvent(bookmarkList.identifier)
|
||||
val listEvent = getBookmarkSetEvent(bookmarkList.identifier)
|
||||
|
||||
val template =
|
||||
listEvent.update {
|
||||
@@ -197,10 +197,10 @@ class LabeledBookmarkListsState(
|
||||
|
||||
suspend fun renameBookmarkList(
|
||||
newName: String,
|
||||
bookmarkList: LabeledBookmarkList,
|
||||
bookmarkList: BookmarkSet,
|
||||
account: Account,
|
||||
) {
|
||||
val listEvent = getLabeledBookmarkListEvent(bookmarkList.identifier)
|
||||
val listEvent = getBookmarkSetEvent(bookmarkList.identifier)
|
||||
val renamedList =
|
||||
BookmarkSetEvent.modifyName(
|
||||
earlierVersion = listEvent,
|
||||
@@ -212,10 +212,10 @@ class LabeledBookmarkListsState(
|
||||
|
||||
suspend fun modifyListDescription(
|
||||
newDescription: String?,
|
||||
bookmarkList: LabeledBookmarkList,
|
||||
bookmarkList: BookmarkSet,
|
||||
account: Account,
|
||||
) {
|
||||
val listEvent = getLabeledBookmarkListEvent(bookmarkList.identifier)
|
||||
val listEvent = getBookmarkSetEvent(bookmarkList.identifier)
|
||||
val modifiedList =
|
||||
BookmarkSetEvent.modifyDescription(
|
||||
earlierVersion = listEvent,
|
||||
@@ -226,7 +226,7 @@ class LabeledBookmarkListsState(
|
||||
}
|
||||
|
||||
suspend fun cloneBookmarkList(
|
||||
currentBookmarkList: LabeledBookmarkList,
|
||||
currentBookmarkList: BookmarkSet,
|
||||
customCloneName: String?,
|
||||
customCloneDescription: String?,
|
||||
account: Account,
|
||||
@@ -246,7 +246,7 @@ class LabeledBookmarkListsState(
|
||||
bookmarkListIdentifier: String,
|
||||
account: Account,
|
||||
) {
|
||||
val listEvent = getLabeledBookmarkListEvent(bookmarkListIdentifier)
|
||||
val listEvent = getBookmarkSetEvent(bookmarkListIdentifier)
|
||||
val deletionEventTemplate = DeletionRequestEvent.build(listOf(listEvent))
|
||||
val deletionEvent = account.signer.sign(deletionEventTemplate)
|
||||
account.sendMyPublicAndPrivateOutbox(deletionEvent)
|
||||
@@ -258,7 +258,7 @@ class LabeledBookmarkListsState(
|
||||
isBookmarkPrivate: Boolean,
|
||||
account: Account,
|
||||
) {
|
||||
val currentBookmarkList = getLabeledBookmarkListEvent(bookmarkListIdentifier)
|
||||
val currentBookmarkList = getBookmarkSetEvent(bookmarkListIdentifier)
|
||||
val updatedList =
|
||||
BookmarkSetEvent.addBookmark(
|
||||
earlierVersion = currentBookmarkList,
|
||||
@@ -275,7 +275,7 @@ class LabeledBookmarkListsState(
|
||||
isBookmarkCurrentlyPrivate: Boolean,
|
||||
account: Account,
|
||||
) {
|
||||
val bookmarkList = getLabeledBookmarkListEvent(bookmarkListIdentifier)
|
||||
val bookmarkList = getBookmarkSetEvent(bookmarkListIdentifier)
|
||||
val updatedList =
|
||||
BookmarkSetEvent.moveBookmark(
|
||||
earlierVersion = bookmarkList,
|
||||
@@ -292,7 +292,7 @@ class LabeledBookmarkListsState(
|
||||
isBookmarkPrivate: Boolean,
|
||||
account: Account,
|
||||
) {
|
||||
val currentBookmarkList = getLabeledBookmarkListEvent(bookmarkListIdentifier)
|
||||
val currentBookmarkList = getBookmarkSetEvent(bookmarkListIdentifier)
|
||||
val updatedList =
|
||||
BookmarkSetEvent.removeBookmark(
|
||||
earlierVersion = currentBookmarkList,
|
||||
@@ -311,7 +311,7 @@ class LabeledBookmarkListsState(
|
||||
) {
|
||||
if (deletedEventIds.isEmpty() && deletedAddresses.isEmpty()) return
|
||||
|
||||
val currentList = getLabeledBookmarkListNote(bookmarkListIdentifier)?.event as? BookmarkSetEvent ?: return
|
||||
val currentList = getBookmarkSetNote(bookmarkListIdentifier)?.event as? BookmarkSetEvent ?: return
|
||||
|
||||
val newPublicTags =
|
||||
currentList.tags
|
||||
+25
-25
@@ -18,12 +18,12 @@
|
||||
* AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
package com.vitorpamplona.amethyst.model.nip51Lists.relayFeeds
|
||||
package com.vitorpamplona.amethyst.model.nip51Lists.favoriteRelays
|
||||
|
||||
import com.vitorpamplona.amethyst.commons.model.Note
|
||||
import com.vitorpamplona.amethyst.commons.model.NoteState
|
||||
import com.vitorpamplona.amethyst.commons.model.cache.LocalCache
|
||||
import com.vitorpamplona.amethyst.commons.model.nip51Lists.relayFeeds.RelayFeedsListDecryptionCache
|
||||
import com.vitorpamplona.amethyst.commons.model.nip51Lists.favoriteRelays.FavoriteRelayListDecryptionCache
|
||||
import com.vitorpamplona.amethyst.model.AccountSettings
|
||||
import com.vitorpamplona.quartz.nip01Core.relay.normalizer.NormalizedRelayUrl
|
||||
import com.vitorpamplona.quartz.nip01Core.signers.NostrSigner
|
||||
@@ -40,32 +40,32 @@ import kotlinx.coroutines.flow.onStart
|
||||
import kotlinx.coroutines.flow.stateIn
|
||||
import kotlinx.coroutines.launch
|
||||
|
||||
class RelayFeedListState(
|
||||
class FavoriteRelayListState(
|
||||
val signer: NostrSigner,
|
||||
val cache: LocalCache,
|
||||
val decryptionCache: RelayFeedsListDecryptionCache,
|
||||
val decryptionCache: FavoriteRelayListDecryptionCache,
|
||||
val scope: CoroutineScope,
|
||||
val settings: AccountSettings,
|
||||
) {
|
||||
// Creates a long-term reference for this note so that the GC doesn't collect the note it self
|
||||
val relayFeedListNote = cache.getOrCreateAddressableNote(getRelayFeedsListAddress())
|
||||
val favoriteRelayListNote = cache.getOrCreateAddressableNote(getFavoriteRelayListAddress())
|
||||
|
||||
fun getRelayFeedsListAddress() = FavoriteRelayListEvent.createAddress(signer.pubKey)
|
||||
fun getFavoriteRelayListAddress() = FavoriteRelayListEvent.createAddress(signer.pubKey)
|
||||
|
||||
fun getRelayFeedsListFlow(): StateFlow<NoteState> = relayFeedListNote.flow().metadata.stateFlow
|
||||
fun getFavoriteRelayListFlow(): StateFlow<NoteState> = favoriteRelayListNote.flow().metadata.stateFlow
|
||||
|
||||
fun getRelayFeedsList(): FavoriteRelayListEvent? = relayFeedListNote.event as? FavoriteRelayListEvent
|
||||
fun getFavoriteRelayList(): FavoriteRelayListEvent? = favoriteRelayListNote.event as? FavoriteRelayListEvent
|
||||
|
||||
fun relayFeedsListEvent(note: Note) = note.event as? FavoriteRelayListEvent ?: settings.backupRelayFeedsList
|
||||
fun favoriteRelayListEvent(note: Note) = note.event as? FavoriteRelayListEvent ?: settings.backupFavoriteRelayList
|
||||
|
||||
suspend fun normalizeRelayFeedsListWithBackup(note: Note): Set<NormalizedRelayUrl> = relayFeedsListEvent(note)?.let { decryptionCache.relays(it) }?.ifEmpty { null } ?: emptySet()
|
||||
suspend fun normalizeFavoriteRelayListWithBackup(note: Note): Set<NormalizedRelayUrl> = favoriteRelayListEvent(note)?.let { decryptionCache.relays(it) }?.ifEmpty { null } ?: emptySet()
|
||||
|
||||
suspend fun normalizeRelayFeedsListWithBackupNoDefaults(note: Note): Set<NormalizedRelayUrl> = relayFeedsListEvent(note)?.let { decryptionCache.relays(it) } ?: emptySet()
|
||||
suspend fun normalizeFavoriteRelayListWithBackupNoDefaults(note: Note): Set<NormalizedRelayUrl> = favoriteRelayListEvent(note)?.let { decryptionCache.relays(it) } ?: emptySet()
|
||||
|
||||
val flow =
|
||||
getRelayFeedsListFlow()
|
||||
.map { normalizeRelayFeedsListWithBackup(it.note) }
|
||||
.onStart { emit(normalizeRelayFeedsListWithBackup(relayFeedListNote)) }
|
||||
getFavoriteRelayListFlow()
|
||||
.map { normalizeFavoriteRelayListWithBackup(it.note) }
|
||||
.onStart { emit(normalizeFavoriteRelayListWithBackup(favoriteRelayListNote)) }
|
||||
.flowOn(Dispatchers.IO)
|
||||
.stateIn(
|
||||
scope,
|
||||
@@ -74,9 +74,9 @@ class RelayFeedListState(
|
||||
)
|
||||
|
||||
val flowNoDefaults =
|
||||
getRelayFeedsListFlow()
|
||||
.map { normalizeRelayFeedsListWithBackupNoDefaults(it.note) }
|
||||
.onStart { emit(normalizeRelayFeedsListWithBackupNoDefaults(relayFeedListNote)) }
|
||||
getFavoriteRelayListFlow()
|
||||
.map { normalizeFavoriteRelayListWithBackupNoDefaults(it.note) }
|
||||
.onStart { emit(normalizeFavoriteRelayListWithBackupNoDefaults(favoriteRelayListNote)) }
|
||||
.flowOn(Dispatchers.IO)
|
||||
.stateIn(
|
||||
scope,
|
||||
@@ -85,24 +85,24 @@ class RelayFeedListState(
|
||||
)
|
||||
|
||||
suspend fun addRelay(relay: NormalizedRelayUrl): FavoriteRelayListEvent {
|
||||
val current = normalizeRelayFeedsListWithBackupNoDefaults(relayFeedListNote).toMutableList()
|
||||
val current = normalizeFavoriteRelayListWithBackupNoDefaults(favoriteRelayListNote).toMutableList()
|
||||
if (relay !in current) current.add(relay)
|
||||
return saveRelayList(current)
|
||||
}
|
||||
|
||||
suspend fun removeRelay(relay: NormalizedRelayUrl): FavoriteRelayListEvent? {
|
||||
val current = normalizeRelayFeedsListWithBackupNoDefaults(relayFeedListNote).toMutableList()
|
||||
val current = normalizeFavoriteRelayListWithBackupNoDefaults(favoriteRelayListNote).toMutableList()
|
||||
if (relay !in current) return null
|
||||
current.remove(relay)
|
||||
return saveRelayList(current)
|
||||
}
|
||||
|
||||
suspend fun saveRelayList(relayFeeds: List<NormalizedRelayUrl>): FavoriteRelayListEvent {
|
||||
val relayFeedsList = getRelayFeedsList()
|
||||
val favoriteRelayList = getFavoriteRelayList()
|
||||
|
||||
return if (relayFeedsList != null && relayFeedsList.tags.isNotEmpty()) {
|
||||
return if (favoriteRelayList != null && favoriteRelayList.tags.isNotEmpty()) {
|
||||
FavoriteRelayListEvent.updateRelayList(
|
||||
earlierVersion = relayFeedsList,
|
||||
earlierVersion = favoriteRelayList,
|
||||
relays = relayFeeds,
|
||||
signer = signer,
|
||||
)
|
||||
@@ -115,7 +115,7 @@ class RelayFeedListState(
|
||||
}
|
||||
|
||||
init {
|
||||
settings.backupRelayFeedsList?.let {
|
||||
settings.backupFavoriteRelayList?.let {
|
||||
Log.d("AccountRegisterObservers") { "Loading saved relay feeds list ${it.toJson()}" }
|
||||
@OptIn(DelicateCoroutinesApi::class)
|
||||
scope.launch(Dispatchers.IO) { LocalCache.justConsumeMyOwnEvent(it) }
|
||||
@@ -123,10 +123,10 @@ class RelayFeedListState(
|
||||
|
||||
scope.launch(Dispatchers.IO) {
|
||||
Log.d("AccountRegisterObservers", "Relay feeds list Collector Start")
|
||||
getRelayFeedsListFlow().collect {
|
||||
getFavoriteRelayListFlow().collect {
|
||||
Log.d("AccountRegisterObservers") { "Updating Relay feeds list for ${signer.pubKey}" }
|
||||
(it.note.event as? FavoriteRelayListEvent)?.let {
|
||||
settings.updateRelayFeedList(it)
|
||||
settings.updateFavoriteRelayList(it)
|
||||
}
|
||||
}
|
||||
}
|
||||
+9
-9
@@ -18,7 +18,7 @@
|
||||
* AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
package com.vitorpamplona.amethyst.model.nip51Lists.peopleList
|
||||
package com.vitorpamplona.amethyst.model.nip51Lists.followSets
|
||||
|
||||
import com.vitorpamplona.amethyst.commons.model.AddressableNote
|
||||
import com.vitorpamplona.amethyst.commons.model.Note
|
||||
@@ -28,19 +28,19 @@ import com.vitorpamplona.amethyst.commons.model.cache.LocalCache
|
||||
import com.vitorpamplona.amethyst.commons.model.cache.filter
|
||||
import com.vitorpamplona.amethyst.commons.model.eventIdSet
|
||||
import com.vitorpamplona.amethyst.commons.model.events
|
||||
import com.vitorpamplona.amethyst.commons.model.nip51Lists.peopleList.PeopleList
|
||||
import com.vitorpamplona.amethyst.commons.model.nip51Lists.peopleList.PeopleListDecryptionCache
|
||||
import com.vitorpamplona.amethyst.commons.model.nip51Lists.followSets.FollowSetDecryptionCache
|
||||
import com.vitorpamplona.amethyst.commons.model.nip51Lists.followSets.PeopleList
|
||||
import com.vitorpamplona.amethyst.commons.model.updateFlow
|
||||
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.DeletionRequestEvent
|
||||
import com.vitorpamplona.quartz.nip51Lists.followSet.FollowSetEvent
|
||||
import com.vitorpamplona.quartz.nip51Lists.followSet.description
|
||||
import com.vitorpamplona.quartz.nip51Lists.followSet.image
|
||||
import com.vitorpamplona.quartz.nip51Lists.followSet.title
|
||||
import com.vitorpamplona.quartz.nip51Lists.muteList.tags.UserTag
|
||||
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
|
||||
import com.vitorpamplona.quartz.utils.flattenToSet
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
@@ -62,10 +62,10 @@ import java.util.UUID
|
||||
*
|
||||
* This class must receive updates from Large Cache as it receives new events.
|
||||
*/
|
||||
class PeopleListsState(
|
||||
class FollowSetsState(
|
||||
val signer: NostrSigner,
|
||||
val cache: LocalCache,
|
||||
val decryptionCache: PeopleListDecryptionCache,
|
||||
val decryptionCache: FollowSetDecryptionCache,
|
||||
val scope: CoroutineScope,
|
||||
) {
|
||||
val user = cache.getOrCreateUser(signer.pubKey)
|
||||
+18
-18
@@ -18,7 +18,7 @@
|
||||
* AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
package com.vitorpamplona.amethyst.model.nip51Lists.peopleList
|
||||
package com.vitorpamplona.amethyst.model.nip51Lists.followSets
|
||||
|
||||
import com.vitorpamplona.amethyst.commons.model.AddressableNote
|
||||
import com.vitorpamplona.amethyst.commons.model.Note
|
||||
@@ -28,7 +28,7 @@ import com.vitorpamplona.amethyst.commons.model.cache.LocalCache
|
||||
import com.vitorpamplona.amethyst.commons.model.cache.filter
|
||||
import com.vitorpamplona.amethyst.commons.model.eventIdSet
|
||||
import com.vitorpamplona.amethyst.commons.model.events
|
||||
import com.vitorpamplona.amethyst.commons.model.nip51Lists.peopleList.PeopleList
|
||||
import com.vitorpamplona.amethyst.commons.model.nip51Lists.followSets.PeopleList
|
||||
import com.vitorpamplona.amethyst.commons.model.updateFlow
|
||||
import com.vitorpamplona.amethyst.model.Account
|
||||
import com.vitorpamplona.quartz.nip01Core.core.HexKey
|
||||
@@ -36,14 +36,14 @@ 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.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
|
||||
import com.vitorpamplona.quartz.nip51Lists.followList.personFirst
|
||||
import com.vitorpamplona.quartz.nip51Lists.followList.removePerson
|
||||
import com.vitorpamplona.quartz.nip51Lists.followList.title
|
||||
import com.vitorpamplona.quartz.nip51Lists.muteList.tags.UserTag
|
||||
import com.vitorpamplona.quartz.nip51Lists.starterPack.StarterPackEvent
|
||||
import com.vitorpamplona.quartz.nip51Lists.starterPack.description
|
||||
import com.vitorpamplona.quartz.nip51Lists.starterPack.image
|
||||
import com.vitorpamplona.quartz.nip51Lists.starterPack.person
|
||||
import com.vitorpamplona.quartz.nip51Lists.starterPack.personFirst
|
||||
import com.vitorpamplona.quartz.nip51Lists.starterPack.removePerson
|
||||
import com.vitorpamplona.quartz.nip51Lists.starterPack.title
|
||||
import com.vitorpamplona.quartz.utils.flattenToSet
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
@@ -65,7 +65,7 @@ import java.util.UUID
|
||||
*
|
||||
* This class must receive updates from Large Cache as it receives new events.
|
||||
*/
|
||||
class FollowListsState(
|
||||
class StarterPacksState(
|
||||
val signer: NostrSigner,
|
||||
val cache: LocalCache,
|
||||
val scope: CoroutineScope,
|
||||
@@ -84,25 +84,25 @@ class FollowListsState(
|
||||
|
||||
val followListVersions = MutableStateFlow(0)
|
||||
|
||||
val followListNotes =
|
||||
val starterPackNotes =
|
||||
followListVersions
|
||||
.map { existingPeopleListNotes() }
|
||||
.onStart { emit(existingPeopleListNotes()) }
|
||||
.flowOn(Dispatchers.IO)
|
||||
.stateIn(scope, SharingStarted.Eagerly, emptyList())
|
||||
|
||||
val followListsEventIds =
|
||||
followListNotes
|
||||
val starterPacksEventIds =
|
||||
starterPackNotes
|
||||
.map { it.eventIdSet() }
|
||||
.onStart { emit(followListNotes.value.eventIdSet()) }
|
||||
.onStart { emit(starterPackNotes.value.eventIdSet()) }
|
||||
.flowOn(Dispatchers.IO)
|
||||
.stateIn(scope, SharingStarted.Eagerly, emptySet())
|
||||
|
||||
@OptIn(ExperimentalCoroutinesApi::class)
|
||||
val latestLists: StateFlow<List<StarterPackEvent>> =
|
||||
followListNotes
|
||||
starterPackNotes
|
||||
.transformLatest { emitAll(it.updateFlow<StarterPackEvent>()) }
|
||||
.onStart { emit(followListNotes.value.events()) }
|
||||
.onStart { emit(starterPackNotes.value.events()) }
|
||||
.flowOn(Dispatchers.IO)
|
||||
.stateIn(scope, SharingStarted.Eagerly, emptyList())
|
||||
|
||||
@@ -148,12 +148,12 @@ class FollowListsState(
|
||||
|
||||
fun isUserInFollowSets(user: User): Boolean = allPeopleListProfiles.value.contains(user.pubkeyHex)
|
||||
|
||||
fun DeletionRequestEvent.hasDeletedAnyFollowList() = deleteAddressesWithKind(StarterPackEvent.KIND) || deletesAnyEventIn(followListsEventIds.value)
|
||||
fun DeletionRequestEvent.hasDeletedAnyStarterPack() = deleteAddressesWithKind(StarterPackEvent.KIND) || deletesAnyEventIn(starterPacksEventIds.value)
|
||||
|
||||
fun hasItemInNoteList(notes: Set<Note>): Boolean =
|
||||
notes.anyNotNullEvent { event ->
|
||||
if (event.pubKey == signer.pubKey) {
|
||||
event is StarterPackEvent || (event is DeletionRequestEvent && event.hasDeletedAnyFollowList())
|
||||
event is StarterPackEvent || (event is DeletionRequestEvent && event.hasDeletedAnyStarterPack())
|
||||
} else {
|
||||
false
|
||||
}
|
||||
+17
-17
@@ -18,15 +18,15 @@
|
||||
* AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
package com.vitorpamplona.amethyst.model.nip51Lists.hashtagLists
|
||||
package com.vitorpamplona.amethyst.model.nip51Lists.interestLists
|
||||
|
||||
import com.vitorpamplona.amethyst.commons.model.Note
|
||||
import com.vitorpamplona.amethyst.commons.model.NoteState
|
||||
import com.vitorpamplona.amethyst.commons.model.cache.LocalCache
|
||||
import com.vitorpamplona.amethyst.commons.model.nip51Lists.hashtagLists.HashtagListDecryptionCache
|
||||
import com.vitorpamplona.amethyst.commons.model.nip51Lists.interestLists.InterestListDecryptionCache
|
||||
import com.vitorpamplona.amethyst.model.AccountSettings
|
||||
import com.vitorpamplona.quartz.nip01Core.signers.NostrSigner
|
||||
import com.vitorpamplona.quartz.nip51Lists.hashtagList.InterestListEvent
|
||||
import com.vitorpamplona.quartz.nip51Lists.interestList.InterestListEvent
|
||||
import com.vitorpamplona.quartz.utils.Log
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.DelicateCoroutinesApi
|
||||
@@ -40,30 +40,30 @@ import kotlinx.coroutines.flow.stateIn
|
||||
import kotlinx.coroutines.flow.transformLatest
|
||||
import kotlinx.coroutines.launch
|
||||
|
||||
class HashtagListState(
|
||||
class InterestListState(
|
||||
val signer: NostrSigner,
|
||||
val cache: LocalCache,
|
||||
val decryptionCache: HashtagListDecryptionCache,
|
||||
val decryptionCache: InterestListDecryptionCache,
|
||||
val scope: CoroutineScope,
|
||||
val settings: AccountSettings,
|
||||
) {
|
||||
// Creates a long-term reference for this note so that the GC doesn't collect the note it self
|
||||
val hashtagListNote = cache.getOrCreateAddressableNote(getHashtagListAddress())
|
||||
val hashtagListNote = cache.getOrCreateAddressableNote(getInterestListAddress())
|
||||
|
||||
fun getHashtagListAddress() = InterestListEvent.createAddress(signer.pubKey)
|
||||
fun getInterestListAddress() = InterestListEvent.createAddress(signer.pubKey)
|
||||
|
||||
fun getHashtagListFlow(): StateFlow<NoteState> = hashtagListNote.flow().metadata.stateFlow
|
||||
fun getInterestListFlow(): StateFlow<NoteState> = hashtagListNote.flow().metadata.stateFlow
|
||||
|
||||
fun getHashtagList(): InterestListEvent? = hashtagListNote.event as? InterestListEvent
|
||||
fun getInterestList(): InterestListEvent? = hashtagListNote.event as? InterestListEvent
|
||||
|
||||
suspend fun hashtagListWithBackup(note: Note): Set<String> {
|
||||
val event = note.event as? InterestListEvent ?: settings.backupHashtagList
|
||||
val event = note.event as? InterestListEvent ?: settings.backupInterestList
|
||||
return event?.let { decryptionCache.hashtags(it).mapTo(mutableSetOf()) { it.lowercase() } } ?: emptySet()
|
||||
}
|
||||
|
||||
@OptIn(ExperimentalCoroutinesApi::class)
|
||||
val flow: StateFlow<Set<String>> =
|
||||
getHashtagListFlow()
|
||||
getInterestListFlow()
|
||||
.transformLatest { noteState ->
|
||||
emit(hashtagListWithBackup(noteState.note))
|
||||
}.onStart {
|
||||
@@ -76,7 +76,7 @@ class HashtagListState(
|
||||
)
|
||||
|
||||
suspend fun follow(hashtags: List<String>): InterestListEvent {
|
||||
val hashtagList = getHashtagList()
|
||||
val hashtagList = getInterestList()
|
||||
|
||||
return if (hashtagList == null) {
|
||||
InterestListEvent.create(hashtags, true, signer)
|
||||
@@ -86,7 +86,7 @@ class HashtagListState(
|
||||
}
|
||||
|
||||
suspend fun follow(hashtag: String): InterestListEvent {
|
||||
val hashtagList = getHashtagList()
|
||||
val hashtagList = getInterestList()
|
||||
|
||||
return if (hashtagList == null) {
|
||||
InterestListEvent.create(hashtag.lowercase(), true, signer)
|
||||
@@ -96,7 +96,7 @@ class HashtagListState(
|
||||
}
|
||||
|
||||
suspend fun unfollow(hashtag: String): InterestListEvent? {
|
||||
val hashtagList = getHashtagList()
|
||||
val hashtagList = getInterestList()
|
||||
|
||||
return if (hashtagList != null) {
|
||||
InterestListEvent.remove(hashtagList, hashtag, signer)
|
||||
@@ -106,7 +106,7 @@ class HashtagListState(
|
||||
}
|
||||
|
||||
init {
|
||||
settings.backupHashtagList?.let { event ->
|
||||
settings.backupInterestList?.let { event ->
|
||||
Log.d("AccountRegisterObservers") { "Loading saved Hashtag list ${event.toJson()}" }
|
||||
@OptIn(DelicateCoroutinesApi::class)
|
||||
scope.launch(Dispatchers.IO) {
|
||||
@@ -116,10 +116,10 @@ class HashtagListState(
|
||||
|
||||
scope.launch(Dispatchers.IO) {
|
||||
Log.d("AccountRegisterObservers", "Hashtag List Collector Start")
|
||||
getHashtagListFlow().collect {
|
||||
getInterestListFlow().collect {
|
||||
Log.d("AccountRegisterObservers") { "Hashtag List for ${signer.pubKey}" }
|
||||
(it.note.event as? InterestListEvent)?.let {
|
||||
settings.updateHashtagListTo(it)
|
||||
settings.updateInterestListTo(it)
|
||||
}
|
||||
}
|
||||
}
|
||||
+2
-2
@@ -49,9 +49,9 @@ class AccountPreferenceStores(
|
||||
val latestMuteList = stringPreferencesKey("latestMuteList")
|
||||
val latestPrivateHomeRelayList = stringPreferencesKey("latestPrivateHomeRelayList")
|
||||
val latestAppSpecificData = stringPreferencesKey("latestAppSpecificData")
|
||||
val latestChannelList = stringPreferencesKey("latestChannelList")
|
||||
val latestPublicChatList = stringPreferencesKey("latestChannelList")
|
||||
val latestCommunityList = stringPreferencesKey("latestCommunityList")
|
||||
val latestHashtagList = stringPreferencesKey("latestHashtagList")
|
||||
val latestInterestList = stringPreferencesKey("latestHashtagList")
|
||||
val latestGeohashList = stringPreferencesKey("latestGeohashList")
|
||||
val latestEphemeralChatList = stringPreferencesKey("latestEphemeralChatList")
|
||||
|
||||
|
||||
+15
-15
@@ -22,10 +22,10 @@ package com.vitorpamplona.amethyst.model.serverList
|
||||
|
||||
import androidx.compose.runtime.Immutable
|
||||
import com.vitorpamplona.amethyst.model.nip02FollowLists.Kind3FollowListState
|
||||
import com.vitorpamplona.amethyst.model.nip51Lists.followSets.FollowSetsState
|
||||
import com.vitorpamplona.amethyst.model.nip51Lists.followSets.StarterPacksState
|
||||
import com.vitorpamplona.amethyst.model.nip51Lists.geohashLists.GeohashListState
|
||||
import com.vitorpamplona.amethyst.model.nip51Lists.hashtagLists.HashtagListState
|
||||
import com.vitorpamplona.amethyst.model.nip51Lists.peopleList.FollowListsState
|
||||
import com.vitorpamplona.amethyst.model.nip51Lists.peopleList.PeopleListsState
|
||||
import com.vitorpamplona.amethyst.model.nip51Lists.interestLists.InterestListState
|
||||
import com.vitorpamplona.amethyst.model.nip72Communities.CommunityListState
|
||||
import com.vitorpamplona.quartz.nip01Core.core.HexKey
|
||||
import com.vitorpamplona.quartz.nip72ModCommunities.follow.tags.CommunityTag
|
||||
@@ -44,9 +44,9 @@ import kotlinx.coroutines.flow.stateIn
|
||||
@Suppress("UNCHECKED_CAST")
|
||||
class MergedFollowListsState(
|
||||
val kind3List: Kind3FollowListState,
|
||||
val peopleList: PeopleListsState,
|
||||
val followList: FollowListsState,
|
||||
val hashtagList: HashtagListState,
|
||||
val followSets: FollowSetsState,
|
||||
val starterPacks: StarterPacksState,
|
||||
val interestList: InterestListState,
|
||||
val geohashList: GeohashListState,
|
||||
val communityList: CommunityListState,
|
||||
val scope: CoroutineScope,
|
||||
@@ -85,9 +85,9 @@ class MergedFollowListsState(
|
||||
combine(
|
||||
listOf(
|
||||
kind3List.flow,
|
||||
peopleList.allGoodPeopleListProfiles,
|
||||
followList.allPeopleListProfiles,
|
||||
hashtagList.flow,
|
||||
followSets.allGoodPeopleListProfiles,
|
||||
starterPacks.allPeopleListProfiles,
|
||||
interestList.flow,
|
||||
geohashList.flow,
|
||||
communityList.flow,
|
||||
),
|
||||
@@ -104,9 +104,9 @@ class MergedFollowListsState(
|
||||
emit(
|
||||
mergeLists(
|
||||
kind3List.flow.value,
|
||||
peopleList.allGoodPeopleListProfiles.value,
|
||||
followList.allPeopleListProfiles.value,
|
||||
hashtagList.flow.value,
|
||||
followSets.allGoodPeopleListProfiles.value,
|
||||
starterPacks.allPeopleListProfiles.value,
|
||||
interestList.flow.value,
|
||||
geohashList.flow.value,
|
||||
communityList.flow.value,
|
||||
),
|
||||
@@ -118,9 +118,9 @@ class MergedFollowListsState(
|
||||
SharingStarted.Eagerly,
|
||||
mergeLists(
|
||||
kind3List.flow.value,
|
||||
peopleList.allGoodPeopleListProfiles.value,
|
||||
followList.allPeopleListProfiles.value,
|
||||
hashtagList.flow.value,
|
||||
followSets.allGoodPeopleListProfiles.value,
|
||||
starterPacks.allPeopleListProfiles.value,
|
||||
interestList.flow.value,
|
||||
geohashList.flow.value,
|
||||
communityList.flow.value,
|
||||
),
|
||||
|
||||
+3
-3
@@ -101,7 +101,7 @@ class NotificationDispatcher(
|
||||
private const val TAG = "NotificationDispatcher"
|
||||
|
||||
// The dispatcher observes the *final* notification payload kinds.
|
||||
// GiftWrap/EphemeralGiftWrap (1059/21059) and SealedRumor (13) are NOT
|
||||
// GiftWrap/EphemeralGiftWrap (1059/21059) and Seal (13) are NOT
|
||||
// listed here — by the time we care, Account.newNotesPreProcessor has
|
||||
// already unwrapped them and inserted the inner payload into LocalCache,
|
||||
// which fires the observer a second time on the inner event.
|
||||
@@ -235,8 +235,8 @@ class NotificationDispatcher(
|
||||
if (event is WakeUpEvent) return@predicate true
|
||||
|
||||
// getNoteIfExists(event) — not (event.id) — so
|
||||
// AddressableEvent kinds (LongTextNote, WikiNote,
|
||||
// LiveChess*, VideoHorizontal/Vertical) resolve to
|
||||
// AddressableEvent kinds (LongFormContent, WikiArticle,
|
||||
// LiveChess*, AddressableNormalVideo/AddressableShortVideo) resolve to
|
||||
// their address-keyed replaceable note. The id-keyed
|
||||
// version note has its replyTo moved away during
|
||||
// insertion (LocalCache.consumeBaseReplaceable), so
|
||||
|
||||
+1
-1
@@ -36,7 +36,7 @@ import kotlinx.coroutines.CancellationException
|
||||
*
|
||||
* Once decrypted, the inner event is fed into [LocalCache]. From there the
|
||||
* normal `Account.newNotesPreProcessor` → `GiftWrapEventHandler` →
|
||||
* `SealedRumorEventHandler` chain unwraps any remaining layers, and
|
||||
* `SealEventHandler` chain unwraps any remaining layers, and
|
||||
* [NotificationDispatcher] picks up the final payload and notifies.
|
||||
*/
|
||||
object PushWrapDecryptor {
|
||||
|
||||
+3
-3
@@ -20,7 +20,7 @@
|
||||
*/
|
||||
package com.vitorpamplona.amethyst.service.relayClient.reqCommand.account.metadata
|
||||
|
||||
import com.vitorpamplona.amethyst.commons.relayClient.assemblers.filterContactCardsByAuthorInTheRelay
|
||||
import com.vitorpamplona.amethyst.commons.relayClient.assemblers.filterUserAssertionsByAuthorInTheRelay
|
||||
import com.vitorpamplona.amethyst.commons.relayClient.subscriptions.ExplainedFilter
|
||||
import com.vitorpamplona.amethyst.commons.relayClient.subscriptions.SubPurpose
|
||||
import com.vitorpamplona.amethyst.model.nip78AppSpecific.AppSpecificState.Companion.APP_SPECIFIC_DATA_D_TAG
|
||||
@@ -37,7 +37,7 @@ import com.vitorpamplona.quartz.nip37Drafts.privateOutbox.PrivateOutboxRelayList
|
||||
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.InterestListEvent
|
||||
import com.vitorpamplona.quartz.nip51Lists.interestList.InterestListEvent
|
||||
import com.vitorpamplona.quartz.nip51Lists.interestSet.InterestSetEvent
|
||||
import com.vitorpamplona.quartz.nip51Lists.relayLists.BlockedRelayListEvent
|
||||
import com.vitorpamplona.quartz.nip51Lists.relayLists.BroadcastRelayListEvent
|
||||
@@ -141,7 +141,7 @@ fun filterAccountInfoAndListsFromKey(
|
||||
),
|
||||
// The account's own kind:30382 contact cards (nicknames, NIP-44 encrypted).
|
||||
// Addressable — one card per target user — hence its own larger-limit filter.
|
||||
filterContactCardsByAuthorInTheRelay(
|
||||
filterUserAssertionsByAuthorInTheRelay(
|
||||
relay = relay,
|
||||
authors = pubkeys,
|
||||
since = since,
|
||||
|
||||
+19
-19
@@ -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.InterestListEvent
|
||||
import com.vitorpamplona.quartz.nip51Lists.interestList.InterestListEvent
|
||||
import kotlinx.collections.immutable.ImmutableList
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.ExperimentalCoroutinesApi
|
||||
@@ -64,11 +64,11 @@ fun observeUserName(
|
||||
// Subscribe in the relay for changes in the metadata of this user.
|
||||
UserFinderFilterAssemblerSubscription(user, accountViewModel)
|
||||
|
||||
val contactCards = accountViewModel.account.contactCards
|
||||
val flow = remember(user) { contactCards.displayNameFlow(user) }
|
||||
val userAssertions = accountViewModel.account.userAssertions
|
||||
val flow = remember(user) { userAssertions.displayNameFlow(user) }
|
||||
|
||||
// Subscribe in the LocalCache for changes that arrive in the device
|
||||
return flow.collectAsStateWithLifecycle(remember(user) { contactCards.cachedDisplayName(user) })
|
||||
return flow.collectAsStateWithLifecycle(remember(user) { userAssertions.cachedDisplayName(user) })
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -83,10 +83,10 @@ fun observeUserNickname(
|
||||
user: User,
|
||||
accountViewModel: AccountViewModel,
|
||||
): State<Nickname?> {
|
||||
val contactCards = accountViewModel.account.contactCards
|
||||
val flow = remember(user) { contactCards.nicknameFlow(user) }
|
||||
val userAssertions = accountViewModel.account.userAssertions
|
||||
val flow = remember(user) { userAssertions.nicknameFlow(user) }
|
||||
|
||||
return flow.collectAsStateWithLifecycle(remember(user) { contactCards.cachedNickname(user) })
|
||||
return flow.collectAsStateWithLifecycle(remember(user) { userAssertions.cachedNickname(user) })
|
||||
}
|
||||
|
||||
@Composable
|
||||
@@ -132,9 +132,9 @@ fun observeUserTagFollowCount(
|
||||
.metadata.stateFlow
|
||||
.sample(1000)
|
||||
.mapLatest { noteState ->
|
||||
(noteState.note.event as? InterestListEvent)?.let { accountViewModel.account.hashtagListDecryptionCache.hashtags(it) }?.size ?: 0
|
||||
(noteState.note.event as? InterestListEvent)?.let { accountViewModel.account.interestListDecryptionCache.hashtags(it) }?.size ?: 0
|
||||
}.onStart {
|
||||
emit((accountViewModel.hashtagFollows(user).event as? InterestListEvent)?.let { accountViewModel.account.hashtagListDecryptionCache.hashtags(it) }?.size ?: 0)
|
||||
emit((accountViewModel.hashtagFollows(user).event as? InterestListEvent)?.let { accountViewModel.account.interestListDecryptionCache.hashtags(it) }?.size ?: 0)
|
||||
}.distinctUntilChanged()
|
||||
.flowOn(Dispatchers.IO)
|
||||
}
|
||||
@@ -160,9 +160,9 @@ fun observeUserTagFollows(
|
||||
.metadata.stateFlow
|
||||
.sample(200)
|
||||
.mapLatest { noteState ->
|
||||
(noteState.note.event as? InterestListEvent)?.let { accountViewModel.account.hashtagListDecryptionCache.hashtags(it) }?.sorted() ?: emptyList()
|
||||
(noteState.note.event as? InterestListEvent)?.let { accountViewModel.account.interestListDecryptionCache.hashtags(it) }?.sorted() ?: emptyList()
|
||||
}.onStart {
|
||||
emit((accountViewModel.hashtagFollows(user).event as? InterestListEvent)?.let { accountViewModel.account.hashtagListDecryptionCache.hashtags(it) }?.sorted() ?: emptyList())
|
||||
emit((accountViewModel.hashtagFollows(user).event as? InterestListEvent)?.let { accountViewModel.account.interestListDecryptionCache.hashtags(it) }?.sorted() ?: emptyList())
|
||||
}.distinctUntilChanged()
|
||||
.flowOn(Dispatchers.IO)
|
||||
}
|
||||
@@ -304,16 +304,16 @@ fun observeUserIsFollowingHashtag(
|
||||
// Subscribe in the LocalCache for changes that arrive in the device
|
||||
val flow =
|
||||
remember(accountViewModel) {
|
||||
accountViewModel.account.hashtagList.flow
|
||||
accountViewModel.account.interestList.flow
|
||||
.mapLatest { hashtags ->
|
||||
hashtag in hashtags
|
||||
}.onStart {
|
||||
emit(hashtag in accountViewModel.account.hashtagList.flow.value)
|
||||
emit(hashtag in accountViewModel.account.interestList.flow.value)
|
||||
}.distinctUntilChanged()
|
||||
.flowOn(Dispatchers.IO)
|
||||
}
|
||||
|
||||
return flow.collectAsStateWithLifecycle(hashtag in accountViewModel.account.hashtagList.flow.value)
|
||||
return flow.collectAsStateWithLifecycle(hashtag in accountViewModel.account.interestList.flow.value)
|
||||
}
|
||||
|
||||
@SuppressLint("StateFlowValueCalledInComposition")
|
||||
@@ -373,16 +373,16 @@ fun observeUserIsFollowingRelay(
|
||||
): State<Boolean> {
|
||||
val flow =
|
||||
remember(accountViewModel) {
|
||||
accountViewModel.account.relayFeedsList.flowNoDefaults
|
||||
accountViewModel.account.favoriteRelayList.flowNoDefaults
|
||||
.mapLatest { relays ->
|
||||
relayUrl in relays
|
||||
}.onStart {
|
||||
emit(relayUrl in accountViewModel.account.relayFeedsList.flowNoDefaults.value)
|
||||
emit(relayUrl in accountViewModel.account.favoriteRelayList.flowNoDefaults.value)
|
||||
}.distinctUntilChanged()
|
||||
.flowOn(Dispatchers.IO)
|
||||
}
|
||||
|
||||
return flow.collectAsStateWithLifecycle(relayUrl in accountViewModel.account.relayFeedsList.flowNoDefaults.value)
|
||||
return flow.collectAsStateWithLifecycle(relayUrl in accountViewModel.account.favoriteRelayList.flowNoDefaults.value)
|
||||
}
|
||||
|
||||
@OptIn(ExperimentalCoroutinesApi::class, FlowPreview::class)
|
||||
@@ -439,7 +439,7 @@ fun observeUserIsFollowingChannel(
|
||||
|
||||
@OptIn(ExperimentalCoroutinesApi::class, FlowPreview::class)
|
||||
@Composable
|
||||
fun observeUserContactCardsScore(
|
||||
fun observeUserAssertionsScore(
|
||||
user: User,
|
||||
accountViewModel: AccountViewModel,
|
||||
subscribe: Boolean = true,
|
||||
@@ -459,7 +459,7 @@ fun observeUserContactCardsScore(
|
||||
|
||||
@OptIn(ExperimentalCoroutinesApi::class, FlowPreview::class)
|
||||
@Composable
|
||||
fun observeUserContactCardsFollowerCount(
|
||||
fun observeUserAssertionsFollowerCount(
|
||||
user: User,
|
||||
accountViewModel: AccountViewModel,
|
||||
): State<String> {
|
||||
|
||||
+2
-2
@@ -26,7 +26,7 @@ import com.vitorpamplona.quartz.nip01Core.signers.NostrSigner
|
||||
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.PrivateZapEvent
|
||||
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: ZapRequestEvent): LnZapPrivateEvent = metered(UsageKeys.DECRYPT_COUNT, UsageKeys.DECRYPT_US) { inner.decryptZapEvent(event) }
|
||||
override suspend fun decryptZapEvent(event: ZapRequestEvent): PrivateZapEvent = metered(UsageKeys.DECRYPT_COUNT, UsageKeys.DECRYPT_US) { inner.decryptZapEvent(event) }
|
||||
|
||||
override suspend fun deriveKey(nonce: HexKey): HexKey = inner.deriveKey(nonce)
|
||||
|
||||
|
||||
@@ -32,8 +32,8 @@ import com.vitorpamplona.quartz.nip01Core.core.HexKey
|
||||
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.followSet.FollowSetEvent
|
||||
import com.vitorpamplona.quartz.nip51Lists.muteList.MuteListEvent
|
||||
import com.vitorpamplona.quartz.nip51Lists.peopleList.FollowSetEvent
|
||||
import com.vitorpamplona.quartz.utils.TimeUtils
|
||||
|
||||
class FilterByListParams(
|
||||
|
||||
@@ -303,7 +303,7 @@ private class WarmTargets {
|
||||
* `m` mime type (a hashed URL may carry no extension). The decode listener
|
||||
* records their dims like any other image.
|
||||
*/
|
||||
fun addFileHeaderMedia(event: Event) {
|
||||
fun addFileMetadataMedia(event: Event) {
|
||||
val urls = event.tags.mapNotNull(UrlTag::parse)
|
||||
if (urls.isEmpty()) return
|
||||
val imageMime = event.tags.firstNotNullOfOrNull(MimeTypeTag::parse)?.startsWith("image/") == true
|
||||
@@ -335,7 +335,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 (FileMetadataEvent and the gallery/file kinds). The generic
|
||||
// imetas() above misses these entirely.
|
||||
targets.addFileHeaderMedia(ev)
|
||||
targets.addFileMetadataMedia(ev)
|
||||
|
||||
// NIP-73 external-content scope. Only kind 1111 carries one, so the type check
|
||||
// keeps every other kind out of the tag walk. Reuses scope() — the same accessor
|
||||
|
||||
+2
-2
@@ -146,7 +146,7 @@ import com.vitorpamplona.amethyst.commons.ui.theme.profileContentHeaderModifier
|
||||
import com.vitorpamplona.amethyst.isDebug
|
||||
import com.vitorpamplona.amethyst.model.Account
|
||||
import com.vitorpamplona.amethyst.service.relayClient.reqCommand.event.observeNote
|
||||
import com.vitorpamplona.amethyst.service.relayClient.reqCommand.user.observeUserContactCardsFollowerCount
|
||||
import com.vitorpamplona.amethyst.service.relayClient.reqCommand.user.observeUserAssertionsFollowerCount
|
||||
import com.vitorpamplona.amethyst.service.relayClient.reqCommand.user.observeUserInfo
|
||||
import com.vitorpamplona.amethyst.service.relayClient.reqCommand.user.observeUserStatuses
|
||||
import com.vitorpamplona.amethyst.ui.layouts.PermanentDrawerWidth
|
||||
@@ -587,7 +587,7 @@ fun DisplayFollowerCount(
|
||||
baseAccountUser: Account,
|
||||
accountViewModel: AccountViewModel,
|
||||
) {
|
||||
val followerCount by observeUserContactCardsFollowerCount(baseAccountUser.userProfile(), accountViewModel)
|
||||
val followerCount by observeUserAssertionsFollowerCount(baseAccountUser.userProfile(), accountViewModel)
|
||||
|
||||
Text(
|
||||
text = followerCount,
|
||||
|
||||
+1
-1
@@ -57,7 +57,7 @@ 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.StarterPackEvent
|
||||
import com.vitorpamplona.quartz.nip51Lists.starterPack.StarterPackEvent
|
||||
import com.vitorpamplona.quartz.nip53LiveActivities.chat.LiveActivitiesChatMessageEvent
|
||||
import com.vitorpamplona.quartz.nip53LiveActivities.streaming.LiveActivitiesEvent
|
||||
import com.vitorpamplona.quartz.nip57Zaps.ZapReceiptEvent
|
||||
|
||||
@@ -106,7 +106,7 @@ import com.vitorpamplona.amethyst.commons.ui.theme.bitcoinColor
|
||||
import com.vitorpamplona.amethyst.commons.ui.theme.overPictureBackground
|
||||
import com.vitorpamplona.amethyst.commons.ui.theme.profile35dpModifier
|
||||
import com.vitorpamplona.amethyst.service.relayClient.reqCommand.user.UserFinderFilterAssemblerSubscription
|
||||
import com.vitorpamplona.amethyst.service.relayClient.reqCommand.user.observeUserContactCardsScore
|
||||
import com.vitorpamplona.amethyst.service.relayClient.reqCommand.user.observeUserAssertionsScore
|
||||
import com.vitorpamplona.amethyst.service.relayClient.reqCommand.user.observeUserPicture
|
||||
import com.vitorpamplona.amethyst.ui.components.CoreSecretMessage
|
||||
import com.vitorpamplona.amethyst.ui.components.ExpandableRichTextViewer
|
||||
@@ -841,7 +841,7 @@ fun ObserveAndRenderBoxedUserCards(
|
||||
accountViewModel: AccountViewModel,
|
||||
subscribe: Boolean = true,
|
||||
) {
|
||||
val score by observeUserContactCardsScore(user, accountViewModel, subscribe)
|
||||
val score by observeUserAssertionsScore(user, accountViewModel, subscribe)
|
||||
|
||||
score?.let {
|
||||
Box(modifier = Size35Modifier, contentAlignment = Alignment.BottomCenter) {
|
||||
|
||||
@@ -140,18 +140,18 @@ import com.vitorpamplona.amethyst.ui.note.types.DisplayBlockedRelayList
|
||||
import com.vitorpamplona.amethyst.ui.note.types.DisplayBroadcastRelayList
|
||||
import com.vitorpamplona.amethyst.ui.note.types.DisplayContactList
|
||||
import com.vitorpamplona.amethyst.ui.note.types.DisplayDMRelayList
|
||||
import com.vitorpamplona.amethyst.ui.note.types.DisplayFollowList
|
||||
import com.vitorpamplona.amethyst.ui.note.types.DisplayFavoriteRelayList
|
||||
import com.vitorpamplona.amethyst.ui.note.types.DisplayIndexerRelayList
|
||||
import com.vitorpamplona.amethyst.ui.note.types.DisplayNIP65RelayList
|
||||
import com.vitorpamplona.amethyst.ui.note.types.DisplayPeopleList
|
||||
import com.vitorpamplona.amethyst.ui.note.types.DisplayProxyRelayList
|
||||
import com.vitorpamplona.amethyst.ui.note.types.DisplayRelayFeedsList
|
||||
import com.vitorpamplona.amethyst.ui.note.types.DisplayRelaySet
|
||||
import com.vitorpamplona.amethyst.ui.note.types.DisplaySearchRelayList
|
||||
import com.vitorpamplona.amethyst.ui.note.types.DisplayStarterPack
|
||||
import com.vitorpamplona.amethyst.ui.note.types.DisplayTrustedRelayList
|
||||
import com.vitorpamplona.amethyst.ui.note.types.EditState
|
||||
import com.vitorpamplona.amethyst.ui.note.types.EmptyState
|
||||
import com.vitorpamplona.amethyst.ui.note.types.FileHeaderDisplay
|
||||
import com.vitorpamplona.amethyst.ui.note.types.FileMetadataDisplay
|
||||
import com.vitorpamplona.amethyst.ui.note.types.FileStorageHeaderDisplay
|
||||
import com.vitorpamplona.amethyst.ui.note.types.PictureDisplay
|
||||
import com.vitorpamplona.amethyst.ui.note.types.RenderAppDefinition
|
||||
@@ -180,6 +180,8 @@ import com.vitorpamplona.amethyst.ui.note.types.RenderCitation
|
||||
import com.vitorpamplona.amethyst.ui.note.types.RenderClassifieds
|
||||
import com.vitorpamplona.amethyst.ui.note.types.RenderCommunity
|
||||
import com.vitorpamplona.amethyst.ui.note.types.RenderCyberspaceBag
|
||||
import com.vitorpamplona.amethyst.ui.note.types.RenderDvmContentDiscoveryResponse
|
||||
import com.vitorpamplona.amethyst.ui.note.types.RenderDvmStatus
|
||||
import com.vitorpamplona.amethyst.ui.note.types.RenderEmojiPack
|
||||
import com.vitorpamplona.amethyst.ui.note.types.RenderEntityRating
|
||||
import com.vitorpamplona.amethyst.ui.note.types.RenderExternalReaction
|
||||
@@ -200,14 +202,11 @@ import com.vitorpamplona.amethyst.ui.note.types.RenderLiveActivityChatMessage
|
||||
import com.vitorpamplona.amethyst.ui.note.types.RenderLiveActivityEvent
|
||||
import com.vitorpamplona.amethyst.ui.note.types.RenderLiveChessChallenge
|
||||
import com.vitorpamplona.amethyst.ui.note.types.RenderLiveChessGameEnd
|
||||
import com.vitorpamplona.amethyst.ui.note.types.RenderLnZap
|
||||
import com.vitorpamplona.amethyst.ui.note.types.RenderLongFormContent
|
||||
import com.vitorpamplona.amethyst.ui.note.types.RenderMeetingRoomEvent
|
||||
import com.vitorpamplona.amethyst.ui.note.types.RenderMeetingSpaceEvent
|
||||
import com.vitorpamplona.amethyst.ui.note.types.RenderMusicPlaylist
|
||||
import com.vitorpamplona.amethyst.ui.note.types.RenderMusicTrack
|
||||
import com.vitorpamplona.amethyst.ui.note.types.RenderNIP90ContentDiscoveryResponse
|
||||
import com.vitorpamplona.amethyst.ui.note.types.RenderNIP90Status
|
||||
import com.vitorpamplona.amethyst.ui.note.types.RenderNamedNappletEvent
|
||||
import com.vitorpamplona.amethyst.ui.note.types.RenderNamedSiteEvent
|
||||
import com.vitorpamplona.amethyst.ui.note.types.RenderNipContent
|
||||
@@ -257,21 +256,22 @@ import com.vitorpamplona.amethyst.ui.note.types.RenderWikiMergeAcceptance
|
||||
import com.vitorpamplona.amethyst.ui.note.types.RenderWikiMergeRequest
|
||||
import com.vitorpamplona.amethyst.ui.note.types.RenderWikiRedirect
|
||||
import com.vitorpamplona.amethyst.ui.note.types.RenderZapPoll
|
||||
import com.vitorpamplona.amethyst.ui.note.types.RenderZapReceipt
|
||||
import com.vitorpamplona.amethyst.ui.note.types.ReplyRenderType
|
||||
import com.vitorpamplona.amethyst.ui.note.types.VideoDisplay
|
||||
import com.vitorpamplona.amethyst.ui.note.types.lists.RenderAppCurationSet
|
||||
import com.vitorpamplona.amethyst.ui.note.types.lists.RenderArticleCurationSet
|
||||
import com.vitorpamplona.amethyst.ui.note.types.lists.RenderBookmarkList
|
||||
import com.vitorpamplona.amethyst.ui.note.types.lists.RenderBookmarkSet
|
||||
import com.vitorpamplona.amethyst.ui.note.types.lists.RenderFavoriteAlgoFeedsList
|
||||
import com.vitorpamplona.amethyst.ui.note.types.lists.RenderGeohashList
|
||||
import com.vitorpamplona.amethyst.ui.note.types.lists.RenderGitAuthorList
|
||||
import com.vitorpamplona.amethyst.ui.note.types.lists.RenderGitRepositoryList
|
||||
import com.vitorpamplona.amethyst.ui.note.types.lists.RenderGoodWikiAuthorList
|
||||
import com.vitorpamplona.amethyst.ui.note.types.lists.RenderGoodWikiRelayList
|
||||
import com.vitorpamplona.amethyst.ui.note.types.lists.RenderHashtagList
|
||||
import com.vitorpamplona.amethyst.ui.note.types.lists.RenderInterestList
|
||||
import com.vitorpamplona.amethyst.ui.note.types.lists.RenderInterestSet
|
||||
import com.vitorpamplona.amethyst.ui.note.types.lists.RenderKindMuteSet
|
||||
import com.vitorpamplona.amethyst.ui.note.types.lists.RenderLabeledBookmarkList
|
||||
import com.vitorpamplona.amethyst.ui.note.types.lists.RenderMediaFollowList
|
||||
import com.vitorpamplona.amethyst.ui.note.types.lists.RenderMediaStarterPack
|
||||
import com.vitorpamplona.amethyst.ui.note.types.lists.RenderMuteList
|
||||
@@ -367,21 +367,20 @@ import com.vitorpamplona.quartz.nip51Lists.appCurationSet.AppCurationSetEvent
|
||||
import com.vitorpamplona.quartz.nip51Lists.articleCurationSet.ArticleCurationSetEvent
|
||||
import com.vitorpamplona.quartz.nip51Lists.bookmarkList.BookmarkListEvent
|
||||
import com.vitorpamplona.quartz.nip51Lists.bookmarkList.OldBookmarkListEvent
|
||||
import com.vitorpamplona.quartz.nip51Lists.bookmarkSet.BookmarkSetEvent
|
||||
import com.vitorpamplona.quartz.nip51Lists.favoriteAlgoFeedsList.FavoriteAlgoFeedsListEvent
|
||||
import com.vitorpamplona.quartz.nip51Lists.followList.StarterPackEvent
|
||||
import com.vitorpamplona.quartz.nip51Lists.followSet.FollowSetEvent
|
||||
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.InterestListEvent
|
||||
import com.vitorpamplona.quartz.nip51Lists.interestList.InterestListEvent
|
||||
import com.vitorpamplona.quartz.nip51Lists.interestSet.InterestSetEvent
|
||||
import com.vitorpamplona.quartz.nip51Lists.kindMuteSet.KindMuteSetEvent
|
||||
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.FollowSetEvent
|
||||
import com.vitorpamplona.quartz.nip51Lists.pictureCurationSet.PictureCurationSetEvent
|
||||
import com.vitorpamplona.quartz.nip51Lists.relayLists.BlockedRelayListEvent
|
||||
import com.vitorpamplona.quartz.nip51Lists.relayLists.BroadcastRelayListEvent
|
||||
@@ -392,6 +391,7 @@ import com.vitorpamplona.quartz.nip51Lists.relayLists.TrustedRelayListEvent
|
||||
import com.vitorpamplona.quartz.nip51Lists.relaySets.RelaySetEvent
|
||||
import com.vitorpamplona.quartz.nip51Lists.releaseArtifactSet.ReleaseArtifactSetEvent
|
||||
import com.vitorpamplona.quartz.nip51Lists.simpleGroupList.SimpleGroupListEvent
|
||||
import com.vitorpamplona.quartz.nip51Lists.starterPack.StarterPackEvent
|
||||
import com.vitorpamplona.quartz.nip51Lists.videoCurationSet.VideoCurationSetEvent
|
||||
import com.vitorpamplona.quartz.nip52Calendar.appt.day.CalendarDateSlotEvent
|
||||
import com.vitorpamplona.quartz.nip52Calendar.appt.time.CalendarTimeSlotEvent
|
||||
@@ -1192,7 +1192,7 @@ private fun RenderNoteRow(
|
||||
}
|
||||
|
||||
is ZapReceiptEvent -> {
|
||||
RenderLnZap(baseNote, quotesLeft, backgroundColor, accountViewModel, nav)
|
||||
RenderZapReceipt(baseNote, quotesLeft, backgroundColor, accountViewModel, nav)
|
||||
}
|
||||
|
||||
is NutzapEvent -> {
|
||||
@@ -1220,7 +1220,7 @@ private fun RenderNoteRow(
|
||||
}
|
||||
|
||||
is StarterPackEvent -> {
|
||||
DisplayFollowList(baseNote, true, accountViewModel, nav)
|
||||
DisplayStarterPack(baseNote, true, accountViewModel, nav)
|
||||
}
|
||||
|
||||
is ContactListEvent -> {
|
||||
@@ -1252,7 +1252,7 @@ private fun RenderNoteRow(
|
||||
}
|
||||
|
||||
is FavoriteRelayListEvent -> {
|
||||
DisplayRelayFeedsList(baseNote, backgroundColor, accountViewModel, nav)
|
||||
DisplayFavoriteRelayList(baseNote, backgroundColor, accountViewModel, nav)
|
||||
}
|
||||
|
||||
is IndexerRelayListEvent -> {
|
||||
@@ -1551,7 +1551,7 @@ private fun RenderNoteRow(
|
||||
}
|
||||
|
||||
is DvmContentDiscoveryResponseEvent -> {
|
||||
RenderNIP90ContentDiscoveryResponse(
|
||||
RenderDvmContentDiscoveryResponse(
|
||||
baseNote,
|
||||
makeItShort,
|
||||
canPreview,
|
||||
@@ -1563,7 +1563,7 @@ private fun RenderNoteRow(
|
||||
}
|
||||
|
||||
is DvmStatusEvent -> {
|
||||
RenderNIP90Status(
|
||||
RenderDvmStatus(
|
||||
baseNote,
|
||||
accountViewModel,
|
||||
nav,
|
||||
@@ -1641,7 +1641,7 @@ private fun RenderNoteRow(
|
||||
}
|
||||
|
||||
is FileMetadataEvent -> {
|
||||
FileHeaderDisplay(baseNote, true, ContentScale.FillWidth, accountViewModel)
|
||||
FileMetadataDisplay(baseNote, true, ContentScale.FillWidth, accountViewModel)
|
||||
}
|
||||
|
||||
// Covers every NIP-71 video kind (21, 22, 34235, 34236) via the shared interface,
|
||||
@@ -1880,7 +1880,7 @@ private fun RenderNoteRow(
|
||||
}
|
||||
|
||||
is BookmarkSetEvent -> {
|
||||
RenderLabeledBookmarkList(baseNote, quotesLeft, backgroundColor, accountViewModel, nav)
|
||||
RenderBookmarkSet(baseNote, quotesLeft, backgroundColor, accountViewModel, nav)
|
||||
}
|
||||
|
||||
is ArticleCurationSetEvent -> {
|
||||
@@ -1924,7 +1924,7 @@ private fun RenderNoteRow(
|
||||
}
|
||||
|
||||
is InterestListEvent -> {
|
||||
RenderHashtagList(baseNote, backgroundColor, accountViewModel, nav)
|
||||
RenderInterestList(baseNote, backgroundColor, accountViewModel, nav)
|
||||
}
|
||||
|
||||
is InterestSetEvent -> {
|
||||
|
||||
@@ -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.StarterPackEvent
|
||||
import com.vitorpamplona.quartz.nip51Lists.peopleList.FollowSetEvent
|
||||
import com.vitorpamplona.quartz.nip51Lists.followSet.FollowSetEvent
|
||||
import com.vitorpamplona.quartz.nip51Lists.starterPack.StarterPackEvent
|
||||
import kotlinx.coroutines.launch
|
||||
import org.jetbrains.compose.resources.StringResource
|
||||
|
||||
|
||||
+1
-1
@@ -358,7 +358,7 @@ private fun EmojiSelector(
|
||||
onClick: ((EmojiUrlTag) -> Unit)? = null,
|
||||
) {
|
||||
LoadAddressableNote(
|
||||
accountViewModel.account.emoji.getEmojiPackSelectionAddress(),
|
||||
accountViewModel.account.emoji.getEmojiListAddress(),
|
||||
) { emptyNote ->
|
||||
emptyNote?.let { usersEmojiList ->
|
||||
val collections by observeNoteEventAndMapNotNull(usersEmojiList, accountViewModel) { event: EmojiListEvent ->
|
||||
|
||||
@@ -52,7 +52,7 @@ import com.vitorpamplona.amethyst.commons.ui.note.ScoreTag
|
||||
import com.vitorpamplona.amethyst.commons.ui.screen.LocalDisplaySettings
|
||||
import com.vitorpamplona.amethyst.commons.ui.stringRes
|
||||
import com.vitorpamplona.amethyst.commons.ui.theme.ThemeComparisonColumn
|
||||
import com.vitorpamplona.amethyst.service.relayClient.reqCommand.user.observeUserContactCardsScore
|
||||
import com.vitorpamplona.amethyst.service.relayClient.reqCommand.user.observeUserAssertionsScore
|
||||
import com.vitorpamplona.amethyst.service.relayClient.reqCommand.user.observeUserInfo
|
||||
import com.vitorpamplona.amethyst.ui.navigation.routes.routeFor
|
||||
import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountViewModel
|
||||
@@ -565,7 +565,7 @@ fun ObserveAndRenderUserCards(
|
||||
modifier: Modifier = Modifier,
|
||||
accountViewModel: AccountViewModel,
|
||||
) {
|
||||
val score by observeUserContactCardsScore(user, accountViewModel)
|
||||
val score by observeUserAssertionsScore(user, accountViewModel)
|
||||
|
||||
score?.let {
|
||||
ScoreTag(it, size, modifier)
|
||||
|
||||
+3
-3
@@ -67,7 +67,7 @@ import com.vitorpamplona.amethyst.commons.model.composer.AudienceList
|
||||
import com.vitorpamplona.amethyst.commons.model.composer.AudienceListKind
|
||||
import com.vitorpamplona.amethyst.commons.model.composer.AudienceMember
|
||||
import com.vitorpamplona.amethyst.commons.model.composer.AudienceSelection
|
||||
import com.vitorpamplona.amethyst.commons.model.nip51Lists.peopleList.PeopleList
|
||||
import com.vitorpamplona.amethyst.commons.model.nip51Lists.followSets.PeopleList
|
||||
import com.vitorpamplona.amethyst.commons.resources.Res
|
||||
import com.vitorpamplona.amethyst.commons.resources.audience_add_anyway
|
||||
import com.vitorpamplona.amethyst.commons.resources.audience_add_people
|
||||
@@ -545,9 +545,9 @@ private fun MemberBadge(
|
||||
*/
|
||||
@Composable
|
||||
fun rememberAudienceLists(accountViewModel: AccountViewModel): List<AudienceList> {
|
||||
val peopleLists by accountViewModel.account.peopleLists.uiListFlow
|
||||
val peopleLists by accountViewModel.account.followSets.uiListFlow
|
||||
.collectAsStateWithLifecycle()
|
||||
val followPacks by accountViewModel.account.followLists.uiListFlow
|
||||
val followPacks by accountViewModel.account.starterPacks.uiListFlow
|
||||
.collectAsStateWithLifecycle()
|
||||
|
||||
return remember(peopleLists, followPacks) {
|
||||
|
||||
@@ -281,7 +281,7 @@ fun observeBookmarksFollowsAndAccount(
|
||||
accountViewModel.account.bookmarkState.bookmarks,
|
||||
accountViewModel.account.pinState.pinnedEventIdSet,
|
||||
accountViewModel.showSensitiveContent(),
|
||||
accountViewModel.account.emoji.getEmojiPackSelectionFlow(),
|
||||
accountViewModel.account.emoji.getEmojiListFlow(),
|
||||
) { follows, bookmarks, pinnedIds, showSensitiveContent, emojiSelectionState ->
|
||||
val isEmojiPackInMyList =
|
||||
if (noteIdForEmoji != null) {
|
||||
@@ -312,7 +312,7 @@ fun observeBookmarksFollowsAndAccount(
|
||||
isEmojiPackInMyList =
|
||||
noteIdForEmoji?.let {
|
||||
accountViewModel.account.emoji
|
||||
.getEmojiPackSelection()
|
||||
.getEmojiList()
|
||||
?.isTaggedAddressableNote(it) == true
|
||||
} ?: false,
|
||||
),
|
||||
|
||||
+5
-5
@@ -103,10 +103,10 @@ private object ActivityCardPreviewData {
|
||||
}
|
||||
}
|
||||
|
||||
fun lnZapNote(): Note {
|
||||
fun zapReceiptNote(): Note {
|
||||
val id = "d".repeat(64)
|
||||
// Receipts are signed by the lightning provider; the preview skips the
|
||||
// embedded request and feeds the decrypted card to RenderLnZapCard directly.
|
||||
// embedded request and feeds the decrypted card to RenderZapReceiptCard directly.
|
||||
return Note(id).apply {
|
||||
event = null
|
||||
author = null
|
||||
@@ -171,12 +171,12 @@ fun EmojiReactionActivityCardPreview() {
|
||||
|
||||
@Preview
|
||||
@Composable
|
||||
fun LnZapActivityCardPreview() {
|
||||
fun ZapReceiptActivityCardPreview() {
|
||||
val accountViewModel = mockAccountViewModel()
|
||||
|
||||
ThemeComparisonColumn {
|
||||
RenderLnZapCard(
|
||||
note = ActivityCardPreviewData.lnZapNote(),
|
||||
RenderZapReceiptCard(
|
||||
note = ActivityCardPreviewData.zapReceiptNote(),
|
||||
card =
|
||||
ZapAmountCommentNotification(
|
||||
user = LocalCache.getOrCreateUser(ActivityCardPreviewData.senderHex),
|
||||
|
||||
@@ -181,7 +181,7 @@ private fun EmojiListOptions(
|
||||
emojiPackNote: Note,
|
||||
) {
|
||||
LoadAddressableNote(
|
||||
accountViewModel.account.emoji.getEmojiPackSelectionAddress(),
|
||||
accountViewModel.account.emoji.getEmojiListAddress(),
|
||||
) {
|
||||
it?.let { usersEmojiList ->
|
||||
val hasAddedThis by observeNoteAndMap(usersEmojiList, accountViewModel) {
|
||||
|
||||
@@ -38,7 +38,7 @@ import com.vitorpamplona.quartz.nip31Alts.alt
|
||||
import com.vitorpamplona.quartz.nip94FileMetadata.FileMetadataEvent
|
||||
|
||||
@Composable
|
||||
fun FileHeaderDisplay(
|
||||
fun FileMetadataDisplay(
|
||||
note: Note,
|
||||
roundedCorner: Boolean,
|
||||
contentScale: ContentScale,
|
||||
@@ -53,7 +53,7 @@ fun FileHeaderDisplay(
|
||||
// which viewer happens to render it, so an NSFW-tagged archive stays behind the same gate.
|
||||
SensitivityWarning(note = note, accountViewModel = accountViewModel) {
|
||||
if (content == null) {
|
||||
FileHeaderAttachmentCard(event, fullUrl, mimeType)
|
||||
FileMetadataAttachmentCard(event, fullUrl, mimeType)
|
||||
} else {
|
||||
ZoomableContentView(
|
||||
content = content,
|
||||
@@ -70,7 +70,7 @@ fun FileHeaderDisplay(
|
||||
*
|
||||
* Kind 1063 is a *generic* file container — its `m` tag can name any type, so unlike a NIP-71
|
||||
* video event the kind itself asserts nothing about how to render the payload. A null here means
|
||||
* the file belongs in [FileHeaderAttachmentCard] rather than being pushed into the video player.
|
||||
* the file belongs in [FileMetadataAttachmentCard] rather than being pushed into the video player.
|
||||
*/
|
||||
internal fun FileMetadataEvent.toMediaContent(
|
||||
note: Note,
|
||||
@@ -128,7 +128,7 @@ internal fun FileMetadataEvent.toMediaContent(
|
||||
|
||||
/** The link card a kind-1063 header falls back to when [toMediaContent] returns null. */
|
||||
@Composable
|
||||
internal fun FileHeaderAttachmentCard(
|
||||
internal fun FileMetadataAttachmentCard(
|
||||
event: FileMetadataEvent,
|
||||
url: String,
|
||||
mimeType: String?,
|
||||
|
||||
+1
-1
@@ -38,7 +38,7 @@ import com.vitorpamplona.quartz.nip01Core.tags.hashtags.hasHashtags
|
||||
import com.vitorpamplona.quartz.nip90Dvms.contentDiscoveryResponse.DvmContentDiscoveryResponseEvent
|
||||
|
||||
@Composable
|
||||
fun RenderNIP90ContentDiscoveryResponse(
|
||||
fun RenderDvmContentDiscoveryResponse(
|
||||
note: Note,
|
||||
makeItShort: Boolean,
|
||||
canPreview: Boolean,
|
||||
|
||||
@@ -28,7 +28,7 @@ import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountViewModel
|
||||
import com.vitorpamplona.quartz.nip90Dvms.status.DvmStatusEvent
|
||||
|
||||
@Composable
|
||||
fun RenderNIP90Status(
|
||||
fun RenderDvmStatus(
|
||||
note: Note,
|
||||
accountViewModel: AccountViewModel,
|
||||
nav: INav,
|
||||
|
||||
@@ -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.FollowSetEvent
|
||||
import com.vitorpamplona.quartz.nip51Lists.followSet.FollowSetEvent
|
||||
import kotlinx.collections.immutable.ImmutableList
|
||||
import kotlinx.collections.immutable.persistentListOf
|
||||
|
||||
|
||||
@@ -76,7 +76,7 @@ import com.vitorpamplona.amethyst.commons.ui.theme.bitcoinColor
|
||||
import com.vitorpamplona.amethyst.commons.ui.theme.innerPostModifier
|
||||
import com.vitorpamplona.amethyst.commons.ui.theme.placeholderText
|
||||
import com.vitorpamplona.amethyst.service.relayClient.reqCommand.account.observeAccountIsHiddenUser
|
||||
import com.vitorpamplona.amethyst.service.relayClient.reqCommand.user.observeUserContactCardsFollowerCount
|
||||
import com.vitorpamplona.amethyst.service.relayClient.reqCommand.user.observeUserAssertionsFollowerCount
|
||||
import com.vitorpamplona.amethyst.service.relayClient.reqCommand.user.observeUserInfo
|
||||
import com.vitorpamplona.amethyst.service.relayClient.reqCommand.user.observeUserIsFollowing
|
||||
import com.vitorpamplona.amethyst.ui.navigation.routes.routeFor
|
||||
@@ -288,7 +288,7 @@ private fun ProfileCardChips(
|
||||
// A self-follow in your own kind:3 is common; "Follows you" on your own card is not a fact.
|
||||
val followsMe by observeUserIsFollowing(author, accountViewModel.account.userProfile(), accountViewModel)
|
||||
val followsYou = followsMe && !accountViewModel.isLoggedUser(author)
|
||||
val followerCount by observeUserContactCardsFollowerCount(author, accountViewModel)
|
||||
val followerCount by observeUserAssertionsFollowerCount(author, accountViewModel)
|
||||
// "--" is the placeholder the contact-card observer emits while no trusted
|
||||
// assertion for this user has arrived. Nothing to brag about yet.
|
||||
val followers = followerCount.takeIf { it != "--" }
|
||||
|
||||
@@ -241,14 +241,14 @@ fun DisplayTrustedRelayList(
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun DisplayRelayFeedsList(
|
||||
fun DisplayFavoriteRelayList(
|
||||
baseNote: Note,
|
||||
backgroundColor: MutableState<Color>,
|
||||
accountViewModel: AccountViewModel,
|
||||
nav: INav,
|
||||
) {
|
||||
val relays by accountViewModel.account.relayFeedsListDecryptionCache.observeDecryptedRelayList(baseNote).collectAsStateWithLifecycle(
|
||||
accountViewModel.account.relayFeedsListDecryptionCache.fastStartValueForRelayList(baseNote),
|
||||
val relays by accountViewModel.account.favoriteRelayListDecryptionCache.observeDecryptedRelayList(baseNote).collectAsStateWithLifecycle(
|
||||
accountViewModel.account.favoriteRelayListDecryptionCache.fastStartValueForRelayList(baseNote),
|
||||
)
|
||||
|
||||
DisplayRelaySet(
|
||||
|
||||
+2
-2
@@ -66,13 +66,13 @@ 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.StarterPackEvent
|
||||
import com.vitorpamplona.quartz.nip51Lists.starterPack.StarterPackEvent
|
||||
import kotlinx.collections.immutable.persistentListOf
|
||||
import kotlinx.collections.immutable.toImmutableList
|
||||
|
||||
@OptIn(ExperimentalLayoutApi::class)
|
||||
@Composable
|
||||
fun DisplayFollowList(
|
||||
fun DisplayStarterPack(
|
||||
baseNote: Note,
|
||||
makeItShort: Boolean,
|
||||
accountViewModel: AccountViewModel,
|
||||
@@ -60,11 +60,11 @@ fun RenderWikiContent(
|
||||
) {
|
||||
val noteEvent = note.event as? WikiArticleEvent ?: return
|
||||
|
||||
WikiNoteHeader(noteEvent, note, accountViewModel, nav)
|
||||
WikiArticleHeader(noteEvent, note, accountViewModel, nav)
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun WikiNoteHeader(
|
||||
private fun WikiArticleHeader(
|
||||
noteEvent: WikiArticleEvent,
|
||||
note: Note,
|
||||
accountViewModel: AccountViewModel,
|
||||
|
||||
@@ -97,7 +97,7 @@ fun RenderZappedPost(
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun RenderLnZap(
|
||||
fun RenderZapReceipt(
|
||||
note: Note,
|
||||
quotesLeft: Int,
|
||||
backgroundColor: MutableState<Color>,
|
||||
@@ -108,7 +108,7 @@ fun RenderLnZap(
|
||||
|
||||
val card by parseAuthorCommentAndAmount(note, accountViewModel)
|
||||
|
||||
RenderLnZapCard(
|
||||
RenderZapReceiptCard(
|
||||
note = note,
|
||||
card = card,
|
||||
recipientKey = zapEvent.zappedAuthor().firstOrNull(),
|
||||
@@ -120,7 +120,7 @@ fun RenderLnZap(
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun RenderLnZapCard(
|
||||
fun RenderZapReceiptCard(
|
||||
note: Note,
|
||||
card: ZapAmountCommentNotification,
|
||||
recipientKey: String?,
|
||||
|
||||
+2
-2
@@ -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.BookmarkSetEvent
|
||||
import com.vitorpamplona.quartz.nip51Lists.bookmarkSet.BookmarkSetEvent
|
||||
import com.vitorpamplona.quartz.nip51Lists.pictureCurationSet.PictureCurationSetEvent
|
||||
|
||||
/** NIP-51 kind 10003: the user's bookmarks — notes, articles, hashtags and links. */
|
||||
@@ -101,7 +101,7 @@ fun RenderOldBookmarkList(
|
||||
|
||||
/** NIP-51 kind 30003: "user-defined bookmarks categories, for when bookmarks must be in labeled separate groups". */
|
||||
@Composable
|
||||
fun RenderLabeledBookmarkList(
|
||||
fun RenderBookmarkSet(
|
||||
baseNote: Note,
|
||||
quotesLeft: Int,
|
||||
backgroundColor: MutableState<Color>,
|
||||
|
||||
@@ -36,12 +36,12 @@ 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.InterestListEvent
|
||||
import com.vitorpamplona.quartz.nip51Lists.interestList.InterestListEvent
|
||||
import com.vitorpamplona.quartz.nip51Lists.interestSet.InterestSetEvent
|
||||
|
||||
/** NIP-51 kind 10015: the hashtags this user follows. */
|
||||
@Composable
|
||||
fun RenderHashtagList(
|
||||
fun RenderInterestList(
|
||||
baseNote: Note,
|
||||
backgroundColor: MutableState<Color>,
|
||||
accountViewModel: AccountViewModel,
|
||||
|
||||
+4
-4
@@ -303,8 +303,8 @@ class AccountSessionManager(
|
||||
accountSettings.backupKeyPackageRelayList?.let { client.publish(it, toPost) }
|
||||
accountSettings.backupSearchRelayList?.let { client.publish(it, toPost) }
|
||||
accountSettings.backupIndexRelayList?.let { client.publish(it, toPost) }
|
||||
accountSettings.backupRelayFeedsList?.let { client.publish(it, toPost) }
|
||||
accountSettings.backupChannelList?.let { client.publish(it, toPost) }
|
||||
accountSettings.backupFavoriteRelayList?.let { client.publish(it, toPost) }
|
||||
accountSettings.backupPublicChatList?.let { client.publish(it, toPost) }
|
||||
accountSettings.backupCashuWallet?.let { client.publish(it, toPost) }
|
||||
accountSettings.backupNutzapInfo?.let { client.publish(it, toPost) }
|
||||
}
|
||||
@@ -329,8 +329,8 @@ class AccountSessionManager(
|
||||
backupKeyPackageRelayList = bootstrap.keyPackageRelayList,
|
||||
backupSearchRelayList = bootstrap.searchRelayList,
|
||||
backupIndexRelayList = bootstrap.indexerRelayList,
|
||||
backupChannelList = bootstrap.channelList,
|
||||
backupRelayFeedsList = bootstrap.relayFeedsList,
|
||||
backupPublicChatList = bootstrap.publicChatList,
|
||||
backupFavoriteRelayList = bootstrap.favoriteRelayList,
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
@@ -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.StarterPackEvent
|
||||
import com.vitorpamplona.quartz.nip51Lists.followSet.FollowSetEvent
|
||||
import com.vitorpamplona.quartz.nip51Lists.interestSet.InterestSetEvent
|
||||
import com.vitorpamplona.quartz.nip51Lists.peopleList.FollowSetEvent
|
||||
import com.vitorpamplona.quartz.nip51Lists.starterPack.StarterPackEvent
|
||||
import com.vitorpamplona.quartz.nip72ModCommunities.definition.CommunityDefinitionEvent
|
||||
import com.vitorpamplona.quartz.nip89AppHandlers.definition.AppDefinitionEvent
|
||||
import com.vitorpamplona.quartz.utils.Log
|
||||
@@ -162,14 +162,14 @@ class TopNavFilterState(
|
||||
|
||||
val livePeopleListsFlow: Flow<List<FeedDefinition>> =
|
||||
combine(
|
||||
account.peopleLists.peopleListNotes,
|
||||
account.followLists.followListNotes,
|
||||
account.followSets.peopleListNotes,
|
||||
account.starterPacks.starterPackNotes,
|
||||
::mergePeopleLists,
|
||||
).onStart {
|
||||
emit(
|
||||
mergePeopleLists(
|
||||
account.peopleLists.peopleListNotes.value,
|
||||
account.followLists.followListNotes.value,
|
||||
account.followSets.peopleListNotes.value,
|
||||
account.starterPacks.starterPackNotes.value,
|
||||
),
|
||||
)
|
||||
}
|
||||
@@ -247,10 +247,10 @@ class TopNavFilterState(
|
||||
@OptIn(ExperimentalCoroutinesApi::class)
|
||||
val liveInterestFlows: Flow<List<FeedDefinition>> =
|
||||
combine(
|
||||
account.hashtagList.flow,
|
||||
account.interestList.flow,
|
||||
account.geohashList.flow,
|
||||
account.communityList.flowNotes,
|
||||
account.relayFeedsList.flow,
|
||||
account.favoriteRelayList.flow,
|
||||
combine(
|
||||
account.favoriteAlgoFeedsList.flowNotes,
|
||||
account.interestSets.listFeedFlow,
|
||||
@@ -261,10 +261,10 @@ class TopNavFilterState(
|
||||
}.onStart {
|
||||
emit(
|
||||
mergeInterests(
|
||||
account.hashtagList.flow.value,
|
||||
account.interestList.flow.value,
|
||||
account.geohashList.flow.value,
|
||||
account.communityList.flowNotes.value,
|
||||
account.relayFeedsList.flow.value,
|
||||
account.favoriteRelayList.flow.value,
|
||||
account.favoriteAlgoFeedsList.flowNotes.value,
|
||||
account.interestSets.listFeedFlow.value,
|
||||
),
|
||||
|
||||
+3
-3
@@ -202,7 +202,7 @@ 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.InterestListEvent
|
||||
import com.vitorpamplona.quartz.nip51Lists.interestList.InterestListEvent
|
||||
import com.vitorpamplona.quartz.nip56Reports.ReportType
|
||||
import com.vitorpamplona.quartz.nip57Zaps.ZapReceiptEvent
|
||||
import com.vitorpamplona.quartz.nip57Zaps.ZapRequestEvent
|
||||
@@ -2144,11 +2144,11 @@ class AccountViewModel(
|
||||
|
||||
fun hide(user: User) = launchSigner { account.hideUser(user.pubkeyHex) }
|
||||
|
||||
fun updateContactCardPetName(
|
||||
fun updateUserAssertionPetName(
|
||||
user: User,
|
||||
petName: String?,
|
||||
summary: String?,
|
||||
) = launchSigner { account.updateContactCardPetName(user.pubkeyHex, petName, summary) }
|
||||
) = launchSigner { account.updateUserAssertionPetName(user.pubkeyHex, petName, summary) }
|
||||
|
||||
fun hide(word: String) = launchSigner { account.hideWord(word) }
|
||||
|
||||
|
||||
+11
-11
@@ -68,10 +68,10 @@ class EventProcessor(
|
||||
private val draftHandler = DraftEventHandler(account, cache)
|
||||
|
||||
private val giftWrapHandler = GiftWrapEventHandler(account, cache, this)
|
||||
private val sealHandler = SealedRumorEventHandler(account, cache, this)
|
||||
private val sealHandler = SealEventHandler(account, cache, this)
|
||||
|
||||
private val zapRequest = LnZapRequestEventHandler(account.privateZapsDecryptionCache)
|
||||
private val zapEvent = LnZapEventHandler(account.privateZapsDecryptionCache)
|
||||
private val zapRequest = ZapRequestEventHandler(account.privateZapsDecryptionCache)
|
||||
private val zapEvent = ZapReceiptEventHandler(account.privateZapsDecryptionCache)
|
||||
|
||||
private val groupEventHandler = GroupEventHandler(account, cache)
|
||||
|
||||
@@ -389,7 +389,7 @@ class GiftWrapEventHandler(
|
||||
/**
|
||||
* Shared Marmot Welcome handler used by both [GiftWrapEventHandler]
|
||||
* (in case a Welcome arrives directly inside a kind:1059 with no Seal
|
||||
* layer) and [SealedRumorEventHandler] (the actual production path —
|
||||
* layer) and [SealEventHandler] (the actual production path —
|
||||
* Welcomes are wrapped GiftWrap → Seal → Welcome per
|
||||
* [com.vitorpamplona.quartz.marmot.mip02Welcome.WelcomeGiftWrap]).
|
||||
*/
|
||||
@@ -453,7 +453,7 @@ private suspend fun processMarmotWelcomeFlow(
|
||||
}
|
||||
}
|
||||
|
||||
class SealedRumorEventHandler(
|
||||
class SealEventHandler(
|
||||
private val account: Account,
|
||||
private val cache: LocalCache,
|
||||
private val eventProcessor: EventProcessor,
|
||||
@@ -465,14 +465,14 @@ class SealedRumorEventHandler(
|
||||
) {
|
||||
val rumorId = event.innerEventId
|
||||
if (rumorId == null) {
|
||||
processNewSealedRumor(event, eventNote, publicNote)
|
||||
processNewSeal(event, eventNote, publicNote)
|
||||
} else {
|
||||
// Replayed seal: re-link the rumor to its delivering envelope so
|
||||
// broadcast can republish the wrap after a cache rebuild.
|
||||
// publicNote is the outermost event of this unwrap chain — the
|
||||
// kind-1059 wrap normally, the seal itself when it arrived bare.
|
||||
publicNote.event?.let { envelope -> cache.getOrCreateNote(rumorId).recordRumorHost(envelope) }
|
||||
processExistingSealedRumor(rumorId, publicNote)
|
||||
processExistingSeal(rumorId, publicNote)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -488,7 +488,7 @@ class SealedRumorEventHandler(
|
||||
}
|
||||
}
|
||||
|
||||
private suspend fun processNewSealedRumor(
|
||||
private suspend fun processNewSeal(
|
||||
event: SealEvent,
|
||||
eventNote: Note,
|
||||
publicNote: Note,
|
||||
@@ -524,7 +524,7 @@ class SealedRumorEventHandler(
|
||||
eventNote.flowSet?.metadata?.invalidateData()
|
||||
}
|
||||
|
||||
private suspend fun processExistingSealedRumor(
|
||||
private suspend fun processExistingSeal(
|
||||
rumorId: String,
|
||||
publicNote: Note,
|
||||
) {
|
||||
@@ -536,7 +536,7 @@ class SealedRumorEventHandler(
|
||||
}
|
||||
}
|
||||
|
||||
class LnZapRequestEventHandler(
|
||||
class ZapRequestEventHandler(
|
||||
val decryptionCache: PrivateZapCache,
|
||||
) : EventHandler<ZapRequestEvent> {
|
||||
override suspend fun add(
|
||||
@@ -559,7 +559,7 @@ class LnZapRequestEventHandler(
|
||||
}
|
||||
}
|
||||
|
||||
class LnZapEventHandler(
|
||||
class ZapReceiptEventHandler(
|
||||
val decryptionCache: PrivateZapCache,
|
||||
) : EventHandler<ZapReceiptEvent> {
|
||||
override suspend fun delete(
|
||||
|
||||
+7
-7
@@ -65,10 +65,10 @@ import com.vitorpamplona.quartz.nip01Core.diff.ContentChange
|
||||
import com.vitorpamplona.quartz.nip01Core.diff.EventDiff
|
||||
import com.vitorpamplona.quartz.nip01Core.metadata.MetadataDiff
|
||||
import com.vitorpamplona.quartz.nip02FollowList.ContactListDiff
|
||||
import com.vitorpamplona.quartz.nip28PublicChat.list.ChannelListDiff
|
||||
import com.vitorpamplona.quartz.nip28PublicChat.list.PublicChatListDiff
|
||||
import com.vitorpamplona.quartz.nip51Lists.favoriteAlgoFeedsList.FavoriteAlgoFeedsListDiff
|
||||
import com.vitorpamplona.quartz.nip51Lists.geohashList.GeohashListDiff
|
||||
import com.vitorpamplona.quartz.nip51Lists.hashtagList.HashtagListDiff
|
||||
import com.vitorpamplona.quartz.nip51Lists.interestList.InterestListDiff
|
||||
import com.vitorpamplona.quartz.nip51Lists.muteList.MuteListDiff
|
||||
import com.vitorpamplona.quartz.nip51Lists.relayLists.RelayListDiff
|
||||
import com.vitorpamplona.quartz.nip51Lists.simpleGroupList.SimpleGroupListDiff
|
||||
@@ -130,12 +130,12 @@ private fun headlineOf(
|
||||
is MetadataDiff -> plural(R.plurals.backup_card_profile_lost_fields, counts.removed)
|
||||
is AdvertisedRelayListDiff -> plural(R.plurals.backup_card_nip65_lost, diff.relays.removed.size)
|
||||
is RelayListDiff -> relayListHeadline(conflict.eventType, diff.relays.removed.size)
|
||||
is ChannelListDiff -> plural(R.plurals.backup_card_leave_chats, diff.channels.removed.size)
|
||||
is PublicChatListDiff -> plural(R.plurals.backup_card_leave_chats, diff.channels.removed.size)
|
||||
is CommunityListDiff -> plural(R.plurals.backup_card_leave_communities, diff.communities.removed.size)
|
||||
is EphemeralChatListDiff -> plural(R.plurals.backup_card_leave_rooms, diff.rooms.removed.size)
|
||||
is SimpleGroupListDiff -> plural(R.plurals.backup_card_leave_groups, diff.groups.removed.size)
|
||||
is FavoriteAlgoFeedsListDiff -> plural(R.plurals.backup_card_feeds_removed, diff.feeds.removed.size)
|
||||
is HashtagListDiff -> plural(R.plurals.backup_card_hashtags_unfollowed, diff.hashtags.removed.size)
|
||||
is InterestListDiff -> plural(R.plurals.backup_card_hashtags_unfollowed, diff.hashtags.removed.size)
|
||||
is GeohashListDiff -> plural(R.plurals.backup_card_places_unfollowed, diff.geohashes.removed.size)
|
||||
is TrustProviderListDiff -> plural(R.plurals.backup_card_trust_services_changed, diff.providers.removed.size + diff.providers.changed.size)
|
||||
is NutzapInfoDiff ->
|
||||
@@ -162,7 +162,7 @@ private fun headlineOf(
|
||||
when {
|
||||
diff is ContactListDiff -> R.plurals.backup_card_and_followed
|
||||
diff is MuteListDiff -> R.plurals.backup_card_and_muted
|
||||
diff is ChannelListDiff || diff is CommunityListDiff || diff is EphemeralChatListDiff || diff is SimpleGroupListDiff -> R.plurals.backup_card_and_joined
|
||||
diff is PublicChatListDiff || diff is CommunityListDiff || diff is EphemeralChatListDiff || diff is SimpleGroupListDiff -> R.plurals.backup_card_and_joined
|
||||
conflict.eventType == BackupEventType.BLOCKED_RELAYS -> R.plurals.backup_card_and_blocked
|
||||
else -> R.plurals.backup_card_and_added
|
||||
}
|
||||
@@ -182,12 +182,12 @@ private fun addedToHeadlinedList(
|
||||
is MuteListDiff -> diff.publicMutes.added.size
|
||||
is AdvertisedRelayListDiff -> diff.relays.added.size
|
||||
is RelayListDiff -> if (relayListHasHeadline(type)) diff.relays.added.size else 0
|
||||
is ChannelListDiff -> diff.channels.added.size
|
||||
is PublicChatListDiff -> diff.channels.added.size
|
||||
is CommunityListDiff -> diff.communities.added.size
|
||||
is EphemeralChatListDiff -> diff.rooms.added.size
|
||||
is SimpleGroupListDiff -> diff.groups.added.size
|
||||
is FavoriteAlgoFeedsListDiff -> diff.feeds.added.size
|
||||
is HashtagListDiff -> diff.hashtags.added.size
|
||||
is InterestListDiff -> diff.hashtags.added.size
|
||||
is GeohashListDiff -> diff.geohashes.added.size
|
||||
is NutzapInfoDiff -> if (diff.p2pkPubkey == null) diff.mints.added.size else 0
|
||||
is PaymentTargetsDiff -> diff.targets.added.size
|
||||
|
||||
+6
-6
@@ -93,7 +93,7 @@ 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.DmRelayListEvent
|
||||
import com.vitorpamplona.quartz.nip28PublicChat.list.ChannelListDiff
|
||||
import com.vitorpamplona.quartz.nip28PublicChat.list.PublicChatListDiff
|
||||
import com.vitorpamplona.quartz.nip28PublicChat.list.PublicChatListEvent
|
||||
import com.vitorpamplona.quartz.nip28PublicChat.list.channels
|
||||
import com.vitorpamplona.quartz.nip37Drafts.privateOutbox.PrivateOutboxRelayListEvent
|
||||
@@ -102,8 +102,8 @@ import com.vitorpamplona.quartz.nip51Lists.favoriteAlgoFeedsList.FavoriteAlgoFee
|
||||
import com.vitorpamplona.quartz.nip51Lists.favoriteAlgoFeedsList.FavoriteAlgoFeedsListEvent
|
||||
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.InterestListEvent
|
||||
import com.vitorpamplona.quartz.nip51Lists.interestList.InterestListDiff
|
||||
import com.vitorpamplona.quartz.nip51Lists.interestList.InterestListEvent
|
||||
import com.vitorpamplona.quartz.nip51Lists.relayLists.BlockedRelayListEvent
|
||||
import com.vitorpamplona.quartz.nip51Lists.relayLists.FavoriteRelayListEvent
|
||||
import com.vitorpamplona.quartz.nip51Lists.relayLists.IndexerRelayListEvent
|
||||
@@ -133,11 +133,11 @@ internal fun LazyListScope.listDiffItems(
|
||||
): Boolean {
|
||||
when (val diff = conflict.diff) {
|
||||
is RelayListDiff -> relayListItems(diff, conflict, nav)
|
||||
is ChannelListDiff -> publicChatItems(diff, conflict, accountViewModel, nav)
|
||||
is PublicChatListDiff -> publicChatItems(diff, conflict, accountViewModel, nav)
|
||||
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? InterestListEvent)?.publicHashtags().orEmpty(), diff.privateItems, isPlace = false, nav)
|
||||
is InterestListDiff -> 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)
|
||||
@@ -446,7 +446,7 @@ private fun SpaceTile(
|
||||
}
|
||||
|
||||
private fun LazyListScope.publicChatItems(
|
||||
diff: ChannelListDiff,
|
||||
diff: PublicChatListDiff,
|
||||
conflict: ReplaceableBackupConflict,
|
||||
accountViewModel: AccountViewModel,
|
||||
nav: INav,
|
||||
|
||||
+6
-6
@@ -41,10 +41,10 @@ import com.vitorpamplona.quartz.nip01Core.metadata.Birthday
|
||||
import com.vitorpamplona.quartz.nip01Core.metadata.MetadataDiff
|
||||
import com.vitorpamplona.quartz.nip01Core.relay.normalizer.NormalizedRelayUrl
|
||||
import com.vitorpamplona.quartz.nip02FollowList.ContactListDiff
|
||||
import com.vitorpamplona.quartz.nip28PublicChat.list.ChannelListDiff
|
||||
import com.vitorpamplona.quartz.nip28PublicChat.list.PublicChatListDiff
|
||||
import com.vitorpamplona.quartz.nip51Lists.favoriteAlgoFeedsList.FavoriteAlgoFeedsListDiff
|
||||
import com.vitorpamplona.quartz.nip51Lists.geohashList.GeohashListDiff
|
||||
import com.vitorpamplona.quartz.nip51Lists.hashtagList.HashtagListDiff
|
||||
import com.vitorpamplona.quartz.nip51Lists.interestList.InterestListDiff
|
||||
import com.vitorpamplona.quartz.nip51Lists.muteList.MuteListDiff
|
||||
import com.vitorpamplona.quartz.nip51Lists.muteList.tags.EventTag
|
||||
import com.vitorpamplona.quartz.nip51Lists.muteList.tags.HashtagTag
|
||||
@@ -104,9 +104,9 @@ fun countsOf(diff: EventDiff): ConflictCounts =
|
||||
is MuteListDiff -> diff.publicMutes.asCounts() + diff.privateItems.asCounts()
|
||||
is AdvertisedRelayListDiff -> diff.relays.asCounts()
|
||||
is RelayListDiff -> diff.relays.asCounts() + diff.privateRelays.asCounts()
|
||||
is ChannelListDiff -> diff.channels.asCounts() + diff.privateItems.asCounts()
|
||||
is PublicChatListDiff -> diff.channels.asCounts() + diff.privateItems.asCounts()
|
||||
is CommunityListDiff -> diff.communities.asCounts() + diff.privateItems.asCounts()
|
||||
is HashtagListDiff -> diff.hashtags.asCounts() + diff.privateItems.asCounts()
|
||||
is InterestListDiff -> diff.hashtags.asCounts() + diff.privateItems.asCounts()
|
||||
is GeohashListDiff -> diff.geohashes.asCounts() + diff.privateItems.asCounts()
|
||||
is FavoriteAlgoFeedsListDiff -> diff.feeds.asCounts() + diff.privateItems.asCounts()
|
||||
is EphemeralChatListDiff -> diff.rooms.asCounts() + diff.privateItems.asCounts()
|
||||
@@ -330,7 +330,7 @@ fun presentationOf(
|
||||
) to ContentChange.NONE
|
||||
}
|
||||
is RelayListDiff -> listOf(listGroup(R.string.backup_entry_relay, diff.relays, { ReviewItem.Relay(it) })) to diff.privateRelays
|
||||
is ChannelListDiff ->
|
||||
is PublicChatListDiff ->
|
||||
listOf(
|
||||
listGroup(
|
||||
R.string.backup_entry_public_chat,
|
||||
@@ -348,7 +348,7 @@ fun presentationOf(
|
||||
{ ReviewItem.Addressable(it.after.address, arrow(it.before.relayHint?.url, it.after.relayHint?.url)) },
|
||||
),
|
||||
) to diff.privateItems
|
||||
is HashtagListDiff -> listOf(listGroup(R.string.backup_entry_hashtag, diff.hashtags, { ReviewItem.Text("#$it") })) to diff.privateItems
|
||||
is InterestListDiff -> listOf(listGroup(R.string.backup_entry_hashtag, diff.hashtags, { ReviewItem.Text("#$it") })) to diff.privateItems
|
||||
is GeohashListDiff -> listOf(listGroup(R.string.backup_entry_location, diff.geohashes, { ReviewItem.Text(it) })) to diff.privateItems
|
||||
is FavoriteAlgoFeedsListDiff ->
|
||||
listOf(
|
||||
|
||||
+4
-4
@@ -86,10 +86,10 @@ import com.vitorpamplona.quartz.experimental.ephemChat.list.EphemeralChatListDif
|
||||
import com.vitorpamplona.quartz.nip01Core.diff.ContentChange
|
||||
import com.vitorpamplona.quartz.nip01Core.metadata.MetadataDiff
|
||||
import com.vitorpamplona.quartz.nip02FollowList.ContactListDiff
|
||||
import com.vitorpamplona.quartz.nip28PublicChat.list.ChannelListDiff
|
||||
import com.vitorpamplona.quartz.nip28PublicChat.list.PublicChatListDiff
|
||||
import com.vitorpamplona.quartz.nip51Lists.favoriteAlgoFeedsList.FavoriteAlgoFeedsListDiff
|
||||
import com.vitorpamplona.quartz.nip51Lists.geohashList.GeohashListDiff
|
||||
import com.vitorpamplona.quartz.nip51Lists.hashtagList.HashtagListDiff
|
||||
import com.vitorpamplona.quartz.nip51Lists.interestList.InterestListDiff
|
||||
import com.vitorpamplona.quartz.nip51Lists.muteList.MuteListDiff
|
||||
import com.vitorpamplona.quartz.nip51Lists.relayLists.RelayListDiff
|
||||
import com.vitorpamplona.quartz.nip51Lists.simpleGroupList.SimpleGroupListDiff
|
||||
@@ -373,11 +373,11 @@ private fun actionLabels(conflict: ReplaceableBackupConflict): Pair<String, Stri
|
||||
stringRes(R.string.backup_conflict_keep_new) to stringRes(R.string.backup_action_restore_relays)
|
||||
}
|
||||
is AdvertisedRelayListDiff -> stringRes(R.string.backup_conflict_keep_new) to stringRes(R.string.backup_action_restore_relays)
|
||||
is ChannelListDiff -> rejoinLabels(diff.channels.removed.size)
|
||||
is PublicChatListDiff -> rejoinLabels(diff.channels.removed.size)
|
||||
is CommunityListDiff -> rejoinLabels(diff.communities.removed.size)
|
||||
is EphemeralChatListDiff -> rejoinLabels(diff.rooms.removed.size)
|
||||
is FavoriteAlgoFeedsListDiff -> stringRes(R.string.backup_conflict_keep_new) to stringRes(R.string.backup_action_restore_feeds)
|
||||
is HashtagListDiff -> stringRes(R.string.backup_conflict_keep_new) to stringRes(R.string.backup_action_restore_topics)
|
||||
is InterestListDiff -> stringRes(R.string.backup_conflict_keep_new) to stringRes(R.string.backup_action_restore_topics)
|
||||
// Geohashes are places, not topics: the screen counts "places gone" under "Followed
|
||||
// locations", so the button has to agree with the page it sits on.
|
||||
is GeohashListDiff -> stringRes(R.string.backup_conflict_keep_new) to stringRes(R.string.backup_action_restore_places)
|
||||
|
||||
+1
-1
@@ -101,7 +101,7 @@ fun BookmarkGroupScreen(
|
||||
bookmarkType,
|
||||
broadcastBookmarkGroup = {
|
||||
accountViewModel.launchSigner {
|
||||
val groupNote = accountViewModel.account.labeledBookmarkLists.getLabeledBookmarkListNote(bookmarkIdentifier)
|
||||
val groupNote = accountViewModel.account.bookmarkSets.getBookmarkSetNote(bookmarkIdentifier)
|
||||
groupNote?.let {
|
||||
accountViewModel.broadcast(it)
|
||||
}
|
||||
|
||||
+7
-7
@@ -40,8 +40,8 @@ class BookmarkGroupViewModel(
|
||||
val bookmarkGroupIdentifier: String,
|
||||
) : ViewModel() {
|
||||
val selectedBookmarkGroupFlow =
|
||||
account.labeledBookmarkLists
|
||||
.getLabeledBookmarkListFlow(bookmarkGroupIdentifier)
|
||||
account.bookmarkSets
|
||||
.getBookmarkSetFlow(bookmarkGroupIdentifier)
|
||||
.stateIn(viewModelScope, SharingStarted.WhileSubscribed(2500), null)
|
||||
|
||||
fun publicPosts() =
|
||||
@@ -69,7 +69,7 @@ class BookmarkGroupViewModel(
|
||||
.stateIn(viewModelScope, SharingStarted.Eagerly, emptyList())
|
||||
|
||||
suspend fun deleteBookmarkGroup(groupIdentifier: String) {
|
||||
account.labeledBookmarkLists.deleteBookmarkList(groupIdentifier, account)
|
||||
account.bookmarkSets.deleteBookmarkList(groupIdentifier, account)
|
||||
}
|
||||
|
||||
suspend fun addBookmarkToGroup(
|
||||
@@ -77,7 +77,7 @@ class BookmarkGroupViewModel(
|
||||
bookmark: BookmarkIdTag,
|
||||
isPrivate: Boolean,
|
||||
) {
|
||||
account.labeledBookmarkLists.addBookmarkToList(
|
||||
account.bookmarkSets.addBookmarkToList(
|
||||
bookmark,
|
||||
groupIdentifier,
|
||||
isPrivate,
|
||||
@@ -108,7 +108,7 @@ class BookmarkGroupViewModel(
|
||||
bookmark: BookmarkIdTag,
|
||||
isCurrentlyPrivate: Boolean,
|
||||
) {
|
||||
account.labeledBookmarkLists.moveBookmarkInList(
|
||||
account.bookmarkSets.moveBookmarkInList(
|
||||
bookmark,
|
||||
groupIdentifier,
|
||||
isCurrentlyPrivate,
|
||||
@@ -147,7 +147,7 @@ class BookmarkGroupViewModel(
|
||||
bookmark: BookmarkIdTag,
|
||||
isPrivate: Boolean,
|
||||
) {
|
||||
account.labeledBookmarkLists.removeBookmarkFromList(
|
||||
account.bookmarkSets.removeBookmarkFromList(
|
||||
bookmark,
|
||||
groupIdentifier,
|
||||
isPrivate,
|
||||
@@ -160,7 +160,7 @@ class BookmarkGroupViewModel(
|
||||
deletedEventIds: Set<String>,
|
||||
deletedAddresses: Set<Address>,
|
||||
) {
|
||||
account.labeledBookmarkLists.removeDeletedBookmarksFromList(
|
||||
account.bookmarkSets.removeDeletedBookmarksFromList(
|
||||
groupIdentifier,
|
||||
deletedEventIds,
|
||||
deletedAddresses,
|
||||
|
||||
+9
-9
@@ -40,7 +40,7 @@ import com.vitorpamplona.amethyst.commons.model.nip51Lists.BookmarkType
|
||||
import com.vitorpamplona.amethyst.commons.model.nip51Lists.GitRepositoryListState
|
||||
import com.vitorpamplona.amethyst.commons.model.nip51Lists.OldBookmarkListState
|
||||
import com.vitorpamplona.amethyst.commons.model.nip51Lists.PinListState
|
||||
import com.vitorpamplona.amethyst.commons.model.nip51Lists.labeledBookmarkLists.LabeledBookmarkList
|
||||
import com.vitorpamplona.amethyst.commons.model.nip51Lists.bookmarkSets.BookmarkSet
|
||||
import com.vitorpamplona.amethyst.commons.nip51Lists.ui.ListOfBookmarkGroupsFeedView
|
||||
import com.vitorpamplona.amethyst.commons.resources.Res
|
||||
import com.vitorpamplona.amethyst.commons.resources.bookmark_lists
|
||||
@@ -64,7 +64,7 @@ fun ListOfBookmarkGroupsScreen(
|
||||
oldBookmarks = accountViewModel.account.oldBookmarkState,
|
||||
pinnedNotes = accountViewModel.account.pinState,
|
||||
repositories = accountViewModel.account.gitRepositoryListState,
|
||||
listSource = accountViewModel.account.labeledBookmarkLists.listFeedFlow,
|
||||
listSource = accountViewModel.account.bookmarkSets.listFeedFlow,
|
||||
openDefaultBookmarks = { nav.nav(Route.Bookmarks) },
|
||||
openOldBookmarks = { nav.nav(Route.OldBookmarks) },
|
||||
openPinnedNotes = { nav.nav(Route.PinnedNotes) },
|
||||
@@ -82,7 +82,7 @@ fun ListOfBookmarkGroupsScreen(
|
||||
},
|
||||
cloneBookmarkGroup = { bookmarkGroup, customName, customDesc ->
|
||||
accountViewModel.launchSigner {
|
||||
accountViewModel.account.labeledBookmarkLists.cloneBookmarkList(
|
||||
accountViewModel.account.bookmarkSets.cloneBookmarkList(
|
||||
currentBookmarkList = bookmarkGroup,
|
||||
customCloneName = customName,
|
||||
customCloneDescription = customDesc,
|
||||
@@ -92,7 +92,7 @@ fun ListOfBookmarkGroupsScreen(
|
||||
},
|
||||
deleteBookmarkGroup = { bookmarkGroup ->
|
||||
accountViewModel.launchSigner {
|
||||
accountViewModel.account.labeledBookmarkLists.deleteBookmarkList(
|
||||
accountViewModel.account.bookmarkSets.deleteBookmarkList(
|
||||
bookmarkListIdentifier = bookmarkGroup.identifier,
|
||||
account = accountViewModel.account,
|
||||
)
|
||||
@@ -109,7 +109,7 @@ fun ListOfBookmarkGroupsFeed(
|
||||
oldBookmarks: OldBookmarkListState,
|
||||
pinnedNotes: PinListState,
|
||||
repositories: GitRepositoryListState,
|
||||
listSource: StateFlow<List<LabeledBookmarkList>>,
|
||||
listSource: StateFlow<List<BookmarkSet>>,
|
||||
openDefaultBookmarks: () -> Unit,
|
||||
openOldBookmarks: () -> Unit,
|
||||
openPinnedNotes: () -> Unit,
|
||||
@@ -117,10 +117,10 @@ fun ListOfBookmarkGroupsFeed(
|
||||
openPodcasts: () -> Unit,
|
||||
addBookmarkGroup: () -> Unit,
|
||||
openBookmarkGroup: (identifier: String, bookmarkType: BookmarkType) -> Unit,
|
||||
renameBookmarkGroup: (bookmarkGroup: LabeledBookmarkList) -> Unit,
|
||||
changeBookmarkGroupDescription: (bookmarkGroup: LabeledBookmarkList) -> Unit,
|
||||
cloneBookmarkGroup: (bookmarkGroup: LabeledBookmarkList, customName: String?, customDesc: String?) -> Unit,
|
||||
deleteBookmarkGroup: (bookmarkGroup: LabeledBookmarkList) -> Unit,
|
||||
renameBookmarkGroup: (bookmarkGroup: BookmarkSet) -> Unit,
|
||||
changeBookmarkGroupDescription: (bookmarkGroup: BookmarkSet) -> Unit,
|
||||
cloneBookmarkGroup: (bookmarkGroup: BookmarkSet, customName: String?, customDesc: String?) -> Unit,
|
||||
deleteBookmarkGroup: (bookmarkGroup: BookmarkSet) -> Unit,
|
||||
accountViewModel: AccountViewModel,
|
||||
nav: INav,
|
||||
) {
|
||||
|
||||
+5
-5
@@ -31,7 +31,7 @@ import androidx.lifecycle.ViewModel
|
||||
import androidx.lifecycle.viewModelScope
|
||||
import com.vitorpamplona.amethyst.Amethyst
|
||||
import com.vitorpamplona.amethyst.commons.model.mediaServers.ServerType
|
||||
import com.vitorpamplona.amethyst.commons.model.nip51Lists.labeledBookmarkLists.LabeledBookmarkList
|
||||
import com.vitorpamplona.amethyst.commons.model.nip51Lists.bookmarkSets.BookmarkSet
|
||||
import com.vitorpamplona.amethyst.commons.resources.Res
|
||||
import com.vitorpamplona.amethyst.commons.resources.avif_metadata_strip_failed
|
||||
import com.vitorpamplona.amethyst.commons.resources.failed_to_upload_media_no_details
|
||||
@@ -59,7 +59,7 @@ class BookmarkGroupMetadataViewModel : ViewModel() {
|
||||
private lateinit var accountViewModel: AccountViewModel
|
||||
private lateinit var account: Account
|
||||
|
||||
var bookmarkGroup by mutableStateOf<LabeledBookmarkList?>(null)
|
||||
var bookmarkGroup by mutableStateOf<BookmarkSet?>(null)
|
||||
val isNewList by derivedStateOf { bookmarkGroup == null }
|
||||
|
||||
val name = mutableStateOf(TextFieldValue())
|
||||
@@ -83,7 +83,7 @@ class BookmarkGroupMetadataViewModel : ViewModel() {
|
||||
}
|
||||
|
||||
fun load(dTag: String) {
|
||||
bookmarkGroup = account.labeledBookmarkLists.getBookmarkList(dTag)
|
||||
bookmarkGroup = account.bookmarkSets.getBookmarkList(dTag)
|
||||
name.value = TextFieldValue(bookmarkGroup?.title ?: "")
|
||||
picture.value = TextFieldValue(bookmarkGroup?.image ?: "")
|
||||
description.value = TextFieldValue(bookmarkGroup?.description ?: "")
|
||||
@@ -95,14 +95,14 @@ class BookmarkGroupMetadataViewModel : ViewModel() {
|
||||
accountViewModel.launchSigner {
|
||||
val bookmarkGroup = bookmarkGroup
|
||||
if (bookmarkGroup == null) {
|
||||
accountViewModel.account.labeledBookmarkLists.addLabeledBookmarkList(
|
||||
accountViewModel.account.bookmarkSets.addBookmarkSet(
|
||||
listName = name.value.text,
|
||||
listDescription = description.value.text,
|
||||
listImage = picture.value.text,
|
||||
account = accountViewModel.account,
|
||||
)
|
||||
} else {
|
||||
accountViewModel.account.labeledBookmarkLists.updateMetadata(
|
||||
accountViewModel.account.bookmarkSets.updateMetadata(
|
||||
listName = name.value.text,
|
||||
listDescription = description.value.text,
|
||||
listImage = picture.value.text,
|
||||
|
||||
+5
-5
@@ -37,7 +37,7 @@ import androidx.lifecycle.compose.collectAsStateWithLifecycle
|
||||
import com.vitorpamplona.amethyst.commons.model.AddressableNote
|
||||
import com.vitorpamplona.amethyst.commons.model.navigation.Route
|
||||
import com.vitorpamplona.amethyst.commons.model.nip51Lists.BookmarkType
|
||||
import com.vitorpamplona.amethyst.commons.model.nip51Lists.labeledBookmarkLists.LabeledBookmarkList
|
||||
import com.vitorpamplona.amethyst.commons.model.nip51Lists.bookmarkSets.BookmarkSet
|
||||
import com.vitorpamplona.amethyst.commons.nip51Lists.ui.BookmarkGroupManagementItem
|
||||
import com.vitorpamplona.amethyst.commons.resources.Res
|
||||
import com.vitorpamplona.amethyst.commons.resources.article_bookmark_management_title
|
||||
@@ -109,7 +109,7 @@ private fun ListManagementViewBody(
|
||||
accountViewModel: AccountViewModel,
|
||||
nav: INav,
|
||||
) {
|
||||
val bookmarkGroups by accountViewModel.account.labeledBookmarkLists.listFeedFlow
|
||||
val bookmarkGroups by accountViewModel.account.bookmarkSets.listFeedFlow
|
||||
.collectAsStateWithLifecycle()
|
||||
|
||||
val defaultBookmarks by accountViewModel.account.bookmarkState.bookmarks
|
||||
@@ -146,7 +146,7 @@ private fun ListManagementViewBody(
|
||||
)
|
||||
}
|
||||
|
||||
itemsIndexed(items = bookmarkGroups, key = { _: Int, item: LabeledBookmarkList -> item.identifier }) { _, bookmarkList ->
|
||||
itemsIndexed(items = bookmarkGroups, key = { _: Int, item: BookmarkSet -> item.identifier }) { _, bookmarkList ->
|
||||
val maybePublicBookmark = bookmarkList.publicArticleBookmarks.firstOrNull { it.address == note.address }
|
||||
val maybePrivateBookmark = bookmarkList.privateArticleBookmarks.firstOrNull { it.address == note.address }
|
||||
BookmarkGroupManagementItem(
|
||||
@@ -159,7 +159,7 @@ private fun ListManagementViewBody(
|
||||
onClick = { nav.nav(Route.BookmarkGroupView(bookmarkList.identifier, BookmarkType.ArticleBookmark)) },
|
||||
onAddBookmarkToGroup = { shouldBePrivate ->
|
||||
accountViewModel.launchSigner {
|
||||
accountViewModel.account.labeledBookmarkLists.addBookmarkToList(
|
||||
accountViewModel.account.bookmarkSets.addBookmarkToList(
|
||||
bookmark = AddressBookmark(address = note.address, relayHint = note.relayHintUrl()),
|
||||
bookmarkListIdentifier = bookmarkList.identifier,
|
||||
isBookmarkPrivate = shouldBePrivate,
|
||||
@@ -169,7 +169,7 @@ private fun ListManagementViewBody(
|
||||
},
|
||||
onRemoveBookmarkFromGroup = {
|
||||
accountViewModel.launchSigner {
|
||||
accountViewModel.account.labeledBookmarkLists.removeBookmarkFromList(
|
||||
accountViewModel.account.bookmarkSets.removeBookmarkFromList(
|
||||
bookmark = AddressBookmark(address = note.address),
|
||||
bookmarkListIdentifier = bookmarkList.identifier,
|
||||
isBookmarkPrivate = maybePrivateBookmark != null,
|
||||
|
||||
+5
-5
@@ -37,7 +37,7 @@ import androidx.lifecycle.compose.collectAsStateWithLifecycle
|
||||
import com.vitorpamplona.amethyst.commons.model.Note
|
||||
import com.vitorpamplona.amethyst.commons.model.navigation.Route
|
||||
import com.vitorpamplona.amethyst.commons.model.nip51Lists.BookmarkType
|
||||
import com.vitorpamplona.amethyst.commons.model.nip51Lists.labeledBookmarkLists.LabeledBookmarkList
|
||||
import com.vitorpamplona.amethyst.commons.model.nip51Lists.bookmarkSets.BookmarkSet
|
||||
import com.vitorpamplona.amethyst.commons.nip51Lists.ui.BookmarkGroupManagementItem
|
||||
import com.vitorpamplona.amethyst.commons.resources.Res
|
||||
import com.vitorpamplona.amethyst.commons.resources.bookmarks_title
|
||||
@@ -108,7 +108,7 @@ private fun ListManagementViewBody(
|
||||
accountViewModel: AccountViewModel,
|
||||
nav: INav,
|
||||
) {
|
||||
val bookmarkGroups by accountViewModel.account.labeledBookmarkLists.listFeedFlow
|
||||
val bookmarkGroups by accountViewModel.account.bookmarkSets.listFeedFlow
|
||||
.collectAsStateWithLifecycle()
|
||||
|
||||
val defaultBookmarks by accountViewModel.account.bookmarkState.bookmarks
|
||||
@@ -145,7 +145,7 @@ private fun ListManagementViewBody(
|
||||
)
|
||||
}
|
||||
|
||||
itemsIndexed(items = bookmarkGroups, key = { _: Int, item: LabeledBookmarkList -> item.identifier }) { _, bookmarkList ->
|
||||
itemsIndexed(items = bookmarkGroups, key = { _: Int, item: BookmarkSet -> item.identifier }) { _, bookmarkList ->
|
||||
val maybePublicBookmark = bookmarkList.publicPostBookmarks.firstOrNull { it.eventId == note.idHex }
|
||||
val maybePrivateBookmark = bookmarkList.privatePostBookmarks.firstOrNull { it.eventId == note.idHex }
|
||||
BookmarkGroupManagementItem(
|
||||
@@ -158,7 +158,7 @@ private fun ListManagementViewBody(
|
||||
onClick = { nav.nav(Route.BookmarkGroupView(bookmarkList.identifier, BookmarkType.PostBookmark)) },
|
||||
onAddBookmarkToGroup = { shouldBePrivate ->
|
||||
accountViewModel.launchSigner {
|
||||
accountViewModel.account.labeledBookmarkLists.addBookmarkToList(
|
||||
accountViewModel.account.bookmarkSets.addBookmarkToList(
|
||||
bookmark = EventBookmark(eventId = note.idHex, relay = note.relayHintUrl(), author = note.author?.pubkeyHex),
|
||||
bookmarkListIdentifier = bookmarkList.identifier,
|
||||
isBookmarkPrivate = shouldBePrivate,
|
||||
@@ -168,7 +168,7 @@ private fun ListManagementViewBody(
|
||||
},
|
||||
onRemoveBookmarkFromGroup = {
|
||||
accountViewModel.launchSigner {
|
||||
accountViewModel.account.labeledBookmarkLists.removeBookmarkFromList(
|
||||
accountViewModel.account.bookmarkSets.removeBookmarkFromList(
|
||||
bookmark = EventBookmark(eventId = note.idHex),
|
||||
bookmarkListIdentifier = bookmarkList.identifier,
|
||||
isBookmarkPrivate = maybePrivateBookmark != null,
|
||||
|
||||
+2
-2
@@ -250,7 +250,7 @@ private fun WatchAccountForRelayGroupDiscovery(
|
||||
.collectAsStateWithLifecycle()
|
||||
val joinedServers by accountViewModel.account.relayGroupList.liveRelayGroupServers
|
||||
.collectAsStateWithLifecycle()
|
||||
val favoriteRelays by accountViewModel.account.relayFeedsList.flow
|
||||
val favoriteRelays by accountViewModel.account.favoriteRelayList.flow
|
||||
.collectAsStateWithLifecycle()
|
||||
|
||||
// Discovery only shows groups whose 39000 is signed by the host relay's own key (NIP-29's
|
||||
@@ -349,7 +349,7 @@ private fun RelayRailHeader(
|
||||
val info = loadRelayInfo(relay)
|
||||
val host = relay.displayUrl()
|
||||
val name = info.value.name?.takeIf { it.isNotBlank() } ?: host
|
||||
val favoriteRelays by accountViewModel.account.relayFeedsList.flow
|
||||
val favoriteRelays by accountViewModel.account.favoriteRelayList.flow
|
||||
.collectAsStateWithLifecycle()
|
||||
val isFavorite = relay in favoriteRelays
|
||||
|
||||
|
||||
+1
-1
@@ -408,7 +408,7 @@ private fun RelayGroupMemberRow(
|
||||
)
|
||||
}
|
||||
DropdownMenu(expanded = menuOpen, onDismissRequest = { menuOpen = false }) {
|
||||
val declaredRoles = channel.supportedRoles
|
||||
val declaredRoles = channel.groupRoles
|
||||
if (declaredRoles.isNotEmpty()) {
|
||||
// The relay declares its own role set (kind 39003) — offer exactly those
|
||||
// instead of the built-in admin/moderator pair. Roles are privilege grants,
|
||||
|
||||
+2
-2
@@ -100,7 +100,7 @@ class RelayGroupsDiscoverySubAssembler(
|
||||
account.relayGroupList.liveRelayGroupServers.value.forEach { server ->
|
||||
RelayUrlNormalizer.normalizeOrNull(server)?.let(::add)
|
||||
}
|
||||
addAll(account.relayFeedsList.flow.value)
|
||||
addAll(account.favoriteRelayList.flow.value)
|
||||
}
|
||||
|
||||
override fun user(key: RelayGroupsDiscoveryQueryState) = key.account.userProfile()
|
||||
@@ -156,7 +156,7 @@ class RelayGroupsDiscoverySubAssembler(
|
||||
}
|
||||
},
|
||||
key.account.scope.launch(Dispatchers.IO) {
|
||||
key.account.relayFeedsList.flow.sample(500).collectLatest {
|
||||
key.account.favoriteRelayList.flow.sample(500).collectLatest {
|
||||
invalidateFilters()
|
||||
}
|
||||
},
|
||||
|
||||
+1
-1
@@ -45,7 +45,7 @@ import com.vitorpamplona.amethyst.ui.screen.loggedIn.discover.nip90DVMs.RenderCo
|
||||
import com.vitorpamplona.amethyst.ui.screen.loggedIn.discover.nip99Classifieds.RenderClassifiedsThumb
|
||||
import com.vitorpamplona.quartz.nip23LongContent.LongFormContentEvent
|
||||
import com.vitorpamplona.quartz.nip28PublicChat.admin.ChannelCreateEvent
|
||||
import com.vitorpamplona.quartz.nip51Lists.followList.StarterPackEvent
|
||||
import com.vitorpamplona.quartz.nip51Lists.starterPack.StarterPackEvent
|
||||
import com.vitorpamplona.quartz.nip53LiveActivities.meetingSpaces.MeetingRoomEvent
|
||||
import com.vitorpamplona.quartz.nip53LiveActivities.meetingSpaces.MeetingSpaceEvent
|
||||
import com.vitorpamplona.quartz.nip53LiveActivities.streaming.LiveActivitiesEvent
|
||||
|
||||
+1
-1
@@ -88,7 +88,7 @@ import com.vitorpamplona.amethyst.ui.screen.loggedIn.discover.datasource.Discove
|
||||
import com.vitorpamplona.amethyst.ui.screen.loggedIn.home.TabItem
|
||||
import com.vitorpamplona.quartz.nip23LongContent.LongFormContentEvent
|
||||
import com.vitorpamplona.quartz.nip28PublicChat.admin.ChannelCreateEvent
|
||||
import com.vitorpamplona.quartz.nip51Lists.followList.StarterPackEvent
|
||||
import com.vitorpamplona.quartz.nip51Lists.starterPack.StarterPackEvent
|
||||
import com.vitorpamplona.quartz.nip53LiveActivities.streaming.LiveActivitiesEvent
|
||||
import com.vitorpamplona.quartz.nip72ModCommunities.definition.CommunityDefinitionEvent
|
||||
import com.vitorpamplona.quartz.nip89AppHandlers.definition.AppDefinitionEvent
|
||||
|
||||
+1
-1
@@ -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.nip51Lists.followList.StarterPackEvent
|
||||
import com.vitorpamplona.quartz.nip51Lists.starterPack.StarterPackEvent
|
||||
|
||||
open class DiscoverFollowSetsFeedFilter(
|
||||
val account: Account,
|
||||
|
||||
+1
-1
@@ -63,7 +63,7 @@ import com.vitorpamplona.amethyst.ui.note.elements.DefaultImageBannerBackground
|
||||
import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountViewModel
|
||||
import com.vitorpamplona.amethyst.ui.screen.loggedIn.mockAccountViewModel
|
||||
import com.vitorpamplona.quartz.nip01Core.core.HexKey
|
||||
import com.vitorpamplona.quartz.nip51Lists.followList.StarterPackEvent
|
||||
import com.vitorpamplona.quartz.nip51Lists.starterPack.StarterPackEvent
|
||||
import kotlinx.collections.immutable.ImmutableList
|
||||
import kotlinx.collections.immutable.toImmutableList
|
||||
|
||||
|
||||
+6
-6
@@ -87,7 +87,7 @@ import com.vitorpamplona.amethyst.ui.screen.RenderFeedState
|
||||
import com.vitorpamplona.amethyst.ui.screen.SaveableFeedState
|
||||
import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountViewModel
|
||||
import com.vitorpamplona.amethyst.ui.screen.loggedIn.discover.nip90DVMs.DVMCard
|
||||
import com.vitorpamplona.amethyst.ui.screen.loggedIn.dvms.dal.NIP90ContentDiscoveryFeedViewModel
|
||||
import com.vitorpamplona.amethyst.ui.screen.loggedIn.dvms.dal.DvmContentDiscoveryFeedViewModel
|
||||
import com.vitorpamplona.quartz.lightning.LnInvoiceUtil
|
||||
import com.vitorpamplona.quartz.nip01Core.relay.filters.Filter
|
||||
import com.vitorpamplona.quartz.nip89AppHandlers.definition.AppDefinitionEvent
|
||||
@@ -263,22 +263,22 @@ fun PrepareViewContentDiscoveryModels(
|
||||
accountViewModel: AccountViewModel,
|
||||
nav: INav,
|
||||
) {
|
||||
val resultFeedViewModel: NIP90ContentDiscoveryFeedViewModel =
|
||||
val resultFeedViewModel: DvmContentDiscoveryFeedViewModel =
|
||||
viewModel(
|
||||
key = "NostrNIP90ContentDiscoveryFeedViewModel${dvm.pubkeyHex}$dvmRequestId",
|
||||
factory = NIP90ContentDiscoveryFeedViewModel.Factory(accountViewModel.account, dvmKey = dvm.pubkeyHex, requestId = dvmRequestId),
|
||||
factory = DvmContentDiscoveryFeedViewModel.Factory(accountViewModel.account, dvmKey = dvm.pubkeyHex, requestId = dvmRequestId),
|
||||
)
|
||||
|
||||
LaunchedEffect(key1 = dvmRequestId, latestResponse.id) {
|
||||
resultFeedViewModel.invalidateData()
|
||||
}
|
||||
|
||||
RenderNostrNIP90ContentDiscoveryScreen(resultFeedViewModel, onRefresh, accountViewModel, nav)
|
||||
RenderNostrDvmContentDiscoveryScreen(resultFeedViewModel, onRefresh, accountViewModel, nav)
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun RenderNostrNIP90ContentDiscoveryScreen(
|
||||
resultFeedViewModel: NIP90ContentDiscoveryFeedViewModel,
|
||||
fun RenderNostrDvmContentDiscoveryScreen(
|
||||
resultFeedViewModel: DvmContentDiscoveryFeedViewModel,
|
||||
onRefresh: () -> Unit,
|
||||
accountViewModel: AccountViewModel,
|
||||
nav: INav,
|
||||
|
||||
+3
-3
@@ -27,17 +27,17 @@ import com.vitorpamplona.amethyst.model.Account
|
||||
import com.vitorpamplona.amethyst.ui.screen.AndroidFeedViewModel
|
||||
|
||||
@Stable
|
||||
class NIP90ContentDiscoveryFeedViewModel(
|
||||
class DvmContentDiscoveryFeedViewModel(
|
||||
val account: Account,
|
||||
dvmKey: String,
|
||||
requestId: String,
|
||||
) : AndroidFeedViewModel(NIP90ContentDiscoveryResponseFilter(account, dvmKey, requestId)) {
|
||||
) : AndroidFeedViewModel(DvmContentDiscoveryResponseFilter(account, dvmKey, requestId)) {
|
||||
class Factory(
|
||||
val account: Account,
|
||||
val dvmKey: String,
|
||||
val requestId: String,
|
||||
) : ViewModelProvider.Factory {
|
||||
@Suppress("UNCHECKED_CAST")
|
||||
override fun <T : ViewModel> create(modelClass: Class<T>): T = NIP90ContentDiscoveryFeedViewModel(account, dvmKey, requestId) as T
|
||||
override fun <T : ViewModel> create(modelClass: Class<T>): T = DvmContentDiscoveryFeedViewModel(account, dvmKey, requestId) as T
|
||||
}
|
||||
}
|
||||
+1
-1
@@ -29,7 +29,7 @@ import com.vitorpamplona.amethyst.ui.dal.FilterByListParams
|
||||
import com.vitorpamplona.quartz.nip01Core.tags.events.isTaggedEvent
|
||||
import com.vitorpamplona.quartz.nip90Dvms.contentDiscoveryResponse.DvmContentDiscoveryResponseEvent
|
||||
|
||||
open class NIP90ContentDiscoveryResponseFilter(
|
||||
open class DvmContentDiscoveryResponseFilter(
|
||||
val account: Account,
|
||||
val dvmkey: String,
|
||||
val request: String,
|
||||
+1
-1
@@ -315,7 +315,7 @@ private fun EmojiListToggleRow(
|
||||
LoadAddressableNote(packAddress) {
|
||||
it?.let { packNote ->
|
||||
LoadAddressableNote(
|
||||
accountViewModel.account.emoji.getEmojiPackSelectionAddress(),
|
||||
accountViewModel.account.emoji.getEmojiListAddress(),
|
||||
) { selectionNote ->
|
||||
selectionNote?.let { usersEmojiList ->
|
||||
val hasAddedThis by observeNoteAndMap(usersEmojiList, accountViewModel) {
|
||||
|
||||
+1
-1
@@ -121,7 +121,7 @@ private fun EmojiPackSelectionBody(
|
||||
) {
|
||||
item {
|
||||
LoadAddressableNote(
|
||||
address = accountViewModel.account.emoji.getEmojiPackSelectionAddress(),
|
||||
address = accountViewModel.account.emoji.getEmojiListAddress(),
|
||||
) { selectionNote ->
|
||||
selectionNote?.let {
|
||||
val hasAddedThis by observeNoteAndMap(it, accountViewModel) { currentNote ->
|
||||
|
||||
+1
-1
@@ -79,7 +79,7 @@ fun MyEmojiListScreen(
|
||||
nav: INav,
|
||||
) {
|
||||
LoadAddressableNote(
|
||||
address = accountViewModel.account.emoji.getEmojiPackSelectionAddress(),
|
||||
address = accountViewModel.account.emoji.getEmojiListAddress(),
|
||||
) { selectionNote ->
|
||||
selectionNote?.let {
|
||||
MyEmojiListView(
|
||||
|
||||
+1
-1
@@ -85,7 +85,7 @@ import com.vitorpamplona.amethyst.ui.screen.loggedIn.followPacks.feed.dal.Follow
|
||||
import com.vitorpamplona.amethyst.ui.screen.loggedIn.followPacks.feed.dal.FollowPackMembersUserFeedViewModel
|
||||
import com.vitorpamplona.amethyst.ui.screen.loggedIn.followPacks.feed.datasource.FollowPackFeedFilterAssemblerSubscription
|
||||
import com.vitorpamplona.quartz.nip01Core.core.Address
|
||||
import com.vitorpamplona.quartz.nip51Lists.followList.StarterPackEvent
|
||||
import com.vitorpamplona.quartz.nip51Lists.starterPack.StarterPackEvent
|
||||
import kotlinx.coroutines.launch
|
||||
|
||||
@OptIn(ExperimentalMaterial3Api::class)
|
||||
|
||||
+1
-1
@@ -37,7 +37,7 @@ import com.vitorpamplona.quartz.nip01Core.relay.normalizer.NormalizedRelayUrl
|
||||
import com.vitorpamplona.quartz.nip10Notes.TextNoteEvent
|
||||
import com.vitorpamplona.quartz.nip22Comments.CommentEvent
|
||||
import com.vitorpamplona.quartz.nip28PublicChat.message.ChannelMessageEvent
|
||||
import com.vitorpamplona.quartz.nip51Lists.followList.StarterPackEvent
|
||||
import com.vitorpamplona.quartz.nip51Lists.starterPack.StarterPackEvent
|
||||
import com.vitorpamplona.quartz.nip53LiveActivities.chat.LiveActivitiesChatMessageEvent
|
||||
import com.vitorpamplona.quartz.nipA0VoiceMessages.VoiceReplyEvent
|
||||
import com.vitorpamplona.quartz.nipA4PublicMessages.PublicMessageEvent
|
||||
|
||||
+1
-1
@@ -46,7 +46,7 @@ import com.vitorpamplona.quartz.nip18Reposts.GenericRepostEvent
|
||||
import com.vitorpamplona.quartz.nip18Reposts.RepostEvent
|
||||
import com.vitorpamplona.quartz.nip22Comments.CommentEvent
|
||||
import com.vitorpamplona.quartz.nip23LongContent.LongFormContentEvent
|
||||
import com.vitorpamplona.quartz.nip51Lists.followList.StarterPackEvent
|
||||
import com.vitorpamplona.quartz.nip51Lists.starterPack.StarterPackEvent
|
||||
import com.vitorpamplona.quartz.nip54Wiki.WikiArticleEvent
|
||||
import com.vitorpamplona.quartz.nip84Highlights.HighlightEvent
|
||||
import com.vitorpamplona.quartz.nip99Classifieds.ClassifiedsEvent
|
||||
|
||||
+1
-1
@@ -25,7 +25,7 @@ import com.vitorpamplona.amethyst.commons.model.AddressableNote
|
||||
import com.vitorpamplona.amethyst.commons.model.User
|
||||
import com.vitorpamplona.amethyst.commons.model.cache.LocalCache.checkGetOrCreateUser
|
||||
import com.vitorpamplona.amethyst.model.Account
|
||||
import com.vitorpamplona.quartz.nip51Lists.followList.StarterPackEvent
|
||||
import com.vitorpamplona.quartz.nip51Lists.starterPack.StarterPackEvent
|
||||
|
||||
class FollowPackMembersFeedFilter(
|
||||
val followPackNote: AddressableNote,
|
||||
|
||||
+1
-1
@@ -27,7 +27,7 @@ import com.vitorpamplona.amethyst.commons.relayClient.home.nip65Follows.filterHo
|
||||
import com.vitorpamplona.amethyst.commons.relays.SincePerRelayMap
|
||||
import com.vitorpamplona.quartz.nip01Core.relay.client.INostrClient
|
||||
import com.vitorpamplona.quartz.nip01Core.relay.client.pool.RelayBasedFilter
|
||||
import com.vitorpamplona.quartz.nip51Lists.followList.StarterPackEvent
|
||||
import com.vitorpamplona.quartz.nip51Lists.starterPack.StarterPackEvent
|
||||
|
||||
class FollowPackFeedFilterSubAssembler(
|
||||
client: INostrClient,
|
||||
|
||||
+1
-1
@@ -38,7 +38,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.nip51Lists.followList.StarterPackEvent
|
||||
import com.vitorpamplona.quartz.nip51Lists.starterPack.StarterPackEvent
|
||||
|
||||
@OptIn(ExperimentalFoundationApi::class)
|
||||
@Composable
|
||||
|
||||
+1
-1
@@ -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.nip51Lists.followList.StarterPackEvent
|
||||
import com.vitorpamplona.quartz.nip51Lists.starterPack.StarterPackEvent
|
||||
|
||||
@Composable
|
||||
fun FollowPacksTopBar(
|
||||
|
||||
+6
-6
@@ -31,7 +31,7 @@ import com.vitorpamplona.amethyst.commons.model.User
|
||||
import com.vitorpamplona.amethyst.model.Account
|
||||
import com.vitorpamplona.amethyst.ui.note.creators.userSuggestions.UserSuggestionState
|
||||
import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountViewModel
|
||||
import com.vitorpamplona.quartz.nip51Lists.peopleList.FollowSetEvent
|
||||
import com.vitorpamplona.quartz.nip51Lists.followSet.FollowSetEvent
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.ExperimentalCoroutinesApi
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
@@ -57,7 +57,7 @@ class PeopleListViewModel : ViewModel() {
|
||||
selectedDTag
|
||||
.transformLatest {
|
||||
emitAll(
|
||||
account.peopleLists.selectListFlow(it).flowOn(Dispatchers.IO),
|
||||
account.followSets.selectListFlow(it).flowOn(Dispatchers.IO),
|
||||
)
|
||||
}.flowOn(Dispatchers.IO)
|
||||
.stateIn(viewModelScope, SharingStarted.WhileSubscribed(5000), null)
|
||||
@@ -79,23 +79,23 @@ class PeopleListViewModel : ViewModel() {
|
||||
}
|
||||
|
||||
suspend fun deleteFollowSet() {
|
||||
account.peopleLists.deleteFollowSet(selectedDTag.value, account)
|
||||
account.followSets.deleteFollowSet(selectedDTag.value, account)
|
||||
}
|
||||
|
||||
fun loadNote(): AddressableNote? = account.peopleLists.getPeopleListNote(selectedDTag.value)
|
||||
fun loadNote(): AddressableNote? = account.followSets.getPeopleListNote(selectedDTag.value)
|
||||
|
||||
suspend fun removeUserFromSet(
|
||||
user: User,
|
||||
isPrivate: Boolean,
|
||||
) {
|
||||
account.peopleLists.removeUserFromSet(user, isPrivate, selectedDTag.value, account)
|
||||
account.followSets.removeUserFromSet(user, isPrivate, selectedDTag.value, account)
|
||||
}
|
||||
|
||||
suspend fun addUserToSet(
|
||||
user: User,
|
||||
isPrivate: Boolean,
|
||||
) {
|
||||
account.peopleLists.addUserToSet(user, selectedDTag.value, isPrivate, account)
|
||||
account.followSets.addUserToSet(user, selectedDTag.value, isPrivate, account)
|
||||
}
|
||||
|
||||
fun hasUserFlow(
|
||||
|
||||
+6
-6
@@ -31,7 +31,7 @@ import com.vitorpamplona.amethyst.commons.model.User
|
||||
import com.vitorpamplona.amethyst.model.Account
|
||||
import com.vitorpamplona.amethyst.ui.note.creators.userSuggestions.UserSuggestionState
|
||||
import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountViewModel
|
||||
import com.vitorpamplona.quartz.nip51Lists.followList.StarterPackEvent
|
||||
import com.vitorpamplona.quartz.nip51Lists.starterPack.StarterPackEvent
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.ExperimentalCoroutinesApi
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
@@ -57,7 +57,7 @@ class FollowPackViewModel : ViewModel() {
|
||||
selectedDTag
|
||||
.transformLatest {
|
||||
emitAll(
|
||||
account.followLists.selectListFlow(it).flowOn(Dispatchers.IO),
|
||||
account.starterPacks.selectListFlow(it).flowOn(Dispatchers.IO),
|
||||
)
|
||||
}.flowOn(Dispatchers.IO)
|
||||
.stateIn(viewModelScope, SharingStarted.WhileSubscribed(5000), null)
|
||||
@@ -79,17 +79,17 @@ class FollowPackViewModel : ViewModel() {
|
||||
}
|
||||
|
||||
suspend fun deleteFollowSet() {
|
||||
account.followLists.deleteFollowSet(selectedDTag.value, account)
|
||||
account.starterPacks.deleteFollowSet(selectedDTag.value, account)
|
||||
}
|
||||
|
||||
fun loadNote(): AddressableNote? = account.followLists.getPeopleListNote(selectedDTag.value)
|
||||
fun loadNote(): AddressableNote? = account.starterPacks.getPeopleListNote(selectedDTag.value)
|
||||
|
||||
suspend fun removeUserFromSet(user: User) {
|
||||
account.followLists.removeUserFromSet(user, selectedDTag.value, account)
|
||||
account.starterPacks.removeUserFromSet(user, selectedDTag.value, account)
|
||||
}
|
||||
|
||||
suspend fun addUserToSet(user: User) {
|
||||
account.followLists.addUserToSet(user, selectedDTag.value, account)
|
||||
account.starterPacks.addUserToSet(user, selectedDTag.value, account)
|
||||
}
|
||||
|
||||
fun hasUserFlow(user: User): Flow<Boolean> =
|
||||
|
||||
+4
-4
@@ -22,7 +22,7 @@ package com.vitorpamplona.amethyst.ui.screen.loggedIn.lists.list
|
||||
|
||||
import androidx.compose.runtime.Stable
|
||||
import androidx.lifecycle.ViewModel
|
||||
import com.vitorpamplona.amethyst.commons.model.nip51Lists.peopleList.PeopleList
|
||||
import com.vitorpamplona.amethyst.commons.model.nip51Lists.followSets.PeopleList
|
||||
import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountViewModel
|
||||
|
||||
@Stable
|
||||
@@ -33,7 +33,7 @@ class FollowPackViewModel : ViewModel() {
|
||||
this.accountViewModel = accountViewModel
|
||||
}
|
||||
|
||||
fun listFlow() = accountViewModel.account.followLists.uiListFlow
|
||||
fun listFlow() = accountViewModel.account.starterPacks.uiListFlow
|
||||
|
||||
fun cloneItem(
|
||||
followSet: PeopleList,
|
||||
@@ -41,7 +41,7 @@ class FollowPackViewModel : ViewModel() {
|
||||
customDescription: String?,
|
||||
) {
|
||||
accountViewModel.launchSigner {
|
||||
accountViewModel.account.followLists.cloneFollowSet(
|
||||
accountViewModel.account.starterPacks.cloneFollowSet(
|
||||
currentFollowPack = followSet,
|
||||
customCloneName = customName,
|
||||
customCloneDescription = customDescription,
|
||||
@@ -52,7 +52,7 @@ class FollowPackViewModel : ViewModel() {
|
||||
|
||||
fun deleteItem(followSet: PeopleList) {
|
||||
accountViewModel.launchSigner {
|
||||
accountViewModel.account.followLists.deleteFollowSet(
|
||||
accountViewModel.account.starterPacks.deleteFollowSet(
|
||||
identifierTag = followSet.identifierTag,
|
||||
account = accountViewModel.account,
|
||||
)
|
||||
|
||||
+4
-4
@@ -22,7 +22,7 @@ package com.vitorpamplona.amethyst.ui.screen.loggedIn.lists.list
|
||||
|
||||
import androidx.compose.runtime.Stable
|
||||
import androidx.lifecycle.ViewModel
|
||||
import com.vitorpamplona.amethyst.commons.model.nip51Lists.peopleList.PeopleList
|
||||
import com.vitorpamplona.amethyst.commons.model.nip51Lists.followSets.PeopleList
|
||||
import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountViewModel
|
||||
|
||||
@Stable
|
||||
@@ -33,7 +33,7 @@ class PeopleListViewModel : ViewModel() {
|
||||
this.accountViewModel = accountViewModel
|
||||
}
|
||||
|
||||
fun listFlow() = accountViewModel.account.peopleLists.uiListFlow
|
||||
fun listFlow() = accountViewModel.account.followSets.uiListFlow
|
||||
|
||||
fun cloneItem(
|
||||
followSet: PeopleList,
|
||||
@@ -41,7 +41,7 @@ class PeopleListViewModel : ViewModel() {
|
||||
customDescription: String?,
|
||||
) {
|
||||
accountViewModel.launchSigner {
|
||||
accountViewModel.account.peopleLists.cloneFollowSet(
|
||||
accountViewModel.account.followSets.cloneFollowSet(
|
||||
currentPeopleList = followSet,
|
||||
customCloneName = customName,
|
||||
customCloneDescription = customDescription,
|
||||
@@ -52,7 +52,7 @@ class PeopleListViewModel : ViewModel() {
|
||||
|
||||
fun deleteItem(followSet: PeopleList) {
|
||||
accountViewModel.launchSigner {
|
||||
accountViewModel.account.peopleLists.deleteFollowSet(
|
||||
accountViewModel.account.followSets.deleteFollowSet(
|
||||
identifierTag = followSet.identifierTag,
|
||||
account = accountViewModel.account,
|
||||
)
|
||||
|
||||
+4
-4
@@ -31,7 +31,7 @@ import androidx.lifecycle.ViewModel
|
||||
import androidx.lifecycle.viewModelScope
|
||||
import com.vitorpamplona.amethyst.Amethyst
|
||||
import com.vitorpamplona.amethyst.commons.model.mediaServers.ServerType
|
||||
import com.vitorpamplona.amethyst.commons.model.nip51Lists.peopleList.PeopleList
|
||||
import com.vitorpamplona.amethyst.commons.model.nip51Lists.followSets.PeopleList
|
||||
import com.vitorpamplona.amethyst.commons.resources.Res
|
||||
import com.vitorpamplona.amethyst.commons.resources.avif_metadata_strip_failed
|
||||
import com.vitorpamplona.amethyst.commons.resources.failed_to_upload_media_no_details
|
||||
@@ -83,7 +83,7 @@ class FollowPackMetadataViewModel : ViewModel() {
|
||||
}
|
||||
|
||||
fun load(dTag: String) {
|
||||
peopleList = account.followLists.selectList(dTag)
|
||||
peopleList = account.starterPacks.selectList(dTag)
|
||||
name.value = TextFieldValue(peopleList?.title ?: "")
|
||||
picture.value = TextFieldValue(peopleList?.image ?: "")
|
||||
description.value = TextFieldValue(peopleList?.description ?: "")
|
||||
@@ -94,14 +94,14 @@ class FollowPackMetadataViewModel : ViewModel() {
|
||||
val peopleList = peopleList
|
||||
if (peopleList == null) {
|
||||
val newListIdentifier =
|
||||
accountViewModel.account.followLists.addFollowList(
|
||||
accountViewModel.account.starterPacks.addFollowList(
|
||||
name = name.value.text,
|
||||
desc = description.value.text,
|
||||
image = picture.value.text,
|
||||
account = accountViewModel.account,
|
||||
)
|
||||
} else {
|
||||
accountViewModel.account.followLists.updateMetadata(
|
||||
accountViewModel.account.starterPacks.updateMetadata(
|
||||
name = name.value.text,
|
||||
desc = description.value.text,
|
||||
image = picture.value.text,
|
||||
|
||||
+4
-4
@@ -31,7 +31,7 @@ import androidx.lifecycle.ViewModel
|
||||
import androidx.lifecycle.viewModelScope
|
||||
import com.vitorpamplona.amethyst.Amethyst
|
||||
import com.vitorpamplona.amethyst.commons.model.mediaServers.ServerType
|
||||
import com.vitorpamplona.amethyst.commons.model.nip51Lists.peopleList.PeopleList
|
||||
import com.vitorpamplona.amethyst.commons.model.nip51Lists.followSets.PeopleList
|
||||
import com.vitorpamplona.amethyst.commons.resources.Res
|
||||
import com.vitorpamplona.amethyst.commons.resources.avif_metadata_strip_failed
|
||||
import com.vitorpamplona.amethyst.commons.resources.failed_to_upload_media_no_details
|
||||
@@ -83,7 +83,7 @@ class PeopleListMetadataViewModel : ViewModel() {
|
||||
}
|
||||
|
||||
fun load(dTag: String) {
|
||||
peopleList = account.peopleLists.selectList(dTag)
|
||||
peopleList = account.followSets.selectList(dTag)
|
||||
name.value = TextFieldValue(peopleList?.title ?: "")
|
||||
picture.value = TextFieldValue(peopleList?.image ?: "")
|
||||
description.value = TextFieldValue(peopleList?.description ?: "")
|
||||
@@ -95,14 +95,14 @@ class PeopleListMetadataViewModel : ViewModel() {
|
||||
accountViewModel.launchSigner {
|
||||
val peopleList = peopleList
|
||||
if (peopleList == null) {
|
||||
accountViewModel.account.peopleLists.addFollowList(
|
||||
accountViewModel.account.followSets.addFollowList(
|
||||
listName = name.value.text,
|
||||
listDescription = description.value.text,
|
||||
listImage = picture.value.text,
|
||||
account = accountViewModel.account,
|
||||
)
|
||||
} else {
|
||||
accountViewModel.account.peopleLists.updateMetadata(
|
||||
accountViewModel.account.followSets.updateMetadata(
|
||||
listName = name.value.text,
|
||||
listDescription = description.value.text,
|
||||
listImage = picture.value.text,
|
||||
|
||||
+6
-6
@@ -63,9 +63,9 @@ fun FollowListAndPackAndUserView(
|
||||
accountViewModel: AccountViewModel,
|
||||
nav: INav,
|
||||
) {
|
||||
val followSetsState by accountViewModel.account.peopleLists.uiListFlow
|
||||
val followSetsState by accountViewModel.account.followSets.uiListFlow
|
||||
.collectAsStateWithLifecycle()
|
||||
val followPackFeedState by accountViewModel.account.followLists.uiListFlow
|
||||
val followPackFeedState by accountViewModel.account.starterPacks.uiListFlow
|
||||
.collectAsStateWithLifecycle()
|
||||
|
||||
val userName by observeUserName(userToAddOrRemove, accountViewModel)
|
||||
@@ -114,7 +114,7 @@ fun FollowListAndPackAndUserView(
|
||||
userIsPublicMember = list.publicMembers.contains(userToAddOrRemove),
|
||||
onRemoveUser = {
|
||||
accountViewModel.launchSigner {
|
||||
accountViewModel.account.peopleLists.removeUserFromSet(
|
||||
accountViewModel.account.followSets.removeUserFromSet(
|
||||
userToAddOrRemove,
|
||||
isPrivate = list.privateMembers.contains(userToAddOrRemove),
|
||||
list.identifierTag,
|
||||
@@ -129,7 +129,7 @@ fun FollowListAndPackAndUserView(
|
||||
},
|
||||
onAddUserToList = { userShouldBePrivate ->
|
||||
accountViewModel.launchSigner {
|
||||
accountViewModel.account.peopleLists.addUserToSet(
|
||||
accountViewModel.account.followSets.addUserToSet(
|
||||
userToAddOrRemove,
|
||||
list.identifierTag,
|
||||
userShouldBePrivate,
|
||||
@@ -189,7 +189,7 @@ fun FollowListAndPackAndUserView(
|
||||
},
|
||||
onRemoveUser = {
|
||||
accountViewModel.launchSigner {
|
||||
accountViewModel.account.followLists.removeUserFromSet(
|
||||
accountViewModel.account.starterPacks.removeUserFromSet(
|
||||
userToAddOrRemove,
|
||||
list.identifierTag,
|
||||
accountViewModel.account,
|
||||
@@ -199,7 +199,7 @@ fun FollowListAndPackAndUserView(
|
||||
memberSize = list.publicMembers.size,
|
||||
onAddUserToList = {
|
||||
accountViewModel.launchSigner {
|
||||
accountViewModel.account.followLists.addUserToSet(
|
||||
accountViewModel.account.starterPacks.addUserToSet(
|
||||
userToAddOrRemove,
|
||||
list.identifierTag,
|
||||
accountViewModel.account,
|
||||
|
||||
+2
-2
@@ -28,7 +28,7 @@ import com.vitorpamplona.amethyst.commons.model.User
|
||||
import com.vitorpamplona.amethyst.commons.resources.Res
|
||||
import com.vitorpamplona.amethyst.commons.resources.number_followers
|
||||
import com.vitorpamplona.amethyst.commons.ui.stringRes
|
||||
import com.vitorpamplona.amethyst.service.relayClient.reqCommand.user.observeUserContactCardsFollowerCount
|
||||
import com.vitorpamplona.amethyst.service.relayClient.reqCommand.user.observeUserAssertionsFollowerCount
|
||||
import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountViewModel
|
||||
import com.vitorpamplona.amethyst.ui.screen.loggedIn.profile.followers.dal.UserProfileFollowersUserFeedViewModel
|
||||
|
||||
@@ -38,7 +38,7 @@ fun FollowersTabHeader(
|
||||
followersFeedViewModel: UserProfileFollowersUserFeedViewModel,
|
||||
accountViewModel: AccountViewModel,
|
||||
) {
|
||||
val followerCount by observeUserContactCardsFollowerCount(baseUser, accountViewModel)
|
||||
val followerCount by observeUserAssertionsFollowerCount(baseUser, accountViewModel)
|
||||
|
||||
if (followerCount == "--") {
|
||||
FollowersTabHeaderLocal(followersFeedViewModel, accountViewModel)
|
||||
|
||||
+2
-2
@@ -73,8 +73,8 @@ fun UserNicknameCard(
|
||||
WatchAndLoadMyEmojiList(accountViewModel)
|
||||
EditNicknameDialog(
|
||||
user = baseUser,
|
||||
contactCards = accountViewModel.account.contactCards,
|
||||
onSave = { petName, summary -> accountViewModel.updateContactCardPetName(baseUser, petName, summary) },
|
||||
userAssertions = accountViewModel.account.userAssertions,
|
||||
onSave = { petName, summary -> accountViewModel.updateUserAssertionPetName(baseUser, petName, summary) },
|
||||
onDismiss = { isEditDialogOpen.value = false },
|
||||
)
|
||||
}
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user