diff --git a/package.json b/package.json index e68b515..64c7716 100644 --- a/package.json +++ b/package.json @@ -7,6 +7,7 @@ "@emoji-mart/react": "^1.1.1", "@noble/curves": "^1.1.0", "@noble/hashes": "^1.3.1", + "@radix-ui/react-collapsible": "^1.0.3", "@radix-ui/react-dialog": "^1.0.4", "@radix-ui/react-progress": "^1.0.3", "@radix-ui/react-tabs": "^1.0.4", diff --git a/public/icons.svg b/public/icons.svg index 291ba8d..8de597e 100644 --- a/public/icons.svg +++ b/public/icons.svg @@ -73,5 +73,17 @@ fill="currentColor" /> + + + + + + + + + + + + diff --git a/src/element/Event.tsx b/src/element/Event.tsx index c9160fb..f07fae3 100644 --- a/src/element/Event.tsx +++ b/src/element/Event.tsx @@ -1,33 +1,80 @@ import "./event.css"; -import { type NostrLink, EventKind } from "@snort/system"; -import { useEvent } from "hooks/event"; -import { GOAL } from "const"; +import { + type NostrLink, + type NostrEvent as NostrEventType, + EventKind, +} from "@snort/system"; + +import { Icon } from "element/icon"; import { Goal } from "element/goal"; import { Note } from "element/note"; +import { EmojiPack } from "element/emoji-pack"; +import { Badge } from "element/badge"; +import { useEvent } from "hooks/event"; +import { GOAL, EMOJI_PACK } from "const"; interface EventProps { link: NostrLink; } -export function Event({ link }: EventProps) { - const event = useEvent(link); +export function EventIcon({ kind }: { kind: EventKind }) { + if (kind === GOAL) { + return ; + } - if (event?.kind === GOAL) { + if (kind === EMOJI_PACK) { + return ; + } + + if (kind === EventKind.Badge) { + return ; + } + + if (kind === EventKind.TextNote) { + return ; + } + + return null; +} + +export function NostrEvent({ ev }: { ev: NostrEventType }) { + if (ev?.kind === GOAL) { return (
- +
); } - if (event?.kind === EventKind.TextNote) { + if (ev?.kind === EMOJI_PACK) { return (
- + +
+ ); + } + + if (ev?.kind === EventKind.Badge) { + return ( +
+ +
+ ); + } + + if (ev?.kind === EventKind.TextNote) { + return ( +
+
); } return null; } + +export function Event({ link }: EventProps) { + const event = useEvent(link); + return event ? : null; +} diff --git a/src/element/address.tsx b/src/element/address.tsx deleted file mode 100644 index 548a7cd..0000000 --- a/src/element/address.tsx +++ /dev/null @@ -1,24 +0,0 @@ -import { type NostrLink, EventKind } from "@snort/system"; - -import { useEvent } from "hooks/event"; -import { EMOJI_PACK } from "const"; -import { EmojiPack } from "element/emoji-pack"; -import { Badge } from "element/badge"; - -interface AddressProps { - link: NostrLink; -} - -export function Address({ link }: AddressProps) { - const event = useEvent(link); - - if (event?.kind === EMOJI_PACK) { - return ; - } - - if (event?.kind === EventKind.Badge) { - return ; - } - - return null; -} diff --git a/src/element/badge.css b/src/element/badge.css index 1f5758e..d9ef50c 100644 --- a/src/element/badge.css +++ b/src/element/badge.css @@ -3,7 +3,8 @@ flex-direction: column; align-items: center; gap: 6px; - padding: 6px 0; + background: transparent; + margin: 8px 0; } .badge .badge-details { @@ -23,6 +24,7 @@ .badge .badge-description { margin: 0; color: var(--text-muted); + text-align: center; } .badge .badge-thumbnail { diff --git a/src/element/chat-message.tsx b/src/element/chat-message.tsx index 6e752f1..858d16d 100644 --- a/src/element/chat-message.tsx +++ b/src/element/chat-message.tsx @@ -14,6 +14,7 @@ import { Emoji as EmojiComponent } from "element/emoji"; import { Profile } from "./profile"; import { Text } from "element/text"; import { SendZapsDialog } from "element/send-zap"; +import { CollapsibleEvent } from "element/collapsible"; import { useLogin } from "hooks/login"; import { formatSats } from "number"; import { findTag } from "utils"; @@ -30,6 +31,10 @@ function emojifyReaction(reaction: string) { return reaction; } +const customComponents = { + Event: CollapsibleEvent, +}; + export function ChatMessage({ streamer, ev, @@ -137,7 +142,6 @@ export function ChatMessage({
setShowZapDialog(true)} > - + {(hasReactions || hasZaps) && (
{hasZaps && ( diff --git a/src/element/collapsible.css b/src/element/collapsible.css new file mode 100644 index 0000000..9266cf4 --- /dev/null +++ b/src/element/collapsible.css @@ -0,0 +1,41 @@ +.collapsible-media { + display: flex; + flex-direction: column; + gap: 12px; +} + +.collapsible-media a { + color: var(--text-link); + word-wrap: break-word; +} + +.collapsible-media img, +.collapsible-media video { + width: 100%; +} + +.url-preview { + color: var(--text-link); + cursor: zoom-in; +} + +.collapsible { + width: 100%; +} + +.collapsed-event { + display: flex; + align-items: center; + justify-content: space-between; + margin: 8px 0; +} + +.collapsed-event-header { + display: flex; + align-items: center; + gap: 8px; +} + +.collapsed-event-header svg { + color: var(--text-muted); +} diff --git a/src/element/collapsible.tsx b/src/element/collapsible.tsx new file mode 100644 index 0000000..34be76c --- /dev/null +++ b/src/element/collapsible.tsx @@ -0,0 +1,74 @@ +import "./collapsible.css"; +import type { ReactNode } from "react"; +import { useState } from "react"; + +import * as Dialog from "@radix-ui/react-dialog"; +import * as Collapsible from "@radix-ui/react-collapsible"; + +import type { NostrLink } from "@snort/system"; + +import { Mention } from "element/mention"; +import { NostrEvent, EventIcon } from "element/Event"; +import { ExternalLink } from "element/external-link"; +import { useEvent } from "hooks/event"; + +interface MediaURLProps { + url: URL; + children: ReactNode; +} + +export function MediaURL({ url, children }: MediaURLProps) { + const preview = {url.toString()}; + return ( + + {preview} + + + +
+ {url.toString()} + {children} +
+ + + +
+
+
+ ); +} + +export function CollapsibleEvent({ link }: { link: NostrLink }) { + const event = useEvent(link); + const [open, setOpen] = useState(false); + const author = event?.pubkey || link.author; + + return ( + +
+
+ {event && } + {author && } +
+ + + +
+ + {open && event && } + +
+ ); +} diff --git a/src/element/emoji-pack.css b/src/element/emoji-pack.css index 40f3dd9..e48f5de 100644 --- a/src/element/emoji-pack.css +++ b/src/element/emoji-pack.css @@ -1,18 +1,22 @@ -.emoji-pack-title { +.emoji-pack { + margin: 8px 0; +} + +.emoji-pack .emoji-pack-title { display: flex; - align-items: flex-start; + align-items: center; justify-content: space-between; } -.emoji-pack-title .name { +.emoji-pack .emoji-pack-title .name { margin: 0; } -.emoji-pack-title a { +.emoji-pack .emoji-pack-title a { font-size: 14px; } -.emoji-pack-emojis { +.emoji-pack .emoji-pack-emojis { margin-top: 12px; display: flex; flex-direction: row; @@ -20,14 +24,14 @@ gap: 4px; } -.emoji-definition { +.emoji-pack .emoji-definition { display: flex; flex-direction: column; align-items: center; flex: 1; } -.emoji-name { +.emoji-pack .emoji-name { font-size: 10px; } diff --git a/src/element/emoji-pack.tsx b/src/element/emoji-pack.tsx index 4c84aca..4213728 100644 --- a/src/element/emoji-pack.tsx +++ b/src/element/emoji-pack.tsx @@ -4,7 +4,6 @@ import { type NostrEvent } from "@snort/system"; import { useLogin } from "hooks/login"; import { toEmojiPack } from "hooks/emoji"; import AsyncButton from "element/async-button"; -import { Mention } from "element/mention"; import { findTag } from "utils"; import { USER_EMOJIS } from "const"; import { Login, System } from "index"; @@ -44,15 +43,14 @@ export function EmojiPack({ ev }: { ev: NostrEvent }) { } return ( -
+
-
-

{name}

- -
+

{name}

{login?.pubkey && ( {isUsed ? "Remove" : "Add"} diff --git a/src/element/external-link.tsx b/src/element/external-link.tsx index 51c02bc..cbcf39e 100644 --- a/src/element/external-link.tsx +++ b/src/element/external-link.tsx @@ -15,6 +15,7 @@ export function ExternalLink({ children, href }: ExternalLinkProps) { } interface ExternalIconLinkProps extends Omit { + className?: string; size?: number; } diff --git a/src/element/hypertext.tsx b/src/element/hypertext.tsx index 34293eb..d65be95 100644 --- a/src/element/hypertext.tsx +++ b/src/element/hypertext.tsx @@ -1,5 +1,6 @@ import type { ReactNode } from "react"; import { NostrLink } from "element/nostr-link"; +import { MediaURL } from "element/collapsible"; const FileExtensionRegex = /\.([\w]+)$/i; @@ -23,17 +24,23 @@ export function HyperText({ link, children }: HyperTextProps) { case "bmp": case "webp": { return ( - {url.toString()} + + {url.toString()} + ); } case "wav": case "mp3": case "ogg": { - return