gitbay/Views/Releases/ReleaseView.swift

5411753023a5e2958f17b523903abce380941107
gitbay-ios/gitbay/Views/Releases/ReleaseView.swift history · blame · raw

113 lines · 4578 bytes

  1import SwiftUI
  2
  3struct ReleaseView: View {
  4
  5    @State private var model: ReleaseDetailViewModel
  6    @State private var editing = false
  7    @State private var draftTitle = ""
  8    @State private var draftNotes = ""
  9
 10    init(client: GitbayClient, repo: String, tag: String) {
 11        _model = State(initialValue: ReleaseDetailViewModel(
 12            client: client, repoPath: repo, tag: tag
 13        ))
 14    }
 15
 16    var body: some View {
 17        List {
 18            if let release = model.state.value {
 19                if let error = model.actionError {
 20                    Section {
 21                        Label(error, systemImage: "hand.raised")
 22                            .foregroundStyle(.orange)
 23                            .font(.subheadline)
 24                    }
 25                }
 26                Section {
 27                    VStack(alignment: .leading, spacing: 6) {
 28                        Text(release.title.isEmpty ? release.tag : release.title)
 29                            .font(.headline)
 30                        HStack(spacing: 6) {
 31                            Text(release.tag)
 32                                .font(.caption.monospaced())
 33                            if let author = release.author {
 34                                Text("by \(author)")
 35                            }
 36                            Text(release.createdAt, format: .relative(presentation: .named))
 37                                .foregroundStyle(.tertiary)
 38                        }
 39                        .font(.caption)
 40                        .foregroundStyle(.secondary)
 41                    }
 42                    .padding(.vertical, 2)
 43                }
 44
 45                if let notes = release.notes, !notes.isEmpty {
 46                    Section {
 47                        MarkdownView(markdown: notes)
 48                            .padding(.vertical, 4)
 49                    }
 50                }
 51
 52                if let assets = release.assets, !assets.isEmpty {
 53                    Section("Assets") {
 54                        ForEach(assets) { asset in
 55                            Link(destination: model.downloadURL(for: asset)) {
 56                                HStack {
 57                                    VStack(alignment: .leading, spacing: 2) {
 58                                        Text(asset.name)
 59                                            .font(.caption.monospaced())
 60                                            .foregroundStyle(.primary)
 61                                            .lineLimit(1)
 62                                        Text(String(asset.sha256.prefix(16)))
 63                                            .font(.caption2.monospaced())
 64                                            .foregroundStyle(.tertiary)
 65                                    }
 66                                    Spacer()
 67                                    Text(asset.size.formatted(.byteCount(style: .file)))
 68                                        .font(.caption)
 69                                        .foregroundStyle(.secondary)
 70                                    Image(systemName: "arrow.down.circle")
 71                                        .foregroundStyle(.secondary)
 72                                }
 73                            }
 74                        }
 75                    }
 76                }
 77            }
 78        }
 79        .overlay { LoadStateOverlay(state: model.state) }
 80        .navigationTitle(model.tag)
 81        .navigationBarTitleDisplayMode(.inline)
 82        .toolbar {
 83            ToolbarItem(placement: .topBarTrailing) {
 84                if let release = model.state.value {
 85                    Button("Edit") {
 86                        draftTitle = release.title
 87                        draftNotes = release.notes ?? ""
 88                        editing = true
 89                    }
 90                    .disabled(model.working)
 91                    .accessibilityIdentifier("release-edit-button")
 92                }
 93            }
 94        }
 95        .sheet(isPresented: $editing) {
 96            ComposeSheet(
 97                heading: "Edit \(model.tag)",
 98                submitLabel: "Save",
 99                working: model.working,
100                errorMessage: model.actionError,
101                title: $draftTitle,
102                bodyText: $draftNotes
103            ) {
104                Task {
105                    await model.edit(title: draftTitle, notes: draftNotes)
106                    if model.actionError == nil { editing = false }
107                }
108            }
109        }
110        .task { await model.load() }
111        .refreshable { await model.load() }
112    }
113}