mirror of
https://github.com/coracle-social/coracle.git
synced 2024-09-18 19:23:40 +00:00
Add PersonFeed
This commit is contained in:
parent
b2cd58cf8b
commit
51b7de9c29
@ -12,6 +12,7 @@
|
||||
import Onboarding from "src/app/views/Onboarding.svelte"
|
||||
import NoteCreate from "src/app/views/NoteCreate.svelte"
|
||||
import NoteDetail from "src/app/views/NoteDetail.svelte"
|
||||
import PersonFeed from "src/app/views/PersonFeed.svelte"
|
||||
import PersonList from "src/app/shared/PersonList.svelte"
|
||||
import PersonProfileInfo from "src/app/views/PersonProfileInfo.svelte"
|
||||
import PersonShare from "src/app/views/PersonShare.svelte"
|
||||
@ -47,6 +48,8 @@
|
||||
<LoginPubKey />
|
||||
{:else if m.type === "login/connect"}
|
||||
<LoginConnect />
|
||||
{:else if m.type === "person/feed"}
|
||||
<PersonFeed pubkey={m.pubkey} />
|
||||
{:else if m.type === "person/info"}
|
||||
<PersonProfileInfo person={m.person} />
|
||||
{:else if m.type === "person/share"}
|
||||
|
@ -19,6 +19,7 @@
|
||||
export let delta = timedelta(6, "hours")
|
||||
export let shouldDisplay = always(true)
|
||||
export let parentsTimeout = 500
|
||||
export let invertColors = false
|
||||
|
||||
let notes = []
|
||||
let notesBuffer = []
|
||||
@ -145,7 +146,7 @@
|
||||
|
||||
<div class="flex flex-col gap-4">
|
||||
{#each notes as note (note.id)}
|
||||
<Note depth={2} {note} {feedRelay} {setFeedRelay} />
|
||||
<Note depth={2} {note} {feedRelay} {setFeedRelay} {invertColors} />
|
||||
{/each}
|
||||
</div>
|
||||
|
||||
|
@ -2,6 +2,7 @@
|
||||
import {nip19} from "nostr-tools"
|
||||
import {find, last} from "ramda"
|
||||
import {onMount} from "svelte"
|
||||
import {navigate} from "svelte-routing"
|
||||
import {quantify} from "hurdak/lib/hurdak"
|
||||
import {findRootId, findReplyId, displayPerson} from "src/util/nostr"
|
||||
import {formatTimestamp} from "src/util/misc"
|
||||
@ -63,6 +64,14 @@
|
||||
}
|
||||
}
|
||||
|
||||
const goToAuthor = () => {
|
||||
if (document.querySelector(".modal-content")) {
|
||||
navigate(routes.person(note.pubkey))
|
||||
} else {
|
||||
modal.push({type: "person/feed", pubkey: note.pubkey})
|
||||
}
|
||||
}
|
||||
|
||||
const goToParent = async () => {
|
||||
const relays = getRelaysForEventParent(note)
|
||||
|
||||
@ -122,22 +131,34 @@
|
||||
</div>
|
||||
<div class="flex min-w-0 flex-grow flex-col gap-2">
|
||||
<div class="flex flex-col items-start justify-between sm:flex-row sm:items-center">
|
||||
<Popover triggerType={isMobile ? "click" : "mouseenter"}>
|
||||
<div slot="trigger">
|
||||
<Anchor
|
||||
type="unstyled"
|
||||
class="flex items-center gap-2 pr-16 text-lg font-bold sm:pr-0"
|
||||
href={isMobile ? null : routes.person($author.pubkey)}>
|
||||
<span>{displayPerson($author)}</span>
|
||||
{#if $author.verified_as}
|
||||
<i class="fa fa-circle-check text-sm text-accent" />
|
||||
{/if}
|
||||
</Anchor>
|
||||
</div>
|
||||
<div slot="tooltip">
|
||||
<PersonSummary pubkey={$author.pubkey} />
|
||||
</div>
|
||||
</Popover>
|
||||
{#if isMobile}
|
||||
<Anchor
|
||||
type="unstyled"
|
||||
class="flex items-center gap-2 pr-16 text-lg font-bold"
|
||||
on:click={goToAuthor}>
|
||||
<span>{displayPerson($author)}</span>
|
||||
{#if $author.verified_as}
|
||||
<i class="fa fa-circle-check text-sm text-accent" />
|
||||
{/if}
|
||||
</Anchor>
|
||||
{:else}
|
||||
<Popover triggerType="mouseenter">
|
||||
<div slot="trigger">
|
||||
<Anchor
|
||||
type="unstyled"
|
||||
class="flex items-center gap-2 pr-16 text-lg font-bold"
|
||||
on:click={goToAuthor}>
|
||||
<span>{displayPerson($author)}</span>
|
||||
{#if $author.verified_as}
|
||||
<i class="fa fa-circle-check text-sm text-accent" />
|
||||
{/if}
|
||||
</Anchor>
|
||||
</div>
|
||||
<div slot="tooltip">
|
||||
<PersonSummary pubkey={$author.pubkey} />
|
||||
</div>
|
||||
</Popover>
|
||||
{/if}
|
||||
<Anchor
|
||||
href={"/" + nip19.neventEncode({id: note.id, relays: note.seen_on})}
|
||||
class="text-sm text-gray-1"
|
||||
|
95
src/app/shared/PersonActions.svelte
Normal file
95
src/app/shared/PersonActions.svelte
Normal file
@ -0,0 +1,95 @@
|
||||
<script lang="ts">
|
||||
import {find} from "ramda"
|
||||
import {nip19} from "nostr-tools"
|
||||
import {navigate} from "svelte-routing"
|
||||
import {displayPerson} from "src/util/nostr"
|
||||
import {modal} from "src/partials/state"
|
||||
import Popover from "src/partials/Popover.svelte"
|
||||
import OverflowMenu from "src/partials/OverflowMenu.svelte"
|
||||
import {getPubkeyWriteRelays} from "src/agent/relays"
|
||||
import user from "src/agent/user"
|
||||
import pool from "src/agent/pool"
|
||||
|
||||
export let person
|
||||
|
||||
const npub = nip19.npubEncode(person.pubkey)
|
||||
const {petnamePubkeys, canPublish, mutes} = user
|
||||
|
||||
let actions = []
|
||||
|
||||
$: muted = find(m => m[1] === person.pubkey, $mutes)
|
||||
$: following = $petnamePubkeys.includes(person.pubkey)
|
||||
$: {
|
||||
actions = []
|
||||
|
||||
actions.push({onClick: share, label: "Share", icon: "share-nodes"})
|
||||
|
||||
if (user.getPubkey() !== person.pubkey && $canPublish) {
|
||||
actions.push({
|
||||
onClick: () => navigate(`/messages/${npub}`),
|
||||
label: "Message",
|
||||
icon: "envelope",
|
||||
})
|
||||
|
||||
if (muted) {
|
||||
actions.push({onClick: unmute, label: "Unmute", icon: "microphone"})
|
||||
} else if (user.getPubkey() !== person.pubkey) {
|
||||
actions.push({onClick: mute, label: "Mute", icon: "microphone-slash"})
|
||||
}
|
||||
}
|
||||
|
||||
if (pool.forceUrls.length === 0) {
|
||||
actions.push({onClick: openProfileInfo, label: "Details", icon: "info"})
|
||||
}
|
||||
|
||||
if (user.getPubkey() === person.pubkey && $canPublish) {
|
||||
actions.push({
|
||||
onClick: () => navigate("/profile"),
|
||||
label: "Edit",
|
||||
icon: "edit",
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
const follow = async () => {
|
||||
const [{url}] = getPubkeyWriteRelays(person.pubkey)
|
||||
|
||||
user.addPetname(person.pubkey, url, displayPerson(person))
|
||||
}
|
||||
|
||||
const unfollow = async () => {
|
||||
user.removePetname(person.pubkey)
|
||||
}
|
||||
|
||||
const mute = async () => {
|
||||
user.addMute("p", person.pubkey)
|
||||
}
|
||||
|
||||
const unmute = async () => {
|
||||
user.removeMute(person.pubkey)
|
||||
}
|
||||
|
||||
const openProfileInfo = () => {
|
||||
modal.push({type: "person/info", person})
|
||||
}
|
||||
|
||||
const share = () => {
|
||||
modal.push({type: "person/share", person})
|
||||
}
|
||||
</script>
|
||||
|
||||
<div class="flex items-center gap-3">
|
||||
{#if $canPublish}
|
||||
<Popover triggerType="mouseenter">
|
||||
<div slot="trigger">
|
||||
{#if following}
|
||||
<i class="fa fa-user-minus cursor-pointer" on:click={unfollow} />
|
||||
{:else if user.getPubkey() !== person.pubkey}
|
||||
<i class="fa fa-user-plus cursor-pointer" on:click={follow} />
|
||||
{/if}
|
||||
</div>
|
||||
<div slot="tooltip">{following ? "Unfollow" : "Follow"}</div>
|
||||
</Popover>
|
||||
{/if}
|
||||
<OverflowMenu {actions} />
|
||||
</div>
|
@ -4,8 +4,9 @@
|
||||
|
||||
export let pubkey
|
||||
export let relays
|
||||
export let invertColors = false
|
||||
|
||||
const filter = {kinds: [1], authors: [pubkey]}
|
||||
</script>
|
||||
|
||||
<Feed {relays} {filter} parentsTimeout={3000} delta={timedelta(1, "days")} />
|
||||
<Feed {relays} {filter} {invertColors} parentsTimeout={3000} delta={timedelta(1, "days")} />
|
59
src/app/shared/PersonStats.svelte
Normal file
59
src/app/shared/PersonStats.svelte
Normal file
@ -0,0 +1,59 @@
|
||||
<script lang="ts">
|
||||
import {onMount} from "svelte"
|
||||
import {fly} from "svelte/transition"
|
||||
import {tweened} from "svelte/motion"
|
||||
import {numberFmt} from "src/util/misc"
|
||||
import {modal} from "src/partials/state"
|
||||
import {sampleRelays, getPubkeyWriteRelays} from "src/agent/relays"
|
||||
import network from "src/agent/network"
|
||||
import pool from "src/agent/pool"
|
||||
|
||||
export let person
|
||||
|
||||
const interpolate = (a, b) => t => a + Math.round((b - a) * t)
|
||||
|
||||
let followersCount = tweened(0, {interpolate, duration: 1000})
|
||||
|
||||
const showFollows = () => {
|
||||
modal.push({type: "person/follows", pubkey: person.pubkey})
|
||||
}
|
||||
|
||||
const showFollowers = () => {
|
||||
modal.push({type: "person/followers", pubkey: person.pubkey})
|
||||
}
|
||||
|
||||
onMount(async () => {
|
||||
// Get our followers count
|
||||
const count = await pool.count({kinds: [3], "#p": [person.pubkey]})
|
||||
|
||||
if (count) {
|
||||
followersCount.set(count)
|
||||
} else {
|
||||
const followers = new Set()
|
||||
|
||||
await network.load({
|
||||
relays: sampleRelays(getPubkeyWriteRelays(person.pubkey)),
|
||||
shouldProcess: false,
|
||||
filter: [{kinds: [3], "#p": [person.pubkey]}],
|
||||
onChunk: events => {
|
||||
for (const e of events) {
|
||||
followers.add(e.pubkey)
|
||||
}
|
||||
|
||||
followersCount.set(followers.size)
|
||||
},
|
||||
})
|
||||
}
|
||||
})
|
||||
</script>
|
||||
|
||||
{#if person?.petnames}
|
||||
<div class="flex gap-8" in:fly={{y: 20}}>
|
||||
<button on:click={showFollows}>
|
||||
<strong>{person.petnames.length}</strong> following
|
||||
</button>
|
||||
<button on:click={showFollowers}>
|
||||
<strong>{numberFmt.format($followersCount)}</strong> followers
|
||||
</button>
|
||||
</div>
|
||||
{/if}
|
@ -1,13 +1,10 @@
|
||||
<script lang="ts">
|
||||
import {last, nth} from "ramda"
|
||||
import {navigate} from "svelte-routing"
|
||||
import {displayPerson} from "src/util/nostr"
|
||||
import Anchor from "src/partials/Anchor.svelte"
|
||||
import user from "src/agent/user"
|
||||
import {sampleRelays, getPubkeyWriteRelays} from "src/agent/relays"
|
||||
import {getPersonWithFallback} from "src/agent/db"
|
||||
import {watch} from "src/agent/db"
|
||||
import {routes} from "src/app/state"
|
||||
import PersonCircle from "src/app/shared/PersonCircle.svelte"
|
||||
import PersonAbout from "src/app/shared/PersonAbout.svelte"
|
||||
|
||||
@ -37,12 +34,7 @@
|
||||
<div class="flex gap-4">
|
||||
<PersonCircle size={14} person={$person} />
|
||||
<div class="flex flex-grow flex-col gap-2">
|
||||
<Anchor
|
||||
type="unstyled"
|
||||
class="flex items-center gap-2"
|
||||
on:click={() => navigate(routes.person(pubkey))}>
|
||||
<h2 class="text-lg">{displayPerson($person)}</h2>
|
||||
</Anchor>
|
||||
<h2 class="text-lg">{displayPerson($person)}</h2>
|
||||
{#if $person.verified_as}
|
||||
<div class="flex gap-1 text-sm">
|
||||
<i class="fa fa-user-check text-accent" />
|
||||
|
@ -1,7 +1,5 @@
|
||||
<script lang="ts">
|
||||
import {find, last, propEq} from "ramda"
|
||||
import Anchor from "src/partials/Anchor.svelte"
|
||||
import Popover from "src/partials/Popover.svelte"
|
||||
import OverflowMenu from "src/partials/OverflowMenu.svelte"
|
||||
import user from "src/agent/user"
|
||||
import {getRelayWithFallback} from "src/agent/db"
|
||||
@ -42,15 +40,4 @@
|
||||
}
|
||||
</script>
|
||||
|
||||
{#if actions.length > 0}
|
||||
{#if actions.length === 1}
|
||||
<Popover triggerType="mouseenter">
|
||||
<Anchor slot="trigger" type="button-circle" on:click={actions[0].onClick}>
|
||||
<i class={`fa fa-${actions[0].icon}`} />
|
||||
</Anchor>
|
||||
<p slot="tooltip">{actions[0].label}</p>
|
||||
</Popover>
|
||||
{:else}
|
||||
<OverflowMenu {actions} />
|
||||
{/if}
|
||||
{/if}
|
||||
<OverflowMenu {actions} />
|
||||
|
@ -1,5 +1,4 @@
|
||||
<script lang="ts">
|
||||
import {displayRelay} from "src/util/nostr"
|
||||
import Content from "src/partials/Content.svelte"
|
||||
import Spinner from "src/partials/Spinner.svelte"
|
||||
import RelayTitle from "src/app/shared/RelayTitle.svelte"
|
||||
@ -22,9 +21,8 @@
|
||||
{#if feedRelay.description}
|
||||
<p>{feedRelay.description}</p>
|
||||
{/if}
|
||||
<p class="text-gray-4">
|
||||
<i class="fa fa-info-circle" />
|
||||
Below is your current feed including only notes seen on {displayRelay(feedRelay)}
|
||||
<p class="border-l-2 border-gray-6 pl-4 text-gray-4">
|
||||
Below is your current feed including only notes seen on this relay.
|
||||
</p>
|
||||
|
||||
<div class="flex flex-col gap-4">
|
||||
|
@ -1,45 +1,34 @@
|
||||
<script lang="ts">
|
||||
import {last, identity, find} from "ramda"
|
||||
import {onMount} from "svelte"
|
||||
import {tweened} from "svelte/motion"
|
||||
import {fly} from "svelte/transition"
|
||||
import {last, identity} from "ramda"
|
||||
import {navigate} from "svelte-routing"
|
||||
import {log} from "src/util/logger"
|
||||
import {parseHex} from "src/util/html"
|
||||
import {numberFmt} from "src/util/misc"
|
||||
import {displayPerson, toHex} from "src/util/nostr"
|
||||
import {modal, theme, getThemeColor} from "src/partials/state"
|
||||
import {theme, getThemeColor} from "src/partials/state"
|
||||
import Tabs from "src/partials/Tabs.svelte"
|
||||
import Content from "src/partials/Content.svelte"
|
||||
import OverflowMenu from "src/partials/OverflowMenu.svelte"
|
||||
import Spinner from "src/partials/Spinner.svelte"
|
||||
import Notes from "src/app/views/PersonNotes.svelte"
|
||||
import Likes from "src/app/views/PersonLikes.svelte"
|
||||
import Relays from "src/app/views/PersonRelays.svelte"
|
||||
import user from "src/agent/user"
|
||||
import PersonActions from "src/app/shared/PersonActions.svelte"
|
||||
import PersonNotes from "src/app/shared/PersonNotes.svelte"
|
||||
import PersonLikes from "src/app/shared/PersonLikes.svelte"
|
||||
import PersonRelays from "src/app/shared/PersonRelays.svelte"
|
||||
import pool from "src/agent/pool"
|
||||
import {sampleRelays, getPubkeyWriteRelays} from "src/agent/relays"
|
||||
import network from "src/agent/network"
|
||||
import {getPersonWithFallback, watch} from "src/agent/db"
|
||||
import {routes} from "src/app/state"
|
||||
import PersonCircle from "src/app/shared/PersonCircle.svelte"
|
||||
import PersonAbout from "src/app/shared/PersonAbout.svelte"
|
||||
import PersonStats from "src/app/shared/PersonStats.svelte"
|
||||
|
||||
export let npub
|
||||
export let activeTab
|
||||
export let relays = []
|
||||
|
||||
const interpolate = (a, b) => t => a + Math.round((b - a) * t)
|
||||
const {petnamePubkeys, canPublish, mutes} = user
|
||||
const tabs = ["notes", "likes", pool.forceUrls.length === 0 && "relays"].filter(identity)
|
||||
const pubkey = toHex(npub)
|
||||
const person = watch("people", () => getPersonWithFallback(pubkey))
|
||||
|
||||
let following = false
|
||||
let muted = false
|
||||
let followersCount = tweened(0, {interpolate, duration: 1000})
|
||||
let loading = true
|
||||
let actions = []
|
||||
let rgb, rgba
|
||||
|
||||
$: ownRelays = getPubkeyWriteRelays(pubkey)
|
||||
@ -52,118 +41,11 @@
|
||||
rgb = `rgba(${color.join(", ")})`
|
||||
}
|
||||
|
||||
$: following = $petnamePubkeys.includes(pubkey)
|
||||
$: muted = find(m => m[1] === pubkey, $mutes)
|
||||
log("Person", npub, $person)
|
||||
|
||||
$: {
|
||||
actions = []
|
||||
|
||||
if ($canPublish) {
|
||||
if (following) {
|
||||
actions.push({onClick: unfollow, label: "Unfollow", icon: "user-minus"})
|
||||
} else if (user.getPubkey() !== pubkey) {
|
||||
actions.push({onClick: follow, label: "Follow", icon: "user-plus"})
|
||||
}
|
||||
}
|
||||
|
||||
actions.push({onClick: share, label: "Share", icon: "share-nodes"})
|
||||
|
||||
if (user.getPubkey() !== pubkey && $canPublish) {
|
||||
actions.push({
|
||||
onClick: () => navigate(`/messages/${npub}`),
|
||||
label: "Message",
|
||||
icon: "envelope",
|
||||
})
|
||||
|
||||
if (muted) {
|
||||
actions.push({onClick: unmute, label: "Unmute", icon: "microphone"})
|
||||
} else if (user.getPubkey() !== pubkey) {
|
||||
actions.push({onClick: mute, label: "Mute", icon: "microphone-slash"})
|
||||
}
|
||||
}
|
||||
|
||||
if (pool.forceUrls.length === 0) {
|
||||
actions.push({onClick: openProfileInfo, label: "Details", icon: "info"})
|
||||
}
|
||||
|
||||
if (user.getPubkey() === pubkey && $canPublish) {
|
||||
actions.push({
|
||||
onClick: () => navigate("/profile"),
|
||||
label: "Edit",
|
||||
icon: "edit",
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
onMount(async () => {
|
||||
log("Person", npub, $person)
|
||||
|
||||
document.title = displayPerson($person)
|
||||
|
||||
// Refresh our person
|
||||
network.loadPeople([pubkey], {relays, force: true}).then(() => {
|
||||
ownRelays = getPubkeyWriteRelays(pubkey)
|
||||
loading = false
|
||||
})
|
||||
|
||||
// Get our followers count
|
||||
const count = await pool.count({kinds: [3], "#p": [pubkey]})
|
||||
|
||||
if (count) {
|
||||
followersCount.set(count)
|
||||
} else {
|
||||
const followers = new Set()
|
||||
|
||||
await network.load({
|
||||
relays,
|
||||
shouldProcess: false,
|
||||
filter: [{kinds: [3], "#p": [pubkey]}],
|
||||
onChunk: events => {
|
||||
for (const e of events) {
|
||||
followers.add(e.pubkey)
|
||||
}
|
||||
|
||||
followersCount.set(followers.size)
|
||||
},
|
||||
})
|
||||
}
|
||||
})
|
||||
document.title = displayPerson($person)
|
||||
|
||||
const setActiveTab = tab => navigate(routes.person(pubkey, tab))
|
||||
|
||||
const showFollows = () => {
|
||||
modal.push({type: "person/follows", pubkey})
|
||||
}
|
||||
|
||||
const showFollowers = () => {
|
||||
modal.push({type: "person/followers", pubkey})
|
||||
}
|
||||
|
||||
const follow = async () => {
|
||||
const [{url}] = relays
|
||||
|
||||
user.addPetname(pubkey, url, displayPerson($person))
|
||||
}
|
||||
|
||||
const unfollow = async () => {
|
||||
user.removePetname(pubkey)
|
||||
}
|
||||
|
||||
const mute = async () => {
|
||||
user.addMute("p", pubkey)
|
||||
}
|
||||
|
||||
const unmute = async () => {
|
||||
user.removeMute(pubkey)
|
||||
}
|
||||
|
||||
const openProfileInfo = () => {
|
||||
modal.push({type: "person/info", person: $person})
|
||||
}
|
||||
|
||||
const share = () => {
|
||||
modal.push({type: "person/share", person: $person})
|
||||
}
|
||||
</script>
|
||||
|
||||
<div
|
||||
@ -190,31 +72,22 @@
|
||||
</div>
|
||||
{/if}
|
||||
</div>
|
||||
<OverflowMenu {actions} />
|
||||
<PersonActions person={$person} />
|
||||
</div>
|
||||
<PersonAbout person={$person} />
|
||||
{#if $person?.petnames}
|
||||
<div class="flex gap-8" in:fly={{y: 20}}>
|
||||
<button on:click={showFollows}>
|
||||
<strong>{$person.petnames.length}</strong> following
|
||||
</button>
|
||||
<button on:click={showFollowers}>
|
||||
<strong>{numberFmt.format($followersCount)}</strong> followers
|
||||
</button>
|
||||
</div>
|
||||
{/if}
|
||||
<PersonStats person={$person} />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<Tabs {tabs} {activeTab} {setActiveTab} />
|
||||
|
||||
{#if activeTab === "notes"}
|
||||
<Notes {pubkey} {relays} />
|
||||
<PersonNotes {pubkey} {relays} />
|
||||
{:else if activeTab === "likes"}
|
||||
<Likes {pubkey} {relays} />
|
||||
<PersonLikes {pubkey} {relays} />
|
||||
{:else if activeTab === "relays"}
|
||||
{#if ownRelays.length > 0}
|
||||
<Relays relays={ownRelays} />
|
||||
<PersonRelays relays={ownRelays} />
|
||||
{:else if loading}
|
||||
<Spinner />
|
||||
{:else}
|
||||
|
51
src/app/views/PersonFeed.svelte
Normal file
51
src/app/views/PersonFeed.svelte
Normal file
@ -0,0 +1,51 @@
|
||||
<script lang="ts">
|
||||
import {last} from "ramda"
|
||||
import {displayPerson} from "src/util/nostr"
|
||||
import Content from "src/partials/Content.svelte"
|
||||
import Anchor from "src/partials/Anchor.svelte"
|
||||
import PersonActions from "src/app/shared/PersonActions.svelte"
|
||||
import {sampleRelays, getPubkeyWriteRelays} from "src/agent/relays"
|
||||
import {getPersonWithFallback, watch} from "src/agent/db"
|
||||
import PersonCircle from "src/app/shared/PersonCircle.svelte"
|
||||
import PersonAbout from "src/app/shared/PersonAbout.svelte"
|
||||
import PersonNotes from "src/app/shared/PersonNotes.svelte"
|
||||
import PersonStats from "src/app/shared/PersonStats.svelte"
|
||||
import {routes} from "src/app/state"
|
||||
|
||||
export let pubkey
|
||||
|
||||
const person = watch("people", () => getPersonWithFallback(pubkey))
|
||||
|
||||
$: relays = sampleRelays(getPubkeyWriteRelays(pubkey))
|
||||
|
||||
document.title = displayPerson($person)
|
||||
</script>
|
||||
|
||||
<Content>
|
||||
<div class="flex gap-4 text-gray-1">
|
||||
<PersonCircle person={$person} size={16} class="sm:h-32 sm:w-32" />
|
||||
<div class="flex flex-grow flex-col gap-4">
|
||||
<div class="flex items-start justify-between gap-4">
|
||||
<div class="flex flex-grow flex-col gap-2">
|
||||
<div class="flex items-center gap-2">
|
||||
<Anchor type="unstyled" href={routes.person(pubkey)}>
|
||||
<h1 class="text-2xl">
|
||||
{displayPerson($person)}
|
||||
</h1>
|
||||
</Anchor>
|
||||
</div>
|
||||
{#if $person.verified_as}
|
||||
<div class="flex gap-1 text-sm">
|
||||
<i class="fa fa-user-check text-accent" />
|
||||
<span class="text-gray-1">{last($person.verified_as.split("@"))}</span>
|
||||
</div>
|
||||
{/if}
|
||||
</div>
|
||||
<PersonActions person={$person} />
|
||||
</div>
|
||||
<PersonAbout person={$person} />
|
||||
<PersonStats person={$person} />
|
||||
</div>
|
||||
</div>
|
||||
<PersonNotes invertColors {pubkey} {relays} />
|
||||
</Content>
|
@ -32,15 +32,14 @@
|
||||
class:cursor-pointer={onEscape}
|
||||
on:click={onEscape}>
|
||||
<div class="mt-12 min-h-full">
|
||||
{#if onEscape}
|
||||
<div class="pointer-events-none sticky top-0 z-10 flex w-full justify-end p-2">
|
||||
<div
|
||||
class="pointer-events-auto flex h-10 w-10 cursor-pointer items-center justify-center
|
||||
rounded-full border border-solid border-accent-light bg-accent text-white">
|
||||
<i class="fa fa-times fa-lg" />
|
||||
</div>
|
||||
<div class="pointer-events-none sticky top-0 z-10 flex w-full justify-end p-2">
|
||||
<div
|
||||
class:opacity-0={!onEscape}
|
||||
class="pointer-events-auto flex h-10 w-10 cursor-pointer items-center justify-center
|
||||
rounded-full border border-solid border-accent-light bg-accent text-white">
|
||||
<i class="fa fa-times fa-lg" />
|
||||
</div>
|
||||
{/if}
|
||||
</div>
|
||||
<div class="absolute mt-12 h-full w-full bg-gray-7" />
|
||||
<div
|
||||
class="relative h-full w-full cursor-auto border-t border-solid border-gray-6 bg-gray-7 pt-2 pb-10"
|
||||
|
@ -6,24 +6,36 @@
|
||||
export let size = ""
|
||||
</script>
|
||||
|
||||
<Popover theme="transparent">
|
||||
<div slot="trigger" class="cursor-pointer px-2">
|
||||
<i class={`fa fa-${size} fa-ellipsis-v`} />
|
||||
</div>
|
||||
<div
|
||||
slot="tooltip"
|
||||
let:instance
|
||||
class="relative flex flex-col gap-2"
|
||||
on:click={() => instance.hide()}>
|
||||
<div
|
||||
class="absolute top-0 right-0 bottom-0 w-32 rounded-full bg-gray-8"
|
||||
style="filter: blur(15px) opacity(0.75)" />
|
||||
|
||||
{#each actions as { label, icon, onClick }}
|
||||
<div class="relative z-10 cursor-pointer" on:click={onClick}>
|
||||
<span class="absolute right-0 mr-12 mt-2">{label}</span>
|
||||
<Anchor type="button-circle"><i class={`fa fa-${icon}`} /></Anchor>
|
||||
{#if actions.length > 0}
|
||||
{#if actions.length === 1}
|
||||
<Popover triggerType="mouseenter">
|
||||
<i
|
||||
slot="trigger"
|
||||
class={`fa fa-${actions[0].icon} cursor-pointer`}
|
||||
on:click={actions[0].onClick} />
|
||||
<p slot="tooltip">{actions[0].label}</p>
|
||||
</Popover>
|
||||
{:else}
|
||||
<Popover theme="transparent">
|
||||
<div slot="trigger" class="cursor-pointer px-2">
|
||||
<i class={`fa fa-${size} fa-ellipsis-v`} />
|
||||
</div>
|
||||
{/each}
|
||||
</div>
|
||||
</Popover>
|
||||
<div
|
||||
slot="tooltip"
|
||||
let:instance
|
||||
class="relative flex flex-col gap-2"
|
||||
on:click={() => instance.hide()}>
|
||||
<div
|
||||
class="absolute top-0 right-0 bottom-0 w-32 rounded-full bg-gray-8"
|
||||
style="filter: blur(15px) opacity(0.75)" />
|
||||
|
||||
{#each actions as { label, icon, onClick }}
|
||||
<div class="relative z-10 cursor-pointer" on:click={onClick}>
|
||||
<span class="absolute right-0 mr-12 mt-2">{label}</span>
|
||||
<Anchor type="button-circle"><i class={`fa fa-${icon}`} /></Anchor>
|
||||
</div>
|
||||
{/each}
|
||||
</div>
|
||||
</Popover>
|
||||
{/if}
|
||||
{/if}
|
||||
|
Loading…
Reference in New Issue
Block a user