Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Customer Center] Add contact support button #4023

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -103,7 +103,8 @@ enum CustomerCenterConfigTestData {
"cancel": "Cancel",
"back": "Back"
]
)
),
support: .init(email: "test-support@revenuecat.com")
)

static let subscriptionInformationMonthlyRenewing: SubscriptionInformation = .init(
Expand Down
21 changes: 16 additions & 5 deletions RevenueCatUI/CustomerCenter/Data/CustomerCenterEnvironment.swift
Original file line number Diff line number Diff line change
Expand Up @@ -21,16 +21,22 @@ struct LocalizationKey: EnvironmentKey {

}

extension CustomerCenterConfigData.Localization {
struct AppearanceKey: EnvironmentKey {

/// Default ``CustomerCenterConfigData.Localization`` value for Environment usage
public static let `default` = CustomerCenterConfigData.Localization(locale: "en_US", localizedStrings: [:])
static let defaultValue: CustomerCenterConfigData.Appearance = .default

}

struct AppearanceKey: EnvironmentKey {
struct SupportKey: EnvironmentKey {

static let defaultValue: CustomerCenterConfigData.Appearance = .default
static let defaultValue: CustomerCenterConfigData.Support? = nil

}

extension CustomerCenterConfigData.Localization {

/// Default ``CustomerCenterConfigData.Localization`` value for Environment usage
public static let `default` = CustomerCenterConfigData.Localization(locale: "en_US", localizedStrings: [:])

}

Expand All @@ -53,4 +59,9 @@ extension EnvironmentValues {
set { self[AppearanceKey.self] = newValue }
}

var supportInformation: CustomerCenterConfigData.Support? {
get { self[SupportKey.self] }
set { self[SupportKey.self] = newValue }
}

}
26 changes: 19 additions & 7 deletions RevenueCatUI/CustomerCenter/URLUtilities.swift
Original file line number Diff line number Diff line change
Expand Up @@ -14,19 +14,31 @@
//

import Foundation
import SwiftUI

enum URLUtilities {

static func createMailURL() -> URL? {
let subject = "Support Request"
let body = "Please describe your issue or question."
#if os(iOS)

@available(iOS 15.0, macOS 12.0, tvOS 15.0, watchOS 8.0, *)
@available(macOS, unavailable)
@available(tvOS, unavailable)
@available(watchOS, unavailable)
@available(visionOS, unavailable)
static func createMailURLIfPossible(email: String, subject: String, body: String) -> URL? {
let encodedSubject = subject.addingPercentEncoding(withAllowedCharacters: .urlQueryAllowed) ?? ""
let encodedBody = body.addingPercentEncoding(withAllowedCharacters: .urlQueryAllowed) ?? ""

// swiftlint:disable:next todo
// TODO: make configurable
let urlString = "mailto:support@revenuecat.com?subject=\(encodedSubject)&body=\(encodedBody)"
return URL(string: urlString)
let urlString = "mailto:\(email)?subject=\(encodedSubject)&body=\(encodedBody)"

if let url = URL(string: urlString),
UIApplication.shared.canOpenURL(url) {
return url
}

return nil
}

#endif

}
2 changes: 2 additions & 0 deletions RevenueCatUI/CustomerCenter/Views/CustomerCenterView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ public struct CustomerCenterView: View {

private var localization: CustomerCenterConfigData.Localization
private var appearance: CustomerCenterConfigData.Appearance
private var supportInformation: CustomerCenterConfigData.Support?

/// Create a view to handle common customer support tasks
public init(customerCenterActionHandler: CustomerCenterActionHandler? = nil,
Expand Down Expand Up @@ -59,6 +60,7 @@ public struct CustomerCenterView: View {
destinationView(configuration: configuration)
.environment(\.localization, configuration.localization)
.environment(\.appearance, configuration.appearance)
.environment(\.supportInformation, configuration.support)
}
}
}
Expand Down
25 changes: 13 additions & 12 deletions RevenueCatUI/CustomerCenter/Views/ManageSubscriptionsView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -25,18 +25,14 @@ import SwiftUI
@available(visionOS, unavailable)
struct ManageSubscriptionsView: View {

@Environment(\.openURL)
var openURL

@Environment(\.appearance)
private var appearance: CustomerCenterConfigData.Appearance
@Environment(\.localization)
private var localization: CustomerCenterConfigData.Localization

@StateObject
private var viewModel: ManageSubscriptionsViewModel

@Environment(\.localization)
private var localization: CustomerCenterConfigData.Localization

init(screen: CustomerCenterConfigData.Screen,
customerCenterActionHandler: CustomerCenterActionHandler?) {
let viewModel = ManageSubscriptionsViewModel(screen: screen,
Expand Down Expand Up @@ -87,7 +83,7 @@ struct ManageSubscriptionsView: View {

if let subscriptionInformation = self.viewModel.subscriptionInformation {
SubscriptionDetailsView(subscriptionInformation: subscriptionInformation,
localization: localization,
localization: self.localization,
refundRequestStatusMessage: self.viewModel.refundRequestStatusMessage)
}

Expand Down Expand Up @@ -251,15 +247,20 @@ struct ManageSubscriptionsButtonsView: View {
var viewModel: ManageSubscriptionsViewModel
@Binding
var loadingPath: CustomerCenterConfigData.HelpPath?
@Environment(\.openURL)
var openURL

@Environment(\.localization)
private var localization: CustomerCenterConfigData.Localization

var body: some View {
VStack(spacing: 16) {
let filteredPaths = self.viewModel.screen.paths.filter { path in
#if targetEnvironment(macCatalyst)
return path.type == .refundRequest
#else
return true
#endif
#if targetEnvironment(macCatalyst)
return path.type == .refundRequest
#else
return true
#endif
}
ForEach(filteredPaths, id: \.id) { path in
ManageSubscriptionButton(path: path, viewModel: self.viewModel)
Expand Down
23 changes: 20 additions & 3 deletions RevenueCatUI/CustomerCenter/Views/RestorePurchasesAlert.swift
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,10 @@ struct RestorePurchasesAlert: ViewModifier {
private var alertType: AlertType = .restorePurchases
@Environment(\.dismiss)
private var dismiss
@Environment(\.localization)
private var localization
@Environment(\.supportInformation)
private var supportInformation: CustomerCenterConfigData.Support?

enum AlertType: Identifiable {
case purchasesRecovered, purchasesNotFound, restorePurchases
Expand All @@ -60,22 +64,35 @@ struct RestorePurchasesAlert: ViewModifier {
self.setAlertType(alertType)
}
}),
secondaryButton: .cancel(Text("Cancel"))
secondaryButton: .cancel(Text(localization.commonLocalizedString(for: .cancel)))
)

case .purchasesRecovered:
return Alert(title: Text("Purchases recovered!"),
message: Text("We applied the previously purchased items to your account. " +
"Sorry for the inconvenience."),
dismissButton: .default(Text("Dismiss")) {
dismissButton: .default(Text(localization.commonLocalizedString(for: .dismiss))) {
dismiss()
})

case .purchasesNotFound:
return Alert(title: Text(""),
message: Text("We couldn’t find any additional purchases under this account. \n\n" +
"Contact support for assistance if you think this is an error."),
dismissButton: .default(Text("Dismiss")) {
primaryButton: .default(Text(localization.commonLocalizedString(for: .contactSupport)),
action: {
let subject = self.localization.commonLocalizedString(for: .defaultSubject)
let body = self.localization.commonLocalizedString(for: .defaultBody)
if let supportInformation = self.supportInformation,
let url = URLUtilities.createMailURLIfPossible(email: supportInformation.email,
subject: subject,
body: body) {
Task {
openURL(url)
}
}
}),
secondaryButton: .default(Text(localization.commonLocalizedString(for: .dismiss))) {
dismiss()
})
}
Expand Down
41 changes: 38 additions & 3 deletions Sources/CustomerCenter/CustomerCenterConfigData.swift
Original file line number Diff line number Diff line change
Expand Up @@ -15,20 +15,24 @@

import Foundation

// swiftlint:disable missing_docs
// swiftlint:disable missing_docs nesting file_length
public typealias RCColor = PaywallColor

// swiftlint:disable nesting
public struct CustomerCenterConfigData {

public let screens: [Screen.ScreenType: Screen]
public let appearance: Appearance
public let localization: Localization
public let support: Support

public init(screens: [Screen.ScreenType: Screen], appearance: Appearance, localization: Localization) {
public init(screens: [Screen.ScreenType: Screen],
appearance: Appearance,
localization: Localization,
support: Support) {
self.screens = screens
self.appearance = appearance
self.localization = localization
self.support = support
}

public struct Localization {
Expand Down Expand Up @@ -60,6 +64,10 @@ public struct CustomerCenterConfigData {
case subEarliestExpiration = "sub_earliest_expiration"
case subEarliestRenewal = "sub_earliest_renewal"
case subExpired = "sub_expired"
case contactSupport = "contact_support"
case defaultBody = "default_body"
case defaultSubject = "default_subject"
case dismiss = "dismiss"

var defaultValue: String {
switch self {
Expand Down Expand Up @@ -97,6 +105,14 @@ public struct CustomerCenterConfigData {
return "This is your subscription with the earliest billing date."
case .subExpired:
return "This subscription has expired."
case .contactSupport:
return "Contact support"
case .defaultBody:
return "Please describe your issue or question."
case .defaultSubject:
return "Support Request"
case .dismiss:
return "Dismiss"
}
}

Expand Down Expand Up @@ -267,6 +283,16 @@ public struct CustomerCenterConfigData {

}

public struct Support {

public let email: String

public init(email: String) {
self.email = email
}

}

}

@available(iOS 14.0, macOS 11.0, tvOS 14.0, watchOS 7.0, *)
Expand All @@ -280,6 +306,7 @@ extension CustomerCenterConfigData {
let type = CustomerCenterConfigData.Screen.ScreenType(from: $0.key)
return (type, Screen(from: $0.value, localization: localization))
})
self.support = Support(from: response.customerCenter.support)
}

}
Expand Down Expand Up @@ -396,3 +423,11 @@ extension CustomerCenterConfigData.HelpPath.FeedbackSurvey.Option {
}

}

extension CustomerCenterConfigData.Support {

init(from response: CustomerCenterConfigResponse.Support) {
self.email = response.email
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,12 @@ func checkCustomerCenterConfigData(_ data: CustomerCenterConfigData) {
let screens: [CustomerCenterConfigData.Screen.ScreenType: CustomerCenterConfigData.Screen] = data.screens
let appearance: CustomerCenterConfigData.Appearance = data.appearance
let localization: CustomerCenterConfigData.Localization = data.localization
let support: CustomerCenterConfigData.Support = data.support

let _: CustomerCenterConfigData = .init(screens: screens, appearance: appearance, localization: localization)
let _: CustomerCenterConfigData = .init(screens: screens,
appearance: appearance,
localization: localization,
support: support)
}

func checkHelpPath(_ path: CustomerCenterConfigData.HelpPath) {
Expand Down