ai/at
1
0

add social-app service ai.syui.at

This commit is contained in:
2026-02-16 04:05:12 +09:00
parent a49a2ff61f
commit 0506a32fd8
6 changed files with 159 additions and 0 deletions

View File

@@ -0,0 +1,18 @@
--- a/src/screens/Profile/Header/ProfileHeaderStandard.tsx
+++ b/src/screens/Profile/Header/ProfileHeaderStandard.tsx
@@ -46,6 +46,7 @@
import {ProfileHeaderHandle} from './Handle'
import {ProfileHeaderMetrics} from './Metrics'
import {ProfileHeaderShell} from './Shell'
+import {ProfileAtLinks} from './ProfileAtLinks'
import {ProfileHeaderSuggestedFollows} from './SuggestedFollows'
const SERVICE_FAVICONS: Record<string, any> = {
@@ -231,6 +232,7 @@
<View style={a.gap_md}>
<ProfileHeaderMetrics profile={profile} />
<ProfileServiceLinks profile={profile} />
+ <ProfileAtLinks profile={profile} />
{descriptionRT && !moderation.ui('profileView').blur ? (
<View pointerEvents="auto">
<RichText

View File

@@ -0,0 +1,131 @@
import React from 'react'
import {Pressable, View} from 'react-native'
import {type AppBskyActorDefs} from '@atproto/api'
import {useQuery} from '@tanstack/react-query'
import {useOpenLink} from '#/lib/hooks/useOpenLink'
import {useAgent} from '#/state/session'
import {atoms as a, useTheme} from '#/alf'
import {Text} from '#/components/Typography'
import {createSinglePathSVG} from '#/components/icons/TEMPLATE'
// --- SVG Icons (viewBox 0 0 640 640, Font Awesome) ---
const GithubIcon = createSinglePathSVG({
path: 'M237.9 461.4C237.9 463.4 235.6 465 232.7 465C229.4 465.3 227.1 463.7 227.1 461.4C227.1 459.4 229.4 457.8 232.3 457.8C235.3 457.5 237.9 459.1 237.9 461.4zM206.8 456.9C206.1 458.9 208.1 461.2 211.1 461.8C213.7 462.8 216.7 461.8 217.3 459.8C217.9 457.8 216 455.5 213 454.6C210.4 453.9 207.5 454.9 206.8 456.9zM251 455.2C248.1 455.9 246.1 457.8 246.4 460.1C246.7 462.1 249.3 463.4 252.3 462.7C255.2 462 257.2 460.1 256.9 458.1C256.6 456.2 253.9 454.9 251 455.2zM316.8 72C178.1 72 72 177.3 72 316C72 426.9 141.8 521.8 241.5 555.2C254.3 557.5 258.8 549.6 258.8 543.1C258.8 536.9 258.5 502.7 258.5 481.7C258.5 481.7 188.5 496.7 173.8 451.9C173.8 451.9 162.4 422.8 146 415.3C146 415.3 123.1 399.6 147.6 399.9C147.6 399.9 172.5 401.9 186.2 425.7C208.1 464.3 244.8 453.2 259.1 446.6C261.4 430.6 267.9 419.5 275.1 412.9C219.2 406.7 162.8 398.6 162.8 302.4C162.8 274.9 170.4 261.1 186.4 243.5C183.8 237 175.3 210.2 189 175.6C209.9 169.1 258 202.6 258 202.6C278 197 299.5 194.1 320.8 194.1C342.1 194.1 363.6 197 383.6 202.6C383.6 202.6 431.7 169 452.6 175.6C466.3 210.3 457.8 237 455.2 243.5C471.2 261.2 481 275 481 302.4C481 398.9 422.1 406.6 366.2 412.9C375.4 420.8 383.2 435.8 383.2 459.3C383.2 493 382.9 534.7 382.9 542.9C382.9 549.4 387.5 557.3 400.2 555C500.2 521.8 568 426.9 568 316C568 177.3 455.5 72 316.8 72zM169.2 416.9C167.9 417.9 168.2 420.2 169.9 422.1C171.5 423.7 173.8 424.4 175.1 423.1C176.4 422.1 176.1 419.8 174.4 417.9C172.8 416.3 170.5 415.6 169.2 416.9zM158.4 408.8C157.7 410.1 158.7 411.7 160.7 412.7C162.3 413.7 164.3 413.4 165 412C165.7 410.7 164.7 409.1 162.7 408.1C160.7 407.5 159.1 407.8 158.4 408.8zM190.8 444.4C189.2 445.7 189.8 448.7 192.1 450.6C194.4 452.9 197.3 453.2 198.6 451.6C199.9 450.3 199.3 447.3 197.3 445.4C195.1 443.1 192.1 442.8 190.8 444.4zM179.4 429.7C177.8 430.7 177.8 433.3 179.4 435.6C181 437.9 183.7 438.9 185 437.9C186.6 436.6 186.6 434 185 431.7C183.6 429.4 181 428.4 179.4 429.7z',
viewBox: '0 0 640 640',
})
const XIcon = createSinglePathSVG({
path: 'M453.2 112L523.8 112L369.6 288.2L551 528L409 528L297.7 382.6L170.5 528L99.8 528L264.7 339.5L90.8 112L236.4 112L336.9 244.9L453.2 112zM428.4 485.8L467.5 485.8L215.1 152L173.1 152L428.4 485.8z',
viewBox: '0 0 640 640',
})
const YoutubeIcon = createSinglePathSVG({
path: 'M581.7 188.1C575.5 164.4 556.9 145.8 533.4 139.5C490.9 128 320.1 128 320.1 128C320.1 128 149.3 128 106.7 139.5C83.2 145.8 64.7 164.4 58.4 188.1C47 231 47 320.4 47 320.4C47 320.4 47 409.8 58.4 452.7C64.7 476.3 83.2 494.2 106.7 500.5C149.3 512 320.1 512 320.1 512C320.1 512 490.9 512 533.5 500.5C557 494.2 575.5 476.3 581.8 452.7C593.2 409.8 593.2 320.4 593.2 320.4C593.2 320.4 593.2 231 581.8 188.1zM264.2 401.6L264.2 239.2L406.9 320.4L264.2 401.6z',
viewBox: '0 0 640 640',
})
// --- Types ---
interface LinkItem {
service: string
username: string
}
interface LinkCollection {
links: LinkItem[]
createdAt: string
updatedAt?: string
}
// --- Service Config ---
const SERVICE_CONFIG: Record<
string,
{
name: string
urlTemplate: string
icon: React.ComponentType<{size?: 'xs' | 'sm' | 'md' | 'lg'; fill?: string}>
}
> = {
github: {
name: 'GitHub',
urlTemplate: 'https://github.com/{username}',
icon: GithubIcon,
},
x: {
name: 'X',
urlTemplate: 'https://x.com/{username}',
icon: XIcon,
},
youtube: {
name: 'YouTube',
urlTemplate: 'https://youtube.com/@{username}',
icon: YoutubeIcon,
},
}
// --- Component ---
export function ProfileAtLinks({
profile,
}: {
profile: AppBskyActorDefs.ProfileViewDetailed
}) {
const t = useTheme()
const agent = useAgent()
const openLink = useOpenLink()
const {data: linkData} = useQuery({
queryKey: ['at-links', profile.did],
queryFn: async () => {
const res = await agent.com.atproto.repo.getRecord({
repo: profile.did,
collection: 'ai.syui.at.link',
rkey: 'self',
})
return res.data.value as LinkCollection
},
retry: false,
staleTime: 1000 * 60 * 5,
})
if (!linkData?.links?.length) return null
return (
<View style={[a.flex_row, a.flex_wrap, a.gap_sm, a.pt_xs]}>
{linkData.links.map(link => {
const config = SERVICE_CONFIG[link.service]
if (!config) return null
const url = config.urlTemplate.replace('{username}', link.username)
const Icon = config.icon
return (
<Pressable
key={link.service}
onPress={() => openLink(url)}
accessibilityRole="link"
accessibilityLabel={`${config.name}: ${link.username}`}
style={[
a.flex_row,
a.align_center,
a.gap_xs,
a.rounded_full,
t.atoms.bg_contrast_50,
{paddingVertical: 6, paddingHorizontal: 10},
]}>
<Icon size="xs" fill={t.atoms.text_contrast_medium.color} />
<Text
style={[
a.text_xs,
a.font_medium,
t.atoms.text_contrast_medium,
]}>
{link.username}
</Text>
</Pressable>
)
})}
</View>
)
}