Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Improve ContactList layout and improve subtitle line breaking #18

Merged
merged 3 commits into from
Nov 5, 2023
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
4 changes: 2 additions & 2 deletions Package.swift
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
// swift-tools-version:5.7
// swift-tools-version:5.9

//
// This source file is part of the Stanford Spezi open-source project
Expand All @@ -15,7 +15,7 @@ let package = Package(
name: "SpeziContact",
defaultLocalization: "en",
platforms: [
.iOS(.v16)
.iOS(.v17)
],
products: [
.library(name: "SpeziContact", targets: ["SpeziContact"])
Expand Down
41 changes: 0 additions & 41 deletions Sources/SpeziContact/Contact Views/ContactCard.swift

This file was deleted.

48 changes: 30 additions & 18 deletions Sources/SpeziContact/Contact Views/ContactView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -26,23 +26,37 @@ public struct ContactView: View {
return (contact.contactOptions.dropLast(leftOverElements), contact.contactOptions.dropFirst(columnCount * numberOfRows))
}

private var subtitleLabel: Text {
private var subtitleText: Text {
var text = Text(verbatim: "")
if let title = contact.title {
text = text + Text(verbatim: title) // swiftlint:disable:this shorthand_operator
}
if contact.title != nil && contact.organization != nil {
text = text + Text(verbatim: " - ") // swiftlint:disable:this shorthand_operator
}
if let organization = contact.organization {
text = text + Text(verbatim: organization) // swiftlint:disable:this shorthand_operator
}
return text
}

private var subtitleAccessibilityLabel: Text {
var text: Text?
if let title = contact.title {
text = Text(verbatim: title)
}

if let organization = contact.organization {
if let titleText = text {
text = titleText + Text(" ") + Text("TITLE_AT_ORG", bundle: .module) + Text(" ") + Text(verbatim: organization)
text = titleText + Text(" at \(organization)", bundle: .module, comment: "Accessibility label: ' at <organization>'")
} else {
text = Text(verbatim: organization)
}
}

return text ?? Text(verbatim: "")
}

public var body: some View {
VStack {
header
Expand All @@ -60,7 +74,7 @@ public struct ContactView: View {
addressButton
}
}

private var header: some View {
HStack(spacing: 0) {
UserProfileView(name: contact.name) {
Expand All @@ -74,25 +88,19 @@ public struct ContactView: View {
let name = contact.name.formatted(.name(style: .long))
Text(verbatim: name)
.font(.title3.bold())
.accessibilityLabel(Text("CONTACT \(name)", bundle: .module))
.accessibilityLabel(Text("Contact: \(name)", bundle: .module, comment: "Accessibility Label"))
.accessibilityAddTraits(.isHeader)

if contact.title != nil || contact.organization != nil {
HStack(spacing: 0) {
if let title = contact.title {
Text(verbatim: title)
}
if contact.title != nil && contact.organization != nil {
Text(" - ")
}
if let organization = contact.organization {
Text(verbatim: organization)
}
subtitleText
.lineLimit(1...2)
.fixedSize(horizontal: false, vertical: true)
}
.foregroundColor(Color(.secondaryLabel))
.font(.subheadline)
.accessibilityRepresentation {
subtitleLabel
subtitleAccessibilityLabel
}
}
}
Expand All @@ -104,7 +112,7 @@ public struct ContactView: View {
private var contactSection: some View {
VStack(spacing: 8) {
LazyVGrid(
columns: [GridItem(.adaptive(minimum: 100))],
columns: [GridItem(.adaptive(minimum: 95))],
alignment: .center,
spacing: 8
) {
Expand Down Expand Up @@ -135,7 +143,7 @@ public struct ContactView: View {
.foregroundStyle(Color(uiColor: .secondarySystemBackground))
HStack(alignment: .top) {
VStack(alignment: .leading, spacing: 4) {
Text("CONTACT_ADDRESS", bundle: .module)
Text("Address", bundle: .module, comment: "Contact Button Title")
.foregroundColor(.accentColor)
Text(verbatim: CNPostalAddressFormatter().string(from: address))
.multilineTextAlignment(.leading)
Expand All @@ -151,7 +159,11 @@ public struct ContactView: View {
}
.fixedSize(horizontal: false, vertical: true)
}
.accessibilityLabel(Text("ADDRESS_NAVIGATE \(Text(verbatim: CNPostalAddressFormatter().string(from: address)))", bundle: .module))
.accessibilityLabel(Text(
"Address: \(Text(verbatim: CNPostalAddressFormatter().string(from: address)))",
bundle: .module,
comment: "Accessibility Label"
))
} else {
EmptyView()
}
Expand Down
13 changes: 6 additions & 7 deletions Sources/SpeziContact/Contact Views/ContactsList.swift
Original file line number Diff line number Diff line change
Expand Up @@ -22,15 +22,14 @@


public var body: some View {
ScrollView(.vertical) {
List {
ForEach(contacts, id: \.id) { contact in
ContactCard(contact: contact)
.padding(.horizontal)
.padding(.vertical, 6)
Section {
ContactView(contact: contact)
.buttonStyle(.plain) // ensure the whole list row doesn't render as a button
}
}
.padding(.vertical, 6)
}
.background(Color(.systemGroupedBackground))
}


Expand All @@ -53,7 +52,7 @@
ContactView_Previews.mock
]
)
.navigationTitle("Contacts")
.navigationTitle(Text(verbatim: "Contacts"))

Check warning on line 55 in Sources/SpeziContact/Contact Views/ContactsList.swift

View check run for this annotation

Codecov / codecov/patch

Sources/SpeziContact/Contact Views/ContactsList.swift#L55

Added line #L55 was not covered by tests
.background(Color(.systemGroupedBackground))
}
}
Expand Down
6 changes: 3 additions & 3 deletions Sources/SpeziContact/Models/Contact.swift
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ public struct Contact {
public let image: Image?
/// The title of the individual.
public let title: String?
/// The desciption of the individual.
/// The description of the individual.
public let description: String?
/// The organization of the individual.
public let organization: String?
Expand All @@ -30,11 +30,11 @@ public struct Contact {


/// - Parameters:
/// - id: Identiifer of the `Contact` instance.
/// - id: Identifier of the `Contact` instance.
/// - name: The name of the individual. Ideally provide at least a first and given name.
/// - image: The image of the ``Contact``.
/// - title: The title of the individual.
/// - description: The desciption of the individual.
/// - description: The description of the individual.
/// - organization: The organization of the individual.
/// - address: The address of the individual.
/// - contactOptions: The contact options of the individual.
Expand Down
24 changes: 14 additions & 10 deletions Sources/SpeziContact/Models/ContactOption.swift
Original file line number Diff line number Diff line change
Expand Up @@ -51,12 +51,12 @@
public static func call(_ number: String) -> ContactOption {
ContactOption(
image: Image(systemName: "phone.fill"),
title: String(localized: "CONTACT_OPTION_CALL", bundle: .module)
title: String(localized: "Call", bundle: .module, comment: "Contact Option")
) {
guard let url = URL(string: "tel://\(number)"), UIApplication.shared.canOpenURL(url) else {
presentAlert(
title: String(localized: "CONTACT_OPTION_CALL", bundle: .module),
message: String(localized: "CONTACT_OPTION_CALL_MANUAL \(number)", bundle: .module)
title: String(localized: "Call", bundle: .module),
message: String(localized: "Call unavailable. You can manually reach out to \(number)", bundle: .module, comment: "Call unavailable. Manual approach.")
)
return
}
Expand All @@ -69,12 +69,12 @@
public static func text(_ number: String) -> ContactOption {
ContactOption(
image: Image(systemName: "message.fill"),
title: String(localized: "CONTACT_OPTION_TEXT", bundle: .module)
title: String(localized: "Text", bundle: .module, comment: "Contact Option")
) {
guard let url = URL(string: "sms:\(number)"), UIApplication.shared.canOpenURL(url) else {
presentAlert(
title: String(localized: "CONTACT_OPTION_TEXT", bundle: .module),
message: String(localized: "CONTACT_OPTION_TEXT_MANUAL \(number)", bundle: .module)
title: String(localized: "Text", bundle: .module),
message: String(localized: "Text unavailable. You can manually reach out to \(number)", bundle: .module, comment: "Text unavailable. Manual approach.")

Check warning on line 77 in Sources/SpeziContact/Models/ContactOption.swift

View check run for this annotation

Codecov / codecov/patch

Sources/SpeziContact/Models/ContactOption.swift#L76-L77

Added lines #L76 - L77 were not covered by tests
)
return
}
Expand All @@ -90,13 +90,17 @@
public static func email(addresses: [String], subject: String? = nil) -> ContactOption {
ContactOption(
image: Image(systemName: "envelope.fill"),
title: String(localized: "CONTACT_OPTION_EMAIL", bundle: .module)
title: String(localized: "Email", bundle: .module, comment: "Contact Option")
) {
guard let subject = (subject ?? "").addingPercentEncoding(withAllowedCharacters: .urlQueryAllowed),
let url = URL(string: "mailto:\(addresses.joined(separator: ";"))?subject=\(subject)"), UIApplication.shared.canOpenURL(url) else {
presentAlert(
title: String(localized: "CONTACT_OPTION_EMAIL", bundle: .module),
message: String(localized: "CONTACT_OPTION_EMAIL_MANUAL \(addresses.joined(separator: ", "))", bundle: .module)
title: String(localized: "Email", bundle: .module),
message: String(
localized: "Email unavailable. You can manually reach out to \(addresses.joined(separator: ", "))",
bundle: .module,
comment: "Email unavailable. Manual approach."
)
)
return
}
Expand All @@ -106,7 +110,7 @@

private static func presentAlert(title: String, message: String) {
let alert = UIAlertController(title: title, message: message, preferredStyle: .alert)
alert.addAction(UIAlertAction(title: String(localized: "CONTACT_MANUAL_DISMISS", bundle: .module), style: .default))
alert.addAction(UIAlertAction(title: String(localized: "Ok", bundle: .module, comment: "Dismiss alert"), style: .default))
rootViewController?.present(alert, animated: true, completion: nil)
}
}