Show issue state beside title
This commit is contained in:
@@ -133,8 +133,10 @@ xcrun simctl launch booted de.rfc1437.gotcha
|
|||||||
- [ ] Open a repository; the issue list defaults to the saved Open/Closed filter.
|
- [ ] Open a repository; the issue list defaults to the saved Open/Closed filter.
|
||||||
- [ ] Change the native filter menu between Open and Closed; the checkmark,
|
- [ ] Change the native filter menu between Open and Closed; the checkmark,
|
||||||
rows, and persisted selection update.
|
rows, and persisted selection update.
|
||||||
- [ ] Open an issue and verify title, state/author metadata, Markdown body, and
|
- [ ] Open both an open and a closed issue. A green open or purple closed icon
|
||||||
comments. Links and selectable text use normal iOS interaction.
|
appears beside the title and VoiceOver announces the state; author
|
||||||
|
metadata, Markdown body, and comments remain correct. Links and
|
||||||
|
selectable text use normal iOS interaction.
|
||||||
|
|
||||||
## Repositories and commits
|
## Repositories and commits
|
||||||
|
|
||||||
|
|||||||
@@ -59,6 +59,7 @@ pub struct CommentRow {
|
|||||||
#[derive(Clone, uniffi::Record)]
|
#[derive(Clone, uniffi::Record)]
|
||||||
pub struct IssuePage {
|
pub struct IssuePage {
|
||||||
pub title: String,
|
pub title: String,
|
||||||
|
pub state: String,
|
||||||
pub meta: String,
|
pub meta: String,
|
||||||
pub milestone: String,
|
pub milestone: String,
|
||||||
pub body: String,
|
pub body: String,
|
||||||
@@ -355,6 +356,11 @@ pub fn issue_page(details: IssueDetails) -> IssuePage {
|
|||||||
.title
|
.title
|
||||||
.clone()
|
.clone()
|
||||||
.unwrap_or_else(|| "Untitled issue".into()),
|
.unwrap_or_else(|| "Untitled issue".into()),
|
||||||
|
state: details
|
||||||
|
.issue
|
||||||
|
.state
|
||||||
|
.clone()
|
||||||
|
.unwrap_or_else(|| "unknown".into()),
|
||||||
meta: issue_meta(&details.issue),
|
meta: issue_meta(&details.issue),
|
||||||
milestone: issue_milestone(&details.issue),
|
milestone: issue_milestone(&details.issue),
|
||||||
body: details
|
body: details
|
||||||
@@ -860,4 +866,17 @@ mod tests {
|
|||||||
};
|
};
|
||||||
assert_eq!(issue_rows(&[issue])[0].milestone, "Version 1");
|
assert_eq!(issue_rows(&[issue])[0].milestone, "Version 1");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn issue_page_exposes_state() {
|
||||||
|
let page = issue_page(IssueDetails {
|
||||||
|
issue: models::Issue {
|
||||||
|
state: Some("closed".into()),
|
||||||
|
..Default::default()
|
||||||
|
},
|
||||||
|
comments: Vec::new(),
|
||||||
|
});
|
||||||
|
|
||||||
|
assert_eq!(page.state, "closed");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1659,6 +1659,7 @@ public func FfiConverterTypeHomePage_lower(_ value: HomePage) -> RustBuffer {
|
|||||||
|
|
||||||
public struct IssuePage: Equatable, Hashable {
|
public struct IssuePage: Equatable, Hashable {
|
||||||
public var title: String
|
public var title: String
|
||||||
|
public var state: String
|
||||||
public var meta: String
|
public var meta: String
|
||||||
public var milestone: String
|
public var milestone: String
|
||||||
public var body: String
|
public var body: String
|
||||||
@@ -1666,8 +1667,9 @@ public struct IssuePage: Equatable, Hashable {
|
|||||||
|
|
||||||
// Default memberwise initializers are never public by default, so we
|
// Default memberwise initializers are never public by default, so we
|
||||||
// declare one manually.
|
// declare one manually.
|
||||||
public init(title: String, meta: String, milestone: String, body: String, comments: [CommentRow]) {
|
public init(title: String, state: String, meta: String, milestone: String, body: String, comments: [CommentRow]) {
|
||||||
self.title = title
|
self.title = title
|
||||||
|
self.state = state
|
||||||
self.meta = meta
|
self.meta = meta
|
||||||
self.milestone = milestone
|
self.milestone = milestone
|
||||||
self.body = body
|
self.body = body
|
||||||
@@ -1691,6 +1693,7 @@ public struct FfiConverterTypeIssuePage: FfiConverterRustBuffer {
|
|||||||
return
|
return
|
||||||
try IssuePage(
|
try IssuePage(
|
||||||
title: FfiConverterString.read(from: &buf),
|
title: FfiConverterString.read(from: &buf),
|
||||||
|
state: FfiConverterString.read(from: &buf),
|
||||||
meta: FfiConverterString.read(from: &buf),
|
meta: FfiConverterString.read(from: &buf),
|
||||||
milestone: FfiConverterString.read(from: &buf),
|
milestone: FfiConverterString.read(from: &buf),
|
||||||
body: FfiConverterString.read(from: &buf),
|
body: FfiConverterString.read(from: &buf),
|
||||||
@@ -1700,6 +1703,7 @@ public struct FfiConverterTypeIssuePage: FfiConverterRustBuffer {
|
|||||||
|
|
||||||
public static func write(_ value: IssuePage, into buf: inout [UInt8]) {
|
public static func write(_ value: IssuePage, into buf: inout [UInt8]) {
|
||||||
FfiConverterString.write(value.title, into: &buf)
|
FfiConverterString.write(value.title, into: &buf)
|
||||||
|
FfiConverterString.write(value.state, into: &buf)
|
||||||
FfiConverterString.write(value.meta, into: &buf)
|
FfiConverterString.write(value.meta, into: &buf)
|
||||||
FfiConverterString.write(value.milestone, into: &buf)
|
FfiConverterString.write(value.milestone, into: &buf)
|
||||||
FfiConverterString.write(value.body, into: &buf)
|
FfiConverterString.write(value.body, into: &buf)
|
||||||
|
|||||||
@@ -103,6 +103,7 @@ final class IssueViewController: MarkdownPageViewController {
|
|||||||
)
|
)
|
||||||
replaceContent(detailViews(
|
replaceContent(detailViews(
|
||||||
title: page.title,
|
title: page.title,
|
||||||
|
issueState: page.state,
|
||||||
meta: page.meta,
|
meta: page.meta,
|
||||||
body: page.body,
|
body: page.body,
|
||||||
comments: page.comments,
|
comments: page.comments,
|
||||||
@@ -665,6 +666,7 @@ final class DiffViewController: UIViewController {
|
|||||||
|
|
||||||
private func detailViews(
|
private func detailViews(
|
||||||
title: String,
|
title: String,
|
||||||
|
issueState: String? = nil,
|
||||||
meta: String,
|
meta: String,
|
||||||
body: String,
|
body: String,
|
||||||
comments: [CommentRow],
|
comments: [CommentRow],
|
||||||
@@ -680,7 +682,7 @@ private func detailViews(
|
|||||||
metaLabel.textColor = .secondaryLabel
|
metaLabel.textColor = .secondaryLabel
|
||||||
metaLabel.numberOfLines = 0
|
metaLabel.numberOfLines = 0
|
||||||
let bodyView = markdownView(body)
|
let bodyView = markdownView(body)
|
||||||
var views: [UIView] = [titleLabel, metaLabel]
|
var views: [UIView] = [issueState.map { issueTitle(titleLabel, state: $0) } ?? titleLabel, metaLabel]
|
||||||
if !milestone.isEmpty {
|
if !milestone.isEmpty {
|
||||||
let milestoneLabel = UILabel()
|
let milestoneLabel = UILabel()
|
||||||
milestoneLabel.attributedText = symbolText(
|
milestoneLabel.attributedText = symbolText(
|
||||||
@@ -695,6 +697,31 @@ private func detailViews(
|
|||||||
return views + [separator(), bodyView] + commentViews(comments)
|
return views + [separator(), bodyView] + commentViews(comments)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private func issueTitle(_ titleLabel: UILabel, state: String) -> UIView {
|
||||||
|
let symbol: String
|
||||||
|
let color: UIColor
|
||||||
|
let accessibilityLabel: String
|
||||||
|
switch state {
|
||||||
|
case "open":
|
||||||
|
(symbol, color, accessibilityLabel) = ("exclamationmark.circle.fill", .systemGreen, "Open issue")
|
||||||
|
case "closed":
|
||||||
|
(symbol, color, accessibilityLabel) = ("checkmark.circle.fill", .systemPurple, "Closed issue")
|
||||||
|
default:
|
||||||
|
(symbol, color, accessibilityLabel) = ("questionmark.circle.fill", .systemGray, "Unknown issue state")
|
||||||
|
}
|
||||||
|
let icon = UIImageView(image: UIImage(systemName: symbol))
|
||||||
|
icon.tintColor = color
|
||||||
|
icon.preferredSymbolConfiguration = UIImage.SymbolConfiguration(textStyle: .title1)
|
||||||
|
icon.setContentHuggingPriority(.required, for: .horizontal)
|
||||||
|
icon.isAccessibilityElement = true
|
||||||
|
icon.accessibilityLabel = accessibilityLabel
|
||||||
|
let stack = UIStackView(arrangedSubviews: [icon, titleLabel])
|
||||||
|
stack.axis = .horizontal
|
||||||
|
stack.alignment = .firstBaseline
|
||||||
|
stack.spacing = 8
|
||||||
|
return stack
|
||||||
|
}
|
||||||
|
|
||||||
private func commentViews(_ comments: [CommentRow]) -> [UIView] {
|
private func commentViews(_ comments: [CommentRow]) -> [UIView] {
|
||||||
guard !comments.isEmpty else { return [] }
|
guard !comments.isEmpty else { return [] }
|
||||||
var views: [UIView] = [sectionLabel("Comments")]
|
var views: [UIView] = [sectionLabel("Comments")]
|
||||||
|
|||||||
Reference in New Issue
Block a user