forked from shadowfacts/Tusker
parent
45c844b065
commit
ab19922530
|
@ -167,5 +167,12 @@ extension Account {
|
|||
public struct Field: Codable {
|
||||
public let name: String
|
||||
public let value: String
|
||||
public let verifiedAt: Date?
|
||||
|
||||
enum CodingKeys: String, CodingKey {
|
||||
case name
|
||||
case value
|
||||
case verifiedAt = "verified_at"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -7,13 +7,15 @@
|
|||
//
|
||||
|
||||
import UIKit
|
||||
import Pachyderm
|
||||
import SwiftUI
|
||||
|
||||
class ProfileFieldsView: UIView {
|
||||
|
||||
weak var delegate: ProfileHeaderViewDelegate?
|
||||
|
||||
private let stack = UIStackView()
|
||||
private var fieldViews: [(EmojiLabel, ContentTextView)] = []
|
||||
private var fieldViews: [(EmojiLabel, ProfileFieldValueView)] = []
|
||||
private var fieldConstraints: [NSLayoutConstraint] = []
|
||||
|
||||
private var isUsingSingleColumn: Bool = false
|
||||
|
@ -80,16 +82,11 @@ class ProfileFieldsView: UIView {
|
|||
nameLabel.setEmojis(account.emojis, identifier: account.id)
|
||||
nameLabel.setContentCompressionResistancePriority(.defaultLow, for: .horizontal)
|
||||
|
||||
let valueTextView = ContentTextView()
|
||||
valueTextView.isSelectable = false
|
||||
valueTextView.defaultFont = .preferredFont(forTextStyle: .body)
|
||||
valueTextView.adjustsFontForContentSizeCategory = true
|
||||
valueTextView.setTextFromHtml(field.value)
|
||||
valueTextView.setEmojis(account.emojis, identifier: account.id)
|
||||
valueTextView.navigationDelegate = delegate
|
||||
valueTextView.setContentCompressionResistancePriority(.defaultLow, for: .horizontal)
|
||||
let valueView = ProfileFieldValueView(field: field, account: account)
|
||||
valueView.navigationDelegate = delegate
|
||||
valueView.setContentCompressionResistancePriority(.defaultLow, for: .horizontal)
|
||||
|
||||
fieldViews.append((nameLabel, valueTextView))
|
||||
fieldViews.append((nameLabel, valueView))
|
||||
}
|
||||
|
||||
configureFields()
|
||||
|
@ -121,7 +118,7 @@ class ProfileFieldsView: UIView {
|
|||
}
|
||||
name.textAlignment = .natural
|
||||
stack.addArrangedSubview(name)
|
||||
value.textAlignment = .natural
|
||||
value.setTextAlignment(.natural)
|
||||
stack.addArrangedSubview(value)
|
||||
}
|
||||
} else {
|
||||
|
@ -137,7 +134,7 @@ class ProfileFieldsView: UIView {
|
|||
name.textAlignment = .right
|
||||
name.translatesAutoresizingMaskIntoConstraints = false
|
||||
|
||||
value.textAlignment = .left
|
||||
value.setTextAlignment(.left)
|
||||
value.translatesAutoresizingMaskIntoConstraints = false
|
||||
|
||||
let fieldContainer = UIView()
|
||||
|
@ -165,3 +162,159 @@ class ProfileFieldsView: UIView {
|
|||
}
|
||||
|
||||
}
|
||||
|
||||
private class ProfileFieldValueView: UIView {
|
||||
weak var navigationDelegate: TuskerNavigationDelegate? {
|
||||
didSet {
|
||||
textView.navigationDelegate = navigationDelegate
|
||||
}
|
||||
}
|
||||
|
||||
private let account: AccountMO
|
||||
private let field: Account.Field
|
||||
|
||||
private let textView = ContentTextView()
|
||||
private var iconView: UIView?
|
||||
|
||||
init(field: Account.Field, account: AccountMO) {
|
||||
self.account = account
|
||||
self.field = field
|
||||
|
||||
super.init(frame: .zero)
|
||||
|
||||
textView.isSelectable = false
|
||||
textView.defaultFont = .preferredFont(forTextStyle: .body)
|
||||
textView.adjustsFontForContentSizeCategory = true
|
||||
textView.setTextFromHtml(field.value)
|
||||
textView.setEmojis(account.emojis, identifier: account.id)
|
||||
textView.setContentCompressionResistancePriority(.defaultLow, for: .horizontal)
|
||||
textView.translatesAutoresizingMaskIntoConstraints = false
|
||||
addSubview(textView)
|
||||
|
||||
let textViewTrailingConstraint: NSLayoutConstraint
|
||||
|
||||
if field.verifiedAt != nil {
|
||||
var config = UIButton.Configuration.plain()
|
||||
config.image = UIImage(systemName: "checkmark")
|
||||
config.baseForegroundColor = .systemGreen
|
||||
let icon = UIButton(configuration: config)
|
||||
self.iconView = icon
|
||||
icon.translatesAutoresizingMaskIntoConstraints = false
|
||||
icon.setContentHuggingPriority(.defaultHigh, for: .horizontal)
|
||||
icon.addTarget(self, action: #selector(verifiedIconTapped), for: .touchUpInside)
|
||||
icon.isPointerInteractionEnabled = true
|
||||
icon.accessibilityLabel = "Verified link"
|
||||
addSubview(icon)
|
||||
textViewTrailingConstraint = textView.trailingAnchor.constraint(equalTo: icon.leadingAnchor, constant: -4)
|
||||
NSLayoutConstraint.activate([
|
||||
icon.centerYAnchor.constraint(equalTo: centerYAnchor),
|
||||
icon.trailingAnchor.constraint(equalTo: trailingAnchor),
|
||||
])
|
||||
} else {
|
||||
textViewTrailingConstraint = textView.trailingAnchor.constraint(equalTo: trailingAnchor)
|
||||
}
|
||||
|
||||
NSLayoutConstraint.activate([
|
||||
textView.leadingAnchor.constraint(equalTo: leadingAnchor),
|
||||
textViewTrailingConstraint,
|
||||
textView.topAnchor.constraint(equalTo: topAnchor),
|
||||
textView.bottomAnchor.constraint(equalTo: bottomAnchor),
|
||||
])
|
||||
|
||||
}
|
||||
|
||||
required init?(coder: NSCoder) {
|
||||
fatalError("init(coder:) has not been implemented")
|
||||
}
|
||||
|
||||
func setTextAlignment(_ alignment: NSTextAlignment) {
|
||||
textView.textAlignment = alignment
|
||||
}
|
||||
|
||||
@objc private func verifiedIconTapped() {
|
||||
guard let navigationDelegate else {
|
||||
return
|
||||
}
|
||||
let view = ProfileFieldVerificationView(
|
||||
acct: account.acct,
|
||||
verifiedAt: field.verifiedAt!,
|
||||
linkText: textView.text,
|
||||
navigationDelegate: navigationDelegate
|
||||
)
|
||||
let host = UIHostingController(rootView: view)
|
||||
let toPresent: UIViewController
|
||||
if traitCollection.horizontalSizeClass == .compact || traitCollection.verticalSizeClass == .compact {
|
||||
toPresent = UINavigationController(rootViewController: host)
|
||||
toPresent.modalPresentationStyle = .pageSheet
|
||||
let sheetPresentationController = toPresent.sheetPresentationController!
|
||||
sheetPresentationController.detents = [
|
||||
.medium()
|
||||
]
|
||||
} else {
|
||||
host.modalPresentationStyle = .popover
|
||||
let popoverPresentationController = host.popoverPresentationController!
|
||||
popoverPresentationController.sourceView = iconView
|
||||
host.preferredContentSize = host.sizeThatFits(in: CGSize(width: 400, height: CGFloat.infinity))
|
||||
toPresent = host
|
||||
}
|
||||
navigationDelegate.present(toPresent, animated: true)
|
||||
}
|
||||
}
|
||||
|
||||
private struct ProfileFieldVerificationView: View {
|
||||
let acct: String
|
||||
let verifiedAt: Date
|
||||
let linkText: String
|
||||
let navigationDelegate: TuskerNavigationDelegate
|
||||
|
||||
var body: some View {
|
||||
VStack(alignment: .leading, spacing: 8) {
|
||||
firstLine
|
||||
secondLine
|
||||
}
|
||||
.padding()
|
||||
.navigationTitle(Text("Verified Link"))
|
||||
.navigationBarTitleDisplayMode(.inline)
|
||||
.toolbar {
|
||||
ToolbarItem(placement: .cancellationAction) {
|
||||
Button("Done") {
|
||||
navigationDelegate.dismiss(animated: true)
|
||||
}
|
||||
}
|
||||
}
|
||||
.environment(\.openURL, OpenURLAction(handler: { url in
|
||||
// dismiss the sheet/popover first
|
||||
navigationDelegate.dismiss(animated: true) {
|
||||
navigationDelegate.selected(url: url)
|
||||
}
|
||||
return .handled
|
||||
}))
|
||||
}
|
||||
|
||||
private var firstLine: Text {
|
||||
var attrStr: AttributedString = "This link has been verified by your instance, "
|
||||
var instance = AttributedString(navigationDelegate.apiController!.instanceURL.host!)
|
||||
instance.font = .body.bold()
|
||||
attrStr += instance
|
||||
attrStr += "."
|
||||
return Text(attrStr)
|
||||
}
|
||||
|
||||
private var secondLine: Text {
|
||||
var attrStr: AttributedString = "The page at "
|
||||
var linkStr: AttributedString
|
||||
if linkText.count > 43 {
|
||||
linkStr = AttributedString(linkText.prefix(40) + "…")
|
||||
} else {
|
||||
linkStr = AttributedString(linkText)
|
||||
}
|
||||
linkStr.link = URL(string: linkText)
|
||||
attrStr += linkStr
|
||||
attrStr += " was confirmed to link back to "
|
||||
var acctStr = AttributedString("@\(acct)")
|
||||
acctStr.font = .body.bold()
|
||||
attrStr += acctStr
|
||||
attrStr += AttributedString(" as of \(verifiedAt.formatted(date: .abbreviated, time: .shortened)).")
|
||||
return Text(attrStr)
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue