Commit d58e5ef7d8

d58e5ef7d8a719fdf2a0117721a6a5dd7d44131a

parent: 0a519cfa8f

Verified · cmc

cmc <hello@cleberg.net> · 2026-09-22 18:04 UTC

profile: render the about text under about_path

Layout: unified · split

gitbay/Discovery/ProfileViewModel.swift +4 −7
@@ -16,7 +16,9 @@ final class ProfileViewModel {
1616 /// Long-form profile text, rendered between the header and the
1717 /// activity graph the way the web renders it.
1818 let about: String?
19 let aboutFormat: String?
19 /// The file in `<name>/.gitbay` the text was read from, e.g.
20 /// `profile/README.org`. Its extension picks the renderer.
21 let aboutPath: String?
2022 let links: [Link]?
2123 let orgs: [Member]?
2224 let members: [Member]?
@@ -26,7 +28,7 @@ final class ProfileViewModel {
2628
2729 enum CodingKeys: String, CodingKey {
2830 case name, kind, description, website, about, links, orgs, members, repos, activity
29 case aboutFormat = "about_format"
31 case aboutPath = "about_path"
3032 case activityTotal = "activity_total"
3133 }
3234
@@ -42,11 +44,6 @@ final class ProfileViewModel {
4244 return name == username
4345 }
4446
45 /// The filename the about text is rendered under. The server has
46 /// none to dispatch on either, so the stored format picks the
47 /// extension and anything but org is markdown.
48 var aboutFile: String { aboutFormat == "org" ? "about.org" : "about.md" }
49
5047 /// One of the free-form links a profile carries beside its
5148 /// website. The server caps the list at five.
5249 nonisolated struct Link: Decodable, Sendable, Hashable {
gitbay/Views/Discovery/ProfileView.swift +1 −1
@@ -142,7 +142,7 @@ struct ProfileView: View {
142142 private func aboutSection(_ profile: ProfileViewModel.Profile) -> some View {
143143 if let about = profile.about, !about.isEmpty {
144144 Section("About") {
145 ReadmeView(name: profile.aboutFile, content: about)
145 ReadmeView(name: profile.aboutPath ?? "", content: about)
146146 .padding(.vertical, 4)
147147 }
148148 }
gitbayTests/ProfileCommitTests.swift +5 −5
@@ -15,7 +15,7 @@ private func makeClient() throws -> (GitbayClient, StubProtocol.Box) {
1515private let userProfileJSON = """
1616 {"protocol_version":1,"data":{"name":"cmc","kind":"user",\
1717 "description":"Org-Mode · Self-Hosting · Privacy","website":"https://cleberg.net",\
18 "about":"* Christian\\n\\nOrg-Mode.","about_format":"org",\
18 "about":"* Christian\\n\\nOrg-Mode.","about_format":"org","about_path":"profile/README.org",\
1919 "links":[{"label":"Tor","url":"http://example.onion/"},{"url":"https://krz.sh"}],\
2020 "orgs":[{"name":"krz","role":"admin"},{"name":"audit-labs","role":"admin"}],\
2121 "repos":[{"path":"cmc/notes","visibility":"private","description":"notes",\
@@ -53,8 +53,8 @@ struct ProfileAggregateTests {
5353 #expect(profile.repos[1].topics == nil)
5454 #expect(profile.repos[1].meta.isEmpty)
5555 #expect(profile.about == "* Christian\n\nOrg-Mode.")
56 // The format picks the filename the about text renders under.
57 #expect(profile.aboutFile == "about.org")
56 // The file's own path picks the renderer, as it does on the web.
57 #expect(profile.aboutPath == "profile/README.org")
5858 #expect(profile.links?.map(\.title) == ["Tor", "https://krz.sh"])
5959 #expect(profile.activityTotal == 15)
6060 // One read builds the whole page.
@@ -78,8 +78,8 @@ struct ProfileAggregateTests {
7878 // A profile with no about text and no links renders neither.
7979 #expect(profile.about == nil)
8080 #expect(profile.links == nil)
81 // Markdown is the default for anything but org.
82 #expect(profile.aboutFile == "about.md")
81 // No about file, no path.
82 #expect(profile.aboutPath == nil)
8383 }
8484
8585 @Test func anUnknownNameIsAnEmptyState() async throws {