--- a/src/screens/Profile/Header/ProfileHeaderStandard.tsx +++ b/src/screens/Profile/Header/ProfileHeaderStandard.tsx @@ -1,5 +1,5 @@ import {memo, useMemo, useState} from 'react' -import {View} from 'react-native' +import {Image, Pressable, View} from 'react-native' import { type AppBskyActorDefs, moderateProfile, @@ -9,9 +9,12 @@ } 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 {BSKY_SERVICE} from '#/lib/constants' 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' @@ -45,6 +48,84 @@ import {ProfileHeaderMetrics} from './Metrics' import {ProfileHeaderShell} from './Shell' import {ProfileHeaderSuggestedFollows} 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 openLink = useOpenLink() + + const {data: services} = useQuery({ + queryKey: ['profile-services', profile.did], + queryFn: async () => { + const res = await fetch( + `${BSKY_SERVICE}/xrpc/com.atproto.repo.describeRepo?repo=${encodeURIComponent(profile.did)}`, + ) + if (!res.ok) throw new Error('failed') + const data = await res.json() + const collections: string[] = 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://at.syu.is/@${profile.handle}/at/service/${service}`, + ) + } + style={[ + a.flex_row, + a.align_center, + a.gap_xs, + a.rounded_full, + t.atoms.bg_contrast_50, + {paddingVertical: 6, paddingHorizontal: 10}, + ]}> + + + {service} + + + ))} + + ) +} interface Props { profile: AppBskyActorDefs.ProfileViewDetailed @@ -151,6 +232,7 @@ {!isPlaceholderProfile && !isBlockedUser && ( + {descriptionRT && !moderation.ui('profileView').blur ? (