Switch to pnpm, use new welshman stuff

This commit is contained in:
Jon Staab
2025-04-09 15:32:18 -07:00
parent 33902dbefe
commit 489707b9b2
29 changed files with 226 additions and 15744 deletions

View File

@@ -1,2 +1,2 @@
npm run lint pnpm run lint
npm run check pnpm run check

View File

@@ -8,8 +8,8 @@ If you would like to be interoperable with Flotilla, please check out this draft
To run your own Flotilla, it's as simple as: To run your own Flotilla, it's as simple as:
- `npm install` - `pnpm install`
- `npm run build` - `pnpm run build`
- `npx serve build` - `npx serve build`
## Environment ## Environment
@@ -65,12 +65,12 @@ git clone https://github.com/coracle-social/flotilla.git
cd ~/flotilla cd ~/flotilla
nvm install nvm install
nvm use nvm use
npm i pnpm i
# Optionally create and populate .env.local to suit your use case # Optionally create and populate .env.local to suit your use case
# Build the app # Build the app
NODE_OPTIONS=--max_old_space_size=16384 npm run build NODE_OPTIONS=--max_old_space_size=16384 pnpm run build
# Exit back to root # Exit back to root
exit exit
@@ -108,4 +108,4 @@ Now, visit your domain. You should be all set up!
# Development # Development
Run `npm run dev` to get a dev server, and `npm run check:watch` to watch for typescript errors. When you're ready to commit, run `npm run format && npm run lint` and fix any errors that come up. Run `pnpm run dev` to get a dev server, and `pnpm run check:watch` to watch for typescript errors. When you're ready to commit, run `pnpm run format && pnpm run lint` and fix any errors that come up.

15449
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@@ -51,16 +51,17 @@
"@types/qrcode": "^1.5.5", "@types/qrcode": "^1.5.5",
"@vite-pwa/assets-generator": "^0.2.6", "@vite-pwa/assets-generator": "^0.2.6",
"@vite-pwa/sveltekit": "^0.6.6", "@vite-pwa/sveltekit": "^0.6.6",
"@welshman/app": "^0.0.44", "@welshman/app": "^0.1.2",
"@welshman/content": "^0.1.1", "@welshman/content": "^0.1.3",
"@welshman/dvm": "^0.0.15", "@welshman/dvm": "^0.1.2",
"@welshman/editor": "^0.1.0", "@welshman/editor": "^0.1.2",
"@welshman/feeds": "^0.1.0", "@welshman/feeds": "^0.1.2",
"@welshman/lib": "^0.1.0", "@welshman/lib": "^0.1.2",
"@welshman/net": "^0.0.49", "@welshman/net": "^0.1.2",
"@welshman/signer": "^0.1.1", "@welshman/relay": "^0.1.2",
"@welshman/store": "^0.1.0", "@welshman/signer": "^0.1.4",
"@welshman/util": "^0.1.0", "@welshman/store": "^0.1.3",
"@welshman/util": "^0.1.3",
"daisyui": "^4.12.10", "daisyui": "^4.12.10",
"date-picker-svelte": "^2.13.0", "date-picker-svelte": "^2.13.0",
"dotenv": "^16.4.5", "dotenv": "^16.4.5",
@@ -72,5 +73,20 @@
"nostr-tools": "^2.7.2", "nostr-tools": "^2.7.2",
"prettier-plugin-tailwindcss": "^0.6.5", "prettier-plugin-tailwindcss": "^0.6.5",
"qrcode": "^1.5.4" "qrcode": "^1.5.4"
},
"pnpm": {
"overrides": {
"@welshman/lib": "link:../welshman/packages/lib",
"@welshman/util": "link:../welshman/packages/util",
"@welshman/app": "link:../welshman/packages/app",
"@welshman/content": "link:../welshman/packages/content",
"@welshman/dvm": "link:../welshman/packages/dvm",
"@welshman/feeds": "link:../welshman/packages/feeds",
"@welshman/net": "link:../welshman/packages/net",
"@welshman/relay": "link:../welshman/packages/relay",
"@welshman/signer": "link:../welshman/packages/signer",
"@welshman/store": "link:../welshman/packages/store",
"@welshman/editor": "link:../welshman/packages/editor"
}
} }
} }

BIN
pnpm-lock.yaml generated Normal file

Binary file not shown.

View File

@@ -1,6 +1,6 @@
import * as nip19 from "nostr-tools/nip19" import * as nip19 from "nostr-tools/nip19"
import {get} from "svelte/store" import {get} from "svelte/store"
import {ctx, randomId, uniq, equals} from "@welshman/lib" import {randomId, poll, uniq, equals} from "@welshman/lib"
import { import {
DELETE, DELETE,
REPORT, REPORT,
@@ -31,7 +31,7 @@ import {
unionFilters, unionFilters,
} from "@welshman/util" } from "@welshman/util"
import type {TrustedEvent, Filter, EventContent, EventTemplate} from "@welshman/util" import type {TrustedEvent, Filter, EventContent, EventTemplate} from "@welshman/util"
import {PublishStatus, AuthStatus, SocketStatus} from "@welshman/net" import {Pool, PublishStatus, AuthStatus, SocketStatus} from "@welshman/net"
import {Nip59, makeSecret, stamp, Nip46Broker} from "@welshman/signer" import {Nip59, makeSecret, stamp, Nip46Broker} from "@welshman/signer"
import { import {
pubkey, pubkey,
@@ -54,6 +54,7 @@ import {
dropSession, dropSession,
tagEventForComment, tagEventForComment,
tagEventForQuote, tagEventForQuote,
Router,
} from "@welshman/app" } from "@welshman/app"
import type {Thunk} from "@welshman/app" import type {Thunk} from "@welshman/app"
import { import {
@@ -101,7 +102,7 @@ export const prependParent = (parent: TrustedEvent | undefined, {content, tags}:
id: parent.id, id: parent.id,
kind: parent.kind, kind: parent.kind,
author: parent.pubkey, author: parent.pubkey,
relays: ctx.app.router.Event(parent).limit(3).getUrls(), relays: Router.get().Event(parent).limit(3).getUrls(),
}) })
tags = [...tags, tagEventForQuote(parent)] tags = [...tags, tagEventForQuote(parent)]
@@ -203,7 +204,7 @@ export const nip29 = {
export const addSpaceMembership = async (url: string) => { export const addSpaceMembership = async (url: string) => {
const list = get(userMembership) || makeList({kind: GROUPS}) const list = get(userMembership) || makeList({kind: GROUPS})
const event = await addToListPublicly(list, ["r", url]).reconcile(nip44EncryptToSelf) const event = await addToListPublicly(list, ["r", url]).reconcile(nip44EncryptToSelf)
const relays = uniq([...ctx.app.router.FromUser().getUrls(), ...getRelayTagValues(event.tags)]) const relays = uniq([...Router.get().FromUser().getUrls(), ...getRelayTagValues(event.tags)])
return publishThunk({event, relays}) return publishThunk({event, relays})
} }
@@ -212,11 +213,7 @@ export const removeSpaceMembership = async (url: string) => {
const list = get(userMembership) || makeList({kind: GROUPS}) const list = get(userMembership) || makeList({kind: GROUPS})
const pred = (t: string[]) => t[t[0] === "r" ? 1 : 2] === url const pred = (t: string[]) => t[t[0] === "r" ? 1 : 2] === url
const event = await removeFromListByPredicate(list, pred).reconcile(nip44EncryptToSelf) const event = await removeFromListByPredicate(list, pred).reconcile(nip44EncryptToSelf)
const relays = uniq([ const relays = uniq([url, ...Router.get().FromUser().getUrls(), ...getRelayTagValues(event.tags)])
url,
...ctx.app.router.FromUser().getUrls(),
...getRelayTagValues(event.tags),
])
return publishThunk({event, relays}) return publishThunk({event, relays})
} }
@@ -228,7 +225,7 @@ export const addRoomMembership = async (url: string, room: string, name: string)
["group", room, url, name], ["group", room, url, name],
] ]
const event = await addToListPublicly(list, ...newTags).reconcile(nip44EncryptToSelf) const event = await addToListPublicly(list, ...newTags).reconcile(nip44EncryptToSelf)
const relays = uniq([...ctx.app.router.FromUser().getUrls(), ...getRelayTagValues(event.tags)]) const relays = uniq([...Router.get().FromUser().getUrls(), ...getRelayTagValues(event.tags)])
return publishThunk({event, relays}) return publishThunk({event, relays})
} }
@@ -237,11 +234,7 @@ export const removeRoomMembership = async (url: string, room: string) => {
const list = get(userMembership) || makeList({kind: GROUPS}) const list = get(userMembership) || makeList({kind: GROUPS})
const pred = (t: string[]) => equals(["group", room, url], t.slice(0, 3)) const pred = (t: string[]) => equals(["group", room, url], t.slice(0, 3))
const event = await removeFromListByPredicate(list, pred).reconcile(nip44EncryptToSelf) const event = await removeFromListByPredicate(list, pred).reconcile(nip44EncryptToSelf)
const relays = uniq([ const relays = uniq([url, ...Router.get().FromUser().getUrls(), ...getRelayTagValues(event.tags)])
url,
...ctx.app.router.FromUser().getUrls(),
...getRelayTagValues(event.tags),
])
return publishThunk({event, relays}) return publishThunk({event, relays})
} }
@@ -263,7 +256,7 @@ export const setRelayPolicy = (url: string, read: boolean, write: boolean) => {
relays: [ relays: [
url, url,
...INDEXER_RELAYS, ...INDEXER_RELAYS,
...ctx.app.router.FromUser().getUrls(), ...Router.get().FromUser().getUrls(),
...userRoomsByUrl.get().keys(), ...userRoomsByUrl.get().keys(),
], ],
}) })
@@ -284,7 +277,7 @@ export const setInboxRelayPolicy = (url: string, enabled: boolean) => {
event: createEvent(list.kind, {tags}), event: createEvent(list.kind, {tags}),
relays: [ relays: [
...INDEXER_RELAYS, ...INDEXER_RELAYS,
...ctx.app.router.FromUser().getUrls(), ...Router.get().FromUser().getUrls(),
...userRoomsByUrl.get().keys(), ...userRoomsByUrl.get().keys(),
], ],
}) })
@@ -294,9 +287,9 @@ export const setInboxRelayPolicy = (url: string, enabled: boolean) => {
// Relay access // Relay access
export const checkRelayAccess = async (url: string, claim = "") => { export const checkRelayAccess = async (url: string, claim = "") => {
const connection = ctx.net.pool.get(url) const socket = Pool.getSingleton().get(url)
await connection.auth.attempt(5000) await socket.auth.attemptAuth(signer.get().sign)
const thunk = publishThunk({ const thunk = publishThunk({
event: createEvent(AUTH_JOIN, {tags: [["claim", claim]]}), event: createEvent(AUTH_JOIN, {tags: [["claim", claim]]}),
@@ -307,7 +300,7 @@ export const checkRelayAccess = async (url: string, claim = "") => {
if (result[url].status === PublishStatus.Failure) { if (result[url].status === PublishStatus.Failure) {
const message = const message =
connection.auth.message?.replace(/^.*: /, "") || socket.auth.details?.replace(/^.*: /, "") ||
result[url].message?.replace(/^.*: /, "") || result[url].message?.replace(/^.*: /, "") ||
"join request rejected" "join request rejected"
@@ -328,26 +321,30 @@ export const checkRelayProfile = async (url: string) => {
} }
export const checkRelayConnection = async (url: string) => { export const checkRelayConnection = async (url: string) => {
const connection = ctx.net.pool.get(url) const socket = Pool.getSingleton().get(url)
await connection.socket.open() socket.attemptToOpen()
await connection.socket.wait(3000)
if (connection.socket.status !== SocketStatus.Open) { await poll({
signal: AbortSignal.timeout(3000),
condition: () => socket.status === SocketStatus.Open,
})
if (socket.status !== SocketStatus.Open) {
return `Failed to connect` return `Failed to connect`
} }
} }
export const checkRelayAuth = async (url: string, timeout = 3000) => { export const checkRelayAuth = async (url: string, timeout = 3000) => {
const connection = ctx.net.pool.get(url) const socket = Pool.getSingleton().get(url)
const okStatuses = [AuthStatus.None, AuthStatus.Ok] const okStatuses = [AuthStatus.None, AuthStatus.Ok]
await connection.auth.attempt(timeout) await socket.auth.attemptAuth(signer.get().sign)
// Only raise an error if it's not a timeout. // Only raise an error if it's not a timeout.
// If it is, odds are the problem is with our signer, not the relay // If it is, odds are the problem is with our signer, not the relay
if (!okStatuses.includes(connection.auth.status) && connection.auth.message) { if (!okStatuses.includes(socket.auth.status) && socket.auth.details) {
return `Failed to authenticate (${connection.auth.message})` return `Failed to authenticate (${socket.auth.details})`
} }
} }
@@ -384,7 +381,7 @@ export const sendWrapped = async ({
await Promise.all( await Promise.all(
uniq(pubkeys).map(async recipient => ({ uniq(pubkeys).map(async recipient => ({
event: await nip59.wrap(recipient, stamp(template)), event: await nip59.wrap(recipient, stamp(template)),
relays: ctx.app.router.PubkeyInbox(recipient).getUrls(), relays: Router.get().PubkeyInbox(recipient).getUrls(),
delay, delay,
})), })),
), ),

View File

@@ -2,14 +2,15 @@
import type {Snippet} from "svelte" import type {Snippet} from "svelte"
import {onMount} from "svelte" import {onMount} from "svelte"
import {int, nthNe, MINUTE, sortBy, remove} from "@welshman/lib" import {int, nthNe, MINUTE, sortBy, remove} from "@welshman/lib"
import {load} from "@welshman/net"
import type {TrustedEvent, EventContent} from "@welshman/util" import type {TrustedEvent, EventContent} from "@welshman/util"
import {createEvent, DIRECT_MESSAGE, INBOX_RELAYS} from "@welshman/util" import {createEvent, DIRECT_MESSAGE, INBOX_RELAYS} from "@welshman/util"
import { import {
pubkey, pubkey,
Router,
tagPubkey, tagPubkey,
formatTimestampAsDate, formatTimestampAsDate,
inboxRelaySelectionsByPubkey, inboxRelaySelectionsByPubkey,
load,
} from "@welshman/app" } from "@welshman/app"
import Icon from "@lib/components/Icon.svelte" import Icon from "@lib/components/Icon.svelte"
import Link from "@lib/components/Link.svelte" import Link from "@lib/components/Link.svelte"
@@ -106,7 +107,10 @@
onMount(() => { onMount(() => {
// Don't use loadInboxRelaySelection because we want to force reload // Don't use loadInboxRelaySelection because we want to force reload
load({filters: [{kinds: [INBOX_RELAYS], authors: others}]}) load({
relays: Router.get().FromPubkeys(others).getUrls(),
filters: [{kinds: [INBOX_RELAYS], authors: others}],
})
const observer = new ResizeObserver(() => { const observer = new ResizeObserver(() => {
dynamicPadding!.style.minHeight = `${chatCompose!.offsetHeight}px` dynamicPadding!.style.minHeight = `${chatCompose!.offsetHeight}px`

View File

@@ -1,8 +1,8 @@
<script lang="ts"> <script lang="ts">
import {nip19} from "nostr-tools" import * as nip19 from "nostr-tools/nip19"
import {goto} from "$app/navigation" import {goto} from "$app/navigation"
import {ctx, nthEq} from "@welshman/lib" import {nthEq} from "@welshman/lib"
import {tracker, repository} from "@welshman/app" import {Router, tracker, repository} from "@welshman/app"
import {Address, DIRECT_MESSAGE, MESSAGE, THREAD, EVENT_TIME} from "@welshman/util" import {Address, DIRECT_MESSAGE, MESSAGE, THREAD, EVENT_TIME} from "@welshman/util"
import {scrollToEvent} from "@lib/html" import {scrollToEvent} from "@lib/html"
import Button from "@lib/components/Button.svelte" import Button from "@lib/components/Button.svelte"
@@ -16,10 +16,7 @@
const {id, identifier, kind, pubkey, relays: relayHints = []} = value const {id, identifier, kind, pubkey, relays: relayHints = []} = value
const idOrAddress = id || new Address(kind, pubkey, identifier).toString() const idOrAddress = id || new Address(kind, pubkey, identifier).toString()
const mergedRelays = [ const mergedRelays = [...relays, ...Router.get().Quote(event, idOrAddress, relayHints).getUrls()]
...relays,
...ctx.app.router.Quote(event, idOrAddress, relayHints).getUrls(),
]
const quote = deriveEvent(idOrAddress, mergedRelays) const quote = deriveEvent(idOrAddress, mergedRelays)
const entity = id const entity = id
? nip19.neventEncode({id, relays: mergedRelays}) ? nip19.neventEncode({id, relays: mergedRelays})

View File

@@ -2,9 +2,10 @@
import {onMount} from "svelte" import {onMount} from "svelte"
import {max} from "@welshman/lib" import {max} from "@welshman/lib"
import {COMMENT} from "@welshman/util" import {COMMENT} from "@welshman/util"
import {load} from "@welshman/net"
import {deriveEvents} from "@welshman/store" import {deriveEvents} from "@welshman/store"
import type {TrustedEvent} from "@welshman/util" import type {TrustedEvent} from "@welshman/util"
import {formatTimestampRelative, repository, load} from "@welshman/app" import {formatTimestampRelative, repository} from "@welshman/app"
import {notifications} from "@app/notifications" import {notifications} from "@app/notifications"
import Icon from "@lib/components/Icon.svelte" import Icon from "@lib/components/Icon.svelte"

View File

@@ -1,6 +1,6 @@
<script lang="ts"> <script lang="ts">
import {nip19} from "nostr-tools" import * as nip19 from "nostr-tools/nip19"
import {ctx} from "@welshman/lib" import {Router} from "@welshman/app"
import type {TrustedEvent} from "@welshman/util" import type {TrustedEvent} from "@welshman/util"
import Icon from "@lib/components/Icon.svelte" import Icon from "@lib/components/Icon.svelte"
import FieldInline from "@lib/components/FieldInline.svelte" import FieldInline from "@lib/components/FieldInline.svelte"
@@ -15,7 +15,7 @@
const {url, event}: Props = $props() const {url, event}: Props = $props()
const relays = url ? [url] : ctx.app.router.Event(event).getUrls() const relays = url ? [url] : Router.get().Event(event).getUrls()
const nevent1 = nip19.neventEncode({...event, relays}) const nevent1 = nip19.neventEncode({...event, relays})
const npub1 = nip19.npubEncode(event.pubkey) const npub1 = nip19.npubEncode(event.pubkey)
const json = JSON.stringify(event, null, 2) const json = JSON.stringify(event, null, 2)

View File

@@ -24,7 +24,7 @@
const signUp = () => pushModal(SignUp) const signUp = () => pushModal(SignUp)
const onSuccess = async (session: Session, relays: string[] = []) => { const onSuccess = async (session: Session, relays: string[] = []) => {
await loadUserData(session.pubkey, {relays}) await loadUserData(session.pubkey, relays)
addSession(session) addSession(session)
pushToast({message: "Successfully logged in!"}) pushToast({message: "Successfully logged in!"})

View File

@@ -1,11 +1,10 @@
<script lang="ts"> <script lang="ts">
import cx from "classnames" import cx from "classnames"
import type {Snippet} from "svelte" import type {Snippet} from "svelte"
import {nip19} from "nostr-tools" import * as nip19 from "nostr-tools/nip19"
import {ctx} from "@welshman/lib"
import {getListTags, getPubkeyTagValues} from "@welshman/util" import {getListTags, getPubkeyTagValues} from "@welshman/util"
import type {TrustedEvent} from "@welshman/util" import type {TrustedEvent} from "@welshman/util"
import {formatTimestamp, userMutes} from "@welshman/app" import {formatTimestamp, Router, userMutes} from "@welshman/app"
import Link from "@lib/components/Link.svelte" import Link from "@lib/components/Link.svelte"
import Icon from "@lib/components/Icon.svelte" import Icon from "@lib/components/Icon.svelte"
import Button from "@lib/components/Button.svelte" import Button from "@lib/components/Button.svelte"
@@ -27,7 +26,7 @@
class?: string class?: string
} = $props() } = $props()
const relays = ctx.app.router.Event(event).getUrls() const relays = Router.get().Event(event).getUrls()
const nevent = nip19.neventEncode({id: event.id, relays}) const nevent = nip19.neventEncode({id: event.id, relays})
const ignoreMute = () => { const ignoreMute = () => {

View File

@@ -1,9 +1,9 @@
<script lang="ts"> <script lang="ts">
import {onMount} from "svelte" import {onMount} from "svelte"
import {ctx} from "@welshman/lib"
import type {Filter} from "@welshman/util" import type {Filter} from "@welshman/util"
import {deriveEvents} from "@welshman/store" import {deriveEvents} from "@welshman/store"
import {repository, load, loadRelaySelections, formatTimestampRelative} from "@welshman/app" import {load} from "@welshman/net"
import {Router, repository, loadRelaySelections, formatTimestampRelative} from "@welshman/app"
import Icon from "@lib/components/Icon.svelte" import Icon from "@lib/components/Icon.svelte"
import Link from "@lib/components/Link.svelte" import Link from "@lib/components/Link.svelte"
import Profile from "@app/components/Profile.svelte" import Profile from "@app/components/Profile.svelte"
@@ -22,7 +22,7 @@
// Load at least one note, regardless of time frame // Load at least one note, regardless of time frame
load({ load({
filters: [{authors: [pubkey], limit: 1}], filters: [{authors: [pubkey], limit: 1}],
relays: ctx.app.router.FromPubkeys([pubkey]).getUrls(), relays: Router.get().FromPubkeys([pubkey]).getUrls(),
}) })
}) })
</script> </script>

View File

@@ -1,5 +1,4 @@
<script lang="ts"> <script lang="ts">
import {ctx} from "@welshman/lib"
import type {Profile} from "@welshman/util" import type {Profile} from "@welshman/util"
import { import {
createEvent, createEvent,
@@ -8,7 +7,7 @@
createProfile, createProfile,
isPublishedProfile, isPublishedProfile,
} from "@welshman/util" } from "@welshman/util"
import {pubkey, profilesByPubkey, publishThunk} from "@welshman/app" import {Router, pubkey, profilesByPubkey, publishThunk} from "@welshman/app"
import Button from "@lib/components/Button.svelte" import Button from "@lib/components/Button.svelte"
import ProfileEditForm from "@app/components/ProfileEditForm.svelte" import ProfileEditForm from "@app/components/ProfileEditForm.svelte"
import {clearModals} from "@app/modal" import {clearModals} from "@app/modal"
@@ -19,7 +18,7 @@
const back = () => history.back() const back = () => history.back()
const onsubmit = (profile: Profile) => { const onsubmit = (profile: Profile) => {
const relays = ctx.app.router.FromUser().getUrls() const relays = Router.get().FromUser().getUrls()
const template = isPublishedProfile(profile) ? editProfile(profile) : createProfile(profile) const template = isPublishedProfile(profile) ? editProfile(profile) : createProfile(profile)
const event = createEvent(template.kind, template) const event = createEvent(template.kind, template)

View File

@@ -5,7 +5,9 @@
import {REACTION, getTag, REPORT, DELETE} from "@welshman/util" import {REACTION, getTag, REPORT, DELETE} from "@welshman/util"
import type {TrustedEvent} from "@welshman/util" import type {TrustedEvent} from "@welshman/util"
import {deriveEvents} from "@welshman/store" import {deriveEvents} from "@welshman/store"
import {pubkey, repository, load, displayProfileByPubkey} from "@welshman/app" import type {MultiRequest} from "@welshman/net"
import {load, request, RequestEvent} from "@welshman/net"
import {pubkey, repository, displayProfileByPubkey} from "@welshman/app"
import {displayList} from "@lib/util" import {displayList} from "@lib/util"
import {isMobile, preventDefault, stopPropagation} from "@lib/html" import {isMobile, preventDefault, stopPropagation} from "@lib/html"
import Icon from "@lib/components/Icon.svelte" import Icon from "@lib/components/Icon.svelte"
@@ -51,17 +53,27 @@
) )
onMount(() => { onMount(() => {
let req: MultiRequest
if (url) { if (url) {
load({ req = request({
relays: [url], relays: [url],
filters: [{kinds: [REACTION, REPORT, DELETE], "#e": [event.id]}], filters: [{kinds: [REACTION, REPORT, DELETE], "#e": [event.id]}],
onEvent: batch(300, (events: TrustedEvent[]) => { })
req.on(
RequestEvent.Event,
batch(300, (events: TrustedEvent[]) => {
load({ load({
relays: [url], relays: [url],
filters: [{kinds: [DELETE], "#e": events.map(e => e.id)}], filters: [{kinds: [DELETE], "#e": events.map(e => e.id)}],
}) })
}), }),
}) )
}
return () => {
req?.close()
} }
}) })
</script> </script>

View File

@@ -1,6 +1,7 @@
<script lang="ts"> <script lang="ts">
import {onMount} from "svelte" import {onMount} from "svelte"
import {ctx, sleep} from "@welshman/lib" import {sleep} from "@welshman/lib"
import {Pool, AuthStatus} from "@welshman/net"
import {displayRelayUrl} from "@welshman/util" import {displayRelayUrl} from "@welshman/util"
import {preventDefault} from "@lib/html" import {preventDefault} from "@lib/html"
import Icon from "@lib/components/Icon.svelte" import Icon from "@lib/components/Icon.svelte"
@@ -17,7 +18,7 @@
const back = () => history.back() const back = () => history.back()
const next = () => { const next = () => {
if (!error && ctx.net.pool.get(url).stats.lastAuth === 0) { if (!error && Pool.getSingleton().get(url).auth.status === AuthStatus.None) {
pushModal(SpaceVisitConfirm, {url}, {replaceState: true}) pushModal(SpaceVisitConfirm, {url}, {replaceState: true})
} else { } else {
confirmSpaceVisit(url) confirmSpaceVisit(url)

View File

@@ -1,7 +1,7 @@
<script lang="ts"> <script lang="ts">
import {onMount} from "svelte" import {onMount} from "svelte"
import {sleep, identity, nthEq} from "@welshman/lib" import {sleep, identity, nthEq} from "@welshman/lib"
import {load} from "@welshman/app" import {load} from "@welshman/net"
import {displayRelayUrl, AUTH_INVITE} from "@welshman/util" import {displayRelayUrl, AUTH_INVITE} from "@welshman/util"
import {slide} from "@lib/transition" import {slide} from "@lib/transition"
import Spinner from "@lib/components/Spinner.svelte" import Spinner from "@lib/components/Spinner.svelte"

View File

@@ -1,6 +1,7 @@
<script lang="ts"> <script lang="ts">
import {ctx, tryCatch} from "@welshman/lib" import {tryCatch} from "@welshman/lib"
import {isRelayUrl, normalizeRelayUrl} from "@welshman/util" import {isRelayUrl, normalizeRelayUrl} from "@welshman/util"
import {Pool, AuthStatus} from "@welshman/net"
import {preventDefault} from "@lib/html" import {preventDefault} from "@lib/html"
import Spinner from "@lib/components/Spinner.svelte" import Spinner from "@lib/components/Spinner.svelte"
import Button from "@lib/components/Button.svelte" import Button from "@lib/components/Button.svelte"
@@ -25,9 +26,9 @@
return pushToast({theme: "error", message: error}) return pushToast({theme: "error", message: error})
} }
const connection = ctx.net.pool.get(url) const socket = Pool.getSingleton().get(url)
if (connection.stats.lastAuth === 0) { if (socket.auth.status === AuthStatus.None) {
pushModal(SpaceJoinConfirm, {url}, {replaceState: true}) pushModal(SpaceJoinConfirm, {url}, {replaceState: true})
} else { } else {
await confirmSpaceJoin(url) await confirmSpaceJoin(url)

View File

@@ -1,11 +1,9 @@
import {mount} from "svelte" import {mount} from "svelte"
import type {Writable} from "svelte/store" import type {Writable} from "svelte/store"
import {get} from "svelte/store" import {get} from "svelte/store"
import {Editor} from "@tiptap/core"
import {ctx} from "@welshman/lib"
import type {StampedEvent} from "@welshman/util" import type {StampedEvent} from "@welshman/util"
import {signer, profileSearch} from "@welshman/app" import {Router, signer, profileSearch} from "@welshman/app"
import {MentionSuggestion, WelshmanExtension} from "@welshman/editor" import {Editor, MentionSuggestion, WelshmanExtension} from "@welshman/editor"
import {getSetting, userSettingValues} from "@app/state" import {getSetting, userSettingValues} from "@app/state"
import {MentionNodeView} from "./MentionNodeView" import {MentionNodeView} from "./MentionNodeView"
import ProfileSuggestion from "./ProfileSuggestion.svelte" import ProfileSuggestion from "./ProfileSuggestion.svelte"
@@ -84,7 +82,7 @@ export const makeEditor = ({
MentionSuggestion({ MentionSuggestion({
editor: (this as any).editor, editor: (this as any).editor,
search: (term: string) => get(profileSearch).searchValues(term), search: (term: string) => get(profileSearch).searchValues(term),
getRelays: (pubkey: string) => ctx.app.router.FromPubkeys([pubkey]).getUrls(), getRelays: (pubkey: string) => Router.get().FromPubkeys([pubkey]).getUrls(),
createSuggestion: (value: string) => { createSuggestion: (value: string) => {
const target = document.createElement("div") const target = document.createElement("div")

View File

@@ -28,15 +28,14 @@ import {
} from "@welshman/util" } from "@welshman/util"
import type {TrustedEvent, Filter, List} from "@welshman/util" import type {TrustedEvent, Filter, List} from "@welshman/util"
import {feedFromFilters, makeRelayFeed, makeIntersectionFeed} from "@welshman/feeds" import {feedFromFilters, makeRelayFeed, makeIntersectionFeed} from "@welshman/feeds"
import type {Subscription, SubscribeRequestWithHandlers} from "@welshman/net" import {load, request, RequestEvent} from "@welshman/net"
import type {MultiRequest} from "@welshman/net"
import type {AppSyncOpts, Thunk} from "@welshman/app" import type {AppSyncOpts, Thunk} from "@welshman/app"
import { import {
subscribe,
load,
repository, repository,
pull, pull,
hasNegentropy, hasNegentropy,
thunkWorker, thunkQueue,
createFeedController, createFeedController,
loadRelay, loadRelay,
loadMutes, loadMutes,
@@ -152,13 +151,11 @@ export const makeFeed = ({
onExhausted, onExhausted,
}) })
const sub = subscribe({ const req = request({relays, filters: subscriptionFilters})
relays,
filters: subscriptionFilters, req.on(RequestEvent.Event, (e: TrustedEvent) => {
onEvent: (e: TrustedEvent) => { if (matchFilters(feedFilters, e)) insertEvent(e)
if (matchFilters(feedFilters, e)) insertEvent(e) if (e.kind === DELETE) handleDelete(e)
if (e.kind === DELETE) handleDelete(e)
},
}) })
const scroller = createScroller({ const scroller = createScroller({
@@ -176,14 +173,14 @@ export const makeFeed = ({
}, },
}) })
thunkWorker.addGlobalHandler(onThunk) const unsubscribe = thunkQueue.subscribe(onThunk)
return { return {
events, events,
cleanup: () => { cleanup: () => {
sub.close() req.close()
unsubscribe()
scroller.stop() scroller.stop()
thunkWorker.removeGlobalHandler(onThunk)
}, },
} }
} }
@@ -245,22 +242,22 @@ export const makeCalendarFeed = ({
} }
} }
const sub = subscribe({ const req = request({relays, filters: subscriptionFilters})
relays,
filters: subscriptionFilters, req.on(RequestEvent.Event, (e: TrustedEvent) => {
onEvent: (e: TrustedEvent) => { if (matchFilters(feedFilters, e)) insertEvent(e)
if (matchFilters(feedFilters, e)) insertEvent(e)
},
}) })
const loadTimeframe = (since: number, until: number) => { const loadTimeframe = (since: number, until: number) => {
const hashes = daysBetween(since, until).map(String) const hashes = daysBetween(since, until).map(String)
load({ const req = request({
relays, relays,
autoClose: true,
filters: [{kinds: [EVENT_TIME], "#D": hashes}], filters: [{kinds: [EVENT_TIME], "#D": hashes}],
onEvent: insertEvent,
}) })
req.on(RequestEvent.Event, insertEvent)
} }
const maybeExhausted = () => { const maybeExhausted = () => {
@@ -302,15 +299,15 @@ export const makeCalendarFeed = ({
}, },
}) })
thunkWorker.addGlobalHandler(onThunk) const unsubscribe = thunkQueue.subscribe(onThunk)
return { return {
events, events,
cleanup: () => { cleanup: () => {
thunkWorker.removeGlobalHandler(onThunk)
backwardScroller.stop() backwardScroller.stop()
forwardScroller.stop() forwardScroller.stop()
sub.close() unsubscribe()
req.close()
}, },
} }
} }
@@ -332,7 +329,7 @@ export const loadAlertStatuses = (pubkey: string) =>
// Application requests // Application requests
export const listenForNotifications = () => { export const listenForNotifications = () => {
const subs: Subscription[] = [] const reqs: MultiRequest[] = []
for (const [url, allRooms] of userRoomsByUrl.get()) { for (const [url, allRooms] of userRoomsByUrl.get()) {
// Limit how many rooms we load at a time, since we have to send a separate filter // Limit how many rooms we load at a time, since we have to send a separate filter
@@ -350,8 +347,8 @@ export const listenForNotifications = () => {
], ],
}) })
subs.push( reqs.push(
subscribe({ request({
relays: [url], relays: [url],
filters: [ filters: [
{kinds: [THREAD, EVENT_TIME], since: now()}, {kinds: [THREAD, EVENT_TIME], since: now()},
@@ -363,25 +360,22 @@ export const listenForNotifications = () => {
} }
return () => { return () => {
for (const sub of subs) { for (const req of reqs) {
sub.close() req.close()
} }
} }
} }
export const loadUserData = ( export const loadUserData = (pubkey: string, relays: string[] = []) => {
pubkey: string,
request: Partial<SubscribeRequestWithHandlers> = {},
) => {
const promise = Promise.race([ const promise = Promise.race([
sleep(3000), sleep(3000),
Promise.all([ Promise.all([
loadInboxRelaySelections(pubkey, request), loadInboxRelaySelections(pubkey, relays),
loadMembership(pubkey, request), loadMembership(pubkey, relays),
loadSettings(pubkey, request), loadSettings(pubkey, relays),
loadProfile(pubkey, request), loadProfile(pubkey, relays),
loadFollows(pubkey, request), loadFollows(pubkey, relays),
loadMutes(pubkey, request), loadMutes(pubkey, relays),
loadAlertStatuses(pubkey), loadAlertStatuses(pubkey),
loadAlerts(pubkey), loadAlerts(pubkey),
]), ]),
@@ -396,10 +390,10 @@ export const loadUserData = (
await sleep(1000) await sleep(1000)
for (const pubkey of pubkeys) { for (const pubkey of pubkeys) {
loadMembership(pubkey, {relays}) loadMembership(pubkey, relays)
loadProfile(pubkey, {relays}) loadProfile(pubkey, relays)
loadFollows(pubkey, {relays}) loadFollows(pubkey, relays)
loadMutes(pubkey, {relays}) loadMutes(pubkey, relays)
} }
} }
}) })
@@ -408,4 +402,8 @@ export const loadUserData = (
} }
export const discoverRelays = (lists: List[]) => export const discoverRelays = (lists: List[]) =>
Promise.all(uniq(lists.flatMap(getRelayUrls)).filter(isShareableRelayUrl).map(loadRelay)) Promise.all(
uniq(lists.flatMap(getRelayUrls))
.filter(isShareableRelayUrl)
.map(url => loadRelay(url)),
)

View File

@@ -1,9 +1,7 @@
import twColors from "tailwindcss/colors" import twColors from "tailwindcss/colors"
import {get, derived} from "svelte/store" import {get, derived} from "svelte/store"
import {nip19} from "nostr-tools" import * as nip19 from "nostr-tools/nip19"
import { import {
ctx,
setContext,
remove, remove,
sortBy, sortBy,
sort, sort,
@@ -17,7 +15,9 @@ import {
memoize, memoize,
addToMapKey, addToMapKey,
identity, identity,
always,
} from "@welshman/lib" } from "@welshman/lib"
import {load} from "@welshman/net"
import { import {
getIdFilters, getIdFilters,
WRAP, WRAP,
@@ -46,12 +46,8 @@ import {Nip59, decrypt} from "@welshman/signer"
import { import {
pubkey, pubkey,
repository, repository,
load,
collection, collection,
profilesByPubkey, profilesByPubkey,
getDefaultAppContext,
getDefaultNetContext,
makeRouter,
tracker, tracker,
makeTrackerStore, makeTrackerStore,
makeRepositoryStore, makeRepositoryStore,
@@ -64,9 +60,12 @@ import {
thunks, thunks,
walkThunks, walkThunks,
signer, signer,
Router,
loadWithAsapMetaRelayUrls,
routerContext,
appContext,
} from "@welshman/app" } from "@welshman/app"
import type {Thunk, Relay} from "@welshman/app" import type {Thunk, Relay} from "@welshman/app"
import type {SubscribeRequestWithHandlers} from "@welshman/net"
import {deriveEvents, deriveEventsMapped, withGetter, synced} from "@welshman/store" import {deriveEvents, deriveEventsMapped, withGetter, synced} from "@welshman/store"
export const fromCsv = (s: string) => (s || "").split(",").filter(identity) export const fromCsv = (s: string) => (s || "").split(",").filter(identity)
@@ -162,10 +161,8 @@ export const imgproxy = (url: string, {w = 640, h = 1024} = {}) => {
export const entityLink = (entity: string) => `https://coracle.social/${entity}` export const entityLink = (entity: string) => `https://coracle.social/${entity}`
export const pubkeyLink = ( export const pubkeyLink = (pubkey: string, relays = Router.get().FromPubkeys([pubkey]).getUrls()) =>
pubkey: string, entityLink(nip19.nprofileEncode({pubkey, relays}))
relays = ctx.app.router.FromPubkeys([pubkey]).getUrls(),
) => entityLink(nip19.nprofileEncode({pubkey, relays}))
export const tagRoom = (room: string, url: string) => [ROOM, room] export const tagRoom = (room: string, url: string) => [ROOM, room]
@@ -283,15 +280,9 @@ export const deriveEventsForUrl = (url: string, filters: Filter[]) =>
// Context // Context
setContext({ appContext.dufflepudUrl = DUFFLEPUD_URL
net: getDefaultNetContext(),
app: getDefaultAppContext({ routerContext.getIndexerRelays = always(INDEXER_RELAYS)
dufflepudUrl: DUFFLEPUD_URL,
indexerRelays: INDEXER_RELAYS,
requestTimeout: 5000,
router: makeRouter(),
}),
})
// Settings // Settings
@@ -341,8 +332,8 @@ export const {
name: "settings", name: "settings",
store: settings, store: settings,
getKey: settings => settings.event.pubkey, getKey: settings => settings.event.pubkey,
load: (pubkey: string, request: Partial<SubscribeRequestWithHandlers> = {}) => load: (pubkey: string, relays: string[]) =>
load({...request, filters: [{kinds: [SETTINGS], authors: [pubkey]}]}), loadWithAsapMetaRelayUrls(pubkey, relays, [{kinds: [SETTINGS], authors: [pubkey]}]),
}) })
// Alerts // Alerts
@@ -419,8 +410,8 @@ export const {
name: "memberships", name: "memberships",
store: memberships, store: memberships,
getKey: list => list.event.pubkey, getKey: list => list.event.pubkey,
load: (pubkey: string, request: Partial<SubscribeRequestWithHandlers> = {}) => load: (pubkey: string, relays: string[]) =>
load({...request, filters: [{kinds: [GROUPS], authors: [pubkey]}]}), loadWithAsapMetaRelayUrls(pubkey, relays, [{kinds: [GROUPS], authors: [pubkey]}]),
}) })
// Chats // Chats

View File

@@ -7,51 +7,23 @@
import {dev} from "$app/environment" import {dev} from "$app/environment"
import {goto} from "$app/navigation" import {goto} from "$app/navigation"
import {bytesToHex, hexToBytes} from "@noble/hashes/utils" import {bytesToHex, hexToBytes} from "@noble/hashes/utils"
import { import {identity, sleep, defer, ago, WEEK, TaskQueue} from "@welshman/lib"
identity, import type {TrustedEvent, StampedEvent} from "@welshman/util"
sleep, import {WRAP} from "@welshman/util"
take,
sortBy,
defer,
ago,
now,
HOUR,
WEEK,
MONTH,
Worker,
} from "@welshman/lib"
import type {TrustedEvent} from "@welshman/util"
import {
MESSAGE,
PROFILE,
DELETE,
REACTION,
ZAP_RESPONSE,
FOLLOWS,
RELAYS,
INBOX_RELAYS,
WRAP,
getPubkeyTagValues,
getListTags,
} from "@welshman/util"
import {Nip46Broker, getPubkey, makeSecret} from "@welshman/signer" import {Nip46Broker, getPubkey, makeSecret} from "@welshman/signer"
import type {Socket} from "@welshman/net"
import {request, defaultSocketPolicies, makeSocketPolicyAuth} from "@welshman/net"
import { import {
relays,
handles,
loadRelay, loadRelay,
db, db,
initStorage, initStorage,
repository, repository,
pubkey, pubkey,
plaintext, defaultStorageAdapters,
freshness,
storageAdapters,
tracker,
session, session,
signer, signer,
dropSession, dropSession,
getRelayUrls, getRelayUrls,
subscribe,
userInboxRelaySelections, userInboxRelaySelections,
addSession, addSession,
} from "@welshman/app" } from "@welshman/app"
@@ -140,78 +112,20 @@
} }
}) })
initStorage("flotilla", 5, { initStorage("flotilla", 6, defaultStorageAdapters).then(async () => {
relays: storageAdapters.fromCollectionStore("url", relays, {throttle: 3000}),
handles: storageAdapters.fromCollectionStore("nip05", handles, {throttle: 3000}),
freshness: storageAdapters.fromObjectStore(freshness, {
throttle: 3000,
migrate: (data: {key: string; value: number}[]) => {
const cutoff = ago(HOUR)
return data.filter(({value}) => value > cutoff)
},
}),
plaintext: storageAdapters.fromObjectStore(plaintext, {
throttle: 3000,
migrate: (data: {key: string; value: number}[]) => data.slice(0, 10_000),
}),
events2: storageAdapters.fromRepositoryAndTracker(repository, tracker, {
throttle: 3000,
migrate: (events: TrustedEvent[]) => {
if (events.length < 15_000) {
return events
}
const NEVER_KEEP = 0
const ALWAYS_KEEP = Infinity
const reactionKinds = [REACTION, ZAP_RESPONSE, DELETE]
const metaKinds = [PROFILE, FOLLOWS, RELAYS, INBOX_RELAYS]
const sessionKeys = new Set(Object.keys(app.sessions.get()))
const userFollows = new Set(getPubkeyTagValues(getListTags(get(app.userFollows))))
const maxWot = get(app.maxWot)
const scoreEvent = (e: TrustedEvent) => {
const isFollowing = userFollows.has(e.pubkey)
// No need to keep a record of everyone who follows the current user
if (e.kind === FOLLOWS && !isFollowing) return NEVER_KEEP
// Drop room messages after a month, re-load on demand
if (e.kind === MESSAGE && e.created_at < ago(MONTH)) return NEVER_KEEP
// Always keep stuff by or tagging a signed in user
if (sessionKeys.has(e.pubkey)) return ALWAYS_KEEP
if (e.tags.some(t => sessionKeys.has(t[1]))) return ALWAYS_KEEP
// Get rid of irrelevant messages, reactions, and likes
if (e.wrap || e.kind === 4 || e.kind === WRAP) return NEVER_KEEP
if (reactionKinds.includes(e.kind)) return NEVER_KEEP
// If the user follows this person, use max wot score
let score = isFollowing ? maxWot : app.getUserWotScore(e.pubkey)
// Inflate the score for profiles/relays/follows to avoid redundant fetches
// Demote non-metadata type events, and introduce recency bias
score *= metaKinds.includes(e.kind) ? 2 : e.created_at / now()
return score
}
return take(
10_000,
sortBy(e => -scoreEvent(e), events),
)
},
}),
}).then(async () => {
await sleep(300) await sleep(300)
ready.resolve() ready.resolve()
}) })
// Unwrap gift wraps as they come in, but throttled defaultSocketPolicies.push(
const unwrapper = new Worker<TrustedEvent>({chunkSize: 10}) makeSocketPolicyAuth({
sign: (event: StampedEvent) => signer.get()?.sign(event),
shouldAuth: (socket: Socket) => true,
}),
)
unwrapper.addGlobalHandler(ensureUnwrapped) // Unwrap gift wraps as they come in, but throttled
const unwrapper = new TaskQueue<TrustedEvent>({batchSize: 10, processItem: ensureUnwrapped})
repository.on("update", ({added}) => { repository.on("update", ({added}) => {
if (!$canDecrypt) { if (!$canDecrypt) {
@@ -244,14 +158,14 @@
}) })
// Listen for chats, populate chat-based notifications // Listen for chats, populate chat-based notifications
let chatsSub: any let chatsReq: any
derived([pubkey, canDecrypt, userInboxRelaySelections], identity).subscribe( derived([pubkey, canDecrypt, userInboxRelaySelections], identity).subscribe(
([$pubkey, $canDecrypt, $userInboxRelaySelections]) => { ([$pubkey, $canDecrypt, $userInboxRelaySelections]) => {
chatsSub?.close() chatsReq?.close()
if ($pubkey && $canDecrypt) { if ($pubkey && $canDecrypt) {
chatsSub = subscribe({ chatsReq = request({
filters: [ filters: [
{kinds: [WRAP], "#p": [$pubkey], since: ago(WEEK, 2)}, {kinds: [WRAP], "#p": [$pubkey], since: ago(WEEK, 2)},
{kinds: [WRAP], "#p": [$pubkey], limit: 100}, {kinds: [WRAP], "#p": [$pubkey], limit: 100},

View File

@@ -3,7 +3,7 @@
import * as nip19 from "nostr-tools/nip19" import * as nip19 from "nostr-tools/nip19"
import type {TrustedEvent} from "@welshman/util" import type {TrustedEvent} from "@welshman/util"
import {Address, getIdFilters, getTagValue} from "@welshman/util" import {Address, getIdFilters, getTagValue} from "@welshman/util"
import {load} from "@welshman/app" import {request, RequestEvent} from "@welshman/net"
import {page} from "$app/stores" import {page} from "$app/stores"
import {goto} from "$app/navigation" import {goto} from "$app/navigation"
import {scrollToEvent} from "@lib/html" import {scrollToEvent} from "@lib/html"
@@ -21,26 +21,29 @@
let found = false let found = false
load({ const req = request({
autoClose: true,
filters: getIdFilters([type === "nevent" ? data.id : Address.fromNaddr(bech32).toString()]), filters: getIdFilters([type === "nevent" ? data.id : Address.fromNaddr(bech32).toString()]),
relays: data.relays, relays: data.relays,
onEvent: (event: TrustedEvent) => { })
found = true
if (event.kind === 9) { req.on(RequestEvent.Event, (event: TrustedEvent) => {
goto(makeRoomPath(data.relays[0], getTagValue("h", event.tags)!), {replaceState: true}) found = true
scrollToEvent(event.id)
} else if (event.kind === 11) { if (event.kind === 9) {
goto(makeThreadPath(data.relays[0], event.id), {replaceState: true}) goto(makeRoomPath(data.relays[0], getTagValue("h", event.tags)!), {replaceState: true})
} else { scrollToEvent(event.id)
goto("/", {replaceState: true}) } else if (event.kind === 11) {
} goto(makeThreadPath(data.relays[0], event.id), {replaceState: true})
}, } else {
onComplete: () => { goto("/", {replaceState: true})
if (!found) { }
goto("/", {replaceState: true}) })
}
}, req.on(RequestEvent.Close, () => {
if (!found) {
goto("/", {replaceState: true})
}
}) })
} }

View File

@@ -1,8 +1,7 @@
<script lang="ts"> <script lang="ts">
import {page} from "$app/stores" import {page} from "$app/stores"
import {ctx} from "@welshman/lib"
import {WRAP} from "@welshman/util" import {WRAP} from "@welshman/util"
import {pubkey} from "@welshman/app" import {pubkey, Router} from "@welshman/app"
import Icon from "@lib/components/Icon.svelte" import Icon from "@lib/components/Icon.svelte"
import Page from "@lib/components/Page.svelte" import Page from "@lib/components/Page.svelte"
import Button from "@lib/components/Button.svelte" import Button from "@lib/components/Button.svelte"
@@ -25,7 +24,7 @@
const promise = pullConservatively({ const promise = pullConservatively({
filters: [{kinds: [WRAP], "#p": [$pubkey!]}], filters: [{kinds: [WRAP], "#p": [$pubkey!]}],
relays: ctx.app.router.UserInbox().getUrls(), relays: Router.get().UserInbox().getUrls(),
}) })
let term = $state("") let term = $state("")

View File

@@ -1,7 +1,6 @@
<script lang="ts"> <script lang="ts">
import {ctx} from "@welshman/lib"
import {getListTags, createEvent, getPubkeyTagValues, MUTES} from "@welshman/util" import {getListTags, createEvent, getPubkeyTagValues, MUTES} from "@welshman/util"
import {pubkey, signer, userMutes, tagPubkey, publishThunk} from "@welshman/app" import {pubkey, Router, signer, userMutes, tagPubkey, publishThunk} from "@welshman/app"
import {preventDefault} from "@lib/html" import {preventDefault} from "@lib/html"
import Field from "@lib/components/Field.svelte" import Field from "@lib/components/Field.svelte"
import FieldInline from "@lib/components/FieldInline.svelte" import FieldInline from "@lib/components/FieldInline.svelte"
@@ -22,12 +21,12 @@
publishThunk({ publishThunk({
event: createEvent(SETTINGS, {content}), event: createEvent(SETTINGS, {content}),
relays: ctx.app.router.FromUser().getUrls(), relays: Router.get().FromUser().getUrls(),
}) })
publishThunk({ publishThunk({
event: createEvent(MUTES, {tags: mutedPubkeys.map(tagPubkey)}), event: createEvent(MUTES, {tags: mutedPubkeys.map(tagPubkey)}),
relays: ctx.app.router.FromUser().getUrls(), relays: Router.get().FromUser().getUrls(),
}) })
pushToast({message: "Your settings have been saved!"}) pushToast({message: "Your settings have been saved!"})

View File

@@ -1,5 +1,5 @@
<script lang="ts"> <script lang="ts">
import {nip19} from "nostr-tools" import * as nip19 from "nostr-tools/nip19"
import {hexToBytes} from "@noble/hashes/utils" import {hexToBytes} from "@noble/hashes/utils"
import {displayPubkey, displayProfile} from "@welshman/util" import {displayPubkey, displayProfile} from "@welshman/util"
import {pubkey, session, displayNip05, deriveProfile} from "@welshman/app" import {pubkey, session, displayNip05, deriveProfile} from "@welshman/app"

View File

@@ -3,7 +3,7 @@
import {page} from "$app/stores" import {page} from "$app/stores"
import {ago, MONTH} from "@welshman/lib" import {ago, MONTH} from "@welshman/lib"
import {GROUPS, THREAD, COMMENT, MESSAGE, DELETE} from "@welshman/util" import {GROUPS, THREAD, COMMENT, MESSAGE, DELETE} from "@welshman/util"
import {subscribe, load} from "@welshman/app" import {request, load} from "@welshman/net"
import Page from "@lib/components/Page.svelte" import Page from "@lib/components/Page.svelte"
import SecondaryNav from "@lib/components/SecondaryNav.svelte" import SecondaryNav from "@lib/components/SecondaryNav.svelte"
import MenuSpace from "@app/components/MenuSpace.svelte" import MenuSpace from "@app/components/MenuSpace.svelte"
@@ -56,7 +56,7 @@
// Load all groups for this space to populate navigation. It would be nice to sync, but relay29 // Load all groups for this space to populate navigation. It would be nice to sync, but relay29
// is too picky about how requests are built. // is too picky about how requests are built.
load({relays, filters: [{kinds: [GROUPS]}], delay: 0}) load({relays, filters: [{kinds: [GROUPS]}]})
// Load threads, comments, and recent messages for user rooms to help with a quick page transition // Load threads, comments, and recent messages for user rooms to help with a quick page transition
pullConservatively({ pullConservatively({
@@ -69,10 +69,10 @@
}) })
// Listen for deletes that would apply to messages we already have, and new groups // Listen for deletes that would apply to messages we already have, and new groups
const sub = subscribe({relays, filters: [{kinds: [DELETE, GROUPS], since}]}) const req = request({relays, filters: [{kinds: [DELETE, GROUPS], since}]})
return () => { return () => {
sub.close() req.close()
} }
}) })
</script> </script>

View File

@@ -3,7 +3,8 @@
import {page} from "$app/stores" import {page} from "$app/stores"
import {sortBy, sleep} from "@welshman/lib" import {sortBy, sleep} from "@welshman/lib"
import {COMMENT, getTagValue} from "@welshman/util" import {COMMENT, getTagValue} from "@welshman/util"
import {repository, subscribe} from "@welshman/app" import {request} from "@welshman/net"
import {repository} from "@welshman/app"
import {deriveEvents} from "@welshman/store" import {deriveEvents} from "@welshman/store"
import Icon from "@lib/components/Icon.svelte" import Icon from "@lib/components/Icon.svelte"
import PageBar from "@lib/components/PageBar.svelte" import PageBar from "@lib/components/PageBar.svelte"
@@ -45,10 +46,10 @@
let showReply = $state(false) let showReply = $state(false)
onMount(() => { onMount(() => {
const sub = subscribe({relays: [url], filters}) const req = request({relays: [url], filters})
return () => { return () => {
sub.close() req.close()
setChecked($page.url.pathname) setChecked($page.url.pathname)
} }
}) })

View File

@@ -3,7 +3,8 @@
import {page} from "$app/stores" import {page} from "$app/stores"
import {sortBy, sleep} from "@welshman/lib" import {sortBy, sleep} from "@welshman/lib"
import {COMMENT, getTagValue} from "@welshman/util" import {COMMENT, getTagValue} from "@welshman/util"
import {repository, subscribe} from "@welshman/app" import {repository} from "@welshman/app"
import {request} from "@welshman/net"
import {deriveEvents} from "@welshman/store" import {deriveEvents} from "@welshman/store"
import Icon from "@lib/components/Icon.svelte" import Icon from "@lib/components/Icon.svelte"
import PageBar from "@lib/components/PageBar.svelte" import PageBar from "@lib/components/PageBar.svelte"
@@ -42,10 +43,10 @@
let showReply = $state(false) let showReply = $state(false)
onMount(() => { onMount(() => {
const sub = subscribe({relays: [url], filters}) const req = request({relays: [url], filters})
return () => { return () => {
sub.close() req.close()
setChecked($page.url.pathname) setChecked($page.url.pathname)
} }
}) })