Migrate collections to new stores, change some conventions
This commit is contained in:
@@ -2,23 +2,23 @@ import {BLOSSOM_SERVERS, 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"
|
||||
import {makeOutboxLoader} from "./relayLists.js"
|
||||
|
||||
export const blossomServersByPubkey = deriveItemsByKey({
|
||||
export const blossomServerListsByPubkey = deriveItemsByKey({
|
||||
repository,
|
||||
eventToItem: (event: TrustedEvent) => readList(asDecryptedEvent(event)),
|
||||
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,
|
||||
editProfile,
|
||||
RelayMode,
|
||||
INBOX_RELAYS,
|
||||
MESSAGING_RELAYS,
|
||||
FOLLOWS,
|
||||
RELAYS,
|
||||
MUTES,
|
||||
@@ -32,16 +32,16 @@ import {
|
||||
import type {RoomMeta, Profile} from "@welshman/util"
|
||||
import {Router, addMaximalFallbacks} from "@welshman/router"
|
||||
import {
|
||||
userRelaySelections,
|
||||
loadUserRelaySelections,
|
||||
userInboxRelaySelections,
|
||||
loadUserInboxRelaySelections,
|
||||
userFollows,
|
||||
loadUserFollows,
|
||||
userMutes,
|
||||
loadUserMutes,
|
||||
userPins,
|
||||
loadUserPins,
|
||||
userRelayList,
|
||||
forceLoadUserRelayList,
|
||||
userMessagingRelayList,
|
||||
forceLoadUserMessagingRelayList,
|
||||
userFollowList,
|
||||
forceLoadUserFollowList,
|
||||
userMuteList,
|
||||
forceLoadUserMuteList,
|
||||
userPinList,
|
||||
forceLoadUserPinList,
|
||||
} from "./user.js"
|
||||
import {nip44EncryptToSelf, signer} from "./session.js"
|
||||
import {ThunkOptions, MergedThunk, publishThunk} from "./thunk.js"
|
||||
@@ -49,9 +49,9 @@ import {ThunkOptions, MergedThunk, publishThunk} from "./thunk.js"
|
||||
// NIP 65
|
||||
|
||||
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 alt = mode === RelayMode.Read ? RelayMode.Write : RelayMode.Read
|
||||
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) => {
|
||||
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 tag = removeUndefined(["r", url, dup && dup[2] !== mode ? undefined : mode])
|
||||
const tags = [...list.publicTags.filter(nthNe(1, url)), tag]
|
||||
@@ -85,20 +85,20 @@ export const addRelay = async (url: string, mode: RelayMode) => {
|
||||
|
||||
// NIP 17
|
||||
|
||||
export const removeInboxRelay = async (url: string) => {
|
||||
await loadUserInboxRelaySelections([], true)
|
||||
export const removeMessagingRelay = async (url: string) => {
|
||||
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 relays = Router.get().FromUser().policy(addMaximalFallbacks).getUrls()
|
||||
|
||||
return publishThunk({event, relays})
|
||||
}
|
||||
|
||||
export const addInboxRelay = async (url: string) => {
|
||||
await loadUserInboxRelaySelections([], true)
|
||||
export const addMessagingRelay = async (url: string) => {
|
||||
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 relays = Router.get().FromUser().policy(addMaximalFallbacks).getUrls()
|
||||
|
||||
@@ -118,9 +118,9 @@ export const setProfile = (profile: Profile) => {
|
||||
// NIP 02
|
||||
|
||||
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 relays = Router.get().FromUser().policy(addMaximalFallbacks).getUrls()
|
||||
|
||||
@@ -128,9 +128,9 @@ export const unfollow = async (value: 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 relays = Router.get().FromUser().policy(addMaximalFallbacks).getUrls()
|
||||
|
||||
@@ -138,9 +138,9 @@ export const follow = async (tag: 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 relays = Router.get().FromUser().policy(addMaximalFallbacks).getUrls()
|
||||
|
||||
@@ -148,9 +148,9 @@ export const unmute = async (value: 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 relays = Router.get().FromUser().policy(addMaximalFallbacks).getUrls()
|
||||
|
||||
@@ -158,9 +158,9 @@ export const mutePublicly = 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 relays = Router.get().FromUser().policy(addMaximalFallbacks).getUrls()
|
||||
|
||||
@@ -174,9 +174,9 @@ export const setMutes = async ({
|
||||
publicTags?: 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 relays = Router.get().FromUser().policy(addMaximalFallbacks).getUrls()
|
||||
|
||||
@@ -184,9 +184,9 @@ export const setMutes = async ({
|
||||
}
|
||||
|
||||
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 relays = Router.get().FromUser().policy(addMaximalFallbacks).getUrls()
|
||||
|
||||
@@ -194,9 +194,9 @@ export const unpin = async (value: 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 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) =>
|
||||
new MergedThunk(
|
||||
uniq(recipients).map(recipient => {
|
||||
const relays = Router.get().PubkeyInbox(recipient).getUrls()
|
||||
const relays = Router.get().MessagesForPubkey(recipient).getUrls()
|
||||
|
||||
return publishThunk({event, relays, recipient, ...options})
|
||||
}),
|
||||
|
||||
@@ -1,55 +1,5 @@
|
||||
import {throttle} from "@welshman/lib"
|
||||
import {Repository, Tracker} from "@welshman/net"
|
||||
import {custom} from "@welshman/store"
|
||||
|
||||
export const tracker = new Tracker()
|
||||
|
||||
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 {deriveItemsByKey, deriveItems, makeForceLoadItem, makeLoadItem, makeDeriveItem, getter} from "@welshman/store"
|
||||
import {repository} from "./core.js"
|
||||
import {makeOutboxLoader} from "./relaySelections.js"
|
||||
import {makeOutboxLoader} from "./relayLists.js"
|
||||
|
||||
export const followsByPubkey = deriveItemsByKey({
|
||||
export const followListsByPubkey = deriveItemsByKey({
|
||||
repository,
|
||||
eventToItem: (event: TrustedEvent) => readList(asDecryptedEvent(event)),
|
||||
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 {tryCatch, fetchJson, uniq, batcher, postJson, last} from "@welshman/lib"
|
||||
import {collection} from "@welshman/store"
|
||||
import {deriveProfile} from "./profiles.js"
|
||||
import {getter, deriveItems, makeForceLoadItem, makeLoadItem, makeDeriveItem} from "@welshman/store"
|
||||
import {deriveProfile, loadProfile} from "./profiles.js"
|
||||
import {appContext} from "./context.js"
|
||||
|
||||
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[]) => {
|
||||
const base = appContext.dufflepudUrl!
|
||||
export const handles = deriveItems(handlesByNip05)
|
||||
|
||||
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>()
|
||||
|
||||
// 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(
|
||||
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 || []) {
|
||||
@@ -72,50 +79,40 @@ export const fetchHandles = async (nip05s: string[]) => {
|
||||
}
|
||||
}
|
||||
|
||||
return handlesByNip05
|
||||
}
|
||||
return nip05s.map(nip05 => {
|
||||
const info = handlesByNip05.get(nip05)
|
||||
|
||||
export const {
|
||||
indexStore: handlesByNip05,
|
||||
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})
|
||||
}
|
||||
if (info) {
|
||||
return {...info, nip05}
|
||||
}
|
||||
|
||||
handles.set(Array.from(stale.values()))
|
||||
|
||||
return nip05s
|
||||
}),
|
||||
})
|
||||
})
|
||||
|
||||
export const deriveHandleForPubkey = (pubkey: string, relays: string[] = []) =>
|
||||
derived([handlesByNip05, deriveProfile(pubkey, relays)], ([$handlesByNip05, $profile]) => {
|
||||
if (!$profile?.nip05) {
|
||||
return undefined
|
||||
}
|
||||
export const forceLoadHandle = makeForceLoadItem(fetchHandle, getHandle)
|
||||
|
||||
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)
|
||||
|
||||
if (handle?.pubkey === pubkey) {
|
||||
return handle
|
||||
}
|
||||
if (handle?.pubkey !== pubkey) return undefined
|
||||
|
||||
return handle
|
||||
})
|
||||
}
|
||||
|
||||
export const displayNip05 = (nip05: string) =>
|
||||
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 "./relays.js"
|
||||
export * from "./relayStats.js"
|
||||
export * from "./relaySelections.js"
|
||||
export * from "./inboxRelaySelections.js"
|
||||
export * from "./relayLists.js"
|
||||
export * from "./messagingRelayLists.js"
|
||||
export * from "./search.js"
|
||||
export * from "./session.js"
|
||||
export * from "./sync.js"
|
||||
@@ -37,10 +37,10 @@ import {routerContext} from "@welshman/router"
|
||||
import {Pool, SocketEvent, isRelayEvent, netContext} from "@welshman/net"
|
||||
import {pubkey, unwrapAndStore} from "./session.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 {relaySelectionsByPubkey} from "./relaySelections.js"
|
||||
import {inboxRelaySelectionsByPubkey} from "./inboxRelaySelections.js"
|
||||
import {deriveRelayList, getRelayList} from "./relayLists.js"
|
||||
import {deriveMessagingRelayList, getMessagingRelayList} from "./messagingRelayLists.js"
|
||||
|
||||
// Sync relays with our database
|
||||
|
||||
@@ -73,7 +73,7 @@ Pool.get().subscribe(socket => {
|
||||
|
||||
const _relayGetter = (fn?: (relay: RelayProfile) => any) =>
|
||||
throttleWithValue(200, () => {
|
||||
let _relays = relays.get()
|
||||
let _relays = getRelays()
|
||||
|
||||
if (fn) {
|
||||
_relays = _relays.filter(fn)
|
||||
@@ -85,14 +85,14 @@ const _relayGetter = (fn?: (relay: RelayProfile) => any) =>
|
||||
})
|
||||
|
||||
export const getPubkeyRelays = (pubkey: string, mode?: RelayMode) =>
|
||||
mode === RelayMode.Inbox
|
||||
? getRelaysFromList(inboxRelaySelectionsByPubkey.get().get(pubkey))
|
||||
: getRelaysFromList(relaySelectionsByPubkey.get().get(pubkey), mode)
|
||||
mode === RelayMode.Messages
|
||||
? getRelaysFromList(getMessagingRelayList(pubkey))
|
||||
: getRelaysFromList(getRelayList(pubkey), mode)
|
||||
|
||||
export const derivePubkeyRelays = (pubkey: string, mode?: RelayMode) =>
|
||||
mode === RelayMode.Inbox
|
||||
? derived(inboxRelaySelectionsByPubkey, $m => getRelaysFromList($m.get(pubkey)))
|
||||
: derived(relaySelectionsByPubkey, $m => getRelaysFromList($m.get(pubkey), mode))
|
||||
mode === RelayMode.Messages
|
||||
? derived(deriveMessagingRelayList(pubkey), list => getRelaysFromList(list))
|
||||
: derived(deriveRelayList(pubkey), list => getRelaysFromList(list, mode))
|
||||
|
||||
routerContext.getUserPubkey = () => pubkey.get()
|
||||
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 {repository} from "./core.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,
|
||||
eventToItem: async (event: TrustedEvent) =>
|
||||
readList(
|
||||
@@ -17,14 +17,16 @@ export const mutesByPubkey = deriveItemsByKey({
|
||||
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 {deriveItemsByKey, deriveItems, makeForceLoadItem, makeLoadItem, makeDeriveItem, getter} from "@welshman/store"
|
||||
import {repository} from "./core.js"
|
||||
import {makeOutboxLoader} from "./relaySelections.js"
|
||||
import {makeOutboxLoader} from "./relayLists.js"
|
||||
|
||||
export const pinsByPubkey = deriveItemsByKey({
|
||||
export const pinListsByPubkey = deriveItemsByKey({
|
||||
repository,
|
||||
eventToItem: (event: TrustedEvent) => readList(asDecryptedEvent(event)),
|
||||
filters: [{kinds: [PINS]}],
|
||||
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 {deriveItemsByKey, deriveItems, makeForceLoadItem, makeLoadItem, makeDeriveItem, getter} from "@welshman/store"
|
||||
import {repository} from "./core.js"
|
||||
import {makeOutboxLoaderWithIndexers} from "./relaySelections.js"
|
||||
import {makeOutboxLoaderWithIndexers} from "./relayLists.js"
|
||||
|
||||
export const profilesByPubkey = deriveItemsByKey({
|
||||
repository,
|
||||
@@ -15,6 +15,8 @@ export const profiles = deriveItems(profilesByPubkey)
|
||||
|
||||
export const getProfilesByPubkey = getter(profilesByPubkey)
|
||||
|
||||
export const getProfiles = getter(profiles)
|
||||
|
||||
export const getProfile = (pubkey: string) => getProfilesByPubkey().get(pubkey)
|
||||
|
||||
export const forceLoadProfile = makeForceLoadItem(makeOutboxLoaderWithIndexers(PROFILE), getProfile)
|
||||
|
||||
@@ -41,21 +41,23 @@ export const makeOutboxLoaderWithIndexers =
|
||||
])
|
||||
}
|
||||
|
||||
export const relaySelectionsByPubkey = deriveItemsByKey({
|
||||
export const relayListsByPubkey = deriveItemsByKey({
|
||||
repository,
|
||||
eventToItem: (event: TrustedEvent) => readList(asDecryptedEvent(event)),
|
||||
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,
|
||||
postJson,
|
||||
Maybe,
|
||||
noop,
|
||||
} from "@welshman/lib"
|
||||
import {withGetter} from "@welshman/store"
|
||||
import {RelayProfile} 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"
|
||||
|
||||
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 {
|
||||
return fetchJson(url.replace(/^ws/, "http"), {
|
||||
const json = fetchJson(url.replace(/^ws/, "http"), {
|
||||
headers: {
|
||||
Accept: "application/nostr+json",
|
||||
},
|
||||
})
|
||||
|
||||
if (json) {
|
||||
return {...json, url}
|
||||
}
|
||||
} catch (e) {
|
||||
// pass
|
||||
}
|
||||
}
|
||||
|
||||
export const fetchRelayProfilesDirectly = async (
|
||||
urls: string[],
|
||||
): Promise<Map<string, RelayProfile>> =>
|
||||
indexBy(
|
||||
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)
|
||||
export const fetchRelayUsingProxy = batcher(800, async (urls: string[]) => {
|
||||
// Handle a race condition edge case where dufflepud url changes under us
|
||||
if (!appContext.dufflepudUrl) {
|
||||
return urls.map(noop)
|
||||
}
|
||||
|
||||
return profilesByUrl
|
||||
}
|
||||
const res: any = await postJson(`${appContext.dufflepudUrl}/relay/info`, {urls})
|
||||
const relaysByUrl = new Map<string, RelayProfile>()
|
||||
|
||||
export const fetchRelayProfiles = (urls: string[]) =>
|
||||
appContext.dufflepudUrl
|
||||
? fetchRelayProfilesUsingProxy(appContext.dufflepudUrl, urls)
|
||||
: fetchRelayProfilesDirectly(urls)
|
||||
for (const {url, info} of res?.data || []) {
|
||||
relaysByUrl.set(url, info)
|
||||
}
|
||||
|
||||
export const {
|
||||
indexStore: relaysByUrl,
|
||||
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()
|
||||
return urls.map(url => {
|
||||
const info = relaysByUrl.get(url)
|
||||
|
||||
for (const url of urls) {
|
||||
const profile = fresh.get(url)
|
||||
|
||||
if (!url || !isRelayUrl(url)) {
|
||||
console.warn(`Attempted to load invalid relay url: ${url}`)
|
||||
continue
|
||||
}
|
||||
|
||||
if (profile) {
|
||||
stale.set(url, {...profile, url})
|
||||
}
|
||||
if (info) {
|
||||
return {...info, 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) =>
|
||||
displayRelayProfile(relaysByUrl.get().get(url), displayRelayUrl(url))
|
||||
displayRelayProfile(getRelay(url), displayRelayUrl(url))
|
||||
|
||||
export const deriveRelayDisplay = (url: string) =>
|
||||
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 {push as basePush, pull as basePull, publishOne, requestOne} from "@welshman/net"
|
||||
import {repository} from "./core.js"
|
||||
import {relaysByUrl} from "./relays.js"
|
||||
import {getRelay} from "./relays.js"
|
||||
|
||||
const query = (filters: Filter[]) =>
|
||||
repository.query(filters, {shouldSort: filters.every(f => f.limit === undefined)})
|
||||
|
||||
export const hasNegentropy = (url: string) => {
|
||||
const relay = relaysByUrl.get().get(url)
|
||||
const relay = getRelay(url)
|
||||
|
||||
if (relay?.negentropy) return true
|
||||
if (relay?.supported_nips?.includes?.(77)) return true
|
||||
|
||||
+34
-16
@@ -1,5 +1,7 @@
|
||||
import {inc, throttle} from "@welshman/lib"
|
||||
import {custom} from "@welshman/store"
|
||||
import {readable} from 'svelte/store'
|
||||
import {on, call} from "@welshman/lib"
|
||||
import {deriveItems} from "@welshman/store"
|
||||
import {getTopicTagValues} from "@welshman/util"
|
||||
import {repository} from "./core.js"
|
||||
|
||||
export type Topic = {
|
||||
@@ -7,25 +9,41 @@ export type Topic = {
|
||||
count: number
|
||||
}
|
||||
|
||||
export const topics = custom<Topic[]>(setter => {
|
||||
const getTopics = () => {
|
||||
const topics = new Map<string, number>()
|
||||
for (const tagString of repository.eventsByTag.keys()) {
|
||||
if (tagString.startsWith("t:")) {
|
||||
const topic = tagString.slice(2).toLowerCase()
|
||||
export const topicsByName = call(() => {
|
||||
const topicsByName = new Map<string, Topic>()
|
||||
|
||||
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 {withGetter, memoized} from "@welshman/store"
|
||||
import {pubkey} from "./session.js"
|
||||
import {profilesByPubkey, loadProfile} from "./profiles.js"
|
||||
import {followsByPubkey, loadFollows} from "./follows.js"
|
||||
import {loadPins, pinsByPubkey} from "./pins.js"
|
||||
import {mutesByPubkey, loadMutes} from "./mutes.js"
|
||||
import {blossomServersByPubkey, loadBlossomServers} from "./blossom.js"
|
||||
import {relaySelectionsByPubkey, loadRelaySelections} from "./relaySelections.js"
|
||||
import {inboxRelaySelectionsByPubkey, loadInboxRelaySelections} from "./inboxRelaySelections.js"
|
||||
import {profilesByPubkey, forceLoadProfile, loadProfile} from "./profiles.js"
|
||||
import {followListsByPubkey, forceLoadFollowList, loadFollowList} from "./follows.js"
|
||||
import {pinListsByPubkey, forceLoadPinList, loadPinList} from "./pins.js"
|
||||
import {muteListsByPubkey, forceLoadMuteList, loadMuteList} from "./mutes.js"
|
||||
import {blossomServerListsByPubkey, forceLoadBlossomServerList, loadBlossomServerList} from "./blossom.js"
|
||||
import {relayListsByPubkey, forceLoadRelayList, loadRelayList} from "./relayLists.js"
|
||||
import {messagingRelayListsByPubkey, forceLoadMessagingRelayList, loadMessagingRelayList} from "./messagingRelayLists.js"
|
||||
import {wotGraph} from "./wot.js"
|
||||
|
||||
export type UserDataLoader = (pubkey: string, relays?: string[], force?: boolean) => unknown
|
||||
@@ -45,49 +45,56 @@ export const userProfile = makeUserData({
|
||||
loadItem: loadProfile,
|
||||
})
|
||||
|
||||
export const forceLoadUserProfile = makeUserLoader(forceLoadProfile)
|
||||
export const loadUserProfile = makeUserLoader(loadProfile)
|
||||
|
||||
export const userFollows = makeUserData({
|
||||
mapStore: followsByPubkey,
|
||||
loadItem: loadFollows,
|
||||
export const userFollowList = makeUserData({
|
||||
mapStore: followListsByPubkey,
|
||||
loadItem: loadFollowList,
|
||||
})
|
||||
|
||||
export const loadUserFollows = makeUserLoader(loadFollows)
|
||||
export const forceLoadUserFollowList = makeUserLoader(forceLoadFollowList)
|
||||
export const loadUserFollowList = makeUserLoader(loadFollowList)
|
||||
|
||||
export const userMutes = makeUserData({
|
||||
mapStore: mutesByPubkey,
|
||||
loadItem: loadMutes,
|
||||
export const userMuteList = makeUserData({
|
||||
mapStore: muteListsByPubkey,
|
||||
loadItem: loadMuteList,
|
||||
})
|
||||
|
||||
export const loadUserMutes = makeUserLoader(loadMutes)
|
||||
export const forceLoadUserMuteList = makeUserLoader(forceLoadMuteList)
|
||||
export const loadUserMuteList = makeUserLoader(loadMuteList)
|
||||
|
||||
export const userPins = makeUserData({
|
||||
mapStore: pinsByPubkey,
|
||||
loadItem: loadPins,
|
||||
export const userPinList = makeUserData({
|
||||
mapStore: pinListsByPubkey,
|
||||
loadItem: loadPinList,
|
||||
})
|
||||
|
||||
export const loadUserPins = makeUserLoader(loadPins)
|
||||
export const forceLoadUserPinList = makeUserLoader(forceLoadPinList)
|
||||
export const loadUserPinList = makeUserLoader(loadPinList)
|
||||
|
||||
export const userRelaySelections = makeUserData({
|
||||
mapStore: relaySelectionsByPubkey,
|
||||
loadItem: loadRelaySelections,
|
||||
export const userRelayList = makeUserData({
|
||||
mapStore: relayListsByPubkey,
|
||||
loadItem: loadRelayList,
|
||||
})
|
||||
|
||||
export const loadUserRelaySelections = makeUserLoader(loadRelaySelections)
|
||||
export const forceLoadUserRelayList = makeUserLoader(forceLoadRelayList)
|
||||
export const loadUserRelayList = makeUserLoader(loadRelayList)
|
||||
|
||||
export const userInboxRelaySelections = makeUserData({
|
||||
mapStore: inboxRelaySelectionsByPubkey,
|
||||
loadItem: loadInboxRelaySelections,
|
||||
export const userMessagingRelayList = makeUserData({
|
||||
mapStore: messagingRelayListsByPubkey,
|
||||
loadItem: loadMessagingRelayList,
|
||||
})
|
||||
|
||||
export const loadUserInboxRelaySelections = makeUserLoader(loadInboxRelaySelections)
|
||||
export const forceLoadUserMessagingRelayList = makeUserLoader(forceLoadMessagingRelayList)
|
||||
export const loadUserMessagingRelayList = makeUserLoader(loadMessagingRelayList)
|
||||
|
||||
export const userBlossomServers = makeUserData({
|
||||
mapStore: blossomServersByPubkey,
|
||||
loadItem: loadBlossomServers,
|
||||
export const userBlossomServerList = makeUserData({
|
||||
mapStore: blossomServerListsByPubkey,
|
||||
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
|
||||
|
||||
|
||||
@@ -3,14 +3,14 @@ import {max, throttle, addToMapKey, inc, dec} from "@welshman/lib"
|
||||
import {getListTags, getPubkeyTagValues} from "@welshman/util"
|
||||
import {throttled, withGetter} from "@welshman/store"
|
||||
import {pubkey} from "./session.js"
|
||||
import {follows, followsByPubkey} from "./follows.js"
|
||||
import {mutes, mutesByPubkey} from "./mutes.js"
|
||||
import {followLists, getFollowListsByPubkey, getFollowList} from "./follows.js"
|
||||
import {muteLists, getMuteList} from "./mutes.js"
|
||||
|
||||
export const getFollows = (pubkey: string) =>
|
||||
getPubkeyTagValues(getListTags(followsByPubkey.get().get(pubkey)))
|
||||
getPubkeyTagValues(getListTags(getFollowList(pubkey)))
|
||||
|
||||
export const getMutes = (pubkey: string) =>
|
||||
getPubkeyTagValues(getListTags(mutesByPubkey.get().get(pubkey)))
|
||||
getPubkeyTagValues(getListTags(getMuteList(pubkey)))
|
||||
|
||||
export const getNetwork = (pubkey: string) => {
|
||||
const pubkeys = new Set(getFollows(pubkey))
|
||||
@@ -28,7 +28,7 @@ export const getNetwork = (pubkey: string) => {
|
||||
}
|
||||
|
||||
export const followersByPubkey = withGetter(
|
||||
derived(throttled(1000, follows), lists => {
|
||||
derived(throttled(1000, followLists), lists => {
|
||||
const $followersByPubkey = new Map<string, Set<string>>()
|
||||
|
||||
for (const list of lists) {
|
||||
@@ -42,7 +42,7 @@ export const followersByPubkey = withGetter(
|
||||
)
|
||||
|
||||
export const mutersByPubkey = withGetter(
|
||||
derived(throttled(1000, mutes), lists => {
|
||||
derived(throttled(1000, muteLists), lists => {
|
||||
const $mutersByPubkey = new Map<string, Set<string>>()
|
||||
|
||||
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 $pubkey = pubkey.get()
|
||||
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 pubkey of getFollows(follow)) {
|
||||
@@ -89,8 +89,8 @@ const buildGraph = throttle(1000, () => {
|
||||
})
|
||||
|
||||
pubkey.subscribe(buildGraph)
|
||||
follows.subscribe(buildGraph)
|
||||
mutes.subscribe(buildGraph)
|
||||
followLists.subscribe(buildGraph)
|
||||
muteLists.subscribe(buildGraph)
|
||||
|
||||
export const getWotScore = (pubkey: string, target: string) => {
|
||||
const follows = pubkey ? getFollowsWhoFollow(pubkey, target) : getFollowers(target)
|
||||
|
||||
+29
-45
@@ -10,15 +10,21 @@ import {
|
||||
batcher,
|
||||
postJson,
|
||||
} from "@welshman/lib"
|
||||
import {collection} from "@welshman/store"
|
||||
import {getter, deriveItems, makeForceLoadItem, makeLoadItem, makeDeriveItem} from "@welshman/store"
|
||||
import {deriveProfile, loadProfile} from "./profiles.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 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
|
||||
if (base) {
|
||||
@@ -30,7 +36,7 @@ export const fetchZappers = async (lnurls: string[]) => {
|
||||
)
|
||||
|
||||
for (const {lnurl, info} of res?.data || []) {
|
||||
tryCatch(() => zappersByLnurl.set(hexToBech32("lnurl", lnurl), info))
|
||||
tryCatch(() => result.set(hexToBech32("lnurl", lnurl), info))
|
||||
}
|
||||
}
|
||||
} else {
|
||||
@@ -45,61 +51,39 @@ export const fetchZappers = async (lnurls: string[]) => {
|
||||
|
||||
for (const {lnurl, info} of results) {
|
||||
if (info) {
|
||||
zappersByLnurl.set(lnurl, info)
|
||||
result.set(lnurl, info)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return zappersByLnurl
|
||||
}
|
||||
return lnurls.map(lnurl => {
|
||||
const info = result.get(lnurl)
|
||||
|
||||
export const {
|
||||
indexStore: zappersByLnurl,
|
||||
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})
|
||||
}
|
||||
if (info) {
|
||||
return {...info, 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[] = []) => {
|
||||
const $profile = await loadProfile(pubkey, relays)
|
||||
|
||||
if (!$profile?.lnurl) {
|
||||
return undefined
|
||||
}
|
||||
|
||||
return loadZapper($profile.lnurl)
|
||||
return $profile?.lnurl ? loadZapper($profile.lnurl) : undefined
|
||||
}
|
||||
|
||||
export const deriveZapperForPubkey = (pubkey: string, relays: string[] = []) =>
|
||||
derived([zappersByLnurl, deriveProfile(pubkey, relays)], ([$zappersByLnurl, $profile]) => {
|
||||
if (!$profile?.lnurl) {
|
||||
return undefined
|
||||
}
|
||||
export const deriveZapperForPubkey = (pubkey: string, relays: string[] = []) => {
|
||||
loadZapperForPubkey(pubkey, relays)
|
||||
|
||||
loadZapper($profile.lnurl)
|
||||
|
||||
return $zappersByLnurl.get($profile.lnurl)
|
||||
return derived([zappersByLnurl, deriveProfile(pubkey, relays)], ([$zappersByLnurl, $profile]) => {
|
||||
return $profile?.lnurl ? $zappersByLnurl.get($profile.lnurl) : undefined
|
||||
})
|
||||
}
|
||||
|
||||
export const getLnUrlsForEvent = async (event: TrustedEvent) => {
|
||||
const lnurls = removeUndefined(getTagValues("zap", event.tags).map(getLnUrl))
|
||||
|
||||
@@ -21,7 +21,7 @@ import {
|
||||
isShareableRelayUrl,
|
||||
PROFILE,
|
||||
RELAYS,
|
||||
INBOX_RELAYS,
|
||||
MESSAGING_RELAYS,
|
||||
FOLLOWS,
|
||||
WRAP,
|
||||
getPubkeyTagValues,
|
||||
@@ -37,7 +37,7 @@ import {
|
||||
} from "@welshman/util"
|
||||
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 = {
|
||||
relays: string[]
|
||||
@@ -54,7 +54,7 @@ export type RouterOptions = {
|
||||
/**
|
||||
* Retrieves relays for the specified public key and mode.
|
||||
* @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.
|
||||
*/
|
||||
getPubkeyRelays?: (pubkey: string, mode?: RelayMode) => string[]
|
||||
@@ -174,20 +174,20 @@ export class Router {
|
||||
|
||||
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))
|
||||
|
||||
FromPubkey = (pubkey: string) => this.FromRelays(this.getRelaysForPubkey(pubkey, RelayMode.Write))
|
||||
|
||||
PubkeyInbox = (pubkey: string) =>
|
||||
this.FromRelays(this.getRelaysForPubkey(pubkey, RelayMode.Inbox))
|
||||
MessagesForPubkey = (pubkey: string) =>
|
||||
this.FromRelays(this.getRelaysForPubkey(pubkey, RelayMode.Messages))
|
||||
|
||||
ForPubkeys = (pubkeys: string[]) => this.merge(pubkeys.map(pubkey => this.ForPubkey(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) =>
|
||||
this.FromRelays(this.getRelaysForPubkey(event.pubkey, RelayMode.Write))
|
||||
@@ -371,7 +371,7 @@ export const getFilterSelectionsForWraps = (filter: Filter) => {
|
||||
return [
|
||||
{
|
||||
filter: {...filter, kinds: [WRAP]},
|
||||
scenario: Router.get().UserInbox(),
|
||||
scenario: Router.get().UserMessages(),
|
||||
},
|
||||
]
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
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 {Repository, RepositoryUpdate, Tracker} from "@welshman/net"
|
||||
import {deriveDeduplicated} from "./misc.js"
|
||||
@@ -175,7 +175,7 @@ export const deriveEventsByIdForUrl = (
|
||||
|
||||
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>
|
||||
|
||||
|
||||
@@ -135,7 +135,7 @@ export const ROOMS = 10009
|
||||
export const FEEDS = 10014
|
||||
export const TOPICS = 10015
|
||||
export const EMOJIS = 10030
|
||||
export const INBOX_RELAYS = 10050
|
||||
export const MESSAGING_RELAYS = 10050
|
||||
export const BLOSSOM_SERVERS = 10063
|
||||
export const FILE_SERVERS = 10096
|
||||
export const RELAY_MEMBERS = 13534
|
||||
|
||||
@@ -5,7 +5,7 @@ import {last, normalizeUrl, stripProtocol} from "@welshman/lib"
|
||||
export enum RelayMode {
|
||||
Read = "read",
|
||||
Write = "write",
|
||||
Inbox = "inbox",
|
||||
Messages = "messages",
|
||||
}
|
||||
|
||||
export type RelayProfile = {
|
||||
|
||||
Reference in New Issue
Block a user