Creates a special data source enum for ConversationAvatarView data providers. Before, ContactCell and ConversationAvatarView used the data source, neither applying to each perfectly
759 lines
35 KiB
Swift
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
|
|
}
|
|
}
|