a native ios client for gitbay

client ios swift

https://gitbay.org

gitbay/Views/Releases/ReleaseView.swift

111 lines · 4475 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                        GBNotice(error, .gbWarn)
 22                    }
 23                }
 24                Section {
 25                    VStack(alignment: .leading, spacing: 6) {
 26                        Text(release.title.isEmpty ? release.tag : release.title)
 27                            .font(.gbSans(.headline))
 28                        HStack(spacing: 6) {
 29                            Text(release.tag)
 30                                .font(.gbMono(.caption))
 31                            if let author = release.author {
 32                                Text("by \(author)")
 33                            }
 34                            Text(release.createdAt, format: .relative(presentation: .named))
 35                                .foregroundStyle(.tertiary)
 36                        }
 37                        .font(.gbSans(.caption))
 38                        .foregroundStyle(.secondary)
 39                    }
 40                    .padding(.vertical, 2)
 41                }
 42
 43                if let notes = release.notes, !notes.isEmpty {
 44                    Section {
 45                        MarkdownView(markdown: notes)
 46                            .padding(.vertical, 4)
 47                    }
 48                }
 49
 50                if let assets = release.assets, !assets.isEmpty {
 51                    Section("Assets") {
 52                        ForEach(assets) { asset in
 53                            Link(destination: model.downloadURL(for: asset)) {
 54                                HStack {
 55                                    VStack(alignment: .leading, spacing: 2) {
 56                                        Text(asset.name)
 57                                            .font(.gbMono(.caption))
 58                                            .foregroundStyle(.primary)
 59                                            .lineLimit(1)
 60                                        Text(String(asset.sha256.prefix(16)))
 61                                            .font(.gbMono(.caption2))
 62                                            .foregroundStyle(.tertiary)
 63                                    }
 64                                    Spacer()
 65                                    Text(asset.size.formatted(.byteCount(style: .file)))
 66                                        .font(.gbSans(.caption))
 67                                        .foregroundStyle(.secondary)
 68                                    Image(systemName: "arrow.down.circle")
 69                                        .foregroundStyle(.secondary)
 70                                }
 71                            }
 72                        }
 73                    }
 74                }
 75            }
 76        }
 77        .overlay { LoadStateOverlay(state: model.state) }
 78        .navigationTitle(model.tag)
 79        .navigationBarTitleDisplayMode(.inline)
 80        .toolbar {
 81            ToolbarItem(placement: .topBarTrailing) {
 82                if let release = model.state.value {
 83                    Button("Edit") {
 84                        draftTitle = release.title
 85                        draftNotes = release.notes ?? ""
 86                        editing = true
 87                    }
 88                    .disabled(model.working)
 89                    .accessibilityIdentifier("release-edit-button")
 90                }
 91            }
 92        }
 93        .sheet(isPresented: $editing) {
 94            ComposeSheet(
 95                heading: "Edit \(model.tag)",
 96                submitLabel: "Save",
 97                working: model.working,
 98                errorMessage: model.actionError,
 99                title: $draftTitle,
100                bodyText: $draftNotes
101            ) {
102                Task {
103                    await model.edit(title: draftTitle, notes: draftNotes)
104                    if model.actionError == nil { editing = false }
105                }
106            }
107        }
108        .task { await model.load() }
109        .refreshable { await model.load() }
110    }
111}