forked from coracle/flotilla
Compare commits
1 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| cd40768f1c |
@@ -0,0 +1 @@
|
||||
[{"eventName":"NEXT_CLI_SESSION_STOPPED","payload":{"nextVersion":"16.2.2","nodeVersion":"v24.7.0","cliCommand":"dev","durationMilliseconds":6291,"turboFlag":true,"pagesDir":false,"appDir":true,"isRspack":false}}]
|
||||
Vendored
+1
@@ -0,0 +1 @@
|
||||
{"encryption.key":"mT8zxPCClGbcV2DiRln03XdmvHCQ+orzt0Q2KKaiDd4=","encryption.expire_at":1776709211601}
|
||||
BIN
Binary file not shown.
@@ -0,0 +1,3 @@
|
||||
{
|
||||
"type": "commonjs"
|
||||
}
|
||||
@@ -156,6 +156,24 @@ import {readFeed} from "@lib/feeds"
|
||||
|
||||
export const fromCsv = (s: string) => (s || "").split(",").filter(identity)
|
||||
|
||||
// Keep sync logic informed when the tab sleeps or wakes so subscriptions can be torn down cleanly.
|
||||
export const documentVisibility = readable<DocumentVisibilityState>(
|
||||
typeof document !== "undefined" ? document.visibilityState : "visible",
|
||||
set => {
|
||||
if (typeof document === "undefined") return
|
||||
|
||||
const onVisibilityChange = () => {
|
||||
set(document.visibilityState)
|
||||
}
|
||||
|
||||
document.addEventListener("visibilitychange", onVisibilityChange)
|
||||
|
||||
return () => {
|
||||
document.removeEventListener("visibilitychange", onVisibilityChange)
|
||||
}
|
||||
},
|
||||
)
|
||||
|
||||
export const ROOM = "h"
|
||||
|
||||
export const PROTECTED = ["-"]
|
||||
|
||||
+158
-55
@@ -6,6 +6,7 @@ import {PollResponse} from "nostr-tools/kinds"
|
||||
import {
|
||||
getListTags,
|
||||
getRelayTagValues,
|
||||
type List,
|
||||
WRAP,
|
||||
ROOM_META,
|
||||
ROOM_DELETE,
|
||||
@@ -22,7 +23,7 @@ import {
|
||||
getTagValue,
|
||||
} from "@welshman/util"
|
||||
import type {Filter, TrustedEvent} from "@welshman/util"
|
||||
import {request, requestOne, Difference, DifferenceEvent} from "@welshman/net"
|
||||
import {request, requestOne, Difference, DifferenceEvent, Pool} from "@welshman/net"
|
||||
import {
|
||||
pubkey,
|
||||
loadRelay,
|
||||
@@ -45,6 +46,7 @@ import {
|
||||
MESSAGE_KINDS,
|
||||
CONTENT_KINDS,
|
||||
INDEXER_RELAYS,
|
||||
documentVisibility,
|
||||
loadSettings,
|
||||
loadGroupList,
|
||||
userSpaceUrls,
|
||||
@@ -68,6 +70,10 @@ type SyncOpts = {
|
||||
onEvent?: (event: TrustedEvent) => void
|
||||
}
|
||||
|
||||
const resetRelaySocket = (url: string) => {
|
||||
Pool.get().remove(url)
|
||||
}
|
||||
|
||||
const pullOneWithFallback = async (
|
||||
url: string,
|
||||
filter: Filter,
|
||||
@@ -86,6 +92,12 @@ const pullOneWithFallback = async (
|
||||
const shouldFallback =
|
||||
!hasNegentropy(url) ||
|
||||
(await new Promise(resolve => {
|
||||
if (signal.aborted) {
|
||||
resolve(false)
|
||||
return
|
||||
}
|
||||
|
||||
// If visibility flips while the negentropy diff is opening, skip the fallback path and let teardown win.
|
||||
const diff = new Difference({relay: url, filter, events: cachedEvents, signal})
|
||||
|
||||
diff.on(DifferenceEvent.Error, () => {
|
||||
@@ -111,9 +123,7 @@ export const pullWithFallback = async ({url, signal, filters, onEvent}: SyncOpts
|
||||
|
||||
if (signal.aborted) return
|
||||
|
||||
for (const filter of filters) {
|
||||
pullOneWithFallback(url, filter, signal, onEvent)
|
||||
}
|
||||
await Promise.all(filters.map(filter => pullOneWithFallback(url, filter, signal, onEvent)))
|
||||
}
|
||||
|
||||
const listen = ({url, signal, filters, onEvent}: SyncOpts) => {
|
||||
@@ -197,7 +207,7 @@ const syncUserRoomMembership = (url: string, h: string) => {
|
||||
const syncUserData = () => {
|
||||
const unsubscribersByKey = new Map<string, Unsubscriber>()
|
||||
|
||||
const unsubscribeGroupList = userGroupList.subscribe($userGroupList => {
|
||||
const syncGroupList = ($userGroupList: List | undefined) => {
|
||||
if ($userGroupList) {
|
||||
const keys = new Set<string>()
|
||||
|
||||
@@ -226,37 +236,76 @@ const syncUserData = () => {
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
const unsubscribeRelayList = userRelayList.subscribe($userRelayList => {
|
||||
if ($userRelayList) {
|
||||
loadBlossomServerList($userRelayList.event.pubkey)
|
||||
loadBlockedRelayList($userRelayList.event.pubkey)
|
||||
loadFollowList($userRelayList.event.pubkey)
|
||||
loadGroupList($userRelayList.event.pubkey)
|
||||
loadMuteList($userRelayList.event.pubkey)
|
||||
loadProfile($userRelayList.event.pubkey)
|
||||
loadSettings($userRelayList.event.pubkey)
|
||||
loadFeedsForPubkey($userRelayList.event.pubkey)
|
||||
}
|
||||
})
|
||||
const syncRelayList = ($userRelayList: List | undefined) => {
|
||||
const pubkey = $userRelayList?.event?.pubkey
|
||||
|
||||
const unsubscribeFollows = userFollowList.subscribe(async $userFollowList => {
|
||||
if (!pubkey) return
|
||||
|
||||
loadBlossomServerList(pubkey)
|
||||
loadBlockedRelayList(pubkey)
|
||||
loadFollowList(pubkey)
|
||||
loadGroupList(pubkey)
|
||||
loadMuteList(pubkey)
|
||||
loadProfile(pubkey)
|
||||
loadSettings(pubkey)
|
||||
loadFeedsForPubkey(pubkey)
|
||||
}
|
||||
|
||||
const syncFollowList = () => {
|
||||
for (const pubkeys of chunk(10, get(bootstrapPubkeys))) {
|
||||
// This isn't urgent, avoid clogging other stuff up
|
||||
await sleep(1000)
|
||||
void (async () => {
|
||||
// This isn't urgent, avoid clogging other stuff up
|
||||
await sleep(1000)
|
||||
|
||||
await Promise.all(
|
||||
pubkeys.flatMap(pk => [
|
||||
loadRelayList(pk),
|
||||
loadGroupList(pk),
|
||||
loadProfile(pk),
|
||||
loadFollowList(pk),
|
||||
loadMuteList(pk),
|
||||
]),
|
||||
)
|
||||
await Promise.all(
|
||||
pubkeys.flatMap(pk => [
|
||||
loadRelayList(pk),
|
||||
loadGroupList(pk),
|
||||
loadProfile(pk),
|
||||
loadFollowList(pk),
|
||||
loadMuteList(pk),
|
||||
]),
|
||||
)
|
||||
})()
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
const unsubscribeGroupList = derived([userGroupList, documentVisibility], identity).subscribe(
|
||||
([$userGroupList, $visibility]) => {
|
||||
if ($visibility === "hidden") {
|
||||
const urls = new Set(Array.from(unsubscribersByKey.keys()).map(key => key.split("'")[0]))
|
||||
|
||||
unsubscribersByKey.forEach(call)
|
||||
unsubscribersByKey.clear()
|
||||
|
||||
for (const url of urls) {
|
||||
resetRelaySocket(url)
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
syncGroupList($userGroupList)
|
||||
},
|
||||
)
|
||||
|
||||
const unsubscribeRelayList = derived([userRelayList, documentVisibility], identity).subscribe(
|
||||
([$userRelayList, $visibility]) => {
|
||||
if ($visibility === "hidden") return
|
||||
|
||||
syncRelayList($userRelayList)
|
||||
},
|
||||
)
|
||||
|
||||
const unsubscribeFollows = derived([userFollowList, documentVisibility], identity).subscribe(
|
||||
([$userFollowList, $visibility]) => {
|
||||
if ($visibility === "hidden") return
|
||||
|
||||
syncFollowList()
|
||||
},
|
||||
)
|
||||
|
||||
return () => {
|
||||
unsubscribersByKey.forEach(call)
|
||||
@@ -321,11 +370,26 @@ const syncSpace = (url: string, rooms: string[]) => {
|
||||
}
|
||||
|
||||
const syncSpaces = () => {
|
||||
const store = derived([userGroupList, page], identity)
|
||||
const store = derived([userGroupList, page, documentVisibility], identity)
|
||||
const unsubscribersByUrl = new Map<string, Unsubscriber>()
|
||||
const roomsByUrl = new Map<string, string>()
|
||||
|
||||
const unsubscribe = store.subscribe(([$userGroupList, $page]) => {
|
||||
const unsubscribe = store.subscribe(([$userGroupList, $page, $visibility]) => {
|
||||
if ($visibility === "hidden") {
|
||||
// Hidden tabs should drop every live space subscription so we restart from a clean slate on wake.
|
||||
for (const url of unsubscribersByUrl.keys()) {
|
||||
resetRelaySocket(url)
|
||||
}
|
||||
|
||||
for (const unsubscribe of unsubscribersByUrl.values()) {
|
||||
unsubscribe()
|
||||
}
|
||||
|
||||
unsubscribersByUrl.clear()
|
||||
roomsByUrl.clear()
|
||||
return
|
||||
}
|
||||
|
||||
const urls = new Set(getSpaceUrlsFromGroupList($userGroupList))
|
||||
|
||||
if ($page.params.relay) {
|
||||
@@ -338,6 +402,7 @@ const syncSpaces = () => {
|
||||
unsubscribersByUrl.delete(url)
|
||||
roomsByUrl.delete(url)
|
||||
unsubscribe()
|
||||
resetRelaySocket(url)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -357,8 +422,9 @@ const syncSpaces = () => {
|
||||
})
|
||||
|
||||
return () => {
|
||||
for (const unsubscriber of unsubscribersByUrl.values()) {
|
||||
for (const [url, unsubscriber] of unsubscribersByUrl.entries()) {
|
||||
unsubscriber()
|
||||
resetRelaySocket(url)
|
||||
}
|
||||
|
||||
unsubscribe()
|
||||
@@ -383,11 +449,51 @@ const syncDMs = () => {
|
||||
const unsubscribersByUrl = new Map<string, Unsubscriber>()
|
||||
|
||||
let currentPubkey: string | undefined
|
||||
let currentShouldUnwrap = false
|
||||
// Late relay-list promises can resolve after a hide/show cycle, so keep the last visible state here.
|
||||
let currentVisibility: DocumentVisibilityState = "visible"
|
||||
|
||||
const unsubscribeAll = () => {
|
||||
const unsubscribeAll = (resetSockets = false) => {
|
||||
for (const [url, unsubscribe] of unsubscribersByUrl.entries()) {
|
||||
unsubscribersByUrl.delete(url)
|
||||
unsubscribe()
|
||||
|
||||
if (resetSockets) {
|
||||
resetRelaySocket(url)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const syncPubkey = ($pubkey: string | undefined, $shouldUnwrap: boolean) => {
|
||||
if ($pubkey !== currentPubkey) {
|
||||
unsubscribeAll()
|
||||
}
|
||||
|
||||
if ($pubkey && $shouldUnwrap) {
|
||||
loadRelayList($pubkey)
|
||||
.then(() => loadMessagingRelayList($pubkey))
|
||||
.then($l => {
|
||||
if (
|
||||
$l &&
|
||||
currentVisibility === "visible" &&
|
||||
currentPubkey === $pubkey &&
|
||||
currentShouldUnwrap === $shouldUnwrap
|
||||
) {
|
||||
subscribeAll($pubkey, getRelayTagValues(getListTags($l)))
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
currentPubkey = $pubkey
|
||||
currentShouldUnwrap = $shouldUnwrap
|
||||
}
|
||||
|
||||
const syncList = ($userMessagingRelayList: List | undefined) => {
|
||||
const $pubkey = pubkey.get()
|
||||
const $shouldUnwrap = shouldUnwrap.get()
|
||||
|
||||
if ($pubkey && $shouldUnwrap) {
|
||||
subscribeAll($pubkey, getRelayTagValues(getListTags($userMessagingRelayList)))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -408,33 +514,30 @@ const syncDMs = () => {
|
||||
}
|
||||
}
|
||||
|
||||
// When pubkey changes, re-sync
|
||||
const unsubscribePubkey = derived([pubkey, shouldUnwrap], identity).subscribe(
|
||||
([$pubkey, $shouldUnwrap]) => {
|
||||
if ($pubkey !== currentPubkey) {
|
||||
unsubscribeAll()
|
||||
// When pubkey or visibility changes, re-sync
|
||||
const unsubscribePubkey = derived([pubkey, shouldUnwrap, documentVisibility], identity).subscribe(
|
||||
([$pubkey, $shouldUnwrap, $visibility]) => {
|
||||
currentVisibility = $visibility
|
||||
|
||||
if ($visibility === "hidden") {
|
||||
unsubscribeAll(true)
|
||||
return
|
||||
}
|
||||
|
||||
// If we have a pubkey, refresh our user's relay list then sync our subscriptions
|
||||
if ($pubkey && $shouldUnwrap) {
|
||||
loadRelayList($pubkey)
|
||||
.then(() => loadMessagingRelayList($pubkey))
|
||||
.then($l => subscribeAll($pubkey, getRelayTagValues(getListTags($l))))
|
||||
}
|
||||
|
||||
currentPubkey = $pubkey
|
||||
syncPubkey($pubkey, $shouldUnwrap)
|
||||
},
|
||||
)
|
||||
|
||||
// When user messaging relays change, update synchronization
|
||||
const unsubscribeList = userMessagingRelayList.subscribe($userMessagingRelayList => {
|
||||
const $pubkey = pubkey.get()
|
||||
const $shouldUnwrap = shouldUnwrap.get()
|
||||
const unsubscribeList = derived([userMessagingRelayList, documentVisibility], identity).subscribe(
|
||||
([$userMessagingRelayList, $visibility]) => {
|
||||
currentVisibility = $visibility
|
||||
|
||||
if ($pubkey && $shouldUnwrap) {
|
||||
subscribeAll($pubkey, getRelayTagValues(getListTags($userMessagingRelayList)))
|
||||
}
|
||||
})
|
||||
if ($visibility === "hidden") return
|
||||
|
||||
syncList($userMessagingRelayList)
|
||||
},
|
||||
)
|
||||
|
||||
return () => {
|
||||
unsubscribeAll()
|
||||
|
||||
Reference in New Issue
Block a user