import { ChannelChat } from "@components/PageComponents/Messages/ChannelChat.tsx"; import { PageLayout } from "@components/PageLayout.tsx"; import { Sidebar } from "@components/Sidebar.tsx"; import { Avatar } from "@components/UI/Avatar.tsx"; import { SidebarSection } from "@components/UI/Sidebar/SidebarSection.tsx"; import { SidebarButton } from "@components/UI/Sidebar/SidebarButton.tsx"; import { useToast } from "@core/hooks/useToast.ts"; import { useDevice } from "@core/stores/deviceStore.ts"; import { Protobuf, Types } from "@meshtastic/core"; import { getChannelName } from "@pages/Channels.tsx"; import { HashIcon, LockIcon, LockOpenIcon } from "lucide-react"; import { useCallback, useDeferredValue, useMemo, useState } from "react"; import { MessageInput } from "@components/PageComponents/Messages/MessageInput.tsx"; import { cn } from "@core/utils/cn.ts"; import { MessageState, MessageType, useMessageStore, } from "@core/stores/messageStore/index.ts"; import { useSidebar } from "@core/stores/sidebarStore.tsx"; import { Input } from "@components/UI/Input.tsx"; import { randId } from "@core/utils/randId.ts"; type NodeInfoWithUnread = Protobuf.Mesh.NodeInfo & { unreadCount: number }; export const MessagesPage = () => { const { channels, getNodes, getNode, hasNodeError, unreadCounts, resetUnread, connection, } = useDevice(); const { getMyNodeNum, getMessages, setActiveChat, chatType, activeChat, setChatType, setMessageState, } = useMessageStore(); const { toast } = useToast(); const { isCollapsed } = useSidebar(); const [searchTerm, setSearchTerm] = useState(""); const deferredSearch = useDeferredValue(searchTerm); const filteredNodes = (): NodeInfoWithUnread[] => { const lowerCaseSearchTerm = deferredSearch.toLowerCase(); return getNodes((node) => { const longName = node.user?.longName?.toLowerCase() ?? ""; const shortName = node.user?.shortName?.toLowerCase() ?? ""; return longName.includes(lowerCaseSearchTerm) || shortName.includes(lowerCaseSearchTerm); }) .map((node) => ({ ...node, unreadCount: unreadCounts.get(node.num) ?? 0, })) .sort((a, b) => b.unreadCount - a.unreadCount); }; const allChannels = Array.from(channels.values()); const filteredChannels = allChannels.filter( (ch) => ch.role !== Protobuf.Channel.Channel_Role.DISABLED, ); const currentChannel = channels.get(activeChat); const otherNode = getNode(activeChat); const isDirect = chatType === MessageType.Direct; const isBroadcast = chatType === MessageType.Broadcast; const sendText = useCallback(async (message: string) => { const isDirect = chatType === MessageType.Direct; const toValue = isDirect ? activeChat : MessageType.Broadcast; const channelValue = isDirect ? Types.ChannelNumber.Primary : activeChat ?? 0; let messageId: number | undefined; try { messageId = await connection?.sendText( message, toValue, true, channelValue, ); if (messageId !== undefined) { if (chatType === MessageType.Broadcast) { setMessageState({ type: chatType, channelId: channelValue, messageId, newState: MessageState.Ack, }); } else { setMessageState({ type: chatType, nodeA: getMyNodeNum(), nodeB: activeChat, messageId, newState: MessageState.Ack, }); } } else { console.warn("sendText completed but messageId is undefined"); } // deno-lint-ignore no-explicit-any } catch (e: any) { console.error("Failed to send message:", e); // Note: messageId might be undefined here if the error occurred before it was assigned const failedId = messageId ?? randId(); if (chatType === MessageType.Broadcast) { setMessageState({ type: chatType, channelId: channelValue, messageId: failedId, newState: MessageState.Failed, }); } else { // MessageType.Direct const failedId = messageId ?? randId(); setMessageState({ type: chatType, nodeA: getMyNodeNum(), nodeB: activeChat, messageId: failedId, newState: MessageState.Failed, }); } } }, [activeChat, chatType, connection, getMyNodeNum, setMessageState]); const renderChatContent = () => { switch (chatType) { case MessageType.Broadcast: return ( ); case MessageType.Direct: return ( ); default: return (
Select a channel or node to start messaging.
); } }; const leftSidebar = useMemo(() => ( {filteredChannels?.map((channel) => ( { setChatType(MessageType.Broadcast); setActiveChat(channel.index); resetUnread(channel.index); }} > ))} ), [ filteredChannels, unreadCounts, activeChat, chatType, isCollapsed, setActiveChat, setChatType, resetUnread, ]); const rightSidebar = useMemo( () => (
{filteredNodes()?.map((node) => ( 0 ? node.unreadCount : undefined} active={activeChat === node.num && chatType === MessageType.Direct} onClick={() => { setChatType(MessageType.Direct); setActiveChat(node.num); resetUnread(node.num); }} > ))}
), [ filteredNodes, searchTerm, activeChat, chatType, setActiveChat, setChatType, resetUnread, hasNodeError, ], ); return (
{renderChatContent()}
{(isBroadcast || isDirect) ? ( ) : (
Select a chat to send a message.
)}
); }; export default MessagesPage;