Show commit details above changed files
This commit is contained in:
@@ -613,9 +613,9 @@ public protocol GotchaCoreProtocol: AnyObject, Sendable {
|
||||
|
||||
func clearPullFilters() throws
|
||||
|
||||
func commitDiff(owner: String, repository: String, sha: String, path: String) async throws -> DiffPage
|
||||
func commitDetails(owner: String, repository: String, sha: String, branch: String?) async throws -> CommitDetailsPage
|
||||
|
||||
func commitFiles(owner: String, repository: String, sha: String) async throws -> [FileRow]
|
||||
func commitDiff(owner: String, repository: String, sha: String, path: String) async throws -> DiffPage
|
||||
|
||||
func commits(owner: String, repository: String, branch: String?, path: String, pages: UInt32) async throws -> CommitPage
|
||||
|
||||
@@ -793,6 +793,22 @@ open func clearPullFilters()throws {try rustCallWithError(FfiConverterTypeGotc
|
||||
}
|
||||
}
|
||||
|
||||
open func commitDetails(owner: String, repository: String, sha: String, branch: String?)async throws -> CommitDetailsPage {
|
||||
return
|
||||
try await uniffiRustCallAsync(
|
||||
rustFutureFunc: {
|
||||
uniffi_gotcha_core_fn_method_gotchacore_commit_details(
|
||||
self.uniffiCloneHandle(),FfiConverterString.lower(owner),FfiConverterString.lower(repository),FfiConverterString.lower(sha),FfiConverterOptionString.lower(branch)
|
||||
)
|
||||
},
|
||||
pollFunc: ffi_gotcha_core_rust_future_poll_rust_buffer,
|
||||
completeFunc: ffi_gotcha_core_rust_future_complete_rust_buffer,
|
||||
freeFunc: ffi_gotcha_core_rust_future_free_rust_buffer,
|
||||
liftFunc: FfiConverterTypeCommitDetailsPage_lift,
|
||||
errorHandler: FfiConverterTypeGotchaError_lift
|
||||
)
|
||||
}
|
||||
|
||||
open func commitDiff(owner: String, repository: String, sha: String, path: String)async throws -> DiffPage {
|
||||
return
|
||||
try await uniffiRustCallAsync(
|
||||
@@ -809,22 +825,6 @@ open func commitDiff(owner: String, repository: String, sha: String, path: Strin
|
||||
)
|
||||
}
|
||||
|
||||
open func commitFiles(owner: String, repository: String, sha: String)async throws -> [FileRow] {
|
||||
return
|
||||
try await uniffiRustCallAsync(
|
||||
rustFutureFunc: {
|
||||
uniffi_gotcha_core_fn_method_gotchacore_commit_files(
|
||||
self.uniffiCloneHandle(),FfiConverterString.lower(owner),FfiConverterString.lower(repository),FfiConverterString.lower(sha)
|
||||
)
|
||||
},
|
||||
pollFunc: ffi_gotcha_core_rust_future_poll_rust_buffer,
|
||||
completeFunc: ffi_gotcha_core_rust_future_complete_rust_buffer,
|
||||
freeFunc: ffi_gotcha_core_rust_future_free_rust_buffer,
|
||||
liftFunc: FfiConverterSequenceTypeFileRow.lift,
|
||||
errorHandler: FfiConverterTypeGotchaError_lift
|
||||
)
|
||||
}
|
||||
|
||||
open func commits(owner: String, repository: String, branch: String?, path: String, pages: UInt32)async throws -> CommitPage {
|
||||
return
|
||||
try await uniffiRustCallAsync(
|
||||
@@ -1443,6 +1443,122 @@ public func FfiConverterTypeCommentRow_lower(_ value: CommentRow) -> RustBuffer
|
||||
}
|
||||
|
||||
|
||||
public struct CommitDetailsPage: Equatable, Hashable {
|
||||
public var title: String
|
||||
public var description: String
|
||||
public var metadata: [CommitMetadataRow]
|
||||
public var files: [FileRow]
|
||||
|
||||
// Default memberwise initializers are never public by default, so we
|
||||
// declare one manually.
|
||||
public init(title: String, description: String, metadata: [CommitMetadataRow], files: [FileRow]) {
|
||||
self.title = title
|
||||
self.description = description
|
||||
self.metadata = metadata
|
||||
self.files = files
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
#if compiler(>=6)
|
||||
extension CommitDetailsPage: Sendable {}
|
||||
#endif
|
||||
|
||||
#if swift(>=5.8)
|
||||
@_documentation(visibility: private)
|
||||
#endif
|
||||
public struct FfiConverterTypeCommitDetailsPage: FfiConverterRustBuffer {
|
||||
public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> CommitDetailsPage {
|
||||
return
|
||||
try CommitDetailsPage(
|
||||
title: FfiConverterString.read(from: &buf),
|
||||
description: FfiConverterString.read(from: &buf),
|
||||
metadata: FfiConverterSequenceTypeCommitMetadataRow.read(from: &buf),
|
||||
files: FfiConverterSequenceTypeFileRow.read(from: &buf)
|
||||
)
|
||||
}
|
||||
|
||||
public static func write(_ value: CommitDetailsPage, into buf: inout [UInt8]) {
|
||||
FfiConverterString.write(value.title, into: &buf)
|
||||
FfiConverterString.write(value.description, into: &buf)
|
||||
FfiConverterSequenceTypeCommitMetadataRow.write(value.metadata, into: &buf)
|
||||
FfiConverterSequenceTypeFileRow.write(value.files, into: &buf)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
#if swift(>=5.8)
|
||||
@_documentation(visibility: private)
|
||||
#endif
|
||||
public func FfiConverterTypeCommitDetailsPage_lift(_ buf: RustBuffer) throws -> CommitDetailsPage {
|
||||
return try FfiConverterTypeCommitDetailsPage.lift(buf)
|
||||
}
|
||||
|
||||
#if swift(>=5.8)
|
||||
@_documentation(visibility: private)
|
||||
#endif
|
||||
public func FfiConverterTypeCommitDetailsPage_lower(_ value: CommitDetailsPage) -> RustBuffer {
|
||||
return FfiConverterTypeCommitDetailsPage.lower(value)
|
||||
}
|
||||
|
||||
|
||||
public struct CommitMetadataRow: Equatable, Hashable {
|
||||
public var label: String
|
||||
public var value: String
|
||||
|
||||
// Default memberwise initializers are never public by default, so we
|
||||
// declare one manually.
|
||||
public init(label: String, value: String) {
|
||||
self.label = label
|
||||
self.value = value
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
#if compiler(>=6)
|
||||
extension CommitMetadataRow: Sendable {}
|
||||
#endif
|
||||
|
||||
#if swift(>=5.8)
|
||||
@_documentation(visibility: private)
|
||||
#endif
|
||||
public struct FfiConverterTypeCommitMetadataRow: FfiConverterRustBuffer {
|
||||
public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> CommitMetadataRow {
|
||||
return
|
||||
try CommitMetadataRow(
|
||||
label: FfiConverterString.read(from: &buf),
|
||||
value: FfiConverterString.read(from: &buf)
|
||||
)
|
||||
}
|
||||
|
||||
public static func write(_ value: CommitMetadataRow, into buf: inout [UInt8]) {
|
||||
FfiConverterString.write(value.label, into: &buf)
|
||||
FfiConverterString.write(value.value, into: &buf)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
#if swift(>=5.8)
|
||||
@_documentation(visibility: private)
|
||||
#endif
|
||||
public func FfiConverterTypeCommitMetadataRow_lift(_ buf: RustBuffer) throws -> CommitMetadataRow {
|
||||
return try FfiConverterTypeCommitMetadataRow.lift(buf)
|
||||
}
|
||||
|
||||
#if swift(>=5.8)
|
||||
@_documentation(visibility: private)
|
||||
#endif
|
||||
public func FfiConverterTypeCommitMetadataRow_lower(_ value: CommitMetadataRow) -> RustBuffer {
|
||||
return FfiConverterTypeCommitMetadataRow.lower(value)
|
||||
}
|
||||
|
||||
|
||||
public struct CommitPage: Equatable, Hashable {
|
||||
public var branches: [String]
|
||||
public var commits: [CommitRow]
|
||||
@@ -3744,6 +3860,31 @@ fileprivate struct FfiConverterSequenceTypeCommentRow: FfiConverterRustBuffer {
|
||||
}
|
||||
}
|
||||
|
||||
#if swift(>=5.8)
|
||||
@_documentation(visibility: private)
|
||||
#endif
|
||||
fileprivate struct FfiConverterSequenceTypeCommitMetadataRow: FfiConverterRustBuffer {
|
||||
typealias SwiftType = [CommitMetadataRow]
|
||||
|
||||
public static func write(_ value: [CommitMetadataRow], into buf: inout [UInt8]) {
|
||||
let len = Int32(value.count)
|
||||
writeInt(&buf, len)
|
||||
for item in value {
|
||||
FfiConverterTypeCommitMetadataRow.write(item, into: &buf)
|
||||
}
|
||||
}
|
||||
|
||||
public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> [CommitMetadataRow] {
|
||||
let len: Int32 = try readInt(&buf)
|
||||
var seq = [CommitMetadataRow]()
|
||||
seq.reserveCapacity(Int(len))
|
||||
for _ in 0 ..< len {
|
||||
seq.append(try FfiConverterTypeCommitMetadataRow.read(from: &buf))
|
||||
}
|
||||
return seq
|
||||
}
|
||||
}
|
||||
|
||||
#if swift(>=5.8)
|
||||
@_documentation(visibility: private)
|
||||
#endif
|
||||
@@ -4147,10 +4288,10 @@ private let initializationResult: InitializationResult = {
|
||||
if (uniffi_gotcha_core_checksum_method_gotchacore_clear_pull_filters() != 36676) {
|
||||
return InitializationResult.apiChecksumMismatch
|
||||
}
|
||||
if (uniffi_gotcha_core_checksum_method_gotchacore_commit_diff() != 56887) {
|
||||
if (uniffi_gotcha_core_checksum_method_gotchacore_commit_details() != 19611) {
|
||||
return InitializationResult.apiChecksumMismatch
|
||||
}
|
||||
if (uniffi_gotcha_core_checksum_method_gotchacore_commit_files() != 43926) {
|
||||
if (uniffi_gotcha_core_checksum_method_gotchacore_commit_diff() != 56887) {
|
||||
return InitializationResult.apiChecksumMismatch
|
||||
}
|
||||
if (uniffi_gotcha_core_checksum_method_gotchacore_commits() != 60062) {
|
||||
|
||||
@@ -284,16 +284,16 @@ void uniffi_gotcha_core_fn_method_gotchacore_clear_issue_filters(uint64_t ptr, R
|
||||
void uniffi_gotcha_core_fn_method_gotchacore_clear_pull_filters(uint64_t ptr, RustCallStatus *_Nonnull out_status
|
||||
);
|
||||
#endif
|
||||
#ifndef UNIFFI_FFIDEF_UNIFFI_GOTCHA_CORE_FN_METHOD_GOTCHACORE_COMMIT_DETAILS
|
||||
#define UNIFFI_FFIDEF_UNIFFI_GOTCHA_CORE_FN_METHOD_GOTCHACORE_COMMIT_DETAILS
|
||||
uint64_t uniffi_gotcha_core_fn_method_gotchacore_commit_details(uint64_t ptr, RustBuffer owner, RustBuffer repository, RustBuffer sha, RustBuffer branch
|
||||
);
|
||||
#endif
|
||||
#ifndef UNIFFI_FFIDEF_UNIFFI_GOTCHA_CORE_FN_METHOD_GOTCHACORE_COMMIT_DIFF
|
||||
#define UNIFFI_FFIDEF_UNIFFI_GOTCHA_CORE_FN_METHOD_GOTCHACORE_COMMIT_DIFF
|
||||
uint64_t uniffi_gotcha_core_fn_method_gotchacore_commit_diff(uint64_t ptr, RustBuffer owner, RustBuffer repository, RustBuffer sha, RustBuffer path
|
||||
);
|
||||
#endif
|
||||
#ifndef UNIFFI_FFIDEF_UNIFFI_GOTCHA_CORE_FN_METHOD_GOTCHACORE_COMMIT_FILES
|
||||
#define UNIFFI_FFIDEF_UNIFFI_GOTCHA_CORE_FN_METHOD_GOTCHACORE_COMMIT_FILES
|
||||
uint64_t uniffi_gotcha_core_fn_method_gotchacore_commit_files(uint64_t ptr, RustBuffer owner, RustBuffer repository, RustBuffer sha
|
||||
);
|
||||
#endif
|
||||
#ifndef UNIFFI_FFIDEF_UNIFFI_GOTCHA_CORE_FN_METHOD_GOTCHACORE_COMMITS
|
||||
#define UNIFFI_FFIDEF_UNIFFI_GOTCHA_CORE_FN_METHOD_GOTCHACORE_COMMITS
|
||||
uint64_t uniffi_gotcha_core_fn_method_gotchacore_commits(uint64_t ptr, RustBuffer owner, RustBuffer repository, RustBuffer branch, RustBuffer path, uint32_t pages
|
||||
@@ -739,15 +739,15 @@ uint16_t uniffi_gotcha_core_checksum_method_gotchacore_clear_pull_filters(void
|
||||
|
||||
);
|
||||
#endif
|
||||
#ifndef UNIFFI_FFIDEF_UNIFFI_GOTCHA_CORE_CHECKSUM_METHOD_GOTCHACORE_COMMIT_DIFF
|
||||
#define UNIFFI_FFIDEF_UNIFFI_GOTCHA_CORE_CHECKSUM_METHOD_GOTCHACORE_COMMIT_DIFF
|
||||
uint16_t uniffi_gotcha_core_checksum_method_gotchacore_commit_diff(void
|
||||
#ifndef UNIFFI_FFIDEF_UNIFFI_GOTCHA_CORE_CHECKSUM_METHOD_GOTCHACORE_COMMIT_DETAILS
|
||||
#define UNIFFI_FFIDEF_UNIFFI_GOTCHA_CORE_CHECKSUM_METHOD_GOTCHACORE_COMMIT_DETAILS
|
||||
uint16_t uniffi_gotcha_core_checksum_method_gotchacore_commit_details(void
|
||||
|
||||
);
|
||||
#endif
|
||||
#ifndef UNIFFI_FFIDEF_UNIFFI_GOTCHA_CORE_CHECKSUM_METHOD_GOTCHACORE_COMMIT_FILES
|
||||
#define UNIFFI_FFIDEF_UNIFFI_GOTCHA_CORE_CHECKSUM_METHOD_GOTCHACORE_COMMIT_FILES
|
||||
uint16_t uniffi_gotcha_core_checksum_method_gotchacore_commit_files(void
|
||||
#ifndef UNIFFI_FFIDEF_UNIFFI_GOTCHA_CORE_CHECKSUM_METHOD_GOTCHACORE_COMMIT_DIFF
|
||||
#define UNIFFI_FFIDEF_UNIFFI_GOTCHA_CORE_CHECKSUM_METHOD_GOTCHACORE_COMMIT_DIFF
|
||||
uint16_t uniffi_gotcha_core_checksum_method_gotchacore_commit_diff(void
|
||||
|
||||
);
|
||||
#endif
|
||||
|
||||
@@ -1229,7 +1229,8 @@ final class CommitsViewController: RefreshingTableViewController {
|
||||
context: context,
|
||||
owner: owner,
|
||||
repository: repository,
|
||||
sha: row.sha
|
||||
sha: row.sha,
|
||||
branch: row.branchLabel ?? branch
|
||||
),
|
||||
animated: true
|
||||
)
|
||||
|
||||
@@ -398,19 +398,96 @@ private final class FileListView: UITableView, UITableViewDataSource, UITableVie
|
||||
}
|
||||
}
|
||||
|
||||
@MainActor
|
||||
private final class CommitHeaderView: UIView {
|
||||
private let stack = UIStackView()
|
||||
private let titleLabel = UILabel()
|
||||
private let descriptionLabel = UILabel()
|
||||
private let metadataStack = UIStackView()
|
||||
|
||||
override init(frame: CGRect) {
|
||||
super.init(frame: frame)
|
||||
backgroundColor = .systemBackground
|
||||
directionalLayoutMargins = .init(top: 20, leading: 20, bottom: 20, trailing: 20)
|
||||
|
||||
titleLabel.font = .preferredFont(forTextStyle: .title2)
|
||||
titleLabel.adjustsFontForContentSizeCategory = true
|
||||
titleLabel.numberOfLines = 0
|
||||
titleLabel.accessibilityTraits = .header
|
||||
descriptionLabel.font = .preferredFont(forTextStyle: .body)
|
||||
descriptionLabel.adjustsFontForContentSizeCategory = true
|
||||
descriptionLabel.numberOfLines = 0
|
||||
|
||||
metadataStack.axis = .vertical
|
||||
metadataStack.spacing = 10
|
||||
stack.axis = .vertical
|
||||
stack.spacing = 12
|
||||
stack.translatesAutoresizingMaskIntoConstraints = false
|
||||
stack.addArrangedSubview(titleLabel)
|
||||
stack.addArrangedSubview(descriptionLabel)
|
||||
stack.addArrangedSubview(metadataStack)
|
||||
addSubview(stack)
|
||||
NSLayoutConstraint.activate([
|
||||
stack.leadingAnchor.constraint(equalTo: layoutMarginsGuide.leadingAnchor),
|
||||
stack.trailingAnchor.constraint(equalTo: layoutMarginsGuide.trailingAnchor),
|
||||
stack.topAnchor.constraint(equalTo: layoutMarginsGuide.topAnchor),
|
||||
stack.bottomAnchor.constraint(equalTo: layoutMarginsGuide.bottomAnchor),
|
||||
])
|
||||
}
|
||||
|
||||
@available(*, unavailable)
|
||||
required init?(coder: NSCoder) { fatalError("init(coder:) is not supported") }
|
||||
|
||||
func configure(_ page: CommitDetailsPage) {
|
||||
titleLabel.text = page.title
|
||||
descriptionLabel.text = page.description
|
||||
descriptionLabel.isHidden = page.description.isEmpty
|
||||
metadataStack.arrangedSubviews.forEach { $0.removeFromSuperview() }
|
||||
for metadata in page.metadata {
|
||||
let isCommit = metadata.label == "Commit"
|
||||
let label = UILabel()
|
||||
label.text = metadata.label.uppercased()
|
||||
label.font = .preferredFont(forTextStyle: .caption2)
|
||||
label.adjustsFontForContentSizeCategory = true
|
||||
label.textColor = .secondaryLabel
|
||||
|
||||
let value = UILabel()
|
||||
value.text = metadata.value
|
||||
value.font = isCommit
|
||||
? UIFontMetrics(forTextStyle: .subheadline).scaledFont(
|
||||
for: .monospacedSystemFont(ofSize: 15, weight: .regular)
|
||||
)
|
||||
: .preferredFont(forTextStyle: .subheadline)
|
||||
value.adjustsFontForContentSizeCategory = true
|
||||
value.numberOfLines = 0
|
||||
value.lineBreakMode = isCommit ? .byCharWrapping : .byWordWrapping
|
||||
|
||||
let row = UIStackView(arrangedSubviews: [label, value])
|
||||
row.axis = .vertical
|
||||
row.spacing = 2
|
||||
row.isAccessibilityElement = true
|
||||
row.accessibilityLabel = "\(metadata.label): \(metadata.value)"
|
||||
metadataStack.addArrangedSubview(row)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@MainActor
|
||||
final class FilesViewController: RefreshingTableViewController {
|
||||
private let context: AppContext
|
||||
private let owner: String
|
||||
private let repository: String
|
||||
private let sha: String
|
||||
private var rows: [FileRow] = []
|
||||
private let branch: String?
|
||||
private let commitHeader = CommitHeaderView()
|
||||
private var page: CommitDetailsPage?
|
||||
|
||||
init(context: AppContext, owner: String, repository: String, sha: String) {
|
||||
init(context: AppContext, owner: String, repository: String, sha: String, branch: String? = nil) {
|
||||
self.context = context
|
||||
self.owner = owner
|
||||
self.repository = repository
|
||||
self.sha = sha
|
||||
self.branch = branch
|
||||
super.init()
|
||||
title = "Changed Files"
|
||||
}
|
||||
@@ -423,18 +500,35 @@ final class FilesViewController: RefreshingTableViewController {
|
||||
loadContent(refreshing: false)
|
||||
}
|
||||
|
||||
override func viewDidLayoutSubviews() {
|
||||
super.viewDidLayoutSubviews()
|
||||
guard let header = tableView.tableHeaderView else { return }
|
||||
let size = header.systemLayoutSizeFitting(
|
||||
CGSize(width: tableView.bounds.width, height: 0),
|
||||
withHorizontalFittingPriority: .required,
|
||||
verticalFittingPriority: .fittingSizeLevel
|
||||
)
|
||||
guard header.frame.height != size.height else { return }
|
||||
header.frame.size.height = size.height
|
||||
tableView.tableHeaderView = header
|
||||
}
|
||||
|
||||
override func loadContent(refreshing: Bool) {
|
||||
beginLoading(refreshing: refreshing)
|
||||
loadingTask?.cancel()
|
||||
loadingTask = Task {
|
||||
do {
|
||||
rows = try await context.core.commitFiles(
|
||||
let page = try await context.core.commitDetails(
|
||||
owner: owner,
|
||||
repository: repository,
|
||||
sha: sha
|
||||
sha: sha,
|
||||
branch: branch
|
||||
)
|
||||
self.page = page
|
||||
commitHeader.configure(page)
|
||||
tableView.tableHeaderView = commitHeader
|
||||
tableView.reloadData()
|
||||
tableView.backgroundView = rows.isEmpty
|
||||
tableView.backgroundView = page.files.isEmpty
|
||||
? EmptyBackgroundView(title: "No changed files", detail: "This commit does not contain file changes.")
|
||||
: nil
|
||||
} catch {
|
||||
@@ -445,7 +539,7 @@ final class FilesViewController: RefreshingTableViewController {
|
||||
}
|
||||
|
||||
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
|
||||
rows.count
|
||||
page?.files.count ?? 0
|
||||
}
|
||||
|
||||
override func tableView(
|
||||
@@ -454,7 +548,7 @@ final class FilesViewController: RefreshingTableViewController {
|
||||
) -> UITableViewCell {
|
||||
let cell = tableView.dequeueReusableCell(withIdentifier: "file")
|
||||
?? UITableViewCell(style: .subtitle, reuseIdentifier: "file")
|
||||
let row = rows[indexPath.row]
|
||||
let row = page!.files[indexPath.row]
|
||||
configureTextCell(cell, title: row.path, detail: row.status)
|
||||
cell.accessoryType = .disclosureIndicator
|
||||
return cell
|
||||
@@ -462,7 +556,7 @@ final class FilesViewController: RefreshingTableViewController {
|
||||
|
||||
override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
|
||||
tableView.deselectRow(at: indexPath, animated: true)
|
||||
let row = rows[indexPath.row]
|
||||
guard let row = page?.files[indexPath.row] else { return }
|
||||
navigationController?.pushViewController(
|
||||
DiffViewController(
|
||||
context: context,
|
||||
@@ -633,7 +727,8 @@ final class RepositoryDirectoryViewController: RefreshingTableViewController {
|
||||
context: context,
|
||||
owner: owner,
|
||||
repository: repository,
|
||||
sha: commit.sha
|
||||
sha: commit.sha,
|
||||
branch: commit.branchLabel
|
||||
),
|
||||
animated: true
|
||||
)
|
||||
@@ -780,7 +875,8 @@ final class RepositoryHistoryViewController: RefreshingTableViewController {
|
||||
context: context,
|
||||
owner: owner,
|
||||
repository: repository,
|
||||
sha: commit.sha
|
||||
sha: commit.sha,
|
||||
branch: commit.branchLabel
|
||||
),
|
||||
animated: true
|
||||
)
|
||||
|
||||
Reference in New Issue
Block a user