feat: chat stickers
This commit is contained in:
@@ -14,8 +14,15 @@ import { MessageAttachments } from "@/components/chat/message-attachments";
|
||||
import { MessageTicks } from "@/components/chat/message-ticks";
|
||||
import { tickState } from "@/lib/chat-format";
|
||||
import { HEART_EMOJI } from "@/components/chat/reaction-row";
|
||||
import { StickerGrid } from "@/components/chat/sticker-grid";
|
||||
import { cn } from "@/lib/utils";
|
||||
import type { ChatMessage, ChatPeer, Receipts, ReplyPreview } from "@/lib/chat";
|
||||
import type {
|
||||
Attachment,
|
||||
ChatMessage,
|
||||
ChatPeer,
|
||||
Receipts,
|
||||
ReplyPreview,
|
||||
} from "@/lib/chat";
|
||||
|
||||
/** Time only — the day is already established by the date separator above. */
|
||||
function clockTime(iso: string): string {
|
||||
@@ -33,6 +40,20 @@ function sharesAnEntity(m: ChatMessage): boolean {
|
||||
return (m.attachments ?? []).some((a) => a.kind === "entity");
|
||||
}
|
||||
|
||||
/** Stickers on a message, if any. */
|
||||
function stickersOn(m: ChatMessage): Attachment[] {
|
||||
return (m.attachments ?? []).filter((a) => a.kind === "sticker");
|
||||
}
|
||||
|
||||
/**
|
||||
* True when a message is nothing but stickers. Those render bare — artwork
|
||||
* inside a coloured bubble reads as a pasted image rather than a sticker.
|
||||
*/
|
||||
function isStickerOnly(m: ChatMessage): boolean {
|
||||
const atts = m.attachments ?? [];
|
||||
return !m.body && atts.length > 0 && atts.every((a) => a.kind === "sticker");
|
||||
}
|
||||
|
||||
/** Distinct emoji shown before the rest collapse into "+N". */
|
||||
const MAX_TRAIL_EMOJI = 4;
|
||||
|
||||
@@ -208,7 +229,7 @@ export function MessageGroup({
|
||||
// own border, rounding and background, so the bubble drops
|
||||
// its chrome rather than drawing a second frame around it.
|
||||
// Any caption gets the padding back on its own line below.
|
||||
sharesAnEntity(m)
|
||||
sharesAnEntity(m) || isStickerOnly(m)
|
||||
? "overflow-hidden bg-transparent"
|
||||
: cn(
|
||||
"px-3 py-2",
|
||||
@@ -227,8 +248,17 @@ export function MessageGroup({
|
||||
{/* A shared post leads and its caption follows, the way
|
||||
the post itself reads. Everything else keeps text
|
||||
first, with attachments beneath it. */}
|
||||
{sharesAnEntity(m) && (
|
||||
<MessageAttachments attachments={m.attachments} />
|
||||
{isStickerOnly(m) ? (
|
||||
<StickerGrid
|
||||
conversationId={m.conversation_id}
|
||||
messageId={m.id}
|
||||
stickers={stickersOn(m)}
|
||||
mine={mine}
|
||||
/>
|
||||
) : (
|
||||
sharesAnEntity(m) && (
|
||||
<MessageAttachments attachments={m.attachments} />
|
||||
)
|
||||
)}
|
||||
{m.body && (
|
||||
<span
|
||||
@@ -244,7 +274,7 @@ export function MessageGroup({
|
||||
<MessageText body={m.body} entities={m.entities} />
|
||||
</span>
|
||||
)}
|
||||
{!sharesAnEntity(m) && (
|
||||
{!sharesAnEntity(m) && !isStickerOnly(m) && (
|
||||
<MessageAttachments attachments={m.attachments} />
|
||||
)}
|
||||
{/* An optimistic bubble for a captionless attachment has
|
||||
|
||||
Reference in New Issue
Block a user