Add emoji reactions

This commit is contained in:
Jon Staab
2024-09-25 13:46:10 -07:00
parent 0b8f80ed0e
commit ce733e5743
10 changed files with 145 additions and 16 deletions
+43 -8
View File
@@ -1,9 +1,10 @@
<script lang="ts">
import {onMount} from "svelte"
import type {Emoji} from 'emoji-picker-element/shared'
import twColors from "tailwindcss/colors"
import type {Readable} from "svelte/store"
import {readable, derived} from "svelte/store"
import {hash, groupBy, now} from "@welshman/lib"
import {hash, uniqBy, groupBy, now} from "@welshman/lib"
import type {TrustedEvent} from "@welshman/util"
import {deriveEvents} from "@welshman/store"
import {PublishStatus} from "@welshman/net"
@@ -12,17 +13,25 @@
deriveProfile,
deriveProfileDisplay,
formatTimestampAsTime,
tagReactionTo,
tagEvent,
makeThunk,
publishThunk,
pubkey,
} from "@welshman/app"
import type {PublishStatusData} from "@welshman/app"
import {REACTION, ZAP_RESPONSE, displayRelayUrl, getAncestorTags} from "@welshman/util"
import {REACTION, DELETE, ZAP_RESPONSE, createEvent, displayRelayUrl, getAncestorTags} from "@welshman/util"
import {repository} from "@welshman/app"
import {fly} from "@lib/transition"
import Icon from "@lib/components/Icon.svelte"
import Button from "@lib/components/Button.svelte"
import Avatar from "@lib/components/Avatar.svelte"
import Content from "@app/components/Content.svelte"
import {REPLY, deriveEvent, displayReaction} from "@app/state"
import ChatMessageEmojiButton from "@app/components/ChatMessageEmojiButton.svelte"
import {ROOM, REPLY, deriveEvent, displayReaction} from "@app/state"
export let url
export let room
export let event: TrustedEvent
export let showPubkey: boolean
@@ -62,6 +71,34 @@
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]}))
} else {
createReaction(content)
}
}
const onEmoji = (emoji: Emoji) => createReaction(emoji.unicode)
$: parentPubkey = $parentEvent?.pubkey || replies[0]?.[4]
$: parentProfile = deriveProfile(parentPubkey || "")
$: parentProfileDisplay = deriveProfileDisplay(parentPubkey || "")
@@ -119,8 +156,8 @@
</div>
{#if $reactions.length > 0 || $zaps.length > 0}
<div class="ml-12 text-xs">
{#each groupBy(e => e.content, $reactions).entries() as [content, events]}
<Button class="flex-inline btn btn-neutral btn-xs mr-2 gap-1 rounded-full">
{#each groupBy(e => e.content, uniqBy(e => e.pubkey + e.content, $reactions)).entries() as [content, events]}
<Button class="flex-inline btn btn-neutral btn-xs mr-2 gap-1 rounded-full" on:click={() => onReactionClick(content, events)}>
<span>{displayReaction(content)}</span>
{#if events.length > 1}
<span>{events.length}</span>
@@ -134,9 +171,7 @@
<button class="btn join-item btn-xs">
<Icon icon="reply" size={4} />
</button>
<button class="btn join-item btn-xs">
<Icon icon="smile-circle" size={4} />
</button>
<ChatMessageEmojiButton onEmoji={onEmoji} />
<button class="btn join-item btn-xs">
<Icon icon="menu-dots" size={4} />
</button>
@@ -0,0 +1,45 @@
<script lang="ts">
import tippy, {type Instance} from "tippy.js"
import type {Emoji} 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"
export let onEmoji
const open = () => popover.show()
const onClick = (emoji: Emoji) => {
onEmoji(emoji)
popover.hide()
}
const onMouseMove = ({clientX, clientY}: any) => {
const {x, y, width, height} = popover.popper.getBoundingClientRect()
if (!between([x, x + width], clientX) || !between([y, y + height + 30], clientY)) {
popover.hide()
}
}
let popover: Instance
</script>
<svelte:document on:mousemove={onMouseMove} />
<div class="flex">
<Button class="btn join-item btn-xs" on:click={open}>
<Icon icon="smile-circle" size={4} />
</Button>
<Tippy
bind:popover
component={EmojiPicker}
props={{onClick}}
params={{
trigger: "manual",
interactive: true,
}}
/>
</div>