- {groupIcon ? (
-

{
- e.currentTarget.style.display = "none";
- }}
- />
- ) : (
-
-
-
+
+
+
+ {groupName}
+
+ {group.lastMessage && (
+
+
+
)}
-
-
{groupName}
- {group.lastMessageTimestamp && (
-
-
-
- )}
+ {/* Last message preview */}
+ {lastMessageAuthor && lastMessageContent && (
+
+
+ :{" "}
+
+
+
+
+ )}
+
+ );
+});
+
+/**
+ * ResizableDivider - Draggable divider for resizing panels
+ */
+function ResizableDivider({
+ onResize,
+}: {
+ onResize: (deltaX: number) => void;
+}) {
+ const [isDragging, setIsDragging] = useState(false);
+
+ const handleMouseDown = useCallback(
+ (e: React.MouseEvent) => {
+ e.preventDefault();
+ setIsDragging(true);
+
+ const startX = e.clientX;
+
+ const handleMouseMove = (moveEvent: MouseEvent) => {
+ const deltaX = moveEvent.clientX - startX;
+ onResize(deltaX);
+ };
+
+ const handleMouseUp = () => {
+ setIsDragging(false);
+ document.removeEventListener("mousemove", handleMouseMove);
+ document.removeEventListener("mouseup", handleMouseUp);
+ };
+
+ document.addEventListener("mousemove", handleMouseMove);
+ document.addEventListener("mouseup", handleMouseUp);
+ },
+ [onResize],
+ );
+
+ return (
+
);
}
+/**
+ * MemoizedChatViewer - Memoized chat viewer to prevent unnecessary re-renders
+ */
+const MemoizedChatViewer = memo(
+ function MemoizedChatViewer({
+ groupId,
+ relayUrl,
+ }: {
+ groupId: string;
+ relayUrl: string;
+ }) {
+ return (
+
+ );
+ },
+ // Custom comparison: only re-render if group actually changed
+ (prev, next) =>
+ prev.groupId === next.groupId && prev.relayUrl === next.relayUrl,
+);
+
/**
* GroupListViewer - Multi-room chat interface
*
@@ -108,6 +186,18 @@ export function GroupListViewer() {
relayUrl: string;
} | null>(null);
+ // State for sidebar width
+ const [sidebarWidth, setSidebarWidth] = useState(280);
+
+ // Handle resize
+ const handleResize = useCallback((deltaX: number) => {
+ setSidebarWidth((prev) => {
+ const newWidth = prev + deltaX;
+ // Clamp between 200px and 500px
+ return Math.max(200, Math.min(500, newWidth));
+ });
+ }, []);
+
// Load user's kind 10009 (group list) event
const groupListEvent = use$(
() =>
@@ -254,14 +344,14 @@ export function GroupListViewer() {
)
.pipe(
map((events) => {
- // Create a map of groupId -> latest message timestamp
- const recencyMap = new Map
();
+ // Create a map of groupId -> latest message
+ const messageMap = new Map();
for (const evt of events) {
const hTag = evt.tags.find((t) => t[0] === "h");
if (hTag && hTag[1]) {
- const existing = recencyMap.get(hTag[1]);
- if (!existing || evt.created_at > existing) {
- recencyMap.set(hTag[1], evt.created_at);
+ const existing = messageMap.get(hTag[1]);
+ if (!existing || evt.created_at > existing.created_at) {
+ messageMap.set(hTag[1], evt);
}
}
}
@@ -271,13 +361,13 @@ export function GroupListViewer() {
groupId: g.groupId,
relayUrl: g.relayUrl,
metadata: groupMetadataMap?.get(g.groupId),
- lastMessageTimestamp: recencyMap.get(g.groupId),
+ lastMessage: messageMap.get(g.groupId),
}));
// Sort by recency (most recent first)
groupsWithInfo.sort((a, b) => {
- const aTime = a.lastMessageTimestamp || 0;
- const bTime = b.lastMessageTimestamp || 0;
+ const aTime = a.lastMessage?.created_at || 0;
+ const bTime = b.lastMessage?.created_at || 0;
return bTime - aTime;
});
@@ -286,17 +376,6 @@ export function GroupListViewer() {
);
}, [groups, groupMetadataMap]);
- // Auto-select first group if none selected
- useEffect(() => {
- if (!selectedGroup && groupsWithRecency && groupsWithRecency.length > 0) {
- const first = groupsWithRecency[0];
- setSelectedGroup({
- groupId: first.groupId,
- relayUrl: first.relayUrl,
- });
- }
- }, [selectedGroup, groupsWithRecency]);
-
if (!activePubkey) {
return (
@@ -334,12 +413,14 @@ export function GroupListViewer() {
return (
{/* Left panel: Group list */}
-
-
-
Groups
-
- {groupsWithRecency.length}{" "}
- {groupsWithRecency.length === 1 ? "group" : "groups"}
+
+ {/* Header matching ChatViewer style */}
+
+
+
Groups
+
+ {groupsWithRecency.length}
+
@@ -362,18 +443,15 @@ export function GroupListViewer() {
+ {/* Resizable divider */}
+
+
{/* Right panel: Chat view */}
-
+
{selectedGroup ? (
-
) : (