Get nip 17 chat working

This commit is contained in:
Jon Staab
2024-10-08 13:20:55 -07:00
parent 8698dcc359
commit a351d9d792
8 changed files with 106 additions and 119 deletions
+5 -6
View File
@@ -125,7 +125,7 @@ export const updateList = async (kind: number, modifyTags: ModifyTags) => {
? {...prev, tags: modifyTags(prev.tags)} ? {...prev, tags: modifyTags(prev.tags)}
: createEvent(kind, {tags: modifyTags([])}) : createEvent(kind, {tags: modifyTags([])})
publishThunk(makeThunk({event, relays})) return publishThunk(makeThunk({event, relays}))
} }
export const addSpaceMembership = (url: string) => export const addSpaceMembership = (url: string) =>
@@ -215,13 +215,12 @@ export const sendWrapped = async ({
await Promise.all( await Promise.all(
uniq(pubkeys).map(async recipient => { uniq(pubkeys).map(async recipient => {
const rumor = await nip59.wrap(recipient, stamp(template)) return publishThunk(
const thunk = makeThunk({ makeThunk({
event: rumor.wrap, event: await nip59.wrap(recipient, stamp(template)),
relays: ctx.app.router.PublishMessage(recipient).getUrls(), relays: ctx.app.router.PublishMessage(recipient).getUrls(),
}) })
)
return publishThunk(thunk)
}), }),
) )
} }
+5 -3
View File
@@ -50,12 +50,13 @@
<button <button
type="button" type="button"
class="group relative flex w-full flex-col gap-1 p-2 text-left transition-colors hover:bg-base-300"> class="group chat relative flex w-full flex-col gap-1 p-2 text-left"
class:chat-start={event.pubkey !== $pubkey}
class:chat-end={event.pubkey === $pubkey}>
<div class="chat-bubble max-w-sm">
<div class="flex gap-2"> <div class="flex gap-2">
{#if showPubkey} {#if showPubkey}
<Avatar src={$profile?.picture} class="border border-solid border-base-content" size={10} /> <Avatar src={$profile?.picture} class="border border-solid border-base-content" size={10} />
{:else}
<div class="w-10 min-w-10 max-w-10" />
{/if} {/if}
<div class="-mt-1 flex-grow pr-1"> <div class="-mt-1 flex-grow pr-1">
{#if showPubkey} {#if showPubkey}
@@ -111,4 +112,5 @@
<Icon icon="menu-dots" size={4} /> <Icon icon="menu-dots" size={4} />
</button> </button>
</button> </button>
</div>
</button> </button>
+1 -2
View File
@@ -13,8 +13,7 @@
const back = () => history.back() const back = () => history.back()
const tryCreate = async () => { const tryCreate = async () => {
await addRoomMembership(url, room) addRoomMembership(url, room)
goto(makeSpacePath(url, room)) goto(makeSpacePath(url, room))
} }
+4 -17
View File
@@ -149,7 +149,10 @@ export const ensureUnwrapped = async (event: TrustedEvent) => {
} }
if (rumor && isHashedEvent(rumor)) { if (rumor && isHashedEvent(rumor)) {
// Copy urls over to the rumor
tracker.copy(event.id, rumor.id) tracker.copy(event.id, rumor.id)
// Send the rumor via our relay so listeners get updated
relay.send("EVENT", rumor) relay.send("EVENT", rumor)
} }
@@ -187,12 +190,6 @@ setContext({
}), }),
}) })
repository.on("update", ({added}) => {
for (const event of added) {
ensureUnwrapped(event)
}
})
export const deriveEvent = (idOrAddress: string, hints: string[] = []) => { export const deriveEvent = (idOrAddress: string, hints: string[] = []) => {
let attempted = false let attempted = false
@@ -350,7 +347,7 @@ export const chats = derived(
const messagesByChatId = new Map<string, TrustedEvent[]>() const messagesByChatId = new Map<string, TrustedEvent[]>()
for (const message of $messages) { for (const message of $messages) {
const chatId = makeChatId(getPubkeyTagValues(message.tags)) const chatId = makeChatId(getPubkeyTagValues(message.tags).concat(message.pubkey))
pushToMapKey(messagesByChatId, chatId, message) pushToMapKey(messagesByChatId, chatId, message)
} }
@@ -383,16 +380,6 @@ export const {
name: "chats", name: "chats",
store: chats, store: chats,
getKey: chat => chat.id, getKey: chat => chat.id,
load: async (id: string, request: Partial<SubscribeRequestWithHandlers> = {}) => {
const $pubkey = pubkey.get()
const chat = get(chatsById).get(id)
const timestamps = chat?.messages.map(e => e.created_at) || []
const since = Math.max(0, max(timestamps) - 3600)
if ($pubkey) {
await load({...request, filters: [{kinds: [WRAP], "#p": [$pubkey], since}]})
}
},
}) })
export const chatSearch = derived(chats, $chats => export const chatSearch = derived(chats, $chats =>
+11 -7
View File
@@ -68,12 +68,11 @@
} }
} }
onMount(() => { onMount(async () => {
Object.assign(window, {get, ...app, ...state}) Object.assign(window, {get, ...app, ...state})
ready = db if (!db) {
? Promise.resolve() await initStorage("flotilla", 4, {
: initStorage("flotilla", 4, {
events: storageAdapters.fromRepository(repository, {throttle: 300}), events: storageAdapters.fromRepository(repository, {throttle: 300}),
relays: {keyPath: "url", store: throttled(1000, relays)}, relays: {keyPath: "url", store: throttled(1000, relays)},
handles: {keyPath: "nip05", store: throttled(1000, handles)}, handles: {keyPath: "nip05", store: throttled(1000, handles)},
@@ -81,7 +80,13 @@
freshness: storageAdapters.fromObjectStore(freshness, {throttle: 1000}), freshness: storageAdapters.fromObjectStore(freshness, {throttle: 1000}),
plaintext: storageAdapters.fromObjectStore(plaintext, {throttle: 1000}), plaintext: storageAdapters.fromObjectStore(plaintext, {throttle: 1000}),
tracker: storageAdapters.fromTracker(tracker, {throttle: 1000}), tracker: storageAdapters.fromTracker(tracker, {throttle: 1000}),
}).then(() => sleep(300)) // Wait an extra few ms because of repository throttle })
repository.on("update", ({added}) => {
for (const event of added) {
state.ensureUnwrapped(event)
}
})
dialog.addEventListener("close", () => { dialog.addEventListener("close", () => {
if (modal) { if (modal) {
@@ -89,7 +94,6 @@
} }
}) })
ready.then(() => {
for (const url of INDEXER_RELAYS) { for (const url of INDEXER_RELAYS) {
loadRelay(url) loadRelay(url)
} }
@@ -97,7 +101,7 @@
if ($pubkey) { if ($pubkey) {
loadUserData($pubkey) loadUserData($pubkey)
} }
}) }
}) })
</script> </script>
+5 -2
View File
@@ -1,5 +1,6 @@
<script lang="ts"> <script lang="ts">
import {onMount} from "svelte" import {onMount} from "svelte"
import {page} from "$app/stores"
import {ctx, ago, remove} from "@welshman/lib" import {ctx, ago, remove} from "@welshman/lib"
import {WRAP} from "@welshman/util" import {WRAP} from "@welshman/util"
import {pubkey, subscribe} from "@welshman/app" import {pubkey, subscribe} from "@welshman/app"
@@ -65,7 +66,7 @@
</div> </div>
</SecondaryNavSection> </SecondaryNavSection>
<label <label
class="input input-sm input-bordered mx-6 -mt-4 flex items-center gap-2" class="input input-sm input-bordered mx-6 -mt-4 flex items-center gap-2 mb-2"
in:fly={{delay: 200}}> in:fly={{delay: 200}}>
<Icon icon="magnifer" /> <Icon icon="magnifer" />
<input bind:value={term} class="grow" type="text" /> <input bind:value={term} class="grow" type="text" />
@@ -74,8 +75,10 @@
{#each chats as { id, pubkeys, messages }, i (id)} {#each chats as { id, pubkeys, messages }, i (id)}
{@const message = messages[0]} {@const message = messages[0]}
{@const others = remove($pubkey, pubkeys)} {@const others = remove($pubkey, pubkeys)}
{@const active = $page.params.chat === id}
<div <div
class="cursor-pointer border-t border-solid border-base-100 px-6 py-2 transition-colors hover:bg-base-100"> class="cursor-pointer border-t border-solid border-base-100 px-6 py-2 transition-colors hover:bg-base-100"
class:bg-base-100={active}>
<Link class="flex flex-col justify-start gap-1" href="/home/{id}"> <Link class="flex flex-col justify-start gap-1" href="/home/{id}">
<div class="flex items-center gap-2"> <div class="flex items-center gap-2">
{#if others.length === 1} {#if others.length === 1}
+2 -11
View File
@@ -30,6 +30,7 @@
import ChatMessage from "@app/components/ChatMessage.svelte" import ChatMessage from "@app/components/ChatMessage.svelte"
import ChatCompose from "@app/components/ChannelCompose.svelte" import ChatCompose from "@app/components/ChannelCompose.svelte"
import {deriveChat, splitChatId} from "@app/state" import {deriveChat, splitChatId} from "@app/state"
import {sendWrapped} from "@app/commands"
const {chat: id} = $page.params const {chat: id} = $page.params
const chat = deriveChat(id) const chat = deriveChat(id)
@@ -41,18 +42,8 @@
const onSubmit = async ({content, ...params}: EventContent) => { const onSubmit = async ({content, ...params}: EventContent) => {
const tags = [...params.tags, ...pubkeys.map(pubkey => tagPubkey(pubkey))] const tags = [...params.tags, ...pubkeys.map(pubkey => tagPubkey(pubkey))]
const template = createEvent(DIRECT_MESSAGE, {content, tags}) const template = createEvent(DIRECT_MESSAGE, {content, tags})
const nip59 = Nip59.fromSigner($signer!)
for (const recipient of uniq(pubkeys)) { await sendWrapped({template, pubkeys})
const rumor = await nip59.wrap(recipient, template)
publishThunk(
makeThunk({
event: rumor.wrap,
relays: ctx.app.router.PublishMessage(recipient).getUrls(),
}),
)
}
} }
let loading = true let loading = true
+7 -5
View File
@@ -1,7 +1,7 @@
<script lang="ts"> <script lang="ts">
import {onMount} from "svelte" import {onMount} from "svelte"
import {page} from "$app/stores" import {page} from "$app/stores"
import {sort} from "@welshman/lib" import {sort, ago} from "@welshman/lib"
import {displayRelayUrl, REACTION, NOTE, EVENT_DATE, EVENT_TIME, CLASSIFIED} from "@welshman/util" import {displayRelayUrl, REACTION, NOTE, EVENT_DATE, EVENT_TIME, CLASSIFIED} from "@welshman/util"
import {subscribe} from "@welshman/app" import {subscribe} from "@welshman/app"
import {fly, slide} from "@lib/transition" import {fly, slide} from "@lib/transition"
@@ -16,7 +16,7 @@
import SpaceExit from "@app/components/SpaceExit.svelte" import SpaceExit from "@app/components/SpaceExit.svelte"
import SpaceJoin from "@app/components/SpaceJoin.svelte" import SpaceJoin from "@app/components/SpaceJoin.svelte"
import RoomCreate from "@app/components/RoomCreate.svelte" import RoomCreate from "@app/components/RoomCreate.svelte"
import {userMembership, roomsByUrl, decodeNRelay, GENERAL, MESSAGE} from "@app/state" import {userMembership, pullConservatively, roomsByUrl, decodeNRelay, GENERAL, MESSAGE} from "@app/state"
import {pushModal} from "@app/modal" import {pushModal} from "@app/modal"
import {makeSpacePath} from "@app/routes" import {makeSpacePath} from "@app/routes"
@@ -52,8 +52,10 @@
$: otherRooms = ($roomsByUrl.get(url) || []).filter(room => !rooms.concat(GENERAL).includes(room)) $: otherRooms = ($roomsByUrl.get(url) || []).filter(room => !rooms.concat(GENERAL).includes(room))
onMount(() => { onMount(() => {
const kinds = [NOTE, REACTION, MESSAGE, EVENT_DATE, EVENT_TIME, CLASSIFIED] const filter = {kinds: [NOTE, REACTION, MESSAGE, EVENT_DATE, EVENT_TIME, CLASSIFIED]}
const sub = subscribe({filters: [{kinds}], relays: [url]}) const sub = subscribe({filters: [{...filter, since: ago(30)}]})
pullConservatively({filters: [filter], relays: [url]})
return () => sub.close() return () => sub.close()
}) })
@@ -99,7 +101,7 @@
</div> </div>
<div in:fly={{delay: getDelay(true)}}> <div in:fly={{delay: getDelay(true)}}>
<SecondaryNavItem href={makeSpacePath(url, "threads")}> <SecondaryNavItem href={makeSpacePath(url, "threads")}>
<Icon icon="notes-minimalistic" /> Threads <Icon icon="notes-minimalistic" /> Notes
</SecondaryNavItem> </SecondaryNavItem>
</div> </div>
<div in:fly={{delay: getDelay()}}> <div in:fly={{delay: getDelay()}}>