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:
Dan Ditomaso
2025-05-04 15:55:04 -04:00
committed by GitHub
co-authored by philon- Kamil Dzieniszewski
parent b7bdb1a502
commit 480ca46a95
104 changed files with 3104 additions and 2527 deletions
@@ -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>
);
};
};