diff --git a/src/components/EmojiPicker.tsx b/src/components/EmojiPicker.tsx index f2d7c34..49168ad 100644 --- a/src/components/EmojiPicker.tsx +++ b/src/components/EmojiPicker.tsx @@ -3,17 +3,34 @@ import { Component, JSX, createSignal } from 'solid-js'; import { Picker } from 'emoji-mart'; import Popup, { PopupRef } from '@/components/utils/Popup'; +import useConfig from '@/core/useConfig'; type EmojiPickerProps = { onEmojiSelect?: (emoji: string) => void; + customEmojis?: boolean; children: JSX.Element; }; const EmojiPicker: Component = (props) => { let popupRef: PopupRef | undefined; + const { config } = useConfig(); const [pickerElement, setPickerElement] = createSignal(undefined); + /* + const buildCustom = () => { + const emojis = Object.values(config().customEmojis).map(({ shortcode, url }) => ({ + id: shortcode, + name: shortcode, + keywords: [shortcode], + skins: [{ src: url }], + })); + + console.log(emojis); + return [{ id: 'custom_rabbit', name: 'カスタム絵文字', emojis }]; + }; + */ + const handleOpen = () => { const picker = new Picker({ data: async () => { @@ -24,11 +41,12 @@ const EmojiPicker: Component = (props) => { const response = await fetch('https://cdn.jsdelivr.net/npm/@emoji-mart/data/i18n/ja.json'); return response.json(); }, + // custom: props.customEmojis ? buildCustom() : [], autoFocus: false, locale: 'ja', theme: 'light', - onEmojiSelect: (emoji: { id: string; native: string }) => { - props.onEmojiSelect?.(emoji.native); + onEmojiSelect: (emoji: { id: string; native?: string }) => { + props.onEmojiSelect?.(emoji.native ?? `:${emoji.id}:`); popupRef?.close(); }, }); diff --git a/src/components/NotePostForm.tsx b/src/components/NotePostForm.tsx index d67f713..e9cc582 100644 --- a/src/components/NotePostForm.tsx +++ b/src/components/NotePostForm.tsx @@ -1,21 +1,14 @@ -import { - createSignal, - createMemo, - onMount, - Show, - For, - type Component, - type JSX, - type Accessor, -} from 'solid-js'; +import { createSignal, createMemo, onMount, Show, For, type Component, type JSX } from 'solid-js'; import { createMutation } from '@tanstack/solid-query'; +import FaceSmile from 'heroicons/24/outline/face-smile.svg'; import Photo from 'heroicons/24/outline/photo.svg'; import XMark from 'heroicons/24/outline/x-mark.svg'; import PaperAirplane from 'heroicons/24/solid/paper-airplane.svg'; import uniq from 'lodash/uniq'; import { Event as NostrEvent } from 'nostr-tools'; +import EmojiPicker from '@/components/EmojiPicker'; import UserNameDisplay from '@/components/UserDisplayName'; import useConfig from '@/core/useConfig'; import { useHandleCommand } from '@/hooks/useCommandBus'; @@ -48,12 +41,13 @@ const extract = (parsed: ParsedTextNote) => { const pubkeyReferences: string[] = []; const eventReferences: string[] = []; const urlReferences: string[] = []; + const emojis: string[] = []; parsed.forEach((node) => { - if (node.type === 'HashTag') { - hashtags.push(node.tagName); - } else if (node.type === 'URL') { + if (node.type === 'URL') { urlReferences.push(node.content); + } else if (node.type === 'HashTag') { + hashtags.push(node.tagName); } else if (node.type === 'Bech32Entity') { if (node.data.type === 'npub') { pubkeyReferences.push(node.data.data); @@ -63,14 +57,17 @@ const extract = (parsed: ParsedTextNote) => { // TODO nevent can contain an event not only textnote (kind:1). // In my understanding, it is not allowed to include other kinds of events in `tags`. // It is needed to verify that the kind of the event is 1. + } else if (node.type === 'CustomEmoji' && !emojis.includes(node.shortcode)) { + emojis.push(node.shortcode); } }); return { hashtags, + urlReferences, pubkeyReferences, eventReferences, - urlReferences, + emojis, }; }; @@ -94,6 +91,8 @@ const NotePostForm: Component = (props) => { const [contentWarning, setContentWarning] = createSignal(false); const [contentWarningReason, setContentWarningReason] = createSignal(''); + const appendText = (s: string) => setText((current) => `${current} ${s}`); + const clearText = () => { setText(''); setContentWarningReason(''); @@ -106,7 +105,7 @@ const NotePostForm: Component = (props) => { props.onClose(); }; - const { config } = useConfig(); + const { config, getEmoji } = useConfig(); const getPubkey = usePubkey(); const commands = useCommands(); @@ -140,7 +139,7 @@ const NotePostForm: Component = (props) => { uploadResults.forEach((result) => { if (result.status === 'fulfilled') { console.log('succeeded to upload', result); - setText((current) => `${current} ${result.value.imageUrl}`); + appendText(result.value.imageUrl); resizeTextArea(); } else { console.error('failed to upload', result); @@ -169,6 +168,19 @@ const NotePostForm: Component = (props) => { ]); }; + const buildEmojiTags = (emojis: string[]): string[][] => { + const emojiTags: string[][] = []; + + emojis.forEach((shortcode) => { + const emoji = getEmoji(shortcode); + if (emoji != null) { + emojiTags.push(['emoji', shortcode, emoji.url]); + } + }); + + return emojiTags; + }; + const submit = () => { if (text().length === 0) return; if (publishTextNoteMutation.isLoading) return; @@ -180,8 +192,9 @@ const NotePostForm: Component = (props) => { } const parsed = parseTextNote(text()); - const { hashtags, pubkeyReferences, eventReferences, urlReferences } = extract(parsed); + const { hashtags, urlReferences, pubkeyReferences, eventReferences, emojis } = extract(parsed); const formattedContent = format(parsed); + const emojiTags = buildEmojiTags(emojis); let textNote: PublishTextNoteParams = { relayUrls: config().relayUrls, @@ -191,6 +204,7 @@ const NotePostForm: Component = (props) => { mentionEventIds: eventReferences, hashtags, urls: urlReferences, + tags: emojiTags, }; if (replyTo() != null) { @@ -329,6 +343,13 @@ const NotePostForm: Component = (props) => { + {/* + appendText(emoji)}> + + + + + */} + + )} + + +
+ + + +
+ + ); +}; + const MuteConfig = () => { const { config, removeMutedPubkey, addMutedKeyword, removeMutedKeyword } = useConfig(); @@ -335,6 +403,7 @@ const ConfigUI = (props: ConfigProps) => { + diff --git a/src/components/utils/Popup.tsx b/src/components/utils/Popup.tsx index 9f93514..8010783 100644 --- a/src/components/utils/Popup.tsx +++ b/src/components/utils/Popup.tsx @@ -104,6 +104,7 @@ const Popup: Component = (props) => { return (