From 91a6df25a6e38b7f841865eb7710d3b0ce855d82 Mon Sep 17 00:00:00 2001 From: Bryant Kelley Date: Thu, 4 Sep 2025 15:58:11 -0700 Subject: [PATCH] Update styling for region selection and chat box --- src/components/ChatBox.tsx | 227 ++++++++++++++---------------- src/components/RegionSelector.tsx | 22 ++- 2 files changed, 116 insertions(+), 133 deletions(-) diff --git a/src/components/ChatBox.tsx b/src/components/ChatBox.tsx index d9b99a9..36d15e3 100644 --- a/src/components/ChatBox.tsx +++ b/src/components/ChatBox.tsx @@ -19,7 +19,7 @@ function formatHex(hex: string): string { function formatLocalTime(utcString: string): string { // Parse as UTC and display in local time - const utcDate = new Date(utcString + (utcString.endsWith('Z') ? '' : 'Z')); + const utcDate = new Date(utcString + (utcString.endsWith("Z") ? "" : "Z")); return utcDate.toLocaleString(); } @@ -35,18 +35,22 @@ interface TabItem { isAllMessages?: boolean; } -export default function ChatBox({ showAllMessagesTab = false, className = "", startExpanded = false }: ChatBoxProps) { +export default function ChatBox({ + showAllMessagesTab = false, + className = "", + startExpanded = false, +}: ChatBoxProps) { const { config } = useConfig(); const meshcoreKeys: TabItem[] = [ { channelName: "Public", privateKey: "izOH6cXN6mrJ5e26oRXNcg==" }, - ...(config?.meshcoreKeys || []) + ...(config?.meshcoreKeys || []), ]; - + // Add "All Messages" tab if requested - const allTabs: TabItem[] = showAllMessagesTab + const allTabs: TabItem[] = showAllMessagesTab ? [{ channelName: "All Messages", privateKey: "", isAllMessages: true }, ...meshcoreKeys] : meshcoreKeys; - + const [selectedTab, setSelectedTab] = useState(showAllMessagesTab ? 1 : 0); const [minimized, setMinimized] = useState(!startExpanded); // Use startExpanded as default for minimized state const [messages, setMessages] = useState([]); @@ -55,54 +59,61 @@ export default function ChatBox({ showAllMessagesTab = false, className = "", st const [lastBefore, setLastBefore] = useState(undefined); const selectedKey = allTabs[selectedTab]; - const channelId = selectedKey.isAllMessages ? undefined : getChannelIdFromKey(selectedKey.privateKey).toUpperCase(); + const channelId = selectedKey.isAllMessages + ? undefined + : getChannelIdFromKey(selectedKey.privateKey).toUpperCase(); // Only show tabs if more than one channel (or if we have all messages tab) const showTabs = allTabs.length > 1; - const fetchMessages = useCallback(async (before?: string, replace = false, after?: string) => { - if (!config?.selectedRegion) return; - - setLoading(true); - try { - let url = `/api/chat?limit=${PAGE_SIZE}®ion=${encodeURIComponent(config.selectedRegion!)}`; - if (channelId) url += `&channel_id=${channelId}`; - - if (after) { - // Fetch newer messages using the after parameter - url += `&after=${encodeURIComponent(after)}`; - } else if (before) { - // Fetch older messages using before parameter - url += `&before=${encodeURIComponent(before)}`; - } - - const res = await fetch(buildApiUrl(url)); - const data = await res.json(); - if (Array.isArray(data)) { + const fetchMessages = useCallback( + async (before?: string, replace = false, after?: string) => { + if (!config?.selectedRegion) return; + + setLoading(true); + try { + let url = `/api/chat?limit=${PAGE_SIZE}®ion=${encodeURIComponent( + config.selectedRegion! + )}`; + if (channelId) url += `&channel_id=${channelId}`; + if (after) { - // Add newer messages to the beginning (most recent first) - if (data.length > 0) { - setMessages((prev) => [...data, ...prev]); + // Fetch newer messages using the after parameter + url += `&after=${encodeURIComponent(after)}`; + } else if (before) { + // Fetch older messages using before parameter + url += `&before=${encodeURIComponent(before)}`; + } + + const res = await fetch(buildApiUrl(url)); + const data = await res.json(); + if (Array.isArray(data)) { + if (after) { + // Add newer messages to the beginning (most recent first) + if (data.length > 0) { + setMessages((prev) => [...data, ...prev]); + } + } else { + setMessages((prev) => (replace ? data : [...prev, ...data])); + setHasMore(data.length === PAGE_SIZE); + if (data.length > 0) { + setLastBefore(data[data.length - 1].ingest_timestamp); + } } } else { - setMessages((prev) => replace ? data : [...prev, ...data]); - setHasMore(data.length === PAGE_SIZE); - if (data.length > 0) { - setLastBefore(data[data.length - 1].ingest_timestamp); + // Only set hasMore to false if this is not an auto-refresh request + if (!after) { + setHasMore(false); } } - } else { - // Only set hasMore to false if this is not an auto-refresh request - if (!after) { - setHasMore(false); - } + } catch (error) { + console.error("Load failed:", error); + } finally { + setLoading(false); } - } catch (error) { - console.error('Load failed:', error); - } finally { - setLoading(false); - } - }, [channelId, config.selectedRegion]); + }, + [channelId, config.selectedRegion] + ); useEffect(() => { if (!minimized) { @@ -122,7 +133,7 @@ export default function ChatBox({ showAllMessagesTab = false, className = "", st const mostRecentTimestamp = messages[0].ingest_timestamp; fetchMessages(undefined, false, mostRecentTimestamp); }, 5000); - + return () => clearInterval(interval); } }, [minimized, channelId, messages, fetchMessages]); @@ -142,7 +153,7 @@ export default function ChatBox({ showAllMessagesTab = false, className = "", st const LoadMoreButton = () => ( ); - // If no region is selected, show the region selector - if (!config?.selectedRegion) { - return ( -
-
- MeshCore Chat - {!startExpanded && ( - - )} -
- - {!minimized && ( -
- { - setMessages([]); - setHasMore(true); - setLastBefore(undefined); - fetchMessages(undefined, true); - }} - className="w-full" - /> -
- )} -
- ); - } - return (
-
+
- MeshCore Chat - + + MeshCore Chat + + {getRegionConfig(config.selectedRegion!)?.friendlyName || config.selectedRegion}
- {(!minimized) && ( + {!minimized && config?.selectedRegion && ( setMinimized((m) => !m)} aria-label={minimized ? "Maximize MeshCore Chat" : "Minimize MeshCore Chat"} > - {minimized ? ( - - ) : ( - - )} + {minimized ? : } )}
- - {(!minimized) && ( + + {!minimized && config?.selectedRegion && ( <> {showTabs && ( -
+
{allTabs.map((key, idx) => (
)} - -
-
+ +
+
{messages.length === 0 && !loading && ( -
No chat messages found.
- )} - {hasMore && !startExpanded && ( - +
+ No chat messages found. +
)} + {hasMore && !startExpanded && } {(startExpanded ? messages : messages.toReversed()).map((msg, i) => ( - ))} - {hasMore && startExpanded && ( - - )} + {hasMore && startExpanded && }
)} + {!minimized && !config?.selectedRegion && ( +
+ { + setMessages([]); + setHasMore(true); + setLastBefore(undefined); + fetchMessages(undefined, true); + }} + className="w-full" + /> +
+ )}
); -} \ No newline at end of file +} diff --git a/src/components/RegionSelector.tsx b/src/components/RegionSelector.tsx index 29efb30..e7293f3 100644 --- a/src/components/RegionSelector.tsx +++ b/src/components/RegionSelector.tsx @@ -19,16 +19,14 @@ export default function RegionSelector({ onRegionSelected, className = "" }: Reg }; return ( -
+

Select a Chat Region

-

- Choose a region to filter chat messages -

+

Choose a region to filter chat messages

- +
{regions.map(({ name, friendlyName }) => ( ))}
- +

You can change this selection later in the Settings menu @@ -56,4 +53,3 @@ export default function RegionSelector({ onRegionSelected, className = "" }: Reg

); } -