diff --git a/src/element/live-chat.css b/src/element/live-chat.css index 6faea88..1481a6f 100644 --- a/src/element/live-chat.css +++ b/src/element/live-chat.css @@ -262,7 +262,7 @@ border: none; cursor: pointer; height: 24px; - padding: 0px 4px; + padding: 4px; justify-content: center; align-items: center; gap: 2px; @@ -315,3 +315,26 @@ font-weight: 500; line-height: 18px; } + +.message-composer { + display: flex; + flex-direction: column; +} + +.write-message-container { + display: flex; + align-items: center; + gap: 8px; +} + +.write-message-container .paper { + flex: 1; +} + +.write-emoji-button { + color: #FFFFFF80; + cursor: pointer; +} +.write-emoji-button:hover { + color: white; +} diff --git a/src/element/live-chat.tsx b/src/element/live-chat.tsx index ab180c6..2adf90d 100644 --- a/src/element/live-chat.tsx +++ b/src/element/live-chat.tsx @@ -14,13 +14,13 @@ import { useRef, type KeyboardEvent, type ChangeEvent, - type LegacyRef, + type RefObject, } from "react"; import { useHover, useOnClickOutside, useMediaQuery } from "usehooks-ts"; import data from "@emoji-mart/data"; import Picker from "@emoji-mart/react"; -import useEmoji from "hooks/emoji"; +import useEmoji, { type EmojiPack } from "hooks/emoji"; import { System } from "index"; import { useLiveChatFeed } from "hooks/live-chat"; import AsyncButton from "./async-button"; @@ -37,6 +37,71 @@ import useTopZappers from "hooks/top-zappers"; import { LIVE_STREAM_CHAT } from "const"; import { findTag } from "utils"; +interface EmojiPickerProps { + topOffset: number; + leftOffset: number; + emojiPacks?: EmojiPack[]; + onEmojiSelect: (e: Emoji) => void; + onClickOutside: () => void; + height?: number; + ref: RefObject; +} + +function EmojiPicker({ + topOffset, + leftOffset, + onEmojiSelect, + onClickOutside, + emojiPacks = [], + height = 300, + ref, +}: EmojiPickerProps) { + const customEmojiList = emojiPacks.map((pack) => { + return { + id: pack.address, + name: pack.name, + emojis: pack.emojis.map((e) => { + const [, name, url] = e; + return { + id: name, + name, + skins: [{ src: url }], + }; + }), + }; + }); + return ( + <> +
+ + +
+ + ); +} + export interface LiveChatOptions { canWrite?: boolean; showHeader?: boolean; @@ -144,7 +209,8 @@ function emojifyReaction(reaction: string) { } interface Emoji { - native: string; + id: string; + native?: string; } function ChatMessage({ @@ -198,7 +264,7 @@ function ChatMessage({ const pub = await EventPublisher.nip7(); const reply = await pub?.generic((eb) => { eb.kind(EventKind.Reaction) - .content(emoji.native) + .content(emoji.native || "+1") .tag(["e", ev.id]) .tag(["p", ev.pubkey]); return eb; @@ -215,6 +281,11 @@ function ChatMessage({ // @ts-expect-error const leftOffset = ref.current?.getBoundingClientRect().left; + function pickEmoji(ev: any) { + ev.stopPropagation(); + setShowEmojiPicker(!showEmojiPicker); + } + return ( <>
)} -
)} {showEmojiPicker && ( -
setShowEmojiPicker(false)} ref={emojiRef} - > - - -
+ /> )} ); @@ -353,12 +405,22 @@ function ChatZap({ streamer, ev }: { streamer: string; ev: TaggedRawEvent }) { } function WriteMessage({ link }: { link: NostrLink }) { + const ref = useRef(null); + const emojiRef = useRef(null); const [chat, setChat] = useState(""); + const [showEmojiPicker, setShowEmojiPicker] = useState(false); const login = useLogin(); - const userEmojis = useEmoji(login!.pubkey); - const channelEmojis = useEmoji(link.author!); + const userEmojiPacks = useEmoji(login!.pubkey); + const userEmojis = userEmojiPacks.map((pack) => pack.emojis).flat(); + const channelEmojiPacks = useEmoji(link.author!); + const channelEmojis = channelEmojiPacks.map((pack) => pack.emojis).flat(); const emojis = userEmojis.concat(channelEmojis); const names = emojis.map((t) => t.at(1)); + const allEmojiPacks = userEmojiPacks.concat(channelEmojiPacks); + // @ts-expect-error + const topOffset = ref.current?.getBoundingClientRect().top; + // @ts-expect-error + const leftOffset = ref.current?.getBoundingClientRect().left; async function sendChatMessage() { const pub = await EventPublisher.nip7(); @@ -394,6 +456,15 @@ function WriteMessage({ link }: { link: NostrLink }) { } } + function onEmojiSelect(emoji: Emoji) { + if (emoji.native) { + setChat(`${chat}${emoji.native}`); + } else { + setChat(`${chat}:${emoji.id}:`); + } + setShowEmojiPicker(false); + } + async function onKeyDown(e: KeyboardEvent) { if (e.code === "Enter") { e.preventDefault(); @@ -406,15 +477,33 @@ function WriteMessage({ link }: { link: NostrLink }) { setChat(e.target.value); } + function pickEmoji(ev: any) { + ev.stopPropagation(); + setShowEmojiPicker(!showEmojiPicker); + } + return ( <> -
+