remove profile popover and add mute button to profile feed

This commit is contained in:
Jonathan Staab 2023-07-27 13:00:55 -07:00
parent 2a0780e55b
commit fd29fb82f9
13 changed files with 61 additions and 85 deletions

View File

@ -13,6 +13,7 @@
- [x] Emphasize follow status instead of nip05 addresses
- [x] Add image uploads to chat
- [x] Add new thread view
- [x] Removed profile popover, click on a person's name to find mute and follow buttons
# 0.2.35

View File

@ -1,9 +1,7 @@
# Current
- [ ] Refactor
- [ ] Add thread view
- [ ] Use image and fetch proxies for user-provided content (especially profile data: nip05, picture, banner) to avoid security warnings
- [ ] Remove profile popover
- [ ] Re-work note media
- [ ] Add webcam image/video capture
- [ ] Show list of media that can be viewed/removed below post
@ -12,7 +10,6 @@
- [ ] Support bigger files, or at least handle errors. Monetize, or use nip 95
- [ ] Fork and white label blowater
- [ ] Add word/char count to compose
- [ ] Show full nip05 unless it starts with an underscore, change icon to non-verified
- [ ] Add relayset support with kind 30022
- [ ] White-labeled
- [ ] Add invite code registration for relay
@ -26,12 +23,6 @@
- [ ] Add imgproxy https://github.com/imgproxy/imgproxy
- Protects metadata, saves bandwidth, fixes void.cat?
- [ ] Put search icon in header or hover button, open in modal
- [ ] Centralize relays
- This is ok, relays are the source of decentralization
- clients can help with discoverability
- Add agent to dufflepud database that scrapes relays and reviews
- Serve relays from dufflepud
- Or maybe set up a special purpose relay for relay recs?
# Core

View File

@ -9,7 +9,7 @@
import Popover from "src/partials/Popover.svelte"
import Anchor from "src/partials/Anchor.svelte"
import PersonCircle from "src/app/shared/PersonCircle.svelte"
import PersonSummary from "src/app/shared/PersonSummary.svelte"
import PersonName from "src/app/shared/PersonName.svelte"
import NoteReply from "src/app/shared/NoteReply.svelte"
import NoteActions from "src/app/shared/NoteActions.svelte"
import Card from "src/partials/Card.svelte"
@ -37,7 +37,6 @@
const interactive = !anchorId || !showEntire
const author = Directory.profiles.key(note.pubkey).derived(defaultTo({pubkey: note.pubkey}))
const muted = Nip02.graph.key(Keys.pubkey.get()).derived(() => user.isIgnoring(note.id))
const following = user.followsSet.derived(s => s.has(note.pubkey))
let border, childrenContainer, noteContainer
@ -119,22 +118,12 @@
</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="mouseenter">
<div slot="trigger">
<Anchor
type="unstyled"
class="flex items-center gap-2 pr-16 text-lg font-bold"
on:click={() => modal.push({type: "person/feed", pubkey: note.pubkey})}>
<span>{Directory.displayProfile($author)}</span>
{#if $following}
<i class="fa fa-user-check text-sm text-accent" />
{/if}
</Anchor>
</div>
<div slot="tooltip">
<PersonSummary pubkey={note.pubkey} />
</div>
</Popover>
<Anchor
type="unstyled"
class="pr-16 text-lg font-bold"
on:click={() => modal.push({type: "person/feed", pubkey: note.pubkey})}>
<PersonName pubkey={$author.pubkey} />
</Anchor>
<Anchor
href={"/" + nip19.neventEncode({id: note.id, relays: note.seen_on})}
class="text-sm text-gray-1"

View File

@ -9,6 +9,8 @@
export let pubkey
const canSign = Keys.canSign.get()
const isSelf = Keys.pubkey.get() === pubkey
const npub = nip19.npubEncode(pubkey)
const graphEntry = Nip02.graph.key(Keys.pubkey.get())
const following = graphEntry.derived(() => user.isFollowing(pubkey))
@ -19,7 +21,7 @@
$: {
actions = []
if (Keys.canSign.get()) {
if (canSign) {
actions.push({
onClick: () => addToList("p", pubkey),
label: "Add to list",
@ -29,25 +31,19 @@
actions.push({onClick: share, label: "Share", icon: "share-nodes"})
if (Keys.pubkey.get() !== pubkey && Keys.canSign.get()) {
if (!isSelf && canSign) {
actions.push({
onClick: () => navigate(`/messages/${npub}`),
label: "Message",
icon: "envelope",
})
if ($muted) {
actions.push({onClick: unmute, label: "Unmute", icon: "microphone"})
} else if (Keys.pubkey.get() !== pubkey) {
actions.push({onClick: mute, label: "Mute", icon: "microphone-slash"})
}
}
if (Env.FORCE_RELAYS.length === 0) {
actions.push({onClick: openProfileInfo, label: "Details", icon: "info"})
}
if (Keys.pubkey.get() === pubkey && Keys.canSign.get()) {
if (isSelf && canSign) {
actions.push({
onClick: () => navigate("/profile"),
label: "Edit",
@ -70,12 +66,22 @@
</script>
<div class="flex items-center gap-3">
{#if Keys.canSign.get()}
{#if canSign && !isSelf}
<Popover triggerType="mouseenter">
<div slot="trigger">
<div slot="trigger" class="w-6 text-center">
{#if $muted}
<i class="fa fa-microphone-slash cursor-pointer" on:click={unmute} />
{:else}
<i class="fa fa-microphone cursor-pointer" on:click={mute} />
{/if}
</div>
<div slot="tooltip">{$muted ? "Unmute" : "Mute"}</div>
</Popover>
<Popover triggerType="mouseenter">
<div slot="trigger" class="w-6 text-center">
{#if $following}
<i class="fa fa-user-minus cursor-pointer" on:click={unfollow} />
{:else if Keys.pubkey.get() !== pubkey}
{:else}
<i class="fa fa-user-plus cursor-pointer" on:click={follow} />
{/if}
</div>

View File

@ -4,13 +4,19 @@
import {Directory, user} from "src/app/engine"
export let pubkey
export let inert = false
const profile = Directory.profiles.key(pubkey).derived(defaultTo({pubkey}))
const following = user.followsSet.derived(s => s.has(pubkey))
</script>
<div class={cx("flex items-center gap-2", $$props.class)}>
<span>{Directory.displayProfile($profile)}</span>
<div
class={cx("flex items-center gap-2 group/PersonName", $$props.class)}>
<span class={cx({
"duration-600 transition-all border-b border-solid border-transparent group-hover/PersonName:border-gray-3": !inert,
})}>
{Directory.displayProfile($profile)}
</span>
{#if $following}
<i class="fa fa-user-check text-xs text-accent" />
{/if}

View File

@ -8,6 +8,7 @@
import {Keys, user} from "src/app/engine"
export let pubkey
export let inert = false
export let hideActions = false
const following = user.followsSet.derived(s => s.has(pubkey))
@ -20,10 +21,10 @@
</script>
<div class="relative flex flex-grow flex-col gap-4 px-3 py-2">
<Anchor on:click={showDetail} class="flex gap-4">
<Anchor on:click={inert ? null : showDetail} class="flex gap-4">
<PersonCircle size={14} {pubkey} />
<div class="mr-16 flex flex-grow flex-col gap-1">
<PersonName class="text-lg" {pubkey} />
<PersonName inert class="text-lg" {pubkey} />
<PersonHandle {pubkey} />
</div>
</Anchor>

View File

@ -16,7 +16,7 @@
import NoteContent from "src/app/shared/NoteContent.svelte"
import RelaySearch from "src/app/shared/RelaySearch.svelte"
import {Directory, user, Builder, Nip65, Keys} from "src/app/engine"
import {toast, modal} from "src/partials/state"
import {modal} from "src/partials/state"
import {publishWithToast} from "src/app/state"
export let quote = null
@ -43,27 +43,7 @@
}
if (content) {
const rawEvent = Builder.createNote(content.trim(), tags)
const [event, promise] = await publishWithToast(rawEvent, $relays)
promise.then(() =>
setTimeout(
() =>
toast.show("info", {
text: `Your note has been created!`,
link: {
text: "View",
href:
"/" +
nip19.neventEncode({
id: event.id,
relays: $relays.slice(0, 3),
}),
},
}),
3000
)
)
await publishWithToast(Builder.createNote(content.trim(), tags), $relays)
modal.clear()
}

View File

@ -56,7 +56,7 @@
<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">
<PersonName class="text-2xl" {pubkey} />
<PersonName inert class="text-2xl" {pubkey} />
<PersonHandle {pubkey} />
</div>
<PersonActions {pubkey} />

View File

@ -40,12 +40,12 @@
<Content>
<div class="z-10 flex gap-4 text-gray-1">
<PersonCircle pubkey={$profile.pubkey} size={16} class="sm:h-32 sm:w-32" />
<PersonCircle {pubkey} 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">
<Anchor href={routes.person(pubkey)}>
<PersonName {pubkey} />
<PersonName inert class="text-2xl" {pubkey} />
</Anchor>
<PersonHandle {pubkey} />
</div>

View File

@ -118,7 +118,6 @@
class="fa-solid fa-camera cursor-pointer text-accent"
on:click={() => scanner.start()} />
</Input>
{#each search(q).slice(0, 50) as result (result.type + result.id)}
{#if result.type === "topic"}
<BorderLeft on:click={() => openTopic(result.topic.name)}>
@ -126,7 +125,7 @@
</BorderLeft>
{:else if result.type === "profile"}
<BorderLeft on:click={() => modal.push({type: "person/feed", pubkey: result.id})}>
<PersonSummary hideActions pubkey={result.id} />
<PersonSummary inert hideActions pubkey={result.id} />
</BorderLeft>
{/if}
{/each}

View File

@ -107,13 +107,16 @@ export class FeedLoader {
this.feed.update($feed => {
// Avoid showing the same note twice, even if it's once as
// a parent and once as a child
const ids = new Set(pluck('id', $feed))
const ids = new Set(pluck("id", $feed))
return uniqBy(
prop("id"),
$feed.concat(
this.context.applyContext(
sortBy(e => -e.created_at, notes.filter(e => !ids.has(e))),
sortBy(
e => -e.created_at,
notes.filter(e => !ids.has(e.id))
),
true
)
)

View File

@ -5,7 +5,7 @@
<Anchor
type="unstyled"
class="flex gap-4 border-l-2 border-solid border-gray-6 py-2 px-4 transition-all
hover:border-accent hover:bg-gray-8"
hover:border-accent hover:bg-gray-8 hover:pl-5"
on:click>
<slot />
</Anchor>

View File

@ -15,8 +15,11 @@
const id = randomId()
const {stack} = modal
$: _isOnTop = virtual || isOnTop
$: _onEscape = _isOnTop ? onEscape : null
const escapeIfOnTop = () => {
if (virtual || isOnTop) {
onEscape()
}
}
onMount(() => {
if (virtual) {
@ -34,28 +37,25 @@
<svelte:body
on:keydown={e => {
if (e.key === "Escape" && !root.querySelector(".modal")) {
_onEscape?.()
escapeIfOnTop?.()
}
}} />
<div
bind:this={root}
transition:fade
class="modal fixed inset-0 z-30"
class:pointer-events-none={mini}>
{#if !mini}
<div
class="fixed inset-0 cursor-pointer bg-black opacity-50"
on:click|stopPropagation={_onEscape} />
{/if}
class="modal fixed inset-0 z-30">
<div
class="fixed inset-0 cursor-pointer bg-black opacity-50"
on:click|stopPropagation={escapeIfOnTop} />
<div
class="modal-content h-full overflow-auto"
class:overflow-hidden={mini}
class:pointer-events-none={mini}
class:cursor-pointer={_onEscape}
class:cursor-pointer={escapeIfOnTop}
transition:fly={{y: 1000}}
bind:this={content}
on:click={_onEscape}>
on:click={escapeIfOnTop}>
<div
class="pointer-events-auto mt-12 min-h-full transition transition-all duration-500"
style={mini ? "margin-top: 55vh" : ""}>