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

import SignalServiceKit
import SignalUI

enum ContactSupportActionSheet {
    enum EmailFilter: Equatable {
        enum RegistrationPINMode: String {
            case v2NoReglock
            case v2WithReglock
            case v2WithUnknownReglockState
        }

        case registrationPINMode(RegistrationPINMode)
        case deviceTransfer
        case backupExportFailed
        case backupImportFailed
        case backupDisableFailed
        case custom(String)

        fileprivate var asString: String {
            return switch self {
            case .registrationPINMode(.v2NoReglock): "Signal PIN - iOS (V2 PIN without RegLock)"
            case .registrationPINMode(.v2WithReglock): "Signal PIN - iOS (V2 PIN)"
            case .registrationPINMode(.v2WithUnknownReglockState): "Signal PIN - iOS (V2 PIN with unknown reglock)"
            case .deviceTransfer: "Signal iOS Transfer"
            case .backupExportFailed: "iOS Backup Export Failed"
            case .backupImportFailed: "iOS Backup Import Failed"
            case .backupDisableFailed: "iOS Backup Disable Failed"
            case .custom(let string): string
            }
        }
    }

    static func present(
        emailFilter: EmailFilter,
        logDumper: DebugLogDumper,
        fromViewController: UIViewController,
        completion: (() -> Void)? = nil,
    ) {
        Logger.warn("Presenting contact-support action sheet! \(emailFilter)")

        let submitWithLogTitle = OWSLocalizedString("CONTACT_SUPPORT_SUBMIT_WITH_LOG", comment: "Button text")
        let submitWithLogAction = ActionSheetAction(title: submitWithLogTitle, style: .default) { [weak fromViewController] _ in
            guard let fromViewController else { return }

            let emailRequest = SupportEmailModel(
                userDescription: nil,
                emojiMood: nil,
                supportFilter: emailFilter.asString,
                debugLogPolicy: .requireUpload(logDumper),
                hasRecentChallenge: logDumper.challengeReceivedRecently(),
            )

            ModalActivityIndicatorViewController.present(
                fromViewController: fromViewController,
                title: CommonStrings.preparingModal,
                canCancel: true,
                asyncBlock: { modal in
                    let result = await Result {
                        try await ComposeSupportEmailOperation(model: emailRequest).perform()
                    }
                    modal.dismissIfNotCanceled(completionIfNotCanceled: {
                        do {
                            try result.get()
                            completion?()
                        } catch {
                            showError(
                                error,
                                emailFilter: emailFilter,
                                logDumper: logDumper,
                                fromViewController: fromViewController,
                                completion: completion,
                            )
                        }
                    })
                },
            )
        }

        let submitWithoutLogTitle = OWSLocalizedString("CONTACT_SUPPORT_SUBMIT_WITHOUT_LOG", comment: "Button text")
        let submitWithoutLogAction = ActionSheetAction(title: submitWithoutLogTitle, style: .default) { [weak fromViewController] _ in
            guard let fromViewController else { return }
            Task {
                do {
                    try await ComposeSupportEmailOperation.sendEmail(
                        supportFilter: emailFilter.asString,
                        logUrl: nil,
                        hasRecentChallenge: logDumper.challengeReceivedRecently(),
                    )
                } catch {
                    showError(error, emailFilter: emailFilter, logDumper: logDumper, fromViewController: fromViewController)
                }
            }
        }

        let title = OWSLocalizedString("CONTACT_SUPPORT_PROMPT_TO_INCLUDE_DEBUG_LOG_TITLE", comment: "Alert title")
        let message = OWSLocalizedString("CONTACT_SUPPORT_PROMPT_TO_INCLUDE_DEBUG_LOG_MESSAGE", comment: "Alert body")
        let actionSheet = ActionSheetController(title: title, message: message)
        actionSheet.addAction(submitWithLogAction)
        actionSheet.addAction(submitWithoutLogAction)
        actionSheet.addAction(ActionSheetAction(
            title: CommonStrings.cancelButton,
            style: .cancel,
            handler: { _ in
                completion?()
            },
        ))

        fromViewController.present(actionSheet, animated: true)
    }

    private static func showError(
        _ error: Error,
        emailFilter: EmailFilter,
        logDumper: DebugLogDumper,
        fromViewController: UIViewController,
        completion: (() -> Void)? = nil,
    ) {
        let retryTitle = OWSLocalizedString("CONTACT_SUPPORT_PROMPT_ERROR_TRY_AGAIN", comment: "button text")
        let retryAction = ActionSheetAction(title: retryTitle, style: .default) { [weak fromViewController] _ in
            guard let fromViewController else { return }

            present(
                emailFilter: emailFilter,
                logDumper: logDumper,
                fromViewController: fromViewController,
                completion: completion,
            )
        }

        let message = OWSLocalizedString("CONTACT_SUPPORT_PROMPT_ERROR_ALERT_BODY", comment: "Alert body")
        let actionSheet = ActionSheetController(title: error.userErrorDescription, message: message)
        actionSheet.addAction(retryAction)
        actionSheet.addAction(ActionSheetAction(
            title: CommonStrings.cancelButton,
            style: .cancel,
            handler: { _ in
                completion?()
            },
        ))

        fromViewController.present(actionSheet, animated: true)
    }
}

// MARK: -

extension ActionSheetAction {
    static func contactSupport(
        emailFilter: ContactSupportActionSheet.EmailFilter,
        fromViewController: UIViewController,
    ) -> ActionSheetAction {
        ActionSheetAction(title: CommonStrings.contactSupport) { _ in
            ContactSupportActionSheet.present(
                emailFilter: emailFilter,
                logDumper: .fromGlobals(),
                fromViewController: fromViewController,
            )
        }
    }
}

extension OWSActionSheets {
    static func showContactSupportActionSheet(
        title: String? = nil,
        message: String,
        emailFilter: ContactSupportActionSheet.EmailFilter,
        fromViewController: UIViewController,
    ) {
        let actionSheet = ActionSheetController(title: title, message: message)

        actionSheet.addAction(ActionSheetAction(title: CommonStrings.contactSupport) { _ in
            ContactSupportActionSheet.present(
                emailFilter: emailFilter,
                logDumper: .fromGlobals(),
                fromViewController: fromViewController,
            )
        })

        actionSheet.addAction(.okay)
        showActionSheet(actionSheet, fromViewController: fromViewController)
    }
}