import { cn, replyAt } from "@/commons"; import { Note, User } from "@/components"; import { type LumeEvent, LumeWindow } from "@/system"; import { CaretDown } from "@phosphor-icons/react"; import { Link, useSearch } from "@tanstack/react-router"; import { Menu, MenuItem } from "@tauri-apps/api/menu"; import { writeText } from "@tauri-apps/plugin-clipboard-manager"; import { nip19 } from "nostr-tools"; import { type ReactNode, memo, useCallback, useMemo } from "react"; import reactStringReplace from "react-string-replace"; import { Hashtag } from "./note/mentions/hashtag"; import { MentionUser } from "./note/mentions/user"; export const ReplyNote = memo(function ReplyNote({ event, className, }: { event: LumeEvent; className?: string; }) { const search = useSearch({ strict: false }); const showContextMenu = useCallback(async (e: React.MouseEvent) => { e.preventDefault(); const menuItems = await Promise.all([ MenuItem.new({ text: "View Profile", action: () => LumeWindow.openProfile(event.pubkey), }), MenuItem.new({ text: "Copy Public Key", action: async () => { const pubkey = await event.pubkeyAsBech32(); await writeText(pubkey); }, }), ]); const menu = await Menu.new({ items: menuItems, }); await menu.popup().catch((e) => console.error(e)); }, []); return (
{replyAt(event.created_at)}
{event.replies?.length ? (
{event.replies.slice(0, 2).map((reply) => ( ))} {event.replies.length > 2 ? (
All {event.replies.length} replies
) : null}
) : null}
); }); function ChildReply({ event }: { event: LumeEvent }) { const search = useSearch({ strict: false }); const showContextMenu = useCallback(async (e: React.MouseEvent) => { e.preventDefault(); const menuItems = await Promise.all([ MenuItem.new({ text: "View Profile", action: () => LumeWindow.openProfile(event.pubkey), }), MenuItem.new({ text: "Copy Public Key", action: async () => { const pubkey = await event.pubkeyAsBech32(); await writeText(pubkey); }, }), ]); const menu = await Menu.new({ items: menuItems, }); await menu.popup().catch((e) => console.error(e)); }, []); return (
{replyAt(event.created_at)}
{event.replies?.length ? (
{event.replies.slice(0, 2).map((reply) => ( ))} {event.replies.length > 2 ? (
All {event.replies.length} replies
) : null}
) : null}
); } function Content({ text, className }: { text: string; className?: string }) { const content = useMemo(() => { let replacedText: ReactNode[] | string = text.trim(); const nostr = replacedText .split(/\s+/) .filter((w) => w.startsWith("nostr:")); replacedText = reactStringReplace(text, /(https?:\/\/\S+)/g, (match, i) => ( {match} )); replacedText = reactStringReplace(replacedText, /#(\w+)/g, (match, i) => ( )); for (const word of nostr) { const bech32 = word.replace("nostr:", ""); const data = nip19.decode(bech32); switch (data.type) { case "npub": replacedText = reactStringReplace(replacedText, word, (match, i) => ( )); break; case "nprofile": replacedText = reactStringReplace(replacedText, word, (match, i) => ( )); break; default: replacedText = reactStringReplace(replacedText, word, (match, i) => ( {match} )); break; } } return replacedText; }, [text]); return
{content}
; }