From 822cf1f7697d7a14a54e7a4b2c0290d9e6c95d3f Mon Sep 17 00:00:00 2001 From: Georg Bauer Date: Fri, 31 Jul 2026 17:21:38 +0200 Subject: [PATCH] Match pull request file list styling --- TESTING.md | 3 +- ios/Sources/DetailScreens.swift | 70 +++++++++++++++++++++++++-------- 2 files changed, 56 insertions(+), 17 deletions(-) diff --git a/TESTING.md b/TESTING.md index 81aebe8..cd01a5f 100644 --- a/TESTING.md +++ b/TESTING.md @@ -253,7 +253,8 @@ xcrun simctl launch booted de.rfc1437.gotcha - [ ] Each row shows repository/number, title, author/update metadata, comment count, and draft/merged state where applicable. - [ ] Open a pull request and verify title, metadata, Markdown body, comments, - changed-file reference, file paths, and statuses. + and changed-file reference. Its file paths, statuses, left alignment, + separators, and disclosure indicators match the commit changed-file list. - [ ] Open a changed file and run the same diff checks as for a commit. ## Milestones diff --git a/ios/Sources/DetailScreens.swift b/ios/Sources/DetailScreens.swift index c52f021..6cde9b2 100644 --- a/ios/Sources/DetailScreens.swift +++ b/ios/Sources/DetailScreens.swift @@ -310,8 +310,7 @@ final class PullViewController: MarkdownPageViewController { ) if !page.files.isEmpty { views.append(sectionLabel(page.filesRef)) - views.append(contentsOf: page.files.map { file in - detailButton(title: file.path, detail: file.status) { [weak self] in + views.append(FileListView(rows: page.files) { [weak self] file in guard let self else { return } self.navigationController?.pushViewController( DiffViewController( @@ -325,7 +324,6 @@ final class PullViewController: MarkdownPageViewController { ), animated: true ) - } }) } views.append(contentsOf: commentViews(page.comments)) @@ -343,6 +341,59 @@ final class PullViewController: MarkdownPageViewController { } } +private final class FileListView: UITableView, UITableViewDataSource, UITableViewDelegate { + private let rows: [FileRow] + private let select: (FileRow) -> Void + private var contentHeight: NSLayoutConstraint! + + init(rows: [FileRow], select: @escaping (FileRow) -> Void) { + self.rows = rows + self.select = select + super.init(frame: .zero, style: .plain) + translatesAutoresizingMaskIntoConstraints = false + dataSource = self + delegate = self + isScrollEnabled = false + rowHeight = UITableView.automaticDimension + estimatedRowHeight = 55 + separatorInset = .zero + backgroundColor = .systemGroupedBackground + contentHeight = heightAnchor.constraint(equalToConstant: CGFloat(rows.count) * estimatedRowHeight) + contentHeight.isActive = true + } + + @available(*, unavailable) + required init?(coder: NSCoder) { fatalError("init(coder:) is not supported") } + + override func layoutSubviews() { + super.layoutSubviews() + if abs(contentHeight.constant - contentSize.height) > 0.5 { + contentHeight.constant = contentSize.height + } + } + + func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int { + rows.count + } + + func tableView( + _ tableView: UITableView, + cellForRowAt indexPath: IndexPath + ) -> UITableViewCell { + let cell = tableView.dequeueReusableCell(withIdentifier: "file") + ?? UITableViewCell(style: .subtitle, reuseIdentifier: "file") + let row = rows[indexPath.row] + configureTextCell(cell, title: row.path, detail: row.status) + cell.accessoryType = .disclosureIndicator + return cell + } + + func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) { + tableView.deselectRow(at: indexPath, animated: true) + select(rows[indexPath.row]) + } +} + @MainActor final class FilesViewController: RefreshingTableViewController { private let context: AppContext @@ -1192,16 +1243,3 @@ private func separator() -> UIView { line.heightAnchor.constraint(equalToConstant: 1 / UIScreen.main.scale).isActive = true return line } - -private func detailButton(title: String, detail: String, action: @escaping () -> Void) -> UIButton { - var configuration = UIButton.Configuration.plain() - configuration.title = title - configuration.subtitle = detail - configuration.image = UIImage(systemName: "chevron.right") - configuration.imagePlacement = .trailing - configuration.imagePadding = 8 - configuration.contentInsets = .init(top: 10, leading: 0, bottom: 10, trailing: 0) - let button = UIButton(configuration: configuration, primaryAction: UIAction { _ in action() }) - button.contentHorizontalAlignment = .fill - return button -}