760 lines
35 KiB
Swift
760 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 hasExistingContact: Bool {
|
|
guard let contactThread = thread as? TSContactThread else {
|
|
owsFailDebug("Invalid thread.")
|
|
return false
|
|
}
|
|
return contactsManagerImpl.hasSignalAccount(for: contactThread.contactAddress)
|
|
}
|
|
|
|
// 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 hasExistingContact {
|
|
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(kSmallAvatarSize) + 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: kSmallAvatarSize,
|
|
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.build(address: memberAddress,
|
|
localUserDisplayMode: .asLocalUser,
|
|
transaction: transaction)
|
|
|
|
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: kSmallAvatarSize,
|
|
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 canEditConversationAccess {
|
|
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(kSmallAvatarSize) + 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: kSmallAvatarSize,
|
|
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: kSmallAvatarSize,
|
|
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
|
|
}
|
|
}
|