From 2597c6f2ec07e5409707791170b7924e11342b8f Mon Sep 17 00:00:00 2001 From: gosangam Date: Sun, 30 Aug 2026 02:19:31 +0530 Subject: [PATCH] feat: chat stickers --- app/(app)/chats/[id]/page.tsx | 9 ++- components/chat/chat-composer.tsx | 25 ++++++- components/chat/message-group.tsx | 40 ++++++++++-- components/chat/sticker-grid.tsx | 79 ++++++++++++++++++++++ components/chat/sticker-picker.tsx | 83 ++++++++++++++++++++++++ components/sadhna/reward-celebration.tsx | 4 +- components/sadhna/rewards-gallery.tsx | 71 +++++++++++++++++--- lib/chat.ts | 27 +++++++- lib/sadhna.ts | 12 +++- 9 files changed, 332 insertions(+), 18 deletions(-) create mode 100644 components/chat/sticker-grid.tsx create mode 100644 components/chat/sticker-picker.tsx diff --git a/app/(app)/chats/[id]/page.tsx b/app/(app)/chats/[id]/page.tsx index a6856221..9ba2d268 100644 --- a/app/(app)/chats/[id]/page.tsx +++ b/app/(app)/chats/[id]/page.tsx @@ -168,6 +168,7 @@ export default function ChatThreadPage() { replyToId?: string, previewUrl?: string, attachments?: UploadedAttachment[], + stickerKey?: string, ) => { const clientMsgId = crypto.randomUUID(); const optimistic: ChatMessage = { @@ -188,7 +189,13 @@ export default function ChatThreadPage() { setMessages((prev) => [...prev, optimistic]); chat - .send(id, body, { clientMsgId, replyToId, previewUrl, attachments }) + .send(id, body, { + clientMsgId, + replyToId, + previewUrl, + attachments, + stickerKey, + }) .then((saved) => { setMessages((prev) => { // Drop the placeholder and any realtime echo of the same message. diff --git a/components/chat/chat-composer.tsx b/components/chat/chat-composer.tsx index fa13ba39..4312816c 100644 --- a/components/chat/chat-composer.tsx +++ b/components/chat/chat-composer.tsx @@ -13,12 +13,13 @@ */ import { useCallback, useEffect, useRef, useState } from "react"; -import { Paperclip, SendHorizonal, X } from "lucide-react"; +import { Paperclip, Smile, SendHorizonal, X } from "lucide-react"; import { LinkPreviewCard } from "@/components/chat/link-preview-card"; import { ComposerAttachments, type StagedAttachment, } from "@/components/chat/composer-attachments"; +import { StickerPicker } from "@/components/chat/sticker-picker"; import { compressImage } from "@/lib/compress-image"; import { cn } from "@/lib/utils"; import { @@ -53,6 +54,8 @@ export function ChatComposer({ replyToId?: string, previewUrl?: string, attachments?: UploadedAttachment[], + /** Reward artwork sent on its own. */ + stickerKey?: string, ) => void; disabled?: boolean; placeholder?: string; @@ -67,6 +70,7 @@ export function ChatComposer({ const [staged, setStaged] = useState([]); const ref = useRef(null); const fileRef = useRef(null); + const [pickingSticker, setPickingSticker] = useState(false); const lookupSeq = useRef(0); // Grow with the content, up to a cap. @@ -253,6 +257,15 @@ export function ChatComposer({ > +