diff --git a/src/screens/Profile/Header/ProfileHeaderStandard.tsx b/src/screens/Profile/Header/ProfileHeaderStandard.tsx --- a/src/screens/Profile/Header/ProfileHeaderStandard.tsx +++ b/src/screens/Profile/Header/ProfileHeaderStandard.tsx @@ -1,25 +1,27 @@ import {memo, useMemo, useState} from 'react' -import {View} from 'react-native' +import {Image, Pressable, View} from 'react-native' import { type AppBskyActorDefs, moderateProfile, type ModerationDecision, type ModerationOpts, type RichText as RichTextAPI, } from '@atproto/api' import {msg, Trans} from '@lingui/macro' import {useLingui} from '@lingui/react' +import {useQuery} from '@tanstack/react-query' import {useActorStatus} from '#/lib/actor-status' import {useHaptics} from '#/lib/haptics' +import {useOpenLink} from '#/lib/hooks/useOpenLink' import {sanitizeDisplayName} from '#/lib/strings/display-names' import {sanitizeHandle} from '#/lib/strings/handles' import {logger} from '#/logger' import {type Shadow, useProfileShadow} from '#/state/cache/profile-shadow' import { useProfileBlockMutationQueue, useProfileFollowMutationQueue, } from '#/state/queries/profile' -import {useRequireAuth, useSession} from '#/state/session' +import {useAgent, useRequireAuth, useSession} from '#/state/session' import {ProfileMenu} from '#/view/com/profile/ProfileMenu' import {atoms as a, platform, useBreakpoints, useTheme} from '#/alf' @@ -45,6 +47,71 @@ import {ProfileHeaderShell} from './Shell' import {AnimatedProfileHeaderSuggestedFollows} from './SuggestedFollows' +const SERVICE_FAVICONS: Record = { + 'syui.ai': require('../../../../assets/favicons/syui.ai.png'), + 'bsky.app': require('../../../../assets/favicons/bsky.app.png'), + 'atproto.com': require('../../../../assets/favicons/atproto.com.png'), +} + +function ProfileServiceLinks({ + profile, +}: { + profile: AppBskyActorDefs.ProfileViewDetailed +}) { + const t = useTheme() + const agent = useAgent() + const openLink = useOpenLink() + + const {data: services} = useQuery({ + queryKey: ['profile-services', profile.did], + queryFn: async () => { + const res = await agent.com.atproto.repo.describeRepo({ + repo: profile.did, + }) + const collections: string[] = res.data.collections || [] + const serviceSet = new Set() + for (const nsid of collections) { + const parts = nsid.split('.') + if (parts.length >= 2) { + const domain = parts.slice(0, 2).reverse().join('.') + serviceSet.add(domain) + } + } + return Array.from(serviceSet) + }, + }) + + if (!services || services.length === 0) return null + + return ( + + {services.map(service => ( + + openLink( + `https://syui.ai/@${profile.handle}/at/service/${service}`, + ) + } + style={[a.flex_row, a.align_center, a.gap_xs]}> + + + {service} + + + ))} + + ) +} + interface Props { profile: AppBskyActorDefs.ProfileViewDetailed descriptionRT: RichTextAPI | null @@ -150,6 +217,7 @@ {!isPlaceholderProfile && !isBlockedUser && ( + {descriptionRT && !moderation.ui('profileView').blur ? (