From 86fb3656b3e6b63b467b5e81b1821a2d034cef07 Mon Sep 17 00:00:00 2001 From: klopez4212 Date: Mon, 15 Jun 2026 12:17:15 +0100 Subject: [PATCH 1/6] Polish direct message and members modals --- .../channels/lib/dmParticipantDisplay.ts | 34 + .../channels/ui/ChannelMemberInviteCard.tsx | 109 +-- .../src/features/channels/ui/ChannelPane.tsx | 41 +- .../features/channels/ui/ChannelScreen.tsx | 2 + .../channels/ui/ChannelScreenHeader.tsx | 80 +- .../features/channels/ui/MembersSidebar.tsx | 631 +++++++++++++--- .../channels/ui/MembersSidebarMemberCard.tsx | 105 +-- .../channels/useActiveChannelHeader.ts | 54 +- .../messages/lib/useRichTextEditor.ts | 2 +- .../src/features/messages/ui/MessageRow.tsx | 4 +- .../messages/ui/MessageThreadSummaryRow.tsx | 15 +- .../features/messages/ui/MessageTimeline.tsx | 71 +- .../messages/ui/TimelineMessageList.tsx | 3 + .../src/features/sidebar/lib/channelLabels.ts | 7 +- .../src/features/sidebar/ui/AppSidebar.tsx | 40 +- .../sidebar/ui/NewDirectMessageDialog.tsx | 698 ++++++++++++++---- .../features/sidebar/ui/SidebarSection.tsx | 39 +- desktop/src/shared/styles/globals.css | 2 +- desktop/src/shared/ui/dialog.tsx | 26 +- desktop/src/shared/ui/sidebar.tsx | 4 +- desktop/tests/e2e/channels.spec.ts | 164 +++- desktop/tests/e2e/messaging.spec.ts | 11 + 22 files changed, 1638 insertions(+), 504 deletions(-) create mode 100644 desktop/src/features/channels/lib/dmParticipantDisplay.ts diff --git a/desktop/src/features/channels/lib/dmParticipantDisplay.ts b/desktop/src/features/channels/lib/dmParticipantDisplay.ts new file mode 100644 index 000000000..edbe7d582 --- /dev/null +++ b/desktop/src/features/channels/lib/dmParticipantDisplay.ts @@ -0,0 +1,34 @@ +export const DM_PARTICIPANT_PREVIEW_LIMIT = 3; + +export type DmParticipantDisplay = { + displayName: string; +}; + +export function getDmParticipantPreview(participants: readonly T[]) { + const visibleParticipants = participants.slice( + 0, + DM_PARTICIPANT_PREVIEW_LIMIT, + ); + + return { + hiddenCount: Math.max( + 0, + participants.length - DM_PARTICIPANT_PREVIEW_LIMIT, + ), + visibleParticipants, + }; +} + +export function formatDmParticipantDisplayName( + participants: readonly DmParticipantDisplay[], +) { + const { hiddenCount, visibleParticipants } = + getDmParticipantPreview(participants); + const names = visibleParticipants.map( + (participant) => participant.displayName, + ); + + return hiddenCount > 0 + ? [...names, `+${hiddenCount} more`].join(", ") + : names.join(", "); +} diff --git a/desktop/src/features/channels/ui/ChannelMemberInviteCard.tsx b/desktop/src/features/channels/ui/ChannelMemberInviteCard.tsx index 7f093294d..e31004a97 100644 --- a/desktop/src/features/channels/ui/ChannelMemberInviteCard.tsx +++ b/desktop/src/features/channels/ui/ChannelMemberInviteCard.tsx @@ -1,4 +1,4 @@ -import { ChevronDown, Search, UserPlus, X } from "lucide-react"; +import { Search, UserPlus, X } from "lucide-react"; import * as React from "react"; import { formatPubkey } from "@/features/channels/lib/memberUtils"; @@ -9,10 +9,8 @@ import type { ChannelMember, UserSearchResult, } from "@/shared/api/types"; -import { cn } from "@/shared/lib/cn"; import { Button } from "@/shared/ui/button"; import { Input } from "@/shared/ui/input"; -import { Textarea } from "@/shared/ui/textarea"; import { UserAvatar } from "@/shared/ui/UserAvatar"; function formatSearchUserName(user: UserSearchResult) { @@ -23,17 +21,6 @@ function formatSearchUserName(user: UserSearchResult) { ); } -function formatSearchUserSecondary(user: UserSearchResult) { - const displayName = user.displayName?.trim(); - const nip05Handle = user.nip05Handle?.trim(); - - if (displayName && nip05Handle) { - return nip05Handle; - } - - return formatPubkey(user.pubkey); -} - export function ChannelMemberInviteCard({ canAssignElevatedRoles, existingMembers, @@ -52,10 +39,7 @@ export function ChannelMemberInviteCard({ open: boolean; requestErrorMessage?: string | null; }) { - const [invitePubkeys, setInvitePubkeys] = React.useState(""); const [inviteQuery, setInviteQuery] = React.useState(""); - const [isDirectPubkeyEntryOpen, setIsDirectPubkeyEntryOpen] = - React.useState(false); const [selectedInvitees, setSelectedInvitees] = React.useState< UserSearchResult[] >([]); @@ -122,36 +106,17 @@ export function ChannelMemberInviteCard({ React.useEffect(() => { if (!open) { - setInvitePubkeys(""); setInviteQuery(""); - setIsDirectPubkeyEntryOpen(false); setSelectedInvitees([]); setSubmissionErrors([]); } }, [open]); - const parsedInvitePubkeys = React.useMemo( - () => - invitePubkeys - .split(/[\s,]+/) - .map((value) => value.trim()) - .filter((value) => value.length > 0), - [invitePubkeys], - ); - const inviteTargets = [ - ...new Set([ - ...selectedInvitees.map((invitee) => invitee.pubkey), - ...parsedInvitePubkeys, - ]), - ]; - const directEntryLabel = - parsedInvitePubkeys.length > 0 && !isDirectPubkeyEntryOpen - ? `Direct pubkey entry (${parsedInvitePubkeys.length} ready)` - : "Direct pubkey entry"; + const inviteTargets = selectedInvitees.map((invitee) => invitee.pubkey); return (
{ event.preventDefault(); void onSubmit({ @@ -166,13 +131,6 @@ export function ChannelMemberInviteCard({ (invitee) => !addedPubkeys.has(invitee.pubkey.toLowerCase()), ), ); - const remainingPubkeys = parsedInvitePubkeys - .filter((pubkey) => !addedPubkeys.has(pubkey.toLowerCase())) - .join("\n"); - setInvitePubkeys(remainingPubkeys); - if (remainingPubkeys.length > 0) { - setIsDirectPubkeyEntryOpen(true); - } setInviteQuery(""); setSubmissionErrors(result.errors); }); @@ -202,7 +160,7 @@ export function ChannelMemberInviteCard({ disabled={isPending} id="channel-management-search-users" onChange={(event) => setInviteQuery(event.target.value)} - placeholder="Search by name or NIP-05." + placeholder="Search people and agents" value={inviteQuery} /> @@ -265,14 +223,14 @@ export function ChannelMemberInviteCard({ displayName={formatSearchUserName(result)} size="xs" /> -
-

- {formatSearchUserName(result)} -

-

- {formatSearchUserSecondary(result)} -

-
+

+ {formatSearchUserName(result)} +

+ {result.isAgent ? ( + + agent + + ) : null} Add @@ -292,49 +250,6 @@ export function ChannelMemberInviteCard({

) : null} -
- - - {isDirectPubkeyEntryOpen ? ( -
- -

- For exact pubkeys when search is not the right fit. -

-