Tweak profiles/search

This commit is contained in:
Jon Staab
2025-06-05 12:25:55 -07:00
parent ac756bf266
commit e3fbd69e6e
11 changed files with 58 additions and 55 deletions

View File

@@ -2,15 +2,18 @@
import {onMount} from "svelte" import {onMount} from "svelte"
import {formatTimestampRelative} from "@welshman/lib" import {formatTimestampRelative} from "@welshman/lib"
import type {Filter} from "@welshman/util" import type {Filter} from "@welshman/util"
import {NOTE, GROUPS, MESSAGE, THREAD, COMMENT, getRelayTags, getListTags} from "@welshman/util"
import {deriveEvents} from "@welshman/store" import {deriveEvents} from "@welshman/store"
import {load} from "@welshman/net" import {load} from "@welshman/net"
import {Router} from "@welshman/router" import {Router} from "@welshman/router"
import {repository, loadRelaySelections} from "@welshman/app" import {repository, loadRelaySelections} 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 Button from "@lib/components/Button.svelte"
import Profile from "@app/components/Profile.svelte" import Profile from "@app/components/Profile.svelte"
import ProfileInfo from "@app/components/ProfileInfo.svelte" import ProfileInfo from "@app/components/ProfileInfo.svelte"
import {pubkeyLink} from "@app/state" import ProfileDetail from "@app/components/ProfileDetail.svelte"
import {membershipsByPubkey} from "@app/state"
import {pushModal} from "@app/modal"
type Props = { type Props = {
pubkey: string pubkey: string
@@ -18,17 +21,23 @@
} }
const {pubkey, url}: Props = $props() 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})
const membership = $derived($membershipsByPubkey.get(pubkey))
const relays = $derived(getRelayTags(getListTags(membership)))
const openProfile = () => pushModal(ProfileDetail, {pubkey, url})
onMount(async () => { onMount(async () => {
// Make sure we have their relay selections before we load their posts // Make sure we have their relay selections before we load their posts
await loadRelaySelections(pubkey) await loadRelaySelections(pubkey)
// Load at least one note, regardless of time frame // Load groups and at least one note, regardless of time frame
load({ load({
filters: [{authors: [pubkey], limit: 1}], filters: [
{authors: [pubkey], kinds: [GROUPS]},
{authors: [pubkey], limit: 1, kinds: [NOTE, MESSAGE, THREAD, COMMENT]},
],
relays: Router.get().FromPubkeys([pubkey]).getUrls(), relays: Router.get().FromPubkeys([pubkey]).getUrls(),
}) })
}) })
@@ -37,19 +46,27 @@
<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} {url} /> <Profile {pubkey} {url} />
<Link external href={pubkeyLink(pubkey)} class="btn btn-primary hidden sm:flex"> <Button onclick={openProfile} class="btn btn-primary hidden sm:flex">
<Icon icon="user-circle" /> <Icon icon="user-circle" />
See Complete Profile View Profile
</Link> </Button>
</div> </div>
<ProfileInfo {pubkey} {url} /> <ProfileInfo {pubkey} {url} />
{#if $events.length > 0} <div class="flex flex-wrap gap-2">
<div class="bg-alt badge badge-neutral border-none"> {#if $events.length > 0}
Last active {formatTimestampRelative($events[0].created_at)} <div class="badge badge-neutral">
</div> Last active {formatTimestampRelative($events[0].created_at)}
{/if} </div>
<Link external href={pubkeyLink(pubkey)} class="btn btn-primary sm:hidden"> {/if}
{#if relays.length > 0}
<div class="badge badge-neutral">
{relays.length}
{relays.length === 1 ? "space" : "spaces"}
</div>
{/if}
</div>
<Button onclick={openProfile} class="btn btn-primary sm:hidden">
<Icon icon="user-circle" /> <Icon icon="user-circle" />
See Complete Profile View Profile
</Link> </Button>
</div> </div>

View File

@@ -1,11 +1,8 @@
<script lang="ts"> <script lang="ts">
import * as nip19 from "nostr-tools/nip19" import * as nip19 from "nostr-tools/nip19"
import {removeNil} from "@welshman/lib" import {removeNil} from "@welshman/lib"
import {displayPubkey, getPubkeyTagValues, getListTags} from "@welshman/util" import {displayPubkey} from "@welshman/util"
import { import {
session,
userFollows,
deriveUserWotScore,
deriveHandleForPubkey, deriveHandleForPubkey,
displayHandle, displayHandle,
deriveProfile, deriveProfile,
@@ -14,7 +11,7 @@
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 Avatar from "@lib/components/Avatar.svelte" import Avatar from "@lib/components/Avatar.svelte"
import WotScore from "@lib/components/WotScore.svelte" import WotScore from "@app/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 {clip} from "@app/toast" import {clip} from "@app/toast"
@@ -32,15 +29,10 @@
const profile = deriveProfile(pubkey, relays) const profile = deriveProfile(pubkey, relays)
const profileDisplay = deriveProfileDisplay(pubkey, relays) const profileDisplay = deriveProfileDisplay(pubkey, relays)
const handle = deriveHandleForPubkey(pubkey) const handle = deriveHandleForPubkey(pubkey)
const score = deriveUserWotScore(pubkey)
const openProfile = () => pushModal(ProfileDetail, {pubkey, url}) const openProfile = () => pushModal(ProfileDetail, {pubkey, url})
const copyPubkey = () => clip(nip19.npubEncode(pubkey)) const copyPubkey = () => clip(nip19.npubEncode(pubkey))
const following = $derived(
pubkey === $session!.pubkey || getPubkeyTagValues(getListTags($userFollows)).includes(pubkey),
)
</script> </script>
<div class="flex max-w-full items-start gap-3"> <div class="flex max-w-full items-start gap-3">
@@ -52,7 +44,7 @@
<Button onclick={openProfile} class="text-bold overflow-hidden text-ellipsis"> <Button onclick={openProfile} class="text-bold overflow-hidden text-ellipsis">
{$profileDisplay} {$profileDisplay}
</Button> </Button>
<WotScore score={$score} active={following} /> <WotScore {pubkey} />
</div> </div>
{#if $handle} {#if $handle}
<div class="overflow-hidden text-ellipsis text-sm opacity-75"> <div class="overflow-hidden text-ellipsis text-sm opacity-75">

View File

@@ -1,6 +1,7 @@
<script lang="ts"> <script lang="ts">
import {goto} from "$app/navigation" import {goto} from "$app/navigation"
import Icon from "@lib/components/Icon.svelte" import Icon from "@lib/components/Icon.svelte"
import Avatar from "@lib/components/Avatar.svelte"
import Link from "@lib/components/Link.svelte" import Link from "@lib/components/Link.svelte"
import Button from "@lib/components/Button.svelte" import Button from "@lib/components/Button.svelte"
import ModalFooter from "@lib/components/ModalFooter.svelte" import ModalFooter from "@lib/components/ModalFooter.svelte"
@@ -35,8 +36,8 @@
</Button> </Button>
<div class="flex gap-2"> <div class="flex gap-2">
<Link external href={pubkeyLink(pubkey)} class="btn btn-neutral"> <Link external href={pubkeyLink(pubkey)} class="btn btn-neutral">
<Icon icon="user-circle" /> <Avatar src="/coracle.png" />
See Complete Profile Open in Coracle
</Link> </Link>
<Button onclick={openChat} class="btn btn-primary"> <Button onclick={openChat} class="btn btn-primary">
<Icon icon="letter" /> <Icon icon="letter" />

View File

@@ -14,5 +14,5 @@
</script> </script>
{#if $profile} {#if $profile}
<Content event={{content: $profile.about, tags: []}} hideMediaAtDepth={0} /> <Content event={{content: $profile.about || "", tags: []}} hideMediaAtDepth={0} />
{/if} {/if}

View File

@@ -15,19 +15,21 @@
<script lang="ts"> <script lang="ts">
import {clamp} from "@welshman/lib" import {clamp} from "@welshman/lib"
import {pubkey, getFollows, deriveUserWotScore} from "@welshman/app"
interface Props { interface Props {
score: any pubkey: string
max?: number
active?: boolean
} }
const {score, max = 100, active = false}: Props = $props() const {pubkey: target}: Props = $props()
const max = 100
const radius = 6 const radius = 6
const center = radius + 1 const center = radius + 1
const normalizedScore = $derived(clamp([0, max], score) / max) const score = deriveUserWotScore(target)
const active = $derived(getFollows($pubkey!).includes(target))
const normalizedScore = $derived(clamp([0, max], $score) / max)
const dashOffset = $derived(100 - 44 * normalizedScore) const dashOffset = $derived(100 - 44 * normalizedScore)
const style = $derived(`transform: rotate(${135 - normalizedScore * 180}deg)`) const style = $derived(`transform: rotate(${135 - normalizedScore * 180}deg)`)
const stroke = $derived(active ? "var(--primary)" : "var(--base-content)") const stroke = $derived(active ? "var(--primary)" : "var(--base-content)")

View File

@@ -1,14 +1,8 @@
<script lang="ts"> <script lang="ts">
import {removeNil} from "@welshman/lib" import {removeNil} from "@welshman/lib"
import {displayPubkey, getPubkeyTagValues, getListTags} from "@welshman/util" import {displayPubkey} from "@welshman/util"
import { import {deriveHandleForPubkey, displayHandle, deriveProfileDisplay} from "@welshman/app"
userFollows, import WotScore from "@app/components/WotScore.svelte"
deriveUserWotScore,
deriveHandleForPubkey,
displayHandle,
deriveProfileDisplay,
} from "@welshman/app"
import WotScore from "@lib/components/WotScore.svelte"
import ProfileCircle from "@app/components/ProfileCircle.svelte" import ProfileCircle from "@app/components/ProfileCircle.svelte"
type Props = { type Props = {
@@ -21,9 +15,6 @@
const pubkey = value const pubkey = value
const profileDisplay = deriveProfileDisplay(pubkey, removeNil([url])) const profileDisplay = deriveProfileDisplay(pubkey, removeNil([url]))
const handle = deriveHandleForPubkey(pubkey) const handle = deriveHandleForPubkey(pubkey)
const score = deriveUserWotScore(pubkey)
const following = $derived(getPubkeyTagValues(getListTags($userFollows)).includes(pubkey))
</script> </script>
<div class="flex max-w-full gap-3"> <div class="flex max-w-full gap-3">
@@ -35,7 +26,7 @@
<div class="text-bold overflow-hidden text-ellipsis text-base"> <div class="text-bold overflow-hidden text-ellipsis text-base">
{$profileDisplay} {$profileDisplay}
</div> </div>
<WotScore score={$score} active={following} /> <WotScore {pubkey} />
</div> </div>
<div class="overflow-hidden text-ellipsis text-sm opacity-75"> <div class="overflow-hidden text-ellipsis text-sm opacity-75">
{$handle ? displayHandle($handle) : displayPubkey(pubkey)} {$handle ? displayHandle($handle) : displayPubkey(pubkey)}

View File

@@ -405,7 +405,7 @@ export const memberships = deriveEventsMapped<PublishedList>(repository, {
}) })
export const { export const {
indexStore: membershipByPubkey, indexStore: membershipsByPubkey,
deriveItem: deriveMembership, deriveItem: deriveMembership,
loadItem: loadMembership, loadItem: loadMembership,
} = collection({ } = collection({
@@ -593,12 +593,12 @@ export const userSettingValues = withGetter(
export const getSetting = <T>(key: keyof Settings["values"]) => userSettingValues.get()[key] as T export const getSetting = <T>(key: keyof Settings["values"]) => userSettingValues.get()[key] as T
export const userMembership = withGetter( export const userMembership = withGetter(
derived([pubkey, membershipByPubkey], ([$pubkey, $membershipByPubkey]) => { derived([pubkey, membershipsByPubkey], ([$pubkey, $membershipsByPubkey]) => {
if (!$pubkey) return undefined if (!$pubkey) return undefined
loadMembership($pubkey) loadMembership($pubkey)
return $membershipByPubkey.get($pubkey) return $membershipsByPubkey.get($pubkey)
}), }),
) )

View File

@@ -14,7 +14,7 @@
{@render props.input?.()} {@render props.input?.()}
</div> </div>
</div> </div>
<div class="scroll-container content-sizing overflow-auto pt-2"> <div class="scroll-container content-sizing h-full overflow-auto pt-2">
{@render props.content?.()} {@render props.content?.()}
</div> </div>
</div> </div>

View File

@@ -18,7 +18,7 @@
import SpaceCheck from "@app/components/SpaceCheck.svelte" import SpaceCheck from "@app/components/SpaceCheck.svelte"
import ProfileCircles from "@app/components/ProfileCircles.svelte" import ProfileCircles from "@app/components/ProfileCircles.svelte"
import { import {
membershipByPubkey, membershipsByPubkey,
getMembershipUrls, getMembershipUrls,
loadMembership, loadMembership,
userRoomsByUrl, userRoomsByUrl,
@@ -46,7 +46,7 @@
const scores = new Map<string, Set<string>>() const scores = new Map<string, Set<string>>()
for (const pubkey of getDefaultPubkeys()) { for (const pubkey of getDefaultPubkeys()) {
for (const url of getMembershipUrls($membershipByPubkey.get(pubkey))) { for (const url of getMembershipUrls($membershipsByPubkey.get(pubkey))) {
addToMapKey(scores, url, pubkey) addToMapKey(scores, url, pubkey)
} }
} }

View File

@@ -43,7 +43,7 @@
</label> </label>
{/snippet} {/snippet}
{#snippet content()} {#snippet content()}
<div class="col-2" bind:this={element}> <div class="col-2 h-full" bind:this={element}>
{#each pubkeys.slice(0, limit) as pubkey (pubkey)} {#each pubkeys.slice(0, limit) as pubkey (pubkey)}
<PeopleItem {pubkey} /> <PeopleItem {pubkey} />
{/each} {/each}

BIN
static/coracle.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 89 KiB