Migrate collections to new stores, change some conventions
This commit is contained in:
@@ -9,11 +9,11 @@ removeRelay(url: string, mode: RelayMode): Promise<Thunk>
|
|||||||
addRelay(url: string, mode: RelayMode): Promise<Thunk>
|
addRelay(url: string, mode: RelayMode): Promise<Thunk>
|
||||||
```
|
```
|
||||||
|
|
||||||
## Inbox Relay Management (NIP 17)
|
## Messaging Relay Management (NIP 17)
|
||||||
|
|
||||||
```typescript
|
```typescript
|
||||||
removeInboxRelay(url: string): Promise<Thunk>
|
removeMessagingRelay(url: string): Promise<Thunk>
|
||||||
addInboxRelay(url: string): Promise<Thunk>
|
addMessagingRelay(url: string): Promise<Thunk>
|
||||||
```
|
```
|
||||||
|
|
||||||
## Profile Management (NIP 01)
|
## Profile Management (NIP 01)
|
||||||
|
|||||||
+1
-1
@@ -36,7 +36,7 @@ defaultSocketPolicies.push(
|
|||||||
|
|
||||||
// This will fetch the user's profile automatically, and return a store that updates
|
// This will fetch the user's profile automatically, and return a store that updates
|
||||||
// automatically. Several different stores exist that are ready to go, including handles,
|
// automatically. Several different stores exist that are ready to go, including handles,
|
||||||
// zappers, relaySelections, relays, follows, mutes.
|
// zappers, relayLists, relays, follows, mutes.
|
||||||
const profile = deriveProfile(pubkey.get())
|
const profile = deriveProfile(pubkey.get())
|
||||||
|
|
||||||
// Publish is done using thunks, which optimistically publish to the local database, deferring
|
// Publish is done using thunks, which optimistically publish to the local database, deferring
|
||||||
|
|||||||
@@ -59,8 +59,8 @@ pins → pinsByPubkey → derivePins → loadPins
|
|||||||
|
|
||||||
// Relays
|
// Relays
|
||||||
relays → relaysByUrl → deriveRelay → loadRelay
|
relays → relaysByUrl → deriveRelay → loadRelay
|
||||||
relaySelections → relaySelectionsByPubkey → deriveRelaySelections → loadRelaySelections
|
relayLists → relayListsByPubkey → deriveRelayLists → loadRelayLists
|
||||||
inboxRelaySelections → inboxRelaySelectionsByPubkey → deriveInboxRelaySelections → loadInboxRelaySelections
|
messagingRelayLists → messagingRelayListsByPubkey → deriveMessagingRelayLists → loadMessagingRelayLists
|
||||||
|
|
||||||
// Identity
|
// Identity
|
||||||
handles → handlesByNip05 → deriveHandle → loadHandle
|
handles → handlesByNip05 → deriveHandle → loadHandle
|
||||||
|
|||||||
@@ -57,8 +57,8 @@ Several thunk factories are provided for common or more complicated scenarios li
|
|||||||
|
|
||||||
- `removeRelay(url: string, mode: RelayMode)`
|
- `removeRelay(url: string, mode: RelayMode)`
|
||||||
- `addRelay(url: string, mode: RelayMode)`
|
- `addRelay(url: string, mode: RelayMode)`
|
||||||
- `removeInboxRelay(url: string)`
|
- `removeMessagingRelay(url: string)`
|
||||||
- `addInboxRelay(url: string)`
|
- `addMessagingRelay(url: string)`
|
||||||
- `setProfile(profile: Profile)`
|
- `setProfile(profile: Profile)`
|
||||||
- `unfollow(value: string)`
|
- `unfollow(value: string)`
|
||||||
- `follow(tag: string[])`
|
- `follow(tag: string[])`
|
||||||
|
|||||||
+8
-8
@@ -39,10 +39,10 @@ export const userMutes: Store<List | undefined>
|
|||||||
export const userPins: Store<List | undefined>
|
export const userPins: Store<List | undefined>
|
||||||
|
|
||||||
// User relay selections
|
// User relay selections
|
||||||
export const userRelaySelections: Store<List | undefined>
|
export const userRelayLists: Store<List | undefined>
|
||||||
|
|
||||||
// User inbox relay selections
|
// User messaging relay selections
|
||||||
export const userInboxRelaySelections: Store<List | undefined>
|
export const userMessagingRelayLists: Store<List | undefined>
|
||||||
|
|
||||||
// User blossom servers
|
// User blossom servers
|
||||||
export const userBlossomServers: Store<List | undefined>
|
export const userBlossomServers: Store<List | undefined>
|
||||||
@@ -66,10 +66,10 @@ function loadUserMutes(relays?: string[], force?: boolean): Promise<void>
|
|||||||
function loadUserPins(relays?: string[], force?: boolean): Promise<void>
|
function loadUserPins(relays?: string[], force?: boolean): Promise<void>
|
||||||
|
|
||||||
// Load user relay selections
|
// Load user relay selections
|
||||||
function loadUserRelaySelections(relays?: string[], force?: boolean): Promise<void>
|
function loadUserRelayLists(relays?: string[], force?: boolean): Promise<void>
|
||||||
|
|
||||||
// Load user inbox relay selections
|
// Load user messaging relay selections
|
||||||
function loadUserInboxRelaySelections(relays?: string[], force?: boolean): Promise<void>
|
function loadUserMessagingRelayLists(relays?: string[], force?: boolean): Promise<void>
|
||||||
|
|
||||||
// Load user blossom servers
|
// Load user blossom servers
|
||||||
function loadUserBlossomServers(relays?: string[], force?: boolean): Promise<void>
|
function loadUserBlossomServers(relays?: string[], force?: boolean): Promise<void>
|
||||||
@@ -94,13 +94,13 @@ const follows = userFollows.get()
|
|||||||
|
|
||||||
### Manual Loading
|
### Manual Loading
|
||||||
```typescript
|
```typescript
|
||||||
import { loadUserMutes, loadUserRelaySelections } from '@welshman/app'
|
import { loadUserMutes, loadUserRelayLists } from '@welshman/app'
|
||||||
|
|
||||||
// Load user mutes from specific relays
|
// Load user mutes from specific relays
|
||||||
await loadUserMutes(['wss://relay1.com', 'wss://relay2.com'])
|
await loadUserMutes(['wss://relay1.com', 'wss://relay2.com'])
|
||||||
|
|
||||||
// Force refresh user relay selections
|
// Force refresh user relay selections
|
||||||
await loadUserRelaySelections([], true)
|
await loadUserRelayLists([], true)
|
||||||
|
|
||||||
// Load from default relays
|
// Load from default relays
|
||||||
await loadUserProfile()
|
await loadUserProfile()
|
||||||
|
|||||||
@@ -31,7 +31,7 @@ const router = Router.get()
|
|||||||
// Get relays for reading events from specific pubkeys
|
// Get relays for reading events from specific pubkeys
|
||||||
const readRelays = router.FromPubkeys(['pubkey1', 'pubkey2']).getUrls()
|
const readRelays = router.FromPubkeys(['pubkey1', 'pubkey2']).getUrls()
|
||||||
|
|
||||||
// Get relays for publishing an event (author's outbox + mentions' inboxes)
|
// Get relays for publishing an event (author's outbox + mentions' messaginges)
|
||||||
const publishRelays = router.PublishEvent(event).getUrls()
|
const publishRelays = router.PublishEvent(event).getUrls()
|
||||||
|
|
||||||
// Try hard to find a quoted note with maximal fallbacks
|
// Try hard to find a quoted note with maximal fallbacks
|
||||||
@@ -65,7 +65,7 @@ The main class for relay selection. Configure it once with your relay discovery
|
|||||||
|
|
||||||
**Configuration Options:**
|
**Configuration Options:**
|
||||||
- `getUserPubkey()` - Returns the current user's pubkey
|
- `getUserPubkey()` - Returns the current user's pubkey
|
||||||
- `getPubkeyRelays(pubkey, mode)` - Returns relays for a pubkey ("read", "write", or "inbox")
|
- `getPubkeyRelays(pubkey, mode)` - Returns relays for a pubkey ("read", "write", or "messaging")
|
||||||
- `getDefaultRelays()` - Returns fallback relays
|
- `getDefaultRelays()` - Returns fallback relays
|
||||||
- `getIndexerRelays()` - Returns relays that index profiles and relay lists
|
- `getIndexerRelays()` - Returns relays that index profiles and relay lists
|
||||||
- `getSearchRelays()` - Returns relays that support NIP-50 search
|
- `getSearchRelays()` - Returns relays that support NIP-50 search
|
||||||
@@ -74,8 +74,8 @@ The main class for relay selection. Configure it once with your relay discovery
|
|||||||
|
|
||||||
**Scenario Methods:**
|
**Scenario Methods:**
|
||||||
- `FromRelays(relays)` - Use specific relays
|
- `FromRelays(relays)` - Use specific relays
|
||||||
- `ForUser()` / `FromUser()` / `UserInbox()` - User's read/write/inbox relays
|
- `ForUser()` / `FromUser()` / `UserMessaging()` - User's read/write/messaging relays
|
||||||
- `ForPubkey(pubkey)` / `FromPubkey(pubkey)` / `PubkeyInbox(pubkey)` - Pubkey's relays
|
- `ForPubkey(pubkey)` / `FromPubkey(pubkey)` / `PubkeyMessaging(pubkey)` - Pubkey's relays
|
||||||
- `ForPubkeys(pubkeys)` / `FromPubkeys(pubkeys)` - Multiple pubkeys' relays
|
- `ForPubkeys(pubkeys)` / `FromPubkeys(pubkeys)` - Multiple pubkeys' relays
|
||||||
- `Event(event)` - Relays for an event's author
|
- `Event(event)` - Relays for an event's author
|
||||||
- `PublishEvent(event)` - Relays for publishing (author + mentions)
|
- `PublishEvent(event)` - Relays for publishing (author + mentions)
|
||||||
@@ -105,7 +105,7 @@ Functions that determine how many fallback relays to add:
|
|||||||
|
|
||||||
`getFilterSelections(filters)` automatically chooses appropriate relays based on filter content:
|
`getFilterSelections(filters)` automatically chooses appropriate relays based on filter content:
|
||||||
- Search filters → search relays
|
- Search filters → search relays
|
||||||
- Wrap events → user's inbox
|
- Wrap events → user's messaging
|
||||||
- Profile/relay kinds → indexer relays
|
- Profile/relay kinds → indexer relays
|
||||||
- Author filters → authors' relays
|
- Author filters → authors' relays
|
||||||
- Everything else → user's relays (low weight)
|
- Everything else → user's relays (low weight)
|
||||||
|
|||||||
+1
-1
@@ -11,7 +11,7 @@ The `Relay` module provides utilities for working with Nostr relays, including U
|
|||||||
export enum RelayMode {
|
export enum RelayMode {
|
||||||
Read = "read",
|
Read = "read",
|
||||||
Write = "write",
|
Write = "write",
|
||||||
Inbox = "inbox"
|
Messaging = "messaging"
|
||||||
}
|
}
|
||||||
|
|
||||||
// Relay information from NIP-11
|
// Relay information from NIP-11
|
||||||
|
|||||||
@@ -2,23 +2,23 @@ import {BLOSSOM_SERVERS, asDecryptedEvent, readList} from "@welshman/util"
|
|||||||
import {TrustedEvent, PublishedList} from "@welshman/util"
|
import {TrustedEvent, PublishedList} from "@welshman/util"
|
||||||
import {deriveItemsByKey, deriveItems, makeForceLoadItem, makeLoadItem, makeDeriveItem, getter} from "@welshman/store"
|
import {deriveItemsByKey, deriveItems, makeForceLoadItem, makeLoadItem, makeDeriveItem, getter} from "@welshman/store"
|
||||||
import {repository} from "./core.js"
|
import {repository} from "./core.js"
|
||||||
import {makeOutboxLoader} from "./relaySelections.js"
|
import {makeOutboxLoader} from "./relayLists.js"
|
||||||
|
|
||||||
export const blossomServersByPubkey = deriveItemsByKey({
|
export const blossomServerListsByPubkey = deriveItemsByKey({
|
||||||
repository,
|
repository,
|
||||||
eventToItem: (event: TrustedEvent) => readList(asDecryptedEvent(event)),
|
eventToItem: (event: TrustedEvent) => readList(asDecryptedEvent(event)),
|
||||||
filters: [{kinds: [BLOSSOM_SERVERS]}],
|
filters: [{kinds: [BLOSSOM_SERVERS]}],
|
||||||
getKey: blossomServers => blossomServers.event.pubkey,
|
getKey: blossomServerList => blossomServerList.event.pubkey,
|
||||||
})
|
})
|
||||||
|
|
||||||
export const blossomServers = deriveItems(blossomServersByPubkey)
|
export const blossomServerLists = deriveItems(blossomServerListsByPubkey)
|
||||||
|
|
||||||
export const getBlossomServersByPubkey = getter(blossomServersByPubkey)
|
export const getBlossomServerListsByPubkey = getter(blossomServerListsByPubkey)
|
||||||
|
|
||||||
export const getBlossomServers = (pubkey: string) => getBlossomServersByPubkey().get(pubkey)
|
export const getBlossomServerList = (pubkey: string) => getBlossomServerListsByPubkey().get(pubkey)
|
||||||
|
|
||||||
export const forceLoadBlossomServers = makeForceLoadItem(makeOutboxLoader(BLOSSOM_SERVERS), getBlossomServers)
|
export const forceLoadBlossomServerList = makeForceLoadItem(makeOutboxLoader(BLOSSOM_SERVERS), getBlossomServerList)
|
||||||
|
|
||||||
export const loadBlossomServers = makeLoadItem(makeOutboxLoader(BLOSSOM_SERVERS), getBlossomServers)
|
export const loadBlossomServerList = makeLoadItem(makeOutboxLoader(BLOSSOM_SERVERS), getBlossomServerList)
|
||||||
|
|
||||||
export const deriveBlossomServers = makeDeriveItem(blossomServersByPubkey, loadBlossomServers)
|
export const deriveBlossomServerList = makeDeriveItem(blossomServerListsByPubkey, loadBlossomServerList)
|
||||||
|
|||||||
@@ -23,7 +23,7 @@ import {
|
|||||||
createProfile,
|
createProfile,
|
||||||
editProfile,
|
editProfile,
|
||||||
RelayMode,
|
RelayMode,
|
||||||
INBOX_RELAYS,
|
MESSAGING_RELAYS,
|
||||||
FOLLOWS,
|
FOLLOWS,
|
||||||
RELAYS,
|
RELAYS,
|
||||||
MUTES,
|
MUTES,
|
||||||
@@ -32,16 +32,16 @@ import {
|
|||||||
import type {RoomMeta, Profile} from "@welshman/util"
|
import type {RoomMeta, Profile} from "@welshman/util"
|
||||||
import {Router, addMaximalFallbacks} from "@welshman/router"
|
import {Router, addMaximalFallbacks} from "@welshman/router"
|
||||||
import {
|
import {
|
||||||
userRelaySelections,
|
userRelayList,
|
||||||
loadUserRelaySelections,
|
forceLoadUserRelayList,
|
||||||
userInboxRelaySelections,
|
userMessagingRelayList,
|
||||||
loadUserInboxRelaySelections,
|
forceLoadUserMessagingRelayList,
|
||||||
userFollows,
|
userFollowList,
|
||||||
loadUserFollows,
|
forceLoadUserFollowList,
|
||||||
userMutes,
|
userMuteList,
|
||||||
loadUserMutes,
|
forceLoadUserMuteList,
|
||||||
userPins,
|
userPinList,
|
||||||
loadUserPins,
|
forceLoadUserPinList,
|
||||||
} from "./user.js"
|
} from "./user.js"
|
||||||
import {nip44EncryptToSelf, signer} from "./session.js"
|
import {nip44EncryptToSelf, signer} from "./session.js"
|
||||||
import {ThunkOptions, MergedThunk, publishThunk} from "./thunk.js"
|
import {ThunkOptions, MergedThunk, publishThunk} from "./thunk.js"
|
||||||
@@ -49,9 +49,9 @@ import {ThunkOptions, MergedThunk, publishThunk} from "./thunk.js"
|
|||||||
// NIP 65
|
// NIP 65
|
||||||
|
|
||||||
export const removeRelay = async (url: string, mode: RelayMode) => {
|
export const removeRelay = async (url: string, mode: RelayMode) => {
|
||||||
await loadUserRelaySelections([], true)
|
await forceLoadUserRelayList([])
|
||||||
|
|
||||||
const list = get(userRelaySelections) || makeList({kind: RELAYS})
|
const list = get(userRelayList) || makeList({kind: RELAYS})
|
||||||
const dup = getRelayTags(getListTags(list)).find(nthEq(1, url))
|
const dup = getRelayTags(getListTags(list)).find(nthEq(1, url))
|
||||||
const alt = mode === RelayMode.Read ? RelayMode.Write : RelayMode.Read
|
const alt = mode === RelayMode.Read ? RelayMode.Write : RelayMode.Read
|
||||||
const tags = list.publicTags.filter(nthNe(1, url))
|
const tags = list.publicTags.filter(nthNe(1, url))
|
||||||
@@ -71,9 +71,9 @@ export const removeRelay = async (url: string, mode: RelayMode) => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export const addRelay = async (url: string, mode: RelayMode) => {
|
export const addRelay = async (url: string, mode: RelayMode) => {
|
||||||
await loadUserRelaySelections([], true)
|
await forceLoadUserRelayList([])
|
||||||
|
|
||||||
const list = get(userRelaySelections) || makeList({kind: RELAYS})
|
const list = get(userRelayList) || makeList({kind: RELAYS})
|
||||||
const dup = getRelayTags(getListTags(list)).find(nthEq(1, url))
|
const dup = getRelayTags(getListTags(list)).find(nthEq(1, url))
|
||||||
const tag = removeUndefined(["r", url, dup && dup[2] !== mode ? undefined : mode])
|
const tag = removeUndefined(["r", url, dup && dup[2] !== mode ? undefined : mode])
|
||||||
const tags = [...list.publicTags.filter(nthNe(1, url)), tag]
|
const tags = [...list.publicTags.filter(nthNe(1, url)), tag]
|
||||||
@@ -85,20 +85,20 @@ export const addRelay = async (url: string, mode: RelayMode) => {
|
|||||||
|
|
||||||
// NIP 17
|
// NIP 17
|
||||||
|
|
||||||
export const removeInboxRelay = async (url: string) => {
|
export const removeMessagingRelay = async (url: string) => {
|
||||||
await loadUserInboxRelaySelections([], true)
|
await forceLoadUserMessagingRelayList([])
|
||||||
|
|
||||||
const list = get(userInboxRelaySelections) || makeList({kind: INBOX_RELAYS})
|
const list = get(userMessagingRelayList) || makeList({kind: MESSAGING_RELAYS})
|
||||||
const event = await removeFromList(list, url).reconcile(nip44EncryptToSelf)
|
const event = await removeFromList(list, url).reconcile(nip44EncryptToSelf)
|
||||||
const relays = Router.get().FromUser().policy(addMaximalFallbacks).getUrls()
|
const relays = Router.get().FromUser().policy(addMaximalFallbacks).getUrls()
|
||||||
|
|
||||||
return publishThunk({event, relays})
|
return publishThunk({event, relays})
|
||||||
}
|
}
|
||||||
|
|
||||||
export const addInboxRelay = async (url: string) => {
|
export const addMessagingRelay = async (url: string) => {
|
||||||
await loadUserInboxRelaySelections([], true)
|
await forceLoadUserMessagingRelayList([])
|
||||||
|
|
||||||
const list = get(userInboxRelaySelections) || makeList({kind: INBOX_RELAYS})
|
const list = get(userMessagingRelayList) || makeList({kind: MESSAGING_RELAYS})
|
||||||
const event = await addToListPublicly(list, ["relay", url]).reconcile(nip44EncryptToSelf)
|
const event = await addToListPublicly(list, ["relay", url]).reconcile(nip44EncryptToSelf)
|
||||||
const relays = Router.get().FromUser().policy(addMaximalFallbacks).getUrls()
|
const relays = Router.get().FromUser().policy(addMaximalFallbacks).getUrls()
|
||||||
|
|
||||||
@@ -118,9 +118,9 @@ export const setProfile = (profile: Profile) => {
|
|||||||
// NIP 02
|
// NIP 02
|
||||||
|
|
||||||
export const unfollow = async (value: string) => {
|
export const unfollow = async (value: string) => {
|
||||||
await loadUserFollows([], true)
|
await forceLoadUserFollowList([])
|
||||||
|
|
||||||
const list = get(userFollows) || makeList({kind: FOLLOWS})
|
const list = get(userFollowList) || makeList({kind: FOLLOWS})
|
||||||
const event = await removeFromList(list, value).reconcile(nip44EncryptToSelf)
|
const event = await removeFromList(list, value).reconcile(nip44EncryptToSelf)
|
||||||
const relays = Router.get().FromUser().policy(addMaximalFallbacks).getUrls()
|
const relays = Router.get().FromUser().policy(addMaximalFallbacks).getUrls()
|
||||||
|
|
||||||
@@ -128,9 +128,9 @@ export const unfollow = async (value: string) => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export const follow = async (tag: string[]) => {
|
export const follow = async (tag: string[]) => {
|
||||||
await loadUserFollows([], true)
|
await forceLoadUserFollowList([])
|
||||||
|
|
||||||
const list = get(userFollows) || makeList({kind: FOLLOWS})
|
const list = get(userFollowList) || makeList({kind: FOLLOWS})
|
||||||
const event = await addToListPublicly(list, tag).reconcile(nip44EncryptToSelf)
|
const event = await addToListPublicly(list, tag).reconcile(nip44EncryptToSelf)
|
||||||
const relays = Router.get().FromUser().policy(addMaximalFallbacks).getUrls()
|
const relays = Router.get().FromUser().policy(addMaximalFallbacks).getUrls()
|
||||||
|
|
||||||
@@ -138,9 +138,9 @@ export const follow = async (tag: string[]) => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export const unmute = async (value: string) => {
|
export const unmute = async (value: string) => {
|
||||||
await loadUserMutes([], true)
|
await forceLoadUserMuteList([])
|
||||||
|
|
||||||
const list = get(userMutes) || makeList({kind: MUTES})
|
const list = get(userMuteList) || makeList({kind: MUTES})
|
||||||
const event = await removeFromList(list, value).reconcile(nip44EncryptToSelf)
|
const event = await removeFromList(list, value).reconcile(nip44EncryptToSelf)
|
||||||
const relays = Router.get().FromUser().policy(addMaximalFallbacks).getUrls()
|
const relays = Router.get().FromUser().policy(addMaximalFallbacks).getUrls()
|
||||||
|
|
||||||
@@ -148,9 +148,9 @@ export const unmute = async (value: string) => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export const mutePublicly = async (tag: string[]) => {
|
export const mutePublicly = async (tag: string[]) => {
|
||||||
await loadUserMutes([], true)
|
await forceLoadUserMuteList([])
|
||||||
|
|
||||||
const list = get(userMutes) || makeList({kind: MUTES})
|
const list = get(userMuteList) || makeList({kind: MUTES})
|
||||||
const event = await addToListPublicly(list, tag).reconcile(nip44EncryptToSelf)
|
const event = await addToListPublicly(list, tag).reconcile(nip44EncryptToSelf)
|
||||||
const relays = Router.get().FromUser().policy(addMaximalFallbacks).getUrls()
|
const relays = Router.get().FromUser().policy(addMaximalFallbacks).getUrls()
|
||||||
|
|
||||||
@@ -158,9 +158,9 @@ export const mutePublicly = async (tag: string[]) => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export const mutePrivately = async (tag: string[]) => {
|
export const mutePrivately = async (tag: string[]) => {
|
||||||
await loadUserMutes([], true)
|
await forceLoadUserMuteList([])
|
||||||
|
|
||||||
const list = get(userMutes) || makeList({kind: MUTES})
|
const list = get(userMuteList) || makeList({kind: MUTES})
|
||||||
const event = await addToListPrivately(list, tag).reconcile(nip44EncryptToSelf)
|
const event = await addToListPrivately(list, tag).reconcile(nip44EncryptToSelf)
|
||||||
const relays = Router.get().FromUser().policy(addMaximalFallbacks).getUrls()
|
const relays = Router.get().FromUser().policy(addMaximalFallbacks).getUrls()
|
||||||
|
|
||||||
@@ -174,9 +174,9 @@ export const setMutes = async ({
|
|||||||
publicTags?: string[][]
|
publicTags?: string[][]
|
||||||
privateTags?: string[][]
|
privateTags?: string[][]
|
||||||
}) => {
|
}) => {
|
||||||
await loadUserMutes([], true)
|
await forceLoadUserMuteList([])
|
||||||
|
|
||||||
const list = get(userMutes) || makeList({kind: MUTES})
|
const list = get(userMuteList) || makeList({kind: MUTES})
|
||||||
const event = await updateList(list, {publicTags, privateTags}).reconcile(nip44EncryptToSelf)
|
const event = await updateList(list, {publicTags, privateTags}).reconcile(nip44EncryptToSelf)
|
||||||
const relays = Router.get().FromUser().policy(addMaximalFallbacks).getUrls()
|
const relays = Router.get().FromUser().policy(addMaximalFallbacks).getUrls()
|
||||||
|
|
||||||
@@ -184,9 +184,9 @@ export const setMutes = async ({
|
|||||||
}
|
}
|
||||||
|
|
||||||
export const unpin = async (value: string) => {
|
export const unpin = async (value: string) => {
|
||||||
await loadUserPins([], true)
|
await forceLoadUserPinList([])
|
||||||
|
|
||||||
const list = get(userPins) || makeList({kind: PINS})
|
const list = get(userPinList) || makeList({kind: PINS})
|
||||||
const event = await removeFromList(list, value).reconcile(nip44EncryptToSelf)
|
const event = await removeFromList(list, value).reconcile(nip44EncryptToSelf)
|
||||||
const relays = Router.get().FromUser().policy(addMaximalFallbacks).getUrls()
|
const relays = Router.get().FromUser().policy(addMaximalFallbacks).getUrls()
|
||||||
|
|
||||||
@@ -194,9 +194,9 @@ export const unpin = async (value: string) => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export const pin = async (tag: string[]) => {
|
export const pin = async (tag: string[]) => {
|
||||||
await loadUserPins([], true)
|
await forceLoadUserPinList([])
|
||||||
|
|
||||||
const list = get(userPins) || makeList({kind: PINS})
|
const list = get(userPinList) || makeList({kind: PINS})
|
||||||
const event = await addToListPublicly(list, tag).reconcile(nip44EncryptToSelf)
|
const event = await addToListPublicly(list, tag).reconcile(nip44EncryptToSelf)
|
||||||
const relays = Router.get().FromUser().policy(addMaximalFallbacks).getUrls()
|
const relays = Router.get().FromUser().policy(addMaximalFallbacks).getUrls()
|
||||||
|
|
||||||
@@ -213,7 +213,7 @@ export type SendWrappedOptions = Omit<ThunkOptions, "event" | "relays"> & {
|
|||||||
export const sendWrapped = ({event, recipients, ...options}: SendWrappedOptions) =>
|
export const sendWrapped = ({event, recipients, ...options}: SendWrappedOptions) =>
|
||||||
new MergedThunk(
|
new MergedThunk(
|
||||||
uniq(recipients).map(recipient => {
|
uniq(recipients).map(recipient => {
|
||||||
const relays = Router.get().PubkeyInbox(recipient).getUrls()
|
const relays = Router.get().MessagesForPubkey(recipient).getUrls()
|
||||||
|
|
||||||
return publishThunk({event, relays, recipient, ...options})
|
return publishThunk({event, relays, recipient, ...options})
|
||||||
}),
|
}),
|
||||||
|
|||||||
@@ -1,55 +1,5 @@
|
|||||||
import {throttle} from "@welshman/lib"
|
|
||||||
import {Repository, Tracker} from "@welshman/net"
|
import {Repository, Tracker} from "@welshman/net"
|
||||||
import {custom} from "@welshman/store"
|
|
||||||
|
|
||||||
export const tracker = new Tracker()
|
export const tracker = new Tracker()
|
||||||
|
|
||||||
export const repository = Repository.get()
|
export const repository = Repository.get()
|
||||||
|
|
||||||
// Adapt objects to stores
|
|
||||||
|
|
||||||
export const makeRepositoryStore = ({throttle: t = 300}: {throttle?: number} = {}) =>
|
|
||||||
custom(
|
|
||||||
setter => {
|
|
||||||
let onUpdate = () => setter(repository)
|
|
||||||
|
|
||||||
if (t) {
|
|
||||||
onUpdate = throttle(t, onUpdate)
|
|
||||||
}
|
|
||||||
|
|
||||||
onUpdate()
|
|
||||||
repository.on("update", onUpdate)
|
|
||||||
|
|
||||||
return () => repository.off("update", onUpdate)
|
|
||||||
},
|
|
||||||
{
|
|
||||||
onUpdate: (other: Repository) => repository.load(other.dump()),
|
|
||||||
},
|
|
||||||
)
|
|
||||||
|
|
||||||
export const makeTrackerStore = ({throttle: t = 300}: {throttle?: number} = {}) =>
|
|
||||||
custom(
|
|
||||||
setter => {
|
|
||||||
let onUpdate = () => setter(tracker)
|
|
||||||
|
|
||||||
if (t) {
|
|
||||||
onUpdate = throttle(t, onUpdate)
|
|
||||||
}
|
|
||||||
|
|
||||||
onUpdate()
|
|
||||||
tracker.on("add", onUpdate)
|
|
||||||
tracker.on("remove", onUpdate)
|
|
||||||
tracker.on("load", onUpdate)
|
|
||||||
tracker.on("clear", onUpdate)
|
|
||||||
|
|
||||||
return () => {
|
|
||||||
tracker.off("add", onUpdate)
|
|
||||||
tracker.off("remove", onUpdate)
|
|
||||||
tracker.off("load", onUpdate)
|
|
||||||
tracker.off("clear", onUpdate)
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
onUpdate: (other: Tracker) => tracker.load(other.relaysById),
|
|
||||||
},
|
|
||||||
)
|
|
||||||
|
|||||||
+12
-10
@@ -2,23 +2,25 @@ import {FOLLOWS, asDecryptedEvent, readList} from "@welshman/util"
|
|||||||
import {TrustedEvent, PublishedList} from "@welshman/util"
|
import {TrustedEvent, PublishedList} from "@welshman/util"
|
||||||
import {deriveItemsByKey, deriveItems, makeForceLoadItem, makeLoadItem, makeDeriveItem, getter} from "@welshman/store"
|
import {deriveItemsByKey, deriveItems, makeForceLoadItem, makeLoadItem, makeDeriveItem, getter} from "@welshman/store"
|
||||||
import {repository} from "./core.js"
|
import {repository} from "./core.js"
|
||||||
import {makeOutboxLoader} from "./relaySelections.js"
|
import {makeOutboxLoader} from "./relayLists.js"
|
||||||
|
|
||||||
export const followsByPubkey = deriveItemsByKey({
|
export const followListsByPubkey = deriveItemsByKey({
|
||||||
repository,
|
repository,
|
||||||
eventToItem: (event: TrustedEvent) => readList(asDecryptedEvent(event)),
|
|
||||||
filters: [{kinds: [FOLLOWS]}],
|
filters: [{kinds: [FOLLOWS]}],
|
||||||
getKey: follows => follows.event.pubkey,
|
eventToItem: (event: TrustedEvent) => readList(asDecryptedEvent(event)),
|
||||||
|
getKey: followList => followList.event.pubkey,
|
||||||
})
|
})
|
||||||
|
|
||||||
export const follows = deriveItems(followsByPubkey)
|
export const followLists = deriveItems(followListsByPubkey)
|
||||||
|
|
||||||
export const getFollowsByPubkey = getter(followsByPubkey)
|
export const getFollowListsByPubkey = getter(followListsByPubkey)
|
||||||
|
|
||||||
export const getFollows = (pubkey: string) => getFollowsByPubkey().get(pubkey)
|
export const getFollowLists = getter(followLists)
|
||||||
|
|
||||||
export const forceLoadFollows = makeForceLoadItem(makeOutboxLoader(FOLLOWS), getFollows)
|
export const getFollowList = (pubkey: string) => getFollowListsByPubkey().get(pubkey)
|
||||||
|
|
||||||
export const loadFollows = makeLoadItem(makeOutboxLoader(FOLLOWS), getFollows)
|
export const forceLoadFollowList = makeForceLoadItem(makeOutboxLoader(FOLLOWS), getFollowList)
|
||||||
|
|
||||||
export const deriveFollows = makeDeriveItem(followsByPubkey, loadFollows)
|
export const loadFollowList = makeLoadItem(makeOutboxLoader(FOLLOWS), getFollowList)
|
||||||
|
|
||||||
|
export const deriveFollowList = makeDeriveItem(followListsByPubkey, loadFollowList)
|
||||||
|
|||||||
+39
-42
@@ -1,7 +1,7 @@
|
|||||||
import {writable, derived} from "svelte/store"
|
import {writable, derived} from "svelte/store"
|
||||||
import {tryCatch, fetchJson, uniq, batcher, postJson, last} from "@welshman/lib"
|
import {tryCatch, fetchJson, uniq, batcher, postJson, last} from "@welshman/lib"
|
||||||
import {collection} from "@welshman/store"
|
import {getter, deriveItems, makeForceLoadItem, makeLoadItem, makeDeriveItem} from "@welshman/store"
|
||||||
import {deriveProfile} from "./profiles.js"
|
import {deriveProfile, loadProfile} from "./profiles.js"
|
||||||
import {appContext} from "./context.js"
|
import {appContext} from "./context.js"
|
||||||
|
|
||||||
export type Handle = {
|
export type Handle = {
|
||||||
@@ -40,16 +40,23 @@ export async function queryProfile(nip05: string) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export const handles = writable<Handle[]>([])
|
export const handlesByNip05 = writable(new Map<string, Handle>())
|
||||||
|
|
||||||
export const fetchHandles = async (nip05s: string[]) => {
|
export const handles = deriveItems(handlesByNip05)
|
||||||
const base = appContext.dufflepudUrl!
|
|
||||||
|
export const getHandlesByNip05 = getter(handlesByNip05)
|
||||||
|
|
||||||
|
export const getHandles = getter(handles)
|
||||||
|
|
||||||
|
export const getHandle = (nip05: string) => getHandlesByNip05().get(nip05)
|
||||||
|
|
||||||
|
export const fetchHandle = batcher(800, async (nip05s: string[]) => {
|
||||||
const handlesByNip05 = new Map<string, Handle>()
|
const handlesByNip05 = new Map<string, Handle>()
|
||||||
|
|
||||||
// Use dufflepud if we it's set up to protect user privacy, otherwise fetch directly
|
// Use dufflepud if we it's set up to protect user privacy, otherwise fetch directly
|
||||||
if (base) {
|
if (appContext.dufflepudUrl) {
|
||||||
const res: any = await tryCatch(
|
const res: any = await tryCatch(
|
||||||
async () => await postJson(`${base}/handle/info`, {handles: nip05s}),
|
async () => await postJson(`${appContext.dufflepudUrl}/handle/info`, {handles: nip05s}),
|
||||||
)
|
)
|
||||||
|
|
||||||
for (const {handle: nip05, info} of res?.data || []) {
|
for (const {handle: nip05, info} of res?.data || []) {
|
||||||
@@ -72,50 +79,40 @@ export const fetchHandles = async (nip05s: string[]) => {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return handlesByNip05
|
return nip05s.map(nip05 => {
|
||||||
}
|
const info = handlesByNip05.get(nip05)
|
||||||
|
|
||||||
export const {
|
if (info) {
|
||||||
indexStore: handlesByNip05,
|
return {...info, nip05}
|
||||||
deriveItem: deriveHandle,
|
|
||||||
loadItem: loadHandle,
|
|
||||||
onItem: onHandle,
|
|
||||||
} = collection({
|
|
||||||
name: "handles",
|
|
||||||
store: handles,
|
|
||||||
getKey: (handle: Handle) => handle.nip05,
|
|
||||||
load: batcher(800, async (nip05s: string[]) => {
|
|
||||||
const fresh = await fetchHandles(uniq(nip05s))
|
|
||||||
const stale = handlesByNip05.get()
|
|
||||||
|
|
||||||
for (const nip05 of nip05s) {
|
|
||||||
const newHandle = fresh.get(nip05)
|
|
||||||
|
|
||||||
if (newHandle) {
|
|
||||||
stale.set(nip05, {...newHandle, nip05})
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
})
|
||||||
handles.set(Array.from(stale.values()))
|
|
||||||
|
|
||||||
return nip05s
|
|
||||||
}),
|
|
||||||
})
|
})
|
||||||
|
|
||||||
export const deriveHandleForPubkey = (pubkey: string, relays: string[] = []) =>
|
export const forceLoadHandle = makeForceLoadItem(fetchHandle, getHandle)
|
||||||
derived([handlesByNip05, deriveProfile(pubkey, relays)], ([$handlesByNip05, $profile]) => {
|
|
||||||
if (!$profile?.nip05) {
|
|
||||||
return undefined
|
|
||||||
}
|
|
||||||
|
|
||||||
loadHandle($profile.nip05)
|
export const loadHandle = makeLoadItem(fetchHandle, getHandle)
|
||||||
|
|
||||||
|
export const deriveHandle = makeDeriveItem(handlesByNip05, loadHandle)
|
||||||
|
|
||||||
|
export const loadHandleForPubkey = async (pubkey: string, relays: string[] = []) => {
|
||||||
|
const $profile = await loadProfile(pubkey, relays)
|
||||||
|
|
||||||
|
return $profile?.nip05 ? loadHandle($profile.nip05) : undefined
|
||||||
|
}
|
||||||
|
|
||||||
|
export const deriveHandleForPubkey = (pubkey: string, relays: string[] = []) => {
|
||||||
|
loadHandleForPubkey(pubkey, relays)
|
||||||
|
|
||||||
|
return derived([handlesByNip05, deriveProfile(pubkey, relays)], ([$handlesByNip05, $profile]) => {
|
||||||
|
if (!$profile?.nip05) return undefined
|
||||||
|
|
||||||
const handle = $handlesByNip05.get($profile.nip05)
|
const handle = $handlesByNip05.get($profile.nip05)
|
||||||
|
|
||||||
if (handle?.pubkey === pubkey) {
|
if (handle?.pubkey !== pubkey) return undefined
|
||||||
return handle
|
|
||||||
}
|
return handle
|
||||||
})
|
})
|
||||||
|
}
|
||||||
|
|
||||||
export const displayNip05 = (nip05: string) =>
|
export const displayNip05 = (nip05: string) =>
|
||||||
nip05?.startsWith("_@") ? last(nip05.split("@")) : nip05
|
nip05?.startsWith("_@") ? last(nip05.split("@")) : nip05
|
||||||
|
|||||||
@@ -1,24 +0,0 @@
|
|||||||
import {INBOX_RELAYS, asDecryptedEvent, readList} from "@welshman/util"
|
|
||||||
import {TrustedEvent, PublishedList} from "@welshman/util"
|
|
||||||
import {deriveItemsByKey, deriveItems, makeForceLoadItem, makeLoadItem, makeDeriveItem, getter} from "@welshman/store"
|
|
||||||
import {repository} from "./core.js"
|
|
||||||
import {makeOutboxLoader} from "./relaySelections.js"
|
|
||||||
|
|
||||||
export const inboxRelaySelectionsByPubkey = deriveItemsByKey({
|
|
||||||
repository,
|
|
||||||
eventToItem: (event: TrustedEvent) => readList(asDecryptedEvent(event)),
|
|
||||||
filters: [{kinds: [INBOX_RELAYS]}],
|
|
||||||
getKey: inboxRelaySelections => inboxRelaySelections.event.pubkey,
|
|
||||||
})
|
|
||||||
|
|
||||||
export const inboxRelaySelections = deriveItems(inboxRelaySelectionsByPubkey)
|
|
||||||
|
|
||||||
export const getInboxRelaySelectionsByPubkey = getter(inboxRelaySelectionsByPubkey)
|
|
||||||
|
|
||||||
export const getInboxRelaySelections = (pubkey: string) => getInboxRelaySelectionsByPubkey().get(pubkey)
|
|
||||||
|
|
||||||
export const forceLoadInboxRelaySelections = makeForceLoadItem(makeOutboxLoader(INBOX_RELAYS), getInboxRelaySelections)
|
|
||||||
|
|
||||||
export const loadInboxRelaySelections = makeLoadItem(makeOutboxLoader(INBOX_RELAYS), getInboxRelaySelections)
|
|
||||||
|
|
||||||
export const deriveInboxRelaySelections = makeDeriveItem(inboxRelaySelectionsByPubkey, loadInboxRelaySelections)
|
|
||||||
+12
-12
@@ -11,8 +11,8 @@ export * from "./profiles.js"
|
|||||||
export * from "./pins.js"
|
export * from "./pins.js"
|
||||||
export * from "./relays.js"
|
export * from "./relays.js"
|
||||||
export * from "./relayStats.js"
|
export * from "./relayStats.js"
|
||||||
export * from "./relaySelections.js"
|
export * from "./relayLists.js"
|
||||||
export * from "./inboxRelaySelections.js"
|
export * from "./messagingRelayLists.js"
|
||||||
export * from "./search.js"
|
export * from "./search.js"
|
||||||
export * from "./session.js"
|
export * from "./session.js"
|
||||||
export * from "./sync.js"
|
export * from "./sync.js"
|
||||||
@@ -37,10 +37,10 @@ import {routerContext} from "@welshman/router"
|
|||||||
import {Pool, SocketEvent, isRelayEvent, netContext} from "@welshman/net"
|
import {Pool, SocketEvent, isRelayEvent, netContext} from "@welshman/net"
|
||||||
import {pubkey, unwrapAndStore} from "./session.js"
|
import {pubkey, unwrapAndStore} from "./session.js"
|
||||||
import {repository, tracker} from "./core.js"
|
import {repository, tracker} from "./core.js"
|
||||||
import {relays, loadRelay} from "./relays.js"
|
import {getRelays, loadRelay} from "./relays.js"
|
||||||
import {trackRelayStats, getRelayQuality} from "./relayStats.js"
|
import {trackRelayStats, getRelayQuality} from "./relayStats.js"
|
||||||
import {relaySelectionsByPubkey} from "./relaySelections.js"
|
import {deriveRelayList, getRelayList} from "./relayLists.js"
|
||||||
import {inboxRelaySelectionsByPubkey} from "./inboxRelaySelections.js"
|
import {deriveMessagingRelayList, getMessagingRelayList} from "./messagingRelayLists.js"
|
||||||
|
|
||||||
// Sync relays with our database
|
// Sync relays with our database
|
||||||
|
|
||||||
@@ -73,7 +73,7 @@ Pool.get().subscribe(socket => {
|
|||||||
|
|
||||||
const _relayGetter = (fn?: (relay: RelayProfile) => any) =>
|
const _relayGetter = (fn?: (relay: RelayProfile) => any) =>
|
||||||
throttleWithValue(200, () => {
|
throttleWithValue(200, () => {
|
||||||
let _relays = relays.get()
|
let _relays = getRelays()
|
||||||
|
|
||||||
if (fn) {
|
if (fn) {
|
||||||
_relays = _relays.filter(fn)
|
_relays = _relays.filter(fn)
|
||||||
@@ -85,14 +85,14 @@ const _relayGetter = (fn?: (relay: RelayProfile) => any) =>
|
|||||||
})
|
})
|
||||||
|
|
||||||
export const getPubkeyRelays = (pubkey: string, mode?: RelayMode) =>
|
export const getPubkeyRelays = (pubkey: string, mode?: RelayMode) =>
|
||||||
mode === RelayMode.Inbox
|
mode === RelayMode.Messages
|
||||||
? getRelaysFromList(inboxRelaySelectionsByPubkey.get().get(pubkey))
|
? getRelaysFromList(getMessagingRelayList(pubkey))
|
||||||
: getRelaysFromList(relaySelectionsByPubkey.get().get(pubkey), mode)
|
: getRelaysFromList(getRelayList(pubkey), mode)
|
||||||
|
|
||||||
export const derivePubkeyRelays = (pubkey: string, mode?: RelayMode) =>
|
export const derivePubkeyRelays = (pubkey: string, mode?: RelayMode) =>
|
||||||
mode === RelayMode.Inbox
|
mode === RelayMode.Messages
|
||||||
? derived(inboxRelaySelectionsByPubkey, $m => getRelaysFromList($m.get(pubkey)))
|
? derived(deriveMessagingRelayList(pubkey), list => getRelaysFromList(list))
|
||||||
: derived(relaySelectionsByPubkey, $m => getRelaysFromList($m.get(pubkey), mode))
|
: derived(deriveRelayList(pubkey), list => getRelaysFromList(list, mode))
|
||||||
|
|
||||||
routerContext.getUserPubkey = () => pubkey.get()
|
routerContext.getUserPubkey = () => pubkey.get()
|
||||||
routerContext.getPubkeyRelays = getPubkeyRelays
|
routerContext.getPubkeyRelays = getPubkeyRelays
|
||||||
|
|||||||
@@ -0,0 +1,24 @@
|
|||||||
|
import {MESSAGING_RELAYS, asDecryptedEvent, readList} from "@welshman/util"
|
||||||
|
import {TrustedEvent, PublishedList} from "@welshman/util"
|
||||||
|
import {deriveItemsByKey, deriveItems, makeForceLoadItem, makeLoadItem, makeDeriveItem, getter} from "@welshman/store"
|
||||||
|
import {repository} from "./core.js"
|
||||||
|
import {makeOutboxLoader} from "./relayLists.js"
|
||||||
|
|
||||||
|
export const messagingRelayListsByPubkey = deriveItemsByKey({
|
||||||
|
repository,
|
||||||
|
eventToItem: (event: TrustedEvent) => readList(asDecryptedEvent(event)),
|
||||||
|
filters: [{kinds: [MESSAGING_RELAYS]}],
|
||||||
|
getKey: messagingRelayLists => messagingRelayLists.event.pubkey,
|
||||||
|
})
|
||||||
|
|
||||||
|
export const messagingRelayLists = deriveItems(messagingRelayListsByPubkey)
|
||||||
|
|
||||||
|
export const getMessagingRelayListsByPubkey = getter(messagingRelayListsByPubkey)
|
||||||
|
|
||||||
|
export const getMessagingRelayList = (pubkey: string) => getMessagingRelayListsByPubkey().get(pubkey)
|
||||||
|
|
||||||
|
export const forceLoadMessagingRelayList = makeForceLoadItem(makeOutboxLoader(MESSAGING_RELAYS), getMessagingRelayList)
|
||||||
|
|
||||||
|
export const loadMessagingRelayList = makeLoadItem(makeOutboxLoader(MESSAGING_RELAYS), getMessagingRelayList)
|
||||||
|
|
||||||
|
export const deriveMessagingRelayList = makeDeriveItem(messagingRelayListsByPubkey, loadMessagingRelayList)
|
||||||
@@ -3,9 +3,9 @@ import {TrustedEvent, PublishedList} from "@welshman/util"
|
|||||||
import {deriveItemsByKey, deriveItems, makeForceLoadItem, makeLoadItem, makeDeriveItem, getter} from "@welshman/store"
|
import {deriveItemsByKey, deriveItems, makeForceLoadItem, makeLoadItem, makeDeriveItem, getter} from "@welshman/store"
|
||||||
import {repository} from "./core.js"
|
import {repository} from "./core.js"
|
||||||
import {ensurePlaintext} from "./plaintext.js"
|
import {ensurePlaintext} from "./plaintext.js"
|
||||||
import {makeOutboxLoader} from "./relaySelections.js"
|
import {makeOutboxLoader} from "./relayLists.js"
|
||||||
|
|
||||||
export const mutesByPubkey = deriveItemsByKey({
|
export const muteListsByPubkey = deriveItemsByKey<PublishedList>({
|
||||||
repository,
|
repository,
|
||||||
eventToItem: async (event: TrustedEvent) =>
|
eventToItem: async (event: TrustedEvent) =>
|
||||||
readList(
|
readList(
|
||||||
@@ -17,14 +17,16 @@ export const mutesByPubkey = deriveItemsByKey({
|
|||||||
getKey: mute => mute.event.pubkey,
|
getKey: mute => mute.event.pubkey,
|
||||||
})
|
})
|
||||||
|
|
||||||
export const mutes = deriveItems(mutesByPubkey)
|
export const muteLists = deriveItems(muteListsByPubkey)
|
||||||
|
|
||||||
export const getMutesByPubkey = getter(mutesByPubkey)
|
export const getMuteListsByPubkey = getter(muteListsByPubkey)
|
||||||
|
|
||||||
export const getMutes = (pubkey: string) => getMutesByPubkey().get(pubkey)
|
export const getMuteLists = getter(muteLists)
|
||||||
|
|
||||||
export const forceLoadMutes = makeForceLoadItem(makeOutboxLoader(MUTES), getMutes)
|
export const getMuteList = (pubkey: string) => getMuteListsByPubkey().get(pubkey)
|
||||||
|
|
||||||
export const loadMutes = makeLoadItem(makeOutboxLoader(MUTES), getMutes)
|
export const forceLoadMuteList = makeForceLoadItem(makeOutboxLoader(MUTES), getMuteList)
|
||||||
|
|
||||||
export const deriveMutes = makeDeriveItem(mutesByPubkey, loadMutes)
|
export const loadMuteList = makeLoadItem(makeOutboxLoader(MUTES), getMuteList)
|
||||||
|
|
||||||
|
export const deriveMuteList = makeDeriveItem(muteListsByPubkey, loadMuteList)
|
||||||
|
|||||||
@@ -2,23 +2,25 @@ import {PINS, asDecryptedEvent, readList} from "@welshman/util"
|
|||||||
import {TrustedEvent, PublishedList} from "@welshman/util"
|
import {TrustedEvent, PublishedList} from "@welshman/util"
|
||||||
import {deriveItemsByKey, deriveItems, makeForceLoadItem, makeLoadItem, makeDeriveItem, getter} from "@welshman/store"
|
import {deriveItemsByKey, deriveItems, makeForceLoadItem, makeLoadItem, makeDeriveItem, getter} from "@welshman/store"
|
||||||
import {repository} from "./core.js"
|
import {repository} from "./core.js"
|
||||||
import {makeOutboxLoader} from "./relaySelections.js"
|
import {makeOutboxLoader} from "./relayLists.js"
|
||||||
|
|
||||||
export const pinsByPubkey = deriveItemsByKey({
|
export const pinListsByPubkey = deriveItemsByKey({
|
||||||
repository,
|
repository,
|
||||||
eventToItem: (event: TrustedEvent) => readList(asDecryptedEvent(event)),
|
eventToItem: (event: TrustedEvent) => readList(asDecryptedEvent(event)),
|
||||||
filters: [{kinds: [PINS]}],
|
filters: [{kinds: [PINS]}],
|
||||||
getKey: pins => pins.event.pubkey,
|
getKey: pins => pins.event.pubkey,
|
||||||
})
|
})
|
||||||
|
|
||||||
export const pins = deriveItems(pinsByPubkey)
|
export const pinLists = deriveItems(pinListsByPubkey)
|
||||||
|
|
||||||
export const getPinsByPubkey = getter(pinsByPubkey)
|
export const getPinListsByPubkey = getter(pinListsByPubkey)
|
||||||
|
|
||||||
export const getPins = (pubkey: string) => getPinsByPubkey().get(pubkey)
|
export const getPinLists = getter(pinLists)
|
||||||
|
|
||||||
export const forceLoadPins = makeForceLoadItem(makeOutboxLoader(PINS), getPins)
|
export const getPinList = (pubkey: string) => getPinListsByPubkey().get(pubkey)
|
||||||
|
|
||||||
export const loadPins = makeLoadItem(makeOutboxLoader(PINS), getPins)
|
export const forceLoadPinList = makeForceLoadItem(makeOutboxLoader(PINS), getPinList)
|
||||||
|
|
||||||
export const derivePins = makeDeriveItem(pinsByPubkey, loadPins)
|
export const loadPinList = makeLoadItem(makeOutboxLoader(PINS), getPinList)
|
||||||
|
|
||||||
|
export const derivePinList = makeDeriveItem(pinListsByPubkey, loadPinList)
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ import {derived, readable} from "svelte/store"
|
|||||||
import {readProfile, displayProfile, displayPubkey, PROFILE} from "@welshman/util"
|
import {readProfile, displayProfile, displayPubkey, PROFILE} from "@welshman/util"
|
||||||
import {deriveItemsByKey, deriveItems, makeForceLoadItem, makeLoadItem, makeDeriveItem, getter} from "@welshman/store"
|
import {deriveItemsByKey, deriveItems, makeForceLoadItem, makeLoadItem, makeDeriveItem, getter} from "@welshman/store"
|
||||||
import {repository} from "./core.js"
|
import {repository} from "./core.js"
|
||||||
import {makeOutboxLoaderWithIndexers} from "./relaySelections.js"
|
import {makeOutboxLoaderWithIndexers} from "./relayLists.js"
|
||||||
|
|
||||||
export const profilesByPubkey = deriveItemsByKey({
|
export const profilesByPubkey = deriveItemsByKey({
|
||||||
repository,
|
repository,
|
||||||
@@ -15,6 +15,8 @@ export const profiles = deriveItems(profilesByPubkey)
|
|||||||
|
|
||||||
export const getProfilesByPubkey = getter(profilesByPubkey)
|
export const getProfilesByPubkey = getter(profilesByPubkey)
|
||||||
|
|
||||||
|
export const getProfiles = getter(profiles)
|
||||||
|
|
||||||
export const getProfile = (pubkey: string) => getProfilesByPubkey().get(pubkey)
|
export const getProfile = (pubkey: string) => getProfilesByPubkey().get(pubkey)
|
||||||
|
|
||||||
export const forceLoadProfile = makeForceLoadItem(makeOutboxLoaderWithIndexers(PROFILE), getProfile)
|
export const forceLoadProfile = makeForceLoadItem(makeOutboxLoaderWithIndexers(PROFILE), getProfile)
|
||||||
|
|||||||
@@ -41,21 +41,23 @@ export const makeOutboxLoaderWithIndexers =
|
|||||||
])
|
])
|
||||||
}
|
}
|
||||||
|
|
||||||
export const relaySelectionsByPubkey = deriveItemsByKey({
|
export const relayListsByPubkey = deriveItemsByKey({
|
||||||
repository,
|
repository,
|
||||||
eventToItem: (event: TrustedEvent) => readList(asDecryptedEvent(event)),
|
eventToItem: (event: TrustedEvent) => readList(asDecryptedEvent(event)),
|
||||||
filters: [{kinds: [RELAYS]}],
|
filters: [{kinds: [RELAYS]}],
|
||||||
getKey: relaySelections => relaySelections.event.pubkey,
|
getKey: relayList => relayList.event.pubkey,
|
||||||
})
|
})
|
||||||
|
|
||||||
export const relaySelections = deriveItems(relaySelectionsByPubkey)
|
export const relayLists = deriveItems(relayListsByPubkey)
|
||||||
|
|
||||||
export const getRelaySelectionsByPubkey = getter(relaySelectionsByPubkey)
|
export const getRelayListsByPubkey = getter(relayListsByPubkey)
|
||||||
|
|
||||||
export const getRelaySelections = (pubkey: string) => getRelaySelectionsByPubkey().get(pubkey)
|
export const getRelayLists = getter(relayLists)
|
||||||
|
|
||||||
export const forceLoadRelaySelections = makeForceLoadItem(makeOutboxLoaderWithIndexers(RELAYS), getRelaySelections)
|
export const getRelayList = (pubkey: string) => getRelayListsByPubkey().get(pubkey)
|
||||||
|
|
||||||
export const loadRelaySelections = makeLoadItem(makeOutboxLoaderWithIndexers(RELAYS), getRelaySelections)
|
export const forceLoadRelayList = makeForceLoadItem(makeOutboxLoaderWithIndexers(RELAYS), getRelayList)
|
||||||
|
|
||||||
export const deriveRelaySelections = makeDeriveItem(relaySelectionsByPubkey, loadRelaySelections)
|
export const loadRelayList = makeLoadItem(makeOutboxLoaderWithIndexers(RELAYS), getRelayList)
|
||||||
|
|
||||||
|
export const deriveRelayList = makeDeriveItem(relayListsByPubkey, loadRelayList)
|
||||||
+41
-67
@@ -8,99 +8,73 @@ import {
|
|||||||
fetchJson,
|
fetchJson,
|
||||||
postJson,
|
postJson,
|
||||||
Maybe,
|
Maybe,
|
||||||
|
noop,
|
||||||
} from "@welshman/lib"
|
} from "@welshman/lib"
|
||||||
import {withGetter} from "@welshman/store"
|
import {withGetter} from "@welshman/store"
|
||||||
import {RelayProfile} from "@welshman/util"
|
import {RelayProfile} from "@welshman/util"
|
||||||
import {normalizeRelayUrl, displayRelayUrl, displayRelayProfile, isRelayUrl} from "@welshman/util"
|
import {normalizeRelayUrl, displayRelayUrl, displayRelayProfile, isRelayUrl} from "@welshman/util"
|
||||||
import {collection} from "@welshman/store"
|
import {getter, deriveItems, makeForceLoadItem, makeLoadItem, makeDeriveItem} from "@welshman/store"
|
||||||
import {appContext} from "./context.js"
|
import {appContext} from "./context.js"
|
||||||
|
|
||||||
export const relays = withGetter(writable<RelayProfile[]>([]))
|
export const relaysByUrl = writable(new Map<string, RelayProfile>())
|
||||||
|
|
||||||
export const fetchRelayProfileDirectly = async (url: string): Promise<Maybe<RelayProfile>> => {
|
export const relays = deriveItems(relaysByUrl)
|
||||||
|
|
||||||
|
export const getRelaysByUrl = getter(relaysByUrl)
|
||||||
|
|
||||||
|
export const getRelays = getter(relays)
|
||||||
|
|
||||||
|
export const getRelay = (url: string) => getRelaysByUrl().get(url)
|
||||||
|
|
||||||
|
export const fetchRelayDirectly = async (url: string): Promise<Maybe<RelayProfile>> => {
|
||||||
try {
|
try {
|
||||||
return fetchJson(url.replace(/^ws/, "http"), {
|
const json = fetchJson(url.replace(/^ws/, "http"), {
|
||||||
headers: {
|
headers: {
|
||||||
Accept: "application/nostr+json",
|
Accept: "application/nostr+json",
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
|
|
||||||
|
if (json) {
|
||||||
|
return {...json, url}
|
||||||
|
}
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
// pass
|
// pass
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export const fetchRelayProfilesDirectly = async (
|
export const fetchRelayUsingProxy = batcher(800, async (urls: string[]) => {
|
||||||
urls: string[],
|
// Handle a race condition edge case where dufflepud url changes under us
|
||||||
): Promise<Map<string, RelayProfile>> =>
|
if (!appContext.dufflepudUrl) {
|
||||||
indexBy(
|
return urls.map(noop)
|
||||||
prop("url"),
|
|
||||||
removeUndefined(
|
|
||||||
await Promise.all(
|
|
||||||
urls.map(async url => {
|
|
||||||
const profile = await fetchRelayProfileDirectly(url)
|
|
||||||
|
|
||||||
if (profile) {
|
|
||||||
return {...profile, url}
|
|
||||||
}
|
|
||||||
}),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
)
|
|
||||||
|
|
||||||
export const fetchRelayProfilesUsingProxy = async (
|
|
||||||
proxy: string,
|
|
||||||
urls: string[],
|
|
||||||
): Promise<Map<string, RelayProfile>> => {
|
|
||||||
const profilesByUrl = new Map<string, RelayProfile>()
|
|
||||||
const res: any = await postJson(`${proxy}/relay/info`, {urls})
|
|
||||||
|
|
||||||
for (const {url, info} of res?.data || []) {
|
|
||||||
profilesByUrl.set(url, info)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return profilesByUrl
|
const res: any = await postJson(`${appContext.dufflepudUrl}/relay/info`, {urls})
|
||||||
}
|
const relaysByUrl = new Map<string, RelayProfile>()
|
||||||
|
|
||||||
export const fetchRelayProfiles = (urls: string[]) =>
|
for (const {url, info} of res?.data || []) {
|
||||||
appContext.dufflepudUrl
|
relaysByUrl.set(url, info)
|
||||||
? fetchRelayProfilesUsingProxy(appContext.dufflepudUrl, urls)
|
}
|
||||||
: fetchRelayProfilesDirectly(urls)
|
|
||||||
|
|
||||||
export const {
|
return urls.map(url => {
|
||||||
indexStore: relaysByUrl,
|
const info = relaysByUrl.get(url)
|
||||||
deriveItem: deriveRelay,
|
|
||||||
loadItem: loadRelay,
|
|
||||||
onItem: onRelay,
|
|
||||||
} = collection({
|
|
||||||
name: "relays",
|
|
||||||
store: relays,
|
|
||||||
getKey: (relay: RelayProfile) => relay.url,
|
|
||||||
load: batcher(800, async (rawUrls: string[]) => {
|
|
||||||
const urls = rawUrls.map(normalizeRelayUrl)
|
|
||||||
const fresh = await fetchRelayProfiles(uniq(urls))
|
|
||||||
const stale = relaysByUrl.get()
|
|
||||||
|
|
||||||
for (const url of urls) {
|
if (info) {
|
||||||
const profile = fresh.get(url)
|
return {...info, url}
|
||||||
|
|
||||||
if (!url || !isRelayUrl(url)) {
|
|
||||||
console.warn(`Attempted to load invalid relay url: ${url}`)
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
if (profile) {
|
|
||||||
stale.set(url, {...profile, url})
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
})
|
||||||
relays.set(Array.from(stale.values()))
|
|
||||||
|
|
||||||
return urls
|
|
||||||
}),
|
|
||||||
})
|
})
|
||||||
|
|
||||||
|
export const fetchRelay = (url: string) =>
|
||||||
|
appContext.dufflepudUrl ? fetchRelayUsingProxy(url) : fetchRelayDirectly(url)
|
||||||
|
|
||||||
|
export const forceLoadRelay = makeForceLoadItem(fetchRelay, getRelay)
|
||||||
|
|
||||||
|
export const loadRelay = makeLoadItem(fetchRelay, getRelay)
|
||||||
|
|
||||||
|
export const deriveRelay = makeDeriveItem(relaysByUrl, loadRelay)
|
||||||
|
|
||||||
export const displayRelayByPubkey = (url: string) =>
|
export const displayRelayByPubkey = (url: string) =>
|
||||||
displayRelayProfile(relaysByUrl.get().get(url), displayRelayUrl(url))
|
displayRelayProfile(getRelay(url), displayRelayUrl(url))
|
||||||
|
|
||||||
export const deriveRelayDisplay = (url: string) =>
|
export const deriveRelayDisplay = (url: string) =>
|
||||||
derived(deriveRelay(url), $relay => displayRelayProfile($relay, displayRelayUrl(url)))
|
derived(deriveRelay(url), $relay => displayRelayProfile($relay, displayRelayUrl(url)))
|
||||||
|
|||||||
@@ -2,13 +2,13 @@ import type {Filter} from "@welshman/util"
|
|||||||
import {isSignedEvent, SignedEvent} from "@welshman/util"
|
import {isSignedEvent, SignedEvent} from "@welshman/util"
|
||||||
import {push as basePush, pull as basePull, publishOne, requestOne} from "@welshman/net"
|
import {push as basePush, pull as basePull, publishOne, requestOne} from "@welshman/net"
|
||||||
import {repository} from "./core.js"
|
import {repository} from "./core.js"
|
||||||
import {relaysByUrl} from "./relays.js"
|
import {getRelay} from "./relays.js"
|
||||||
|
|
||||||
const query = (filters: Filter[]) =>
|
const query = (filters: Filter[]) =>
|
||||||
repository.query(filters, {shouldSort: filters.every(f => f.limit === undefined)})
|
repository.query(filters, {shouldSort: filters.every(f => f.limit === undefined)})
|
||||||
|
|
||||||
export const hasNegentropy = (url: string) => {
|
export const hasNegentropy = (url: string) => {
|
||||||
const relay = relaysByUrl.get().get(url)
|
const relay = getRelay(url)
|
||||||
|
|
||||||
if (relay?.negentropy) return true
|
if (relay?.negentropy) return true
|
||||||
if (relay?.supported_nips?.includes?.(77)) return true
|
if (relay?.supported_nips?.includes?.(77)) return true
|
||||||
|
|||||||
+34
-16
@@ -1,5 +1,7 @@
|
|||||||
import {inc, throttle} from "@welshman/lib"
|
import {readable} from 'svelte/store'
|
||||||
import {custom} from "@welshman/store"
|
import {on, call} from "@welshman/lib"
|
||||||
|
import {deriveItems} from "@welshman/store"
|
||||||
|
import {getTopicTagValues} from "@welshman/util"
|
||||||
import {repository} from "./core.js"
|
import {repository} from "./core.js"
|
||||||
|
|
||||||
export type Topic = {
|
export type Topic = {
|
||||||
@@ -7,25 +9,41 @@ export type Topic = {
|
|||||||
count: number
|
count: number
|
||||||
}
|
}
|
||||||
|
|
||||||
export const topics = custom<Topic[]>(setter => {
|
export const topicsByName = call(() => {
|
||||||
const getTopics = () => {
|
const topicsByName = new Map<string, Topic>()
|
||||||
const topics = new Map<string, number>()
|
|
||||||
for (const tagString of repository.eventsByTag.keys()) {
|
|
||||||
if (tagString.startsWith("t:")) {
|
|
||||||
const topic = tagString.slice(2).toLowerCase()
|
|
||||||
|
|
||||||
topics.set(topic, inc(topics.get(topic)))
|
const addTopic = (name: string) => {
|
||||||
}
|
const topic = topicsByName.get(name)
|
||||||
|
|
||||||
|
if (topic) {
|
||||||
|
topic.count++
|
||||||
|
} else {
|
||||||
|
topicsByName.set(name, {name, count: 0})
|
||||||
}
|
}
|
||||||
|
|
||||||
return Array.from(topics.entries()).map(([name, count]) => ({name, count}))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
setter(getTopics())
|
for (const tagString of repository.eventsByTag.keys()) {
|
||||||
|
if (tagString.startsWith("t:")) {
|
||||||
|
addTopic(tagString.slice(2).toLowerCase())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
const onUpdate = throttle(3000, () => setter(getTopics()))
|
return readable<Map<string, Topic>>(topicsByName, set => {
|
||||||
|
return on(repository, 'update', ({added}) => {
|
||||||
|
let dirty = false
|
||||||
|
|
||||||
repository.on("update", onUpdate)
|
for (const event of added) {
|
||||||
|
for (const name of getTopicTagValues(event.tags)) {
|
||||||
|
addTopic(name)
|
||||||
|
dirty = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return () => repository.off("update", onUpdate)
|
if (dirty) {
|
||||||
|
set(topicsByName)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
|
export const topics = deriveItems(topicsByName)
|
||||||
|
|||||||
+38
-31
@@ -1,13 +1,13 @@
|
|||||||
import {derived, Readable} from "svelte/store"
|
import {derived, Readable} from "svelte/store"
|
||||||
import {withGetter, memoized} from "@welshman/store"
|
import {withGetter, memoized} from "@welshman/store"
|
||||||
import {pubkey} from "./session.js"
|
import {pubkey} from "./session.js"
|
||||||
import {profilesByPubkey, loadProfile} from "./profiles.js"
|
import {profilesByPubkey, forceLoadProfile, loadProfile} from "./profiles.js"
|
||||||
import {followsByPubkey, loadFollows} from "./follows.js"
|
import {followListsByPubkey, forceLoadFollowList, loadFollowList} from "./follows.js"
|
||||||
import {loadPins, pinsByPubkey} from "./pins.js"
|
import {pinListsByPubkey, forceLoadPinList, loadPinList} from "./pins.js"
|
||||||
import {mutesByPubkey, loadMutes} from "./mutes.js"
|
import {muteListsByPubkey, forceLoadMuteList, loadMuteList} from "./mutes.js"
|
||||||
import {blossomServersByPubkey, loadBlossomServers} from "./blossom.js"
|
import {blossomServerListsByPubkey, forceLoadBlossomServerList, loadBlossomServerList} from "./blossom.js"
|
||||||
import {relaySelectionsByPubkey, loadRelaySelections} from "./relaySelections.js"
|
import {relayListsByPubkey, forceLoadRelayList, loadRelayList} from "./relayLists.js"
|
||||||
import {inboxRelaySelectionsByPubkey, loadInboxRelaySelections} from "./inboxRelaySelections.js"
|
import {messagingRelayListsByPubkey, forceLoadMessagingRelayList, loadMessagingRelayList} from "./messagingRelayLists.js"
|
||||||
import {wotGraph} from "./wot.js"
|
import {wotGraph} from "./wot.js"
|
||||||
|
|
||||||
export type UserDataLoader = (pubkey: string, relays?: string[], force?: boolean) => unknown
|
export type UserDataLoader = (pubkey: string, relays?: string[], force?: boolean) => unknown
|
||||||
@@ -45,49 +45,56 @@ export const userProfile = makeUserData({
|
|||||||
loadItem: loadProfile,
|
loadItem: loadProfile,
|
||||||
})
|
})
|
||||||
|
|
||||||
|
export const forceLoadUserProfile = makeUserLoader(forceLoadProfile)
|
||||||
export const loadUserProfile = makeUserLoader(loadProfile)
|
export const loadUserProfile = makeUserLoader(loadProfile)
|
||||||
|
|
||||||
export const userFollows = makeUserData({
|
export const userFollowList = makeUserData({
|
||||||
mapStore: followsByPubkey,
|
mapStore: followListsByPubkey,
|
||||||
loadItem: loadFollows,
|
loadItem: loadFollowList,
|
||||||
})
|
})
|
||||||
|
|
||||||
export const loadUserFollows = makeUserLoader(loadFollows)
|
export const forceLoadUserFollowList = makeUserLoader(forceLoadFollowList)
|
||||||
|
export const loadUserFollowList = makeUserLoader(loadFollowList)
|
||||||
|
|
||||||
export const userMutes = makeUserData({
|
export const userMuteList = makeUserData({
|
||||||
mapStore: mutesByPubkey,
|
mapStore: muteListsByPubkey,
|
||||||
loadItem: loadMutes,
|
loadItem: loadMuteList,
|
||||||
})
|
})
|
||||||
|
|
||||||
export const loadUserMutes = makeUserLoader(loadMutes)
|
export const forceLoadUserMuteList = makeUserLoader(forceLoadMuteList)
|
||||||
|
export const loadUserMuteList = makeUserLoader(loadMuteList)
|
||||||
|
|
||||||
export const userPins = makeUserData({
|
export const userPinList = makeUserData({
|
||||||
mapStore: pinsByPubkey,
|
mapStore: pinListsByPubkey,
|
||||||
loadItem: loadPins,
|
loadItem: loadPinList,
|
||||||
})
|
})
|
||||||
|
|
||||||
export const loadUserPins = makeUserLoader(loadPins)
|
export const forceLoadUserPinList = makeUserLoader(forceLoadPinList)
|
||||||
|
export const loadUserPinList = makeUserLoader(loadPinList)
|
||||||
|
|
||||||
export const userRelaySelections = makeUserData({
|
export const userRelayList = makeUserData({
|
||||||
mapStore: relaySelectionsByPubkey,
|
mapStore: relayListsByPubkey,
|
||||||
loadItem: loadRelaySelections,
|
loadItem: loadRelayList,
|
||||||
})
|
})
|
||||||
|
|
||||||
export const loadUserRelaySelections = makeUserLoader(loadRelaySelections)
|
export const forceLoadUserRelayList = makeUserLoader(forceLoadRelayList)
|
||||||
|
export const loadUserRelayList = makeUserLoader(loadRelayList)
|
||||||
|
|
||||||
export const userInboxRelaySelections = makeUserData({
|
export const userMessagingRelayList = makeUserData({
|
||||||
mapStore: inboxRelaySelectionsByPubkey,
|
mapStore: messagingRelayListsByPubkey,
|
||||||
loadItem: loadInboxRelaySelections,
|
loadItem: loadMessagingRelayList,
|
||||||
})
|
})
|
||||||
|
|
||||||
export const loadUserInboxRelaySelections = makeUserLoader(loadInboxRelaySelections)
|
export const forceLoadUserMessagingRelayList = makeUserLoader(forceLoadMessagingRelayList)
|
||||||
|
export const loadUserMessagingRelayList = makeUserLoader(loadMessagingRelayList)
|
||||||
|
|
||||||
export const userBlossomServers = makeUserData({
|
export const userBlossomServerList = makeUserData({
|
||||||
mapStore: blossomServersByPubkey,
|
mapStore: blossomServerListsByPubkey,
|
||||||
loadItem: loadBlossomServers,
|
loadItem: loadBlossomServerList,
|
||||||
})
|
})
|
||||||
|
|
||||||
export const loadUserBlossomServers = makeUserLoader(loadBlossomServers)
|
export const forceLoadUserBlossomServerList = makeUserLoader(forceLoadBlossomServerList)
|
||||||
|
export const loadUserBlossomServerList = makeUserLoader(loadBlossomServerList)
|
||||||
|
|
||||||
export const getUserWotScore = (tpk: string) => wotGraph.get().get(tpk) || 0
|
export const getUserWotScore = (tpk: string) => wotGraph.get().get(tpk) || 0
|
||||||
|
|
||||||
|
|||||||
@@ -3,14 +3,14 @@ import {max, throttle, addToMapKey, inc, dec} from "@welshman/lib"
|
|||||||
import {getListTags, getPubkeyTagValues} from "@welshman/util"
|
import {getListTags, getPubkeyTagValues} from "@welshman/util"
|
||||||
import {throttled, withGetter} from "@welshman/store"
|
import {throttled, withGetter} from "@welshman/store"
|
||||||
import {pubkey} from "./session.js"
|
import {pubkey} from "./session.js"
|
||||||
import {follows, followsByPubkey} from "./follows.js"
|
import {followLists, getFollowListsByPubkey, getFollowList} from "./follows.js"
|
||||||
import {mutes, mutesByPubkey} from "./mutes.js"
|
import {muteLists, getMuteList} from "./mutes.js"
|
||||||
|
|
||||||
export const getFollows = (pubkey: string) =>
|
export const getFollows = (pubkey: string) =>
|
||||||
getPubkeyTagValues(getListTags(followsByPubkey.get().get(pubkey)))
|
getPubkeyTagValues(getListTags(getFollowList(pubkey)))
|
||||||
|
|
||||||
export const getMutes = (pubkey: string) =>
|
export const getMutes = (pubkey: string) =>
|
||||||
getPubkeyTagValues(getListTags(mutesByPubkey.get().get(pubkey)))
|
getPubkeyTagValues(getListTags(getMuteList(pubkey)))
|
||||||
|
|
||||||
export const getNetwork = (pubkey: string) => {
|
export const getNetwork = (pubkey: string) => {
|
||||||
const pubkeys = new Set(getFollows(pubkey))
|
const pubkeys = new Set(getFollows(pubkey))
|
||||||
@@ -28,7 +28,7 @@ export const getNetwork = (pubkey: string) => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export const followersByPubkey = withGetter(
|
export const followersByPubkey = withGetter(
|
||||||
derived(throttled(1000, follows), lists => {
|
derived(throttled(1000, followLists), lists => {
|
||||||
const $followersByPubkey = new Map<string, Set<string>>()
|
const $followersByPubkey = new Map<string, Set<string>>()
|
||||||
|
|
||||||
for (const list of lists) {
|
for (const list of lists) {
|
||||||
@@ -42,7 +42,7 @@ export const followersByPubkey = withGetter(
|
|||||||
)
|
)
|
||||||
|
|
||||||
export const mutersByPubkey = withGetter(
|
export const mutersByPubkey = withGetter(
|
||||||
derived(throttled(1000, mutes), lists => {
|
derived(throttled(1000, muteLists), lists => {
|
||||||
const $mutersByPubkey = new Map<string, Set<string>>()
|
const $mutersByPubkey = new Map<string, Set<string>>()
|
||||||
|
|
||||||
for (const list of lists) {
|
for (const list of lists) {
|
||||||
@@ -73,7 +73,7 @@ export const maxWot = withGetter(derived(wotGraph, $g => max(Array.from($g.value
|
|||||||
const buildGraph = throttle(1000, () => {
|
const buildGraph = throttle(1000, () => {
|
||||||
const $pubkey = pubkey.get()
|
const $pubkey = pubkey.get()
|
||||||
const $graph = new Map<string, number>()
|
const $graph = new Map<string, number>()
|
||||||
const $follows = $pubkey ? getFollows($pubkey) : followsByPubkey.get().keys()
|
const $follows = $pubkey ? getFollows($pubkey) : getFollowListsByPubkey().keys()
|
||||||
|
|
||||||
for (const follow of $follows) {
|
for (const follow of $follows) {
|
||||||
for (const pubkey of getFollows(follow)) {
|
for (const pubkey of getFollows(follow)) {
|
||||||
@@ -89,8 +89,8 @@ const buildGraph = throttle(1000, () => {
|
|||||||
})
|
})
|
||||||
|
|
||||||
pubkey.subscribe(buildGraph)
|
pubkey.subscribe(buildGraph)
|
||||||
follows.subscribe(buildGraph)
|
followLists.subscribe(buildGraph)
|
||||||
mutes.subscribe(buildGraph)
|
muteLists.subscribe(buildGraph)
|
||||||
|
|
||||||
export const getWotScore = (pubkey: string, target: string) => {
|
export const getWotScore = (pubkey: string, target: string) => {
|
||||||
const follows = pubkey ? getFollowsWhoFollow(pubkey, target) : getFollowers(target)
|
const follows = pubkey ? getFollowsWhoFollow(pubkey, target) : getFollowers(target)
|
||||||
|
|||||||
+29
-45
@@ -10,15 +10,21 @@ import {
|
|||||||
batcher,
|
batcher,
|
||||||
postJson,
|
postJson,
|
||||||
} from "@welshman/lib"
|
} from "@welshman/lib"
|
||||||
import {collection} from "@welshman/store"
|
import {getter, deriveItems, makeForceLoadItem, makeLoadItem, makeDeriveItem} from "@welshman/store"
|
||||||
import {deriveProfile, loadProfile} from "./profiles.js"
|
import {deriveProfile, loadProfile} from "./profiles.js"
|
||||||
import {appContext} from "./context.js"
|
import {appContext} from "./context.js"
|
||||||
|
|
||||||
export const zappers = writable<Zapper[]>([])
|
export const zappersByLnurl = writable(new Map<string, Zapper>())
|
||||||
|
|
||||||
export const fetchZappers = async (lnurls: string[]) => {
|
export const zappers = deriveItems(zappersByLnurl)
|
||||||
|
|
||||||
|
export const getZappersByLnurl = getter(zappersByLnurl)
|
||||||
|
|
||||||
|
export const getZapper = (lnurl: string) => getZappersByLnurl().get(lnurl)
|
||||||
|
|
||||||
|
export const fetchZapper = batcher(800, async (lnurls: string[]) => {
|
||||||
const base = appContext.dufflepudUrl
|
const base = appContext.dufflepudUrl
|
||||||
const zappersByLnurl = new Map<string, Zapper>()
|
const result = new Map<string, Zapper>()
|
||||||
|
|
||||||
// Use dufflepud if we it's set up to protect user privacy, otherwise fetch directly
|
// Use dufflepud if we it's set up to protect user privacy, otherwise fetch directly
|
||||||
if (base) {
|
if (base) {
|
||||||
@@ -30,7 +36,7 @@ export const fetchZappers = async (lnurls: string[]) => {
|
|||||||
)
|
)
|
||||||
|
|
||||||
for (const {lnurl, info} of res?.data || []) {
|
for (const {lnurl, info} of res?.data || []) {
|
||||||
tryCatch(() => zappersByLnurl.set(hexToBech32("lnurl", lnurl), info))
|
tryCatch(() => result.set(hexToBech32("lnurl", lnurl), info))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
@@ -45,61 +51,39 @@ export const fetchZappers = async (lnurls: string[]) => {
|
|||||||
|
|
||||||
for (const {lnurl, info} of results) {
|
for (const {lnurl, info} of results) {
|
||||||
if (info) {
|
if (info) {
|
||||||
zappersByLnurl.set(lnurl, info)
|
result.set(lnurl, info)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return zappersByLnurl
|
return lnurls.map(lnurl => {
|
||||||
}
|
const info = result.get(lnurl)
|
||||||
|
|
||||||
export const {
|
if (info) {
|
||||||
indexStore: zappersByLnurl,
|
return {...info, lnurl}
|
||||||
deriveItem: deriveZapper,
|
|
||||||
loadItem: loadZapper,
|
|
||||||
onItem: onZapper,
|
|
||||||
} = collection({
|
|
||||||
name: "zappers",
|
|
||||||
store: zappers,
|
|
||||||
getKey: (zapper: Zapper) => zapper.lnurl,
|
|
||||||
load: batcher(800, async (lnurls: string[]) => {
|
|
||||||
const fresh = await fetchZappers(uniq(lnurls))
|
|
||||||
const stale = zappersByLnurl.get()
|
|
||||||
|
|
||||||
for (const lnurl of lnurls) {
|
|
||||||
const newZapper = fresh.get(lnurl)
|
|
||||||
|
|
||||||
if (newZapper) {
|
|
||||||
stale.set(lnurl, {...newZapper, lnurl})
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
})
|
||||||
zappers.set(Array.from(stale.values()))
|
|
||||||
|
|
||||||
return lnurls
|
|
||||||
}),
|
|
||||||
})
|
})
|
||||||
|
|
||||||
|
export const forceLoadZapper = makeForceLoadItem(fetchZapper, getZapper)
|
||||||
|
|
||||||
|
export const loadZapper = makeLoadItem(fetchZapper, getZapper)
|
||||||
|
|
||||||
|
export const deriveZapper = makeDeriveItem(zappersByLnurl, loadZapper)
|
||||||
|
|
||||||
export const loadZapperForPubkey = async (pubkey: string, relays: string[] = []) => {
|
export const loadZapperForPubkey = async (pubkey: string, relays: string[] = []) => {
|
||||||
const $profile = await loadProfile(pubkey, relays)
|
const $profile = await loadProfile(pubkey, relays)
|
||||||
|
|
||||||
if (!$profile?.lnurl) {
|
return $profile?.lnurl ? loadZapper($profile.lnurl) : undefined
|
||||||
return undefined
|
|
||||||
}
|
|
||||||
|
|
||||||
return loadZapper($profile.lnurl)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export const deriveZapperForPubkey = (pubkey: string, relays: string[] = []) =>
|
export const deriveZapperForPubkey = (pubkey: string, relays: string[] = []) => {
|
||||||
derived([zappersByLnurl, deriveProfile(pubkey, relays)], ([$zappersByLnurl, $profile]) => {
|
loadZapperForPubkey(pubkey, relays)
|
||||||
if (!$profile?.lnurl) {
|
|
||||||
return undefined
|
|
||||||
}
|
|
||||||
|
|
||||||
loadZapper($profile.lnurl)
|
return derived([zappersByLnurl, deriveProfile(pubkey, relays)], ([$zappersByLnurl, $profile]) => {
|
||||||
|
return $profile?.lnurl ? $zappersByLnurl.get($profile.lnurl) : undefined
|
||||||
return $zappersByLnurl.get($profile.lnurl)
|
|
||||||
})
|
})
|
||||||
|
}
|
||||||
|
|
||||||
export const getLnUrlsForEvent = async (event: TrustedEvent) => {
|
export const getLnUrlsForEvent = async (event: TrustedEvent) => {
|
||||||
const lnurls = removeUndefined(getTagValues("zap", event.tags).map(getLnUrl))
|
const lnurls = removeUndefined(getTagValues("zap", event.tags).map(getLnUrl))
|
||||||
|
|||||||
@@ -21,7 +21,7 @@ import {
|
|||||||
isShareableRelayUrl,
|
isShareableRelayUrl,
|
||||||
PROFILE,
|
PROFILE,
|
||||||
RELAYS,
|
RELAYS,
|
||||||
INBOX_RELAYS,
|
MESSAGING_RELAYS,
|
||||||
FOLLOWS,
|
FOLLOWS,
|
||||||
WRAP,
|
WRAP,
|
||||||
getPubkeyTagValues,
|
getPubkeyTagValues,
|
||||||
@@ -37,7 +37,7 @@ import {
|
|||||||
} from "@welshman/util"
|
} from "@welshman/util"
|
||||||
import {Repository} from "@welshman/net"
|
import {Repository} from "@welshman/net"
|
||||||
|
|
||||||
export const INDEXED_KINDS = [PROFILE, RELAYS, INBOX_RELAYS, FOLLOWS]
|
export const INDEXED_KINDS = [PROFILE, RELAYS, MESSAGING_RELAYS, FOLLOWS]
|
||||||
|
|
||||||
export type RelaysAndFilters = {
|
export type RelaysAndFilters = {
|
||||||
relays: string[]
|
relays: string[]
|
||||||
@@ -54,7 +54,7 @@ export type RouterOptions = {
|
|||||||
/**
|
/**
|
||||||
* Retrieves relays for the specified public key and mode.
|
* Retrieves relays for the specified public key and mode.
|
||||||
* @param pubkey - The public key to retrieve relays for.
|
* @param pubkey - The public key to retrieve relays for.
|
||||||
* @param mode - The relay mode (optional). May be "read", "write", or "inbox".
|
* @param mode - The relay mode (optional). May be "read", "write", or "messaging".
|
||||||
* @returns An array of relay URLs as strings.
|
* @returns An array of relay URLs as strings.
|
||||||
*/
|
*/
|
||||||
getPubkeyRelays?: (pubkey: string, mode?: RelayMode) => string[]
|
getPubkeyRelays?: (pubkey: string, mode?: RelayMode) => string[]
|
||||||
@@ -174,20 +174,20 @@ export class Router {
|
|||||||
|
|
||||||
FromUser = () => this.FromRelays(this.getRelaysForUser(RelayMode.Write))
|
FromUser = () => this.FromRelays(this.getRelaysForUser(RelayMode.Write))
|
||||||
|
|
||||||
UserInbox = () => this.FromRelays(this.getRelaysForUser(RelayMode.Inbox))
|
UserMessages = () => this.FromRelays(this.getRelaysForUser(RelayMode.Messages))
|
||||||
|
|
||||||
ForPubkey = (pubkey: string) => this.FromRelays(this.getRelaysForPubkey(pubkey, RelayMode.Read))
|
ForPubkey = (pubkey: string) => this.FromRelays(this.getRelaysForPubkey(pubkey, RelayMode.Read))
|
||||||
|
|
||||||
FromPubkey = (pubkey: string) => this.FromRelays(this.getRelaysForPubkey(pubkey, RelayMode.Write))
|
FromPubkey = (pubkey: string) => this.FromRelays(this.getRelaysForPubkey(pubkey, RelayMode.Write))
|
||||||
|
|
||||||
PubkeyInbox = (pubkey: string) =>
|
MessagesForPubkey = (pubkey: string) =>
|
||||||
this.FromRelays(this.getRelaysForPubkey(pubkey, RelayMode.Inbox))
|
this.FromRelays(this.getRelaysForPubkey(pubkey, RelayMode.Messages))
|
||||||
|
|
||||||
ForPubkeys = (pubkeys: string[]) => this.merge(pubkeys.map(pubkey => this.ForPubkey(pubkey)))
|
ForPubkeys = (pubkeys: string[]) => this.merge(pubkeys.map(pubkey => this.ForPubkey(pubkey)))
|
||||||
|
|
||||||
FromPubkeys = (pubkeys: string[]) => this.merge(pubkeys.map(pubkey => this.FromPubkey(pubkey)))
|
FromPubkeys = (pubkeys: string[]) => this.merge(pubkeys.map(pubkey => this.FromPubkey(pubkey)))
|
||||||
|
|
||||||
PubkeyInboxes = (pubkeys: string[]) => this.merge(pubkeys.map(pubkey => this.PubkeyInbox(pubkey)))
|
MessagesForPubkeys = (pubkeys: string[]) => this.merge(pubkeys.map(pubkey => this.MessagesForPubkey(pubkey)))
|
||||||
|
|
||||||
Event = (event: TrustedEvent) =>
|
Event = (event: TrustedEvent) =>
|
||||||
this.FromRelays(this.getRelaysForPubkey(event.pubkey, RelayMode.Write))
|
this.FromRelays(this.getRelaysForPubkey(event.pubkey, RelayMode.Write))
|
||||||
@@ -371,7 +371,7 @@ export const getFilterSelectionsForWraps = (filter: Filter) => {
|
|||||||
return [
|
return [
|
||||||
{
|
{
|
||||||
filter: {...filter, kinds: [WRAP]},
|
filter: {...filter, kinds: [WRAP]},
|
||||||
scenario: Router.get().UserInbox(),
|
scenario: Router.get().UserMessages(),
|
||||||
},
|
},
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import {derived, readable, Readable} from "svelte/store"
|
import {derived, readable, Readable} from "svelte/store"
|
||||||
import {on, now, indexBy, mapPop, Maybe, call, sortBy, first} from "@welshman/lib"
|
import {on, now, indexBy, mapPop, Maybe, MaybeAsync, call, sortBy, first} from "@welshman/lib"
|
||||||
import {matchFilters, getIdFilters, Filter, TrustedEvent} from "@welshman/util"
|
import {matchFilters, getIdFilters, Filter, TrustedEvent} from "@welshman/util"
|
||||||
import {Repository, RepositoryUpdate, Tracker} from "@welshman/net"
|
import {Repository, RepositoryUpdate, Tracker} from "@welshman/net"
|
||||||
import {deriveDeduplicated} from "./misc.js"
|
import {deriveDeduplicated} from "./misc.js"
|
||||||
@@ -175,7 +175,7 @@ export const deriveEventsByIdForUrl = (
|
|||||||
|
|
||||||
export type ItemsByKey<T> = Map<string, T>
|
export type ItemsByKey<T> = Map<string, T>
|
||||||
|
|
||||||
export type EventToItem<T> = (event: TrustedEvent) => T
|
export type EventToItem<T> = (event: TrustedEvent) => MaybeAsync<Maybe<T>>
|
||||||
|
|
||||||
export type GetItem<T> = (key: string, ...args: any[]) => Maybe<T>
|
export type GetItem<T> = (key: string, ...args: any[]) => Maybe<T>
|
||||||
|
|
||||||
|
|||||||
@@ -135,7 +135,7 @@ export const ROOMS = 10009
|
|||||||
export const FEEDS = 10014
|
export const FEEDS = 10014
|
||||||
export const TOPICS = 10015
|
export const TOPICS = 10015
|
||||||
export const EMOJIS = 10030
|
export const EMOJIS = 10030
|
||||||
export const INBOX_RELAYS = 10050
|
export const MESSAGING_RELAYS = 10050
|
||||||
export const BLOSSOM_SERVERS = 10063
|
export const BLOSSOM_SERVERS = 10063
|
||||||
export const FILE_SERVERS = 10096
|
export const FILE_SERVERS = 10096
|
||||||
export const RELAY_MEMBERS = 13534
|
export const RELAY_MEMBERS = 13534
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ import {last, normalizeUrl, stripProtocol} from "@welshman/lib"
|
|||||||
export enum RelayMode {
|
export enum RelayMode {
|
||||||
Read = "read",
|
Read = "read",
|
||||||
Write = "write",
|
Write = "write",
|
||||||
Inbox = "inbox",
|
Messages = "messages",
|
||||||
}
|
}
|
||||||
|
|
||||||
export type RelayProfile = {
|
export type RelayProfile = {
|
||||||
|
|||||||
Reference in New Issue
Block a user