import { Tooltip, TooltipArrow, TooltipContent, TooltipProvider, TooltipTrigger, } from "@components/UI/Tooltip.tsx"; import { useDevice } from "@core/stores/deviceStore.ts"; import { cn } from "@core/utils/cn.ts"; import { Avatar } from "@components/UI/Avatar.tsx"; import { AlertCircle, CheckCircle2, CircleEllipsis } from "lucide-react"; import type { LucideIcon } from "lucide-react"; import { ReactNode, useMemo } from "react"; import { MessageState, useMessageStore, } from "@core/stores/messageStore/index.ts"; import { Protobuf, Types } from "@meshtastic/core"; import { Message } from "@core/stores/messageStore/types.ts"; import { useTranslation } from "react-i18next"; // import { MessageActionsMenu } from "@components/PageComponents/Messages/MessageActionsMenu.tsx"; // TODO: Uncomment when actions menu is implemented interface MessageStatusInfo { displayText: string; icon: LucideIcon; ariaLabel: string; iconClassName?: string; } const StatusTooltip = ( { statusInfo, children }: { statusInfo: MessageStatusInfo; children: ReactNode; }, ) => ( {children} {statusInfo.displayText} ); interface MessageItemProps { message: Message; } export const MessageItem = ({ message }: MessageItemProps) => { const { getNode } = useDevice(); const { getMyNodeNum } = useMessageStore(); const { t, i18n } = useTranslation("messages"); const MESSAGE_STATUS_MAP = useMemo( (): Record => ({ [MessageState.Ack]: { displayText: t("deliveryStatus.delivered.displayText"), icon: CheckCircle2, ariaLabel: t("deliveryStatus.delivered.label"), iconClassName: "text-green-500", }, [MessageState.Waiting]: { displayText: t("deliveryStatus.waiting.displayText"), icon: CircleEllipsis, ariaLabel: t("deliveryStatus.waiting.label"), iconClassName: "text-slate-400", }, [MessageState.Failed]: { displayText: t("deliveryStatus.failed.displayText"), icon: AlertCircle, ariaLabel: t("deliveryStatus.failed.label"), iconClassName: "text-red-500 dark:text-red-400", }, }), [t], ); const UNKNOWN_STATUS = useMemo((): MessageStatusInfo => ({ displayText: t("deliveryStatus.unknown.displayText"), icon: AlertCircle, ariaLabel: t("deliveryStatus.unknown.label"), iconClassName: "text-red-500 dark:text-red-400", }), [t]); const getMessageStatusInfo = useMemo( () => (state: MessageState): MessageStatusInfo => MESSAGE_STATUS_MAP[state] ?? UNKNOWN_STATUS, [MESSAGE_STATUS_MAP, UNKNOWN_STATUS], ); const messageUser: Protobuf.Mesh.NodeInfo | null | undefined = useMemo(() => { return message.from != null ? getNode(message.from) : null; }, [getNode, message.from]); const myNodeNum = useMemo(() => getMyNodeNum(), [getMyNodeNum]); const { displayName, shortName, isFavorite } = useMemo(() => { const userIdHex = message.from.toString(16).toUpperCase().padStart(2, "0"); const last4 = userIdHex.slice(-4); const fallbackName = t("fallbackName", { last4 }); const longName = messageUser?.user?.longName; const derivedShortName = messageUser?.user?.shortName || fallbackName; const derivedDisplayName = longName || derivedShortName; const isFavorite = messageUser?.num !== myNodeNum && messageUser?.isFavorite; return { displayName: derivedDisplayName, shortName: derivedShortName, isFavorite: isFavorite, }; }, [messageUser, message.from, t, myNodeNum]); const messageStatusInfo = getMessageStatusInfo(message.state); const StatusIconComponent = messageStatusInfo.icon; const messageDate = useMemo( () => message.date ? new Date(message.date) : null, [message.date], ); const locale = i18n.language; const formattedTime = useMemo( () => messageDate?.toLocaleTimeString(locale, { hour: "numeric", minute: "2-digit", hour12: true, }) ?? "", [messageDate, locale], ); const fullDateTime = useMemo( () => messageDate?.toLocaleString(locale, { dateStyle: "medium", timeStyle: "short", }) ?? "", [messageDate, locale], ); const isSender = myNodeNum !== undefined && message.from === myNodeNum; const isOnPrimaryChannel = message.channel === Types.ChannelNumber.Primary; // Use the enum const shouldShowStatusIcon = isSender && isOnPrimaryChannel; const messageItemWrapperClass = cn( "group w-full py-2 relative list-none", "rounded-md", "hover:bg-slate-300/15 dark:hover:bg-slate-600/20", "transition-colors duration-100 ease-in-out", ); const dateTextStyle = "text-xs text-slate-500 dark:text-slate-400"; return (
  • {displayName} {messageDate && ( )} {shouldShowStatusIcon && ( )}
    {message?.message && (
    {message.message}
    )}
    {/* Actions Menu Placeholder */} { /*
    console.log("Reply")} />
    */ }
  • ); };