gitbay/Views/Discovery/ProfileView.swift
78 lines · 3306 bytes
1import SwiftUI
2
3/// A user or organization: `profile show` plus their reachable repos.
4struct ProfileView: View {
5
6 @State private var model: ProfileViewModel
7
8 init(client: GitbayClient, name: String) {
9 _model = State(initialValue: ProfileViewModel(client: client, name: name))
10 }
11
12 var body: some View {
13 List {
14 if let profile = model.state.value {
15 Section {
16 VStack(alignment: .leading, spacing: 6) {
17 HStack(spacing: 8) {
18 Image(systemName: profile.kind == "org"
19 ? "building.2" : "person.crop.circle")
20 .font(.title2)
21 .foregroundStyle(.secondary)
22 Text(profile.name)
23 .font(.title3.weight(.semibold))
24 Text(profile.kind)
25 .font(.caption2)
26 .padding(.horizontal, 6)
27 .padding(.vertical, 1)
28 .background(.quaternary, in: Capsule())
29 }
30 if let description = profile.description, !description.isEmpty {
31 Text(description)
32 .font(.subheadline)
33 .foregroundStyle(.secondary)
34 }
35 if let website = profile.website, let url = URL(string: website) {
36 Link(website, destination: url)
37 .font(.caption)
38 .lineLimit(1)
39 }
40 }
41 .padding(.vertical, 4)
42 }
43
44 if profile.kind == "org" {
45 Section {
46 NavigationLink(value: OrgRoute.org(profile.name)) {
47 Label("Members & Teams", systemImage: "person.3")
48 }
49 }
50 }
51
52 if !model.repos.isEmpty {
53 Section("Repositories") {
54 ForEach(model.repos) { repo in
55 NavigationLink(value: RepoRoute.repo(repo.path)) {
56 VStack(alignment: .leading, spacing: 2) {
57 Text(repo.name)
58 .font(.subheadline.weight(.medium))
59 if let description = repo.description, !description.isEmpty {
60 Text(description)
61 .font(.caption)
62 .foregroundStyle(.secondary)
63 .lineLimit(1)
64 }
65 }
66 }
67 }
68 }
69 }
70 }
71 }
72 .overlay { LoadStateOverlay(state: model.state) }
73 .navigationTitle(model.name)
74 .navigationBarTitleDisplayMode(.inline)
75 .task { await model.load() }
76 .refreshable { await model.load() }
77 }
78}