Convert ContactCellConfiguration and ContactCellAccessoryView to structs.
This commit is contained in:
parent
d535e8bfe2
commit
2260eb9b8f
@ -125,6 +125,7 @@ extension NewCallViewController: RecipientContextMenuHelperDelegate {
|
||||
// MARK: - RecipientPickerDelegate
|
||||
|
||||
extension NewCallViewController: RecipientPickerDelegate, UsernameLinkScanDelegate {
|
||||
|
||||
func recipientPicker(
|
||||
_ recipientPickerViewController: RecipientPickerViewController,
|
||||
selectionStyleForRecipient recipient: PickedRecipient,
|
||||
@ -133,7 +134,10 @@ extension NewCallViewController: RecipientPickerDelegate, UsernameLinkScanDelega
|
||||
return .default
|
||||
}
|
||||
|
||||
func recipientPicker(_ recipientPickerViewController: RecipientPickerViewController, didSelectRecipient recipient: PickedRecipient) {
|
||||
func recipientPicker(
|
||||
_ recipientPickerViewController: RecipientPickerViewController,
|
||||
didSelectRecipient recipient: PickedRecipient,
|
||||
) {
|
||||
switch recipient.identifier {
|
||||
case let .address(address):
|
||||
let thread = TSContactThread.getOrCreateThread(contactAddress: address)
|
||||
@ -143,7 +147,12 @@ extension NewCallViewController: RecipientPickerDelegate, UsernameLinkScanDelega
|
||||
}
|
||||
}
|
||||
|
||||
func recipientPicker(_ recipientPickerViewController: RecipientPickerViewController, accessoryViewForRecipient recipient: PickedRecipient, transaction: DBReadTransaction) -> ContactCellAccessoryView? {
|
||||
func recipientPicker(
|
||||
_ recipientPickerViewController: RecipientPickerViewController,
|
||||
contactCellAccessoryForRecipient recipient: PickedRecipient,
|
||||
transaction: DBReadTransaction,
|
||||
) -> ContactCellView.Accessory? {
|
||||
|
||||
let stackView = UIStackView()
|
||||
stackView.axis = .horizontal
|
||||
stackView.spacing = 20
|
||||
|
||||
@ -159,7 +159,7 @@ class MessageUserSubsetSheet: OWSTableSheetViewController {
|
||||
|
||||
cell.selectionStyle = .none
|
||||
|
||||
let configuration = ContactCellConfiguration(address: address, localUserDisplayMode: .asLocalUser)
|
||||
var configuration = ContactCellView.Configuration(address: address, localUserDisplayMode: .asLocalUser)
|
||||
configuration.forceDarkAppearance = self?.forceDarkMode ?? false
|
||||
|
||||
if
|
||||
|
||||
@ -95,10 +95,7 @@ class BlockListViewController: OWSTableViewController2 {
|
||||
OWSTableItem(
|
||||
dequeueCellBlock: { [weak self] tableView in
|
||||
let cell = tableView.dequeueReusableCell(withIdentifier: ContactTableViewCell.reuseIdentifier) as! ContactTableViewCell
|
||||
let config = ContactCellConfiguration(
|
||||
address: address,
|
||||
localUserDisplayMode: .asUser,
|
||||
)
|
||||
let config = ContactCellView.Configuration(address: address, localUserDisplayMode: .asUser)
|
||||
if self != nil {
|
||||
SSKEnvironment.shared.databaseStorageRef.read { transaction in
|
||||
cell.configure(configuration: config, transaction: transaction)
|
||||
|
||||
@ -89,10 +89,7 @@ class GroupStorySettingsViewController: OWSTableViewController2 {
|
||||
return UITableViewCell()
|
||||
}
|
||||
|
||||
SSKEnvironment.shared.databaseStorageRef.read { transaction in
|
||||
let configuration = ContactCellConfiguration(address: viewerAddress, localUserDisplayMode: .asLocalUser)
|
||||
cell.configure(configuration: configuration, transaction: transaction)
|
||||
}
|
||||
cell.configureWithSneakyTransaction(address: viewerAddress, localUserDisplayMode: .asLocalUser)
|
||||
|
||||
return cell
|
||||
}, actionBlock: { [weak self] in
|
||||
|
||||
@ -121,10 +121,7 @@ final class PrivateStorySettingsViewController: OWSTableViewController2 {
|
||||
return UITableViewCell()
|
||||
}
|
||||
|
||||
SSKEnvironment.shared.databaseStorageRef.read { transaction in
|
||||
let configuration = ContactCellConfiguration(address: viewerAddress, localUserDisplayMode: .asLocalUser)
|
||||
cell.configure(configuration: configuration, transaction: transaction)
|
||||
}
|
||||
cell.configureWithSneakyTransaction(address: viewerAddress, localUserDisplayMode: .asLocalUser)
|
||||
|
||||
return cell
|
||||
}, actionBlock: { [weak self] in
|
||||
|
||||
@ -253,7 +253,7 @@ private class StoryThreadCell: ContactTableViewCell {
|
||||
// MARK: - ContactTableViewCell
|
||||
|
||||
func configure(conversationItem: StoryConversationItem, transaction: DBReadTransaction) {
|
||||
let configuration: ContactCellConfiguration
|
||||
var configuration: ContactCellView.Configuration
|
||||
switch conversationItem.messageRecipient {
|
||||
case .contact:
|
||||
owsFailDebug("Unexpected recipient for story")
|
||||
@ -268,21 +268,21 @@ private class StoryThreadCell: ContactTableViewCell {
|
||||
owsFailDebug("Failed to find group thread")
|
||||
return
|
||||
}
|
||||
configuration = ContactCellConfiguration(groupThread: groupThread, localUserDisplayMode: .noteToSelf)
|
||||
configuration = ContactCellView.Configuration(groupThread: groupThread, localUserDisplayMode: .noteToSelf)
|
||||
case .privateStory(_, let isMyStory):
|
||||
if isMyStory {
|
||||
guard let localAddress = DependenciesBridge.shared.tsAccountManager.localIdentifiersWithMaybeSneakyTransaction?.aciAddress else {
|
||||
owsFailDebug("Unexpectedly missing local address")
|
||||
return
|
||||
}
|
||||
configuration = ContactCellConfiguration(address: localAddress, localUserDisplayMode: .asUser)
|
||||
configuration = ContactCellView.Configuration(address: localAddress, localUserDisplayMode: .asUser)
|
||||
configuration.customName = conversationItem.title(transaction: transaction)
|
||||
} else {
|
||||
guard let image = conversationItem.image else {
|
||||
owsFailDebug("Unexpectedly missing image for private story")
|
||||
return
|
||||
}
|
||||
configuration = ContactCellConfiguration(name: conversationItem.title(transaction: transaction), avatar: image)
|
||||
configuration = ContactCellView.Configuration(name: conversationItem.title(transaction: transaction), avatar: image)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -301,9 +301,9 @@ class StoryInfoSheet: OWSTableViewController2, DatabaseChangeDelegate, UIAdaptiv
|
||||
}
|
||||
|
||||
SSKEnvironment.shared.databaseStorageRef.read { transaction in
|
||||
let configuration = ContactCellConfiguration(address: address, localUserDisplayMode: .asUser)
|
||||
var configuration = ContactCellView.Configuration(address: address, localUserDisplayMode: .asUser)
|
||||
configuration.forceDarkAppearance = true
|
||||
configuration.accessoryView = self.buildAccessoryView(
|
||||
configuration.accessory = self.buildContactCellAccessory(
|
||||
text: accessoryText,
|
||||
transaction: transaction,
|
||||
)
|
||||
@ -325,10 +325,10 @@ class StoryInfoSheet: OWSTableViewController2, DatabaseChangeDelegate, UIAdaptiv
|
||||
})
|
||||
}
|
||||
|
||||
private func buildAccessoryView(
|
||||
private func buildContactCellAccessory(
|
||||
text: String,
|
||||
transaction: DBReadTransaction,
|
||||
) -> ContactCellAccessoryView {
|
||||
) -> ContactCellView.Accessory {
|
||||
let label = CVLabel()
|
||||
let labelConfig = CVLabelConfig.unstyledText(
|
||||
text,
|
||||
@ -338,7 +338,7 @@ class StoryInfoSheet: OWSTableViewController2, DatabaseChangeDelegate, UIAdaptiv
|
||||
labelConfig.applyForRendering(label: label)
|
||||
let labelSize = CVText.measureLabel(config: labelConfig, maxWidth: .greatestFiniteMagnitude)
|
||||
|
||||
return ContactCellAccessoryView(accessoryView: label, size: labelSize)
|
||||
return .init(accessoryView: label, size: labelSize)
|
||||
}
|
||||
|
||||
// MARK: - DatabaseChangeDelegate
|
||||
|
||||
@ -501,7 +501,7 @@ class MemberLabelViewController: OWSViewController, UITextFieldDelegate {
|
||||
for (memberAddress, memberLabel) in sortedNonLocalMembers {
|
||||
let cell = ContactCellView()
|
||||
SSKEnvironment.shared.databaseStorageRef.read { tx in
|
||||
let configuration = ContactCellConfiguration(address: memberAddress, localUserDisplayMode: .asLocalUser)
|
||||
var configuration = ContactCellView.Configuration(address: memberAddress, localUserDisplayMode: .asLocalUser)
|
||||
|
||||
configuration.memberLabel = memberLabel
|
||||
|
||||
|
||||
@ -522,8 +522,8 @@ class MessageDetailViewController: OWSTableViewController2 {
|
||||
}
|
||||
|
||||
SSKEnvironment.shared.databaseStorageRef.read { transaction in
|
||||
let configuration = ContactCellConfiguration(address: address, localUserDisplayMode: .asUser)
|
||||
configuration.accessoryView = self.buildAccessoryView(
|
||||
var configuration = ContactCellView.Configuration(address: address, localUserDisplayMode: .asUser)
|
||||
configuration.accessory = self.buildContactCellAccessory(
|
||||
text: accessoryText,
|
||||
displayUDIndicator: displayUDIndicator,
|
||||
transaction: transaction,
|
||||
@ -548,11 +548,11 @@ class MessageDetailViewController: OWSTableViewController2 {
|
||||
expiryLabel?.attributedText = expiryLabelAttributedText
|
||||
}
|
||||
|
||||
private func buildAccessoryView(
|
||||
private func buildContactCellAccessory(
|
||||
text: String,
|
||||
displayUDIndicator: Bool,
|
||||
transaction: DBReadTransaction,
|
||||
) -> ContactCellAccessoryView {
|
||||
) -> ContactCellView.Accessory {
|
||||
let label = CVLabel()
|
||||
label.textAlignment = .right
|
||||
let labelConfig = CVLabelConfig.unstyledText(
|
||||
@ -566,7 +566,7 @@ class MessageDetailViewController: OWSTableViewController2 {
|
||||
let shouldShowUD = SSKEnvironment.shared.preferencesRef.shouldShowUnidentifiedDeliveryIndicators(transaction: transaction)
|
||||
|
||||
guard displayUDIndicator, shouldShowUD else {
|
||||
return ContactCellAccessoryView(accessoryView: label, size: labelSize)
|
||||
return .init(accessoryView: label, size: labelSize)
|
||||
}
|
||||
|
||||
let imageView = CVImageView()
|
||||
@ -589,7 +589,7 @@ class MessageDetailViewController: OWSTableViewController2 {
|
||||
],
|
||||
)
|
||||
let hStackSize = hStackMeasurement.measuredSize
|
||||
return ContactCellAccessoryView(accessoryView: hStack, size: hStackSize)
|
||||
return .init(accessoryView: hStack, size: hStackSize)
|
||||
}
|
||||
|
||||
private static func valueLabelAttributedText(name: String, value: String) -> NSAttributedString {
|
||||
|
||||
@ -194,11 +194,8 @@ public class NewGroupConfirmViewController: OWSTableViewController2 {
|
||||
}
|
||||
|
||||
cell.selectionStyle = .none
|
||||
cell.configureWithSneakyTransaction(address: address, localUserDisplayMode: .asUser)
|
||||
|
||||
SSKEnvironment.shared.databaseStorageRef.read { transaction in
|
||||
let configuration = ContactCellConfiguration(address: address, localUserDisplayMode: .asUser)
|
||||
cell.configure(configuration: configuration, transaction: transaction)
|
||||
}
|
||||
return cell
|
||||
},
|
||||
))
|
||||
|
||||
@ -264,7 +264,7 @@ struct PollDetailsView: View {
|
||||
|
||||
private func addressCell(address: SignalServiceAddress) -> ManualStackView? {
|
||||
let cell = ContactCellView()
|
||||
let config = ContactCellConfiguration(address: address, localUserDisplayMode: .asLocalUser)
|
||||
var config = ContactCellView.Configuration(address: address, localUserDisplayMode: .asLocalUser)
|
||||
config.avatarSizeClass = .twentyEight
|
||||
|
||||
SSKEnvironment.shared.databaseStorageRef.read { transaction in
|
||||
|
||||
@ -913,7 +913,7 @@ extension ConversationSettingsViewController {
|
||||
}
|
||||
|
||||
SSKEnvironment.shared.databaseStorageRef.read { transaction in
|
||||
let configuration = ContactCellConfiguration(address: memberAddress, localUserDisplayMode: .asLocalUser)
|
||||
var configuration = ContactCellView.Configuration(address: memberAddress, localUserDisplayMode: .asLocalUser)
|
||||
let isGroupAdmin = groupMembership.isFullMemberAndAdministrator(memberAddress)
|
||||
let isVerified = verificationState == .verified
|
||||
let isNoLongerVerified = verificationState == .noLongerVerified
|
||||
|
||||
@ -172,12 +172,12 @@ public class GroupMemberRequestsAndInvitesViewController: OWSTableViewController
|
||||
let cell = ContactTableViewCell(style: .default, reuseIdentifier: nil)
|
||||
|
||||
SSKEnvironment.shared.databaseStorageRef.read { transaction in
|
||||
let configuration = ContactCellConfiguration(address: address, localUserDisplayMode: .asLocalUser)
|
||||
var configuration = ContactCellView.Configuration(address: address, localUserDisplayMode: .asLocalUser)
|
||||
configuration.allowUserInteraction = true
|
||||
configuration.avatarSizeClass = .forty
|
||||
|
||||
if canApproveMemberRequests {
|
||||
configuration.accessoryView = self.buildMemberRequestButtons(address: address)
|
||||
configuration.accessory = self.buildMemberRequestButtons(address: address)
|
||||
}
|
||||
|
||||
if address.isLocalAddress {
|
||||
@ -204,7 +204,7 @@ public class GroupMemberRequestsAndInvitesViewController: OWSTableViewController
|
||||
contents.add(section)
|
||||
}
|
||||
|
||||
private func buildMemberRequestButtons(address: SignalServiceAddress) -> ContactCellAccessoryView {
|
||||
private func buildMemberRequestButtons(address: SignalServiceAddress) -> ContactCellView.Accessory {
|
||||
let denyButton = UIButton(
|
||||
configuration: .roundGray(image: UIImage(resource: .x)),
|
||||
primaryAction: UIAction { [weak self] _ in
|
||||
@ -242,7 +242,7 @@ public class GroupMemberRequestsAndInvitesViewController: OWSTableViewController
|
||||
],
|
||||
)
|
||||
let stackSize = stackMeasurement.measuredSize
|
||||
return ContactCellAccessoryView(accessoryView: stackView, size: stackSize)
|
||||
return ContactCellView.Accessory(accessoryView: stackView, size: stackSize)
|
||||
}
|
||||
|
||||
private func approveMemberRequest(address: SignalServiceAddress) {
|
||||
@ -311,7 +311,7 @@ public class GroupMemberRequestsAndInvitesViewController: OWSTableViewController
|
||||
cell.selectionStyle = canRevokeInvites ? .default : .none
|
||||
|
||||
SSKEnvironment.shared.databaseStorageRef.read { transaction in
|
||||
let configuration = ContactCellConfiguration(address: address, localUserDisplayMode: .asUser)
|
||||
var configuration = ContactCellView.Configuration(address: address, localUserDisplayMode: .asUser)
|
||||
configuration.avatarSizeClass = .forty
|
||||
cell.configure(configuration: configuration, transaction: transaction)
|
||||
}
|
||||
@ -366,8 +366,7 @@ public class GroupMemberRequestsAndInvitesViewController: OWSTableViewController
|
||||
cell.selectionStyle = canRevokeInvites ? .default : .none
|
||||
|
||||
databaseStorage.read { transaction in
|
||||
let configuration = ContactCellConfiguration(address: inviterAddress, localUserDisplayMode: .asUser)
|
||||
configuration.avatarSizeClass = .forty
|
||||
var configuration = ContactCellView.Configuration(address: inviterAddress, localUserDisplayMode: .asUser)
|
||||
let inviterName = contactManager.displayName(for: inviterAddress, tx: transaction).resolvedValue()
|
||||
let format = OWSLocalizedString(
|
||||
"PENDING_GROUP_MEMBERS_MEMBER_INVITED_USERS_%d",
|
||||
@ -375,6 +374,7 @@ public class GroupMemberRequestsAndInvitesViewController: OWSTableViewController
|
||||
comment: "Format for label indicating the a group member has invited N other users to the group. Embeds {{ %1$@ the number of users they have invited, %2$@ name of the inviting group member }}.",
|
||||
)
|
||||
configuration.customName = String.localizedStringWithFormat(format, invitedAddresses.count, inviterName)
|
||||
configuration.avatarSizeClass = .forty
|
||||
cell.configure(configuration: configuration, transaction: transaction)
|
||||
}
|
||||
|
||||
|
||||
@ -82,12 +82,7 @@ class ReplaceAdminViewController: OWSTableViewController2 {
|
||||
owsFailDebug("Missing cell.")
|
||||
return UITableViewCell()
|
||||
}
|
||||
|
||||
SSKEnvironment.shared.databaseStorageRef.read { transaction in
|
||||
let configuration = ContactCellConfiguration(address: address, localUserDisplayMode: .asUser)
|
||||
cell.configure(configuration: configuration, transaction: transaction)
|
||||
}
|
||||
|
||||
cell.configureWithSneakyTransaction(address: address, localUserDisplayMode: .asUser)
|
||||
return cell
|
||||
},
|
||||
actionBlock: { [weak self] in
|
||||
|
||||
@ -399,9 +399,9 @@ extension BaseMemberViewController: RecipientPickerDelegate {
|
||||
|
||||
public func recipientPicker(
|
||||
_ recipientPickerViewController: RecipientPickerViewController,
|
||||
accessoryViewForRecipient recipient: PickedRecipient,
|
||||
contactCellAccessoryForRecipient recipient: PickedRecipient,
|
||||
transaction: DBReadTransaction,
|
||||
) -> ContactCellAccessoryView? {
|
||||
) -> ContactCellView.Accessory? {
|
||||
guard let address = recipient.address else {
|
||||
owsFailDebug("Missing address.")
|
||||
return nil
|
||||
@ -441,7 +441,7 @@ extension BaseMemberViewController: RecipientPickerDelegate {
|
||||
accessoryView = SelectionIndicatorView()
|
||||
}
|
||||
let accessoryViewWrapper = ManualLayoutView.wrapSubviewUsingIOSAutoLayout(accessoryView)
|
||||
return ContactCellAccessoryView(accessoryView: accessoryViewWrapper, size: .square(24))
|
||||
return ContactCellView.Accessory(accessoryView: accessoryViewWrapper, size: .square(24))
|
||||
}
|
||||
|
||||
public func recipientPicker(
|
||||
|
||||
@ -3,102 +3,88 @@
|
||||
// SPDX-License-Identifier: AGPL-3.0-only
|
||||
//
|
||||
|
||||
import Foundation
|
||||
public import SignalServiceKit
|
||||
|
||||
public class ContactCellAccessoryView: NSObject {
|
||||
let accessoryView: UIView
|
||||
let size: CGSize
|
||||
|
||||
public init(accessoryView: UIView, size: CGSize) {
|
||||
self.accessoryView = accessoryView
|
||||
self.size = size
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: -
|
||||
|
||||
public class ContactCellConfiguration: NSObject {
|
||||
fileprivate enum CellDataSource {
|
||||
case address(SignalServiceAddress)
|
||||
case groupThread(TSGroupThread)
|
||||
case `static`(name: String, avatar: UIImage)
|
||||
}
|
||||
|
||||
fileprivate let dataSource: CellDataSource
|
||||
|
||||
public let localUserDisplayMode: LocalUserDisplayMode
|
||||
|
||||
public var forceDarkAppearance = false
|
||||
|
||||
public var accessoryMessage: String?
|
||||
|
||||
public var customName: String?
|
||||
|
||||
public var accessoryView: ContactCellAccessoryView?
|
||||
|
||||
public var attributedSubtitle: NSAttributedString?
|
||||
|
||||
public var shouldShowContactIcon = false
|
||||
|
||||
public var allowUserInteraction = false
|
||||
|
||||
public var badged = true // TODO: Badges — Default false? Configure each use-case?
|
||||
|
||||
public var storyState: StoryContextViewState?
|
||||
|
||||
public var hasAccessoryText: Bool {
|
||||
accessoryMessage?.nilIfEmpty != nil
|
||||
}
|
||||
|
||||
public var avatarSizeClass: ConversationAvatarView.Configuration.SizeClass?
|
||||
|
||||
public var memberLabel: MemberLabelForRendering?
|
||||
|
||||
public init(address: SignalServiceAddress, localUserDisplayMode: LocalUserDisplayMode) {
|
||||
self.dataSource = .address(address)
|
||||
self.localUserDisplayMode = localUserDisplayMode
|
||||
super.init()
|
||||
}
|
||||
|
||||
public init(groupThread: TSGroupThread, localUserDisplayMode: LocalUserDisplayMode) {
|
||||
self.dataSource = .groupThread(groupThread)
|
||||
self.localUserDisplayMode = localUserDisplayMode
|
||||
super.init()
|
||||
}
|
||||
|
||||
public init(name: String, avatar: UIImage) {
|
||||
self.dataSource = .static(name: name, avatar: avatar)
|
||||
self.localUserDisplayMode = .asUser
|
||||
super.init()
|
||||
}
|
||||
|
||||
public func useVerifiedSubtitle() {
|
||||
let text = NSMutableAttributedString()
|
||||
text.append(SignalSymbol.safetyNumber.attributedString(for: .caption1, clamped: true))
|
||||
text.append(" ", attributes: [:])
|
||||
text.append(SafetyNumberStrings.verified, attributes: [:])
|
||||
self.attributedSubtitle = text
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: -
|
||||
|
||||
public class ContactCellView: ManualStackView {
|
||||
|
||||
private var configuration: ContactCellConfiguration? {
|
||||
didSet {
|
||||
ensureObservers()
|
||||
public struct Configuration {
|
||||
fileprivate enum CellDataSource {
|
||||
case address(SignalServiceAddress)
|
||||
case groupThread(TSGroupThread)
|
||||
case `static`(name: String, avatar: UIImage)
|
||||
}
|
||||
|
||||
fileprivate let dataSource: CellDataSource
|
||||
|
||||
public let localUserDisplayMode: LocalUserDisplayMode
|
||||
|
||||
public var forceDarkAppearance = false
|
||||
|
||||
public var accessoryMessage: String?
|
||||
|
||||
public var customName: String?
|
||||
|
||||
public var accessory: ContactCellView.Accessory?
|
||||
|
||||
public var attributedSubtitle: NSAttributedString?
|
||||
|
||||
public var shouldShowContactIcon = false
|
||||
|
||||
public var allowUserInteraction = false
|
||||
|
||||
public var badged = true // TODO: Badges — Default false? Configure each use-case?
|
||||
|
||||
public var storyState: StoryContextViewState?
|
||||
|
||||
public var hasAccessoryText: Bool {
|
||||
accessoryMessage?.nilIfEmpty != nil
|
||||
}
|
||||
|
||||
public var avatarSizeClass: ConversationAvatarView.Configuration.SizeClass?
|
||||
|
||||
public var memberLabel: MemberLabelForRendering?
|
||||
|
||||
public init(address: SignalServiceAddress, localUserDisplayMode: LocalUserDisplayMode) {
|
||||
self.dataSource = .address(address)
|
||||
self.localUserDisplayMode = localUserDisplayMode
|
||||
}
|
||||
|
||||
public init(groupThread: TSGroupThread, localUserDisplayMode: LocalUserDisplayMode) {
|
||||
self.dataSource = .groupThread(groupThread)
|
||||
self.localUserDisplayMode = localUserDisplayMode
|
||||
}
|
||||
|
||||
public init(name: String, avatar: UIImage) {
|
||||
self.dataSource = .static(name: name, avatar: avatar)
|
||||
self.localUserDisplayMode = .asUser
|
||||
}
|
||||
|
||||
public mutating func useVerifiedSubtitle() {
|
||||
let text = NSMutableAttributedString()
|
||||
text.append(SignalSymbol.safetyNumber.attributedString(for: .caption1, clamped: true))
|
||||
text.append(" ", attributes: [:])
|
||||
text.append(SafetyNumberStrings.verified, attributes: [:])
|
||||
self.attributedSubtitle = text
|
||||
}
|
||||
}
|
||||
|
||||
public struct Accessory {
|
||||
let accessoryView: UIView
|
||||
let size: CGSize
|
||||
|
||||
public init(accessoryView: UIView, size: CGSize) {
|
||||
self.accessoryView = accessoryView
|
||||
self.size = size
|
||||
}
|
||||
}
|
||||
|
||||
public static var avatarSizeClass: ConversationAvatarView.Configuration.SizeClass { .thirtySix }
|
||||
private var avatarDataSource: ConversationAvatarDataSource? {
|
||||
switch configuration?.dataSource {
|
||||
|
||||
private func avatarDataSource(configuration: Configuration) -> ConversationAvatarDataSource {
|
||||
switch configuration.dataSource {
|
||||
case .groupThread(let thread): return .thread(thread)
|
||||
case .address(let address): return .address(address)
|
||||
case .static(_, let avatar): return .asset(avatar: avatar, badge: nil)
|
||||
case nil: return nil
|
||||
}
|
||||
}
|
||||
|
||||
@ -165,15 +151,16 @@ public class ContactCellView: ManualStackView {
|
||||
}
|
||||
|
||||
public func configure(
|
||||
configuration: ContactCellConfiguration,
|
||||
configuration: Configuration,
|
||||
transaction: DBReadTransaction,
|
||||
) {
|
||||
AssertIsOnMainThread()
|
||||
owsAssertDebug(!shouldDeactivateConstraints)
|
||||
|
||||
self.configuration = configuration
|
||||
setupObservations(configuration: configuration)
|
||||
|
||||
self.isUserInteractionEnabled = configuration.allowUserInteraction
|
||||
isUserInteractionEnabled = configuration.allowUserInteraction
|
||||
|
||||
let avatarDataSource = avatarDataSource(configuration: configuration)
|
||||
|
||||
avatarView.update(transaction) { config in
|
||||
config.dataSource = avatarDataSource
|
||||
@ -189,16 +176,13 @@ public class ContactCellView: ManualStackView {
|
||||
}
|
||||
}
|
||||
|
||||
if
|
||||
avatarDataSource?.isGroupAvatar ?? false,
|
||||
let storyState = configuration.storyState
|
||||
{
|
||||
if avatarDataSource.isGroupAvatar, let storyState = configuration.storyState {
|
||||
// Group story. Add badge
|
||||
avatarView.addSubview(groupStoryBadgeView)
|
||||
let badgeColor: UIColor
|
||||
switch storyState {
|
||||
case .unviewed:
|
||||
badgeColor = .ows_accentBlue
|
||||
badgeColor = .Signal.accent
|
||||
case .viewed, .noStories:
|
||||
badgeColor = Theme.isDarkThemeEnabled ? .ows_gray65 : .ows_gray25
|
||||
}
|
||||
@ -277,17 +261,18 @@ public class ContactCellView: ManualStackView {
|
||||
rootStackSubviewInfos.append(textStackMeasurement.measuredSize.asManualSubviewInfo)
|
||||
}
|
||||
|
||||
var accessory = configuration.accessory
|
||||
if let accessoryMessage = configuration.accessoryMessage {
|
||||
accessoryLabel.text = accessoryMessage
|
||||
let labelSize = accessoryLabel.sizeThatFits(.square(.greatestFiniteMagnitude))
|
||||
configuration.accessoryView = ContactCellAccessoryView(
|
||||
accessory = Accessory(
|
||||
accessoryView: accessoryLabel,
|
||||
size: labelSize,
|
||||
)
|
||||
}
|
||||
if let accessoryView = configuration.accessoryView {
|
||||
rootStackSubviews.append(accessoryView.accessoryView)
|
||||
rootStackSubviewInfos.append(accessoryView.size.asManualSubviewInfo(hasFixedSize: true))
|
||||
if let accessory {
|
||||
rootStackSubviews.append(accessory.accessoryView)
|
||||
rootStackSubviewInfos.append(accessory.size.asManualSubviewInfo(hasFixedSize: true))
|
||||
}
|
||||
|
||||
let rootStackConfig = ManualStackView.Config(
|
||||
@ -310,32 +295,44 @@ public class ContactCellView: ManualStackView {
|
||||
|
||||
// MARK: - Notifications
|
||||
|
||||
private func ensureObservers() {
|
||||
NotificationCenter.default.removeObserver(self)
|
||||
if case .address = configuration?.dataSource {
|
||||
NotificationCenter.default.addObserver(
|
||||
self,
|
||||
selector: #selector(otherUsersProfileChanged(notification:)),
|
||||
private var observation: NotificationCenter.Observer?
|
||||
|
||||
private func setupObservations(configuration: Configuration) {
|
||||
if let observation {
|
||||
NotificationCenter.default.removeObserver(observation)
|
||||
self.observation = nil
|
||||
}
|
||||
|
||||
if case .address = configuration.dataSource {
|
||||
observation = NotificationCenter.default.addObserver(
|
||||
name: UserProfileNotifications.otherUsersProfileDidChange,
|
||||
object: nil,
|
||||
)
|
||||
) { [weak self] notification in
|
||||
guard
|
||||
let changedAddress = notification.userInfo?[UserProfileNotifications.profileAddressKey] as? SignalServiceAddress,
|
||||
changedAddress.isValid
|
||||
else {
|
||||
owsFailDebug("changedAddress was unexpectedly nil")
|
||||
return
|
||||
}
|
||||
if case .address(changedAddress) = configuration.dataSource {
|
||||
self?.updateNameLabelsWithSneakyTransaction(configuration: configuration)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: -
|
||||
|
||||
private func updateNameLabelsWithSneakyTransaction(configuration: ContactCellConfiguration) {
|
||||
private func updateNameLabelsWithSneakyTransaction(configuration: Configuration) {
|
||||
SSKEnvironment.shared.databaseStorageRef.read { transaction in
|
||||
updateNameLabels(configuration: configuration, transaction: transaction)
|
||||
}
|
||||
}
|
||||
|
||||
private func updateNameLabels(
|
||||
configuration: ContactCellConfiguration,
|
||||
configuration: Configuration,
|
||||
transaction: DBReadTransaction,
|
||||
) {
|
||||
AssertIsOnMainThread()
|
||||
|
||||
let textColor = self.nameLabelColor(forceDarkAppearance: configuration.forceDarkAppearance)
|
||||
|
||||
let nameString = { () -> NSAttributedString in
|
||||
@ -389,9 +386,10 @@ public class ContactCellView: ManualStackView {
|
||||
override public func reset() {
|
||||
super.reset()
|
||||
|
||||
NotificationCenter.default.removeObserver(self)
|
||||
|
||||
configuration = nil
|
||||
if let observation {
|
||||
NotificationCenter.default.removeObserver(observation)
|
||||
self.observation = nil
|
||||
}
|
||||
|
||||
avatarView.reset()
|
||||
textStack.reset()
|
||||
@ -400,24 +398,4 @@ public class ContactCellView: ManualStackView {
|
||||
subtitleLabel.text = nil
|
||||
accessoryLabel.text = nil
|
||||
}
|
||||
|
||||
@objc
|
||||
private func otherUsersProfileChanged(notification: Notification) {
|
||||
AssertIsOnMainThread()
|
||||
|
||||
guard let configuration = self.configuration else {
|
||||
return
|
||||
}
|
||||
guard
|
||||
let changedAddress = notification.userInfo?[UserProfileNotifications.profileAddressKey] as? SignalServiceAddress,
|
||||
changedAddress.isValid
|
||||
else {
|
||||
owsFailDebug("changedAddress was unexpectedly nil")
|
||||
return
|
||||
}
|
||||
|
||||
if case .address(changedAddress) = configuration.dataSource {
|
||||
updateNameLabelsWithSneakyTransaction(configuration: configuration)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -56,7 +56,7 @@ open class ContactTableViewCell: UITableViewCell, ReusableTableViewCell {
|
||||
localUserDisplayMode: LocalUserDisplayMode,
|
||||
transaction: DBReadTransaction,
|
||||
) {
|
||||
let configuration = ContactCellConfiguration(
|
||||
let configuration = ContactCellView.Configuration(
|
||||
address: address,
|
||||
localUserDisplayMode: localUserDisplayMode,
|
||||
)
|
||||
@ -68,7 +68,7 @@ open class ContactTableViewCell: UITableViewCell, ReusableTableViewCell {
|
||||
localUserDisplayMode: LocalUserDisplayMode,
|
||||
transaction: DBReadTransaction,
|
||||
) {
|
||||
let configuration = ContactCellConfiguration(
|
||||
let configuration = ContactCellView.Configuration(
|
||||
address: thread.contactAddress,
|
||||
localUserDisplayMode: localUserDisplayMode,
|
||||
)
|
||||
@ -76,7 +76,7 @@ open class ContactTableViewCell: UITableViewCell, ReusableTableViewCell {
|
||||
}
|
||||
|
||||
open func configure(
|
||||
configuration: ContactCellConfiguration,
|
||||
configuration: ContactCellView.Configuration,
|
||||
transaction: DBReadTransaction,
|
||||
) {
|
||||
OWSTableItem.configureCell(self)
|
||||
|
||||
@ -1295,10 +1295,10 @@ class ConversationPickerCell: ContactTableViewCell {
|
||||
// MARK: - ContactTableViewCell
|
||||
|
||||
func configure(conversationItem: ConversationItem, transaction: DBReadTransaction) {
|
||||
let configuration: ContactCellConfiguration
|
||||
var configuration: ContactCellView.Configuration
|
||||
switch conversationItem.messageRecipient {
|
||||
case .contact(let address):
|
||||
configuration = ContactCellConfiguration(address: address, localUserDisplayMode: .noteToSelf)
|
||||
configuration = ContactCellView.Configuration(address: address, localUserDisplayMode: .noteToSelf)
|
||||
case .group(let groupThreadId):
|
||||
guard
|
||||
let groupThread = TSGroupThread.fetchGroupThreadViaCache(
|
||||
@ -1309,27 +1309,27 @@ class ConversationPickerCell: ContactTableViewCell {
|
||||
owsFailDebug("Failed to find group thread")
|
||||
return
|
||||
}
|
||||
configuration = ContactCellConfiguration(groupThread: groupThread, localUserDisplayMode: .noteToSelf)
|
||||
configuration = ContactCellView.Configuration(groupThread: groupThread, localUserDisplayMode: .noteToSelf)
|
||||
case .privateStory(_, let isMyStory):
|
||||
if isMyStory {
|
||||
guard let localAddress = DependenciesBridge.shared.tsAccountManager.localIdentifiersWithMaybeSneakyTransaction?.aciAddress else {
|
||||
owsFailDebug("Unexpectedly missing local address")
|
||||
return
|
||||
}
|
||||
configuration = ContactCellConfiguration(address: localAddress, localUserDisplayMode: .asUser)
|
||||
configuration = ContactCellView.Configuration(address: localAddress, localUserDisplayMode: .asUser)
|
||||
configuration.customName = conversationItem.title(transaction: transaction)
|
||||
} else {
|
||||
guard let image = conversationItem.image else {
|
||||
owsFailDebug("Unexpectedly missing image for private story")
|
||||
return
|
||||
}
|
||||
configuration = ContactCellConfiguration(name: conversationItem.title(transaction: transaction), avatar: image)
|
||||
configuration = ContactCellView.Configuration(name: conversationItem.title(transaction: transaction), avatar: image)
|
||||
}
|
||||
}
|
||||
if conversationItem.isBlocked {
|
||||
configuration.accessoryMessage = MessageStrings.conversationIsBlocked
|
||||
} else {
|
||||
configuration.accessoryView = buildAccessoryView(disappearingMessagesConfig: conversationItem.disappearingMessagesConfig)
|
||||
configuration.accessory = buildContactCellAccessory(disappearingMessagesConfig: conversationItem.disappearingMessagesConfig)
|
||||
}
|
||||
|
||||
if let storyItem = conversationItem as? StoryConversationItem {
|
||||
@ -1355,7 +1355,7 @@ class ConversationPickerCell: ContactTableViewCell {
|
||||
|
||||
private lazy var selectionView = SelectionIndicatorView()
|
||||
|
||||
func buildAccessoryView(disappearingMessagesConfig: DisappearingMessagesConfigurationRecord?) -> ContactCellAccessoryView {
|
||||
func buildContactCellAccessory(disappearingMessagesConfig: DisappearingMessagesConfigurationRecord?) -> ContactCellView.Accessory {
|
||||
|
||||
selectionView.removeFromSuperview()
|
||||
let selectionWrapper = ManualLayoutView.wrapSubviewUsingIOSAutoLayout(selectionView)
|
||||
@ -1364,7 +1364,7 @@ class ConversationPickerCell: ContactTableViewCell {
|
||||
let disappearingMessagesConfig,
|
||||
disappearingMessagesConfig.isEnabled
|
||||
else {
|
||||
return ContactCellAccessoryView(
|
||||
return ContactCellView.Accessory(
|
||||
accessoryView: selectionWrapper,
|
||||
size: selectionView.intrinsicContentSize,
|
||||
)
|
||||
@ -1389,7 +1389,7 @@ class ConversationPickerCell: ContactTableViewCell {
|
||||
],
|
||||
)
|
||||
let stackSize = stackMeasurement.measuredSize
|
||||
return ContactCellAccessoryView(accessoryView: stackView, size: stackSize)
|
||||
return ContactCellView.Accessory(accessoryView: stackView, size: stackSize)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -25,9 +25,9 @@ public protocol RecipientPickerDelegate: RecipientContextMenuHelperDelegate {
|
||||
|
||||
func recipientPicker(
|
||||
_ recipientPickerViewController: RecipientPickerViewController,
|
||||
accessoryViewForRecipient recipient: PickedRecipient,
|
||||
contactCellAccessoryForRecipient recipient: PickedRecipient,
|
||||
transaction: DBReadTransaction,
|
||||
) -> ContactCellAccessoryView?
|
||||
) -> ContactCellView.Accessory?
|
||||
|
||||
func recipientPicker(
|
||||
_ recipientPickerViewController: RecipientPickerViewController,
|
||||
@ -58,9 +58,9 @@ public extension RecipientPickerDelegate {
|
||||
|
||||
func recipientPicker(
|
||||
_ recipientPickerViewController: RecipientPickerViewController,
|
||||
accessoryViewForRecipient recipient: PickedRecipient,
|
||||
contactCellAccessoryForRecipient recipient: PickedRecipient,
|
||||
transaction: DBReadTransaction,
|
||||
) -> ContactCellAccessoryView? { nil }
|
||||
) -> ContactCellView.Accessory? { nil }
|
||||
|
||||
func recipientPicker(
|
||||
_ recipientPickerViewController: RecipientPickerViewController,
|
||||
|
||||
@ -1021,11 +1021,11 @@ extension RecipientPickerViewController {
|
||||
private func addressCell(for address: SignalServiceAddress, recipient: PickedRecipient, tableView: UITableView) -> UITableViewCell? {
|
||||
guard let cell = tableView.dequeueReusableCell(ContactTableViewCell.self) else { return nil }
|
||||
SSKEnvironment.shared.databaseStorageRef.read { transaction in
|
||||
let configuration = ContactCellConfiguration(address: address, localUserDisplayMode: .noteToSelf)
|
||||
var configuration = ContactCellView.Configuration(address: address, localUserDisplayMode: .noteToSelf)
|
||||
if let delegate {
|
||||
cell.selectionStyle = delegate.recipientPicker(self, selectionStyleForRecipient: recipient, transaction: transaction)
|
||||
if let accessoryView = delegate.recipientPicker(self, accessoryViewForRecipient: recipient, transaction: transaction) {
|
||||
configuration.accessoryView = accessoryView
|
||||
if let accessory = delegate.recipientPicker(self, contactCellAccessoryForRecipient: recipient, transaction: transaction) {
|
||||
configuration.accessory = accessory
|
||||
} else {
|
||||
let accessoryMessage = delegate.recipientPicker(self, accessoryMessageForRecipient: recipient, transaction: transaction)
|
||||
configuration.accessoryMessage = accessoryMessage
|
||||
@ -1051,7 +1051,7 @@ extension RecipientPickerViewController {
|
||||
SSKEnvironment.shared.databaseStorageRef.read { tx in
|
||||
cell.selectionStyle = delegate.recipientPicker(self, selectionStyleForRecipient: recipient, transaction: tx)
|
||||
cell.accessoryMessage = delegate.recipientPicker(self, accessoryMessageForRecipient: recipient, transaction: tx)
|
||||
cell.customAccessoryView = delegate.recipientPicker(self, accessoryViewForRecipient: recipient, transaction: tx)?.accessoryView
|
||||
cell.customAccessoryView = delegate.recipientPicker(self, contactCellAccessoryForRecipient: recipient, transaction: tx)?.accessoryView
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -682,7 +682,7 @@ private class SafetyNumberCell: ContactTableViewCell {
|
||||
}
|
||||
|
||||
SSKEnvironment.shared.databaseStorageRef.read { transaction in
|
||||
let configuration = ContactCellConfiguration(address: item.address, localUserDisplayMode: .asUser)
|
||||
var configuration = ContactCellView.Configuration(address: item.address, localUserDisplayMode: .asUser)
|
||||
configuration.allowUserInteraction = true
|
||||
|
||||
configuration.forceDarkAppearance = traitCollection.userInterfaceStyle == .dark
|
||||
@ -690,7 +690,7 @@ private class SafetyNumberCell: ContactTableViewCell {
|
||||
let buttonSize = button.intrinsicContentSize
|
||||
button.removeFromSuperview()
|
||||
let buttonWrapper = ManualLayoutView.wrapSubviewUsingIOSAutoLayout(button)
|
||||
configuration.accessoryView = ContactCellAccessoryView(
|
||||
configuration.accessory = ContactCellView.Accessory(
|
||||
accessoryView: buttonWrapper,
|
||||
size: buttonSize,
|
||||
)
|
||||
@ -720,7 +720,7 @@ private class SafetyNumberCell: ContactTableViewCell {
|
||||
}
|
||||
}
|
||||
|
||||
override func configure(configuration: ContactCellConfiguration, transaction: DBReadTransaction) {
|
||||
override func configure(configuration: ContactCellView.Configuration, transaction: DBReadTransaction) {
|
||||
super.configure(configuration: configuration, transaction: transaction)
|
||||
backgroundColor = nil
|
||||
}
|
||||
|
||||
@ -142,7 +142,7 @@ public class NewPrivateStoryConfirmViewController: OWSTableViewController2 {
|
||||
cell.selectionStyle = .none
|
||||
|
||||
SSKEnvironment.shared.databaseStorageRef.read { transaction in
|
||||
let configuration = ContactCellConfiguration(address: address, localUserDisplayMode: .asUser)
|
||||
let configuration = ContactCellView.Configuration(address: address, localUserDisplayMode: .asUser)
|
||||
cell.configure(configuration: configuration, transaction: transaction)
|
||||
}
|
||||
return cell
|
||||
|
||||
Loading…
Reference in New Issue
Block a user