mirror of
https://github.com/coracle-social/coracle.git
synced 2024-10-06 11:43:30 +00:00
Add person popover
This commit is contained in:
parent
0a293ca354
commit
c500c7354f
@ -5,6 +5,9 @@
|
|||||||
- [x] Improve paste support
|
- [x] Improve paste support
|
||||||
- [x] Add timestamps to messages
|
- [x] Add timestamps to messages
|
||||||
- [x] Support installation as a PWA
|
- [x] Support installation as a PWA
|
||||||
|
- [x] Fix social share image, add description
|
||||||
|
- [x] Clean up person detail actions, maybe click one circle and show the rest
|
||||||
|
- [x] Add person popover to notes/alerts
|
||||||
|
|
||||||
## 0.2.13
|
## 0.2.13
|
||||||
|
|
||||||
|
10
ROADMAP.md
10
ROADMAP.md
@ -1,6 +1,8 @@
|
|||||||
# Current
|
# Current
|
||||||
|
|
||||||
- [ ] Fix iOS
|
- [ ] Fix iOS
|
||||||
|
- [ ] Hover badge to view profile like twitter
|
||||||
|
- [ ] Cache follower numbers to avoid re-fetching so much
|
||||||
- [ ] Make the note relays button modal make sense, one relay with no explanation is not good
|
- [ ] Make the note relays button modal make sense, one relay with no explanation is not good
|
||||||
|
|
||||||
# Lightning
|
# Lightning
|
||||||
@ -18,22 +20,17 @@
|
|||||||
|
|
||||||
# More
|
# More
|
||||||
|
|
||||||
|
- [ ] Allow the user to disable likes/zaps
|
||||||
- [ ] Polls
|
- [ ] Polls
|
||||||
- Find the best implementation https://github.com/nostr-protocol/nips/search?q=poll&type=issues
|
- Find the best implementation https://github.com/nostr-protocol/nips/search?q=poll&type=issues
|
||||||
- Comment on all three nip drafts which one I implemented
|
- Comment on all three nip drafts which one I implemented
|
||||||
- [ ] Micro app DSL
|
- [ ] Micro app DSL
|
||||||
- [ ] Fix social share image, add description
|
|
||||||
- [ ] Sort feeds by created date on profile page?
|
- [ ] Sort feeds by created date on profile page?
|
||||||
- [ ] Implement https://media.nostr.band/
|
- [ ] Implement https://media.nostr.band/
|
||||||
- [ ] Cache follower numbers to avoid re-fetching so much
|
|
||||||
- [ ] Groups - may need a new NIP, or maybe use topics
|
- [ ] Groups - may need a new NIP, or maybe use topics
|
||||||
- [ ] Support https://github.com/nostr-protocol/nips/pull/211 as a bech32 entity
|
- [ ] Support https://github.com/nostr-protocol/nips/pull/211 as a bech32 entity
|
||||||
- [ ] Add new DM button to dms list
|
- [ ] Add new DM button to dms list
|
||||||
- [ ] Add suggested relays based on follows or topics
|
- [ ] Add suggested relays based on follows or topics
|
||||||
- [ ] Combine alerts/messages and any other top-level subscriptions to avoid sub limit
|
|
||||||
- [ ] Clean up person detail actions, maybe click one circle and show the rest
|
|
||||||
- [ ] Hover badge to view profile like twitter
|
|
||||||
- [ ] Show created date as bitcoin block height (add a setting?)
|
|
||||||
- [ ] Support relay auth
|
- [ ] Support relay auth
|
||||||
- [ ] Following indicator on person info
|
- [ ] Following indicator on person info
|
||||||
- [ ] Share button for notes, shows qr code and nevent
|
- [ ] Share button for notes, shows qr code and nevent
|
||||||
@ -75,6 +72,7 @@
|
|||||||
- [ ] Release to android
|
- [ ] Release to android
|
||||||
- https://svelte-native.technology/docs
|
- https://svelte-native.technology/docs
|
||||||
- https://ionic.io/blog/capacitor-everything-youve-ever-wanted-to-know
|
- https://ionic.io/blog/capacitor-everything-youve-ever-wanted-to-know
|
||||||
|
- Or just wrap it
|
||||||
- [ ] When publishing fails, enqueue and retry
|
- [ ] When publishing fails, enqueue and retry
|
||||||
- Track which relays the events should be published to, and which ones have succeeded
|
- Track which relays the events should be published to, and which ones have succeeded
|
||||||
- Maybe notify and ask user which events to re-publish.
|
- Maybe notify and ask user which events to re-publish.
|
||||||
|
BIN
package-lock.json
generated
BIN
package-lock.json
generated
Binary file not shown.
@ -51,6 +51,7 @@
|
|||||||
"svelte-routing": "^1.6.0",
|
"svelte-routing": "^1.6.0",
|
||||||
"svelte-switch": "^0.0.5",
|
"svelte-switch": "^0.0.5",
|
||||||
"throttle-debounce": "^5.0.0",
|
"throttle-debounce": "^5.0.0",
|
||||||
|
"tippy.js": "^6.3.7",
|
||||||
"vite-plugin-node-polyfills": "^0.5.0",
|
"vite-plugin-node-polyfills": "^0.5.0",
|
||||||
"vite-plugin-pwa": "^0.14.4"
|
"vite-plugin-pwa": "^0.14.4"
|
||||||
}
|
}
|
||||||
|
19
src/app.css
19
src/app.css
@ -57,3 +57,22 @@
|
|||||||
html, body, #app, #app > div {
|
html, body, #app, #app > div {
|
||||||
height: 100%;
|
height: 100%;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Tippy */
|
||||||
|
|
||||||
|
.tippy-box {
|
||||||
|
background-color: #0f0f0e !important;
|
||||||
|
border: 1px solid #403D39;
|
||||||
|
box-shadow: 3px 3px 20px #0f0f0e,
|
||||||
|
3px -3px 20px #0f0f0e,
|
||||||
|
-3px 3px 20px #0f0f0e,
|
||||||
|
-3px -3px 20px #0f0f0e;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tippy-box[data-placement^=top]>.tippy-arrow:before {
|
||||||
|
border-top-color: #403D39 !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tippy-box[data-placement^=top]>.tippy-arrow {
|
||||||
|
bottom: -1px !important;
|
||||||
|
}
|
||||||
|
62
src/partials/Popover.svelte
Normal file
62
src/partials/Popover.svelte
Normal file
@ -0,0 +1,62 @@
|
|||||||
|
<script lang="ts">
|
||||||
|
import 'tippy.js/dist/tippy.css'
|
||||||
|
import 'tippy.js/animations/shift-away.css'
|
||||||
|
import tippy from 'tippy.js'
|
||||||
|
import {onMount} from 'svelte'
|
||||||
|
|
||||||
|
let trigger
|
||||||
|
let tooltip
|
||||||
|
let instance
|
||||||
|
|
||||||
|
onMount(() => {
|
||||||
|
instance = tippy(trigger, {
|
||||||
|
appendTo: () => document.body,
|
||||||
|
allowHTML: true,
|
||||||
|
interactive: true,
|
||||||
|
trigger: 'click',
|
||||||
|
animation: 'shift-away',
|
||||||
|
onShow: () => {
|
||||||
|
const [tooltipContents] = tooltip.children
|
||||||
|
|
||||||
|
instance.popper.querySelector('.tippy-content').appendChild(tooltipContents)
|
||||||
|
instance.popper.addEventListener('mouseleave', e => instance.hide())
|
||||||
|
instance.popper.addEventListener('click', e => {
|
||||||
|
if (e.target.closest('.tippy-close')) {
|
||||||
|
instance.hide()
|
||||||
|
}
|
||||||
|
})
|
||||||
|
},
|
||||||
|
onHidden: () => {
|
||||||
|
const [tooltipContents] = instance.popper.querySelector('.tippy-content').children
|
||||||
|
|
||||||
|
tooltip.appendChild(tooltipContents)
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
|
return () => {
|
||||||
|
instance.destroy()
|
||||||
|
}
|
||||||
|
})
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<svelte:window
|
||||||
|
on:scroll={e => {
|
||||||
|
instance.hide()
|
||||||
|
}} />
|
||||||
|
|
||||||
|
<svelte:body
|
||||||
|
on:keydown={e => {
|
||||||
|
if (e.key === 'Escape') {
|
||||||
|
instance.hide()
|
||||||
|
}
|
||||||
|
}} />
|
||||||
|
|
||||||
|
<div bind:this={trigger}>
|
||||||
|
<slot name="trigger" />
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div bind:this={tooltip} class="hidden">
|
||||||
|
<div>
|
||||||
|
<slot name="tooltip" />
|
||||||
|
</div>
|
||||||
|
</div>
|
@ -23,7 +23,7 @@
|
|||||||
</script>
|
</script>
|
||||||
|
|
||||||
<ul
|
<ul
|
||||||
class="mt-16 pt-4 pb-20 lg:mt-4 w-56 bg-dark fixed top-0 bottom-0 left-0 transition-all shadow-xl
|
class="mt-16 pt-4 pb-20 lg:mt-0 w-56 bg-dark fixed top-0 bottom-0 left-0 transition-all shadow-xl
|
||||||
border-r border-medium text-white overflow-hidden z-20 lg:ml-0"
|
border-r border-medium text-white overflow-hidden z-20 lg:ml-0"
|
||||||
class:-ml-56={!$menuIsOpen}
|
class:-ml-56={!$menuIsOpen}
|
||||||
>
|
>
|
||||||
|
@ -2,18 +2,14 @@
|
|||||||
import {sortBy, assoc} from 'ramda'
|
import {sortBy, assoc} from 'ramda'
|
||||||
import {onMount} from 'svelte'
|
import {onMount} from 'svelte'
|
||||||
import {fly} from 'svelte/transition'
|
import {fly} from 'svelte/transition'
|
||||||
import {ellipsize} from 'hurdak/lib/hurdak'
|
import {now, createScroller} from 'src/util/misc'
|
||||||
import {displayPerson} from 'src/util/nostr'
|
|
||||||
import {now, formatTimestamp, createScroller} from 'src/util/misc'
|
|
||||||
import Spinner from 'src/partials/Spinner.svelte'
|
import Spinner from 'src/partials/Spinner.svelte'
|
||||||
import Content from 'src/partials/Content.svelte'
|
import Content from 'src/partials/Content.svelte'
|
||||||
import Anchor from 'src/partials/Anchor.svelte'
|
|
||||||
import ImageCircle from "src/partials/ImageCircle.svelte"
|
|
||||||
import Alert from 'src/views/alerts/Alert.svelte'
|
import Alert from 'src/views/alerts/Alert.svelte'
|
||||||
|
import Mention from 'src/views/alerts/Mention.svelte'
|
||||||
import database from 'src/agent/database'
|
import database from 'src/agent/database'
|
||||||
import user from 'src/agent/user'
|
import user from 'src/agent/user'
|
||||||
import {lastChecked} from 'src/app/alerts'
|
import {lastChecked} from 'src/app/alerts'
|
||||||
import {modal, routes} from 'src/app/ui'
|
|
||||||
|
|
||||||
let limit = 0
|
let limit = 0
|
||||||
let notes = null
|
let notes = null
|
||||||
@ -37,29 +33,13 @@
|
|||||||
{#if notes}
|
{#if notes}
|
||||||
<Content>
|
<Content>
|
||||||
{#each notes as note (note.id)}
|
{#each notes as note (note.id)}
|
||||||
{@const person = database.getPersonWithFallback(note.pubkey)}
|
|
||||||
<div in:fly={{y: 20}}>
|
<div in:fly={{y: 20}}>
|
||||||
{#if note.replies.length > 0}
|
{#if note.replies.length > 0}
|
||||||
<Alert type="replies" {note} />
|
<Alert type="replies" {note} />
|
||||||
{:else if note.likedBy.length > 0}
|
{:else if note.likedBy.length > 0}
|
||||||
<Alert type="likes" {note} />
|
<Alert type="likes" {note} />
|
||||||
{:else}
|
{:else}
|
||||||
<button
|
<Mention {note} />
|
||||||
class="py-2 px-3 flex flex-col gap-2 text-white cursor-pointer transition-all w-full
|
|
||||||
border border-solid border-black hover:border-medium hover:bg-dark text-left"
|
|
||||||
on:click={() => modal.set({type: 'note/detail', note})}>
|
|
||||||
<div class="flex gap-2 items-center justify-between relative w-full">
|
|
||||||
<Anchor type="unstyled" href={routes.person(person.pubkey)} class="align-middle">
|
|
||||||
<ImageCircle src={person.kind0?.picture} />
|
|
||||||
<span class="text-lg font-bold ml-1">{displayPerson(person)}</span>
|
|
||||||
<span>mentioned you.</span>
|
|
||||||
</Anchor>
|
|
||||||
<p class="text-sm text-light">{formatTimestamp(note.created_at)}</p>
|
|
||||||
</div>
|
|
||||||
<div class="ml-6 text-light">
|
|
||||||
{ellipsize(note.content, 120)}
|
|
||||||
</div>
|
|
||||||
</button>
|
|
||||||
{/if}
|
{/if}
|
||||||
</div>
|
</div>
|
||||||
{:else}
|
{:else}
|
||||||
|
41
src/views/alerts/Mention.svelte
Normal file
41
src/views/alerts/Mention.svelte
Normal file
@ -0,0 +1,41 @@
|
|||||||
|
<script lang="ts">
|
||||||
|
import {ellipsize} from 'hurdak/lib/hurdak'
|
||||||
|
import {formatTimestamp} from 'src/util/misc'
|
||||||
|
import {displayPerson} from 'src/util/nostr'
|
||||||
|
import Anchor from 'src/partials/Anchor.svelte'
|
||||||
|
import ImageCircle from "src/partials/ImageCircle.svelte"
|
||||||
|
import Popover from "src/partials/Popover.svelte"
|
||||||
|
import PersonSummary from "src/views/person/PersonSummary.svelte"
|
||||||
|
import database from 'src/agent/database'
|
||||||
|
import {modal} from 'src/app/ui'
|
||||||
|
|
||||||
|
export let note
|
||||||
|
|
||||||
|
const person = database.getPersonWithFallback(note.pubkey)
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<button
|
||||||
|
class="py-2 px-3 flex flex-col gap-2 text-white cursor-pointer transition-all w-full
|
||||||
|
border border-solid border-black hover:border-medium hover:bg-dark text-left"
|
||||||
|
on:click={() => modal.set({type: 'note/detail', note})}>
|
||||||
|
<div class="flex gap-2 items-center justify-between relative w-full">
|
||||||
|
<div class="flex gap-1 items-center" on:click|stopPropagation>
|
||||||
|
<Popover>
|
||||||
|
<div slot="trigger">
|
||||||
|
<Anchor type="unstyled" class="text-lg font-bold flex gap-2 items-center">
|
||||||
|
<ImageCircle src={person.kind0?.picture} />
|
||||||
|
<span class="text-lg font-bold ml-1">{displayPerson(person)}</span>
|
||||||
|
</Anchor>
|
||||||
|
</div>
|
||||||
|
<div slot="tooltip">
|
||||||
|
<PersonSummary pubkey={note.pubkey} />
|
||||||
|
</div>
|
||||||
|
</Popover>
|
||||||
|
<span>mentioned you.</span>
|
||||||
|
</div>
|
||||||
|
<p class="text-sm text-light">{formatTimestamp(note.created_at)}</p>
|
||||||
|
</div>
|
||||||
|
<div class="ml-6 text-light">
|
||||||
|
{ellipsize(note.content, 120)}
|
||||||
|
</div>
|
||||||
|
</button>
|
@ -11,6 +11,8 @@
|
|||||||
import {extractUrls} from "src/util/html"
|
import {extractUrls} from "src/util/html"
|
||||||
import ImageCircle from 'src/partials/ImageCircle.svelte'
|
import ImageCircle from 'src/partials/ImageCircle.svelte'
|
||||||
import Content from 'src/partials/Content.svelte'
|
import Content from 'src/partials/Content.svelte'
|
||||||
|
import PersonSummary from 'src/views/person/PersonSummary.svelte'
|
||||||
|
import Popover from 'src/partials/Popover.svelte'
|
||||||
import RelayCard from 'src/views/relays/RelayCard.svelte'
|
import RelayCard from 'src/views/relays/RelayCard.svelte'
|
||||||
import Modal from 'src/partials/Modal.svelte'
|
import Modal from 'src/partials/Modal.svelte'
|
||||||
import Preview from 'src/partials/Preview.svelte'
|
import Preview from 'src/partials/Preview.svelte'
|
||||||
@ -225,12 +227,19 @@
|
|||||||
</div>
|
</div>
|
||||||
<div class="flex flex-col gap-2 flex-grow min-w-0">
|
<div class="flex flex-col gap-2 flex-grow min-w-0">
|
||||||
<div class="flex items-center justify-between">
|
<div class="flex items-center justify-between">
|
||||||
<Anchor type="unstyled" class="text-lg font-bold flex gap-2 items-center" href={routes.person($person.pubkey)}>
|
<Popover>
|
||||||
<span>{displayPerson($person)}</span>
|
<div slot="trigger">
|
||||||
{#if $person.verified_as}
|
<Anchor type="unstyled" class="text-lg font-bold flex gap-2 items-center">
|
||||||
<i class="fa fa-circle-check text-accent text-sm" />
|
<span>{displayPerson($person)}</span>
|
||||||
{/if}
|
{#if $person.verified_as}
|
||||||
</Anchor>
|
<i class="fa fa-circle-check text-accent text-sm" />
|
||||||
|
{/if}
|
||||||
|
</Anchor>
|
||||||
|
</div>
|
||||||
|
<div slot="tooltip">
|
||||||
|
<PersonSummary pubkey={$person.pubkey} />
|
||||||
|
</div>
|
||||||
|
</Popover>
|
||||||
<Anchor
|
<Anchor
|
||||||
href={"/" + nip19.neventEncode({id: note.id, relays: [note.seen_on]})}
|
href={"/" + nip19.neventEncode({id: note.id, relays: [note.seen_on]})}
|
||||||
class="text-sm text-light"
|
class="text-sm text-light"
|
||||||
|
@ -36,9 +36,34 @@
|
|||||||
let person = database.getPersonWithFallback(pubkey)
|
let person = database.getPersonWithFallback(pubkey)
|
||||||
let loading = true
|
let loading = true
|
||||||
let showActions = false
|
let showActions = false
|
||||||
|
let actions = []
|
||||||
|
|
||||||
$: following = $petnamePubkeys.includes(pubkey)
|
$: following = $petnamePubkeys.includes(pubkey)
|
||||||
|
|
||||||
|
$: {
|
||||||
|
actions = []
|
||||||
|
|
||||||
|
if (showActions) {
|
||||||
|
actions.push({onClick: share, label: 'Share', icon: 'share-nodes'})
|
||||||
|
|
||||||
|
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'})
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($canPublish) {
|
||||||
|
actions.push({href: `/messages/${npub}`, label: 'Message', icon: 'envelope'})
|
||||||
|
actions.push({onClick: openAdvanced, label: 'Advanced', icon: 'sliders'})
|
||||||
|
}
|
||||||
|
|
||||||
|
if (user.getPubkey() === pubkey) {
|
||||||
|
actions.push({href: '/profile', label: 'Edit', icon: 'edit'})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
onMount(async () => {
|
onMount(async () => {
|
||||||
log('Person', npub, person)
|
log('Person', npub, person)
|
||||||
|
|
||||||
@ -75,12 +100,6 @@
|
|||||||
showActions = !showActions
|
showActions = !showActions
|
||||||
}
|
}
|
||||||
|
|
||||||
const makeGetTransition = () => {
|
|
||||||
let i = 0
|
|
||||||
|
|
||||||
return () => ({y: 20, delay: i++ * 30})
|
|
||||||
}
|
|
||||||
|
|
||||||
const setActiveTab = tab => navigate(routes.person(pubkey, tab))
|
const setActiveTab = tab => navigate(routes.person(pubkey, tab))
|
||||||
|
|
||||||
const showFollows = () => {
|
const showFollows = () => {
|
||||||
@ -142,78 +161,27 @@
|
|||||||
</div>
|
</div>
|
||||||
<div class="whitespace-nowrap flex gap-3 flex-wrap relative">
|
<div class="whitespace-nowrap flex gap-3 flex-wrap relative">
|
||||||
<div on:click|stopPropagation={toggleActions} class="px-5 py-2 cursor-pointer">
|
<div on:click|stopPropagation={toggleActions} class="px-5 py-2 cursor-pointer">
|
||||||
<i class="fa fa-ellipsis-vertical" />
|
<i class="fa fa-xl fa-ellipsis-vertical" />
|
||||||
</div>
|
</div>
|
||||||
{#if showActions}
|
|
||||||
{@const getTransition = makeGetTransition()}
|
|
||||||
<div class="absolute top-0 right-0 mt-12 flex flex-col gap-2 opacity-90">
|
<div class="absolute top-0 right-0 mt-12 flex flex-col gap-2 opacity-90">
|
||||||
<div
|
<div
|
||||||
class="absolute inset-0 bg-black rounded-full"
|
class="absolute inset-0 bg-black rounded-full"
|
||||||
|
class:hidden={!showActions}
|
||||||
style="filter: blur(15px)"
|
style="filter: blur(15px)"
|
||||||
transition:fade />
|
transition:fade|local />
|
||||||
|
{#each actions as {onClick, href, label, icon}, i}
|
||||||
<div
|
<div
|
||||||
class="flex gap-2 justify-end items-center z-10 cursor-pointer"
|
class="flex gap-2 justify-end items-center z-10 cursor-pointer"
|
||||||
transition:fly={getTransition()}
|
in:fly|local={{y: 20, delay: i * 30}}
|
||||||
on:click={share}>
|
out:fly|local={{y: 20, delay: (actions.length - i - 1) * 30}}
|
||||||
<div class="text-light">Share</div>
|
on:click={onClick}>
|
||||||
|
<div class="text-light">{label}</div>
|
||||||
<Anchor type="button-circle">
|
<Anchor type="button-circle">
|
||||||
<i class="fa fa-share-nodes" />
|
<i class={`fa fa-${icon}`} />
|
||||||
</Anchor>
|
</Anchor>
|
||||||
</div>
|
</div>
|
||||||
{#if following}
|
{/each}
|
||||||
<div
|
|
||||||
class="flex gap-2 justify-end items-center z-10 cursor-pointer"
|
|
||||||
transition:fly={getTransition()}
|
|
||||||
on:click={unfollow}>
|
|
||||||
<div class="text-light">Unfollow</div>
|
|
||||||
<Anchor type="button-circle">
|
|
||||||
<i class="fa fa-user-minus" />
|
|
||||||
</Anchor>
|
|
||||||
</div>
|
|
||||||
{:else if user.getPubkey() !== pubkey}
|
|
||||||
<div
|
|
||||||
class="flex gap-2 justify-end items-center z-10 cursor-pointer"
|
|
||||||
transition:fly={getTransition()}
|
|
||||||
on:click={follow}>
|
|
||||||
<div class="text-light">Follow</div>
|
|
||||||
<Anchor type="button-circle">
|
|
||||||
<i class="fa fa-user-plus" />
|
|
||||||
</Anchor>
|
|
||||||
</div>
|
|
||||||
{/if}
|
|
||||||
{#if $canPublish}
|
|
||||||
<div
|
|
||||||
class="flex gap-2 justify-end items-center z-10 cursor-pointer"
|
|
||||||
transition:fly={getTransition()}
|
|
||||||
on:click={() => navigate(`/messages/${npub}`)}>
|
|
||||||
<div class="text-light">Message</div>
|
|
||||||
<Anchor type="button-circle">
|
|
||||||
<i class="fa fa-envelope" />
|
|
||||||
</Anchor>
|
|
||||||
</div>
|
|
||||||
<div
|
|
||||||
class="flex gap-2 justify-end items-center z-10 cursor-pointer"
|
|
||||||
transition:fly={getTransition()}
|
|
||||||
on:click={openAdvanced}>
|
|
||||||
<div class="text-light">Advanced</div>
|
|
||||||
<Anchor type="button-circle">
|
|
||||||
<i class="fa fa-sliders" />
|
|
||||||
</Anchor>
|
|
||||||
</div>
|
|
||||||
{/if}
|
|
||||||
{#if user.getPubkey() === pubkey}
|
|
||||||
<div
|
|
||||||
class="flex gap-2 justify-end items-center z-10 cursor-pointer"
|
|
||||||
transition:fly={getTransition()}
|
|
||||||
on:click={() => navigate("/profile")}>
|
|
||||||
<div class="text-light">Edit</div>
|
|
||||||
<Anchor type="button-circle">
|
|
||||||
<i class="fa fa-edit" />
|
|
||||||
</Anchor>
|
|
||||||
</div>
|
|
||||||
{/if}
|
|
||||||
</div>
|
</div>
|
||||||
{/if}
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<p>{@html renderContent(person?.kind0?.about || '')}</p>
|
<p>{@html renderContent(person?.kind0?.about || '')}</p>
|
||||||
|
72
src/views/person/PersonSummary.svelte
Normal file
72
src/views/person/PersonSummary.svelte
Normal file
@ -0,0 +1,72 @@
|
|||||||
|
<script lang="ts">
|
||||||
|
import {last} from 'ramda'
|
||||||
|
import {navigate} from 'svelte-routing'
|
||||||
|
import {renderContent} from 'src/util/html'
|
||||||
|
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 database from "src/agent/database"
|
||||||
|
import {routes, modal} from "src/app/ui"
|
||||||
|
|
||||||
|
export let pubkey
|
||||||
|
|
||||||
|
const {petnamePubkeys} = user
|
||||||
|
const getRelays = () => sampleRelays(getPubkeyWriteRelays(pubkey))
|
||||||
|
|
||||||
|
let following = false
|
||||||
|
let person = database.getPersonWithFallback(pubkey)
|
||||||
|
|
||||||
|
$: following = $petnamePubkeys.includes(pubkey)
|
||||||
|
|
||||||
|
const follow = async () => {
|
||||||
|
const [{url}] = getRelays()
|
||||||
|
|
||||||
|
user.addPetname(pubkey, url, displayPerson(person))
|
||||||
|
}
|
||||||
|
|
||||||
|
const unfollow = async () => {
|
||||||
|
user.removePetname(pubkey)
|
||||||
|
}
|
||||||
|
|
||||||
|
const share = () => {
|
||||||
|
modal.set({type: 'person/share', person})
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<div class="flex flex-col gap-4 py-2 px-3 relative">
|
||||||
|
<div class="flex gap-4">
|
||||||
|
<div
|
||||||
|
class="overflow-hidden w-14 h-14 rounded-full bg-cover bg-center shrink-0 border border-solid border-white"
|
||||||
|
style="background-image: url({person.kind0?.picture})" />
|
||||||
|
<div class="flex-grow flex flex-col gap-2">
|
||||||
|
<Anchor
|
||||||
|
type="unstyled"
|
||||||
|
class="flex items-center gap-2"
|
||||||
|
on:click={() => navigate(routes.person(pubkey))}>
|
||||||
|
<h1 class="text-2xl">{displayPerson(person)}</h1>
|
||||||
|
</Anchor>
|
||||||
|
{#if person.verified_as}
|
||||||
|
<div class="flex gap-1 text-sm">
|
||||||
|
<i class="fa fa-user-check text-accent" />
|
||||||
|
<span class="text-light">{last(person.verified_as.split('@'))}</span>
|
||||||
|
</div>
|
||||||
|
{/if}
|
||||||
|
</div>
|
||||||
|
<div class="flex gap-2">
|
||||||
|
<Anchor class="tippy-close" type="button-circle" on:click={share}>
|
||||||
|
<i class="fa fa-share-nodes" />
|
||||||
|
</Anchor>
|
||||||
|
{#if following}
|
||||||
|
<Anchor type="button-circle" on:click={unfollow}>
|
||||||
|
<i class="fa fa-user-minus" />
|
||||||
|
</Anchor>
|
||||||
|
{:else}
|
||||||
|
<Anchor type="button-circle" on:click={follow}>
|
||||||
|
<i class="fa fa-user-plus" />
|
||||||
|
</Anchor>
|
||||||
|
{/if}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<p>{@html renderContent(person?.kind0?.about || '')}</p>
|
||||||
|
</div>
|
Loading…
Reference in New Issue
Block a user