mirror of
https://github.com/purrgrammer/grimoire.git
synced 2026-04-10 15:36:53 +02:00
feat: add rich text rendering for live chat messages (kind 1311)
- Created LiveChatMessageRenderer component for NIP-53 live chat messages - Displays messages with RichText component for full formatting support - Links to parent live activity event (kind 30311) with clickable header - Shows activity title or fallback to "Live chat with [host]" - Registered kind 1311 in renderer registry - Exported both human-readable name (LiveChatMessageRenderer) and kind alias (Kind1311Renderer)
This commit is contained in:
87
src/components/nostr/kinds/LiveChatMessageRenderer.tsx
Normal file
87
src/components/nostr/kinds/LiveChatMessageRenderer.tsx
Normal file
@@ -0,0 +1,87 @@
|
||||
import { useMemo } from "react";
|
||||
import { RichText } from "../RichText";
|
||||
import { BaseEventContainer, type BaseEventProps } from "./BaseEventRenderer";
|
||||
import { useNostrEvent } from "@/hooks/useNostrEvent";
|
||||
import { parseReplaceableAddress } from "applesauce-core/helpers/pointers";
|
||||
import { getDisplayName } from "@/lib/nostr-utils";
|
||||
import { useProfile } from "@/hooks/useProfile";
|
||||
import { Video } from "lucide-react";
|
||||
import { useGrimoire } from "@/core/state";
|
||||
|
||||
/**
|
||||
* Renderer for Kind 1311 - Live Chat Message (NIP-53)
|
||||
* Displays live chat messages from live streaming events with rich text formatting
|
||||
* and a link to the original live activity
|
||||
*/
|
||||
export function LiveChatMessageRenderer({ event, depth = 0 }: BaseEventProps) {
|
||||
const { addWindow } = useGrimoire();
|
||||
|
||||
// Get the 'a' tag pointing to the live activity (kind 30311)
|
||||
const aTag = event.tags.find((tag) => tag[0] === "a");
|
||||
const activityAddress = aTag?.[1]; // Format: kind:pubkey:d-tag
|
||||
|
||||
// Parse the address pointer
|
||||
const addressPointer = useMemo(
|
||||
() => (activityAddress ? parseReplaceableAddress(activityAddress) : null),
|
||||
[activityAddress],
|
||||
);
|
||||
|
||||
// Fetch the live activity event
|
||||
const liveActivity = useNostrEvent(
|
||||
addressPointer
|
||||
? {
|
||||
kind: addressPointer.kind,
|
||||
pubkey: addressPointer.pubkey,
|
||||
identifier: addressPointer.identifier || "",
|
||||
relays: [],
|
||||
}
|
||||
: undefined,
|
||||
);
|
||||
|
||||
// Get host profile for display name
|
||||
const hostProfile = useProfile(addressPointer?.pubkey);
|
||||
const hostName = hostProfile
|
||||
? getDisplayName(addressPointer!.pubkey, hostProfile)
|
||||
: addressPointer?.pubkey.slice(0, 8);
|
||||
|
||||
// Get live activity title from tags
|
||||
const activityTitle = liveActivity?.tags.find((t) => t[0] === "title")?.[1];
|
||||
|
||||
const handleActivityClick = () => {
|
||||
if (!addressPointer) return;
|
||||
addWindow(
|
||||
"open",
|
||||
{
|
||||
pointer: {
|
||||
kind: addressPointer.kind,
|
||||
pubkey: addressPointer.pubkey,
|
||||
identifier: addressPointer.identifier || "",
|
||||
},
|
||||
},
|
||||
activityTitle || "Live Activity",
|
||||
);
|
||||
};
|
||||
|
||||
return (
|
||||
<BaseEventContainer event={event}>
|
||||
{/* Link to original live activity */}
|
||||
{addressPointer && (
|
||||
<button
|
||||
onClick={handleActivityClick}
|
||||
className="flex items-center gap-1.5 text-xs text-muted-foreground hover:text-foreground transition-colors mb-1 cursor-crosshair"
|
||||
>
|
||||
<Video className="size-3" />
|
||||
<span className="truncate">
|
||||
{activityTitle || `Live chat with ${hostName}`}
|
||||
</span>
|
||||
</button>
|
||||
)}
|
||||
|
||||
{/* Message content with rich text */}
|
||||
<RichText event={event} className="text-sm" depth={depth} />
|
||||
</BaseEventContainer>
|
||||
);
|
||||
}
|
||||
|
||||
// Export with human-readable name as primary
|
||||
export { LiveChatMessageRenderer as Kind1311Renderer };
|
||||
@@ -7,6 +7,7 @@ import { Kind3DetailView } from "./ContactListRenderer";
|
||||
import { RepostRenderer } from "./RepostRenderer";
|
||||
import { Kind7Renderer } from "./ReactionRenderer";
|
||||
import { Kind9Renderer } from "./ChatMessageRenderer";
|
||||
import { LiveChatMessageRenderer } from "./LiveChatMessageRenderer";
|
||||
import { Kind20Renderer } from "./PictureRenderer";
|
||||
import { Kind21Renderer } from "./VideoRenderer";
|
||||
import { Kind22Renderer } from "./ShortVideoRenderer";
|
||||
@@ -85,6 +86,7 @@ const kindRenderers: Record<number, React.ComponentType<BaseEventProps>> = {
|
||||
1063: Kind1063Renderer, // File Metadata (NIP-94)
|
||||
1111: Kind1111Renderer, // Post (NIP-22)
|
||||
1222: VoiceMessageRenderer, // Voice Message (NIP-A0)
|
||||
1311: LiveChatMessageRenderer, // Live Chat Message (NIP-53)
|
||||
1244: VoiceMessageRenderer, // Voice Message Reply (NIP-A0)
|
||||
1337: Kind1337Renderer, // Code Snippet (NIP-C0)
|
||||
1617: PatchRenderer, // Patch (NIP-34)
|
||||
@@ -227,6 +229,10 @@ export {
|
||||
} from "./RepostRenderer";
|
||||
export { Kind7Renderer } from "./ReactionRenderer";
|
||||
export { Kind9Renderer } from "./ChatMessageRenderer";
|
||||
export {
|
||||
LiveChatMessageRenderer,
|
||||
Kind1311Renderer,
|
||||
} from "./LiveChatMessageRenderer";
|
||||
export { Kind20Renderer } from "./PictureRenderer";
|
||||
export { Kind21Renderer } from "./VideoRenderer";
|
||||
export { Kind22Renderer } from "./ShortVideoRenderer";
|
||||
|
||||
Reference in New Issue
Block a user