Use new ALIAS kind

This commit is contained in:
Jon Staab
2025-04-15 15:45:48 -07:00
parent 374ca7f265
commit f3debe6c02
12 changed files with 67 additions and 87 deletions

View File

@@ -20,7 +20,7 @@
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, deriveAlias, deriveAliasDisplay} from "@app/state" import {colors, deriveAliasedProfile, 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"
@@ -37,7 +37,7 @@
const thunk = $thunks[event.id] const thunk = $thunks[event.id]
const today = formatTimestampAsDate(now()) const today = formatTimestampAsDate(now())
const alias = deriveAlias(event.pubkey, url) const profile = deriveAliasedProfile(event.pubkey, url)
const aliasDisplay = deriveAliasDisplay(event.pubkey, url) 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))
@@ -66,10 +66,7 @@
<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 <Avatar src={$profile?.picture} class="border border-solid border-base-content" size={8} />
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>

View File

@@ -2,12 +2,10 @@
import type {Snippet} from "svelte" import type {Snippet} from "svelte"
import {onMount} from "svelte" import {onMount} from "svelte"
import {int, nthNe, MINUTE, sortBy, remove} from "@welshman/lib" import {int, nthNe, MINUTE, sortBy, remove} from "@welshman/lib"
import {load} from "@welshman/net"
import type {TrustedEvent, EventContent} from "@welshman/util" import type {TrustedEvent, EventContent} from "@welshman/util"
import {createEvent, DIRECT_MESSAGE, INBOX_RELAYS} from "@welshman/util" import {createEvent, DIRECT_MESSAGE, INBOX_RELAYS} from "@welshman/util"
import { import {
pubkey, pubkey,
Router,
tagPubkey, tagPubkey,
loadUsingOutbox, loadUsingOutbox,
formatTimestampAsDate, formatTimestampAsDate,
@@ -28,7 +26,13 @@
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 {INDEXER_RELAYS, 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"

View File

@@ -2,7 +2,13 @@
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 {thunks, formatTimestampAsTime, pubkey} from "@welshman/app" import {
thunks,
formatTimestampAsTime,
pubkey,
deriveProfile,
deriveProfileDisplay,
} 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"
@@ -15,7 +21,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, deriveAlias, deriveAliasDisplay} from "@app/state" import {colors} 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"
@@ -30,8 +36,8 @@
const thunk = $thunks[event.id] const thunk = $thunks[event.id]
const isOwn = event.pubkey === $pubkey const isOwn = event.pubkey === $pubkey
const alias = deriveAlias(event.pubkey) const profile = deriveProfile(event.pubkey)
const aliasDisplay = deriveAliasDisplay(event.pubkey) const profileDisplay = deriveProfileDisplay(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)
@@ -101,12 +107,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={$alias?.profile?.picture} src={$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}">
{$aliasDisplay} {$profileDisplay}
</Button> </Button>
</div> </div>
</Button> </Button>

View File

@@ -1,11 +1,10 @@
<script lang="ts"> <script lang="ts">
import type {ProfilePointer} from "@welshman/content" import type {ProfilePointer} from "@welshman/content"
import {displayProfile} from "@welshman/util" import {displayProfile} from "@welshman/util"
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" import {deriveAliasedProfile} from "@app/state"
type Props = { type Props = {
value: ProfilePointer value: ProfilePointer
@@ -14,11 +13,11 @@
const {value, url}: Props = $props() const {value, url}: Props = $props()
const alias = deriveAlias(value.pubkey, url) const profile = deriveAliasedProfile(value.pubkey, url)
const openProfile = () => pushModal(ProfileDetail, {pubkey: 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($alias?.profile)} @{displayProfile($profile)}
</Button> </Button>

View File

@@ -12,7 +12,7 @@
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" import {deriveAliasedProfile, deriveAliasDisplay} from "@app/state"
type Props = { type Props = {
pubkey: string pubkey: string
@@ -21,7 +21,7 @@
const {pubkey, url}: Props = $props() const {pubkey, url}: Props = $props()
const alias = deriveAlias(pubkey, url) const profile = deriveAliasedProfile(pubkey, url)
const aliasDisplay = deriveAliasDisplay(pubkey, url) const aliasDisplay = deriveAliasDisplay(pubkey, url)
const handle = deriveHandleForPubkey(pubkey) const handle = deriveHandleForPubkey(pubkey)
const score = deriveUserWotScore(pubkey) const score = deriveUserWotScore(pubkey)
@@ -35,7 +35,7 @@
<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={$alias?.profile?.picture} size={10} /> <Avatar src={$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">

View File

@@ -1,6 +1,6 @@
<script lang="ts"> <script lang="ts">
import Avatar from "@lib/components/Avatar.svelte" import Avatar from "@lib/components/Avatar.svelte"
import {deriveAlias} from "@app/state" import {deriveAliasedProfile} from "@app/state"
type Props = { type Props = {
pubkey: string pubkey: string
@@ -9,7 +9,7 @@
const {pubkey, url, ...props}: Props = $props() const {pubkey, url, ...props}: Props = $props()
const alias = deriveAlias(pubkey, url) const profile = deriveAliasedProfile(pubkey, url)
</script> </script>
<Avatar src={$alias?.profile?.picture} icon="user-circle" {...props} /> <Avatar src={$profile?.picture} icon="user-circle" {...props} />

View File

@@ -16,7 +16,7 @@
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, deriveAlias, deriveAliasDisplay} from "@app/state" import {canDecrypt, pubkeyLink, deriveAliasedProfile, deriveAliasDisplay} from "@app/state"
import {pushModal} from "@app/modal" import {pushModal} from "@app/modal"
import {makeChatPath} from "@app/routes" import {makeChatPath} from "@app/routes"
@@ -27,8 +27,8 @@
const {pubkey, url}: Props = $props() const {pubkey, url}: Props = $props()
const alias = deriveAlias(pubkey, url) const profile = deriveAliasedProfile(pubkey, url)
const aliasDisplay = deriveAliasDisplay(pubkey, url) const display = deriveAliasDisplay(pubkey, url)
const handle = deriveHandleForPubkey(pubkey) const handle = deriveHandleForPubkey(pubkey)
const score = deriveUserWotScore(pubkey) const score = deriveUserWotScore(pubkey)
@@ -46,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={$alias?.profile?.picture} size={10} /> <Avatar src={$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">
{$aliasDisplay} {$display}
</span> </span>
<WotScore score={$score} active={following} /> <WotScore score={$score} active={following} />
</div> </div>

View File

@@ -91,8 +91,8 @@
{/snippet} {/snippet}
{#snippet info()} {#snippet info()}
<p> <p>
If enabled, changes will be published to the broader nostr network in addition to If enabled, changes will be published to the broader nostr network in addition to spaces you
spaces you are a member of. are a member of.
</p> </p>
{/snippet} {/snippet}
</FieldInline> </FieldInline>

View File

@@ -1,6 +1,6 @@
<script lang="ts"> <script lang="ts">
import Content from "@app/components/Content.svelte" import Content from "@app/components/Content.svelte"
import {deriveAlias} from "@app/state" import {deriveAliasedProfile} from "@app/state"
export type Props = { export type Props = {
pubkey: string pubkey: string
@@ -9,9 +9,9 @@
const {pubkey, url}: Props = $props() const {pubkey, url}: Props = $props()
const alias = deriveAlias(pubkey, url) const profile = deriveAliasedProfile(pubkey, url)
</script> </script>
{#if $alias?.profile} {#if $profile}
<Content event={{content: $alias.profile.about, tags: []}} hideMediaAtDepth={0} /> <Content event={{content: $profile.about, tags: []}} hideMediaAtDepth={0} />
{/if} {/if}

View File

@@ -1,5 +1,5 @@
import twColors from "tailwindcss/colors" import twColors from "tailwindcss/colors"
import {get, derived, writable} from "svelte/store" import {get, derived, readable, writable} from "svelte/store"
import * as nip19 from "nostr-tools/nip19" import * as nip19 from "nostr-tools/nip19"
import { import {
remove, remove,
@@ -16,6 +16,7 @@ import {
addToMapKey, addToMapKey,
identity, identity,
always, always,
omit,
} from "@welshman/lib" } from "@welshman/lib"
import {load} from "@welshman/net" import {load} from "@welshman/net"
import { import {
@@ -31,7 +32,6 @@ import {
GROUPS, GROUPS,
THREAD, THREAD,
COMMENT, COMMENT,
PROFILE,
getGroupTags, getGroupTags,
getRelayTagValues, getRelayTagValues,
getPubkeyTagValues, getPubkeyTagValues,
@@ -44,7 +44,6 @@ import {
normalizeRelayUrl, normalizeRelayUrl,
displayPubkey, displayPubkey,
} from "@welshman/util" } from "@welshman/util"
import {LOCAL_RELAY_URL} from "@welshman/relay"
import type {TrustedEvent, Profile, SignedEvent, PublishedList, List, Filter} from "@welshman/util" 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 {
@@ -82,6 +81,8 @@ export const GENERAL = "_"
export const PROTECTED = ["-"] export const PROTECTED = ["-"]
export const ALIAS = 11000
export const ALERT = 32830 export const ALERT = 32830
export const ALERT_STATUS = 32831 export const ALERT_STATUS = 32831
@@ -401,7 +402,7 @@ export const loadAliasByKey = makeCachedLoader({
return load({ return load({
relays: [url], relays: [url],
filters: [{kinds: [PROFILE], authors: [pubkey]}], filters: [{kinds: [ALIAS], authors: [pubkey]}],
onEvent: (event: TrustedEvent) => { onEvent: (event: TrustedEvent) => {
const profile = readProfile(event) const profile = readProfile(event)
@@ -416,50 +417,23 @@ export const loadAliasByKey = makeCachedLoader({
}) })
export const deriveAlias = (pubkey: string, url?: string) => { export const deriveAlias = (pubkey: string, url?: string) => {
const membershipUrls = getMembershipUrls(userMembership.get()) if (!url) return readable(undefined)
// Attempt to load all relevant aliases const key = encodeAliasKey(pubkey, url)
for (const $url of [url, ...membershipUrls]) {
if ($url) {
const key = encodeAliasKey(pubkey, $url)
loadAliasByKey(key) loadAliasByKey(key)
}
}
return derived([aliasesByKey, deriveProfile(pubkey)], ([$aliasesByKey, $profile]) => { return derived(aliasesByKey, $aliasesByKey => $aliasesByKey.get(key))
// 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 deriveAliasedProfile = (pubkey: string, url?: string) =>
derived([deriveProfile(pubkey), deriveAlias(pubkey, url)], ([$profile, $alias]) =>
omit(["event"], {...$profile, ...$alias}),
)
export const deriveAliasDisplay = (pubkey: string, url?: string) => export const deriveAliasDisplay = (pubkey: string, url?: string) =>
derived(deriveAlias(pubkey, url), $alias => derived(deriveAliasedProfile(pubkey, url), $profile =>
displayProfile($alias?.profile, displayPubkey(pubkey)), displayProfile($profile, displayPubkey(pubkey)),
) )
// Membership // Membership

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, PROFILE, getTag} from "@welshman/util" import {WRAP} 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"

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} from "@welshman/app" import {pubkey, session, displayNip05, deriveProfile} 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, deriveAlias} from "@app/state" import {PLATFORM_NAME} from "@app/state"
import {pushModal} from "@app/modal" import {pushModal} from "@app/modal"
import {clip} from "@app/toast" import {clip} from "@app/toast"
const alias = deriveAlias($pubkey!) const profile = deriveProfile($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={$alias?.profile?.picture} size={10} /> <Avatar src={$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($alias?.profile, pubkeyDisplay)} {displayProfile($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">
{$alias?.profile?.nip05 ? displayNip05($alias?.profile.nip05) : pubkeyDisplay} {$profile?.nip05 ? displayNip05($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 $alias?.profile?.about} {#key $profile?.about}
<Content event={{content: $alias?.profile?.about || "", tags: []}} hideMediaAtDepth={0} /> <Content event={{content: $profile?.about || "", tags: []}} hideMediaAtDepth={0} />
{/key} {/key}
</div> </div>
{#if $session?.email} {#if $session?.email}