From d9d54b3f74aecf4c57d31a1f2d80d9a29c8373ae Mon Sep 17 00:00:00 2001 From: Natalie Bridgers Date: Thu, 2 Apr 2026 15:35:13 -0700 Subject: [PATCH] slight little redesign --- js/components/src/components/chat/badge.tsx | 22 ++- .../src/components/chat/user-profile-card.tsx | 144 ++++++++++-------- js/components/src/icons/icon-bsky.tsx | 29 ++++ 3 files changed, 123 insertions(+), 72 deletions(-) create mode 100644 js/components/src/icons/icon-bsky.tsx diff --git a/js/components/src/components/chat/badge.tsx b/js/components/src/components/chat/badge.tsx index 3f501a28b..36bebce92 100644 --- a/js/components/src/components/chat/badge.tsx +++ b/js/components/src/components/chat/badge.tsx @@ -11,22 +11,28 @@ export const BADGE_IMAGES: Record> = { export const Badge = ({ badgeType, size = 18, + isInChat = false, }: { badgeType: string; size?: number; + isInChat?: boolean; }) => { const source = BADGE_IMAGES[badgeType]; if (!source) return null; return ( ); }; @@ -40,7 +46,7 @@ export const BadgeDisplayRow = ({ return ( <> {badges.map((badge, index) => ( - + ))} ); diff --git a/js/components/src/components/chat/user-profile-card.tsx b/js/components/src/components/chat/user-profile-card.tsx index 9fa4b3465..6174e3037 100644 --- a/js/components/src/components/chat/user-profile-card.tsx +++ b/js/components/src/components/chat/user-profile-card.tsx @@ -10,9 +10,11 @@ import { useRef, useState, } from "react"; -import { Platform, Pressable, View } from "react-native"; +import { Linking, Platform, Pressable, View } from "react-native"; import { ChatMessageViewHydrated } from "streamplace"; +import { zero } from "../.."; import { useAvatars } from "../../hooks/useAvatars"; +import IconBsky from "../../icons/icon-bsky"; import { borders, gap, @@ -29,6 +31,7 @@ import { useLivestreamStore } from "../../livestream-store"; import { useUrl } from "../../streamplace-store"; import { useTheme } from "../../ui"; import { formatHandleWithAt } from "../../utils/format-handle"; +import { Button, MenuGroup } from "../ui"; import { DropdownMenu, DropdownMenuContent, @@ -39,23 +42,21 @@ import { Badge } from "./badge"; interface BadgeMeta { label: string; - description: string; + description?: string; issuedBy?: string; } const BADGE_META: Record = { "place.stream.badge.defs#mod": { label: "Moderator", - description: "This user is a moderator.", issuedBy: "{issuer} for {streamer}", }, "place.stream.badge.defs#streamer": { label: "Streamer", - description: "This user is the streamer.", }, "place.stream.badge.defs#vip": { label: "VIP", - description: "This user is a very important person.", + description: "This user is clearly a very important person.", }, }; @@ -70,13 +71,11 @@ interface OpenCardData { interface OpenCardContextValue { openCard: OpenCardData | null; setOpenCard: (card: OpenCardData | null) => void; - closeTimeoutRef: React.MutableRefObject | null>; } const OpenCardContext = createContext({ openCard: null, setOpenCard: () => {}, - closeTimeoutRef: { current: null }, }); // All hook-derived data needed to render the card — computed outside any Modal boundary. @@ -165,14 +164,16 @@ const BadgeRow = ({ ]} > - + {meta.label} Issued by {issuerLabel} - - {meta.description} - + {meta.description && ( + + {meta.description} + + )} ); @@ -189,11 +190,11 @@ const ProfileCardContent = ({ data; return ( - <> + {profile?.banner ? ( ) : ( {profile?.avatar ? ( @@ -234,36 +240,69 @@ const ProfileCardContent = ({ /> )} - - @{author.handle} - {profile?.description ? ( - - {profile.description} - - ) : null} - - {serviceBadges.length > 0 && serviceDid ? ( + - {serviceBadges.map((badge, i) => ( - - ))} + @{author.handle} + {Platform.OS === "web" && ( + + + + )} - ) : null} - + {serviceBadges.length > 0 && serviceDid ? ( + + + {serviceBadges.map((badge, i) => ( + + ))} + + + ) : null} + + {Platform.OS !== "web" && ( + + + + )} + ); }; @@ -271,26 +310,13 @@ const ProfileCardContent = ({ const ProfileCardOverlay = ({ card, onClose, - closeTimeoutRef, }: { card: OpenCardData; onClose: () => void; - closeTimeoutRef: React.MutableRefObject | null>; }) => { const { theme } = useTheme(); const data = useProfileCardData(card.author, card.badges); - const cancelClose = useCallback(() => { - if (closeTimeoutRef.current) { - clearTimeout(closeTimeoutRef.current); - closeTimeoutRef.current = null; - } - }, [closeTimeoutRef]); - - const scheduleClose = useCallback(() => { - closeTimeoutRef.current = setTimeout(onClose, 150); - }, [closeTimeoutRef, onClose]); - const [portalContainer, setPortalContainer] = useState(null); useEffect(() => { if (typeof document !== "undefined") { @@ -330,8 +356,6 @@ const ProfileCardOverlay = ({ }} /> { const [openCard, setOpenCard] = useState(null); - const closeTimeoutRef = useRef | null>(null); - const value = useMemo( - () => ({ openCard, setOpenCard, closeTimeoutRef }), - [openCard], - ); + const value = useMemo(() => ({ openCard, setOpenCard }), [openCard]); return ( {children} {openCard && Platform.OS === "web" && ( - setOpenCard(null)} - closeTimeoutRef={closeTimeoutRef} - /> + setOpenCard(null)} /> )} ); diff --git a/js/components/src/icons/icon-bsky.tsx b/js/components/src/icons/icon-bsky.tsx new file mode 100644 index 000000000..de507f655 --- /dev/null +++ b/js/components/src/icons/icon-bsky.tsx @@ -0,0 +1,29 @@ +import Svg from "react-native-svg"; + +import { G, Path, SvgProps } from "react-native-svg"; + +export interface IconBskyProps extends SvgProps { + size?: number; + color?: string; +} + +export const IconBsky = ({ + size = 24, + color = "#0F73FF", + style, + ...props +}: IconBskyProps) => ( + + + + + +); + +export default IconBsky; -- 2.51.2