diff --git a/src/webapp/features/cards/components/urlCardContent/SembleProfileCardContent.tsx b/src/webapp/features/cards/components/urlCardContent/SembleProfileCardContent.tsx new file mode 100644 index 00000000..4e5152e3 --- /dev/null +++ b/src/webapp/features/cards/components/urlCardContent/SembleProfileCardContent.tsx @@ -0,0 +1,46 @@ +'use client'; + +import useProfile from '@/features/profile/lib/queries/useProfile'; +import { Avatar, Badge, Card, Group, Stack, Text } from '@mantine/core'; +import { sanitizeText } from '@/lib/utils/text'; +import RichTextRenderer from '@/components/contentDisplay/richTextRenderer/RichTextRenderer'; + +interface Props { + handle: string; +} + +export default function SembleProfileCardContent(props: Props) { + const { data: profile } = useProfile({ didOrHandle: props.handle }); + + return ( + + + + + + + + {sanitizeText(profile.name) || profile.handle} + + + @{profile.handle} + + + + + + {profile.followsYou && ( + + Follows you + + )} + + + {profile.description && } + + + ); +} diff --git a/src/webapp/features/cards/components/urlCardContent/UrlCardContent.tsx b/src/webapp/features/cards/components/urlCardContent/UrlCardContent.tsx index db5feab2..021653d9 100644 --- a/src/webapp/features/cards/components/urlCardContent/UrlCardContent.tsx +++ b/src/webapp/features/cards/components/urlCardContent/UrlCardContent.tsx @@ -3,6 +3,7 @@ import { detectUrlPlatform, SupportedPlatform } from '@/lib/utils/link'; import { UrlCard } from '@semble/types'; import SembleCollectionCardContent from './SembleCollectionCardContent'; +import SembleProfileCardContent from './SembleProfileCardContent'; import LinkCardContent from './LinkCardContent'; import BlueskyPost from '@/features/platforms/bluesky/components/blueskyPost/BlueskyPost'; import { Suspense } from 'react'; @@ -53,6 +54,33 @@ export default function UrlCardContent(props: Props) { ); } + if (platform.type === SupportedPlatform.SEMBLE_PROFILE) { + if (!platform.handle) { + return ( + + ); + } + return ( + + } + > + }> + + + + ); + } + if ( platform.type === SupportedPlatform.BLUESKY_POST || platform.type === SupportedPlatform.BLACKSKY_POST diff --git a/src/webapp/features/profile/components/profileCard/ProfileCard.tsx b/src/webapp/features/profile/components/profileCard/ProfileCard.tsx index 72621fe1..073626d7 100644 --- a/src/webapp/features/profile/components/profileCard/ProfileCard.tsx +++ b/src/webapp/features/profile/components/profileCard/ProfileCard.tsx @@ -18,7 +18,7 @@ export default function ProfileCard(props: Props) { href={`/profile/${props.profile.handle}`} h={'100%'} > - + - + diff --git a/src/webapp/lib/utils/link.ts b/src/webapp/lib/utils/link.ts index 54db07d6..e1c90866 100644 --- a/src/webapp/lib/utils/link.ts +++ b/src/webapp/lib/utils/link.ts @@ -61,8 +61,7 @@ export const isProfilePage = (url: string) => { if (!isSembleUrl(parsedUrl.href)) return false; // matches: // /profile/:handle - // /profile/:handle/:subroute - const pattern = /^\/profile\/[^/]+(?:\/[^/]+)?\/?$/; + const pattern = /^\/profile/; return pattern.test(parsedUrl.pathname); } catch { // invalid URL @@ -74,6 +73,7 @@ export enum SupportedPlatform { BLUESKY_POST = 'bluesky post', BLACKSKY_POST = 'blacksky post', SEMBLE_COLLECTION = 'semble collection', + SEMBLE_PROFILE = 'semble profile', SPOTIFY = 'spotify', PLYRFM_TRACK = 'plyr.fm', YOUTUBE_VIDEO = 'youtube video', @@ -90,7 +90,15 @@ type PlatformData = rkey: string; } | { - type: Exclude; + type: SupportedPlatform.SEMBLE_PROFILE; + url: string; + handle: string; + } + | { + type: Exclude< + SupportedPlatform, + SupportedPlatform.SEMBLE_COLLECTION | SupportedPlatform.SEMBLE_PROFILE + >; url: string; }; @@ -113,6 +121,18 @@ export const detectUrlPlatform = (url: string): PlatformData => { } } + if (isProfilePage(url)) { + try { + const parsedUrl = new URL(url, window.location.origin); + const pathParts = parsedUrl.pathname.split('/').filter(Boolean); + // expected format: profile/:handle or profile/:handle/:subroute + const handle = pathParts[1]; + return { type: SupportedPlatform.SEMBLE_PROFILE, url, handle }; + } catch { + return { type: SupportedPlatform.DEFAULT, url }; + } + } + try { const parsedUrl = new URL(url);