Signal-iOS/Signal/src/ViewControllers/ThreadSettings/ConversationSettingsViewController+Contents.swift
Michelle Linington dea35199b2 Clean up ConversationAvatarView data sources
Creates a special data source enum for ConversationAvatarView data
providers. Before, ContactCell and ConversationAvatarView used the data
source, neither applying to each perfectly
2021-11-09 14:25:55 -08:00

759 lines
35 KiB
Swift

//
// Copyright (c) 2021 Open Whisper Systems. All rights reserved.
//
import Foundation
import UIKit
extension ConversationSettingsViewController {
// MARK: - Helpers
private var iconSpacingSmall: CGFloat {
return ContactCellView.avatarTextHSpacing
}
private var iconSpacingLarge: CGFloat {
return OWSTableItem.iconSpacing
}
private var isContactThread: Bool {
return !thread.isGroupThread
}
private var hasExistingSystemContact: Bool {
guard let contactThread = thread as? TSContactThread else {
owsFailDebug("Invalid thread.")
return false
}
return databaseStorage.read { transaction in
contactsManagerImpl.isSystemContact(address: contactThread.contactAddress, transaction: transaction)
}
}
// MARK: - Table
override func themeDidChange() {
super.themeDidChange()
updateTableContents()
}
@objc
func updateTableContents(shouldReload: Bool = true) {
let contents = OWSTableContents()
let isNoteToSelf = thread.isNoteToSelf
// Main section.
let mainSection = OWSTableSection()
let header = buildMainHeader()
lastContentWidth = view.width
mainSection.customHeaderView = header
addDisappearingMessagesItem(to: mainSection)
addColorAndWallpaperSettingsItem(to: mainSection)
if !isNoteToSelf { addSoundAndNotificationSettingsItem(to: mainSection) }
addSystemContactItemIfNecessary(to: mainSection)
addSafetyNumberItemIfNecessary(to: mainSection)
contents.addSection(mainSection)
addAllMediaSectionIfNecessary(to: contents)
if let groupModel = currentGroupModel, !groupModel.isPlaceholder {
contents.addSection(buildGroupMembershipSection(groupModel: groupModel, sectionIndex: contents.sections.count))
if let groupModelV2 = groupModel as? TSGroupModelV2 {
buildGroupSettingsSection(groupModelV2: groupModelV2, contents: contents)
}
} else if isContactThread, hasGroupThreads, !isNoteToSelf {
contents.addSection(buildMutualGroupsSection(sectionIndex: contents.sections.count))
}
if !isNoteToSelf {
contents.addSection(buildBlockAndLeaveSection())
}
if DebugFlags.internalSettings {
contents.addSection(buildInternalSection())
}
let emptySection = OWSTableSection()
emptySection.customFooterHeight = 24
contents.addSection(emptySection)
setContents(contents, shouldReload: shouldReload)
updateNavigationBar()
}
private func addAllMediaSectionIfNecessary(to contents: OWSTableContents) {
guard !recentMedia.isEmpty else { return }
let section = OWSTableSection()
section.headerTitle = MediaStrings.allMedia
section.add(.init(
customCellBlock: { [weak self] in
let cell = OWSTableItem.newCell()
guard let self = self else { return cell }
let stackView = UIStackView()
stackView.axis = .horizontal
stackView.spacing = 5
cell.contentView.addSubview(stackView)
stackView.autoPinEdges(toSuperviewMarginsExcludingEdge: .bottom)
let totalSpacerSize = CGFloat(self.maximumRecentMedia - 1) * stackView.spacing
let availableWidth = self.view.width - ((Self.cellHInnerMargin * 2) + self.cellOuterInsets.totalWidth + self.view.safeAreaInsets.totalWidth)
let imageWidth = (availableWidth - totalSpacerSize) / CGFloat(self.maximumRecentMedia)
for (attachmentStream, imageView) in self.recentMedia.orderedValues {
let button = OWSButton { [weak self] in
self?.showMediaPageView(for: attachmentStream)
}
stackView.addArrangedSubview(button)
button.autoSetDimensions(to: CGSize(square: imageWidth))
imageView.backgroundColor = Theme.middleGrayColor
button.addSubview(imageView)
imageView.autoPinEdgesToSuperviewEdges()
let overlayView = UIView()
overlayView.isUserInteractionEnabled = false
overlayView.backgroundColor = .ows_blackAlpha05
button.addSubview(overlayView)
overlayView.autoPinEdgesToSuperviewEdges()
}
if self.recentMedia.count < self.maximumRecentMedia {
stackView.addArrangedSubview(.hStretchingSpacer())
stackView.autoPinEdge(toSuperviewMargin: .bottom)
} else {
let seeAllLabel = UILabel()
seeAllLabel.textColor = Theme.primaryTextColor
seeAllLabel.font = OWSTableItem.primaryLabelFont
seeAllLabel.text = CommonStrings.seeAllButton
seeAllLabel.autoSetDimension(.height, toSize: OWSTableItem.primaryLabelFont.lineHeight)
cell.contentView.addSubview(seeAllLabel)
seeAllLabel.autoPinEdges(toSuperviewMarginsExcludingEdge: .top)
seeAllLabel.autoPinEdge(.top, to: .bottom, of: stackView, withOffset: 14)
}
return cell
},
actionBlock: { [weak self] in
self?.showMediaGallery()
}
))
contents.addSection(section)
}
private func addSafetyNumberItemIfNecessary(to section: OWSTableSection) {
guard !thread.isNoteToSelf, !isGroupThread, thread.hasSafetyNumbers() else { return }
section.add(OWSTableItem(customCellBlock: { [weak self] in
guard let self = self else {
owsFailDebug("Missing self")
return OWSTableItem.newCell()
}
return OWSTableItem.buildDisclosureCell(name: NSLocalizedString("VERIFY_PRIVACY",
comment: "Label for button or row which allows users to verify the safety number of another user."),
icon: .settingsViewSafetyNumber,
accessibilityIdentifier: UIView.accessibilityIdentifier(in: self, name: "safety_numbers"))
},
actionBlock: { [weak self] in
self?.showVerificationView()
}))
}
private func addSystemContactItemIfNecessary(to section: OWSTableSection) {
guard !thread.isNoteToSelf,
let contactThread = thread as? TSContactThread,
contactsManagerImpl.supportsContactEditing else { return }
if hasExistingSystemContact {
section.add(OWSTableItem(customCellBlock: { [weak self] in
guard let self = self else {
owsFailDebug("Missing self")
return OWSTableItem.newCell()
}
return OWSTableItem.buildDisclosureCell(name: NSLocalizedString(
"CONVERSATION_SETTINGS_VIEW_IS_SYSTEM_CONTACT",
comment: "Indicates that user is in the system contacts list."),
icon: .settingsUserInContacts,
accessibilityIdentifier: UIView.accessibilityIdentifier(in: self, name: "is_in_contacts"))
},
actionBlock: { [weak self] in
guard let self = self else {
owsFailDebug("Missing self")
return
}
if self.contactsManagerImpl.supportsContactEditing {
self.presentContactViewController()
}
}))
} else {
section.add(OWSTableItem(customCellBlock: { [weak self] in
guard let self = self else {
owsFailDebug("Missing self")
return OWSTableItem.newCell()
}
return OWSTableItem.buildDisclosureCell(name: NSLocalizedString("CONVERSATION_SETTINGS_ADD_TO_SYSTEM_CONTACTS",
comment: "button in conversation settings view."),
icon: .settingsAddToContacts,
accessibilityIdentifier: UIView.accessibilityIdentifier(in: self, name: "add_to_system_contacts"))
},
actionBlock: { [weak self] in
self?.showAddToSystemContactsActionSheet(contactThread: contactThread)
}))
}
}
private func addColorAndWallpaperSettingsItem(to section: OWSTableSection) {
section.add(OWSTableItem(customCellBlock: { [weak self] in
guard let self = self else {
owsFailDebug("Missing self")
return OWSTableItem.newCell()
}
let cell = OWSTableItem.buildCellWithAccessoryLabel(
icon: .color24,
itemName: NSLocalizedString("SETTINGS_ITEM_COLOR_AND_WALLPAPER",
comment: "Label for settings view that allows user to change the chat color and wallpaper."),
accessibilityIdentifier: UIView.accessibilityIdentifier(in: self, name: "color_and_wallpaper")
)
return cell
},
actionBlock: { [weak self] in
self?.showColorAndWallpaperSettingsView()
}))
}
private func addSoundAndNotificationSettingsItem(to section: OWSTableSection) {
section.add(OWSTableItem(customCellBlock: { [weak self] in
guard let self = self else {
owsFailDebug("Missing self")
return OWSTableItem.newCell()
}
let cell = OWSTableItem.buildCellWithAccessoryLabel(
icon: .settingsMessageSound,
itemName: NSLocalizedString(
"SOUND_AND_NOTIFICATION_SETTINGS",
comment: "table cell label in conversation settings"
),
accessibilityIdentifier: UIView.accessibilityIdentifier(in: self, name: "sound_and_notifications")
)
return cell
},
actionBlock: { [weak self] in
self?.showSoundAndNotificationsSettingsView()
}))
}
private func addDisappearingMessagesItem(to section: OWSTableSection) {
let canEditConversationAttributes = self.canEditConversationAttributes
let disappearingMessagesConfiguration = self.disappearingMessagesConfiguration
let thread = self.thread
section.add(.init(
customCellBlock: { [weak self] in
guard let self = self else { return UITableViewCell() }
let cell = OWSTableItem.buildIconNameCell(
icon: disappearingMessagesConfiguration.isEnabled
? .settingsTimer
: .settingsTimerDisabled,
itemName: NSLocalizedString(
"DISAPPEARING_MESSAGES",
comment: "table cell label in conversation settings"
),
accessoryText: disappearingMessagesConfiguration.isEnabled
? NSString.formatDurationSeconds(disappearingMessagesConfiguration.durationSeconds, useShortFormat: true)
: CommonStrings.switchOff,
accessoryType: .disclosureIndicator,
accessoryImage: nil,
customColor: canEditConversationAttributes ? nil : Theme.secondaryTextAndIconColor,
accessibilityIdentifier: UIView.accessibilityIdentifier(in: self, name: "disappearing_messages")
)
cell.isUserInteractionEnabled = canEditConversationAttributes
return cell
}, actionBlock: { [weak self] in
let vc = DisappearingMessagesTimerSettingsViewController(
thread: thread,
configuration: disappearingMessagesConfiguration
) { configuration in
self?.disappearingMessagesConfiguration = configuration
self?.updateTableContents()
}
self?.presentFormSheet(OWSNavigationController(rootViewController: vc), animated: true)
}
))
}
private func buildBlockAndLeaveSection() -> OWSTableSection {
let section = OWSTableSection()
section.footerTitle = isGroupThread
? NSLocalizedString("CONVERSATION_SETTINGS_BLOCK_AND_LEAVE_SECTION_FOOTER",
comment: "Footer text for the 'block and leave' section of group conversation settings view.")
: NSLocalizedString("CONVERSATION_SETTINGS_BLOCK_AND_LEAVE_SECTION_CONTACT_FOOTER",
comment: "Footer text for the 'block and leave' section of contact conversation settings view.")
if isGroupThread, isLocalUserFullOrInvitedMember {
section.add(OWSTableItem(customCellBlock: { [weak self] in
guard let self = self else {
owsFailDebug("Missing self")
return OWSTableItem.newCell()
}
return OWSTableItem.buildIconNameCell(icon: .settingsLeaveGroup,
itemName: NSLocalizedString("LEAVE_GROUP_ACTION",
comment: "table cell label in conversation settings"),
customColor: UIColor.ows_accentRed,
accessibilityIdentifier: UIView.accessibilityIdentifier(in: self, name: "leave_group"))
},
actionBlock: { [weak self] in
self?.didTapLeaveGroup()
}))
}
let isCurrentlyBlocked = blockingManager.isThreadBlocked(thread)
section.add(OWSTableItem(customCellBlock: { [weak self] in
guard let self = self else {
owsFailDebug("Missing self")
return OWSTableItem.newCell()
}
let cellTitle: String
var customColor: UIColor?
if isCurrentlyBlocked {
cellTitle =
(self.thread.isGroupThread
? NSLocalizedString("CONVERSATION_SETTINGS_UNBLOCK_GROUP",
comment: "Label for 'unblock group' action in conversation settings view.")
: NSLocalizedString("CONVERSATION_SETTINGS_UNBLOCK_USER",
comment: "Label for 'unblock user' action in conversation settings view."))
} else {
cellTitle =
(self.thread.isGroupThread
? NSLocalizedString("CONVERSATION_SETTINGS_BLOCK_GROUP",
comment: "Label for 'block group' action in conversation settings view.")
: NSLocalizedString("CONVERSATION_SETTINGS_BLOCK_USER",
comment: "Label for 'block user' action in conversation settings view."))
customColor = UIColor.ows_accentRed
}
let cell = OWSTableItem.buildIconNameCell(icon: .settingsBlock,
itemName: cellTitle,
customColor: customColor,
accessibilityIdentifier: UIView.accessibilityIdentifier(in: self, name: "block"))
return cell
},
actionBlock: { [weak self] in
if isCurrentlyBlocked {
self?.didTapUnblockThread()
} else {
self?.didTapBlockThread()
}
}))
return section
}
private func buildInternalSection() -> OWSTableSection {
let section = OWSTableSection()
section.add(OWSTableItem(customCellBlock: { [weak self] in
guard let self = self else {
owsFailDebug("Missing self")
return OWSTableItem.newCell()
}
return OWSTableItem.buildIconNameCell(icon: .settingsAdvanced,
itemName: "Internal",
accessoryType: .disclosureIndicator,
accessibilityIdentifier: UIView.accessibilityIdentifier(in: self, name: "internal"))
},
actionBlock: { [weak self] in
self?.didTapInternalSettings()
}))
return section
}
private func accessoryLabel(forAccess access: GroupV2Access) -> String {
switch access {
case .any, .member:
if access != .member {
owsFailDebug("Invalid attributes access: \(access.rawValue)")
}
return NSLocalizedString("CONVERSATION_SETTINGS_ATTRIBUTES_ACCESS_MEMBER",
comment: "Label indicating that all group members can update the group's attributes: name, avatar, etc.")
case .administrator:
return NSLocalizedString("CONVERSATION_SETTINGS_ATTRIBUTES_ACCESS_ADMINISTRATOR",
comment: "Label indicating that only administrators can update the group's attributes: name, avatar, etc.")
case .unknown, .unsatisfiable:
owsFailDebug("Invalid access")
return NSLocalizedString("CONVERSATION_SETTINGS_ATTRIBUTES_ACCESS_NONE",
comment: "Label indicating that no member can update the group's attributes: name, avatar, etc.")
}
}
private func buildGroupMembershipSection(groupModel: TSGroupModel, sectionIndex: Int) -> OWSTableSection {
let section = OWSTableSection()
section.separatorInsetLeading = NSNumber(value: Float(Self.cellHInnerMargin + CGFloat(AvatarBuilder.smallAvatarSizePoints) + ContactCellView.avatarTextHSpacing))
let helper = contactsViewHelper
let groupMembership = groupModel.groupMembership
// "Add Members" cell.
if canEditConversationMembership {
section.add(OWSTableItem(customCellBlock: { [weak self] in
guard let self = self else {
owsFailDebug("Missing self")
return OWSTableItem.newCell()
}
let cell = OWSTableItem.newCell()
cell.preservesSuperviewLayoutMargins = true
cell.contentView.preservesSuperviewLayoutMargins = true
let iconView = OWSTableItem.buildIconInCircleView(icon: .settingsAddMembers,
iconSize: AvatarBuilder.smallAvatarSizePoints,
innerIconSize: 24,
iconTintColor: Theme.primaryTextColor)
let rowLabel = UILabel()
rowLabel.text = NSLocalizedString("CONVERSATION_SETTINGS_ADD_MEMBERS",
comment: "Label for 'add members' button in conversation settings view.")
rowLabel.textColor = Theme.primaryTextColor
rowLabel.font = OWSTableItem.primaryLabelFont
rowLabel.lineBreakMode = .byTruncatingTail
let contentRow = UIStackView(arrangedSubviews: [ iconView, rowLabel ])
contentRow.spacing = self.iconSpacingSmall
cell.contentView.addSubview(contentRow)
contentRow.autoPinWidthToSuperviewMargins()
contentRow.autoPinHeightToSuperview(withMargin: 7)
return cell
}) { [weak self] in
self?.showAddMembersView()
})
}
let totalMemberCount = sortedGroupMembers.count
if totalMemberCount > 1 {
let headerFormat = NSLocalizedString("CONVERSATION_SETTINGS_MEMBERS_SECTION_TITLE_FORMAT",
comment: "Format for the section title of the 'members' section in conversation settings view. Embeds: {{ the number of group members }}.")
section.headerTitle = String(format: headerFormat,
OWSFormat.formatInt(totalMemberCount))
} else {
section.headerTitle = NSLocalizedString("CONVERSATION_SETTINGS_MEMBERS_SECTION_TITLE",
comment: "Section title of the 'members' section in conversation settings view.")
}
var membersToRender = sortedGroupMembers
let maxMembersToShow = 6
let hasMoreMembers = !isShowingAllGroupMembers && membersToRender.count > maxMembersToShow
if hasMoreMembers {
membersToRender = Array(membersToRender.prefix(maxMembersToShow - 1))
}
for memberAddress in membersToRender {
guard let verificationState = groupMemberStateMap[memberAddress] else {
owsFailDebug("Missing verificationState.")
continue
}
let isLocalUser = memberAddress.isLocalAddress
section.add(OWSTableItem(customCellBlock: { [weak self] in
guard let self = self else {
owsFailDebug("Missing self")
return OWSTableItem.newCell()
}
let tableView = self.tableView
guard let cell = tableView.dequeueReusableCell(withIdentifier: ContactTableViewCell.reuseIdentifier) as? ContactTableViewCell else {
owsFailDebug("Missing cell.")
return UITableViewCell()
}
Self.databaseStorage.read { transaction in
let configuration = ContactCellConfiguration(address: memberAddress, localUserDisplayMode: .asLocalUser)
let isGroupAdmin = groupMembership.isFullMemberAndAdministrator(memberAddress)
let isVerified = verificationState == .verified
let isNoLongerVerified = verificationState == .noLongerVerified
let isBlocked = helper.isSignalServiceAddressBlocked(memberAddress)
if isGroupAdmin {
configuration.accessoryMessage = NSLocalizedString("GROUP_MEMBER_ADMIN_INDICATOR",
comment: "Label indicating that a group member is an admin.")
} else if isNoLongerVerified {
configuration.accessoryMessage = NSLocalizedString("CONTACT_CELL_IS_NO_LONGER_VERIFIED",
comment: "An indicator that a contact is no longer verified.")
} else if isBlocked {
configuration.accessoryMessage = MessageStrings.conversationIsBlocked
}
if isLocalUser {
cell.selectionStyle = .none
} else {
cell.selectionStyle = .default
}
if isVerified {
configuration.useVerifiedSubtitle()
} else if !memberAddress.isLocalAddress,
let bioForDisplay = (Self.profileManagerImpl.profileBioForDisplay(for: memberAddress,
transaction: transaction)) {
configuration.attributedSubtitle = NSAttributedString(string: bioForDisplay)
} else {
owsAssertDebug(configuration.attributedSubtitle == nil)
}
cell.configure(configuration: configuration, transaction: transaction)
}
let cellName = "user.\(memberAddress.stringForDisplay)"
cell.accessibilityIdentifier = UIView.accessibilityIdentifier(in: self, name: cellName)
return cell
}) { [weak self] in
self?.didSelectGroupMember(memberAddress)
})
}
if hasMoreMembers {
let offset = canEditConversationMembership ? 1 : 0
let expandedMemberIndices = ((membersToRender.count + offset)..<(totalMemberCount + offset)).map {
IndexPath(row: $0, section: sectionIndex)
}
section.add(OWSTableItem(
customCellBlock: { [weak self] in
guard let self = self else {
owsFailDebug("Missing self")
return OWSTableItem.newCell()
}
let cell = OWSTableItem.newCell()
cell.preservesSuperviewLayoutMargins = true
cell.contentView.preservesSuperviewLayoutMargins = true
let iconView = OWSTableItem.buildIconInCircleView(icon: .settingsShowAllMembers,
iconSize: AvatarBuilder.smallAvatarSizePoints,
innerIconSize: 24,
iconTintColor: Theme.primaryTextColor)
let rowLabel = UILabel()
rowLabel.text = CommonStrings.seeAllButton
rowLabel.textColor = Theme.primaryTextColor
rowLabel.font = OWSTableItem.primaryLabelFont
rowLabel.lineBreakMode = .byTruncatingTail
let contentRow = UIStackView(arrangedSubviews: [ iconView, rowLabel ])
contentRow.spacing = self.iconSpacingSmall
cell.contentView.addSubview(contentRow)
contentRow.autoPinWidthToSuperviewMargins()
contentRow.autoPinHeightToSuperview(withMargin: 7)
return cell
},
actionBlock: { [weak self] in
self?.showAllGroupMembers(revealingIndices: expandedMemberIndices)
}
))
}
return section
}
private func buildGroupSettingsSection(
groupModelV2: TSGroupModelV2,
contents: OWSTableContents
) {
let section = OWSTableSection()
if RemoteConfig.groupsV2InviteLinks {
let groupLinkStatus = (groupModelV2.isGroupInviteLinkEnabled
? CommonStrings.switchOn
: CommonStrings.switchOff)
section.add(OWSTableItem.disclosureItem(icon: .settingsLink,
name: NSLocalizedString("CONVERSATION_SETTINGS_GROUP_LINK",
comment: "Label for 'group link' action in conversation settings view."),
accessoryText: groupLinkStatus,
accessibilityIdentifier: "conversation_settings_group_link",
actionBlock: { [weak self] in
self?.showGroupLinkView()
}))
}
let itemTitle = (RemoteConfig.groupsV2InviteLinks
? NSLocalizedString("CONVERSATION_SETTINGS_MEMBER_REQUESTS_AND_INVITES",
comment: "Label for 'member requests & invites' action in conversation settings view.")
: NSLocalizedString("CONVERSATION_SETTINGS_MEMBER_INVITES",
comment: "Label for 'member invites' action in conversation settings view."))
section.add(OWSTableItem.disclosureItem(icon: .settingsViewRequestAndInvites,
name: itemTitle,
accessoryText: OWSFormat.formatInt(groupModelV2.groupMembership.invitedOrRequestMembers.count),
accessibilityIdentifier: "conversation_settings_requests_and_invites",
actionBlock: { [weak self] in
self?.showMemberRequestsAndInvitesView()
}))
if canEditPermissions {
let itemTitle = NSLocalizedString(
"CONVERSATION_SETTINGS_PERMISSIONS",
comment: "Label for 'permissions' action in conversation settings view."
)
section.add(OWSTableItem.disclosureItem(
icon: .settingsPrivacy,
name: itemTitle,
accessibilityIdentifier: "conversation_settings_permissions",
actionBlock: { [weak self] in
self?.showPermissionsSettingsView()
}
))
}
contents.addSection(section)
}
private func buildMutualGroupsSection(sectionIndex: Int) -> OWSTableSection {
let section = OWSTableSection()
section.separatorInsetLeading = NSNumber(value: Float(Self.cellHInnerMargin + CGFloat(AvatarBuilder.smallAvatarSizePoints) + ContactCellView.avatarTextHSpacing))
// "Add to a Group" cell.
section.add(OWSTableItem(
customCellBlock: { [weak self] in
guard let self = self else {
owsFailDebug("Missing self")
return OWSTableItem.newCell()
}
let cell = OWSTableItem.newCell()
cell.preservesSuperviewLayoutMargins = true
cell.contentView.preservesSuperviewLayoutMargins = true
let iconView = OWSTableItem.buildIconInCircleView(
icon: .settingsAddMembers,
iconSize: AvatarBuilder.smallAvatarSizePoints,
innerIconSize: 24,
iconTintColor: Theme.primaryTextColor
)
let rowLabel = UILabel()
rowLabel.text = NSLocalizedString("ADD_TO_GROUP_TITLE", comment: "Title of the 'add to group' view.")
rowLabel.textColor = Theme.primaryTextColor
rowLabel.font = OWSTableItem.primaryLabelFont
rowLabel.lineBreakMode = .byTruncatingTail
let contentRow = UIStackView(arrangedSubviews: [ iconView, rowLabel ])
contentRow.spacing = self.iconSpacingSmall
cell.contentView.addSubview(contentRow)
contentRow.autoPinWidthToSuperviewMargins()
contentRow.autoPinHeightToSuperview(withMargin: 7)
return cell
},
actionBlock: { [weak self] in
self?.showAddToGroupView()
}
))
if mutualGroupThreads.count > 1 {
let headerFormat = NSLocalizedString(
"CONVERSATION_SETTINGS_MUTUAL_GROUPS_SECTION_TITLE_FORMAT",
comment: "Format for the section title of the 'mutual groups' section in conversation settings view. Embeds: {{ the number of shared groups }}."
)
section.headerTitle = String(format: headerFormat, OWSFormat.formatInt(mutualGroupThreads.count))
} else if mutualGroupThreads.count == 1 {
section.headerTitle = NSLocalizedString(
"CONVERSATION_SETTINGS_ONE_MUTUAL_GROUPS_SECTION_TITLE",
comment: "Section title of the 'mutual groups' section in conversation settings view when the contact shares one mutual group."
)
} else {
section.headerTitle = NSLocalizedString(
"CONVERSATION_SETTINGS_NO_MUTUAL_GROUPS_SECTION_TITLE",
comment: "Section title of the 'mutual groups' section in conversation settings view when the contact shares no mutual groups."
)
}
let maxGroupsToShow = 6
let hasMoreGroups = !isShowingAllMutualGroups && mutualGroupThreads.count > maxGroupsToShow
let groupThreadsToRender: [TSGroupThread]
if hasMoreGroups {
groupThreadsToRender = Array(mutualGroupThreads.prefix(maxGroupsToShow - 1))
} else {
groupThreadsToRender = mutualGroupThreads
}
for groupThread in groupThreadsToRender {
section.add(OWSTableItem(
customCellBlock: {
let cell = GroupTableViewCell()
cell.configure(thread: groupThread)
return cell
},
actionBlock: { [weak self] in
self?.signalApp.presentConversation(for: groupThread, animated: true)
}
))
}
if hasMoreGroups {
let expandedGroupIndices = ((groupThreadsToRender.count + 1)..<(mutualGroupThreads.count + 1)).map {
IndexPath(row: $0, section: sectionIndex)
}
section.add(OWSTableItem(
customCellBlock: { [weak self] in
guard let self = self else {
owsFailDebug("Missing self")
return OWSTableItem.newCell()
}
let cell = OWSTableItem.newCell()
cell.preservesSuperviewLayoutMargins = true
cell.contentView.preservesSuperviewLayoutMargins = true
let iconView = OWSTableItem.buildIconInCircleView(
icon: .settingsShowAllMembers,
iconSize: AvatarBuilder.smallAvatarSizePoints,
innerIconSize: 24,
iconTintColor: Theme.primaryTextColor
)
let rowLabel = UILabel()
rowLabel.text = CommonStrings.seeAllButton
rowLabel.textColor = Theme.primaryTextColor
rowLabel.font = OWSTableItem.primaryLabelFont
rowLabel.lineBreakMode = .byTruncatingTail
let contentRow = UIStackView(arrangedSubviews: [ iconView, rowLabel ])
contentRow.spacing = self.iconSpacingSmall
cell.contentView.addSubview(contentRow)
contentRow.autoPinWidthToSuperviewMargins()
contentRow.autoPinHeightToSuperview(withMargin: 7)
return cell
},
actionBlock: { [weak self] in
self?.showAllMutualGroups(revealingIndices: expandedGroupIndices)
}
))
}
return section
}
}