Show labels on issue details

This commit is contained in:
Georg Bauer
2026-07-31 18:27:57 +02:00
parent 0a38dbe9f2
commit acf2c839ad
6 changed files with 101 additions and 13 deletions

View File

@@ -2236,17 +2236,19 @@ public struct IssuePage: Equatable, Hashable {
public var state: String
public var meta: String
public var milestone: String
public var labels: [LabelRow]
public var body: String
public var comments: [CommentRow]
public var hasMore: Bool
// Default memberwise initializers are never public by default, so we
// declare one manually.
public init(title: String, state: String, meta: String, milestone: String, body: String, comments: [CommentRow], hasMore: Bool) {
public init(title: String, state: String, meta: String, milestone: String, labels: [LabelRow], body: String, comments: [CommentRow], hasMore: Bool) {
self.title = title
self.state = state
self.meta = meta
self.milestone = milestone
self.labels = labels
self.body = body
self.comments = comments
self.hasMore = hasMore
@@ -2272,6 +2274,7 @@ public struct FfiConverterTypeIssuePage: FfiConverterRustBuffer {
state: FfiConverterString.read(from: &buf),
meta: FfiConverterString.read(from: &buf),
milestone: FfiConverterString.read(from: &buf),
labels: FfiConverterSequenceTypeLabelRow.read(from: &buf),
body: FfiConverterString.read(from: &buf),
comments: FfiConverterSequenceTypeCommentRow.read(from: &buf),
hasMore: FfiConverterBool.read(from: &buf)
@@ -2283,6 +2286,7 @@ public struct FfiConverterTypeIssuePage: FfiConverterRustBuffer {
FfiConverterString.write(value.state, into: &buf)
FfiConverterString.write(value.meta, into: &buf)
FfiConverterString.write(value.milestone, into: &buf)
FfiConverterSequenceTypeLabelRow.write(value.labels, into: &buf)
FfiConverterString.write(value.body, into: &buf)
FfiConverterSequenceTypeCommentRow.write(value.comments, into: &buf)
FfiConverterBool.write(value.hasMore, into: &buf)

View File

@@ -370,14 +370,7 @@ final class IssueCell: UITableViewCell {
labels.arrangedSubviews.forEach { $0.removeFromSuperview() }
labels.isHidden = row.labels.isEmpty
for label in row.labels.prefix(3) {
let view = UILabel()
view.text = " \(label.name) "
view.font = .preferredFont(forTextStyle: .caption2)
view.textColor = label.light ? .black : .white
view.backgroundColor = UIColor(hex: label.color)
view.layer.cornerRadius = 9
view.clipsToBounds = true
labels.addArrangedSubview(view)
labels.addArrangedSubview(issueLabelView(label))
}
labels.addArrangedSubview(UIView())
}

View File

@@ -232,7 +232,8 @@ final class IssueViewController: MarkdownPageViewController {
body: page.body,
comments: page.comments,
editComment: { [weak self] comment in self?.editComment(comment) },
milestone: page.milestone
milestone: page.milestone,
labels: page.labels
))
finishPagination(hasMore: page.hasMore)
}
@@ -1142,7 +1143,8 @@ private func detailViews(
body: String,
comments: [CommentRow],
editComment: ((CommentRow) -> Void)? = nil,
milestone: String = ""
milestone: String = "",
labels: [LabelRow] = []
) -> [UIView] {
let titleLabel = UILabel()
titleLabel.text = title
@@ -1166,6 +1168,7 @@ private func detailViews(
milestoneLabel.accessibilityLabel = "Milestone \(milestone)"
views.append(milestoneLabel)
}
if !labels.isEmpty { views.append(IssueLabelsView(labels)) }
return views + [separator(), bodyView] + commentViews(comments, editComment: editComment)
}

View File

@@ -287,3 +287,74 @@ func configureIssueStateIcon(
icon.isAccessibilityElement = true
icon.accessibilityLabel = accessibilityLabel
}
func issueLabelView(_ label: LabelRow) -> UILabel {
let view = UILabel()
view.text = " \(label.name) "
view.font = .preferredFont(forTextStyle: .caption2)
view.adjustsFontForContentSizeCategory = true
view.textColor = label.light ? .black : .white
view.backgroundColor = UIColor(hex: label.color)
view.layer.cornerRadius = 9
view.clipsToBounds = true
view.accessibilityLabel = "Label \(label.name)"
return view
}
final class IssueLabelsView: UIView {
private let labels: [UILabel]
private var contentHeight: CGFloat
private let spacing: CGFloat = 6
init(_ rows: [LabelRow]) {
labels = rows.map(issueLabelView)
contentHeight = labels.first?.intrinsicContentSize.height ?? 0
super.init(frame: .zero)
labels.forEach(addSubview)
NotificationCenter.default.addObserver(
self,
selector: #selector(contentSizeCategoryDidChange),
name: UIContentSizeCategory.didChangeNotification,
object: nil
)
}
@available(*, unavailable)
required init?(coder: NSCoder) { fatalError("init(coder:) is not supported") }
override var intrinsicContentSize: CGSize {
CGSize(width: UIView.noIntrinsicMetric, height: contentHeight)
}
override func layoutSubviews() {
super.layoutSubviews()
guard bounds.width > 0 else { return }
var origin = CGPoint.zero
var rowHeight: CGFloat = 0
for label in labels {
let size = label.sizeThatFits(
CGSize(width: bounds.width, height: .greatestFiniteMagnitude)
)
if origin.x > 0, origin.x + size.width > bounds.width {
origin.x = 0
origin.y += rowHeight + spacing
rowHeight = 0
}
label.frame = CGRect(origin: origin, size: size)
origin.x += size.width + spacing
rowHeight = max(rowHeight, size.height)
}
let height = origin.y + rowHeight
if contentHeight != height {
contentHeight = height
invalidateIntrinsicContentSize()
}
}
@objc private func contentSizeCategoryDidChange() {
labels.forEach { $0.font = .preferredFont(forTextStyle: .caption2) }
contentHeight = labels.first?.intrinsicContentSize.height ?? 0
invalidateIntrinsicContentSize()
setNeedsLayout()
}
}