From a2d8cefba737cd90bdc1772b7cda3358755ae2d3 Mon Sep 17 00:00:00 2001 From: Saeed Bashir Date: Thu, 15 Feb 2024 10:49:34 +0500 Subject: [PATCH] chore: learner feedback collection from the mobile app (#1819) --- Source/OEXAnalytics+Swift.swift | 2 + Source/ProfileOptionsViewController.swift | 57 +++++++++++++++++++---- Source/ServerConfiguration.swift | 3 ++ 3 files changed, 54 insertions(+), 8 deletions(-) diff --git a/Source/OEXAnalytics+Swift.swift b/Source/OEXAnalytics+Swift.swift index e901e00f7..b5b5abf42 100644 --- a/Source/OEXAnalytics+Swift.swift +++ b/Source/OEXAnalytics+Swift.swift @@ -103,6 +103,7 @@ public enum AnalyticsDisplayName : String { case PrivacyPolicyClicked = "Privacy Policy Clicked" case CookiePolicyClicked = "Cookie Policy Clicked" case DataSellConsentClicked = "Do Not Sell Data Clicked" + case SubmitFeedbackClicked = "Submit feedback clicked" } public enum AnalyticsEventName: String { @@ -194,6 +195,7 @@ public enum AnalyticsEventName: String { case PrivacyPolicyClicked = "edx.bi.app.profile.privacy_policy.clicked" case CookiePolicyClicked = "edx.bi.app.profile.cookie_policy.clicked" case DataSellConsentClicked = "edx.bi.app.profile.do_not_sell_data.clicked" + case SubmitFeedbackClicked = "edx.bi.app.profile.submit_feedback.clicked" } public enum AnalyticsScreenName: String { diff --git a/Source/ProfileOptionsViewController.swift b/Source/ProfileOptionsViewController.swift index d2c48c748..941bf8c95 100644 --- a/Source/ProfileOptionsViewController.swift +++ b/Source/ProfileOptionsViewController.swift @@ -21,7 +21,7 @@ class ProfileOptionsViewController: UIViewController { case personalInformation case restorePurchase case privacy - case help(Bool, Bool) + case help(Bool, Bool, Bool) case signout case deleteAccount } @@ -139,10 +139,11 @@ class ProfileOptionsViewController: UIViewController { } let feedbackEnabled = environment.config.feedbackEmailAddress() != nil + let submitFeedbackEnabled = environment.serverConfig.feedbackFormURL != nil let faqEnabled = environment.config.faqURL != nil if feedbackEnabled || faqEnabled { - options.append(.help(feedbackEnabled, faqEnabled)) + options.append(.help(feedbackEnabled, submitFeedbackEnabled, faqEnabled)) } options.append(.signout) @@ -217,8 +218,8 @@ extension ProfileOptionsViewController: UITableViewDataSource { case .privacy: return privacyCell(tableView, indexPath: indexPath) - case .help(let feedbackEnabled, let faqEnabled): - return helpCell(tableView, indexPath: indexPath, feedbackEnabled: feedbackEnabled, faqEnabled: faqEnabled) + case .help(let feedbackEnabled, let submitFeedbackEnabled, let faqEnabled): + return helpCell(tableView, indexPath: indexPath, feedbackEnabled: feedbackEnabled, submitFeedbackEnabled: submitFeedbackEnabled, faqEnabled: faqEnabled) case .signout: return signoutCell(tableView, indexPath: indexPath) @@ -271,10 +272,10 @@ extension ProfileOptionsViewController: UITableViewDataSource { return cell } - private func helpCell(_ tableView: UITableView, indexPath: IndexPath, feedbackEnabled: Bool, faqEnabled: Bool) -> UITableViewCell { + private func helpCell(_ tableView: UITableView, indexPath: IndexPath, feedbackEnabled: Bool, submitFeedbackEnabled: Bool, faqEnabled: Bool) -> UITableViewCell { let cell = tableView.dequeueReusableCell(withIdentifier: HelpCell.identifier, for: indexPath) as! HelpCell cell.delegate = self - cell.update(feedbackEnabled: feedbackEnabled, faqEnabled: faqEnabled, platformName: environment.config.platformName()) + cell.update(feedbackEnabled: feedbackEnabled, faqEnabled: faqEnabled, submitFeedbackEnabled: submitFeedbackEnabled, platformName: environment.config.platformName()) return cell } @@ -345,6 +346,14 @@ extension ProfileOptionsViewController: HelpCellDelegate { launchEmailComposer() } + func didTapSubmitFeedback() { + guard let url = environment.serverConfig.feedbackFormURL else { return } + environment.analytics.trackProfileOptionClcikEvent(displayName: AnalyticsDisplayName.SubmitFeedbackCLicked, name: AnalyticsEventName.SubmitFeedbackCLicked) + if UIApplication.shared.canOpenURL(url) { + UIApplication.shared.open(url, options: [:]) + } + } + func didTapFAQ() { guard let faqURL = environment.config.faqURL, let url = URL(string: faqURL) else { return } environment.analytics.trackProfileOptionClcikEvent(displayName: AnalyticsDisplayName.FAQClicked, name: AnalyticsEventName.FAQClicked) @@ -1132,6 +1141,7 @@ class PrivacyCell: UITableViewCell { protocol HelpCellDelegate: AnyObject { func didTapEmail() + func didTapSubmitFeedback() func didTapFAQ() } @@ -1142,6 +1152,7 @@ class HelpCell: UITableViewCell { private var feedbackEnabled: Bool = false private var faqEnabled: Bool = false + private var submitFeedbackEnabled: Bool = false private let lineSpacing: CGFloat = 4 @@ -1199,6 +1210,21 @@ class HelpCell: UITableViewCell { return button }() + private lazy var submitFeedbackButton: UIButton = { + let button = UIButton() + button.layer.borderWidth = 1 + button.layer.borderColor = OEXStyles.shared().neutralXLight().cgColor + button.oex_addAction({ [weak self] _ in + self?.delegate?.didTapSubmitFeedback() + }, for: .touchUpInside) + + let faqButtonTitle = [buttonStyle.attributedString(withText: Strings.ProfileOptions.Help.Heading.feedback), faqButtonIcon] + let attributedText = NSAttributedString.joinInNaturalLayout(attributedStrings: faqButtonTitle) + button.setAttributedTitle(attributedText, for: .normal) + button.accessibilityIdentifier = "HelpCell:submit-feedback-button" + return button + }() + private lazy var supportLabel: UILabel = { let label = UILabel() label.attributedText = subtitleTextStyle.attributedString(withText: Strings.ProfileOptions.Help.Heading.support) @@ -1253,10 +1279,11 @@ class HelpCell: UITableViewCell { fatalError("init(coder:) has not been implemented") } - func update(feedbackEnabled: Bool, faqEnabled: Bool, platformName: String) { + func update(feedbackEnabled: Bool, faqEnabled: Bool, submitFeedbackEnabled: Bool, platformName: String) { self.feedbackEnabled = feedbackEnabled self.faqEnabled = faqEnabled self.platformName = platformName + self.submitFeedbackEnabled = submitFeedbackEnabled setupViews() setupConstrains() } @@ -1268,6 +1295,9 @@ class HelpCell: UITableViewCell { feedbackSupportContainer.addSubview(feedbackLabel) feedbackSupportContainer.addSubview(feedbackSubtitleLabel) feedbackSupportContainer.addSubview(emailFeedbackButton) + if submitFeedbackEnabled { + feedbackSupportContainer.addSubview(submitFeedbackButton) + } contentView.addSubview(feedbackSupportContainer) } @@ -1312,8 +1342,19 @@ class HelpCell: UITableViewCell { make.height.greaterThanOrEqualTo(StandardVerticalMargin * 2) } + if submitFeedbackEnabled { + submitFeedbackButton.snp.makeConstraints { make in + make.top.equalTo(feedbackSubtitleLabel.snp.bottom).offset(StandardVerticalMargin) + make.leading.equalTo(feedbackSupportContainer) + make.trailing.equalTo(feedbackSupportContainer) + make.height.equalTo(StandardVerticalMargin * 5) + } + } + + let constraintView = submitFeedbackEnabled ? submitFeedbackButton : feedbackSubtitleLabel + emailFeedbackButton.snp.makeConstraints { make in - make.top.equalTo(feedbackSubtitleLabel.snp.bottom).offset(StandardVerticalMargin) + make.top.equalTo(constraintView.snp.bottom).offset(StandardVerticalMargin) make.leading.equalTo(feedbackSupportContainer) make.trailing.equalTo(feedbackSupportContainer) make.height.equalTo(StandardVerticalMargin * 5) diff --git a/Source/ServerConfiguration.swift b/Source/ServerConfiguration.swift index 6a389e79b..88bb38c9d 100644 --- a/Source/ServerConfiguration.swift +++ b/Source/ServerConfiguration.swift @@ -23,12 +23,14 @@ public extension ServerConfigProvider { case valuePropEnabled = "value_prop_enabled" case config = "config" case iapConfig = "iap_config" + case feedbackFormURL = "feedback_form_url" } @objc static let shared = ServerConfiguration() private(set) var valuePropEnabled: Bool = false private(set) var iapConfig: IAPConfig? = nil + private(set) var feedbackFormURL: URL? = nil private override init() { super.init() @@ -41,6 +43,7 @@ public extension ServerConfigProvider { let config = try? JSONSerialization.jsonObject(with: configData, options : []) as? Dictionary else { return } valuePropEnabled = config[Keys.valuePropEnabled] as? Bool ?? false + feedbackFormURL = (config[Keys.feedbackFormURL] as? String).flatMap { URL(string:$0)} if let iapDict = config[Keys.iapConfig] as? Dictionary { iapConfig = IAPConfig(dictionary: iapDict)