227 lines
9.4 KiB
Swift
227 lines
9.4 KiB
Swift
//
|
|
// Copyright (c) 2020 Open Whisper Systems. All rights reserved.
|
|
//
|
|
|
|
import Foundation
|
|
import UIKit
|
|
|
|
// TODO: We should describe which state updates & when it is committed.
|
|
extension ConversationSettingsViewController {
|
|
|
|
private var subtitlePointSize: CGFloat {
|
|
return 12
|
|
}
|
|
|
|
private var threadName: String {
|
|
var threadName = contactsManager.displayNameWithSneakyTransaction(thread: thread)
|
|
|
|
if let contactThread = thread as? TSContactThread {
|
|
if let phoneNumber = contactThread.contactAddress.phoneNumber,
|
|
phoneNumber == threadName {
|
|
threadName = PhoneNumber.bestEffortFormatPartialUserSpecifiedText(toLookLikeAPhoneNumber: phoneNumber)
|
|
}
|
|
}
|
|
|
|
return threadName
|
|
}
|
|
|
|
private struct HeaderBuilder {
|
|
let viewController: ConversationSettingsViewController
|
|
|
|
var subviews = [UIView]()
|
|
|
|
init(viewController: ConversationSettingsViewController) {
|
|
self.viewController = viewController
|
|
|
|
addFirstSubviews()
|
|
}
|
|
|
|
mutating func addFirstSubviews() {
|
|
let avatarView = buildAvatarView()
|
|
|
|
let avatarWrapper = UIView.container()
|
|
avatarWrapper.addSubview(avatarView)
|
|
avatarView.autoPinEdgesToSuperviewEdges()
|
|
|
|
if let groupThread = viewController.thread as? TSGroupThread,
|
|
groupThread.groupModel.groupAvatarData == nil,
|
|
viewController.canEditConversationAttributes {
|
|
let cameraButton = GroupAttributesEditorHelper.buildCameraButtonForCorner()
|
|
avatarWrapper.addSubview(cameraButton)
|
|
cameraButton.autoPinEdge(toSuperviewEdge: .trailing)
|
|
cameraButton.autoPinEdge(toSuperviewEdge: .bottom)
|
|
}
|
|
|
|
subviews.append(avatarWrapper)
|
|
subviews.append(UIView.spacer(withHeight: 8))
|
|
subviews.append(buildThreadNameLabel())
|
|
}
|
|
|
|
func buildAvatarView() -> UIView {
|
|
let avatarImage = OWSAvatarBuilder.buildImage(thread: viewController.thread,
|
|
diameter: kLargeAvatarSize)
|
|
let avatarView = AvatarImageView(image: avatarImage)
|
|
let avatarSize = CGFloat(kLargeAvatarSize)
|
|
avatarView.autoSetDimension(.width, toSize: avatarSize)
|
|
avatarView.autoSetDimension(.height, toSize: avatarSize)
|
|
// Track the most recent avatar view.
|
|
viewController.avatarView = avatarView
|
|
return avatarView
|
|
}
|
|
|
|
func buildThreadNameLabel() -> UILabel {
|
|
let label = UILabel()
|
|
label.text = viewController.threadName
|
|
label.textColor = Theme.primaryTextColor
|
|
label.font = .ows_dynamicTypeTitle2
|
|
label.lineBreakMode = .byTruncatingTail
|
|
return label
|
|
}
|
|
|
|
mutating func addSubtitleLabel(text: String, font: UIFont? = nil) {
|
|
addSubtitleLabel(attributedText: NSAttributedString(string: text), font: font)
|
|
}
|
|
|
|
mutating func addSubtitleLabel(attributedText: NSAttributedString, font: UIFont? = nil) {
|
|
subviews.append(UIView.spacer(withHeight: 2))
|
|
subviews.append(buildHeaderSubtitleLabel(attributedText: attributedText, font: font))
|
|
}
|
|
|
|
func buildHeaderSubtitleLabel(attributedText: NSAttributedString,
|
|
font: UIFont?) -> UILabel {
|
|
let label = UILabel()
|
|
label.attributedText = attributedText
|
|
label.textColor = Theme.secondaryTextAndIconColor
|
|
if let font = font {
|
|
label.font = font
|
|
} else {
|
|
label.font = UIFont.ows_regularFont(withSize: viewController.subtitlePointSize)
|
|
}
|
|
label.lineBreakMode = .byTruncatingTail
|
|
return label
|
|
}
|
|
|
|
mutating func addLastSubviews() {
|
|
// TODO Message Request: In order to debug the profile is getting shared in the right moments,
|
|
// display the thread whitelist state in settings. Eventually we can probably delete this.
|
|
#if DEBUG
|
|
let viewController = self.viewController
|
|
let isThreadInProfileWhitelist =
|
|
viewController.databaseStorage.uiRead { transaction in
|
|
return viewController.profileManager.isThread(inProfileWhitelist: viewController.thread,
|
|
transaction: transaction)
|
|
}
|
|
let hasSharedProfile = String(format: "Whitelisted: %@", isThreadInProfileWhitelist ? "Yes" : "No")
|
|
addSubtitleLabel(text: hasSharedProfile)
|
|
#endif
|
|
}
|
|
|
|
func build() -> UIView {
|
|
let header = UIStackView(arrangedSubviews: subviews)
|
|
header.axis = .vertical
|
|
header.alignment = .center
|
|
header.layoutMargins = UIEdgeInsets(top: 0, leading: 18, bottom: 16, trailing: 18)
|
|
header.isLayoutMarginsRelativeArrangement = true
|
|
|
|
if viewController.canEditConversationAttributes {
|
|
header.addGestureRecognizer(UITapGestureRecognizer(target: viewController, action: #selector(conversationNameTouched)))
|
|
}
|
|
header.isUserInteractionEnabled = true
|
|
header.accessibilityIdentifier = UIView.accessibilityIdentifier(in: viewController, name: "mainSectionHeader")
|
|
|
|
return header
|
|
}
|
|
}
|
|
|
|
private func buildHeaderForGroup(groupThread: TSGroupThread) -> UIView {
|
|
var builder = HeaderBuilder(viewController: self)
|
|
|
|
let groupMembersText = String(format: NSLocalizedString("CONVERSATION_SETTINGS_GROUP_MEMBER_COUNT_FORMAT",
|
|
comment: "Format for the 'group member count' indicator in conversation settings view. Embeds {the number of group members}."),
|
|
OWSFormat.formatInt(groupThread.groupModel.groupMembership.nonPendingMembers.count))
|
|
builder.addSubtitleLabel(text: groupMembersText,
|
|
font: .ows_dynamicTypeBody)
|
|
|
|
builder.addLastSubviews()
|
|
|
|
let header = builder.build()
|
|
|
|
// This will only appear in internal, qa & dev builds.
|
|
if DebugFlags.groupsV2showV2Indicator {
|
|
let indicatorLabel = UILabel()
|
|
indicatorLabel.text = thread.isGroupV2Thread ? "v2" : "v1"
|
|
indicatorLabel.textColor = Theme.secondaryTextAndIconColor
|
|
indicatorLabel.font = .ows_dynamicTypeBody
|
|
header.addSubview(indicatorLabel)
|
|
indicatorLabel.autoPinEdge(toSuperviewMargin: .trailing)
|
|
indicatorLabel.autoPinEdge(toSuperviewMargin: .bottom)
|
|
}
|
|
|
|
return header
|
|
}
|
|
|
|
private func buildHeaderForContact(contactThread: TSContactThread) -> UIView {
|
|
var builder = HeaderBuilder(viewController: self)
|
|
|
|
let threadName = contactsManager.displayNameWithSneakyTransaction(thread: contactThread)
|
|
let recipientAddress = contactThread.contactAddress
|
|
if let phoneNumber = recipientAddress.phoneNumber {
|
|
let formattedPhoneNumber =
|
|
PhoneNumber.bestEffortFormatPartialUserSpecifiedText(toLookLikeAPhoneNumber: phoneNumber)
|
|
if threadName != formattedPhoneNumber {
|
|
builder.addSubtitleLabel(text: formattedPhoneNumber)
|
|
}
|
|
}
|
|
|
|
if let username = (databaseStorage.uiRead { transaction in
|
|
return self.profileManager.username(for: recipientAddress, transaction: transaction)
|
|
}),
|
|
username.count > 0 {
|
|
if let formattedUsername = CommonFormats.formatUsername(username),
|
|
threadName != formattedUsername {
|
|
builder.addSubtitleLabel(text: formattedUsername)
|
|
}
|
|
}
|
|
|
|
if !RemoteConfig.messageRequests
|
|
&& !contactsManager.hasNameInSystemContacts(for: recipientAddress) {
|
|
if let profileName = contactsManager.formattedProfileName(for: recipientAddress) {
|
|
builder.addSubtitleLabel(text: profileName)
|
|
}
|
|
}
|
|
|
|
#if TESTABLE_BUILD
|
|
let uuidText = String(format: "UUID: %@", contactThread.contactAddress.uuid?.uuidString ?? "Unknown")
|
|
builder.addSubtitleLabel(text: uuidText)
|
|
#endif
|
|
|
|
let isVerified = identityManager.verificationState(for: recipientAddress) == .verified
|
|
if isVerified {
|
|
let subtitle = NSMutableAttributedString()
|
|
// "checkmark"
|
|
subtitle.append("\u{f00c} ",
|
|
attributes: [
|
|
.font: UIFont.ows_fontAwesomeFont(subtitlePointSize)
|
|
])
|
|
subtitle.append(NSLocalizedString("PRIVACY_IDENTITY_IS_VERIFIED_BADGE",
|
|
comment: "Badge indicating that the user is verified."))
|
|
builder.addSubtitleLabel(attributedText: subtitle)
|
|
}
|
|
|
|
builder.addLastSubviews()
|
|
|
|
return builder.build()
|
|
}
|
|
|
|
func buildMainHeader() -> UIView {
|
|
if let groupThread = thread as? TSGroupThread {
|
|
return buildHeaderForGroup(groupThread: groupThread)
|
|
} else if let contactThread = thread as? TSContactThread {
|
|
return buildHeaderForContact(contactThread: contactThread)
|
|
} else {
|
|
owsFailDebug("Invalid thread.")
|
|
return UIView()
|
|
}
|
|
}
|
|
}
|