diff --git a/src/app/components/ChatMessageMenuMobile.svelte b/src/app/components/ChatMessageMenuMobile.svelte
index b7466980..446387e6 100644
--- a/src/app/components/ChatMessageMenuMobile.svelte
+++ b/src/app/components/ChatMessageMenuMobile.svelte
@@ -24,7 +24,10 @@
const onEmoji = ((event: TrustedEvent, pubkeys: string[], emoji: NativeEmoji) => {
history.back()
- sendWrapped({template: makeReaction({event, content: emoji.unicode, protect: false}), pubkeys})
+ sendWrapped({
+ event: makeReaction({event, content: emoji.unicode, protect: false}),
+ recipients: pubkeys,
+ })
}).bind(undefined, event, pubkeys)
const showEmojiPicker = () => pushModal(EmojiPicker, {onClick: onEmoji}, {replaceState: true})
diff --git a/src/app/components/PrimaryNav.svelte b/src/app/components/PrimaryNav.svelte
index 6a018bea..a95f370f 100644
--- a/src/app/components/PrimaryNav.svelte
+++ b/src/app/components/PrimaryNav.svelte
@@ -3,7 +3,7 @@
import {page} from "$app/stores"
import {goto} from "$app/navigation"
import {splitAt} from "@welshman/lib"
- import {userProfile} from "@welshman/app"
+ import {userProfile, shouldUnwrap} from "@welshman/app"
import Avatar from "@lib/components/Avatar.svelte"
import Divider from "@lib/components/Divider.svelte"
import PrimaryNavItem from "@lib/components/PrimaryNavItem.svelte"
@@ -13,7 +13,7 @@
import MenuOtherSpaces from "@app/components/MenuOtherSpaces.svelte"
import MenuSettings from "@app/components/MenuSettings.svelte"
import PrimaryNavItemSpace from "@app/components/PrimaryNavItemSpace.svelte"
- import {userRoomsByUrl, canDecrypt, PLATFORM_RELAYS, PLATFORM_LOGO} from "@app/core/state"
+ import {userRoomsByUrl, PLATFORM_RELAYS, PLATFORM_LOGO} from "@app/core/state"
import {pushModal} from "@app/util/modal"
import {makeSpacePath} from "@app/util/routes"
import {notifications} from "@app/util/notifications"
@@ -37,7 +37,7 @@
const showSettingsMenu = () => pushModal(MenuSettings)
- const openChat = () => ($canDecrypt ? goto("/chat") : pushModal(ChatEnable, {next: "/chat"}))
+ const openChat = () => ($shouldUnwrap ? goto("/chat") : pushModal(ChatEnable, {next: "/chat"}))
const hasNotification = (url: string) => {
const path = makeSpacePath(url)
diff --git a/src/app/components/ProfileDetail.svelte b/src/app/components/ProfileDetail.svelte
index 27c45b40..13720155 100644
--- a/src/app/components/ProfileDetail.svelte
+++ b/src/app/components/ProfileDetail.svelte
@@ -1,5 +1,6 @@
diff --git a/src/app/components/ThunkFailure.svelte b/src/app/components/ThunkFailure.svelte
index 70febc88..c09186e3 100644
--- a/src/app/components/ThunkFailure.svelte
+++ b/src/app/components/ThunkFailure.svelte
@@ -1,14 +1,8 @@
{#if $deleted}
diff --git a/src/app/core/commands.ts b/src/app/core/commands.ts
index fa57d16a..12ec2c69 100644
--- a/src/app/core/commands.ts
+++ b/src/app/core/commands.ts
@@ -90,6 +90,7 @@ import {
waitForThunkError,
getPubkeyRelays,
userBlossomServers,
+ shouldUnwrap,
} from "@welshman/app"
import {compressFile} from "@src/lib/html"
import type {SettingsValues, Alert} from "@app/core/state"
@@ -103,8 +104,6 @@ import {
DEFAULT_BLOSSOM_SERVERS,
userRoomsByUrl,
userSettingsValues,
- canDecrypt,
- ensureUnwrapped,
userInboxRelays,
getMembershipUrls,
} from "@app/core/state"
@@ -593,8 +592,8 @@ export const createAlert = async (params: CreateAlertParams): Promise {
- if (!get(canDecrypt)) {
- enableGiftWraps()
+ if (!shouldUnwrap.get()) {
+ shouldUnwrap.set(true)
}
return createAlert({
@@ -658,16 +657,6 @@ export const payInvoice = async (invoice: string) => {
}
}
-// Gift Wraps
-
-export const enableGiftWraps = () => {
- canDecrypt.set(true)
-
- for (const event of repository.query([{kinds: [WRAP]}])) {
- ensureUnwrapped(event)
- }
-}
-
// File upload
export const normalizeBlossomUrl = (url: string) => normalizeUrl(url.replace(/^ws/, "http"))
diff --git a/src/app/core/state.ts b/src/app/core/state.ts
index 0dc553a3..ba8fe21e 100644
--- a/src/app/core/state.ts
+++ b/src/app/core/state.ts
@@ -27,14 +27,7 @@ import {
} from "@welshman/lib"
import type {Socket} from "@welshman/net"
import {Pool, load, AuthStateEvent, AuthStatus, SocketEvent, netContext} from "@welshman/net"
-import {
- collection,
- custom,
- deriveEvents,
- deriveEventsMapped,
- withGetter,
- synced,
-} from "@welshman/store"
+import {collection, custom, deriveEvents, deriveEventsMapped, withGetter} from "@welshman/store"
import {isKindFeed, findFeed} from "@welshman/feeds"
import {
getIdFilters,
@@ -68,7 +61,6 @@ import {
getGroupTags,
getRelayTagValues,
getPubkeyTagValues,
- isHashedEvent,
displayProfile,
readList,
getListTags,
@@ -82,8 +74,8 @@ import {
RelayMode,
getRelaysFromList,
} from "@welshman/util"
-import type {TrustedEvent, SignedEvent, PublishedList, List, Filter} from "@welshman/util"
-import {Nip59, decrypt} from "@welshman/signer"
+import type {TrustedEvent, PublishedList, List, Filter} from "@welshman/util"
+import {decrypt} from "@welshman/signer"
import {routerContext, Router} from "@welshman/router"
import {
pubkey,
@@ -92,14 +84,10 @@ import {
tracker,
makeTrackerStore,
makeRepositoryStore,
- relay,
- getSession,
- getSigner,
createSearch,
userFollows,
ensurePlaintext,
thunks,
- flattenThunks,
signer,
makeOutboxLoader,
appContext,
@@ -109,7 +97,6 @@ import {
userInboxRelaySelections,
} from "@welshman/app"
import type {Thunk, Relay} from "@welshman/app"
-import {preferencesStorageProvider} from "@src/lib/storage"
export const fromCsv = (s: string) => (s || "").split(",").filter(identity)
@@ -195,46 +182,6 @@ export const defaultPubkeys = derived(userFollows, $userFollows => {
return userPubkeys.length > 5 ? userPubkeys : [...userPubkeys, ...appPubkeys]
})
-const failedUnwraps = new Set()
-
-export const ensureUnwrapped = async (event: TrustedEvent) => {
- if (event.kind !== WRAP) {
- return event
- }
-
- let rumor = repository.eventsByWrap.get(event.id)
-
- if (rumor || failedUnwraps.has(event.id)) {
- return rumor
- }
-
- for (const recipient of getPubkeyTagValues(event.tags)) {
- const session = getSession(recipient)
- const signer = getSigner(session)
-
- if (signer) {
- try {
- rumor = await Nip59.fromSigner(signer).unwrap(event as SignedEvent)
- break
- } catch (e) {
- // pass
- }
- }
- }
-
- if (rumor && isHashedEvent(rumor)) {
- // Copy urls over to the rumor
- tracker.copy(event.id, rumor.id)
-
- // Send the rumor via our relay so listeners get updated
- relay.send("EVENT", rumor)
- } else {
- failedUnwraps.add(event.id)
- }
-
- return rumor
-}
-
export const trackerStore = makeTrackerStore()
export const repositoryStore = makeRepositoryStore()
@@ -262,7 +209,7 @@ export const getUrlsForEvent = derived([trackerStore, thunks], ([$tracker, $thun
const getThunksByEventId = memoize(() => {
const thunksByEventId = new Map()
- for (const thunk of flattenThunks(Object.values($thunks))) {
+ for (const thunk of $thunks) {
pushToMapKey(thunksByEventId, thunk.event.id, thunk)
}
@@ -285,7 +232,7 @@ export const getUrlsForEvent = derived([trackerStore, thunks], ([$tracker, $thun
export const getEventsForUrl = (url: string, filters: Filter[]) => {
const ids = uniq([
...tracker.getIds(url),
- ...Array.from(flattenThunks(Object.values(get(thunks))))
+ ...get(thunks)
.filter(t => t.options.relays.includes(url))
.map(t => t.event.id),
])
@@ -297,9 +244,7 @@ export const deriveEventsForUrl = (url: string, filters: Filter[]) =>
derived([trackerStore, thunks], ([$tracker, $thunks]) => {
const ids = uniq([
...$tracker.getIds(url),
- ...Array.from(flattenThunks(Object.values($thunks)))
- .filter(t => t.options.relays.includes(url))
- .map(t => t.event.id),
+ ...$thunks.filter(t => t.options.relays.includes(url)).map(t => t.event.id),
])
return repository.query(filters.map(assoc("ids", ids)))
@@ -336,12 +281,6 @@ export const COMMENT_FILTER = makeCommentFilter(MESSAGE_KINDS)
// Settings
-export const canDecrypt = synced({
- key: "canDecrypt",
- defaultValue: false,
- storage: preferencesStorageProvider,
-})
-
export const SETTINGS = "flotilla/settings"
export type SettingsValues = {
@@ -555,11 +494,6 @@ export const chats = derived(
const messagesByChatId = new Map()
for (const message of $messages) {
- // Filter out messages we sent but aren't addressed to the user
- if (!getPubkeyTagValues(message.wrap?.tags || []).includes($pubkey!)) {
- continue
- }
-
const chatId = makeChatId(getPubkeyTagValues(message.tags).concat(message.pubkey))
pushToMapKey(messagesByChatId, chatId, message)
diff --git a/src/app/core/sync.ts b/src/app/core/sync.ts
index 687b61be..a6f71d59 100644
--- a/src/app/core/sync.ts
+++ b/src/app/core/sync.ts
@@ -39,6 +39,7 @@ import {
loadMutes,
loadProfile,
repository,
+ shouldUnwrap,
hasNegentropy,
} from "@welshman/app"
import {
@@ -46,7 +47,6 @@ import {
COMMENT_FILTER,
INDEXER_RELAYS,
REACTION_KINDS,
- canDecrypt,
loadSettings,
userMembership,
defaultPubkeys,
@@ -334,14 +334,14 @@ const syncDMs = () => {
}
// When pubkey changes, re-sync
- const unsubscribePubkey = derived([pubkey, canDecrypt], identity).subscribe(
- ([$pubkey, $canDecrypt]) => {
+ const unsubscribePubkey = derived([pubkey, shouldUnwrap], identity).subscribe(
+ ([$pubkey, $shouldUnwrap]) => {
if ($pubkey !== currentPubkey) {
unsubscribeAll()
}
// If we have a pubkey, refresh our user's relay selections then sync our subscriptions
- if ($pubkey && $canDecrypt) {
+ if ($pubkey && $shouldUnwrap) {
loadRelaySelections($pubkey)
.then(() => loadInboxRelaySelections($pubkey))
.then($l => subscribeAll($pubkey, getRelayTagValues(getListTags($l))))
@@ -352,11 +352,12 @@ const syncDMs = () => {
)
// When user inbox relays change, update synchronization
- const unsubscribeSelections = userInboxRelaySelections.subscribe($l => {
+ const unsubscribeSelections = userInboxRelaySelections.subscribe($userInboxRelaySelections => {
const $pubkey = pubkey.get()
+ const $shouldUnwrap = shouldUnwrap.get()
- if ($pubkey && $l) {
- subscribeAll($pubkey, getRelayTagValues(getListTags($l)))
+ if ($pubkey && $shouldUnwrap) {
+ subscribeAll($pubkey, getRelayTagValues(getListTags($userInboxRelaySelections)))
}
})
diff --git a/src/app/util/routes.ts b/src/app/util/routes.ts
index 5ad0ac8a..18a201d6 100644
--- a/src/app/util/routes.ts
+++ b/src/app/util/routes.ts
@@ -17,7 +17,6 @@ import {
getPubkeyTagValues,
} from "@welshman/util"
import {
- ensureUnwrapped,
makeChatId,
entityLink,
decodeRelay,
@@ -85,20 +84,16 @@ export const getPrimaryNavItemIndex = ($page: Page) => {
}
export const goToEvent = async (event: TrustedEvent, options: Record = {}) => {
- const unwrapped = await ensureUnwrapped(event)
+ const urls = Array.from(tracker.getRelays(event.id))
+ const path = await getEventPath(event, urls)
- if (unwrapped) {
- const urls = Array.from(tracker.getRelays(unwrapped.id))
- const path = await getEventPath(unwrapped, urls)
+ if (path.includes("://")) {
+ window.open(path)
+ } else {
+ goto(path, options)
- if (path.includes("://")) {
- window.open(path)
- } else {
- goto(path, options)
-
- await sleep(300)
- await scrollToEvent(unwrapped.id)
- }
+ await sleep(300)
+ await scrollToEvent(event.id)
}
}
diff --git a/src/app/util/storage.ts b/src/app/util/storage.ts
index 49cc8c01..9499db3e 100644
--- a/src/app/util/storage.ts
+++ b/src/app/util/storage.ts
@@ -33,7 +33,7 @@ import {
verifiedSymbol,
} from "@welshman/util"
import type {Zapper, TrustedEvent} from "@welshman/util"
-import type {RepositoryUpdate} from "@welshman/relay"
+import type {RepositoryUpdate, WrapItem} from "@welshman/net"
import type {Handle, Relay} from "@welshman/app"
import {
plaintext,
@@ -44,6 +44,7 @@ import {
zappers,
onZapper,
onHandle,
+ wrapManager,
} from "@welshman/app"
import {Collection} from "@lib/storage"
@@ -258,6 +259,28 @@ const syncPlaintext = async () => {
})
}
+const syncWrapManager = async () => {
+ const collection = new Collection({
+ table: "wraps",
+ shards: Array.from("0123456789abcdef"),
+ getShard: (item: WrapItem) => last(hash(item.id)),
+ })
+
+ wrapManager.load(await collection.get())
+
+ const addOne = batch(3000, (wrapItems: WrapItem[]) => collection.add(wrapItems))
+
+ const updateAll = throttle(3000, () => collection.set(wrapManager.dump()))
+
+ wrapManager.on("add", addOne)
+ wrapManager.on("remove", updateAll)
+
+ return () => {
+ wrapManager.off("add", addOne)
+ wrapManager.off("remove", updateAll)
+ }
+}
+
export const syncDataStores = () =>
Promise.all([
syncEvents(),
@@ -267,4 +290,5 @@ export const syncDataStores = () =>
syncZappers(),
syncFreshness(),
syncPlaintext(),
+ syncWrapManager(),
])
diff --git a/src/routes/+layout.svelte b/src/routes/+layout.svelte
index 38530683..059df604 100644
--- a/src/routes/+layout.svelte
+++ b/src/routes/+layout.svelte
@@ -8,10 +8,9 @@
import {App, type URLOpenListenerEvent} from "@capacitor/app"
import {dev} from "$app/environment"
import {goto} from "$app/navigation"
- import {sync, localStorageProvider} from "@welshman/store"
- import {assoc, call, defer, dissoc, on, sleep, spec, TaskQueue} from "@welshman/lib"
- import type {TrustedEvent, StampedEvent} from "@welshman/util"
- import {WRAP} from "@welshman/util"
+ import {sync} from "@welshman/store"
+ import {assoc, call, defer, dissoc, on, sleep, spec} from "@welshman/lib"
+ import type {StampedEvent} from "@welshman/util"
import {Nip46Broker, makeSecret} from "@welshman/signer"
import type {Socket, RelayMessage, ClientMessage} from "@welshman/net"
import {
@@ -33,6 +32,7 @@
signer,
signerLog,
dropSession,
+ shouldUnwrap,
loginWithNip01,
loginWithNip46,
loadRelaySelections,
@@ -55,8 +55,6 @@
import {
userSettingsValues,
relaysPendingTrust,
- ensureUnwrapped,
- canDecrypt,
getSetting,
relaysMostlyRestricted,
} from "@app/core/state"
@@ -106,44 +104,6 @@
...notifications,
})
- // migrate from localStorage to capacitor Preferences storage if needed
- const runMigration = async () => {
- const isSome = (item: any) => {
- return item !== undefined && item !== null && item !== ""
- }
-
- const localStoragePubKey = await localStorageProvider.get("pubkey")
- if (isSome(localStoragePubKey)) {
- await preferencesStorageProvider.set("pubkey", localStoragePubKey)
- localStorage.removeItem("pubkey")
- }
-
- const localStorageSessions = await localStorageProvider.get("sessions")
- if (isSome(localStorageSessions)) {
- await preferencesStorageProvider.set("sessions", localStorageSessions)
- localStorage.removeItem("sessions")
- }
-
- const localStorageCanDecrypt = await localStorageProvider.get("canDecrypt")
- if (isSome(localStorageCanDecrypt)) {
- await preferencesStorageProvider.set("canDecrypt", localStorageCanDecrypt)
- localStorage.removeItem("canDecrypt")
- }
-
- const localStorageChecked = await localStorageProvider.get("checked")
- if (isSome(localStorageChecked)) {
- await preferencesStorageProvider.set("checked", localStorageChecked)
- localStorage.removeItem("checked")
- }
-
- const localStorageTheme = await localStorageProvider.get("theme")
- if (isSome(localStorageTheme)) {
- await preferencesStorageProvider.set("theme", localStorageTheme)
- localStorage.removeItem("theme")
- }
- }
- await runMigration()
-
// Listen for navigation messages from service worker
navigator.serviceWorker?.addEventListener("message", event => {
if (event.data && event.data.type === "NAVIGATE") {
@@ -217,16 +177,9 @@
}
})
- // Unwrap gift wraps as they come in, but throttled
- const unwrapper = new TaskQueue({batchSize: 10, processItem: ensureUnwrapped})
-
repository.on("update", ({added}) => {
for (const event of added) {
loadRelaySelections(event.pubkey)
-
- if ($canDecrypt && event.kind === WRAP) {
- unwrapper.push(event)
- }
}
})
@@ -244,6 +197,13 @@
storage: preferencesStorageProvider,
})
+ // Sync shouldUnwrap
+ await sync({
+ key: "shouldUnwrap",
+ store: shouldUnwrap,
+ storage: preferencesStorageProvider,
+ })
+
// Sync application data (relay, events, etc)
await storage.syncDataStores()
diff --git a/src/routes/[bech32]/+page.svelte b/src/routes/[bech32]/+page.svelte
index d7597dc6..af15a035 100644
--- a/src/routes/[bech32]/+page.svelte
+++ b/src/routes/[bech32]/+page.svelte
@@ -4,8 +4,7 @@
import type {MakeNonOptional} from "@welshman/lib"
import type {TrustedEvent} from "@welshman/util"
import {Address, getIdFilters} from "@welshman/util"
- import {LOCAL_RELAY_URL} from "@welshman/relay"
- import {load} from "@welshman/net"
+ import {load, LOCAL_RELAY_URL} from "@welshman/net"
import {page} from "$app/stores"
import {goto} from "$app/navigation"
import Spinner from "@lib/components/Spinner.svelte"
diff --git a/src/routes/home/+page.svelte b/src/routes/home/+page.svelte
index 3bcd3f94..55271657 100644
--- a/src/routes/home/+page.svelte
+++ b/src/routes/home/+page.svelte
@@ -1,6 +1,7 @@