Handle a tag replies

This commit is contained in:
Jonathan Staab 2023-10-10 06:36:06 -07:00
parent 3d3e78b493
commit b271474128
14 changed files with 80 additions and 67 deletions

View File

@ -1,5 +1,12 @@
# Changelog
# 0.3.11
- [x] Re-write routing to make modal links persistent and handle history better
- [x] Handle a tag replies
- [x] Fix feed search
- [x] Simplify card theme using css
# 0.3.10
- [x] Use local relay on all requests

View File

@ -50,7 +50,7 @@
"date-picker-svelte": "^2.8.0",
"fuse.js": "^6.6.2",
"hls.js": "^1.4.10",
"hurdak": "^0.2.4",
"hurdak": "^0.2.5",
"husky": "^8.0.3",
"insane": "^2.6.2",
"lru-cache": "^7.18.3",

View File

@ -93,7 +93,7 @@
const mediaRouteOpts = {
decode: {
url: v => ({url: atob(v)}),
url: v => ({url: decodeURIComponent(v)}),
},
}

View File

@ -1,10 +1,10 @@
import {nip19} from "nostr-tools"
import {Router} from "src/util/router"
import {getNip24ChannelId} from 'src/engine'
import {getNip24ChannelId} from "src/engine"
export const router = new Router()
router.extend("media", v => btoa(v))
router.extend("media", encodeURIComponent)
router.extend("notes", nip19.noteEncode)
router.extend("people", nip19.npubEncode)
router.extend("relays", nip19.nrelayEncode)

View File

@ -2,7 +2,7 @@
import {last, sortBy, uniqBy, prop} from "ramda"
import {onMount, onDestroy} from "svelte"
import {quantify} from "hurdak"
import {findRootId, findReplyId, isLike} from "src/util/nostr"
import {findRootId, isChildOf, findReplyId, isLike} from "src/util/nostr"
import {formatTimestamp} from "src/util/misc"
import Popover from "src/partials/Popover.svelte"
import Spinner from "src/partials/Spinner.svelte"
@ -26,6 +26,7 @@
getParentHints,
getEventHints,
getIdFilters,
getReplyFilters,
selectHints,
mergeHints,
loadPubkeys,
@ -113,7 +114,7 @@
$: muted = !showMuted && $isEventMuted(event)
$: children = ctx.filter(e => findReplyId(e) === event.id)
$: children = ctx.filter(e => isChildOf(e, event))
$: replies = sortBy(
(e: Event) => -e.created_at,
@ -153,7 +154,7 @@
load({
relays: mergeHints([relays, getReplyHints(event)]),
filters: [{kinds, "#e": [event.id]}],
filters: getReplyFilters([event], {kinds}),
onEvent: e => {
if (!$isEventMuted(e)) {
ctx = uniqBy(prop("id"), ctx.concat(e))

View File

@ -2,9 +2,6 @@
import Feed from "src/app/shared/Feed.svelte"
export let pubkey
export let relays
const filter = {kinds: [7], authors: [pubkey]}
</script>
<Feed hideControls {relays} {filter} />
<Feed hideControls filter={{kinds: [7], authors: [pubkey]}} />

View File

@ -3,9 +3,6 @@
import Feed from "src/app/shared/Feed.svelte"
export let pubkey
export let relays
const filter = {kinds: noteKinds, authors: [pubkey]}
</script>
<Feed {relays} {filter} />
<Feed filter={{kinds: noteKinds, authors: [pubkey]}} />

View File

@ -1,5 +1,4 @@
<script lang="ts">
import cx from "classnames"
import {fly} from "src/util/transition"
import {stringToHue, hsl} from "src/util/misc"
import Toggle from "src/partials/Toggle.svelte"
@ -12,23 +11,21 @@
export let relay
export let rating = null
export let theme = "gray-8"
export let showStatus = false
export let hideActions = false
export let showControls = false
</script>
<div
class={cx(
`bg-${theme}`,
"flex flex-col justify-between gap-3 rounded-xl border border-l-2 border-solid border-gray-6 px-6 py-3 shadow"
)}
class="flex flex-col justify-between gap-3 rounded-xl border border-l-2 border-solid border-gray-6 bg-gray-8 px-6 py-3 shadow"
style={`border-left-color: ${hsl(stringToHue(relay.url))}`}
in:fly={{y: 20}}>
<div class="flex items-center justify-between gap-2">
<div class="flex items-center gap-2 text-xl min-w-0">
<div class="flex min-w-0 items-center gap-2 text-xl">
<i class={relay.url.startsWith("ws://") ? "fa fa-unlock" : "fa fa-lock"} />
<Anchor href={router.at("relays").of(relay.url).path} class="overflow-hidden whitespace-nowrap text-ellipsis">
<Anchor
href={router.at("relays").of(relay.url).path}
class="overflow-hidden text-ellipsis whitespace-nowrap">
{displayRelay(relay)}
</Anchor>
{#if showStatus && !getSetting("multiplextr_url")}

View File

@ -1,10 +1,7 @@
<script lang="ts">
import {quantify} from "hurdak"
import Content from "src/partials/Content.svelte"
import Anchor from "src/partials/Anchor.svelte"
import NoteContent from "src/app/shared/NoteContent.svelte"
import {router} from "src/app/router"
import {load, displayPubkey, selectHints} from "src/engine"
import Note from "src/app/shared/Note.svelte"
import {load, selectHints} from "src/engine"
export let identifier
export let kind
@ -13,8 +10,6 @@
let note
const display = displayPubkey(pubkey)
load({
relays: selectHints(relays),
filters: [{kinds: [kind], authors: [pubkey], "#d": [identifier]}],
@ -25,30 +20,7 @@
</script>
<Content>
<p>
This is a kind {kind} event called "{identifier}", published by
<Anchor class="underline" href={router.at("people").of(pubkey).path}>@{display}</Anchor>.
</p>
{#if note}
<NoteContent showEntire {note} />
{/if}
{#if note?.tags.length > 1}
<p>This note has {quantify(note.tags.length - 1, "tag")}:</p>
<ul class="list-inside list-disc">
{#each note.tags as [type, value, ...rest]}
{#if type !== "d"}
<li>
{#if type === "p"}
<Anchor class="underline" href={router.at("people").of(value).path}
>@{display}</Anchor>
{:else if type === "e"}
<Anchor class="underline" href={value}>Event {value}</Anchor>
{:else}
{type}: {value} {rest.length > 0 ? rest.join(", ") : ""}
{/if}
</li>
{/if}
{/each}
</ul>
<Note topLevel depth={3} anchorId={note.id} {note} />
{/if}
</Content>

View File

@ -24,9 +24,6 @@
loadPubkeys,
imgproxy,
getPubkeyRelays,
mergeHints,
selectHints,
getPubkeyHints,
} from "src/engine"
export let npub
@ -41,12 +38,11 @@
let loading = true
$: ownRelays = getPubkeyRelays(pubkey)
$: mergedRelays = selectHints(mergeHints([relays, getPubkeyHints(pubkey, "write")]))
$: banner = imgproxy($person.profile?.banner, {w: window.innerWidth})
info("Person", npub, pubkey, relays, $person)
info("Person", npub, pubkey, $person)
loadPubkeys([pubkey], {force: true})
loadPubkeys([pubkey], {force: true, relays})
document.title = displayPerson($person)
@ -100,9 +96,9 @@
{#if $mutes.has(pubkey)}
<Content size="lg" class="text-center">You have muted this person.</Content>
{:else if activeTab === "notes"}
<PersonNotes {pubkey} relays={mergedRelays} />
<PersonNotes {pubkey} />
{:else if activeTab === "likes"}
<PersonLikes {pubkey} relays={mergedRelays} />
<PersonLikes {pubkey} />
{:else if activeTab === "relays"}
{#if ownRelays.length > 0}
<PersonRelays relays={ownRelays} />

View File

@ -1,6 +1,7 @@
import {verifySignature, getEventHash, matchFilter as nostrToolsMatchFilter} from "nostr-tools"
import {omit, any, find, prop, groupBy, uniq} from "ramda"
import {shuffle, tryFunc, seconds, avg} from "hurdak"
import {Tags} from "src/util/nostr"
import {cached} from "src/util/lruCache"
import {env, pubkey} from "src/engine/session/state"
import {follows, network} from "src/engine/people/derived"
@ -83,6 +84,33 @@ export const getIdFilters = values => {
return filters
}
export const getReplyFilters = (events, filter) => {
const a = []
const e = []
for (const event of events) {
e.push(event.id)
if (event.kind >= 10000) {
const tags = Tags.from(event).asMeta()
a.push([event.kind, event.pubkey, tags.d || ""].join(":"))
}
}
const filters = []
if (a.length > 0) {
filters.push({...filter, "#a": a})
}
if (e.length > 0) {
filters.push({...filter, "#e": e})
}
return filters
}
export const getFilterGenerality = filter => {
if (filter.ids) {
return 1

View File

@ -48,11 +48,13 @@ export const loadPubkeys = async (
const pubkeys = force ? uniq(rawPubkeys) : getStalePubkeys(rawPubkeys)
const getChunkRelays = (chunk: string[]) => {
if (relays?.length > 0) {
return relays
const groups = chunk.map(pubkey => getPubkeyHints(pubkey, "write"))
if (relays) {
groups.push(relays)
}
return mergeHints(chunk.map(pubkey => getPubkeyHints(pubkey, "write")))
return mergeHints(groups)
}
const getChunkFilters = (chunk: string[]) => {

View File

@ -10,10 +10,12 @@
in:fly={{y: 20}}
class={cx(
$$props.class,
"card rounded-2xl border border-solid border-gray-6 bg-gray-7 p-3 text-gray-2",
"card group rounded-2xl border border-solid border-gray-6 bg-gray-7 p-3 text-gray-2",
"group-[.modal]:border group-[.modal]:border-solid group-[.modal]:border-gray-6 group-[.modal]:bg-gray-8",
"group-[.card]:border group-[.card]:border-solid group-[.card]:border-gray-6 group-[.card]:bg-gray-8",
{
"cursor-pointer transition-all hover:bg-gray-8 group-[.modal]:hover:bg-gray-7": interactive,
"cursor-pointer transition-all hover:bg-gray-8 group-[.card]:hover:bg-gray-7 group-[.modal]:hover:bg-gray-7":
interactive,
}
)}>
<slot />

View File

@ -111,6 +111,18 @@ export class Tags {
}
}
export const getNaddr = (e: Event) => [e.kind, e.pubkey, Tags.from(e).getMeta("d") || ""].join(":")
export const getIds = (e: Event) => {
const ids = [e.id]
if (e.kind >= 10000) {
ids.push(getNaddr(e))
}
return ids
}
export const findReplyAndRoot = (e: Event) => {
const tags = Tags.from(e)
.type(["a", "e"])
@ -141,6 +153,8 @@ export const findRoot = (e: Event) => prop("root", findReplyAndRoot(e))
export const findRootId = (e: Event) => findRoot(e)?.[1]
export const isChildOf = (a, b) => getIds(b).includes(findReplyId(a))
export const isLike = (content: string) =>
["", "+", "🤙", "👍", "❤️", "😎", "🏅", "🫂", "🤣", "😂", "💜"].includes(content)