diff --git a/src/components/chat/EmojiPickerDialog.tsx b/src/components/chat/EmojiPickerDialog.tsx index 8303899..fdb528a 100644 --- a/src/components/chat/EmojiPickerDialog.tsx +++ b/src/components/chat/EmojiPickerDialog.tsx @@ -6,8 +6,7 @@ import type { EmojiSearchResult } from "@/services/emoji-search"; import type { EmojiTag } from "@/lib/emoji-helpers"; import { useEmojiSearch } from "@/hooks/useEmojiSearch"; import { useEmojiFrequency } from "@/hooks/useEmojiFrequency"; -import { CustomEmoji } from "../nostr/CustomEmoji"; -import { UnicodeEmoji } from "../nostr/UnicodeEmoji"; +import { Emoji } from "../nostr/Emoji"; interface EmojiPickerDialogProps { open: boolean; @@ -173,20 +172,13 @@ export function EmojiPickerDialog({ className="hover:bg-muted rounded p-2 transition-colors flex items-center justify-center aspect-square" title={`:${result.shortcode}:`} > - {result.source === "unicode" ? ( - - ) : ( - - )} + )) ) : ( diff --git a/src/components/chat/MessageReactions.tsx b/src/components/chat/MessageReactions.tsx index 425d83e..f18c907 100644 --- a/src/components/chat/MessageReactions.tsx +++ b/src/components/chat/MessageReactions.tsx @@ -5,8 +5,7 @@ import eventStore from "@/services/event-store"; import pool from "@/services/relay-pool"; import accountManager from "@/services/accounts"; import { EMOJI_SHORTCODE_REGEX } from "@/lib/emoji-helpers"; -import { CustomEmoji } from "../nostr/CustomEmoji"; -import { UnicodeEmoji } from "../nostr/UnicodeEmoji"; +import { Emoji } from "../nostr/Emoji"; interface MessageReactionsProps { messageId: string; @@ -178,22 +177,14 @@ function ReactionBadge({ reaction }: { reaction: ReactionSummary }) { className="inline-flex items-center gap-1.5 text-[10px] leading-tight" title={tooltip} > - {reaction.customEmoji ? ( - - ) : ( - - )} + - {item.source === "unicode" ? ( - // Unicode emoji - render with UnicodeEmoji component - - ) : ( - // Custom emoji - render using CustomEmoji component - - )} + ))} diff --git a/src/components/nostr/CustomEmoji.tsx b/src/components/nostr/CustomEmoji.tsx deleted file mode 100644 index 106b949..0000000 --- a/src/components/nostr/CustomEmoji.tsx +++ /dev/null @@ -1,78 +0,0 @@ -import { useState } from "react"; -import { cn } from "@/lib/utils"; -import { Tooltip, TooltipContent, TooltipTrigger } from "../ui/tooltip"; - -export interface CustomEmojiProps { - /** The shortcode (without colons) */ - shortcode: string; - /** The image URL */ - url: string; - /** Size variant */ - size?: "xs" | "sm" | "md" | "lg"; - /** Additional class names */ - className?: string; - /** Whether to show tooltip on hover (default: true) */ - showTooltip?: boolean; -} - -const sizeClasses = { - xs: "size-3.5", - sm: "size-4", - md: "size-6", - lg: "size-12", -}; - -/** - * Renders a custom emoji image from NIP-30 - * Handles loading states and errors gracefully - */ -export function CustomEmoji({ - shortcode, - url, - size = "md", - className, - showTooltip = true, -}: CustomEmojiProps) { - const [error, setError] = useState(false); - - if (error) { - return ( - - ? - - ); - } - - const img = ( - {`:${shortcode}:`} setError(true)} - /> - ); - - if (!showTooltip) { - return img; - } - - return ( - - {img} - :{shortcode}: - - ); -} diff --git a/src/components/nostr/Emoji.tsx b/src/components/nostr/Emoji.tsx new file mode 100644 index 0000000..d77e76b --- /dev/null +++ b/src/components/nostr/Emoji.tsx @@ -0,0 +1,136 @@ +import { useState } from "react"; +import { cn } from "@/lib/utils"; +import { Tooltip, TooltipContent, TooltipTrigger } from "../ui/tooltip"; + +export interface EmojiProps { + /** Source type - determines how to render */ + source: "unicode" | "custom"; + /** The value - unicode character for unicode, URL for custom */ + value: string; + /** Shortcode for tooltip (without colons) */ + shortcode: string; + /** Size variant */ + size?: "xs" | "sm" | "md" | "lg"; + /** Additional class names */ + className?: string; + /** Whether to show tooltip on hover (default: true) */ + showTooltip?: boolean; +} + +/** + * Size classes for custom emoji images + */ +const imageSizeClasses = { + xs: "size-3.5", + sm: "size-4", + md: "size-6", + lg: "size-12", +}; + +/** + * Text size classes for unicode emoji that visually match image sizes + * - xs: size-3.5 (14px) → text-sm (14px) + * - sm: size-4 (16px) → text-base (16px) + * - md: size-6 (24px) → text-2xl (24px) + * - lg: size-12 (48px) → text-5xl (48px) + */ +const textSizeClasses = { + xs: "text-sm", + sm: "text-base", + md: "text-2xl", + lg: "text-5xl", +}; + +/** + * Unified emoji component that renders both unicode and custom emoji + * with consistent sizing, tooltips, and error handling. + * + * @example + * // Unicode emoji + * + * + * // Custom emoji + * + * + * // From EmojiSearchResult + * + */ +export function Emoji({ + source, + value, + shortcode, + size = "md", + className, + showTooltip = true, +}: EmojiProps) { + const [error, setError] = useState(false); + + // Render custom emoji with error handling + if (source === "custom") { + if (error) { + return ( + + ? + + ); + } + + const img = ( + {`:${shortcode}:`} setError(true)} + /> + ); + + if (!showTooltip) { + return img; + } + + return ( + + {img} + :{shortcode}: + + ); + } + + // Render unicode emoji + const emojiSpan = ( + + {value} + + ); + + if (!showTooltip) { + return emojiSpan; + } + + return ( + + {emojiSpan} + :{shortcode}: + + ); +} diff --git a/src/components/nostr/RichText/Emoji.tsx b/src/components/nostr/RichText/Emoji.tsx index 8ffceee..9b4bb95 100644 --- a/src/components/nostr/RichText/Emoji.tsx +++ b/src/components/nostr/RichText/Emoji.tsx @@ -1,4 +1,4 @@ -import { CustomEmoji } from "../CustomEmoji"; +import { Emoji as EmojiComponent } from "../Emoji"; interface EmojiNodeProps { node: { @@ -7,11 +7,16 @@ interface EmojiNodeProps { }; } +/** + * RichText emoji node renderer - renders custom emoji in parsed content + * Note: Named export "Emoji" for RichText compatibility, uses EmojiComponent internally + */ export function Emoji({ node }: EmojiNodeProps) { return ( - diff --git a/src/components/nostr/UnicodeEmoji.tsx b/src/components/nostr/UnicodeEmoji.tsx deleted file mode 100644 index 96e93ac..0000000 --- a/src/components/nostr/UnicodeEmoji.tsx +++ /dev/null @@ -1,63 +0,0 @@ -import { cn } from "@/lib/utils"; -import { Tooltip, TooltipContent, TooltipTrigger } from "../ui/tooltip"; - -export interface UnicodeEmojiProps { - /** The emoji character */ - emoji: string; - /** The shortcode for tooltip (without colons) */ - shortcode?: string; - /** Size variant - matches CustomEmoji sizes */ - size?: "xs" | "sm" | "md" | "lg"; - /** Additional class names */ - className?: string; - /** Whether to show tooltip on hover (default: true, requires shortcode) */ - showTooltip?: boolean; -} - -/** - * Text size classes that visually match CustomEmoji image sizes - * - xs: size-3.5 (14px) → text-sm (14px) - * - sm: size-4 (16px) → text-base (16px) - * - md: size-6 (24px) → text-2xl (24px) - * - lg: size-12 (48px) → text-5xl (48px) - */ -const sizeClasses = { - xs: "text-sm", - sm: "text-base", - md: "text-2xl", - lg: "text-5xl", -}; - -/** - * Renders a unicode emoji with consistent sizing - * Size variants match CustomEmoji for visual consistency - */ -export function UnicodeEmoji({ - emoji, - shortcode, - size = "md", - className, - showTooltip = true, -}: UnicodeEmojiProps) { - const emojiSpan = ( - - {emoji} - - ); - - // Only show tooltip if shortcode is provided and showTooltip is true - if (!showTooltip || !shortcode) { - return emojiSpan; - } - - return ( - - {emojiSpan} - :{shortcode}: - - ); -} diff --git a/src/components/nostr/compact/ReactionCompactPreview.tsx b/src/components/nostr/compact/ReactionCompactPreview.tsx index 8d5f788..a85febb 100644 --- a/src/components/nostr/compact/ReactionCompactPreview.tsx +++ b/src/components/nostr/compact/ReactionCompactPreview.tsx @@ -5,8 +5,7 @@ import { useNostrEvent } from "@/hooks/useNostrEvent"; import { UserName } from "../UserName"; import { RichText } from "../RichText"; import { EMOJI_SHORTCODE_REGEX } from "@/lib/emoji-helpers"; -import { CustomEmoji } from "../CustomEmoji"; -import { UnicodeEmoji } from "../UnicodeEmoji"; +import { Emoji } from "../Emoji"; /** * Compact preview for Kind 7 (Reaction) @@ -102,16 +101,25 @@ export function ReactionCompactPreview({ event }: { event: NostrEvent }) { case "😊": return ; default: - return ; + return ( + + ); } }; return ( {parsedReaction.type === "custom" ? ( - diff --git a/src/components/nostr/kinds/EmojiListRenderer.tsx b/src/components/nostr/kinds/EmojiListRenderer.tsx index 71838b3..9c54a21 100644 --- a/src/components/nostr/kinds/EmojiListRenderer.tsx +++ b/src/components/nostr/kinds/EmojiListRenderer.tsx @@ -1,7 +1,7 @@ import { Smile } from "lucide-react"; import { getAddressPointerFromATag } from "applesauce-core/helpers"; import { getEmojiTags } from "@/lib/emoji-helpers"; -import { CustomEmoji } from "@/components/nostr/CustomEmoji"; +import { Emoji } from "@/components/nostr/Emoji"; import { BaseEventProps, BaseEventContainer, @@ -64,10 +64,11 @@ export function EmojiListRenderer({ event }: BaseEventProps) { {emojis.length > 0 && (
{previewEmojis.map((emoji) => ( - ))} @@ -116,9 +117,10 @@ export function EmojiListDetailRenderer({ event }: { event: NostrEvent }) { key={emoji.shortcode} className="flex items-center gap-1.5 px-2 py-1 bg-muted rounded" > - diff --git a/src/components/nostr/kinds/EmojiSetDetailRenderer.tsx b/src/components/nostr/kinds/EmojiSetDetailRenderer.tsx index 4bb80f0..ecb9204 100644 --- a/src/components/nostr/kinds/EmojiSetDetailRenderer.tsx +++ b/src/components/nostr/kinds/EmojiSetDetailRenderer.tsx @@ -1,6 +1,6 @@ import { getTagValue } from "applesauce-core/helpers"; import { getEmojiTags } from "@/lib/emoji-helpers"; -import { CustomEmoji } from "@/components/nostr/CustomEmoji"; +import { Emoji } from "@/components/nostr/Emoji"; import { NostrEvent } from "@/types/nostr"; /** @@ -31,9 +31,10 @@ export function EmojiSetDetailRenderer({ event }: { event: NostrEvent }) { className="flex flex-col items-center gap-2 p-3 rounded-lg bg-muted/30" title={`:${emoji.shortcode}:`} > -
diff --git a/src/components/nostr/kinds/EmojiSetRenderer.tsx b/src/components/nostr/kinds/EmojiSetRenderer.tsx index e77bece..7522b5b 100644 --- a/src/components/nostr/kinds/EmojiSetRenderer.tsx +++ b/src/components/nostr/kinds/EmojiSetRenderer.tsx @@ -1,6 +1,6 @@ import { getTagValue } from "applesauce-core/helpers"; import { getEmojiTags } from "@/lib/emoji-helpers"; -import { CustomEmoji } from "@/components/nostr/CustomEmoji"; +import { Emoji } from "@/components/nostr/Emoji"; import { BaseEventProps, BaseEventContainer, @@ -36,10 +36,11 @@ export function EmojiSetRenderer({ event }: BaseEventProps) { ) : (
{previewEmojis.map((emoji) => ( - ))} diff --git a/src/components/nostr/kinds/ReactionRenderer.tsx b/src/components/nostr/kinds/ReactionRenderer.tsx index b78a1d6..de8b815 100644 --- a/src/components/nostr/kinds/ReactionRenderer.tsx +++ b/src/components/nostr/kinds/ReactionRenderer.tsx @@ -7,8 +7,7 @@ import { KindRenderer } from "./index"; import { EventCardSkeleton } from "@/components/ui/skeleton"; import { parseReplaceableAddress } from "applesauce-core/helpers/pointers"; import { EMOJI_SHORTCODE_REGEX } from "@/lib/emoji-helpers"; -import { CustomEmoji } from "../CustomEmoji"; -import { UnicodeEmoji } from "../UnicodeEmoji"; +import { Emoji } from "../Emoji"; /** * Renderer for Kind 7 - Reactions @@ -102,7 +101,15 @@ export function Kind7Renderer({ event }: BaseEventProps) { case "😊": return ; default: - return ; + return ( + + ); } }; @@ -112,9 +119,10 @@ export function Kind7Renderer({ event }: BaseEventProps) { {/* Reaction indicator */}
{parsedReaction.type === "custom" ? ( - ) : (