diff --git a/ios/assets/icons/github.svg b/ios/assets/icons/github.svg
new file mode 100644
index 0000000..a606fcf
--- /dev/null
+++ b/ios/assets/icons/github.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/ios/assets/icons/x.svg b/ios/assets/icons/x.svg
new file mode 100644
index 0000000..cdc0570
--- /dev/null
+++ b/ios/assets/icons/x.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/ios/assets/icons/youtube.svg b/ios/assets/icons/youtube.svg
new file mode 100644
index 0000000..c5361d9
--- /dev/null
+++ b/ios/assets/icons/youtube.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/ios/patching/042-social-app-ios-at-links.patch b/ios/patching/042-social-app-ios-at-links.patch
new file mode 100644
index 0000000..99fbddd
--- /dev/null
+++ b/ios/patching/042-social-app-ios-at-links.patch
@@ -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 = {
+@@ -231,6 +232,7 @@
+
+
+
++
+ {descriptionRT && !moderation.ui('profileView').blur ? (
+
+
+ }
+> = {
+ 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 (
+
+ {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 (
+ 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},
+ ]}>
+
+
+ {link.username}
+
+
+ )
+ })}
+
+ )
+}
diff --git a/ios/setup.zsh b/ios/setup.zsh
index 5cbcbc0..9f51585 100755
--- a/ios/setup.zsh
+++ b/ios/setup.zsh
@@ -49,6 +49,7 @@ PATCH_FILES_IOS=(
"039-social-app-ios-hide-feed-controls.patch"
"040-social-app-ios-hide-composer-prompt.patch"
"041-social-app-ios-splash-signin-button.patch"
+ "042-social-app-ios-at-links.patch"
)
function ios-env() {
@@ -184,6 +185,12 @@ function ios-copy-new-files() {
echo "✅ Copied AppInfo.tsx"
fi
+ # Copy ProfileAtLinks.tsx
+ if [ -f "$patching_dir/ProfileAtLinks.tsx" ]; then
+ cp "$patching_dir/ProfileAtLinks.tsx" "$target_dir/src/screens/Profile/Header/ProfileAtLinks.tsx"
+ echo "✅ Copied ProfileAtLinks.tsx"
+ fi
+
# Copy pre-generated favicons for bskyweb
local favicon_src="$d/ios/assets/favicons"
local bskyweb_static="$target_dir/bskyweb/static"