feat(about): add real avatar and brand SVG icons for social badges

Replace placeholder SF Symbols with actual brand assets: avatar photo,
WeChat/Xiaohongshu/X/Discord SVG icons. Merge social links into author
card with horizontal badge layout. Update copy and localization keys.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
zhukang
2026-03-10 20:58:36 +08:00
parent 7a1f8e3d56
commit c435b5e301
15 changed files with 168 additions and 20 deletions

View File

@@ -596,6 +596,9 @@
"about.opensource.detail" = "This project is open source. Star, fork, or contribute on GitHub."; "about.opensource.detail" = "This project is open source. Star, fork, or contribute on GitHub.";
"about.opensource.visit" = "View on GitHub"; "about.opensource.visit" = "View on GitHub";
"about.social.title" = "Follow Me"; "about.social.title" = "Follow Me";
"about.social.detail" = "WeChat Official Account & Xiaohongshu"; "about.social.wechat" = "WeChat Official Account";
"about.social.xiaohongshu" = "Xiaohongshu";
"about.social.x" = "X (Twitter)";
"about.social.discord" = "Discord";
"smartclean.preview.callout.review.title" = "Some steps in this plan need a closer review"; "smartclean.preview.callout.review.title" = "Some steps in this plan need a closer review";

View File

@@ -596,6 +596,9 @@
"about.opensource.detail" = "本项目已开源。欢迎在 GitHub 上 Star、Fork 或参与贡献。"; "about.opensource.detail" = "本项目已开源。欢迎在 GitHub 上 Star、Fork 或参与贡献。";
"about.opensource.visit" = "在 GitHub 上查看"; "about.opensource.visit" = "在 GitHub 上查看";
"about.social.title" = "关注作者"; "about.social.title" = "关注作者";
"about.social.detail" = "微信公众号 & 小红书"; "about.social.wechat" = "微信公众号";
"about.social.xiaohongshu" = "小红书";
"about.social.x" = "X (Twitter)";
"about.social.discord" = "Discord";
"smartclean.preview.callout.review.title" = "这份计划中仍有步骤需要复核"; "smartclean.preview.callout.review.title" = "这份计划中仍有步骤需要复核";

View File

@@ -14,9 +14,11 @@ public struct AboutFeatureView: View {
title: AtlasL10n.string("about.author.title") title: AtlasL10n.string("about.author.title")
) { ) {
HStack(alignment: .center, spacing: AtlasSpacing.md) { HStack(alignment: .center, spacing: AtlasSpacing.md) {
Image(systemName: "person.crop.circle.fill") Image("avatar", bundle: .module)
.font(.system(size: 40, weight: .light)) .resizable()
.foregroundStyle(AtlasColor.brand) .scaledToFill()
.frame(width: 48, height: 48)
.clipShape(Circle())
.accessibilityHidden(true) .accessibilityHidden(true)
VStack(alignment: .leading, spacing: AtlasSpacing.xxs) { VStack(alignment: .leading, spacing: AtlasSpacing.xxs) {
@@ -33,6 +35,30 @@ public struct AboutFeatureView: View {
Text(AtlasL10n.string("about.author.bio")) Text(AtlasL10n.string("about.author.bio"))
.font(AtlasTypography.body) .font(AtlasTypography.body)
.foregroundStyle(.secondary) .foregroundStyle(.secondary)
Divider()
.padding(.vertical, AtlasSpacing.xs)
HStack(spacing: AtlasSpacing.md) {
SocialBadge(
assetName: "icon-wechat",
label: AtlasL10n.string("about.social.wechat")
)
SocialBadge(
assetName: "icon-xiaohongshu",
label: AtlasL10n.string("about.social.xiaohongshu")
)
SocialBadge(
assetName: "icon-x",
label: AtlasL10n.string("about.social.x"),
url: "https://x.com/lizikk_zhu"
)
SocialBadge(
assetName: "icon-discord",
label: AtlasL10n.string("about.social.discord"),
url: "https://discord.gg"
)
}
} }
AtlasCallout( AtlasCallout(
@@ -76,25 +102,41 @@ public struct AboutFeatureView: View {
.padding(.top, AtlasSpacing.sm) .padding(.top, AtlasSpacing.sm)
} }
AtlasInfoCard(
title: AtlasL10n.string("about.social.title")
) {
HStack(spacing: AtlasSpacing.md) {
Image(systemName: "ellipsis.bubble")
.font(.title3)
.foregroundStyle(AtlasColor.brand)
.accessibilityHidden(true)
Text(AtlasL10n.string("about.social.detail"))
.font(AtlasTypography.body)
.foregroundStyle(.secondary)
}
}
} }
.accessibilityIdentifier("about.screen") .accessibilityIdentifier("about.screen")
} }
} }
private struct SocialBadge: View {
let assetName: String
let label: String
var url: String? = nil
var body: some View {
let content = VStack(spacing: AtlasSpacing.xs) {
Image(assetName, bundle: .module)
.resizable()
.scaledToFit()
.frame(width: 28, height: 28)
.clipShape(RoundedRectangle(cornerRadius: 6, style: .continuous))
Text(label)
.font(.caption2)
.foregroundStyle(.secondary)
.lineLimit(1)
}
.frame(maxWidth: .infinity)
.accessibilityElement(children: .combine)
.accessibilityLabel(label)
if let url, let destination = URL(string: url) {
Link(destination: destination) { content }
} else {
content
}
}
}
#Preview { #Preview {
AboutFeatureView() AboutFeatureView()
} }

View File

@@ -0,0 +1,6 @@
{
"info" : {
"author" : "xcode",
"version" : 1
}
}

View File

@@ -0,0 +1,13 @@
{
"images" : [
{
"filename" : "avatar.png",
"idiom" : "universal",
"scale" : "2x"
}
],
"info" : {
"author" : "xcode",
"version" : 1
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 286 KiB

View File

@@ -0,0 +1,15 @@
{
"images" : [
{
"filename" : "icon-discord.svg",
"idiom" : "universal"
}
],
"info" : {
"author" : "xcode",
"version" : 1
},
"properties" : {
"preserves-vector-representation" : true
}
}

View File

@@ -0,0 +1,4 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 48 48" width="48" height="48">
<rect width="48" height="48" rx="10" fill="#5865F2"/>
<path d="M32.5 16.3a21.6 21.6 0 00-5.4-1.7l-.2.5a20 20 0 00-5.8 0l-.2-.5a21.5 21.5 0 00-5.4 1.7C11.2 22.5 10.3 28.5 10.7 34.4a22 22 0 006.7 3.4l.5-.7 1-1.7a14.2 14.2 0 01-2.2-1.1l.5-.4a15.4 15.4 0 0013.6 0l.5.4c-.7.4-1.4.8-2.2 1.1l1 1.7.5.7a22 22 0 006.7-3.4c.5-6.9-1-12.8-4.8-18.1zM19.6 31c-1.5 0-2.8-1.4-2.8-3.2s1.2-3.2 2.8-3.2 2.8 1.5 2.8 3.2-1.3 3.2-2.8 3.2zm8.8 0c-1.5 0-2.8-1.4-2.8-3.2s1.2-3.2 2.8-3.2 2.8 1.5 2.7 3.2-1.2 3.2-2.7 3.2z" fill="white"/>
</svg>

After

Width:  |  Height:  |  Size: 606 B

View File

@@ -0,0 +1,15 @@
{
"images" : [
{
"filename" : "icon-wechat.svg",
"idiom" : "universal"
}
],
"info" : {
"author" : "xcode",
"version" : 1
},
"properties" : {
"preserves-vector-representation" : true
}
}

View File

@@ -0,0 +1,8 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 48 48" width="48" height="48">
<path d="M18.5 6C10.5 6 4 11.4 4 18.2c0 3.9 2.2 7.4 5.6 9.8l-1.4 4.2c-.2.5.4.9.8.6l5-3c1.4.4 2.9.6 4.5.6.5 0 1-.02 1.5-.06-.3-1.1-.5-2.2-.5-3.4 0-7.5 7-13.6 15.5-13.6.5 0 1 .02 1.5.06C34.7 7.9 27.2 6 18.5 6z" fill="#2DC100"/>
<circle cx="13" cy="16" r="1.5" fill="white"/>
<circle cx="22" cy="16" r="1.5" fill="white"/>
<path d="M44 27c0-5.8-5.8-10.5-13-10.5S18 21.2 18 27s5.8 10.5 13 10.5c1.3 0 2.5-.15 3.7-.4l4.2 2.5c.4.3.9-.1.7-.6l-1.1-3.5C41.4 33.4 44 30.4 44 27z" fill="#2DC100"/>
<circle cx="27" cy="26" r="1.3" fill="white"/>
<circle cx="35" cy="26" r="1.3" fill="white"/>
</svg>

After

Width:  |  Height:  |  Size: 683 B

View File

@@ -0,0 +1,15 @@
{
"images" : [
{
"filename" : "icon-x.svg",
"idiom" : "universal"
}
],
"info" : {
"author" : "xcode",
"version" : 1
},
"properties" : {
"preserves-vector-representation" : true
}
}

View File

@@ -0,0 +1,4 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 48 48" width="48" height="48">
<rect width="48" height="48" rx="10" fill="#000000"/>
<path d="M28.9 13h3.8l-8.3 9.5L34 35h-7.6l-6-7.8L13.6 35H9.8l8.9-10.1L9.4 13H17l5.4 7.1L28.9 13zm-1.3 19.8h2.1L16 15.1h-2.3l13.9 17.7z" fill="white"/>
</svg>

After

Width:  |  Height:  |  Size: 300 B

View File

@@ -0,0 +1,15 @@
{
"images" : [
{
"filename" : "icon-xiaohongshu.svg",
"idiom" : "universal"
}
],
"info" : {
"author" : "xcode",
"version" : 1
},
"properties" : {
"preserves-vector-representation" : true
}
}

View File

@@ -0,0 +1,4 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 48 48" width="48" height="48">
<rect x="4" y="4" width="40" height="40" rx="10" fill="#FE2C55"/>
<text x="24" y="32" font-family="Helvetica,Arial,sans-serif" font-weight="bold" font-size="18" fill="white" text-anchor="middle">RED</text>
</svg>

After

Width:  |  Height:  |  Size: 301 B

View File

@@ -67,7 +67,8 @@ let package = Package(
.target( .target(
name: "AtlasFeaturesAbout", name: "AtlasFeaturesAbout",
dependencies: ["AtlasDesignSystem", "AtlasDomain"], dependencies: ["AtlasDesignSystem", "AtlasDomain"],
path: "AtlasFeaturesAbout/Sources/AtlasFeaturesAbout" path: "AtlasFeaturesAbout/Sources/AtlasFeaturesAbout",
resources: [.process("Resources")]
), ),
.target( .target(
name: "AtlasFeaturesApps", name: "AtlasFeaturesApps",