Signal-iOS/Signal/src/ViewControllers/ThreadSettings/AddGroupMembersViewController.swift
2020-04-06 11:09:06 -03:00

246 lines
9.7 KiB
Swift

//
// Copyright (c) 2020 Open Whisper Systems. All rights reserved.
//
import Foundation
import PromiseKit
protocol AddGroupMembersViewControllerDelegate: class {
func addGroupMembersViewDidUpdate()
}
// MARK: -
@objc
public class AddGroupMembersViewController: BaseGroupMemberViewController {
// MARK: - Dependencies
fileprivate var databaseStorage: SDSDatabaseStorage {
return SDSDatabaseStorage.shared
}
fileprivate var tsAccountManager: TSAccountManager {
return .sharedInstance()
}
private var contactsManager: OWSContactsManager {
return Environment.shared.contactsManager
}
// MARK: -
weak var addGroupMembersViewControllerDelegate: AddGroupMembersViewControllerDelegate?
private let groupThread: TSGroupThread
private let oldGroupModel: TSGroupModel
private var newRecipientSet = OrderedSet<PickedRecipient>()
public required init(groupThread: TSGroupThread) {
self.groupThread = groupThread
self.oldGroupModel = groupThread.groupModel
super.init()
groupMemberViewDelegate = self
}
// MARK: - View Lifecycle
@objc
public override func viewDidLoad() {
super.viewDidLoad()
title = NSLocalizedString("ADD_GROUP_MEMBERS_VIEW_TITLE",
comment: "The title for the 'add group members' view.")
}
// MARK: -
fileprivate func updateNavbar() {
if hasUnsavedChanges {
navigationItem.rightBarButtonItem = UIBarButtonItem(title: NSLocalizedString("EDIT_GROUP_UPDATE_BUTTON",
comment: "The title for the 'update group' button."),
style: .plain,
target: self,
action: #selector(updateGroupPressed),
accessibilityIdentifier: UIView.accessibilityIdentifier(in: self, name: "update"))
} else {
navigationItem.rightBarButtonItem = nil
}
}
@objc func updateGroupPressed() {
showConfirmationAlert()
}
private func showConfirmationAlert() {
let newMemberCount = newRecipientSet.count
guard newMemberCount > 0 else {
owsFailDebug("No new members.")
return
}
let groupName = contactsManager.displayNameWithSneakyTransaction(thread: groupThread)
let alertTitle: String
let alertMessage: String
let actionTitle: String
if newMemberCount > 1 {
let messageFormat = NSLocalizedString("ADD_GROUP_MEMBERS_VIEW_CONFIRM_ALERT_MESSAGE_N_FORMAT",
comment: "Format for the message for the 'add group members' confirmation alert. Embeds {{ %1$@ number of new members, %2$@ name of the group. }}.")
alertMessage = String(format: messageFormat,
OWSFormat.formatInt(newRecipientSet.count),
groupName)
alertTitle = NSLocalizedString("ADD_GROUP_MEMBERS_VIEW_CONFIRM_ALERT_TITLE_N",
comment: "Title for the 'add group members' confirmation alert.")
actionTitle = NSLocalizedString("ADD_GROUP_MEMBERS_ACTION_TITLE_N",
comment: "Label for the 'add group members' button.")
} else {
let messageFormat = NSLocalizedString("ADD_GROUP_MEMBERS_VIEW_CONFIRM_ALERT_MESSAGE_1_FORMAT",
comment: "Format for the message for the 'add group member' confirmation alert. Embeds {{ the name of the group. }}.")
alertMessage = String(format: messageFormat, groupName)
alertTitle = NSLocalizedString("ADD_GROUP_MEMBERS_VIEW_CONFIRM_ALERT_TITLE_1",
comment: "Title for the 'add group member' confirmation alert.")
actionTitle = NSLocalizedString("ADD_GROUP_MEMBERS_ACTION_TITLE_1",
comment: "Label for the 'add group member' button.")
}
let actionSheet = ActionSheetController(title: alertTitle, message: alertMessage)
actionSheet.addAction(ActionSheetAction(title: actionTitle,
style: .default,
handler: { [weak self] _ in
self?.updateGroupThreadAndDismiss()
}))
actionSheet.addAction(OWSActionSheets.cancelAction)
self.presentActionSheet(actionSheet)
}
}
// MARK: -
private extension AddGroupMembersViewController {
func buildNewGroupModel() -> TSGroupModel? {
do {
return try databaseStorage.read { transaction in
var builder = self.oldGroupModel.asBuilder
let oldGroupMembership = self.oldGroupModel.groupMembership
var groupMembershipBuilder = oldGroupMembership.asBuilder
let addresses = self.newRecipientSet.orderedMembers.compactMap { $0.address }
guard !addresses.isEmpty else {
owsFailDebug("No valid recipients.")
return nil
}
for address in addresses {
guard !oldGroupMembership.isPendingOrNonPendingMember(address) else {
owsFailDebug("Recipient is already in group.")
continue
}
// GroupManager will separate out members as pending if necessary.
groupMembershipBuilder.addNonPendingMember(address, role: .normal)
}
builder.groupMembership = groupMembershipBuilder.build()
return try builder.build(transaction: transaction)
}
} catch {
owsFailDebug("Error: \(error)")
return nil
}
}
func updateGroupThreadAndDismiss() {
let dismissAndUpdateDelegate = { [weak self] in
guard let self = self else {
return
}
self.addGroupMembersViewControllerDelegate?.addGroupMembersViewDidUpdate()
self.navigationController?.popViewController(animated: true)
}
guard hasUnsavedChanges else {
owsFailDebug("!hasUnsavedChanges.")
return dismissAndUpdateDelegate()
}
let oldGroupModel = self.oldGroupModel
guard let newGroupModel = buildNewGroupModel() else {
let error = OWSAssertionError("Couldn't build group model.")
GroupViewUtils.showUpdateErrorUI(error: error)
return
}
GroupViewUtils.updateGroupWithActivityIndicator(fromViewController: self,
updatePromiseBlock: {
self.updateGroupThreadPromise(oldGroupModel: oldGroupModel,
newGroupModel: newGroupModel)
},
completion: {
dismissAndUpdateDelegate()
})
}
func updateGroupThreadPromise(oldGroupModel: TSGroupModel,
newGroupModel: TSGroupModel) -> Promise<Void> {
guard let localAddress = tsAccountManager.localAddress else {
return Promise(error: OWSAssertionError("Missing localAddress."))
}
return firstly { () -> Promise<Void> in
return GroupManager.messageProcessingPromise(for: oldGroupModel,
description: self.logTag)
}.then(on: .global()) { _ in
// dmConfiguration: nil means don't change disappearing messages configuration.
GroupManager.localUpdateExistingGroup(groupModel: newGroupModel,
dmConfiguration: nil,
groupUpdateSourceAddress: localAddress)
}.asVoid()
}
}
// MARK: -
extension AddGroupMembersViewController: GroupMemberViewDelegate {
var groupMemberViewRecipientSet: OrderedSet<PickedRecipient> {
return newRecipientSet
}
var groupMemberViewHasUnsavedChanges: Bool {
return !newRecipientSet.isEmpty
}
func groupMemberViewRemoveRecipient(_ recipient: PickedRecipient) {
newRecipientSet.remove(recipient)
updateNavbar()
}
func groupMemberViewAddRecipient(_ recipient: PickedRecipient) {
newRecipientSet.append(recipient)
updateNavbar()
}
func groupMemberViewCanAddRecipient(_ recipient: PickedRecipient) -> Bool {
guard let address = recipient.address else {
owsFailDebug("Invalid recipient.")
return false
}
return databaseStorage.read { transaction in
return GroupManager.doesUserSupportGroupsV2(address: address, transaction: transaction)
}
}
func groupMemberViewIsPreExistingMember(_ recipient: PickedRecipient) -> Bool {
guard let address = recipient.address else {
owsFailDebug("Invalid recipient.")
return false
}
return oldGroupModel.groupMembership.isPendingOrNonPendingMember(address)
}
func groupMemberViewDismiss() {
navigationController?.popViewController(animated: true)
}
}