"use client"; import { useEffect, useRef, useState } from "react"; import clsx from "clsx"; import { useFeatureFlags, useSdk, type Message, type RichBlock, } from "@lynkd/messaging-inbox-sdk"; import { useUi } from "@/lib/ui-state"; import { Avatar } from "@/components/ui/primitives"; import { RichText } from "@/lib/rich-text"; import { clockTime, relativeTime } from "@/lib/format"; import { EmojiPicker } from "./EmojiPicker"; import { LanguageMenu } from "./LanguageMenu"; import { SmilePlus, MessageSquare, Pin, Bookmark, MoreHorizontal, FileText, ImageIcon, Clock, Link2, Copy, Forward, Trash2, Pencil, Eye, Download, Sparkles, Languages, RefreshCw, } from "lucide-react"; export function MessageItem({ message, grouped, highlighted, autoTranslateTo, onReact, onOpenThread, onPin, onSave, onEdit, onDelete, }: { message: Message; grouped?: boolean; highlighted?: boolean; autoTranslateTo?: string | null; onReact?: (emoji: string) => void; onOpenThread?: () => void; onPin?: () => void; onSave?: () => void; onEdit?: (body: string) => void; onDelete?: () => void; }) { const { userById, me, client } = useSdk(); const { openProfile, toast, openAi } = useUi(); const flags = useFeatureFlags(); const author = userById(message.authorId); const [hover, setHover] = useState(false); const [emojiOpen, setEmojiOpen] = useState(false); const [menuOpen, setMenuOpen] = useState(false); const [langOpen, setLangOpen] = useState(false); const [editing, setEditing] = useState(false); const [draft, setDraft] = useState(message.body); const [translated, setTranslated] = useState<{ lang: string; text: string } | null>(null); const [translating, setTranslating] = useState(false); const mine = message.authorId === me?.id; const toolbarVisible = hover || emojiOpen || menuOpen; const copy = async (text: string, label: string) => { try { await navigator.clipboard?.writeText(text); toast(`${label} copied to clipboard`, "success"); } catch { toast("Couldn't access the clipboard", "danger"); } }; const saveEdit = () => { const v = draft.trim(); if (v && v !== message.body) onEdit?.(v); setEditing(false); }; const doTranslate = async (lang: string) => { setLangOpen(false); setTranslating(true); try { const res = await client.translate(message.body, lang); if (res.ok && res.text) setTranslated({ lang, text: res.text }); else toast(res.error || "Translation failed", "danger"); } catch (e) { toast(`Translation failed: ${(e as Error).message}`, "danger"); } finally { setTranslating(false); } }; // whole-conversation translate: react to the channel-level language toggle useEffect(() => { if (autoTranslateTo) void doTranslate(autoTranslateTo); else if (autoTranslateTo === null) setTranslated(null); // eslint-disable-next-line react-hooks/exhaustive-deps }, [autoTranslateTo]); if (!author) return null; return (
setHover(true)} onMouseLeave={() => setHover(false)} >
{!grouped ? ( ) : ( {clockTime(message.ts)} )}
{!grouped && (
{author.isBot && app} {clockTime(message.ts)} {message.scheduledFor && ( scheduled · {relativeTime(message.scheduledFor)} )} {message.isPinned && }
)} {editing ? (