mirror of
https://github.com/purrgrammer/grimoire.git
synced 2026-04-17 02:47:18 +02:00
feat: add emoji autocompletion to zap comments and refine UI
Zap Comment Enhancements: - Replace plain Input with MentionEditor for emoji autocompletion - Add NIP-30 emoji tag support to zap requests (kind 9734) - Emoji tags are properly serialized and included in zap events - Support :emoji: syntax with custom emoji from emoji search Event Preview Refinements: - Remove Card wrapper from zapped event preview - Remove padding and borders for cleaner display - Event renders directly without container styling Implementation Details: - Add EmojiTag interface to create-zap-request.ts - Update ZapRequestParams to include emojiTags array - Extract emoji tags from MentionEditor in handleZap - Pass emoji tags through zap request creation pipeline - Add useProfileSearch and useEmojiSearch hooks to ZapWindow - Use MentionEditor ref to get serialized content with emojis All tests passing (939 passed) Build successful
This commit is contained in:
@@ -11,7 +11,7 @@
|
||||
* - Shows feed render of zapped event
|
||||
*/
|
||||
|
||||
import { useState, useMemo, useEffect } from "react";
|
||||
import { useState, useMemo, useEffect, useRef } from "react";
|
||||
import { toast } from "sonner";
|
||||
import {
|
||||
Zap,
|
||||
@@ -24,7 +24,6 @@ import {
|
||||
} from "lucide-react";
|
||||
import { Button } from "@/components/ui/button";
|
||||
import { Input } from "@/components/ui/input";
|
||||
import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card";
|
||||
import {
|
||||
Dialog,
|
||||
DialogContent,
|
||||
@@ -43,6 +42,12 @@ import { KindRenderer } from "./nostr/kinds";
|
||||
import type { EventPointer, AddressPointer } from "@/lib/open-parser";
|
||||
import { useGrimoire } from "@/core/state";
|
||||
import accountManager from "@/services/accounts";
|
||||
import {
|
||||
MentionEditor,
|
||||
type MentionEditorHandle,
|
||||
} from "./editor/MentionEditor";
|
||||
import { useEmojiSearch } from "@/hooks/useEmojiSearch";
|
||||
import { useProfileSearch } from "@/hooks/useProfileSearch";
|
||||
|
||||
export interface ZapWindowProps {
|
||||
/** Recipient pubkey (who receives the zap) */
|
||||
@@ -99,13 +104,17 @@ export function ZapWindow({
|
||||
|
||||
const [selectedAmount, setSelectedAmount] = useState<number | null>(null);
|
||||
const [customAmount, setCustomAmount] = useState("");
|
||||
const [comment, setComment] = useState("");
|
||||
const [isProcessing, setIsProcessing] = useState(false);
|
||||
const [isPaid, setIsPaid] = useState(false);
|
||||
const [qrCodeUrl, setQrCodeUrl] = useState<string>("");
|
||||
const [invoice, setInvoice] = useState<string>("");
|
||||
const [showQrDialog, setShowQrDialog] = useState(false);
|
||||
|
||||
// Editor ref and search functions
|
||||
const editorRef = useRef<MentionEditorHandle>(null);
|
||||
const { searchProfiles } = useProfileSearch();
|
||||
const { searchEmojis } = useEmojiSearch();
|
||||
|
||||
// Load custom amounts and usage stats from localStorage
|
||||
const [customAmounts, setCustomAmounts] = useState<number[]>(() => {
|
||||
const stored = localStorage.getItem(STORAGE_KEY_CUSTOM_AMOUNTS);
|
||||
@@ -235,6 +244,15 @@ export function ZapWindow({
|
||||
);
|
||||
}
|
||||
|
||||
// Get comment and emoji tags from editor
|
||||
const serialized = editorRef.current?.getSerializedContent() || {
|
||||
text: "",
|
||||
emojiTags: [],
|
||||
blobAttachments: [],
|
||||
};
|
||||
const comment = serialized.text;
|
||||
const emojiTags = serialized.emojiTags;
|
||||
|
||||
// Validate comment length if provided
|
||||
if (comment && lnurlData.commentAllowed) {
|
||||
if (comment.length > lnurlData.commentAllowed) {
|
||||
@@ -255,6 +273,7 @@ export function ZapWindow({
|
||||
comment,
|
||||
eventPointer,
|
||||
lnurl: lud16 || undefined,
|
||||
emojiTags,
|
||||
});
|
||||
|
||||
const serializedZapRequest = serializeZapRequest(zapRequest);
|
||||
@@ -331,18 +350,7 @@ export function ZapWindow({
|
||||
<div className="flex-1 overflow-y-auto">
|
||||
<div className="max-w-2xl mx-auto p-6 space-y-6">
|
||||
{/* Show event preview if zapping an event */}
|
||||
{event && (
|
||||
<Card>
|
||||
<CardHeader>
|
||||
<CardTitle className="text-sm font-medium text-muted-foreground">
|
||||
Zapping Event
|
||||
</CardTitle>
|
||||
</CardHeader>
|
||||
<CardContent>
|
||||
<KindRenderer event={event} />
|
||||
</CardContent>
|
||||
</Card>
|
||||
)}
|
||||
{event && <KindRenderer event={event} />}
|
||||
|
||||
{/* Amount Selection */}
|
||||
<div className="space-y-4">
|
||||
@@ -386,15 +394,15 @@ export function ZapWindow({
|
||||
/>
|
||||
</div>
|
||||
|
||||
{/* Comment */}
|
||||
{/* Comment with emoji support */}
|
||||
<div className="space-y-2">
|
||||
<Label>Comment (optional)</Label>
|
||||
<Input
|
||||
id="comment"
|
||||
<MentionEditor
|
||||
ref={editorRef}
|
||||
placeholder="Say something nice..."
|
||||
value={comment}
|
||||
onChange={(e) => setComment(e.target.value)}
|
||||
maxLength={200}
|
||||
searchProfiles={searchProfiles}
|
||||
searchEmojis={searchEmojis}
|
||||
className="min-h-[60px] rounded-md border border-input bg-background px-3 py-2"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -9,6 +9,11 @@ import accountManager from "@/services/accounts";
|
||||
import { relayListCache } from "@/services/relay-list-cache";
|
||||
import { AGGREGATOR_RELAYS } from "@/services/loaders";
|
||||
|
||||
export interface EmojiTag {
|
||||
shortcode: string;
|
||||
url: string;
|
||||
}
|
||||
|
||||
export interface ZapRequestParams {
|
||||
/** Recipient pubkey (who receives the zap) */
|
||||
recipientPubkey: string;
|
||||
@@ -22,6 +27,8 @@ export interface ZapRequestParams {
|
||||
relays?: string[];
|
||||
/** LNURL for the recipient */
|
||||
lnurl?: string;
|
||||
/** NIP-30 custom emoji tags */
|
||||
emojiTags?: EmojiTag[];
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -87,6 +94,13 @@ export async function createZapRequest(
|
||||
}
|
||||
}
|
||||
|
||||
// Add NIP-30 emoji tags
|
||||
if (params.emojiTags) {
|
||||
for (const emoji of params.emojiTags) {
|
||||
tags.push(["emoji", emoji.shortcode, emoji.url]);
|
||||
}
|
||||
}
|
||||
|
||||
// Create event template
|
||||
const template = {
|
||||
kind: 9734,
|
||||
|
||||
Reference in New Issue
Block a user