198 lines
5.8 KiB
TypeScript
198 lines
5.8 KiB
TypeScript
import { createEffect, createResource, createSignal, onCleanup } from "solid-js"
|
|
import { AccountManager } from "applesauce-accounts"
|
|
import type { IAccount } from "applesauce-accounts"
|
|
import { registerCommonAccountTypes } from "applesauce-accounts/accounts"
|
|
import { EventStore } from "applesauce-core"
|
|
import { getProfilePicture } from "applesauce-core/helpers/profile"
|
|
import { createOutboxMap, selectOptimalRelays, setFallbackRelays } from "applesauce-core/helpers/relay-selection"
|
|
import { includeMailboxes } from "applesauce-core/observable"
|
|
import { createEventLoaderForStore } from "applesauce-loaders/loaders"
|
|
import { Relay as NostrRelay, RelayManagement } from "applesauce-relay"
|
|
import { RelayPool } from "applesauce-relay"
|
|
import { NostrConnectSigner } from "applesauce-signers"
|
|
import { map, of } from "rxjs"
|
|
import {
|
|
createRelay,
|
|
deactivateRelay,
|
|
getIdentity,
|
|
getRelay,
|
|
getTenant,
|
|
listRelays,
|
|
listTenantInvoices,
|
|
listTenantRelays,
|
|
listTenants,
|
|
updateRelay,
|
|
updateTenantBilling,
|
|
type CreateRelayInput,
|
|
type Relay,
|
|
type Tenant,
|
|
type UpdateRelayInput,
|
|
} from "./api"
|
|
|
|
export type UnsignedEvent = {
|
|
kind: number
|
|
content: string
|
|
created_at: number
|
|
tags: string[][]
|
|
}
|
|
|
|
export type SignedEvent = UnsignedEvent & {
|
|
id: string
|
|
pubkey: string
|
|
sig: string
|
|
}
|
|
|
|
export type EventSigner = {
|
|
signEvent(event: UnsignedEvent): Promise<SignedEvent>
|
|
}
|
|
|
|
export const PLATFORM_NAME = import.meta.env.VITE_PLATFORM_NAME || "Caravel"
|
|
|
|
export const eventStore = new EventStore()
|
|
export const pool = new RelayPool()
|
|
|
|
createEventLoaderForStore(eventStore, pool, {
|
|
lookupRelays: ["wss://purplepag.es/", "wss://relay.damus.io/", "wss://indexer.coracle.social/"],
|
|
extraRelays: ["wss://relay.damus.io/", "wss://nos.lol/", "wss://relay.primal.net/"],
|
|
})
|
|
|
|
NostrConnectSigner.subscriptionMethod = pool.subscription.bind(pool)
|
|
NostrConnectSigner.publishMethod = pool.publish.bind(pool)
|
|
|
|
export const accountManager = new AccountManager()
|
|
|
|
registerCommonAccountTypes(accountManager)
|
|
|
|
export const [account, setAccount] = createSignal<IAccount | undefined>()
|
|
|
|
export const [identity, { refetch: refetchIdentity, mutate: setIdentity }] = createResource(
|
|
() => account()?.pubkey,
|
|
pubkey => {
|
|
if (pubkey) return getIdentity()
|
|
}
|
|
)
|
|
|
|
;(() => {
|
|
try {
|
|
accountManager.fromJSON(JSON.parse(localStorage.getItem("caravel.accounts")!))
|
|
} catch {
|
|
// pass
|
|
}
|
|
|
|
const active = localStorage.getItem("caravel.accounts.active")
|
|
|
|
if (active) {
|
|
accountManager.setActive(active)
|
|
}
|
|
|
|
accountManager.active$.subscribe(account => {
|
|
setAccount(account)
|
|
|
|
localStorage.setItem("caravel.accounts", JSON.stringify(accountManager.toJSON(true)))
|
|
|
|
if (account) {
|
|
localStorage.setItem("caravel.accounts.active", account.id)
|
|
} else {
|
|
localStorage.removeItem("caravel.accounts.active")
|
|
}
|
|
|
|
refetchIdentity()
|
|
})
|
|
})()
|
|
|
|
export function useProfilePicture(pubkey: () => string | undefined) {
|
|
const [picture, setPicture] = createSignal<string | undefined>()
|
|
|
|
createEffect(() => {
|
|
const pk = pubkey()
|
|
|
|
if (!pk) {
|
|
setPicture(undefined)
|
|
return
|
|
}
|
|
|
|
const profileSub = eventStore.profile(pk).subscribe((profile) => {
|
|
setPicture(getProfilePicture(profile))
|
|
})
|
|
|
|
const reqSub = primeProfiles([pk])
|
|
|
|
onCleanup(() => {
|
|
profileSub.unsubscribe()
|
|
reqSub.unsubscribe()
|
|
})
|
|
})
|
|
|
|
return picture
|
|
}
|
|
|
|
export function primeProfiles(pubkeys: string[]) {
|
|
const uniquePubkeys = Array.from(new Set(pubkeys.filter(Boolean)))
|
|
if (uniquePubkeys.length === 0) {
|
|
return { unsubscribe() {} }
|
|
}
|
|
|
|
const seedRelays = Array.from(pool.relays.keys())
|
|
const mailboxSeedSub = seedRelays.length
|
|
? pool.request(seedRelays, { kinds: [10002], authors: uniquePubkeys }).subscribe((event) => {
|
|
eventStore.add(event)
|
|
})
|
|
: undefined
|
|
|
|
const outboxMap$ = of(uniquePubkeys.map((pubkey) => ({ pubkey }))).pipe(
|
|
includeMailboxes(eventStore),
|
|
map((pointers) => (seedRelays.length > 0 ? setFallbackRelays(pointers, seedRelays) : pointers)),
|
|
map((pointers) => selectOptimalRelays(pointers, { maxConnections: 8, maxRelaysPerUser: 3 })),
|
|
map(createOutboxMap),
|
|
)
|
|
|
|
const profileSub = pool.outboxSubscription(outboxMap$, { kinds: [0] }).subscribe((message) => {
|
|
if (message !== "EOSE") eventStore.add(message)
|
|
})
|
|
|
|
return {
|
|
unsubscribe() {
|
|
profileSub.unsubscribe()
|
|
mailboxSeedSub?.unsubscribe()
|
|
},
|
|
}
|
|
}
|
|
|
|
export const useTenant = () => createResource(() => getTenant(account()!.pubkey))
|
|
|
|
export const useTenantRelays = () => createResource(() => listTenantRelays(account()!.pubkey))
|
|
|
|
export const useTenantInvoices = () => createResource(() => listTenantInvoices(account()!.pubkey))
|
|
|
|
export const useRelay = (relayId: () => string) => createResource(relayId, getRelay)
|
|
|
|
export const useAdminTenants = () => createResource(listTenants)
|
|
|
|
export const useAdminRelays = () => createResource(listRelays)
|
|
|
|
export const useAdminTenant = (pubkey: () => string) => createResource(pubkey, getTenant)
|
|
|
|
export const useAdminTenantRelays = (pubkey: () => string) => createResource(pubkey, listTenantRelays)
|
|
|
|
export const createRelayForActiveTenant = (input: CreateRelayInput) => createRelay({ ...input, tenant: account()!.pubkey })
|
|
|
|
export const updateActiveTenantBilling = (nwc_url: string) => updateTenantBilling(account()!.pubkey, { nwc_url })
|
|
|
|
export const updateRelayById = (id: string, input: UpdateRelayInput) => updateRelay(id, input)
|
|
|
|
export const updateRelayPlanById = (id: string, plan: string) => updateRelay(id, { plan })
|
|
|
|
export const deactivateRelayById = (id: string) => deactivateRelay(id)
|
|
|
|
export async function getRelayMembers(url: string) {
|
|
const management = new RelayManagement(new NostrRelay(url), account()!.signer)
|
|
|
|
try {
|
|
return await management.listAllowedPubkeys()
|
|
} catch {
|
|
return []
|
|
}
|
|
}
|
|
|
|
export type { Relay, Tenant }
|