chore: lint/format all files (#604)
* chore: lint/format all files * Fix config sidebar button state (#602) * chore: Update deno.lock version and add Radix UI slider component (#601) * fix: improve how table addresses even/odd rows --------- Co-authored-by: philon- <philon-@users.noreply.github.com> Co-authored-by: Kamil Dzieniszewski <kamil.dzieniszewski@gmail.com>
This commit is contained in:
co-authored by
philon-
Kamil Dzieniszewski
parent
b7bdb1a502
commit
480ca46a95
@@ -11,7 +11,10 @@ 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 {
|
||||
MessageState,
|
||||
useMessageStore,
|
||||
} from "@core/stores/messageStore/index.ts";
|
||||
import { Protobuf, Types } from "@meshtastic/js";
|
||||
import { Message } from "@core/stores/messageStore/types.ts";
|
||||
// import { MessageActionsMenu } from "@components/PageComponents/Messages/MessageActionsMenu.tsx"; // Uncomment if needed later
|
||||
@@ -24,17 +27,42 @@ interface MessageStatusInfo {
|
||||
}
|
||||
|
||||
const MESSAGE_STATUS_MAP: Record<MessageState, MessageStatusInfo> = {
|
||||
[MessageState.Ack]: { displayText: "Message delivered", icon: CheckCircle2, ariaLabel: "Message delivered", iconClassName: "text-green-500" },
|
||||
[MessageState.Waiting]: { displayText: "Waiting for delivery", icon: CircleEllipsis, ariaLabel: "Sending message", iconClassName: "text-slate-400" },
|
||||
[MessageState.Failed]: { displayText: "Delivery failed", icon: AlertCircle, ariaLabel: "Message delivery failed", iconClassName: "text-red-500 dark:text-red-400" },
|
||||
[MessageState.Ack]: {
|
||||
displayText: "Message delivered",
|
||||
icon: CheckCircle2,
|
||||
ariaLabel: "Message delivered",
|
||||
iconClassName: "text-green-500",
|
||||
},
|
||||
[MessageState.Waiting]: {
|
||||
displayText: "Waiting for delivery",
|
||||
icon: CircleEllipsis,
|
||||
ariaLabel: "Sending message",
|
||||
iconClassName: "text-slate-400",
|
||||
},
|
||||
[MessageState.Failed]: {
|
||||
displayText: "Delivery failed",
|
||||
icon: AlertCircle,
|
||||
ariaLabel: "Message delivery failed",
|
||||
iconClassName: "text-red-500 dark:text-red-400",
|
||||
},
|
||||
};
|
||||
|
||||
const UNKNOWN_STATUS: MessageStatusInfo = { displayText: "Unknown state", icon: AlertCircle, ariaLabel: "Message status unknown", iconClassName: "text-red-500 dark:text-red-400" };
|
||||
const UNKNOWN_STATUS: MessageStatusInfo = {
|
||||
displayText: "Unknown state",
|
||||
icon: AlertCircle,
|
||||
ariaLabel: "Message status unknown",
|
||||
iconClassName: "text-red-500 dark:text-red-400",
|
||||
};
|
||||
|
||||
const getMessageStatusInfo = (state: MessageState): MessageStatusInfo =>
|
||||
MESSAGE_STATUS_MAP[state] ?? UNKNOWN_STATUS;
|
||||
|
||||
const StatusTooltip = ({ statusInfo, children }: { statusInfo: MessageStatusInfo; children: ReactNode }) => (
|
||||
const StatusTooltip = (
|
||||
{ statusInfo, children }: {
|
||||
statusInfo: MessageStatusInfo;
|
||||
children: ReactNode;
|
||||
},
|
||||
) => (
|
||||
<TooltipProvider delayDuration={300}>
|
||||
<Tooltip>
|
||||
<TooltipTrigger asChild>{children}</TooltipTrigger>
|
||||
@@ -52,18 +80,17 @@ interface MessageItemProps {
|
||||
|
||||
export const MessageItem = ({ message }: MessageItemProps) => {
|
||||
const { getNode } = useDevice();
|
||||
const { getMyNodeNum } = useMessageStore()
|
||||
const { getMyNodeNum } = useMessageStore();
|
||||
|
||||
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 } = useMemo(() => {
|
||||
const userIdHex = message.from.toString(16).toUpperCase().padStart(2, '0');
|
||||
const userIdHex = message.from.toString(16).toUpperCase().padStart(2, "0");
|
||||
const last4 = userIdHex.slice(-4);
|
||||
const fallbackName = `Meshtastic ${last4}`
|
||||
const fallbackName = `Meshtastic ${last4}`;
|
||||
const longName = messageUser?.user?.longName;
|
||||
const derivedShortName = messageUser?.user?.shortName || fallbackName;
|
||||
const derivedDisplayName = longName || derivedShortName;
|
||||
@@ -73,22 +100,35 @@ export const MessageItem = ({ message }: MessageItemProps) => {
|
||||
const messageStatusInfo = getMessageStatusInfo(message.state);
|
||||
const StatusIconComponent = messageStatusInfo.icon;
|
||||
|
||||
const messageDate = useMemo(() => message.date ? new Date(message.date) : null, [message.date]);
|
||||
const locale = 'en-US'; // TODO: Make dynamic via props or context
|
||||
const messageDate = useMemo(
|
||||
() => message.date ? new Date(message.date) : null,
|
||||
[message.date],
|
||||
);
|
||||
const locale = "en-US"; // TODO: Make dynamic via props or context
|
||||
|
||||
const formattedTime = useMemo(() =>
|
||||
messageDate?.toLocaleTimeString(locale, { hour: 'numeric', minute: '2-digit', hour12: true }) ?? '',
|
||||
[messageDate, locale]);
|
||||
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 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",
|
||||
@@ -97,7 +137,6 @@ export const MessageItem = ({ message }: MessageItemProps) => {
|
||||
);
|
||||
const dateTextStyle = "text-xs text-slate-500 dark:text-slate-400";
|
||||
|
||||
|
||||
return (
|
||||
<li className={messageItemWrapperClass}>
|
||||
<div className="grid grid-cols-[auto_1fr] gap-x-2">
|
||||
@@ -109,7 +148,10 @@ export const MessageItem = ({ message }: MessageItemProps) => {
|
||||
{displayName}
|
||||
</span>
|
||||
{messageDate && (
|
||||
<time dateTime={messageDate.toISOString()} className={dateTextStyle}>
|
||||
<time
|
||||
dateTime={messageDate.toISOString()}
|
||||
className={dateTextStyle}
|
||||
>
|
||||
<span aria-hidden="true">{formattedTime}</span>
|
||||
<span className="sr-only">{fullDateTime}</span>
|
||||
</time>
|
||||
@@ -118,7 +160,10 @@ export const MessageItem = ({ message }: MessageItemProps) => {
|
||||
<StatusTooltip statusInfo={messageStatusInfo}>
|
||||
<span aria-label={messageStatusInfo.ariaLabel} role="img">
|
||||
<StatusIconComponent
|
||||
className={cn("size-4 shrink-0", messageStatusInfo.iconClassName)}
|
||||
className={cn(
|
||||
"size-4 shrink-0",
|
||||
messageStatusInfo.iconClassName,
|
||||
)}
|
||||
aria-hidden="true"
|
||||
/>
|
||||
</span>
|
||||
@@ -134,9 +179,11 @@ export const MessageItem = ({ message }: MessageItemProps) => {
|
||||
</div>
|
||||
</div>
|
||||
{/* Actions Menu Placeholder */}
|
||||
{/* <div className="absolute top-1 right-1">
|
||||
{
|
||||
/* <div className="absolute top-1 right-1">
|
||||
<MessageActionsMenu onReply={() => console.log("Reply")} />
|
||||
</div> */}
|
||||
</div> */
|
||||
}
|
||||
</li>
|
||||
);
|
||||
};
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user