mirror of
https://github.com/coracle-social/flotilla.git
synced 2025-12-10 19:07:06 +00:00
Switch to pnpm, use new welshman stuff
This commit is contained in:
@@ -1,2 +1,2 @@
|
|||||||
npm run lint
|
pnpm run lint
|
||||||
npm run check
|
pnpm run check
|
||||||
|
|||||||
10
README.md
10
README.md
@@ -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
15449
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
36
package.json
36
package.json
@@ -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
BIN
pnpm-lock.yaml
generated
Normal file
Binary file not shown.
@@ -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,
|
||||||
})),
|
})),
|
||||||
),
|
),
|
||||||
|
|||||||
@@ -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`
|
||||||
|
|||||||
@@ -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})
|
||||||
|
|||||||
@@ -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"
|
||||||
|
|
||||||
|
|||||||
@@ -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)
|
||||||
|
|||||||
@@ -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!"})
|
||||||
|
|||||||
@@ -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 = () => {
|
||||||
|
|||||||
@@ -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>
|
||||||
|
|||||||
@@ -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)
|
||||||
|
|
||||||
|
|||||||
@@ -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>
|
||||||
|
|||||||
@@ -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)
|
||||||
|
|||||||
@@ -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"
|
||||||
|
|||||||
@@ -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)
|
||||||
|
|||||||
@@ -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")
|
||||||
|
|
||||||
|
|||||||
@@ -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)),
|
||||||
|
)
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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},
|
||||||
|
|||||||
@@ -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})
|
||||||
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -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("")
|
||||||
|
|||||||
@@ -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!"})
|
||||||
|
|||||||
@@ -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"
|
||||||
|
|||||||
@@ -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>
|
||||||
|
|||||||
@@ -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)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -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)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|||||||
Reference in New Issue
Block a user