From 1c7b466e64cdec1b91decd374142dc23c35bfed1 Mon Sep 17 00:00:00 2001 From: Dan Ditomaso Date: Tue, 4 Feb 2025 14:46:46 -0500 Subject: [PATCH] fix: styling fixes from code review --- .../PageComponents/Messages/ChannelChat.tsx | 15 +- .../PageComponents/Messages/Message.tsx | 143 +++++++++++------- src/components/PageLayout.tsx | 2 +- 3 files changed, 95 insertions(+), 65 deletions(-) diff --git a/src/components/PageComponents/Messages/ChannelChat.tsx b/src/components/PageComponents/Messages/ChannelChat.tsx index 46423237..47c9095c 100644 --- a/src/components/PageComponents/Messages/ChannelChat.tsx +++ b/src/components/PageComponents/Messages/ChannelChat.tsx @@ -39,7 +39,6 @@ export const ChannelChat = ({ scrollContainer.scrollTop - scrollContainer.clientHeight < 100; - if (isNearBottom) { messagesEndRef.current?.scrollIntoView({ behavior: "smooth" }); } @@ -52,8 +51,8 @@ export const ChannelChat = ({ if (!messages?.length) { return ( -
-
+
+
@@ -64,9 +63,9 @@ export const ChannelChat = ({ } return ( -
-
-
+
+
+
{messages.map((message, index) => ( ))} -
+
-
+
diff --git a/src/components/PageComponents/Messages/Message.tsx b/src/components/PageComponents/Messages/Message.tsx index 3a96fd3b..66268219 100644 --- a/src/components/PageComponents/Messages/Message.tsx +++ b/src/components/PageComponents/Messages/Message.tsx @@ -4,28 +4,45 @@ import { Avatar } from "@components/UI/Avatar"; import type { Protobuf } from "@meshtastic/js"; import * as Tooltip from "@radix-ui/react-tooltip"; import { AlertCircle, CheckCircle2, CircleEllipsis } from "lucide-react"; +import type { LucideIcon } from "lucide-react"; -export interface MessageProps { +const MESSAGE_STATES = { + ACK: "ack", + WAITING: "waiting", + FAILED: "failed", +} as const; + +type MessageState = MessageWithState["state"]; + +interface MessageProps { lastMsgSameUser: boolean; message: MessageWithState; sender?: Protobuf.Mesh.NodeInfo; } interface StatusTooltipProps { - state: MessageWithState["state"]; + state: MessageState; children: React.ReactNode; } -const getStatusText = (state: MessageWithState["state"]): string => { - switch (state) { - case "ack": - return "Message delivered"; - case "waiting": - return "Waiting for delivery"; - default: - return "Delivery failed"; - } -}; +interface StatusIconProps { + state: MessageState; + className?: string; +} + +const STATUS_TEXT_MAP: Record = { + [MESSAGE_STATES.ACK]: "Message delivered", + [MESSAGE_STATES.WAITING]: "Waiting for delivery", + [MESSAGE_STATES.FAILED]: "Delivery failed", +} as const; + +const STATUS_ICON_MAP: Record = { + [MESSAGE_STATES.ACK]: CheckCircle2, + [MESSAGE_STATES.WAITING]: CircleEllipsis, + [MESSAGE_STATES.FAILED]: AlertCircle, +} as const; + +const getStatusText = (state: MessageState): string => STATUS_TEXT_MAP[state]; const StatusTooltip = ({ state, children }: StatusTooltipProps) => ( @@ -46,78 +63,92 @@ const StatusTooltip = ({ state, children }: StatusTooltipProps) => ( ); -const StatusIcon = ({ - state, - className, -}: { state: MessageWithState["state"]; className?: string }) => { +const StatusIcon = ({ state, className, ...otherProps }: StatusIconProps) => { + const isFailed = state === MESSAGE_STATES.FAILED; const iconClass = cn( className, "text-gray-500 dark:text-gray-400 w-4 h-4 flex-shrink-0", ); - const Icon = (() => { - switch (state) { - case "ack": - return CheckCircle2; - case "waiting": - return CircleEllipsis; - default: - return AlertCircle; - } - })(); + + const Icon = STATUS_ICON_MAP[state]; return ( - + ); }; -export const Message = ({ lastMsgSameUser, message, sender }: MessageProps) => { - const messageTextClass = cn( - "border-l-2 pl-4 break-words min-w-0", - message.state === "ack" - ? "text-gray-900 dark:text-white" - : "text-gray-500 dark:text-gray-400", - lastMsgSameUser - ? "border-gray-600 dark:border-gray-700" - : "border-gray-200 dark:border-gray-600", +const getMessageTextStyles = (state: MessageState) => { + const isAcknowledged = state === MESSAGE_STATES.ACK; + const isFailed = state === MESSAGE_STATES.FAILED; + const isWaiting = state === MESSAGE_STATES.WAITING; + + return cn( + "pl-2 break-words overflow-hidden", + isAcknowledged + ? "text-black dark:text-white" + : "text-black dark:text-gray-400", + isFailed && "text-red-500 dark:text-red-500", ); +}; + +const TimeDisplay = ({ date }: { date: Date }) => ( +
+ + {date.toLocaleDateString()} + + + {date.toLocaleTimeString(undefined, { + hour: "2-digit", + minute: "2-digit", + })} + +
+); + +export const Message = ({ lastMsgSameUser, message, sender }: MessageProps) => { + const messageTextClass = getMessageTextStyles(message.state); + const isFailed = message.state === MESSAGE_STATES.ACK; const baseMessageWrapper = cn( - "ml-12 flex items-start gap-2 w-full max-w-full", - lastMsgSameUser ? "mt-1" : "mt-4", + "flex items-center gap-2 w-full max-w-full pl-11", !lastMsgSameUser && "flex-wrap flex-grow", ); const containerClass = cn( - "px-4 relative", - lastMsgSameUser ? "mt-0" : "mt-2", + "w-full px-4 relative", + lastMsgSameUser ? "mt-1" : "mt-2", !lastMsgSameUser && "pt-2", ); return (
{!lastMsgSameUser && ( -
- - - {sender?.user?.longName ?? "UNK"} - - - {message.rxTime.toLocaleDateString()} - - - {message.rxTime.toLocaleTimeString(undefined, { - hour: "2-digit", - minute: "2-digit", - })} - +
+
+ + + {sender?.user?.longName ?? "UNK"} + +
+
)}
-
+
{message.data}
- +
); diff --git a/src/components/PageLayout.tsx b/src/components/PageLayout.tsx index 79495bdc..f25eecab 100644 --- a/src/components/PageLayout.tsx +++ b/src/components/PageLayout.tsx @@ -49,7 +49,7 @@ export const PageLayout = ({