Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
signalapp
GitHub Repository: signalapp/Signal-iOS
Path: blob/main/Signal/src/ViewControllers/NewGroupView/BaseGroupMemberViewController.swift
1 views
//
// Copyright 2020 Signal Messenger, LLC
// SPDX-License-Identifier: AGPL-3.0-only
//

public import SignalServiceKit
public import SignalUI

protocol GroupMemberViewDelegate: AnyObject {
    var groupMemberViewRecipientSet: OrderedSet<PickedRecipient> { get }

    var groupMemberViewHasUnsavedChanges: Bool { get }

    func groupMemberViewRemoveRecipient(_ recipient: PickedRecipient)

    func groupMemberViewAddRecipient(_ recipient: PickedRecipient)

    func groupMemberViewShouldShowMemberCount() -> Bool

    func groupMemberViewGroupMemberCountForDisplay() -> Int

    func groupMemberViewIsGroupFull_HardLimit() -> Bool

    func groupMemberViewIsGroupFull_RecommendedLimit() -> Bool

    func groupMemberViewIsPreExistingMember(
        _ recipient: PickedRecipient,
        transaction: DBReadTransaction,
    ) -> Bool

    func groupMemberViewDismiss()

    var isNewGroup: Bool { get }

    var groupThreadForGroupMemberView: TSGroupThread? { get }
}

// MARK: -

// A base class used in two scenarios:
//
// * Picking members for a new group.
// * Add new members to an existing group.
//
// Note: Blocked and hidden recipients will be omitted from this view controller.
public class BaseGroupMemberViewController: BaseMemberViewController {

    // This delegate is the subclass.
    weak var groupMemberViewDelegate: GroupMemberViewDelegate?

    override public init() {
        super.init()

        memberViewDelegate = self
    }

    private func showGroupFullAlert_HardLimit() {
        let format = OWSLocalizedString(
            "EDIT_GROUP_ERROR_CANNOT_ADD_MEMBER_GROUP_FULL_%d",
            tableName: "PluralAware",
            comment: "Format for the 'group full' error alert when a user can't be added to a group because the group is full. Embeds {{ the maximum number of members in a group }}.",
        )
        let message = String.localizedStringWithFormat(format, RemoteConfig.current.maxGroupSizeHardLimit)
        OWSActionSheets.showErrorAlert(message: message)
    }

    private var ignoreSoftLimit = false
    private func showGroupFullAlert_SoftLimit(
        recipient: PickedRecipient,
        groupMemberViewDelegate: GroupMemberViewDelegate,
    ) {
        let title = OWSLocalizedString(
            "GROUPS_TOO_MANY_MEMBERS_ALERT_TITLE",
            comment: "Title for alert warning the user that they've reached the recommended limit on how many members can be in a group.",
        )
        let messageFormat = OWSLocalizedString(
            "GROUPS_TOO_MANY_MEMBERS_ALERT_MESSAGE_%d",
            tableName: "PluralAware",
            comment: "Format for the alert warning the user that they've reached the recommended limit on how many members can be in a group when creating a new group. Embeds {{ the maximum number of recommended members in a group }}.",
        )
        var message = String.localizedStringWithFormat(messageFormat, RemoteConfig.current.maxGroupSizeRecommended)

        if groupMemberViewDelegate.isNewGroup {
            let actionSheet = ActionSheetController(title: title, message: message)

            actionSheet.addAction(ActionSheetAction(title: CommonStrings.okayButton) { [weak self] _ in
                guard let self else { return }
                self.ignoreSoftLimit = true
                self.addRecipient(recipient)
                self.ignoreSoftLimit = false
            })
            presentActionSheet(actionSheet)
        } else {
            message += (
                "\n\n"
                    + OWSLocalizedString(
                        "GROUPS_TOO_MANY_MEMBERS_CONFIRM",
                        comment: "Message asking the user to confirm that they want to add a member to the group.",
                    )
            )
            let actionSheet = ActionSheetController(title: title, message: message)

            actionSheet.addAction(ActionSheetAction(title: CommonStrings.addButton) { [weak self] _ in
                guard let self else { return }
                self.ignoreSoftLimit = true
                self.addRecipient(recipient)
                self.ignoreSoftLimit = false
            })
            actionSheet.addAction(OWSActionSheets.cancelAction)
            presentActionSheet(actionSheet)
        }
    }
}

extension BaseGroupMemberViewController: MemberViewDelegate {
    public var memberViewRecipientSet: OrderedSet<PickedRecipient> {
        guard let groupMemberViewDelegate else {
            owsFailDebug("Missing groupMemberViewDelegate.")
            return OrderedSet()
        }
        return groupMemberViewDelegate.groupMemberViewRecipientSet
    }

    public var memberViewHasUnsavedChanges: Bool {
        guard let groupMemberViewDelegate else {
            owsFailDebug("Missing groupMemberViewDelegate.")
            return false
        }
        return groupMemberViewDelegate.groupMemberViewHasUnsavedChanges
    }

    public func memberViewRemoveRecipient(_ recipient: PickedRecipient) {
        guard let groupMemberViewDelegate else {
            owsFailDebug("Missing groupMemberViewDelegate.")
            return
        }
        groupMemberViewDelegate.groupMemberViewRemoveRecipient(recipient)
    }

    public func memberViewAddRecipient(_ recipient: PickedRecipient) -> Bool {
        guard let groupMemberViewDelegate else {
            owsFailDebug("Missing groupMemberViewDelegate.")
            return false
        }
        guard let address = recipient.address else {
            owsFailDebug("Invalid recipient.")
            GroupViewUtils.showInvalidGroupMemberAlert(fromViewController: self)
            return false
        }
        guard GroupManager.doesUserSupportGroupsV2(address: address) else {
            GroupViewUtils.showInvalidGroupMemberAlert(fromViewController: self)
            return false
        }
        guard !groupMemberViewDelegate.groupMemberViewIsGroupFull_HardLimit() else {
            showGroupFullAlert_HardLimit()
            return false
        }
        guard ignoreSoftLimit || !groupMemberViewDelegate.groupMemberViewIsGroupFull_RecommendedLimit() else {
            showGroupFullAlert_SoftLimit(recipient: recipient, groupMemberViewDelegate: groupMemberViewDelegate)
            return false
        }
        groupMemberViewDelegate.groupMemberViewAddRecipient(recipient)
        return true
    }

    public func memberViewShouldShowMemberCount() -> Bool {
        guard let groupMemberViewDelegate else {
            owsFailDebug("Missing groupMemberViewDelegate.")
            return false
        }
        return groupMemberViewDelegate.groupMemberViewShouldShowMemberCount()
    }

    public func memberViewShouldAllowBlockedSelection() -> Bool { false }

    public func memberViewMemberCountForDisplay() -> Int {
        guard let groupMemberViewDelegate else {
            owsFailDebug("Missing groupMemberViewDelegate.")
            return 0
        }
        return groupMemberViewDelegate.groupMemberViewGroupMemberCountForDisplay()
    }

    public func memberViewIsPreExistingMember(_ recipient: PickedRecipient, transaction: DBReadTransaction) -> Bool {
        guard let groupMemberViewDelegate else {
            owsFailDebug("Missing groupMemberViewDelegate.")
            return false
        }
        return groupMemberViewDelegate.groupMemberViewIsPreExistingMember(recipient, transaction: transaction)
    }

    public func memberViewCustomIconNameForPickedMember(_ recipient: PickedRecipient) -> String? { nil }

    public func memberViewCustomIconColorForPickedMember(_ recipient: PickedRecipient) -> UIColor? { nil }

    public func memberViewDismiss() {
        guard let groupMemberViewDelegate else {
            owsFailDebug("Missing groupMemberViewDelegate.")
            return
        }
        groupMemberViewDelegate.groupMemberViewDismiss()
    }
}