Add drawer, start working on replies

This commit is contained in:
Jon Staab
2024-09-25 15:49:17 -07:00
parent b72ee57b1d
commit 9a4c62e740
6 changed files with 116 additions and 28 deletions
+31 -1
View File
@@ -1,5 +1,6 @@
import {uniqBy, sleep, chunk, equals, choice} from "@welshman/lib" import {uniqBy, sleep, chunk, equals, choice} from "@welshman/lib"
import {getPubkeyTagValues, createEvent, displayProfile} from "@welshman/util" import {DELETE, REACTION, getPubkeyTagValues, createEvent, displayProfile} from "@welshman/util"
import type {TrustedEvent} from "@welshman/util"
import type {SubscribeRequestWithHandlers} from "@welshman/net" import type {SubscribeRequestWithHandlers} from "@welshman/net"
import { import {
pubkey, pubkey,
@@ -13,6 +14,8 @@ import {
loadFollows, loadFollows,
loadMutes, loadMutes,
getFollows, getFollows,
tagEvent,
tagReactionTo,
} from "@welshman/app" } from "@welshman/app"
import {ROOM, MEMBERSHIPS, INDEXER_RELAYS} from "@app/state" import {ROOM, MEMBERSHIPS, INDEXER_RELAYS} from "@app/state"
@@ -106,3 +109,30 @@ export const removeSpaceMembership = (url: string) =>
export const removeRoomMembership = (url: string, room: string) => export const removeRoomMembership = (url: string, room: string) =>
updateList(MEMBERSHIPS, (tags: string[][]) => tags.filter(t => !equals([ROOM, room, url], t))) updateList(MEMBERSHIPS, (tags: string[][]) => tags.filter(t => !equals([ROOM, room, url], t)))
// Actions
export const publishReaction = ({relays, event, content, tags = []}: {
relays: string[]
event: TrustedEvent,
content: string,
tags?: string[][]
}) => {
const reaction = createEvent(REACTION, {
content,
tags: [
...tags,
...tagReactionTo(event),
],
})
publishThunk(makeThunk({event: reaction, relays}))
}
export const publishDelete = ({relays, event}: {relays: string[], event: TrustedEvent}) => {
const deleteEvent = createEvent(DELETE, {
tags: [["k", String(event.kind)], ...tagEvent(event)],
})
publishThunk(makeThunk({event: deleteEvent, relays}))
}
+33 -23
View File
@@ -1,5 +1,6 @@
<script lang="ts"> <script lang="ts">
import {onMount} from "svelte" import {onMount} from "svelte"
import type {SvelteComponent} from "svelte"
import type {NativeEmoji} from 'emoji-picker-element/shared' import type {NativeEmoji} from 'emoji-picker-element/shared'
import twColors from "tailwindcss/colors" import twColors from "tailwindcss/colors"
import type {Readable} from "svelte/store" import type {Readable} from "svelte/store"
@@ -26,14 +27,19 @@
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 Drawer from "@lib/components/Drawer.svelte"
import Content from "@app/components/Content.svelte" import Content from "@app/components/Content.svelte"
import ChatMessageEmojiButton from "@app/components/ChatMessageEmojiButton.svelte" import ChatMessageEmojiButton from "@app/components/ChatMessageEmojiButton.svelte"
import ChatMessageReplies from "@app/components/ChatMessageReplies.svelte"
import ChatMessageReply from "@app/components/ChatMessageReply.svelte"
import {ROOM, REPLY, deriveEvent, displayReaction} from "@app/state" import {ROOM, REPLY, deriveEvent, displayReaction} from "@app/state"
import {publishDelete, publishReaction} from "@app/commands"
export let url export let url
export let room export let room
export let event: TrustedEvent export let event: TrustedEvent
export let showPubkey: boolean export let showPubkey: boolean
export let isReply = false
const colors = [ const colors = [
["amber", twColors.amber[600]], ["amber", twColors.amber[600]],
@@ -71,33 +77,23 @@
const findStatus = ($ps: PublishStatusData[], statuses: PublishStatus[]) => const findStatus = ($ps: PublishStatusData[], statuses: PublishStatus[]) =>
$ps.find(({status}) => statuses.includes(status)) $ps.find(({status}) => statuses.includes(status))
const createReaction = (content: string) => {
const reaction = createEvent(REACTION, {
content,
tags: [
[ROOM, room, url],
...tagReactionTo(event),
],
})
publishThunk(makeThunk({event: reaction, relays: [url]}))
}
const onReactionClick = (content: string, events: TrustedEvent[]) => { const onReactionClick = (content: string, events: TrustedEvent[]) => {
const reaction = events.find(e => e.pubkey === $pubkey) const reaction = events.find(e => e.pubkey === $pubkey)
if (reaction) { if (reaction) {
const deleteEvent = createEvent(DELETE, { publishDelete({relays: [url], event: reaction})
tags: [["k", String(reaction.kind)], ...tagEvent(reaction)],
})
publishThunk(makeThunk({event: deleteEvent, relays: [url]}))
} else { } else {
createReaction(content) publishReaction({
event,
content,
relays: [url],
tags: [[ROOM, room, url]],
})
} }
} }
const onEmoji = (emoji: NativeEmoji) => createReaction(emoji.unicode) let drawer: SvelteComponent
$: parentPubkey = $parentEvent?.pubkey || replies[0]?.[4] $: parentPubkey = $parentEvent?.pubkey || replies[0]?.[4]
$: parentProfile = deriveProfile(parentPubkey || "") $: parentProfile = deriveProfile(parentPubkey || "")
@@ -108,7 +104,11 @@
!isPending && !isPublished && findStatus($ps, [PublishStatus.Failure, PublishStatus.Timeout]) !isPending && !isPublished && findStatus($ps, [PublishStatus.Failure, PublishStatus.Timeout])
</script> </script>
<div in:fly class="group relative flex flex-col gap-1 p-2 transition-colors hover:bg-base-300"> <div
in:fly
class="group relative flex flex-col gap-1 p-2 transition-colors"
class:hover:bg-base-300={!isReply}
class:mt-4={isReply}>
{#if event.kind === REPLY} {#if event.kind === REPLY}
<div class="flex items-center gap-1 pl-12 text-xs"> <div class="flex items-center gap-1 pl-12 text-xs">
<Icon icon="arrow-right" /> <Icon icon="arrow-right" />
@@ -168,12 +168,22 @@
{/if} {/if}
<div <div
class="join absolute -top-2 right-0 border border-solid border-neutral text-xs opacity-0 transition-all group-hover:opacity-100"> class="join absolute -top-2 right-0 border border-solid border-neutral text-xs opacity-0 transition-all group-hover:opacity-100">
<button class="btn join-item btn-xs"> {#if !isReply}
<Icon icon="reply" size={4} /> <button class="btn join-item btn-xs" on:click={() => drawer.open()}>
</button> <Icon icon="reply" size={4} />
<ChatMessageEmojiButton onEmoji={onEmoji} /> </button>
{/if}
<ChatMessageEmojiButton {url} {room} {event} />
<button class="btn join-item btn-xs"> <button class="btn join-item btn-xs">
<Icon icon="menu-dots" size={4} /> <Icon icon="menu-dots" size={4} />
</button> </button>
</div> </div>
</div> </div>
{#if !isReply}
<Drawer bind:this={drawer}>
<svelte:self {...$$props} isReply />
<ChatMessageReplies {url} {room} {event} />
<ChatMessageReply {url} {room} {event} />
</Drawer>
{/if}
@@ -1,18 +1,26 @@
<script lang="ts"> <script lang="ts">
import tippy, {type Instance} from "tippy.js" import tippy, {type Instance} from "tippy.js"
import type {Emoji} from 'emoji-picker-element/shared' import type {NativeEmoji} from 'emoji-picker-element/shared'
import {between} from '@welshman/lib' import {between} from '@welshman/lib'
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 Tippy from "@lib/components/Tippy.svelte" import Tippy from "@lib/components/Tippy.svelte"
import EmojiPicker from "@lib/components/EmojiPicker.svelte" import EmojiPicker from "@lib/components/EmojiPicker.svelte"
import {ROOM} from '@app/state'
import {publishReaction} from '@app/commands'
export let onEmoji export let url, room, event
const open = () => popover.show() const open = () => popover.show()
const onClick = (emoji: Emoji) => { const onClick = (emoji: NativeEmoji) => {
onEmoji(emoji) publishReaction({
event,
relays: [url],
content: emoji.unicode,
tags: [[ROOM, room, url]],
})
popover.hide() popover.hide()
} }
@@ -0,0 +1 @@
hi
@@ -0,0 +1 @@
hi
+38
View File
@@ -0,0 +1,38 @@
<script lang="ts">
import {randomId} from '@welshman/lib'
import Icon from '@lib/components/Icon.svelte'
const id = randomId()
let input: any
let label: any
export const open = () => {
if (!input.checked) {
label.click()
}
}
export const close = () => {
if (input.checked) {
label.click()
}
}
export const toggle = () => {
label.click()
}
</script>
<div class="drawer drawer-end">
<input {id} type="checkbox" class="drawer-toggle" bind:this={input} />
<div class="drawer-content">
<label for={id} bind:this={label} />
</div>
<div class="drawer-side z-modal">
<label for={id} aria-label="close sidebar" class="drawer-overlay"></label>
<div class="menu bg-base-200 text-base-content min-h-full w-80">
<slot />
</div>
</div>
</div>