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

import SignalServiceKit
import SignalUI

protocol GroupAttributesViewControllerDelegate: AnyObject {
    func groupAttributesDidUpdate()
}

// MARK: -

class GroupAttributesViewController: OWSTableViewController2 {

    enum EditAction {
        case none
        case avatar
    }

    private weak var attributesDelegate: GroupAttributesViewControllerDelegate?

    private let groupThread: TSGroupThread

    private let helper: GroupAttributesEditorHelper

    private var editAction: EditAction?

    private var hasUnsavedChanges: Bool {
        return helper.hasUnsavedChanges
    }

    init(
        groupThread: TSGroupThread,
        editAction: EditAction,
        delegate: GroupAttributesViewControllerDelegate,
    ) {
        self.groupThread = groupThread
        self.editAction = editAction
        self.attributesDelegate = delegate

        self.helper = GroupAttributesEditorHelper(groupModel: groupThread.groupModel, renderDefaultAvatarWhenCleared: true)

        super.init()
    }

    // MARK: - View Lifecycle

    override func viewDidLoad() {
        super.viewDidLoad()

        view.backgroundColor = Theme.backgroundColor

        title = OWSLocalizedString("EDIT_GROUP_DEFAULT_TITLE", comment: "The navbar title for the 'update group' view.")

        defaultSeparatorInsetLeading = Self.cellHInnerMargin + 24 + OWSTableItem.iconSpacing

        helper.delegate = self
        helper.buildContents()

        updateTableContents()
    }

    func updateTableContents() {
        let contents = OWSTableContents()

        let avatarSection = OWSTableSection()
        avatarSection.hasBackground = false
        avatarSection.add(.init(
            customCellBlock: { [weak self] in
                let cell = OWSTableItem.newCell()
                cell.selectionStyle = .none
                guard let self else { return cell }
                cell.contentView.addSubview(self.helper.avatarWrapper)
                self.helper.avatarWrapper.autoPinHeightToSuperviewMargins()
                self.helper.avatarWrapper.autoHCenterInSuperview()
                return cell
            },
            actionBlock: { [weak self] in
                self?.didTapAvatarView()
            },
        ))
        contents.add(avatarSection)

        let nameAndDescriptionSection = OWSTableSection()
        nameAndDescriptionSection.add(.disclosureItem(
            icon: .groupInfoEditName,
            withText: helper.groupNameCurrent ?? OWSLocalizedString(
                "GROUP_NAME_VIEW_TITLE",
                comment: "Title for the group name view.",
            ),
            actionBlock: { [weak self] in
                guard let self else { return }
                let vc = GroupNameViewController(
                    groupModel: self.groupThread.groupModel,
                    groupNameCurrent: self.helper.groupNameCurrent,
                )
                vc.nameDelegate = self
                self.presentFormSheet(OWSNavigationController(rootViewController: vc), animated: true)
            },
        ))
        nameAndDescriptionSection.add(.disclosureItem(
            icon: .groupInfoEditDescription,
            withText: helper.groupDescriptionCurrent ?? OWSLocalizedString(
                "GROUP_DESCRIPTION_VIEW_TITLE",
                comment: "Title for the group description view.",
            ),
            maxNameLines: 2,
            actionBlock: { [weak self] in
                guard let self else { return }
                let vc = GroupDescriptionViewController(
                    groupModel: self.groupThread.groupModel,
                    groupDescriptionCurrent: self.helper.groupDescriptionCurrent,
                    options: .editImmediately,
                )
                vc.descriptionDelegate = self
                self.presentFormSheet(OWSNavigationController(rootViewController: vc), animated: true)
            },
        ))
        contents.add(nameAndDescriptionSection)

        self.contents = contents
    }

    override func viewDidAppear(_ animated: Bool) {
        super.viewDidAppear(animated)

        if let editAction = self.editAction {
            switch editAction {
            case .none:
                break
            case .avatar:
                helper.showAvatarUI()
            }
            self.editAction = nil
        }
    }

    // MARK: -

    fileprivate func updateNavbar() {
        if helper.hasUnsavedChanges {
            navigationItem.rightBarButtonItem = UIBarButtonItem(
                title: CommonStrings.setButton,
                style: .done,
                target: self,
                action: #selector(setButtonPressed),
                accessibilityIdentifier: "set_button",
            )
        } else {
            navigationItem.rightBarButtonItem = nil
        }
    }

    @objc
    private func setButtonPressed() {
        updateGroupThreadAndDismiss()
    }

    // MARK: - Events

    @objc
    private func didTapAvatarView() {
        helper.didTapAvatarView()
    }
}

// MARK: -

extension GroupAttributesViewController {

    var shouldCancelNavigationBack: Bool {
        let result = hasUnsavedChanges
        if result {
            GroupAttributesViewController.showUnsavedGroupChangesActionSheet(
                from: self,
                saveBlock: {
                    self.updateGroupThreadAndDismiss()
                },
                discardBlock: {
                    self.navigationController?.popViewController(animated: true)
                },
            )
        }
        return result
    }

    static func showUnsavedGroupChangesActionSheet(
        from fromViewController: UIViewController,
        saveBlock: @escaping () -> Void,
        discardBlock: @escaping () -> Void,
    ) {
        let actionSheet = ActionSheetController(
            title: OWSLocalizedString(
                "EDIT_GROUP_VIEW_UNSAVED_CHANGES_TITLE",
                comment: "The alert title if user tries to exit update group view without saving changes.",
            ),
            message: OWSLocalizedString(
                "EDIT_GROUP_VIEW_UNSAVED_CHANGES_MESSAGE",
                comment: "The alert message if user tries to exit update group view without saving changes.",
            ),
        )
        actionSheet.addAction(ActionSheetAction(
            title: CommonStrings.saveButton,
            style: .default,
        ) { _ in
            saveBlock()
        })
        actionSheet.addAction(ActionSheetAction(
            title: OWSLocalizedString(
                "ALERT_DONT_SAVE",
                comment: "The label for the 'don't save' button in action sheets.",
            ),
            style: .destructive,
        ) { _ in
            discardBlock()
        })
        fromViewController.presentActionSheet(actionSheet)
    }
}

// MARK: -

private extension GroupAttributesViewController {
    func updateGroupThreadAndDismiss() {
        helper.updateGroupIfNecessary(fromViewController: self) { [weak self] in
            self?.attributesDelegate?.groupAttributesDidUpdate()
            self?.navigationController?.popViewController(animated: true)
        }
    }
}

// MARK: -

extension GroupAttributesViewController: GroupAttributesEditorHelperDelegate {
    func groupAttributesEditorContentsDidChange() {
        updateNavbar()
    }
}

extension GroupAttributesViewController: GroupDescriptionViewControllerDelegate {
    func groupDescriptionViewControllerDidComplete(groupDescription: String?) {
        helper.groupDescriptionCurrent = groupDescription
        updateNavbar()
        updateTableContents()
    }
}

extension GroupAttributesViewController: GroupNameViewControllerDelegate {
    func groupNameViewControllerDidComplete(groupName: String?) {
        helper.groupNameCurrent = groupName
        updateNavbar()
        updateTableContents()
    }
}