Add per-url aliases

This commit is contained in:
Jon Staab
2025-04-15 11:03:27 -07:00
parent 91689e5b90
commit 374ca7f265
37 changed files with 321 additions and 162 deletions
+1 -1
View File
@@ -81,7 +81,7 @@
} }
const content = initialValues?.content || "" const content = initialValues?.content || ""
const editor = makeEditor({submit, uploading, content}) const editor = makeEditor({url, submit, uploading, content})
let title = $state(initialValues?.title || "") let title = $state(initialValues?.title || "")
let location = $state(initialValues?.location || "") let location = $state(initialValues?.location || "")
+1 -1
View File
@@ -18,7 +18,7 @@
<CalendarEventHeader {event} /> <CalendarEventHeader {event} />
<div class="flex w-full flex-col items-end justify-between gap-2 sm:flex-row"> <div class="flex w-full flex-col items-end justify-between gap-2 sm:flex-row">
<span class="whitespace-nowrap py-1 text-sm opacity-75"> <span class="whitespace-nowrap py-1 text-sm opacity-75">
Posted by <ProfileLink pubkey={event.pubkey} /> Posted by <ProfileLink pubkey={event.pubkey} {url} />
</span> </span>
<CalendarEventActions showActivity {url} {event} /> <CalendarEventActions showActivity {url} {event} />
</div> </div>
+3 -2
View File
@@ -6,16 +6,17 @@
type Props = { type Props = {
event: TrustedEvent event: TrustedEvent
url: string
} }
const {event}: Props = $props() const {event, url}: Props = $props()
const meta = $derived(fromPairs(event.tags) as Record<string, string>) const meta = $derived(fromPairs(event.tags) as Record<string, string>)
</script> </script>
<div class="flex min-w-0 flex-col gap-1 text-sm opacity-75"> <div class="flex min-w-0 flex-col gap-1 text-sm opacity-75">
<span class="flex items-center gap-1"> <span class="flex items-center gap-1">
<Icon icon="user-circle" size={4} /> <Icon icon="user-circle" size={4} />
Posted by <ProfileLink pubkey={event.pubkey} /> Posted by <ProfileLink pubkey={event.pubkey} {url} />
</span> </span>
{#if meta.location} {#if meta.location}
<span class="flex items-start gap-1"> <span class="flex items-start gap-1">
+6 -4
View File
@@ -1,16 +1,18 @@
<script lang="ts"> <script lang="ts">
import {writable} from "svelte/store" import {writable} from "svelte/store"
import type {EventContent} from "@welshman/util"
import {isMobile, preventDefault} from "@lib/html" import {isMobile, preventDefault} from "@lib/html"
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"
import EditorContent from "@app/editor/EditorContent.svelte" import EditorContent from "@app/editor/EditorContent.svelte"
import {makeEditor} from "@app/editor" import {makeEditor} from "@app/editor"
interface Props { type Props = {
onSubmit: any url?: string
onSubmit: (event: EventContent) => void
} }
const {onSubmit}: Props = $props() const {onSubmit, url}: Props = $props()
const autofocus = !isMobile const autofocus = !isMobile
@@ -33,7 +35,7 @@
editor.chain().clearContent().run() editor.chain().clearContent().run()
} }
const editor = makeEditor({autofocus, submit, uploading, aggressive: true}) const editor = makeEditor({url, autofocus, submit, uploading, aggressive: true})
</script> </script>
<form class="relative z-feature flex gap-2 p-2" onsubmit={preventDefault(submit)}> <form class="relative z-feature flex gap-2 p-2" onsubmit={preventDefault(submit)}>
+14 -13
View File
@@ -4,8 +4,6 @@
import { import {
thunks, thunks,
pubkey, pubkey,
deriveProfile,
deriveProfileDisplay,
formatTimestampAsDate, formatTimestampAsDate,
formatTimestampAsTime, formatTimestampAsTime,
thunkIsComplete, thunkIsComplete,
@@ -22,15 +20,15 @@
import ChannelMessageEmojiButton from "@app/components/ChannelMessageEmojiButton.svelte" import ChannelMessageEmojiButton from "@app/components/ChannelMessageEmojiButton.svelte"
import ChannelMessageMenuButton from "@app/components/ChannelMessageMenuButton.svelte" import ChannelMessageMenuButton from "@app/components/ChannelMessageMenuButton.svelte"
import ChannelMessageMenuMobile from "@app/components/ChannelMessageMenuMobile.svelte" import ChannelMessageMenuMobile from "@app/components/ChannelMessageMenuMobile.svelte"
import {colors} from "@app/state" import {colors, deriveAlias, deriveAliasDisplay} from "@app/state"
import {publishDelete, publishReaction} from "@app/commands" import {publishDelete, publishReaction} from "@app/commands"
import {pushModal} from "@app/modal" import {pushModal} from "@app/modal"
interface Props { interface Props {
url: any url: string
room: any room: string
event: TrustedEvent event: TrustedEvent
replyTo?: any replyTo?: (event: TrustedEvent) => void
showPubkey?: boolean showPubkey?: boolean
inert?: boolean inert?: boolean
} }
@@ -39,16 +37,16 @@
const thunk = $thunks[event.id] const thunk = $thunks[event.id]
const today = formatTimestampAsDate(now()) const today = formatTimestampAsDate(now())
const profile = deriveProfile(event.pubkey) const alias = deriveAlias(event.pubkey, url)
const profileDisplay = deriveProfileDisplay(event.pubkey) const aliasDisplay = deriveAliasDisplay(event.pubkey, url)
const [_, colorValue] = colors[parseInt(hash(event.pubkey)) % colors.length] const [_, colorValue] = colors[parseInt(hash(event.pubkey)) % colors.length]
const hideMenuButton = $derived($thunk && !thunkIsComplete($thunk)) const hideMenuButton = $derived($thunk && !thunkIsComplete($thunk))
const reply = () => replyTo(event) const reply = () => replyTo!(event)
const onTap = () => pushModal(ChannelMessageMenuMobile, {url, event, reply}) const onTap = () => pushModal(ChannelMessageMenuMobile, {url, event, reply})
const openProfile = () => pushModal(ProfileDetail, {pubkey: event.pubkey}) const openProfile = () => pushModal(ProfileDetail, {pubkey: event.pubkey, url})
const onReactionClick = (content: string, events: TrustedEvent[]) => { const onReactionClick = (content: string, events: TrustedEvent[]) => {
const reaction = events.find(e => e.pubkey === $pubkey) const reaction = events.find(e => e.pubkey === $pubkey)
@@ -68,7 +66,10 @@
<div class="flex w-full gap-3 overflow-auto"> <div class="flex w-full gap-3 overflow-auto">
{#if showPubkey} {#if showPubkey}
<Button onclick={openProfile} class="flex items-start"> <Button onclick={openProfile} class="flex items-start">
<Avatar src={$profile?.picture} class="border border-solid border-base-content" size={8} /> <Avatar
src={$alias?.profile?.picture}
class="border border-solid border-base-content"
size={8} />
</Button> </Button>
{:else} {:else}
<div class="w-8 min-w-8 max-w-8"></div> <div class="w-8 min-w-8 max-w-8"></div>
@@ -77,7 +78,7 @@
{#if showPubkey} {#if showPubkey}
<div class="flex items-center gap-2"> <div class="flex items-center gap-2">
<Button onclick={openProfile} class="text-sm font-bold" style="color: {colorValue}"> <Button onclick={openProfile} class="text-sm font-bold" style="color: {colorValue}">
{$profileDisplay} {$aliasDisplay}
</Button> </Button>
<span class="text-xs opacity-50"> <span class="text-xs opacity-50">
{#if formatTimestampAsDate(event.created_at) === today} {#if formatTimestampAsDate(event.created_at) === today}
@@ -90,7 +91,7 @@
</div> </div>
{/if} {/if}
<div class="text-sm"> <div class="text-sm">
<Content {event} relays={[url]} /> <Content {event} {url} />
{#if thunk} {#if thunk}
<ThunkStatus {thunk} class="mt-2" /> <ThunkStatus {thunk} class="mt-2" />
{/if} {/if}
+13 -10
View File
@@ -9,6 +9,7 @@
pubkey, pubkey,
Router, Router,
tagPubkey, tagPubkey,
loadUsingOutbox,
formatTimestampAsDate, formatTimestampAsDate,
inboxRelaySelectionsByPubkey, inboxRelaySelectionsByPubkey,
} from "@welshman/app" } from "@welshman/app"
@@ -27,17 +28,16 @@
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 ChatComposeParent from "@app/components/ChannelComposeParent.svelte" import ChatComposeParent from "@app/components/ChannelComposeParent.svelte"
import {userSettingValues, deriveChat, splitChatId, PLATFORM_NAME} from "@app/state" import {INDEXER_RELAYS, userSettingValues, deriveChat, splitChatId, PLATFORM_NAME} from "@app/state"
import {pushModal} from "@app/modal" import {pushModal} from "@app/modal"
import {sendWrapped, prependParent} from "@app/commands" import {sendWrapped, prependParent} from "@app/commands"
const { type Props = {
id,
info,
}: {
id: string id: string
info?: Snippet info?: Snippet
} = $props() }
const {id, info}: Props = $props()
const chat = deriveChat(id) const chat = deriveChat(id)
const pubkeys = splitChatId(id) const pubkeys = splitChatId(id)
@@ -107,10 +107,13 @@
onMount(() => { onMount(() => {
// Don't use loadInboxRelaySelection because we want to force reload // Don't use loadInboxRelaySelection because we want to force reload
load({ for (const pubkey of others) {
relays: Router.get().FromPubkeys(others).getUrls(), loadUsingOutbox({
filters: [{kinds: [INBOX_RELAYS], authors: others}], pubkey,
}) kind: INBOX_RELAYS,
relays: INDEXER_RELAYS,
})
}
const observer = new ResizeObserver(() => { const observer = new ResizeObserver(() => {
if (dynamicPadding && chatCompose) { if (dynamicPadding && chatCompose) {
+1 -1
View File
@@ -40,7 +40,7 @@
<div class="flex items-center justify-between gap-2"> <div class="flex items-center justify-between gap-2">
<div class="flex min-w-0 items-center gap-2"> <div class="flex min-w-0 items-center gap-2">
{#if others.length === 0} {#if others.length === 0}
<ProfileCircle pubkey={$pubkey} size={5} /> <ProfileCircle pubkey={$pubkey!} size={5} />
Note to self Note to self
{:else if others.length === 1} {:else if others.length === 1}
<ProfileCircle pubkey={others[0]} size={5} /> <ProfileCircle pubkey={others[0]} size={5} />
+6 -12
View File
@@ -2,13 +2,7 @@
import {type Instance} from "tippy.js" import {type Instance} from "tippy.js"
import {hash} from "@welshman/lib" import {hash} from "@welshman/lib"
import type {TrustedEvent} from "@welshman/util" import type {TrustedEvent} from "@welshman/util"
import { import {thunks, formatTimestampAsTime, pubkey} from "@welshman/app"
thunks,
deriveProfile,
deriveProfileDisplay,
formatTimestampAsTime,
pubkey,
} from "@welshman/app"
import {isMobile} from "@lib/html" import {isMobile} from "@lib/html"
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"
@@ -21,7 +15,7 @@
import ProfileDetail from "@app/components/ProfileDetail.svelte" import ProfileDetail from "@app/components/ProfileDetail.svelte"
import ChatMessageMenu from "@app/components/ChatMessageMenu.svelte" import ChatMessageMenu from "@app/components/ChatMessageMenu.svelte"
import ChatMessageMenuMobile from "@app/components/ChatMessageMenuMobile.svelte" import ChatMessageMenuMobile from "@app/components/ChatMessageMenuMobile.svelte"
import {colors} from "@app/state" import {colors, deriveAlias, deriveAliasDisplay} from "@app/state"
import {makeDelete, makeReaction, sendWrapped} from "@app/commands" import {makeDelete, makeReaction, sendWrapped} from "@app/commands"
import {pushModal} from "@app/modal" import {pushModal} from "@app/modal"
@@ -36,8 +30,8 @@
const thunk = $thunks[event.id] const thunk = $thunks[event.id]
const isOwn = event.pubkey === $pubkey const isOwn = event.pubkey === $pubkey
const profile = deriveProfile(event.pubkey) const alias = deriveAlias(event.pubkey)
const profileDisplay = deriveProfileDisplay(event.pubkey) const aliasDisplay = deriveAliasDisplay(event.pubkey)
const [_, colorValue] = colors[parseInt(hash(event.pubkey)) % colors.length] const [_, colorValue] = colors[parseInt(hash(event.pubkey)) % colors.length]
const reply = () => replyTo(event) const reply = () => replyTo(event)
@@ -107,12 +101,12 @@
{#if !isOwn} {#if !isOwn}
<Button onclick={openProfile} class="flex items-center gap-1"> <Button onclick={openProfile} class="flex items-center gap-1">
<Avatar <Avatar
src={$profile?.picture} src={$alias?.profile?.picture}
class="border border-solid border-base-content" class="border border-solid border-base-content"
size={4} /> size={4} />
<div class="flex items-center gap-2"> <div class="flex items-center gap-2">
<Button onclick={openProfile} class="text-sm font-bold" style="color: {colorValue}"> <Button onclick={openProfile} class="text-sm font-bold" style="color: {colorValue}">
{$profileDisplay} {$aliasDisplay}
</Button> </Button>
</div> </div>
</Button> </Button>
+4 -4
View File
@@ -38,8 +38,8 @@
showEntire?: boolean showEntire?: boolean
hideMediaAtDepth?: number hideMediaAtDepth?: number
expandMode?: string expandMode?: string
relays?: string[]
depth?: number depth?: number
url?: string
} }
let { let {
@@ -49,8 +49,8 @@
showEntire = $bindable(false), showEntire = $bindable(false),
hideMediaAtDepth = 1, hideMediaAtDepth = 1,
expandMode = "block", expandMode = "block",
relays = [],
depth = 0, depth = 0,
url,
}: Props = $props() }: Props = $props()
const fullContent = parse(event) const fullContent = parse(event)
@@ -146,10 +146,10 @@
<ContentLinkInline value={parsed.value} /> <ContentLinkInline value={parsed.value} />
{/if} {/if}
{:else if isProfile(parsed)} {:else if isProfile(parsed)}
<ContentMention value={parsed.value} /> <ContentMention value={parsed.value} {url} />
{:else if isEvent(parsed) || isAddress(parsed)} {:else if isEvent(parsed) || isAddress(parsed)}
{#if isBlock(i)} {#if isBlock(i)}
<ContentQuote {depth} {relays} {hideMediaAtDepth} value={parsed.value} {event} /> <ContentQuote {depth} {url} {hideMediaAtDepth} value={parsed.value} {event} />
{:else} {:else}
<Link <Link
external external
+11 -4
View File
@@ -1,17 +1,24 @@
<script lang="ts"> <script lang="ts">
import type {ProfilePointer} from "@welshman/content"
import {displayProfile} from "@welshman/util" import {displayProfile} from "@welshman/util"
import {deriveProfile} from "@welshman/app" import {deriveProfile} from "@welshman/app"
import Button from "@lib/components/Button.svelte" import Button from "@lib/components/Button.svelte"
import ProfileDetail from "@app/components/ProfileDetail.svelte" import ProfileDetail from "@app/components/ProfileDetail.svelte"
import {pushModal} from "@app/modal" import {pushModal} from "@app/modal"
import {deriveAlias} from "@app/state"
const {value} = $props() type Props = {
value: ProfilePointer
url?: string
}
const profile = deriveProfile(value.pubkey) const {value, url}: Props = $props()
const openProfile = () => pushModal(ProfileDetail, {pubkey: value.pubkey}) const alias = deriveAlias(value.pubkey, url)
const openProfile = () => pushModal(ProfileDetail, {pubkey: value.pubkey, url})
</script> </script>
<Button onclick={openProfile} class="link-content"> <Button onclick={openProfile} class="link-content">
@{displayProfile($profile)} @{displayProfile($alias?.profile)}
</Button> </Button>
+19 -5
View File
@@ -3,6 +3,7 @@
import {goto} from "$app/navigation" import {goto} from "$app/navigation"
import {nthEq} from "@welshman/lib" import {nthEq} from "@welshman/lib"
import {Router, tracker, repository} from "@welshman/app" import {Router, tracker, repository} from "@welshman/app"
import type {TrustedEvent} from "@welshman/util"
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"
@@ -12,11 +13,24 @@
import {deriveEvent, entityLink, ROOM} from "@app/state" import {deriveEvent, entityLink, ROOM} from "@app/state"
import {makeThreadPath, makeCalendarPath, makeRoomPath} from "@app/routes" import {makeThreadPath, makeCalendarPath, makeRoomPath} from "@app/routes"
const {value, event, depth, hideMediaAtDepth, relays = []} = $props() type Props = {
value: any
hideMediaAtDepth: number
event: TrustedEvent
depth: number
url?: string
}
const {id, identifier, kind, pubkey, relays: relayHints = []} = value const {value, event, depth, hideMediaAtDepth, url}: Props = $props()
const {id, identifier, kind, pubkey, relays = []} = value
const idOrAddress = id || new Address(kind, pubkey, identifier).toString() const idOrAddress = id || new Address(kind, pubkey, identifier).toString()
const mergedRelays = [...relays, ...Router.get().Quote(event, idOrAddress, relayHints).getUrls()] const mergedRelays = Router.get().Quote(event, idOrAddress, relays).getUrls()
if (url) {
mergedRelays.push(url)
}
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})
@@ -80,8 +94,8 @@
<Button class="my-2 block max-w-full text-left" {onclick}> <Button class="my-2 block max-w-full text-left" {onclick}>
{#if $quote} {#if $quote}
<NoteCard event={$quote} class="bg-alt rounded-box p-4"> <NoteCard event={$quote} {url} class="bg-alt rounded-box p-4">
<NoteContent {hideMediaAtDepth} {relays} event={$quote} depth={depth + 1} /> <NoteContent {hideMediaAtDepth} {url} event={$quote} depth={depth + 1} />
</NoteCard> </NoteCard>
{:else} {:else}
<div class="rounded-box p-4"> <div class="rounded-box p-4">
+1 -1
View File
@@ -32,7 +32,7 @@
onSubmit(publishComment({event, content, tags, relays: [url]})) onSubmit(publishComment({event, content, tags, relays: [url]}))
} }
const editor = makeEditor({submit, uploading, autofocus: !isMobile}) const editor = makeEditor({url, submit, uploading, autofocus: !isMobile})
let form: HTMLElement let form: HTMLElement
let spacer: HTMLElement let spacer: HTMLElement
+1 -1
View File
@@ -42,7 +42,7 @@
<div class="column gap-2"> <div class="column gap-2">
<div class="flex justify-between"> <div class="flex justify-between">
<div> <div>
<Profile pubkey={report.pubkey} /> <Profile pubkey={report.pubkey} {url} />
<span>Reported this event as "{reason}"</span> <span>Reported this event as "{reason}"</span>
</div> </div>
{#if report.pubkey === $pubkey} {#if report.pubkey === $pubkey}
+1 -1
View File
@@ -43,7 +43,7 @@
const showMembers = () => const showMembers = () =>
pushModal( pushModal(
ProfileList, ProfileList,
{pubkeys: members, title: `Members of`, subtitle: displayRelayUrl(url)}, {url, pubkeys: members, title: `Members of`, subtitle: displayRelayUrl(url)},
{replaceState}, {replaceState},
) )
+4 -2
View File
@@ -17,12 +17,14 @@
children, children,
minimal = false, minimal = false,
hideProfile = false, hideProfile = false,
url,
...restProps ...restProps
}: { }: {
event: TrustedEvent event: TrustedEvent
children: Snippet children: Snippet
minimal?: boolean minimal?: boolean
hideProfile?: boolean hideProfile?: boolean
url?: string
class?: string class?: string
} = $props() } = $props()
@@ -49,9 +51,9 @@
<div class="flex justify-between gap-2"> <div class="flex justify-between gap-2">
{#if !hideProfile} {#if !hideProfile}
{#if minimal} {#if minimal}
@<ProfileName pubkey={event.pubkey} /> @<ProfileName pubkey={event.pubkey} {url} />
{:else} {:else}
<Profile pubkey={event.pubkey} /> <Profile pubkey={event.pubkey} {url} />
{/if} {/if}
{/if} {/if}
<Link <Link
+1 -1
View File
@@ -25,7 +25,7 @@
publishReaction({event, content: emoji.unicode, relays: [url]}) publishReaction({event, content: emoji.unicode, relays: [url]})
</script> </script>
<NoteCard {event} class="card2 bg-alt"> <NoteCard {event} {url} class="card2 bg-alt">
<NoteContent {event} expandMode="inline" /> <NoteContent {event} expandMode="inline" />
<div class="flex w-full justify-between gap-2"> <div class="flex w-full justify-between gap-2">
<ReactionSummary {url} {event} {onReactionClick} reactionClass="tooltip-right"> <ReactionSummary {url} {event} {onReactionClick} reactionClass="tooltip-right">
+8 -3
View File
@@ -10,7 +10,12 @@
import ProfileInfo from "@app/components/ProfileInfo.svelte" import ProfileInfo from "@app/components/ProfileInfo.svelte"
import {makeChatPath} from "@app/routes" import {makeChatPath} from "@app/routes"
const {pubkey} = $props() type Props = {
pubkey: string
url?: string
}
const {pubkey, url}: Props = $props()
const filters: Filter[] = [{authors: [pubkey], limit: 1}] const filters: Filter[] = [{authors: [pubkey], limit: 1}]
const events = deriveEvents(repository, {filters}) const events = deriveEvents(repository, {filters})
@@ -29,13 +34,13 @@
<div class="card2 bg-alt col-2 shadow-xl"> <div class="card2 bg-alt col-2 shadow-xl">
<div class="flex justify-between"> <div class="flex justify-between">
<Profile {pubkey} /> <Profile {pubkey} {url} />
<Link class="btn btn-primary hidden sm:flex" href={makeChatPath([pubkey])}> <Link class="btn btn-primary hidden sm:flex" href={makeChatPath([pubkey])}>
<Icon icon="letter" /> <Icon icon="letter" />
Start a Chat Start a Chat
</Link> </Link>
</div> </div>
<ProfileInfo {pubkey} /> <ProfileInfo {pubkey} {url} />
{#if $events.length > 0} {#if $events.length > 0}
<div class="bg-alt badge badge-neutral border-none"> <div class="bg-alt badge badge-neutral border-none">
Last active {formatTimestampRelative($events[0].created_at)} Last active {formatTimestampRelative($events[0].created_at)}
+12 -8
View File
@@ -4,25 +4,29 @@
session, session,
userFollows, userFollows,
deriveUserWotScore, deriveUserWotScore,
deriveProfile,
deriveHandleForPubkey, deriveHandleForPubkey,
displayHandle, displayHandle,
deriveProfileDisplay,
} from "@welshman/app" } from "@welshman/app"
import Button from "@lib/components/Button.svelte" import Button from "@lib/components/Button.svelte"
import Avatar from "@lib/components/Avatar.svelte" import Avatar from "@lib/components/Avatar.svelte"
import WotScore from "@lib/components/WotScore.svelte" import WotScore from "@lib/components/WotScore.svelte"
import ProfileDetail from "@app/components/ProfileDetail.svelte" import ProfileDetail from "@app/components/ProfileDetail.svelte"
import {pushModal} from "@app/modal" import {pushModal} from "@app/modal"
import {deriveAlias, deriveAliasDisplay} from "@app/state"
const {pubkey} = $props() type Props = {
pubkey: string
url?: string
}
const profile = deriveProfile(pubkey) const {pubkey, url}: Props = $props()
const profileDisplay = deriveProfileDisplay(pubkey)
const alias = deriveAlias(pubkey, url)
const aliasDisplay = deriveAliasDisplay(pubkey, url)
const handle = deriveHandleForPubkey(pubkey) const handle = deriveHandleForPubkey(pubkey)
const score = deriveUserWotScore(pubkey) const score = deriveUserWotScore(pubkey)
const openProfile = () => pushModal(ProfileDetail, {pubkey}) const openProfile = () => pushModal(ProfileDetail, {pubkey, url})
const following = $derived( const following = $derived(
pubkey === $session!.pubkey || getPubkeyTagValues(getListTags($userFollows)).includes(pubkey), pubkey === $session!.pubkey || getPubkeyTagValues(getListTags($userFollows)).includes(pubkey),
@@ -31,12 +35,12 @@
<div class="flex max-w-full gap-3"> <div class="flex max-w-full gap-3">
<Button onclick={openProfile} class="py-1"> <Button onclick={openProfile} class="py-1">
<Avatar src={$profile?.picture} size={10} /> <Avatar src={$alias?.profile?.picture} size={10} />
</Button> </Button>
<div class="flex min-w-0 flex-col"> <div class="flex min-w-0 flex-col">
<div class="flex items-center gap-2"> <div class="flex items-center gap-2">
<Button onclick={openProfile} class="text-bold overflow-hidden text-ellipsis"> <Button onclick={openProfile} class="text-bold overflow-hidden text-ellipsis">
{$profileDisplay} {$aliasDisplay}
</Button> </Button>
<WotScore score={$score} active={following} /> <WotScore score={$score} active={following} />
</div> </div>
+9 -4
View File
@@ -1,10 +1,15 @@
<script lang="ts"> <script lang="ts">
import {deriveProfile} from "@welshman/app"
import Avatar from "@lib/components/Avatar.svelte" import Avatar from "@lib/components/Avatar.svelte"
import {deriveAlias} from "@app/state"
const {...props} = $props() type Props = {
pubkey: string
url?: string
} & Record<string, any>
const profile = deriveProfile(props.pubkey) const {pubkey, url, ...props}: Props = $props()
const alias = deriveAlias(pubkey, url)
</script> </script>
<Avatar src={$profile?.picture} icon="user-circle" {...props} /> <Avatar src={$alias?.profile?.picture} icon="user-circle" {...props} />
+12 -9
View File
@@ -5,10 +5,8 @@
session, session,
userFollows, userFollows,
deriveUserWotScore, deriveUserWotScore,
deriveProfile,
deriveHandleForPubkey, deriveHandleForPubkey,
displayHandle, displayHandle,
deriveProfileDisplay,
} 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"
@@ -18,14 +16,19 @@
import ModalFooter from "@lib/components/ModalFooter.svelte" import ModalFooter from "@lib/components/ModalFooter.svelte"
import ProfileInfo from "@app/components/ProfileInfo.svelte" import ProfileInfo from "@app/components/ProfileInfo.svelte"
import ChatEnable from "@app/components/ChatEnable.svelte" import ChatEnable from "@app/components/ChatEnable.svelte"
import {canDecrypt, pubkeyLink} from "@app/state" import {canDecrypt, pubkeyLink, deriveAlias, deriveAliasDisplay} from "@app/state"
import {pushModal} from "@app/modal" import {pushModal} from "@app/modal"
import {makeChatPath} from "@app/routes" import {makeChatPath} from "@app/routes"
const {pubkey} = $props() export type Props = {
pubkey: string
url?: string
}
const profile = deriveProfile(pubkey) const {pubkey, url}: Props = $props()
const profileDisplay = deriveProfileDisplay(pubkey)
const alias = deriveAlias(pubkey, url)
const aliasDisplay = deriveAliasDisplay(pubkey, url)
const handle = deriveHandleForPubkey(pubkey) const handle = deriveHandleForPubkey(pubkey)
const score = deriveUserWotScore(pubkey) const score = deriveUserWotScore(pubkey)
@@ -43,12 +46,12 @@
<div class="column gap-4"> <div class="column gap-4">
<div class="flex max-w-full gap-3"> <div class="flex max-w-full gap-3">
<span class="py-1"> <span class="py-1">
<Avatar src={$profile?.picture} size={10} /> <Avatar src={$alias?.profile?.picture} size={10} />
</span> </span>
<div class="flex min-w-0 flex-col"> <div class="flex min-w-0 flex-col">
<div class="flex items-center gap-2"> <div class="flex items-center gap-2">
<span class="text-bold overflow-hidden text-ellipsis"> <span class="text-bold overflow-hidden text-ellipsis">
{$profileDisplay} {$aliasDisplay}
</span> </span>
<WotScore score={$score} active={following} /> <WotScore score={$score} active={following} />
</div> </div>
@@ -57,7 +60,7 @@
</div> </div>
</div> </div>
</div> </div>
<ProfileInfo {pubkey} /> <ProfileInfo {pubkey} {url} />
<ModalFooter> <ModalFooter>
<Button onclick={back} class="btn btn-link"> <Button onclick={back} class="btn btn-link">
<Icon icon="alt-arrow-left" /> <Icon icon="alt-arrow-left" />
+4 -4
View File
@@ -23,12 +23,12 @@
const back = () => history.back() const back = () => history.back()
const onsubmit = ({profile, shouldBroadcast}: {profile: Profile; shouldBroadcast: boolean}) => { const onsubmit = ({profile, shouldBroadcast}: {profile: Profile; shouldBroadcast: boolean}) => {
const relays = shouldBroadcast
? Router.get().FromUser().getUrls()
: getMembershipUrls($userMembership)
const template = isPublishedProfile(profile) ? editProfile(profile) : createProfile(profile) const template = isPublishedProfile(profile) ? editProfile(profile) : createProfile(profile)
const relays = [...getMembershipUrls($userMembership)]
if (!shouldBroadcast) { if (shouldBroadcast) {
relays.push(...Router.get().FromUser().getUrls())
} else {
template.tags = uniqTags([...template.tags, PROTECTED]) template.tags = uniqTags([...template.tags, PROTECTED])
} }
+1 -1
View File
@@ -91,7 +91,7 @@
{/snippet} {/snippet}
{#snippet info()} {#snippet info()}
<p> <p>
If enabled, your profile will be published to the broader nostr network, as well as to If enabled, changes will be published to the broader nostr network in addition to
spaces you are a member of. spaces you are a member of.
</p> </p>
{/snippet} {/snippet}
+10 -5
View File
@@ -1,12 +1,17 @@
<script lang="ts"> <script lang="ts">
import {deriveProfile} from "@welshman/app"
import Content from "@app/components/Content.svelte" import Content from "@app/components/Content.svelte"
import {deriveAlias} from "@app/state"
const {pubkey} = $props() export type Props = {
pubkey: string
url?: string
}
const profile = deriveProfile(pubkey) const {pubkey, url}: Props = $props()
const alias = deriveAlias(pubkey, url)
</script> </script>
{#if $profile} {#if $alias?.profile}
<Content event={{content: $profile.about, tags: []}} /> <Content event={{content: $alias.profile.about, tags: []}} hideMediaAtDepth={0} />
{/if} {/if}
+8 -3
View File
@@ -5,11 +5,16 @@
import ProfileDetail from "@app/components/ProfileDetail.svelte" import ProfileDetail from "@app/components/ProfileDetail.svelte"
import {pushModal} from "@app/modal" import {pushModal} from "@app/modal"
const {pubkey}: {pubkey: string} = $props() type Props = {
pubkey: string
url?: string
}
const openProfile = () => pushModal(ProfileDetail, {pubkey}) const {pubkey, url}: Props = $props()
const openProfile = () => pushModal(ProfileDetail, {pubkey, url})
</script> </script>
<Button onclick={preventDefault(openProfile)} class="link-content"> <Button onclick={preventDefault(openProfile)} class="link-content">
@<ProfileName {pubkey} /> @<ProfileName {pubkey} {url} />
</Button> </Button>
+4 -3
View File
@@ -5,11 +5,12 @@
interface Props { interface Props {
title: any title: any
subtitle?: string
pubkeys: any pubkeys: any
subtitle?: string
url?: string
} }
const {subtitle = "", pubkeys, ...restProps}: Props = $props() const {subtitle = "", pubkeys, url, ...restProps}: Props = $props()
</script> </script>
<div class="column gap-4"> <div class="column gap-4">
@@ -23,7 +24,7 @@
</ModalHeader> </ModalHeader>
{#each pubkeys as pubkey (pubkey)} {#each pubkeys as pubkey (pubkey)}
<div class="card2 bg-alt"> <div class="card2 bg-alt">
<Profile {pubkey} /> <Profile {pubkey} {url} />
</div> </div>
{/each} {/each}
<Button class="btn btn-primary" onclick={() => history.back()}>Got it</Button> <Button class="btn btn-primary" onclick={() => history.back()}>Got it</Button>
+9 -4
View File
@@ -1,9 +1,14 @@
<script lang="ts"> <script lang="ts">
import {deriveProfileDisplay} from "@welshman/app" import {deriveAliasDisplay} from "@app/state"
const {pubkey} = $props() type Props = {
pubkey: string
url?: string
}
const profileDisplay = deriveProfileDisplay(pubkey) const {pubkey, url}: Props = $props()
const aliasDisplay = deriveAliasDisplay(pubkey, url)
</script> </script>
{$profileDisplay} {$aliasDisplay}
+1 -1
View File
@@ -53,7 +53,7 @@
history.back() history.back()
} }
const editor = makeEditor({submit, uploading, placeholder: "What's on your mind?"}) const editor = makeEditor({url, submit, uploading, placeholder: "What's on your mind?"})
let title: string = $state("") let title: string = $state("")
</script> </script>
+6 -7
View File
@@ -8,13 +8,12 @@
import ThreadActions from "@app/components/ThreadActions.svelte" import ThreadActions from "@app/components/ThreadActions.svelte"
import {makeThreadPath} from "@app/routes" import {makeThreadPath} from "@app/routes"
const { type Props = {
url,
event,
}: {
url: string url: string
event: TrustedEvent event: TrustedEvent
} = $props() }
const {url, event}: Props = $props()
const title = event.tags.find(nthEq(0, "title"))?.[1] const title = event.tags.find(nthEq(0, "title"))?.[1]
</script> </script>
@@ -32,10 +31,10 @@
{formatTimestamp(event.created_at)} {formatTimestamp(event.created_at)}
</p> </p>
{/if} {/if}
<Content {event} expandMode="inline" relays={[url]} /> <Content {event} {url} expandMode="inline" />
<div class="flex w-full flex-col items-end justify-between gap-2 sm:flex-row"> <div class="flex w-full flex-col items-end justify-between gap-2 sm:flex-row">
<span class="whitespace-nowrap py-1 text-sm opacity-75"> <span class="whitespace-nowrap py-1 text-sm opacity-75">
Posted by <ProfileLink pubkey={event.pubkey} /> Posted by <ProfileLink pubkey={event.pubkey} {url} />
</span> </span>
<ThreadActions showActivity {url} {event} /> <ThreadActions showActivity {url} {event} />
</div> </div>
+22 -20
View File
@@ -1,26 +1,28 @@
import type {NodeViewProps} from "@tiptap/core" import type {NodeViewProps} from "@tiptap/core"
import {deriveProfileDisplay} from "@welshman/app" import {deriveAliasDisplay} from "@app/state"
export const MentionNodeView = ({node}: NodeViewProps) => { export const makeMentionNodeView =
const dom = document.createElement("span") (url?: string) =>
const display = deriveProfileDisplay(node.attrs.pubkey) ({node}: NodeViewProps) => {
const dom = document.createElement("span")
const display = deriveAliasDisplay(node.attrs.pubkey, url)
dom.classList.add("tiptap-object") dom.classList.add("tiptap-object")
const unsubDisplay = display.subscribe($display => { const unsubDisplay = display.subscribe($display => {
dom.textContent = "@" + $display dom.textContent = "@" + $display
}) })
return { return {
dom, dom,
destroy: () => { destroy: () => {
unsubDisplay() unsubDisplay()
}, },
selectNode() { selectNode() {
dom.classList.add("tiptap-active") dom.classList.add("tiptap-active")
}, },
deselectNode() { deselectNode() {
dom.classList.remove("tiptap-active") dom.classList.remove("tiptap-active")
}, },
}
} }
}
+9 -4
View File
@@ -5,15 +5,20 @@
deriveUserWotScore, deriveUserWotScore,
deriveHandleForPubkey, deriveHandleForPubkey,
displayHandle, displayHandle,
deriveProfileDisplay,
} from "@welshman/app" } from "@welshman/app"
import WotScore from "@lib/components/WotScore.svelte" import WotScore from "@lib/components/WotScore.svelte"
import ProfileCircle from "@app/components/ProfileCircle.svelte" import ProfileCircle from "@app/components/ProfileCircle.svelte"
import {deriveAliasDisplay} from "@app/state"
const {value} = $props() type Props = {
value: string
url?: string
}
const {value, url}: Props = $props()
const pubkey = value const pubkey = value
const profileDisplay = deriveProfileDisplay(pubkey) const profileDisplay = deriveAliasDisplay(pubkey)
const handle = deriveHandleForPubkey(pubkey) const handle = deriveHandleForPubkey(pubkey)
const score = deriveUserWotScore(pubkey) const score = deriveUserWotScore(pubkey)
@@ -22,7 +27,7 @@
<div class="flex max-w-full gap-3"> <div class="flex max-w-full gap-3">
<div class="py-1"> <div class="py-1">
<ProfileCircle {pubkey} /> <ProfileCircle {pubkey} {url} />
</div> </div>
<div class="flex min-w-0 flex-col"> <div class="flex min-w-0 flex-col">
<div class="flex items-center gap-2"> <div class="flex items-center gap-2">
+5 -3
View File
@@ -5,7 +5,7 @@ import type {StampedEvent} from "@welshman/util"
import {Router, signer, profileSearch} from "@welshman/app" import {Router, signer, profileSearch} from "@welshman/app"
import {Editor, 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 {makeMentionNodeView} from "./MentionNodeView"
import ProfileSuggestion from "./ProfileSuggestion.svelte" import ProfileSuggestion from "./ProfileSuggestion.svelte"
export const getUploadType = () => getSetting<"nip96" | "blossom">("upload_type") export const getUploadType = () => getSetting<"nip96" | "blossom">("upload_type")
@@ -30,6 +30,7 @@ export const makeEditor = ({
charCount, charCount,
content = "", content = "",
placeholder = "", placeholder = "",
url,
submit, submit,
uploading, uploading,
wordCount, wordCount,
@@ -39,6 +40,7 @@ export const makeEditor = ({
charCount?: Writable<number> charCount?: Writable<number>
content?: string content?: string
placeholder?: string placeholder?: string
url?: string
submit: () => void submit: () => void
uploading?: Writable<boolean> uploading?: Writable<boolean>
wordCount?: Writable<number> wordCount?: Writable<number>
@@ -76,7 +78,7 @@ export const makeEditor = ({
}, },
nprofile: { nprofile: {
extend: { extend: {
addNodeView: () => MentionNodeView, addNodeView: () => makeMentionNodeView(url),
addProseMirrorPlugins() { addProseMirrorPlugins() {
return [ return [
MentionSuggestion({ MentionSuggestion({
@@ -86,7 +88,7 @@ export const makeEditor = ({
createSuggestion: (value: string) => { createSuggestion: (value: string) => {
const target = document.createElement("div") const target = document.createElement("div")
mount(ProfileSuggestion, {target, props: {value}}) mount(ProfileSuggestion, {target, props: {value, url}})
return target return target
}, },
+96 -2
View File
@@ -1,5 +1,5 @@
import twColors from "tailwindcss/colors" import twColors from "tailwindcss/colors"
import {get, derived} from "svelte/store" import {get, derived, writable} from "svelte/store"
import * as nip19 from "nostr-tools/nip19" import * as nip19 from "nostr-tools/nip19"
import { import {
remove, remove,
@@ -31,6 +31,7 @@ import {
GROUPS, GROUPS,
THREAD, THREAD,
COMMENT, COMMENT,
PROFILE,
getGroupTags, getGroupTags,
getRelayTagValues, getRelayTagValues,
getPubkeyTagValues, getPubkeyTagValues,
@@ -38,10 +39,13 @@ import {
displayProfile, displayProfile,
readList, readList,
getListTags, getListTags,
readProfile,
asDecryptedEvent, asDecryptedEvent,
normalizeRelayUrl, normalizeRelayUrl,
displayPubkey,
} from "@welshman/util" } from "@welshman/util"
import type {TrustedEvent, SignedEvent, PublishedList, List, Filter} from "@welshman/util" import {LOCAL_RELAY_URL} from "@welshman/relay"
import type {TrustedEvent, Profile, SignedEvent, PublishedList, List, Filter} from "@welshman/util"
import {Nip59, decrypt} from "@welshman/signer" import {Nip59, decrypt} from "@welshman/signer"
import { import {
pubkey, pubkey,
@@ -64,6 +68,8 @@ import {
makeOutboxLoader, makeOutboxLoader,
routerContext, routerContext,
appContext, appContext,
deriveProfile,
makeCachedLoader,
} from "@welshman/app" } from "@welshman/app"
import type {Thunk, Relay} from "@welshman/app" import type {Thunk, Relay} from "@welshman/app"
import {deriveEvents, deriveEventsMapped, withGetter, synced} from "@welshman/store" import {deriveEvents, deriveEventsMapped, withGetter, synced} from "@welshman/store"
@@ -369,6 +375,93 @@ export const alertStatuses = deriveEventsMapped<AlertStatus>(repository, {
}, },
}) })
// Aliases
export type Alias = {
url: string
pubkey: string
profile: Profile
}
export const encodeAliasKey = (pubkey: string, url: string) => `${pubkey}:${url}`
export const decodeAliasKey = (key: string) => {
const [pubkey, url] = key.split(/:(.*)/s)
return {pubkey, url}
}
export const aliasesByKey = withGetter(writable(new Map<string, Alias>()))
export const loadAliasByKey = makeCachedLoader({
name: "aliases",
indexStore: aliasesByKey,
load: (key: string) => {
const {pubkey, url} = decodeAliasKey(key)
return load({
relays: [url],
filters: [{kinds: [PROFILE], authors: [pubkey]}],
onEvent: (event: TrustedEvent) => {
const profile = readProfile(event)
aliasesByKey.update($aliasesByKey => {
$aliasesByKey.set(key, {url, pubkey, profile})
return $aliasesByKey
})
},
})
},
})
export const deriveAlias = (pubkey: string, url?: string) => {
const membershipUrls = getMembershipUrls(userMembership.get())
// Attempt to load all relevant aliases
for (const $url of [url, ...membershipUrls]) {
if ($url) {
const key = encodeAliasKey(pubkey, $url)
loadAliasByKey(key)
}
}
return derived([aliasesByKey, deriveProfile(pubkey)], ([$aliasesByKey, $profile]) => {
// Try to find an alias for the url we were asked about
if (url) {
const alias = $aliasesByKey.get(encodeAliasKey(pubkey, url))
if (alias) {
return alias
}
}
// Fall back to global profiles
if ($profile) {
return {
pubkey,
url: LOCAL_RELAY_URL,
profile: $profile,
}
}
// Fall back to other aliases we know about
for (const $url of membershipUrls) {
const alias = $aliasesByKey.get(encodeAliasKey(pubkey, $url))
if (alias) {
return alias
}
}
})
}
export const deriveAliasDisplay = (pubkey: string, url?: string) =>
derived(deriveAlias(pubkey, url), $alias =>
displayProfile($alias?.profile, displayPubkey(pubkey)),
)
// Membership // Membership
export const hasMembershipUrl = (list: List | undefined, url: string) => export const hasMembershipUrl = (list: List | undefined, url: string) =>
@@ -470,6 +563,7 @@ export const {
name: "chats", name: "chats",
store: chats, store: chats,
getKey: chat => chat.id, getKey: chat => chat.id,
load: always(Promise.resolve()),
}) })
export const chatSearch = derived(chats, $chats => export const chatSearch = derived(chats, $chats =>
+1 -1
View File
@@ -9,7 +9,7 @@
import {bytesToHex, hexToBytes} from "@noble/hashes/utils" import {bytesToHex, hexToBytes} from "@noble/hashes/utils"
import {identity, memoize, sleep, defer, ago, WEEK, TaskQueue} from "@welshman/lib" import {identity, memoize, sleep, defer, ago, WEEK, TaskQueue} from "@welshman/lib"
import type {TrustedEvent, StampedEvent} from "@welshman/util" import type {TrustedEvent, StampedEvent} from "@welshman/util"
import {WRAP} from "@welshman/util" import {WRAP, PROFILE, getTag} from "@welshman/util"
import {Nip46Broker, makeSecret} from "@welshman/signer" import {Nip46Broker, makeSecret} from "@welshman/signer"
import type {Socket} from "@welshman/net" import type {Socket} from "@welshman/net"
import {request, defaultSocketPolicies, makeSocketPolicyAuth} from "@welshman/net" import {request, defaultSocketPolicies, makeSocketPolicyAuth} from "@welshman/net"
+8 -8
View File
@@ -2,7 +2,7 @@
import * as nip19 from "nostr-tools/nip19" 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} from "@welshman/app"
import {slideAndFade} from "@lib/transition" import {slideAndFade} from "@lib/transition"
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"
@@ -13,11 +13,11 @@
import ProfileDelete from "@app/components/ProfileDelete.svelte" import ProfileDelete from "@app/components/ProfileDelete.svelte"
import InfoKeys from "@app/components/InfoKeys.svelte" import InfoKeys from "@app/components/InfoKeys.svelte"
import Alerts from "@app/components/Alerts.svelte" import Alerts from "@app/components/Alerts.svelte"
import {PLATFORM_NAME} from "@app/state" import {PLATFORM_NAME, deriveAlias} from "@app/state"
import {pushModal} from "@app/modal" import {pushModal} from "@app/modal"
import {clip} from "@app/toast" import {clip} from "@app/toast"
const profile = deriveProfile($pubkey!) const alias = deriveAlias($pubkey!)
const pubkeyDisplay = displayPubkey($pubkey!) const pubkeyDisplay = displayPubkey($pubkey!)
@@ -39,16 +39,16 @@
<div class="flex justify-between gap-2"> <div class="flex justify-between gap-2">
<div class="flex max-w-full gap-3"> <div class="flex max-w-full gap-3">
<div class="py-1"> <div class="py-1">
<Avatar src={$profile?.picture} size={10} /> <Avatar src={$alias?.profile?.picture} size={10} />
</div> </div>
<div class="flex min-w-0 flex-col"> <div class="flex min-w-0 flex-col">
<div class="flex items-center gap-2"> <div class="flex items-center gap-2">
<div class="text-bold overflow-hidden text-ellipsis"> <div class="text-bold overflow-hidden text-ellipsis">
{displayProfile($profile, pubkeyDisplay)} {displayProfile($alias?.profile, pubkeyDisplay)}
</div> </div>
</div> </div>
<div class="overflow-hidden text-ellipsis text-sm opacity-75"> <div class="overflow-hidden text-ellipsis text-sm opacity-75">
{$profile?.nip05 ? displayNip05($profile.nip05) : pubkeyDisplay} {$alias?.profile?.nip05 ? displayNip05($alias?.profile.nip05) : pubkeyDisplay}
</div> </div>
</div> </div>
</div> </div>
@@ -56,8 +56,8 @@
<Icon icon="pen-new-square" /> <Icon icon="pen-new-square" />
</Button> </Button>
</div> </div>
{#key $profile?.about} {#key $alias?.profile?.about}
<Content event={{content: $profile?.about || "", tags: []}} hideMediaAtDepth={0} /> <Content event={{content: $alias?.profile?.about || "", tags: []}} hideMediaAtDepth={0} />
{/key} {/key}
</div> </div>
{#if $session?.email} {#if $session?.email}
@@ -311,7 +311,7 @@
<ChannelComposeParent event={share} clear={clearShare} verb="Sharing" /> <ChannelComposeParent event={share} clear={clearShare} verb="Sharing" />
{/if} {/if}
</div> </div>
<ChannelCompose bind:this={compose} {onSubmit} /> <ChannelCompose bind:this={compose} {onSubmit} {url} />
</div> </div>
{#if showScrollButton} {#if showScrollButton}
@@ -81,11 +81,11 @@
<CalendarEventDate event={$event} /> <CalendarEventDate event={$event} />
<div class="flex min-w-0 flex-grow flex-col gap-1"> <div class="flex min-w-0 flex-grow flex-col gap-1">
<CalendarEventHeader event={$event} /> <CalendarEventHeader event={$event} />
<CalendarEventMeta event={$event} /> <CalendarEventMeta event={$event} {url} />
<div class="flex py-2 opacity-50"> <div class="flex py-2 opacity-50">
<div class="h-px flex-grow bg-base-content opacity-25"></div> <div class="h-px flex-grow bg-base-content opacity-25"></div>
</div> </div>
<Content showEntire event={$event} relays={[url]} /> <Content showEntire event={$event} {url} />
</div> </div>
</div> </div>
<div class="flex w-full flex-col justify-end sm:flex-row"> <div class="flex w-full flex-col justify-end sm:flex-row">
@@ -101,9 +101,9 @@
</div> </div>
{/if} {/if}
{#each sortBy(e => e.created_at, $replies).slice(0, showAll ? undefined : 4) as reply (reply.id)} {#each sortBy(e => e.created_at, $replies).slice(0, showAll ? undefined : 4) as reply (reply.id)}
<NoteCard event={reply} class="card2 bg-alt z-feature w-full"> <NoteCard event={reply} {url} class="card2 bg-alt z-feature w-full">
<div class="col-3 ml-12"> <div class="col-3 ml-12">
<Content showEntire event={reply} /> <Content showEntire event={reply} {url} />
<CalendarEventActions event={reply} {url} /> <CalendarEventActions event={reply} {url} />
</div> </div>
</NoteCard> </NoteCard>
@@ -76,9 +76,9 @@
<PageContent class="flex flex-col p-2 pt-4"> <PageContent class="flex flex-col p-2 pt-4">
{#if $event} {#if $event}
<div class="flex flex-col gap-3"> <div class="flex flex-col gap-3">
<NoteCard event={$event} class="card2 bg-alt z-feature w-full"> <NoteCard event={$event} {url} class="card2 bg-alt z-feature w-full">
<div class="col-3 ml-12"> <div class="col-3 ml-12">
<Content showEntire event={$event} relays={[url]} /> <Content showEntire event={$event} {url} />
<ThreadActions event={$event} {url} /> <ThreadActions event={$event} {url} />
</div> </div>
</NoteCard> </NoteCard>
@@ -91,9 +91,9 @@
</div> </div>
{/if} {/if}
{#each sortBy(e => -e.created_at, $replies).slice(0, showAll ? undefined : 4) as reply (reply.id)} {#each sortBy(e => -e.created_at, $replies).slice(0, showAll ? undefined : 4) as reply (reply.id)}
<NoteCard event={reply} class="card2 bg-alt z-feature w-full"> <NoteCard event={reply} {url} class="card2 bg-alt z-feature w-full">
<div class="col-3 ml-12"> <div class="col-3 ml-12">
<Content showEntire event={reply} /> <Content showEntire event={reply} {url} />
<ThreadActions event={reply} {url} /> <ThreadActions event={reply} {url} />
</div> </div>
</NoteCard> </NoteCard>