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 {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 {
pubkey,
@@ -13,6 +14,8 @@ import {
loadFollows,
loadMutes,
getFollows,
tagEvent,
tagReactionTo,
} from "@welshman/app"
import {ROOM, MEMBERSHIPS, INDEXER_RELAYS} from "@app/state"
@@ -106,3 +109,30 @@ export const removeSpaceMembership = (url: string) =>
export const removeRoomMembership = (url: string, room: string) =>
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">
import {onMount} from "svelte"
import type {SvelteComponent} from "svelte"
import type {NativeEmoji} from 'emoji-picker-element/shared'
import twColors from "tailwindcss/colors"
import type {Readable} from "svelte/store"
@@ -26,14 +27,19 @@
import Icon from "@lib/components/Icon.svelte"
import Button from "@lib/components/Button.svelte"
import Avatar from "@lib/components/Avatar.svelte"
import Drawer from "@lib/components/Drawer.svelte"
import Content from "@app/components/Content.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 {publishDelete, publishReaction} from "@app/commands"
export let url
export let room
export let event: TrustedEvent
export let showPubkey: boolean
export let isReply = false
const colors = [
["amber", twColors.amber[600]],
@@ -71,33 +77,23 @@
const findStatus = ($ps: PublishStatusData[], statuses: PublishStatus[]) =>
$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 reaction = events.find(e => e.pubkey === $pubkey)
if (reaction) {
const deleteEvent = createEvent(DELETE, {
tags: [["k", String(reaction.kind)], ...tagEvent(reaction)],
})
publishThunk(makeThunk({event: deleteEvent, relays: [url]}))
publishDelete({relays: [url], event: reaction})
} 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]
$: parentProfile = deriveProfile(parentPubkey || "")
@@ -108,7 +104,11 @@
!isPending && !isPublished && findStatus($ps, [PublishStatus.Failure, PublishStatus.Timeout])
</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}
<div class="flex items-center gap-1 pl-12 text-xs">
<Icon icon="arrow-right" />
@@ -168,12 +168,22 @@
{/if}
<div
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">
<Icon icon="reply" size={4} />
</button>
<ChatMessageEmojiButton onEmoji={onEmoji} />
{#if !isReply}
<button class="btn join-item btn-xs" on:click={() => drawer.open()}>
<Icon icon="reply" size={4} />
</button>
{/if}
<ChatMessageEmojiButton {url} {room} {event} />
<button class="btn join-item btn-xs">
<Icon icon="menu-dots" size={4} />
</button>
</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">
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 Icon from "@lib/components/Icon.svelte"
import Button from "@lib/components/Button.svelte"
import Tippy from "@lib/components/Tippy.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 onClick = (emoji: Emoji) => {
onEmoji(emoji)
const onClick = (emoji: NativeEmoji) => {
publishReaction({
event,
relays: [url],
content: emoji.unicode,
tags: [[ROOM, room, url]],
})
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>